Skip to content

Commit df6b29e

Browse files
committed
fix scala/bug#12420: complete LambdaType param symbols lazily
1 parent 4ede583 commit df6b29e

File tree

11 files changed

+493
-175
lines changed

11 files changed

+493
-175
lines changed

src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala.collection.mutable
2121
import scala.reflect.io.AbstractFile
2222
import scala.reflect.internal.Variance
2323
import scala.util.chaining._
24+
import scala.collection.immutable.ArraySeq
2425

2526
/**`TreeUnpickler` is responsible for traversing all trees in the "ASTs" section of a TASTy file, which represent the
2627
* definitions inside the classfile associated with the root class/module. `TreeUnpickler` will enter the public api
@@ -220,7 +221,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
220221
/** Read names in an interleaved sequence of types/bounds and (parameter) names,
221222
* possibly followed by a sequence of modifiers.
222223
*/
223-
def readParamNamesAndMods(end: Addr): (List[TastyName], TastyFlagSet) = {
224+
def readParamNamesAndMods(end: Addr): (ArraySeq[TastyName], TastyFlagSet) = {
224225
val names =
225226
collectWhile(currentAddr != end && !isModifierTag(nextByte)) {
226227
skipTree()
@@ -234,7 +235,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
234235
case GIVEN => mods |= Given
235236
}
236237
}
237-
(names, mods)
238+
(names.to(ArraySeq), mods)
238239
}
239240

