Skip to content

Commit bf3b224

Browse files
committed
Fix #8425: Avoid race between erasure and explicit outer
1 parent f548a18 commit bf3b224

File tree

2 files changed

+24
-11
lines changed

2 files changed

+24
-11
lines changed

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ object Erasure {
710710
if (sym.isEffectivelyErased) erasedDef(sym)
711711
else
712712
val restpe = if sym.isConstructor then defn.UnitType else sym.info.resultType
713-
var vparams = outer.paramDefs(sym)
713+
var vparams = outerParamDefs(sym)
714714
::: ddef.vparamss.flatten.filterConserve(!_.symbol.is(Flags.Erased))
715715

716716
def skipContextClosures(rhs: Tree, crCount: Int)(using Context): Tree =
@@ -746,6 +746,28 @@ object Erasure {
746746
super.typedDefDef(ddef1, sym)
747747
end typedDefDef
748748

749+
/** The outer parameter definition of a constructor if it needs one */
750+
private def outerParamDefs(constr: Symbol)(using ctx: Context): List[ValDef] =
751+
if constr.isConstructor && hasOuterParam(constr.owner.asClass) then
752+
constr.info match
753+
case MethodTpe(outerName :: _, outerType :: _, _) =>
754+
val outerSym = ctx.newSymbol(constr, outerName, Flags.Param, outerType)
755+
ValDef(outerSym) :: Nil
756+
case _ =>
757+
// There's a possible race condition that a constructor was looked at
758+
// after erasure before we had a chance to run ExplicitOuter on its class
759+
// If furthermore the enclosing class does not always have constructors,
760+
// but needs constructors in this particular case, we miss the constructor
761+
// accessor that's produced with an `enteredAfter` in ExplicitOuter, so
762+
// `tranformInfo` of the constructor in erasure yields a method type without
763+
// an outer parameter. We fix this problem by adding the missing outer
764+
// parameter here.
765+
constr.copySymDenotation(
766+
info = outer.addParam(constr.owner.asClass, constr.info)
767+
).installAfter(erasurePhase)
768+
outerParamDefs(constr)
769+
else Nil
770+
749771
override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = {
750772
val xxl = defn.isXXLFunctionClass(tree.typeOpt.typeSymbol)
751773
var implClosure @ Closure(_, meth, _) = super.typedClosure(tree, pt)

compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ object ExplicitOuter {
240240
needsOuterIfReferenced(cls) && outerAccessor(cls).exists
241241

242242
/** Class constructor takes an outer argument. Can be called only after phase ExplicitOuter. */
243-
private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
243+
def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
244244
!cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists
245245

246246
/** Tree references an outer class of `cls` which is not a static owner.
@@ -412,14 +412,5 @@ object ExplicitOuter {
412412
loop(start, count)
413413
catch case ex: ClassCastException =>
414414
throw new ClassCastException(i"no path exists from ${ctx.owner.enclosingClass} to $toCls")
415-
416-
/** The outer parameter definition of a constructor if it needs one */
417-
def paramDefs(constr: Symbol): List[ValDef] =
418-
if (constr.isConstructor && hasOuterParam(constr.owner.asClass)) {
419-
val MethodTpe(outerName :: _, outerType :: _, _) = constr.info
420-
val outerSym = ctx.newSymbol(constr, outerName, Param, outerType)
421-
ValDef(outerSym) :: Nil
422-
}
423-
else Nil
424415
}
425416
}

0 commit comments

Comments
 (0)