Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule Out Exports of Member of the Current Class #22545

Merged
merged 1 commit into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1175,11 +1175,26 @@ class Namer { typer: Typer =>
def canForward(mbr: SingleDenotation, alias: TermName): CanForward = {
import CanForward.*
val sym = mbr.symbol
/**
* The export selects a member of the current class (issue #22147).
* Assumes that cls.classInfo.selfType.derivesFrom(sym.owner) is true.
*/
def isCurrentClassMember: Boolean = expr match
case id: (Ident | This) => // Access through self type or this
/* Given the usage context below, where cls's self type is a subtype of sym.owner,
it suffices to check if symbol is the same class. */
cls == id.symbol
case _ => false
if !sym.isAccessibleFrom(pathType) then
No("is not accessible")
else if sym.isConstructor || sym.is(ModuleClass) || sym.is(Bridge) || sym.is(ConstructorProxy) || sym.isAllOf(JavaModule) then
Skip
else if cls.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred)) then
// if the cls is a subclass or mixes in the owner of the symbol
// and either
// * the symbols owner is the cls itself
// * the symbol is not a deferred symbol
// * the symbol is a member of the current class (#22147)
else if cls.classInfo.selfType.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred) || isCurrentClassMember) then
No(i"is already a member of $cls")
else if pathMethod.exists && mbr.isType then
No("is a type, so it cannot be exported as extension method")
Expand Down
41 changes: 41 additions & 0 deletions tests/neg/exports3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
trait P:
def foo: Int

class A extends P:
export this.foo // error

trait Q extends P:
def bar: Int

trait R extends P:
def baz: Int
val a1: A
val a2: A

abstract class B extends R:
self =>
export this.baz // error
export self.bar // error
export this.a1.foo
export self.a2.foo // error
export a2.foo // error

abstract class D extends P:
val p: P
export p.foo

abstract class E:
self: P =>
export self.foo // error

abstract class F:
self: P =>
export this.foo // error

class G(p: P):
self: P =>
export p.foo

class H(p: P):
self: P =>
export this.p.foo
14 changes: 14 additions & 0 deletions tests/neg/i20245.check
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,17 @@
| Run with both -explain-cyclic and -Ydebug-cyclic to see full stack trace.
|
| longer explanation available when compiling with `-explain`
-- [E046] Cyclic Error: tests/neg/i20245/Typer_2.scala:10:7 ------------------------------------------------------------
10 |import effekt.source.{ resolve } // error
| ^
| Cyclic reference involving class Context
|
| The error occurred while trying to compute the base classes of class Context
| which required to compute the base classes of trait TyperOps
| which required to compute the signature of trait TyperOps
| which required to elaborate the export clause export unification.requireSubtype
| which required to compute the base classes of class Context
|
| Run with both -explain-cyclic and -Ydebug-cyclic to see full stack trace.
|
| longer explanation available when compiling with `-explain`
2 changes: 1 addition & 1 deletion tests/neg/i20245/Typer_2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import effekt.util.messages.ErrorReporter
import effekt.context.{ Context }

// This import is also NECESSARY for the cyclic error
import effekt.source.{ resolve }
import effekt.source.{ resolve } // error


trait TyperOps extends ErrorReporter { self: Context =>
Expand Down
Loading