diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2016-11-23 15:18:55 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2016-11-28 13:46:22 +1000 |
commit | b00a3e50da66086ef0f1a4c214866a591089b934 (patch) | |
tree | 544cdd5ff1e6bffc7165b1a9739bbc9b7d3a493a /src/compiler/scala/tools/nsc/typechecker | |
parent | 9c5d3f81a667917b6a3aed5098182623c417c137 (diff) | |
download | scala-b00a3e50da66086ef0f1a4c214866a591089b934.tar.gz scala-b00a3e50da66086ef0f1a4c214866a591089b934.tar.bz2 scala-b00a3e50da66086ef0f1a4c214866a591089b934.zip |
SI-10009 Fields survive untypecheck/retypecheck
Some places in the compiler, and many places in macros, use
`untypecheck` (aka `resetAttrs`) to strip types and local symbols
from a tree before retypechecking it under some different context.
The refactoring of the desugaring of vals and vars in Scala 2.12.0
broke an assumption in this facility.
When a ValDef must be split into multiple members (e.g. a field and
a getter, or a perhaps also a setter), the ValDef that was parsed
assumes the role of the `field`, and the trees for other members are
stached by `Namer` to the `synthetics` map of the compilation unit,
in order to spliced into the right statement list by typechecking.
See `enterGetterSetter` for more details.
However, the parsed ValDef is now used verbatim, carrying the meaning
(ie, the symbol) of the `private[this]` field. This tree now had
an inconsistency between the flags in `tree.mods.flags` and
`tree.symbol.flags`. `tree.name` also differed from `tree.symbol.name`
(the latter was renamed to be a local name, ie one with a trailing space.)
When `ResetAttrs` stripped off the symbol and we retypechecked, we'd
end up with two symbols in scope with the same name.
In the first from the `run` test:
```
================================================================================
{
class a extends scala.AnyRef {
def <init>(): a = {
a.super.<init>();
()
};
private[this] val x: Int = 42;
<stable> <accessor> def x: Int = a.this.x
};
new a()
}
{
class a extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
val x = 42; // oops, the name is "x" rather than "x " and we've missing `private[this]`!
<stable> <accessor> def x: Int = a.this.x
};
new a()
}
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: x is already defined as value x
```
This commit uses the flags and name of the symbol in `typedValDef`.
I've also had to modify the internals of `CodePrinter` to use the
implicit, override, and deferred flags from the modifiers of an
accessor when recovering pre-typer tree for a ValDef.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 14 |
2 files changed, 14 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index d11417192d..0f257d3717 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -132,7 +132,11 @@ trait MethodSynthesis { // only one symbol can have `tree.pos`, the others must focus their position // normally the field gets the range position, but if there is none, give it to the getter + // + // SI-10009 the tree's modifiers can be temporarily out of sync with the new symbol's flags. + // typedValDef corrects this later on. tree.symbol = fieldSym orElse (getterSym setPos tree.pos) + val namer = namerOf(tree.symbol) // the valdef gets the accessor symbol for a lazy val (too much going on in its RHS) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cca6f280e3..78533bdfc5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,11 +13,12 @@ package scala package tools.nsc package typechecker -import scala.collection.{mutable, immutable} -import scala.reflect.internal.util.{ Statistics, ListOfNil } +import scala.collection.{immutable, mutable} +import scala.reflect.internal.util.{ListOfNil, Statistics} import mutable.ListBuffer import symtab.Flags._ import Mode._ +import scala.reflect.macros.whitebox // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -2020,7 +2021,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // use typedValDef instead. this version is called after creating a new context for the ValDef private def typedValDefImpl(vdef: ValDef) = { val sym = vdef.symbol.initialize - val typedMods = typedModifiers(vdef.mods) + val typedMods = if (nme.isLocalName(sym.name) && sym.isPrivateThis && !vdef.mods.isPrivateLocal) { + // SI-10009 This tree has been given a field symbol by `enterGetterSetter`, patch up the + // modifiers accordingly so that we can survive resetAttrs and retypechecking. + // Similarly, we use `sym.name` rather than `vdef.name` below to use the local name. + typedModifiers(vdef.mods.copy(flags = sym.flags, privateWithin = tpnme.EMPTY)) + } else typedModifiers(vdef.mods) sym.annotations.map(_.completeInfo()) val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) @@ -2055,7 +2061,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else tpt1.tpe transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } - treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType + treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(rhs1)) setType NoType } /** Enter all aliases of local parameter accessors. |