Skip to content

Commit 3a830c8

Browse files
authored
Improve "constructor proxy shadows outer" handling (#17154)
2 parents 3948ecc + 80b165c commit 3a830c8

File tree

8 files changed

+167
-25
lines changed

8 files changed

+167
-25
lines changed

Diff for: compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
190190
case InlineGivenShouldNotBeFunctionID // errorNumber 174
191191
case ValueDiscardingID // errorNumber 175
192192
case UnusedNonUnitValueID // errorNumber 176
193+
case ConstrProxyShadowsID // errorNumber 177
193194

194195
def errorNumber = ordinal - 1
195196

Diff for: compiler/src/dotty/tools/dotc/reporting/messages.scala

+31
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,37 @@ extends SyntaxMsg(VarArgsParamMustComeLastID) {
13051305

13061306
import typer.Typer.BindingPrec
13071307

1308+
class ConstrProxyShadows(proxy: TermRef, shadowed: Type, shadowedIsApply: Boolean)(using Context)
1309+
extends ReferenceMsg(ConstrProxyShadowsID), NoDisambiguation:
1310+
1311+
def clsString(using Context) = proxy.symbol.companionClass.showLocated
1312+
def shadowedString(using Context) = shadowed.termSymbol.showLocated
1313+
def appClause = if shadowedIsApply then " the apply method of" else ""
1314+
def appSuffix = if shadowedIsApply then ".apply" else ""
1315+
1316+
def msg(using Context) =
1317+
i"""Reference to constructor proxy for $clsString
1318+
|shadows outer reference to $shadowedString
1319+
|
1320+
|The instance needs to be created with an explicit `new`."""
1321+
1322+
def explain(using Context) =
1323+
i"""There is an ambiguity in the meaning of the call
1324+
|
1325+
| ${proxy.symbol.name}(...)
1326+
|
1327+
|It could mean creating an instance of $clsString with
1328+
|
1329+
| new ${proxy.symbol.companionClass.name}(...)
1330+
|
1331+
|Or it could mean calling$appClause $shadowedString as in
1332+
|
1333+
| ${shadowed.termSymbol.name}$appSuffix(...)
1334+
|
1335+
|To disambiguate, use an explicit `new` if you mean the former,
1336+
|or use a full prefix for ${shadowed.termSymbol.name} if you mean the latter."""
1337+
end ConstrProxyShadows
1338+
13081339
class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context)
13091340
extends ReferenceMsg(AmbiguousReferenceID), NoDisambiguation {
13101341

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+31-13
Original file line numberDiff line numberDiff line change
@@ -544,22 +544,40 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
544544
unimported = saved1
545545
foundUnderScala2 = saved2
546546

547-
def checkNotShadowed(ownType: Type) = ownType match
548-
case ownType: TermRef if ownType.symbol.is(ConstructorProxy) =>
549-
val shadowed = findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos)
550-
if shadowed.exists then
551-
report.error(
552-
em"""Reference to constructor proxy for ${ownType.symbol.companionClass.showLocated}
553-
|shadows outer reference to ${shadowed.termSymbol.showLocated}""", tree.srcPos)
554-
case _ =>
547+
/** Normally, returns `ownType` except if `ownType` is a constructor proxy,
548+
* and there is another shadowed type accessible with the same name that is not:
549+
* - if the prototype is an application:
550+
* - if the shadowed type has a method alternative or an apply method,
551+
* issue an ambiguity error
552+
* - otherwise again return `ownType`
553+
* - if the prototype is not an application, return the shadowed type
554+
*/
555+
def checkNotShadowed(ownType: Type): Type =
556+
ownType match
557+
case ownType: TermRef if ownType.symbol.is(ConstructorProxy) =>
558+
findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos) match
559+
case shadowed: TermRef =>
560+
pt match
561+
case pt: FunOrPolyProto =>
562+
def err(shadowedIsApply: Boolean) =
563+
report.error(ConstrProxyShadows(ownType, shadowed, shadowedIsApply), tree.srcPos)
564+
if shadowed.denot.hasAltWith(sd => sd.symbol.is(Method, butNot = Accessor)) then
565+
err(shadowedIsApply = false)
566+
else if shadowed.member(nme.apply).hasAltWith(_.symbol.is(Method, butNot = Accessor)) then
567+
err(shadowedIsApply = true)
568+
case _ =>
569+
return shadowed
570+
case shadowed =>
571+
case _ =>
572+
ownType
555573

556574
def setType(ownType: Type): Tree =
557-
checkNotShadowed(ownType)
558-
val tree1 = ownType match
559-
case ownType: NamedType if !prefixIsElidable(ownType) =>
560-
ref(ownType).withSpan(tree.span)
575+
val checkedType = checkNotShadowed(ownType)
576+
val tree1 = checkedType match
577+
case checkedType: NamedType if !prefixIsElidable(checkedType) =>
578+
ref(checkedType).withSpan(tree.span)
561579
case _ =>
562-
tree.withType(ownType)
580+
tree.withType(checkedType)
563581
val tree2 = toNotNullTermRef(tree1, pt)
564582
checkLegalValue(tree2, pt)
565583
tree2

