Skip to content

Commit 6ede8f1

Browse files
committed
More robust self-type handling for cyclic exports
A member could also be visible through a mixin, so we perform the `derivesFrom` check in `canForward` on the self-type of the exporting class.
1 parent 30cdc57 commit 6ede8f1

File tree

2 files changed

+28
-12
lines changed

2 files changed

+28
-12
lines changed

compiler/src/dotty/tools/dotc/typer/Namer.scala

+10-10
Original file line numberDiff line numberDiff line change
@@ -1176,25 +1176,25 @@ class Namer { typer: Typer =>
11761176
import CanForward.*
11771177
val sym = mbr.symbol
11781178
/**
1179-
* Check the export selects an abstract member of the current class (issue #22147).
1179+
* The export selects a member of the current class (issue #22147).
1180+
* Assumes that cls.classInfo.selfType.derivesFrom(sym.owner) is true.
11801181
*/
1181-
def isAbstractMember: Boolean = sym.is(Deferred) && (expr match
1182-
case ths: This if ths.qual.isEmpty => true // access through 'this'
1183-
case id: Ident => id.denot.info match // access through self type
1184-
case cls2: ClassInfo => cls2.cls == cls
1185-
case _ => false
1182+
def isCurrentMember: Boolean = expr match
1183+
case id: (Ident | This) => // Access through self type or this
1184+
/* Given the usage context below, where cls's self type is a subtype of sym.owner,
1185+
it suffices to check if the denotation is a ClassInfo */
1186+
id.denot.info.isInstanceOf[ClassInfo]
11861187
case _ => false
1187-
)
11881188
if !sym.isAccessibleFrom(pathType) then
11891189
No("is not accessible")
11901190
else if sym.isConstructor || sym.is(ModuleClass) || sym.is(Bridge) || sym.is(ConstructorProxy) || sym.isAllOf(JavaModule) then
11911191
Skip
1192-
// if the cls is a subclass of the owner of the symbol
1192+
// if the cls is a subclass or mixes in the owner of the symbol
11931193
// and either
11941194
// * the symbols owner is the cls itself
11951195
// * the symbol is not a deferred symbol
1196-
// * the symbol is an abstract member #22147
1197-
else if cls.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred) || isAbstractMember) then
1196+
// * the symbol is a member of the current class (#22147)
1197+
else if cls.classInfo.selfType.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred) || isCurrentMember) then
11981198
No(i"is already a member of $cls")
11991199
else if pathMethod.exists && mbr.isType then
12001200
No("is a type, so it cannot be exported as extension method")

tests/neg/exports3.scala

+18-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ trait P:
22
def foo: Int
33

44
class A extends P:
5-
export this.foo // error
5+
export this.foo // error
66

77
trait Q extends P:
88
def bar: Int
@@ -18,4 +18,20 @@ class B extends R:
1818
export self.bar // error
1919
export this.a1.foo
2020
export self.a2.foo // error
21-
export a2.foo // error
21+
export a2.foo // error
22+
23+
abstract class D extends P:
24+
val p: P
25+
export p.foo
26+
27+
abstract class E:
28+
self: P =>
29+
export self.foo // error
30+
31+
abstract class F:
32+
self: P =>
33+
export this.foo // error
34+
35+
class G(p: P):
36+
self: P =>
37+
export p.foo

0 commit comments

Comments
 (0)