diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/patmat')
9 files changed, 69 insertions, 69 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 40fcceb0bf..db6eac34cb 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._ @@ -184,8 +184,8 @@ trait Logic extends Debugging { // push negation inside formula def negationNormalFormNot(p: Prop): Prop = p match { - case And(ops) => Or(ops.map(negationNormalFormNot)) // De'Morgan - case Or(ops) => And(ops.map(negationNormalFormNot)) // De'Morgan + case And(ops) => Or(ops.map(negationNormalFormNot)) // De Morgan + case Or(ops) => And(ops.map(negationNormalFormNot)) // De Morgan case Not(p) => negationNormalForm(p) case True => False case False => True @@ -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._ @@ -682,7 +682,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = { def freshExistentialSubtype(tp: Type): Type = { // SI-8611 tp.narrow is tempting, but unsuitable. See `testRefinedTypeSI8611` for an explanation. - NoSymbol.freshExistential("").setInfo(TypeBounds.upper(tp)).tpe + NoSymbol.freshExistential("", 0).setInfo(TypeBounds.upper(tp)).tpe } if (!t.symbol.isStable) { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index c71299b893..b6978f37df 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 @@ -142,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)) } @@ -177,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)))) @@ -350,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))) } @@ -711,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 @@ -738,7 +736,7 @@ trait MatchAnalysis extends MatchApproximation { if (expanded.isEmpty) { List(varAssignment) } else { - // we need the cartesian product here, + // we need the Cartesian product here, // since we want to report all missing cases // (i.e., combinations) val cartesianProd = expanded.reduceLeft((xs, ys) => 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 f827043094..dc0a457be7 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 bf3bc6b26e..39971590c7 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 @@ -117,7 +116,7 @@ trait MatchTranslation { val makers = { val paramType = extractor.aligner.wholeType // Statically conforms to paramType - if (this ensureConformsTo paramType) treeMaker(binder, false, pos) :: Nil + if (tpe <:< paramType) treeMaker(binder, false, pos) :: Nil else { // chain a type-testing extractor before the actual extractor call // it tests the type, checks the outer pointer and casts to the expected type @@ -167,16 +166,6 @@ trait MatchTranslation { setVarInfo(binder, paramType) true } - // If <:< but not =:=, no type test needed, but the tree maker relies on the binder having - // exactly paramType (and not just some type compatible with it.) SI-6624 shows this is necessary - // because apparently patBinder may have an unfortunate type (.decls don't have the case field - // accessors) TODO: get to the bottom of this -- I assume it happens when type checking - // infers a weird type for an unapply call. By going back to the parameterType for the - // extractor call we get a saner type, so let's just do that for now. - def ensureConformsTo(paramType: Type): Boolean = ( - (tpe =:= paramType) - || (tpe <:< paramType) && setInfo(paramType) - ) private def concreteType = tpe.bounds.hi private def unbound = unbind(tree) @@ -401,7 +390,6 @@ trait MatchTranslation { /** Create the TreeMaker that embodies this extractor call * - * `binder` has been casted to `paramType` if necessary * `binderKnownNonNull` indicates whether the cast implies `binder` cannot be null * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder */ @@ -507,7 +495,7 @@ trait MatchTranslation { * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder */ def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = { - val paramAccessors = binder.constrParamAccessors + val paramAccessors = aligner.wholeType.typeSymbol.constrParamAccessors val numParams = paramAccessors.length def paramAccessorAt(subPatIndex: Int) = paramAccessors(math.min(subPatIndex, numParams - 1)) // binders corresponding to mutable fields should be stored (SI-5158, SI-6070) @@ -536,7 +524,7 @@ trait MatchTranslation { // 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 = { - val accessors = binder.caseFieldAccessors + val accessors = aligner.wholeType.typeSymbol.caseFieldAccessors if (accessors isDefinedAt (i-1)) gen.mkAttributedStableRef(binder) DOT accessors(i-1) else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index 3ace61411f..794d3d442a 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,15 +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 = { + override def withOuterTest(orig: Tree)(testedBinder: Symbol, expectedTp: Type): Tree = { val expectedPrefix = expectedTp.prefix - if (expectedPrefix eq NoType) mkTRUE // fallback for SI-6183 - else { - // 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 outerFor = expectedTp.typeSymbol - val outerMarker = outerFor.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedPrefix - Select(codegen._asInstanceOf(testedBinder, expectedTp), outerMarker) OBJ_EQ gen.mkAttributedQualifier(expectedPrefix) + 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) + } + + if ((expectedPrefix eq NoPrefix) + || expectedTp.typeSymbol.isJava + || 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) } } } @@ -354,7 +373,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 @@ -366,7 +384,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 @@ -403,12 +420,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 @@ -427,12 +438,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. @@ -483,7 +493,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)) } @@ -554,11 +564,11 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { else scrut match { case Typed(tree, tpt) => val suppressExhaustive = tpt.tpe hasAnnotation UncheckedClass - val supressUnreachable = tree match { + val suppressUnreachable = tree match { case Ident(name) if name startsWith nme.CHECK_IF_REFUTABLE_STRING => true // SI-7183 don't warn for withFilter's that turn out to be irrefutable. case _ => false } - val suppression = Suppression(suppressExhaustive, supressUnreachable) + val suppression = Suppression(suppressExhaustive, suppressUnreachable) val hasSwitchAnnotation = treeInfo.isSwitchAnnotation(tpt.tpe) // matches with two or fewer cases need not apply for switchiness (if-then-else will do) // `case 1 | 2` is considered as two cases. 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) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala index d4f44303bb..2c1fb064cc 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala @@ -148,7 +148,7 @@ trait ScalacPatternExpanders { val tupled = extractor.asSinglePattern if (effectivePatternArity(args) == 1 && isTupleType(extractor.typeOfSinglePattern)) { val sym = sel.symbol.owner - currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)") + currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)", "2.11.0") } tupled } else extractor |