diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index e9d94fc47ff4..d7fe9dc1916a 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -410,19 +410,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 (#14508) + 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) @@ -503,6 +505,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 + } +}