From e42733e9fe1f3af591976fbb48b66035253d85b9 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 2 Mar 2011 02:34:54 +0000 Subject: Another lap around the track with generic signa... Another lap around the track with generic signatures. At the root of the issue reported in #4214 is our old friend (fondly remembered from the days of primitive equality) boxed/primitive unification. // scala trait T[A] { def f(): A } // the generic signature spec doesn't allow for parameterizing // on primitive types, so this cannot remain Char. However // translating it to Character, as was done, also has issues. class C extends T[Char] { def f(): Char = 'a' } // Note that neither of the signatures for f, the implementation // or the bridge method, matches the type parameter. Generic interfaces in class: T Generic signatures: public char C.f() public java.lang.Object C.f() After this commit, primitive type parameters are translated into Object instead of the boxed type. It was martin's idea, so no review. Closes #4214. --- .../scala/tools/nsc/transform/Erasure.scala | 81 +++++++++++----------- test/files/neg/primitive-sigs-1.check | 6 ++ test/files/neg/primitive-sigs-1/A_1.scala | 9 +++ test/files/neg/primitive-sigs-1/A_3.scala | 5 ++ test/files/neg/primitive-sigs-1/J_2.java | 8 +++ test/files/run/primitive-sigs-2.check | 3 + test/files/run/primitive-sigs-2.scala | 20 ++++++ 7 files changed, 90 insertions(+), 42 deletions(-) create mode 100644 test/files/neg/primitive-sigs-1.check create mode 100644 test/files/neg/primitive-sigs-1/A_1.scala create mode 100644 test/files/neg/primitive-sigs-1/A_3.scala create mode 100644 test/files/neg/primitive-sigs-1/J_2.java create mode 100644 test/files/run/primitive-sigs-2.check create mode 100644 test/files/run/primitive-sigs-2.scala diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index a9a9180495..015481b97a 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -226,38 +226,39 @@ abstract class Erasure extends AddInterfaces * type for constructors. */ def javaSig(sym0: Symbol, info: Type): Option[String] = atPhase(currentRun.erasurePhase) { - def jsig(tp: Type, mustBox: Boolean) = { - (boxedClass get tp.typeSymbol) match { - case Some(boxed) if mustBox => jsig2(false, true, Nil, boxed.tpe) - case _ => jsig2(false, mustBox, Nil, tp) - } - } - def hiBounds(bounds: TypeBounds): List[Type] = bounds.hi.normalize match { + def jsig(tp: Type): String = jsig2(false, Nil, tp) + + def boxedSig(tp: Type) = jsig(squashBoxed(tp)) + def squashBoxed(tp: Type) = + if (boxedClass contains tp.typeSymbol) ObjectClass.tpe + else tp + + def hiBounds(bounds: TypeBounds): List[Type] = (bounds.hi.normalize match { case RefinedType(parents, _) => parents map normalize - case tp => List(tp) - } + case tp => tp :: Nil + }) map squashBoxed - def jsig2(toplevel: Boolean, mustBox: Boolean, tparams: List[Symbol], tp0: Type): String = { + def jsig2(toplevel: Boolean, tparams: List[Symbol], tp0: Type): String = { val tp = tp0.dealias tp match { case st: SubType => - jsig2(toplevel, mustBox, tparams, st.supertype) + jsig2(toplevel, tparams, st.supertype) case ExistentialType(tparams, tpe) => - jsig2(toplevel, true, tparams, tpe) + jsig2(toplevel, tparams, tpe) case TypeRef(pre, sym, args) => def argSig(tp: Type) = if (tparams contains tp.typeSymbol) { val bounds = tp.typeSymbol.info.bounds if (AnyRefClass.tpe <:< bounds.hi) { if (bounds.lo <:< NullClass.tpe) "*" - else "-" + jsig(bounds.lo, true) + else "-" + boxedSig(bounds.lo) } - else "+" + jsig(bounds.hi, true) + else "+" + boxedSig(bounds.hi) } else if (tp.typeSymbol == UnitClass) { - jsig(ObjectClass.tpe, true) + jsig(ObjectClass.tpe) } else { - jsig(tp, true) + boxedSig(tp) } def classSig = ( "L"+atPhase(currentRun.icodePhase)(sym.fullName + global.genJVM.moduleSuffix(sym)).replace('.', '/') @@ -266,27 +267,27 @@ abstract class Erasure extends AddInterfaces // If args isEmpty, Array is being used as a higher-kinded type if (sym == ArrayClass && args.nonEmpty) { - if (unboundedGenericArrayLevel(tp) == 1) jsig(ObjectClass.tpe, true) - else ARRAY_TAG.toString+(args map (x => jsig(x, false))).mkString + if (unboundedGenericArrayLevel(tp) == 1) jsig(ObjectClass.tpe) + else ARRAY_TAG.toString+(args map jsig).mkString } else if (isTypeParameterInSig(sym)) TVAR_TAG.toString+sym.name+";" else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass) - jsig(ObjectClass.tpe, mustBox) + jsig(ObjectClass.tpe) else if (sym == UnitClass) - jsig(BoxedUnitClass.tpe, mustBox) + jsig(BoxedUnitClass.tpe) else if (sym == NothingClass) - jsig(RuntimeNothingClass.tpe, mustBox) + jsig(RuntimeNothingClass.tpe) else if (sym == NullClass) - jsig(RuntimeNullClass.tpe, mustBox) + jsig(RuntimeNullClass.tpe) else if (isValueClass(sym)) - abbrvTag(sym).toString + jsig(ObjectClass.tpe) else if (sym.isClass) { val preRebound = pre.baseType(sym.owner) // #2585 dotCleanup( ( if (needsJavaSig(preRebound)) { - val s = jsig(preRebound, mustBox) + val s = jsig(preRebound) if (s.charAt(0) == 'L') s.substring(0, s.length - 1) + classSigSuffix else classSig } @@ -299,47 +300,43 @@ abstract class Erasure extends AddInterfaces ) ) } - else jsig(erasure(tp), mustBox) + else jsig(erasure(tp)) case PolyType(tparams, restpe) => assert(tparams.nonEmpty) def boundSig(bounds: List[Type]) = { val (isTrait, isClass) = bounds partition (_.typeSymbol.isTrait) ":" + ( - if (isClass.isEmpty) "" else jsig(isClass.head, true) + if (isClass.isEmpty) "" else boxedSig(isClass.head) ) + ( - isTrait map (x => ":" + jsig(x, true)) mkString + isTrait map (x => ":" + boxedSig(x)) mkString ) } def paramSig(tsym: Symbol) = tsym.name + boundSig(hiBounds(tsym.info.bounds)) val paramString = if (toplevel) tparams map paramSig mkString ("<", "", ">") else "" - paramString + jsig(restpe, false) + paramString + jsig(restpe) case MethodType(params, restpe) => - "("+(params map (_.tpe) map (x => jsig(x, false))).mkString+")"+ - (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe, false)) - case RefinedType(parents, decls) if (!parents.isEmpty) => - jsig(parents.head, mustBox) + "("+(params map (_.tpe) map jsig).mkString+")"+ + (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe)) + case RefinedType(parent :: _, decls) => + jsig(parent) case ClassInfoType(parents, _, _) => - (parents map (x => jsig(x, true))).mkString + (parents map jsig).mkString case AnnotatedType(_, atp, _) => - jsig(atp, mustBox) + jsig(atp) case BoundedWildcardType(bounds) => println("something's wrong: "+sym0+":"+sym0.tpe+" has a bounded wildcard type") - jsig(bounds.hi, true) + jsig(bounds.hi) case _ => val etp = erasure(tp) if (etp eq tp) throw new UnknownSig - else jsig(etp, mustBox) + else jsig(etp) } } if (needsJavaSig(info)) { - try { - //println("Java sig of "+sym0+" is "+jsig2(true, List(), sym0.info))//DEBUG - Some(jsig2(true, false, Nil, info)) - } catch { - case ex: UnknownSig => None - } + try Some(jsig2(true, Nil, info)) + catch { case ex: UnknownSig => None } } else None } diff --git a/test/files/neg/primitive-sigs-1.check b/test/files/neg/primitive-sigs-1.check new file mode 100644 index 0000000000..befb8219dd --- /dev/null +++ b/test/files/neg/primitive-sigs-1.check @@ -0,0 +1,6 @@ +A_3.scala:3: error: type mismatch; + found : Bippy + required: AC[java.lang.Integer] + J_2.f(new Bippy()) + ^ +one error found diff --git a/test/files/neg/primitive-sigs-1/A_1.scala b/test/files/neg/primitive-sigs-1/A_1.scala new file mode 100644 index 0000000000..0dd83b5d6a --- /dev/null +++ b/test/files/neg/primitive-sigs-1/A_1.scala @@ -0,0 +1,9 @@ +// scala: the signature in the abstract class will use the +// upper bound as return type, which for us will be Integer +// since primitives can't appear in bounds. +abstract class AC[T <: Int] { + def f(): T +} +class Bippy extends AC[Int] { + def f(): Int = 5 +} \ No newline at end of file diff --git a/test/files/neg/primitive-sigs-1/A_3.scala b/test/files/neg/primitive-sigs-1/A_3.scala new file mode 100644 index 0000000000..dec617a111 --- /dev/null +++ b/test/files/neg/primitive-sigs-1/A_3.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + J_2.f(new Bippy()) + } +} diff --git a/test/files/neg/primitive-sigs-1/J_2.java b/test/files/neg/primitive-sigs-1/J_2.java new file mode 100644 index 0000000000..b416befb4d --- /dev/null +++ b/test/files/neg/primitive-sigs-1/J_2.java @@ -0,0 +1,8 @@ +// java: often the java or scala compiler will save us from +// the untruth in the signature, but not always. +public class J_2 { + public static Integer f(AC x) { return x.f(); } + public static void main(String[] args) { + f(new Bippy()); + } +} diff --git a/test/files/run/primitive-sigs-2.check b/test/files/run/primitive-sigs-2.check new file mode 100644 index 0000000000..4ecec9f199 --- /dev/null +++ b/test/files/run/primitive-sigs-2.check @@ -0,0 +1,3 @@ +T interface scala.ScalaObject +List(A, char, class java.lang.Object) +a diff --git a/test/files/run/primitive-sigs-2.scala b/test/files/run/primitive-sigs-2.scala new file mode 100644 index 0000000000..a8876f7f60 --- /dev/null +++ b/test/files/run/primitive-sigs-2.scala @@ -0,0 +1,20 @@ +trait T[A] { + def f(): A +} +class C extends T[Char] { + def f(): Char = 'a' +} + +object Test { + val c1: Class[_] = classOf[T[_]] + val c2: Class[_] = classOf[C] + + val c1m = c1.getMethods.toList filter (_.getName == "f") map (_.getGenericReturnType.toString) + val c2m = c2.getMethods.toList filter (_.getName == "f") map (_.getGenericReturnType.toString) + + def main(args: Array[String]): Unit = { + println(c2.getGenericInterfaces.map(_.toString).sorted mkString " ") + println(c1m ++ c2m sorted) + println(new C f) + } +} -- cgit v1.2.3