240241
/** Read `n` parameter types or bounds which are interleaved with names */
@@ -332,18 +333,26 @@ class TreeUnpickler[Tasty <: TastyUniverse](
332333
def readLengthType(): Type = {
333334
val end = readEnd()
334335

335-
def readMethodic[N <: TastyName]
336-
(companionOp: TastyFlagSet => LambdaTypeCompanion[N], nameMap: TastyName => N)(implicit ctx: Context): Type = {
336+
def readMethodic[N <: TastyName](
337+
factory: LambdaFactory[N],
338+
parseFlags: FlagSets.FlagParser,
339+
nameMap: TastyName => N
340+
)(implicit ctx: Context): Type = {
337341
val result = typeAtAddr.getOrElse(start, {
342+
// TODO [tasty]: can we share LambdaTypes/RecType/RefinedType safely
343+
// under a new context owner? (aka when referenced by a `SHAREDtype`).
344+
// Perhaps we should redesign so that when one of these is retrieved
345+
// from the cache, and the context owner does not match, all its parameters
346+
// are subtituted for fresh symbols within the new context owner.
338347
val nameReader = fork
339348
nameReader.skipTree() // skip result
340349
val paramReader = nameReader.fork
341350
val (paramNames, mods) = nameReader.readParamNamesAndMods(end)
342-
companionOp(mods)(paramNames.map(nameMap))(
343-
pt => typeAtAddr(start) = pt,
344-
() => paramReader.readParamTypes(paramNames.length),
345-
() => readType()
346-
).tap(typeAtAddr(start) = _)
351+
LambdaFactory.parse(factory, paramNames.map(nameMap), parseFlags(mods)(ctx))(
352+
() => paramReader.readParamTypes(paramNames.length).to(ArraySeq),
353+
() => readType(),
354+
pt => typeAtAddr(start) = pt, // register the lambda so that we can access its parameters
355+
)
347356
})
348357
goto(end)
349358
result
@@ -382,18 +391,10 @@ class TreeUnpickler[Tasty <: TastyUniverse](
382391
case ORtype => unionIsUnsupported
383392
case SUPERtype => defn.SuperType(readType(), readType())
384393
case MATCHtype | MATCHCASEtype => matchTypeIsUnsupported
385-
case POLYtype => readMethodic(Function.const(PolyType), _.toTypeName)
386-
case METHODtype =>
387-
def companion(mods0: TastyFlagSet) = {
388-
var mods = EmptyTastyFlags
389-
if (mods0.is(Erased)) erasedRefinementIsUnsupported[Unit]
390-
if (mods0.isOneOf(Given | Implicit)) mods |= Implicit
391-
methodTypeCompanion(mods)
392-
}
393-
readMethodic(companion, id)
394-
case TYPELAMBDAtype => readMethodic(Function.const(HKTypeLambda), _.toTypeName)
395-
case PARAMtype => // reference to a type parameter within a LambdaType
396-
readTypeRef().typeParams(readNat()).ref
394+
case POLYtype => readMethodic(PolyTypeLambda, FlagSets.addDeferred, _.toTypeName)
395+
case METHODtype => readMethodic(MethodTermLambda, FlagSets.parseMethod, id)
396+
case TYPELAMBDAtype => readMethodic(HKTypeLambda, FlagSets.addDeferred, _.toTypeName)
397+
case PARAMtype => defn.ParamRef(readTypeRef(), readNat()) // reference to a parameter within a LambdaType
397398
}
398399
assert(currentAddr === end, s"$start $currentAddr $end ${astTagToString(tag)}")
399400
result

src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ trait ContextOps { self: TastyUniverse =>
156156

157157
final case class TraceInfo[-T](query: String, qual: String, res: T => String, modifiers: List[String] = Nil)
158158

159+
trait TraceFrame {
160+
def parent: TraceFrame
161+
def id: String
162+
}
163+
159164
/**Maintains state through traversal of a TASTy file, such as the outer scope of the defintion being traversed, the
160165
* traversal mode, and the root owners and source path for the TASTy file.
161166
* It also provides all operations for manipulation of the symbol table, such as creating/updating symbols and
@@ -205,17 +210,17 @@ trait ContextOps { self: TastyUniverse =>
205210

206211
@inline final def trace[T](info: => TraceInfo[T])(op: => T): T = {
207212

208-
def withTrace(info: => TraceInfo[T], op: => T)(traceId: String): T = {
209-
val i = info
213+
def addInfo(i: TraceInfo[T], op: => T)(frame: TraceFrame): T = {
214+
val id0 = frame.id
210215
val modStr = (
211216
if (i.modifiers.isEmpty) ""
212217
else " " + green(i.modifiers.mkString("[", ",", "]"))
213218
)
214-
logImpl(s"${yellow(s"$traceId")} ${cyan(s"<<< ${i.query}:")} ${magenta(i.qual)}$modStr")
215-
op.tap(eval => logImpl(s"${yellow(s"$traceId")} ${cyan(s">>>")} ${magenta(i.res(eval))}$modStr"))
219+
logImpl(s"${yellow(id0)} ${cyan(s"<<< ${i.query}:")} ${magenta(i.qual)}$modStr")
220+
op.tap(eval => logImpl(s"${yellow(id0)} ${cyan(s">>>")} ${magenta(i.res(eval))}$modStr"))
216221
}
217222

218-
if (u.settings.YdebugTasty) initialContext.addFrame(withTrace(info, op))
223+
if (u.settings.YdebugTasty) initialContext.subTrace(addInfo(info, op))
219224
else op
220225
}
221226

@@ -282,6 +287,16 @@ trait ContextOps { self: TastyUniverse =>
282287
)
283288
}
284289

290+
final def newLambdaParameter(tname: TastyName, flags: TastyFlagSet, idx: Int, infoDb: Int => Type): Symbol = {
291+
val flags1 = flags | Param
292+
unsafeNewSymbol(
293+
owner = owner,
294+
name = tname,
295+
flags = flags1,
296+
info = defn.LambdaParamInfo(flags1, idx, infoDb)
297+
)
298+
}
299+
285300
final def findRootSymbol(roots: Set[Symbol], name: TastyName): Option[Symbol] = {
286301
import TastyName.TypeName
287302

@@ -594,32 +609,32 @@ trait ContextOps { self: TastyUniverse =>
594609
def mode: TastyMode = EmptyTastyMode
595610
def owner: Symbol = topLevelClass.owner
596611

597-
private class TraceFrame(val id: Int, val next: TraceFrame) {
612+
private class TraceFrameImpl(val worker: Int, val parent: TraceFrameImpl) extends TraceFrame {
598613

599614
var nextChild: Int = 0
600615

601-
def show: String = {
602-
val buf = mutable.ArrayDeque.empty[String]
616+
val id: String = {
617+
val buf = mutable.ArrayDeque.empty[Int]
603618
var cur = this
604-
while (cur.id != -1) {
605-
buf.prepend(cur.id.toString)
606-
cur = cur.next
619+
while (cur.worker != -1) {
620+
buf.prepend(cur.worker)
621+
cur = cur.parent
607622
}
608623
buf.mkString("[", " ", ")")
609624
}
610625

611626
}
612627

613-
private[this] var _trace: TraceFrame = new TraceFrame(id = -1, next = null)
628+
private[this] var _trace: TraceFrameImpl = new TraceFrameImpl(worker = -1, parent = null)
614629

615-
private[ContextOps] def addFrame[T](op: String => T): T = {
616-
val oldFrame = _trace
617-
val newFrame = new TraceFrame(id = oldFrame.nextChild, next = oldFrame)
618-
_trace = newFrame
619-
try op(newFrame.show)
630+
private[ContextOps] def subTrace[T](op: TraceFrame => T): T = {
631+
val parent = _trace
632+
val child = new TraceFrameImpl(worker = parent.nextChild, parent)
633+
_trace = child
634+
try op(child)
620635
finally {
621-
_trace = oldFrame
622-
_trace.nextChild += 1
636+
parent.nextChild += 1
637+
_trace = parent
623638
}
624639
}
625640

src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,20 @@ trait FlagOps { self: TastyUniverse =>
2929
| Enum | Infix | Open | ParamAlias | Invisible
3030
)
3131

32+
type FlagParser = TastyFlagSet => Context => TastyFlagSet
33+
34+
val addDeferred: FlagParser = flags => _ => flags | Deferred
35+
val parseMethod: FlagParser = { mods0 => implicit ctx =>
36+
var mods = EmptyTastyFlags
37+
if (mods0.is(Erased)) erasedRefinementIsUnsupported[Unit]
38+
if (mods0.isOneOf(Given | Implicit)) mods |= Implicit
39+
mods
40+
}
41+
3242
object Creation {
3343
val ObjectDef: TastyFlagSet = Object | Lazy | Final | Stable
3444
val ObjectClassDef: TastyFlagSet = Object | Final
3545
val Default: u.FlagSet = newSymbolFlagSet(EmptyTastyFlags)
36-
val BoundedType: u.FlagSet = newSymbolFlagSet(Deferred)
3746
}
3847
def withAccess(flags: TastyFlagSet, inheritedAccess: TastyFlagSet): TastyFlagSet =
3948
flags | (inheritedAccess & (Private | Local | Protected))

src/compiler/scala/tools/nsc/tasty/bridge/TastyCore.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,5 @@ abstract class TastyCore { self: TastyUniverse =>
3434
private val Identity = (x: Any) => x
3535

3636
def id[T]: T => T = Identity.asInstanceOf[T => T]
37-
def map[T, U](ts: List[T], f: T => U): List[U] = if (f `eq` Identity) ts.asInstanceOf[List[U]] else ts.map(f)
3837

3938
}

0 commit comments

Comments
 (0)