From e7bc7737c72dff381cfc93d2387a66565de1968b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 12 Nov 2009 16:56:33 +0000 Subject: fixed #2454 --- src/compiler/scala/tools/nsc/Settings.scala | 1 - src/compiler/scala/tools/nsc/symtab/Symbols.scala | 42 ++++++++++++++++++++++ src/compiler/scala/tools/nsc/symtab/Types.scala | 11 +++--- .../scala/tools/nsc/typechecker/Infer.scala | 25 +++---------- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 ++ test/files/pos/t2305.scala | 26 ++++++++++++++ test/files/pos/t2454.scala | 25 +++++++++++++ 7 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 test/files/pos/t2305.scala create mode 100644 test/files/pos/t2454.scala diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index 0f59d9a3d9..5db78420ec 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -836,7 +836,6 @@ trait ScalacSettings { val Ytyperdebug = BooleanSetting ("-Ytyper-debug", "Trace all type assignements") val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.") val Ytailrec = BooleanSetting ("-Ytailrecommend", "Alert methods which would be tail-recursive if private or final.") - val YhigherKindedRaw = BooleanSetting ("-Yhigher-kinded-raw", "(temporary!) Treat raw Java types as higher-kinded types.") val Yjenkins = BooleanSetting ("-Yjenkins-hashCodes", "Use jenkins hash algorithm for case class generated hashCodes.") // Warnings diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index 7e16d1dc9b..e4cbe159a1 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -885,6 +885,48 @@ trait Symbols { infos ne null } + /** Modify term symbol's type so that a raw type C is converted to an existential C[_] + * + * This is done in checkAccessible and overriding checks in refchecks + * We can't do this on class loading because it would result in infinite cycles. + */ + private var triedCooking: Boolean = false + final def cookJavaRawInfo() { + require(isTerm) + // println("cookJavaRawInfo: "+(rawname, triedCooking)) + + if(triedCooking) return else triedCooking = true // only try once... + + def cook(sym: Symbol) { + require(sym hasFlag JAVA) + // @M: I think this is more desirable, but Martin prefers to leave raw-types as-is as much as possible + // object rawToExistentialInJava extends TypeMap { + // def apply(tp: Type): Type = tp match { + // // any symbol that occurs in a java sig, not just java symbols + // // see http://lampsvn.epfl.ch/trac/scala/ticket/2454#comment:14 + // case TypeRef(pre, sym, List()) if !sym.typeParams.isEmpty => + // val eparams = typeParamsToExistentials(sym, sym.typeParams) + // existentialAbstraction(eparams, TypeRef(pre, sym, eparams map (_.tpe))) + // case _ => + // mapOver(tp) + // } + // } + val tpe1 = rawToExistential(sym.tpe) + // println("cooking: "+ sym +": "+ sym.tpe +" to "+ tpe1) + if (tpe1 ne sym.tpe) { + sym.setInfo(tpe1) + } + } + + if (hasFlag(JAVA)) + cook(this) + else if (hasFlag(OVERLOADED)) + for (sym2 <- alternatives) + if (sym2 hasFlag JAVA) + cook(sym2) + } + + /** The type constructor of a symbol is: * For a type symbol, the type corresponding to the symbol itself, * excluding parameters. diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 9727f6aa5d..6145c4a0d7 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -1600,12 +1600,11 @@ A type's typeSymbol should never be inspected directly. override def typeConstructor = rawTypeRef(pre, sym, List()) - // (args.isEmpty && !typeParamsDirect.isEmpty) && !isRawType(this) - // check for isRawType: otherwise raw types are considered higher-kinded types during subtyping: - override def isHigherKinded - = (args.isEmpty && !typeParamsDirect.isEmpty) && (settings.YhigherKindedRaw.value || !isRaw(sym, args)) - // (args.isEmpty && !typeParamsDirect.isEmpty) && (phase.erasedTypes || !sym.hasFlag(JAVA)) - + // a reference (in a Scala program) to a type that has type parameters, but where the reference does not include type arguments + // note that it doesn't matter whether the symbol refers to a java or scala symbol, + // it does matter whether it occurs in java or scala code + // typerefs w/o type params that occur in java signatures/code are considered raw types, and are represented as existential types + override def isHigherKinded = (args.isEmpty && !typeParamsDirect.isEmpty) override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = if (isHigherKinded) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 898ad238a1..0c3e34f8c5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -356,6 +356,8 @@ trait Infer { context.unit.depends += sym.toplevelClass val sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) + // Console.println("check acc " + (sym, sym1) + ":" + (sym.tpe, sym1.tpe) + " from " + pre);//DEBUG + if (sym1 == NoSymbol) { if (settings.debug.value) { Console.println(context) @@ -364,26 +366,9 @@ trait Infer { } accessError("") } else { - // Modify symbol's type so that raw types C - // are converted to existentials C[T] forSome { type T }. - // We can't do this on class loading because it would result - // in infinite cycles. - def cook(sym: Symbol) { - val tpe1 = rawToExistential(sym.tpe) - if (tpe1 ne sym.tpe) { - if (settings.debug.value) println("cooked: "+sym+":"+sym.tpe) - sym.setInfo(tpe1) - } - } - if (sym1.isTerm) { - if (sym1 hasFlag JAVA) - cook(sym1) - else if (sym1 hasFlag OVERLOADED) - for (sym2 <- sym1.alternatives) - if (sym2 hasFlag JAVA) - cook(sym2) - } - //Console.println("check acc " + sym1 + ":" + sym1.tpe + " from " + pre);//DEBUG + if(sym1.isTerm) + sym1.cookJavaRawInfo() // xform java rawtypes into existentials + var owntype = try{ pre.memberType(sym1) } catch { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index aaefab1f74..9b8be2aaec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -370,6 +370,8 @@ abstract class RefChecks extends InfoTransform { kindErrors.toList.mkString("\n", ", ", "")) } } else if (other.isTerm) { + other.cookJavaRawInfo() // #2454 + if (!overridesType(self.memberInfo(member), self.memberInfo(other))) { // 8 overrideTypeError() explainTypes(self.memberInfo(member), self.memberInfo(other)) diff --git a/test/files/pos/t2305.scala b/test/files/pos/t2305.scala new file mode 100644 index 0000000000..8b5abccbe0 --- /dev/null +++ b/test/files/pos/t2305.scala @@ -0,0 +1,26 @@ +import java.util.ArrayList + +trait Bind[Z[_]] + +class MySerializable[X] extends java.io.Serializable + +object Bind { + implicit val JavaArrayListBind: Bind[ArrayList] = new Bind[ArrayList] {} + implicit val MySerializableBind: Bind[MySerializable] = new Bind[MySerializable] {} +} + +object works { + // this works fine: + def runbind(implicit bind: Bind[MySerializable]) {} + runbind +} + +object breaks { + def runbind(implicit bind: Bind[ArrayList]) {} + runbind + /*java.lang.AssertionError: assertion failed: java.io.Serializable + at scala.Predef$.assert(Predef.scala:107) + at scala.tools.nsc.symtab.Types$TypeRef.transform(Types.scala:1417) + at scala.tools.nsc.symtab.Types$TypeRef.baseType(Types.scala:1559) + */ +} diff --git a/test/files/pos/t2454.scala b/test/files/pos/t2454.scala new file mode 100644 index 0000000000..00f2e6f677 --- /dev/null +++ b/test/files/pos/t2454.scala @@ -0,0 +1,25 @@ +package am; + +trait One[M[_]] { + val x : Int +} + +trait Two[M[_,_]] { + val x : Int +} + +object Test { + // Works. + val x = new Two[Map] { + val x = 5 + } + + val o = new One[java.util.List] { + val x = 1 + } + + // Does not work + val y = new Two[java.util.concurrent.ConcurrentHashMap] { + val x = 3 + } +} -- cgit v1.2.3