Skip to content

Commit 138eb55

Browse files
authored
Fix exhaustivity due to separate TypeVar lambdas (#18616)
2 parents 7554ed7 + 5b57e09 commit 138eb55

File tree

12 files changed

+50
-45
lines changed

12 files changed

+50
-45
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ object Trees {
770770
/** A type tree that represents an existing or inferred type */
771771
case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile)
772772
extends DenotingTree[T] with TypTree[T] {
773-
type ThisTree[+T <: Untyped] = TypeTree[T]
773+
type ThisTree[+T <: Untyped] <: TypeTree[T]
774774
override def isEmpty: Boolean = !hasType
775775
override def toString: String =
776776
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
@@ -794,7 +794,8 @@ object Trees {
794794
* - as a (result-)type of an inferred ValDef or DefDef.
795795
* Every TypeVar is created as the type of one InferredTypeTree.
796796
*/
797-
class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]
797+
class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]:
798+
type ThisTree[+T <: Untyped] <: InferredTypeTree[T]
798799

799800
/** ref.type */
800801
case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3253,7 +3253,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32533253
def matchCase(cas: Type): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) {
32543254
val cas1 = cas match {
32553255
case cas: HKTypeLambda =>
3256-
caseLambda = constrained(cas)
3256+
caseLambda = constrained(cas, ast.tpd.EmptyTree)._1
32573257
caseLambda.resultType
32583258
case _ =>
32593259
cas

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4949,6 +4949,9 @@ object Types {
49494949
if (inst.exists) inst else origin
49504950
}
49514951

4952+
def wrapInTypeTree(owningTree: Tree)(using Context): InferredTypeTree =
4953+
new InferredTypeTree().withSpan(owningTree.span).withType(this)
4954+
49524955
override def computeHash(bs: Binders): Int = identityHash(bs)
49534956
override def equals(that: Any): Boolean = this.eq(that.asInstanceOf[AnyRef])
49544957

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
530530
(rawRef, rawInfo)
531531
baseInfo match
532532
case tl: PolyType =>
533-
val (tl1, tpts) = constrained(tl, untpd.EmptyTree, alwaysAddTypeVars = true)
534-
val targs =
535-
for (tpt <- tpts) yield
536-
tpt.tpe match {
537-
case tvar: TypeVar => tvar.instantiate(fromBelow = false)
538-
}
533+
val tvars = constrained(tl)
534+
val targs = for tvar <- tvars yield
535+
tvar.instantiate(fromBelow = false)
539536
(baseRef.appliedTo(targs), extractParams(tl.instantiate(targs)))
540537
case methTpe =>
541538
(baseRef, extractParams(methTpe))

compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ object TypeTestsCasts {
8282
case tp: TypeProxy => underlyingLambda(tp.superType)
8383
}
8484
val typeLambda = underlyingLambda(tycon)
85-
val tvars = constrained(typeLambda, untpd.EmptyTree, alwaysAddTypeVars = true)._2.map(_.tpe)
85+
val tvars = constrained(typeLambda)
8686
val P1 = tycon.appliedTo(tvars)
8787

8888
debug.println("before " + ctx.typerState.constraint.show)

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,8 @@ object SpaceEngine {
476476
erase(parent, inArray, isValue, isTyped)
477477

478478
case tref: TypeRef if tref.symbol.isPatternBound =>
479-
if inArray then tref.underlying
480-
else if isValue then tref.superType
479+
if inArray then erase(tref.underlying, inArray, isValue, isTyped)
480+
else if isValue then erase(tref.superType, inArray, isValue, isTyped)
481481
else WildcardType
482482

483483
case _ => tp
@@ -531,7 +531,7 @@ object SpaceEngine {
531531
val mt: MethodType = unapp.widen match {
532532
case mt: MethodType => mt
533533
case pt: PolyType =>
534-
val tvars = pt.paramInfos.map(newTypeVar(_))
534+
val tvars = constrained(pt)
535535
val mt = pt.instantiate(tvars).asInstanceOf[MethodType]
536536
scrutineeTp <:< mt.paramInfos(0)
537537
// force type inference to infer a narrower type: could be singleton

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ object Inferencing {
317317
def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match
318318
case tl: TypeLambda =>
319319
val (tl1, tvars) = constrained(tl, tree)
320-
var tree1 = AppliedTypeTree(tree.withType(tl1), tvars)
320+
val tree1 = AppliedTypeTree(tree.withType(tl1), tvars.map(_.wrapInTypeTree(tree)))
321321
tree1.tpe <:< pt
322322
if isFullyDefined(tree1.tpe, force = ForceDegree.failBottom) then
323323
tree1

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

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -726,41 +726,39 @@ object ProtoTypes {
726726
tl: TypeLambda, owningTree: untpd.Tree,
727727
alwaysAddTypeVars: Boolean,
728728
nestingLevel: Int = ctx.nestingLevel
729-
): (TypeLambda, List[TypeTree]) = {
729+
): (TypeLambda, List[TypeVar]) = {
730730
val state = ctx.typerState
731731
val addTypeVars = alwaysAddTypeVars || !owningTree.isEmpty
732732
if (tl.isInstanceOf[PolyType])
733733
assert(!ctx.typerState.isCommittable || addTypeVars,
734734
s"inconsistent: no typevars were added to committable constraint ${state.constraint}")
735735
// hk type lambdas can be added to constraints without typevars during match reduction
736736

737-
def newTypeVars(tl: TypeLambda): List[TypeTree] =
738-
for (paramRef <- tl.paramRefs)
739-
yield {
740-
val tt = InferredTypeTree().withSpan(owningTree.span)
737+
def newTypeVars(tl: TypeLambda): List[TypeVar] =
738+
for paramRef <- tl.paramRefs
739+
yield
741740
val tvar = TypeVar(paramRef, state, nestingLevel)
742741
state.ownedVars += tvar
743-
tt.withType(tvar)
744-
}
742+
tvar
745743

746744
val added = state.constraint.ensureFresh(tl)
747-
val tvars = if (addTypeVars) newTypeVars(added) else Nil
748-
TypeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]])
745+
val tvars = if addTypeVars then newTypeVars(added) else Nil
746+
TypeComparer.addToConstraint(added, tvars)
749747
(added, tvars)
750748
}
751749

752-
def constrained(tl: TypeLambda, owningTree: untpd.Tree)(using Context): (TypeLambda, List[TypeTree]) =
750+
def constrained(tl: TypeLambda, owningTree: untpd.Tree)(using Context): (TypeLambda, List[TypeVar]) =
753751
constrained(tl, owningTree,
754752
alwaysAddTypeVars = tl.isInstanceOf[PolyType] && ctx.typerState.isCommittable)
755753

756-
/** Same as `constrained(tl, EmptyTree)`, but returns just the created type lambda */
757-
def constrained(tl: TypeLambda)(using Context): TypeLambda =
758-
constrained(tl, EmptyTree)._1
754+
/** Same as `constrained(tl, EmptyTree, alwaysAddTypeVars = true)`, but returns just the created type vars. */
755+
def constrained(tl: TypeLambda)(using Context): List[TypeVar] =
756+
constrained(tl, EmptyTree, alwaysAddTypeVars = true)._2
759757

760758
/** Instantiate `tl` with fresh type variables added to the constraint. */
761759
def instantiateWithTypeVars(tl: TypeLambda)(using Context): Type =
762-
val targs = constrained(tl, ast.tpd.EmptyTree, alwaysAddTypeVars = true)._2
763-
tl.instantiate(targs.tpes)
760+
val tvars = constrained(tl)
761+
tl.instantiate(tvars)
764762

765763
/** A fresh type variable added to the current constraint.
766764
* @param bounds The initial bounds of the variable
@@ -779,7 +777,7 @@ object ProtoTypes {
779777
pt => bounds :: Nil,
780778
pt => represents.orElse(defn.AnyType))
781779
constrained(poly, untpd.EmptyTree, alwaysAddTypeVars = true, nestingLevel)
782-
._2.head.tpe.asInstanceOf[TypeVar]
780+
._2.head
783781

784782
/** If `param` was created using `newTypeVar(..., represents = X)`, returns X.
785783
* This is used in:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4347,7 +4347,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
43474347
var typeArgs = tree match
43484348
case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree(_))
43494349
case _ => Nil
4350-
if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2
4350+
if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2.map(_.wrapInTypeTree(tree))
43514351
convertNewGenericArray(readapt(tree.appliedToTypeTrees(typeArgs)))
43524352
case wtp =>
43534353
val isStructuralCall = wtp.isValueType && isStructuralTermSelectOrApply(tree)

compiler/test/dotty/tools/SignatureTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class SignatureTest:
6363
| def tuple2(x: Foo *: (T | Tuple) & Foo): Unit = {}
6464
|""".stripMargin):
6565
val cls = requiredClass("A")
66-
val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda], untpd.EmptyTree, alwaysAddTypeVars = true)._2.head.tpe
66+
val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda]).head
6767
tvar <:< defn.TupleTypeRef
6868
val prefix = cls.typeRef.appliedTo(tvar)
6969

@@ -89,7 +89,7 @@ class SignatureTest:
8989
| def and(x: T & Foo): Unit = {}
9090
|""".stripMargin):
9191
val cls = requiredClass("A")
92-
val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda], untpd.EmptyTree, alwaysAddTypeVars = true)._2.head.tpe
92+
val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda]).head
9393
val prefix = cls.typeRef.appliedTo(tvar)
9494
val ref = prefix.select(cls.requiredMethod("and")).asInstanceOf[TermRef]
9595

compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ class ConstraintsTest:
1919
@Test def mergeParamsTransitivity: Unit =
2020
inCompilerContext(TestConfiguration.basicClasspath,
2121
scalaSources = "trait A { def foo[S, T, R]: Any }") {
22-
val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2
23-
val List(s, t, r) = tvars.tpes
22+
val List(s, t, r) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda])
2423

2524
val innerCtx = ctx.fresh.setExploreTyperState()
2625
inContext(innerCtx) {
@@ -38,8 +37,7 @@ class ConstraintsTest:
3837
@Test def mergeBoundsTransitivity: Unit =
3938
inCompilerContext(TestConfiguration.basicClasspath,
4039
scalaSources = "trait A { def foo[S, T]: Any }") {
41-
val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2
42-
val List(s, t) = tvars.tpes
40+
val List(s, t) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda])
4341

4442
val innerCtx = ctx.fresh.setExploreTyperState()
4543
inContext(innerCtx) {
@@ -57,32 +55,29 @@ class ConstraintsTest:
5755
@Test def validBoundsInit: Unit = inCompilerContext(
5856
TestConfiguration.basicClasspath,
5957
scalaSources = "trait A { def foo[S >: T <: T | Int, T <: String]: Any }") {
60-
val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2
61-
val List(s, t) = tvars.tpes
58+
val List(s, t) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda])
6259

63-
val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.asInstanceOf[TypeVar].origin): @unchecked
60+
val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.origin): @unchecked
6461
assert(lo =:= defn.NothingType, i"Unexpected lower bound $lo for $t: ${ctx.typerState.constraint}")
6562
assert(hi =:= defn.StringType, i"Unexpected upper bound $hi for $t: ${ctx.typerState.constraint}") // used to be Any
6663
}
6764

6865
@Test def validBoundsUnify: Unit = inCompilerContext(
6966
TestConfiguration.basicClasspath,
7067
scalaSources = "trait A { def foo[S >: T <: T | Int, T <: String | Int]: Any }") {
71-
val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2
72-
val List(s, t) = tvars.tpes
68+
val List(s, t) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda])
7369

7470
s <:< t
7571

76-
val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.asInstanceOf[TypeVar].origin): @unchecked
72+
val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.origin): @unchecked
7773
assert(lo =:= defn.NothingType, i"Unexpected lower bound $lo for $t: ${ctx.typerState.constraint}")
7874
assert(hi =:= (defn.StringType | defn.IntType), i"Unexpected upper bound $hi for $t: ${ctx.typerState.constraint}")
7975
}
8076

8177
@Test def validBoundsReplace: Unit = inCompilerContext(
8278
TestConfiguration.basicClasspath,
8379
scalaSources = "trait X; trait A { def foo[S <: U | X, T, U]: Any }") {
84-
val tvarTrees = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2
85-
val tvars @ List(s, t, u) = tvarTrees.tpes.asInstanceOf[List[TypeVar]]
80+
val tvars @ List(s, t, u) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda])
8681
s =:= t
8782
t =:= u
8883

tests/pos/i14224.1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//> using options -Werror
2+
3+
// Derived from the extensive test in the gist in i14224
4+
// Minimising to the false positive in SealedTrait1.either
5+
6+
sealed trait Foo[A, A1 <: A]
7+
final case class Bar[A, A1 <: A](value: A1) extends Foo[A, A1]
8+
9+
class Main:
10+
def test[A, A1 <: A](foo: Foo[A, A1]): A1 = foo match
11+
case Bar(v) => v

0 commit comments

Comments
 (0)