Skip to content

Commit 361f3f1

Browse files
committed
Merge pull request scala#5157 from retronym/topic/lambda-statics
Lambda impl methods static and more stably named
2 parents af93c7a + 3f68507 commit 361f3f1

27 files changed

+172
-81
lines changed

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

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
238238
* (outside the synchronized block).
239239
*
240240
* The idiom works only if the condition is using a volatile field.
241-
* @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
241+
*
242+
* @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
242243
*/
243244
def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree =
244245
mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats)
@@ -274,8 +275,19 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
274275
}
275276

276277
// used to create the lifted method that holds a function's body
277-
def mkLiftedFunctionBodyMethod(localTyper: analyzer.Typer)(owner: Symbol, fun: Function) =
278-
mkMethodForFunctionBody(localTyper)(owner, fun, nme.ANON_FUN_NAME)(additionalFlags = ARTIFACT)
278+
def mkLiftedFunctionBodyMethod(localTyper: global.analyzer.Typer)(owner: global.Symbol, fun: global.Function) = {
279+
def nonLocalEnclosingMember(sym: Symbol): Symbol = {
280+
if (sym.isLocalDummy) sym.enclClass.primaryConstructor
281+
else if (sym.isLocalToBlock) nonLocalEnclosingMember(sym.originalOwner)
282+
else sym
283+
}
284+
val ownerName = nonLocalEnclosingMember(fun.symbol.originalOwner).name match {
285+
case nme.CONSTRUCTOR => nme.NEWkw // do as javac does for the suffix, prefer "new" to "$lessinit$greater$1"
286+
case x => x.dropLocal
287+
}
288+
val newName = nme.ANON_FUN_NAME.append(nme.NAME_JOIN_STRING).append(ownerName)
289+
mkMethodForFunctionBody(localTyper)(owner, fun, newName)(additionalFlags = ARTIFACT)
290+
}
279291

280292

281293
/**
@@ -310,6 +322,38 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
310322
newDefDef(methSym, moveToMethod(useMethodParams(fun.body)))(tpt = TypeTree(resTp))
311323
}
312324

325+
/**
326+
* Create a new `DefDef` based on `orig` with an explicit self parameter.
327+
*
328+
* Details:
329+
* - Must by run after erasure
330+
* - If `maybeClone` is the identity function, this runs "in place"
331+
* and mutates the symbol of `orig`. `orig` should be discarded
332+
* - Symbol owners and returns are substituted, as are parameter symbols
333+
* - Recursive calls are not rewritten. This is correct if we assume
334+
* that we either:
335+
* - are in "in-place" mode, but can guarantee that no recursive calls exists
336+
* - are associating the RHS with a cloned symbol, but intend for the original
337+
* method to remain and for recursive calls to target it.
338+
*/
339+
final def mkStatic(orig: DefDef, maybeClone: Symbol => Symbol): DefDef = {
340+
assert(phase.erasedTypes, phase)
341+
assert(!orig.symbol.hasFlag(SYNCHRONIZED), orig.symbol.defString)
342+
val origSym = orig.symbol
343+
val origParams = orig.symbol.info.params
344+
val newSym = maybeClone(orig.symbol)
345+
newSym.setFlag(STATIC)
346+
// Add an explicit self parameter
347+
val selfParamSym = newSym.newSyntheticValueParam(newSym.owner.typeConstructor, nme.SELF).setFlag(ARTIFACT)
348+
newSym.updateInfo(newSym.info match {
349+
case mt @ MethodType(params, res) => copyMethodType(mt, selfParamSym :: params, res)
350+
})
351+
val selfParam = ValDef(selfParamSym)
352+
val rhs = orig.rhs.substituteThis(newSym.owner, atPos(newSym.pos)(gen.mkAttributedIdent(selfParamSym)))
353+
.substituteSymbols(origParams, newSym.info.params.drop(1)).changeOwner(origSym -> newSym)
354+
treeCopy.DefDef(orig, orig.mods, orig.name, orig.tparams, (selfParam :: orig.vparamss.head) :: Nil, orig.tpt, rhs).setSymbol(newSym)
355+
}
356+
313357
// TODO: the rewrite to AbstractFunction is superfluous once we compile FunctionN to a SAM type (aka functional interface)
314358
def functionClassType(fun: Function): Type =
315359
if (isFunctionType(fun.tpe)) abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst)

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

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

