Skip to content

Commit c5a76f0

Browse files
authored
Merge pull request #8808 from dotty-staging/dealias-type-test
Fix #5494: Spurious unchecked warning when type testing an alias
2 parents 35171b2 + 841d191 commit c5a76f0

File tree

18 files changed

+221
-93
lines changed

18 files changed

+221
-93
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ object Trees {
503503
type ThisTree[-T >: Untyped] = If[T]
504504
def isInline = false
505505
}
506-
class InlineIf[T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])(implicit @constructorOnly src: SourceFile)
506+
class InlineIf[-T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])(implicit @constructorOnly src: SourceFile)
507507
extends If(cond, thenp, elsep) {
508508
override def isInline = true
509509
override def toString = s"InlineIf($cond, $thenp, $elsep)"
@@ -529,7 +529,7 @@ object Trees {
529529
type ThisTree[-T >: Untyped] = Match[T]
530530
def isInline = false
531531
}
532-
class InlineMatch[T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile)
532+
class InlineMatch[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile)
533533
extends Match(selector, cases) {
534534
override def isInline = true
535535
override def toString = s"InlineMatch($selector, $cases)"
@@ -579,7 +579,7 @@ object Trees {
579579
}
580580

581581
/** Array(elems) */
582-
class JavaSeqLiteral[T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])(implicit @constructorOnly src: SourceFile)
582+
class JavaSeqLiteral[-T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])(implicit @constructorOnly src: SourceFile)
583583
extends SeqLiteral(elems, elemtpt) {
584584
override def toString: String = s"JavaSeqLiteral($elems, $elemtpt)"
585585
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ object Annotations {
8686
myTree.asInstanceOf[Tree]
8787

8888
override def isEvaluating: Boolean = myTree == null
89-
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree]
89+
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree @unchecked]
9090
}
9191

