diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Constructors.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 85 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Duplicators.scala | 31 | ||||
-rw-r--r-- | test/files/pos/spec-params-new.scala | 2 | ||||
-rw-r--r-- | test/files/run/t5284.check | 1 | ||||
-rw-r--r-- | test/files/run/t5284.scala | 25 | ||||
-rw-r--r-- | test/files/run/t5284b.check | 1 | ||||
-rw-r--r-- | test/files/run/t5284b.scala | 28 | ||||
-rw-r--r-- | test/files/run/t5284c.check | 1 | ||||
-rw-r--r-- | test/files/run/t5284c.scala | 30 |
10 files changed, 180 insertions, 26 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index bc4483923a..e5119eac71 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -323,7 +323,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { // statements coming from the original class need retyping in the current context debuglog("retyping " + stat2) - val d = new specializeTypes.Duplicator + val d = new specializeTypes.Duplicator(Map[Symbol, Type]()) d.retyped(localTyper.context1.asInstanceOf[d.Context], stat2, genericClazz, diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index c4c769d7cf..1d820afe11 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -450,7 +450,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Type parameters that survive when specializing in the specified environment. */ def survivingParams(params: List[Symbol], env: TypeEnv) = - params.filter(p => !p.isSpecialized || !isPrimitiveValueType(env(p))) + 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`. @@ -1176,7 +1181,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { || 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) @@ -1192,10 +1197,58 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } + + 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 + } + } - class Duplicator extends { + /** 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 + } 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 = { + // log(" expected type: " + pt) + // log(" tree type: " + tree.tpe) + tree.tpe = if (tree.tpe != null) fixType(tree.tpe) else null + // 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.tpe = null + ntree + } + } + + protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + + } /** A tree symbol substituter that substitutes on type skolems. * If a type parameter is a skolem, it looks for the original @@ -1475,14 +1528,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { deriveDefDef(tree1)(transform) case NormalizedMember(target) => - debuglog("Normalized member: " + symbol + ", target: " + target) - if (target.isDeferred || conflicting(typeEnv(symbol))) { + val constraints = satisfiabilityConstraints(typeEnv(symbol)) + log("constraints: " + constraints) + if (target.isDeferred || constraints == None) { deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) - } - else { + } else { // we have an rhs, specialize it val tree1 = reportTypeError { - duplicateBody(ddef, target) + duplicateBody(ddef, target, constraints.get) } debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) @@ -1546,7 +1599,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) - val d = new Duplicator + val d = new Duplicator(emptyEnv) val newValDef = d.retyped( localTyper.context1.asInstanceOf[d.Context], tree1, @@ -1571,12 +1624,18 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { super.transform(tree) } } - - private def duplicateBody(tree: DefDef, source: Symbol) = { + + /** 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 Duplicator + val d = new Duplicator(castmap) debuglog("-->d DUPLICATING: " + meth) d.retyped( localTyper.context1.asInstanceOf[d.Context], diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 6386273c9d..63d1bd0e9f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -21,7 +21,7 @@ abstract class Duplicators extends Analyzer { def retyped(context: Context, tree: Tree): Tree = { resetClassOwners - (new BodyDuplicator(context)).typed(tree) + (newBodyDuplicator(context)).typed(tree) } /** Retype the given tree in the given context. Use this method when retyping @@ -37,15 +37,17 @@ abstract class Duplicators extends Analyzer { envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList) debuglog("retyped with env: " + env) - (new BodyDuplicator(context)).typed(tree) + newBodyDuplicator(context).typed(tree) } + protected def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + def retypedMethod(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol): Tree = - (new BodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) + (newBodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) /** Return the special typer for duplicate method bodies. */ override def newTyper(context: Context): Typer = - new BodyDuplicator(context) + newBodyDuplicator(context) private def resetClassOwners() { oldClassOwner = null @@ -209,6 +211,11 @@ abstract class Duplicators extends Analyzer { } } + /** Optionally cast this tree into some other type, if required. + * Unless overridden, just returns the tree. + */ + def castType(tree: Tree, pt: Type): Tree = tree + /** Special typer method for re-type checking trees. It expects a typed tree. * Returns a typed tree that has fresh symbols for all definitions in the original tree. * @@ -319,10 +326,10 @@ abstract class Duplicators extends Analyzer { super.typed(atPos(tree.pos)(tree1), mode, pt) case This(_) => - // log("selection on this, plain: " + tree) + debuglog("selection on this, plain: " + tree) tree.symbol = updateSym(tree.symbol) - tree.tpe = null - val tree1 = super.typed(tree, mode, pt) + val ntree = castType(tree, pt) + val tree1 = super.typed(ntree, mode, pt) // log("plain this typed to: " + tree1) tree1 /* no longer needed, because Super now contains a This(...) @@ -358,16 +365,18 @@ abstract class Duplicators extends Analyzer { case EmptyTree => // no need to do anything, in particular, don't set the type to null, EmptyTree.tpe_= asserts tree - + case _ => - // log("Duplicators default case: " + tree.summaryString + " -> " + tree) + debuglog("Duplicators default case: " + tree.summaryString) + debuglog(" ---> " + tree) if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } - tree.tpe = null - super.typed(tree, mode, pt) + val ntree = castType(tree, pt) + super.typed(ntree, mode, pt) } } + } } diff --git a/test/files/pos/spec-params-new.scala b/test/files/pos/spec-params-new.scala index 661e686f0e..959ce1591c 100644 --- a/test/files/pos/spec-params-new.scala +++ b/test/files/pos/spec-params-new.scala @@ -31,4 +31,4 @@ class Foo[@specialized A: ClassTag] { val xs = new Array[A](1) xs(0) = x } -}
\ No newline at end of file +} diff --git a/test/files/run/t5284.check b/test/files/run/t5284.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t5284.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t5284.scala b/test/files/run/t5284.scala new file mode 100644 index 0000000000..ba0845fb8e --- /dev/null +++ b/test/files/run/t5284.scala @@ -0,0 +1,25 @@ + + + + + +/** Here we have a situation where a normalized method parameter `W` + * is used in a position which accepts an instance of type `T` - we know we can + * safely cast `T` to `W` whenever type bounds on `W` hold. + */ +object Test { + def main(args: Array[String]) { + val a = Blarg(Array(1, 2, 3)) + println(a.m((x: Int) => x + 1)) + } +} + + +object Blarg { + def apply[T: Manifest](a: Array[T]) = new Blarg(a) +} + + +class Blarg[@specialized(Int) T: Manifest](val a: Array[T]) { + def m[@specialized(Int) W >: T, @specialized(Int) S](f: W => S) = f(a(0)) +} diff --git a/test/files/run/t5284b.check b/test/files/run/t5284b.check new file mode 100644 index 0000000000..98d9bcb75a --- /dev/null +++ b/test/files/run/t5284b.check @@ -0,0 +1 @@ +17 diff --git a/test/files/run/t5284b.scala b/test/files/run/t5284b.scala new file mode 100644 index 0000000000..a9282a895f --- /dev/null +++ b/test/files/run/t5284b.scala @@ -0,0 +1,28 @@ + + + + + + +/** Here we have a situation where a normalized method parameter `W` + * is used in a position which expects a type `T` - we know we can + * safely cast `W` to `T` whenever typebounds of `W` hold. + */ +object Test { + def main(args: Array[String]) { + val foo = Foo.createUnspecialized[Int] + println(foo.bar(17)) + } +} + + +object Foo { + def createUnspecialized[T] = new Foo[T] +} + + +class Foo[@specialized(Int) T] { + val id: T => T = x => x + + def bar[@specialized(Int) W <: T, @specialized(Int) S](w: W) = id(w) +} diff --git a/test/files/run/t5284c.check b/test/files/run/t5284c.check new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/test/files/run/t5284c.check @@ -0,0 +1 @@ +3 diff --git a/test/files/run/t5284c.scala b/test/files/run/t5284c.scala new file mode 100644 index 0000000000..383b84c2cc --- /dev/null +++ b/test/files/run/t5284c.scala @@ -0,0 +1,30 @@ + + + + + + +/** Here we have a compound type `List[W]` used in + * a position where `List[T]` is expected. The cast + * emitted in the normalized `bar` is safe because the + * normalized `bar` can only be called if the type + * bounds hold. + */ +object Test { + def main(args: Array[String]) { + val foo = Foo.createUnspecialized[Int] + println(foo.bar(List(1, 2, 3))) + } +} + + +object Foo { + def createUnspecialized[T] = new Foo[T] +} + + +class Foo[@specialized(Int) T] { + val len: List[T] => Int = xs => xs.length + + def bar[@specialized(Int) W <: T](ws: List[W]) = len(ws) +} |