Skip to content

Commit d757454

Browse files
Merge pull request #8836 from dotty-staging/make-inline-vals-effectively-erased
Make inline vals effectively erased
2 parents da65a1a + 01ca6e6 commit d757454

File tree

4 files changed

+75
-8
lines changed

4 files changed

+75
-8
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -990,17 +990,26 @@ object SymDenotations {
990990
def isInlineMethod(implicit ctx: Context): Boolean =
991991
isAllOf(InlineMethod, butNot = Accessor)
992992

993-
def isRetainedInlineMethod(using Context): Boolean =
994-
isAllOf(InlineMethod, butNot = AccessorOrDeferred)
993+
/** Does this method or field need to be retained at runtime */
994+
def isRetainedInline(using Context): Boolean =
995+
is(Inline, butNot = Deferred)
995996
&& allOverriddenSymbols.exists(!_.is(Inline))
996997

998+
/** Does this method need to be retained at runtime */
999+
def isRetainedInlineMethod(using Context): Boolean =
1000+
is(Method, butNot = Accessor) && isRetainedInline
1001+
9971002
/** Is this a Scala 2 macro */
9981003
final def isScala2Macro(implicit ctx: Context): Boolean = is(Macro) && symbol.owner.is(Scala2x)
9991004

10001005
/** An erased value or an inline method.
10011006
*/
10021007
def isEffectivelyErased(implicit ctx: Context): Boolean =
1003-
is(Erased) || isInlineMethod && !isRetainedInlineMethod
1008+
is(Erased) || is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot)
1009+
// Do not mark local inline vals as erased. Currently some inline val references do not get
1010+
// fully inlined and then would fail the erased check.
1011+
// TODO: remove this condition when #8842 and #8843 are fixed
1012+
&& (owner.isClass || is(Method))
10041013

10051014
/** ()T and => T types should be treated as equivalent for this symbol.
10061015
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
213213

214214
private object dropInlines extends TreeMap {
215215
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
216-
case Inlined(call, _, _) =>
217-
cpy.Inlined(tree)(call, Nil, Typed(ref(defn.Predef_undefined), TypeTree(tree.tpe)).withSpan(tree.span))
216+
case Inlined(call, _, expansion) =>
217+
val newExpansion = tree.tpe match
218+
case ConstantType(c) => Literal(c)
219+
case _ => Typed(ref(defn.Predef_undefined), TypeTree(tree.tpe))
220+
cpy.Inlined(tree)(call, Nil, newExpansion.withSpan(tree.span))
218221
case _ => super.transform(tree)
219222
}
220223
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import DenotTransformers.SymTransformer
77
import Flags._
88
import SymDenotations._
99
import Symbols._
10+
import Types._
1011
import typer.RefChecks
1112
import MegaPhase.MiniPhase
1213
import ast.tpd
@@ -34,19 +35,26 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
3435

3536
override def transformApply(tree: Apply)(implicit ctx: Context): Tree =
3637
if (tree.fun.tpe.widen.isErasedMethod)
37-
cpy.Apply(tree)(tree.fun, tree.args.map(arg => ref(defn.Predef_undefined)))
38+
cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree))
3839
else tree
3940

4041
override def transformValDef(tree: ValDef)(implicit ctx: Context): Tree =
4142
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty)
42-
cpy.ValDef(tree)(rhs = ref(defn.Predef_undefined))
43+
cpy.ValDef(tree)(rhs = trivialErasedTree(tree))
4344
else tree
4445

4546
override def transformDefDef(tree: DefDef)(implicit ctx: Context): Tree =
4647
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty)
47-
cpy.DefDef(tree)(rhs = ref(defn.Predef_undefined))
48+
cpy.DefDef(tree)(rhs = trivialErasedTree(tree))
4849
else tree
50+
51+
private def trivialErasedTree(tree: Tree)(using Context): Tree =
52+
tree.tpe.widenTermRefExpr.dealias match
53+
case ConstantType(c) => Literal(c)
54+
case _ => ref(defn.Predef_undefined)
55+
4956
}
57+
5058
object PruneErasedDefs {
5159
val name: String = "pruneErasedDefs"
5260
}

tests/run/erased-inline-vals.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
abstract class A:
3+
def x: Int
4+
val y: Int
5+
6+
class B extends A:
7+
inline def x: Int = 1
8+
inline val y = 2
9+
10+
class C extends A:
11+
final val x: Int = 3
12+
final val y = 4
13+
14+
class D:
15+
inline def x: Int = 5
16+
inline val y = 6
17+
18+
@main def Test =
19+
val b: B = new B
20+
assert(b.x == 1)
21+
assert(b.y == 2)
22+
23+
val a: A = b
24+
assert(a.x == 1)
25+
assert(a.y == 2)
26+
27+
val c: C = new C
28+
assert(c.x == 3)
29+
assert(c.y == 4)
30+
31+
val a2: A = c
32+
assert(a2.x == 3)
33+
assert(a2.y == 4)
34+
35+
val d: D = new D
36+
assert(d.x == 5)
37+
assert(d.y == 6)
38+
39+
40+
assert(classOf[B].getDeclaredMethods.size == 2)
41+
assert(classOf[B].getDeclaredFields.isEmpty)
42+
43+
assert(classOf[C].getDeclaredMethods.size == 2)
44+
assert(classOf[C].getDeclaredFields.size == 1)
45+
46+
assert(classOf[D].getDeclaredMethods.isEmpty)
47+
assert(classOf[D].getFields.isEmpty)

0 commit comments

Comments
 (0)