diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/Fields.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Fields.scala | 117 |
1 files changed, 74 insertions, 43 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index 894d0a1701..6cf6a5abce 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -207,9 +207,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor 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 @@ -238,7 +237,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor */ 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, moduleVarRef) + 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 @@ -305,6 +305,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) @@ -360,9 +364,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 @@ -590,62 +597,81 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } // 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 = @@ -705,7 +731,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor 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) + Thicket(cd :: mkTypedValDef(moduleVar) :: mkAccessor(statSym)(moduleInit(statSym, moduleVar)) :: Nil) } case tree => @@ -724,7 +750,12 @@ 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 || currentOwner.isPackageClass) Nil - else afterOwnPhase { fieldsAndAccessors(currentOwner) } + else { + val thickets = fieldsAndAccessors(currentOwner) + if (thickets exists mustExplodeThicket) + thickets flatMap explodeThicket + else thickets + } val inRealClass = currentOwner.isClass && !(currentOwner.isPackageClass || currentOwner.isTrait) if (inRealClass) |