summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala2
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala3
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala12
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala5
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala9
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala (renamed from src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala)152
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala166
9 files changed, 216 insertions, 136 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index 23d517eba9..7347249b76 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -412,8 +412,6 @@ trait Definitions extends reflect.api.StandardDefinitions {
lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => arrayType(tparam.tpe))
lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => seqType(tparam.tpe))
- lazy val MarkerCPSTypes = getClassIfDefined("scala.util.continuations.cpsParam")
-
def isByNameParamType(tp: Type) = tp.typeSymbol == ByNameParamClass
def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass
def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index 026cd35d23..b8346a663d 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.XoldPatmat.value
+ protected val enableTypeVarExperimentals = settings.Xexperimental.value
/** Empty immutable maps to avoid allocations. */
private val emptySymMap = immutable.Map[Symbol, Symbol]()
@@ -2898,6 +2898,7 @@ trait Types extends api.Types { self: SymbolTable =>
// existential.
// were we compared to skolems at a higher skolemizationLevel?
// EXPERIMENTAL: value will not be considered unless enableTypeVarExperimentals is true
+ // see SI-5729 for why this is still experimental
private var encounteredHigherLevel = false
private def shouldRepackType = enableTypeVarExperimentals && encounteredHigherLevel
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 8c6c927640..ff092416fd 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -354,9 +354,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
// where I need it, and then an override in Global with the setting.
override protected val etaExpandKeepsStar = settings.etaExpandKeepsStar.value
// Here comes another one...
- override protected val enableTypeVarExperimentals = (
- settings.Xexperimental.value || !settings.XoldPatmat.value
- )
+ override protected val enableTypeVarExperimentals = settings.Xexperimental.value
// True if -Xscript has been set, indicating a script run.
def isScriptRun = opt.script.isDefined
@@ -462,6 +460,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
val global: Global.this.type = Global.this
} with Analyzer
+ // phaseName = "patmat"
+ object patmat extends {
+ val global: Global.this.type = Global.this
+ val runsAfter = List("typer")
+ val runsRightAfter = Some("typer")
+ } with PatternMatching
+
// phaseName = "superaccessors"
object superAccessors extends {
val global: Global.this.type = Global.this
@@ -682,6 +687,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
analyzer.namerFactory -> "resolve names, attach symbols to named trees",
analyzer.packageObjects -> "load package objects",
analyzer.typerFactory -> "the meat and potatoes: type the trees",
+ patmat -> "translate match expressions",
superAccessors -> "add super accessors in traits and nested classes",
extensionMethods -> "add extension methods for inline classes",
pickler -> "serialize symbol tables",
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 34b37073fd..a355db4d9a 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -234,6 +234,11 @@ trait Trees extends reflect.internal.Trees { self: Global =>
}
}
+ // used when a phase is disabled
+ object noopTransformer extends Transformer {
+ override def transformUnit(unit: CompilationUnit): Unit = {}
+ }
+
override protected def xtransform(transformer: super.Transformer, tree: Tree): Tree = tree match {
case DocDef(comment, definition) =>
transformer.treeCopy.DocDef(tree, comment, transformer.transform(definition))
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
index e9ed7181e4..b767722b8c 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
@@ -454,7 +454,7 @@ function resizeFilterBlock() {
function printAlphabet() {
var html = '<a target="template" href="index/index-_.html">#</a>';
var c;
- for (c = 'a'; c < 'z'; c = String.fromCharCode(c.charCodeAt(0) + 1)) {
+ for (c = 'a'; c <= 'z'; c = String.fromCharCode(c.charCodeAt(0) + 1)) {
html += [
'<a target="template" href="index/index-',
c,
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 4c71772929..7dc105690c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -248,13 +248,12 @@ trait MethodSynthesis {
else List(Getter(vd))
)
def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
+ val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil
if (forMSIL) Nil
- else if (vd.symbol hasAnnotation BeanPropertyAttr) {
- if (vd.mods.isMutable) List(BeanGetter(vd), BeanSetter(vd))
- else List(BeanGetter(vd))
- }
+ else if (vd.symbol hasAnnotation BeanPropertyAttr)
+ BeanGetter(vd) :: setter
else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr)
- List(BooleanBeanGetter(vd))
+ BooleanBeanGetter(vd) :: setter
else Nil
}
def allValDefDerived(vd: ValDef) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
index e5b5746e8d..c3a7f2bbc5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
@@ -1,5 +1,6 @@
/* NSC -- new Scala compiler
- * Copyright 2005-2011 LAMP/EPFL
+ *
+ * Copyright 2012 LAMP/EPFL
* @author Adriaan Moors
*/
@@ -9,32 +10,75 @@ package typechecker
import symtab._
import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC}
import language.postfixOps
+import scala.tools.nsc.transform.TypingTransformers
+import scala.tools.nsc.transform.Transform
-/** Translate pattern matching into method calls (these methods form a zero-plus monad), similar in spirit to how for-comprehensions are compiled.
+/** Translate pattern matching.
+ *
+ * Either into optimized if/then/else's,
+ * or virtualized as method calls (these methods form a zero-plus monad), similar in spirit to how for-comprehensions are compiled.
*
* For each case, express all patterns as extractor calls, guards as 0-ary extractors, and sequence them using `flatMap`
* (lifting the body of the case into the monad using `one`).
*
* Cases are combined into a pattern match using the `orElse` combinator (the implicit failure case is expressed using the monad's `zero`).
-
+ *
* TODO:
- * - interaction with CPS
+ * - exhaustivity
+ * - DCE (unreachability/refutability/optimization)
+ * - use TypeTags for type testing
* - Array patterns
* - implement spec more closely (see TODO's)
- * - DCE
- * - use TypeTags for type testing
*
* (longer-term) TODO:
* - user-defined unapplyProd
* - recover GADT typing by locally inserting implicit witnesses to type equalities derived from the current case, and considering these witnesses during subtyping (?)
* - recover exhaustivity and unreachability checking using a variation on the type-safe builder pattern
*/
-trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
- import global._
+trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL { // self: Analyzer =>
+ val global: Global // need to repeat here because otherwise last mixin defines global as
+ // SymbolTable. If we had DOT this would not be an issue
+ import global._ // the global environment
+ import definitions._ // standard classes and methods
+
+ val phaseName: String = "patmat"
+
+ def newTransformer(unit: CompilationUnit): Transformer =
+ if (opt.virtPatmat) new MatchTransformer(unit)
+ else noopTransformer
+
+ // duplicated from CPSUtils (avoid dependency from compiler -> cps plugin...)
+ private lazy val MarkerCPSAdaptPlus = definitions.getClassIfDefined("scala.util.continuations.cpsPlus")
+ private lazy val MarkerCPSAdaptMinus = definitions.getClassIfDefined("scala.util.continuations.cpsMinus")
+ private lazy val MarkerCPSSynth = definitions.getClassIfDefined("scala.util.continuations.cpsSynth")
+ private lazy val stripTriggerCPSAnns = List(MarkerCPSSynth, MarkerCPSAdaptMinus, MarkerCPSAdaptPlus)
+ private lazy val MarkerCPSTypes = definitions.getClassIfDefined("scala.util.continuations.cpsParam")
+ private lazy val strippedCPSAnns = MarkerCPSTypes :: stripTriggerCPSAnns
+ private def removeCPSAdaptAnnotations(tp: Type) = tp filterAnnotations (ann => !(strippedCPSAnns exists (ann matches _)))
+
+ class MatchTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
+ override def transform(tree: Tree): Tree = tree match {
+ case Match(sel, cases) =>
+ val origTp = tree.tpe
+ // setType origTp intended for CPS -- TODO: is it necessary?
+ localTyper.typed(translator.translateMatch(treeCopy.Match(tree, transform(sel), transformTrees(cases).asInstanceOf[List[CaseDef]]))) setType origTp
+ case Try(block, catches, finalizer) =>
+ treeCopy.Try(tree, transform(block), translator.translateTry(transformTrees(catches).asInstanceOf[List[CaseDef]], tree.tpe, tree.pos), transform(finalizer))
+ case _ => super.transform(tree)
+ }
+
+ def translator: MatchTranslation with CodegenCore = {
+ new OptimizingMatchTranslator(localTyper)
+ }
+ }
+
import definitions._
+ import analyzer._ //Typer
val SYNTH_CASE = Flags.CASE | SYNTHETIC
+ case class DefaultOverrideMatchAttachment(default: Tree)
+
object vpmName {
val one = newTermName("one")
val drop = newTermName("drop")
@@ -51,22 +95,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
def counted(str: String, i: Int) = newTermName(str+i)
}
- object MatchTranslator {
- def apply(typer: Typer): MatchTranslation with CodegenCore = {
- import typer._
- // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer
- val matchStrategy: Tree = (
- if (!context.isNameInScope(vpmName._match)) null // fast path, avoiding the next line if there's no __match to be seen
- else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
- case SilentResultValue(ms) => ms
- case _ => null
- }
- )
- if (matchStrategy eq null) new OptimizingMatchTranslator(typer)
- else new PureMatchTranslator(typer, matchStrategy)
- }
- }
-
class PureMatchTranslator(val typer: Typer, val matchStrategy: Tree) extends MatchTranslation with TreeMakers with PureCodegen
class OptimizingMatchTranslator(val typer: Typer) extends MatchTranslation with TreeMakers with MatchOptimizations
@@ -136,15 +164,44 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
* thus, you must typecheck the result (and that will in turn translate nested matches)
* 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 = {
+ def translateMatch(match_ : Match): Tree = {
+ val Match(selector, cases) = match_
+
// 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)
- if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ scrut +" match "+ cases)
+ if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ selector +" match "+ cases)
// println("translating "+ cases.mkString("{", "\n", "}"))
- val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag SYNTH_CASE
+
+ def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
+ case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg)
+ case _ => tp
+ }
+
+ val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.widen.withoutAnnotations))
+
+ val origPt = match_.tpe
+ // when one of the internal cps-type-state annotations is present, strip all CPS annotations
+ // a cps-type-state-annotated type makes no sense as an expected type (matchX.tpe is used as pt in translateMatch)
+ // (only test availability of MarkerCPSAdaptPlus assuming they are either all available or none of them are)
+ val ptUnCPS =
+ if (MarkerCPSAdaptPlus != NoSymbol && (stripTriggerCPSAnns exists origPt.hasAnnotation))
+ removeCPSAdaptAnnotations(origPt)
+ else origPt
+
+ // 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
+ // TODO: fix skolemizeExistential (it should preserve annotations, right?)
+ val pt = repeatedToSeq(ptUnCPS.skolemizeExistential(context.owner, context.tree) withAnnotations ptUnCPS.annotations)
+
+ // the alternative to attaching the default case override would be to simply
+ // append the default to the list of cases and suppress the unreachable case error that may arise (once we detect that...)
+ val matchFailGenOverride = match_ firstAttachment {case DefaultOverrideMatchAttachment(default) => ((scrut: Tree) => default)}
+
+ val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag SYNTH_CASE
// pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental
- combineCases(scrut, scrutSym, cases map translateCase(scrutSym, pt), pt, matchOwner, matchFailGenOverride)
+ combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride)
}
// return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard)
@@ -231,7 +288,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
val pos = patTree.pos
def translateExtractorPattern(extractor: ExtractorCall): TranslationStep = {
- if (!extractor.isTyped) throw new TypeError(pos, "Could not typecheck extractor call: "+ extractor)
+ if (!extractor.isTyped) ErrorUtils.issueNormalTypeError(patTree, "Could not typecheck extractor call: "+ extractor)(context)
// if (extractor.resultInMonad == ErrorType) throw new TypeError(pos, "Unsupported extractor type: "+ extractor.tpe)
// must use type `tp`, which is provided by extractor's result, not the type expected by binder,
@@ -296,7 +353,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
**/
case Apply(fun, args) =>
ExtractorCall.fromCaseClass(fun, args) map translateExtractorPattern getOrElse {
- error("cannot find unapply member for "+ fun +" with args "+ args)
+ ErrorUtils.issueNormalTypeError(patTree, "Could not find unapply member for "+ fun +" with args "+ args)(context)
noFurtherSubPats()
}
@@ -552,18 +609,29 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
ProductExtractorTreeMaker(binder, lengthGuard(binder), Substitution(subPatBinders, subPatRefs(binder)))
}
-/* TODO: remove special case when the following bug is fixed
-class Foo(x: Other) { x._1 } // BUG: can't refer to _1 if its defining class has not been type checked yet
-case class Other(y: String)
--- this is ok:
-case class Other(y: String)
-class Foo(x: Other) { x._1 } // no error in this order
-*/
+ // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component
override protected def tupleSel(binder: Symbol)(i: Int): Tree = { import CODE._
- // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component
- val caseAccs = binder.info.typeSymbol.caseFieldAccessors
- if (caseAccs isDefinedAt (i-1)) REF(binder) DOT caseAccs(i-1)
- else codegen.tupleSel(binder)(i)
+ // caseFieldAccessors is messed up after typers (reversed, names mangled for non-public fields)
+ // TODO: figure out why...
+ val accessors = binder.caseFieldAccessors
+ // luckily, the constrParamAccessors are still sorted properly, so sort the field-accessors using them
+ // (need to undo name-mangling, including the sneaky trailing whitespace)
+ val constrParamAccessors = binder.constrParamAccessors
+
+ def indexInCPA(acc: Symbol) =
+ constrParamAccessors indexWhere { orig =>
+ // println("compare: "+ (orig, acc, orig.name, acc.name, (acc.name == orig.name), (acc.name startsWith (orig.name append "$"))))
+ val origName = orig.name.toString.trim
+ val accName = acc.name.toString.trim
+ (accName == origName) || (accName startsWith (origName + "$"))
+ }
+
+ // println("caseFieldAccessors: "+ (accessors, binder.caseFieldAccessors map indexInCPA))
+ // println("constrParamAccessors: "+ constrParamAccessors)
+
+ val accessorsSorted = accessors sortBy indexInCPA
+ if (accessorsSorted isDefinedAt (i-1)) REF(binder) DOT accessorsSorted(i-1)
+ else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN
}
override def toString(): String = "case class "+ (if (constructorTp eq null) fun else paramType.typeSymbol) +" with arguments "+ args
@@ -1586,7 +1654,7 @@ class Foo(x: Other) { x._1 } // no error in this order
else (REF(scrutSym) DOT (nme.toInt))
Some(BLOCK(
VAL(scrutSym) === scrut,
- Match(gen.mkSynthSwitchSelector(scrutToInt), caseDefsWithDefault) // add switch annotation
+ Match(scrutToInt, caseDefsWithDefault) // a switch
))
}
} else None
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index 31d064c824..57e82ed706 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -299,6 +299,7 @@ trait SyntheticMethods extends ast.TreeDSL {
newAcc resetFlag (ACCESSOR | PARAMACCESSOR)
ddef.rhs.duplicate
}
+ // TODO: shouldn't the next line be: `original resetFlag CASEACCESSOR`?
ddef.symbol resetFlag CASEACCESSOR
lb += logResult("case accessor new")(newAcc)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 41b896eb93..b55406761d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -26,12 +26,14 @@ import util.Statistics._
* @author Martin Odersky
* @version 1.0
*/
-trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser {
+trait Typers extends Modes with Adaptations with Taggings {
self: Analyzer =>
import global._
import definitions._
+ import patmat.DefaultOverrideMatchAttachment
+
final def forArgMode(fun: Tree, mode: Int) =
if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode
else mode
@@ -83,8 +85,11 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
private def isPastTyper = phase.id > currentRun.typerPhase.id
- // don't translate matches in presentation compiler: it loses vital symbols that are needed to do hyperlinking
- @inline private def doMatchTranslation = !forInteractive && opt.virtPatmat && (phase.id < currentRun.uncurryPhase.id)
+ // when true:
+ // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope)
+ // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction
+ // this is disabled by: -Xoldpatmat, scaladoc or interactive compilation
+ @inline private def newPatternMatching = opt.virtPatmat && !forScaladoc && !forInteractive // && (phase.id < currentRun.uncurryPhase.id)
abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tagging with TyperContextErrors {
import context0.unit
@@ -2208,54 +2213,61 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe))
+ def ptOrLub(tps: List[Type], pt: Type ) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst))
+ def ptOrLubPacked(trees: List[Tree], pt: Type) = if (isFullyDefined(pt)) (pt, false) else weakLub(trees map (c => packedType(c, context.owner).deconst))
+
// takes untyped sub-trees of a match and type checks them
- 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.withoutAnnotations)
+ def typedMatch(selector: Tree, cases: List[CaseDef], mode: Int, pt: Type, tree: Tree = EmptyTree): Match = {
+ val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
+ val selectorTp = packCaptured(selector1.tpe.widen)
+ val casesTyped = typedCases(cases, selectorTp, pt)
- val casesTyped = typedCases(cases, selectorTp, resTp)
- val caseTypes = casesTyped map (c => packedType(c, context.owner).deconst)
- val (ownType, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(caseTypes)
+ val (resTp, needAdapt) =
+ if (opt.virtPatmat) ptOrLubPacked(casesTyped, pt)
+ else ptOrLub(casesTyped map (_.tpe), pt)
- val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, ownType))
+ val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp))
- (selector1, selectorTp, casesAdapted, ownType, doTranslation)
+ treeCopy.Match(tree, selector1, casesAdapted) setType resTp
}
- // match has been typed, now translate it
- 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
- }
+ // match has been typed -- virtualize it if we're feeling experimental
+ // (virtualized matches are expanded during type checking so they have the full context available)
+ // otherwise, do nothing: matches are translated during phase `patmat` (unless -Xoldpatmat)
+ def virtualizedMatch(match_ : Match, mode: Int, pt: Type) = {
+ import patmat.{vpmName, PureMatchTranslator, OptimizingMatchTranslator}
+
+ // TODO: add fallback __match sentinel to predef
+ val matchStrategy: Tree =
+ if (!(newPatternMatching && opt.experimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen
+ else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
+ case SilentResultValue(ms) => ms
+ case _ => null
+ }
- if (!doTranslation) { // a switch
- 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 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
- // 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)
- }
+ if (matchStrategy ne null) // virtualize
+ typed((new PureMatchTranslator(this.asInstanceOf[patmat.global.analyzer.Typer] /*TODO*/, matchStrategy)).translateMatch(match_), mode, pt)
+ else
+ match_ // will be translated in phase `patmat`
}
- // synthesize and type check a (Partial)Function implementation based on a match specified by `cases`
- // Match(EmptyTree, cases) ==> new <Partial>Function { def apply<OrElse>(params) = `translateMatch('`(param1,...,paramN)` match { cases }')` }
+ // synthesize and type check a PartialFunction implementation based on a match specified by `cases`
+ // Match(EmptyTree, cases) ==> new PartialFunction { def apply<OrElse>(params) = `translateMatch('`(param1,...,paramN)` match { cases }')` }
// for fresh params, the selector of the match we'll translated simply gathers those in a tuple
+ // NOTE: restricted to PartialFunction -- leave Function trees if the expected type does not demand a partial function
class MatchFunTyper(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type) {
+ // TODO: remove FunctionN support -- this is currently designed so that it can emit FunctionN and PartialFunction subclasses
+ // however, we should leave Function nodes until Uncurry so phases after typer can still detect normal Function trees
+ // we need to synthesize PartialFunction impls, though, to avoid nastiness in Uncurry in transforming&duplicating generated pattern matcher trees
+ // TODO: remove PartialFunction support from UnCurry
private val pt = deskolemizeGADTSkolems(pt0)
private val targs = pt.normalize.typeArgs
private val arity = if (isFunctionType(pt)) targs.length - 1 else 1 // TODO pt should always be a (Partial)Function, right?
private val ptRes = if (targs.isEmpty) WildcardType else targs.last // may not be fully defined
private val isPartial = pt.typeSymbol == PartialFunctionClass
+ assert(isPartial)
+
private val anonClass = context.owner.newAnonymousFunctionClass(tree.pos)
private val funThis = This(anonClass)
@@ -2297,7 +2309,8 @@ trait Typers extends Modes with Adaptations with Taggings 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.typedMatch(selector, cases, mode, ptRes)
+ val match_ = methodBodyTyper.typedMatch(selector, cases, mode, ptRes)
+ val resTp = match_.tpe
val methFormals = paramSyms map (_.tpe)
val parents =
@@ -2307,7 +2320,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
methodSym setInfoAndEnter MethodType(paramSyms, resTp)
- DefDef(methodSym, methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation))
+ DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, resTp))
}
}
@@ -2336,16 +2349,17 @@ trait Typers extends Modes with Adaptations with Taggings 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.typedMatch(selector, cases, mode, ptRes)
+ val match_ = methodBodyTyper.typedMatch(selector, cases, mode, ptRes)
+ val resTp = match_.tpe
anonClass setInfo ClassInfoType(parentsPartial(List(argTp, resTp)), newScope, anonClass)
B1 setInfo TypeBounds.lower(resTp)
anonClass.info.decls enter methodSym // methodSym's info need not change (B1's bound has been updated instead)
- // use applyOrElse's first parameter since the scrut's type has been widened
- def doDefault(scrut_ignored: Tree) = REF(default) APPLY (REF(x))
+ match_ setType B1.tpe
- val body = methodBodyTyper.translatedMatch(selector1, selectorTp, casesAdapted, B1.tpe, doTranslation, Some(doDefault))
+ // the default uses applyOrElse's first parameter since the scrut's type has been widened
+ val body = methodBodyTyper.virtualizedMatch(match_ withAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x))), mode, B1.tpe)
DefDef(methodSym, body)
}
@@ -2362,17 +2376,18 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
paramSyms foreach (methodBodyTyper.context.scope enter _)
methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe)
- 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))
+ val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe)
+ val body = methodBodyTyper.virtualizedMatch(match_ withAttachment DefaultOverrideMatchAttachment(FALSE_typed), mode, BooleanClass.tpe)
DefDef(methodSym, body)
}
}
- val members = if (isPartial) {
- // TODO: don't check for MarkerCPSTypes -- check whether all targs are subtype of any (which they are not under CPS)
- if ((MarkerCPSTypes ne NoSymbol) && (targs exists (_ hasAnnotation MarkerCPSTypes))) List(applyMethod, isDefinedAtMethod)
- else List(applyOrElseMethodDef, isDefinedAtMethod)
+ lazy val members = if (isPartial) {
+ // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's
+ // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?)
+ if (targs forall (_ <:< AnyClass.tpe)) List(applyOrElseMethodDef, isDefinedAtMethod)
+ else List(applyMethod, isDefinedAtMethod)
} else List(applyMethod)
def translated =
@@ -2447,7 +2462,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
fun.body match {
// later phase indicates scaladoc is calling (where shit is messed up, I tell you)
// -- so fall back to old patmat, which is more forgiving
- case Match(sel, cases) if (sel ne EmptyTree) && doMatchTranslation =>
+ case Match(sel, cases) if (sel ne EmptyTree) && newPatternMatching && (pt.typeSymbol == PartialFunctionClass) =>
// 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)
@@ -3638,8 +3653,6 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
def isPatternMode = inPatternMode(mode)
//Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")")
- def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst))
-
//@M! get the type of the qualifier in a Select tree, otherwise: NoType
def prefixType(fun: Tree): Type = fun match {
case Select(qualifier, _) => qualifier.tpe
@@ -3829,7 +3842,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
&& 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))
+ else ptOrLub(List(thenp1.tpe, elsep1.tpe), pt)
if (needAdapt) { //isNumericValueType(owntype)) {
thenp1 = adapt(thenp1, mode, owntype)
@@ -3839,34 +3852,26 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
}
}
- def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = {
- if (doMatchTranslation) {
- if (selector ne EmptyTree) {
- val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt)
- typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt)
- } else (new MatchFunTyper(tree, cases, mode, pt)).translated
- } else if (selector == EmptyTree) {
- if (opt.virtPatmat) debugwarn("virtpatmat should not encounter empty-selector matches "+ tree)
- 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) {
- ValDef(Modifiers(PARAM | SYNTHETIC),
- unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
- }
- val ids = for (p <- params) yield Ident(p.name)
- 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 {
- val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
- val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
- if (needAdapt) {
- cases1 = cases1 map (adaptCase(_, mode, owntype))
+ // under -Xexperimental (and not -Xoldpatmat), and when there's a suitable __match in scope, virtualize the pattern match
+ // otherwise, type the Match and leave it until phase `patmat` (immediately after typer)
+ // empty-selector matches are transformed into synthetic PartialFunction implementations when the expected type demands it
+ def typedVirtualizedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree =
+ if (selector == EmptyTree) {
+ if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) (new MatchFunTyper(tree, cases, mode, pt)).translated
+ else {
+ 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) {
+ ValDef(Modifiers(PARAM | SYNTHETIC),
+ unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
+ }
+ val ids = for (p <- params) yield Ident(p.name)
+ 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)
}
- treeCopy.Match(tree, selector1, cases1) setType owntype
- }
- }
+ } else
+ virtualizedMatch(typedMatch(selector, cases, mode, pt, tree), mode, pt)
def typedReturn(expr: Tree) = {
val enclMethod = context.enclMethod
@@ -4708,7 +4713,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
typedIf(cond, thenp, elsep)
case tree @ Match(selector, cases) =>
- typedTranslatedMatch(tree, selector, cases)
+ typedVirtualizedMatch(tree, selector, cases)
case Return(expr) =>
typedReturn(expr)
@@ -4718,15 +4723,12 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
var catches1 = typedCases(catches, ThrowableClass.tpe, pt)
val finalizer1 = if (finalizer.isEmpty) finalizer
else typed(finalizer, UnitClass.tpe)
- val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)))
+ val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)), pt)
if (needAdapt) {
block1 = adapt(block1, mode, owntype)
catches1 = catches1 map (adaptCase(_, mode, owntype))
}
- if (doMatchTranslation)
- catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos)
-
treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype
case Throw(expr) =>