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

Conflicting outer definition in same file: ensure source exists #17439

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
43 changes: 30 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -408,12 +408,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// Does reference `tp` refer only to inherited symbols?
def isInherited(denot: Denotation) =
def isCurrent(mbr: SingleDenotation): Boolean =
!mbr.symbol.exists || mbr.symbol.owner == ctx.owner
!mbr.symbol.exists || mbr.symbol.owner == ctx.owner || ctx.owner.is(Package)
denot match
case denot: SingleDenotation => !isCurrent(denot)
case denot => !denot.hasAltWith(isCurrent)

def checkNoOuterDefs(denot: Denotation, last: Context, prevCtx: Context): Unit =
/* It is an error if an identifier x is available as an inherited member in an inner scope
* and the same name x is defined in an outer scope in the same source file, unless
* the inherited member (has an overloaded alternative that) coincides with
* (an overloaded alternative of) the definition x.
*/
def checkNoOuterDefs(denot: Denotation, ctx: Context, origCtx: Context): Unit =
def sameTermOrType(d1: SingleDenotation, d2: Denotation) =
d2.containsSym(d1.symbol) || d2.hasUniqueSym && {
val sym1 = d1.symbol
Expand All @@ -425,27 +430,39 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
else
(sym1.isAliasType || sym2.isAliasType) && d1.info =:= d2.info
}
val outer = last.outer
val owner = outer.owner
if (owner eq last.owner) && (outer.scope eq last.scope) then
checkNoOuterDefs(denot, outer, prevCtx)
else if !owner.is(Package) then
val scope = if owner.isClass then owner.info.decls else outer.scope
val competing = scope.denotsNamed(name).filterWithFlags(required, excluded)
val outerCtx = ctx.outer
val outerOwner = outerCtx.owner
if (outerOwner eq ctx.owner) && (outerCtx.scope eq ctx.scope) then
checkNoOuterDefs(denot, outerCtx, origCtx)
else if !outerOwner.isRoot then
val found =
if outerOwner.is(Package) then
def notInPackageObject(sym: Symbol) =
sym.owner == outerOwner || // sym.owner.isPackageObject is false if sym is defined in a parent of the package object
sym.owner.isPackageObject && sym.owner.name.endsWith(str.TOPLEVEL_SUFFIX) // top-level definitions
outerOwner.denot.asClass.membersNamed(name)
.filterWithPredicate(d => !d.symbol.is(Package)
&& notInPackageObject(d.symbol)
&& d.symbol.source.exists
&& isDefinedInCurrentUnit(d))
else
val scope = if outerOwner.isClass then outerOwner.info.decls else outerCtx.scope
scope.denotsNamed(name)
val competing = found.filterWithFlags(required, excluded | Synthetic)
if competing.exists then
val symsMatch = competing
.filterWithPredicate(sd => sameTermOrType(sd, denot))
.exists
if !symsMatch && !suppressErrors then
report.errorOrMigrationWarning(
AmbiguousReference(name, Definition, Inheritance, prevCtx)(using outer),
AmbiguousReference(name, Definition, Inheritance, origCtx)(using outerCtx),
pos, from = `3.0`)
if migrateTo3 then
patch(Span(pos.span.start),
if prevCtx.owner == refctx.owner.enclosingClass then "this."
else s"${prevCtx.owner.name}.this.")
if origCtx.owner == refctx.owner.enclosingClass then "this."
else s"${origCtx.owner.name}.this.")
else
checkNoOuterDefs(denot, outer, prevCtx)
checkNoOuterDefs(denot, outerCtx, origCtx)

if isNewDefScope then
val defDenot = ctx.denotNamed(name, required, excluded)
Expand Down
16 changes: 16 additions & 0 deletions tests/neg/ambiref.check
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,19 @@
| and inherited subsequently in class E
|
| longer explanation available when compiling with `-explain`
-- [E049] Reference Error: tests/neg/ambiref.scala:43:10 ---------------------------------------------------------------
43 | println(global) // error
| ^^^^^^
| Reference to global is ambiguous.
| It is both defined in package <empty>
| and inherited subsequently in object D
|
| longer explanation available when compiling with `-explain`
-- [E049] Reference Error: tests/neg/ambiref.scala:49:16 ---------------------------------------------------------------
49 | def t = new T { } // error
| ^
| Reference to T is ambiguous.
| It is both defined in package p
| and inherited subsequently in class C
|
| longer explanation available when compiling with `-explain`
22 changes: 21 additions & 1 deletion tests/neg/ambiref.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,24 @@ val global = 0
class C:
val global = 1
object D extends C:
println(global) // OK, since global is defined in package
println(global) // error

