diff options
author | Paul Phillips <paulp@improving.org> | 2011-11-19 08:39:59 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-11-19 08:39:59 +0000 |
commit | 214c145943ac2c6bf37bd40f5e07e225350201c5 (patch) | |
tree | 9ae086954e4e855e9561bbef0b812d34c82d967c | |
parent | 334872e33be8385678697f3d670c8102d38cdca7 (diff) | |
download | scala-214c145943ac2c6bf37bd40f5e07e225350201c5.tar.gz scala-214c145943ac2c6bf37bd40f5e07e225350201c5.tar.bz2 scala-214c145943ac2c6bf37bd40f5e07e225350201c5.zip |
Cleanups in TypeApply creation and casting.
There's every hint that it's a requirement that a TypeApply have
non-empty typeArgs, but testing for and handling the empty condition
is done irregularly. Made a mkTypeApply which handles the isEmpty case
(returning "fun" unchanged.) Also unified most of the variations of
casts under one umbrella. Review by moors.
13 files changed, 78 insertions, 79 deletions
diff --git a/src/compiler/scala/reflect/internal/TreeGen.scala b/src/compiler/scala/reflect/internal/TreeGen.scala index fae3975ed5..1c93a904c0 100644 --- a/src/compiler/scala/reflect/internal/TreeGen.scala +++ b/src/compiler/scala/reflect/internal/TreeGen.scala @@ -46,10 +46,8 @@ abstract class TreeGen { def mkMethodCall(receiver: Tree, method: Symbol, targs: List[Type], args: List[Tree]): Tree = mkMethodCall(Select(receiver, method), targs, args) - def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree = { - val typeApplied = if (targs.isEmpty) target else TypeApply(target, targs map TypeTree) - Apply(typeApplied, args) - } + def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree = + Apply(mkTypeApply(target, targs map TypeTree), args) /** Builds a reference to value whose type is given stable prefix. * The type must be suitable for this. For example, it @@ -153,12 +151,14 @@ abstract class TreeGen { /** Cast `tree` to type `pt` */ def mkCast(tree: Tree, pt: Type): Tree = { - debuglog("casting " + tree + ":" + tree.tpe + " to " + pt) + debuglog("casting " + tree + ":" + tree.tpe + " to " + pt + " at phase: " + phase) assert(!tree.tpe.isInstanceOf[MethodType], tree) - assert(!pt.typeSymbol.isPackageClass) - assert(!pt.typeSymbol.isPackageObjectClass) - assert(pt eq pt.normalize, tree +" : "+ debugString(pt) +" ~>"+ debugString(pt.normalize)) //@MAT only called during erasure, which already takes care of that - atPos(tree.pos)(mkAsInstanceOf(tree, pt, false)) + assert(!pt.typeSymbol.isPackageClass && !pt.typeSymbol.isPackageObjectClass, pt) + // @MAT only called during erasure, which already takes care of that + // @PP: "only called during erasure" is not very true these days. + // In addition, at least, are: typer, uncurry, explicitouter, cleanup. + assert(pt eq pt.normalize, tree +" : "+ debugString(pt) +" ~>"+ debugString(pt.normalize)) + atPos(tree.pos)(mkAsInstanceOf(tree, pt, any = false, wrapInApply = true)) } /** Builds a reference with stable type to given symbol */ @@ -192,31 +192,33 @@ abstract class TreeGen { } } - private def mkTypeApply(value: Tree, tpe: Type, what: Symbol, wrapInApply: Boolean) = { - val tapp = TypeApply(mkAttributedSelect(value, what), List(TypeTree(tpe.normalize))) - if (wrapInApply) Apply(tapp, List()) else tapp + /** Builds a type application node if args.nonEmpty, returns fun otherwise. */ + def mkTypeApply(fun: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) fun else TypeApply(fun, targs) + def mkTypeApply(target: Tree, method: Symbol, targs: List[Type]): Tree = + mkTypeApply(Select(target, method), targs map TypeTree) + def mkAttributedTypeApply(target: Tree, method: Symbol, targs: List[Type]): Tree = + mkTypeApply(mkAttributedSelect(target, method), targs map TypeTree) + + private def mkSingleTypeApply(value: Tree, tpe: Type, what: Symbol, wrapInApply: Boolean) = { + val tapp = mkAttributedTypeApply(value, what, List(tpe.normalize)) + if (wrapInApply) Apply(tapp, Nil) else tapp } + private def typeTestSymbol(any: Boolean) = if (any) Any_isInstanceOf else Object_isInstanceOf + private def typeCastSymbol(any: Boolean) = if (any) Any_asInstanceOf else Object_asInstanceOf /** Builds an instance test with given value and type. */ def mkIsInstanceOf(value: Tree, tpe: Type, any: Boolean = true, wrapInApply: Boolean = true): Tree = - mkTypeApply(value, tpe, (if (any) Any_isInstanceOf else Object_isInstanceOf), wrapInApply) + mkSingleTypeApply(value, tpe, typeTestSymbol(any), wrapInApply) /** Builds a cast with given value and type. */ def mkAsInstanceOf(value: Tree, tpe: Type, any: Boolean = true, wrapInApply: Boolean = true): Tree = - mkTypeApply(value, tpe, (if (any) Any_asInstanceOf else Object_asInstanceOf), wrapInApply) + mkSingleTypeApply(value, tpe, typeCastSymbol(any), wrapInApply) /** Cast `tree` to `pt`, unless tpe is a subtype of pt, or pt is Unit. */ def maybeMkAsInstanceOf(tree: Tree, pt: Type, tpe: Type, beforeRefChecks: Boolean = false): Tree = - if ((pt == UnitClass.tpe) || (tpe <:< pt)) { - log("no need to cast from " + tpe + " to " + pt) - tree - } else - atPos(tree.pos) { - if (beforeRefChecks) - TypeApply(mkAttributedSelect(tree, Any_asInstanceOf), List(TypeTree(pt))) - else - mkAsInstanceOf(tree, pt) - } + if ((pt == UnitClass.tpe) || (tpe <:< pt)) tree + else atPos(tree.pos)(mkAsInstanceOf(tree, pt, any = true, wrapInApply = !beforeRefChecks)) /** Apparently we smuggle a Type around as a Literal(Constant(tp)) * and the implementation of Constant#tpe is such that x.tpe becomes diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index f8255bc846..85a12193a9 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -2986,6 +2986,11 @@ A type's typeSymbol should never be inspected directly. /** A creator for type parameterizations that strips empty type parameter lists. * Use this factory method to indicate the type has kind * (it's a polymorphic value) * until we start tracking explicit kinds equivalent to typeFun (except that the latter requires tparams nonEmpty). + * + * PP to AM: I've co-opted this for where I know tparams may well be empty, and + * expecting to get back `tpe` in such cases. Re being "forgiving" below, + * can we instead say this is the canonical creator for polyTypes which + * may or may not be poly? (It filched the standard "canonical creator" name.) */ def polyType(tparams: List[Symbol], tpe: Type): Type = if (tparams nonEmpty) typeFun(tparams, tpe) diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 8add556741..efc64dbbc5 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -114,10 +114,7 @@ trait TreeDSL { * * See ticket #2168 for one illustration of AS vs. AS_ANY. */ - def AS(tpe: Type) = TypeApply(Select(target, Any_asInstanceOf), List(TypeTree(tpe))) - def AS_ANY(tpe: Type) = gen.mkAsInstanceOf(target, tpe) - def AS_ATTR(tpe: Type) = gen.mkAttributedCast(target, tpe) - + def AS(tpe: Type) = gen.mkAsInstanceOf(target, tpe, any = true, wrapInApply = false) def IS(tpe: Type) = gen.mkIsInstanceOf(target, tpe, true) def IS_OBJ(tpe: Type) = gen.mkIsInstanceOf(target, tpe, false) diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index b073bb3c96..481a997c00 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -24,7 +24,10 @@ trait ParallelMatching extends ast.TreeDSL self: ExplicitOuter => import global.{ typer => _, _ } - import definitions.{ AnyRefClass, NothingClass, IntClass, BooleanClass, SomeClass, OptionClass, getProductArgs, productProj } + import definitions.{ + AnyRefClass, NothingClass, IntClass, BooleanClass, SomeClass, OptionClass, + getProductArgs, productProj, Object_eq, Any_asInstanceOf + } import CODE._ import Types._ import Debug._ @@ -132,7 +135,7 @@ trait ParallelMatching extends ast.TreeDSL def castedTo(headType: Type) = if (tpe =:= headType) this - else new Scrutinee(createVar(headType, lhs => id AS_ANY lhs.tpe)) + else new Scrutinee(createVar(headType, lhs => gen.mkAsInstanceOf(id, lhs.tpe))) override def toString() = "(%s: %s)".format(id, tpe) } @@ -673,7 +676,7 @@ trait ParallelMatching extends ast.TreeDSL // the val definition's type, or a casted Ident if not. private def newValIdent(lhs: Symbol, rhs: Symbol) = if (rhs.tpe <:< lhs.tpe) Ident(rhs) - else Ident(rhs) AS lhs.tpe + else gen.mkTypeApply(Ident(rhs), Any_asInstanceOf, List(lhs.tpe)) protected def newValDefinition(lhs: Symbol, rhs: Symbol) = typer typedValDef ValDef(lhs, newValIdent(lhs, rhs)) @@ -854,10 +857,11 @@ trait ParallelMatching extends ast.TreeDSL case ThisType(clazz) => THIS(clazz) case pre => REF(pre.prefix, pre.termSymbol) }) - outerAccessor(tpe2test.typeSymbol) match { case NoSymbol => ifDebug(cunit.warning(scrut.pos, "no outer acc for " + tpe2test.typeSymbol)) ; cond - case outerAcc => cond AND (((scrut AS_ANY tpe2test) DOT outerAcc)() OBJ_EQ theRef) + case outerAcc => + val casted = gen.mkAsInstanceOf(scrut, tpe2test, any = true, wrapInApply = true) + cond AND ((casted DOT outerAcc)() OBJ_EQ theRef) } } } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 9f15c768e3..d001a0af8b 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -245,7 +245,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe))) val reflPolyCacheSym: Symbol = addStaticVariableToClass("reflPoly$Cache", SoftReferenceClass.tpe, mkNewPolyCache, false) - def getPolyCache = fn(safeREF(reflPolyCacheSym), nme.get) AS_ATTR MethodCacheClass.tpe + def getPolyCache = gen.mkCast(fn(safeREF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe) addStaticMethodToClass("reflMethod$Method", List(ClassClass.tpe), MethodClass.tpe) { case Pair(reflMethodSym, List(forReceiverSym)) => @@ -405,7 +405,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { def fixResult(tree: Tree, mustBeUnit: Boolean = false) = if (mustBeUnit || resultSym == UnitClass) BLOCK(tree, REF(BoxedUnit_UNIT)) // boxed unit else if (resultSym == ObjectClass) tree // no cast necessary - else tree AS_ATTR boxedResType // cast to expected type + else gen.mkCast(tree, boxedResType) // cast to expected type /** Normal non-Array call */ def genDefaultCall = { diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index a2eddd34bd..9806857ff2 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -490,7 +490,7 @@ abstract class Erasure extends AddInterfaces log("Attempted to cast to Unit: " + tree) tree.duplicate setType pt } - else tree AS_ATTR pt + else gen.mkAttributedCast(tree, pt) } private def isUnboxedValueMember(sym: Symbol) = @@ -1002,7 +1002,7 @@ abstract class Erasure extends AddInterfaces if (isAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) { // insert cast to prevent illegal access error (see #4283) // util.trace("insert erasure cast ") (*/ - treeCopy.Select(tree, qual AS_ATTR qual.tpe.widen, name) //) + treeCopy.Select(tree, gen.mkAttributedCast(qual, qual.tpe.widen), name) //) } else tree } else tree diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 192c4e9b59..fcc03a82d0 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -359,7 +359,7 @@ abstract class ExplicitOuter extends InfoTransform localTyper typed { (DEF(outerAcc) withPos currentClass.pos) === { // Need to cast for nested outer refs in presence of self-types. See ticket #3274. - transformer.transform(path) AS_ANY outerAcc.info.resultType + gen.mkCast(transformer.transform(path), outerAcc.info.resultType) } } } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 97f204bc41..212785a525 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1084,34 +1084,31 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case _ => //log("nope") } + private def unspecializableClass(tp: Type) = ( + definitions.isRepeatedParamType(tp) // ??? + || tp.typeSymbol.isJavaDefined + || tp.typeSymbol.isPackageClass + ) + /** Type transformation. It is applied to all symbols, compiled or loaded. * If it is a 'no-specialization' run, it is applied only to loaded symbols. */ override def transformInfo(sym: Symbol, tpe: Type): Type = { if (settings.nospecialization.value && currentRun.compiles(sym)) tpe - else tpe match { - case PolyType(targs, ClassInfoType(base, decls, clazz)) - if clazz != RepeatedParamClass - && clazz != JavaRepeatedParamClass - && !clazz.isJavaDefined => - val parents = base map specializedType - debuglog("transformInfo (poly) " + clazz + " with parents1: " + parents + " ph: " + phase) - - polyType(targs, ClassInfoType( - parents, - new Scope(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz)), - clazz) - ) - case ClassInfoType(base, decls, clazz) if !clazz.isPackageClass && !clazz.isJavaDefined => - atPhase(phase.next)(base map (_.typeSymbol.info)) - // side effecting? parents is not used except to log. - val parents = base map specializedType - debuglog("transformInfo " + clazz + " with parents1: " + parents + " ph: " + phase) - ClassInfoType( - base map specializedType, - new Scope(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz)), - clazz + else tpe.resultType match { + case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) => + val tparams = tpe.typeParams + if (tparams.isEmpty) + atPhase(phase.next)(parents map (_.typeSymbol.info)) + + val parents1 = parents map specializedType + debuglog("transformInfo %s %s with parents1 %s ph: %s".format( + if (tparams.nonEmpty) " (poly)" else "", + clazz, parents1, phase) ) + val newScope = new Scope(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz)) + // If tparams.isEmpty, this is just the ClassInfoType. + polyType(tparams, ClassInfoType(parents1, newScope, clazz)) case _ => tpe } @@ -1292,9 +1289,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else None } - def maybeTypeApply(fun: Tree, targs: List[Tree]) = - if (targs.isEmpty) fun else TypeApply(fun, targs) - curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => @@ -1333,7 +1327,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { "residual: %s, tparams: %s, env: %s".format(residualTargs, symbol.info.typeParams, env)) ) - val tree1 = maybeTypeApply(Select(qual1, specMember), residualTargs) + val tree1 = gen.mkTypeApply(Select(qual1, specMember), residualTargs) log("rewrote " + tree + " to " + tree1) localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index c5237b4d59..50ebb2b3a5 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -306,7 +306,7 @@ abstract class UnCurry extends InfoTransform else if (IntClass.tpe <:< a.tpe) Literal(Constant(0)) else if (LongClass.tpe <:< a.tpe) Literal(Constant(0L)) else if (CharClass.tpe <:< a.tpe) Literal(Constant(0.toChar)) - else NULL AS a.tpe // must cast, at least when a.tpe <:< NothingClass.tpe + else gen.mkCast(NULL, a.tpe) // must cast, at least when a.tpe <:< NothingClass.tpe Apply(fun.duplicate, List(zero)) case _ => super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index b67198a292..a355085246 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1081,12 +1081,10 @@ trait Implicits { manifestFactoryCall("arrayType", args.head, findManifest(args.head)) } else if (sym.isClass) { val classarg0 = gen.mkClassOf(tp1) - val classarg = ( - if (tp.isInstanceOf[ExistentialType]) - TypeApply(Select(classarg0, Any_asInstanceOf), List(TypeTree(ClassType(tp)))) - else - classarg0 - ) + val classarg = tp match { + case _: ExistentialType => gen.mkCast(classarg0, ClassType(tp)) + case _ => classarg0 + } val suffix = classarg :: (args map findSubManifest) manifestFactoryCall( "classType", tp, diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 3591732c70..cf8c0c596c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -282,7 +282,7 @@ trait SyntheticMethods extends ast.TreeDSL { def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => val arg0 = methodArg(m, 0) val thatTest = gen.mkIsInstanceOf(arg0, clazzTypeToTest(clazz), true, false) - val thatCast = arg0 AS_ATTR clazz.tpe + val thatCast = gen.mkCast(arg0, clazz.tpe) def argsBody: Tree = { val otherName = context.unit.freshTermName(clazz.name + "$") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 65eb6466df..41e0819fe2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1913,12 +1913,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!context.savedTypeBounds.isEmpty) { body1.tpe = context.restoreTypeBounds(body1.tpe) if (isFullyDefined(pt) && !(body1.tpe <:< pt)) { - body1 = - typed { - atPos(body1.pos) { - TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt))) // @M no need for pt.normalize here, is done in erasure - } - } + // @M no need for pt.normalize here, is done in erasure + body1 = typedPos(body1.pos)(gen.mkCast(body1, pt)) } } // body1 = checkNoEscaping.locals(context.scope, pt, body1) diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index 3e5dae739d..64793fb303 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -496,7 +496,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => } /** Explicit type application. - */ + * @PP: All signs point toward it being a requirement that args.nonEmpty, + * but I can't find that explicitly stated anywhere. Unless your last name + * is odersky, you should probably treat it as true. + */ case class TypeApply(fun: Tree, args: List[Tree]) extends GenericApply { override def symbol: Symbol = fun.symbol |