diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 4afd55efdefb..1e9a8c5e5816 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -520,16 +520,6 @@ trait ConstraintHandling[AbstractContext] { } } - /** Instantiate `param` to `tp` if the constraint stays satisfiable */ - protected def tryInstantiate(param: TypeParamRef, tp: Type)(implicit actx: AbstractContext): Boolean = { - val saved = constraint - constraint = - if (addConstraint(param, tp, fromBelow = true) && - addConstraint(param, tp, fromBelow = false)) constraint.replace(param, tp) - else saved - constraint ne saved - } - /** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */ def checkPropagated(msg: => String)(result: Boolean)(implicit actx: AbstractContext): Boolean = { if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) { diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c2ac412132c8..b0a6bf25b9d8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -818,7 +818,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] { tycon1.dealiasKeepRefiningAnnots match { case tycon1: TypeParamRef => (tycon1 == tycon2 || - canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) && + canConstrain(tycon1) && isSubType(tycon1, tycon2)) && isSubArgs(args1, args2, tp1, tparams) case tycon1: TypeRef => tycon2.dealiasKeepRefiningAnnots match { @@ -892,7 +892,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] { tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), tl => tp1base.tycon.appliedTo(args1.take(lengthDiff) ++ tparams1.indices.toList.map(tl.paramRefs(_)))) - (assumedTrue(tycon2) || tryInstantiate(tycon2, tycon1.ensureLambdaSub)) && + (assumedTrue(tycon2) || isSubType(tycon1.ensureLambdaSub, tycon2)) && recur(tp1, tycon1.appliedTo(args2)) } } @@ -977,7 +977,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] { case param1: TypeParamRef => def canInstantiate = tp2 match { case AppliedType(tycon2, args2) => - tryInstantiate(param1, tycon2.ensureLambdaSub) && isSubArgs(args1, args2, tp1, tycon2.typeParams) + isSubType(param1, tycon2.ensureLambdaSub) && isSubArgs(args1, args2, tp1, tycon2.typeParams) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 0902964edea7..1f697678f6ff 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -138,6 +138,33 @@ object Inferencing { } } + /** For all type parameters occurring in `tp`: + * If the bounds of `tp` in the current constraint are equal wrt =:=, + * instantiate the type parameter to the lower bound's approximation + * (approximation because of possible F-bounds). + */ + def replaceSingletons(tp: Type)(implicit ctx: Context): Unit = { + val tr = new TypeTraverser { + def traverse(tp: Type): Unit = { + tp match { + case param: TypeParamRef => + val constraint = ctx.typerState.constraint + constraint.entry(param) match { + case TypeBounds(lo, hi) + if (hi frozen_<:< lo) => + val inst = ctx.typeComparer.approximation(param, fromBelow = true) + typr.println(i"replace singleton $param := $inst") + ctx.typerState.constraint = constraint.replace(param, inst) + case _ => + } + case _ => + } + traverseChildren(tp) + } + } + tr.traverse(tp) + } + /** If `tree` has a type lambda type, infer its type parameters by comparing with expected type `pt` */ def inferTypeParams(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree.tpe match { case tl: TypeLambda => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6d1587ffc03d..d7b836923471 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2607,7 +2607,11 @@ class Typer extends Namer def adaptNoArgsImplicitMethod(wtp: MethodType): Tree = { assert(wtp.isImplicitMethod) val tvarsToInstantiate = tvarsInParams(tree, locked).distinct - wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate)) + def instantiate(tp: Type): Unit = { + instantiateSelected(tp, tvarsToInstantiate) + replaceSingletons(tp) + } + wtp.paramInfos.foreach(instantiate) val constr = ctx.typerState.constraint def dummyArg(tp: Type) = untpd.Ident(nme.???).withTypeUnchecked(tp) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index ac74bde3ed1b..19865bccd1ed 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -1,3 +1,4 @@ +i94-nada.scala i1812.scala i1867.scala i3067.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index ce32f13c2892..1eb150c655f5 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -164,6 +164,7 @@ class CompilationTests extends ParallelTesting { compileFile("tests/neg-custom-args/nopredef.scala", defaultOptions.and("-Yno-predef")), compileFile("tests/neg-custom-args/noimports.scala", defaultOptions.and("-Yno-imports")), compileFile("tests/neg-custom-args/noimports2.scala", defaultOptions.and("-Yno-imports")), + compileFile("tests/neg-custom-args/i1650.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i3882.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i4372.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i1754.scala", allowDeepSubtypes), diff --git a/tests/neg/i1650.scala b/tests/neg-custom-args/i1650.scala similarity index 100% rename from tests/neg/i1650.scala rename to tests/neg-custom-args/i1650.scala diff --git a/tests/neg/i6385a.scala b/tests/neg/i6385a.scala new file mode 100644 index 000000000000..17761cd076ed --- /dev/null +++ b/tests/neg/i6385a.scala @@ -0,0 +1,11 @@ +class Box[F[_]] + +class C[X] +class D[X] extends C[String] + +object Test { + def f[F[_]](x: Box[F]) = ??? + def db: Box[D] = ??? + def cb: Box[C] = db // error + f[[X] => C[X]](db) // error +} \ No newline at end of file diff --git a/tests/pos/i6119.scala b/tests/pos-deep-subtype/i6119.scala similarity index 100% rename from tests/pos/i6119.scala rename to tests/pos-deep-subtype/i6119.scala diff --git a/tests/pos/i4147.scala b/tests/pos/i4147.scala new file mode 100644 index 000000000000..0395e4121e0a --- /dev/null +++ b/tests/pos/i4147.scala @@ -0,0 +1,10 @@ +trait Higher[F[_]] +trait Super[A] +trait Sub[A] extends Super[A] + +object Test { + implicit def higherSub: Higher[Sub] = ??? + implicit def deriv[F[_]](implicit bla: Higher[F]): F[String] = ??? + + val x: Super[String] = deriv +} diff --git a/tests/pos/i6385.scala b/tests/pos/i6385.scala new file mode 100644 index 000000000000..a35dbd975e00 --- /dev/null +++ b/tests/pos/i6385.scala @@ -0,0 +1,17 @@ +trait Tc1[A] +trait Tc2[A] extends Tc1[A] + +class PinTypeTo[K[_]] +object PinTypeTo { + implicit val pinType: PinTypeTo[Tc2] = new PinTypeTo[Tc2] +} + +class X +object X { + implicit def Tc2Instance[F[x] >: Tc2[x]: PinTypeTo]: F[X] = new Tc2[X] {} +} + +object app extends App { + implicitly[Tc2[X]] + implicitly[Tc1[X]] +} \ No newline at end of file