aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2016-07-15 14:53:01 +0200
committerGitHub <noreply@github.com>2016-07-15 14:53:01 +0200
commitdfa32801f5b2c774a8c792353c1bf1d1781a4a0e (patch)
treeae02c267f927c94e97bf1aab2ef6d7b3a287cd09 /src
parentf37e45a516ca97a27fed279c5da26574d2fe77db (diff)
parent9bde23afb0ee6c4dddf637d16defb5074c5a1db8 (diff)
downloaddotty-dfa32801f5b2c774a8c792353c1bf1d1781a4a0e.tar.gz
dotty-dfa32801f5b2c774a8c792353c1bf1d1781a4a0e.tar.bz2
dotty-dfa32801f5b2c774a8c792353c1bf1d1781a4a0e.zip
Merge pull request #1377 from dotty-staging/#1365
Fix 1365: Fix bindings in patterns
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala26
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala63
2 files changed, 67 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)
}
}