From dd36255fac1a7766bbd655770275d6158513b701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 25 Nov 2020 18:09:40 +0100 Subject: [PATCH] Fix #8602: Do not create mixins for constant-expression final vals. This matches what Scala 2 does (addressing binary compatibility with Scala 2) and allows a trait with only such `final val`s to be extended from Java. --- .../src/dotty/tools/dotc/transform/Constructors.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Mixin.scala | 10 ++++++---- compiler/src/dotty/tools/dotc/transform/SymUtils.scala | 8 +++++++- tests/run/i8602/ChildClass_2.java | 5 +++++ tests/run/i8602/ParentTrait_1.scala | 3 +++ tests/run/i8602/Test_3.scala | 8 ++++++++ 6 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 tests/run/i8602/ChildClass_2.java create mode 100644 tests/run/i8602/ParentTrait_1.scala create mode 100644 tests/run/i8602/Test_3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index a46469dddb44..a778ed2325b5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -208,7 +208,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = } else dropped += sym case stat @ DefDef(name, _, _, tpt, _) - if stat.symbol.isGetter && stat.symbol.owner.is(Trait) && !stat.symbol.is(Lazy) => + if stat.symbol.isGetter && stat.symbol.owner.is(Trait) && !stat.symbol.is(Lazy) && !stat.symbol.isConstExprFinalVal => val sym = stat.symbol assert(isRetained(sym), sym) if !stat.rhs.isEmpty && !isWildcardArg(stat.rhs) then diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index d9bcb5fcf544..7b8e2b986e3a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -126,7 +126,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => if (sym.is(Accessor, butNot = Deferred) && ownerIsTrait) { val sym1 = - if (sym.is(Lazy)) sym + if (sym.is(Lazy) || sym.symbol.isConstExprFinalVal) sym else sym.copySymDenotation(initFlags = sym.flags &~ (ParamAccessor | Inline) | Deferred) sym1.ensureNotPrivate } @@ -166,7 +166,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => private def needsTraitSetter(sym: Symbol)(using Context): Boolean = sym.isGetter && !wasOneOf(sym, DeferredOrLazy | ParamAccessor) && atPhase(thisPhase) { !sym.setter.exists } - && !sym.info.resultType.isInstanceOf[ConstantType] + && !sym.isConstExprFinalVal private def makeTraitSetter(getter: TermSymbol)(using Context): Symbol = getter.copy( @@ -251,7 +251,10 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => cls.srcPos) EmptyTree - for (getter <- mixin.info.decls.toList if getter.isGetter && !wasOneOf(getter, Deferred)) yield { + for + getter <- mixin.info.decls.toList + if getter.isGetter && !wasOneOf(getter, Deferred) && !getter.isConstExprFinalVal + yield if (isCurrent(getter) || getter.name.is(ExpandedName)) { val rhs = if (wasOneOf(getter, ParamAccessor)) @@ -266,7 +269,6 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => transformFollowing(DefDef(mkForwarderSym(getter.asTerm), rhs)) } else EmptyTree - } } def setters(mixin: ClassSymbol): List[Tree] = diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 5645e612e05b..ecea13e5b8ee 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -16,6 +16,7 @@ import ValueClasses.isDerivedValueClass import Decorators._ import Constants.Constant import Annotations.Annotation +import Phases._ import ast.tpd.Literal import language.implicitConversions @@ -167,6 +168,11 @@ object SymUtils: self.owner.info.decl(fieldName).suchThat(!_.is(Method)).symbol } + def isConstExprFinalVal(using Context): Boolean = + atPhaseNoLater(erasurePhase) { + self.is(Final) && self.info.resultType.isInstanceOf[ConstantType] + } + def isField(using Context): Boolean = self.isTerm && !self.is(Method) @@ -268,4 +274,4 @@ object SymUtils: Annotation(defn.TargetNameAnnot, Literal(Constant(nameFn(original.targetName).toString)).withSpan(original.span))) end extension -end SymUtils \ No newline at end of file +end SymUtils diff --git a/tests/run/i8602/ChildClass_2.java b/tests/run/i8602/ChildClass_2.java new file mode 100644 index 000000000000..da70975f648e --- /dev/null +++ b/tests/run/i8602/ChildClass_2.java @@ -0,0 +1,5 @@ +public class ChildClass_2 extends Object implements ParentTrait { + public int getFoo() { + return foo(); + } +} diff --git a/tests/run/i8602/ParentTrait_1.scala b/tests/run/i8602/ParentTrait_1.scala new file mode 100644 index 000000000000..6417d554e50d --- /dev/null +++ b/tests/run/i8602/ParentTrait_1.scala @@ -0,0 +1,3 @@ +trait ParentTrait { + final val foo = 5 +} diff --git a/tests/run/i8602/Test_3.scala b/tests/run/i8602/Test_3.scala new file mode 100644 index 000000000000..7b1797f8e586 --- /dev/null +++ b/tests/run/i8602/Test_3.scala @@ -0,0 +1,8 @@ +object Test { + def main(args: Array[String]): Unit = + val child = new ChildClass_2() + assert(child.foo == 5) + assert(child.getFoo() == 5) + val parent: ParentTrait = child + assert(parent.foo == 5) +}