diff options
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/reflect/internal/Definitions.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Types.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 12 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Trees.scala | 5 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala | 9 | ||||
-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.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 166 |
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) => |