diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2010-07-08 15:58:47 +0000 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2010-07-08 15:58:47 +0000 |
commit | d7814a235963f40f4de1bf49efd4b22a8b0da5db (patch) | |
tree | 7e9b127853d072b049f05e2d2376234fefc5c4ca /src/compiler/scala/tools/nsc/typechecker/Typers.scala | |
parent | 356abe3a5b76eb382eca6655624743aa16a001ae (diff) | |
download | scala-d7814a235963f40f4de1bf49efd4b22a8b0da5db.tar.gz scala-d7814a235963f40f4de1bf49efd4b22a8b0da5db.tar.bz2 scala-d7814a235963f40f4de1bf49efd4b22a8b0da5db.zip |
closes #2416.
this patch introduces a new subclass of TypeTree: TypeTreeWithDeferredRefCheck, which tracks the type args and type params of a type application when it was beta-reduced during typing without checking that the application was well-kinded -- that check must wait until refchecks, but was never performed since the application had been beta-reduced away
caveat discovered while working on the fix: adapt turned all trees for
which _.isType holds into TypeTree's
review by odersky
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 68 |
1 files changed, 48 insertions, 20 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index be4cfe6512..6a81f1897f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -555,6 +555,8 @@ trait Typers { self: Analyzer => * If symbol refers to package object, insert `.package` as second to last selector. * (exception for some symbols in scala package which are dealiased immediately) * Call checkAccessible, which sets tree's attributes. + * Also note that checkAccessible looks up sym on pre without checking that pre is well-formed + * (illegal type applications in pre will be skipped -- that's why typedSelect wraps the resulting tree in a TreeWithDeferredChecks) * @return modified tree and new prefix type */ private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) = @@ -2933,6 +2935,11 @@ trait Typers { self: Analyzer => errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.") } + private[this] var typingIndent: String = "" + @inline def deindentTyping() = if (printTypings) typingIndent = typingIndent.substring(0, typingIndent.length() - 2) + @inline def indentTyping() = if (printTypings) typingIndent += " " + @inline def printTyping(s: => String) = if (printTypings) println(typingIndent+s) + /** * @param tree ... * @param mode ... @@ -3250,7 +3257,7 @@ trait Typers { self: Analyzer => def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos) if (fun :: tree :: args exists errorInResult) { - if (printTypings) println("second try for: "+fun+" and "+args) + printTyping("second try for: "+fun+" and "+args) val Select(qual, name) = fun val args1 = tryTypedArgs(args, argMode(fun, mode), ex) val qual1 = @@ -3260,9 +3267,8 @@ trait Typers { self: Analyzer => val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos return typed1(tree1, mode | SNDTRYmode, pt) } - } else if (printTypings) { - println("no second try for "+fun+" and "+args+" because error not in result:"+ex.pos+"!="+tree.pos) - } + } else printTyping("no second try for "+fun+" and "+args+" because error not in result:"+ex.pos+"!="+tree.pos) + reportTypeError(tree.pos, ex) setError(tree) } @@ -3501,9 +3507,9 @@ trait Typers { self: Analyzer => case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) } - //if (name.toString == "Elem") println("typedSelect "+qual+":"+qual.tpe+" "+sym+"/"+tree1+":"+tree1.tpe) val (tree2, pre2) = makeAccessible(tree1, sym, qual.tpe, qual) val result = stabilize(tree2, pre2, mode, pt) + def isPotentialNullDeference() = { phase.id <= currentRun.typerPhase.id && !sym.isConstructor && @@ -3514,7 +3520,20 @@ trait Typers { self: Analyzer => if (settings.Xchecknull.value && isPotentialNullDeference && unit != null) unit.warning(tree.pos, "potential null pointer dereference: "+tree) - result + result match { + // could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual? + case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks + treeCopy.SelectFromTypeTree( + result, + (TypeTreeWithDeferredRefCheck(){ () => val tp = qual.tpe; val sym = tp.typeSymbolDirect + // will execute during refchecks -- TODO: make private checkTypeRef in refchecks public and call that one? + checkBounds(qual.pos, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "") + qual // you only get to see the wrapped tree after running this check :-p + }) setType qual.tpe, + name) + case _ => + result + } } } @@ -3647,6 +3666,7 @@ trait Typers { self: Analyzer => else atPos(tree.pos)(Select(qual, name)) // atPos necessary because qualifier might come from startContext 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) } } @@ -3672,7 +3692,7 @@ trait Typers { self: Analyzer => } else { val tparams = tpt1.symbol.typeParams if (tparams.length == args.length) { - // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) + // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) val args1 = if(!tpt1.symbol.rawInfo.isComplete) args mapConserve (typedHigherKindedType(_, mode)) @@ -3683,11 +3703,8 @@ trait Typers { self: Analyzer => //@M! the polytype denotes the expected kind } val argtypes = args1 map (_.tpe) - val owntype = if (tpt1.symbol.isClass || tpt1.symbol.isNonClassType) - // @M! added the latter condition - appliedType(tpt1.tpe, argtypes) - else tpt1.tpe.instantiateTypeParams(tparams, argtypes) - (args, tparams).zipped map { (arg, tparam) => arg match { + + (args, tparams).zipped foreach { (arg, tparam) => arg match { // note: can't use args1 in selector, because Bind's got replaced case Bind(_, _) => if (arg.symbol.isAbstractType) @@ -3697,7 +3714,17 @@ trait Typers { self: Analyzer => glb(List(arg.symbol.info.bounds.hi, tparam.info.bounds.hi.subst(tparams, argtypes)))) case _ => }} - TypeTree(owntype) setOriginal(tree) // setPos tree.pos + + val result = TypeTree(appliedType(tpt1.tpe, argtypes)) setOriginal(tree) // setPos tree.pos (done by setOriginal) + if(tpt1.tpe.isInstanceOf[PolyType]) // did the type application (performed by appliedType) involve an unchecked beta-reduction? + (TypeTreeWithDeferredRefCheck(){ () => + // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap + // we can't simply use original in refchecks because it does not contains types + // (and the only typed trees we have have been mangled so they're not quite the original tree anymore) + checkBounds(result.pos, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "") + result // you only get to see the wrapped tree after running this check :-p + }).setType(result.tpe) + else result } else if (tparams.length == 0) { errorTree(tree, tpt1.tpe+" does not take type parameters") } else { @@ -4009,7 +4036,7 @@ trait Typers { self: Analyzer => case SelectFromTypeTree(qual, selector) => val qual1 = typedType(qual, mode) if (qual1.tpe.isVolatile) error(tree.pos, "illegal type selection from volatile type "+qual.tpe) - typedSelect(typedType(qual, mode), selector) + typedSelect(qual1, selector) case CompoundTypeTree(templ) => typedCompoundTypeTree(templ) @@ -4025,6 +4052,7 @@ trait Typers { self: Analyzer => case etpt @ ExistentialTypeTree(_, _) => newTyper(context.makeNewScope(tree, context.owner)).typedExistentialTypeTree(etpt, mode) + case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure) case tpt @ TypeTree() => if (tpt.original != null) tree setType typedType(tpt.original, mode).tpe @@ -4047,8 +4075,7 @@ trait Typers { self: Analyzer => * @param pt ... * @return ... */ - def typed(tree: Tree, mode: Int, pt: Type): Tree = { - + def typed(tree: Tree, mode: Int, pt: Type): Tree = { indentTyping() def dropExistential(tp: Type): Type = tp match { case ExistentialType(tparams, tpe) => if (settings.debug.value) println("drop ex "+tree+" "+tp) @@ -4074,15 +4101,15 @@ trait Typers { self: Analyzer => tree.tpe = null if (tree.hasSymbol) tree.symbol = NoSymbol } - if (printTypings) println("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors) //DEBUG + printTyping("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors) //DEBUG var tree1 = if (tree.tpe ne null) tree else typed1(tree, mode, dropExistential(pt)) - if (printTypings) println("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG + printTyping("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG tree1.tpe = addAnnotations(tree1, tree1.tpe) val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree) - if (printTypings) println("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG + printTyping("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG // for (t <- tree1.tpe) assert(t != WildcardType) // if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe) if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result) @@ -4090,7 +4117,7 @@ trait Typers { self: Analyzer => } catch { case ex: TypeError => tree.tpe = null - if (printTypings) println("caught "+ex+" in typed: "+tree) //DEBUG + printTyping("caught "+ex+" in typed: "+tree) //DEBUG reportTypeError(tree.pos, ex) setError(tree) case ex: Exception => @@ -4102,6 +4129,7 @@ trait Typers { self: Analyzer => throw ex } finally { + deindentTyping() if (Statistics.enabled) { val t = currentTime() microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt |