Skip to content

Commit 0037607

Browse files
authored
Merge pull request #13092 from dotty-staging/fix-13087
Refine "can be handled by parent" condition when generting bridges
2 parents 3dd91d1 + 0a77f6b commit 0037607

File tree

6 files changed

+137
-56
lines changed

6 files changed

+137
-56
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) {
3434

3535
override def exclude(sym: Symbol) =
3636
!sym.isOneOf(MethodOrModule) || super.exclude(sym)
37+
38+
override def canBeHandledByParent(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean =
39+
OverridingPairs.isOverridingPair(sym1, sym2, parent.thisType)
3740
}
3841

3942
val site = root.thisType

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

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package transform
44

55
import core._
66
import Flags._, Symbols._, Contexts._, Scopes._, Decorators._, Types.Type
7+
import NameKinds.DefaultGetterName
78
import collection.mutable
89
import collection.immutable.BitSet
910
import scala.annotation.tailrec
@@ -16,12 +17,12 @@ import scala.annotation.tailrec
1617
* Adapted from the 2.9 version of OverridingPairs. The 2.10 version is IMO
1718
* way too unwieldy to be maintained.
1819
*/
19-
object OverridingPairs {
20+
object OverridingPairs:
2021

2122
/** The cursor class
2223
* @param base the base class that contains the overriding pairs
2324
*/
24-
class Cursor(base: Symbol)(using Context) {
25+
class Cursor(base: Symbol)(using Context):
2526

2627
private val self = base.thisType
2728

@@ -165,5 +166,64 @@ object OverridingPairs {
165166

166167
nextOverriding()
167168
next()
168-
}
169-
}
169+
end Cursor
170+
171+
/** Is this `sym1` considered an override of `sym2` (or vice versa) if both are
172+
* seen as members of `site`?
173+
* We declare a match if either we have a full match including matching names
174+
* or we have a loose match with different target name but the types are the same.
175+
* We leave out pairs of methods in Java classes under the assumption since these
176+
* have already been checked and handled by javac.
177+
* This leaves two possible sorts of discrepancies to be reported as errors
178+
* in `RefChecks`:
179+
*
180+
* - matching names, target names, and signatures but different types
181+
* - matching names and types, but different target names
182+
*
183+
* This method is used as a replacement of `matches` in some subclasses of
184+
* OverridingPairs.
185+
*/
186+
def isOverridingPair(sym1: Symbol, sym2: Symbol, self: Type)(using Context): Boolean =
187+
if sym1.owner.is(JavaDefined, butNot = Trait)
188+
&& sym2.owner.is(JavaDefined, butNot = Trait)
189+
then false // javac already handles these checks and inserts bridges
190+
else if sym1.isType then true
191+
else
192+
val sd1 = sym1.asSeenFrom(self)
193+
val sd2 = sym2.asSeenFrom(self)
194+
sd1.matchesLoosely(sd2)
195+
&& (sym1.hasTargetName(sym2.targetName)
196+
|| isOverridingPair(sym1, sd1.info, sym2, sd2.info))
197+
198+
/** Let `member` and `other` be members of some common class C with types
199+
* `memberTp` and `otherTp` in C. Are the two symbols considered an overriding
200+
* pair in C? We assume that names already match so we test only the types here.
201+
* @param fallBack A function called if the initial test is false and
202+
* `member` and `other` are term symbols.
203+
*/
204+
def isOverridingPair(member: Symbol, memberTp: Type, other: Symbol, otherTp: Type, fallBack: => Boolean = false)(using Context): Boolean =
205+
if member.isType then // intersection of bounds to refined types must be nonempty
206+
memberTp.bounds.hi.hasSameKindAs(otherTp.bounds.hi)
207+
&& (
208+
(memberTp frozen_<:< otherTp)
209+
|| !member.owner.derivesFrom(other.owner)
210+
&& {
211+
// if member and other come from independent classes or traits, their
212+
// bounds must have non-empty-intersection
213+
val jointBounds = (memberTp.bounds & otherTp.bounds).bounds
214+
jointBounds.lo frozen_<:< jointBounds.hi
215+
}
216+
)
217+
else
218+
// releaxed override check for explicit nulls if one of the symbols is Java defined,
219+
// force `Null` being a subtype of reference types during override checking
220+
val relaxedCtxForNulls =
221+
if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined)) then
222+
ctx.retractMode(Mode.SafeNulls)
223+
else ctx
224+
member.name.is(DefaultGetterName) // default getters are not checked for compatibility
225+
|| memberTp.overrides(otherTp,
226+
member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
227+
)(using relaxedCtxForNulls)
228+
229+
end OverridingPairs

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

