Skip to content

Commit

Permalink
fix: Drop copied parent refinements before generating bytecode
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
KacperFKorban committed Oct 8, 2024
1 parent b8c5ecb commit 5a73a68
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 6 deletions.
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Getters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/run/i21213-min.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bar
9 changes: 9 additions & 0 deletions tests/run/i21213-min.scala
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions tests/run/i21213.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bar
10 changes: 10 additions & 0 deletions tests/run/i21213.scala
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 5a73a68

Please sign in to comment.