diff options
3 files changed, 29 insertions, 20 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 0d19b781e2..8703de4e18 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -44,6 +44,11 @@ trait TreeDSL { def NULL = LIT(null) def UNIT = LIT(()) + // for those preferring boring, predictable lives, without the thrills of tree-sharing + // (but with the perk of typed trees) + def TRUE_typed = LIT(true) setType ConstantType(Constant(true)) + def FALSE_typed = LIT(false) setType ConstantType(Constant(false)) + object WILD { def empty = Ident(nme.WILDCARD) def apply(tpe: Type) = Ident(nme.WILDCARD) setType tpe diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 5452d13ef0..bbc36e01c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -258,9 +258,9 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => * @arg patBinder symbol used to refer to the result of the previous pattern's extractor (will later be replaced by the outer tree with the correct tree to refer to that patterns result) */ def unapply(tree: Tree): Option[(Symbol, Type)] = tree match { - case Bound(subpatBinder, typed@Typed(expr, tpt)) => Some((subpatBinder, typed.tpe)) - case Bind(_, typed@Typed(expr, tpt)) => Some((patBinder, typed.tpe)) - case Typed(expr, tpt) => Some((patBinder, tree.tpe)) + case Bound(subpatBinder, typed@Typed(expr, tpt)) if typed.tpe ne null => Some((subpatBinder, typed.tpe)) + case Bind(_, typed@Typed(expr, tpt)) if typed.tpe ne null => Some((patBinder, typed.tpe)) + case Typed(expr, tpt) if tree.tpe ne null => Some((patBinder, tree.tpe)) case _ => None } } @@ -918,10 +918,10 @@ class Foo(x: Other) { x._1 } // no error in this order // one alternative may still generate multiple trees (e.g., an extractor call + equality test) // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers val combinedAlts = altss map (altTreeMakers => - ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE_typed)))(casegen)) ) - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE)) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE_typed)) codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) } } @@ -1643,7 +1643,7 @@ class Foo(x: Other) { x._1 } // no error in this order def caseDef(mkCase: Casegen => Tree): Tree = { val currCase = nextCase nextCase = newCaseSym - val casegen = new OptimizedCasegen(matchEnd, nextCase) + val casegen = new OptimizedCasegen(matchEnd, nextCase, restpe) LabelDef(currCase, Nil, mkCase(casegen)) } @@ -1667,14 +1667,14 @@ class Foo(x: Other) { x._1 } // no error in this order ) } - class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol) extends CommonCodegen with Casegen { + class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol, restpe: Type) extends CommonCodegen with Casegen { def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, matchFailGen) // only used to wrap the RHS of a body // res: T // returns MatchMonad[T] - def one(res: Tree): Tree = matchEnd APPLY (res) + def one(res: Tree): Tree = matchEnd APPLY (_asInstanceOf(res, restpe)) // need cast for GADT magic protected def zero: Tree = nextCase APPLY () // prev: MatchMonad[T] @@ -1713,7 +1713,7 @@ class Foo(x: Other) { x._1 } // no error in this order def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = ifThenElseZero(cond, BLOCK( - condSym === TRUE, + condSym === TRUE_typed, nextBinder === res, next )) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index eecf36487c..95a1eafae2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2134,11 +2134,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => - val caseTyped = newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) - if (opt.virtPatmat) { - val tpPacked = packedType(caseTyped, context.owner) - caseTyped setType tpPacked - } else caseTyped + newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) } def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) @@ -2151,7 +2147,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) val selectorTp = packCaptured(selector1.tpe.widen) val casesTyped = typedCases(cases, selectorTp, resTp) - val (ownType, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(casesTyped map (_.tpe.deconst)) + val caseTypes = casesTyped map (c => packedType(c, context.owner).deconst) + val (ownType0, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(caseTypes) + val ownType = ownType0.skolemizeExistential(context.owner, context.tree) val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, ownType)) // val (owntype0, needAdapt) = ptOrLub(casesTyped map (x => repackExistential(x.tpe))) // val owntype = elimAnonymousClass(owntype0) @@ -2205,7 +2203,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { import CODE._ // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up - val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => TRUE).duplicate) else Nil + val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => TRUE_typed).duplicate) else Nil val applyMethod = { // rig the show so we can get started typing the method body -- later we'll correct the infos... @@ -2227,7 +2225,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { anonClass setInfo ClassInfoType(parents, newScope, anonClass) methodSym setInfoAndEnter MethodType(paramSyms, resTp) - val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, if (isPartial) Some(scrut => (funThis DOT nme.missingCase) (scrut)) else None) + // use apply's parameter since the scrut's type has been widened + def missingCase(scrut_ignored: Tree) = (funThis DOT nme.missingCase) (REF(paramSyms.head)) + + val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, if (isPartial) Some(missingCase) else None) DefDef(methodSym, body) } @@ -2240,7 +2241,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe) val (selector1, selectorTp, casesAdapted, resTp, doTranslation) = methodBodyTyper.prepareTranslateMatch(selector, casesTrue, mode, BooleanClass.tpe) - val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE)) + val body = methodBodyTyper.translateMatch(selector1, selectorTp, casesAdapted, resTp, doTranslation, Some(scrutinee => FALSE_typed)) DefDef(methodSym, body) } @@ -3537,8 +3538,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { lazy val thenTp = packedType(thenp1, context.owner) lazy val elseTp = packedType(elsep1, context.owner) val (owntype, needAdapt) = - // virtpatmat needs more aggressive unification of skolemized types, but lub is not robust enough --> middle ground - if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenTp, true) // this breaks src/library/scala/collection/immutable/TrieIterator.scala + // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway + // in the special (though common) case where the types are equal, it pays to pack before comparing + // especially virtpatmat needs more aggressive unification of skolemized types + // this breaks src/library/scala/collection/immutable/TrieIterator.scala + if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenp1.tpe, false) // use unpacked type // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala else ptOrLub(List(thenp1.tpe, elsep1.tpe)) |