diff options
author | Paul Phillips <paulp@improving.org> | 2013-02-10 10:55:47 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-02-10 15:56:52 -0800 |
commit | 22d315d61b11e95c3a18e1285ca2131f614e13fb (patch) | |
tree | e17d4eab194b45613ca3d5136bad553963a11ca6 /src | |
parent | ccf6bc7e860cf87cbba5bcf386bcf2d0cbfa8ddd (diff) | |
parent | db5919a7d3b18be94e79899c2f7e33c535e15a27 (diff) | |
download | scala-22d315d61b11e95c3a18e1285ca2131f614e13fb.tar.gz scala-22d315d61b11e95c3a18e1285ca2131f614e13fb.tar.bz2 scala-22d315d61b11e95c3a18e1285ca2131f614e13fb.zip |
Merge remote-tracking branch 'origin/2.10.x' into merge-210
* origin/2.10.x:
Fix for paramaccessor alias regression.
Expanded bytecode testing code.
SI-5675 Discard duplicate feature warnings at a position
accommodates pull request feedback
term and type reftrees are now reified uniformly
SI-6591 Reify and path-dependent types
SI-7096 SubstSymMap copies trees before modifying their symbols
SI-6961 no structural sharing in list serialization
SI-6187 Make partial functions re-typable
[backport] SI-6478 Fixing JavaTokenParser ident
SI-7100 Fixed infinite recursion in duplicators
SI-6146 More accurate prefixes for sealed subtypes.
SI-5082 Cycle avoidance between case companions
SI-6113 typeOf now works for type lambdas
SI-5824 Fix crashes in reify with _*
SI-7026: parseTree should never return a typed one
SI-7070 Turn restriction on companions in pkg objs into warning
Conflicts:
src/compiler/scala/reflect/reify/codegen/GenSymbols.scala
src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
src/compiler/scala/tools/nsc/typechecker/Typers.scala
src/compiler/scala/tools/reflect/ToolBoxFactory.scala
src/library/scala/collection/immutable/List.scala
src/reflect/scala/reflect/internal/TreeInfo.scala
src/reflect/scala/reflect/internal/Types.scala
src/reflect/scala/reflect/internal/settings/MutableSettings.scala
src/reflect/scala/reflect/runtime/Settings.scala
test/files/buildmanager/t2650_1/t2650_1.check
test/files/buildmanager/t2657/t2657.check
test/files/neg/t3234.check
test/files/run/idempotency-this.check
test/files/run/macro-typecheck-macrosdisabled2.check
test/files/run/showraw_tree.check
test/files/run/showraw_tree_ids.check
test/files/run/showraw_tree_kinds.check
test/files/run/showraw_tree_types_ids.check
test/files/run/showraw_tree_types_typed.check
test/files/run/showraw_tree_types_untyped.check
test/files/run/showraw_tree_ultimate.check
test/files/run/t2886.check
test/files/run/t5225_2.check
test/files/run/t5374.check
test/files/run/t5374.scala
test/files/run/t6329_repl.check
test/files/run/toolbox_typecheck_macrosdisabled2.check
Diffstat (limited to 'src')
27 files changed, 443 insertions, 137 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 731aab93b8..67bc93d407 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -1,6 +1,8 @@ package scala.reflect.reify package codegen +import scala.reflect.internal.Flags._ + trait GenSymbols { self: Reifier => @@ -99,6 +101,33 @@ trait GenSymbols { reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") val name = newTermName("" + nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) + // We need to note whether the free value being reified is stable or not to guide subsequent reflective compilation. + // Here's why reflection compilation needs our help. + // + // When dealing with a tree, which contain free values, toolboxes extract those and wrap the entire tree in a Function + // having parameters defined for every free values in the tree. For example, evaluating + // + // Ident(setTypeSignature(newFreeTerm("x", 2), <Int>)) + // + // Will generate something like + // + // object wrapper { + // def wrapper(x: () => Int) = { + // x() + // } + // } + // + // Note that free values get transformed into, effectively, by-name parameters. This is done to make sure + // that evaluation order is kept intact. And indeed, we cannot just evaluate all free values at once in order + // to obtain arguments for wrapper.wrapper, because if some of the free values end up being unused during evaluation, + // we might end up doing unnecessary calculations. + // + // So far, so good - we didn't need any flags at all. However, if the code being reified contains path-dependent types, + // we're in trouble, because valid code like `free.T` ends up being transformed into `free.apply().T`, which won't compile. + // + // To overcome this glitch, we note whether a given free term is stable or not (because vars can also end up being free terms). + // Then, if a free term is stable, we tell the compiler to treat `free.apply()` specially and assume that it's stable. + if (!sym.isMutable) sym setFlag STABLE if (sym.isCapturedVariable) { assert(binding.isInstanceOf[Ident], showRaw(binding)) val capturedBinding = referenceCapturedVariable(sym) diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index f60089c935..df2eeaa932 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -155,21 +155,23 @@ trait GenTrees { else mirrorCall(nme.Ident, reify(name)) case Select(qual, name) => - if (sym == NoSymbol || sym.name == name) - reifyProduct(tree) - else - reifyProduct(Select(qual, sym.name)) + if (qual.symbol != null && qual.symbol.isPackage) { + mirrorBuildCall(nme.Ident, reify(sym)) + } else { + val effectiveName = if (sym != null && sym != NoSymbol) sym.name else name + reifyProduct(Select(qual, effectiveName)) + } case _ => throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) } } - private def reifyBoundType(tree: Tree): Tree = { + private def reifyBoundType(tree: RefTree): Tree = { val sym = tree.symbol val tpe = tree.tpe - def reifyBoundType(tree: Tree): Tree = { + def reifyBoundType(tree: RefTree): Tree = { assert(tpe != null, "unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) // if a symbol or a type of the scrutinee are local to reifee @@ -177,7 +179,7 @@ trait GenTrees { // then we can reify the scrutinee as a symless AST and that will definitely be hygienic // why? because then typechecking of a scrutinee doesn't depend on the environment external to the quasiquote // otherwise we need to reify the corresponding type - if (sym.isLocalToReifee || tpe.isLocalToReifee) + if (sym.isLocalToReifee || tpe.isLocalToReifee || treeInfo.isWildcardStarType(tree)) reifyProduct(tree) else { if (reifyDebug) println("reifying bound type %s (underlying type is %s)".format(sym, tpe)) @@ -198,13 +200,19 @@ trait GenTrees { mirrorBuildCall(nme.TypeTree, spliced) } } - else if (sym.isLocatable) { - if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) - mirrorBuildCall(nme.Ident, reify(sym)) - } - else { - if (reifyDebug) println("tpe is not locatable: reify as TypeTree(%s)".format(tpe)) - mirrorBuildCall(nme.TypeTree, reify(tpe)) + else tree match { + case Select(qual, name) if !qual.symbol.isPackage => + if (reifyDebug) println(s"reifying Select($qual, $name)") + mirrorCall(nme.Select, reify(qual), reify(name)) + case SelectFromTypeTree(qual, name) => + if (reifyDebug) println(s"reifying SelectFromTypeTree($qual, $name)") + mirrorCall(nme.SelectFromTypeTree, reify(qual), reify(name)) + case _ if sym.isLocatable => + if (reifyDebug) println(s"tpe is locatable: reify as Ident($sym)") + mirrorBuildCall(nme.Ident, reify(sym)) + case _ => + if (reifyDebug) println(s"tpe is not locatable: reify as TypeTree($tpe)") + mirrorBuildCall(nme.TypeTree, reify(tpe)) } } } diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index ca44938f50..2370f18e3a 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -69,8 +69,7 @@ trait GenTypes { def reificationIsConcrete: Boolean = state.reificationIsConcrete def spliceType(tpe: Type): Tree = { - val quantified = currentQuantified - if (tpe.isSpliceable && !(quantified contains tpe.typeSymbol)) { + if (tpe.isSpliceable && !(boundSymbolsInCallstack contains tpe.typeSymbol)) { if (reifyDebug) println("splicing " + tpe) val tagFlavor = if (concrete) tpnme.TypeTag.toString else tpnme.WeakTypeTag.toString diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index 2741785752..eda4cba2bf 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -26,7 +26,10 @@ trait Reify extends GenSymbols finally currents = currents.tail } } - def currentQuantified = flatCollect(reifyStack.currents)({ case ExistentialType(quantified, _) => quantified }) + def boundSymbolsInCallstack = flatCollect(reifyStack.currents) { + case ExistentialType(quantified, _) => quantified + case PolyType(typeParams, _) => typeParams + } def current = reifyStack.currents.head def currents = reifyStack.currents diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index 5dd5f08b45..71fe4ddeea 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -187,8 +187,12 @@ trait Reshape { } private def toPreTyperTypedOrAnnotated(tree: Tree): Tree = tree match { - case ty @ Typed(expr1, tt @ TypeTree()) => + case ty @ Typed(expr1, tpt) => if (reifyDebug) println("reify typed: " + tree) + val original = tpt match { + case tt @ TypeTree() => tt.original + case tpt => tpt + } val annotatedArg = { def loop(tree: Tree): Tree = tree match { case annotated1 @ Annotated(ann, annotated2 @ Annotated(_, _)) => loop(annotated2) @@ -196,15 +200,15 @@ trait Reshape { case _ => EmptyTree } - loop(tt.original) + loop(original) } if (annotatedArg != EmptyTree) { if (annotatedArg.isType) { if (reifyDebug) println("verdict: was an annotated type, reify as usual") ty } else { - if (reifyDebug) println("verdict: was an annotated value, equivalent is " + tt.original) - toPreTyperTypedOrAnnotated(tt.original) + if (reifyDebug) println("verdict: was an annotated value, equivalent is " + original) + toPreTyperTypedOrAnnotated(original) } } else { if (reifyDebug) println("verdict: wasn't annotated, reify as usual") diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index 254cb02ee6..d57188bf6e 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -263,12 +263,12 @@ trait Extractors { } object BoundType { - def unapply(tree: Tree): Option[Tree] = tree match { - case Select(_, name) if name.isTypeName => + def unapply(tree: Tree): Option[RefTree] = tree match { + case tree @ Select(_, name) if name.isTypeName => Some(tree) - case SelectFromTypeTree(_, name) if name.isTypeName => + case tree @ SelectFromTypeTree(_, _) => Some(tree) - case Ident(name) if name.isTypeName => + case tree @ Ident(name) if name.isTypeName => Some(tree) case _ => None diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index d751669612..466f5eb17c 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1107,13 +1107,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Collects for certain classes of warnings during this run. */ class ConditionalWarning(what: String, option: Settings#BooleanSetting) { - val warnings = new mutable.ListBuffer[(Position, String)] + val warnings = mutable.LinkedHashMap[Position, String]() def warn(pos: Position, msg: String) = if (option.value) reporter.warning(pos, msg) - else warnings += ((pos, msg)) + else if (!(warnings contains pos)) warnings += ((pos, msg)) def summarize() = - if (option.isDefault && warnings.nonEmpty) - reporter.warning(NoPosition, "there were %d %s warnings; re-run with %s for details".format(warnings.size, what, option.name)) + if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings.value)) + warning("there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name)) } def newUnitParser(code: String) = new syntaxAnalyzer.UnitParser(newCompilationUnit(code)) diff --git a/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala b/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala index 5e68152936..d4777d7800 100644 --- a/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala +++ b/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala @@ -12,7 +12,7 @@ import typechecker.Analyzer import scala.reflect.internal.util.BatchSourceFile trait ScaladocAnalyzer extends Analyzer { - val global : ScaladocGlobal + val global : Global // generally, a ScaladocGlobal import global._ override def newTyper(context: Context): ScaladocTyper = new ScaladocTyper(context) diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index cf38c629fc..f10c00b8e0 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -256,15 +256,12 @@ trait CompilerControl { self: Global => } /** Returns parse tree for source `source`. No symbols are entered. Syntax errors are reported. - * Can be called asynchronously from presentation compiler. + * + * This method is thread-safe and as such can safely run outside of the presentation + * compiler thread. */ - def parseTree(source: SourceFile): Tree = ask { () => - getUnit(source) match { - case Some(unit) if unit.status >= JustParsed => - unit.body - case _ => - new UnitParser(new CompilationUnit(source)).parse() - } + def parseTree(source: SourceFile): Tree = { + new UnitParser(new CompilationUnit(source)).parse() } /** Asks for a computation to be done quickly on the presentation compiler thread */ diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 31208d2ad7..9bb2d552be 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -221,7 +221,10 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") /** Called from parser, which signals hereby that a method definition has been parsed. */ override def signalParseProgress(pos: Position) { - checkForMoreWork(pos) + // We only want to be interruptible when running on the PC thread. + if(onCompilerThread) { + checkForMoreWork(pos) + } } /** Called from typechecker, which signals hereby that a node has been completely typechecked. @@ -408,7 +411,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") */ @elidable(elidable.WARNING) override def assertCorrectThread() { - assert(initializing || (Thread.currentThread() eq compileRunner), + assert(initializing || onCompilerThread, "Race condition detected: You are running a presentation compiler method outside the PC thread.[phase: %s]".format(globalPhase) + " Please file a ticket with the current stack trace at https://www.assembla.com/spaces/scala-ide/support/tickets") } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 9fe3016c02..a5496f829d 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -170,7 +170,6 @@ trait ScalaSettings extends AbsScalaSettings val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.") val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "") val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") - val companionsInPkgObjs = BooleanSetting("-Ycompanions-in-pkg-objs", "Allow companion objects and case classes in package objects. See issue SI-5954.") val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index ab65e18093..face149b9f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -352,7 +352,7 @@ abstract class Duplicators extends Analyzer { cases } - super.typedPos(tree.pos, mode, pt)(Match(scrut, cases1)) + super.typed(atPos(tree.pos)(Match(scrut, cases1)), mode, pt) case EmptyTree => // no need to do anything, in particular, don't set the type to null, EmptyTree.tpe_= asserts diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index b2eb7c9a75..bbbc5cbb18 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -105,9 +105,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL import definitions._ import analyzer._ //Typer - - case class DefaultOverrideMatchAttachment(default: Tree) - object vpmName { val one = newTermName("one") val drop = newTermName("drop") @@ -217,11 +214,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // However this is a pain (at least the way I'm going about it) // and I have to think these detailed errors are primarily useful // for beginners, not people writing nested pattern matches. - def checkMatchVariablePatterns(m: Match) { + def checkMatchVariablePatterns(cases: List[CaseDef]) { // A string describing the first variable pattern var vpat: String = null // Using an iterator so we can recognize the last case - val it = m.cases.iterator + val it = cases.iterator def addendum(pat: Tree) = { matchingSymbolInScope(pat) match { @@ -264,7 +261,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def translateMatch(match_ : Match): Tree = { val Match(selector, cases) = match_ - checkMatchVariablePatterns(match_) + + val (nonSyntheticCases, defaultOverride) = cases match { + case init :+ last if treeInfo isSyntheticDefaultCase last => + (init, Some(((scrut: Tree) => last.body))) + case _ => + (cases, None) + } + + checkMatchVariablePatterns(nonSyntheticCases) // we don't transform after uncurry // (that would require more sophistication when generating trees, @@ -291,14 +296,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // pt is the skolemized version val pt = repeatedToSeq(ptUnCPS) - // 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_.attachments.get[DefaultOverrideMatchAttachment].map{case DefaultOverrideMatchAttachment(default) => ((scrut: Tree) => default)} - + // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - val combined = combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride) + val combined = combineCases(selector, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, matchOwner, defaultOverride) if (Statistics.canEnable) Statistics.stopTimer(patmatNanos, start) combined @@ -2814,7 +2816,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // compare to the fully known type `tp` (modulo abstract types), // so that we can rule out stuff like: sealed trait X[T]; class XInt extends X[Int] --> XInt not valid when enumerating X[String] // however, must approximate abstract types in - val subTp = appliedType(pre.memberType(sym), sym.typeParams.map(_ => WildcardType)) + + val memberType = nestedMemberType(sym, pre, tpApprox.typeSymbol.owner) + val subTp = appliedType(memberType, sym.typeParams.map(_ => WildcardType)) val subTpApprox = typer.infer.approximateAbstracts(subTp) // TODO: needed? // patmatDebug("subtp"+(subTpApprox <:< tpApprox, subTpApprox, tpApprox)) if (subTpApprox <:< tpApprox) Some(checkableType(subTp)) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 9949e9181b..a2b0530c26 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -6,6 +6,7 @@ package scala.tools.nsc package typechecker +import scala.collection.{ mutable, immutable } import symtab.Flags._ import scala.collection.mutable.ListBuffer @@ -48,6 +49,10 @@ trait SyntheticMethods extends ast.TreeDSL { else if (clazz.isDerivedValueClass) valueSymbols else Nil } + private lazy val renamedCaseAccessors = perRunCaches.newMap[Symbol, mutable.Map[TermName, TermName]]() + /** Does not force the info of `caseclazz` */ + final def caseAccessorName(caseclazz: Symbol, paramName: TermName) = + (renamedCaseAccessors get caseclazz).fold(paramName)(_(paramName)) /** Add the synthetic methods to case classes. */ @@ -369,6 +374,7 @@ trait SyntheticMethods extends ast.TreeDSL { def isRewrite(sym: Symbol) = sym.isCaseAccessorMethod && !sym.isPublic for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body ; if isRewrite(ddef.symbol)) { + val original = ddef.symbol val newAcc = deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc => newAcc.makePublic newAcc resetFlag (ACCESSOR | PARAMACCESSOR) @@ -377,6 +383,8 @@ trait SyntheticMethods extends ast.TreeDSL { // TODO: shouldn't the next line be: `original resetFlag CASEACCESSOR`? ddef.symbol resetFlag CASEACCESSOR lb += logResult("case accessor new")(newAcc) + val renamedInClassMap = renamedCaseAccessors.getOrElseUpdate(clazz, mutable.Map() withDefault(x => x)) + renamedInClassMap(original.name.toTermName) = newAcc.symbol.name.toTermName } (lb ++= templ.body ++= synthesize()).toList diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6cf9dfdd5b..4ab3d684a2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -31,7 +31,6 @@ trait Typers extends Adaptations with Tags { import global._ import definitions._ import TypersStats._ - import patmat.DefaultOverrideMatchAttachment final def forArgMode(fun: Tree, mode: Mode) = if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode @@ -1838,29 +1837,29 @@ trait Typers extends Adaptations with Tags { // SI-5954. On second compile of a companion class contained in a package object we end up // with some confusion of names which leads to having two symbols with the same name in the - // same owner. Until that can be straightened out we can't allow companion objects in package + // same owner. Until that can be straightened out we will warn on companion objects in package // objects. But this code also tries to be friendly by distinguishing between case classes and // user written companion pairs - def restrictPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) { + def warnPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) { // ignore synthetic objects, because the "companion" object to a case class is synthetic and // we only want one error per case class if (!m.isSynthetic) { // can't handle case classes in package objects - if (m.isCaseClass) pkgObjectRestriction(m, mdef, "case") + if (m.isCaseClass) pkgObjectWarning(m, mdef, "case") // can't handle companion class/object pairs in package objects else if ((m.isClass && m.companionModule != NoSymbol && !m.companionModule.isSynthetic) || (m.isModule && m.companionClass != NoSymbol && !m.companionClass.isSynthetic)) - pkgObjectRestriction(m, mdef, "companion") + pkgObjectWarning(m, mdef, "companion") } - def pkgObjectRestriction(m : Symbol, mdef : ModuleDef, restricted : String) = { + def pkgObjectWarning(m : Symbol, mdef : ModuleDef, restricted : String) = { val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString - context.error(if (m.pos.isDefined) m.pos else mdef.pos, s"implementation restriction: package object ${pkgName} cannot contain ${restricted} ${m}. Instead, ${m} should be placed directly in package ${pkgName}.") + context.warning(if (m.pos.isDefined) m.pos else mdef.pos, s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") } } - if (!settings.companionsInPkgObjs.value && mdef.symbol.isPackageObject) - restrictPackageObjectMembers(mdef) + if (mdef.symbol.isPackageObject) + warnPackageObjectMembers(mdef) treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } @@ -2075,7 +2074,7 @@ trait Typers extends Adaptations with Tags { val alias = ( superAcc.initialize.alias orElse (superAcc getter superAcc.owner) - filter (alias => superClazz.info.nonPrivateMember(alias.name) != alias) + filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias) ) if (alias.exists && !alias.accessed.isVariable) { val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match { @@ -2562,8 +2561,13 @@ trait Typers extends Adaptations with Tags { def mkParam(methodSym: Symbol, tp: Type = argTp) = methodSym.newValueParameter(paramName, paramPos.focus, SYNTHETIC) setInfo tp + def mkDefaultCase(body: Tree) = + atPos(tree.pos.makeTransparent) { + CaseDef(Bind(nme.DEFAULT_CASE, Ident(nme.WILDCARD)), body) + } + // `def applyOrElse[A1 <: $argTp, B1 >: $matchResTp](x: A1, default: A1 => B1): B1 = - // ${`$selector match { $cases }` updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x)))}` + // ${`$selector match { $cases; case default$ => default(x) }` def applyOrElseMethodDef = { val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE) @@ -2572,7 +2576,7 @@ trait Typers extends Adaptations with Tags { val x = mkParam(methodSym, A1.tpe) // applyOrElse's default parameter: - val B1 = methodSym newTypeParameter (newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp) + val B1 = methodSym newTypeParameter (newTypeName("B1")) setInfo TypeBounds.empty val default = methodSym newValueParameter (newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) val paramSyms = List(x, default) @@ -2582,19 +2586,72 @@ trait Typers extends Adaptations with Tags { // 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 match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp) + // First, type without the default case; only the cases provided + // by the user are typed. The LUB of these becomes `B`, the lower + // bound of `B1`, which in turn is the result type of the default + // case + val match0 = methodBodyTyper.typedMatch(selector, cases, mode, resTp) + val matchResTp = match0.tpe - val matchResTp = match_.tpe B1 setInfo TypeBounds.lower(matchResTp) // patch info + // the default uses applyOrElse's first parameter since the scrut's type has been widened + val match_ = { + val defaultCase = methodBodyTyper.typedCase( + mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)), argTp, B1.tpe) + treeCopy.Match(match0, match0.selector, match0.cases :+ defaultCase) + } match_ setType B1.tpe - // the default uses applyOrElse's first parameter since the scrut's type has been widened - val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x))) - (DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, B1.tpe)), matchResTp) + // SI-6187 Do you really want to know? Okay, here's what's going on here. + // + // Well behaved trees satisfy the property: + // + // typed(tree) == typed(resetLocalAttrs(typed(tree)) + // + // Trees constructed without low-level symbol manipulation get this for free; + // references to local symbols are cleared by `ResetAttrs`, but bind to the + // corresponding symbol in the re-typechecked tree. But PartialFunction synthesis + // doesn't play by these rules. + // + // During typechecking of method bodies, references to method type parameter from + // the declared types of the value parameters should bind to a fresh set of skolems, + // which have been entered into scope by `Namer#methodSig`. A comment therein: + // + // "since the skolemized tparams are in scope, the TypeRefs in vparamSymss refer to skolemized tparams" + // + // But, if we retypecheck the reset `applyOrElse`, the TypeTree of the `default` + // parameter contains no type. Somehow (where?!) it recovers a type that is _almost_ okay: + // `A1 => B1`. But it should really be `A1&0 => B1&0`. In the test, run/t6187.scala, this + // difference results in a type error, as `default.apply(x)` types as `B1`, which doesn't + // conform to the required `B1&0` + // + // I see three courses of action. + // + // 1) synthesize a `asInstanceOf[B1]` below (I tried this first. But... ewwww.) + // 2) install an 'original' TypeTree that will used after ResetAttrs (the solution below) + // 3) Figure out how the almost-correct type is recovered on re-typechecking, and + // substitute in the skolems. + // + // For 2.11, we'll probably shift this transformation back a phase or two, so macros + // won't be affected. But in any case, we should satisfy retypecheckability. + // + val originals: Map[Symbol, Tree] = { + def typedIdent(sym: Symbol) = methodBodyTyper.typedType(Ident(sym), mode) + val A1Tpt = typedIdent(A1) + val B1Tpt = typedIdent(B1) + Map( + x -> A1Tpt, + default -> gen.scalaFunctionConstr(List(A1Tpt), B1Tpt) + ) + } + val rhs = methodBodyTyper.virtualizedMatch(match_, mode, B1.tpe) + val defdef = DefDef(methodSym, Modifiers(methodSym.flags), originals, rhs) + + (defdef, matchResTp) } - // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue ` updateAttachment DefaultOverrideMatchAttachment(FALSE)}` + // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue; case default$ => false } }` def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL) val paramSym = mkParam(methodSym) @@ -2603,10 +2660,10 @@ trait Typers extends Adaptations with Tags { methodBodyTyper.context.scope enter paramSym methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe) - val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) + val defaultCase = mkDefaultCase(FALSE) + val match_ = methodBodyTyper.typedMatch(selector, casesTrue :+ defaultCase, mode, BooleanClass.tpe) - val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(FALSE) - DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, BooleanClass.tpe)) + DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, BooleanClass.tpe)) } // only used for @cps annotated partial functions @@ -2651,7 +2708,9 @@ trait Typers extends Adaptations with Tags { members foreach (m => anonClass.info.decls enter m.symbol) val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)( + Apply(Select(New(Ident(anonClass.name).setSymbol(anonClass)), nme.CONSTRUCTOR), List()) + )) } if (typedBlock.isErrorTyped) typedBlock diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 36754999fc..589e5ce6fd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -82,12 +82,33 @@ trait Unapplies extends ast.TreeDSL * @param param The name of the parameter of the unapply method, assumed to be of type C[Ts] * @param caseclazz The case class C[Ts] */ - private def caseClassUnapplyReturnValue(param: Name, caseclazz: Symbol) = { - def caseFieldAccessorValue(selector: Symbol): Tree = Ident(param) DOT selector + private def caseClassUnapplyReturnValue(param: Name, caseclazz: ClassDef) = { + def caseFieldAccessorValue(selector: ValDef): Tree = { + val accessorName = selector.name + val privateLocalParamAccessor = caseclazz.impl.body.collectFirst { + case dd: ValOrDefDef if dd.name == accessorName && dd.mods.isPrivateLocal => dd.symbol + } + privateLocalParamAccessor match { + case None => + // Selecting by name seems to be the most straight forward way here to + // avoid forcing the symbol of the case class in order to list the accessors. + val maybeRenamedAccessorName = caseAccessorName(caseclazz.symbol, accessorName) + Ident(param) DOT maybeRenamedAccessorName + case Some(sym) => + // But, that gives a misleading error message in neg/t1422.scala, where a case + // class has an illegal private[this] parameter. We can detect this by checking + // the modifiers on the param accessors. + // + // We just generate a call to that param accessor here, which gives us an inaccessible + // symbol error, as before. + Ident(param) DOT sym + } + } - caseclazz.caseFieldAccessors match { - case Nil => TRUE - case xs => SOME(xs map caseFieldAccessorValue: _*) + // Working with trees, rather than symbols, to avoid cycles like SI-5082 + constrParamss(caseclazz).take(1).flatten match { + case Nil => TRUE + case xs => SOME(xs map caseFieldAccessorValue: _*) } } @@ -146,7 +167,7 @@ trait Unapplies extends ast.TreeDSL } val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), unapplyParamName, classType(cdef, tparams), EmptyTree)) val ifNull = if (constrParamss(cdef).head.isEmpty) FALSE else REF(NoneModule) - val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef.symbol) }, ifNull)(Ident(unapplyParamName)) + val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef) }, ifNull)(Ident(unapplyParamName)) atPos(cdef.pos.focus)( DefDef(caseMods, method, tparams, List(cparams), TypeTree(), body) diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 7b065e7cf6..df9d907377 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -6,6 +6,7 @@ import scala.tools.nsc.reporters._ import scala.tools.nsc.CompilerCommand import scala.tools.nsc.io.VirtualDirectory import scala.tools.nsc.interpreter.AbstractFileClassLoader +import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} import java.lang.{Class => jClass} import scala.compat.Platform.EOL @@ -209,8 +210,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName)) def makeParam(schema: (FreeTermSymbol, TermName)) = { + // see a detailed explanation of the STABLE trick in `GenSymbols.reifyFreeTerm` val (fv, name) = schema - meth.newValueParameter(name) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType)) + meth.newValueParameter(name, newFlags = if (fv.hasStableFlag) STABLE else 0) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType)) } meth setInfo MethodType(freeTerms.map(makeParam).toList, AnyClass.tpe) minfo.decls enter meth @@ -410,4 +412,3 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => def eval(tree: u.Tree): Any = compile(tree)() } } - diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 654ed0286a..be233d06cb 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -55,6 +55,12 @@ import java.io._ * val shorter = mainList.tail // costs nothing as it uses the same 2::1::Nil instances as mainList * }}} * + * @note The functional list is characterized by persistence and structural sharing, thus offering considerable + * performance and space consumption benefits in some scenarios if used correctly. + * However, note that objects having multiple references into the same functional list (that is, + * objects that rely on structural sharing), will be serialized and deserialized with multiple lists, one for + * each reference to it. I.e. structural sharing is lost after serialization/deserialization. + * * @author Martin Odersky and others * @version 2.8 * @since 1.0 @@ -305,7 +311,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] } result } - + override def foldRight[B](z: B)(op: (A, B) => B): B = reverse.foldLeft(z)((right, left) => op(left, right)) @@ -350,25 +356,8 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend override def tail : List[B] = tl override def isEmpty: Boolean = false - private def writeObject(out: ObjectOutputStream) { - out.writeObject(ListSerializeStart) // needed to differentiate with the legacy `::` serialization - out.writeObject(this.hd) - out.writeObject(this.tl) - } - private def readObject(in: ObjectInputStream) { - val obj = in.readObject() - if (obj == ListSerializeStart) { - this.hd = in.readObject().asInstanceOf[B] - this.tl = in.readObject().asInstanceOf[List[B]] - } else oldReadObject(in, obj) - } - - /* The oldReadObject method exists here for compatibility reasons. - * :: objects used to be serialized by serializing all the elements to - * the output stream directly, but this was broken (see SI-5374). - */ - private def oldReadObject(in: ObjectInputStream, firstObject: AnyRef) { + val firstObject = in.readObject() hd = firstObject.asInstanceOf[B] assert(hd != ListSerializeEnd) var current: ::[B] = this @@ -376,12 +365,18 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend case ListSerializeEnd => current.tl = Nil return - case a : Any => + case a => val list : ::[B] = new ::(a.asInstanceOf[B], Nil) current.tl = list current = list } } + + private def writeObject(out: ObjectOutputStream) { + var xs: List[B] = this + while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail } + out.writeObject(ListSerializeEnd) + } } /** $factoryInfo @@ -401,10 +396,6 @@ object List extends SeqFactory[List] { } /** Only used for list serialization */ -@SerialVersionUID(0L - 8287891243975527522L) -private[scala] case object ListSerializeStart - -/** Only used for list serialization */ @SerialVersionUID(0L - 8476791151975527571L) private[scala] case object ListSerializeEnd diff --git a/src/partest/scala/tools/partest/AsmNode.scala b/src/partest/scala/tools/partest/AsmNode.scala new file mode 100644 index 0000000000..d181436676 --- /dev/null +++ b/src/partest/scala/tools/partest/AsmNode.scala @@ -0,0 +1,60 @@ +package scala.tools.partest + +import scala.collection.JavaConverters._ +import scala.tools.asm +import asm._ +import asm.tree._ +import java.lang.reflect.Modifier + +sealed trait AsmNode[+T] { + def node: T + def access: Int + def desc: String + def name: String + def signature: String + def attrs: List[Attribute] + def visibleAnnotations: List[AnnotationNode] + def invisibleAnnotations: List[AnnotationNode] + def characteristics = f"$name%15s $desc%-30s$accessString$sigString" + + private def accessString = if (access == 0) "" else " " + Modifier.toString(access) + private def sigString = if (signature == null) "" else " " + signature + override def toString = characteristics +} + +object AsmNode { + type AsmMethod = AsmNode[MethodNode] + type AsmField = AsmNode[FieldNode] + type AsmMember = AsmNode[_] + + implicit class ClassNodeOps(val node: ClassNode) { + def fieldsAndMethods: List[AsmMember] = { + val xs: List[AsmMember] = ( + node.methods.asScala.toList.map(x => (x: AsmMethod)) + ++ node.fields.asScala.toList.map(x => (x: AsmField)) + ) + xs sortBy (_.characteristics) + } + } + implicit class AsmMethodNode(val node: MethodNode) extends AsmNode[MethodNode] { + def access: Int = node.access + def desc: String = node.desc + def name: String = node.name + def signature: String = node.signature + def attrs: List[Attribute] = node.attrs.asScala.toList + def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList + def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList + } + implicit class AsmFieldNode(val node: FieldNode) extends AsmNode[FieldNode] { + def access: Int = node.access + def desc: String = node.desc + def name: String = node.name + def signature: String = node.signature + def attrs: List[Attribute] = node.attrs.asScala.toList + def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList + def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList + } + + def apply(node: MethodNode): AsmMethodNode = new AsmMethodNode(node) + def apply(node: FieldNode): AsmFieldNode = new AsmFieldNode(node) +} diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala index 41329a8264..2699083069 100644 --- a/src/partest/scala/tools/partest/BytecodeTest.scala +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -3,9 +3,10 @@ package scala.tools.partest import scala.tools.nsc.util.JavaClassPath import scala.collection.JavaConverters._ import scala.tools.asm -import asm.ClassReader +import asm.{ ClassReader } import asm.tree.{ClassNode, MethodNode, InsnList} import java.io.InputStream +import AsmNode._ /** * Provides utilities for inspecting bytecode using ASM library. @@ -29,13 +30,14 @@ import java.io.InputStream * */ abstract class BytecodeTest extends ASMConverters { + import instructions._ /** produce the output to be compared against a checkfile */ protected def show(): Unit def main(args: Array[String]): Unit = show -// asserts + // asserts def sameBytecode(methA: MethodNode, methB: MethodNode) = { val isa = instructions.fromMethod(methA) val isb = instructions.fromMethod(methB) @@ -43,7 +45,32 @@ abstract class BytecodeTest extends ASMConverters { else diffInstructions(isa, isb) } - import instructions._ + // Do these classes have all the same methods, with the same names, access, + // descriptors and generic signatures? Method bodies are not considered, and + // the names of the classes containing the methods are substituted so they do + // not appear as differences. + def sameMethodAndFieldSignatures(clazzA: ClassNode, clazzB: ClassNode): Boolean = { + val ms1 = clazzA.fieldsAndMethods.toIndexedSeq + val ms2 = clazzB.fieldsAndMethods.toIndexedSeq + val name1 = clazzA.name + val name2 = clazzB.name + + if (ms1.length != ms2.length) { + println("Different member counts in $name1 and $name2") + false + } + else (ms1, ms2).zipped forall { (m1, m2) => + val c1 = m1.characteristics + val c2 = m2.characteristics.replaceAllLiterally(name2, name1) + if (c1 == c2) + println(s"[ok] $m1") + else + println(s"[fail]\n in $name1: $c1\n in $name2: $c2") + + c1 == c2 + } + } + // bytecode is equal modulo local variable numbering def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match { case _ if a == b => true diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 6aa7eab689..45c5279574 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -287,7 +287,7 @@ class Flags extends ModifierFlags { * from Modifiers. Others which may be applied at creation time are: * SYNTHETIC. */ - final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM + final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM | STABLE final val BeanPropertyFlags = DEFERRED | OVERRIDE | STATIC final val VarianceFlags = COVARIANT | CONTRAVARIANT diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 3d1701386e..a894bd649c 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -281,6 +281,7 @@ trait StdNames { // Compiler internal names val ANYname: NameType = "<anyname>" val CONSTRUCTOR: NameType = "<init>" + val DEFAULT_CASE: NameType = "defaultCase$" val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" val FAKE_LOCAL_THIS: NameType = "this$" val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? @@ -551,6 +552,7 @@ trait StdNames { val RootPackage: NameType = "RootPackage" val RootClass: NameType = "RootClass" val Select: NameType = "Select" + val SelectFromTypeTree: NameType = "SelectFromTypeTree" val StringContext: NameType = "StringContext" val This: NameType = "This" val ThisType: NameType = "ThisType" diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 9b8f86751e..1edfa84c04 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -92,12 +92,15 @@ abstract class TreeInfo { tree.symbol.isStable && isExprSafeToInline(qual) case TypeApply(fn, _) => isExprSafeToInline(fn) + case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` + free.symbol.hasStableFlag && isExprSafeToInline(free) case Apply(fn, List()) => - /* Note: After uncurry, field accesses are represented as Apply(getter, Nil), - * so an Apply can also be pure. - * However, before typing, applications of nullary functional values are also - * Apply(function, Nil) trees. To prevent them from being treated as pure, - * we check that the callee is a method. */ + // Note: After uncurry, field accesses are represented as Apply(getter, Nil), + // so an Apply can also be pure. + // However, before typing, applications of nullary functional values are also + // Apply(function, Nil) trees. To prevent them from being treated as pure, + // we check that the callee is a method. fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn) case Typed(expr, _) => isExprSafeToInline(expr) @@ -421,6 +424,13 @@ abstract class TreeInfo { case _ => false } + /** Is the argument a wildcard star type of the form `_*`? + */ + def isWildcardStarType(tree: Tree): Boolean = tree match { + case Ident(tpnme.WILDCARD_STAR) => true + case _ => false + } + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ def isDefaultCase(cdef: CaseDef) = cdef match { case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat) @@ -444,6 +454,13 @@ abstract class TreeInfo { case _ => nme.NO_NAME } + /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know + * whether the user provided cases are exhaustive. */ + def isSyntheticDefaultCase(cdef: CaseDef) = cdef match { + case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true + case _ => false + } + /** Does this CaseDef catch Throwable? */ def catchesThrowable(cdef: CaseDef) = ( cdef.guard.isEmpty && (unbind(cdef.pat) match { diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 9b185c1c2d..408c7c648f 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -534,7 +534,11 @@ trait Trees extends api.Trees { self: SymbolTable => override private[scala] def copyAttrs(tree: Tree) = { super.copyAttrs(tree) tree match { - case other: TypeTree => wasEmpty = other.wasEmpty // SI-6648 Critical for correct operation of `resetAttrs`. + case other: TypeTree => + // SI-6648 Critical for correct operation of `resetAttrs`. + wasEmpty = other.wasEmpty + if (other.orig != null) + orig = other.orig.duplicate case _ => } this @@ -1009,6 +1013,18 @@ trait Trees extends api.Trees { self: SymbolTable => def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = DefDef(sym, mods, mapParamss(sym)(ValDef), rhs) + /** A DefDef with original trees attached to the TypeTree of each parameter */ + def DefDef(sym: Symbol, mods: Modifiers, originalParamTpts: Symbol => Tree, rhs: Tree): DefDef = { + val paramms = mapParamss(sym){ sym => + val vd = ValDef(sym, EmptyTree) + (vd.tpt : @unchecked) match { + case tt: TypeTree => tt setOriginal (originalParamTpts(sym) setPos sym.pos.focus) + } + vd + } + DefDef(sym, mods, paramms, rhs) + } + def DefDef(sym: Symbol, rhs: Tree): DefDef = DefDef(sym, Modifiers(sym.flags), rhs) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 810ea70d58..ce7fae8628 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -561,9 +561,9 @@ trait Types extends api.Types { self: SymbolTable => * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes. * Functions on types are also implemented as PolyTypes. * - * Example: (in the below, <List> is the type constructor of List) - * TypeRef(pre, <List>, List()) is replaced by - * PolyType(X, TypeRef(pre, <List>, List(X))) + * Example: (in the below, `<List>` is the type constructor of List) + * TypeRef(pre, `<List>`, List()) is replaced by + * PolyType(X, TypeRef(pre, `<List>`, List(X))) * * Discussion: normalize is NOT usually what you want to be calling. * The (very real) danger with normalize is that it will force types @@ -4578,23 +4578,39 @@ trait Types extends api.Types { self: SymbolTable => } ) - override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { - object trans extends TypeMapTransformer { + object mapTreeSymbols extends TypeMapTransformer { + val strictCopy = newStrictTreeCopier - def termMapsTo(sym: Symbol) = from indexOf sym match { - case -1 => None - case idx => Some(to(idx)) - } + def termMapsTo(sym: Symbol) = from indexOf sym match { + case -1 => None + case idx => Some(to(idx)) + } - override def transform(tree: Tree) = { - termMapsTo(tree.symbol) match { - case Some(tosym) => tree.symbol = tosym - case None => () - } - super.transform(tree) + // if tree.symbol is mapped to another symbol, passes the new symbol into the + // constructor `trans` and sets the symbol and the type on the resulting tree. + def transformIfMapped(tree: Tree)(trans: Symbol => Tree) = termMapsTo(tree.symbol) match { + case Some(toSym) => trans(toSym) setSymbol toSym setType tree.tpe + case None => tree + } + + // changes trees which refer to one of the mapped symbols. trees are copied before attributes are modified. + override def transform(tree: Tree) = { + // super.transform maps symbol references in the types of `tree`. it also copies trees where necessary. + super.transform(tree) match { + case id @ Ident(_) => + transformIfMapped(id)(toSym => + strictCopy.Ident(id, toSym.name)) + + case sel @ Select(qual, name) => + transformIfMapped(sel)(toSym => + strictCopy.Select(sel, qual, toSym.name)) + + case tree => tree } } - trans.transform(tree) + } + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + mapTreeSymbols.transform(tree) } } @@ -4843,6 +4859,51 @@ trait Types extends api.Types { self: SymbolTable => } } + /** + * A more persistent version of `Type#memberType` which does not require + * that the symbol is a direct member of the prefix. + * + * For instance: + * + * {{{ + * class C[T] { + * sealed trait F[A] + * object X { + * object S1 extends F[T] + * } + * class S2 extends F[T] + * } + * object O extends C[Int] { + * def foo(f: F[Int]) = f match {...} // need to enumerate sealed subtypes of the scrutinee here. + * } + * class S3 extends O.F[String] + * + * nestedMemberType(<S1>, <O.type>, <C>) = O.X.S1.type + * nestedMemberType(<S2>, <O.type>, <C>) = O.S2.type + * nestedMemberType(<S3>, <O.type>, <C>) = S3.type + * }}} + * + * @param sym The symbol of the subtype + * @param pre The prefix from which the symbol is seen + * @param owner + */ + def nestedMemberType(sym: Symbol, pre: Type, owner: Symbol): Type = { + def loop(tp: Type): Type = + if (tp.isTrivial) tp + else if (tp.prefix.typeSymbol isNonBottomSubClass owner) { + val widened = tp match { + case _: ConstantType => tp // Java enum constants: don't widen to the enum type! + case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect. + } + widened asSeenFrom (pre, tp.typeSymbol.owner) + } + else loop(tp.prefix) memberType tp.typeSymbol + + val result = loop(sym.tpeHK) + assert(sym.isTerm || result.typeSymbol == sym, s"($result).typeSymbol = ${result.typeSymbol}; expected ${sym}") + result + } + /** The most deeply nested owner that contains all the symbols * of thistype or prefixless typerefs/singletype occurrences in given type. */ diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 3ea8cff989..d5ed9dab5b 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -47,6 +47,4 @@ abstract class MutableSettings extends AbsSettings { def XnoPatmatAnalysis: BooleanSetting def XfullLubs: BooleanSetting def breakCycles: BooleanSetting - def companionsInPkgObjs: BooleanSetting - } diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 9472cefbbf..ba524f4df2 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -43,7 +43,6 @@ private[reflect] class Settings extends MutableSettings { val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) val breakCycles = new BooleanSetting(false) - val companionsInPkgObjs = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) |