Skip to content

Commit 8667689

Browse files
committed
Address review comments
1 parent eb04b8f commit 8667689

File tree

8 files changed

+62
-37
lines changed

8 files changed

+62
-37
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1934,6 +1934,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
19341934
if (t2.exists) t2
19351935
else if (isErased) erasedGlb(tp1, tp2, isJava = false)
19361936
else liftIfHK(tp1, tp2, op, original, _ | _)
1937+
// The ` | ` on variances is needed since variances are associated with bounds
1938+
// not lambdas. Example:
1939+
//
1940+
// trait A { def F[-X] }
1941+
// trait B { def F[+X] }
1942+
// object O extends A, B { ... }
1943+
//
1944+
// Here, `F` is treated as bivariant in `O`. That is, only bivariant implementation
1945+
// of `F` are allowed.
19371946
}
19381947
}
19391948

@@ -2004,7 +2013,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
20042013
HKTypeLambda(
20052014
paramNames = HKTypeLambda.syntheticParamNames(tparams1.length),
20062015
variances =
2007-
if tp1.isVariantLambda && tp2.isVariantLambda then
2016+
if tp1.isDeclaredVarianceLambda && tp2.isDeclaredVarianceLambda then
20082017
tparams1.lazyZip(tparams2).map((p1, p2) => combineVariance(p1.paramVariance, p2.paramVariance))
20092018
else Nil
20102019
)(

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ object Types {
368368
}
369369

370370
/** Is this a higher-kinded type lambda with given parameter variances? */
371-
def isVariantLambda: Boolean = false
371+
def isDeclaredVarianceLambda: Boolean = false
372372

373373
// ----- Higher-order combinators -----------------------------------
374374

@@ -3433,15 +3433,19 @@ object Types {
34333433
}
34343434

34353435
/** A type lambda of the form `[X_0 B_0, ..., X_n B_n] => T`
3436-
* Variances are encoded in parameter names. A name starting with `+`
3437-
* designates a covariant parameter, a name starting with `-` designates
3438-
* a contravariant parameter, and every other name designates a non-variant parameter.
3436+
* Variances are encoded in parameter names. A
34393437
*
34403438
* @param paramNames The names `X_0`, ..., `X_n`
34413439
* @param paramInfosExp A function that, given the polytype itself, returns the
34423440
* parameter bounds `B_1`, ..., `B_n`
34433441
* @param resultTypeExp A function that, given the polytype itself, returns the
34443442
* result type `T`.
3443+
* @param variances The variances of the type parameters, if the type lambda
3444+
* carries variances, i.e. it is a bound of an abstract type
3445+
* or the rhs of a match alias or opaque alias. The parameter
3446+
* is Nil for all other lambdas.
3447+
*
3448+
* Variances are stored in the `typeParams` list of the lambda.
34453449
*/
34463450
class HKTypeLambda(val paramNames: List[TypeName], @constructorOnly variances: List[Variance])(
34473451
paramInfosExp: HKTypeLambda => List[TypeBounds], resultTypeExp: HKTypeLambda => Type)
@@ -3457,28 +3461,28 @@ object Types {
34573461

34583462
private def setVariances(tparams: List[LambdaParam], vs: List[Variance]): Unit =
34593463
if tparams.nonEmpty then
3460-
tparams.head.givenVariance = vs.head
3464+
tparams.head.declaredVariance = vs.head
34613465
setVariances(tparams.tail, vs.tail)
34623466

3463-
override val isVariantLambda = variances.nonEmpty
3464-
if isVariantLambda then setVariances(typeParams, variances)
3467+
override val isDeclaredVarianceLambda = variances.nonEmpty
3468+
if isDeclaredVarianceLambda then setVariances(typeParams, variances)
34653469

3466-
def givenVariances =
3467-
if isVariantLambda then typeParams.map(_.givenVariance)
3470+
def declaredVariances =
3471+
if isDeclaredVarianceLambda then typeParams.map(_.declaredVariance)
34683472
else Nil
34693473

34703474
override def computeHash(bs: Binders): Int =
3471-
doHash(new Binders(this, bs), givenVariances ::: paramNames, resType, paramInfos)
3475+
doHash(new Binders(this, bs), declaredVariances ::: paramNames, resType, paramInfos)
34723476

34733477
// No definition of `eql` --> fall back on equals, which calls iso
34743478

34753479
final override def iso(that: Any, bs: BinderPairs): Boolean = that match {
34763480
case that: HKTypeLambda =>
34773481
paramNames.eqElements(that.paramNames)
3478-
&& isVariantLambda == that.isVariantLambda
3479-
&& (!isVariantLambda
3482+
&& isDeclaredVarianceLambda == that.isDeclaredVarianceLambda
3483+
&& (!isDeclaredVarianceLambda
34803484
|| typeParams.corresponds(that.typeParams)((x, y) =>
3481-
x.givenVariance == y.givenVariance))
3485+
x.declaredVariance == y.declaredVariance))
34823486
&& {
34833487
val bs1 = new BinderPairs(this, that, bs)
34843488
paramInfos.equalElements(that.paramInfos, bs1) &&
@@ -3489,7 +3493,7 @@ object Types {
34893493
}
34903494

34913495
override def newLikeThis(paramNames: List[ThisName], paramInfos: List[PInfo], resType: Type)(implicit ctx: Context): This =
3492-
newLikeThis(paramNames, givenVariances, paramInfos, resType)
3496+
newLikeThis(paramNames, declaredVariances, paramInfos, resType)
34933497

34943498
def newLikeThis(paramNames: List[ThisName], variances: List[Variance], paramInfos: List[PInfo], resType: Type)(implicit ctx: Context): This =
34953499
HKTypeLambda(paramNames, variances)(
@@ -3501,8 +3505,8 @@ object Types {
35013505

35023506
protected def prefixString: String = "HKTypeLambda"
35033507
final override def toString: String =
3504-
if isVariantLambda then
3505-
s"HKTypeLambda($paramNames, $paramInfos, $resType, ${givenVariances.map(_.flagsString)})"
3508+
if isDeclaredVarianceLambda then
3509+
s"HKTypeLambda($paramNames, $paramInfos, $resType, ${declaredVariances.map(_.flagsString)})"
35063510
else super.toString
35073511
}
35083512

@@ -3612,7 +3616,7 @@ object Types {
36123616
bounds.derivedAlias(expand(bounds.alias, true))
36133617
case bounds: TypeAlias =>
36143618
bounds.derivedAlias(expand(bounds.alias,
3615-
isOpaqueAlias | params.exists(!_.paramVariance.isEmpty)))
3619+
isOpaqueAlias || params.exists(!_.paramVariance.isEmpty)))
36163620
case TypeBounds(lo, hi) =>
36173621
bounds.derivedTypeBounds(
36183622
if lo.isRef(defn.NothingClass) then lo else expand(lo, true),
@@ -3659,18 +3663,32 @@ object Types {
36593663
def paramRef(implicit ctx: Context): Type = tl.paramRefs(n)
36603664

36613665
private var myVariance: FlagSet = UndefinedFlags
3666+
3667+
/** Low level setter, only called from Variances.setStructuralVariances */
36623668
def storedVariance_= (v: Variance): Unit =
36633669
myVariance = v
3670+
3671+
/** Low level getter, only called from Variances.setStructuralVariances */
36643672
def storedVariance: Variance =
36653673
myVariance
36663674

3667-
def givenVariance_=(v: Variance): Unit =
3675+
/** Set the declared variance of this parameter.
3676+
* @pre the containing lambda is a isDeclaredVarianceLambda
3677+
*/
3678+
def declaredVariance_=(v: Variance): Unit =
3679+
assert(tl.isDeclaredVarianceLambda)
36683680
assert(myVariance == UndefinedFlags)
36693681
myVariance = v
3670-
def givenVariance: Variance =
3682+
3683+
/** The declared variance of this parameter.
3684+
* @pre the containing lambda is a isDeclaredVarianceLambda
3685+
*/
3686+
def declaredVariance: Variance =
3687+
assert(tl.isDeclaredVarianceLambda)
36713688
assert(myVariance != UndefinedFlags)
36723689
myVariance
36733690

3691+
/** The declared or structural variance of this parameter. */
36743692
def paramVariance(implicit ctx: Context): Variance =
36753693
if myVariance == UndefinedFlags then
36763694
tl match

compiler/src/dotty/tools/dotc/core/Variances.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,19 @@ object Variances {
110110
}
111111

112112
def setStructuralVariances(lam: HKTypeLambda)(implicit ctx: Context): Unit =
113-
assert(!lam.isVariantLambda)
113+
assert(!lam.isDeclaredVarianceLambda)
114114
for param <- lam.typeParams do param.storedVariance = Bivariant
115-
object traverse extends TypeAccumulator[Unit] {
116-
def apply(x: Unit, t: Type): Unit = t match
115+
object narrowVariances extends TypeTraverser {
116+
def traverse(t: Type): Unit = t match
117117
case t: TypeParamRef if t.binder eq lam =>
118118
lam.typeParams(t.paramNum).storedVariance &= varianceFromInt(variance)
119119
case _ =>
120-
foldOver(x, t)
120+
traverseChildren(t)
121121
}
122122
// Note: Normally, we'd need to repeat `traverse` until a fixpoint is reached.
123-
// But since recursive lambdas can only appear in bounds, and bound never have
123+
// But since recursive lambdas can only appear in bounds, and bounds never have
124124
// structural variances, a single traversal is enough.
125-
traverse((), lam.resType)
125+
narrowVariances.traverse(lam.resType)
126126

127127
/** Does variance `v1` conform to variance `v2`?
128128
* This is the case if the variances are the same or `sym` is nonvariant.
@@ -137,12 +137,12 @@ object Variances {
137137

138138
/** Do the variances of type parameters `tparams1` conform to the variances
139139
* of corresponding type parameters `tparams2`?
140-
* This is only the case of `tparams1` and `tparams2` have the same length.
140+
* This is only the case if `tparams1` and `tparams2` have the same length.
141141
*/
142142
def variancesConform(tparams1: List[TypeParamInfo], tparams2: List[TypeParamInfo])(implicit ctx: Context): Boolean =
143143
val needsDetailedCheck = tparams2 match
144144
case (_: Symbol) :: _ => true
145-
case LambdaParam(tl: HKTypeLambda, _) :: _ => tl.isVariantLambda
145+
case LambdaParam(tl: HKTypeLambda, _) :: _ => tl.isDeclaredVarianceLambda
146146
case _ => false
147147
if needsDetailedCheck then tparams1.corresponds(tparams2)(varianceConforms)
148148
else tparams1.hasSameLengthAs(tparams2)

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ class TreePickler(pickler: TastyPickler) {
135135
}
136136

137137
def pickleVariances(tp: Type)(given Context): Unit = tp match
138-
case tp: HKTypeLambda if tp.isVariantLambda =>
139-
for v <- tp.givenVariances do
138+
case tp: HKTypeLambda if tp.isDeclaredVarianceLambda =>
139+
for v <- tp.declaredVariances do
140140
writeByte(
141141
if v.is(Covariant) then COVARIANT
142142
else if v.is(Contravariant) then CONTRAVARIANT

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
331331
def decompose(tp: Type) = tp.stripTypeVar match
332332
case lam: HKTypeLambda =>
333333
val names =
334-
if lam.isVariantLambda then
335-
lam.paramNames.lazyZip(lam.givenVariances).map((name, v) =>
334+
if lam.isDeclaredVarianceLambda then
335+
lam.paramNames.lazyZip(lam.declaredVariances).map((name, v) =>
336336
varianceSign(v) + name)
337337
else lam.paramNames
338338
(names.mkString("[", ", ", "]"), lam.resType)

compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object VarianceChecker {
2727
*/
2828
def checkLambda(tree: tpd.LambdaTypeTree, bounds: TypeBounds)(implicit ctx: Context): Unit =
2929
def checkType(tpe: Type): Unit = tpe match
30-
case tl: HKTypeLambda if tl.isVariantLambda =>
30+
case tl: HKTypeLambda if tl.isDeclaredVarianceLambda =>
3131
val checkOK = new TypeAccumulator[Boolean] {
3232
def paramVarianceSign(tref: TypeParamRef) =
3333
tl.typeParams(tref.paramNum).paramVarianceSign

docs/docs/reference/new-types/type-lambdas-spec.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ its eta expansion. I.e, `List = [X] =>> List[X]`. This allows type constructors
3838

3939
## Relationship with Parameterized Type Definitions
4040

41-
type F[X] <: List[F[X]]
42-
4341
A parameterized type definition
4442
```scala
4543
type T[X] = R
@@ -53,7 +51,7 @@ it is checked that the variance annotations are satisfied by the type lambda.
5351
For instance,
5452
```scala
5553
type F2[A, +B] = A => B
56-
```scala
54+
```
5755
expands to
5856
```scala
5957
type F2 = [A, B] =>> A => B

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ Standard-Section: "ASTs" TopLevelStat*
211211
OPEN -- an open class
212212
Annotation
213213
214-
Variance = SEALED -- invariant
214+
Variance = STABLE -- invariant
215215
| COVARIANT
216216
| CONTRAVARIANT
217217

0 commit comments

Comments
 (0)