summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-08-17 15:45:01 -0700
committerPaul Phillips <paulp@improving.org>2013-08-17 23:40:46 -0700
commit017460e63c3ca9d5cc1ceb836c1e223492d47c7e (patch)
treeb76ce85a96a08118992d270d5194d8e942cb1ced
parentef30ea34b2f44aa385ea2fc247457eb9b90ae1a6 (diff)
downloadscala-017460e63c3ca9d5cc1ceb836c1e223492d47c7e.tar.gz
scala-017460e63c3ca9d5cc1ceb836c1e223492d47c7e.tar.bz2
scala-017460e63c3ca9d5cc1ceb836c1e223492d47c7e.zip
Reworked MaybeTypedBound.
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala161
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala86
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala3
4 files changed, 130 insertions, 121 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index e49dd22948..45aa1106f0 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -563,6 +563,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
// (At least conceptually: `true` is an instance of class `Boolean`)
private def widenToClass(tp: Type): Type =
if (tp.typeSymbol.isClass) tp
+ else if (tp.baseClasses.isEmpty) sys.error("Bad type: " + tp)
else tp.baseType(tp.baseClasses.head)
object TypeConst extends TypeConstExtractor {
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
index 05a4f1b298..a91f24ef0e 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
@@ -54,16 +54,32 @@ trait MatchTranslation extends CpsPatternHacks {
private def setVarInfo(sym: Symbol, info: Type) =
sym setInfo debug.patmatResult(s"changing ${sym.defString} to")(repeatedToSeq(info))
- trait MatchTranslator extends TreeMakers {
+ private def hasSym(t: Tree) = t.symbol != null && t.symbol != NoSymbol
+
+ trait MatchTranslator extends TreeMakers with TreeMakerWarnings {
import typer.context
+ object SymbolBound {
+ def unapply(tree: Tree): Option[(Symbol, Tree)] = tree match {
+ case Bind(_, expr) if hasSym(tree) => Some(tree.symbol -> expr)
+ case _ => None
+ }
+ }
+
+ def newBoundTree(tree: Tree, pt: Type): BoundTree = tree match {
+ case SymbolBound(sym, expr) => BoundTree(setVarInfo(sym, pt), expr)
+ case _ => BoundTree(setVarInfo(freshSym(tree.pos, prefix = "p"), pt), tree)
+ }
+
final case class BoundTree(binder: Symbol, tree: Tree) {
+ private lazy val extractor = ExtractorCall(tree)
+
def pos = tree.pos
def tpe = binder.info.dealiasWiden // the type of the variable bound to the pattern
def pt = unbound match {
- case Star(tpt) => this glbWith seqType(tpt.tpe)
- case Typed(_, tpt) => tpt.tpe
- case tree => tree.tpe
+ case Star(tpt) => this glbWith seqType(tpt.tpe)
+ case TypeBound(tpe) => tpe
+ case tree => tree.tpe
}
def repeatedType = unbound match {
case Star(tpt) => tpt.tpe
@@ -71,29 +87,18 @@ trait MatchTranslation extends CpsPatternHacks {
}
def glbWith(other: Type) = glb(tpe :: other :: Nil).normalize
- private lazy val extractor = ExtractorCall(tree)
-
- object MaybeBoundTyped {
- object NonNullTyped {
- // the Ident subpattern can be ignored, subpatBinder or patBinder tell us all we need to know about it
- def unapply(tree: Typed): Option[Type] = tree match {
- case Typed(Ident(_), _) if tree.tpe != null => Some((tree.tpe))
- case _ => None
- }
+ object SymbolAndTypeBound {
+ def unapply(tree: Tree): Option[(Symbol, Type)] = tree match {
+ case SymbolBound(sym, SymbolAndTypeBound(_, tpe)) => Some(sym -> tpe)
+ case TypeBound(tpe) => Some(binder -> tpe)
+ case _ => None
}
+ }
- /** Decompose the pattern in `tree`, of shape C(p_1, ..., p_N), into a list of N symbols, and a list of its N sub-trees
- * The list of N symbols contains symbols for every bound name as well as the un-named sub-patterns (fresh symbols are generated here for these).
- * The returned type is the one inferred by inferTypedPattern (`owntype`)
- *
- * @arg patBinder symbol used to refer to the result of the previous pattern's extractor
- * (will later be replaced by the outer tree with the correct tree to refer to that patterns result)
- */
- def unapply(tree: Tree): Option[(Symbol, Type)] = tree match {
- case Bound(namedBinder, MaybeBoundTyped(_, tpe)) => Some((namedBinder, tpe)) // possible nested bindings - use the outermost
- case NonNullTyped(tpe) => Some((binder, tpe)) // binder used if no local bindings
- case Bind(_, expr) => unapply(expr)
- case _ => None
+ object TypeBound {
+ def unapply(tree: Tree): Option[Type] = unbind(tree) match {
+ case Typed(Ident(_), _) if tree.tpe != null => Some(tree.tpe)
+ case _ => None
}
}
@@ -108,15 +113,12 @@ trait MatchTranslation extends CpsPatternHacks {
private def noStep() = step()()
private def unsupportedPatternMsg = sm"""
- |unsupported pattern: $this (this is a scalac bug.)
- |Tree diagnostics:
- | ${asCompactDebugString(tree)}
+ |unsupported pattern: ${tree.shortClass} / $this (this is a scalac bug.)
|""".trim
-
// example check: List[Int] <:< ::[Int]
private def extractorStep(): TranslationStep = {
- import extractor.{ paramType, treeMaker, subBindersAndPatterns }
+ import extractor.{ paramType, treeMaker }
if (!extractor.isTyped)
ErrorUtils.issueNormalTypeError(tree, "Could not typecheck extractor call: "+ extractor)(context)
@@ -137,7 +139,7 @@ trait MatchTranslation extends CpsPatternHacks {
if (this ensureConformsTo paramType) treeMaker(binder, false, pos) :: Nil
else typeTest :: extraction :: Nil
)
- step(makers: _*)(subBindersAndPatterns: _*)
+ step(makers: _*)(extractor.subBoundTrees: _*)
}
// Summary of translation cases. I moved the excerpts from the specification further below so all
@@ -157,20 +159,14 @@ trait MatchTranslation extends CpsPatternHacks {
def nextStep(): TranslationStep = tree match {
case WildcardPattern() => noStep()
case _: UnApply | _: Apply => extractorStep()
- case MaybeBoundTyped(sub, subPt) => typeTestStep(sub, subPt)
- case Bound(sub, subTree) => bindingStep(sub, subTree)
+ case SymbolAndTypeBound(sym, tpe) => typeTestStep(sym, tpe)
+ case TypeBound(tpe) => typeTestStep(binder, tpe)
+ case SymbolBound(sym, expr) => bindingStep(sym, expr)
case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => equalityTestStep()
case Alternative(alts) => alternativesStep(alts)
- case Bind(_, _) => devWarning(s"Bind tree with unbound symbol $tree") ; noStep()
case _ => context.unit.error(pos, unsupportedPatternMsg) ; noStep()
}
- def translate(): List[TreeMaker] = tree match {
- case CaseDef(pat, guard, body) => BoundTree(binder, pat).translate() ++ translateGuard(guard) ++ translateBody(body)
- case _ => nextStep() merge (_.translate())
- }
- def translatePat(pat: Tree) = nextStep()
- def translateGuard(guard: Tree) = if (tree eq EmptyTree) Nil else GuardTreeMaker(guard) :: Nil
- def translateBody(body: Tree) = BodyTreeMaker(body, pt) :: Nil
+ def translate(): List[TreeMaker] = nextStep() merge (_.translate())
private def setInfo(paramType: Type): Boolean = {
devWarning(s"resetting info of $this to $paramType")
@@ -204,69 +200,6 @@ trait MatchTranslation extends CpsPatternHacks {
override def toString = if (subpatterns.isEmpty) "" else subpatterns.mkString("(", ", ", ")")
}
- // Why is it so difficult to say "here's a name and a context, give me any
- // matching symbol in scope" ? I am sure this code is wrong, but attempts to
- // use the scopes of the contexts in the enclosing context chain discover
- // nothing. How to associate a name with a symbol would would be a wonderful
- // linkage for which to establish a canonical acquisition mechanism.
- def matchingSymbolInScope(pat: Tree): Symbol = {
- def declarationOfName(tpe: Type, name: Name): Symbol = tpe match {
- case PolyType(tparams, restpe) => tparams find (_.name == name) getOrElse declarationOfName(restpe, name)
- case MethodType(params, restpe) => params find (_.name == name) getOrElse declarationOfName(restpe, name)
- case ClassInfoType(_, _, clazz) => clazz.rawInfo member name
- case _ => NoSymbol
- }
- pat match {
- case Bind(name, _) =>
- context.enclosingContextChain.foldLeft(NoSymbol: Symbol)((res, ctx) =>
- res orElse declarationOfName(ctx.owner.rawInfo, name))
- case _ => NoSymbol
- }
- }
-
- // Issue better warnings than "unreachable code" when people mis-use
- // variable patterns thinking they bind to existing identifiers.
- //
- // Possible TODO: more deeply nested variable patterns, like
- // case (a, b) => 1 ; case (c, d) => 2
- // However this is a pain (at least the way I'm going about it)
- // and I have to think these detailed errors are primarily useful
- // for beginners, not people writing nested pattern matches.
- def checkMatchVariablePatterns(cases: List[CaseDef]) {
- // A string describing the first variable pattern
- var vpat: String = null
- // Using an iterator so we can recognize the last case
- val it = cases.iterator
-
- def addendum(pat: Tree) = {
- matchingSymbolInScope(pat) match {
- case NoSymbol => ""
- case sym =>
- val desc = if (sym.isParameter) s"parameter ${sym.nameString} of" else sym + " in"
- s"\nIf you intended to match against $desc ${sym.owner}, you must use backticks, like: case `${sym.nameString}` =>"
- }
- }
-
- while (it.hasNext) {
- val cdef = it.next()
- // If a default case has been seen, then every succeeding case is unreachable.
- if (vpat != null)
- context.unit./*error*/warning(cdef.body.pos, "unreachable code due to " + vpat + addendum(cdef.pat))
- // If this is a default case and more cases follow, warn about this one so
- // we have a reason to mention its pattern variable name and any corresponding
- // symbol in scope. Errors will follow from the remaining cases, at least
- // once we make the above warning an error.
- else if (it.hasNext && (treeInfo isDefaultCase cdef)) {
- val vpatName = cdef.pat match {
- case Bind(name, _) => s" '$name'"
- case _ => ""
- }
- vpat = s"variable pattern$vpatName on line ${cdef.pat.pos.line}"
- context.unit.warning(cdef.pos, s"patterns after a variable pattern cannot match (SLS 8.1.1)" + addendum(cdef.pat))
- }
- }
- }
-
/** Implement a pattern match by turning its cases (including the implicit failure case)
* into the corresponding (monadic) extractors, and combining them with the `orElse` combinator.
*
@@ -497,20 +430,8 @@ trait MatchTranslation extends CpsPatternHacks {
// as b.info may be based on a Typed type ascription, which has not been taken into account yet by the translation
// (it will later result in a type test when `tp` is not a subtype of `b.info`)
// TODO: can we simplify this, together with the Bound case?
- lazy val subPatBinders = {
- val binders = args map { case Bound(b, _) => b ; case p => freshSym(p.pos, prefix = "p") }
- if (binders.length != subPatTypes.length)
- devWarning(s"Mismatched binders and subpatterns: binders=$binders, subPatTypes=$subPatTypes")
-
- (binders, subPatTypes).zipped map setVarInfo
- }
- lazy val subBindersAndPatterns: List[BoundTree] = (subPatBinders, unboundArgs).zipped map BoundTree
-
- private def unboundArgs = args map unBound
- private def unBound(t: Tree): Tree = t match {
- case Bound(_, p) => p
- case _ => t
- }
+ def subPatBinders = subBoundTrees map (_.binder)
+ lazy val subBoundTrees = (args, subPatTypes).zipped map newBoundTree
// never store these in local variables (for PreserveSubPatBinders)
lazy val ignoredSubPatBinders: Set[Symbol] = subPatBinders zip args collect { case (b, PatternBoundToUnderscore()) => b } toSet
@@ -632,11 +553,12 @@ trait MatchTranslation extends CpsPatternHacks {
// binders corresponding to mutable fields should be stored (SI-5158, SI-6070)
// make an exception for classes under the scala package as they should be well-behaved,
// to optimize matching on List
- val mutableBinders =
+ val mutableBinders = (
if (!binder.info.typeSymbol.hasTransOwner(ScalaPackageClass) &&
(paramAccessors exists (_.isMutable)))
subPatBinders.zipWithIndex.collect{ case (binder, idx) if paramAccessors(idx).isMutable => binder }
else Nil
+ )
// checks binder ne null before chaining to the next extractor
ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders, binderKnownNonNull, ignoredSubPatBinders)
@@ -735,7 +657,6 @@ trait MatchTranslation extends CpsPatternHacks {
object WildcardPattern {
def unapply(pat: Tree): Boolean = pat match {
case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
- case Ident(nme.WILDCARD) => true
case Star(WildcardPattern()) => true
case x: Ident => treeInfo.isVarPattern(x)
case Alternative(ps) => ps forall unapply
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala
new file mode 100644
index 0000000000..a7d7680db1
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala
@@ -0,0 +1,86 @@
+/* NSC -- new Scala compiler
+ *
+ * Copyright 2011-2013 LAMP/EPFL
+ * @author Adriaan Moors
+ */
+
+package scala.tools.nsc.transform.patmat
+
+import scala.language.postfixOps
+import scala.collection.mutable
+import scala.reflect.internal.util.Statistics
+
+trait MatchWarnings {
+ self: PatternMatching =>
+
+ import global._
+
+ trait TreeMakerWarnings {
+ self: MatchTranslator =>
+
+ import typer.context
+
+ // Why is it so difficult to say "here's a name and a context, give me any
+ // matching symbol in scope" ? I am sure this code is wrong, but attempts to
+ // use the scopes of the contexts in the enclosing context chain discover
+ // nothing. How to associate a name with a symbol would would be a wonderful
+ // linkage for which to establish a canonical acquisition mechanism.
+ private def matchingSymbolInScope(pat: Tree): Symbol = {
+ def declarationOfName(tpe: Type, name: Name): Symbol = tpe match {
+ case PolyType(tparams, restpe) => tparams find (_.name == name) getOrElse declarationOfName(restpe, name)
+ case MethodType(params, restpe) => params find (_.name == name) getOrElse declarationOfName(restpe, name)
+ case ClassInfoType(_, _, clazz) => clazz.rawInfo member name
+ case _ => NoSymbol
+ }
+ pat match {
+ case Bind(name, _) =>
+ context.enclosingContextChain.foldLeft(NoSymbol: Symbol)((res, ctx) =>
+ res orElse declarationOfName(ctx.owner.rawInfo, name))
+ case _ => NoSymbol
+ }
+ }
+
+ // Issue better warnings than "unreachable code" when people mis-use
+ // variable patterns thinking they bind to existing identifiers.
+ //
+ // Possible TODO: more deeply nested variable patterns, like
+ // case (a, b) => 1 ; case (c, d) => 2
+ // However this is a pain (at least the way I'm going about it)
+ // and I have to think these detailed errors are primarily useful
+ // for beginners, not people writing nested pattern matches.
+ def checkMatchVariablePatterns(cases: List[CaseDef]) {
+ // A string describing the first variable pattern
+ var vpat: String = null
+ // Using an iterator so we can recognize the last case
+ val it = cases.iterator
+
+ def addendum(pat: Tree) = {
+ matchingSymbolInScope(pat) match {
+ case NoSymbol => ""
+ case sym =>
+ val desc = if (sym.isParameter) s"parameter ${sym.nameString} of" else sym + " in"
+ s"\nIf you intended to match against $desc ${sym.owner}, you must use backticks, like: case `${sym.nameString}` =>"
+ }
+ }
+
+ while (it.hasNext) {
+ val cdef = it.next()
+ // If a default case has been seen, then every succeeding case is unreachable.
+ if (vpat != null)
+ context.unit./*error*/warning(cdef.body.pos, "unreachable code due to " + vpat + addendum(cdef.pat))
+ // If this is a default case and more cases follow, warn about this one so
+ // we have a reason to mention its pattern variable name and any corresponding
+ // symbol in scope. Errors will follow from the remaining cases, at least
+ // once we make the above warning an error.
+ else if (it.hasNext && (treeInfo isDefaultCase cdef)) {
+ val vpatName = cdef.pat match {
+ case Bind(name, _) => s" '$name'"
+ case _ => ""
+ }
+ vpat = s"variable pattern$vpatName on line ${cdef.pat.pos.line}"
+ context.unit.warning(cdef.pos, s"patterns after a variable pattern cannot match (SLS 8.1.1)" + addendum(cdef.pat))
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
index 616abaaf3b..f3514b2d06 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
@@ -43,7 +43,8 @@ trait PatternMatching extends Transform with TypingTransformers
with ScalaLogic
with Solving
with MatchAnalysis
- with MatchOptimization {
+ with MatchOptimization
+ with MatchWarnings {
import global._
val phaseName: String = "patmat"