diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 593d5f036197..cab47025e4b1 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -167,6 +167,11 @@ object NameOps { def expandedName(prefix: Name): N = expandedName(prefix, nme.EXPAND_SEPARATOR) + /** Revert the expanded name. Note: This currently gives incorrect results + * if the normal name contains `nme.EXPAND_SEPARATOR`, i.e. two consecutive '$' + * signs. This can happen for instance if a super accessor is paired with + * an encoded name, e.g. super$$plus$eq. See #765. + */ def unexpandedName: N = { val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR) if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N] diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala index 2e14097232da..26128cf33854 100644 --- a/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -59,7 +59,13 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th private def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail var sym: Symbol = NoSymbol - val SuperAccessorName(memberName) = ctx.atPhase(ctx.picklerPhase){ implicit ctx => acc.name }: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type + val unexpandedAccName = + if (acc.is(ExpandedName)) // Cannot use unexpandedName because of #765. t2183.scala would fail if we did. + acc.name + .drop(acc.name.indexOfSlice(nme.EXPAND_SEPARATOR ++ nme.SUPER_PREFIX)) + .drop(nme.EXPAND_SEPARATOR.length) + else acc.name + val SuperAccessorName(memberName) = unexpandedAccName: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type ctx.debuglog(i"starting rebindsuper from $base of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") while (bcs.nonEmpty && sym == NoSymbol) { val other = bcs.head.info.nonPrivateDecl(memberName) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 23201a9785cc..31cfef9140d7 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -71,11 +71,12 @@ class SuperAccessors(thisTransformer: DenotTransformer) { val Select(qual, name) = sel val sym = sel.symbol val clazz = qual.symbol.asClass - val supername = name.superName + var supername = name.superName + if (clazz is Trait) supername = supername.expandedName(clazz) val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") - val deferredOrPrivate = if (clazz is Trait) Deferred else Private + val deferredOrPrivate = if (clazz is Trait) Deferred | ExpandedName else Private val acc = ctx.newSymbol( clazz, supername, SuperAccessor | Artifact | Method | deferredOrPrivate, sel.tpe.widenSingleton.ensureMethodic, coord = sym.coord).enteredAfter(thisTransformer) @@ -106,10 +107,11 @@ class SuperAccessors(thisTransformer: DenotTransformer) { val member = sym.overridingSymbol(clazz) if (mix != tpnme.EMPTY || !member.exists || - !(member is AbsOverride) && member.isIncompleteIn(clazz)) + !((member is AbsOverride) && member.isIncompleteIn(clazz))) ctx.error( i"${sym.showLocated} is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'", sel.pos) + else println(i"ok super $sel ${sym.showLocated} $member $clazz ${member.isIncompleteIn(clazz)}") } else if (mix == tpnme.EMPTY && !(sym.owner is Trait)) // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index b39d0e928951..26ae099d3c09 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -96,6 +96,7 @@ class tests extends CompilerTest { @Test def new_all = compileFiles(newDir, twice) + @Test def neg_abstractOverride() = compileFile(negDir, "abstract-override", xerrors = 2) @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) @Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2) diff --git a/tests/neg/abstract-override.scala b/tests/neg/abstract-override.scala new file mode 100644 index 000000000000..c1ce83725134 --- /dev/null +++ b/tests/neg/abstract-override.scala @@ -0,0 +1,8 @@ +trait T { def foo: Int } +trait T1 extends T { override def foo = super.foo } +trait T2 extends T { override def foo = super.foo } +object Test extends T2 with T1 { + def main(args: Array[String]) = { + assert(foo == 3) + } +} diff --git a/tests/run/i756.scala b/tests/run/i756.scala new file mode 100644 index 000000000000..dd9b2be4faf8 --- /dev/null +++ b/tests/run/i756.scala @@ -0,0 +1,8 @@ +trait T { def foo: Int = 3 } +trait T1 extends T { override def foo = super.foo } +trait T2 extends T { override def foo = super.foo } +object Test extends T2 with T1 { + def main(args: Array[String]) = { + assert(foo == 3) + } +}