Skip to content

Commit 6e7debe

Browse files
committed
Properly erase Jave intersections
Java intersections erase to their first member which is always a class type. To enforce this we need to: - Make sure we don't use `&` when creating these intersections - Make sure `simplified` does not simplify intersections which appear in method types.
1 parent 5d5a3dd commit 6e7debe

File tree

5 files changed

+22
-2
lines changed

5 files changed

+22
-2
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ object TypeOps:
156156
case _: AppliedType | _: MatchType =>
157157
val normed = tp.tryNormalize
158158
if (normed.exists) normed else mapOver
159+
case tp: MethodicType =>
160+
tp // See documentation of `Types#simplified`
159161
case _ =>
160162
mapOver
161163
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,13 @@ object Types {
16671667
* what was a union or intersection of type variables might be a simpler type
16681668
* after the type variables are instantiated. Finally, it
16691669
* maps poly params in the current constraint set back to their type vars.
1670+
*
1671+
* NOTE: Simplifying an intersection type might change its erasure (for
1672+
* example, the Java erasure of `Object & Serializable` is `Object`,
1673+
* but its simplification is `Serializable`). This means that simplification
1674+
* should never be used in a `MethodicType`, because that could
1675+
* lead to a different `signature`. Since this isn't very useful anyway,
1676+
* this method handles this by never simplifying inside a `MethodicType`.
16701677
*/
16711678
def simplified(implicit ctx: Context): Type = TypeOps.simplify(this, null)
16721679

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ class ClassfileParser(
403403
// if the bound is exactly Object, it will have been converted to Any, and the comparison will fail
404404
// see also RestrictJavaArraysMap (when compiling java sources directly)
405405
if (elemtp.typeSymbol.isAbstractOrParamType && !(elemtp.derivesFrom(defn.ObjectClass)))
406-
elemtp = AndType(elemtp, defn.ObjectType)
406+
elemtp = AndType(defn.ObjectType, elemtp)
407407
defn.ArrayOf(elemtp)
408408
case '(' =>
409409
// we need a method symbol. given in line 486 by calling getType(methodSym, ..)
@@ -434,7 +434,8 @@ class ClassfileParser(
434434
if (sig(index) != ':') // guard against empty class bound
435435
ts += objToAny(sig2type(tparams, skiptvs))
436436
}
437-
TypeBounds.upper(ts.foldLeft(NoType: Type)(_ & _) orElse defn.AnyType)
437+
val bound = if ts.isEmpty then defn.AnyType else ts.reduceLeft(AndType.apply)
438+
TypeBounds.upper(bound)
438439
}
439440

440441
var tparams = classTParams

tests/run/java-intersection/A_1.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public class A_1 {
2+
public <T extends Object & java.io.Serializable> void foo(T x) {}
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
val a = new A_1
4+
val x = new java.io.Serializable {}
5+
a.foo(x)
6+
}
7+
}

0 commit comments

Comments
 (0)