summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-11-22 18:35:43 +0100
committerJason Zaugg <jzaugg@gmail.com>2013-11-27 09:09:46 +0100
commit28d3390e07715b0dcb5ce2f68d72d5a44e6ca74d (patch)
tree8ac9461aa99fd6a2d6b7a937d0459ed8b9c84653 /src
parent073ebbd20ce9775260b83a78ecf9ed6a3e6d3d9e (diff)
downloadscala-28d3390e07715b0dcb5ce2f68d72d5a44e6ca74d.tar.gz
scala-28d3390e07715b0dcb5ce2f68d72d5a44e6ca74d.tar.bz2
scala-28d3390e07715b0dcb5ce2f68d72d5a44e6ca74d.zip
SI-2066 Plug a soundness hole higher order type params, overriding
PolyType-s parameterized by higher order type parameters (HOTPs) should only be relatable with <:< or =:= if the variances of their type parameters line up. This is only enforced for HOTPs defined in method type arguments. Invariant type parameters subsume variant ones. Concretely, as described by @S11001001: > A method taking [F[_]] can implement a method taking [F[+_]]. > Likewise, a method taking [F[_[+_]]] can implement a method > taking [F[_[_]]], as with [F[_[_[_]]]] implementing [F[_[_[+_]]]], > and so on, the variance subsumption flipping at each step. > > This is just the opposite of the variance for passing type > parameters to either instantiate types or call methods; a F[+_] > is a suitable F[_]-argument, a F[_[_]] a suitable F[_[+_]]-argument, > and so on. > > All of the above rules can be duplicated for contravariant positions > by substituting - for +. Also, something similar happens for > weakening/strengthening bounds, I believe. These cases are tested in the `// okay` lines in `neg/t2066.scala`.
Diffstat (limited to 'src')
-rw-r--r--src/reflect/scala/reflect/internal/tpe/TypeComparers.scala16
1 files changed, 14 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
index b60fecd66e..2623a47be6 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
@@ -170,11 +170,20 @@ trait TypeComparers {
// corresponds does not check length of two sequences before checking the predicate,
// but SubstMap assumes it has been checked (SI-2956)
( sameLength(tparams1, tparams2)
- && (tparams1 corresponds tparams2)((p1, p2) => p1.info =:= subst(p2.info))
+ && (tparams1 corresponds tparams2)((p1, p2) => methodHigherOrderTypeParamsSameVariance(p1, p2) && p1.info =:= subst(p2.info))
&& (res1 =:= subst(res2))
)
}
+ // SI-2066 This prevents overrides with incompatible variance in higher order type parameters.
+ private def methodHigherOrderTypeParamsSameVariance(sym1: Symbol, sym2: Symbol) = {
+ def ignoreVariance(sym: Symbol) = !(sym.isHigherOrderTypeParameter && sym.logicallyEnclosingMember.isMethod)
+ ignoreVariance(sym1) || ignoreVariance(sym2) || sym1.variance == sym2.variance
+ }
+
+ private def methodHigherOrderTypeParamsSubVariance(low: Symbol, high: Symbol) =
+ methodHigherOrderTypeParamsSameVariance(low, high) || low.variance.isInvariant
+
def isSameType2(tp1: Type, tp2: Type): Boolean = {
def retry(lhs: Type, rhs: Type) = ((lhs ne tp1) || (rhs ne tp2)) && isSameType(lhs, rhs)
@@ -327,7 +336,10 @@ trait TypeComparers {
val substitutes = if (isMethod) tparams1 else cloneSymbols(tparams1)
def sub1(tp: Type) = if (isMethod) tp else tp.substSym(tparams1, substitutes)
def sub2(tp: Type) = tp.substSym(tparams2, substitutes)
- def cmp(p1: Symbol, p2: Symbol) = sub2(p2.info) <:< sub1(p1.info)
+ def cmp(p1: Symbol, p2: Symbol) = (
+ methodHigherOrderTypeParamsSubVariance(p2, p1)
+ && sub2(p2.info) <:< sub1(p1.info)
+ )
(tparams1 corresponds tparams2)(cmp) && (sub1(res1) <:< sub2(res2))
}