diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index db639d961b23..e4a7cbffeec1 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -990,17 +990,26 @@ object SymDenotations { def isInlineMethod(implicit ctx: Context): Boolean = isAllOf(InlineMethod, butNot = Accessor) - def isRetainedInlineMethod(using Context): Boolean = - isAllOf(InlineMethod, butNot = AccessorOrDeferred) + /** Does this method or field need to be retained at runtime */ + def isRetainedInline(using Context): Boolean = + is(Inline, butNot = Deferred) && allOverriddenSymbols.exists(!_.is(Inline)) + /** Does this method need to be retained at runtime */ + def isRetainedInlineMethod(using Context): Boolean = + is(Method, butNot = Accessor) && isRetainedInline + /** Is this a Scala 2 macro */ final def isScala2Macro(implicit ctx: Context): Boolean = is(Macro) && symbol.owner.is(Scala2x) /** An erased value or an inline method. */ def isEffectivelyErased(implicit ctx: Context): Boolean = - is(Erased) || isInlineMethod && !isRetainedInlineMethod + is(Erased) || is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot) + // Do not mark local inline vals as erased. Currently some inline val references do not get + // fully inlined and then would fail the erased check. + // TODO: remove this condition when #8842 and #8843 are fixed + && (owner.isClass || is(Method)) /** ()T and => T types should be treated as equivalent for this symbol. * Note: For the moment, we treat Scala-2 compiled symbols as loose matching, diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 37e0be2b7789..1e516c9f5037 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -213,8 +213,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private object dropInlines extends TreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Inlined(call, _, _) => - cpy.Inlined(tree)(call, Nil, Typed(ref(defn.Predef_undefined), TypeTree(tree.tpe)).withSpan(tree.span)) + case Inlined(call, _, expansion) => + val newExpansion = tree.tpe match + case ConstantType(c) => Literal(c) + case _ => Typed(ref(defn.Predef_undefined), TypeTree(tree.tpe)) + cpy.Inlined(tree)(call, Nil, newExpansion.withSpan(tree.span)) case _ => super.transform(tree) } } diff --git a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala index 2aeb0a186434..32a0a35f752c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala @@ -7,6 +7,7 @@ import DenotTransformers.SymTransformer import Flags._ import SymDenotations._ import Symbols._ +import Types._ import typer.RefChecks import MegaPhase.MiniPhase import ast.tpd @@ -34,19 +35,26 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => override def transformApply(tree: Apply)(implicit ctx: Context): Tree = if (tree.fun.tpe.widen.isErasedMethod) - cpy.Apply(tree)(tree.fun, tree.args.map(arg => ref(defn.Predef_undefined))) + cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree)) else tree override def transformValDef(tree: ValDef)(implicit ctx: Context): Tree = if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty) - cpy.ValDef(tree)(rhs = ref(defn.Predef_undefined)) + cpy.ValDef(tree)(rhs = trivialErasedTree(tree)) else tree override def transformDefDef(tree: DefDef)(implicit ctx: Context): Tree = if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty) - cpy.DefDef(tree)(rhs = ref(defn.Predef_undefined)) + cpy.DefDef(tree)(rhs = trivialErasedTree(tree)) else tree + + private def trivialErasedTree(tree: Tree)(using Context): Tree = + tree.tpe.widenTermRefExpr.dealias match + case ConstantType(c) => Literal(c) + case _ => ref(defn.Predef_undefined) + } + object PruneErasedDefs { val name: String = "pruneErasedDefs" } diff --git a/tests/run/erased-inline-vals.scala b/tests/run/erased-inline-vals.scala new file mode 100644 index 000000000000..710f81e1fedf --- /dev/null +++ b/tests/run/erased-inline-vals.scala @@ -0,0 +1,47 @@ + +abstract class A: + def x: Int + val y: Int + +class B extends A: + inline def x: Int = 1 + inline val y = 2 + +class C extends A: + final val x: Int = 3 + final val y = 4 + +class D: + inline def x: Int = 5 + inline val y = 6 + +@main def Test = + val b: B = new B + assert(b.x == 1) + assert(b.y == 2) + + val a: A = b + assert(a.x == 1) + assert(a.y == 2) + + val c: C = new C + assert(c.x == 3) + assert(c.y == 4) + + val a2: A = c + assert(a2.x == 3) + assert(a2.y == 4) + + val d: D = new D + assert(d.x == 5) + assert(d.y == 6) + + + assert(classOf[B].getDeclaredMethods.size == 2) + assert(classOf[B].getDeclaredFields.isEmpty) + + assert(classOf[C].getDeclaredMethods.size == 2) + assert(classOf[C].getDeclaredFields.size == 1) + + assert(classOf[D].getDeclaredMethods.isEmpty) + assert(classOf[D].getFields.isEmpty)