Skip to content
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
38 changes: 28 additions & 10 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2364,7 +2364,7 @@ class SymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(SymbolIsN
}

class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context)
extends NamingMsg(DoubleDefinitionID) {
extends NamingMsg(DoubleDefinitionID):
import Signature.MatchDegree.*

private def erasedType: Type =
Expand Down Expand Up @@ -2426,6 +2426,25 @@ extends NamingMsg(DoubleDefinitionID) {
} + details
}
def explain(using Context) =
def givenAddendum =
def isGivenName(sym: Symbol) = sym.name.startsWith("given_") // Desugar.inventGivenName
def print(tpe: Type): String =
def addParams(tpe: Type): List[String] = tpe match
case tpe: MethodType =>
val s = if tpe.isContextualMethod then i"(${tpe.paramInfos}%, %) =>" else ""
s :: addParams(tpe.resType)
case tpe: PolyType =>
i"[${tpe.paramNames}%, %] =>" :: addParams(tpe.resType)
case tpe =>
i"$tpe" :: Nil
addParams(tpe).mkString(" ")
if decl.is(Given) && previousDecl.is(Given) && isGivenName(decl) && isGivenName(previousDecl) then
i"""| Provide an explicit, unique name to given definitions,
| since the names assigned to anonymous givens may clash. For example:
|
| given myGiven: ${print(atPhase(typerPhase)(decl.info))}
|"""
else ""
decl.signature.matchDegree(previousDecl.signature) match
case FullMatch =>
i"""
Expand All @@ -2439,30 +2458,29 @@ extends NamingMsg(DoubleDefinitionID) {
|
|In your code the two declarations
|
| ${previousDecl.showDcl}
| ${decl.showDcl}
| ${atPhase(typerPhase)(previousDecl.showDcl)}
| ${atPhase(typerPhase)(decl.showDcl)}
|
|erase to the identical signature
|
| ${erasedType}
|
|so the compiler cannot keep both: the generated bytecode symbols would collide.
|
|To fix this error, you need to disambiguate the two definitions. You can either:
|To fix this error, you must disambiguate the two definitions by doing one of the following:
|
|1. Rename one of the definitions, or
|1. Rename one of the definitions.$givenAddendum
|2. Keep the same names in source but give one definition a distinct
| bytecode-level name via `@targetName` for example:
| bytecode-level name via `@targetName`; for example:
|
| @targetName("${decl.name.show}_2")
| ${decl.showDcl}
| ${atPhase(typerPhase)(decl.showDcl)}
|
|Choose the `@targetName` argument carefully: it is the name that will be used
|when calling the method externally, so it should be unique and descriptive.
"""
|"""
case _ => ""

}
end DoubleDefinition

class ImportedTwice(sel: Name)(using Context) extends SyntaxMsg(ImportedTwiceID) {
def msg(using Context) = s"${sel.show} is imported twice on the same import line."
Expand Down
7 changes: 3 additions & 4 deletions tests/neg/i23350.check
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,15 @@
|
| so the compiler cannot keep both: the generated bytecode symbols would collide.
|
| To fix this error, you need to disambiguate the two definitions. You can either:
| To fix this error, you must disambiguate the two definitions by doing one of the following:
|
| 1. Rename one of the definitions, or
| 1. Rename one of the definitions.
| 2. Keep the same names in source but give one definition a distinct
| bytecode-level name via `@targetName` for example:
| bytecode-level name via `@targetName`; for example:
|
| @targetName("apply_2")
| def apply(a: UndefOr2[String]): Unit
|
| Choose the `@targetName` argument carefully: it is the name that will be used
| when calling the method externally, so it should be unique and descriptive.
|
---------------------------------------------------------------------------------------------------------------------
7 changes: 3 additions & 4 deletions tests/neg/i23402.check
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,15 @@
|
| so the compiler cannot keep both: the generated bytecode symbols would collide.
|
| To fix this error, you need to disambiguate the two definitions. You can either:
| To fix this error, you must disambiguate the two definitions by doing one of the following:
|
| 1. Rename one of the definitions, or
| 1. Rename one of the definitions.
| 2. Keep the same names in source but give one definition a distinct
| bytecode-level name via `@targetName` for example:
| bytecode-level name via `@targetName`; for example:
|
| @targetName("apply_2")
| def apply(p1: String)(p2: Int): A
|
| Choose the `@targetName` argument carefully: it is the name that will be used
| when calling the method externally, so it should be unique and descriptive.
|
---------------------------------------------------------------------------------------------------------------------
45 changes: 45 additions & 0 deletions tests/neg/i23832a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- [E120] Naming Error: tests/neg/i23832a.scala:9:8 --------------------------------------------------------------------
9 | given Special[Option[Int]] = ??? // error
| ^
| Conflicting definitions:
| final lazy given val given_Special_Option: Special[Option[Long]] in object syntax at line 8 and
| final lazy given val given_Special_Option: Special[Option[Int]] in object syntax at line 9
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| As part of the Scala compilation pipeline every type is reduced to its erased
| (runtime) form. In this phase, among other transformations, generic parameters
| disappear and separate parameter-list boundaries are flattened.
|
| For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit`
| erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that
| parameter names are irrelevant.
|
| In your code the two declarations
|
| final lazy given val given_Special_Option: Special[Option[Long]]
| final lazy given val given_Special_Option: Special[Option[Int]]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be something like:

final lazy given Special[Option[Long]] = ???
final lazy given Special[Option[Int]] = ???

which leads to 2 identical signatures: given_Special_Option?

When Developer names the vals, it is very obvious which names the Developer gives them - problems usually start with non-obvious auto-naming.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The declarations are not printed with "fidelity". That could be fixed as a follow-up. (I assume but did not check that the syntax changed faster than the printer.)

|
| erase to the identical signature
|
| Special
|
| so the compiler cannot keep both: the generated bytecode symbols would collide.
|
| To fix this error, you must disambiguate the two definitions by doing one of the following:
|
| 1. Rename one of the definitions. Provide an explicit, unique name to given definitions,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe Rename one or all the definitions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these errors fail at the first pair that is noticed; and "already defined" in particular works with just minimal info; it does not try to enumerate all problematic definitions and their types. The spec will sometimes say "pairwise distinct", and the user only needs to fix one pair at a time.

| since the names assigned to anonymous givens may clash. For example:
|
| given myGiven: Special[Option[Int]]
|
| 2. Keep the same names in source but give one definition a distinct
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and ..., but give one or to all of them a distinct - like we should not suggest to rename only one of them - sometimes renaming them all makes more sense and improves consistency

| bytecode-level name via `@targetName`; for example:
|
| @targetName("given_Special_Option_2")
| final lazy given val given_Special_Option: Special[Option[Int]]
|
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is 3rd option on how to name them, specifically in for-comprehension (it could shadow previous one):

for {
  // ...
  givenName @ given Type <- ??? 
  // ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mr-git that is a different error ID, though a similar explainer about "how to name givens" may be warranted (since it is not obvious); presumably it is obvious how to rename other things?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you create a ticket and PR for that? I struggled with this case the most.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mr-git I added some explain text on the other PR. There is no pattern syntax for given here because there is no equivalent to val Extract(x, y) = expr.

| Choose the `@targetName` argument carefully: it is the name that will be used
| when calling the method externally, so it should be unique and descriptive.
---------------------------------------------------------------------------------------------------------------------
9 changes: 9 additions & 0 deletions tests/neg/i23832a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//> using options -explain

// follow-up to neg/i23402*.scala

trait Special[A]

object syntax:
given Special[Option[Long]] = ???
given Special[Option[Int]] = ??? // error
45 changes: 45 additions & 0 deletions tests/neg/i23832b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- [E120] Naming Error: tests/neg/i23832b.scala:9:8 --------------------------------------------------------------------
9 | given [A] => Special[Option[A]] = ??? // error
| ^
| Conflicting definitions:
| final lazy given val given_Special_Option: Special[Option[Long]] in object syntax at line 8 and
| final given def given_Special_Option[A]: Special[Option[A]] in object syntax at line 9
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| As part of the Scala compilation pipeline every type is reduced to its erased
| (runtime) form. In this phase, among other transformations, generic parameters
| disappear and separate parameter-list boundaries are flattened.
|
| For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit`
| erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that
| parameter names are irrelevant.
|
| In your code the two declarations
|
| final lazy given val given_Special_Option: Special[Option[Long]]
| final given def given_Special_Option[A]: Special[Option[A]]
|
| erase to the identical signature
|
| (): Special
|
| so the compiler cannot keep both: the generated bytecode symbols would collide.
|
| To fix this error, you must disambiguate the two definitions by doing one of the following:
|
| 1. Rename one of the definitions. Provide an explicit, unique name to given definitions,
| since the names assigned to anonymous givens may clash. For example:
|
| given myGiven: [A] => Special[Option[A]]
|
| 2. Keep the same names in source but give one definition a distinct
| bytecode-level name via `@targetName`; for example:
|
| @targetName("given_Special_Option_2")
| final given def given_Special_Option[A]: Special[Option[A]]
|
| Choose the `@targetName` argument carefully: it is the name that will be used
| when calling the method externally, so it should be unique and descriptive.
---------------------------------------------------------------------------------------------------------------------
9 changes: 9 additions & 0 deletions tests/neg/i23832b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//> using options -explain

// follow-up to neg/i23402*.scala

trait Special[A]

object syntax:
given Special[Option[Long]] = ???
given [A] => Special[Option[A]] = ??? // error
Loading