Skip to content

Commit 46eb23e

Browse files
authored
Merge pull request #11847 from dotty-staging/fix-9999
Don't flag match type aliases as unreducible
2 parents 9396d57 + 3d49ba6 commit 46eb23e

File tree

7 files changed

+116
-38
lines changed

7 files changed

+116
-38
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

+54-33
Original file line numberDiff line numberDiff line change
@@ -4004,7 +4004,7 @@ object Types {
40044004
case nil => x
40054005
foldArgs(op(x, tycon), args)
40064006

4007-
override def tryNormalize(using Context): Type = tycon match {
4007+
override def tryNormalize(using Context): Type = tycon.stripTypeVar match {
40084008
case tycon: TypeRef =>
40094009
def tryMatchAlias = tycon.info match {
40104010
case MatchAlias(alias) =>
@@ -4014,13 +4014,29 @@ object Types {
40144014
case _ =>
40154015
NoType
40164016
}
4017-
40184017
tryCompiletimeConstantFold.orElse(tryMatchAlias)
4019-
40204018
case _ =>
40214019
NoType
40224020
}
40234021

4022+
/** Does this application expand to a match type? */
4023+
def isMatchAlias(using Context): Boolean = tycon.stripTypeVar match
4024+
case tycon: TypeRef =>
4025+
tycon.info match
4026+
case _: MatchAlias => true
4027+
case _ => false
4028+
case _ => false
4029+
4030+
/** Is this an unreducible application to wildcard arguments?
4031+
* This is the case if tycon is higher-kinded. This means
4032+
* it is a subtype of a hk-lambda, but not a match alias.
4033+
* (normal parameterized aliases are removed in `appliedTo`).
4034+
* Applications of hgher-kinded type constructors to wildcard arguments
4035+
* are equivalent to existential types, which are not supported.
4036+
*/
4037+
def isUnreducibleWild(using Context): Boolean =
4038+
tycon.isLambdaSub && hasWildcardArg && !isMatchAlias
4039+
40244040
def tryCompiletimeConstantFold(using Context): Type = tycon match {
40254041
case tycon: TypeRef if defn.isCompiletimeAppliedType(tycon.symbol) =>
40264042
def constValue(tp: Type): Option[Any] = tp.dealias match {
@@ -5471,38 +5487,43 @@ object Types {
54715487
case Range(tyconLo, tyconHi) =>
54725488
range(derivedAppliedType(tp, tyconLo, args), derivedAppliedType(tp, tyconHi, args))
54735489
case _ =>
5474-
if (args.exists(isRange))
5475-
if (variance > 0) tp.derivedAppliedType(tycon, args.map(rangeToBounds))
5476-
else {
5477-
val loBuf, hiBuf = new mutable.ListBuffer[Type]
5478-
// Given `C[A1, ..., An]` where sone A's are ranges, try to find
5479-
// non-range arguments L1, ..., Ln and H1, ..., Hn such that
5480-
// C[L1, ..., Ln] <: C[H1, ..., Hn] by taking the right limits of
5481-
// ranges that appear in as co- or contravariant arguments.
5482-
// Fail for non-variant argument ranges.
5483-
// If successful, the L-arguments are in loBut, the H-arguments in hiBuf.
5484-
// @return operation succeeded for all arguments.
5485-
def distributeArgs(args: List[Type], tparams: List[ParamInfo]): Boolean = args match {
5486-
case Range(lo, hi) :: args1 =>
5487-
val v = tparams.head.paramVarianceSign
5488-
if (v == 0) false
5489-
else {
5490-
if (v > 0) { loBuf += lo; hiBuf += hi }
5491-
else { loBuf += hi; hiBuf += lo }
5492-
distributeArgs(args1, tparams.tail)
5493-
}
5494-
case arg :: args1 =>
5495-
loBuf += arg; hiBuf += arg
5490+
if args.exists(isRange) then
5491+
if variance > 0 then
5492+
tp.derivedAppliedType(tycon, args.map(rangeToBounds)) match
5493+
case tp1: AppliedType if tp1.isUnreducibleWild =>
5494+
// don't infer a type that would trigger an error later in
5495+
// Checling.checkAppliedType; fall through to default handling instead
5496+
case tp1 =>
5497+
return tp1
5498+
end if
5499+
val loBuf, hiBuf = new mutable.ListBuffer[Type]
5500+
// Given `C[A1, ..., An]` where some A's are ranges, try to find
5501+
// non-range arguments L1, ..., Ln and H1, ..., Hn such that
5502+
// C[L1, ..., Ln] <: C[H1, ..., Hn] by taking the right limits of
5503+
// ranges that appear in as co- or contravariant arguments.
5504+
// Fail for non-variant argument ranges.
5505+
// If successful, the L-arguments are in loBut, the H-arguments in hiBuf.
5506+
// @return operation succeeded for all arguments.
5507+
def distributeArgs(args: List[Type], tparams: List[ParamInfo]): Boolean = args match {
5508+
case Range(lo, hi) :: args1 =>
5509+
val v = tparams.head.paramVarianceSign
5510+
if (v == 0) false
5511+
else {
5512+
if (v > 0) { loBuf += lo; hiBuf += hi }
5513+
else { loBuf += hi; hiBuf += lo }
54965514
distributeArgs(args1, tparams.tail)
5497-
case nil =>
5498-
true
5499-
}
5500-
if (distributeArgs(args, tp.tyconTypeParams))
5501-
range(tp.derivedAppliedType(tycon, loBuf.toList),
5502-
tp.derivedAppliedType(tycon, hiBuf.toList))
5503-
else range(defn.NothingType, defn.AnyType)
5504-
// TODO: can we give a better bound than `topType`?
5515+
}
5516+
case arg :: args1 =>
5517+
loBuf += arg; hiBuf += arg
5518+
distributeArgs(args1, tparams.tail)
5519+
case nil =>
5520+
true
55055521
}
5522+
if (distributeArgs(args, tp.tyconTypeParams))
5523+
range(tp.derivedAppliedType(tycon, loBuf.toList),
5524+
tp.derivedAppliedType(tycon, hiBuf.toList))
5525+
else range(defn.NothingType, defn.AnyType)
5526+
// TODO: can we give a better bound than `topType`?
55065527
else tp.derivedAppliedType(tycon, args)
55075528
}
55085529

compiler/src/dotty/tools/dotc/typer/Checking.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ object Checking {
113113

114114
def checkWildcardApply(tp: Type): Unit = tp match {
115115
case tp @ AppliedType(tycon, _) =>
116-
if (tycon.isLambdaSub && tp.hasWildcardArg)
116+
if tp.isUnreducibleWild then
117117
report.errorOrMigrationWarning(
118118
showInferred(UnreducibleApplication(tycon), tp, tpt),
119119
tree.srcPos)

compiler/test/dotc/pos-test-pickling.blacklist

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ i7872.scala
3535
i11236.scala
3636
i11247.scala
3737
i11250
38+
i9999.scala
3839

3940
# Opaque type
4041
i5720.scala

tests/neg/i5302.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
type L[X]
2-
def foo = { class A; null.asInstanceOf[L[A]] } // error
3-
def bar(x: L[_]) = x // error
2+
def foo = { class A; null.asInstanceOf[L[A]] } // was error, now ok, since avoidance does not produce a bad type anymore
3+
def bar(x: L[_]) = x // error

tests/neg/i5592.scala renamed to tests/pos/i5592.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ object Test {
2020
}
2121

2222
val eqSymmetric2: Forall[[x] =>> (y: Obj) => (EQ[x, y.type]) => (EQ[y.type, x])] = {
23-
{ (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // error
23+
{ (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // was error
2424
}
2525

2626
val eqSymmetric3: Forall[[x] =>> Forall[[y] =>> EQ[x, y] => EQ[y, x]]] = {
27-
{ (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // error
27+
{ (x: Obj) => { (y: Obj) => { (xEqy: EQ[x.type, y.type]) => xEqy.commute } } } // was error
2828
}
2929
}

tests/pos/i9999.scala

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
case class A();
2+
case class B();
3+
4+
type M2[X <: A|B] = X match {
5+
case A => A
6+
case B => B
7+
}
8+
9+
def f2(x: A|B): M2[x.type] = x match {
10+
case _: A => A()
11+
case _: B => B()
12+
}
13+
14+
type M1[X <: A|B] = X match {
15+
case A => A
16+
case B => (x: A|B) => M2[x.type]
17+
}
18+
19+
def f1(x: A|B): M1[x.type] = x match {
20+
case _: A => A()
21+
case _: B => (x: A|B) => f2(x)
22+
}
23+
24+
case class More(); case class Stop();
25+
26+
sealed abstract class DSL
27+
case class Fun[F <: More|Stop => DSL](cont: F) extends DSL
28+
case class Nop() extends DSL
29+
30+
type Match2[X <: More|Stop] <: DSL = X match {
31+
case More => Fun[(y: More|Stop) => Match1[y.type]]
32+
case Stop => Nop
33+
}
34+
type Match1[X] <: DSL = X match {
35+
case More => Nop
36+
case Stop => Nop
37+
}
38+
39+
def fun2(x: More|Stop): Match2[x.type] = x match {
40+
case _: More => Fun(fun1) // error
41+
case _: Stop => Nop()
42+
}
43+
44+
def fun1(y: More|Stop): Match1[y.type] = y match {
45+
case _: More => Nop()
46+
case _: Stop => Nop()
47+
}

tests/pos/i9999a.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test:
2+
3+
type T[X] <: List[List[X]]
4+
5+
var y = // inferred type: Any, since `T[?]` is irreducible wildcard application
6+
val x: Any = null
7+
??? : T[x.type]
8+
9+
y = (??? : Any)

0 commit comments

Comments
 (0)