diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 71a10cf6eb20..51e1b847b6f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -710,7 +710,7 @@ object Erasure { if (sym.isEffectivelyErased) erasedDef(sym) else val restpe = if sym.isConstructor then defn.UnitType else sym.info.resultType - var vparams = outer.paramDefs(sym) + var vparams = outerParamDefs(sym) ::: ddef.vparamss.flatten.filterConserve(!_.symbol.is(Flags.Erased)) def skipContextClosures(rhs: Tree, crCount: Int)(using Context): Tree = @@ -746,6 +746,28 @@ object Erasure { super.typedDefDef(ddef1, sym) end typedDefDef + /** The outer parameter definition of a constructor if it needs one */ + private def outerParamDefs(constr: Symbol)(using ctx: Context): List[ValDef] = + if constr.isConstructor && hasOuterParam(constr.owner.asClass) then + constr.info match + case MethodTpe(outerName :: _, outerType :: _, _) => + val outerSym = ctx.newSymbol(constr, outerName, Flags.Param, outerType) + ValDef(outerSym) :: Nil + case _ => + // There's a possible race condition that a constructor was looked at + // after erasure before we had a chance to run ExplicitOuter on its class + // If furthermore the enclosing class does not always have constructors, + // but needs constructors in this particular case, we miss the constructor + // accessor that's produced with an `enteredAfter` in ExplicitOuter, so + // `tranformInfo` of the constructor in erasure yields a method type without + // an outer parameter. We fix this problem by adding the missing outer + // parameter here. + constr.copySymDenotation( + info = outer.addParam(constr.owner.asClass, constr.info) + ).installAfter(erasurePhase) + outerParamDefs(constr) + else Nil + override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = { val xxl = defn.isXXLFunctionClass(tree.typeOpt.typeSymbol) var implClosure @ Closure(_, meth, _) = super.typedClosure(tree, pt) diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index de483dcb333e..5ef687af3d94 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -240,7 +240,7 @@ object ExplicitOuter { needsOuterIfReferenced(cls) && outerAccessor(cls).exists /** Class constructor takes an outer argument. Can be called only after phase ExplicitOuter. */ - private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean = + def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean = !cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists /** Tree references an outer class of `cls` which is not a static owner. @@ -412,14 +412,5 @@ object ExplicitOuter { loop(start, count) catch case ex: ClassCastException => throw new ClassCastException(i"no path exists from ${ctx.owner.enclosingClass} to $toCls") - - /** The outer parameter definition of a constructor if it needs one */ - def paramDefs(constr: Symbol): List[ValDef] = - if (constr.isConstructor && hasOuterParam(constr.owner.asClass)) { - val MethodTpe(outerName :: _, outerType :: _, _) = constr.info - val outerSym = ctx.newSymbol(constr, outerName, Param, outerType) - ValDef(outerSym) :: Nil - } - else Nil } } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 9792ba1ee061..c22eca3996c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -472,7 +472,7 @@ object RefChecks { } } - def ignoreDeferred(mbr: Symbol) = + def ignoreDeferred(mbr: Symbol) = mbr.isType || mbr.isSuperAccessor // not yet synthesized || mbr.is(JavaDefined) && hasJavaErasedOverriding(mbr) @@ -619,15 +619,16 @@ object RefChecks { // (3) is violated but not (2). def checkNoAbstractDecls(bc: Symbol): Unit = { for (decl <- bc.info.decls) - if (decl.is(Deferred) && !ignoreDeferred(decl)) { + if (decl.is(Deferred)) { val impl = decl.matchingMember(clazz.thisType) - if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) { + if (impl == NoSymbol || decl.owner.isSubClass(impl.owner)) + && !ignoreDeferred(decl) + then val impl1 = clazz.thisType.nonPrivateMember(decl.name) // DEBUG ctx.log(i"${impl1}: ${impl1.info}") // DEBUG ctx.log(i"${clazz.thisType.memberInfo(decl)}") // DEBUG abstractClassError(false, "there is a deferred declaration of " + infoString(decl) + " which is not implemented in a subclass" + err.abstractVarMessage(decl)) - } } if (bc.asClass.superClass.is(Abstract)) checkNoAbstractDecls(bc.asClass.superClass) diff --git a/tests/run/i8425.check b/tests/run/i8425.check new file mode 100644 index 000000000000..51e519f6b43f --- /dev/null +++ b/tests/run/i8425.check @@ -0,0 +1,3 @@ +text: 22 +text: 65 +text: 10 diff --git a/tests/run/i8425.scala b/tests/run/i8425.scala new file mode 100644 index 000000000000..b49ab605eb75 --- /dev/null +++ b/tests/run/i8425.scala @@ -0,0 +1,15 @@ +import java.io.{ OutputStream, PrintStream } + +trait T { + val text: String + val stream = new PrintStream(new OutputStream { + def write(b: Int) = Console.println(s"text: $b") + }) { + override def println(x: Any) = ??? + } +} + +@main def Test = + val t = new T { val text = "hello" } + t.stream.write(22) + t.stream.println('A')