diff options
author | odersky <odersky@gmail.com> | 2016-07-15 14:53:01 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-15 14:53:01 +0200 |
commit | dfa32801f5b2c774a8c792353c1bf1d1781a4a0e (patch) | |
tree | ae02c267f927c94e97bf1aab2ef6d7b3a287cd09 | |
parent | f37e45a516ca97a27fed279c5da26574d2fe77db (diff) | |
parent | 9bde23afb0ee6c4dddf637d16defb5074c5a1db8 (diff) | |
download | dotty-dfa32801f5b2c774a8c792353c1bf1d1781a4a0e.tar.gz dotty-dfa32801f5b2c774a8c792353c1bf1d1781a4a0e.tar.bz2 dotty-dfa32801f5b2c774a8c792353c1bf1d1781a4a0e.zip |
Merge pull request #1377 from dotty-staging/#1365
Fix 1365: Fix bindings in patterns
-rw-r--r-- | src/dotty/tools/dotc/core/TypeComparer.scala | 26 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 63 | ||||
-rw-r--r-- | tests/pos/i1365.scala | 13 |
3 files changed, 80 insertions, 22 deletions
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index a895db178..e984970b4 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -936,14 +936,24 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def narrowGADTBounds(tr: NamedType, bound: Type, isUpper: Boolean): Boolean = ctx.mode.is(Mode.GADTflexible) && { val tparam = tr.symbol - typr.println(s"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}") - !bound.isRef(tparam) && { - val oldBounds = ctx.gadt.bounds(tparam) - val newBounds = - if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound) - else TypeBounds(oldBounds.lo | bound, oldBounds.hi) - isSubType(newBounds.lo, newBounds.hi) && - { ctx.gadt.setBounds(tparam, newBounds); true } + typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}") + if (bound.isRef(tparam)) false + else bound match { + case bound: TypeRef + if bound.symbol.is(BindDefinedType) && ctx.gadt.bounds.contains(bound.symbol) && + !tr.symbol.is(BindDefinedType) => + // Avoid having pattern-bound types in gadt bounds, + // as these might be eliminated once the pattern is typechecked. + // Pattern-bound type symbols should be narrowed first, only if that fails + // should symbols in the environment be constrained. + narrowGADTBounds(bound, tr, !isUpper) + case _ => + val oldBounds = ctx.gadt.bounds(tparam) + val newBounds = + if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound) + else TypeBounds(oldBounds.lo | bound, oldBounds.hi) + isSubType(newBounds.lo, newBounds.hi) && + { ctx.gadt.setBounds(tparam, newBounds); true } } } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index b9d5e3817..11a7b6753 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -454,11 +454,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit return typed(untpd.Apply(untpd.TypedSplice(arg), tree.expr), pt) case _ => } - case tref: TypeRef if tref.symbol.isClass && !ctx.isAfterTyper => - val setBefore = ctx.mode is Mode.GADTflexible - tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible)) - if (!setBefore) ctx.retractMode(Mode.GADTflexible) case _ => + if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible)) } ascription(tpt1, isWildcard = true) } @@ -774,17 +771,37 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") { val originalCtx = ctx - def caseRest(pat: Tree)(implicit ctx: Context) = { - pat foreachSubTree { - case b: Bind => - if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) - else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos) - case _ => + /** - replace all references to symbols associated with wildcards by their GADT bounds + * - enter all symbols introduced by a Bind in current scope + */ + val indexPattern = new TreeMap { + val elimWildcardSym = new TypeMap { + def apply(t: Type) = t match { + case ref @ TypeRef(_, tpnme.WILDCARD) if ctx.gadt.bounds.contains(ref.symbol) => + ctx.gadt.bounds(ref.symbol) + case TypeAlias(ref @ TypeRef(_, tpnme.WILDCARD)) if ctx.gadt.bounds.contains(ref.symbol) => + ctx.gadt.bounds(ref.symbol) + case _ => + mapOver(t) + } } + override def transform(tree: Tree)(implicit ctx: Context) = + super.transform(tree.withType(elimWildcardSym(tree.tpe))) match { + case b: Bind => + if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) + else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos) + b.symbol.info = elimWildcardSym(b.symbol.info) + b + case t => t + } + } + + def caseRest(pat: Tree)(implicit ctx: Context) = { + val pat1 = indexPattern.transform(pat) val guard1 = typedExpr(tree.guard, defn.BooleanType) val body1 = ensureNoLocalRefs(typedExpr(tree.body, pt), pt, ctx.scope.toList) .ensureConforms(pt)(originalCtx) // insert a cast if body does not conform to expected type if we disregard gadt bounds - assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) + assignType(cpy.CaseDef(tree)(pat1, guard1, body1), body1) } val gadtCtx = @@ -983,11 +1000,30 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit assignType(cpy.ByNameTypeTree(tree)(result1), result1) } + /** Define a new symbol associated with a Bind or pattern wildcard and + * make it gadt narrowable. + */ + private def newPatternBoundSym(name: Name, info: Type, pos: Position)(implicit ctx: Context) = { + val flags = if (name.isTypeName) BindDefinedType else EmptyFlags + val sym = ctx.newSymbol(ctx.owner, name, flags | Case, info, coord = pos) + if (name.isTypeName) ctx.gadt.setBounds(sym, info.bounds) + sym + } + def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") { val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) val lo1 = typed(lo) val hi1 = typed(hi) - assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) + val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) + if (ctx.mode.is(Mode.Pattern)) { + // Associate a pattern-bound type symbol with the wildcard. + // The bounds of the type symbol can be constrained when comparing a pattern type + // with an expected type in typedTyped. The type symbol is eliminated once + // the enclosing pattern has been typechecked; see `indexPattern` in `typedCase`. + val wildcardSym = newPatternBoundSym(tpnme.WILDCARD, tree1.tpe, tree.pos) + tree1.withType(wildcardSym.typeRef) + } + else tree1 } def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Tree = track("typedBind") { @@ -1003,8 +1039,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tpd.cpy.UnApply(body1)(fn, Nil, typed(untpd.Bind(tree.name, arg).withPos(tree.pos), arg.tpe) :: Nil) case _ => - val flags = if (tree.isType) BindDefinedType else EmptyFlags - val sym = ctx.newSymbol(ctx.owner, tree.name, flags | Case, body1.tpe, coord = tree.pos) + val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos) assignType(cpy.Bind(tree)(tree.name, body1), sym) } } diff --git a/tests/pos/i1365.scala b/tests/pos/i1365.scala new file mode 100644 index 000000000..e7d47da4b --- /dev/null +++ b/tests/pos/i1365.scala @@ -0,0 +1,13 @@ +import scala.collection.mutable.ArrayBuffer + +trait Message[M] +class Script[S] extends ArrayBuffer[Message[S]] with Message[S] + +class Test[A] { + def f(cmd: Message[A]): Unit = cmd match { + case s: Script[_] => s.iterator.foreach(x => f(x)) + } + def g(cmd: Message[A]): Unit = cmd match { + case s: Script[z] => s.iterator.foreach(x => g(x)) + } +} |