Skip to content

Commit e6c9c95

Browse files
committed
Mention named givens in double def explainer
Print info at typer in example code. Could be automatic.
1 parent ebe241e commit e6c9c95

File tree

7 files changed

+130
-13
lines changed

7 files changed

+130
-13
lines changed

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,7 +2364,7 @@ class SymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(SymbolIsN
23642364
}
23652365

23662366
class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context)
2367-
extends NamingMsg(DoubleDefinitionID) {
2367+
extends NamingMsg(DoubleDefinitionID):
23682368
import Signature.MatchDegree.*
23692369

23702370
private def erasedType: Type =
@@ -2426,6 +2426,16 @@ extends NamingMsg(DoubleDefinitionID) {
24262426
} + details
24272427
}
24282428
def explain(using Context) =
2429+
def givenAddendum =
2430+
def isGivenName(sym: Symbol) = sym.name.startsWith("given_") // Desugar.inventGivenName
2431+
if decl.is(Given) && previousDecl.is(Given) && isGivenName(decl) && isGivenName(previousDecl) then
2432+
i"""
2433+
|3. Provide an explicit, unique name to given definitions, since the names
2434+
| assigned to anonymous givens may clash. For example:
2435+
|
2436+
| given myGiven: ${atPhase(typerPhase)(decl.info)}
2437+
|"""
2438+
else ""
24292439
decl.signature.matchDegree(previousDecl.signature) match
24302440
case FullMatch =>
24312441
i"""
@@ -2439,8 +2449,8 @@ extends NamingMsg(DoubleDefinitionID) {
24392449
|
24402450
|In your code the two declarations
24412451
|
2442-
| ${previousDecl.showDcl}
2443-
| ${decl.showDcl}
2452+
| ${atPhase(typerPhase)(previousDecl.showDcl)}
2453+
| ${atPhase(typerPhase)(decl.showDcl)}
24442454
|
24452455
|erase to the identical signature
24462456
|
@@ -2452,17 +2462,16 @@ extends NamingMsg(DoubleDefinitionID) {
24522462
|
24532463
|1. Rename one of the definitions, or
24542464
|2. Keep the same names in source but give one definition a distinct
2455-
| bytecode-level name via `@targetName` for example:
2465+
| bytecode-level name via `@targetName`; for example:
24562466
|
24572467
| @targetName("${decl.name.show}_2")
2458-
| ${decl.showDcl}
2459-
|
2468+
| ${atPhase(typerPhase)(decl.showDcl)}
2469+
|$givenAddendum
24602470
|Choose the `@targetName` argument carefully: it is the name that will be used
24612471
|when calling the method externally, so it should be unique and descriptive.
2462-
"""
2472+
|"""
24632473
case _ => ""
2464-
2465-
}
2474+
end DoubleDefinition
24662475

24672476
class ImportedTwice(sel: Name)(using Context) extends SyntaxMsg(ImportedTwiceID) {
24682477
def msg(using Context) = s"${sel.show} is imported twice on the same import line."

tests/neg/i23350.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@
3535
|
3636
| 1. Rename one of the definitions, or
3737
| 2. Keep the same names in source but give one definition a distinct
38-
| bytecode-level name via `@targetName` for example:
38+
| bytecode-level name via `@targetName`; for example:
3939
|
4040
| @targetName("apply_2")
4141
| def apply(a: UndefOr2[String]): Unit
4242
|
4343
| Choose the `@targetName` argument carefully: it is the name that will be used
4444
| when calling the method externally, so it should be unique and descriptive.
45-
|
4645
---------------------------------------------------------------------------------------------------------------------

tests/neg/i23402.check

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@
3535
|
3636
| 1. Rename one of the definitions, or
3737
| 2. Keep the same names in source but give one definition a distinct
38-
| bytecode-level name via `@targetName` for example:
38+
| bytecode-level name via `@targetName`; for example:
3939
|
4040
| @targetName("apply_2")
4141
| def apply(p1: String)(p2: Int): A
4242
|
4343
| Choose the `@targetName` argument carefully: it is the name that will be used
4444
| when calling the method externally, so it should be unique and descriptive.
45-
|
4645
---------------------------------------------------------------------------------------------------------------------

tests/neg/i23832a.check

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- [E120] Naming Error: tests/neg/i23832a.scala:9:8 --------------------------------------------------------------------
2+
9 | given Special[Option[Int]] = ??? // error
3+
| ^
4+
| Conflicting definitions:
5+
| final lazy given val given_Special_Option: Special[Option[Long]] in object syntax at line 8 and
6+
| final lazy given val given_Special_Option: Special[Option[Int]] in object syntax at line 9
7+
|---------------------------------------------------------------------------------------------------------------------
8+
| Explanation (enabled by `-explain`)
9+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10+
|
11+
| As part of the Scala compilation pipeline every type is reduced to its erased
12+
| (runtime) form. In this phase, among other transformations, generic parameters
13+
| disappear and separate parameter-list boundaries are flattened.
14+
|
15+
| For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit`
16+
| erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that
17+
| parameter names are irrelevant.
18+
|
19+
| In your code the two declarations
20+
|
21+
| final lazy given val given_Special_Option: Special[Option[Long]]
22+
| final lazy given val given_Special_Option: Special[Option[Int]]
23+
|
24+
| erase to the identical signature
25+
|
26+
| Special
27+
|
28+
| so the compiler cannot keep both: the generated bytecode symbols would collide.
29+
|
30+
| To fix this error, you need to disambiguate the two definitions. You can either:
31+
|
32+
| 1. Rename one of the definitions, or
33+
| 2. Keep the same names in source but give one definition a distinct
34+
| bytecode-level name via `@targetName`; for example:
35+
|
36+
| @targetName("given_Special_Option_2")
37+
| final lazy given val given_Special_Option: Special[Option[Int]]
38+
|
39+
| 3. Provide an explicit, unique name to given definitions, since the names
40+
| assigned to anonymous givens may clash. For example:
41+
|
42+
| given myGiven: Special[Option[Int]]
43+
|
44+
| Choose the `@targetName` argument carefully: it is the name that will be used
45+
| when calling the method externally, so it should be unique and descriptive.
46+
---------------------------------------------------------------------------------------------------------------------

tests/neg/i23832a.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options -explain
2+
3+
// follow-up to neg/i23402*.scala
4+
5+
trait Special[A]
6+
7+
object syntax:
8+
given Special[Option[Long]] = ???
9+
given Special[Option[Int]] = ??? // error

tests/neg/i23832b.check

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- [E120] Naming Error: tests/neg/i23832b.scala:9:8 --------------------------------------------------------------------
2+
9 | given [A] => Special[Option[A]] = ??? // error
3+
| ^
4+
| Conflicting definitions:
5+
| final lazy given val given_Special_Option: Special[Option[Long]] in object syntax at line 8 and
6+
| final given def given_Special_Option[A]: Special[Option[A]] in object syntax at line 9
7+
|---------------------------------------------------------------------------------------------------------------------
8+
| Explanation (enabled by `-explain`)
9+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10+
|
11+
| As part of the Scala compilation pipeline every type is reduced to its erased
12+
| (runtime) form. In this phase, among other transformations, generic parameters
13+
| disappear and separate parameter-list boundaries are flattened.
14+
|
15+
| For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit`
16+
| erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that
17+
| parameter names are irrelevant.
18+
|
19+
| In your code the two declarations
20+
|
21+
| final lazy given val given_Special_Option: Special[Option[Long]]
22+
| final given def given_Special_Option[A]: Special[Option[A]]
23+
|
24+
| erase to the identical signature
25+
|
26+
| (): Special
27+
|
28+
| so the compiler cannot keep both: the generated bytecode symbols would collide.
29+
|
30+
| To fix this error, you need to disambiguate the two definitions. You can either:
31+
|
32+
| 1. Rename one of the definitions, or
33+
| 2. Keep the same names in source but give one definition a distinct
34+
| bytecode-level name via `@targetName`; for example:
35+
|
36+
| @targetName("given_Special_Option_2")
37+
| final given def given_Special_Option[A]: Special[Option[A]]
38+
|
39+
| 3. Provide an explicit, unique name to given definitions, since the names
40+
| assigned to anonymous givens may clash. For example:
41+
|
42+
| given myGiven: [A]: Special[Option[A]]
43+
|
44+
| Choose the `@targetName` argument carefully: it is the name that will be used
45+
| when calling the method externally, so it should be unique and descriptive.
46+
---------------------------------------------------------------------------------------------------------------------

tests/neg/i23832b.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options -explain
2+
3+
// follow-up to neg/i23402*.scala
4+
5+
trait Special[A]
6+
7+
object syntax:
8+
given Special[Option[Long]] = ???
9+
given [A] => Special[Option[A]] = ??? // error

0 commit comments

Comments
 (0)