diff options
Diffstat (limited to 'src/compiler')
9 files changed, 170 insertions, 94 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 3e53419573..466793010f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -73,9 +73,11 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { /* ------ (4) exception-handler version of monitor-exit code. * Reached upon abrupt termination of (2). * Protected by whatever protects the whole synchronized expression. + * null => "any" exception in bytecode, like we emit for finally. + * Important not to use j/l/Throwable which dooms the method to a life of interpretation! (SD-233) * ------ */ - protect(startProtected, endProtected, currProgramPoint(), jlThrowableRef) + protect(startProtected, endProtected, currProgramPoint(), null) locals.load(monitor) emit(asm.Opcodes.MONITOREXIT) emit(asm.Opcodes.ATHROW) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 09e82de89b..edb75514e8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -114,7 +114,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { if (classSym == NothingClass) srNothingRef else if (classSym == NullClass) srNullRef else { - val internalName = classSym.javaBinaryName.toString + val internalName = classSym.javaBinaryNameString classBTypeFromInternalName.getOrElse(internalName, { // The new ClassBType is added to the map in its constructor, before we set its info. This // allows initializing cyclic dependencies, see the comment on variable ClassBType._info. @@ -625,7 +625,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { */ def mirrorClassClassBType(moduleClassSym: Symbol): ClassBType = { assert(isTopLevelModuleClass(moduleClassSym), s"not a top-level module class: $moduleClassSym") - val internalName = moduleClassSym.javaBinaryName.dropModule.toString + val internalName = moduleClassSym.javaBinaryNameString.stripSuffix(nme.MODULE_SUFFIX_STRING) classBTypeFromInternalName.getOrElse(internalName, { val c = ClassBType(internalName) // class info consistent with BCodeHelpers.genMirrorClass @@ -642,7 +642,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { } def beanInfoClassClassBType(mainClass: Symbol): ClassBType = { - val internalName = mainClass.javaBinaryName.toString + "BeanInfo" + val internalName = mainClass.javaBinaryNameString + "BeanInfo" classBTypeFromInternalName.getOrElse(internalName, { val c = ClassBType(internalName) c.info = Right(ClassInfo( diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index 0a54767f76..6593d4b725 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -337,7 +337,7 @@ abstract class GenBCode extends BCodeSyncAndTry { bTypes.initializeCoreBTypes() bTypes.javaDefinedClasses.clear() bTypes.javaDefinedClasses ++= currentRun.symSource collect { - case (sym, _) if sym.isJavaDefined => sym.javaBinaryName.toString + case (sym, _) if sym.isJavaDefined => sym.javaBinaryNameString } Statistics.stopTimer(BackendStats.bcodeInitTimer, initStart) diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 8d362f13dd..daf645fd20 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -527,7 +527,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme super.transform(tree) else if (canBeSupplanted(tree.symbol)) gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos - else if (tree.symbol.outerSource == clazz) + else if (tree.symbol.outerSource == clazz && !isDelayedInitSubclass) gen.mkAttributedIdent(parameterNamed(nme.OUTER)) setPos tree.pos else super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 69240b07a1..cc234eb623 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -258,7 +258,7 @@ abstract class Erasure extends InfoTransform // Anything which could conceivably be a module (i.e. isn't known to be // a type parameter or similar) must go through here or the signature is // likely to end up with Foo<T>.Empty where it needs Foo<T>.Empty$. - def fullNameInSig(sym: Symbol) = "L" + enteringJVM(sym.javaBinaryName) + def fullNameInSig(sym: Symbol) = "L" + enteringJVM(sym.javaBinaryNameString) def jsig(tp0: Type, existentiallyBound: List[Symbol] = Nil, toplevel: Boolean = false, primitiveOK: Boolean = true): String = { val tp = tp0.dealias diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index f3d5ceb0f0..7d50c12852 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -8,7 +8,7 @@ package tools.nsc package transform import symtab._ -import Flags.{ CASE => _, _ } +import Flags.{CASE => _, _} import scala.collection.mutable.ListBuffer /** This class ... diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index a383b65192..aa2ccd9788 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -22,7 +22,7 @@ import symtab.Flags._ * in the first (closest in the subclassing lattice) subclass (not a trait) of a trait. * * For lazy vals and modules, we emit accessors that using double-checked locking (DCL) to balance thread safety - * and performance. A lazy val gets a compute method for the DCL's slow path, for a module it's all done in the accessor. + * and performance. For both lazy vals and modules, the a compute method contains the DCL's slow path. * * Local lazy vals do not receive bitmaps, but use a Lazy*Holder that has the volatile init bit and the computed value. * See `mkLazyLocalDef`. @@ -193,23 +193,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // not be emitted as ACC_FINAL. They are FINAL in the Scala sense, though: cannot be overridden. private final val ModuleOrLazyFieldFlags = FINAL | PrivateLocal | SYNTHETIC | NEEDS_TREES - private def newModuleVarSymbol(owner: Symbol, module: Symbol, tp: Type): TermSymbol = { -// println(s"new module var in $site for $module of type $tp") - val flags = MODULEVAR | (if (owner.isClass) ModuleOrLazyFieldFlags else 0) - - val moduleVar = - (owner.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, flags) - setInfo tp - addAnnotation VolatileAttr) - - moduleOrLazyVarOf(module) = moduleVar - - moduleVar - } - - private def moduleInit(module: Symbol) = { + private def moduleInit(module: Symbol, moduleVar: Symbol) = { // println(s"moduleInit for $module in ${module.ownerChain} --> ${moduleVarOf.get(module)}") - val moduleVar = moduleOrLazyVarOf(module) def moduleVarRef = gen.mkAttributedRef(moduleVar) // for local modules, we synchronize on the owner of the method that owns the module @@ -236,7 +221,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor * * TODO: optimize using local variable? */ - Block(If(needsInit, gen.mkSynchronized(monitorHolder)(If(needsInit, init, EmptyTree)), EmptyTree) :: Nil, moduleVarRef) + val computeName = nme.newLazyValSlowComputeName(module.name) + val computeMethod = DefDef(NoMods, computeName, Nil, ListOfNil, TypeTree(UnitTpe), gen.mkSynchronized(monitorHolder)(If(needsInit, init, EmptyTree))) + Block(computeMethod :: If(needsInit, Apply(Ident(computeName), Nil), EmptyTree) :: Nil, + gen.mkCast(moduleVarRef, module.info.resultType)) } // NoSymbol for lazy accessor sym with unit result type @@ -303,6 +291,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor lazyCallingSuper setInfo tp } + private def classNeedsInfoTransform(cls: Symbol): Boolean = { + !(cls.isPackageClass || cls.isJavaDefined) && (currentRun.compiles(cls) || refChecks.isSeparatelyCompiledScalaSuperclass(cls)) + } + 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) @@ -358,9 +350,12 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor ClassInfoType(parents, allDecls, clazz) } else tp + + case tp@ClassInfoType(parents, oldDecls, clazz) if !classNeedsInfoTransform(clazz) => tp + // mix in fields & accessors for all mixed in traits + case tp@ClassInfoType(parents, oldDecls, clazz) => - case tp@ClassInfoType(parents, oldDecls, clazz) if !clazz.isPackageClass => val site = clazz.thisType // setter conflicts cannot arise independently from a getter conflict, since a setter without a getter does not a val definition make @@ -371,8 +366,16 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor (existingGetter ne NoSymbol) && (tp matches (site memberInfo existingGetter).resultType) // !existingGetter.isDeferred && -- see (3) } - def newModuleVarMember(member: Symbol): TermSymbol = - newModuleVarSymbol(clazz, member, site.memberType(member).resultType) + def newModuleVarMember(module: Symbol): TermSymbol = { + val moduleVar = + (clazz.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, MODULEVAR | ModuleOrLazyFieldFlags) + setInfo site.memberType(module).resultType + addAnnotation VolatileAttr) + + moduleOrLazyVarOf(module) = moduleVar + + moduleVar + } def newLazyVarMember(member: Symbol): TermSymbol = Fields.this.newLazyVarMember(clazz, member, site.memberType(member).resultType) @@ -522,7 +525,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor def mkTypedValDef(sym: Symbol, rhs: Tree = EmptyTree) = typedPos(sym.pos)(ValDef(sym, rhs)).asInstanceOf[ValDef] /** - * Desugar a local `lazy val x: Int = rhs` into + * Desugar a local `lazy val x: Int = rhs` + * or a local `object x { ...}` (the rhs will be instantiating the module's class) into: + * * ``` * val x$lzy = new scala.runtime.LazyInt() * def x$lzycompute(): Int = @@ -532,22 +537,24 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor * } * def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute() * ``` + * + * The expansion is the same for local lazy vals and local objects, + * except for the suffix of the underlying val's name ($lzy or $module) */ - private def mkLazyLocalDef(lazyVal: Symbol, rhs: Tree): Tree = { + private def mkLazyLocalDef(lazySym: Symbol, rhs: Tree): Tree = { import CODE._ - import scala.reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING - val owner = lazyVal.owner + import scala.reflect.{NameTransformer => nx} + val owner = lazySym.owner - val lazyValType = lazyVal.tpe.resultType + val lazyValType = lazySym.tpe.resultType val refClass = lazyHolders.getOrElse(lazyValType.typeSymbol, LazyRefClass) val isUnit = refClass == LazyUnitClass val refTpe = if (refClass != LazyRefClass) refClass.tpe else appliedType(refClass.typeConstructor, List(lazyValType)) - val lazyName = lazyVal.name.toTermName - val pos = lazyVal.pos.focus + val lazyName = lazySym.name.toTermName + val pos = lazySym.pos.focus - // used twice: once in the same owner as the lazy val, another time inside the compute method - val localLazyName = lazyName append LAZY_LOCAL_SUFFIX_STRING + val localLazyName = lazyName append (if (lazySym.isModule) nx.MODULE_VAR_SUFFIX_STRING else nx.LAZY_LOCAL_SUFFIX_STRING) // The lazy holder val need not be mutable, as we write to its field. // In fact, it MUST not be mutable to avoid capturing it as an ObjectRef in lambdalift @@ -567,14 +574,14 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val computerSym = owner.newMethod(lazyName append nme.LAZY_SLOW_SUFFIX, pos, ARTIFACT | PRIVATE) setInfo MethodType(Nil, lazyValType) - val rhsAtComputer = rhs.changeOwner(lazyVal -> computerSym) + val rhsAtComputer = rhs.changeOwner(lazySym -> computerSym) val computer = mkAccessor(computerSym)(gen.mkSynchronized(Ident(holderSym))( If(initialized, getValue, if (isUnit) Block(rhsAtComputer :: Nil, Apply(initialize, Nil)) else Apply(initialize, rhsAtComputer :: Nil)))) - val accessor = mkAccessor(lazyVal)( + val accessor = mkAccessor(lazySym)( If(initialized, getValue, Apply(Ident(computerSym), Nil))) @@ -582,68 +589,87 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // remove STABLE: prevent replacing accessor call of type Unit by BoxedUnit.UNIT in erasure // remove ACCESSOR: prevent constructors from eliminating the method body if the lazy val is // lifted into a trait (TODO: not sure about the details here) - lazyVal.resetFlag(STABLE | ACCESSOR) + lazySym.resetFlag(STABLE | ACCESSOR) Thicket(mkTypedValDef(holderSym, New(refTpe)) :: computer :: accessor :: Nil) } // 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): List[Tree] = { - val fieldName = accessor.localName - val field = clazz.info.decl(fieldName) - // The `None` result denotes an error, but it's refchecks' job to report it (this fallback is for robustness). - // This is the result of overriding a val with a def, so that no field is found in the subclass. - if (field.exists) List(Select(This(clazz), field)) - else Nil - } - - def getterBody(getter: Symbol): List[Tree] = { + def fieldsAndAccessors(clazz: Symbol): List[Tree] = { + // scala/scala-dev#219 + // Cast to avoid spurious mismatch in paths containing trait vals that have + // not been rebound to accessors in the subclass we're in now. + // For example, for a lazy val mixed into a class, the lazy var's info + // will not refer to symbols created during our info transformer, + // so if its type depends on a val that is now implemented after the info transformer, + // we'll get a mismatch when assigning `rhs` to `lazyVarOf(getter)`. + // TODO: could we rebind more aggressively? consider overriding in type equality? + def cast(tree: Tree, pt: Type) = gen.mkAsInstanceOf(tree, pt) + + // Could be NoSymbol, which denotes an error, but it's refchecks' job to report it (this fallback is for robustness). + // This is the result of overriding a val with a def, so that no field is found in the subclass. + def fieldAccess(accessor: Symbol): Symbol = + afterOwnPhase { clazz.info.decl(accessor.localName) } + + def getterBody(getter: Symbol): Tree = // accessor created by newMatchingModuleAccessor for a static module that does need an accessor // (because there's a matching member in a super class) - if (getter.asTerm.referenced.isModule) { - List(gen.mkAttributedRef(clazz.thisType, getter.asTerm.referenced)) - } else { + if (getter.asTerm.referenced.isModule) + mkAccessor(getter)(cast(Select(This(clazz), getter.asTerm.referenced), getter.info.resultType)) + else { val fieldMemoization = fieldMemoizationIn(getter, clazz) - if (fieldMemoization.constantTyped) List(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode - else fieldAccess(getter) + // TODO: drop getter for constant? (when we no longer care about producing identical bytecode?) + if (fieldMemoization.constantTyped) mkAccessor(getter)(gen.mkAttributedQualifier(fieldMemoization.tp)) + else fieldAccess(getter) match { + case NoSymbol => EmptyTree + case fieldSel => mkAccessor(getter)(cast(Select(This(clazz), fieldSel), getter.info.resultType)) + } } - } // println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees") - def setterBody(setter: Symbol): List[Tree] = { + def setterBody(setter: Symbol): Tree = // trait setter in trait - if (clazz.isTrait) List(EmptyTree) + if (clazz.isTrait) mkAccessor(setter)(EmptyTree) // trait setter for overridden val in class - else if (checkAndClearOverriddenTraitSetter(setter)) List(mkTypedUnit(setter.pos)) + else if (checkAndClearOverriddenTraitSetter(setter)) mkAccessor(setter)(mkTypedUnit(setter.pos)) // trait val/var setter mixed into class - else fieldAccess(setter) map (fieldSel => Assign(fieldSel, Ident(setter.firstParam))) - } + else fieldAccess(setter) match { + case NoSymbol => EmptyTree + case fieldSel => afterOwnPhase { // the assign only type checks after our phase (assignment to val) + mkAccessor(setter)(Assign(Select(This(clazz), fieldSel), cast(Ident(setter.firstParam), fieldSel.info))) + } + } - def moduleAccessorBody(module: Symbol): List[Tree] = List( + def moduleAccessorBody(module: Symbol): Tree = // added during synthFieldsAndAccessors using newModuleAccessor // a module defined in a trait by definition can't be static (it's a member of the trait and thus gets a new instance for every outer instance) - if (clazz.isTrait) EmptyTree + if (clazz.isTrait) mkAccessor(module)(EmptyTree) // symbol created by newModuleAccessor for a (non-trait) class - else moduleInit(module) - ) + else { + mkAccessor(module)(moduleInit(module, moduleOrLazyVarOf(module))) + } val synthAccessorInClass = new SynthLazyAccessorsIn(clazz) - def superLazy(getter: Symbol): List[ValOrDefDef] = { + def superLazy(getter: Symbol): 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? - val rhs = Apply(Select(Super(This(clazz), tpnme.EMPTY), getter.name), Nil) - explodeThicket(synthAccessorInClass.expandLazyClassMember(lazyVarOf(getter), getter, rhs, Map.empty)).asInstanceOf[List[ValOrDefDef]] + // this contortion was the only way I can get the super select to be type checked correctly.. + // TODO: why does SelectSuper not work? + val selectSuper = Select(Super(This(clazz), tpnme.EMPTY), getter.name) + + val lazyVar = lazyVarOf(getter) + val rhs = cast(Apply(selectSuper, Nil), lazyVar.info) + + synthAccessorInClass.expandLazyClassMember(lazyVar, getter, rhs, Map.empty) } - clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap { - case module if module hasAllFlags (MODULE | METHOD) => moduleAccessorBody(module) map mkAccessor(module) + (afterOwnPhase { clazz.info.decls } toList) filter checkAndClearNeedsTrees map { + case module if module hasAllFlags (MODULE | METHOD) => moduleAccessorBody(module) case getter if getter hasAllFlags (LAZY | METHOD) => superLazy(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(mkTypedValDef(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) - case _ => None - } + case setter if setter.isSetter => setterBody(setter) + case getter if getter.hasFlag(ACCESSOR) => getterBody(getter) + case field if !(field hasFlag METHOD) => mkTypedValDef(field) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) + case _ => EmptyTree + } filterNot (_ == EmptyTree) // there will likely be many EmptyTrees, but perhaps no thicket blocks that need expanding } def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree = @@ -694,7 +720,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // 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) - && fieldMemoizationIn(statSym, currOwner).constantTyped => + && currOwner.isClass && fieldMemoizationIn(statSym, currOwner).constantTyped => EmptyThicket case ModuleDef(_, _, impl) => @@ -702,8 +728,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val cd = super.transform(ClassDef(statSym.moduleClass, impl) setType NoType) if (currOwner.isClass) cd else { // local module -- symbols cannot be generated by info transformer, so do it all here - val moduleVar = newModuleVarSymbol(currOwner, statSym, statSym.info.resultType) - Thicket(cd :: mkTypedValDef(moduleVar) :: mkAccessor(statSym)(moduleInit(statSym)) :: Nil) + val Block(stats, _) = mkLazyLocalDef(statSym, gen.newModule(statSym, statSym.info.resultType)) + + Thicket(cd :: stats) } case tree => @@ -721,8 +748,13 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { val addedStats = - if (!currentOwner.isClass) Nil // TODO: || currentOwner.isPackageClass - else afterOwnPhase { fieldsAndAccessors(currentOwner) } + if (!currentOwner.isClass || currentOwner.isPackageClass) Nil + else { + val thickets = fieldsAndAccessors(currentOwner) + if (thickets exists mustExplodeThicket) + thickets flatMap explodeThicket + else thickets + } val inRealClass = currentOwner.isClass && !(currentOwner.isPackageClass || currentOwner.isTrait) if (inRealClass) diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 74e6c58388..798cfcd072 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -104,8 +104,31 @@ abstract class LambdaLift extends InfoTransform { /** Buffers for lifted out classes and methods */ private val liftedDefs = new LinkedHashMap[Symbol, List[Tree]] + val delayedInitDummies = new mutable.HashMap[Symbol, Symbol] + + /** + * For classes capturing locals, LambdaLift uses `local.logicallyEnclosingMember` to decide + * whether an access to the local is re-written to the field or constructor parameter. If the + * access is in a constructor statement, the constructor parameter is used. + * + * For DelayedInit subclasses, constructor statements end up in the synthetic init method + * instead of the constructor itself, so the access should go to the field. This method changes + * `logicallyEnclosingMember` in this case to return a temprorary symbol corresponding to that + * method. + */ + private def logicallyEnclosingMember(sym: Symbol): Symbol = { + if (sym.isLocalDummy) { + val enclClass = sym.enclClass + if (enclClass.isSubClass(DelayedInitClass)) + delayedInitDummies.getOrElseUpdate(enclClass, enclClass.newMethod(nme.delayedInit)) + else + enclClass.primaryConstructor + } else if (sym.isMethod || sym.isClass || sym == NoSymbol) sym + else logicallyEnclosingMember(sym.owner) + } + private def isSameOwnerEnclosure(sym: Symbol) = - sym.owner.logicallyEnclosingMember == currentOwner.logicallyEnclosingMember + logicallyEnclosingMember(sym.owner) == logicallyEnclosingMember(currentOwner) /** Mark symbol `sym` as being free in `enclosure`, unless `sym` * is defined in `enclosure` or there is a class between `enclosure`s owner @@ -139,9 +162,9 @@ abstract class LambdaLift extends InfoTransform { */ private def markFree(sym: Symbol, enclosure: Symbol): Boolean = { // println(s"mark free: ${sym.fullLocationString} marked free in $enclosure") - (enclosure == sym.owner.logicallyEnclosingMember) || { - debuglog("%s != %s".format(enclosure, sym.owner.logicallyEnclosingMember)) - if (enclosure.isPackageClass || !markFree(sym, enclosure.skipConstructor.owner.logicallyEnclosingMember)) false + (enclosure == logicallyEnclosingMember(sym.owner)) || { + debuglog("%s != %s".format(enclosure, logicallyEnclosingMember(sym.owner))) + if (enclosure.isPackageClass || !markFree(sym, logicallyEnclosingMember(enclosure.skipConstructor.owner))) false else { val ss = symSet(free, enclosure) if (!ss(sym)) { @@ -184,14 +207,14 @@ abstract class LambdaLift extends InfoTransform { if (sym == NoSymbol) { assert(name == nme.WILDCARD) } else if (sym.isLocalToBlock) { - val owner = currentOwner.logicallyEnclosingMember + val owner = logicallyEnclosingMember(currentOwner) if (sym.isTerm && !sym.isMethod) markFree(sym, owner) else if (sym.isMethod) markCalled(sym, owner) //symSet(called, owner) += sym } case Select(_, _) => if (sym.isConstructor && sym.owner.isLocalToBlock) - markCalled(sym, currentOwner.logicallyEnclosingMember) + markCalled(sym, logicallyEnclosingMember(currentOwner)) case _ => } super.traverse(tree) @@ -283,17 +306,18 @@ abstract class LambdaLift extends InfoTransform { private def proxy(sym: Symbol) = { def searchIn(enclosure: Symbol): Symbol = { - if (enclosure eq NoSymbol) throw new IllegalArgumentException("Could not find proxy for "+ sym.defString +" in "+ sym.ownerChain +" (currentOwner= "+ currentOwner +" )") - debuglog("searching for " + sym + "(" + sym.owner + ") in " + enclosure + " " + enclosure.logicallyEnclosingMember) + if (enclosure eq NoSymbol) + throw new IllegalArgumentException("Could not find proxy for "+ sym.defString +" in "+ sym.ownerChain +" (currentOwner= "+ currentOwner +" )") + debuglog("searching for " + sym + "(" + sym.owner + ") in " + enclosure + " " + logicallyEnclosingMember(enclosure)) val proxyName = proxyNames.getOrElse(sym, sym.name) - val ps = (proxies get enclosure.logicallyEnclosingMember).toList.flatten find (_.name == proxyName) + val ps = (proxies get logicallyEnclosingMember(enclosure)).toList.flatten find (_.name == proxyName) ps getOrElse searchIn(enclosure.skipConstructor.owner) } debuglog("proxy %s from %s has logical enclosure %s".format( sym.debugLocationString, currentOwner.debugLocationString, - sym.owner.logicallyEnclosingMember.debugLocationString) + logicallyEnclosingMember(sym.owner).debugLocationString) ) if (isSameOwnerEnclosure(sym)) sym @@ -319,7 +343,14 @@ abstract class LambdaLift extends InfoTransform { else if (clazz.isStaticOwner) gen.mkAttributedQualifier(clazz.thisType) else outerValue match { case EmptyTree => prematureSelfReference() - case o => outerPath(o, currentClass.outerClass, clazz) + case o => + val path = outerPath(o, currentClass.outerClass, clazz) + if (path.tpe <:< clazz.tpeHK) path + else { + // SI-9920 The outer accessor might have an erased type of the self type of a trait, + // rather than the trait itself. Add a cast if necessary. + gen.mkAttributedCast(path, clazz.tpeHK) + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 8034d056d7..106b076eef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -95,6 +95,15 @@ abstract class RefChecks extends Transform { ) } + private val separatelyCompiledScalaSuperclass = perRunCaches.newAnyRefMap[Symbol, Unit]() + final def isSeparatelyCompiledScalaSuperclass(sym: Symbol) = if (globalPhase.refChecked){ + separatelyCompiledScalaSuperclass.contains(sym) + } else { + // conservative approximation in case someone in pre-refchecks phase asks for `exitingFields(someClass.info)` + // and we haven't run the refchecks tree transform which populates `separatelyCompiledScalaSuperclass` + false + } + class RefCheckTransformer(unit: CompilationUnit) extends Transformer { var localTyper: analyzer.Typer = typer @@ -854,6 +863,8 @@ abstract class RefChecks extends Transform { // println("validate base type "+tp) val baseClass = tp.typeSymbol if (baseClass.isClass) { + if (!baseClass.isTrait && !baseClass.isJavaDefined && !currentRun.compiles(baseClass) && !separatelyCompiledScalaSuperclass.contains(baseClass)) + separatelyCompiledScalaSuperclass.update(baseClass, ()) val index = clazz.info.baseTypeIndex(baseClass) if (index >= 0) { if (seenTypes(index) forall (tp1 => !(tp1 <:< tp))) |