From 19bb1732646c77e58fd63490afdca066afd5ec15 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 6 Apr 2012 09:11:00 -0700 Subject: Fix for SI-5644. Don't let OverloadedTypes reach the backend. When you want a method from a particular symbol, avoid getMember, which may inflict upon you an OverloadedType if an inherited member has the same name. Instead, use the (just now appearing) definitions.getDecl. --- .../scala/reflect/internal/Definitions.scala | 26 +++++++++++++++++----- src/compiler/scala/tools/nsc/Global.scala | 2 +- .../scala/tools/nsc/backend/JavaPlatform.scala | 8 +++---- .../scala/tools/nsc/backend/jvm/GenJVM.scala | 12 +++++----- .../tools/nsc/symtab/classfile/ICodeReader.scala | 4 ++-- .../scala/tools/nsc/transform/CleanUp.scala | 10 +++++---- .../scala/tools/nsc/typechecker/TreeCheckers.scala | 19 ++++++++++++++++ 7 files changed, 59 insertions(+), 22 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 0224f9158e..e1740b621e 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -863,7 +863,8 @@ trait Definitions extends reflect.api.StandardDefinitions { // boxed classes lazy val ObjectRefClass = getRequiredClass("scala.runtime.ObjectRef") lazy val VolatileObjectRefClass = getRequiredClass("scala.runtime.VolatileObjectRef") - lazy val BoxesRunTimeClass = getRequiredModule("scala.runtime.BoxesRunTime") + lazy val BoxesRunTimeModule = getRequiredModule("scala.runtime.BoxesRunTime") + lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass lazy val BoxedNumberClass = getClass(sn.BoxedNumber) lazy val BoxedCharacterClass = getClass(sn.BoxedCharacter) lazy val BoxedBooleanClass = getClass(sn.BoxedBoolean) @@ -874,6 +875,9 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val BoxedFloatClass = getRequiredClass("java.lang.Float") lazy val BoxedDoubleClass = getRequiredClass("java.lang.Double") + lazy val Boxes_isNumberOrBool = getDecl(BoxesRunTimeClass, nme.isBoxedNumberOrBoolean) + lazy val Boxes_isNumber = getDecl(BoxesRunTimeClass, nme.isBoxedNumber) + lazy val BoxedUnitClass = getRequiredClass("scala.runtime.BoxedUnit") lazy val BoxedUnitModule = getRequiredModule("scala.runtime.BoxedUnit") def BoxedUnit_UNIT = getMember(BoxedUnitModule, nme.UNIT) @@ -990,12 +994,24 @@ trait Definitions extends reflect.api.StandardDefinitions { else findNamedMember(segs.tail, root.info member segs.head) def getMember(owner: Symbol, name: Name): Symbol = { - if (owner == NoSymbol) NoSymbol - else owner.info.nonPrivateMember(name) match { - case NoSymbol => throw new FatalError(owner + " does not have a member " + name) - case result => result + getMemberIfDefined(owner, name) orElse { + throw new FatalError(owner + " does not have a member " + name) + } + } + def getMemberIfDefined(owner: Symbol, name: Name): Symbol = + owner.info.nonPrivateMember(name) + + /** Using getDecl rather than getMember may avoid issues with + * OverloadedTypes turning up when you don't want them, if you + * know the method in question is uniquely declared in the given owner. + */ + def getDecl(owner: Symbol, name: Name): Symbol = { + getDeclIfDefined(owner, name) orElse { + throw new FatalError(owner + " does not have a decl " + name) } } + def getDeclIfDefined(owner: Symbol, name: Name): Symbol = + owner.info.nonPrivateDecl(name) def packageExists(packageName: String): Boolean = getModuleIfDefined(packageName).isPackage diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 8df687eca9..5e0c24d304 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1002,7 +1002,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Set phase to a newly created syntaxAnalyzer and call definitions.init. */ val parserPhase: Phase = syntaxAnalyzer.newPhase(NoPhase) phase = parserPhase - definitions.init + definitions.init() // Flush the cache in the terminal phase: the chain could have been built // before without being used. (This happens in the interpreter.) diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index 27df45b563..314a3b45a0 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -38,10 +38,10 @@ trait JavaPlatform extends Platform { genJVM // generate .class files ) ++ depAnalysisPhase - lazy val externalEquals = getMember(BoxesRunTimeClass, nme.equals_) - lazy val externalEqualsNumNum = getMember(BoxesRunTimeClass, nme.equalsNumNum) - lazy val externalEqualsNumChar = getMember(BoxesRunTimeClass, nme.equalsNumChar) - lazy val externalEqualsNumObject = getMember(BoxesRunTimeClass, nme.equalsNumObject) + lazy val externalEquals = getDecl(BoxesRunTimeClass, nme.equals_) + lazy val externalEqualsNumNum = getDecl(BoxesRunTimeClass, nme.equalsNumNum) + lazy val externalEqualsNumChar = getDecl(BoxesRunTimeClass, nme.equalsNumChar) + lazy val externalEqualsNumObject = getDecl(BoxesRunTimeClass, nme.equalsNumObject) /** We could get away with excluding BoxedBooleanClass for the * purpose of equality testing since it need not compare equal diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 7b7135d180..18dc4aa56c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -1281,7 +1281,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with val jname = javaName(method) val jtype = javaType(method).asInstanceOf[JMethodType] - def debugMsg(invoke: String) { + def dbg(invoke: String) { debuglog("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype)) } @@ -1299,14 +1299,14 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with } style match { - case Static(true) => jcode.emitINVOKESPECIAL (jowner, jname, jtype) ; debugMsg("invokespecial") - case Static(false) => jcode.emitINVOKESTATIC (jowner, jname, jtype) ; debugMsg("invokestatic") - case Dynamic if isInterfaceCall(receiver) => jcode.emitINVOKEINTERFACE(jowner, jname, jtype) ; debugMsg("invokinterface") - case Dynamic => jcode.emitINVOKEVIRTUAL (jowner, jname, jtype) ; debugMsg("invokevirtual") + case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) + case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) + case Dynamic if isInterfaceCall(receiver) => dbg("invokinterface"); jcode.emitINVOKEINTERFACE(jowner, jname, jtype) + case Dynamic => dbg("invokevirtual"); jcode.emitINVOKEVIRTUAL(jowner, jname, jtype) case SuperCall(_) => + dbg("invokespecial") jcode.emitINVOKESPECIAL(jowner, jname, jtype) initModule() - debugMsg("invokespecial") } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 68af518d3a..775a7a9d38 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -625,11 +625,11 @@ abstract class ICodeReader extends ClassfileParser { * such as Int.box(5). */ def isBox(m: Symbol): Boolean = - (m.owner == definitions.BoxesRunTimeClass.moduleClass + (m.owner == definitions.BoxesRunTimeClass && m.name.startsWith("boxTo")) def isUnbox(m: Symbol): Boolean = - (m.owner == definitions.BoxesRunTimeClass.moduleClass + (m.owner == definitions.BoxesRunTimeClass && m.name.startsWith("unboxTo")) /** Return the icode class that should include members with the given flags. diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index b21fa4bc83..e6f5dc5b5f 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -299,11 +299,11 @@ abstract class CleanUp extends Transform with ast.TreeDSL { def testForName(name: Name): Tree => Tree = t => ( if (nme.CommonOpNames(name)) - gen.mkMethodCall(getMember(BoxesRunTimeClass, nme.isBoxedNumberOrBoolean), t :: Nil) + gen.mkMethodCall(definitions.Boxes_isNumberOrBool, t :: Nil) else if (nme.BooleanOpNames(name)) t IS_OBJ BoxedBooleanClass.tpe else - gen.mkMethodCall(getMember(BoxesRunTimeClass, nme.isBoxedNumber), t :: Nil) + gen.mkMethodCall(definitions.Boxes_isNumber, t :: Nil) ) /** The Tree => Tree function in the return is necessary to prevent the original qual @@ -318,8 +318,10 @@ abstract class CleanUp extends Transform with ast.TreeDSL { else if (params.tail.isEmpty) nme.primitiveInfixMethodName(name) else nme.NO_NAME ) - if (methodName == nme.NO_NAME) None - else Some((getMember(BoxesRunTimeClass, methodName), testForName(name))) + definitions.getDeclIfDefined(BoxesRunTimeClass, methodName) match { + case NoSymbol => None + case sym => assert(!sym.isOverloaded, sym) ; Some((sym, testForName(name))) + } } /* ### BOXING PARAMS & UNBOXING RESULTS ### */ diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 105c2c0b98..8895905ca7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -14,6 +14,20 @@ import util.returning abstract class TreeCheckers extends Analyzer { import global._ + private val everything = ListBuffer[(Phase, Map[Tree, (Symbol, Type)])]() + private val currentTrees = mutable.Map[Tree, (Symbol, Type)]() + + if (settings.debug.value) { + sys addShutdownHook { + for ((ph, map) <- everything.toList) { + println("\n>>>> " + ph + "\n") + for ((tree, (sym, tpe)) <- map.toList.sortBy(_._1.summaryString)) { + println("%20s %20s %s".format(sym, tpe, ("" + tree) take 50)) + } + } + } + } + private def classstr(x: AnyRef) = x.getClass.getName split """\\.|\\$""" last; private def typestr(x: Type) = " (tpe = " + x + ")" private def treestr(t: Tree) = t + " [" + classstr(t) + "]" + typestr(t.tpe) @@ -92,11 +106,16 @@ abstract class TreeCheckers extends Analyzer { if (maps.isEmpty || maps.last._1 != ph) maps += ((ph, new PhaseMap)) + currentTrees.clear() traverse(unit.body) + everything += ((ph, currentTrees.toMap)) + reportChanges() } override def traverse(tree: Tree): Unit = { val sym = tree.symbol + currentTrees(tree) = ((sym, tree.tpe)) + if (sym != null && sym != NoSymbol) { record(sym, tree) tree match { -- cgit v1.2.3