diff options
35 files changed, 404 insertions, 262 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) => diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index bed8e93d1b..862b19d0a4 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -3,8 +3,9 @@ package scala.tools.selectivecps import scala.tools.nsc.Global +import scala.tools.nsc.typechecker.Modes -abstract class CPSAnnotationChecker extends CPSUtils { +abstract class CPSAnnotationChecker extends CPSUtils with Modes { val global: Global import global._ import definitions._ @@ -177,59 +178,38 @@ abstract class CPSAnnotationChecker extends CPSUtils { override def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { if (!cpsEnabled) return tree - vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + modeString(mode) + " / " + pt) - val annots1 = cpsParamAnnotation(tree.tpe) - val annots2 = cpsParamAnnotation(pt) + val patMode = (mode & global.analyzer.PATTERNmode) != 0 + val exprMode = (mode & global.analyzer.EXPRmode) != 0 + val byValMode = (mode & global.analyzer.BYVALmode) != 0 - if ((mode & global.analyzer.PATTERNmode) != 0) { - if (!annots1.isEmpty) { - return tree modifyType removeAllCPSAnnotations - } - } + val annotsTree = cpsParamAnnotation(tree.tpe) + val annotsExpected = cpsParamAnnotation(pt) -/* + // not sure I rephrased this comment correctly: + // replacing `patMode` in the condition below by `patMode || ((mode & global.analyzer.TYPEmode) != 0 && (mode & global.analyzer.BYVALmode))` // doesn't work correctly -- still relying on addAnnotations to remove things from ValDef symbols - if ((mode & global.analyzer.TYPEmode) != 0 && (mode & global.analyzer.BYVALmode) != 0) { - if (!annots1.isEmpty) { - println("removing annotation from " + tree + "/" + tree.tpe) - val s = tree.setType(removeAllCPSAnnotations(tree.tpe)) - println(s) - s - } - } -*/ - - if ((mode & global.analyzer.EXPRmode) != 0) { - if (annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.BYVALmode) == 0)) { // shiftUnit - // add a marker annotation that will make tree.tpe behave as pt, subtyping wise - // tree will look like having any possible annotation - //println("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) - //val same = annots2 forall { case AnnotationInfo(atp: TypeRef, _, _) => atp.typeArgs(0) =:= atp.typeArgs(1) } - // TBD: use same or not? see infer0.scala/infer1.scala - - // CAVEAT: - // for monomorphic answer types we want to have @plus @cps (for better checking) - // for answer type modification we want to have only @plus (because actual answer type may differ from pt) - - //val known = global.analyzer.isFullyDefined(pt) - - if (/*same &&*/ !hasPlusMarker(tree.tpe)) { - //if (known) - return tree modifyType (_ withAnnotations newPlusMarker() :: annots2) // needed for #1807 - //else - // return tree.setType(tree.tpe.withAnnotations(adapt::Nil)) - } - tree - } else if (!annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { // dropping annotation - // add a marker annotation that will make tree.tpe behave as pt, subtyping wise - // tree will look like having no annotation - if (!hasMinusMarker(tree.tpe)) { - return tree modifyType addMinusMarker - } - } - } - tree + if (patMode && !annotsTree.isEmpty) tree modifyType removeAllCPSAnnotations + else if (exprMode && !byValMode && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { // shiftUnit + // add a marker annotation that will make tree.tpe behave as pt, subtyping wise + // tree will look like having any possible annotation + //println("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + + // CAVEAT: + // for monomorphic answer types we want to have @plus @cps (for better checking) + // for answer type modification we want to have only @plus (because actual answer type may differ from pt) + + val res = tree modifyType (_ withAnnotations newPlusMarker() :: annotsExpected) // needed for #1807 + vprintln("adapted annotations (not by val) of " + tree + " to " + res.tpe) + res + } else if (exprMode && byValMode && !hasMinusMarker(tree.tpe) && annotsTree.nonEmpty) { // dropping annotation + // add a marker annotation that will make tree.tpe behave as pt, subtyping wise + // tree will look like having no annotation + val res = tree modifyType addMinusMarker + vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe) + res + } else tree } def updateAttributesFromChildren(tpe: Type, childAnnots: List[AnnotationInfo], byName: List[Tree]): Type = { @@ -454,11 +434,10 @@ abstract class CPSAnnotationChecker extends CPSUtils { transChildrenInOrder(tree, tpe, List(cond), List(thenp, elsep)) case Match(select, cases) => - // TODO: can there be cases that are not CaseDefs?? check collect vs map! - transChildrenInOrder(tree, tpe, List(select), cases:::(cases collect { case CaseDef(_, _, body) => body })) + transChildrenInOrder(tree, tpe, List(select), cases:::(cases map { case CaseDef(_, _, body) => body })) case Try(block, catches, finalizer) => - val tpe1 = transChildrenInOrder(tree, tpe, Nil, block::catches:::(catches collect { case CaseDef(_, _, body) => body })) + val tpe1 = transChildrenInOrder(tree, tpe, Nil, block::catches:::(catches map { case CaseDef(_, _, body) => body })) val annots = cpsParamAnnotation(tpe1) if (annots.nonEmpty) { diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index e1d699debc..e9e9cf0fab 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -241,6 +241,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // where D$idef = def L$i(..) = {L$i.body; L${i+1}(..)} case ldef @ LabelDef(name, params, rhs) => + // println("trans LABELDEF "+(name, params, tree.tpe, hasAnswerTypeAnn(tree.tpe))) + // TODO why does the labeldef's type have a cpsMinus annotation, whereas the rhs does not? (BYVALmode missing/too much somewhere?) if (hasAnswerTypeAnn(tree.tpe)) { // currentOwner.newMethod(name, tree.pos, Flags.SYNTHETIC) setInfo ldef.symbol.info val sym = ldef.symbol resetFlag Flags.LABEL @@ -456,10 +458,11 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with val (anfStats, anfExpr) = rec(stms, cpsA, List()) // println("\nanf-block:\n"+ ((stms :+ expr) mkString ("{", "\n", "}")) +"\nBECAME\n"+ ((anfStats :+ anfExpr) mkString ("{", "\n", "}"))) - + // println("synth case? "+ (anfStats map (t => (t, t.isDef, gen.hasSynthCaseSymbol(t))))) // SUPER UGLY HACK: handle virtpatmat-style matches, whose labels have already been turned into DefDefs if (anfStats.nonEmpty && (anfStats forall (t => !t.isDef || gen.hasSynthCaseSymbol(t)))) { val (prologue, rest) = (anfStats :+ anfExpr) span (s => !s.isInstanceOf[DefDef]) // find first case + // println("rest: "+ rest) // val (defs, calls) = rest partition (_.isInstanceOf[DefDef]) if (rest nonEmpty){ // the filter drops the ()'s emitted when transValue encountered a LabelDef diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index a78de8e6c8..dcb7cd601f 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -65,6 +65,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with class CPSTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + private val patmatTransformer = patmat.newTransformer(unit) override def transform(tree: Tree): Tree = { if (!cpsEnabled) return tree @@ -212,7 +213,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with val catch2 = localTyper.typedCases(List(catchIfDefined), ThrowableClass.tpe, targettp) //typedCases(tree, catches, ThrowableClass.tpe, pt) - localTyper.typed(Block(List(funDef), treeCopy.Try(tree, treeCopy.Block(block1, stms, expr2), catch2, finalizer1))) + patmatTransformer.transform(localTyper.typed(Block(List(funDef), treeCopy.Try(tree, treeCopy.Block(block1, stms, expr2), catch2, finalizer1)))) /* diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 169826034b..e8921ef531 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -26,7 +26,7 @@ package concurrent { object Await { private[concurrent] implicit val canAwaitEvidence = new CanAwait {} - def ready[T <: Awaitable[_]](awaitable: T, atMost: Duration): T = { + def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = { blocking(awaitable, atMost) awaitable } diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index f1e9cc13ca..b82972c9bc 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -110,13 +110,12 @@ trait Trees { self: Universe => def withoutAttachment(att: Any): this.type = { detach(att); this } def attachment[T: ClassTag]: T = attachmentOpt[T] getOrElse { throw new Error("no attachment of type %s".format(classTag[T].erasure)) } def attachmentOpt[T: ClassTag]: Option[T] = + firstAttachment { case attachment if attachment.getClass == classTag[T].erasure => attachment.asInstanceOf[T] } + + def firstAttachment[T](p: PartialFunction[Any, T]): Option[T] = rawatt match { - case NontrivialAttachment(pos, payload) => - val index = payload.indexWhere(p => p.getClass == classTag[T].erasure) - if (index != -1) Some(payload(index).asInstanceOf[T]) - else None - case _ => - None + case NontrivialAttachment(pos, payload) => payload.collectFirst(p) + case _ => None } private[this] var rawtpe: Type = _ diff --git a/test/files/jvm/annotations.scala b/test/files/jvm/annotations.scala index b1c3c8ba40..66ebde592b 100644 --- a/test/files/jvm/annotations.scala +++ b/test/files/jvm/annotations.scala @@ -193,7 +193,9 @@ object Test6 { val c = new C("bob") c.setText("dylan") println(c.getText()) - if (new D(true).isProp()) { + val d = new D(true) + d.setProp(false) + if (!d.isProp()) { println(new D(false).getM()) } } diff --git a/test/files/neg/gadts1.check b/test/files/neg/gadts1.check index 0441f604c9..44d2b114d6 100644 --- a/test/files/neg/gadts1.check +++ b/test/files/neg/gadts1.check @@ -11,7 +11,4 @@ gadts1.scala:20: error: type mismatch; required: a case Cell[a](x: Int) => c.x = 5 ^ -gadts1.scala:20: error: Could not typecheck extractor call: case class <none> with arguments List((x @ (_: Int))) - case Cell[a](x: Int) => c.x = 5 - ^ -four errors found +three errors found diff --git a/test/files/neg/patmat-type-check.check b/test/files/neg/patmat-type-check.check index ab4451f089..e045841ce1 100644 --- a/test/files/neg/patmat-type-check.check +++ b/test/files/neg/patmat-type-check.check @@ -3,31 +3,19 @@ patmat-type-check.scala:22: error: scrutinee is incompatible with pattern type; required: String def f1 = "bob".reverse match { case Seq('b', 'o', 'b') => true } // fail ^ -patmat-type-check.scala:22: error: value _1 is not a member of object Seq - def f1 = "bob".reverse match { case Seq('b', 'o', 'b') => true } // fail - ^ patmat-type-check.scala:23: error: scrutinee is incompatible with pattern type; found : Seq[A] required: Array[Char] def f2 = "bob".toArray match { case Seq('b', 'o', 'b') => true } // fail ^ -patmat-type-check.scala:23: error: value _1 is not a member of object Seq - def f2 = "bob".toArray match { case Seq('b', 'o', 'b') => true } // fail - ^ patmat-type-check.scala:27: error: scrutinee is incompatible with pattern type; found : Seq[A] required: Test.Bop2 def f3(x: Bop2) = x match { case Seq('b', 'o', 'b') => true } // fail ^ -patmat-type-check.scala:27: error: value _1 is not a member of object Seq - def f3(x: Bop2) = x match { case Seq('b', 'o', 'b') => true } // fail - ^ patmat-type-check.scala:30: error: scrutinee is incompatible with pattern type; found : Seq[A] required: Test.Bop3[Char] def f4[T](x: Bop3[Char]) = x match { case Seq('b', 'o', 'b') => true } // fail ^ -patmat-type-check.scala:30: error: value _1 is not a member of object Seq - def f4[T](x: Bop3[Char]) = x match { case Seq('b', 'o', 'b') => true } // fail - ^ -8 errors found +four errors found diff --git a/test/files/neg/t0418.check b/test/files/neg/t0418.check index 50931a1bca..4e9ad2f9ae 100644 --- a/test/files/neg/t0418.check +++ b/test/files/neg/t0418.check @@ -4,7 +4,4 @@ t0418.scala:2: error: not found: value Foo12340771 t0418.scala:2: error: not found: value x null match { case Foo12340771.Bar(x) => x } ^ -t0418.scala:2: error: Could not typecheck extractor call: case class <none> with arguments List((x @ _)) - null match { case Foo12340771.Bar(x) => x } - ^ -three errors found +two errors found diff --git a/test/files/neg/t112706A.check b/test/files/neg/t112706A.check index fb18b31be1..30d0c3ec91 100644 --- a/test/files/neg/t112706A.check +++ b/test/files/neg/t112706A.check @@ -3,7 +3,4 @@ t112706A.scala:5: error: constructor cannot be instantiated to expected type; required: String case Tuple2(node,_) => ^ -t112706A.scala:5: error: Could not typecheck extractor call: case class Tuple2 with arguments List((node @ _), _) - case Tuple2(node,_) => - ^ -two errors found +one error found diff --git a/test/files/neg/t3392.check b/test/files/neg/t3392.check index 3a39098c4e..842d63eec9 100644 --- a/test/files/neg/t3392.check +++ b/test/files/neg/t3392.check @@ -1,7 +1,4 @@ t3392.scala:9: error: not found: value x case x@A(x/*<-- refers to the pattern that includes this comment*/.Ex(42)) => ^ -t3392.scala:9: error: Could not typecheck extractor call: case class <none> with arguments List(42) - case x@A(x/*<-- refers to the pattern that includes this comment*/.Ex(42)) => - ^ -two errors found +one error found diff --git a/test/files/neg/t418.check b/test/files/neg/t418.check index c06088ba9d..1489547823 100644 --- a/test/files/neg/t418.check +++ b/test/files/neg/t418.check @@ -4,7 +4,4 @@ t418.scala:2: error: not found: value Foo12340771 t418.scala:2: error: not found: value x null match { case Foo12340771.Bar(x) => x } ^ -t418.scala:2: error: Could not typecheck extractor call: case class <none> with arguments List((x @ _)) - null match { case Foo12340771.Bar(x) => x } - ^ -three errors found +two errors found diff --git a/test/files/neg/t4515.check b/test/files/neg/t4515.check index 856d252a0f..a60d16295f 100644 --- a/test/files/neg/t4515.check +++ b/test/files/neg/t4515.check @@ -1,6 +1,6 @@ t4515.scala:37: error: type mismatch; - found : _0(in method apply) where type _0(in method apply) - required: (some other)_0(in method apply) + found : _0(in value $anonfun) where type _0(in value $anonfun) + required: (some other)_0(in value $anonfun) handler.onEvent(target, ctx.getEvent, node, ctx) ^ one error found diff --git a/test/files/neg/t5589neg.check b/test/files/neg/t5589neg.check index fb6858a397..b3ff16d7e4 100644 --- a/test/files/neg/t5589neg.check +++ b/test/files/neg/t5589neg.check @@ -22,9 +22,6 @@ t5589neg.scala:4: error: constructor cannot be instantiated to expected type; t5589neg.scala:4: error: not found: value y2 def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) ^ -t5589neg.scala:4: error: Could not typecheck extractor call: case class Tuple1 with arguments List((y2 @ _)) - def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) - ^ t5589neg.scala:5: error: constructor cannot be instantiated to expected type; found : (T1, T2, T3) required: (String, Int) @@ -37,4 +34,4 @@ t5589neg.scala:5: error: not found: value y2 def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) ^ two warnings found -8 errors found +7 errors found diff --git a/test/files/pos/t5542.flags b/test/files/pos/t5542.flags new file mode 100644 index 0000000000..464cc20ea6 --- /dev/null +++ b/test/files/pos/t5542.flags @@ -0,0 +1 @@ +-Xfatal-warnings -unchecked
\ No newline at end of file diff --git a/test/files/pos/t5542.scala b/test/files/pos/t5542.scala new file mode 100644 index 0000000000..80b8cef030 --- /dev/null +++ b/test/files/pos/t5542.scala @@ -0,0 +1,3 @@ +class Test { + Option(3) match { case Some(n) => n; case None => 0 } +}
\ No newline at end of file diff --git a/test/files/pos/t5729.scala b/test/files/pos/t5729.scala new file mode 100644 index 0000000000..9fd9c9ffbb --- /dev/null +++ b/test/files/pos/t5729.scala @@ -0,0 +1,6 @@ +trait T[X] +object Test { + def join(in: Seq[T[_]]): Int = ??? + def join[S](in: Seq[T[S]]): String = ??? + join(null: Seq[T[_]]) +}
\ No newline at end of file diff --git a/test/files/run/inner-parse.check b/test/files/run/inner-parse.check index 87ea9ddeb5..e4a30714bd 100644 --- a/test/files/run/inner-parse.check +++ b/test/files/run/inner-parse.check @@ -5,6 +5,7 @@ class Test$$anonfun$main$1 extends scala.runtime.AbstractFunction1$mcVL$sp descriptor <clinit> ()V descriptor apply (Lscala/Tuple2;)V descriptor apply (Ljava/lang/Object;)Ljava/lang/Object; + descriptor apply (Ljava/lang/Object;)V descriptor cwd$1 Ljava/lang/String; descriptor serialVersionUID J descriptor <init> (Ljava/lang/String;)V diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index d16e2c5178..bdf76ddce1 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -4,27 +4,28 @@ namer 2 resolve names, attach symbols to named trees packageobjects 3 load package objects typer 4 the meat and potatoes: type the trees - superaccessors 5 add super accessors in traits and nested classes - extmethods 6 add extension methods for inline classes - pickler 7 serialize symbol tables - refchecks 8 reference/override checking, translate nested objects - uncurry 9 uncurry, translate function values to anonymous classes - tailcalls 10 replace tail calls by jumps - specialize 11 @specialized-driven class and method specialization - explicitouter 12 this refs to outer pointers, translate patterns - erasure 13 erase types, add interfaces for traits - posterasure 14 clean up erased inline classes - lazyvals 15 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 16 move nested functions to top level - constructors 17 move field definitions into constructors - flatten 18 eliminate inner classes - mixin 19 mixin composition - cleanup 20 platform-specific cleanups, generate reflective calls - icode 21 generate portable intermediate code - inliner 22 optimization: do inlining -inlineExceptionHandlers 23 optimization: inline exception handlers - closelim 24 optimization: eliminate uncalled closures - dce 25 optimization: eliminate dead code - jvm 26 generate JVM bytecode - terminal 27 The last phase in the compiler chain + patmat 5 translate match expressions + superaccessors 6 add super accessors in traits and nested classes + extmethods 7 add extension methods for inline classes + pickler 8 serialize symbol tables + refchecks 9 reference/override checking, translate nested objects + uncurry 10 uncurry, translate function values to anonymous classes + tailcalls 11 replace tail calls by jumps + specialize 12 @specialized-driven class and method specialization + explicitouter 13 this refs to outer pointers, translate patterns + erasure 14 erase types, add interfaces for traits + posterasure 15 clean up erased inline classes + lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + icode 22 generate portable intermediate code + inliner 23 optimization: do inlining +inlineExceptionHandlers 24 optimization: inline exception handlers + closelim 25 optimization: eliminate uncalled closures + dce 26 optimization: eliminate dead code + jvm 27 generate JVM bytecode + terminal 28 The last phase in the compiler chain diff --git a/test/files/run/virtpatmat_staging.flags b/test/files/run/virtpatmat_staging.flags index 3f5a3100e4..48fd867160 100644 --- a/test/files/run/virtpatmat_staging.flags +++ b/test/files/run/virtpatmat_staging.flags @@ -1 +1 @@ - -Xexperimental +-Xexperimental diff --git a/test/pending/run/t5018.scala b/test/pending/run/t5018.scala new file mode 100644 index 0000000000..30c0d5ac94 --- /dev/null +++ b/test/pending/run/t5018.scala @@ -0,0 +1,34 @@ + + + +import java.io._ +import collection._ + + + +object Test { + + def serializeDeserialize[T <: AnyRef](obj: T) = { + val buffer = new ByteArrayOutputStream + val out = new ObjectOutputStream(buffer) + out.writeObject(obj) + val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray)) + in.readObject.asInstanceOf[T] + } + + def main(args: Array[String]) { + val values = mutable.Map(1 -> 1).values + assert(serializeDeserialize(values) == values) + + val keyset = mutable.Map(1 -> 1).keySet + assert(serializeDeserialize(keyset) == keyset) + + val imkeyset = immutable.Map(1 -> 1).keySet + assert(serializeDeserialize(imkeyset) == imkeyset) + + val defaultmap = immutable.Map(1 -> 1).withDefaultValue(1) + assert(serializeDeserialize(defaultmap) == defaultmap) + } + +} + diff --git a/test/pending/run/t5676.flags b/test/pending/run/t5676.flags new file mode 100644 index 0000000000..e1b37447c9 --- /dev/null +++ b/test/pending/run/t5676.flags @@ -0,0 +1 @@ +-Xexperimental
\ No newline at end of file diff --git a/test/pending/run/t5676.scala b/test/pending/run/t5676.scala new file mode 100644 index 0000000000..3ff498eaa2 --- /dev/null +++ b/test/pending/run/t5676.scala @@ -0,0 +1,25 @@ + + + + +class Bar[T] + + +class Foo[T] { + object A extends Bar[T] +} + + +class Baz[S] extends Foo[S] { + override object A extends Bar[S] +} + + +object Test { + + def main(a: Array[String]) { + val b = new Baz[Any] + println(b) + } + +} diff --git a/test/pending/run/t5698/client.scala b/test/pending/run/t5698/client.scala new file mode 100644 index 0000000000..de672c1809 --- /dev/null +++ b/test/pending/run/t5698/client.scala @@ -0,0 +1,9 @@ +package client + + + +object Client extends App { + val peer = actors.remote.Node("localhost", 23456) + val a = actors.remote.RemoteActor.select(peer, 'test) + a ! server.TestMsg +} diff --git a/test/pending/run/t5698/server.scala b/test/pending/run/t5698/server.scala new file mode 100644 index 0000000000..e8f3cea225 --- /dev/null +++ b/test/pending/run/t5698/server.scala @@ -0,0 +1,22 @@ +package server + + + +object Server extends App { + + class ServerActor extends actors.Actor { + def act() { + actors.remote.RemoteActor.alive(23456) + actors.remote.RemoteActor.register('test, actors.Actor.self) + loop { + react { + case TestMsg => println("Yay!") + } + } + } + } + + val a = new ServerActor + a.start() + +} diff --git a/test/pending/run/t5698/testmsg.scala b/test/pending/run/t5698/testmsg.scala new file mode 100644 index 0000000000..004ff0b8c7 --- /dev/null +++ b/test/pending/run/t5698/testmsg.scala @@ -0,0 +1,5 @@ +package server + + + +case object TestMsg |