Lines changed: 12 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import config.Printers.{checks, noPrinter}
1717
import scala.util.{Try, Failure, Success}
1818
import config.{ScalaVersion, NoScalaVersion}
1919
import Decorators._
20+
import OverridingPairs.isOverridingPair
2021
import typer.ErrorReporting._
2122
import config.Feature.{warnOnMigration, migrateTo3}
2223
import config.Printers.refcheck
@@ -264,31 +265,6 @@ object RefChecks {
264265
i"${if (showLocation) sym1.showLocated else sym1}$infoStr"
265266
}
266267

267-
def compatibleTypes(member: Symbol, memberTp: Type, other: Symbol, otherTp: Type, fallBack: => Boolean = false): Boolean =
268-
try
269-
if (member.isType) // intersection of bounds to refined types must be nonempty
270-
memberTp.bounds.hi.hasSameKindAs(otherTp.bounds.hi) &&
271-
((memberTp frozen_<:< otherTp) ||
272-
!member.owner.derivesFrom(other.owner) && {
273-
// if member and other come from independent classes or traits, their
274-
// bounds must have non-empty-intersection
275-
val jointBounds = (memberTp.bounds & otherTp.bounds).bounds
276-
jointBounds.lo frozen_<:< jointBounds.hi
277-
})
278-
else
279-
// releaxed override check for explicit nulls if one of the symbols is Java defined,
280-
// force `Null` being a subtype of reference types during override checking
281-
val relaxedCtxForNulls =
282-
if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined)) then
283-
ctx.retractMode(Mode.SafeNulls)
284-
else ctx
285-
member.name.is(DefaultGetterName) // default getters are not checked for compatibility
286-
|| memberTp.overrides(otherTp, member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack)(using relaxedCtxForNulls)
287-
catch case ex: MissingType =>
288-
// can happen when called with upwardsSelf as qualifier of memberTp and otherTp,
289-
// because in that case we might access types that are not members of the qualifier.
290-
false
291-
292268
/* Check that all conditions for overriding `other` by `member`
293269
* of class `clazz` are met.
294270
*/
@@ -318,10 +294,15 @@ object RefChecks {
318294
}
319295

320296
def compatTypes(memberTp: Type, otherTp: Type): Boolean =
321-
compatibleTypes(member, memberTp, other, otherTp,
322-
fallBack = warnOnMigration(
323-
overrideErrorMsg("no longer has compatible type"),
324-
(if (member.owner == clazz) member else clazz).srcPos))
297+
try
298+
isOverridingPair(member, memberTp, other, otherTp,
299+
fallBack = warnOnMigration(
300+
overrideErrorMsg("no longer has compatible type"),
301+
(if (member.owner == clazz) member else clazz).srcPos))
302+
catch case ex: MissingType =>
303+
// can happen when called with upwardsSelf as qualifier of memberTp and otherTp,
304+
// because in that case we might access types that are not members of the qualifier.
305+
false
325306

326307
/** Do types of term members `member` and `other` as seen from `self` match?
327308
* If not we treat them as not a real override and don't issue override
@@ -488,29 +469,9 @@ object RefChecks {
488469
}*/
489470
}
490471

491-
/** We declare a match if either we have a full match including matching names
492-
* or we have a loose match with different target name but the types are the same.
493-
* This leaves two possible sorts of discrepancies to be reported as errors
494-
* in `checkOveride`:
495-
*
496-
* - matching names, target names, and signatures but different types
497-
* - matching names and types, but different target names
498-
*/
499-
def considerMatching(sym1: Symbol, sym2: Symbol, self: Type): Boolean =
500-
if sym1.owner.is(JavaDefined, butNot = Trait)
501-
&& sym2.owner.is(JavaDefined, butNot = Trait)
502-
then false // javac already handles these checks
503-
else if sym1.isType then true
504-
else
505-
val sd1 = sym1.asSeenFrom(self)
506-
val sd2 = sym2.asSeenFrom(self)
507-
sd1.matchesLoosely(sd2)
508-
&& (sym1.hasTargetName(sym2.targetName)
509-
|| compatibleTypes(sym1, sd1.info, sym2, sd2.info))
510-
511472
val opc = new OverridingPairs.Cursor(clazz):
512473
override def matches(sym1: Symbol, sym2: Symbol): Boolean =
513-
considerMatching(sym1, sym2, self)
474+
isOverridingPair(sym1, sym2, self)
514475

