summaryrefslogtreecommitdiff
path: root/src/compiler/scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-02-10 10:55:47 -0800
committerPaul Phillips <paulp@improving.org>2013-02-10 15:56:52 -0800
commit22d315d61b11e95c3a18e1285ca2131f614e13fb (patch)
treee17d4eab194b45613ca3d5136bad553963a11ca6 /src/compiler/scala
parentccf6bc7e860cf87cbba5bcf386bcf2d0cbfa8ddd (diff)
parentdb5919a7d3b18be94e79899c2f7e33c535e15a27 (diff)
downloadscala-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/compiler/scala')
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenSymbols.scala29
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenTrees.scala36
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenTypes.scala3
-rw-r--r--src/compiler/scala/reflect/reify/phases/Reify.scala5
-rw-r--r--src/compiler/scala/reflect/reify/phases/Reshape.scala12
-rw-r--r--src/compiler/scala/reflect/reify/utils/Extractors.scala8
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interactive/CompilerControl.scala13
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Global.scala7
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala103
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala33
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala5
17 files changed, 219 insertions, 84 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)()
}
}
-