Skip to content

Commit 06903f3

Browse files
committed
Treat wildcard args and pattern type variables the same
In a type pattern, a wildcard argument and a type variable needs to be treated the same. I.e. it should make no difference if I have C[_ >: L <: H] or C[t >: L <: H] where `t` is unused. Previously, the two constructs had largely different codepaths with different things that failed and worked. This fix is necessary to mitigate the fix for #1754. The latter fix uncovered several problems with the way wildcard arguments in patterns were treated. The change also uncovered a problem in transforms: FirstTransform eliminates all type nodes wnd with it any binders bound type symbols. This means that subsequently patVars is wrong, and therefore a TreeTypeMap over a pattern will no longer duplicate pattern- bound type variables. This caused Ycheck to fail after TailRec. The fix is to keep pattern bound type variables around in an internal annotation, which is understood by patVars.
1 parent 521cec3 commit 06903f3

File tree

4 files changed

+44
-26
lines changed

4 files changed

+44
-26
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
545545
val acc = new TreeAccumulator[List[Symbol]] {
546546
def apply(syms: List[Symbol], tree: Tree)(implicit ctx: Context) = tree match {
547547
case Bind(_, body) => apply(tree.symbol :: syms, body)
548+
case Annotated(tree, id @ Ident(tpnme.BOUNDTYPE_ANNOT)) => apply(id.symbol :: syms, tree)
548549
case _ => foldOver(syms, tree)
549550
}
550551
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ object StdNames {
143143
val INITIALIZER_PREFIX: N = "initial$"
144144
val COMPANION_MODULE_METHOD: N = "companion$module"
145145
val COMPANION_CLASS_METHOD: N = "companion$class"
146+
val BOUNDTYPE_ANNOT: N = "$boundType$"
146147
val QUOTE: N = "'"
147148
val TYPE_QUOTE: N = "type_'"
148149
val TRAIT_SETTER_SEPARATOR: N = str.TRAIT_SETTER_SEPARATOR

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import dotty.tools.dotc.ast.tpd
77
import dotty.tools.dotc.core.Phases.NeedsCompanions
88
import dotty.tools.dotc.transform.MegaPhase._
99
import ast.Trees._
10+
import ast.untpd
1011
import Flags._
1112
import Types._
1213
import Constants.Constant
@@ -188,8 +189,27 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
188189
override def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] =
189190
ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisPhase.next)))
190191

191-
private def toTypeTree(tree: Tree)(implicit ctx: Context) =
192-
TypeTree(tree.tpe).withPos(tree.pos)
192+
private object collectBinders extends TreeAccumulator[List[Ident]] {
193+
def apply(annots: List[Ident], t: Tree)(implicit ctx: Context): List[Ident] = t match {
194+
case t @ Bind(_, body) =>
195+
val annot = untpd.Ident(tpnme.BOUNDTYPE_ANNOT).withType(t.symbol.typeRef)
196+
apply(annot :: annots, body)
197+
case _ =>
198+
foldOver(annots, t)
199+
}
200+
}
201+
202+
/** Replace type tree `t` of type `T` with `TypeTree(T)`, but make sure all
203+
* binders in `t` are maintained by rewrapping binders around the type tree.
204+
* E.g. if `t` is `C[t @ (>: L <: H)]`, replace with
205+
* `t @ TC[_ >: L <: H]`. The body of the binder `t` is now wrong, but this does
206+
* not matter, as we only need the info of `t`.
207+
*/
208+
private def toTypeTree(tree: Tree)(implicit ctx: Context) = {
209+
val binders = collectBinders.apply(Nil, tree)
210+
val result: Tree = TypeTree(tree.tpe).withPos(tree.pos)
211+
(result /: binders)(Annotated(_, _))
212+
}
193213

