From 5a73a68fbb6f25da5278dd301d0d644459a2361c Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 26 Sep 2024 10:12:15 +0200 Subject: [PATCH] fix: Drop copied parent refinements before generating bytecode Refinements are copied over from parents, because they might be needed for tracked members that should have more specific types in the child. These members are generated without an implementation and should not be used in runtime. --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../transform/DropParentRefinements.scala | 35 +++++++++++++++++++ .../dotty/tools/dotc/transform/Getters.scala | 2 +- .../src/dotty/tools/dotc/typer/Namer.scala | 8 ++--- .../src/dotty/tools/dotc/typer/Typer.scala | 5 ++- tests/run/i21213-min.check | 1 + tests/run/i21213-min.scala | 9 +++++ tests/run/i21213.check | 1 + tests/run/i21213.scala | 10 ++++++ 9 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/DropParentRefinements.scala create mode 100644 tests/run/i21213-min.check create mode 100644 tests/run/i21213-min.scala create mode 100644 tests/run/i21213.check create mode 100644 tests/run/i21213.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 9b130e7d7804..f9503e4f8554 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -132,6 +132,7 @@ class Compiler { new ElimStaticThis, // Replace `this` references to static objects by global identifiers new CountOuterAccesses) :: // Identify outer accessors that can be dropped List(new DropOuterAccessors, // Drop unused outer accessors + new DropParentRefinements, // Drop parent refinements from a template new CheckNoSuperThis, // Check that supercalls don't contain references to `this` new Flatten, // Lift all inner classes to package scope new TransformWildcards, // Replace wildcards with default values diff --git a/compiler/src/dotty/tools/dotc/transform/DropParentRefinements.scala b/compiler/src/dotty/tools/dotc/transform/DropParentRefinements.scala new file mode 100644 index 000000000000..6062e1078a5e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/DropParentRefinements.scala @@ -0,0 +1,35 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.transform.MegaPhase.MiniPhase +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer +import dotty.tools.dotc.typer.Typer + +object DropParentRefinements: + val name: String = "dropParentRefinements" + val description: String = "drop parent refinements from a template" + +/** Drop parent refinements from a template. because they are generated without + * an implementation. These refinements are unusally required for tracked + * members with more specific types. + */ +class DropParentRefinements extends MiniPhase with IdentityDenotTransformer: + thisPhase => + import tpd.* + + override def phaseName: String = DropParentRefinements.name + + override def description: String = DropParentRefinements.description + + override def runsAfterGroupsOf: Set[String] = Set(CountOuterAccesses.name) + + override def changesMembers: Boolean = true // the phase drops parent refinements + + override def transformTemplate(tree: tpd.Template)(using Context): tpd.Tree = + val newBody = tree.body.filter(!_.hasAttachment(Typer.RefinementFromParent)) + tree.body.foreach { member => + if member.hasAttachment(Typer.RefinementFromParent) then + member.symbol.dropAfter(thisPhase) + } + cpy.Template(tree)(body = newBody) diff --git a/compiler/src/dotty/tools/dotc/transform/Getters.scala b/compiler/src/dotty/tools/dotc/transform/Getters.scala index 43289209d146..a58dffa04223 100644 --- a/compiler/src/dotty/tools/dotc/transform/Getters.scala +++ b/compiler/src/dotty/tools/dotc/transform/Getters.scala @@ -103,7 +103,7 @@ class Getters extends MiniPhase with SymTransformer { thisPhase => override def transformValDef(tree: ValDef)(using Context): Tree = val sym = tree.symbol if !sym.is(Method) then return tree - val getterDef = DefDef(sym.asTerm, tree.rhs).withSpan(tree.span) + val getterDef = DefDef(sym.asTerm, tree.rhs).withSpan(tree.span).withAttachmentsFrom(tree) if !sym.is(Mutable) then return getterDef ensureSetter(sym.asTerm) if !newSetters.contains(sym.setter) then return getterDef diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6167db62fbe0..0849e57b8c7d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -877,16 +877,16 @@ class Namer { typer: Typer => protected def addAnnotations(sym: Symbol): Unit = original match { case original: untpd.MemberDef => lazy val annotCtx = annotContext(original, sym) - original.setMods: + original.setMods: original.mods.withAnnotations : - original.mods.annotations.mapConserve: annotTree => + original.mods.annotations.mapConserve: annotTree => val cls = typedAheadAnnotationClass(annotTree)(using annotCtx) if (cls eq sym) report.error(em"An annotation class cannot be annotated with iself", annotTree.srcPos) annotTree else - val ann = - if cls.is(JavaDefined) then Checking.checkNamedArgumentForJavaAnnotation(annotTree, cls.asClass) + val ann = + if cls.is(JavaDefined) then Checking.checkNamedArgumentForJavaAnnotation(annotTree, cls.asClass) else annotTree val ann1 = Annotation.deferred(cls)(typedAheadExpr(ann)(using annotCtx)) sym.addAnnotation(ann1) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 901e27a2f1a1..8697cc35e650 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -78,6 +78,9 @@ object Typer { /** An attachment for GADT constraints that were inferred for a pattern. */ val InferredGadtConstraints = new Property.StickyKey[core.GadtConstraint] + /** Inducates that a definition was copied over from the parent refinements */ + val RefinementFromParent = new Property.StickyKey[Unit] + /** An attachment on a Select node with an `apply` field indicating that the `apply` * was inserted by the Typer. */ @@ -3081,7 +3084,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ( if sym.isType then TypeDef(sym.asType) else if sym.is(Method) then DefDef(sym.asTerm) else ValDef(sym.asTerm) - ).withSpan(impl.span.startPos) + ).withSpan(impl.span.startPos).withAttachment(RefinementFromParent, ()) body ++ refinements case None => body diff --git a/tests/run/i21213-min.check b/tests/run/i21213-min.check new file mode 100644 index 000000000000..5716ca5987cb --- /dev/null +++ b/tests/run/i21213-min.check @@ -0,0 +1 @@ +bar diff --git a/tests/run/i21213-min.scala b/tests/run/i21213-min.scala new file mode 100644 index 000000000000..0f6aa6f8ddd5 --- /dev/null +++ b/tests/run/i21213-min.scala @@ -0,0 +1,9 @@ +import scala.language.experimental.modularity +import scala.language.future + +sealed abstract class Foo(tracked val discriminator: String) +class Bar extends Foo("bar") + +val bar: Foo = Bar() +object Test extends App: + println(bar.discriminator) diff --git a/tests/run/i21213.check b/tests/run/i21213.check new file mode 100644 index 000000000000..5716ca5987cb --- /dev/null +++ b/tests/run/i21213.check @@ -0,0 +1 @@ +bar diff --git a/tests/run/i21213.scala b/tests/run/i21213.scala new file mode 100644 index 000000000000..ec609afd7da3 --- /dev/null +++ b/tests/run/i21213.scala @@ -0,0 +1,10 @@ +import scala.language.experimental.modularity +import scala.language.future + +enum Foo(tracked val discriminator: String): + case Bar() extends Foo("bar") + case Baz() extends Foo("baz") + +val bar: Foo = Foo.Bar() +object Test extends App: + println(bar.discriminator)