From bba3b00cf737528de9dcb4823806d6928a00474e Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Dec 2011 22:37:58 -0800 Subject: Fix for classOf NPE. Let type parameter be inferred. Closes SI-4871. --- .../scala/reflect/internal/Definitions.scala | 10 +++++++ .../scala/tools/nsc/typechecker/Implicits.scala | 11 +------ .../scala/tools/nsc/typechecker/Typers.scala | 34 +++++++++++++++------- test/files/run/t4871.check | 2 ++ test/files/run/t4871.scala | 12 ++++++++ 5 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 test/files/run/t4871.check create mode 100644 test/files/run/t4871.scala diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 4d71d2a769..6ee9347aab 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -232,6 +232,16 @@ trait Definitions extends reflect.api.StandardDefinitions { def Predef_identity = getMember(PredefModule, nme.identity) def Predef_conforms = getMember(PredefModule, nme.conforms) def Predef_wrapRefArray = getMember(PredefModule, nme.wrapRefArray) + + /** Is `sym` a member of Predef with the given name? + * Note: DON't replace this by sym == Predef_conforms/etc, as Predef_conforms is a `def` + * which does a member lookup (it can't be a lazy val because we might reload Predef + * during resident compilations). + */ + def isPredefMemberNamed(sym: Symbol, name: Name) = ( + (sym.name == name) && (sym.owner == PredefModule.moduleClass) + ) + lazy val ConsoleModule: Symbol = getModule("scala.Console") lazy val ScalaRunTimeModule: Symbol = getModule("scala.runtime.ScalaRunTime") lazy val SymbolModule: Symbol = getModule("scala.Symbol") diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 92be241951..d54cb248cf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -740,7 +740,7 @@ trait Implicits { ) private def isIneligible(info: ImplicitInfo) = ( info.isCyclicOrErroneous - || isView && isConforms(info.sym) + || isView && isPredefMemberNamed(info.sym, nme.conforms) || isShadowed(info.name) ) @@ -760,15 +760,6 @@ trait Implicits { */ private def checkValid(sym: Symbol) = isValid(sym) || { invalidImplicits += sym ; false } - /** Is `sym` the standard conforms method in Predef? - * Note: DON't replace this by sym == Predef_conforms, as Predef_conforms is a `def` - * which does a member lookup (it can't be a lazy val because we might reload Predef - * during resident compilations). - */ - private def isConforms(sym: Symbol) = ( - (sym.name == nme.conforms) && (sym.owner == PredefModule.moduleClass) - ) - /** Preventing a divergent implicit from terminating implicit search, * so that if there is a best candidate it can still be selected. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9b03d59216..341e1bc5ea 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2950,6 +2950,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { new DeSkolemizeMap mapOver tp } + def typedClassOf(tree: Tree, tpt: Tree) = { + checkClassType(tpt, true, false) + atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) + } + protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { for (wc <- tree.whereClauses) if (wc.symbol == NoSymbol) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL } @@ -2989,10 +2994,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sameLength(tparams, args)) { val targs = args map (_.tpe) checkBounds(tree.pos, NoPrefix, NoSymbol, tparams, targs, "") - if (fun.symbol == Predef_classOf) { - checkClassType(args.head, true, false) - atPos(tree.pos) { gen.mkClassOf(targs.head) } - } else { + if (fun.symbol == Predef_classOf) + typedClassOf(tree, args.head) + else { if (!isPastTyper && fun.symbol == Any_isInstanceOf && !targs.isEmpty) checkCheckable(tree.pos, targs.head, "") val resultpe = restpe.instantiateTypeParams(tparams, targs) @@ -3769,7 +3773,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { reallyExists(sym) && ((mode & PATTERNmode | FUNmode) != (PATTERNmode | FUNmode) || !sym.isSourceMethod || sym.hasFlag(ACCESSOR)) } - + if (defSym == NoSymbol) { var defEntry: ScopeEntry = null // the scope entry of defSym, if defined in a local scope @@ -3900,13 +3904,23 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } } - if (defSym.owner.isPackageClass) pre = defSym.owner.thisType + if (defSym.owner.isPackageClass) + pre = defSym.owner.thisType + + // Inferring classOf type parameter from expected type. if (defSym.isThisSym) { typed1(This(defSym.owner) setPos tree.pos, mode, pt) - } else { - val tree1 = if (qual == EmptyTree) tree - else atPos(tree.pos)(Select(qual, name)) - // atPos necessary because qualifier might come from startContext + } + // Inferring classOf type parameter from expected type. Otherwise an + // actual call to the stubbed classOf method is generated, returning null. + else if (isPredefMemberNamed(defSym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) + typedClassOf(tree, TypeTree(pt.typeArgs.head)) + else { + val tree1 = ( + if (qual == EmptyTree) tree + // atPos necessary because qualifier might come from startContext + else atPos(tree.pos)(Select(qual, name)) + ) val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? stabilize(tree2, pre2, mode, pt) match { diff --git a/test/files/run/t4871.check b/test/files/run/t4871.check new file mode 100644 index 0000000000..a60526a0f3 --- /dev/null +++ b/test/files/run/t4871.check @@ -0,0 +1,2 @@ +class Test$C +class Test$D diff --git a/test/files/run/t4871.scala b/test/files/run/t4871.scala new file mode 100644 index 0000000000..70d8b7145c --- /dev/null +++ b/test/files/run/t4871.scala @@ -0,0 +1,12 @@ +object Test { + class C + class D + + def main(args: Array[String]): Unit = { + val z: Class[C] = classOf + val z2: Class[D] = classOf[D] + + println(z) + println(z2) + } +} -- cgit v1.2.3