summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Fields.scala138
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala47
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala298
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala149
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala35
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala51
10 files changed, 333 insertions, 407 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala
index 26e517743a..6339f0002d 100644
--- a/src/compiler/scala/tools/nsc/transform/Fields.scala
+++ b/src/compiler/scala/tools/nsc/transform/Fields.scala
@@ -176,9 +176,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// can't use the referenced field since it already tracks the module's moduleClass
private[this] val moduleVarOf = perRunCaches.newMap[Symbol, Symbol]
- private def newModuleVarSymbol(site: Symbol, module: Symbol, tp: Type, extraFlags: Long): TermSymbol = {
+ private def newModuleVarSymbol(owner: Symbol, module: Symbol, tp: Type, extraFlags: Long): TermSymbol = {
// println(s"new module var in $site for $module of type $tp")
- val moduleVar = site.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, MODULEVAR | extraFlags) setInfo tp addAnnotation VolatileAttr
+ val moduleVar = owner.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, MODULEVAR | extraFlags) setInfo tp addAnnotation VolatileAttr
moduleVarOf(module) = moduleVar
moduleVar
@@ -191,6 +191,37 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
}
+ private def newLazyVarSymbol(owner: Symbol, member: Symbol, tp: Type, extraFlags: Long = 0, localLazyVal: Boolean = false): TermSymbol = {
+ val flags = member.flags | extraFlags
+ val name = member.name.toTermName
+ val pos = member.pos
+
+ // If the owner is not a class, this is a lazy val from a method,
+ // with no associated field. It has an accessor with $lzy appended to its name and
+ // its flags are set differently. The implicit flag is reset because otherwise
+ // a local implicit "lazy val x" will create an ambiguity with itself
+ // via "x$lzy" as can be seen in test #3927.
+ val nameSuffix =
+ if (!localLazyVal) reflect.NameTransformer.LOCAL_SUFFIX_STRING
+ else reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING
+
+ // TODO: should end up final in bytecode
+ val fieldFlags =
+ if (!localLazyVal) flags & FieldFlags | PrivateLocal | MUTABLE
+ else (flags & FieldFlags | ARTIFACT | MUTABLE) & ~(IMPLICIT | STABLE)
+
+// println(s"new lazy var sym in $owner for $member ${symtab.Flags.flagsToString(fieldFlags)}")
+ val sym = owner.newValue(name.append(nameSuffix), pos.focus, fieldFlags | extraFlags) setInfo tp
+ moduleVarOf(member) = sym
+ sym
+ }
+
+ private def lazyValInit(member: Symbol, rhs: Tree) = {
+ val lazyVar = moduleVarOf(member)
+ assert(lazyVar.isMutable, lazyVar)
+ gen.mkAssignAndReturn(lazyVar, rhs)
+ }
+
private object synthFieldsAndAccessors extends TypeMap {
private def newTraitSetter(getter: Symbol, clazz: Symbol) = {
// Add setter for an immutable, memoizing getter
@@ -221,7 +252,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
accessor
}
-
// needed for the following scenario (T could be trait or class)
// trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O.
// marking it as an ACCESSOR so that it will get to `getterBody` when synthesizing trees below
@@ -233,6 +263,18 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
}
+ private def newSuperLazy(lazyCallingSuper: Symbol, site: Type, lazyVar: Symbol) = {
+ lazyCallingSuper.asTerm.referenced = lazyVar
+
+ val tp = site.memberInfo(lazyCallingSuper)
+
+ lazyVar setInfo tp.resultType
+ lazyCallingSuper setInfo tp
+ }
+
+ // make sure they end up final in bytecode
+ final private val fieldFlags = PrivateLocal | FINAL | SYNTHETIC | NEEDS_TREES
+
def apply(tp0: Type): Type = tp0 match {
// TODO: make less destructive (name changes, decl additions, flag setting --
// none of this is actually undone when travelling back in time using atPhase)
@@ -246,7 +288,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
if (member hasFlag ACCESSOR) {
val fieldMemoization = fieldMemoizationIn(member, clazz)
// check flags before calling makeNotPrivate
- val accessorUnderConsideration = !(member hasFlag (DEFERRED | LAZY))
+ val accessorUnderConsideration = !(member hasFlag DEFERRED)
// destructively mangle accessor's name (which may cause rehashing of decls), also sets flags
// this accessor has to be implemented in a subclass -- can't be private
@@ -265,7 +307,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
if (accessorUnderConsideration && fieldMemoization.stored) {
synthesizeImplInSubclasses(member)
- if (member hasFlag STABLE) // TODO: check isGetter?
+ if ((member hasFlag STABLE) && !(member hasFlag LAZY))
newDecls += newTraitSetter(member, clazz)
}
} else if (member hasFlag MODULE) {
@@ -296,27 +338,36 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
(existingGetter ne NoSymbol) && (tp matches (site memberInfo existingGetter).resultType) // !existingGetter.isDeferred && -- see (3)
}
- def newModuleVar(member: Symbol): TermSymbol =
- newModuleVarSymbol(clazz, member, site.memberType(member).resultType, PrivateLocal | SYNTHETIC | NEEDS_TREES)
+ def newModuleVarMember(member: Symbol): TermSymbol =
+ newModuleVarSymbol(clazz, member, site.memberType(member).resultType, fieldFlags)
+
+ def newLazyVarMember(member: Symbol): TermSymbol =
+ newLazyVarSymbol(clazz, member, site.memberType(member).resultType, fieldFlags)
// a module does not need treatment here if it's static, unless it has a matching member in a superclass
// a non-static method needs a module var
- val modulesNeedingExpansion =
- oldDecls.toList.filter(m => m.isModule && (!m.isStatic || m.isOverridingSymbol))
+ val modulesAndLazyValsNeedingExpansion =
+ oldDecls.toList.filter(m =>
+ (m.isModule && (!m.isStatic || m.isOverridingSymbol))
+ || (m.isLazy && !(m.info.isInstanceOf[ConstantType] || isUnitType(m.info))) // no need for ASF since we're in the defining class
+ )
// expand module def in class/object (if they need it -- see modulesNeedingExpansion above)
- val expandedModules =
- modulesNeedingExpansion map { module =>
+ val expandedModulesAndLazyVals =
+ modulesAndLazyValsNeedingExpansion map { member =>
+ if (member.isLazy) {
+ newLazyVarMember(member)
+ }
// expanding module def (top-level or nested in static module)
- if (module.isStatic) { // implies m.isOverridingSymbol as per above filter
+ else if (member.isStatic) { // implies m.isOverridingSymbol as per above filter
// Need a module accessor, to implement/override a matching member in a superclass.
// Never a need for a module var if the module is static.
- newMatchingModuleAccessor(clazz, module)
+ newMatchingModuleAccessor(clazz, member)
} else {
- nonStaticModuleToMethod(module)
+ nonStaticModuleToMethod(member)
// must reuse symbol instead of creating an accessor
- module setFlag NEEDS_TREES
- newModuleVar(module)
+ member setFlag NEEDS_TREES
+ newModuleVarMember(member)
}
}
@@ -344,13 +395,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
clonedAccessor
}
- if (member hasFlag MODULE) {
- val moduleVar = newModuleVar(member)
- List(moduleVar, newModuleAccessor(member, clazz, moduleVar))
- }
// when considering whether to mix in the trait setter, forget about conflicts -- they are reported for the getter
// a trait setter for an overridden val will receive a unit body in the tree transform
- else if (nme.isTraitSetterName(member.name)) {
+ if (nme.isTraitSetterName(member.name)) {
val getter = member.getterIn(member.owner)
val clone = cloneAccessor()
@@ -362,6 +409,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// don't cause conflicts, skip overridden accessors contributed by supertraits (only act on the last overriding one)
// see pos/trait_fields_dependent_conflict.scala and neg/t1960.scala
else if (accessorConflictsExistingVal(member) || isOverriddenAccessor(member, clazz)) Nil
+ else if (member hasFlag MODULE) {
+ val moduleVar = newModuleVarMember(member)
+ List(moduleVar, newModuleAccessor(member, clazz, moduleVar))
+ }
+ else if (member hasFlag LAZY) {
+ val mixedinLazy = cloneAccessor()
+ val lazyVar = newLazyVarMember(mixedinLazy)
+ List(lazyVar, newSuperLazy(mixedinLazy, site, lazyVar))
+ }
else if (member.isGetter && fieldMemoizationIn(member, clazz).stored) {
// add field if needed
val field = clazz.newValue(member.localName, member.pos) setInfo fieldTypeForGetterIn(member, clazz.thisType)
@@ -382,15 +438,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && !fieldMemoizationIn(sym, clazz).stored
val newDecls =
- if (expandedModules.isEmpty && mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
+ if (expandedModulesAndLazyVals.isEmpty && mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
else {
// must not alter `decls` directly
val newDecls = newScope
val enter = newDecls enter (_: Symbol)
val enterAll = (_: List[Symbol]) foreach enter
+ expandedModulesAndLazyVals foreach enter
oldDecls foreach { d => if (!omittableField(d)) enter(d) }
- expandedModules foreach enter
mixedInAccessorAndFields foreach enterAll
newDecls
@@ -421,6 +477,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
def mkField(sym: Symbol) = localTyper.typedPos(sym.pos)(ValDef(sym)).asInstanceOf[ValDef]
+
// synth trees for accessors/fields and trait setters when they are mixed into a class
def fieldsAndAccessors(clazz: Symbol): List[ValOrDefDef] = {
def fieldAccess(accessor: Symbol): Option[Tree] = {
@@ -462,8 +519,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
else moduleInit(module)
)
+ def superLazy(getter: Symbol): Some[Tree] = {
+ assert(!clazz.isTrait)
+ // this contortion was the only way I can get the super select to be type checked correctly.. TODO: why does SelectSuper not work?
+ Some(gen.mkAssignAndReturn(moduleVarOf(getter), Apply(Select(Super(This(clazz), tpnme.EMPTY), getter.name), Nil)))
+ }
+
clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap {
case module if module hasAllFlags (MODULE | METHOD) => moduleAccessorBody(module) map mkAccessor(module)
+ case getter if getter hasAllFlags (LAZY | METHOD) => superLazy(getter) map mkAccessor(getter)
case setter if setter.isSetter => setterBody(setter) map mkAccessor(setter)
case getter if getter.hasFlag(ACCESSOR) => getterBody(getter) map mkAccessor(getter)
case field if !(field hasFlag METHOD) => Some(mkField(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES)
@@ -474,7 +538,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree =
atOwner(newOwner)(super.transform(stat.rhs.changeOwner(stat.symbol -> newOwner)))
-
private def Thicket(trees: List[Tree]) = Block(trees, EmptyTree)
override def transform(stat: Tree): Tree = {
val clazz = currentOwner
@@ -502,7 +565,31 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
&& (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym)
&& !clazz.isTrait // we've already done this for traits.. the asymmetry will be solved by the above todo
&& fieldMemoizationIn(statSym, clazz).pureConstant =>
- deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe)) // TODO: recurse?
+ deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe))
+
+ /** Normalize ValDefs to corresponding accessors + field
+ *
+ * ValDef in trait --> getter DefDef
+ * Lazy val receives field with a new symbol (if stored) and the ValDef's symbol is moved to a DefDef (the lazy accessor):
+ * - for lazy values of type Unit and all lazy fields inside traits,
+ * the rhs is the initializer itself, because we'll just "compute" the result on every access
+ * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap)
+ * - for all other lazy values z the accessor is a block of this form:
+ * { z = <rhs>; z } where z can be an identifier or a field.
+ */
+ case vd@ValDef(mods, name, tpt, rhs) if vd.symbol.hasFlag(ACCESSOR) && treeInfo.noFieldFor(vd, clazz) =>
+ def notStored = {val resultType = statSym.info.resultType ; (resultType.isInstanceOf[ConstantType] || isUnitType(resultType))}
+ val transformedRhs = atOwner(statSym)(transform(rhs))
+
+ if (rhs == EmptyTree) mkAccessor(statSym)(EmptyTree)
+ else if (clazz.isTrait || notStored) mkAccessor(statSym)(transformedRhs)
+ else if (clazz.isClass) mkAccessor(statSym)(gen.mkAssignAndReturn(moduleVarOf(vd.symbol), transformedRhs))
+ else {
+ // local lazy val (same story as modules: info transformer doesn't get here, so can't drive tree synthesis)
+ val lazyVar = newLazyVarSymbol(currentOwner, statSym, statSym.info.resultType, extraFlags = 0, localLazyVal = true)
+ val lazyValInit = gen.mkAssignAndReturn(lazyVar, transformedRhs)
+ Thicket(mkField(lazyVar) :: mkAccessor(statSym)(lazyValInit) :: Nil)
+ }
// drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields
case ValDef(mods, _, _, rhs) if (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym)
@@ -524,6 +611,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
}
}
+
def transformTermsAtExprOwner(exprOwner: Symbol)(stat: Tree) =
if (stat.isTerm) atOwner(exprOwner)(transform(stat))
else transform(stat)
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index f781426f1a..4be611b747 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -11,6 +11,7 @@ import Flags._
import scala.annotation.tailrec
import scala.collection.mutable
+// TODO: move lazy vals bitmap creation to lazy vals phase now that lazy vals are mixed in during fields
abstract class Mixin extends InfoTransform with ast.TreeDSL {
import global._
import definitions._
@@ -57,8 +58,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
*/
private val treatedClassInfos = perRunCaches.newMap[Symbol, Type]() withDefaultValue NoType
- /** Map a lazy, mixedin field accessor to its trait member accessor */
- private val initializer = perRunCaches.newMap[Symbol, Symbol]()
// --------- helper functions -----------------------------------------------
@@ -295,9 +294,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
}
- /* Mix in members of trait mixinClass into class clazz. Also,
- * for each lazy field in mixinClass, add a link from its mixed in member to its
- * initializer method inside the implclass.
+ /* Mix in members of trait mixinClass into class clazz.
*/
def mixinTraitMembers(mixinClass: Symbol) {
// For all members of a trait's interface do:
@@ -319,28 +316,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
}
else if (mixinMember.hasFlag(ACCESSOR) && notDeferred(mixinMember)
- && (mixinMember hasFlag (LAZY | PARAMACCESSOR))
+ && (mixinMember hasFlag PARAMACCESSOR)
&& !isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) {
- // pick up where `fields` left off -- it already mixed in fields and accessors for regular vals.
- // but has ignored lazy vals and constructor parameter accessors
- // TODO: captures added by lambdalift for local traits?
- //
- // mixin accessor for lazy val or constructor parameter
+ // mixin accessor for constructor parameter
// (note that a paramaccessor cannot have a constant type as it must have a user-defined type)
val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember)
val name = mixinMember.name
- if (mixinMember.isLazy)
- initializer(mixedInAccessor) =
- (mixinClass.info.decl(name) orElse abort(s"Could not find initializer for lazy val $name!"))
-
- // Add field while we're mixing in the getter (unless it's a Unit-typed lazy val)
- //
- // lazy val of type Unit doesn't need a field -- the bitmap is enough.
- // TODO: constant-typed lazy vals... it's an extreme corner case, but we could also suppress the field in:
- // `trait T { final lazy val a = "a" }; class C extends T`, but who writes code like that!? :)
- // we'd also have to change the lazyvals logic if we do this
- if (!nme.isSetterName(name) && !(mixinMember.isLazy && isUnitGetter(mixinMember))) {
+ if (!nme.isSetterName(name)) {
// enteringPhase: the private field is moved to the implementation class by erasure,
// so it can no longer be found in the mixinMember's owner (the trait)
val accessed = enteringPickler(mixinMember.accessed)
@@ -354,7 +337,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val newFlags = (
(PrivateLocal)
- | (mixinMember getFlag MUTABLE | LAZY)
+ | (mixinMember getFlag MUTABLE)
| (if (mixinMember.hasStableFlag) 0 else MUTABLE)
)
@@ -499,7 +482,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
else if (needsInitFlag(field) && !field.isDeferred) false
else return NO_NAME
)
- if (field.accessed hasAnnotation TransientAttr) {
+ if (field.accessedOrSelf hasAnnotation TransientAttr) {
if (isNormal) BITMAP_TRANSIENT
else BITMAP_CHECKINIT_TRANSIENT
} else {
@@ -862,17 +845,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
def getterBody(getter: Symbol) = {
assert(getter.isGetter)
- val readValue =
- if (getter.isLazy) {
- getter.tpe.resultType match {
- case ConstantType(c) => Literal(c)
- case _ =>
- val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil)
- val offset = fieldOffset(getter)
- if (isUnitGetter(getter)) mkLazyDef(clazz, getter, List(initCall), UNIT, offset)
- else mkLazyDef(clazz, getter, List(atPos(getter.pos)(Assign(fieldAccess(getter), initCall))), fieldAccess(getter), offset)
- }
- } else {
+ val readValue = {
assert(getter.hasFlag(PARAMACCESSOR))
fieldAccess(getter)
}
@@ -902,7 +875,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
if (clazz.isTrait || sym.isSuperAccessor) addDefDef(sym)
// implement methods mixed in from a supertrait (the symbols were created by mixinTraitMembers)
else if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) {
- assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?")
+ assert(sym hasFlag (PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?")
// add accessor definitions
addDefDef(sym, if (sym.isSetter) setterBody(sym) else getterBody(sym))
@@ -919,7 +892,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
if (clazz.isTrait) stats1 = stats1.filter {
case vd: ValDef =>
- assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR | LAZY), s"unexpected valdef $vd in trait $clazz")
+ assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR), s"unexpected valdef $vd in trait $clazz")
false
case _ => true
}
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index fa7c503213..744b9c8a8e 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -274,10 +274,8 @@ abstract class TailCalls extends Transform {
import runDefinitions.{Boolean_or, Boolean_and}
tree match {
- case ValDef(_, _, _, _) =>
- if (tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass))
- reporter.error(tree.pos, "lazy vals are not tailcall transformed")
-
+ case dd: DefDef if tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass) =>
+ reporter.error(tree.pos, "lazy vals are not tailcall transformed")
super.transform(tree)
case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if isEligible(dd) =>
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 6ade45c41c..f6c667353f 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -439,9 +439,9 @@ abstract class UnCurry extends InfoTransform
super.transform(treeCopy.DefDef(dd, mods, name, tparams, vparamssNoRhs, tpt, rhs))
}
}
- case ValDef(_, _, _, rhs) =>
+ case ValDef(mods, _, _, rhs) =>
if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit)
- if (!sym.owner.isSourceMethod)
+ if (!sym.owner.isSourceMethod || mods.isLazy)
withNeedLift(needLift = true) { super.transform(tree) }
else
super.transform(tree)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
index 78e72cf771..df014b5161 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -151,8 +151,12 @@ abstract class Duplicators extends Analyzer {
ldef.symbol = newsym
debuglog("newsym: " + newsym + " info: " + newsym.info)
- case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) =>
- debuglog("ValDef " + name + " sym.info: " + vdef.symbol.info)
+ // don't retypecheck val members or local lazy vals -- you'll end up with duplicate symbols because
+ // entering a valdef results in synthesizing getters etc
+ // TODO: why retype check any valdefs?? I checked and the rhs is specialized just fine this way
+ // (and there are no args/type params/... to warrant full type checking?)
+ case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) || owner.isClass =>
+ debuglog(s"ValDef $name in $owner sym.info: ${vdef.symbol.info}")
invalidSyms(vdef.symbol) = vdef
val newowner = owner orElse context.owner
val newsym = vdef.symbol.cloneSymbol(newowner)
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index e0b64a7600..d11417192d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -5,6 +5,7 @@
package scala.tools.nsc
package typechecker
+import scala.reflect.NameTransformer
import symtab.Flags._
import scala.reflect.internal.util.StringOps.ojoin
import scala.reflect.internal.util.ListOfNil
@@ -116,38 +117,53 @@ trait MethodSynthesis {
import NamerErrorGen._
- def enterImplicitWrapper(tree: ClassDef): Unit = {
- enterSyntheticSym(ImplicitClassWrapper(tree).derivedTree)
- }
- // trees are later created by addDerivedTrees (common logic is encapsulated in field/standardAccessors/beanAccessors)
+ import treeInfo.noFieldFor
+
+ // populate synthetics for this unit with trees that will later be added by the typer
+ // we get here when entering the symbol for the valdef, so its rhs has not yet been type checked
def enterGetterSetter(tree: ValDef): Unit = {
+ val fieldSym =
+ if (noFieldFor(tree, owner)) NoSymbol
+ else owner.newValue(tree.name append NameTransformer.LOCAL_SUFFIX_STRING, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal)
+
val getter = Getter(tree)
val getterSym = getter.createSym
- val setterSym = if (getter.needsSetter) Setter(tree).createSym else NoSymbol
-
- // a lazy field is linked to its lazy accessor (TODO: can we do the same for field -> getter -> setter)
- val fieldSym = if (Field.noFieldFor(tree)) NoSymbol else Field(tree).createSym(getterSym)
// 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
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)
+ // the fields phase creates the field symbol
+ if (!tree.mods.isLazy) {
+ // if there's a field symbol, the getter is considered a synthetic that must be added later
+ // if there's no field symbol, the ValDef tree receives the getter symbol and thus is not a synthetic
+ if (fieldSym != NoSymbol) {
+ context.unit.synthetics(getterSym) = getter.derivedTree(getterSym)
+ getterSym setInfo namer.accessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = false)
+ } else getterSym setInfo namer.valTypeCompleter(tree)
+
+ enterInScope(getterSym)
+
+ if (getter.needsSetter) {
+ val setter = Setter(tree)
+ val setterSym = setter.createSym
+ context.unit.synthetics(setterSym) = setter.derivedTree(setterSym)
+ setterSym setInfo namer.accessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = true)
+ enterInScope(setterSym)
+ }
- val namer = if (fieldSym != NoSymbol) namerOf(fieldSym) else namerOf(getterSym)
-
- // There's no reliable way to detect all kinds of setters from flags or name!!!
- // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter
- // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors?
- val getterCompleter = namer.accessorTypeCompleter(tree, isSetter = false)
- val setterCompleter = namer.accessorTypeCompleter(tree, isSetter = true)
-
- getterSym setInfo getterCompleter
- setterSym andAlso (_ setInfo setterCompleter)
- fieldSym andAlso (_ setInfo namer.valTypeCompleter(tree))
-
- enterInScope(getterSym)
- setterSym andAlso (enterInScope(_))
- fieldSym andAlso (enterInScope(_))
+ // TODO: delay emitting the field to the fields phase (except for private[this] vals, which only get a field and no accessors)
+ if (fieldSym != NoSymbol) {
+ fieldSym setInfo namer.valTypeCompleter(tree)
+ enterInScope(fieldSym)
+ }
+ } else {
+ getterSym setInfo namer.valTypeCompleter(tree)
+ enterInScope(getterSym)
+ }
deriveBeanAccessors(tree, namer)
}
@@ -188,242 +204,82 @@ trait MethodSynthesis {
sym
}
- val getterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = false)
+ val getterCompleter = namer.accessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = false)
enterInScope(deriveBeanAccessor(if (hasBeanProperty) "get" else "is") setInfo getterCompleter)
if (tree.mods.isMutable) {
- val setterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = true)
+ val setterCompleter = namer.accessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = true)
enterInScope(deriveBeanAccessor("set") setInfo setterCompleter)
}
}
}
- import AnnotationInfo.{mkFilter => annotationFilter}
- def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
- case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar && !vd.symbol.isJava =>
- stat.symbol.initialize // needed!
-
- val getter = Getter(vd)
- getter.validate()
- val accessors = getter :: (if (getter.needsSetter) Setter(vd) :: Nil else Nil)
- (Field(vd) :: accessors).map(_.derivedTree).filter(_ ne EmptyTree)
-
- case cd @ ClassDef(mods, _, _, _) if mods.isImplicit =>
- val annotations = stat.symbol.initialize.annotations
- // TODO: need to shuffle annotations between wrapper and class.
- val wrapper = ImplicitClassWrapper(cd)
- val meth = wrapper.derivedSym
- context.unit.synthetics get meth match {
- case Some(mdef) =>
- context.unit.synthetics -= meth
- meth setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false))
- cd.symbol setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true))
- List(cd, mdef)
- case _ =>
- // Shouldn't happen, but let's give ourselves a reasonable error when it does
- context.error(cd.pos, s"Internal error: Symbol for synthetic factory method not found among ${context.unit.synthetics.keys.mkString(", ")}")
- // Soldier on for the sake of the presentation compiler
- List(cd)
- }
- case _ =>
- stat :: Nil
- }
-
-
- sealed trait Derived {
- /** The derived symbol. It is assumed that this symbol already exists and has been
- * entered in the parent scope when derivedSym is called
- */
- def derivedSym: Symbol
-
- /** The definition tree of the derived symbol. */
- def derivedTree: Tree
+ def enterImplicitWrapper(classDef: ClassDef): Unit = {
+ val methDef = factoryMeth(classDef.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC, classDef.name.toTermName, classDef)
+ val methSym = assignAndEnterSymbol(methDef)
+ context.unit.synthetics(methSym) = methDef
+ methSym setInfo implicitFactoryMethodCompleter(methDef, classDef.symbol, completerOf(methDef).asInstanceOf[LockingTypeCompleter])
}
- /** A synthetic method which performs the implicit conversion implied by
- * the declaration of an implicit class.
- */
- case class ImplicitClassWrapper(tree: ClassDef) extends Derived {
- def derivedSym = {
- val enclClass = tree.symbol.owner.enclClass
- // Only methods will do! Don't want to pick up any stray
- // companion objects of the same name.
- val result = enclClass.info decl derivedName filter (x => x.isMethod && x.isSynthetic)
- if (result == NoSymbol || result.isOverloaded)
- context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $derivedName in $enclClass / ${enclClass.info.decls}")
- result
- }
-
- def derivedTree = factoryMeth(derivedMods, derivedName, tree)
-
- def derivedName = tree.name.toTermName
- def derivedMods = tree.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC
- }
-
- trait DerivedAccessor extends Derived {
+ trait DerivedAccessor {
def tree: ValDef
def derivedName: TermName
def derivedFlags: Long
+ def derivedTree(sym: Symbol): Tree
def derivedPos = tree.pos.focus
def createSym = createMethod(tree, derivedName, derivedPos, derivedFlags)
}
case class Getter(tree: ValDef) extends DerivedAccessor {
- def derivedName = tree.name
-
- def derivedSym =
- if (tree.mods.isLazy) tree.symbol.lazyAccessor
- else if (Field.noFieldFor(tree)) tree.symbol
- else tree.symbol.getterIn(tree.symbol.enclClass)
-
+ def derivedName = tree.name
def derivedFlags = tree.mods.flags & GetterFlags | ACCESSOR.toLong | ( if (needsSetter) 0 else STABLE )
+ def needsSetter = tree.mods.isMutable // implies !lazy
- def needsSetter = tree.mods.isMutable // implies !lazy
-
- override def derivedTree =
- if (tree.mods.isLazy) deriveLazyAccessor
- else newDefDef(derivedSym, if (Field.noFieldFor(tree)) tree.rhs else Select(This(tree.symbol.enclClass), tree.symbol))(tpt = derivedTpt)
-
- /** Implements lazy value accessors:
- * - for lazy values of type Unit and all lazy fields inside traits,
- * the rhs is the initializer itself, because we'll just "compute" the result on every access
- * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap)
- * - for all other lazy values z the accessor is a block of this form:
- * { z = <rhs>; z } where z can be an identifier or a field.
- */
- private def deriveLazyAccessor: DefDef = {
- val ValDef(_, _, tpt0, rhs0) = tree
- val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0)
- val body =
- if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor
- else gen.mkAssignAndReturn(tree.symbol, rhs1)
-
- derivedSym setPos tree.pos // TODO: can we propagate `tree.pos` to `derivedSym` when the symbol is created?
- val ddefRes = DefDef(derivedSym, new ChangeOwnerTraverser(tree.symbol, derivedSym)(body))
- // ValDef will have its position focused whereas DefDef will have original correct rangepos
- // ideally positions would be correct at the creation time but lazy vals are really a special case
- // here so for the sake of keeping api clean we fix positions manually in LazyValGetter
- ddefRes.tpt.setPos(tpt0.pos)
- tpt0.setPos(tpt0.pos.focus)
- ddefRes
- }
+ override def derivedTree(derivedSym: Symbol) = {
+ val missingTpt = tree.tpt.isEmpty
+ val tpt = if (missingTpt) TypeTree() else tree.tpt.duplicate
- // TODO: more principled approach -- this is a bit bizarre
- private def derivedTpt = {
- // For existentials, don't specify a type for the getter, even one derived
- // from the symbol! This leads to incompatible existentials for the field and
- // the getter. Let the typer do all the work. You might think "why only for
- // existentials, why not always," and you would be right, except: a single test
- // fails, but it looked like some work to deal with it. Test neg/t0606.scala
- // starts compiling (instead of failing like it's supposed to) because the typer
- // expects to be able to identify escaping locals in typedDefDef, and fails to
- // spot that brand of them. In other words it's an artifact of the implementation.
- //
- // JZ: ... or we could go back to uniformly using explicit result types in all cases
- // if we fix `dropExistential`. More details https://github.com/scala/scala-dev/issues/165
- val getterTp = derivedSym.tpe_*.finalResultType
- // Range position errors ensue if we don't duplicate this in some
- // circumstances (at least: concrete vals with existential types.)
- def inferredTpt = TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus)
- val tpt = getterTp match {
- case _: ExistentialType => inferredTpt
- case _ => getterTp.widen match {
- case _: ExistentialType => inferredTpt
- case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
- case _ => TypeTree(getterTp)
- }
- }
- tpt setPos tree.tpt.pos.focus
- }
+ val rhs =
+ if (noFieldFor(tree, owner)) tree.rhs // context.unit.transformed.getOrElse(tree.rhs, tree.rhs)
+ else Select(This(tree.symbol.enclClass), tree.symbol)
- def validate() = {
- assert(derivedSym != NoSymbol, tree)
- if (derivedSym.isOverloaded)
- GetterDefinedTwiceError(derivedSym)
+ newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = Nil, tpt = tpt)
}
+// derivedSym setPos tree.pos
+// // ValDef will have its position focused whereas DefDef will have original correct rangepos
+// // ideally positions would be correct at the creation time but lazy vals are really a special case
+// // here so for the sake of keeping api clean we fix positions manually in LazyValGetter
+// tpt.setPos(tree.tpt.pos)
+// tree.tpt.setPos(tree.tpt.pos.focus)
+
}
case class Setter(tree: ValDef) extends DerivedAccessor {
def derivedName = tree.setterName
- def derivedSym = tree.symbol.setterIn(tree.symbol.enclClass)
def derivedFlags = tree.mods.flags & SetterFlags | ACCESSOR
- def derivedTree =
- derivedSym.paramss match {
- case (setterParam :: Nil) :: _ =>
- // assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for ${tree.symbol} in ${tree.symbol.enclClass}")
- val rhs =
- if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree
- else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam))
-
- DefDef(derivedSym, rhs)
- case _ => EmptyTree
- }
- }
-
- object Field {
- // No field for these vals (either never emitted or eliminated later on):
- // - abstract vals have no value we could store (until they become concrete, potentially)
- // - lazy vals of type Unit
- // - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value)
- // Constructors will move the assignment to the constructor, abstracting over the field using the field setter,
- // and Fields will add a field to the class that mixes in the trait, implementing the accessors in terms of it
- // - [Emitted, later removed during Constructors] a concrete val with a statically known value (ConstantType)
- // performs its side effect according to lazy/strict semantics, but doesn't need to store its value
- // each access will "evaluate" the RHS (a literal) again
- // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer.
- // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does.
- // NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`),
- // similarly, the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite)
- // as the symbol info is in the process of being created then.
- // TODO: harmonize tree & symbol creation
- // the middle `&& !owner.isTrait` is needed after `isLazy` because non-unit-typed lazy vals in traits still get a field -- see neg/t5455.scala
- def noFieldFor(vd: ValDef) = (vd.mods.isDeferred
- || (vd.mods.isLazy && !owner.isTrait && isUnitType(vd.symbol.info))
- || (owner.isTrait && !traitFieldFor(vd)))
-
- // TODO: never emit any fields in traits -- only use getter for lazy/presuper ones as well
- private def traitFieldFor(vd: ValDef): Boolean = vd.mods.hasFlag(PRESUPER | LAZY)
- }
+ def derivedTree(derivedSym: Symbol) = {
+ val setterParam = nme.syntheticParamName(1)
- case class Field(tree: ValDef) extends Derived {
- private val isLazy = tree.mods.isLazy
-
- // If the owner is not a class, this is a lazy val from a method,
- // with no associated field. It has an accessor with $lzy appended to its name and
- // its flags are set differently. The implicit flag is reset because otherwise
- // a local implicit "lazy val x" will create an ambiguity with itself
- // via "x$lzy" as can be seen in test #3927.
- private val localLazyVal = isLazy && !owner.isClass
- private val nameSuffix =
- if (!localLazyVal) reflect.NameTransformer.LOCAL_SUFFIX_STRING
- else reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING
-
- def derivedName = tree.name.append(nameSuffix)
-
- def createSym(getter: MethodSymbol) = {
- val sym = owner.newValue(derivedName, tree.pos, derivedMods.flags)
- if (isLazy) sym setLazyAccessor getter
- sym
- }
+ // note: tree.tpt may be EmptyTree, which will be a problem when use as the tpt of a parameter
+ // the completer will patch this up (we can't do this now without completing the field)
+ val missingTpt = tree.tpt.isEmpty
+ val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate
- def derivedSym = tree.symbol
+ val vparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree))
- def derivedMods =
- if (!localLazyVal) tree.mods & FieldFlags | PrivateLocal | (if (isLazy) MUTABLE else 0)
- else (tree.mods | ARTIFACT | MUTABLE) & ~IMPLICIT
+ val tpt = TypeTree(UnitTpe)
- // TODO: why is this different from the symbol!?
- private def derivedModsForTree = tree.mods | PrivateLocal
+ val rhs =
+ if (noFieldFor(tree, owner)) EmptyTree
+ else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam))
- def derivedTree =
- if (Field.noFieldFor(tree)) EmptyTree
- else if (isLazy) copyValDef(tree)(mods = derivedModsForTree, name = derivedName, rhs = EmptyTree).setPos(tree.pos.focus)
- else copyValDef(tree)(mods = derivedModsForTree, name = derivedName)
+ newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt)
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 99c1b6991e..033b6cc0cd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -129,6 +129,7 @@ trait Namers extends MethodSynthesis {
!(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter
!isEnumConstant(vd) // enums can only occur in classes, so only check here
+
/** Determines whether this field holds an enum constant.
* To qualify, the following conditions must be met:
* - The field's class has the ENUM flag set
@@ -803,86 +804,89 @@ trait Namers extends MethodSynthesis {
import AnnotationInfo.{mkFilter => annotationFilter}
- def valTypeCompleter(tree: ValDef) = mkTypeCompleter(tree) { sym =>
- val annots =
- if (tree.mods.annotations.isEmpty) Nil
- else annotSig(tree.mods.annotations) filter annotationFilter(FieldTargetClass, !tree.mods.isParamAccessor)
+ def implicitFactoryMethodCompleter(tree: DefDef, classSym: Symbol, sigCompleter: LockingTypeCompleter) = mkTypeCompleter(tree) { methSym =>
+ sigCompleter.completeImpl(methSym)
- sym setInfo typeSig(tree, annots)
+ val annotations = classSym.initialize.annotations
- validate(sym)
+ methSym setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false))
+ classSym setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true))
}
- /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
- def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym =>
- // println(s"triaging for ${sym.debugFlagString} $sym from $valAnnots to $annots")
-
- // typeSig calls valDefSig (because tree: ValDef)
- // sym is an accessor, while tree is the field (which may have the same symbol as the getter, or maybe it's the field)
- // TODO: can we make this work? typeSig is called on same tree (valdef) to complete info for field and all its accessors
- // reuse work done in valTypeCompleter if we already computed the type signature of the val
- // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait)
-// val valSig =
-// if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info
-// else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig
-
- val valSig = typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig
-
- val sig = accessorSigFromFieldTp(sym, isSetter, valSig)
+ // complete the type of a value definition (may have a method symbol, for those valdefs that never receive a field,
+ // as specified by Field.noFieldFor)
+ def valTypeCompleter(tree: ValDef) = mkTypeCompleter(tree) { fieldOrGetterSym =>
val mods = tree.mods
- if (mods.annotations.nonEmpty) {
- val annotSigs = annotSig(mods.annotations)
-
- // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis
- // (TODO: can we look at symbols earlier?)
- if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot))
- && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr)))
- BeanPropertyAnnotationLimitationError(tree)
+ val isGetter = fieldOrGetterSym.isMethod
+ val annots =
+ if (mods.annotations.isEmpty) Nil
+ else {
+ val annotSigs = annotSig(mods.annotations)
+ if (isGetter) filterAccessorAnnots(annotSigs, tree) // if this is really a getter, retain annots targeting either field/getter
+ else annotSigs filter annotationFilter(FieldTargetClass, !mods.isParamAccessor)
+ }
- sym setAnnotations (annotSigs filter filterAccessorAnnotations(isSetter))
- }
+ // must use typeSig, not memberSig (TODO: when do we need to switch namers?)
+ val sig = typeSig(tree, annots)
- sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym)
+ fieldOrGetterSym setInfo (if (isGetter) NullaryMethodType(sig) else sig)
- validate(sym)
+ validate(fieldOrGetterSym)
}
- /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
- def beanAccessorTypeCompleter(tree: ValDef, missingTpt: Boolean, isSetter: Boolean) = mkTypeCompleter(tree) { sym =>
- context.unit.synthetics get sym match {
+ // knowing `isBean`, we could derive `isSetter` from `valDef.name`
+ def accessorTypeCompleter(valDef: ValDef, missingTpt: Boolean, isBean: Boolean, isSetter: Boolean) = mkTypeCompleter(valDef) { accessorSym =>
+ context.unit.synthetics get accessorSym match {
case Some(ddef: DefDef) =>
- // sym is an accessor, while tree is the field (for traits it's actually the getter, and we're completing the setter)
+ // `accessorSym` is the accessor for which we're completing the info (tree == ddef),
+ // while `valDef` is the field definition that spawned the accessor
+ // NOTE: `valTypeCompleter` handles abstract vals, trait vals and lazy vals, where the ValDef carries the getter's symbol
+
// reuse work done in valTypeCompleter if we already computed the type signature of the val
// (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait)
val valSig =
- if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info
- else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig
+ if ((accessorSym ne valDef.symbol) && valDef.symbol.isInitialized) valDef.symbol.info
+ else typeSig(valDef, Nil) // don't set annotations for the valdef -- we just want to compute the type sig (TODO: dig deeper and see if we can use memberSig)
// patch up the accessor's tree if the valdef's tpt was not known back when the tree was synthesized
- if (missingTpt) { // can't look at tree.tpt here because it may have been completed by now
+ // can't look at `valDef.tpt` here because it may have been completed by now (this is why we pass in `missingTpt`)
+ // HACK: a param accessor `ddef.tpt.tpe` somehow gets out of whack with `accessorSym.info`, so always patch it back...
+ // (the tpt is typed in the wrong namer, using the class as owner instead of the outer context, which is where param accessors should be typed)
+ if (missingTpt || accessorSym.isParamAccessor) {
if (!isSetter) ddef.tpt setType valSig
else if (ddef.vparamss.nonEmpty && ddef.vparamss.head.nonEmpty) ddef.vparamss.head.head.tpt setType valSig
- else throw new TypeError(tree.pos, s"Internal error: could not complete parameter/return type for $ddef from $sym")
+ else throw new TypeError(valDef.pos, s"Internal error: could not complete parameter/return type for $ddef from $accessorSym")
}
+ val mods = valDef.mods
val annots =
- if (tree.mods.annotations.isEmpty) Nil
- else annotSig(tree.mods.annotations) filter filterBeanAccessorAnnotations(isSetter)
+ if (mods.annotations.isEmpty) Nil
+ else filterAccessorAnnots(annotSig(mods.annotations), valDef, isSetter, isBean)
- val sig = typeSig(ddef, annots)
+ // for a setter, call memberSig to attribute the parameter (for a bean, we always use the regular method sig completer since they receive method types)
+ // for a regular getter, make sure it gets a NullaryMethodType (also, no need to recompute it: we already have the valSig)
+ val sig =
+ if (isSetter || isBean) typeSig(ddef, annots)
+ else {
+ if (annots.nonEmpty) annotate(accessorSym, annots)
- sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym)
+ NullaryMethodType(valSig)
+ }
+
+ accessorSym setInfo pluginsTypeSigAccessor(sig, typer, valDef, accessorSym)
- validate(sym)
+ if (!isBean && accessorSym.isOverloaded)
+ if (isSetter) ddef.rhs.setType(ErrorType)
+ else GetterDefinedTwiceError(accessorSym)
+
+ validate(accessorSym)
case _ =>
- throw new TypeError(tree.pos, s"Internal error: no synthetic tree found for bean accessor $sym")
+ throw new TypeError(valDef.pos, s"Internal error: no synthetic tree found for bean accessor $accessorSym")
}
-
}
-
// see scala.annotation.meta's package class for more info
// Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param.
// The defaults are:
@@ -893,24 +897,33 @@ trait Namers extends MethodSynthesis {
//
// TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit?
// (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter)
- private def filterAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean =
- if (isSetter || !owner.isTrait)
- annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false)
- else (ann =>
- annotationFilter(FieldTargetClass, defaultRetention = true)(ann) ||
- annotationFilter(GetterTargetClass, defaultRetention = true)(ann))
-
- private def filterBeanAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean =
- if (isSetter || !owner.isTrait)
- annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false)
- else (ann =>
- annotationFilter(FieldTargetClass, defaultRetention = true)(ann) ||
- annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann))
-
-
- private def accessorSigFromFieldTp(sym: Symbol, isSetter: Boolean, tp: Type): Type =
- if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe)
- else NullaryMethodType(tp)
+ private def filterAccessorAnnots(annotSigs: List[global.AnnotationInfo], tree: global.ValDef, isSetter: Boolean = false, isBean: Boolean = false): List[AnnotationInfo] = {
+ val mods = tree.mods
+ if (!isBean) {
+ // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis
+ // (TODO: can we look at symbols earlier?)
+ if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot))
+ && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr)))
+ BeanPropertyAnnotationLimitationError(tree)
+ }
+
+ def filterAccessorAnnotations: AnnotationInfo => Boolean =
+ if (isSetter || !owner.isTrait)
+ annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false)
+ else (ann =>
+ annotationFilter(FieldTargetClass, defaultRetention = true)(ann) ||
+ annotationFilter(GetterTargetClass, defaultRetention = true)(ann))
+
+ def filterBeanAccessorAnnotations: AnnotationInfo => Boolean =
+ if (isSetter || !owner.isTrait)
+ annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false)
+ else (ann =>
+ annotationFilter(FieldTargetClass, defaultRetention = true)(ann) ||
+ annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann))
+
+ annotSigs filter (if (isBean) filterBeanAccessorAnnotations else filterAccessorAnnotations)
+ }
+
def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym =>
val selftpe = typer.typedType(tree).tpe
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 8449260fe6..8034d056d7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -450,9 +450,9 @@ abstract class RefChecks extends Transform {
} else if (other.isStable && !member.isStable) { // (1.4)
overrideError("needs to be a stable, immutable value")
} else if (member.isValue && member.isLazy &&
- other.isValue && !other.isSourceMethod && !other.isDeferred && !other.isLazy) {
+ other.isValue && other.hasFlag(STABLE) && !(other.isDeferred || other.isLazy)) {
overrideError("cannot override a concrete non-lazy value")
- } else if (other.isValue && other.isLazy && !other.isSourceMethod && !other.isDeferred && // !(other.hasFlag(MODULE) && other.hasFlag(PACKAGE | JAVA)) && other.hasFlag(LAZY) && (!other.isMethod || other.hasFlag(STABLE)) && !other.hasFlag(DEFERRED)
+ } else if (other.isValue && other.isLazy &&
member.isValue && !member.isLazy) {
overrideError("must be declared lazy to override a concrete lazy value")
} else if (other.isDeferred && member.isTermMacro && member.extendedOverriddenSymbols.forall(_.isDeferred)) { // (1.9)
@@ -609,7 +609,7 @@ abstract class RefChecks extends Transform {
val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m))
// Group missing members by the name of the underlying symbol,
// to consolidate getters and setters.
- val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name)
+ val grouped = missing groupBy (_.name.getterName)
val missingMethods = grouped.toList flatMap {
case (name, syms) =>
if (syms exists (_.isSetter)) syms filterNot (_.isGetter)
@@ -651,15 +651,16 @@ abstract class RefChecks extends Transform {
// Give a specific error message for abstract vars based on why it fails:
// It could be unimplemented, have only one accessor, or be uninitialized.
- if (underlying.isVariable) {
- val isMultiple = grouped.getOrElse(underlying.name, Nil).size > 1
+ val groupedAccessors = grouped.getOrElse(member.name.getterName, Nil)
+ val isMultiple = groupedAccessors.size > 1
+ if (groupedAccessors.exists(_.isSetter) || (member.isGetter && !isMultiple && member.setterIn(member.owner).exists)) {
// If both getter and setter are missing, squelch the setter error.
if (member.isSetter && isMultiple) ()
else undefined(
if (member.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)"
else if (member.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)"
- else analyzer.abstractVarMessage(member)
+ else "\n(Note that variables need to be initialized to be defined)"
)
}
else if (underlying.isMethod) {
@@ -919,17 +920,11 @@ abstract class RefChecks extends Transform {
var index = -1
for (stat <- stats) {
index = index + 1
- def enterSym(sym: Symbol) = if (sym.isLocalToBlock) {
- currentLevel.scope.enter(sym)
- symIndex(sym) = index
- }
stat match {
- case DefDef(_, _, _, _, _, _) if stat.symbol.isLazy =>
- enterSym(stat.symbol)
- case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) =>
- //assert(stat.symbol != NoSymbol, stat);//debug
- enterSym(stat.symbol.lazyAccessorOrSelf)
+ case _ : MemberDef if stat.symbol.isLocalToBlock =>
+ currentLevel.scope.enter(stat.symbol)
+ symIndex(stat.symbol) = index
case _ =>
}
}
@@ -1180,10 +1175,10 @@ abstract class RefChecks extends Transform {
val tree1 = transform(tree) // important to do before forward reference check
if (tree1.symbol.isLazy) tree1 :: Nil
else {
- val lazySym = tree.symbol.lazyAccessorOrSelf
- if (lazySym.isLocalToBlock && index <= currentLevel.maxindex) {
+ val sym = tree.symbol
+ if (sym.isLocalToBlock && index <= currentLevel.maxindex) {
debuglog("refsym = " + currentLevel.refsym)
- reporter.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym)
+ reporter.error(currentLevel.refpos, "forward reference extends over definition of " + sym)
}
tree1 :: Nil
}
@@ -1451,9 +1446,9 @@ abstract class RefChecks extends Transform {
)
}
- sym.isSourceMethod &&
+ sym.name == nme.apply &&
+ !(sym hasFlag STABLE) && // ???
sym.isCase &&
- sym.name == nme.apply &&
isClassTypeAccessible(tree) &&
!tree.tpe.finalResultType.typeSymbol.primaryConstructor.isLessAccessibleThan(tree.symbol)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index bee327c760..b66dbf21c0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -97,7 +97,7 @@ trait TypeDiagnostics {
/** An explanatory note to be added to error messages
* when there's a problem with abstract var defs */
def abstractVarMessage(sym: Symbol): String =
- if (underlyingSymbol(sym).isVariable)
+ if (sym.isSetter || sym.isGetter && sym.setterIn(sym.owner).exists)
"\n(Note that variables need to be initialized to be defined)"
else ""
@@ -140,7 +140,7 @@ trait TypeDiagnostics {
* TODO: is it wise to create new symbols simply to generate error message? is this safe in interactive/resident mode?
*/
def underlyingSymbol(member: Symbol): Symbol =
- if (!member.hasAccessorFlag || member.owner.isTrait) member
+ if (!member.hasAccessorFlag || member.accessed == NoSymbol) member
else if (!member.isDeferred) member.accessed
else {
val getter = if (member.isSetter) member.getterIn(member.owner) else member
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index a95ecd360c..3360599c1b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -128,6 +128,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def canTranslateEmptyListToNil = true
def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree
+ // used to exempt synthetic accessors (i.e. those that are synthesized by the compiler to access a field)
+ // from skolemization because there's a weird bug that causes spurious type mismatches
+ // (it seems to have something to do with existential abstraction over values
+ // https://github.com/scala/scala-dev/issues/165
+ // when we're past typer, lazy accessors are synthetic, but before they are user-defined
+ // to make this hack less hacky, we could rework our flag assignment to allow for
+ // requiring both the ACCESSOR and the SYNTHETIC bits to trigger the exemption
+ private def isSyntheticAccessor(sym: Symbol) = sym.isAccessor && (!sym.isLazy || isPastTyper)
+
// when type checking during erasure, generate erased types in spots that aren't transformed by erasure
// (it erases in TypeTrees, but not in, e.g., the type a Function node)
def phasedAppliedType(sym: Symbol, args: List[Type]) = {
@@ -1159,7 +1168,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
adapt(tree setType restpe, mode, pt, original)
case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2)
adapt(tree setType arg, mode, pt, original)
- case tp if mode.typingExprNotLhs && isExistentialType(tp) =>
+ case tp if mode.typingExprNotLhs && isExistentialType(tp) && !isSyntheticAccessor(context.owner) =>
adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original)
case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode) && !context.inTypeConstructorAllowed => // (3)
// assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
@@ -1373,13 +1382,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.")
else if (stat.symbol != null && stat.symbol.isParamAccessor)
notAllowed("additional parameter")
- // concrete accessor (getter) in trait corresponds to a field definition (neg/anytrait.scala)
- // TODO: only reject accessors that actually give rise to field (e.g., a constant-type val is fine)
- else if (!isValueClass && stat.symbol.isAccessor && !stat.symbol.isDeferred)
- notAllowed("field definition")
checkEphemeralDeep.traverse(rhs)
- // for value class or "exotic" vals in traits
- // (traits don't receive ValDefs for regular vals until fields phase -- well, except for early initialized/lazy vals)
case _: ValDef =>
notAllowed("field definition")
case _: ModuleDef =>
@@ -1956,11 +1959,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
- val body2 = {
- val body2 =
- if (isPastTyper || reporter.hasErrors) body1
- else body1 flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
- val primaryCtor = treeInfo.firstConstructor(body2)
+ val bodyWithPrimaryCtor = {
+ val primaryCtor = treeInfo.firstConstructor(body1)
val primaryCtor1 = primaryCtor match {
case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
val argss = superArgs(parents1.head) getOrElse Nil
@@ -1969,10 +1969,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos
case _ => primaryCtor
}
- body2 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
+ body1 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
}
- val body3 = typedStats(body2, templ.symbol)
+ val body3 = typedStats(bodyWithPrimaryCtor, templ.symbol)
if (clazz.info.firstParent.typeSymbol == AnyValClass)
validateDerivedValueClass(clazz, body3)
@@ -2436,13 +2436,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ =>
}
}
- val stats1 = if (isPastTyper) block.stats else
- block.stats.flatMap {
- case vd@ValDef(_, _, _, _) if vd.symbol.isLazy =>
- namer.addDerivedTrees(Typer.this, vd)
- case stat => stat::Nil
- }
- val stats2 = typedStats(stats1, context.owner, warnPure = false)
+ val statsTyped = typedStats(block.stats, context.owner, warnPure = false)
val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt)
// sanity check block for unintended expr placement
@@ -2456,18 +2450,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def checkPure(t: Tree, supple: Boolean): Unit =
if (treeInfo.isPureExprForWarningPurposes(t)) {
val msg = "a pure expression does nothing in statement position"
- val parens = if (stats2.length + count > 1) "multiline expressions might require enclosing parentheses" else ""
+ val parens = if (statsTyped.length + count > 1) "multiline expressions might require enclosing parentheses" else ""
val discard = if (adapted) "; a value can be silently discarded when Unit is expected" else ""
val text =
if (supple) s"${parens}${discard}"
else if (!parens.isEmpty) s"${msg}; ${parens}" else msg
context.warning(t.pos, text)
}
- stats2.foreach(checkPure(_, supple = false))
+ statsTyped.foreach(checkPure(_, supple = false))
if (result0.nonEmpty) checkPure(result0, supple = true)
}
- treeCopy.Block(block, stats2, expr1)
+ treeCopy.Block(block, statsTyped, expr1)
.setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst)
} finally {
// enable escaping privates checking from the outside and recycle
@@ -3171,6 +3165,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case (ClassDef(cmods, cname, _, _), DefDef(dmods, dname, _, _, _, _)) =>
cmods.isImplicit && dmods.isImplicit && cname.toTermName == dname
+ // ValDef and Accessor
+ case (ValDef(_, cname, _, _), DefDef(_, dname, _, _, _, _)) =>
+ cname.getterName == dname.getterName
+
case _ => false
}
@@ -4455,8 +4453,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def narrowRhs(tp: Type) = { val sym = context.tree.symbol
context.tree match {
case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol =>
- val sym1 = if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner)
- else sym.lazyAccessorOrSelf
+ val sym1 =
+ if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner)
+ else sym
val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix
intersectionType(List(tp, singleType(pre, sym1)))
case _ => tp