Skip to content

Commit d6f66ec

Browse files
committed
Merge pull request scala#5082 from lrytz/inlineImplClassCleanup
Cleanups related to the removal of trait impl classes
2 parents a326ef4 + 17d9706 commit d6f66ec

File tree

19 files changed

+164
-338
lines changed

19 files changed

+164
-338
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
3030
* True for classes generated by the Scala compiler that are considered top-level in terms of
3131
* the InnerClass / EnclosingMethod classfile attributes. See comment in BTypes.
3232
*/
33-
def considerAsTopLevelImplementationArtifact(classSym: Symbol) =
34-
classSym.isSpecialized
33+
def considerAsTopLevelImplementationArtifact(classSym: Symbol) = classSym.isSpecialized
3534

3635
/**
3736
* Cache the value of delambdafy == "inline" for each run. We need to query this value many
@@ -146,10 +145,10 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
146145
assert(classSym.isClass, classSym)
147146

148147
def doesNotExist(method: Symbol) = {
149-
// Value classes. Member methods of value classes exist in the generated box class. However,
150-
// nested methods lifted into a value class are moved to the companion object and don't exist
151-
// in the value class itself. We can identify such nested methods: the initial enclosing class
152-
// is a value class, but the current owner is some other class (the module class).
148+
// Value classes. Member methods of value classes exist in the generated box class. However,
149+
// nested methods lifted into a value class are moved to the companion object and don't exist
150+
// in the value class itself. We can identify such nested methods: the initial enclosing class
151+
// is a value class, but the current owner is some other class (the module class).
153152
val enclCls = nextEnclosingClass(method)
154153
exitingPickler(enclCls.isDerivedValueClass) && method.owner != enclCls
155154
}
@@ -171,8 +170,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
171170
private def enclosingClassForEnclosingMethodAttribute(classSym: Symbol): Symbol = {
172171
assert(classSym.isClass, classSym)
173172
val r = nextEnclosingClass(nextEnclosing(classSym))
174-
// this should be an assertion, but we are more cautious for now as it was introduced before the 2.11.6 minor release
175-
if (considerAsTopLevelImplementationArtifact(r)) devWarning(s"enclosing class of $classSym should not be an implementation artifact class: $r")
176173
r
177174
}
178175

@@ -187,20 +184,11 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
187184
* on the implementation of GenASM / GenBCode, so they need to be passed in.
188185
*/
189186
def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = {
190-
// trait impl classes are always top-level, see comment in BTypes
187+
// specialized classes are always top-level, see comment in BTypes
191188
if (isAnonymousOrLocalClass(classSym) && !considerAsTopLevelImplementationArtifact(classSym)) {
192189
val enclosingClass = enclosingClassForEnclosingMethodAttribute(classSym)
193-
val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym) match {
194-
case some @ Some(m) =>
195-
if (m.owner != enclosingClass) {
196-
// This should never happen. In case it does, it prevents emitting an invalid
197-
// EnclosingMethod attribute: if the attribute specifies an enclosing method,
198-
// it needs to exist in the specified enclosing class.
199-
devWarning(s"the owner of the enclosing method ${m.locationString} should be the same as the enclosing class $enclosingClass")
200-
None
201-
} else some
202-
case none => none
203-
}
190+
val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym)
191+
for (m <- methodOpt) assert(m.owner == enclosingClass, s"the owner of the enclosing method ${m.locationString} should be the same as the enclosing class $enclosingClass")
204192
Some(EnclosingMethodEntry(
205193
classDesc(enclosingClass),
206194
methodOpt.map(_.javaSimpleName.toString).orNull,
@@ -246,14 +234,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
246234
* Build the [[InlineInfo]] for a class symbol.
247235
*/
248236
def buildInlineInfoFromClassSymbol(classSym: Symbol, classSymToInternalName: Symbol => InternalName, methodSymToDescriptor: Symbol => String): InlineInfo = {
249-
val traitSelfType = if (classSym.isTrait) {
250-
// The mixin phase uses typeOfThis for the self parameter in implementation class methods.
251-
val selfSym = classSym.typeOfThis.typeSymbol
252-
if (selfSym != classSym) Some(classSymToInternalName(selfSym)) else None
253-
} else {
254-
None
255-
}
256-
257237
val isEffectivelyFinal = classSym.isEffectivelyFinal
258238

259239
val sam = {
@@ -282,23 +262,24 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
282262
val name = methodSym.javaSimpleName.toString // same as in genDefDef
283263
val signature = name + methodSymToDescriptor(methodSym)
284264

285-
// Some detours are required here because of changing flags (lateDEFERRED):
286-
// 1. Why the phase travel? Concrete trait methods obtain the lateDEFERRED flag in Mixin.
287-
// This makes isEffectivelyFinalOrNotOverridden false, which would prevent non-final
288-
// but non-overridden methods of sealed traits from being inlined.
289-
val effectivelyFinal = exitingPickler(methodSym.isEffectivelyFinalOrNotOverridden) && !(methodSym.owner.isTrait && methodSym.isModule)
265+
// In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
266+
// method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
267+
// so they are not marked final in the InlineInfo attribute.
268+
//
269+
// However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
270+
// work, the abstract accessor for O will be marked effectivelyFinal.
271+
val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred
290272

291273
val info = MethodInlineInfo(
292274
effectivelyFinal = effectivelyFinal,
293-
traitMethodWithStaticImplementation = false,
294275
annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
295276
annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)
296277
)
297278
Some((signature, info))
298279
}
299280
}).toMap
300281

