Skip to content

Commit 5e8a595

Browse files
committed
Enforce no shadowing restriction
1 parent 845a1b6 commit 5e8a595

File tree

5 files changed

+50
-25
lines changed

5 files changed

+50
-25
lines changed

compiler/src/dotty/tools/dotc/ast/MainProxies.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ object MainProxies {
4040
}
4141

4242
private def checkNoShadowing(mainFun: Symbol)(using Context) =
43-
val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, mainFun).typeSymbol
43+
val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, EmptyFlags, mainFun).typeSymbol
4444
if cls.exists && cls.owner != ctx.owner then
4545
report.warning(
4646
i"""The class `${ctx.printer.fullNameString(mainFun)}` generated from `@main` will shadow the existing ${cls.showLocated}.

compiler/src/dotty/tools/dotc/core/ContextOps.scala

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,22 @@ object ContextOps:
2525

2626
/** The denotation with the given `name` and all `required` flags in current context
2727
*/
28-
def denotNamed(name: Name, required: FlagSet = EmptyFlags): Denotation = inContext(ctx) {
29-
if (ctx.owner.isClass)
30-
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
31-
if (ctx.scope.size == 1) {
32-
val elem = ctx.scope.lastEntry
33-
if (elem.name == name) return elem.sym.denot // return self
28+
def denotNamed(name: Name, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation =
29+
inContext(ctx) {
30+
if (ctx.owner.isClass)
31+
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
32+
if (ctx.scope.size == 1) {
33+
val elem = ctx.scope.lastEntry
34+
if (elem.name == name) return elem.sym.denot // return self
35+
}
36+
val pre = ctx.owner.thisType
37+
pre.findMember(name, pre, required, excluded)
3438
}
35-
val pre = ctx.owner.thisType
36-
pre.findMember(name, pre, required, EmptyFlags)
37-
}
38-
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
39-
ctx.owner.findMember(name, ctx.owner.thisType, required, EmptyFlags)
40-
else
41-
ctx.scope.denotsNamed(name).filterWithFlags(required, EmptyFlags).toDenot(NoPrefix)
42-
}
39+
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
40+
ctx.owner.findMember(name, ctx.owner.thisType, required, excluded)
41+
else
42+
ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix)
43+
}
4344

4445
/** A fresh local context with given tree and owner.
4546
* Owner might not exist (can happen for self valdefs), in which case

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

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,16 @@ class Typer extends Namer
135135
* @param name the name of the identifier
136136
* @param pt the expected type
137137
* @param required flags the result's symbol must have
138+
* @param excluded flags the result's symbol must not have
138139
* @param pos indicates position to use for error reporting
139140
*/
140-
def findRef(name: Name, pt: Type, required: FlagSet, pos: SrcPos)(using Context): Type = {
141+
def findRef(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos)(using Context): Type = {
141142
val refctx = ctx
142143
val noImports = ctx.mode.is(Mode.InPackageClauseName)
143-
def fail(msg: Message) = report.error(msg, pos)
144+
def suppressErrors = excluded.is(ConstructorProxy)
145+
// when searching for references shadowed by a constructor proxy, do not report errors
146+
def fail(msg: Message) =
147+
if !suppressErrors then report.error(msg, pos)
144148

145149
/** A symbol qualifies if it really exists and is not a package class.
146150
* In addition, if we are in a constructor of a pattern, we ignore all definitions
@@ -204,7 +208,7 @@ class Typer extends Namer
204208
imp.sym.info match
205209
case ImportType(expr) =>
206210
val pre = expr.tpe
207-
var denot = pre.memberBasedOnFlags(name, required, EmptyFlags)
211+
var denot = pre.memberBasedOnFlags(name, required, excluded)
208212
.accessibleFrom(pre)(using refctx)
209213
// Pass refctx so that any errors are reported in the context of the
210214
// reference instead of the context of the import scope
@@ -332,7 +336,7 @@ class Typer extends Namer
332336
val scope = if owner.isClass then owner.info.decls else outer.scope
333337
if scope.lookup(name).exists then
334338
val symsMatch = scope.lookupAll(name).exists(denot.containsSym)
335-
if !symsMatch then
339+
if !symsMatch && !suppressErrors then
336340
report.errorOrMigrationWarning(
337341
AmbiguousReference(name, Definition, Inheritance, prevCtx)(using outer),
338342
pos)
@@ -344,7 +348,7 @@ class Typer extends Namer
344348
checkNoOuterDefs(denot, outer, prevCtx)
345349

346350
if isNewDefScope then
347-
val defDenot = ctx.denotNamed(name, required)
351+
val defDenot = ctx.denotNamed(name, required, excluded)
348352
if (qualifies(defDenot)) {
349353
val found =
350354
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
@@ -462,7 +466,7 @@ class Typer extends Namer
462466
unimported = Set.empty
463467
foundUnderScala2 = NoType
464468
try
465-
val found = findRef(name, pt, EmptyFlags, tree.srcPos)
469+
val found = findRef(name, pt, EmptyFlags, EmptyFlags, tree.srcPos)
466470
if foundUnderScala2.exists && !(foundUnderScala2 =:= found) then
467471
report.migrationWarning(
468472
ex"""Name resolution will change.
@@ -474,7 +478,17 @@ class Typer extends Namer
474478
unimported = saved1
475479
foundUnderScala2 = saved2
476480

481+
def checkNotShadowed(ownType: Type) = ownType match
482+
case ownType: TermRef if ownType.symbol.is(ConstructorProxy) =>
483+
val shadowed = findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos)
484+
if shadowed.exists then
485+
report.error(
486+
em"""Reference to creator proxy for ${ownType.symbol.companionClass.showLocated}
487+
|shadows outer reference to ${shadowed.termSymbol.showLocated}""", tree.srcPos)
488+
case _ =>
489+
477490
def setType(ownType: Type): Tree =
491+
checkNotShadowed(ownType)
478492
val tree1 = ownType match
479493
case ownType: NamedType if !prefixIsElidable(ownType) =>
480494
ref(ownType).withSpan(tree.span)
@@ -3475,10 +3489,10 @@ class Typer extends Namer
34753489
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
34763490
def tryExtension(using Context): Tree =
34773491
try
3478-
findRef(selName, WildcardType, ExtensionMethod, tree.srcPos) match
3492+
findRef(selName, WildcardType, ExtensionMethod, EmptyFlags, tree.srcPos) match
34793493
case ref: TermRef =>
34803494
extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType)
3481-
case _ => findRef(selProto.extensionName, WildcardType, ExtensionMethod, tree.srcPos) match
3495+
case _ => findRef(selProto.extensionName, WildcardType, ExtensionMethod, EmptyFlags, tree.srcPos) match
34823496
case ref: TermRef =>
34833497
extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType)
34843498
case _ => EmptyTree
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
object Test extends App {
3+
def A22(s: String): String = s
4+
class A {
5+
class A22(s: String) {
6+
def run = s
7+
}
8+
val x = A22("") // error: shadowing
9+
}
10+
}

tests/pos/t522.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package imptwice
22

3-
class foo(s: String);
3+
abstract class foo(s: String);
44

55
object Util {
6-
def foo(s: String) = new foo(s)
6+
def foo(s: String) = new foo(s) {}
77
}
88

99
import imptwice.Util._

0 commit comments

Comments
 (0)