/* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Iulian Dragos */ package scala package tools.nsc package transform import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } import scala.annotation.tailrec /** Specialize code on types. * * Make sure you've read the thesis: * * Iulian Dragos: Compiling Scala for Performance (chapter 4) * * There are some things worth noting, (possibly) not mentioned there: * 0) Make sure you understand the meaning of various `SpecializedInfo` descriptors * defined below. * * 1) Specializing traits by introducing bridges in specialized methods * of the specialized trait may introduce problems during mixin composition. * Concretely, it may cause cyclic calls and result in a stack overflow. * See ticket #4351. * This was solved by introducing an `Abstract` specialized info descriptor. * Instead of generating a bridge in the trait, an abstract method is generated. * * 2) Specialized private members sometimes have to be switched to protected. * In some cases, even this is not enough. Example: * * {{{ * class A[@specialized T](protected val d: T) { * def foo(that: A[T]) = that.d * } * }}} * * Specialization will generate a specialized class and a specialized method: * * {{{ * class A$mcI$sp(protected val d: Int) extends A[Int] { * def foo(that: A[Int]) = foo$mcI$sp(that) * def foo(that: A[Int]) = that.d * } * }}} * * Above, `A$mcI$sp` cannot access `d`, so the method cannot be typechecked. */ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { import global._ import definitions._ import Flags._ private val inlineFunctionExpansion = settings.Ydelambdafy.value == "inline" /** the name of the phase: */ val phaseName: String = "specialize" /** The following flags may be set by this phase: */ override def phaseNewFlags: Long = notPRIVATE /** This phase changes base classes. */ override def changesBaseClasses = true override def keepsTypeParams = true type TypeEnv = immutable.Map[Symbol, Type] def emptyEnv: TypeEnv = Map[Symbol, Type]() private implicit val typeOrdering: Ordering[Type] = Ordering[String] on ("" + _.typeSymbol.name) /** TODO - this is a lot of maps. */ /** For a given class and concrete type arguments, give its specialized class */ val specializedClass = perRunCaches.newMap[(Symbol, TypeEnv), Symbol] /** Map a method symbol to a list of its specialized overloads in the same class. */ private val overloads = perRunCaches.newMap[Symbol, List[Overload]]() withDefaultValue Nil /** Map a symbol to additional information on specialization. */ private val info = perRunCaches.newMap[Symbol, SpecializedInfo]() /** Map class symbols to the type environments where they were created. */ private val typeEnv = perRunCaches.newMap[Symbol, TypeEnv]() withDefaultValue emptyEnv // Key: a specialized class or method // Value: a map from tparams in the original class to tparams in the specialized class. private val anyrefSpecCache = perRunCaches.newMap[Symbol, mutable.Map[Symbol, Symbol]]() // holds mappings from members to the type variables in the class // that they were already specialized for, so that they don't get // specialized twice (this is for AnyRef specializations) private val wasSpecializedForTypeVars = perRunCaches.newMap[Symbol, Set[Symbol]]() withDefaultValue Set() /** Concrete methods that use a specialized type, or override such methods. */ private val concreteSpecMethods = perRunCaches.newWeakSet[Symbol]() private def specializedOn(sym: Symbol): List[Symbol] = { val GroupOfSpecializable = currentRun.runDefinitions.GroupOfSpecializable sym getAnnotation SpecializedClass match { case Some(AnnotationInfo(_, Nil, _)) => specializableTypes.map(_.typeSymbol) case Some(ann @ AnnotationInfo(_, args, _)) => { args map (_.tpe) flatMap { tp => tp baseType GroupOfSpecializable match { case TypeRef(_, GroupOfSpecializable, arg :: Nil) => arg.typeArgs map (_.typeSymbol) case _ => tp.typeSymbol :: Nil } } } case _ => Nil } } @annotation.tailrec private def findSymbol[T](candidates: List[T], f: T => Symbol): Symbol = { if (candidates.isEmpty) NoSymbol else f(candidates.head) match { case NoSymbol => findSymbol(candidates.tail, f) case sym => sym } } private def hasNewParents(tree: Tree) = { val parents = tree.symbol.info.parents val prev = enteringPrevPhase(tree.symbol.info.parents) (parents != prev) && { debuglog(s"$tree parents changed from: $prev to: $parents") true } } // If we replace `isBoundedGeneric` with (tp <:< AnyRefTpe), // then pos/spec-List.scala fails - why? Does this kind of check fail // for similar reasons? Does `sym.isAbstractType` make a difference? private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = { specializedOn(sym).exists(s => !isPrimitiveValueClass(s)) && !isPrimitiveValueClass(tp.typeSymbol) && isBoundedGeneric(tp) //(tp <:< AnyRefTpe) } object TypeEnv { /** Return a new type environment binding specialized type parameters of sym to * the given args. Expects the lists to have the same length. */ def fromSpecialization(sym: Symbol, args: List[Type]): TypeEnv = { ifDebug(assert(sym.info.typeParams.length == args.length, sym + " args: " + args)) emptyEnv ++ collectMap2(sym.info.typeParams, args)((k, v) => k.isSpecialized) } /** Does typeenv `t1` include `t2`? All type variables in `t1` * are defined in `t2` and: * - are bound to the same type, or * - are an AnyRef specialization and `t2` is bound to a subtype of AnyRef */ def includes(t1: TypeEnv, t2: TypeEnv) = t1 forall { case (sym, tpe) => t2 get sym exists { t2tp => (tpe == t2tp) || !(isPrimitiveValueType(tpe) || isPrimitiveValueType(t2tp)) // u.t.b. (t2tp <:< AnyRefTpe) } } /** Reduce the given environment to contain mappings only for type variables in tps. */ def restrict(env: TypeEnv, tps: immutable.Set[Symbol]): TypeEnv = env.filterKeys(tps).toMap /** Is the given environment a valid specialization for sym? * It is valid if each binding is from a @specialized type parameter in sym (or its owner) * to a type for which `sym` is specialized. */ def isValid(env: TypeEnv, sym: Symbol): Boolean = { env forall { case (tvar, tpe) => tvar.isSpecialized && (concreteTypes(tvar) contains tpe) && { (sym.typeParams contains tvar) || (sym.owner != rootMirror.RootClass && (sym.owner.typeParams contains tvar)) } } } } case class Overload(sym: Symbol, env: TypeEnv) { override def toString = "specialized overload " + sym + " in " + env def matchesSym(sym1: Symbol) = sym.info =:= sym1.info def matchesEnv(env1: TypeEnv) = TypeEnv.includes(env, env1) } private def newOverload(method: Symbol, specializedMethod: Symbol, env: TypeEnv) = { assert(!specializedMethod.isOverloaded, specializedMethod.defString) val om = Overload(specializedMethod, env) overloads(method) ::= om om } /** Just to mark uncheckable */ override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = new SpecializationPhase(prev) class SpecializationPhase(prev: scala.tools.nsc.Phase) extends super.Phase(prev) { override def checkable = false } protected def newTransformer(unit: CompilationUnit): Transformer = new SpecializationTransformer(unit) abstract class SpecializedInfo { def target: Symbol /** Are type bounds of @specialized type parameters of 'target' now in 'env'? */ def typeBoundsIn(env: TypeEnv) = false /** A degenerated method has @specialized type parameters that appear only in * type bounds of other @specialized type parameters (and not in its result type). */ def degenerate = false } /** Symbol is a special overloaded method of 'original', in the environment env. */ case class SpecialOverload(original: Symbol, env: TypeEnv) extends SpecializedInfo { def target = original } /** Symbol is a method that should be forwarded to 't' */ case class Forward(t: Symbol) extends SpecializedInfo { def target = t } /** Symbol is a specialized abstract method, either specialized or original. The original `t` is abstract. */ case class Abstract(t: Symbol) extends SpecializedInfo { def target = t } /** Symbol is a special overload of the super accessor. */ case class SpecialSuperAccessor(t: Symbol) extends SpecializedInfo { def target = t } /** Symbol is a specialized accessor for the `target` field. */ case class SpecializedAccessor(target: Symbol) extends SpecializedInfo { } /** Symbol is a specialized method whose body should be the target's method body. */ case class Implementation(target: Symbol) extends SpecializedInfo /** Symbol is a specialized override paired with `target`. */ case class SpecialOverride(target: Symbol) extends SpecializedInfo /** A specialized inner class that specializes original inner class `target` on a type parameter of the enclosing class, in the typeenv `env`. */ case class SpecializedInnerClass(target: Symbol, env: TypeEnv) extends SpecializedInfo /** Symbol is a normalized member obtained by specializing 'target'. */ case class NormalizedMember(target: Symbol) extends SpecializedInfo { /** Type bounds of a @specialized type var are now in the environment. */ override def typeBoundsIn(env: TypeEnv): Boolean = { target.info.typeParams exists { tvar => tvar.isSpecialized && (specializedTypeVars(tvar.info.bounds) exists env.isDefinedAt) } } override lazy val degenerate = { val stvTypeParams = specializedTypeVars(target.info.typeParams map (_.info)) val stvResult = specializedTypeVars(target.info.resultType) debuglog("degenerate: " + target + " stv tparams: " + stvTypeParams + " stv info: " + stvResult) (stvTypeParams -- stvResult).nonEmpty } } /** Has `clazz` any type parameters that need be specialized? */ def hasSpecializedParams(clazz: Symbol) = clazz.info.typeParams exists (_.isSpecialized) /** Return specialized type parameters. */ def specializedParams(sym: Symbol): List[Symbol] = sym.info.typeParams filter (_.isSpecialized) /** Given an original class symbol and a list of types its type parameters are instantiated at * returns a list of type parameters that should remain in the TypeRef when instantiating a * specialized type. */ def survivingArgs(sym: Symbol, args: List[Type]): List[Type] = for ((tvar, tpe) <- sym.info.typeParams.zip(args) if !tvar.isSpecialized || !isPrimitiveValueType(tpe)) yield tpe /** Is `member` potentially affected by specialization? This is a gross overapproximation, * but it should be okay for use outside of specialization. */ def possiblySpecialized(sym: Symbol) = specializedTypeVars(sym).nonEmpty /** Refines possiblySpecialized taking into account the instantiation of the specialized type variables at `site` */ def isSpecializedIn(sym: Symbol, site: Type) = specializedTypeVars(sym) exists { tvar => val concretes = concreteTypes(tvar) (concretes contains AnyRefClass) || (concretes contains site.memberType(tvar)) } val specializedType = new TypeMap { override def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) if args.nonEmpty => val pre1 = this(pre) // when searching for a specialized class, take care to map all // type parameters that are subtypes of AnyRef to AnyRef val args1 = map2(args, sym.info.typeParams)((tp, orig) => if (isSpecializedAnyRefSubtype(tp, orig)) AnyRefTpe else tp ) specializedClass.get((sym, TypeEnv.fromSpecialization(sym, args1))) match { case Some(sym1) => typeRef(pre1, sym1, survivingArgs(sym, args)) case None => typeRef(pre1, sym, args) } case _ => tp } } def specializedFunctionName(sym: Symbol, args: List[Type]) = exitingSpecialize { require(isFunctionSymbol(sym), sym) val env: TypeEnv = TypeEnv.fromSpecialization(sym, args) specializedClass.get((sym, env)) match { case Some(x) => x.name case None => sym.name } } /** Return the specialized name of 'sym' in the given environment. It * guarantees the same result regardless of the map order by sorting * type variables alphabetically. * * !!! Is this safe in the face of the following? * scala> trait T { def foo[A] = 0}; object O extends T { override def foo[B] = 0 } */ private def specializedName(sym: Symbol, env: TypeEnv): TermName = { val tvars = ( if (sym.isClass) env.keySet else specializedTypeVars(sym).intersect(env.keySet) ) specializedName(sym.name, tvars, env) } private def specializedName(name: Name, tvars: immutable.Set[Symbol], env: TypeEnv): TermName = { val (methparams, others) = tvars.toList sortBy ("" + _.name) partition (_.owner.isMethod) // debuglog("specName(" + sym + ") env: " + env + " tvars: " + tvars) specializedName(name, methparams map env, others map env) } /** Specialize name for the two list of types. The first one denotes * specialization on method type parameters, the second on outer environment. */ private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = ( if (name == nme.CONSTRUCTOR || (types1.isEmpty && types2.isEmpty)) name.toTermName else if (nme.isSetterName(name)) specializedName(name.getterName, types1, types2).setterName else if (nme.isLocalName(name)) specializedName(name.getterName, types1, types2).localName else { val (base, cs, ms) = nme.splitSpecializedName(name) newTermName(base.toString + "$" + "m" + ms + types1.map(t => abbrvTag(t.typeSymbol)).mkString("", "", "") + "c" + cs + types2.map(t => abbrvTag(t.typeSymbol)).mkString("", "", "$sp")) } ) lazy val specializableTypes = ScalaValueClasses.map(_.tpe).sorted /** If the symbol is the companion of a value class, the value class. * Otherwise, AnyRef. */ def specializesClass(sym: Symbol): Symbol = { val c = sym.companionClass if (isPrimitiveValueClass(c)) c else AnyRefClass } /** Return the types `sym` should be specialized at. This may be some of the primitive types * or AnyRef. AnyRef means that a new type parameter T will be generated later, known to be a * subtype of AnyRef (T <: AnyRef). * These are in a meaningful order for stability purposes. */ def concreteTypes(sym: Symbol): List[Type] = { val types = if (!sym.isSpecialized) Nil // no @specialized Annotation else specializedOn(sym).map(s => specializesClass(s).tpe).sorted if (isBoundedGeneric(sym.tpe) && (types contains AnyRefClass)) reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefTpe + ".") types } /** Return a list of all type environments for all specializations * of @specialized types in `tps`. */ private def specializations(tps: List[Symbol]): List[TypeEnv] = { // the keys in each TypeEnv val keys: List[Symbol] = tps filter (_.isSpecialized) // creating each permutation of concrete types def loop(ctypes: List[List[Type]]): List[List[Type]] = ctypes match { case Nil => Nil case set :: Nil => set map (_ :: Nil) case set :: sets => for (x <- set ; xs <- loop(sets)) yield x :: xs } // zip the keys with each permutation to create a TypeEnv. // If we don't exclude the "all AnyRef" specialization, we will // incur duplicate members and crash during mixin. loop(keys map concreteTypes) filterNot (_ forall (_ <:< AnyRefTpe)) map (xss => Map(keys zip xss: _*)) } /** Does the given 'sym' need to be specialized in the environment 'env'? * Specialization is needed for * - members with specialized type parameters found in the given environment * - constructors of specialized classes * - normalized members whose type bounds appear in the environment * But suppressed for: * - any member with the @unspecialized annotation, or which has an * enclosing member with the annotation. */ private def needsSpecialization(env: TypeEnv, sym: Symbol): Boolean = ( !hasUnspecializableAnnotation(sym) && ( specializedTypeVars(sym).intersect(env.keySet).diff(wasSpecializedForTypeVars(sym)).nonEmpty || sym.isClassConstructor && (sym.enclClass.typeParams exists (_.isSpecialized)) || isNormalizedMember(sym) && info(sym).typeBoundsIn(env) ) ) private def hasUnspecializableAnnotation(sym: Symbol): Boolean = sym.ownerChain.exists(_ hasAnnotation UnspecializedClass) def isNormalizedMember(m: Symbol) = m.isSpecialized && (info get m exists { case NormalizedMember(_) => true case _ => false }) def specializedTypeVars(tpes: List[Type]): immutable.Set[Symbol] = { @tailrec def loop(result: immutable.Set[Symbol], xs: List[Type]): immutable.Set[Symbol] = { if (xs.isEmpty) result else loop(result ++ specializedTypeVars(xs.head), xs.tail) } loop(immutable.Set.empty, tpes) } def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = ( if (neverHasTypeParameters(sym)) immutable.Set.empty else enteringTyper(specializedTypeVars(sym.info)) ) /** Return the set of @specialized type variables mentioned by the given type. * It only counts type variables that appear: * - naked * - as arguments to type constructors in @specialized positions * (arrays are considered as Array[@specialized T]) */ def specializedTypeVars(tpe: Type): immutable.Set[Symbol] = tpe match { case TypeRef(pre, sym, args) => if (sym.isAliasType) specializedTypeVars(tpe.dealiasWiden) else if (sym.isTypeParameter && sym.isSpecialized || (sym.isTypeSkolem && sym.deSkolemize.isSpecialized)) Set(sym) else if (sym == ArrayClass) specializedTypeVars(args) else if (args.isEmpty) Set() else specializedTypeVars(sym.typeParams zip args collect { case (tp, arg) if tp.isSpecialized => arg }) case PolyType(tparams, resTpe) => specializedTypeVars(resTpe :: mapList(tparams)(symInfo)) // OPT // since this method may be run at phase typer (before uncurry, where NMTs are eliminated) case NullaryMethodType(resTpe) => specializedTypeVars(resTpe) case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: mapList(argSyms)(symTpe)) // OPT case ExistentialType(_, res) => specializedTypeVars(res) case AnnotatedType(_, tp) => specializedTypeVars(tp) case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil) case RefinedType(parents, _) => parents.flatMap(specializedTypeVars).toSet case _ => immutable.Set.empty } /** Returns the type parameter in the specialized class `sClass` that corresponds to type parameter * `tparam` in the original class. It will create it if needed or use the one from the cache. */ private def typeParamSubAnyRef(tparam: Symbol, sClass: Symbol): Type = { val sClassMap = anyrefSpecCache.getOrElseUpdate(sClass, mutable.Map[Symbol, Symbol]()) sClassMap.getOrElseUpdate(tparam, tparam.cloneSymbol(sClass, tparam.flags, tparam.name append tpnme.SPECIALIZED_SUFFIX) modifyInfo (info => TypeBounds(info.bounds.lo, AnyRefTpe)) ).tpe } /** Cleans the anyrefSpecCache of all type parameter symbols of a class. */ private def cleanAnyRefSpecCache(clazz: Symbol, decls: List[Symbol]) { // remove class type parameters and those of normalized members. clazz :: decls foreach (anyrefSpecCache remove _) } /** Type parameters that survive when specializing in the specified environment. */ def survivingParams(params: List[Symbol], env: TypeEnv) = params filter { p => !p.isSpecialized || !env.contains(p) || !isPrimitiveValueType(env(p)) } /** Produces the symbols from type parameters `syms` of the original owner, * in the given type environment `env`. The new owner is `nowner`. * * Non-specialized type parameters are cloned into new ones. * Type parameters specialized on AnyRef have preexisting symbols. * * For instance, a @specialized(AnyRef) T, will become T$sp <: AnyRef. */ def produceTypeParameters(syms: List[Symbol], nowner: Symbol, env: TypeEnv) = { val cloned = for (s <- syms) yield if (!env.contains(s)) s.cloneSymbol(nowner) else env(s).typeSymbol // log("producing type params: " + cloned.map(t => (t, t.tpe.bounds.hi))) foreach2(syms, cloned) { (orig, cln) => cln.removeAnnotation(SpecializedClass) if (env.contains(orig)) cln modifyInfo (info => TypeBounds(info.bounds.lo, AnyRefTpe)) } cloned map (_ substInfo (syms, cloned)) } /** Maps AnyRef bindings from a raw environment (holding AnyRefs) into type parameters from * the specialized symbol (class (specialization) or member (normalization)), leaves everything else as-is. */ private def mapAnyRefsInSpecSym(env: TypeEnv, origsym: Symbol, specsym: Symbol): TypeEnv = env map { case (sym, AnyRefTpe) if sym.owner == origsym => (sym, typeParamSubAnyRef(sym, specsym)) case x => x } /** Maps AnyRef bindings from a raw environment (holding AnyRefs) into type parameters from * the original class, leaves everything else as-is. */ private def mapAnyRefsInOrigCls(env: TypeEnv, origcls: Symbol): TypeEnv = env map { case (sym, AnyRefTpe) if sym.owner == origcls => (sym, sym.tpe) case x => x } /** Specialize 'clazz', in the environment `outerEnv`. The outer * environment contains bindings for specialized types of enclosing * classes. * * A class C is specialized w.r.t to its own specialized type params * `stps`, by specializing its members, and creating a new class for * each combination of `stps`. */ def specializeClass(clazz: Symbol, outerEnv: TypeEnv): List[Symbol] = { def specializedClass(env0: TypeEnv, normMembers: List[Symbol]): Symbol = { /* It gets hard to follow all the clazz and cls, and specializedClass * was both already used for a map and mucho long. So "sClass" is the * specialized subclass of "clazz" throughout this file. */ // SI-5545: Eliminate classes with the same name loaded from the bytecode already present - all we need to do is // to force .info on them, as their lazy type will be evaluated and the symbols will be eliminated. Unfortunately // evaluating the info after creating the specialized class will mess the specialized class signature, so we'd // better evaluate it before creating the new class symbol val clazzName = specializedName(clazz, env0).toTypeName val bytecodeClazz = clazz.owner.info.decl(clazzName) // debuglog("Specializing " + clazz + ", but found " + bytecodeClazz + " already there") bytecodeClazz.info val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) sClass.setAnnotations(clazz.annotations) // SI-8574 important that the subclass picks up @SerialVersionUID, @strictfp, etc. def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) = member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED), newName) sClass.associatedFile = clazz.sourceFile currentRun.symSource(sClass) = clazz.sourceFile // needed later on by mixin val env = mapAnyRefsInSpecSym(env0, clazz, sClass) typeEnv(sClass) = env this.specializedClass((clazz, env0)) = sClass val decls1 = newScope // declarations of the newly specialized class 'sClass' var oldClassTParams: List[Symbol] = Nil // original unspecialized type parameters var newClassTParams: List[Symbol] = Nil // unspecialized type parameters of 'specializedClass' (cloned) // has to be a val in order to be computed early. It is later called // within 'enteringPhase(next)', which would lead to an infinite cycle otherwise val specializedInfoType: Type = { oldClassTParams = survivingParams(clazz.info.typeParams, env) newClassTParams = produceTypeParameters(oldClassTParams, sClass, env) map subst(env) // log("new tparams " + newClassTParams.zip(newClassTParams map {s => (s.tpe, s.tpe.bounds.hi)}) + ", in env: " + env) def applyContext(tpe: Type) = subst(env, tpe).instantiateTypeParams(oldClassTParams, newClassTParams map (_.tpe)) /* Return a list of specialized parents to be re-mixed in a specialized subclass. * Assuming env = [T -> Int] and * class Integral[@specialized T] extends Numeric[T] * and Numeric[U] is specialized on U, this produces List(Numeric$mcI). * * so that class Integral$mci extends Integral[Int] with Numeric$mcI. */ def specializedParents(parents: List[Type]): List[Type] = { var res: List[Type] = Nil // log(specializedClass + ": seeking specialized parents of class with parents: " + parents.map(_.typeSymbol)) for (p <- parents) { val stp = exitingSpecialize(specializedType(p)) if (stp != p) if (p.typeSymbol.isTrait) res ::= stp else if (currentRun.compiles(clazz)) reporter.warning(clazz.pos, p.typeSymbol + " must be a trait. Specialized version of " + clazz + " will inherit generic " + p) // TODO change to error } res } var parents = List(applyContext(enteringTyper(clazz.tpe_*))) // log("!!! Parents: " + parents + ", sym: " + parents.map(_.typeSymbol)) if (parents.head.typeSymbol.isTrait) parents = parents.head.parents.head :: parents val extraSpecializedMixins = specializedParents(clazz.info.parents map applyContext) if (extraSpecializedMixins.nonEmpty) debuglog("extra specialized mixins for %s: %s".format(clazz.name.decode, extraSpecializedMixins.mkString(", "))) // If the class being specialized has a self-type, the self type may // require specialization. First exclude classes whose self types have // the same type constructor as the class itself, since they will // already be covered. Then apply the current context to the self-type // as with the parents and assign it to typeOfThis. if (clazz.typeOfThis.typeConstructor ne clazz.typeConstructor) { sClass.typeOfThis = applyContext(clazz.typeOfThis) debuglog("Rewriting self-type for specialized class:\n" + " " + clazz.defStringSeenAs(clazz.typeOfThis) + "\n" + " => " + sClass.defStringSeenAs(sClass.typeOfThis) ) } GenPolyType(newClassTParams, ClassInfoType(parents ::: extraSpecializedMixins, decls1, sClass)) } exitingSpecialize(sClass setInfo specializedInfoType) val fullEnv = outerEnv ++ env /* Enter 'sym' in the scope of the current specialized class. Its type is * mapped through the active environment, binding type variables to concrete * types. The existing typeEnv for `sym` is composed with the current active * environment */ def enterMember(sym: Symbol): Symbol = { typeEnv(sym) = fullEnv ++ typeEnv(sym) // append the full environment sym modifyInfo (_.substThis(clazz, sClass).instantiateTypeParams(oldClassTParams, newClassTParams map (_.tpe))) // we remove any default parameters. At this point, they have been all // resolved by the type checker. Later on, erasure re-typechecks everything and // chokes if it finds default parameters for specialized members, even though // they are never needed. mapParamss(sym)(_ resetFlag DEFAULTPARAM) decls1 enter subst(fullEnv)(sym) } /* Create and enter in scope an overridden symbol m1 for `m` that forwards * to `om`. `om` is a fresh, special overload of m1 that is an implementation * of `m`. For example, for a * * class Foo[@specialized A] { * def m(x: A) = // m * } * , for class Foo$I extends Foo[Int], this method enters two new symbols in * the scope of Foo$I: * * def m(x: Int) = m$I(x) // m1 * def m$I(x: Int) = /adapted to env {A -> Int} // om */ def forwardToOverload(m: Symbol): Symbol = { val specMember = enterMember(cloneInSpecializedClass(m, f => (f | OVERRIDE) & ~(DEFERRED | CASEACCESSOR))) val om = specializedOverload(sClass, m, env).setFlag(OVERRIDE) val original = info.get(m) match { case Some(NormalizedMember(tg)) => tg case _ => m } info(specMember) = Forward(om) info(om) = if (original.isDeferred) Forward(original) else Implementation(original) typeEnv(om) = env ++ typeEnv(m) // add the environment for any method tparams newOverload(specMember, om, typeEnv(om)) enterMember(om) } for (m <- normMembers ; if needsSpecialization(outerEnv ++ env, m) && satisfiable(fullEnv)) { if (!m.isDeferred) addConcreteSpecMethod(m) // specialized members have to be overridable. if (m.isPrivate) m.resetFlag(PRIVATE).setFlag(PROTECTED) if (m.isConstructor) { val specCtor = enterMember(cloneInSpecializedClass(m, x => x)) info(specCtor) = Forward(m) } else if (isNormalizedMember(m)) { // methods added by normalization val NormalizedMember(original) = info(m) if (nonConflicting(env ++ typeEnv(m))) { if (info(m).degenerate) { debuglog("degenerate normalized member " + m.defString) val specMember = enterMember(cloneInSpecializedClass(m, _ & ~DEFERRED)) info(specMember) = Implementation(original) typeEnv(specMember) = env ++ typeEnv(m) } else { val om = forwardToOverload(m) debuglog("normalizedMember " + m + " om: " + om + " " + pp(typeEnv(om))) } } else debuglog("conflicting env for " + m + " env: " + env) } else if (m.isDeferred && m.isSpecialized) { // abstract methods val specMember = enterMember(cloneInSpecializedClass(m, _ | DEFERRED)) // debuglog("deferred " + specMember.fullName + " remains abstract") info(specMember) = new Abstract(specMember) // was: new Forward(specMember) { // override def target = m.owner.info.member(specializedName(m, env)) // } } else if (!sClass.isTrait && m.isMethod && !m.hasAccessorFlag) { // other concrete methods // log("other concrete " + m) forwardToOverload(m) } else if (!sClass.isTrait && m.isMethod && m.hasFlag(LAZY)) { forwardToOverload(m) } else if (m.isValue && !m.isMethod) { // concrete value definition def mkAccessor(field: Symbol, name: Name) = { val newFlags = (SPECIALIZED | m.getterIn(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR) // we rely on the super class to initialize param accessors val sym = sClass.newMethod(name.toTermName, field.pos, newFlags) info(sym) = SpecializedAccessor(field) sym } def overrideIn(clazz: Symbol, sym: Symbol) = { val newFlags = (sym.flags | OVERRIDE | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR | PARAMACCESSOR) val sym1 = sym.cloneSymbol(clazz, newFlags) sym1 modifyInfo (_ asSeenFrom (clazz.tpe, sym1.owner)) } val specVal = specializedOverload(sClass, m, env) addConcreteSpecMethod(m) specVal.asInstanceOf[TermSymbol].setAlias(m) enterMember(specVal) // create accessors if (m.isLazy) { // no getters needed (we'll specialize the compute method and accessor separately), can stay private // m.setFlag(PRIVATE) -- TODO: figure out how to leave the non-specialized lazy var private // (the implementation needs it to be visible while duplicating and retypechecking, // but it really could be private in bytecode) specVal.setFlag(PRIVATE) } else if (nme.isLocalName(m.name)) { val specGetter = mkAccessor(specVal, specVal.getterName) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getterIn(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) enterMember(origGetter) debuglog("specialize accessor in %s: %s -> %s".format(sClass.name.decode, origGetter.name.decode, specGetter.name.decode)) clazz.caseFieldAccessors.find(_.name.startsWith(m.name)) foreach { cfa => val cfaGetter = overrideIn(sClass, cfa) info(cfaGetter) = SpecializedAccessor(specVal) enterMember(cfaGetter) debuglog("override case field accessor %s -> %s".format(m.name.decode, cfaGetter.name.decode)) } if (specVal.isVariable && m.setterIn(clazz) != NoSymbol) { val specSetter = mkAccessor(specVal, specGetter.setterName) .resetFlag(STABLE) specSetter.setInfo(MethodType(specSetter.newSyntheticValueParams(List(specVal.info)), UnitTpe)) val origSetter = overrideIn(sClass, m.setterIn(clazz)) info(origSetter) = Forward(specSetter) enterMember(specSetter) enterMember(origSetter) } } else { // if there are no accessors, specialized methods will need to access this field in specialized subclasses m.resetFlag(PRIVATE) specVal.resetFlag(PRIVATE) debuglog("no accessors for %s/%s, specialized methods must access field in subclass".format( m.name.decode, specVal.name.decode)) } } else if (m.isClass) { val specClass: Symbol = cloneInSpecializedClass(m, x => x) typeEnv(specClass) = fullEnv specClass setName specializedName(specClass, fullEnv).toTypeName enterMember(specClass) debuglog("entered specialized class " + specClass.fullName) info(specClass) = SpecializedInnerClass(m, fullEnv) } } sClass } val decls1 = clazz.info.decls.toList flatMap { m: Symbol => if (m.isAnonymousClass) List(m) else { normalizeMember(m.owner, m, outerEnv) flatMap { normalizedMember => val ms = specializeMember(m.owner, normalizedMember, outerEnv, clazz.info.typeParams) // interface traits have concrete members now if (ms.nonEmpty && clazz.isTrait && clazz.isInterface) clazz.resetFlag(INTERFACE) if (normalizedMember.isMethod) { val newTpe = subst(outerEnv, normalizedMember.info) // only do it when necessary, otherwise the method type might be at a later phase already if (newTpe != normalizedMember.info) { normalizedMember updateInfo newTpe } } normalizedMember :: ms } } } val subclasses = specializations(clazz.info.typeParams) filter satisfiable subclasses foreach { env => val spc = specializedClass(env, decls1) val existing = clazz.owner.info.decl(spc.name) // a symbol for the specialized class already exists if there's a classfile for it. // keeping both crashes the compiler on test/files/pos/spec-Function1.scala if (existing != NoSymbol) clazz.owner.info.decls.unlink(existing) exitingSpecialize(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes } if (subclasses.nonEmpty) clazz.resetFlag(FINAL) cleanAnyRefSpecCache(clazz, decls1) decls1 } /** Expand member `sym` to a set of normalized members. Normalized members * are monomorphic or polymorphic only in non-specialized types. * * Given method m[@specialized T, U](x: T, y: U) it returns * m[T, U](x: T, y: U), * m$I[ U](x: Int, y: U), * m$D[ U](x: Double, y: U) * // etc. */ private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = { sym :: ( if (!sym.isMethod || enteringTyper(sym.typeParams.isEmpty)) Nil else if (sym.hasDefault) { /* Specializing default getters is useless, also see SI-7329 . */ sym.resetFlag(SPECIALIZED) Nil } else { // debuglog("normalizeMember: " + sym.fullNameAsName('.').decode) var specializingOn = specializedParams(sym) val unusedStvars = specializingOn filterNot specializedTypeVars(sym.info) // I think the last condition should be !sym.isArtifact, but that made the // compiler start warning about Tuple1.scala and Tuple2.scala claiming // their type parameters are used in non-specializable positions. Why is // unusedStvars.nonEmpty for these classes??? if (unusedStvars.nonEmpty && currentRun.compiles(sym) && !sym.isSynthetic) { reporter.warning(sym.pos, "%s %s unused or used in non-specializable positions.".format( unusedStvars.mkString("", ", ", ""), if (unusedStvars.length == 1) "is" else "are") ) unusedStvars foreach (_ removeAnnotation SpecializedClass) specializingOn = specializingOn filterNot (unusedStvars contains _) } for (env0 <- specializations(specializingOn) if needsSpecialization(env0, sym)) yield { // !!! Can't this logic be structured so that the new symbol's name is // known when the symbol is cloned? It is much cleaner not to be mutating // names after the fact. And it adds about a billion lines of // "Renaming value _1 in class Tuple2 to _1$mcZ$sp" to obscure the small // number of other (important) actual symbol renamings. val tps = survivingParams(sym.info.typeParams, env0) val specMember = sym.cloneSymbol(owner, (sym.flags | SPECIALIZED) & ~DEFERRED) // <-- this needs newName = ... val env = mapAnyRefsInSpecSym(env0, sym, specMember) val (keys, vals) = env.toList.unzip specMember setName specializedName(sym, env) // <-- but the name is calculated based on the cloned symbol // debuglog("%s normalizes to %s%s".format(sym, specMember, // if (tps.isEmpty) "" else " with params " + tps.mkString(", "))) typeEnv(specMember) = outerEnv ++ env val tps1 = produceTypeParameters(tps, specMember, env) tps1 foreach (_ modifyInfo (_.instantiateTypeParams(keys, vals))) // the cloneInfo is necessary so that method parameter symbols are cloned at the new owner val methodType = sym.info.resultType.instantiateTypeParams(keys ++ tps, vals ++ tps1.map(_.tpe)).cloneInfo(specMember) specMember setInfo GenPolyType(tps1, methodType) debuglog("%s expands to %s in %s".format(sym, specMember.name.decode, pp(env))) info(specMember) = NormalizedMember(sym) newOverload(sym, specMember, env) specMember } } ) } // concise printing of type env private def pp(env: TypeEnv): String = { env.toList.sortBy(_._1.name) map { case (k, v) => val vsym = v.typeSymbol if (k == vsym) "" + k.name else k.name + ":" + vsym.name } mkString ("env(", ", ", ")") } /** Specialize member `m` w.r.t. to the outer environment and the type * parameters of the innermost enclosing class. * * Turns 'private' into 'protected' for members that need specialization. * * Return a list of symbols that are specializations of 'sym', owned by 'owner'. */ private def specializeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv, tps: List[Symbol]): List[Symbol] = { def specializeOn(tparams: List[Symbol]): List[Symbol] = specializations(tparams) map { spec0 => val spec = mapAnyRefsInOrigCls(spec0, owner) if (sym.isPrivate) { sym.resetFlag(PRIVATE).setFlag(PROTECTED) debuglog("Set %s to private[%s]".format(sym, sym.enclosingPackage)) } val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec)) typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s } val wasSpec = wasSpecializedForTypeVars(specMember) if (wasSpec.nonEmpty) debuglog("specialized overload for %s in %s".format(specMember, pp(typeEnv(specMember)))) newOverload(sym, specMember, spec) info(specMember) = SpecialOverload(sym, typeEnv(specMember)) specMember } if (sym.isMethod) { if (hasUnspecializableAnnotation(sym)) { List() } else { val stvars = specializedTypeVars(sym) if (stvars.nonEmpty) debuglog("specialized %s on %s".format(sym.fullLocationString, stvars.map(_.name).mkString(", "))) val tps1 = if (sym.isConstructor) tps filter (sym.info.paramTypes contains _) else tps val tps2 = tps1 filter stvars if (!sym.isDeferred) addConcreteSpecMethod(sym) specializeOn(tps2) } } else Nil } /** Return the specialized overload of `m`, in the given environment. */ private def specializedOverload(owner: Symbol, sym: Symbol, env: TypeEnv, nameSymbol: Symbol = NoSymbol): Symbol = { val newFlags = (sym.flags | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR | LAZY) // this method properly duplicates the symbol's info val specname = specializedName(nameSymbol orElse sym, env) ( sym.cloneSymbol(owner, newFlags, newName = specname) modifyInfo (info => subst(env, info.asSeenFrom(owner.thisType, sym.owner))) ) } /** For each method m that overrides an inherited method m', add a special * overload method `om` that overrides the corresponding overload in the * superclass. For the following example: * * class IntFun extends Function1[Int, Int] { * def apply(x: Int): Int = .. * } * * this method will return List('apply$mcII$sp') */ private def specialOverrides(clazz: Symbol) = logResultIf[List[Symbol]]("specialized overrides in " + clazz, _.nonEmpty) { /* Return the overridden symbol in syms that needs a specialized overriding symbol, * together with its specialization environment. The overridden symbol may not be * the closest to 'overriding', in a given hierarchy. * * An method m needs a special override if * * m overrides a method whose type contains specialized type variables * * there is a valid specialization environment that maps the overridden method type to m's type. */ def needsSpecialOverride(overriding: Symbol): (Symbol, TypeEnv) = { def checkOverriddenTParams(overridden: Symbol) { foreach2(overridden.info.typeParams, overriding.info.typeParams) { (baseTvar, derivedTvar) => val missing = concreteTypes(baseTvar).toSet -- concreteTypes(derivedTvar).toSet if (missing.nonEmpty) { reporter.error(derivedTvar.pos, "Type parameter has to be specialized at least for the same types as in the overridden method. Missing " + "types: " + missing.mkString("", ", ", "") ) } } } if (!overriding.isParamAccessor) { for (overridden <- overriding.allOverriddenSymbols) { val stvars = specializedTypeVars(overridden.info) if (stvars.nonEmpty) { debuglog("specialized override of %s by %s%s".format(overridden.fullLocationString, overriding.fullLocationString, if (stvars.isEmpty) "" else stvars.map(_.name).mkString("(", ", ", ")"))) if (currentRun compiles overriding) checkOverriddenTParams(overridden) val env = unify(overridden.info, overriding.info, emptyEnv, false, true) def atNext = exitingSpecialize(overridden.owner.info.decl(specializedName(overridden, env))) if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol) { debuglog(" " + pp(env) + " found " + atNext) return (overridden, env) } } } } (NoSymbol, emptyEnv) } (clazz.info.decls flatMap { overriding => needsSpecialOverride(overriding) match { case (NoSymbol, _) => if (overriding.isSuperAccessor) { val alias = overriding.alias debuglog(s"checking special overload for super accessor: ${overriding.fullName}, alias for ${alias.fullName}") needsSpecialOverride(alias) match { case nope @ (NoSymbol, _) => None case (overridden, env) => val om = specializedOverload(clazz, overriding, env, overridden) om.setName(nme.superName(om.name)) om.asInstanceOf[TermSymbol].setAlias(info(alias).target) om.owner.info.decls.enter(om) info(om) = SpecialSuperAccessor(om) om.makeNotPrivate(om.owner) newOverload(overriding, om, env) Some(om) } } else None case (overridden, env) => val om = specializedOverload(clazz, overridden, env) clazz.info.decls.enter(om) foreachWithIndex(om.paramss) { (params, i) => foreachWithIndex(params) { (param, j) => param.name = overriding.paramss(i)(j).name // SI-6555 Retain the parameter names from the subclass. } } debuglog(s"specialized overload $om for ${overriding.name.decode} in ${pp(env)}: ${om.info}") om.setFlag(overriding.flags & (ABSOVERRIDE | SYNCHRONIZED)) om.withAnnotations(overriding.annotations.filter(_.symbol == ScalaStrictFPAttr)) typeEnv(om) = env addConcreteSpecMethod(overriding) if (overriding.isDeferred) { // abstract override debuglog("abstract override " + overriding.fullName + " with specialized " + om.fullName) info(om) = Forward(overriding) } else { // if the override is a normalized member, 'om' gets the // implementation from its original target, and adds the // environment of the normalized member (that is, any // specialized /method/ type parameter bindings) info get overriding match { case Some(NormalizedMember(target)) => typeEnv(om) = env ++ typeEnv(overriding) info(om) = Forward(target) case _ => info(om) = SpecialOverride(overriding) } info(overriding) = Forward(om setPos overriding.pos) } newOverload(overriding, om, env) ifDebug(exitingSpecialize(assert( overridden.owner.info.decl(om.name) != NoSymbol, "Could not find " + om.name + " in " + overridden.owner.info.decls)) ) Some(om) } }).toList } case object UnifyError extends scala.util.control.ControlThrowable private[this] def unifyError(tp1: Any, tp2: Any): Nothing = { log("unifyError" + ((tp1, tp2))) throw UnifyError } /** Return the most general type environment that specializes tp1 to tp2. * It only allows binding of type parameters annotated with @specialized. * Fails if such an environment cannot be found. * * If `strict` is true, a UnifyError is thrown if unification is impossible. * * If `tparams` is true, then the methods tries to unify over type params in polytypes as well. */ private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean, tparams: Boolean = false): TypeEnv = (tp1, tp2) match { case (TypeRef(_, sym1, _), _) if sym1.isSpecialized => debuglog(s"Unify $tp1, $tp2") if (isPrimitiveValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1)) env + ((sym1, tp2)) else if (isSpecializedAnyRefSubtype(tp2, sym1)) env + ((sym1, tp2)) else if (strict) unifyError(tp1, tp2) else env case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => if (args1.nonEmpty || args2.nonEmpty) debuglog(s"Unify types $tp1 and $tp2") if (strict && args1.length != args2.length) unifyError(tp1, tp2) val e = unify(args1, args2, env, strict) if (e.nonEmpty) debuglog(s"unified to: $e") e case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem => env case (MethodType(params1, res1), MethodType(params2, res2)) => if (strict && params1.length != params2.length) unifyError(tp1, tp2) debuglog(s"Unify methods $tp1 and $tp2") unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict) case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => debuglog(s"Unify polytypes $tp1 and $tp2") if (strict && tparams1.length != tparams2.length) unifyError(tp1, tp2) else if (tparams && tparams1.length == tparams2.length) unify(res1 :: tparams1.map(_.info), res2 :: tparams2.map(_.info), env, strict) else unify(res1, res2, env, strict) case (PolyType(_, res), other) => unify(res, other, env, strict) case (ThisType(_), ThisType(_)) => env case (_, SingleType(_, _)) => unify(tp1, tp2.underlying, env, strict) case (SingleType(_, _), _) => unify(tp1.underlying, tp2, env, strict) case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict) case (_, ThisType(_)) => unify(tp1, tp2.widen, env, strict) case (RefinedType(_, _), RefinedType(_, _)) => env case (AnnotatedType(_, tp1), tp2) => unify(tp2, tp1, env, strict) case (ExistentialType(_, res1), _) => unify(tp2, res1, env, strict) case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => unify(List(lo1, hi1), List(lo2, hi2), env, strict) case _ => debuglog(s"don't know how to unify $tp1 [${tp1.getClass}] with $tp2 [${tp2.getClass}]") env } private def unify(tp1: List[Type], tp2: List[Type], env: TypeEnv, strict: Boolean): TypeEnv = { if (tp1.isEmpty || tp2.isEmpty) env else (tp1 zip tp2).foldLeft(env) { (env, args) => if (!strict) unify(args._1, args._2, env, strict) else { val nenv = unify(args._1, args._2, emptyEnv, strict) if (env.keySet.intersect(nenv.keySet).isEmpty) env ++ nenv else { debuglog(s"could not unify: u(${args._1}, ${args._2}) yields $nenv, env: $env") unifyError(tp1, tp2) } } } } /** Apply the type environment 'env' to the given type. All type * bindings are supposed to be to primitive types. A type variable * that is annotated with 'uncheckedVariance' is mapped to the corresponding * primitive type losing the annotation. */ private def subst(env: TypeEnv, tpe: Type): Type = { class FullTypeMap(from: List[Symbol], to: List[Type]) extends SubstTypeMap(from, to) with AnnotationFilter { def keepAnnotation(annot: AnnotationInfo) = !(annot matches uncheckedVarianceClass) override def mapOver(tp: Type): Type = tp match { case ClassInfoType(parents, decls, clazz) => val parents1 = parents mapConserve this val decls1 = mapOver(decls) if ((parents1 eq parents) && (decls1 eq decls)) tp else ClassInfoType(parents1, decls1, clazz) case _ => super.mapOver(tp) } } val (keys, values) = env.toList.unzip (new FullTypeMap(keys, values))(tpe) } private def subst(env: TypeEnv)(decl: Symbol): Symbol = decl modifyInfo (info => if (decl.isConstructor) MethodType(subst(env, info).params, decl.owner.tpe_*) else subst(env, info) ) private def unspecializableClass(tp: Type) = ( 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 && currentRun.compiles(sym)) tpe else tpe.resultType match { case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) => val tparams = tpe.typeParams if (tparams.isEmpty) exitingSpecialize(parents map (_.typeSymbol.info)) val parents1 = parents mapConserve specializedType if (parents ne parents1) { debuglog("specialization transforms %s%s parents to %s".format( if (tparams.nonEmpty) "(poly) " else "", clazz, parents1) ) } val newScope = newScopeWith(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz): _*) // If tparams.isEmpty, this is just the ClassInfoType. GenPolyType(tparams, ClassInfoType(parents1, newScope, clazz)) case _ => tpe } } /** Is any type variable in `env` conflicting with any if its type bounds, when * type bindings in `env` are taken into account? * * A conflicting type environment could still be satisfiable. */ def nonConflicting(env: TypeEnv) = env forall { case (tvar, tpe) => (subst(env, tvar.info.bounds.lo) <:< tpe) && (tpe <:< subst(env, tvar.info.bounds.hi)) } /** The type environment is sound w.r.t. to all type bounds or only soft * conflicts appear. An environment is sound if all bindings are within * the bounds of the given type variable. A soft conflict is a binding * that does not fall within the bounds, but whose bounds contain * type variables that are @specialized, (that could become satisfiable). */ def satisfiable(env: TypeEnv): Boolean = satisfiable(env, false) def satisfiable(env: TypeEnv, warnings: Boolean): Boolean = { def matches(tpe1: Type, tpe2: Type): Boolean = { val t1 = subst(env, tpe1) val t2 = subst(env, tpe2) ((t1 <:< t2) || specializedTypeVars(t1).nonEmpty || specializedTypeVars(t2).nonEmpty) } env forall { case (tvar, tpe) => matches(tvar.info.bounds.lo, tpe) && matches(tpe, tvar.info.bounds.hi) || { if (warnings) reporter.warning(tvar.pos, s"Bounds prevent specialization of $tvar") debuglog("specvars: " + tvar.info.bounds.lo + ": " + specializedTypeVars(tvar.info.bounds.lo) + " " + subst(env, tvar.info.bounds.hi) + ": " + specializedTypeVars(subst(env, tvar.info.bounds.hi)) ) false } } } def satisfiabilityConstraints(env: TypeEnv): Option[TypeEnv] = { val noconstraints = Some(emptyEnv) def matches(tpe1: Type, tpe2: Type): Option[TypeEnv] = { val t1 = subst(env, tpe1) val t2 = subst(env, tpe2) // log("---------> " + tpe1 + " matches " + tpe2) // log(t1 + ", " + specializedTypeVars(t1)) // log(t2 + ", " + specializedTypeVars(t2)) // log("unify: " + unify(t1, t2, env, false, false) + " in " + env) if (t1 <:< t2) noconstraints else if (specializedTypeVars(t1).nonEmpty) Some(unify(t1, t2, env, false, false) -- env.keys) else if (specializedTypeVars(t2).nonEmpty) Some(unify(t2, t1, env, false, false) -- env.keys) else None } env.foldLeft[Option[TypeEnv]](noconstraints) { case (constraints, (tvar, tpe)) => val loconstraints = matches(tvar.info.bounds.lo, tpe) val hiconstraints = matches(tpe, tvar.info.bounds.hi) val allconstraints = for (c <- constraints; l <- loconstraints; h <- hiconstraints) yield c ++ l ++ h allconstraints } } /** This duplicator additionally performs casts of expressions if that is allowed by the `casts` map. */ class Duplicator(casts: Map[Symbol, Type]) extends { val global: SpecializeTypes.this.global.type = SpecializeTypes.this.global } with typechecker.Duplicators { private val (castfrom, castto) = casts.unzip private object CastMap extends SubstTypeMap(castfrom.toList, castto.toList) class BodyDuplicator(_context: Context) extends super.BodyDuplicator(_context) { override def castType(tree: Tree, pt: Type): Tree = { tree modifyType fixType // log(" tree type: " + tree.tpe) val ntree = if (tree.tpe != null && !(tree.tpe <:< pt)) { val casttpe = CastMap(tree.tpe) if (casttpe <:< pt) gen.mkCast(tree, casttpe) else if (casttpe <:< CastMap(pt)) gen.mkCast(tree, pt) else tree } else tree ntree.clearType() } } protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context) } /** Introduced to fix SI-7343: Phase ordering problem between Duplicators and Specialization. * brief explanation: specialization rewires class parents during info transformation, and * the new info then guides the tree changes. But if a symbol is created during duplication, * which runs after specialization, its info is not visited and thus the corresponding tree * is not specialized. One manifestation is the following: * ``` * object Test { * class Parent[@specialized(Int) T] * * def spec_method[@specialized(Int) T](t: T, expectedXSuper: String) = { * class X extends Parent[T]() * // even in the specialized variant, the local X class * // doesn't extend Parent$mcI$sp, since its symbol has * // been created after specialization and was not seen * // by specialization's info transformer. * ... * } * } * ``` * We fix this by forcing duplication to take place before specialization. * * Note: The constructors phase (which also uses duplication) comes after erasure and uses the * post-erasure typer => we must protect it from the beforeSpecialization phase shifting. */ class SpecializationDuplicator(casts: Map[Symbol, Type]) extends Duplicator(casts) { override def retyped(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol, env: scala.collection.Map[Symbol, Type]): Tree = enteringSpecialize(super.retyped(context, tree, oldThis, newThis, env)) } /** A tree symbol substituter that substitutes on type skolems. * If a type parameter is a skolem, it looks for the original * symbol in the 'from' and maps it to the corresponding new * symbol. The new symbol should probably be a type skolem as * well (not enforced). * * All private members are made protected in order to be accessible from * specialized classes. */ class ImplementationAdapter(from: List[Symbol], to: List[Symbol], targetClass: Symbol, addressFields: Boolean) extends TreeSymSubstituter(from, to) { override val symSubst = new SubstSymMap(from, to) { override def matches(sym1: Symbol, sym2: Symbol) = if (sym2.isTypeSkolem) sym2.deSkolemize eq sym1 else sym1 eq sym2 } private def isAccessible(sym: Symbol): Boolean = if (currentOwner.isAnonymousFunction) { if (inlineFunctionExpansion) devWarning("anonymous function made it to specialization even though inline expansion is set.") false } else (currentClass == sym.owner.enclClass) && (currentClass != targetClass) private def shouldMakePublic(sym: Symbol): Boolean = sym.hasFlag(PRIVATE | PROTECTED) && (addressFields || !nme.isLocalName(sym.name)) /** All private members that are referenced are made protected, * in order to be accessible from specialized subclasses. */ override def transform(tree: Tree): Tree = tree match { case Select(qual, name) => val sym = tree.symbol if (sym.isPrivate) debuglog( "seeing private member %s, currentClass: %s, owner: %s, isAccessible: %b, isLocalName: %b".format( sym, currentClass, sym.owner.enclClass, isAccessible(sym), nme.isLocalName(sym.name)) ) if (shouldMakePublic(sym) && !isAccessible(sym)) { debuglog(s"changing private flag of $sym") sym.makeNotPrivate(sym.owner) } super.transform(tree) case _ => super.transform(tree) } } /** Return the generic class corresponding to this specialized class. */ def originalClass(clazz: Symbol): Symbol = if (clazz.isSpecialized) { val (originalName, _, _) = nme.splitSpecializedName(clazz.name) clazz.owner.info.decl(originalName).suchThat(_.isClass) } else NoSymbol def illegalSpecializedInheritance(clazz: Symbol): Boolean = ( clazz.isSpecialized && originalClass(clazz).parentSymbols.exists(p => hasSpecializedParams(p) && !p.isTrait) ) def specializeCalls(unit: CompilationUnit) = new TypingTransformer(unit) { /** Map a specializable method to its rhs, when not deferred. */ val body = perRunCaches.newMap[Symbol, Tree]() /** Map a specializable method to its value parameter symbols. */ val parameters = perRunCaches.newMap[Symbol, List[Symbol]]() /** Collect method bodies that are concrete specialized methods. */ class CollectMethodBodies extends Traverser { override def traverse(tree: Tree) = tree match { case DefDef(_, _, _, vparams :: Nil, _, rhs) => if (concreteSpecMethods(tree.symbol) || tree.symbol.isConstructor) { // debuglog("!!! adding body of a defdef %s, symbol %s: %s".format(tree, tree.symbol, rhs)) body(tree.symbol) = rhs // body(tree.symbol) = tree // whole method parameters(tree.symbol) = vparams.map(_.symbol) concreteSpecMethods -= tree.symbol } // no need to descend further down inside method bodies case ValDef(mods, name, tpt, rhs) if concreteSpecMethods(tree.symbol) => body(tree.symbol) = rhs // log("!!! adding body of a valdef " + tree.symbol + ": " + rhs) //super.traverse(tree) case _ => super.traverse(tree) } } def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = { (treeType =:= memberType) || { // anyref specialization memberType match { case PolyType(_, resTpe) => debuglog(s"Conformance for anyref - polytype with result type: $resTpe and $treeType\nOrig. sym.: $origSymbol") try { val e = unify(origSymbol.tpe, memberType, emptyEnv, true) debuglog(s"obtained env: $e") e.keySet == env.keySet } catch { case _: Throwable => debuglog("Could not unify.") false } case _ => false } } } def reportError[T](body: =>T)(handler: TypeError => T): T = try body catch { case te: TypeError => reporter.error(te.pos, te.msg) handler(te) } override def transform(tree: Tree): Tree = reportError { transform1(tree) } {_ => tree} def transform1(tree: Tree) = { val symbol = tree.symbol /* The specialized symbol of 'tree.symbol' for tree.tpe, if there is one */ def specSym(qual: Tree): Symbol = { val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) def isMatch(member: Symbol) = { val memberType = qual.tpe memberType member val residualTreeType = tree match { case TypeApply(fun, targs) if fun.symbol == symbol => // SI-6308 Handle methods with only some type parameters specialized. // drop the specialized type parameters from the PolyType, and // substitute in the type environment. val GenPolyType(tparams, tpe) = fun.tpe val (from, to) = env.toList.unzip val residualTParams = tparams.filterNot(env.contains) GenPolyType(residualTParams, tpe).substituteTypes(from, to) case _ => tree.tpe } ( doesConform(symbol, residualTreeType, memberType, env) && TypeEnv.includes(typeEnv(member), env) ) } if (env.isEmpty) NoSymbol else qual.tpe member specializedName(symbol, env) suchThat isMatch } def matchingSymbolInPrefix(pre: Type, member: Symbol, env: TypeEnv): Symbol = { pre member specializedName(member, env) suchThat (_.tpe matches subst(env, member.tpe)) } def transformSelect(sel: Select) = { val Select(qual, name) = sel debuglog(s"specializing Select(sym=${symbol.defString}, tree.tpe=${tree.tpe})") val qual1 = transform(qual) def copySelect = treeCopy.Select(tree, qual1, name) def newSelect(member: Symbol) = atPos(tree.pos)(Select(qual1, member)) def typedOp(member: Symbol) = localTyper typedOperator newSelect(member) def typedTree(member: Symbol) = localTyper typed newSelect(member) val ignoreEnv = specializedTypeVars(symbol.info).isEmpty || name == nme.CONSTRUCTOR if (ignoreEnv) overloads(symbol) find (_ matchesSym symbol) match { case Some(Overload(member, _)) => typedOp(member) case _ => copySelect } else { val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) overloads(symbol) find (_ matchesEnv env) match { case Some(Overload(member, _)) => typedOp(member) case _ => matchingSymbolInPrefix(qual1.tpe, symbol, env) match { case NoSymbol => copySelect case member if member.isMethod => typedOp(member) case member => typedTree(member) } } } } /** Computes residual type parameters after rewiring, like "String" in the following example: * ``` * def specMe[@specialized T, U](t: T, u: U) = ??? * specMe[Int, String](1, "2") => specMe$mIc$sp[String](1, "2") * ``` */ def computeResidualTypeVars(baseTree: Tree, specMember: Symbol, specTree: Tree, baseTargs: List[Tree], env: TypeEnv): Tree = { val residualTargs = symbol.info.typeParams zip baseTargs collect { case (tvar, targ) if !env.contains(tvar) || !isPrimitiveValueClass(env(tvar).typeSymbol) => targ } ifDebug(assert(residualTargs.length == specMember.info.typeParams.length, "residual: %s, tparams: %s, env: %s".format(residualTargs, specMember.info.typeParams, env)) ) val tree1 = gen.mkTypeApply(specTree, residualTargs) debuglog(s"rewrote $tree to $tree1") localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method } curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => def transformNew = { debuglog(s"Attempting to specialize new $tpt(${args.mkString(", ")})") val found = specializedType(tpt.tpe) if (found.typeSymbol ne tpt.tpe.typeSymbol) { // the ctor can be specialized val inst = New(found, transformTrees(args): _*) reportError(localTyper.typedPos(tree.pos)(inst))(_ => super.transform(tree)) } else super.transform(tree) } transformNew case Apply(sel @ Select(sup @ Super(qual, name), name1), args) if hasNewParents(sup) => def transformSuperApply = { val sup1 = Super(qual, name) setPos sup.pos val tree1 = Apply(Select(sup1, name1) setPos sel.pos, transformTrees(args)) val res = localTyper.typedPos(tree.pos)(tree1) debuglog(s"retyping call to super, from: $symbol to ${res.symbol}") res } transformSuperApply // This rewires calls to specialized methods defined in a class (which have a receiver) // class C { // def foo[@specialized T](t: T): T = t // C.this.foo(3) // TypeApply(Select(This(C), foo), List(Int)) => C.this.foo$mIc$sp(3) // } case TypeApply(sel @ Select(qual, name), targs) if (specializedTypeVars(symbol.info).nonEmpty && name != nme.CONSTRUCTOR) => debuglog("checking typeapp for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe) val qual1 = transform(qual) log(">>> TypeApply: " + tree + ", qual1: " + qual1) specSym(qual1) match { case NoSymbol => // See pos/exponential-spec.scala - can't call transform on the whole tree again. treeCopy.TypeApply(tree, treeCopy.Select(sel, qual1, name), transformTrees(targs)) case specMember => debuglog("found " + specMember.fullName) ifDebug(assert(symbol.info.typeParams.length == targs.length, symbol.info.typeParams + " / " + targs)) val env = typeEnv(specMember) computeResidualTypeVars(tree, specMember, gen.mkAttributedSelect(qual1, specMember), targs, env) } // This rewires calls to specialized methods defined in the local scope. For example: // def outerMethod = { // def foo[@specialized T](t: T): T = t // foo(3) // TypeApply(Ident(foo), List(Int)) => foo$mIc$sp(3) // } case TypeApply(sel @ Ident(name), targs) if name != nme.CONSTRUCTOR => val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) if (env.isEmpty) super.transform(tree) else { overloads(symbol) find (_ matchesEnv env) match { case Some(Overload(specMember, _)) => computeResidualTypeVars(tree, specMember, Ident(specMember), targs, env) case _ => super.transform(tree) } } case Select(Super(_, _), _) if illegalSpecializedInheritance(currentClass) => val pos = tree.pos debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.\n" + pos.lineContent) tree case sel @ Select(_, _) => transformSelect(sel) case PackageDef(pid, stats) => tree.symbol.info // make sure specializations have been performed atOwner(tree, symbol) { val specMembers = implSpecClasses(stats) map localTyper.typed treeCopy.PackageDef(tree, pid, transformStats(stats ::: specMembers, symbol.moduleClass)) } case Template(parents, self, body) => def transformTemplate = { val specMembers = makeSpecializedMembers(tree.symbol.enclClass) ::: (implSpecClasses(body) map localTyper.typed) if (!symbol.isPackageClass) (new CollectMethodBodies)(tree) val parents1 = map2(currentOwner.info.parents, parents)((tpe, parent) => TypeTree(tpe) setPos parent.pos) treeCopy.Template(tree, parents1 /*currentOwner.info.parents.map(tpe => TypeTree(tpe) setPos parents.head.pos)*/ , self, atOwner(currentOwner)(transformTrees(body ::: specMembers))) } transformTemplate case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) => def transformDefDef = { if (symbol.isConstructor) { val t = atOwner(symbol)(forwardCtorCall(tree.pos, gen.mkSuperInitCall, vparamss, symbol.owner)) if (symbol.isPrimaryConstructor) localTyper.typedPos(symbol.pos)(deriveDefDef(tree)(_ => Block(List(t), Literal(Constant(()))))) else // duplicate the original constructor reportError(duplicateBody(ddef, info(symbol).target))(_ => ddef) } else info(symbol) match { case Implementation(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) // we have an rhs, specialize it val tree1 = reportError(duplicateBody(ddef, target))(_ => ddef) debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) case NormalizedMember(target) => logResult("constraints")(satisfiabilityConstraints(typeEnv(symbol))) match { case Some(constraint) if !target.isDeferred => // we have an rhs, specialize it val tree1 = reportError(duplicateBody(ddef, target, constraint))(_ => ddef) debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) case _ => deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) } case SpecialOverride(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) //debuglog("moving implementation, body of target " + target + ": " + body(target)) log("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor)) // we have an rhs, specialize it val tree1 = addBody(ddef, target) (new ChangeOwnerTraverser(target, tree1.symbol))(tree1.rhs) debuglog("changed owners, now: " + tree1) deriveDefDef(tree1)(transform) case SpecialOverload(original, env) => debuglog("completing specialized " + symbol.fullName + " calling " + original) debuglog("special overload " + original + " -> " + env) val t = DefDef(symbol, { vparamss: List[List[Symbol]] => val fun = Apply(Select(This(symbol.owner), original), makeArguments(original, vparamss.head)) debuglog("inside defdef: " + symbol + "; type: " + symbol.tpe + "; owner: " + symbol.owner) gen.maybeMkAsInstanceOf(fun, symbol.owner.thisType.memberType(symbol).finalResultType, symbol.owner.thisType.memberType(original).finalResultType) }) debuglog("created special overload tree " + t) debuglog("created " + t) reportError { localTyper.typed(t) } { _ => super.transform(tree) } case fwd @ Forward(_) => debuglog("forward: " + fwd + ", " + ddef) val rhs1 = forwardCall(tree.pos, gen.mkAttributedRef(symbol.owner.thisType, fwd.target), vparamss) debuglog("-->d completed forwarder to specialized overload: " + fwd.target + ": " + rhs1) reportError { localTyper.typed(deriveDefDef(tree)(_ => rhs1)) } { _ => super.transform(tree) } case SpecializedAccessor(target) => val rhs1 = if (symbol.isGetter) gen.mkAttributedRef(target) else Assign(gen.mkAttributedRef(target), Ident(vparamss.head.head.symbol)) debuglog("specialized accessor: " + target + " -> " + rhs1) localTyper.typed(deriveDefDef(tree)(_ => rhs1)) case Abstract(targ) => debuglog("abstract: " + targ) localTyper.typed(deriveDefDef(tree)(rhs => rhs)) case SpecialSuperAccessor(targ) => debuglog("special super accessor: " + targ + " for " + tree) localTyper.typed(deriveDefDef(tree)(rhs => rhs)) } } expandInnerNormalizedMembers(transformDefDef) case ddef @ DefDef(_, _, _, _, _, _) => val tree1 = expandInnerNormalizedMembers(tree) super.transform(tree1) case ValDef(_, _, _, _) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor => def transformValDef = { assert(body.isDefinedAt(symbol.alias), body) val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) val d = new SpecializationDuplicator(emptyEnv) val newValDef = d.retyped( localTyper.context1.asInstanceOf[d.Context], tree1, symbol.alias.enclClass, symbol.enclClass, typeEnv(symbol.alias) ++ typeEnv(tree.symbol) ) deriveValDef(newValDef)(transform) } transformValDef case _ => super.transform(tree) } } /** * This performs method specialization inside a scope other than a {class, trait, object}: could be another method * or a value. This specialization is much simpler, since there is no need to record the new members in the class * signature, their signatures are only visible locally. It works according to the usual logic: * - we use normalizeMember to create the specialized symbols * - we leave DefDef stubs in the tree that are later filled in by tree duplication and adaptation * @see duplicateBody */ private def expandInnerNormalizedMembers(tree: Tree) = tree match { case ddef @ DefDef(_, _, _, vparams :: Nil, _, rhs) if ddef.symbol.owner.isMethod && specializedTypeVars(ddef.symbol.info).nonEmpty && !ddef.symbol.hasFlag(SPECIALIZED) => val sym = ddef.symbol val owner = sym.owner val norm = normalizeMember(owner, sym, emptyEnv) if (norm.length > 1) { // record the body for duplication body(sym) = rhs parameters(sym) = vparams.map(_.symbol) // to avoid revisiting the member, we can set the SPECIALIZED // flag. nobody has to see this anyway :) sym.setFlag(SPECIALIZED) // create empty bodies for specializations localTyper.typed(Block(norm.tail.map(sym => DefDef(sym, { vparamss: List[List[Symbol]] => EmptyTree })), ddef)) } else tree case _ => tree } /** Duplicate the body of the given method `tree` to the new symbol `source`. * * Knowing that the method can be invoked only in the `castmap` type environment, * this method will insert casts for all the expressions of types mappend in the * `castmap`. */ private def duplicateBody(tree: DefDef, source: Symbol, castmap: TypeEnv = emptyEnv) = { val symbol = tree.symbol val meth = addBody(tree, source) val d = new SpecializationDuplicator(castmap) debuglog("-->d DUPLICATING: " + meth) d.retyped( localTyper.context1.asInstanceOf[d.Context], meth, source.enclClass, symbol.enclClass, typeEnv(source) ++ typeEnv(symbol) ) } /** Put the body of 'source' as the right hand side of the method 'tree'. * The destination method gets fresh symbols for type and value parameters, * and the body is updated to the new symbols, and owners adjusted accordingly. * However, if the same source tree is used in more than one place, full re-typing * is necessary. @see method duplicateBody */ private def addBody(tree: DefDef, source: Symbol): DefDef = { val symbol = tree.symbol debuglog("specializing body of" + symbol.defString) val DefDef(_, _, tparams, vparams :: Nil, tpt, _) = tree val env = typeEnv(symbol) val boundTvars = env.keySet val origtparams = source.typeParams.filter(tparam => !boundTvars(tparam) || !isPrimitiveValueType(env(tparam))) if (origtparams.nonEmpty || symbol.typeParams.nonEmpty) debuglog("substituting " + origtparams + " for " + symbol.typeParams) // skolemize type parameters val oldtparams = tparams map (_.symbol) val newtparams = deriveFreshSkolems(oldtparams) map2(tparams, newtparams)(_ setSymbol _) // create fresh symbols for value parameters to hold the skolem types val newSyms = cloneSymbolsAtOwnerAndModify(vparams map (_.symbol), symbol, _.substSym(oldtparams, newtparams)) // replace value and type parameters of the old method with the new ones // log("Adding body for " + tree.symbol + " - origtparams: " + origtparams + "; tparams: " + tparams) // log("Type vars of: " + source + ": " + source.typeParams) // log("Type env of: " + tree.symbol + ": " + boundTvars) // log("newtparams: " + newtparams) val symSubstituter = new ImplementationAdapter( parameters(source) ::: origtparams, newSyms ::: newtparams, source.enclClass, false) // don't make private fields public val newBody = symSubstituter(body(source).duplicate) tpt modifyType (_.substSym(oldtparams, newtparams)) copyDefDef(tree)(vparamss = List(newSyms map ValDef.apply), rhs = newBody) } /** Create trees for specialized members of 'sClass', based on the * symbols that are already there. */ private def makeSpecializedMembers(sClass: Symbol): List[Tree] = { // add special overrides first // if (!specializedClass.hasFlag(SPECIALIZED)) // for (m <- specialOverrides(specializedClass)) specializedClass.info.decls.enter(m) val mbrs = new mutable.ListBuffer[Tree] var hasSpecializedFields = false for (m <- sClass.info.decls if m.hasFlag(SPECIALIZED) && (m.sourceFile ne null) && satisfiable(typeEnv(m), !sClass.hasFlag(SPECIALIZED))) { debuglog("creating tree for " + m.fullName) if (m.isMethod) { if (info(m).target.hasAccessorFlag) hasSpecializedFields = true if (m.isClassConstructor) { val origParams = parameters(info(m).target) val vparams = ( map2(m.info.paramTypes, origParams)((tp, sym) => m.newValue(specializedName(sym, typeEnv(sClass)), sym.pos, sym.flags) setInfo tp ) ) // param accessors for private members (the others are inherited from the generic class) if (m.isPrimaryConstructor) { for (param <- vparams ; if sClass.info.nonPrivateMember(param.name) == NoSymbol) { val acc = param.cloneSymbol(sClass, param.flags | PARAMACCESSOR | PRIVATE) sClass.info.decls.enter(acc) mbrs += ValDef(acc, EmptyTree).setType(NoType).setPos(m.pos) } } // ctor mbrs += DefDef(m, Modifiers(m.flags), mmap(List(vparams))(ValDef.apply), EmptyTree) } else { mbrs += DefDef(m, { paramss: List[List[Symbol]] => EmptyTree }) } } else if (m.isValue) { mbrs += ValDef(m).setType(NoType) } else if (m.isClass) { // mbrs += // ClassDef(m, Template(m.info.parents map TypeTree, noSelfType, List()) // .setSymbol(m.newLocalDummy(m.pos))) // log("created synthetic class: " + m.fullName) } } if (hasSpecializedFields) { val isSpecializedInstance = sClass :: sClass.parentSymbols exists (_ hasFlag SPECIALIZED) val sym = sClass.newMethod(nme.SPECIALIZED_INSTANCE, sClass.pos) setInfoAndEnter MethodType(Nil, BooleanTpe) mbrs += DefDef(sym, Literal(Constant(isSpecializedInstance)).setType(BooleanTpe)).setType(NoType) } mbrs.toList } /** Create specialized class definitions */ def implSpecClasses(trees: List[Tree]): List[Tree] = { trees flatMap { case tree @ ClassDef(_, _, _, impl) => tree.symbol.info // force specialization for (((sym1, env), specCls) <- specializedClass if sym1 == tree.symbol) yield { debuglog("created synthetic class: " + specCls + " of " + sym1 + " in " + pp(env)) val parents = specCls.info.parents.map(TypeTree) ClassDef(specCls, atPos(impl.pos)(Template(parents, noSelfType, List())) .setSymbol(specCls.newLocalDummy(sym1.pos))) setPos tree.pos } case _ => Nil } sortBy (_.name.decoded) } } private def forwardCall(pos: scala.reflect.internal.util.Position, receiver: Tree, paramss: List[List[ValDef]]): Tree = { val argss = mmap(paramss)(x => Ident(x.symbol)) atPos(pos) { (receiver /: argss) (Apply.apply) } } /** Forward to the generic class constructor. If the current class initializes * specialized fields corresponding to parameters, it passes null to the superclass * constructor. * * For example: * {{{ * case class Tuple2[T, U](x: T, y: U) * * class Tuple2$II { * val _x$I: Int = .. * def x = _x$I * // same for y * def this(x: Int, y: Int) { * super.this(null.asInstanceOf[Int], null.asInstanceOf[Int]) * } * } * }}} * * Note that erasure first transforms `null.asInstanceOf[Int]` to `unbox(null)`, which is 0. * Then it adapts the argument `unbox(null)` of type Int to the erased parameter type of Tuple2, * which is Object, so it inserts a `box` call and we get `box(unbox(null))`, which is * `new Integer(0)` (not `null`). * * However it does not make sense to create an Integer instance to be stored in the generic field * of the superclass: that field is never used. Therefore we mark the `null` tree with the * [[SpecializedSuperConstructorCallArgument]] attachment and special-case erasure to replace * `box(unbox(null))` by `null` in this case. */ private def forwardCtorCall(pos: scala.reflect.internal.util.Position, receiver: Tree, paramss: List[List[ValDef]], clazz: Symbol): Tree = { log(s"forwardCtorCall($pos, $receiver, $paramss, $clazz)") /* A constructor parameter `f` initializes a specialized field * iff: * - it is specialized itself * - there is a getter for the original (non-specialized) field in the same class * - there is a getter for the specialized field in the same class */ def initializesSpecializedField(f: Symbol) = ( (f.name endsWith nme.SPECIALIZED_SUFFIX) && clazz.info.member(f.unexpandedName).isPublic && clazz.info.decl(f.name).suchThat(_.isGetter) != NoSymbol ) val argss = mmap(paramss)(x => if (initializesSpecializedField(x.symbol)) gen.mkAsInstanceOf(Literal(Constant(null)).updateAttachment(SpecializedSuperConstructorCallArgument), x.symbol.tpe) else Ident(x.symbol) ) atPos(pos) { (receiver /: argss) (Apply.apply) } } /** Add method m to the set of symbols for which we need an implementation tree * in the tree transformer. * * @note This field is part of the specializeTypes subcomponent, so any symbols * that here are not garbage collected at the end of a compiler run! */ def addConcreteSpecMethod(m: Symbol) { if (currentRun.compiles(m)) concreteSpecMethods += m } private def makeArguments(fun: Symbol, vparams: List[Symbol]): List[Tree] = ( //! TODO: make sure the param types are seen from the right prefix map2(fun.info.paramTypes, vparams)((tp, arg) => gen.maybeMkAsInstanceOf(Ident(arg), tp, arg.tpe)) ) class SpecializationTransformer(unit: CompilationUnit) extends Transformer { informProgress("specializing " + unit) override def transform(tree: Tree) = { val resultTree = if (settings.nospecialization) tree else exitingSpecialize(specializeCalls(unit).transform(tree)) // Remove the final modifier and @inline annotation from anything in the // original class (since it's being overridden in at least one subclass). // // We do this here so that the specialized subclasses will correctly copy // final and @inline. info.foreach { case (sym, SpecialOverload(target, _)) => { sym.resetFlag(FINAL) target.resetFlag(FINAL) sym.removeAnnotation(ScalaInlineClass) target.removeAnnotation(ScalaInlineClass) } case _ => {} } resultTree } } object SpecializedSuperConstructorCallArgument }