9292
/** An annotation indicating the body of a right-hand side,
@@ -119,7 +119,7 @@ object Annotations {
119119
myTree.asInstanceOf[Tree]
120120

121121
override def isEvaluating: Boolean = myTree == null
122-
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree]
122+
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree @unchecked]
123123
}
124124

125125
object LazyBodyAnnotation {

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ object Completion {
8989
completionPrefix(selector :: Nil, pos)
9090
}.getOrElse("")
9191

92-
case (ref: RefTree) :: _ =>
92+
case (ref: untpd.RefTree) :: _ =>
9393
if (ref.name == nme.ERROR) ""
9494
else ref.name.toString.take(pos.span.point - ref.span.point)
9595

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

Lines changed: 67 additions & 67 deletions
Large diffs are not rendered by default.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ object Erasure {
907907
*/
908908
private def addRetainedInlineBodies(stats: List[untpd.Tree])(using Context): List[untpd.Tree] =
909909
lazy val retainerDef: Map[Symbol, DefDef] = stats.collect {
910-
case stat: DefDef if stat.symbol.name.is(BodyRetainerName) =>
910+
case stat: DefDef @unchecked if stat.symbol.name.is(BodyRetainerName) =>
911911
val retainer = stat.symbol
912912
val origName = retainer.name.asTermName.exclude(BodyRetainerName)
913913
val inlineMeth = ctx.atPhase(ctx.typerPhase) {
@@ -918,7 +918,7 @@ object Erasure {
918918
(inlineMeth, stat)
919919
}.toMap
920920
stats.mapConserve {
921-
case stat: DefDef if stat.symbol.isRetainedInlineMethod =>
921+
case stat: DefDef @unchecked if stat.symbol.isRetainedInlineMethod =>
922922
val rdef = retainerDef(stat.symbol)
923923
val fromParams = untpd.allParamSyms(rdef)
924924
val toParams = untpd.allParamSyms(stat)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ class TreeChecker extends Phase with SymTransformer {
455455

456456
override def typedClosure(tree: untpd.Closure, pt: Type)(using Context): Tree = {
457457
if (!ctx.phase.lambdaLifted) nestingBlock match {
458-
case block @ Block((meth : DefDef) :: Nil, closure: Closure) =>
458+
case block @ Block((meth : untpd.DefDef) :: Nil, closure: untpd.Closure) =>
459459
assert(meth.symbol == closure.meth.symbol, "closure.meth symbol not equal to method symbol. Block: " + block.show)
460460

461461
case block: untpd.Block =>

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ object TypeTestsCasts {
4848
* 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`:
4949
* (a) replace `Ts` with fresh type variables `Xs`
5050
* (b) constrain `Xs` with `pre.F[Xs] <:< X`
51-
* (c) instantiate Xs and check `pre.F[Xs] <:< P`
51+
* (c) maximize `pre.F[Xs]` and check `pre.F[Xs] <:< P`
5252
* 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
5353
* 7. if `P` is a refinement type, FALSE
5454
* 8. otherwise, TRUE
@@ -90,7 +90,8 @@ object TypeTestsCasts {
9090
}
9191
}.apply(tp)
9292

93-
def isClassDetermined(X: Type, P: AppliedType)(implicit ctx: Context) = {
93+
/** Returns true if the type arguments of `P` can be determined from `X` */
94+
def typeArgsTrivial(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) {
9495
val AppliedType(tycon, _) = P
9596

9697
def underlyingLambda(tp: Type): TypeLambda = tp.ensureLambdaSub match {
@@ -105,10 +106,18 @@ object TypeTestsCasts {
105106
debug.println("P1 : " + P1.show)
106107
debug.println("X : " + X.show)
107108

108-
P1 <:< X // constraint P1
109+
// It does not matter if P1 is not a subtype of X.
110+
// It just tries to infer type arguments of P1 from X if the value x
111+
// conforms to the type skeleton pre.F[_]. Then it goes on to check
112+
// if P1 <: P, which means the type arguments in P are trivial,
113+
// thus no runtime checks are needed for them.
114+
P1 <:< X
109115

110-
// use fromScala2x to avoid generating pattern bound symbols
111-
maximizeType(P1, span, fromScala2x = true)
116+
// Maximization of the type means we try to cover all possible values
117+
// which conform to the skeleton pre.F[_] and X. Then we have to make
118+
// sure all of them are actually of the type P, which implies that the
119+
// type arguments in P are trivial (no runtime check needed).
120+
maximizeType(P1, span, fromScala2x = false)
112121

113122
val res = P1 <:< P
114123
debug.println("P1 : " + P1.show)
@@ -117,7 +126,7 @@ object TypeTestsCasts {
117126
res
118127
}
119128

120-
def recur(X: Type, P: Type): Boolean = (X <:< P) || (P match {
129+
def recur(X: Type, P: Type): Boolean = (X <:< P) || (P.dealias match {
121130
case _: SingletonType => true
122131
case _: TypeProxy
123132
if isAbstract(P) => false
@@ -136,10 +145,12 @@ object TypeTestsCasts {
136145
// See TypeComparer#either
137146
recur(tp1, P) && recur(tp2, P)
138147
case _ =>
139-
// first try withou striping type parameters for performance
140-
X.classSymbol.exists && P.classSymbol.exists && !X.classSymbol.asClass.mayHaveCommonChild(P.classSymbol.asClass) ||
141-
isClassDetermined(X, tpe)(ctx.fresh.setNewTyperState()) ||
142-
isClassDetermined(stripTypeParam(X), tpe)(ctx.fresh.setNewTyperState())
148+
// always false test warnings are emitted elsewhere
149+
X.classSymbol.exists && P.classSymbol.exists &&
150+
!X.classSymbol.asClass.mayHaveCommonChild(P.classSymbol.asClass) ||
151+
// first try without striping type parameters for performance
152+
typeArgsTrivial(X, tpe) ||
153+
typeArgsTrivial(stripTypeParam(X), tpe)
143154
}
144155
case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
145156
case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -913,8 +913,8 @@ trait Applications extends Compatibility {
913913
tree.args match {
914914
case (arg @ Match(EmptyTree, cases)) :: Nil =>
915915
cases.foreach {
916-
case CaseDef(Typed(_: Ident, _), _, _) => // OK
917-
case CaseDef(Bind(_, Typed(_: Ident, _)), _, _) => // OK
916+
case CaseDef(Typed(_: untpd.Ident, _), _, _) => // OK
917+
case CaseDef(Bind(_, Typed(_: untpd.Ident, _)), _, _) => // OK
918918
case CaseDef(Ident(name), _, _) if name == nme.WILDCARD => // Ok
919919
case CaseDef(pat, _, _) =>
920920
ctx.error(UnexpectedPatternForSummonFrom(pat), pat.sourcePos)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,8 @@ object Nullables:
489489

490490
object retyper extends ReTyper:
491491
override def typedUnadapted(t: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = t match
492-
case t: ValDef if !t.symbol.is(Lazy) => super.typedUnadapted(t, pt, locked)
493-
case t: MemberDef => promote(t)
492+
case t: untpd.ValDef if !t.symbol.is(Lazy) => super.typedUnadapted(t, pt, locked)
493+
case t: untpd.MemberDef => promote(t)
494494
case _ => super.typedUnadapted(t, pt, locked)
495495

496496
def postProcess(formal: Type, arg: Tree): Tree =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ class Typer extends Namer
12641264
.map(cas => untpd.unbind(untpd.unsplice(cas.pat)))
12651265
.zip(mt.cases)
12661266
.forall {
1267-
case (pat: Typed, pt) =>
1267+
case (pat: untpd.Typed, pt) =>
12681268
// To check that pattern types correspond we need to type
12691269
// check `pat` here and throw away the result.
12701270
val gadtCtx: Context = ctx.fresh.setFreshGADTBounds

language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ class DottyLanguageServer extends LanguageServer
392392
val refs =
393393
path match {
394394
// Selected a renaming in an import node
395-
case untpd.ImportSelector(_, rename: Ident, _) :: (_: Import) :: rest if rename.span.contains(pos.span) =>
395+
case untpd.ImportSelector(_, rename: untpd.Ident, _) :: (_: Import) :: rest if rename.span.contains(pos.span) =>
396396
findRenamedReferences(uriTrees, syms, rename.name)
397397

398398
// Selected a reference that has been renamed

tests/neg-custom-args/isInstanceOf/3324g.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Test {
1212
}
1313

1414
def quux[T](a: A[T]): Unit = a match {
15-
case _: B[T] => // should be an error!!
15+
case _: B[T] => // error!!
1616
}
1717

1818
quux(new C[Int])
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
object Test1 {
2+
trait Tree[-T]
3+
4+
class JavaSeqLiteral[T] extends Tree[T]
5+
6+
trait Type
7+
8+
class DummyTree extends JavaSeqLiteral[Any]
9+
10+
def foo1(tree: Tree[Type]) =
11+
tree.isInstanceOf[JavaSeqLiteral[Type]] // error
12+
13+
foo1(new DummyTree)
14+
}
15+
16+
object Test2 {
17+
trait Tree[-T]
18+
19+
class JavaSeqLiteral[-T] extends Tree[T]
20+
21+
trait Type
22+
23+
class DummyTree extends JavaSeqLiteral[Any]
24+
25+
def foo1(tree: Tree[Type]) =
26+
tree.isInstanceOf[JavaSeqLiteral[Type]]
27+
28+
foo1(new DummyTree)
29+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Test {
2+
trait A[+T]
3+
class B[T] extends A[T]
4+
5+
class C
6+
class D extends C
7+
8+
def quux(a: A[C]): Unit = a match {
9+
case _: B[C] => // error!!
10+
}
11+
12+
quux(new B[D])
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class A
2+
class B
3+
4+
type AorB = A | B
5+
6+
def foo(any: Any) = any match {
7+
case aorb: AorB =>
8+
case _ =>
9+
}
10+
11+
def bar[T](x: List[T]) = x.isInstanceof[List[Int]] // error
12+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
object Test1 {
2+
trait Tree
3+
trait Context
4+
5+
def foo1(myTree: Tree | (Context => Tree)) =
6+
println(myTree.isInstanceOf[Tree])
7+
8+
def foo2(myTree: Tree | (Context => Tree)) =
9+
myTree match
10+
case treeFn: (Context => Tree) => // error
11+
case _ =>
12+
13+
def foo3(myTree: Tree | (Context => Tree)) =
14+
myTree match
15+
case treeFn: (_ => _) => // ok
16+
case _ =>
17+
}
18+
19+
object Test2 {
20+
trait Tree[-T]
21+
trait Context
22+
23+
trait Type
24+
25+
def foo1(myTree: Tree[Type] | (Context => Tree[Type])) =
26+
println(myTree.isInstanceOf[Tree[Type]]) // error
27+
/* class DummyTree extends Tree[Nothing] with (Context => Tree[Type]) */
28+
29+
def foo2(myTree: Tree[Type] | (Context => Tree[Type])) =
30+
myTree match
31+
case treeFn: (Context => Tree[Type]) => // error
32+
case _ =>
33+
34+
def foo3(myTree: Tree[Type] | (Context => Tree[Type])) =
35+
myTree match
36+
case treeFn: (_ => _) => // ok
37+
case _ =>
38+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class A[-T]
2+
class B[T] extends A[T]
3+
4+
object Test {
5+
def foo(x: A[Null]) = x match {
6+
case x: B[Null] =>
7+
case _ =>
8+
}
9+
}
10+
11+
def bar[T](x: List[T]) = x.isInstanceof[List[Int]] // error
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait A[T]
2+
trait B[T] extends A[T]
3+
4+
object Test {
5+
def foo(x: ([X] =>> A[X])[Any]) = x match {
6+
case x: ([X] =>> B[X])[Any] =>
7+
case _ =>
8+
}
9+
10+
def bar(x: ([X] =>> A[X])[Any]) = x match {
11+
case x: ([X] =>> B[Nothing])[Any] => // error
12+
case _ =>
13+
}
14+
}

0 commit comments

Comments
 (0)