Diff for: docs/_docs/reference/other-new-features/creator-applications.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@ be selected with `apply` (or be applied to arguments, in which case the `apply`
4747
inserted).
4848

4949
Constructor proxies are also not allowed to shadow normal definitions. That is,
50-
if an identifier resolves to a constructor proxy, and the same identifier is also
51-
defined or imported in some other scope, an ambiguity is reported.
50+
an ambiguity is reported, if
51+
52+
- an identifier resolves to a constructor proxy,
53+
- the same identifier is also defined or imported in some other scope,
54+
- the other reference can be applied to a (possibly empty) parameter list. That
55+
is, it refers either to a method or to a value containing an apply method as member.
5256

5357
## Motivation
5458

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
-- [E177] Reference Error: tests/neg-custom-args/explain/constructor-proxy-shadowing.scala:10:12 -----------------------
2+
10 | val x = A22("") // error: shadowing
3+
| ^^^
4+
| Reference to constructor proxy for class A22 in class A
5+
| shadows outer reference to method A22 in object Test
6+
|
7+
| The instance needs to be created with an explicit `new`.
8+
|--------------------------------------------------------------------------------------------------------------------
9+
| Explanation (enabled by `-explain`)
10+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
11+
| There is an ambiguity in the meaning of the call
12+
|
13+
| A22(...)
14+
|
15+
| It could mean creating an instance of class A22 in class A with
16+
|
17+
| new A22(...)
18+
|
19+
| Or it could mean calling method A22 in object Test as in
20+
|
21+
| A22(...)
22+
|
23+
| To disambiguate, use an explicit `new` if you mean the former,
24+
| or use a full prefix for A22 if you mean the latter.
25+
--------------------------------------------------------------------------------------------------------------------
26+
-- [E177] Reference Error: tests/neg-custom-args/explain/constructor-proxy-shadowing.scala:11:12 -----------------------
27+
11 | val y = A33("") // error: shadowing
28+
| ^^^
29+
| Reference to constructor proxy for class A33 in class A
30+
| shadows outer reference to object A33 in object Test
31+
|
32+
| The instance needs to be created with an explicit `new`.
33+
|--------------------------------------------------------------------------------------------------------------------
34+
| Explanation (enabled by `-explain`)
35+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36+
| There is an ambiguity in the meaning of the call
37+
|
38+
| A33(...)
39+
|
40+
| It could mean creating an instance of class A33 in class A with
41+
|
42+
| new A33(...)
43+
|
44+
| Or it could mean calling the apply method of object A33 in object Test as in
45+
|
46+
| A33.apply(...)
47+
|
48+
| To disambiguate, use an explicit `new` if you mean the former,
49+
| or use a full prefix for A33 if you mean the latter.
50+
--------------------------------------------------------------------------------------------------------------------
51+
-- [E177] Reference Error: tests/neg-custom-args/explain/constructor-proxy-shadowing.scala:16:8 ------------------------
52+
16 |val x = Seq(3) // error: shadowing
53+
| ^^^
54+
| Reference to constructor proxy for class Seq
55+
| shadows outer reference to getter Seq in package scala
56+
|
57+
| The instance needs to be created with an explicit `new`.
58+
|--------------------------------------------------------------------------------------------------------------------
59+
| Explanation (enabled by `-explain`)
60+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61+
| There is an ambiguity in the meaning of the call
62+
|
63+
| Seq(...)
64+
|
65+
| It could mean creating an instance of class Seq with
66+
|
67+
| new Seq(...)
68+
|
69+
| Or it could mean calling the apply method of getter Seq in package scala as in
70+
|
71+
| Seq.apply(...)
72+
|
73+
| To disambiguate, use an explicit `new` if you mean the former,
74+
| or use a full prefix for Seq if you mean the latter.
75+
--------------------------------------------------------------------------------------------------------------------
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
object Test extends App {
3+
def A22(s: String): String = s
4+
class A33(s: String)
5+
object A33:
6+
def apply(s: String) = ???
7+
class A {
8+
class A22(s: String)
9+
class A33(s: String)
10+
val x = A22("") // error: shadowing
11+
val y = A33("") // error: shadowing
12+
}
13+
}
14+
15+
class Seq(n: Int)
16+
val x = Seq(3) // error: shadowing

Diff for: tests/neg/constructor-proxy-shadowing.scala

-10
This file was deleted.

Diff for: tests/pos/constr-proxy-shadowing.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Number(n: Int)
2+
val x = Number(3)
3+
4+
class Seq(n: Int)
5+
val y = Seq
6+
7+

0 commit comments

Comments
 (0)