Skip to content

Commit e4d530b

Browse files
committed
Report more double def errors
We were missing "has same signature after erasure" errors if the two methods in questions came from different parents.
1 parent 2f7ac63 commit e4d530b

File tree

7 files changed

+59
-18
lines changed

7 files changed

+59
-18
lines changed

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import TypeErasure.ErasedValueType, ValueClasses._
1111
/** This phase erases ErasedValueType to their underlying type.
1212
* It also removes the synthetic cast methods u2evt$ and evt2u$ which are
1313
* no longer needed afterwards.
14+
* Finally, it checks that we don't introduce "double definitions" of pairs
15+
* of methods that now have the same signature but were not considered matching
16+
* before erasure.
1417
*/
1518
class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer {
1619

@@ -67,6 +70,44 @@ class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer {
6770
transformTypeOfTree(t)
6871
}
6972

73+
/** Check that we don't have pairs of methods that override each other after
74+
* this phase, yet do not have matching types before erasure.
75+
* The before erasure test is performed after phase elimRepeated, so we
76+
* do not need to special case pairs of `T* / Seq[T]` parameters.
77+
*/
78+
private def checkNoClashes(root: Symbol)(implicit ctx: Context) = {
79+
val opc = new OverridingPairs.Cursor(root) {
80+
override def exclude(sym: Symbol) =
81+
!sym.is(Method) || sym.is(Bridge) || super.exclude(sym)
82+
override def matches(sym1: Symbol, sym2: Symbol) =
83+
sym1.signature == sym2.signature
84+
}
85+
def checkNoConflict(sym1: Symbol, sym2: Symbol, info: Type)(implicit ctx: Context): Unit = {
86+
val site = root.thisType
87+
val info1 = site.memberInfo(sym1)
88+
val info2 = site.memberInfo(sym2)
89+
if (!info1.matchesLoosely(info2))
90+
ctx.error(
91+
em"""double definition:
92+
|$sym1: $info1 in ${sym1.owner} ${sym1.flags} and
93+
|$sym2: $info2 in ${sym2.owner} ${sym2.flags}
94+
|have same type after erasure: $info""",
95+
root.pos)
96+
}
97+
val earlyCtx = ctx.withPhase(ctx.elimRepeatedPhase.next)
98+
while (opc.hasNext) {
99+
val sym1 = opc.overriding
100+
val sym2 = opc.overridden
101+
checkNoConflict(sym1, sym2, sym1.info)(earlyCtx)
102+
opc.next()
103+
}
104+
}
105+
106+
override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
107+
checkNoClashes(tree.symbol)
108+
tree
109+
}
110+
70111
override def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree =
71112
transformTypeOfTree(tree)
72113

compiler/test/dotc/tests.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ class tests extends CompilerTest {
193193
@Test def neg_i1050 = compileFile(negCustomArgs, "i1050", List("-strict"))
194194
@Test def neg_i1240 = compileFile(negCustomArgs, "i1240")(allowDoubleBindings)
195195
@Test def neg_i2002 = compileFile(negCustomArgs, "i2002")(allowDoubleBindings)
196+
@Test def neg_valueclasses_doubledefs = compileFile(negCustomArgs, "valueclasses-doubledefs")(allowDoubleBindings)
197+
@Test def neg_valueclasses_doubledefs2 = compileFile(negCustomArgs, "valueclasses-doubledefs2")(allowDoubleBindings)
198+
@Test def neg_valueclasses_pavlov = compileFile(negCustomArgs, "valueclasses-pavlov")(allowDoubleBindings)
196199

197200
val negTailcallDir = negDir + "tailcall/"
198201
@Test def neg_tailcall_t1672b = compileFile(negTailcallDir, "t1672b")

tests/untried/neg/valueclasses-doubledefs.scala renamed to tests/neg/customArgs/valueclasses-doubledefs.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ class Meter(val x: Double) extends AnyVal
22

33
class Foo {
44
def apply(x: Double) = x.toString
5-
def apply(x: Meter) = x.toString
5+
def apply(x: Meter) = x.toString // error: double def
66
}
7+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Meter(val x: Double) extends AnyVal
2+
3+
trait A {
4+
def apply(x: Double) = x.toString
5+
}
6+
trait B {
7+
def apply(x: Meter) = x.toString
8+
}
9+
10+
object Test extends A with B // error: double def

tests/untried/neg/valueclasses-pavlov.scala renamed to tests/neg/customArgs/valueclasses-pavlov.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait Foo[T <: AnyVal] extends Any {
55

66
class Box1(val value: String) extends AnyVal with Foo[Box2] {
77
def foo(x: String) = "foo(String): ok"
8-
def foo(x: Box2) = "foo(Box2): ok"
8+
def foo(x: Box2) = "foo(Box2): ok" // error: double def
99
}
1010

1111
class Box2(val value: String) extends AnyVal
@@ -17,7 +17,7 @@ object test2a {
1717
val b1 = new Box1(null)
1818
val b2 = new Box2(null)
1919
val f: Foo[Box2] = b1
20-
println(f.foo(""))
21-
println(f.foo(b2))
20+
println(f.foo("")) // error: cannot merge
21+
println(f.foo(b2)) // error: cannot merge
2222
}
2323
}

tests/untried/neg/valueclasses-doubledefs.check

Lines changed: 0 additions & 7 deletions
This file was deleted.

tests/untried/neg/valueclasses-pavlov.check

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)