Skip to content

Commit f01d061

Browse files
lrytzretronym
authored andcommitted
Treat self parameter as non-null in the optimizer
1 parent 0533a3d commit f01d061

File tree

6 files changed

+16
-10
lines changed

6 files changed

+16
-10
lines changed

src/compiler/scala/tools/nsc/ast/TreeGen.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
344344
val newSym = maybeClone(orig.symbol)
345345
newSym.setFlag(STATIC)
346346
// Add an explicit self parameter
347-
val selfParamSym = newSym.newSyntheticValueParam(newSym.owner.typeConstructor, nme.SELF)
347+
val selfParamSym = newSym.newSyntheticValueParam(newSym.owner.typeConstructor, nme.SELF).setFlag(ARTIFACT)
348348
newSym.updateInfo(newSym.info match {
349349
case mt @ MethodType(params, res) => copyMethodType(mt, selfParamSym :: params, res)
350350
})

src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ package analysis
55
import java.util
66

77
import scala.annotation.switch
8-
import scala.tools.asm.{Type, Opcodes}
9-
import scala.tools.asm.tree.{MethodInsnNode, LdcInsnNode, AbstractInsnNode}
8+
import scala.tools.asm.{Opcodes, Type}
9+
import scala.tools.asm.tree.{AbstractInsnNode, LdcInsnNode, MethodInsnNode, MethodNode}
1010
import scala.tools.asm.tree.analysis._
1111
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils
1212
import BytecodeUtils._
@@ -63,7 +63,7 @@ object NullnessValue {
6363
def unknown(insn: AbstractInsnNode) = if (BytecodeUtils.instructionResultSize(insn) == 2) UnknownValue2 else UnknownValue1
6464
}
6565

66-
final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValue](Opcodes.ASM5) {
66+
final class NullnessInterpreter(bTypes: BTypes, method: MethodNode) extends Interpreter[NullnessValue](Opcodes.ASM5) {
6767
def newValue(tp: Type): NullnessValue = {
6868
// ASM loves giving semantics to null. The behavior here is the same as in SourceInterpreter,
6969
// which is provided by the framework.
@@ -80,7 +80,13 @@ final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValu
8080

8181
override def newParameterValue(isInstanceMethod: Boolean, local: Int, tp: Type): NullnessValue = {
8282
// For instance methods, the `this` parameter is known to be not null.
83-
if (isInstanceMethod && local == 0) NotNullValue
83+
val isThis = local == 0 && (isInstanceMethod || {
84+
method.parameters != null && !method.parameters.isEmpty && {
85+
val p = method.parameters.get(0)
86+
(p.access & Opcodes.ACC_SYNTHETIC) != 0 && p.name == "$this"
87+
}
88+
})
89+
if (isThis) NotNullValue
8490
else super.newParameterValue(isInstanceMethod, local, tp)
8591
}
8692

@@ -197,7 +203,7 @@ class NullnessFrame(nLocals: Int, nStack: Int) extends AliasingFrame[NullnessVal
197203
* This class is required to override the `newFrame` methods, which makes makes sure the analyzer
198204
* uses NullnessFrames.
199205
*/
200-
class NullnessAnalyzer(bTypes: BTypes) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes)) {
206+
class NullnessAnalyzer(bTypes: BTypes, method: MethodNode) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes, method)) {
201207
override def newFrame(nLocals: Int, nStack: Int): NullnessFrame = new NullnessFrame(nLocals, nStack)
202208
override def newFrame(src: Frame[_ <: NullnessValue]): NullnessFrame = new NullnessFrame(src)
203209
}

src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
103103

104104
val analyzer = {
105105
if (compilerSettings.optNullnessTracking && AsmAnalyzer.sizeOKForNullness(methodNode)) {
106-
Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes)))
106+
Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes, methodNode)))
107107
} else if (AsmAnalyzer.sizeOKForBasicValue(methodNode)) {
108108
Some(new AsmAnalyzer(methodNode, definingClass.internalName))
109109
} else None

src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
397397
*/
398398
def nullnessOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = {
399399
AsmAnalyzer.sizeOKForNullness(method) && {
400-
lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes))
400+
lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes, method))
401401

402402
// When running nullness optimizations the method may still have unreachable code. Analyzer
403403
// frames of unreachable instructions are `null`.

test/junit/scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class OptimizedBytecodeTest extends BytecodeTesting {
4444

4545
assertSameSummary(getMethod(c, "t"), List(
4646
LDC, ASTORE, ALOAD /*0*/, ALOAD /*1*/, "$anonfun$t$1", IRETURN))
47-
assertSameSummary(getMethod(c, "$anonfun$t$1"), List(ALOAD, IFNONNULL, ACONST_NULL, ATHROW, -1, LDC, "$anonfun$t$2", IRETURN))
47+
assertSameSummary(getMethod(c, "$anonfun$t$1"), List(LDC, "$anonfun$t$2", IRETURN))
4848
assertSameSummary(getMethod(c, "$anonfun$t$2"), List(-1 /*A*/, GOTO /*A*/))
4949
}
5050

test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class NullnessAnalyzerTest extends BytecodeTesting {
2121
import compiler._
2222
import global.genBCode.bTypes.backendUtils._
2323

24-
def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer(global.genBCode.bTypes))
24+
def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer(global.genBCode.bTypes, methodNode))
2525

2626
def testNullness(analyzer: AsmAnalyzer[NullnessValue], method: MethodNode, query: String, index: Int, nullness: NullnessValue): Unit = {
2727
for (i <- findInstrs(method, query)) {

0 commit comments

Comments
 (0)