194214
override def transformOther(tree: Tree)(implicit ctx: Context) = tree match {
195215
case tree: Import => EmptyTree

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

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -549,8 +549,8 @@ class Typer extends Namer
549549
def typedTpt = checkSimpleKinded(typedType(tree.tpt))
550550
def handlePattern: Tree = {
551551
val tpt1 = typedTpt
552-
// special case for an abstract type that comes with a class tag
553552
if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible))
553+
// special case for an abstract type that comes with a class tag
554554
tryWithClassTag(ascription(tpt1, isWildcard = true), pt)
555555
}
556556
cases(
@@ -993,37 +993,29 @@ class Typer extends Namer
993993
if (!gadtCtx.gadt.bounds.contains(sym))
994994
gadtCtx.gadt.setBounds(sym, TypeBounds.empty)
995995

996-
/** - replace all references to symbols associated with wildcards by their GADT bounds
996+
/** - strip all instantiated TypeVars from pattern types.
997+
* run/reducable.scala is a test case that shows stripping typevars is necessary.
997998
* - enter all symbols introduced by a Bind in current scope
998999
*/
9991000
val indexPattern = new TreeMap {
1000-
val elimWildcardSym = new TypeMap {
1001-
def apply(t: Type) = t match {
1002-
case ref: TypeRef if ref.name == tpnme.WILDCARD && gadtCtx.gadt.bounds.contains(ref.symbol) =>
1003-
gadtCtx.gadt.bounds(ref.symbol)
1004-
case TypeAlias(ref: TypeRef) if ref.name == tpnme.WILDCARD && gadtCtx.gadt.bounds.contains(ref.symbol) =>
1005-
gadtCtx.gadt.bounds(ref.symbol)
1006-
case _ =>
1007-
mapOver(t)
1008-
}
1001+
val stripTypeVars = new TypeMap {
1002+
def apply(t: Type) = mapOver(t)
10091003
}
10101004
override def transform(trt: Tree)(implicit ctx: Context) =
1011-
super.transform(trt.withType(elimWildcardSym(trt.tpe))) match {
1005+
super.transform(trt.withType(stripTypeVars(trt.tpe))) match {
10121006
case b: Bind =>
10131007
val sym = b.symbol
1014-
if (sym.exists) {
1008+
if (sym.name != tpnme.WILDCARD)
10151009
if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(sym)
10161010
else ctx.error(new DuplicateBind(b, tree), b.pos)
1017-
sym.info = elimWildcardSym(sym.info)
1018-
b
1019-
}
1020-
else {
1021-
assert(b.name == tpnme.WILDCARD)
1022-
b.body
1011+
if (!ctx.isAfterTyper) {
1012+
val bounds = ctx.gadt.bounds(sym)
1013+
if (bounds != null) sym.info = bounds
10231014
}
1015+
b
10241016
case t => t
10251017
}
1026-
}
1018+
}
10271019

10281020
def caseRest(pat: Tree)(implicit ctx: Context) = {
10291021
val pat1 = indexPattern.transform(pat)
@@ -1259,7 +1251,7 @@ class Typer extends Namer
12591251
assignType(cpy.ByNameTypeTree(tree)(result1), result1)
12601252
}
12611253

1262-
def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): Tree = track("typedTypeBoundsTree") {
1254+
def typedTypeBoundsTree(tree: untpd.TypeBoundsTree, pt: Type)(implicit ctx: Context): Tree = track("typedTypeBoundsTree") {
12631255
val TypeBoundsTree(lo, hi) = tree
12641256
val lo1 = typed(lo)
12651257
val hi1 = typed(hi)
@@ -1274,8 +1266,12 @@ class Typer extends Namer
12741266
// with an expected type in typedTyped. The type symbol and the defining Bind node
12751267
// are eliminated once the enclosing pattern has been typechecked; see `indexPattern`
12761268
// in `typedCase`.
1277-
val wildcardSym = ctx.newPatternBoundSymbol(tpnme.WILDCARD, tree1.tpe, tree.pos)
1278-
untpd.Bind(tpnme.WILDCARD, tree1).withType(wildcardSym.typeRef)
1269+
//val ptt = if (lo.isEmpty && hi.isEmpty) pt else
1270+
if (ctx.isAfterTyper) tree1
1271+
else {
1272+
val wildcardSym = ctx.newPatternBoundSymbol(tpnme.WILDCARD, tree1.tpe & pt, tree.pos)
1273+
untpd.Bind(tpnme.WILDCARD, tree1).withType(wildcardSym.typeRef)
1274+
}
12791275
}
12801276
else tree1
12811277
}
@@ -1744,7 +1740,7 @@ class Typer extends Namer
17441740
case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree)
17451741
case tree: untpd.LambdaTypeTree => typedLambdaTypeTree(tree)(localContext(tree, NoSymbol).setNewScope)
17461742
case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree)
1747-
case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree)
1743+
case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree, pt)
17481744
case tree: untpd.Alternative => typedAlternative(tree, pt)
17491745
case tree: untpd.PackageDef => typedPackageDef(tree)
17501746
case tree: untpd.Annotated => typedAnnotated(tree, pt)

0 commit comments

Comments
 (0)