diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/patmat')
8 files changed, 69 insertions, 57 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 91777b34d5..4ae97ce281 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -8,9 +8,9 @@ package scala package tools.nsc.transform.patmat import scala.language.postfixOps + import scala.collection.mutable import scala.reflect.internal.util.{NoPosition, Position, Statistics, HashSet} -import scala.tools.nsc.Global trait Logic extends Debugging { import PatternMatchingStats._ @@ -646,7 +646,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { } - import global.{ConstantType, Constant, EmptyScope, SingletonType, Literal, Ident, refinedType, singleType, TypeBounds, NoSymbol} + import global.{ConstantType, SingletonType, Literal, Ident, singleType, TypeBounds, NoSymbol} import global.definitions._ @@ -691,7 +691,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { // if X is mutable. freshExistentialSubtype(t.tpe) } - else trees find (a => a.correspondsStructure(t)(sameValue)) match { + else trees find (a => equivalentTree(a, t)) match { case Some(orig) => debug.patmat("unique tp for tree: " + ((orig, orig.tpe))) orig.tpe diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 1331eb6993..ec493b9507 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -6,9 +6,6 @@ package scala.tools.nsc.transform.patmat -import scala.annotation.tailrec -import scala.collection.immutable.{IndexedSeq, Iterable} -import scala.language.postfixOps import scala.collection.mutable import scala.reflect.internal.util.Statistics @@ -85,11 +82,15 @@ trait TreeAndTypeAnalysis extends Debugging { tp <:< tpImpliedNormalizedToAny } - // TODO: improve, e.g., for constants - def sameValue(a: Tree, b: Tree): Boolean = (a eq b) || ((a, b) match { - case (_ : Ident, _ : Ident) => a.symbol eq b.symbol - case _ => false - }) + def equivalentTree(a: Tree, b: Tree): Boolean = (a, b) match { + case (Select(qual1, _), Select(qual2, _)) => equivalentTree(qual1, qual2) && a.symbol == b.symbol + case (Ident(_), Ident(_)) => a.symbol == b.symbol + case (Literal(c1), Literal(c2)) => c1 == c2 + case (This(_), This(_)) => a.symbol == b.symbol + case (Apply(fun1, args1), Apply(fun2, args2)) => equivalentTree(fun1, fun2) && args1.corresponds(args2)(equivalentTree) + // Those are the only cases we need to handle in the pattern matcher + case _ => false + } trait CheckableTreeAndTypeAnalysis { val typer: Typer @@ -138,7 +139,7 @@ trait TreeAndTypeAnalysis extends Debugging { if(grouped) { def enumerateChildren(sym: Symbol) = { - sym.children.toList + sym.sealedChildren.toList .sortBy(_.sealedSortName) .filterNot(x => x.isSealed && x.isAbstractClass && !isPrimitiveValueClass(x)) } @@ -173,6 +174,8 @@ trait TreeAndTypeAnalysis extends Debugging { filterChildren(subclasses) }) } + case sym if sym.isCase => + List(List(tp)) case sym => debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym)))) @@ -277,7 +280,7 @@ trait MatchApproximation extends TreeAndTypeAnalysis with ScalaLogic with MatchT // hashconsing trees (modulo value-equality) def unique(t: Tree, tpOverride: Type = NoType): Tree = - trees find (a => a.correspondsStructure(t)(sameValue)) match { + trees find (a => equivalentTree(a, t)) match { case Some(orig) => // debug.patmat("unique: "+ (t eq orig, orig)) orig @@ -346,7 +349,7 @@ trait MatchApproximation extends TreeAndTypeAnalysis with ScalaLogic with MatchT object condStrategy extends TypeTestTreeMaker.TypeTestCondStrategy { type Result = Prop def and(a: Result, b: Result) = And(a, b) - def outerTest(testedBinder: Symbol, expectedTp: Type) = True // TODO OuterEqProp(testedBinder, expectedType) + def withOuterTest(testedBinder: Symbol, expectedTp: Type) = True // TODO OuterEqProp(testedBinder, expectedType) def typeTest(b: Symbol, pt: Type) = { // a type test implies the tested path is non-null (null.isInstanceOf[T] is false for all T) val p = binderToUniqueTree(b); And(uniqueNonNullProp(p), uniqueTypeProp(p, uniqueTp(pt))) } @@ -707,9 +710,8 @@ trait MatchAnalysis extends MatchApproximation { val (equal, notEqual) = varAssignment.getOrElse(variable, Nil -> Nil) - def addVarAssignment(equalTo: List[Const], notEqualTo: List[Const]) = { - Map(variable ->(equal ++ equalTo, notEqual ++ notEqualTo)) - } + def addVarAssignment(equalTo: List[Const], notEqualTo: List[Const]) = + Map(variable ->((equal ++ equalTo, notEqual ++ notEqualTo))) // this assignment is needed in case that // there exists already an assign diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala index 1642613b9b..03d0a28fb1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala @@ -6,9 +6,9 @@ package scala.tools.nsc.transform.patmat -import scala.tools.nsc.symtab.Flags.SYNTHETIC import scala.language.postfixOps -import scala.reflect.internal.util.Statistics + +import scala.tools.nsc.symtab.Flags.SYNTHETIC import scala.reflect.internal.util.Position /** Factory methods used by TreeMakers to make the actual trees. @@ -55,7 +55,15 @@ trait MatchCodeGen extends Interface { def flatMap(prev: Tree, b: Symbol, next: Tree): Tree def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree def flatMapGuard(cond: Tree, next: Tree): Tree - def ifThenElseZero(c: Tree, thenp: Tree): Tree = IF (c) THEN thenp ELSE zero + def ifThenElseZero(c: Tree, thenp: Tree): Tree = { + val z = zero + thenp match { + case If(c1, thenp1, elsep1) if z equalsStructure elsep1 => + If(c AND c1, thenp1, elsep1) // cleaner, leaner trees + case _ => + If(c, thenp, zero) + } + } protected def zero: Tree } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index cca8d2dbb8..6fdaa79c10 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -6,10 +6,10 @@ package scala.tools.nsc.transform.patmat -import scala.tools.nsc.symtab.Flags.MUTABLE import scala.language.postfixOps + +import scala.tools.nsc.symtab.Flags.MUTABLE import scala.collection.mutable -import scala.reflect.internal.util.Statistics import scala.reflect.internal.util.Position /** Optimize and analyze matches based on their TreeMaker-representation. diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index a2afb76b0e..e12b8548a8 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -7,7 +7,7 @@ package scala.tools.nsc.transform.patmat import scala.language.postfixOps -import scala.collection.mutable + import scala.reflect.internal.util.Statistics /** Translate typed Trees that represent pattern matches into the patternmatching IR, defined by TreeMakers. @@ -18,8 +18,7 @@ trait MatchTranslation { import PatternMatchingStats._ import global._ import definitions._ - import global.analyzer.{ErrorUtils, formalTypes} - import treeInfo.{ WildcardStarArg, Unapplied, isStar, unbind } + import treeInfo.{ Unapplied, unbind } import CODE._ // Always map repeated params to sequences diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index e0fcc05de2..8c59ced28f 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -6,10 +6,10 @@ package scala.tools.nsc.transform.patmat -import scala.tools.nsc.symtab.Flags.{SYNTHETIC, ARTIFACT} import scala.language.postfixOps + +import scala.tools.nsc.symtab.Flags.{SYNTHETIC, ARTIFACT} import scala.collection.mutable -import scala.reflect.internal.util.Statistics import scala.reflect.internal.util.Position /** Translate our IR (TreeMakers) into actual Scala Trees using the factory methods in MatchCodeGen. @@ -101,7 +101,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { val pos = NoPosition - val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder)) + val localSubstitution = Substitution(prevBinder, gen.mkAttributedStableRef(nextBinder)) def chainBefore(next: Tree)(casegen: Casegen): Tree = substitution(next) override def toString = "S"+ localSubstitution } @@ -118,7 +118,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { val res: Tree lazy val nextBinder = freshSym(pos, nextBinderTp) - lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) + lazy val localSubstitution = Substitution(List(prevBinder), List(gen.mkAttributedStableRef(nextBinder))) def chainBefore(next: Tree)(casegen: Casegen): Tree = atPos(pos)(casegen.flatMapCond(cond, res, nextBinder, substitution(next))) @@ -316,7 +316,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { trait TypeTestCondStrategy { type Result - def outerTest(testedBinder: Symbol, expectedTp: Type): Result + def withOuterTest(orig: Result)(testedBinder: Symbol, expectedTp: Type): Result = orig // TODO: can probably always widen def typeTest(testedBinder: Symbol, expectedTp: Type): Result def nonNullTest(testedBinder: Symbol): Result @@ -336,18 +336,34 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) def eqTest(pat: Tree, testedBinder: Symbol) = REF(testedBinder) OBJ_EQ pat - def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = { - val expectedOuter = expectedTp.prefix match { - case ThisType(clazz) => This(clazz) - case NoType => mkTRUE // fallback for SI-6183 - case pre => REF(pre.prefix, pre.termSymbol) + override def withOuterTest(orig: Tree)(testedBinder: Symbol, expectedTp: Type): Tree = { + val expectedPrefix = expectedTp.prefix + val testedPrefix = testedBinder.info.prefix + + // Check if a type is defined in a static location. Unlike `tp.isStatic` before `flatten`, + // this also includes methods and (possibly nested) objects inside of methods. + def definedInStaticLocation(tp: Type): Boolean = { + def isStatic(tp: Type): Boolean = + if (tp == NoType || tp.typeSymbol.isPackageClass || tp == NoPrefix) true + else if (tp.typeSymbol.isModuleClass) isStatic(tp.prefix) + else false + tp.typeSymbol.owner == tp.prefix.typeSymbol && isStatic(tp.prefix) } - // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` - // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix - - (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter + if ((expectedPrefix eq NoPrefix) + || definedInStaticLocation(expectedTp) + || testedPrefix =:= expectedPrefix) orig + else gen.mkAttributedQualifierIfPossible(expectedPrefix) match { + case None => orig + case Some(expectedOuterRef) => + // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` + // by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` + // if there's an outer accessor, otherwise the condition becomes `true` + // TODO: centralize logic whether there's an outer accessor and use here? + val synthOuterGetter = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedPrefix + val outerTest = (Select(codegen._asInstanceOf(testedBinder, expectedTp), synthOuterGetter)) OBJ_EQ expectedOuterRef + and(orig, outerTest) + } } } @@ -356,7 +372,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def typeTest(testedBinder: Symbol, expectedTp: Type): Result = true - def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false def nonNullTest(testedBinder: Symbol): Result = false def equalsTest(pat: Tree, testedBinder: Symbol): Result = false def eqTest(pat: Tree, testedBinder: Symbol): Result = false @@ -368,7 +383,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { type Result = Boolean def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder - def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null @@ -405,12 +419,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { import TypeTestTreeMaker._ debug.patmat("TTTM"+((prevBinder, extractorArgTypeTest, testedBinder, expectedTp, nextBinderTp))) - lazy val outerTestNeeded = ( - (expectedTp.prefix ne NoPrefix) - && !expectedTp.prefix.typeSymbol.isPackageClass - && needsOuterTest(expectedTp, testedBinder.info, matchOwner) - ) - // the logic to generate the run-time test that follows from the fact that // a `prevBinder` is expected to have type `expectedTp` // the actual tree-generation logic is factored out, since the analyses generate Cond(ition)s rather than Trees @@ -429,12 +437,11 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def isExpectedPrimitiveType = isAsExpected && isPrimitiveValueType(expectedTp) def isExpectedReferenceType = isAsExpected && (expectedTp <:< AnyRefTpe) def mkNullTest = nonNullTest(testedBinder) - def mkOuterTest = outerTest(testedBinder, expectedTp) def mkTypeTest = typeTest(testedBinder, expectedWide) def mkEqualsTest(lhs: Tree): cs.Result = equalsTest(lhs, testedBinder) def mkEqTest(lhs: Tree): cs.Result = eqTest(lhs, testedBinder) - def addOuterTest(res: cs.Result): cs.Result = if (outerTestNeeded) and(res, mkOuterTest) else res + def addOuterTest(res: cs.Result): cs.Result = withOuterTest(res)(testedBinder, expectedTp) // If we conform to expected primitive type: // it cannot be null and cannot have an outer pointer. No further checking. @@ -485,7 +492,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { // NOTE: generate `patTree == patBinder`, since the extractor must be in control of the equals method (also, patBinder may be null) // equals need not be well-behaved, so don't intersect with pattern's (stabilized) type (unlike MaybeBoundTyped's accumType, where it's required) val cond = codegen._equals(patTree, prevBinder) - val res = CODE.REF(prevBinder) + val res = gen.mkAttributedStableRef(prevBinder) override def toString = "ET"+((prevBinder.name, patTree)) } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala index 8beb1837ad..3f27d18e64 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala @@ -6,10 +6,6 @@ package scala.tools.nsc.transform.patmat -import scala.language.postfixOps -import scala.collection.mutable -import scala.reflect.internal.util.Statistics - trait MatchWarnings { self: PatternMatching => @@ -83,4 +79,4 @@ trait MatchWarnings { } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index b2f2516b5b..05f2d60be1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -222,7 +222,7 @@ trait Interface extends ast.TreeDSL { object substIdentsForTrees extends Transformer { private def typedIfOrigTyped(to: Tree, origTp: Type): Tree = if (origTp == null || origTp == NoType) to - // important: only type when actually substing and when original tree was typed + // important: only type when actually substituting and when original tree was typed // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors) else typer.typed(to) |