diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c9930e036559..5e45833ad10e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4004,7 +4004,7 @@ object Types { case nil => x foldArgs(op(x, tycon), args) - override def tryNormalize(using Context): Type = tycon match { + override def tryNormalize(using Context): Type = tycon.stripTypeVar match { case tycon: TypeRef => def tryMatchAlias = tycon.info match { case MatchAlias(alias) => @@ -4014,13 +4014,29 @@ object Types { case _ => NoType } - tryCompiletimeConstantFold.orElse(tryMatchAlias) - case _ => NoType } + /** Does this application expand to a match type? */ + def isMatchAlias(using Context): Boolean = tycon.stripTypeVar match + case tycon: TypeRef => + tycon.info match + case _: MatchAlias => true + case _ => false + case _ => false + + /** Is this an unreducible application to wildcard arguments? + * This is the case if tycon is higher-kinded. This means + * it is a subtype of a hk-lambda, but not a match alias. + * (normal parameterized aliases are removed in `appliedTo`). + * Applications of hgher-kinded type constructors to wildcard arguments + * are equivalent to existential types, which are not supported. + */ + def isUnreducibleWild(using Context): Boolean = + tycon.isLambdaSub && hasWildcardArg && !isMatchAlias + def tryCompiletimeConstantFold(using Context): Type = tycon match { case tycon: TypeRef if defn.isCompiletimeAppliedType(tycon.symbol) => def constValue(tp: Type): Option[Any] = tp.dealias match { @@ -5471,38 +5487,43 @@ object Types { case Range(tyconLo, tyconHi) => range(derivedAppliedType(tp, tyconLo, args), derivedAppliedType(tp, tyconHi, args)) case _ => - if (args.exists(isRange)) - if (variance > 0) tp.derivedAppliedType(tycon, args.map(rangeToBounds)) - else { - val loBuf, hiBuf = new mutable.ListBuffer[Type] - // Given `C[A1, ..., An]` where sone A's are ranges, try to find - // non-range arguments L1, ..., Ln and H1, ..., Hn such that - // C[L1, ..., Ln] <: C[H1, ..., Hn] by taking the right limits of - // ranges that appear in as co- or contravariant arguments. - // Fail for non-variant argument ranges. - // If successful, the L-arguments are in loBut, the H-arguments in hiBuf. - // @return operation succeeded for all arguments. - def distributeArgs(args: List[Type], tparams: List[ParamInfo]): Boolean = args match { - case Range(lo, hi) :: args1 => - val v = tparams.head.paramVarianceSign - if (v == 0) false - else { - if (v > 0) { loBuf += lo; hiBuf += hi } - else { loBuf += hi; hiBuf += lo } - distributeArgs(args1, tparams.tail) - } - case arg :: args1 => - loBuf += arg; hiBuf += arg + if args.exists(isRange) then + if variance > 0 then + tp.derivedAppliedType(tycon, args.map(rangeToBounds)) match + case tp1: AppliedType if tp1.isUnreducibleWild => + // don't infer a type that would trigger an error later in + // Checling.checkAppliedType; fall through to default handling instead + case tp1 => + return tp1 + end if + val loBuf, hiBuf = new mutable.ListBuffer[Type] + // Given `C[A1, ..., An]` where some A's are ranges, try to find + // non-range arguments L1, ..., Ln and H1, ..., Hn such that + // C[L1, ..., Ln] <: C[H1, ..., Hn] by taking the right limits of + // ranges that appear in as co- or contravariant arguments. + // Fail for non-variant argument ranges. + // If successful, the L-arguments are in loBut, the H-arguments in hiBuf. + // @return operation succeeded for all arguments. + def distributeArgs(args: List[Type], tparams: List[ParamInfo]): Boolean = args match { + case Range(lo, hi) :: args1 => + val v = tparams.head.paramVarianceSign + if (v == 0) false + else { + if (v > 0) { loBuf += lo; hiBuf += hi } + else { loBuf += hi; hiBuf += lo } distributeArgs(args1, tparams.tail) - case nil => - true - } - if (distributeArgs(args, tp.tyconTypeParams)) - range(tp.derivedAppliedType(tycon, loBuf.toList), - tp.derivedAppliedType(tycon, hiBuf.toList)) - else range(defn.NothingType, defn.AnyType) - // TODO: can we give a better bound than `topType`? + } + case arg :: args1 => + loBuf += arg; hiBuf += arg + distributeArgs(args1, tparams.tail) + case nil => + true } + if (distributeArgs(args, tp.tyconTypeParams)) + range(tp.derivedAppliedType(tycon, loBuf.toList), + tp.derivedAppliedType(tycon, hiBuf.toList)) + else range(defn.NothingType, defn.AnyType) + // TODO: can we give a better bound than `topType`? else tp.derivedAppliedType(tycon, args) } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 7b799a25c996..24bf8886a9f2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -113,7 +113,7 @@ object Checking { def checkWildcardApply(tp: Type): Unit = tp match { case tp @ AppliedType(tycon, _) => - if (tycon.isLambdaSub && tp.hasWildcardArg) + if tp.isUnreducibleWild then report.errorOrMigrationWarning( showInferred(UnreducibleApplication(tycon), tp, tpt), tree.srcPos) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index c770c6cd1866..e6fd9deb979d 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -35,6 +35,7 @@ i7872.scala i11236.scala i11247.scala i11250 +i9999.scala # Opaque type i5720.scala diff --git a/tests/neg/i5302.scala b/tests/neg/i5302.scala index 43485ff7e61b..65f5370e175f 100644 --- a/tests/neg/i5302.scala +++ b/tests/neg/i5302.scala @@ -1,3 +1,3 @@ type L[X] -def foo = { class A; null.asInstanceOf[L[A]] } // error -def bar(x: L[_]) = x // error \ No newline at end of file +def foo = { class A; null.asInstanceOf[L[A]] } // was error, now ok, since avoidance does not produce a bad type anymore +def bar(x: L[_]) = x // error diff --git a/tests/neg/i5592.scala b/tests/pos/i5592.scala similarity index 93% rename from tests/neg/i5592.scala rename to tests/pos/i5592.scala index 37702b7b00d9..1b6f91e440da 100644 --- a/tests/neg/i5592.scala +++ b/tests/pos/i5592.scala @@ -20,10 +20,10 @@ object Test { } val eqSymmetric2: Forall[[x] =>> (y: Obj) => (EQ[x, y.type]) => (EQ[y.type, x])] = { - { (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // error + { (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // was error } val eqSymmetric3: Forall[[x] =>> Forall[[y] =>> EQ[x, y] => EQ[y, x]]] = { - { (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // error + { (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // was error } } \ No newline at end of file diff --git a/tests/pos/i9999.scala b/tests/pos/i9999.scala new file mode 100644 index 000000000000..61b84156ff4a --- /dev/null +++ b/tests/pos/i9999.scala @@ -0,0 +1,47 @@ +case class A(); +case class B(); + +type M2[X <: A|B] = X match { + case A => A + case B => B +} + +def f2(x: A|B): M2[x.type] = x match { + case _: A => A() + case _: B => B() +} + +type M1[X <: A|B] = X match { + case A => A + case B => (x: A|B) => M2[x.type] +} + +def f1(x: A|B): M1[x.type] = x match { + case _: A => A() + case _: B => (x: A|B) => f2(x) +} + +case class More(); case class Stop(); + +sealed abstract class DSL +case class Fun[F <: More|Stop => DSL](cont: F) extends DSL +case class Nop() extends DSL + +type Match2[X <: More|Stop] <: DSL = X match { + case More => Fun[(y: More|Stop) => Match1[y.type]] + case Stop => Nop +} +type Match1[X] <: DSL = X match { + case More => Nop + case Stop => Nop +} + +def fun2(x: More|Stop): Match2[x.type] = x match { + case _: More => Fun(fun1) // error + case _: Stop => Nop() +} + +def fun1(y: More|Stop): Match1[y.type] = y match { + case _: More => Nop() + case _: Stop => Nop() +} diff --git a/tests/pos/i9999a.scala b/tests/pos/i9999a.scala new file mode 100644 index 000000000000..77c678a4d33f --- /dev/null +++ b/tests/pos/i9999a.scala @@ -0,0 +1,9 @@ +object Test: + + type T[X] <: List[List[X]] + + var y = // inferred type: Any, since `T[?]` is irreducible wildcard application + val x: Any = null + ??? : T[x.type] + + y = (??? : Any)