301-
InlineInfo(traitSelfType, isEffectivelyFinal, sam, methodInlineInfos, warning)
282+
InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
302283
}
303284

304285
/*

src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
232232
}
233233

234234
def addClassFields() {
235-
/* Non-method term members are fields, except for module members. Module
236-
* members can only happen on .NET (no flatten) for inner traits. There,
237-
* a module symbol is generated (transformInfo in mixin) which is used
238-
* as owner for the members of the implementation class (so that the
239-
* backend emits them as static).
240-
* No code is needed for this module symbol.
241-
*/
242235
for (f <- fieldSymbols(claszSymbol)) {
243236
val javagensig = getGenericSignature(f, claszSymbol)
244237
val flags = javaFieldFlags(f)

src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,11 @@ abstract class BTypes {
255255
val methodInfos = classNode.methods.asScala.map(methodNode => {
256256
val info = MethodInlineInfo(
257257
effectivelyFinal = BytecodeUtils.isFinalMethod(methodNode),
258-
traitMethodWithStaticImplementation = false,
259258
annotatedInline = false,
260259
annotatedNoInline = false)
261260
(methodNode.name + methodNode.desc, info)
262261
}).toMap
263262
InlineInfo(
264-
traitImplClassSelfType = None,
265263
isEffectivelyFinal = BytecodeUtils.isFinalClass(classNode),
266264
sam = inlinerHeuristics.javaSam(classNode.name),
267265
methodInfos = methodInfos,
@@ -792,26 +790,17 @@ abstract class BTypes {
792790
* }
793791
*
794792
*
795-
* Traits Members
796-
* --------------
797-
*
798-
* Some trait methods don't exist in the generated interface, but only in the implementation class
799-
* (private methods in traits for example). Since EnclosingMethod expresses a source-level property,
800-
* but the source-level enclosing method doesn't exist in the classfile, we the enclosing method
801-
* is null (the enclosing class is still emitted).
802-
* See BCodeAsmCommon.considerAsTopLevelImplementationArtifact
803-
*
804-
*
805-
* Implementation Classes, Specialized Classes, Delambdafy:method closure classes
806-
* ------------------------------------------------------------------------------
793+
* Specialized Classes, Delambdafy:method closure classes
794+
* ------------------------------------------------------
807795
*
808-
* Trait implementation classes and specialized classes are always considered top-level. Again,
809-
* the InnerClass / EnclosingMethod attributes describe a source-level properties. The impl
810-
* classes are compilation artifacts.
796+
* Specialized classes are always considered top-level, as the InnerClass / EnclosingMethod
797+
* attributes describe a source-level properties.
811798
*
812799
* The same is true for delambdafy:method closure classes. These classes are generated at
813800
* top-level in the delambdafy phase, no special support is required in the backend.
814801
*
802+
* See also BCodeHelpers.considerAsTopLevelImplementationArtifact.
803+
*
815804
*
816805
* Mirror Classes
817806
* --------------
@@ -1139,22 +1128,8 @@ object BTypes {
11391128
* Note that this class should contain information that can only be obtained from the ClassSymbol.
11401129
* Information that can be computed from the ClassNode should be added to the call graph instead.
11411130
*
1142-
* @param traitImplClassSelfType `Some(tp)` if this InlineInfo describes a trait, and the `self`
1143-
* parameter type of the methods in the implementation class is not
1144-
* the trait itself. Example:
1145-
* trait T { self: U => def f = 1 }
1146-
* Generates something like:
1147-
* class T$class { static def f(self: U) = 1 }
1148-
*
1149-
* In order to inline a trat method call, the INVOKEINTERFACE is
1150-
* rewritten to an INVOKESTATIC of the impl class, so we need the
1151-
* self type (U) to get the right signature.
1152-
*
1153-
* `None` if the self type is the interface type, or if this
1154-
* InlineInfo does not describe a trait.
1155-
*
11561131
* @param isEffectivelyFinal True if the class cannot have subclasses: final classes, module
1157-
* classes, trait impl classes.
1132+
* classes.
11581133
*
11591134
* @param sam If this class is a SAM type, the SAM's "$name$descriptor".
11601135
*
@@ -1166,26 +1141,21 @@ object BTypes {
11661141
* InlineInfo, for example if some classfile could not be found on
11671142
* the classpath. This warning can be reported later by the inliner.
11681143
*/
1169-
final case class InlineInfo(traitImplClassSelfType: Option[InternalName],
1170-
isEffectivelyFinal: Boolean,
1144+
final case class InlineInfo(isEffectivelyFinal: Boolean,
11711145
sam: Option[String],
11721146
methodInfos: Map[String, MethodInlineInfo],
11731147
warning: Option[ClassInlineInfoWarning])
11741148

1175-
val EmptyInlineInfo = InlineInfo(None, false, None, Map.empty, None)
1149+
val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None)
11761150

11771151
/**
11781152
* Metadata about a method, used by the inliner.
11791153
*
11801154
* @param effectivelyFinal True if the method cannot be overridden (in Scala)
1181-
* @param traitMethodWithStaticImplementation True if the method is an interface method method of
1182-
* a trait method and has a static counterpart in the
1183-
* implementation class.
11841155
* @param annotatedInline True if the method is annotated `@inline`
11851156
* @param annotatedNoInline True if the method is annotated `@noinline`
11861157
*/
11871158
final case class MethodInlineInfo(effectivelyFinal: Boolean,
1188-
traitMethodWithStaticImplementation: Boolean,
11891159
annotatedInline: Boolean,
11901160
annotatedNoInline: Boolean)
11911161

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
9696
* scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files
9797
* for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
9898
* in the classfile method signature.
99-
*
100-
* Note that the referenced class symbol may be an implementation class. For example when
101-
* compiling a mixed-in method that forwards to the static method in the implementation class,
102-
* the class descriptor of the receiver (the implementation class) is obtained by creating the
103-
* ClassBType.
10499
*/
105100
final def classBTypeFromSymbol(classSym: Symbol): ClassBType = {
106101
assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
@@ -159,9 +154,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
159154

160155
/**
161156
* This method returns the BType for a type reference, for example a parameter type.
162-
*
163-
* If `t` references a class, typeToBType ensures that the class is not an implementation class.
164-
* See also comment on classBTypeFromSymbol, which is invoked for implementation classes.
165157
*/
166158
final def typeToBType(t: Type): BType = {
167159
import definitions.ArrayClass
@@ -264,12 +256,12 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
264256
* current phase, for example, after lambdalift, all local classes become member of the enclosing
265257
* class.
266258
*
267-
* Impl classes are always considered top-level, see comment in BTypes.
259+
* Specialized classes are always considered top-level, see comment in BTypes.
268260
*/
269261
private def memberClassesForInnerClassTable(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
270262
case sym if sym.isClass && !considerAsTopLevelImplementationArtifact(sym) =>
271263
sym
272-
case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) => // impl classes get the lateMODULE flag in mixin
264+
case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) =>
273265
val r = exitingPickler(sym.moduleClass)
274266
assert(r != NoSymbol, sym.fullLocationString)
275267
r
@@ -317,7 +309,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
317309
)
318310
}
319311

320-
// Check for isImplClass: trait implementation classes have NoSymbol as superClass
321312
// Check for hasAnnotationFlag for SI-9393: the classfile / java source parsers add
322313
// scala.annotation.Annotation as superclass to java annotations. In reality, java
323314
// annotation classfiles have superclass Object (like any interface classfile).
@@ -380,8 +371,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
380371
}
381372

382373
val companionModuleMembers = if (considerAsTopLevelImplementationArtifact(classSym)) Nil else {
383-
// If this is a top-level non-impl (*) class, the member classes of the companion object are
384-
// added as members of the class. For example:
374+
// If this is a top-level class, the member classes of the companion object are added as
375+
// members of the class. For example:
385376
// class C { }
386377
// object C {
387378
// class D
@@ -392,11 +383,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
392383
// (done by buildNestedInfo). See comment in BTypes.
393384
// For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
394385
// like D is a member of C, not C$.
395-
//
396-
// (*) We exclude impl classes: if the classfile for the impl class exists on the classpath,
397-
// a linkedClass symbol is found for which isTopLevelModule is true, so we end up searching
398-
// members of that weird impl-class-module-class-symbol. that search probably cannot return
399-
// any classes, but it's better to exclude it.
400386
val javaCompatMembers = {
401387
if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass))
402388
// phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only sees member
@@ -454,7 +440,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
454440
assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
455441

456442
val isTopLevel = innerClassSym.rawowner.isPackageClass
457-
// impl classes are considered top-level, see comment in BTypes
443+
// specialized classes are considered top-level, see comment in BTypes
458444
if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None
459445
else if (innerClassSym.rawowner.isTerm) {
460446
// This case should never be reached: the lambdalift phase mutates the rawowner field of all
@@ -592,17 +578,11 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
592578

593579
/**
594580
* True for module classes of modules that are top-level or owned only by objects. Module classes
595-
* for such objects will get a MODULE$ flag and a corresponding static initializer.
581+
* for such objects will get a MODULE$ field and a corresponding static initializer.
596582
*/
597583
final def isStaticModuleClass(sym: Symbol): Boolean = {
598-
/* (1) Phase travel to to pickler is required to exclude implementation classes; they have the
599-
* lateMODULEs after mixin, so isModuleClass would be true.
600-
* (2) isStaticModuleClass is a source-level property. See comment on isOriginallyStaticOwner.
601-
*/
602-
exitingPickler { // (1)
603-
sym.isModuleClass &&
604-
isOriginallyStaticOwner(sym.originalOwner) // (2)
605-
}
584+
sym.isModuleClass &&
585+
isOriginallyStaticOwner(sym.originalOwner) // isStaticModuleClass is a source-level property, see comment on isOriginallyStaticOwner
606586
}
607587

608588
// legacy, to be removed when the @remote annotation gets removed

src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,6 @@ object BackendReporting {
170170

171171
case MethodInlineInfoError(_, _, _, cause) =>
172172
s"Error while computing the inline information for method $warningMessageSignature:\n" + cause
173-
174-
case RewriteTraitCallToStaticImplMethodFailed(_, _, _, cause) =>
175-
cause.toString
176173
}
177174

178175
def emitWarning(settings: ScalaSettings): Boolean = this match {
@@ -182,15 +179,12 @@ object BackendReporting {
182179
case MethodInlineInfoMissing(_, _, _, None) => settings.YoptWarningNoInlineMissingBytecode
183180

184181
case MethodInlineInfoError(_, _, _, cause) => cause.emitWarning(settings)
185-
186-
case RewriteTraitCallToStaticImplMethodFailed(_, _, _, cause) => cause.emitWarning(settings)
187182
}
188183
}
189184

190185
case class MethodInlineInfoIncomplete(declarationClass: InternalName, name: String, descriptor: String, cause: ClassInlineInfoWarning) extends CalleeInfoWarning
191186
case class MethodInlineInfoMissing(declarationClass: InternalName, name: String, descriptor: String, cause: Option[ClassInlineInfoWarning]) extends CalleeInfoWarning
192187
case class MethodInlineInfoError(declarationClass: InternalName, name: String, descriptor: String, cause: NoClassBTypeInfo) extends CalleeInfoWarning
193-
case class RewriteTraitCallToStaticImplMethodFailed(declarationClass: InternalName, name: String, descriptor: String, cause: OptimizerWarning) extends CalleeInfoWarning
194188

195189
sealed trait CannotInlineWarning extends OptimizerWarning {
196190
def calleeDeclarationClass: InternalName

0 commit comments

Comments
 (0)