|
1 | 1 | package dotty.tools.dotc |
2 | | -package typer |
| 2 | +package core |
3 | 3 |
|
4 | | -import core._ |
5 | 4 | import Types._, Contexts._, Flags._, Symbols._, Annotations._ |
| 5 | +import TypeApplications.TypeParamInfo |
6 | 6 |
|
7 | 7 | object Variances { |
8 | 8 |
|
9 | 9 | type Variance = FlagSet |
10 | 10 | val Bivariant: Variance = VarianceFlags |
11 | 11 | val Invariant: Variance = EmptyFlags |
12 | 12 |
|
| 13 | + def varianceFromInt(v: Int) = |
| 14 | + if v < 0 then Covariant |
| 15 | + else if v > 0 then Contravariant |
| 16 | + else Invariant |
| 17 | + |
13 | 18 | /** Flip between covariant and contravariant */ |
14 | 19 | def flip(v: Variance): Variance = |
15 | 20 | if (v == Covariant) Contravariant |
@@ -98,6 +103,53 @@ object Variances { |
98 | 103 | Bivariant |
99 | 104 | } |
100 | 105 |
|
| 106 | + /** A map from the index of a lambda parameter to its variance in -1 .. 1 */ |
| 107 | + type ParamVarianceMap = Map[Int, Int] |
| 108 | + |
| 109 | + def lambdaVariances(lam: HKTypeLambda)(implicit ctx: Context): ParamVarianceMap = |
| 110 | + object accu extends TypeAccumulator[ParamVarianceMap] { |
| 111 | + def apply(vmap: ParamVarianceMap, t: Type): ParamVarianceMap = t match { |
| 112 | + case t: TypeParamRef if t.binder eq lam => |
| 113 | + val idx = t.paramNum |
| 114 | + vmap.get(idx) match |
| 115 | + case None => |
| 116 | + vmap.updated(idx, variance) |
| 117 | + case Some(v) => |
| 118 | + if v == variance || v == 0 then vmap else vmap.updated(idx, 0) |
| 119 | + case _ => |
| 120 | + foldOver(vmap, t) |
| 121 | + } |
| 122 | + } |
| 123 | + accu(Map(), lam.resType) |
| 124 | + |
| 125 | + /** Does variance `v1` conform to variance `v2`? |
| 126 | + * This is the case if the variances are the same or `sym` is nonvariant. |
| 127 | + */ |
| 128 | + def varianceConforms(v1: Int, v2: Int): Boolean = |
| 129 | + v1 == v2 || v2 == 0 |
| 130 | + |
| 131 | + /** Does the variance of type parameter `tparam1` conform to the variance of type parameter `tparam2`? |
| 132 | + */ |
| 133 | + def varianceConforms(tparam1: TypeParamInfo, tparam2: TypeParamInfo)(implicit ctx: Context): Boolean = |
| 134 | + varianceConforms(tparam1.paramVariance, tparam2.paramVariance) |
| 135 | + |
| 136 | + /** Do the variances of type parameters `tparams1` conform to the variances |
| 137 | + * of corresponding type parameters `tparams2`? |
| 138 | + * This is only the case of `tparams1` and `tparams2` have the same length. |
| 139 | + */ |
| 140 | + def variancesConform(tparams1: List[TypeParamInfo], tparams2: List[TypeParamInfo])(implicit ctx: Context): Boolean = |
| 141 | + tparams1.corresponds(tparams2)(varianceConforms) |
| 142 | + |
| 143 | + def variancesConform(vs1: List[Variance], vs2: List[Variance]): Boolean = vs2 match |
| 144 | + case v2 :: rest2 => |
| 145 | + vs1 match |
| 146 | + case v1 :: rest1 => v1.isAllOf(v2) && variancesConform(rest1, rest2) |
| 147 | + case nil => v2.isEmpty && variancesConform(vs1, rest2) |
| 148 | + case nil => true |
| 149 | + |
| 150 | + def varianceString(sym: Symbol)(implicit ctx: Context): String = |
| 151 | + varianceString(sym.variance) |
| 152 | + |
101 | 153 | def varianceString(v: Variance): String = |
102 | 154 | if (v is Covariant) "covariant" |
103 | 155 | else if (v is Contravariant) "contravariant" |
|
0 commit comments