515476
private def inLinearizationOrder(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean =
516477
val owner1 = sym1.owner
@@ -530,7 +491,7 @@ object RefChecks {
530491
// - They overriding/overridden appear in linearization order.
531492
// See neg/i5094.scala for an example where this matters.
532493
override def canBeHandledByParent(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean =
533-
considerMatching(sym1, sym2, parent.thisType)
494+
isOverridingPair(sym1, sym2, parent.thisType)
534495
.showing(i"already handled ${sym1.showLocated}: ${sym1.asSeenFrom(parent.thisType).signature}, ${sym2.showLocated}: ${sym2.asSeenFrom(parent.thisType).signature} = $result", refcheck)
535496
&& inLinearizationOrder(sym1, sym2, parent)
536497
end opc

tests/run/i1240.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ object Test {
1919
// In Java, this gives an error like this:
2020
// methods foo(A) from C[D] and foo(String) from C[D] are inherited with the same signature
2121
// But the analogous example with `b1` compiles OK in Java.
22-
assert(b2.foo(new D) == "T foo")
22+
assert(b2.foo(new D) == "D foo")
23+
// Here we get "D foo" since a bridge method for foo(x: D) was inserted
24+
// in the anonymous class of b2.
2325
}
2426
}
2527

tests/run/i13087.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import scala.collection.mutable.Builder
2+
3+
class DDD[S,T,A]
4+
5+
trait NN[S, T, A, K[_], +D <: DDD[Set[S],T,K[A]]]
6+
class NNN[S, T, K[_], A] extends NN[S, T, A, K, DDD[Set[S],T,K[A]]]
7+
8+
object NN {
9+
def newBuilder[S, T, A, K[_]]:
10+
NNbuilder[S, T, K, A, DDD[Set[S],T,K[A]], NN[S,T,A,K,?], Unit] =
11+
new NNNbuilder[S, T, K, A]
12+
}
13+
14+
// Remove the type parameter E, hardcoding in E := Unit, and the issue
15+
// goes away.
16+
trait NNbuilder
17+
[S, T, K[_], A, +D <: DDD[Set[S],T,K[A]], +N <: NN[S,T,A,K,D], E]
18+
extends Builder[Unit, N] {
19+
def clear(): Unit = throw new UnsupportedOperationException()
20+
final def addOne(builder: E): this.type = this
21+
}
22+
23+
// Unfold this class defn, and the issue goes away
24+
abstract class AbstractNNNbuilder
25+
[S, T, K[_], A, +D <: DDD[Set[S],T,K[A]], +N <: NN[S,T,A,K,D], E]
26+
extends NNbuilder[S,T,K,A,D,N,E]
27+
28+
class NNNbuilder[S, T, K[_], A] extends AbstractNNNbuilder[
29+
S, T, K, A, DDD[Set[S], T, K[A]], NNN[S, T, K, A], Unit
30+
] {
31+
override def result(): NNN[S, T, K, A] = new NNN[S, T, K, A]
32+
}
33+
34+
@main def Test: Unit = {
35+
val builder = NN.newBuilder[String, Char, Int, Set]
36+
builder += ()
37+
builder.result()
38+
}

tests/run/i13087a.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
trait SimpleTrait[T] {
2+
def myMethod(t: T): Int
3+
def doIt(t: T): Unit = {
4+
myMethod(t) // java.lang.AbstractMethodError: Method BadClass.myMethod(Ljava/lang/Object;)I is abstract
5+
}
6+
}
7+
8+
abstract class SimpleClass[T] extends SimpleTrait[T] {
9+
def myMethod(t: String): Int = 5
10+
}
11+
12+
class BadClass extends SimpleClass[String]
13+
14+
object Test {
15+
def main(args: Array[String]): Unit =
16+
(new BadClass).doIt("foobar")
17+
}

0 commit comments

Comments
 (0)