Skip to content

Commit 44b9cf0

Browse files
committed
Modified erasure so we have enough information ...
Modified erasure so we have enough information to determine whether we need to use scala or java erasure semantics. This fixes the runtime failure illustrated here: % scala29 -e 'java.util.Collections.max(null)' java.lang.NoSuchMethodError: java.util.Collections.max(Ljava/util/Collection;)Ljava/lang/Comparable; Review by odersky.
1 parent 658ba1b commit 44b9cf0

File tree

7 files changed

+149
-81
lines changed

7 files changed

+149
-81
lines changed

src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
594594
if ((settings.check.value contains "genjvm")) {
595595
val normalizedTpe = atPhase(currentRun.erasurePhase)(erasure.prepareSigMap(memberTpe))
596596
val bytecodeTpe = owner.thisType.memberInfo(sym)
597-
if (!sym.isType && !sym.isConstructor && !(erasure.erasure(normalizedTpe) =:= bytecodeTpe)) {
597+
if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym, normalizedTpe) =:= bytecodeTpe)) {
598598
clasz.cunit.warning(sym.pos,
599599
"""|compiler bug: created generic signature for %s in %s that does not conform to its erasure
600600
|signature: %s

src/compiler/scala/tools/nsc/transform/AddInterfaces.scala

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@ abstract class AddInterfaces extends InfoTransform {
2626
*/
2727
override def phaseNewFlags: Long = lateDEFERRED | lateINTERFACE
2828

29-
/** Type reference after erasure; to be defined in subclass
30-
* <code>Erasure</code>.
29+
/** Type reference after erasure; defined in Erasure.
3130
*/
3231
def erasedTypeRef(sym: Symbol): Type
3332

34-
/** Erasure type-map; to be defined in subclass
35-
* <code>Erasure</code>.
33+
/** Erasure calculation; defined in Erasure.
3634
*/
37-
def erasure: TypeMap
35+
def erasure(sym: Symbol, tpe: Type): Type
3836

3937
/** A lazily constructed map that associates every non-interface trait with
4038
* its implementation class.
@@ -160,39 +158,34 @@ abstract class AddInterfaces extends InfoTransform {
160158
}
161159

162160
override def complete(sym: Symbol) {
161+
/** If `tp` refers to a non-interface trait, return a
162+
* reference to its implementation class. Otherwise return `tp`.
163+
*/
164+
def mixinToImplClass(tp: Type): Type = erasure(sym,
165+
tp match { //@MATN: no normalize needed (comes after erasure)
166+
case TypeRef(pre, sym, args) if sym.needsImplClass =>
167+
typeRef(pre, implClass(sym), args)
168+
case _ =>
169+
tp
170+
}
171+
)
163172
def implType(tp: Type): Type = tp match {
164173
case ClassInfoType(parents, decls, _) =>
165174
assert(phase == implClassPhase)
166175
ClassInfoType(
167-
ObjectClass.tpe :: (parents.tail map mixinToImplClass filter (_.typeSymbol != ObjectClass))
168-
::: List(iface.tpe),
176+
ObjectClass.tpe +: (parents.tail map mixinToImplClass filter (_.typeSymbol != ObjectClass)) :+ iface.tpe,
169177
implDecls(sym, decls),
170-
sym)
171-
case PolyType(tparams, restpe) =>
178+
sym
179+
)
180+
case PolyType(_, restpe) =>
172181
implType(restpe)
173182
}
174-
sym.setInfo(implType(atPhase(currentRun.erasurePhase)(iface.info)))
183+
sym setInfo implType(atPhase(currentRun.erasurePhase)(iface.info))
175184
}
176185

177186
override def load(clazz: Symbol) { complete(clazz) }
178187
}
179188

180-
/** If type <code>tp</code> refers to a non-interface trait, return a
181-
* reference to its implementation class. Otherwise return <code>tp</code>
182-
* itself.
183-
*
184-
* @param tp ...
185-
* @return ...
186-
*/
187-
private def mixinToImplClass(tp: Type): Type =
188-
erasure(
189-
tp match { //@MATN: no normalize needed (comes after erasure)
190-
case TypeRef(pre, sym, args) if (sym.needsImplClass) =>
191-
typeRef(pre, implClass(sym), args)
192-
case _ =>
193-
tp
194-
})
195-
196189
def transformMixinInfo(tp: Type): Type = tp match {
197190
case ClassInfoType(parents, decls, clazz) =>
198191
if (clazz.needsImplClass) {

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 114 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ abstract class Erasure extends AddInterfaces
8888
val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams)
8989
val upperBound = (
9090
if (isPhantomClass(sym)) AnyClass.tpe
91-
else if (sym.isLocalClass) erasure.intersectionDominator(tp.parents) // erasure(tp)
91+
else if (sym.isLocalClass) intersectionDominator(tp.parents)
9292
else tp.widen
9393
)
9494

@@ -134,8 +134,9 @@ abstract class Erasure extends AddInterfaces
134134
* - For a typeref P.C[Ts] where C refers to an alias type, the erasure of C's alias.
135135
* - For a typeref P.C[Ts] where C refers to an abstract type, the
136136
* erasure of C's upper bound.
137-
* - For a non-empty type intersection (possibly with refinement),
138-
* the erasure of its first parent.
137+
* - For a non-empty type intersection (possibly with refinement)
138+
* - in scala, the erasure of the intersection dominator
139+
* - in java, the erasure of its first parent <--- @PP: not yet in spec.
139140
* - For an empty type intersection, java.lang.Object.
140141
* - For a method type (Fs)scala.Unit, (|Fs|)scala#Unit.
141142
* - For any other method type (Fs)Y, (|Fs|)|T|.
@@ -146,29 +147,84 @@ abstract class Erasure extends AddInterfaces
146147
* parents |Ps|, but with duplicate references of Object removed.
147148
* - for all other types, the type itself (with any sub-components erased)
148149
*/
149-
object erasure extends TypeMap {
150-
// Compute the dominant part of the intersection type with given `parents` according to new spec.
151-
def intersectionDominator(parents: List[Type]): Type =
150+
def erasure(sym: Symbol, tp: Type): Type = {
151+
if (sym != NoSymbol && sym.enclClass.isJavaDefined) {
152+
val res = javaErasure(tp)
153+
if (verifyJavaErasure && sym.isMethod) {
154+
val old = scalaErasure(tp)
155+
if (!(res =:= old))
156+
log("Identified divergence between java/scala erasure:\n scala: " + old + "\n java: " + res)
157+
}
158+
res
159+
}
160+
else scalaErasure(tp)
161+
}
162+
163+
/** Scala's more precise erasure than java's is problematic as follows:
164+
*
165+
* - Symbols are read from classfiles and populated with types
166+
* - The textual signature read from the bytecode is forgotten
167+
* - Bytecode generation must know the precise signature of a method
168+
* - the signature is derived from the erasure of the method type
169+
* - If that derivation does not adhere to the rules by which the original
170+
* signature was created, a NoSuchMethod error will result.
171+
*
172+
* For this reason and others (such as distinguishing constructors from other methods)
173+
* erasure is now (Symbol, Type) => Type rather than Type => Type.
174+
*/
175+
object scalaErasure extends ErasureMap {
176+
/** In scala, calculate a useful parent.
177+
* An intersection such as `Object with Trait` erases to Trait.
178+
*/
179+
def mergeParents(parents: List[Type]): Type =
180+
intersectionDominator(parents)
181+
}
182+
object javaErasure extends ErasureMap {
183+
/** In java, always take the first parent.
184+
* An intersection such as `Object with Trait` erases to Object.
185+
*/
186+
def mergeParents(parents: List[Type]): Type =
152187
if (parents.isEmpty) ObjectClass.tpe
153-
else {
154-
val psyms = parents map (_.typeSymbol)
155-
if (psyms contains ArrayClass) {
156-
// treat arrays specially
157-
arrayType(
158-
intersectionDominator(
159-
parents filter (_.typeSymbol == ArrayClass) map (_.typeArgs.head)))
160-
} else {
161-
// implement new spec for erasure of refined types.
162-
def isUnshadowed(psym: Symbol) =
163-
!(psyms exists (qsym => (psym ne qsym) && (qsym isNonBottomSubClass psym)))
164-
val cs = parents.iterator.filter { p => // isUnshadowed is a bit expensive, so try classes first
165-
val psym = p.typeSymbol
166-
psym.initialize
167-
psym.isClass && !psym.isTrait && isUnshadowed(psym)
168-
}
169-
(if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.typeSymbol))).next()
188+
else parents.head
189+
}
190+
191+
/** The intersection dominator (SLS 3.7) of a list of types is computed as follows.
192+
*
193+
* - If the list contains one or more occurrences of scala.Array with
194+
* type parameters El1, El2, ... then the dominator is scala.Array with
195+
* type parameter of intersectionDominator(List(El1, El2, ...)). <--- @PP: not yet in spec.
196+
* - Otherwise, the list is reduced to a subsequence containing only types
197+
* which are not subtypes of other listed types (the span.)
198+
* - If the span is empty, the dominator is Object.
199+
* - If the span contains a class Tc which is not a trait and which is
200+
* not Object, the dominator is Tc. <--- @PP: "which is not Object" not in spec.
201+
* - Otherwise, the dominator is the first element of the span.
202+
*/
203+
def intersectionDominator(parents: List[Type]): Type = {
204+
if (parents.isEmpty) ObjectClass.tpe
205+
else {
206+
val psyms = parents map (_.typeSymbol)
207+
if (psyms contains ArrayClass) {
208+
// treat arrays specially
209+
arrayType(
210+
intersectionDominator(
211+
parents filter (_.typeSymbol == ArrayClass) map (_.typeArgs.head)))
212+
} else {
213+
// implement new spec for erasure of refined types.
214+
def isUnshadowed(psym: Symbol) =
215+
!(psyms exists (qsym => (psym ne qsym) && (qsym isNonBottomSubClass psym)))
216+
val cs = parents.iterator.filter { p => // isUnshadowed is a bit expensive, so try classes first
217+
val psym = p.typeSymbol
218+
psym.initialize
219+
psym.isClass && !psym.isTrait && isUnshadowed(psym)
170220
}
221+
(if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.typeSymbol))).next()
171222
}
223+
}
224+
}
225+
226+
abstract class ErasureMap extends TypeMap {
227+
def mergeParents(parents: List[Type]): Type
172228

173229
def apply(tp: Type): Type = {
174230
tp match {
@@ -183,7 +239,7 @@ abstract class Erasure extends AddInterfaces
183239
else typeRef(apply(pre), sym, args map this)
184240
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass)
185241
else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass)
186-
else if (sym.isRefinementClass) apply(intersectionDominator(tp.parents))
242+
else if (sym.isRefinementClass) apply(mergeParents(tp.parents))
187243
else if (sym.isClass) typeRef(apply(rebindInnerClass(pre, sym)), sym, List()) // #2585
188244
else apply(sym.info) // alias type or abstract type
189245
case PolyType(tparams, restpe) =>
@@ -196,12 +252,13 @@ abstract class Erasure extends AddInterfaces
196252
if (restpe.typeSymbol == UnitClass)
197253
erasedTypeRef(UnitClass)
198254
else if (settings.YdepMethTpes.value)
199-
// this replaces each typeref that refers to an argument by the type `p.tpe` of the actual argument p (p in params)
255+
// this replaces each typeref that refers to an argument
256+
// by the type `p.tpe` of the actual argument p (p in params)
200257
apply(mt.resultType(params map (_.tpe)))
201258
else
202259
apply(restpe))
203260
case RefinedType(parents, decls) =>
204-
apply(intersectionDominator(parents))
261+
apply(mergeParents(parents))
205262
case AnnotatedType(_, atp, _) =>
206263
apply(atp)
207264
case ClassInfoType(parents, decls, clazz) =>
@@ -229,8 +286,8 @@ abstract class Erasure extends AddInterfaces
229286
else if (!sym.owner.isPackageClass) traverse(pre)
230287
case PolyType(_, _) | ExistentialType(_, _) =>
231288
result = true
232-
case RefinedType(parents, decls) =>
233-
if (!parents.isEmpty) traverse(parents.head)
289+
case RefinedType(parents, _) =>
290+
parents foreach traverse
234291
case ClassInfoType(parents, _, _) =>
235292
parents foreach traverse
236293
case AnnotatedType(_, atp, _) =>
@@ -242,6 +299,7 @@ abstract class Erasure extends AddInterfaces
242299
}
243300
}
244301

302+
private def verifyJavaErasure = settings.Xverify.value || settings.debug.value
245303
private def needsJavaSig(tp: Type) = !settings.Ynogenericsig.value && NeedsSigCollector.collect(tp)
246304

247305
// only refer to type params that will actually make it into the sig, this excludes:
@@ -418,7 +476,7 @@ abstract class Erasure extends AddInterfaces
418476
)
419477
}
420478
}
421-
else jsig(erasure(tp), existentiallyBound, toplevel, primitiveOK)
479+
else jsig(erasure(sym0, tp), existentiallyBound, toplevel, primitiveOK)
422480
case PolyType(tparams, restpe) =>
423481
assert(tparams.nonEmpty)
424482
def boundSig(bounds: List[Type]) = {
@@ -447,7 +505,7 @@ abstract class Erasure extends AddInterfaces
447505
println("something's wrong: "+sym0+":"+sym0.tpe+" has a bounded wildcard type")
448506
jsig(bounds.hi, existentiallyBound, toplevel, primitiveOK)
449507
case _ =>
450-
val etp = erasure(tp)
508+
val etp = erasure(sym0, tp)
451509
if (etp eq tp) throw new UnknownSig
452510
else jsig(etp)
453511
}
@@ -465,7 +523,7 @@ abstract class Erasure extends AddInterfaces
465523

466524
/** Type reference after erasure */
467525
def erasedTypeRef(sym: Symbol): Type =
468-
typeRef(erasure(sym.owner.tpe), sym, List())
526+
typeRef(erasure(sym, sym.owner.tpe), sym, List())
469527

470528
/** Remove duplicate references to class Object in a list of parent classes */
471529
private def removeDoubleObject(tps: List[Type]): List[Type] = tps match {
@@ -487,25 +545,25 @@ abstract class Erasure extends AddInterfaces
487545
if (sym == Object_asInstanceOf)
488546
sym.info
489547
else if (sym == Object_isInstanceOf || sym == ArrayClass)
490-
PolyType(sym.info.typeParams, erasure(sym.info.resultType))
548+
PolyType(sym.info.typeParams, erasure(sym, sym.info.resultType))
491549
else if (sym.isAbstractType)
492550
TypeBounds(WildcardType, WildcardType)
493551
else if (sym.isTerm && sym.owner == ArrayClass) {
494552
if (sym.isClassConstructor)
495553
tp match {
496-
case MethodType(params, TypeRef(pre, sym, args)) =>
497-
MethodType(cloneSymbols(params) map (p => p.setInfo(erasure(p.tpe))),
498-
typeRef(erasure(pre), sym, args))
554+
case MethodType(params, TypeRef(pre, sym1, args)) =>
555+
MethodType(cloneSymbols(params) map (p => p.setInfo(erasure(sym, p.tpe))),
556+
typeRef(erasure(sym, pre), sym1, args))
499557
}
500558
else if (sym.name == nme.apply)
501559
tp
502560
else if (sym.name == nme.update)
503561
(tp: @unchecked) match {
504562
case MethodType(List(index, tvar), restpe) =>
505-
MethodType(List(index.cloneSymbol.setInfo(erasure(index.tpe)), tvar),
563+
MethodType(List(index.cloneSymbol.setInfo(erasure(sym, index.tpe)), tvar),
506564
erasedTypeRef(UnitClass))
507565
}
508-
else erasure(tp)
566+
else erasure(sym, tp)
509567
} else if (
510568
sym.owner != NoSymbol &&
511569
sym.owner.owner == ArrayClass &&
@@ -522,7 +580,7 @@ abstract class Erasure extends AddInterfaces
522580
else
523581
erasure(tp)
524582
*/
525-
transformMixinInfo(erasure(tp))
583+
transformMixinInfo(erasure(sym, tp))
526584
}
527585
}
528586

@@ -903,7 +961,7 @@ abstract class Erasure extends AddInterfaces
903961
val other = opc.overridden
904962
//Console.println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG
905963
if (atPhase(currentRun.explicitouterPhase)(!member.isDeferred)) {
906-
val otpe = erasure(other.tpe)
964+
val otpe = erasure(owner, other.tpe)
907965
val bridgeNeeded = atPhase(phase.next) (
908966
!(other.tpe =:= member.tpe) &&
909967
!(deconstMap(other.tpe) =:= deconstMap(member.tpe)) &&
@@ -940,7 +998,7 @@ abstract class Erasure extends AddInterfaces
940998
((fun, vparams) => Apply(fun, vparams map Ident)))
941999
});
9421000
if (settings.debug.value)
943-
log("generating bridge from " + other + "(" + Flags.flagsToString(bridge.flags) + ")" + ":" + otpe + other.locationString + " to " + member + ":" + erasure(member.tpe) + member.locationString + " =\n " + bridgeDef);
1001+
log("generating bridge from " + other + "(" + Flags.flagsToString(bridge.flags) + ")" + ":" + otpe + other.locationString + " to " + member + ":" + erasure(owner, member.tpe) + member.locationString + " =\n " + bridgeDef);
9441002
bridgeDef
9451003
}
9461004
} :: bridges
@@ -1004,16 +1062,20 @@ abstract class Erasure extends AddInterfaces
10041062
val level = unboundedGenericArrayLevel(arg.tpe)
10051063
def isArrayTest(arg: Tree) =
10061064
gen.mkRuntimeCall("isArray", List(arg, Literal(Constant(level))))
1065+
10071066
global.typer.typedPos(tree.pos) {
10081067
if (level == 1) isArrayTest(qual)
1009-
else
1010-
gen.evalOnce(qual, currentOwner, unit) { qual1 =>
1011-
gen.mkAnd(
1012-
Apply(TypeApply(Select(qual1(), fun.symbol),
1013-
List(TypeTree(erasure(arg.tpe)))),
1014-
List()),
1015-
isArrayTest(qual1()))
1016-
}
1068+
else gen.evalOnce(qual, currentOwner, unit) { qual1 =>
1069+
gen.mkAnd(
1070+
gen.mkMethodCall(
1071+
qual1(),
1072+
fun.symbol,
1073+
List(erasure(fun.symbol, arg.tpe)),
1074+
Nil
1075+
),
1076+
isArrayTest(qual1())
1077+
)
1078+
}
10171079
}
10181080
case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
10191081
fun.symbol != Object_asInstanceOf &&
@@ -1030,7 +1092,7 @@ abstract class Erasure extends AddInterfaces
10301092
// need to do the cast in adaptMember
10311093
treeCopy.Apply(
10321094
tree,
1033-
SelectFromArray(qual, name, erasure(qual.tpe)).copyAttrs(fn),
1095+
SelectFromArray(qual, name, erasure(tree.symbol, qual.tpe)).copyAttrs(fn),
10341096
args)
10351097

10361098
case Apply(fn @ Select(qual, _), Nil) if interceptedMethods(fn.symbol) =>
@@ -1150,7 +1212,7 @@ abstract class Erasure extends AddInterfaces
11501212

11511213
case Literal(ct) if ct.tag == ClassTag
11521214
&& ct.typeValue.typeSymbol != definitions.UnitClass =>
1153-
treeCopy.Literal(tree, Constant(erasure(ct.typeValue)))
1215+
treeCopy.Literal(tree, Constant(erasure(NoSymbol, ct.typeValue)))
11541216

11551217
case _ =>
11561218
tree
@@ -1168,10 +1230,10 @@ abstract class Erasure extends AddInterfaces
11681230
val tree1 = preErase(tree)
11691231
tree1 match {
11701232
case EmptyTree | TypeTree() =>
1171-
tree1 setType erasure(tree1.tpe)
1233+
tree1 setType erasure(NoSymbol, tree1.tpe)
11721234
case DefDef(_, _, _, _, tpt, _) =>
11731235
val result = super.transform(tree1) setType null
1174-
tpt.tpe = erasure(tree1.symbol.tpe).resultType
1236+
tpt.tpe = erasure(tree1.symbol, tree1.symbol.tpe).resultType
11751237
result
11761238
case _ =>
11771239
super.transform(tree1) setType null

0 commit comments

Comments
 (0)