Skip to content

Commit 9362914

Browse files
authored
Merge pull request #6025 from dotty-staging/fix/double-defs
Improve double definition errors
2 parents 5b1f277 + d8164b5 commit 9362914

File tree

9 files changed

+154
-20
lines changed

9 files changed

+154
-20
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
8282
else NoMatch
8383
else NoMatch
8484

85+
/** Does this signature potentially clash with `that` ? */
86+
def clashes(that: Signature)(implicit ctx: Context): Boolean =
87+
matchDegree(that) == FullMatch
88+
8589
/** name.toString == "" or name.toString == "_" */
8690
private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD
8791

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -797,19 +797,27 @@ object Symbols {
797797
NoDenotation // force it in order to set `denot` field of NoSymbol
798798

799799
implicit class Copier[N <: Name](sym: Symbol { type ThisName = N })(implicit ctx: Context) {
800-
/** Copy a symbol, overriding selective fields */
800+
/** Copy a symbol, overriding selective fields.
801+
* Note that `coord` and `associatedFile` will be set from the fields in `owner`, not
802+
* the fields in `sym`.
803+
*/
801804
def copy(
802805
owner: Symbol = sym.owner,
803806
name: N = (sym.name: N), // Dotty deviation: type ascription to avoid leaking private sym (only happens in unpickling), won't be needed once #1723 is fixed
804807
flags: FlagSet = sym.flags,
805808
info: Type = sym.info,
806809
privateWithin: Symbol = sym.privateWithin,
807-
coord: Coord = sym.coord,
808-
associatedFile: AbstractFile = sym.associatedFile): Symbol =
810+
coord: Coord = NoCoord, // Can be `= owner.coord` once we boostrap
811+
associatedFile: AbstractFile = null // Can be `= owner.associatedFile` once we boostrap
812+
): Symbol = {
813+
val coord1 = if (coord == NoCoord) owner.coord else coord
814+
val associatedFile1 = if (associatedFile == null) owner.associatedFile else associatedFile
815+
809816
if (sym.isClass)
810-
ctx.newClassSymbol(owner, name.asTypeName, flags, _ => info, privateWithin, coord, associatedFile)
817+
ctx.newClassSymbol(owner, name.asTypeName, flags, _ => info, privateWithin, coord1, associatedFile1)
811818
else
812-
ctx.newSymbol(owner, name, flags, info, privateWithin, coord)
819+
ctx.newSymbol(owner, name, flags, info, privateWithin, coord1)
820+
}
813821
}
814822

