From 59794010f69819c5134487445cefab1df86e56a4 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 13 Dec 2021 10:44:30 -0800 Subject: [PATCH] Allow override protected[C] in companion When extending C in its companion, allow override protected[C] where C denotes the enclosing companion module of C. Lookup of local companion is broken under `-from-tasty`, so this accommodation is disallowed for local companions. --- .../dotty/tools/dotc/typer/RefChecks.scala | 23 +++++++++++-------- tests/neg/t12494.scala | 17 ++++++++++++++ tests/pos/t12494.scala | 10 ++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 tests/neg/t12494.scala create mode 100644 tests/pos/t12494.scala diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 834cfba2a8a0..3f7874f15673 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -409,19 +409,21 @@ object RefChecks { overrideError("has weaker access privileges; it should not be private") // todo: align accessibility implication checking with isAccessible in Contexts - val ob = other.accessBoundary(member.owner) - val mb = member.accessBoundary(member.owner) def isOverrideAccessOK = - (member.flags & AccessFlags).isEmpty - && !member.privateWithin.exists // member is public, or - || (!other.is(Protected) || member.is(Protected)) - // if o is protected, so is m, and - && (ob.isContainedIn(mb) || other.isAllOf(JavaProtected)) - // m relaxes o's access boundary, - // or o is Java defined and protected (see #3946) + val memberIsPublic = (member.flags & AccessFlags).isEmpty && !member.privateWithin.exists + def protectedOK = !other.is(Protected) || member.is(Protected) // if o is protected, so is m + def accessBoundaryOK = + val ob = other.accessBoundary(member.owner) + val mb = member.accessBoundary(member.owner) + // restriction isLocalToBlock because companionModule fails under -from-tasty + def companionBoundaryOK = ob.isClass && !ob.isLocalToBlock && mb.is(Module) && (ob.companionModule eq mb.companionModule) + ob.isContainedIn(mb) || companionBoundaryOK // m relaxes o's access boundary, + def otherIsJavaProtected = other.isAllOf(JavaProtected) // or o is Java defined and protected (see #3946) + memberIsPublic || protectedOK && (accessBoundaryOK || otherIsJavaProtected) + end isOverrideAccessOK if !member.hasTargetName(other.targetName) then overrideTargetNameError() - else if (!isOverrideAccessOK) + else if !isOverrideAccessOK then overrideAccessError() else if (other.isClass) // direct overrides were already checked on completion (see Checking.chckWellFormed) @@ -502,6 +504,7 @@ object RefChecks { else checkOverrideDeprecated() } + end checkOverride /* TODO enable; right now the annotation is scala-private, so cannot be seen * here. diff --git a/tests/neg/t12494.scala b/tests/neg/t12494.scala new file mode 100644 index 000000000000..135ba49bbe40 --- /dev/null +++ b/tests/neg/t12494.scala @@ -0,0 +1,17 @@ + +object X { + // restriction in Scala 2 for local companions + // restriction in Scala 3 under -from-tasty + def m: Int = { + trait C { + protected[C] def f: Int + } + object C { + class C2 extends C { + protected[C] def f: Int = 42 // error // ok except for restrictions noted + def test = f + } + } + C.C2().test + } +} diff --git a/tests/pos/t12494.scala b/tests/pos/t12494.scala new file mode 100644 index 000000000000..ee0d6c1eb890 --- /dev/null +++ b/tests/pos/t12494.scala @@ -0,0 +1,10 @@ + +trait Base { + protected[Base] def f: Int +} +object Base { + class Child extends Base { + protected[Base] def f: Int = 42 // ok + def test = f + } +}