Skip to content

Commit

Permalink
Backported fix for SI-7753 to 2.10.x.
Browse files Browse the repository at this point in the history
  • Loading branch information
milessabin committed Feb 9, 2015
1 parent ad0ddd4 commit 2f5ff59
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 37 deletions.
89 changes: 55 additions & 34 deletions src/reflect/scala/reflect/internal/Types.scala
Expand Up @@ -4818,49 +4818,74 @@ trait Types extends api.Types { self: SymbolTable =>
}

/** Note: This map is needed even for non-dependent method types, despite what the name might imply.
*/
*/
class InstantiateDependentMap(params: List[Symbol], actuals0: List[Type]) extends TypeMap with KeepOnlyTypeConstraints {
private val actuals = actuals0.toIndexedSeq
private val existentials = new Array[Symbol](actuals.size)
def existentialsNeeded: List[Symbol] = existentials.filter(_ ne null).toList

private object StableArg {
def unapply(param: Symbol) = Arg unapply param map actuals filter (tp =>
tp.isStable && (tp.typeSymbol != NothingClass)
)
}
private object Arg {
def unapply(param: Symbol) = Some(params indexOf param) filter (_ >= 0)
}

def apply(tp: Type): Type = mapOver(tp) match {
// unsound to replace args by unstable actual #3873
case SingleType(NoPrefix, StableArg(arg)) => arg
// (soundly) expand type alias selections on implicit arguments,
// see depmet_implicit_oopsla* test cases -- typically, `param.isImplicit`
case tp1 @ TypeRef(SingleType(NoPrefix, Arg(pid)), sym, targs) =>
val arg = actuals(pid)
val res = typeRef(arg, sym, targs)
if (res.typeSymbolDirect.isAliasType) res.dealias else tp1
// don't return the original `tp`, which may be different from `tp1`,
// due to dropping annotations
case tp1 => tp1
def existentialsNeeded: List[Symbol] = existentials.iterator.filter(_ ne null).toList

private object StableArgTp {
// type of actual arg corresponding to param -- if the type is stable
def unapply(param: Symbol): Option[Type] = (params indexOf param) match {
case -1 => None
case pid =>
val tp = actuals(pid)
if (tp.isStable && (tp.typeSymbol != NothingClass)) Some(tp)
else None
}
}

/* Return the type symbol for referencing a parameter inside the existential quantifier.
* (Only needed if the actual is unstable.)
/** Return the type symbol for referencing a parameter that's instantiated to an unstable actual argument.
*
* To soundly abstract over an unstable value (x: T) while retaining the most type information,
* use `x.type forSome { type x.type <: T with Singleton}`
* `typeOf[T].narrowExistentially(symbolOf[x])`.
*
* See also: captureThis in AsSeenFromMap.
*/
private def existentialFor(pid: Int) = {
if (existentials(pid) eq null) {
val param = params(pid)
existentials(pid) = (
param.owner.newExistential(param.name.toTypeName append nme.SINGLETON_SUFFIX, param.pos, param.flags)
setInfo singletonBounds(actuals(pid))
)
)
}
existentials(pid)
}

private object UnstableArgTp {
// existential quantifier and type of corresponding actual arg with unstable type
def unapply(param: Symbol): Option[(Symbol, Type)] = (params indexOf param) match {
case -1 => None
case pid =>
val sym = existentialFor(pid)
Some((sym, sym.tpe)) // refers to an actual value, must be kind-*
}
}

private object StabilizedArgTp {
def unapply(param: Symbol): Option[Type] =
param match {
case StableArgTp(tp) => Some(tp) // (1)
case UnstableArgTp(_, tp) => Some(tp) // (2)
case _ => None
}
}

/** instantiate `param.type` to the (sound approximation of the) type `T`
* of the actual argument `arg` that was passed in for `param`
*
* (1) If `T` is stable, we can just use that.
*
* (2) SI-3873: it'd be unsound to instantiate `param.type` to an unstable `T`,
* so we approximate to `X forSome {type X <: T with Singleton}` -- we can't soundly say more.
*/
def apply(tp: Type): Type = tp match {
case SingleType(NoPrefix, StabilizedArgTp(tp)) => tp
case _ => mapOver(tp)
}

//AM propagate more info to annotations -- this seems a bit ad-hoc... (based on code by spoon)
override def mapOver(arg: Tree, giveup: ()=>Nothing): Tree = {
// TODO: this should be simplified; in the stable case, one can
Expand All @@ -4883,13 +4908,9 @@ trait Types extends api.Types { self: SymbolTable =>
// Both examples are from run/constrained-types.scala.
object treeTrans extends Transformer {
override def transform(tree: Tree): Tree = tree.symbol match {
case StableArg(actual) =>
gen.mkAttributedQualifier(actual, tree.symbol)
case Arg(pid) =>
val sym = existentialFor(pid)
Ident(sym) copyAttrs tree setType typeRef(NoPrefix, sym, Nil)
case _ =>
super.transform(tree)
case StableArgTp(tp) => gen.mkAttributedQualifier(tp, tree.symbol)
case UnstableArgTp(quant, tp) => Ident(quant) copyAttrs tree setType tp
case _ => super.transform(tree)
}
}
treeTrans transform arg
Expand Down
4 changes: 2 additions & 2 deletions test/files/neg/t3873.check
@@ -1,6 +1,6 @@
t3873.scala:11: error: type mismatch;
found : Test.a.B
required: a.B
wrongf(new A)(a.b) // should not compile -- TODO: improve error message? the "a" is ambiguous
required: a.B where val a: A
wrongf(new A)(a.b) // should not compile
^
one error found
2 changes: 1 addition & 1 deletion test/files/neg/t3873.scala
Expand Up @@ -8,5 +8,5 @@ object Test {

val a = new A
wrongf(a)(a.b)
wrongf(new A)(a.b) // should not compile -- TODO: improve error message? the "a" is ambiguous
wrongf(new A)(a.b) // should not compile
}
36 changes: 36 additions & 0 deletions test/files/pos/t7753.scala
@@ -0,0 +1,36 @@
import scala.language.{ higherKinds, implicitConversions }

trait Foo { type Out }

trait SI {
val instance: Foo
type Out
}

object Test {
def test {
def indirect(si: SI)(v: si.instance.Out) = v

val foo: Foo { type Out = Int } = ???
def conv(i: Foo): SI { type Out = i.Out; val instance: i.type } = ???

val converted = conv(foo)

val v1: Int = indirect(converted)(23) // Okay (after refining the return type `instance` in the return type of `conv`)
/*
indirect(converted){(v: converted.instance.Out)converted.instance.Out}(
23{Int(23)}
){converted.instance.Out};
*/

val v2: Int = indirect(conv(foo))(23) // Used to fail as follows:
/*
indirect(
conv(foo){si.SI{type Out = foo.Out; val instance: si.Test.<refinement>.type}}
){(v: si.instance.Out)si.instance.Out}(
23{<error>}
){<error>};
*/

}
}
29 changes: 29 additions & 0 deletions test/files/pos/t8223.scala
@@ -0,0 +1,29 @@
package p {
class ViewEnv[AIn] {
type A = AIn
class SubView { def has(x: A): Boolean = ??? }
def get: SubView = new SubView
}

trait HasA { type A }
trait Indexable[R] extends HasA
class ArrayTC[AIn] extends Indexable[Array[AIn]] { type A = AIn }
}

package object p {
implicit def arrayTypeClass[A] : ArrayTC[A] = new ArrayTC[A]
object intArrayTC extends ArrayTC[Int]

type EnvAlias[W <: HasA] = ViewEnv[W#A]
type SubAlias[W <: HasA] = ViewEnv[W#A]#SubView

def f0[R](xs: R)(implicit tc: Indexable[R]): ViewEnv[tc.A]#SubView = new ViewEnv[tc.A]() get
def f1[R](xs: R)(implicit tc: Indexable[R]): EnvAlias[tc.type]#SubView = new ViewEnv[tc.A]() get
def f2[R](xs: R)(implicit tc: Indexable[R]): SubAlias[tc.type] = new ViewEnv[tc.A]() get

def g0 = f0(Array(1)) has 2 // ok
def g1 = f1(Array(1)) has 2 // ok
def g2 = f2(Array(1)) has 2 // "found: Int(2), required: tc.A"
def g3 = f2(Array(1))(new ArrayTC[Int]) has 2 // "found: Int(2), required: tc.A"
def g4 = f2(Array(1))(intArrayTC) has 2 // ok
}

0 comments on commit 2f5ff59

Please sign in to comment.