122122
def getBoxedUnit: FieldInsnNode = new FieldInsnNode(GETSTATIC, srBoxedUnitRef.internalName, "UNIT", srBoxedUnitRef.descriptor)
123123

124-
private val anonfunAdaptedName = """.*\$anonfun\$\d+\$adapted""".r
124+
private val anonfunAdaptedName = """.*\$anonfun\$.*\$\d+\$adapted""".r
125125
def hasAdaptedImplMethod(closureInit: ClosureInstantiation): Boolean = {
126126
isBuiltinFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) &&
127127
anonfunAdaptedName.pattern.matcher(closureInit.lambdaMetaFactoryCall.implMethod.getName).matches

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`.

src/compiler/scala/tools/nsc/transform/Delambdafy.scala

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,18 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
6161

6262
private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, samUserDefined: Symbol, isSpecialized: Boolean): Tree = {
6363
val pos = fun.pos
64+
def isSelfParam(p: Symbol) = p.isSynthetic && p.name == nme.SELF
65+
val hasSelfParam = isSelfParam(target.firstParam)
66+
6467
val allCapturedArgRefs = {
6568
// find which variables are free in the lambda because those are captures that need to be
6669
// passed into the constructor of the anonymous function class
6770
val captureArgs = FreeVarTraverser.freeVarsOf(fun).iterator.map(capture =>
6871
gen.mkAttributedRef(capture) setPos pos
6972
).toList
7073

71-
if (target hasFlag STATIC) captureArgs // no `this` reference needed
74+
if (!hasSelfParam) captureArgs.filterNot(arg => isSelfParam(arg.symbol))
75+
else if (currentMethod.hasFlag(Flags.STATIC)) captureArgs
7276
else (gen.mkAttributedThis(fun.symbol.enclClass) setPos pos) :: captureArgs
7377
}
7478

@@ -179,7 +183,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
179183
val numCaptures = targetParams.length - functionParamTypes.length
180184
val (targetCapturedParams, targetFunctionParams) = targetParams.splitAt(numCaptures)
181185

182-
val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT)
186+
val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT | STATIC)
183187
val bridgeCapturedParams = targetCapturedParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName))
184188
val bridgeFunctionParams =
185189
map2(targetFunctionParams, bridgeParamTypes)((param, tp) => methSym.newSyntheticValueParam(tp, param.name.toTermName))
@@ -223,10 +227,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
223227

224228
private def transformFunction(originalFunction: Function): Tree = {
225229
val target = targetMethod(originalFunction)
226-
target.makeNotPrivate(target.owner)
227-
228-
// must be done before calling createBoxingBridgeMethod and mkLambdaMetaFactoryCall
229-
if (!(target hasFlag STATIC) && !methodReferencesThis(target)) target setFlag STATIC
230+
assert(target.hasFlag(Flags.STATIC))
231+
target.setFlag(notPRIVATE)
230232

231233
val funSym = originalFunction.tpe.typeSymbolDirect
232234
// The functional interface that can be used to adapt the lambda target method `target` to the given function type.
@@ -252,11 +254,22 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
252254
// here's the main entry point of the transform
253255
override def transform(tree: Tree): Tree = tree match {
254256
// the main thing we care about is lambdas
255-
case fun: Function => super.transform(transformFunction(fun))
257+
case fun: Function =>
258+
super.transform(transformFunction(fun))
256259
case Template(_, _, _) =>
260+
def pretransform(tree: Tree): Tree = tree match {
261+
case dd: DefDef if dd.symbol.isDelambdafyTarget =>
262+
if (!dd.symbol.hasFlag(STATIC) && methodReferencesThis(dd.symbol)) {
263+
gen.mkStatic(dd, sym => sym)
264+
} else {
265+
dd.symbol.setFlag(STATIC)
266+
dd
267+
}
268+
case t => t
269+
}
257270
try {
258271
// during this call boxingBridgeMethods will be populated from the Function case
259-
val Template(parents, self, body) = super.transform(tree)
272+
val Template(parents, self, body) = super.transform(deriveTemplate(tree)(_.mapConserve(pretransform)))
260273
Template(parents, self, body ++ boxingBridgeMethods)
261274
} finally boxingBridgeMethods.clear()
262275
case _ => super.transform(tree)

src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
13291329
class SpecializationDuplicator(casts: Map[Symbol, Type]) extends Duplicator(casts) {
13301330
override def retyped(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol, env: scala.collection.Map[Symbol, Type]): Tree =
13311331
enteringSpecialize(super.retyped(context, tree, oldThis, newThis, env))
1332+
13321333
}
13331334

13341335
/** A tree symbol substituter that substitutes on type skolems.

src/compiler/scala/tools/nsc/transform/UnCurry.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ abstract class UnCurry extends InfoTransform
213213
// Expand the function body into an anonymous class
214214
gen.expandFunction(localTyper)(fun, inConstructorFlag)
215215
} else {
216+
val mustExpand = mustExpandFunction(fun)
216217
// method definition with the same arguments, return type, and body as the original lambda
217218
val liftedMethod = gen.mkLiftedFunctionBodyMethod(localTyper)(fun.symbol.owner, fun)
218219

@@ -221,11 +222,18 @@ abstract class UnCurry extends InfoTransform
221222
gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), (fun.vparams map (_.symbol)) :: Nil)
222223
))
223224

225+
if (!mustExpand) {
226+
liftedMethod.symbol.updateAttachment(DelambdafyTarget)
227+
liftedMethod.updateAttachment(DelambdafyTarget)
228+
}
229+
224230
val typedNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod, super.transform(newFun)))
225-
if (mustExpandFunction(fun)) {
231+
if (mustExpand) {
226232
val Block(stats, expr : Function) = typedNewFun
227233
treeCopy.Block(typedNewFun, stats, gen.expandFunction(localTyper)(expr, inConstructorFlag))
228-
} else typedNewFun
234+
} else {
235+
typedNewFun
236+
}
229237
}
230238

231239
def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = {
@@ -341,13 +349,18 @@ abstract class UnCurry extends InfoTransform
341349

342350
private def isSelfSynchronized(ddef: DefDef) = ddef.rhs match {
343351
case Apply(fn @ TypeApply(Select(sel, _), _), _) =>
344-
fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait
352+
fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait &&
353+
!ddef.symbol.isDelambdafyTarget /* these become static later, unsuitable for ACC_SYNCHRONIZED */
345354
case _ => false
346355
}
347356

348357
/** If an eligible method is entirely wrapped in a call to synchronized
349358
* locked on the same instance, remove the synchronized scaffolding and
350359
* mark the method symbol SYNCHRONIZED for bytecode generation.
360+
*
361+
* Delambdafy targets are deemed ineligible as the Delambdafy phase will
362+
* replace `this.synchronized` with `$this.synchronzed` now that it emits
363+
* all lambda impl methods as static.
351364
*/
352365
private def translateSynchronized(tree: Tree) = tree match {
353366
case dd @ DefDef(_, _, _, _, _, Apply(fn, body :: Nil)) if isSelfSynchronized(dd) =>

src/compiler/scala/tools/nsc/typechecker/Duplicators.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,12 @@ abstract class Duplicators extends Analyzer {
229229

230230
case ddef @ DefDef(_, _, _, _, tpt, rhs) =>
231231
ddef.tpt modifyType fixType
232-
super.typed(ddef.clearType(), mode, pt)
232+
val result = super.typed(ddef.clearType(), mode, pt)
233+
// TODO this is a hack, we really need a cleaner way to transport symbol attachments to duplicated methods
234+
// bodies in specialized subclasses.
235+
if (ddef.hasAttachment[DelambdafyTarget.type])
236+
result.symbol.updateAttachment(DelambdafyTarget)
237+
result
233238

234239
case fun: Function =>
235240
debuglog("Clearing the type and retyping Function: " + fun)

src/reflect/scala/reflect/api/Internals.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ trait Internals { self: Universe =>
116116
/** Substitute given tree `to` for occurrences of nodes that represent
117117
* `C.this`, where `C` refers to the given class `clazz`.
118118
*/
119-
def substituteThis(tree: Tree, clazz: Symbol, to: Tree): Tree
119+
def substituteThis(tree: Tree, clazz: Symbol, to: => Tree): Tree
120120

121121
/** A factory method for `ClassDef` nodes.
122122
*/
@@ -391,7 +391,7 @@ trait Internals { self: Universe =>
391391
def substituteTypes(from: List[Symbol], to: List[Type]): Tree = internal.substituteTypes(tree, from, to)
392392

393393
/** @see [[internal.substituteThis]] */
394-
def substituteThis(clazz: Symbol, to: Tree): Tree = internal.substituteThis(tree, clazz, to)
394+
def substituteThis(clazz: Symbol, to: => Tree): Tree = internal.substituteThis(tree, clazz, to)
395395
}
396396

397397
/** Extension methods for symbols */
@@ -1143,7 +1143,7 @@ trait Internals { self: Universe =>
11431143

11441144
/** @see [[InternalApi.substituteThis]] */
11451145
@deprecated("use `internal.substituteThis` instead or import `internal.decorators._` for infix syntax", "2.11.0")
1146-
def substituteThis(clazz: Symbol, to: Tree): Tree = internal.substituteThis(tree, clazz, to)
1146+
def substituteThis(clazz: Symbol, to: => Tree): Tree = internal.substituteThis(tree, clazz, to)
11471147
}
11481148

11491149
/** Scala 2.10 compatibility enrichments for Tree. */

src/reflect/scala/reflect/internal/Internals.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ trait Internals extends api.Internals {
2929
def freeTypes(tree: Tree): List[FreeTypeSymbol] = tree.freeTypes
3030
def substituteSymbols(tree: Tree, from: List[Symbol], to: List[Symbol]): Tree = tree.substituteSymbols(from, to)
3131
def substituteTypes(tree: Tree, from: List[Symbol], to: List[Type]): Tree = tree.substituteTypes(from, to)
32-
def substituteThis(tree: Tree, clazz: Symbol, to: Tree): Tree = tree.substituteThis(clazz, to)
32+
def substituteThis(tree: Tree, clazz: Symbol, to: => Tree): Tree = tree.substituteThis(clazz, to)
3333
def attachments(tree: Tree): Attachments { type Pos = Position } = tree.attachments
3434
def updateAttachment[T: ClassTag](tree: Tree, attachment: T): tree.type = tree.updateAttachment(attachment)
3535
def removeAttachment[T: ClassTag](tree: Tree): tree.type = tree.removeAttachment[T]

src/reflect/scala/reflect/internal/StdAttachments.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ trait StdAttachments {
5151
*/
5252
case class SAMFunction(samTp: Type, sam: Symbol) extends PlainAttachment
5353

54+
case object DelambdafyTarget extends PlainAttachment
55+
5456
/** When present, indicates that the host `Ident` has been created from a backquoted identifier.
5557
*/
5658
case object BackquotedIdentifierAttachment extends PlainAttachment

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
807807

808808
final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME)
809809
final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME)
810-
final def isDelambdafyTarget = isArtifact && isMethod && (name containsName tpnme.ANON_FUN_NAME)
810+
final def isDelambdafyTarget = isArtifact && isMethod && hasAttachment[DelambdafyTarget.type]
811811
final def isDefinedInPackage = effectiveOwner.isPackageClass
812812
final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass
813813

src/reflect/scala/reflect/internal/Trees.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ trait Trees extends api.Trees {
181181
def substituteTypes(from: List[Symbol], to: List[Type]): Tree =
182182
new TreeTypeSubstituter(from, to)(this)
183183

184-
def substituteThis(clazz: Symbol, to: Tree): Tree =
184+
def substituteThis(clazz: Symbol, to: => Tree): Tree =
185185
new ThisSubstituter(clazz, to) transform this
186186

187187
def hasExistingSymbol = (symbol ne null) && (symbol ne NoSymbol)

src/reflect/scala/reflect/runtime/JavaUniverseForce.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
3838
this.FixedMirrorTypeCreator
3939
this.CompoundTypeTreeOriginalAttachment
4040
this.SAMFunction
41+
this.DelambdafyTarget
4142
this.BackquotedIdentifierAttachment
4243
this.ForAttachment
4344
this.SyntheticUnitAttachment

0 commit comments

Comments
 (0)