Skip to content

Commit 804706c

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Represent variance as an object rather than an integer
This has at least three advantages: - Type safety. Only a valid variance value can ever be passed in to any of the methods. - Encapsulation. If we ever need to change the representation of variance it will only need to be done in one place. - Brevity. Much of the code is shorter and easier to read. Change-Id: I82756f48aedd09232e9b8cdda155da83793a155f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124580 Reviewed-by: Kallen Tu <kallentu@google.com> Reviewed-by: Leaf Petersen <leafp@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
1 parent 782b048 commit 804706c

File tree

3 files changed

+110
-78
lines changed

3 files changed

+110
-78
lines changed

pkg/analyzer/lib/src/dart/resolver/variance.dart

Lines changed: 106 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -5,108 +5,142 @@
55
import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/dart/element/type.dart';
77

8-
/// Computes the variance of the [typeParameter] in the [type].
9-
int computeVariance(TypeParameterElement typeParameter, DartType type) {
10-
if (type is TypeParameterType) {
11-
if (type.element == typeParameter) {
12-
return Variance.covariant;
13-
} else {
14-
return Variance.unrelated;
15-
}
16-
} else if (type is InterfaceType) {
17-
var result = Variance.unrelated;
18-
for (var argument in type.typeArguments) {
19-
result = Variance.meet(
20-
result,
21-
computeVariance(typeParameter, argument),
22-
);
23-
}
24-
return result;
25-
} else if (type is FunctionType) {
26-
var result = computeVariance(typeParameter, type.returnType);
27-
28-
for (var parameter in type.typeFormals) {
29-
// If [parameter] is referenced in the bound at all, it makes the
30-
// variance of [parameter] in the entire type invariant. The invocation
31-
// of [computeVariance] below is made to simply figure out if [variable]
32-
// occurs in the bound.
33-
var bound = parameter.bound;
34-
if (bound != null &&
35-
computeVariance(typeParameter, bound) != Variance.unrelated) {
36-
result = Variance.invariant;
37-
}
38-
}
39-
40-
for (var parameter in type.parameters) {
41-
result = Variance.meet(
42-
result,
43-
Variance.combine(
44-
Variance.contravariant,
45-
computeVariance(typeParameter, parameter.type),
46-
),
47-
);
48-
}
49-
return result;
50-
}
51-
return Variance.unrelated;
52-
}
53-
54-
/// Value set for variance of a type parameter `X` in a type `T`.
8+
/// The variance of a type parameter `X` in a type `T`.
559
class Variance {
5610
/// Used when `X` does not occur free in `T`.
57-
static const int unrelated = 0;
11+
static const Variance _unrelated = Variance._(0);
5812

5913
/// Used when `X` occurs free in `T`, and `U <: V` implies `[U/X]T <: [V/X]T`.
60-
static const int covariant = 1;
14+
static const Variance _covariant = Variance._(1);
6115

6216
/// Used when `X` occurs free in `T`, and `U <: V` implies `[V/X]T <: [U/X]T`.
63-
static const int contravariant = 2;
17+
static const Variance _contravariant = Variance._(2);
6418

6519
/// Used when there exists a pair `U` and `V` such that `U <: V`, but
6620
/// `[U/X]T` and `[V/X]T` are incomparable.
67-
static const int invariant = 3;
21+
static const Variance _invariant = Variance._(3);
22+
23+
/// The encoding associated with the variance.
24+
final int _encoding;
25+
26+
/// Computes the variance of the [typeParameter] in the [type].
27+
factory Variance(TypeParameterElement typeParameter, DartType type) {
28+
if (type is TypeParameterType) {
29+
if (type.element == typeParameter) {
30+
return _covariant;
31+
} else {
32+
return _unrelated;
33+
}
34+
} else if (type is InterfaceType) {
35+
var result = _unrelated;
36+
for (var argument in type.typeArguments) {
37+
result = result.meet(
38+
Variance(typeParameter, argument),
39+
);
40+
}
41+
return result;
42+
} else if (type is FunctionType) {
43+
var result = Variance(typeParameter, type.returnType);
44+
45+
for (var parameter in type.typeFormals) {
46+
// If [parameter] is referenced in the bound at all, it makes the
47+
// variance of [parameter] in the entire type invariant. The invocation
48+
// of [computeVariance] below is made to simply figure out if [variable]
49+
// occurs in the bound.
50+
var bound = parameter.bound;
51+
if (bound != null && !Variance(typeParameter, bound).isUnrelated) {
52+
result = _invariant;
53+
}
54+
}
55+
56+
for (var parameter in type.parameters) {
57+
result = result.meet(
58+
_contravariant.combine(
59+
Variance(typeParameter, parameter.type),
60+
),
61+
);
62+
}
63+
return result;
64+
}
65+
return _unrelated;
66+
}
67+
68+
/// Initialize a newly created variance to have the given [encoding].
69+
const Variance._(this._encoding);
70+
71+
/// Return the variance with the given [encoding].
72+
factory Variance._fromEncoding(int encoding) {
73+
switch (encoding) {
74+
case 0:
75+
return _unrelated;
76+
case 1:
77+
return _covariant;
78+
case 2:
79+
return _contravariant;
80+
case 3:
81+
return _invariant;
82+
}
83+
throw new ArgumentError('Invalid encoding for variance: $encoding');
84+
}
85+
86+
/// Return `true` if this represents the case when `X` occurs free in `T`, and
87+
/// `U <: V` implies `[V/X]T <: [U/X]T`.
88+
bool get isContravariant => this == _contravariant;
89+
90+
/// Return `true` if this represents the case when `X` occurs free in `T`, and
91+
/// `U <: V` implies `[U/X]T <: [V/X]T`.
92+
bool get isCovariant => this == _covariant;
93+
94+
/// Return `true` if this represents the case when there exists a pair `U` and
95+
/// `V` such that `U <: V`, but `[U/X]T` and `[V/X]T` are incomparable.
96+
bool get isInvariant => this == _invariant;
97+
98+
/// Return `true` if this represents the case when `X` does not occur free in
99+
/// `T`.
100+
bool get isUnrelated => this == _unrelated;
68101

69102
/// Combines variances of `X` in `T` and `Y` in `S` into variance of `X` in
70103
/// `[Y/T]S`.
71104
///
72105
/// Consider the following examples:
73106
///
74-
/// * variance of `X` in `Function(X)` is [contravariant], variance of `Y`
75-
/// in `List<Y>` is [covariant], so variance of `X` in `List<Function(X)>` is
76-
/// [contravariant];
107+
/// * variance of `X` in `Function(X)` is contravariant, variance of `Y`
108+
/// in `List<Y>` is covariant, so variance of `X` in `List<Function(X)>` is
109+
/// contravariant;
77110
///
78-
/// * variance of `X` in `List<X>` is [covariant], variance of `Y` in
79-
/// `Function(Y)` is [contravariant], so variance of `X` in
80-
/// `Function(List<X>)` is [contravariant];
111+
/// * variance of `X` in `List<X>` is covariant, variance of `Y` in
112+
/// `Function(Y)` is contravariant, so variance of `X` in
113+
/// `Function(List<X>)` is contravariant;
81114
///
82-
/// * variance of `X` in `Function(X)` is [contravariant], variance of `Y` in
83-
/// `Function(Y)` is [contravariant], so variance of `X` in
84-
/// `Function(Function(X))` is [covariant];
115+
/// * variance of `X` in `Function(X)` is contravariant, variance of `Y` in
116+
/// `Function(Y)` is contravariant, so variance of `X` in
117+
/// `Function(Function(X))` is covariant;
85118
///
86119
/// * let the following be declared:
87120
///
88121
/// typedef F<Z> = Function();
89122
///
90-
/// then variance of `X` in `F<X>` is [unrelated], variance of `Y` in
91-
/// `List<Y>` is [covariant], so variance of `X` in `List<F<X>>` is
92-
/// [unrelated];
123+
/// then variance of `X` in `F<X>` is unrelated, variance of `Y` in
124+
/// `List<Y>` is covariant, so variance of `X` in `List<F<X>>` is
125+
/// unrelated;
93126
///
94127
/// * let the following be declared:
95128
///
96129
/// typedef G<Z> = Z Function(Z);
97130
///
98-
/// then variance of `X` in `List<X>` is [covariant], variance of `Y` in
99-
/// `G<Y>` is [invariant], so variance of `X` in `G<List<X>>` is [invariant].
100-
static int combine(int a, int b) {
101-
if (a == unrelated || b == unrelated) return unrelated;
102-
if (a == invariant || b == invariant) return invariant;
103-
return a == b ? covariant : contravariant;
131+
/// then variance of `X` in `List<X>` is covariant, variance of `Y` in
132+
/// `G<Y>` is invariant, so variance of `X` in `G<List<X>>` is invariant.
133+
Variance combine(Variance other) {
134+
if (isUnrelated || other.isUnrelated) return _unrelated;
135+
if (isInvariant || other.isInvariant) return _invariant;
136+
return this == other ? _covariant : _contravariant;
104137
}
105138

106-
/// Variance values form a lattice where [unrelated] is the top, [invariant]
107-
/// is the bottom, and [covariant] and [contravariant] are incomparable.
139+
/// Variance values form a lattice where unrelated is the top, invariant
140+
/// is the bottom, and covariant and contravariant are incomparable.
108141
/// [meet] calculates the meet of two elements of such lattice. It can be
109142
/// used, for example, to calculate the variance of a typedef type parameter
110143
/// if it's encountered on the RHS of the typedef multiple times.
111-
static int meet(int a, int b) => a | b;
144+
Variance meet(Variance other) =>
145+
Variance._fromEncoding(_encoding | other._encoding);
112146
}

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5304,9 +5304,8 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
53045304
void checkOne(DartType superInterface) {
53055305
if (superInterface != null) {
53065306
for (var typeParameter in _enclosingClass.typeParameters) {
5307-
var variance = computeVariance(typeParameter, superInterface);
5308-
if (variance == Variance.contravariant ||
5309-
variance == Variance.invariant) {
5307+
var variance = Variance(typeParameter, superInterface);
5308+
if (variance.isContravariant || variance.isInvariant) {
53105309
_errorReporter.reportErrorForElement(
53115310
CompileTimeErrorCode
53125311
.WRONG_TYPE_PARAMETER_VARIANCE_IN_SUPERINTERFACE,

pkg/analyzer/lib/src/task/strong/checker.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -910,9 +910,8 @@ class CodeChecker extends RecursiveAstVisitor {
910910
// Check if the return type uses a class type parameter contravariantly.
911911
bool needsCheck = false;
912912
for (var typeParameter in classElement.typeParameters) {
913-
var variance = computeVariance(typeParameter, rawReturnType);
914-
if (variance == Variance.contravariant ||
915-
variance == Variance.invariant) {
913+
var variance = Variance(typeParameter, rawReturnType);
914+
if (variance.isContravariant || variance.isInvariant) {
916915
needsCheck = true;
917916
break;
918917
}

0 commit comments

Comments
 (0)