package p:
class T
trait P { trait T }
class C extends P:
def t = new T { } // error

package scala:
trait P { trait Option[+A] }
class C extends P:
def t = new Option[String] { } // OK, competing scala.Option is not defined in the same compilation unit

object test5:
class Mu // generates a synthetic companion object with an apply method
trait A {
val Mu = 1
}
trait B extends A {
def t = Mu // don't warn about synthetic companion
}
8 changes: 8 additions & 0 deletions tests/neg/i17433.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- [E049] Reference Error: tests/neg/i17433.scala:9:10 -----------------------------------------------------------------
9 | def g = f(42) // error
| ^
| Reference to f is ambiguous.
| It is both defined in package <empty>
| and inherited subsequently in class D
|
| longer explanation available when compiling with `-explain`
12 changes: 12 additions & 0 deletions tests/neg/i17433.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

class C:
def f(i: Int) = i + 1
def f(s: String) = s + "_1"

def f = 42

class D extends C:
def g = f(42) // error

@main def test() = println:
D().g
36 changes: 36 additions & 0 deletions tests/neg/t12186.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package object p extends p.U {
def b: Int = 0
trait Y
}

package p {
trait U {
def a: Int = 0
trait X
}

object c
def c1 = 0 // top-level def
trait Z
trait T {
def a = 1
def b = 1
def c = 1
def c1 = 1

trait X
trait Y
trait Z
}

trait RR extends T {
def m1 = a // ok
def m2 = b // ok
def m3 = c // error
def m4 = c1 // error

def n1: X // ok
def n2: Y // ok
def n3: Z // error
}
}
4 changes: 4 additions & 0 deletions tests/neg/t12186b/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package object p extends p.U {
def b: Int = 0
trait Y
}
31 changes: 31 additions & 0 deletions tests/neg/t12186b/B.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package p {
trait U {
def a: Int = 0
trait X
}

object c
def c1 = 0 // top-level def
trait Z
trait T {
def a = 1
def b = 1
def c = 1
def c1 = 1

trait X
trait Y
trait Z
}

trait RR extends T {
def m1 = a // ok
def m2 = b // ok
def m3 = c // error
def m4 = c1 // error

def n1: X // ok
def n2: Y // ok
def n3: Z // error
}
}
2 changes: 1 addition & 1 deletion tests/pos-special/fatal-warnings/i9260.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ end AstImpl

object untpd extends AstImpl[Null]:

def DefDef(ast: Ast): DefDef = ast match
def DefDef(ast: this.Ast): DefDef = ast match
case ast: DefDef => ast

end untpd
3 changes: 3 additions & 0 deletions tests/pos/i17433a/A_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package p

object Value
5 changes: 5 additions & 0 deletions tests/pos/i17433a/B_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package p

object B extends Enumeration {
val A = Value
}
7 changes: 7 additions & 0 deletions tests/pos/i17433b/p.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

package p:
trait T:
def t(i: Int) = i + 1
def t(s: String) = s + "_1"

package object q extends T
6 changes: 6 additions & 0 deletions tests/pos/i17433b/q.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

package p
package q

class C extends T:
def c = t(42)
11 changes: 11 additions & 0 deletions tests/pos/i17433c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

package p:
trait T:
def t(i: Int) = i + 1

package object q extends T

package q:

class C extends T:
def c = t(42) // OK
13 changes: 13 additions & 0 deletions tests/pos/i17433d.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

package p:
trait T:
def t(i: Int) = i + 1

package object q extends T:
override def t(i: Int) = i + 2
def t(s: String) = s + "_2"

package q:

class C extends T:
def c = t(42) // OK
12 changes: 12 additions & 0 deletions tests/pos/i17433e.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

package p:
trait T:
def t(i: Int) = i + 1
def t(s: String) = s + "_1"

package object q extends T

package q:

class C extends T:
def c = t(42) // OK
2 changes: 1 addition & 1 deletion tests/run/protectedacc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ package p {

abstract class X[T] extends PolyA[T] {

trait Inner extends B {
trait Inner extends this.B {
def self: T;
def self2: Node;
def getB: Inner;
Expand Down
Loading