summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-04-14 11:51:05 +0100
committerPaul Phillips <paulp@improving.org>2012-04-14 11:51:05 +0100
commit57c7debd62cd8c37723d80450141e3e3524a351c (patch)
tree23d22e428d200a3398b8310052e812b109cc141a /src/compiler
parent0b3b12fb29b8a4a624c4a0bb4520114f10599036 (diff)
parent631f60e4238552c614702d4594afaa01fe2ecd04 (diff)
downloadscala-57c7debd62cd8c37723d80450141e3e3524a351c.tar.gz
scala-57c7debd62cd8c37723d80450141e3e3524a351c.tar.bz2
scala-57c7debd62cd8c37723d80450141e3e3524a351c.zip
Merge branch 'develop'
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/reflect/internal/TreePrinters.scala12
-rw-r--r--src/compiler/scala/reflect/internal/Trees.scala6
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala2
-rw-r--r--src/compiler/scala/reflect/internal/settings/MutableSettings.scala2
-rw-r--r--src/compiler/scala/reflect/runtime/Settings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala55
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala9
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala20
-rw-r--r--src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/AestheticSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala9
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala146
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala25
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala44
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala13
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala70
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala24
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala112
22 files changed, 392 insertions, 173 deletions
diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala
index 9b4c18ce86..486a3d3567 100644
--- a/src/compiler/scala/reflect/internal/TreePrinters.scala
+++ b/src/compiler/scala/reflect/internal/TreePrinters.scala
@@ -103,6 +103,16 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable =>
}
}
+ def printLabelParams(ps: List[Ident]) {
+ print("(")
+ printSeq(ps){printLabelParam}{print(", ")}
+ print(")")
+ }
+
+ def printLabelParam(p: Ident) {
+ print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe)
+ }
+
def printValueParams(ts: List[ValDef]) {
print("(")
if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "")
@@ -219,7 +229,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable =>
}
case LabelDef(name, params, rhs) =>
- print(symName(tree, name)); printRow(params, "(", ",", ")"); printBlock(rhs)
+ print(symName(tree, name)); printLabelParams(params); printBlock(rhs)
case Import(expr, selectors) =>
// Is this selector remapping a name (i.e, {name1 => name2})
diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala
index 0413fd9896..5f1a8f3fbe 100644
--- a/src/compiler/scala/reflect/internal/Trees.scala
+++ b/src/compiler/scala/reflect/internal/Trees.scala
@@ -144,11 +144,9 @@ trait Trees extends api.Trees { self: SymbolTable =>
* less than the whole tree.
*/
def summaryString: String = tree match {
- case Select(qual, name) => qual.summaryString + "." + name.decode
- case Ident(name) => name.longString
case Literal(const) => "Literal(" + const + ")"
- case t: DefTree => t.shortClass + " `" + t.name.decode + "`"
- case t: RefTree => t.shortClass + " `" + t.name.longString + "`"
+ case Select(qual, name) => qual.summaryString + "." + name.decode
+ case t: NameTree => t.name.longString
case t =>
t.shortClass + (
if (t.symbol != null && t.symbol != NoSymbol) " " + t.symbol
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index 3efbe4b4df..17c9b955ca 100644
--- a/src/compiler/scala/reflect/internal/Types.scala
+++ b/src/compiler/scala/reflect/internal/Types.scala
@@ -97,7 +97,7 @@ trait Types extends api.Types { self: SymbolTable =>
*/
private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints"
- protected val enableTypeVarExperimentals = settings.Xexperimental.value || settings.YvirtPatmat.value
+ protected val enableTypeVarExperimentals = settings.Xexperimental.value || !settings.XoldPatmat.value
/** Empty immutable maps to avoid allocations. */
private val emptySymMap = immutable.Map[Symbol, Symbol]()
diff --git a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala
index b556c33aba..45ba4ed3e6 100644
--- a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala
+++ b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala
@@ -43,5 +43,5 @@ abstract class MutableSettings extends AbsSettings {
def Yrecursion: IntSetting
def maxClassfileName: IntSetting
def Xexperimental: BooleanSetting
- def YvirtPatmat: BooleanSetting
+ def XoldPatmat: BooleanSetting
} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/runtime/Settings.scala b/src/compiler/scala/reflect/runtime/Settings.scala
index 27e90c94bd..bbe4d60e9c 100644
--- a/src/compiler/scala/reflect/runtime/Settings.scala
+++ b/src/compiler/scala/reflect/runtime/Settings.scala
@@ -34,5 +34,5 @@ class Settings extends internal.settings.MutableSettings {
val maxClassfileName = new IntSetting(255)
val Xexperimental = new BooleanSetting(false)
val deepCloning = new BooleanSetting (false)
- val YvirtPatmat = new BooleanSetting(false)
+ val XoldPatmat = new BooleanSetting(false)
}
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 0f9f7df548..403b5717b0 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -347,7 +347,7 @@ class Global(var currentSettings: Settings, var reporter: NscReporter) extends S
override protected val etaExpandKeepsStar = settings.etaExpandKeepsStar.value
// Here comes another one...
override protected val enableTypeVarExperimentals = (
- settings.Xexperimental.value || settings.YvirtPatmat.value
+ settings.Xexperimental.value || !settings.XoldPatmat.value
)
// True if -Xscript has been set, indicating a script run.
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index e566384713..d3e64811d3 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -72,14 +72,6 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
Annotated(Ident(nme.synthSwitch), expr)
}
- // must be kept in synch with the codegen in PatMatVirtualiser
- object VirtualCaseDef {
- def unapply(b: Block): Option[(Assign, Tree, Tree)] = b match {
- case Block(List(assign@Assign(keepGoingLhs, falseLit), matchRes), zero) => Some((assign, matchRes, zero)) // TODO: check tree annotation
- case _ => None
- }
- }
-
def hasSynthCaseSymbol(t: Tree) = (t.symbol ne null) && (t.symbol hasFlag (CASE | SYNTHETIC))
// TODO: would be so much nicer if we would know during match-translation (i.e., type checking)
@@ -87,9 +79,11 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
class MatchMatcher {
def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = unknownTree(orig)
def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = unknownTree(orig)
- def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = unknownTree(orig)
+ def caseVirtualizedMatchOpt(orig: Tree, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree): Tree = unknownTree(orig)
- def apply(matchExpr: Tree): Tree = (matchExpr: @unchecked) match {
+ def genVirtualizedMatch(prologue: List[Tree], cases: List[Tree], matchEndDef: Tree): Tree = Block(prologue ++ cases, matchEndDef)
+
+ def apply(matchExpr: Tree): Tree = matchExpr match {
// old-style match or virtpatmat switch
case Match(selector, cases) => // println("simple match: "+ (selector, cases) + "for:\n"+ matchExpr )
caseMatch(matchExpr, selector, cases, identity)
@@ -100,11 +94,15 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if opt.virtPatmat => // println("virt match: "+ (tgt, targs, scrut, matcher) + "for:\n"+ matchExpr )
caseVirtualizedMatch(matchExpr, tgt, targs, scrut, matcher)
// optimized version of virtpatmat
- case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue) if opt.virtPatmat => // TODO: check tree annotation // println("virtopt match: "+ (zero, x, matchRes, keepGoing, stats) + "for:\n"+ matchExpr )
- caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, identity)
+ case Block(stats, matchEndDef) if opt.virtPatmat && (stats forall hasSynthCaseSymbol) =>
+ // the assumption is once we encounter a case, the remainder of the block will consist of cases
+ // the prologue may be empty, usually it is the valdef that stores the scrut
+ val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef])
+ caseVirtualizedMatchOpt(matchExpr, prologue, cases, matchEndDef, identity)
// optimized version of virtpatmat
- case Block(outerStats, orig@Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue)) if opt.virtPatmat => // TODO: check tree annotation // println("virt opt block match: "+ (zero, x, matchRes, keepGoing, stats, outerStats) + "for:\n"+ matchExpr )
- caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, m => copyBlock(matchExpr, outerStats, m))
+ case Block(outerStats, orig@Block(stats, matchEndDef)) if opt.virtPatmat && (stats forall hasSynthCaseSymbol) =>
+ val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef])
+ caseVirtualizedMatchOpt(matchExpr, prologue, cases, matchEndDef, m => copyBlock(matchExpr, outerStats, m))
case other =>
unknownTree(other)
}
@@ -120,35 +118,6 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
}
}
- def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = {
- object withDefaultTransformer extends MatchMatcher {
- override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = {
- val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
- if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) orig
- else {
- val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate))
- wrap(Match(selector, casesNoSynthCatchAll :+ defaultCase))
- }
- }
- override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { import CODE._
- ((matcher APPLY (scrut)) DOT nme.getOrElse) APPLY (defaultAction(scrut.duplicate)) // TODO: pass targs
- }
- override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = { import CODE._
- wrap(Block(
- zero ::
- x ::
- matchRes ::
- keepGoing ::
- stats,
- // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `if (keepGoing) ${defaultAction(`x`)} else matchRes`
- (IF (REF(keepGoing.symbol)) THEN defaultAction(x.rhs.duplicate) ELSE REF(matchRes.symbol))
- ))
- }
- }
- withDefaultTransformer(matchExpr)
- }
-
-
def mkCached(cvar: Symbol, expr: Tree): Tree = {
val cvarRef = mkUnattributedRef(cvar)
Block(
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 59e36e86f6..04452c68e5 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -255,6 +255,7 @@ trait Trees extends reflect.internal.Trees { self: Global =>
def resetAllAttrs[A <: Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(false, leaveAlone).transform(x)
def resetLocalAttrs[A <: Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(true, leaveAlone).transform(x)
+ def resetLocalAttrsKeepLabels[A<:Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(true, leaveAlone, true).transform(x)
/** A transformer which resets symbol and tpe fields of all nodes in a given tree,
* with special treatment of:
@@ -265,7 +266,7 @@ trait Trees extends reflect.internal.Trees { self: Global =>
*
* (bq:) This transformer has mutable state and should be discarded after use
*/
- private class ResetAttrs(localOnly: Boolean, leaveAlone: Tree => Boolean = null) {
+ private class ResetAttrs(localOnly: Boolean, leaveAlone: Tree => Boolean = null, keepLabels: Boolean = false) {
val debug = settings.debug.value
val trace = scala.tools.nsc.util.trace when debug
@@ -328,18 +329,18 @@ trait Trees extends reflect.internal.Trees { self: Global =>
case EmptyTree =>
tree
case _ =>
- if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)))
+ if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel))
tree.symbol = NoSymbol
tree.tpe = null
tree
}
}
- }
+ }
}
def transform[T <: Tree](x: T): T = {
if (localOnly)
- new MarkLocals().traverse(x)
+ new MarkLocals().traverse(x)
if (localOnly && debug) {
assert(locals.size == orderedLocals.size)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 65225b185b..eb4deeeee2 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -70,6 +70,9 @@ trait ParsersCommon extends ScannersCommon {
@inline final def inBracesOrNil[T](body: => List[T]): List[T] = inBracesOrError(body, Nil)
@inline final def inBracesOrUnit[T](body: => Tree): Tree = inBracesOrError(body, Literal(Constant()))
+ @inline final def dropAnyBraces[T](body: => T): T =
+ if (in.token == LBRACE) inBraces(body)
+ else body
@inline final def inBrackets[T](body: => T): T = {
accept(LBRACKET)
@@ -1106,7 +1109,7 @@ self =>
* }}}
* @note The returned tree does not yet have a position
*/
- def literal(isNegated: Boolean = false): Tree = {
+ def literal(isNegated: Boolean = false, inPattern: Boolean = false): Tree = {
def finish(value: Any): Tree = {
val t = Literal(Constant(value))
in.nextToken()
@@ -1115,7 +1118,7 @@ self =>
if (in.token == SYMBOLLIT)
Apply(scalaDot(nme.Symbol), List(finish(in.strVal)))
else if (in.token == INTERPOLATIONID)
- interpolatedString()
+ interpolatedString(inPattern)
else finish(in.token match {
case CHARLIT => in.charVal
case INTLIT => in.intVal(isNegated).toInt
@@ -1141,7 +1144,7 @@ self =>
}
}
- private def interpolatedString(): Tree = atPos(in.offset) {
+ private def interpolatedString(inPattern: Boolean = false): Tree = atPos(in.offset) {
val start = in.offset
val interpolator = in.name
@@ -1151,8 +1154,11 @@ self =>
while (in.token == STRINGPART) {
partsBuf += literal()
exprBuf += {
- if (in.token == IDENTIFIER) atPos(in.offset)(Ident(ident()))
- else expr()
+ if (inPattern) dropAnyBraces(pattern())
+ else {
+ if (in.token == IDENTIFIER) atPos(in.offset)(Ident(ident()))
+ else expr()
+ }
}
}
if (in.token == STRINGLIT) partsBuf += literal()
@@ -1837,7 +1843,7 @@ self =>
case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT =>
t match {
case Ident(nme.MINUS) =>
- return atPos(start) { literal(isNegated = true) }
+ return atPos(start) { literal(isNegated = true, inPattern = true) }
case _ =>
}
case _ =>
@@ -1855,7 +1861,7 @@ self =>
atPos(start, start) { Ident(nme.WILDCARD) }
case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT |
STRINGLIT | INTERPOLATIONID | SYMBOLLIT | TRUE | FALSE | NULL =>
- atPos(start) { literal() }
+ atPos(start) { literal(inPattern = true) }
case LPAREN =>
atPos(start)(makeParens(noSeq.patterns()))
case XMLSTART =>
diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala
index 40bbd3fa8e..99541ff4b4 100644
--- a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala
+++ b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala
@@ -81,7 +81,7 @@ private[tests] trait CoreTestDefs
else {
reporter.println("\naskHyperlinkPos for `" + tree.symbol.name + "` at " + format(pos) + " " + pos.source.file.name)
val r = new Response[Position]
- // `tree.symbol.sourceFile` was discovered to be null when testing -Yvirtpatmat on the akka presentation test, where a position had shifted to point to `Int`
+ // `tree.symbol.sourceFile` was discovered to be null when testing using virtpatmat on the akka presentation test, where a position had shifted to point to `Int`
// askHyperlinkPos for `Int` at (73,19) pi.scala --> class Int in package scala has null sourceFile!
val treePath = if (tree.symbol.sourceFile ne null) tree.symbol.sourceFile.path else null
val treeName = if (tree.symbol.sourceFile ne null) tree.symbol.sourceFile.name else null
diff --git a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala
index 2fdc3004d6..63775ff1c5 100644
--- a/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/AestheticSettings.scala
@@ -31,7 +31,7 @@ trait AestheticSettings {
def target = settings.target.value
def unchecked = settings.unchecked.value
def verbose = settings.verbose.value
- def virtPatmat = settings.YvirtPatmat.value
+ def virtPatmat = !settings.XoldPatmat.value
/** Derived values */
def jvm = target startsWith "jvm"
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index f7878cae71..d4c2ffa832 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -108,6 +108,8 @@ trait ScalaSettings extends AbsScalaSettings
val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.")
val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "")
+ val XoldPatmat = BooleanSetting ("-Xoldpatmat", "Use the pre-2.10 pattern matcher. Otherwise, the 'virtualizing' pattern matcher is used in 2.10.")
+
/** Compatibility stubs for options whose value name did
* not previously match the option name.
*/
@@ -175,7 +177,6 @@ trait ScalaSettings extends AbsScalaSettings
val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.")
val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.")
val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
- val YvirtPatmat = BooleanSetting ("-Yvirtpatmat", "Translate pattern matches into flatMap/orElse calls. See scala.MatchingStrategy.")
val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index bdf2f2883f..5e61359a25 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -480,11 +480,10 @@ abstract class Erasure extends AddInterfaces
// TODO: should we do this for user-defined unapplies as well?
// does the first argument list have exactly one argument -- for user-defined unapplies we can't be sure
def maybeWrap(bridgingCall: Tree): Tree = {
- val canReturnNone = afterErasure(
- member.isSynthetic
- && (member.name == nme.unapply || member.name == nme.unapplySeq)
- && !(member.tpe <:< other.tpe) // no static guarantees (TODO: is the subtype test ever true?)
- )
+ val canReturnNone = ( // can't statically know which member is going to be selected, so don't let this depend on member.isSynthetic
+ (member.name == nme.unapply || member.name == nme.unapplySeq)
+ && !afterErasure((member.tpe <:< other.tpe))) // no static guarantees (TODO: is the subtype test ever true?)
+
if (canReturnNone) {
import CODE._
val typeTest = gen.mkIsInstanceOf(REF(bridge.firstParam), member.tpe.params.head.tpe)
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 1d2206bc3d..57cd51ad35 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -241,7 +241,6 @@ abstract class UnCurry extends InfoTransform
def owner = fun.symbol.owner
def targs = fun.tpe.typeArgs
def isPartial = fun.tpe.typeSymbol == PartialFunctionClass
- assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions
def parents =
if (isFunctionType(fun.tpe)) List(abstractFunctionForFunctionType(fun.tpe), SerializableClass.tpe)
@@ -282,7 +281,44 @@ abstract class UnCurry extends InfoTransform
val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), List(x))
val body = localTyper.typedPos(fun.pos) { import CODE._
- gen.mkUncheckedMatch(gen.withDefaultCase(substParam(fun.body), scrut => REF(default) APPLY (REF(x))))
+ def defaultAction(scrut: Tree) = REF(default) APPLY (REF(x))
+
+ object withDefaultTransformer extends gen.MatchMatcher {
+ override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = {
+ val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
+ if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) orig
+ else {
+ val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate))
+ wrap(Match(/*gen.mkUnchecked*/(selector), casesNoSynthCatchAll :+ defaultCase))
+ }
+ }
+ override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { import CODE._
+ ((matcher APPLY (scrut)) DOT nme.getOrElse) APPLY (defaultAction(scrut.duplicate)) // TODO: pass targs
+ }
+ override def caseVirtualizedMatchOpt(orig: Tree, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree): Tree = { import CODE._
+ val scrutRef = REF(prologue.head.symbol) // scrut valdef is always emitted (except for nested matchers that handle alternatives)
+
+ val casesNewSynthCatchAll = cases.init :+ (deriveLabelDef(cases.last){
+ case Apply(matchEnd, List(Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _)))) if exTpt.tpe.typeSymbol eq MatchErrorClass =>
+ assert(matchEnd.symbol == matchEndDef.symbol, "matchEnd discrepancy "+(matchEnd, matchEndDef))
+ matchEnd APPLY (defaultAction(scrutRef))
+ case x => x
+ } setSymbol cases.last.symbol setType null)
+
+ val LabelDef(_, List(matchRes), rhs) = matchEndDef
+ val matchEnd = matchEndDef.symbol
+ matchRes setType B1.tpe
+ rhs setType B1.tpe
+ matchEndDef setType B1.tpe
+ matchRes.symbol setInfo B1.tpe
+ matchEnd setInfo MethodType(List(matchRes.symbol), B1.tpe)
+ cases foreach (c => c.symbol setInfo MethodType(List(), B1.tpe))
+
+ wrap(Block(prologue ++ casesNewSynthCatchAll, matchEndDef))
+ }
+ }
+
+ withDefaultTransformer(substParam(fun.body))
}
body.changeOwner(fun.symbol -> methSym)
@@ -294,30 +330,115 @@ abstract class UnCurry extends InfoTransform
methDef
}
- // duplicate before applyOrElseMethodDef is run so we start with the same symbols as applyOrElseMethodDef
+ // duplicate before applyOrElseMethodDef is run so that it does not mess up our trees and label symbols (we have a fresh set)
// otherwise `TreeSymSubstituter(fun.vparams map (_.symbol), params)` won't work as the subst has been run already
- val bodyForIDA = fun.body.duplicate
+ val bodyForIDA = {
+ val duped = fun.body.duplicate
+ val oldParams = new mutable.ListBuffer[Symbol]()
+ val newParams = new mutable.ListBuffer[Symbol]()
+
+ val oldSyms0 =
+ duped filter {
+ case l@LabelDef(_, params, _) =>
+ params foreach {p =>
+ val oldSym = p.symbol
+ p.symbol = oldSym.cloneSymbol
+ oldParams += oldSym
+ newParams += p.symbol
+ }
+ true
+ case _ => false
+ } map (_.symbol)
+ val oldSyms = oldParams.toList ++ oldSyms0
+ val newSyms = newParams.toList ++ (oldSyms0 map (_.cloneSymbol))
+ // println("duping "+ oldSyms +" --> "+ (newSyms map (_.ownerChain)))
+
+ val substLabels = new TreeSymSubstituter(oldSyms, newSyms)
+
+ substLabels(duped)
+ }
+
def isDefinedAtMethodDef = {
val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL)
val params = methSym newSyntheticValueParams formals
methSym setInfoAndEnter MethodType(params, BooleanClass.tpe)
val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params)
- def doSubst(x: Tree) = substParam(resetLocalAttrs(x)) // see pos/t1761 for why `resetLocalAttrs`
+ def doSubst(x: Tree) = substParam(resetLocalAttrsKeepLabels(x)) // see pos/t1761 for why `resetLocalAttrs`, but must keep label symbols around
+
object isDefinedAtTransformer extends gen.MatchMatcher {
// TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { import CODE._
- gen.mkUncheckedMatch(
- if (cases exists treeInfo.isDefaultCase) TRUE_typed
+ val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
+ if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) TRUE_typed
else
doSubst(wrap(
- Match(selector,
- (cases map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ (
+ Match(/*gen.mkUnchecked*/(selector),
+ (casesNoSynthCatchAll map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ (
DEFAULT ==> FALSE_typed)
)))
- )
+ }
+ override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = {
+ object noOne extends Transformer {
+ override val treeCopy = newStrictTreeCopier // must duplicate everything
+ val one = _match.tpe member newTermName("one")
+ override def transform(tree: Tree): Tree = tree match {
+ case Apply(fun, List(a)) if fun.symbol == one =>
+ // blow one's argument away since all we want to know is whether the match succeeds or not
+ // (the alternative, making `one` CBN, would entail moving away from Option)
+ Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe)))
+ case _ =>
+ super.transform(tree)
+ }
+ }
+ doSubst(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher))))
+ }
+
+ override def caseVirtualizedMatchOpt(orig: Tree, prologue: List[Tree], cases: List[Tree], matchEndDef: Tree, wrap: Tree => Tree) = {
+ val matchEnd = matchEndDef.symbol
+ val LabelDef(_, List(matchRes), rhs) = matchEndDef
+ matchRes setType BooleanClass.tpe
+ rhs setType BooleanClass.tpe
+ matchEndDef setType BooleanClass.tpe
+ matchRes.symbol setInfo BooleanClass.tpe
+ matchEnd setInfo MethodType(List(matchRes.symbol), BooleanClass.tpe)
+ cases foreach (c => c.symbol setInfo MethodType(List(), BooleanClass.tpe))
+ // println("matchEnd: "+ matchEnd)
+
+ // when the type of the selector contains a skolem owned by the applyOrElseMethod, should reskolemize everything,
+ // for now, just cast the RHS (since we're just trying to keep the typer happy, the cast is meaningless)
+ // ARGH -- this is why I would prefer the typedMatchAnonFun approach (but alas, CPS blocks that)
+ val newPrologue = prologue match {
+ case List(vd@ValDef(mods, name, tpt, rhs)) => List(treeCopy.ValDef(vd, mods, name, tpt, gen.mkAsInstanceOf(rhs, tpt.tpe, true, false)))
+ case _ => prologue
+ }
+ object casesReturnTrue extends Transformer {
+ // override val treeCopy = newStrictTreeCopier // will duplicate below
+ override def transform(tree: Tree): Tree = tree match {
+ // don't compute the result of the match, return true instead
+ case Apply(fun, List(res)) if fun.symbol eq matchEnd =>
+ // println("matchend call "+ fun.symbol)
+ Apply(fun, List(TRUE_typed)) setType BooleanClass.tpe
+ case _ => super.transform(tree)
+ }
+ }
+ val newCatchAll = cases.last match {
+ case LabelDef(n, ps, Apply(matchEnd1, List(Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))))) if exTpt.tpe.typeSymbol eq MatchErrorClass =>
+ assert(matchEnd1.symbol == matchEnd, "matchEnd discrepancy "+(matchEnd, matchEndDef))
+ List(treeCopy.LabelDef(cases.last, n, ps, matchEnd APPLY (FALSE_typed)) setSymbol cases.last.symbol)
+ case x => Nil
+ }
+ val casesWithoutCatchAll = if(newCatchAll.isEmpty) cases else cases.init
+ doSubst(wrap(Block(newPrologue ++ casesReturnTrue.transformTrees(casesWithoutCatchAll) ++ newCatchAll, matchEndDef)))
+
+ // val duped = idaBlock //.duplicate // TODO: duplication of labeldefs is BROKEN
+ // duped foreach {
+ // case l@LabelDef(name, params, rhs) if gen.hasSynthCaseSymbol(l) => println("newInfo"+ l.symbol.info)
+ // case _ =>
+ // }
}
}
+
val body = isDefinedAtTransformer(bodyForIDA)
body.changeOwner(fun.symbol -> methSym)
@@ -328,11 +449,14 @@ abstract class UnCurry extends InfoTransform
if (isPartial) List(applyOrElseMethodDef, isDefinedAtMethodDef)
else List(applyMethodDef)
- localTyper.typedPos(fun.pos) {
+ // println("MEMBERS "+ members)
+ val res = localTyper.typedPos(fun.pos) {
Block(
List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)),
Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
}
+ // println("MEMBERS TYPED "+ members)
+ res
}
def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index fe1c90fe67..e2d4efab83 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -67,6 +67,7 @@ trait Contexts { self: Analyzer =>
val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports)
if (erasedTypes) c.setThrowErrors() else c.setReportErrors()
c.implicitsEnabled = !erasedTypes
+ c.enrichmentEnabled = c.implicitsEnabled
c
}
@@ -106,7 +107,7 @@ trait Contexts { self: Analyzer =>
var depth: Int = 0
var imports: List[ImportInfo] = List() // currently visible imports
var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments
- // are currently searched
+ // are currently searched
// for a named application block (Tree) the corresponding NamedApplyInfo
var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None
var prefix: Type = NoPrefix
@@ -120,6 +121,7 @@ trait Contexts { self: Analyzer =>
var diagnostic: List[String] = Nil // these messages are printed when issuing an error
var implicitsEnabled = false
var macrosEnabled = true
+ var enrichmentEnabled = false // to selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed
var checking = false
var retyping = false
@@ -192,8 +194,25 @@ trait Contexts { self: Analyzer =>
def withImplicitsDisabled[T](op: => T): T = {
val saved = implicitsEnabled
implicitsEnabled = false
+ val savedP = enrichmentEnabled
+ enrichmentEnabled = false
try op
- finally implicitsEnabled = saved
+ finally {
+ implicitsEnabled = saved
+ enrichmentEnabled = savedP
+ }
+ }
+
+ def withImplicitsDisabledAllowEnrichment[T](op: => T): T = {
+ val saved = implicitsEnabled
+ implicitsEnabled = false
+ val savedP = enrichmentEnabled
+ enrichmentEnabled = true
+ try op
+ finally {
+ implicitsEnabled = saved
+ enrichmentEnabled = savedP
+ }
}
def withMacrosEnabled[T](op: => T): T = {
@@ -246,6 +265,7 @@ trait Contexts { self: Analyzer =>
c.typingIndentLevel = typingIndentLevel
c.implicitsEnabled = this.implicitsEnabled
c.macrosEnabled = this.macrosEnabled
+ c.enrichmentEnabled = this.enrichmentEnabled
c.checking = this.checking
c.retyping = this.retyping
c.openImplicits = this.openImplicits
@@ -298,6 +318,7 @@ trait Contexts { self: Analyzer =>
def makeImplicit(reportAmbiguousErrors: Boolean) = {
val c = makeSilent(reportAmbiguousErrors)
c.implicitsEnabled = false
+ c.enrichmentEnabled = false
c
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index b4e0ad6edf..4fb9362ccc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -166,7 +166,7 @@ trait Implicits {
}
def isCyclicOrErroneous =
- try containsError(tpe)
+ try sym.hasFlag(LOCKED) || containsError(tpe)
catch { case _: CyclicReference => true }
var useCountArg: Int = 0
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index d78fd35d25..fb0616c890 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1189,6 +1189,50 @@ trait Infer {
}
}
+ /** Does `tp` contain any types that cannot be checked at run-time (i.e., after erasure, will isInstanceOf[erased(tp)] imply conceptualIsInstanceOf[tp]?)
+ * we should find a way to ask erasure: hey, is `tp` going to make it through you with all of its isInstanceOf resolving powers intact?
+ * TODO: at the very least, reduce duplication wrt checkCheckable
+ */
+ def containsUnchecked(tp: Type): Boolean = {
+ def check(tp: Type, bound: List[Symbol]): Boolean = {
+ def isSurroundingTypeParam(sym: Symbol) = {
+ val e = context.scope.lookupEntry(sym.name)
+ ( (e ne null)
+ && (e.sym == sym )
+ && !e.sym.isTypeParameterOrSkolem
+ && (e.owner == context.scope)
+ )
+ }
+ def isLocalBinding(sym: Symbol) = (
+ sym.isAbstractType && (
+ (bound contains sym)
+ || (sym.name == tpnme.WILDCARD)
+ || isSurroundingTypeParam(sym)
+ )
+ )
+ tp.normalize match {
+ case SingleType(pre, _) =>
+ check(pre, bound)
+ case TypeRef(_, ArrayClass, arg :: _) =>
+ check(arg, bound)
+ case tp @ TypeRef(pre, sym, args) =>
+ ( (sym.isAbstractType && !isLocalBinding(sym))
+ || (args exists (x => !isLocalBinding(x.typeSymbol)))
+ || check(pre, bound)
+ )
+ // case RefinedType(_, decls) if decls.nonEmpty =>
+ // patternWarning(tp, "refinement ")
+ case RefinedType(parents, _) =>
+ parents exists (p => check(p, bound))
+ case ExistentialType(quantified, tp1) =>
+ check(tp1, bound ::: quantified)
+ case _ =>
+ false
+ }
+ }
+ check(tp, Nil)
+ }
+
def checkCheckable(tree: Tree, tp: Type, kind: String) {
def patternWarning(tp0: Type, prefix: String) = {
context.unit.uncheckedWarning(tree.pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure")
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 988b821d1a..b7e0eaef2b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -184,8 +184,21 @@ trait Macros { self: Analyzer =>
def typedMacroBody(typer: Typer, ddef: DefDef): Tree = {
import typer.context
if (macroDebug) println("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos))
+
typer.checkFeature(ddef.pos, MacrosFeature)
+ // [Eugene to Martin] todo. copy/pasted this from checkFeature, because don't know a better way
+ // this is necessary to prevent macros from typechecking/expanding when they are not enabled
+ // `checkFeature` call alone is not enough, because it merely posts validation callback to unit.toCheck
+ def hasImport = inferImplicit(EmptyTree: Tree, MacrosFeature.tpe, true, false, typer.context) != SearchFailure
+ val nestedOwners = MacrosFeature.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse
+ val featureName = (nestedOwners map (_.name + ".")).mkString + MacrosFeature.name
+ def hasOption = settings.language.value contains featureName
+ if (!hasImport && !hasOption) {
+ ddef.symbol setFlag IS_ERROR
+ return EmptyTree
+ }
+
implicit class AugmentedString(s: String) {
def abbreviateCoreAliases: String = { // hack!
var result = s
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
index e8e65071af..1fd9f6fc13 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
@@ -22,7 +22,7 @@ import language.postfixOps
* - Array patterns
* - implement spec more closely (see TODO's)
* - DCE
- * - use manifests for type testing
+ * - use TypeTags for type testing
*
* (longer-term) TODO:
* - user-defined unapplyProd
@@ -119,6 +119,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore =>
import typer.{typed, context, silent, reallyExists}
+ // import typer.infer.containsUnchecked
/** 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.
@@ -131,10 +132,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
* this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch)
*/
def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type, scrutType: Type, matchFailGenOverride: Option[Tree => Tree] = None): Tree = {
- // we don't transform after typers
- // (that would require much more sophistication when generating trees,
+ // we don't transform after uncurry
+ // (that would require more sophistication when generating trees,
// and the only place that emits Matches after typers is for exception handling anyway)
- assert(phase.id <= currentRun.typerPhase.id, phase)
+ assert(phase.id < currentRun.uncurryPhase.id, phase)
val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag SYNTH_CASE
// pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental
@@ -820,13 +821,34 @@ class Foo(x: Other) { x._1 } // no error in this order
cond
}
- // TODO: also need to test when erasing pt loses crucial information (and if we can recover it using a manifest)
- def needsTypeTest(tp: Type, pt: Type) = !(tp <:< pt)
- private def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(codegen._isInstanceOf(binder, pt))
+ // containsUnchecked: also need to test when erasing pt loses crucial information (maybe we can recover it using a TypeTag)
+ def needsTypeTest(tp: Type, pt: Type): Boolean = !(tp <:< pt) // || containsUnchecked(pt)
+ // TODO: try to find the TypeTag for the binder's type and the expected type, and if they exists,
+ // check that the TypeTag of the binder's type conforms to the TypeTag of the expected type
+ private def typeTest(binderToTest: Symbol, expectedTp: Type, disableOuterCheck: Boolean = false, dynamic: Boolean = false): Tree = { import CODE._
+ // def coreTest =
+ if (disableOuterCheck) codegen._isInstanceOf(binderToTest, expectedTp) else maybeWithOuterCheck(binderToTest, expectedTp)(codegen._isInstanceOf(binderToTest, expectedTp))
+ // if (opt.experimental && containsUnchecked(expectedTp)) {
+ // if (dynamic) {
+ // val expectedTpTagTree = findManifest(expectedTp, true)
+ // if (!expectedTpTagTree.isEmpty)
+ // ((expectedTpTagTree DOT "erasure".toTermName) DOT "isAssignableFrom".toTermName)(REF(binderToTest) DOT nme.getClass_)
+ // else
+ // coreTest
+ // } else {
+ // val expectedTpTagTree = findManifest(expectedTp, true)
+ // val binderTpTagTree = findManifest(binderToTest.info, true)
+ // if(!(expectedTpTagTree.isEmpty || binderTpTagTree.isEmpty))
+ // coreTest AND (binderTpTagTree DOT nme.CONFORMS)(expectedTpTagTree)
+ // else
+ // coreTest
+ // }
+ // } else coreTest
+ }
// need to substitute since binder may be used outside of the next extractor call (say, in the body of the case)
case class TypeTestTreeMaker(prevBinder: Symbol, nextBinderTp: Type, pos: Position) extends CondTreeMaker {
- val cond = typeTest(prevBinder, nextBinderTp)
+ val cond = typeTest(prevBinder, nextBinderTp, dynamic = true)
val res = codegen._asInstanceOf(prevBinder, nextBinderTp)
override def toString = "TT"+(prevBinder, nextBinderTp)
}
@@ -866,7 +888,7 @@ class Foo(x: Other) { x._1 } // no error in this order
// TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null`
// don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false")
def genEqualsAndInstanceOf(sym: Symbol): Tree
- = codegen._equals(REF(sym), patBinder) AND codegen._isInstanceOf(patBinder, pt.widen)
+ = codegen._equals(REF(sym), patBinder) AND typeTest(patBinder, pt.widen, disableOuterCheck = true)
def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe
@@ -874,7 +896,7 @@ class Foo(x: Other) { x._1 } // no error in this order
def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt)
// TODO: [SPEC] type test for Array
- // TODO: use manifests to improve tests (for erased types we can do better when we have a manifest)
+ // TODO: use TypeTags to improve tests (for erased types we can do better when we have a TypeTag)
pt match {
case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here
case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil
@@ -1083,22 +1105,14 @@ class Foo(x: Other) { x._1 } // no error in this order
def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya
def and(a: Tree, b: Tree): Tree = a AND b
+ // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any
+ // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree
+ private def mkCast(t: Tree, tp: Type) = Typed(gen.mkAsInstanceOf(t, tp.withoutAnnotations, true, false), TypeTree() setType tp)
// the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly)
- def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = /*repackExistential*/(tp)
- if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX)
- else gen.mkAsInstanceOf(t, tpX, true, false)
- }
-
- def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), /*repackExistential*/(tp), true, false)
- // { val tpX = /*repackExistential*/(tp)
+ def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp)
+ def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp)
+ def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false)
// if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE }
- // else gen.mkIsInstanceOf(REF(b), tpX, true, false)
- // }
-
- def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = /*repackExistential*/(tp)
- if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX)
- else gen.mkAsInstanceOf(REF(b), tpX, true, false)
- }
// duplicated out of frustration with cast generation
def mkZero(tp: Type): Tree = {
@@ -1636,7 +1650,7 @@ class Foo(x: Other) { x._1 } // no error in this order
*/
def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = {
val matchEnd = NoSymbol.newLabel(freshName("matchEnd"), NoPosition) setFlag SYNTH_CASE
- val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe
+ val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations //
matchEnd setInfo MethodType(List(matchRes), restpe)
def newCaseSym = NoSymbol.newLabel(freshName("case"), NoPosition) setInfo MethodType(Nil, restpe) setFlag SYNTH_CASE
@@ -1650,7 +1664,11 @@ class Foo(x: Other) { x._1 } // no error in this order
def catchAll = matchFailGen map { matchFailGen =>
val scrutRef = if(scrutSym ne NoSymbol) REF(scrutSym) else EmptyTree // for alternatives
- LabelDef(nextCase, Nil, matchEnd APPLY (_asInstanceOf(matchFailGen(scrutRef), restpe))) // need to jump to matchEnd with result generated by matchFailGen (could be `FALSE` for isDefinedAt)
+ // must jump to matchEnd, use result generated by matchFailGen (could be `FALSE` for isDefinedAt)
+ LabelDef(nextCase, Nil, matchEnd APPLY (matchFailGen(scrutRef)))
+ // don't cast the arg to matchEnd when using PartialFun synth in uncurry, since it won't detect the throw (see gen.withDefaultCase)
+ // the cast is necessary when using typedMatchAnonFun-style PartialFun synth:
+ // (_asInstanceOf(matchFailGen(scrutRef), restpe))
} toList
// catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default)
// if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index af75c75156..2d656e02b4 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1655,9 +1655,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
inPattern = false
treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
case LabelDef(_, _, _) if gen.hasSynthCaseSymbol(result) =>
+ val old = inPattern
inPattern = true
val res = deriveLabelDef(result)(transform)
- inPattern = false
+ inPattern = old
res
case _ =>
super.transform(result)
diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
index 45d916c633..4319dd10c7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
@@ -14,8 +14,9 @@ import util.returning
abstract class TreeCheckers extends Analyzer {
import global._
- private val everything = ListBuffer[(Phase, Map[Tree, (Symbol, Type)])]()
+ private val everything = ListBuffer[(Phase, Map[Tree, (Symbol, Type)])]()
private val currentTrees = mutable.Map[Tree, (Symbol, Type)]()
+ private val tpeOfTree = mutable.HashMap[Tree, Type]()
if (settings.debug.value) {
sys addShutdownHook {
@@ -49,12 +50,13 @@ abstract class TreeCheckers extends Analyzer {
object SymbolTracker extends Traverser {
type PhaseMap = mutable.HashMap[Symbol, List[Tree]]
+ val defSyms = mutable.HashMap[Symbol, List[DefTree]]() withDefaultValue Nil
+ val newSyms = mutable.HashSet[Symbol]()
val maps = ListBuffer[(Phase, PhaseMap)]()
+ val movedMsgs = ListBuffer[String]()
+
def prev = maps.init.last._2
def latest = maps.last._2
- val defSyms = mutable.HashMap[Symbol, List[DefTree]]()
- val newSyms = mutable.HashSet[Symbol]()
- val movedMsgs = new ListBuffer[String]
def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString)
def inPrev(sym: Symbol) = {
@@ -119,10 +121,8 @@ abstract class TreeCheckers extends Analyzer {
if (sym != null && sym != NoSymbol) {
record(sym, tree)
tree match {
- case x: DefTree =>
- if (defSyms contains sym) defSyms(sym) = defSyms(sym) :+ x
- else defSyms(sym) = List(x)
- case _ => ()
+ case x: DefTree => defSyms(sym) :+= x
+ case _ => ()
}
}
@@ -130,8 +130,6 @@ abstract class TreeCheckers extends Analyzer {
}
}
- lazy val tpeOfTree = mutable.HashMap[Tree, Type]()
-
def posstr(p: Position) =
try p.source.path + ":" + p.line
catch { case _: UnsupportedOperationException => p.toString }
@@ -147,9 +145,7 @@ abstract class TreeCheckers extends Analyzer {
if (!cond) errorFn(msg)
def checkTrees() {
- if (settings.verbose.value)
- Console.println("[consistency check at the beginning of phase " + phase + "]")
-
+ informFn("[consistency check at the beginning of phase " + phase + "]")
currentRun.units foreach check
}
@@ -172,7 +168,7 @@ abstract class TreeCheckers extends Analyzer {
informProgress("checking "+unit)
val context = rootContext(unit)
context.checking = true
- tpeOfTree.clear
+ tpeOfTree.clear()
SymbolTracker.check(phase, unit)
val checker = new TreeChecker(context)
runWithUnit(unit) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index a6893ff4b2..0541f85f31 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1203,7 +1203,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
&& !qtpe.typeSymbol.isBottomClass
&& qtpe != WildcardType
&& !qual.isInstanceOf[ApplyImplicitView] // don't chain views
- && context.implicitsEnabled
+ && (context.implicitsEnabled || context.enrichmentEnabled)
// Elaborating `context.implicitsEnabled`:
// don't try to adapt a top-level type that's the subject of an implicit search
// this happens because, if isView, typedImplicit tries to apply the "current" implicit value to
@@ -2193,13 +2193,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe))
- def prepareTranslateMatch(selector0: Tree, cases: List[CaseDef], mode: Int, resTp: Type) = {
+ def typedMatch(selector0: Tree, cases: List[CaseDef], mode: Int, resTp: Type) = {
val (selector, doTranslation) = selector0 match {
case Annotated(Ident(nme.synthSwitch), selector) => (selector, false)
case s => (s, true)
}
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- val selectorTp = packCaptured(selector1.tpe.widen)
+ val selectorTp = packCaptured(selector1.tpe.widen.withoutAnnotations)
val casesTyped = typedCases(cases, selectorTp, resTp)
val caseTypes = casesTyped map (c => packedType(c, context.owner).deconst)
@@ -2210,7 +2210,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
(selector1, selectorTp, casesAdapted, ownType, doTranslation)
}
- def translateMatch(selector1: Tree, selectorTp: Type, casesAdapted: List[CaseDef], ownType: Type, doTranslation: Boolean, matchFailGen: Option[Tree => Tree] = None) = {
+ def translatedMatch(selector1: Tree, selectorTp: Type, casesAdapted: List[CaseDef], ownType: Type, doTranslation: Boolean, matchFailGen: Option[Tree => Tree] = None) = {
def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg)
case _ => tp
@@ -2220,10 +2220,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
Match(selector1, casesAdapted) setType ownType // setType of the Match to avoid recursing endlessly
} else {
val scrutType = repeatedToSeq(elimAnonymousClass(selectorTp))
- // we've packed the type for each case in prepareTranslateMatch so that if all cases have the same existential case, we get a clean lub
+ // we've packed the type for each case in typedMatch so that if all cases have the same existential case, we get a clean lub
// here, we should open up the existential again
// relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala
- MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownType.skolemizeExistential(context.owner, context.tree)), scrutType, matchFailGen)
+ // TODO: fix skolemizeExistential (it should preserve annotations, right?)
+ val ownTypeSkolemized = ownType.skolemizeExistential(context.owner, context.tree) withAnnotations ownType.annotations
+ MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownTypeSkolemized), scrutType, matchFailGen)
}
}
@@ -2283,7 +2285,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
paramSyms foreach (methodBodyTyper.context.scope enter _)
- val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, cases, mode, ptRes)
+ val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes)
val methFormals = paramSyms map (_.tpe)
val parents = List(abstractFunctionType(methFormals, resTp), SerializableClass.tpe)
@@ -2291,7 +2293,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
methodSym setInfoAndEnter MethodType(paramSyms, resTp)
- DefDef(methodSym, methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation))
+ DefDef(methodSym, methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation))
}
}
@@ -2321,7 +2323,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
paramSyms foreach (methodBodyTyper.context.scope enter _)
- val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, cases, mode, ptRes)
+ val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, cases, mode, ptRes)
anonClass setInfo ClassInfoType(parents(List(argTp, resTp)), newScope, anonClass)
B1 setInfo TypeBounds.lower(resTp)
@@ -2330,7 +2332,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// use applyOrElse's first parameter since the scrut's type has been widened
def doDefault(scrut_ignored: Tree) = REF(default) APPLY (REF(x))
- val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault))
+ val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault))
DefDef(methodSym, body)
}
@@ -2345,8 +2347,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
paramSyms foreach (methodBodyTyper.context.scope enter _)
methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe)
- val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, casesTrue, mode, BooleanClass.tpe)
- val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE_typed))
+ val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe)
+ val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE_typed))
DefDef(methodSym, body)
}
@@ -2407,29 +2409,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
- fun.body match {
- case Match(sel, cases) if opt.virtPatmat =>
- // go to outer context -- must discard the context that was created for the Function since we're discarding the function
- // thus, its symbol, which serves as the current context.owner, is not the right owner
- // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner)
- newTyper(context.outer).typedMatchAnonFun(fun, cases, mode, pt, Some((fun.vparams, sel)))
- case _ =>
- val vparamSyms = fun.vparams map { vparam =>
- enterSym(context, vparam)
- if (context.retyping) context.scope enter vparam.symbol
- vparam.symbol
- }
- val vparams = fun.vparams mapConserve (typedValDef)
- // for (vparam <- vparams) {
- // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
- // }
- val formals = vparamSyms map (_.tpe)
- val body1 = typed(fun.body, respt)
- val restpe = packedType(body1, fun.symbol).deconst.resultType
- val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe)
- // body = checkNoEscaping.locals(context.scope, restpe, body)
- treeCopy.Function(fun, vparams, body1).setType(funtpe)
+ val vparamSyms = fun.vparams map { vparam =>
+ enterSym(context, vparam)
+ if (context.retyping) context.scope enter vparam.symbol
+ vparam.symbol
}
+ val vparams = fun.vparams mapConserve (typedValDef)
+// for (vparam <- vparams) {
+// checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
+// }
+ val formals = vparamSyms map (_.tpe)
+ val body1 = typed(fun.body, respt)
+ val restpe = packedType(body1, fun.symbol).deconst.resultType
+ val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe)
+// body = checkNoEscaping.locals(context.scope, restpe, body)
+ treeCopy.Function(fun, vparams, body1).setType(funtpe)
}
}
@@ -3780,12 +3774,16 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
lazy val thenTp = packedType(thenp1, context.owner)
lazy val elseTp = packedType(elsep1, context.owner)
+ // println("typedIf: "+(thenp1.tpe, elsep1.tpe, ptOrLub(List(thenp1.tpe, elsep1.tpe)),"\n", thenTp, elseTp, thenTp =:= elseTp))
val (owntype, needAdapt) =
// in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway
// in the special (though common) case where the types are equal, it pays to pack before comparing
// especially virtpatmat needs more aggressive unification of skolemized types
// this breaks src/library/scala/collection/immutable/TrieIterator.scala
- if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenp1.tpe, false) // use unpacked type
+ if ( opt.virtPatmat && !isPastTyper
+ && thenp1.tpe.annotations.isEmpty && elsep1.tpe.annotations.isEmpty // annotated types need to be lubbed regardless (at least, continations break if you by pass them like this)
+ && thenTp =:= elseTp
+ ) (thenp1.tpe, false) // use unpacked type
// TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala
else ptOrLub(List(thenp1.tpe, elsep1.tpe))
@@ -3797,13 +3795,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
- def typedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = {
- if (opt.virtPatmat && !isPastTyper) {
- if (selector ne EmptyTree) {
- val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = prepareTranslateMatch(selector, cases, mode, pt)
- typed(translateMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt)
- } else typedMatchAnonFun(tree, cases, mode, pt)
- } else if (selector == EmptyTree) {
+ // translation only happens when (selector != EmptyTree) && !isPastTyper && opt.virtPatmat
+ def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = {
+ if (selector == EmptyTree) {
val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1
val params = for (i <- List.range(0, arity)) yield
atPos(tree.pos.focusStart) {
@@ -3814,7 +3808,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
val body = treeCopy.Match(tree, selector1, cases)
typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
- } else {
+ } else if (!((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat)) {
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
@@ -3822,6 +3816,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
cases1 = cases1 map (adaptCase(_, mode, owntype))
}
treeCopy.Match(tree, selector1, cases1) setType owntype
+ } else {
+ val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt)
+ typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt)
}
}
@@ -4213,7 +4210,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
} else {
member(qual, name)
}
- if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) {
+
+ // symbol not found? --> try to convert implicitly to a type that does have the required member
+ // added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an xml member to StringContext, which in turn has an unapply[Seq] method)
+ if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & (EXPRmode | PATTERNmode)) != 0) {
val qual1 =
if (member(qual, name) != NoSymbol) qual
else adaptToMemberWithArgs(tree, qual, name, mode, true, true)
@@ -4221,6 +4221,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (qual1 ne qual)
return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}
+
if (!reallyExists(sym)) {
if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) {
val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) }
@@ -4681,7 +4682,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
typedIf(cond, thenp, elsep)
case tree @ Match(selector, cases) =>
- typedMatch(tree, selector, cases)
+ typedTranslatedMatch(tree, selector, cases)
case Return(expr) =>
typedReturn(expr)
@@ -4697,7 +4698,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
catches1 = catches1 map (adaptCase(_, mode, owntype))
}
- if(!isPastTyper && opt.virtPatmat) {
+ if((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat) {
catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos)
}
@@ -4957,6 +4958,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
ptLine("typing %s: pt = %s".format(ptTree(tree), pt),
"undetparams" -> context.undetparams,
"implicitsEnabled" -> context.implicitsEnabled,
+ "enrichmentEnabled" -> context.enrichmentEnabled,
+ "mode" -> modeString(mode),
"silent" -> context.bufferErrors,
"context.owner" -> context.owner
)
@@ -5060,7 +5063,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// We disable implicits because otherwise some constructs will
// type check which should not. The pattern matcher does not
// perform implicit conversions in an attempt to consummate a match.
- context.withImplicitsDisabled(typed(tree, PATTERNmode, pt))
+
+ // on the one hand,
+ // "abc" match { case Seq('a', 'b', 'c') => true }
+ // should be ruled out statically, otherwise this is a runtime
+ // error both because there is an implicit from String to Seq
+ // (even though such implicits are not used by the matcher) and
+ // because the typer is fine with concluding that "abc" might
+ // be of type "String with Seq[T]" and thus eligible for a call
+ // to unapplySeq.
+
+ // on the other hand, we want to be able to use implicits to add members retro-actively (e.g., add xml to StringContext)
+
+ // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich,
+ // but arbitrary conversions (in adapt) are disabled
+ // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this?
+ context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))
}
/** Types a (fully parameterized) type tree */