815823
/** Makes all denotation operations available on symbols */

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public enum ErrorMessageID {
128128
PolymorphicMethodMissingTypeInParentID,
129129
ParamsNoInlineID,
130130
JavaSymbolIsNotAValueID,
131-
DoubleDeclarationID,
131+
DoubleDefinitionID,
132132
MatchCaseOnlyNullWarningID,
133133
ImportRenamedTwiceID,
134134
TypeTestAlwaysSucceedsID,

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,18 +2137,38 @@ object messages {
21372137
val explanation: String = ""
21382138
}
21392139

2140-
case class DoubleDeclaration(decl: Symbol, previousDecl: Symbol)(implicit ctx: Context) extends Message(DoubleDeclarationID) {
2140+
case class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(implicit ctx: Context) extends Message(DoubleDefinitionID) {
21412141
val kind: String = "Duplicate Symbol"
21422142
val msg: String = {
21432143
val details = if (decl.isRealMethod && previousDecl.isRealMethod) {
21442144
// compare the signatures when both symbols represent methods
21452145
decl.signature.matchDegree(previousDecl.signature) match {
2146-
/* case Signature.NoMatch => // can't happen because decl.matches(previousDecl) is checked before reporting this error */
2147-
case Signature.ParamMatch => "\nOverloads with matching parameter types are not allowed."
2148-
case _ /* Signature.FullMatch */ => "\nThe definitions have matching type signatures after erasure."
2146+
case Signature.NoMatch =>
2147+
"" // shouldn't be reachable
2148+
case Signature.ParamMatch =>
2149+
"have matching parameter types."
2150+
case Signature.FullMatch =>
2151+
"have the same type after erasure."
21492152
}
21502153
} else ""
2151-
hl"${decl.showLocated} is already defined as ${previousDecl.showDcl} ${if (previousDecl.span.exists) s"at line ${previousDecl.sourcePos.line + 1}" else ""}." + details
2154+
def symLocation(sym: Symbol) = {
2155+
val lineDesc =
2156+
if (sym.span.exists && sym.span != sym.owner.span)
2157+
s" at line ${sym.sourcePos.line + 1}" else ""
2158+
i"in ${sym.owner}${lineDesc}"
2159+
}
2160+
val clashDescription =
2161+
if (decl.owner eq previousDecl.owner)
2162+
"Double definition"
2163+
else if ((decl.owner eq base) || (previousDecl eq base))
2164+
"Name clash between defined and inherited member"
2165+
else
2166+
"Name clash between inherited members"
2167+
2168+
em"""$clashDescription:
2169+
|${previousDecl.showDcl} ${symLocation(previousDecl)} and
2170+
|${decl.showDcl} ${symLocation(decl)}
2171+
|""" + details
21522172
}
21532173
val explanation: String = ""
21542174
}

compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import MegaPhase._
77
import Types._, Contexts._, Flags._, DenotTransformers._
88
import Symbols._, StdNames._, Trees._
99
import TypeErasure.ErasedValueType, ValueClasses._
10+
import reporting.diagnostic.messages.DoubleDefinition
1011

1112
object ElimErasedValueType {
1213
val name: String = "elimErasedValueType"
@@ -103,12 +104,7 @@ class ElimErasedValueType extends MiniPhase with InfoTransformer {
103104
//
104105
// The problem is that `map` was forwarded twice, with different instantiated types.
105106
// Maybe we should move mixin forwarding after erasure to avoid redundant forwarders like these.
106-
ctx.error(
107-
em"""double definition:
108-
|$sym1: $info1 in ${sym1.owner} and
109-
|$sym2: $info2 in ${sym2.owner}
110-
|have same type after erasure: $info""",
111-
root.sourcePos)
107+
ctx.error(DoubleDefinition(sym1, sym2, root), root.sourcePos)
112108
}
113109
val earlyCtx = ctx.withPhase(ctx.elimRepeatedPhase.next)
114110
while (opc.hasNext) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -752,10 +752,10 @@ trait Checking {
752752
def javaFieldMethodPair =
753753
decl.is(JavaDefined) && other.is(JavaDefined) &&
754754
decl.is(Method) != other.is(Method)
755-
if (decl.matches(other) && !javaFieldMethodPair) {
755+
if ((decl.signature.clashes(other.signature) || decl.matches(other)) && !javaFieldMethodPair) {
756756
def doubleDefError(decl: Symbol, other: Symbol): Unit =
757757
if (!decl.info.isErroneous && !other.info.isErroneous)
758-
ctx.error(DoubleDeclaration(decl, other), decl.sourcePos)
758+
ctx.error(DoubleDefinition(decl, other, cls), decl.sourcePos)
759759
if (decl is Synthetic) doubleDefError(other, decl)
760760
else doubleDefError(decl, other)
761761
}

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1514,7 +1514,7 @@ class ErrorMessagesTests extends ErrorMessagesTest {
15141514
}.expect { (ictx, messages) =>
15151515
implicit val ctx: Context = ictx
15161516
assertMessageCount(1, messages)
1517-
val DoubleDeclaration(symbol, previousSymbol) :: Nil = messages
1517+
val DoubleDefinition(symbol, previousSymbol, _) :: Nil = messages
15181518
assertEquals(symbol.name.mangledString, "a")
15191519
}
15201520

tests/neg/doubleDefinition.check

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<2471..2471> in doubleDefinition.scala
2+
Double definition:
3+
def foo: [T <: Int](x: T): T in class Test20 at line 143 and
4+
def foo: [S <: Int, T <: Int](x: S): T in class Test20 at line 144
5+
have the same type after erasure.
6+
<2338..2338> in doubleDefinition.scala
7+
Double definition:
8+
def foo: [T <: Int](x: T): T in class Test19 at line 137 and
9+
def foo(x: Int): Int in class Test19 at line 138
10+
have the same type after erasure.
11+
<2016..2016> in doubleDefinition.scala
12+
Double definition:
13+
var foo: Int in class Test16 at line 115 and
14+
def foo: => Int in class Test16 at line 116
15+
<1949..1949> in doubleDefinition.scala
16+
Double definition:
17+
var foo: Int in class Test15 at line 111 and
18+
def foo: => String in class Test15 at line 112
19+
<1885..1885> in doubleDefinition.scala
20+
Double definition:
21+
var foo: Int in class Test14 at line 107 and
22+
def foo: => Int in class Test14 at line 108
23+
<1827..1827> in doubleDefinition.scala
24+
Double definition:
25+
var foo: Int in class Test13 at line 103 and
26+
def foo: => String in class Test13 at line 104
27+
<1772..1772> in doubleDefinition.scala
28+
Double definition:
29+
val foo: Int in class Test12 at line 99 and
30+
def foo: => Int in class Test12 at line 100
31+
<1705..1705> in doubleDefinition.scala
32+
Double definition:
33+
val foo: Int in class Test11 at line 95 and
34+
def foo: => String in class Test11 at line 96
35+
<1641..1641> in doubleDefinition.scala
36+
Double definition:
37+
val foo: Int in class Test10 at line 91 and
38+
def foo: => Int in class Test10 at line 92
39+
<1583..1583> in doubleDefinition.scala
40+
Double definition:
41+
val foo: Int in class Test9 at line 87 and
42+
def foo: => String in class Test9 at line 88
43+
<1481..1481> in doubleDefinition.scala
44+
Double definition:
45+
var foo: Int in class Test8d at line 81 and
46+
def foo: => Int in class Test8d at line 82
47+
<1428..1428> in doubleDefinition.scala
48+
Double definition:
49+
def foo: => Int in class Test8c at line 76 and
50+
var foo: Int in class Test8c at line 77
51+
<1375..1375> in doubleDefinition.scala
52+
Double definition:
53+
def foo: => Int in class Test8b at line 71 and
54+
val foo: Int in class Test8b at line 72
55+
<1322..1322> in doubleDefinition.scala
56+
Double definition:
57+
val foo: Int in class Test8 at line 66 and
58+
def foo: => Int in class Test8 at line 67
59+
<1236..1236> in doubleDefinition.scala
60+
Double definition:
61+
def foo(x: List[A]): A => A in trait Test7 at line 61 and
62+
def foo(x: List[A]): (B, B) => B in trait Test7 at line 62
63+
have matching parameter types.
64+
<984..984> in doubleDefinition.scala
65+
Double definition:
66+
def foo(x: List[A]): A => A in trait Test6 at line 54 and
67+
def foo(x: List[B]): B => B in trait Test6 at line 55
68+
have the same type after erasure.
69+
<739..739> in doubleDefinition.scala
70+
Double definition:
71+
var foo: Int in class Test4d at line 40 and
72+
def foo: => Int in class Test4d at line 41
73+
<686..686> in doubleDefinition.scala
74+
Double definition:
75+
def foo: => Int in class Test4c at line 35 and
76+
var foo: Int in class Test4c at line 36
77+
<633..633> in doubleDefinition.scala
78+
Double definition:
79+
def foo: => Int in class Test4b at line 30 and
80+
val foo: Int in class Test4b at line 31
81+
<580..580> in doubleDefinition.scala
82+
Double definition:
83+
val foo: Int in class Test4 at line 25 and
84+
def foo: => Int in class Test4 at line 26
85+
<494..494> in doubleDefinition.scala
86+
Double definition:
87+
def foo(x: List[A]): A => A in class Test3 at line 20 and
88+
def foo(x: List[A]): (B, B) => B in class Test3 at line 21
89+
have matching parameter types.
90+
<242..242> in doubleDefinition.scala
91+
Double definition:
92+
def foo(x: List[A]): A => A in class Test2 at line 13 and
93+
def foo(x: List[B]): B => B in class Test2 at line 14
94+
have the same type after erasure.

tests/neg/doubleDefinition.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,15 @@ class Test18 {
131131
def foo(a: A) = 1
132132
def foo(b: B) = 1
133133
}
134+
135+
// Error when overloading polymorphic and non-polymorphic methods
136+
class Test19 {
137+
def foo[T <: Int](x: T): T = x
138+
def foo(x: Int): Int = x // error
139+
}
140+
141+
// Error when overloading polymorphic methods
142+
class Test20 {
143+
def foo[T <: Int](x: T): T = x
144+
def foo[S <: Int, T <: Int](x: S): T = ??? // error
145+
}

0 commit comments

Comments
 (0)