diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index f7ffb7289568..8d3ce14983b7 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -338,11 +338,23 @@ object LambdaLift { else (topClass, JavaStatic) } else (lOwner, EmptyFlags) + // Drop Module because class is no longer a singleton in the lifted context. + var initFlags = local.flags &~ Module | Private | Lifted | maybeStatic + if (local is Method) { + if (newOwner is Trait) + // Drop Final when a method is lifted into a trait. + // According to the JVM specification, a method declared inside interface cannot have the final flag. + // "Methods of interfaces may have any of the flags in Table 4.6-A set except ACC_PROTECTED, ACC_FINAL, ..." + // (https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6) + initFlags = initFlags &~ Final + else + // Add Final when a method is lifted into a class. + initFlags = initFlags | Final + } local.copySymDenotation( owner = newOwner, name = newName(local), - initFlags = local.flags &~ Module &~ Final | Private | Lifted | maybeStatic, - // drop Module because class is no longer a singleton in the lifted context. + initFlags = initFlags, info = liftedInfo(local)).installAfter(thisPhase) } for (local <- free.keys) diff --git a/tests/run/i4404a.check b/tests/run/i4404a.check new file mode 100644 index 000000000000..11444aa1f1fe --- /dev/null +++ b/tests/run/i4404a.check @@ -0,0 +1,8 @@ +false +false +false +true +false +true +true +true diff --git a/tests/run/i4404a.scala b/tests/run/i4404a.scala new file mode 100644 index 000000000000..b322218cf930 --- /dev/null +++ b/tests/run/i4404a.scala @@ -0,0 +1,19 @@ +import java.lang.reflect.Modifier + +class Car +final class Volvo extends Car + +object Car { + val car = new Car {} +} + +object Test { + def main(args: Array[String]) = { + List(new Car, new Volvo, Car, Car.car) + .map(_.getClass.getModifiers) + .foreach { m => + println(Modifier.isPrivate(m)) + println(Modifier.isFinal(m)) + } + } +} diff --git a/tests/run/i4404b.check b/tests/run/i4404b.check new file mode 100644 index 000000000000..bb101b641b9b --- /dev/null +++ b/tests/run/i4404b.check @@ -0,0 +1,2 @@ +true +true diff --git a/tests/run/i4404b.scala b/tests/run/i4404b.scala new file mode 100644 index 000000000000..cf9653692b86 --- /dev/null +++ b/tests/run/i4404b.scala @@ -0,0 +1,14 @@ +import java.lang.reflect.Modifier + +object Test { + val x = { + final class Foo + new Foo + } + + def main(args: Array[String]) = { + val m = x.getClass.getModifiers + println(Modifier.isPrivate(m)) + println(Modifier.isFinal(m)) + } +} diff --git a/tests/run/i4404c.check b/tests/run/i4404c.check new file mode 100644 index 000000000000..1d474d525571 --- /dev/null +++ b/tests/run/i4404c.check @@ -0,0 +1,2 @@ +false +true diff --git a/tests/run/i4404c.scala b/tests/run/i4404c.scala new file mode 100644 index 000000000000..29d504d57bce --- /dev/null +++ b/tests/run/i4404c.scala @@ -0,0 +1,23 @@ +import java.lang.reflect.Modifier + +trait T { + def f = { + def f0 = () + } +} + +class C { + def g = { + def g0 = () + } +} + +object Test { + def main(args: Array[String]) = { + List((new T {}).getClass.getInterfaces.head, (new C).getClass) + .map(_.getDeclaredMethods) + .foreach { ms => + println(ms.exists(m => Modifier.isFinal(m.getModifiers))) + } + } +}