Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: lib/type_algebra.dart

Issue 2439043002: Introduce Substitution class and Supertype class. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/transformations/treeshaker.dart ('k') | lib/type_propagation/builder.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 library kernel.type_algebra; 4 library kernel.type_algebra;
5 5
6 import 'ast.dart'; 6 import 'ast.dart';
7 import 'type_algebra.dart' as toplevel;
8 7
9 /// Returns a type where all occurrences of the given type parameters have been 8 /// Returns a type where all occurrences of the given type parameters have been
10 /// replaced with the corresponding types. 9 /// replaced with the corresponding types.
11 /// 10 ///
12 /// This will copy only the subterms of [type] that contain substituted 11 /// This will copy only the subterms of [type] that contain substituted
13 /// variables; all other [DartType] objects will be reused. 12 /// variables; all other [DartType] objects will be reused.
14 /// 13 ///
15 /// In particular, if no variables were substituted, this is guaranteed to 14 /// In particular, if no variables were substituted, this is guaranteed to
16 /// return the [type] instance (not a copy), so the caller may use [identical] 15 /// return the [type] instance (not a copy), so the caller may use [identical]
17 /// to efficiently check if a distinct type was created. 16 /// to efficiently check if a distinct type was created.
18 DartType substitute(DartType type, Map<TypeParameter, DartType> substitution) { 17 DartType substitute(DartType type, Map<TypeParameter, DartType> substitution) {
19 if (substitution.isEmpty) return type; 18 if (substitution.isEmpty) return type;
20 return new _TypeSubstitutor(substitution, substitution).visit(type); 19 return Substitution.fromMap(substitution).substituteType(type);
21 } 20 }
22 21
23 /// Returns a mapping from the type parameters declared on the class of [type] 22 /// Returns a mapping from the type parameters declared on the class of [type]
24 /// to the actual type arguments provided in [type]. 23 /// to the actual type arguments provided in [type].
25 /// 24 ///
26 /// This can be passed as argument to [substitute]. 25 /// This can be passed as argument to [substitute].
27 Map<TypeParameter, DartType> getSubstitutionMap(InterfaceType type) { 26 Map<TypeParameter, DartType> getSubstitutionMap(Supertype type) {
28 return type.typeArguments.isEmpty 27 return type.typeArguments.isEmpty
29 ? const <TypeParameter, DartType>{} 28 ? const <TypeParameter, DartType>{}
30 : new Map<TypeParameter, DartType>.fromIterables( 29 : new Map<TypeParameter, DartType>.fromIterables(
31 type.classNode.typeParameters, type.typeArguments); 30 type.classNode.typeParameters, type.typeArguments);
32 } 31 }
33 32
34 /// Like [substitute], but the substitution map is given as a list of keys
35 /// and a list of values.
36 DartType substitutePairwise(DartType type, List<TypeParameter> typeParameters,
37 List<DartType> typeArguments) {
38 if (typeParameters.isEmpty) return type;
39 // TODO: Investigate if it is more efficient to implement substitution based
40 // on parallel pairwise lists instead of Maps.
41 return substitute(
42 type,
43 new Map<TypeParameter, DartType>.fromIterables(
44 typeParameters, typeArguments));
45 }
46
47 /// Returns [type] where the type parameters declared on the class of [thisType]
48 /// have been substituted with the type arguments provided in [thisType].
49 ///
50 /// For example, if [thisType] is `Iterable<String>`, this will substitute
51 /// `Iterable::E` with `String` in [type].
52 ///
53 /// If `thisType` is null, nothing is substituted.
54 DartType substituteThisType(DartType type, InterfaceType thisType) {
55 if (thisType == null) return type;
56 return substitutePairwise(
57 type, thisType.classNode.typeParameters, thisType.typeArguments);
58 }
59
60 /// Returns a type where all occurrences of the given type parameters have been
61 /// replaced with the corresponding upper or lower bound, depending on the
62 /// variance of the context where it occurs.
63 ///
64 /// For example the type `(T) => T` with the bounds `bottom <: T <: num`
65 /// becomes `(bottom) => num` (in this example, `num` is the upper bound,
66 /// and `bottom` is the lower bound).
67 ///
68 /// This is a way to obtain an upper bound for a type while eliminating all
69 /// references to certain type variables.
70 ///
71 /// This will copy only the subterms of [type] that contain substituted
72 /// variables; all other [DartType] objects will be reused.
73 ///
74 /// In particular, if no variables were substituted, this is guaranteed to
75 /// return the [type] instance (not a copy), so the caller may use [identical]
76 /// to efficiently check if a distinct type was created.
77 DartType substituteBounds(
78 DartType type,
79 Map<TypeParameter, DartType> upperBounds,
80 Map<TypeParameter, DartType> lowerBounds) {
81 assert(upperBounds.length == lowerBounds.length);
82 if (upperBounds.isEmpty) return type;
83 return new _TypeSubstitutor(upperBounds, lowerBounds).visit(type);
84 }
85
86 /// Like [substitute], except when a type in the [substitution] map references 33 /// Like [substitute], except when a type in the [substitution] map references
87 /// another substituted type variable, the mapping for that type is recursively 34 /// another substituted type variable, the mapping for that type is recursively
88 /// inserted. 35 /// inserted.
89 /// 36 ///
90 /// For example `Set<G>` substituted with `{T -> String, G -> List<T>}` results 37 /// For example `Set<G>` substituted with `{T -> String, G -> List<T>}` results
91 /// in `Set<List<String>>`. 38 /// in `Set<List<String>>`.
92 /// 39 ///
93 /// Returns `null` if the substitution map contains a cycle reachable from a 40 /// Returns `null` if the substitution map contains a cycle reachable from a
94 /// type variable in [type] (the resulting type would be infinite). 41 /// type variable in [type] (the resulting type would be infinite).
95 /// 42 ///
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 86
140 /// Generates a fresh copy of the given type parameters, with their bounds 87 /// Generates a fresh copy of the given type parameters, with their bounds
141 /// substituted to reference the new parameters. 88 /// substituted to reference the new parameters.
142 /// 89 ///
143 /// The returned object contains the fresh type parameter list as well as a 90 /// The returned object contains the fresh type parameter list as well as a
144 /// mapping to be used for replacing other types to use the new type parameters. 91 /// mapping to be used for replacing other types to use the new type parameters.
145 FreshTypeParameters getFreshTypeParameters(List<TypeParameter> typeParameters) { 92 FreshTypeParameters getFreshTypeParameters(List<TypeParameter> typeParameters) {
146 var freshParameters = new List<TypeParameter>.generate( 93 var freshParameters = new List<TypeParameter>.generate(
147 typeParameters.length, (i) => new TypeParameter(typeParameters[i].name), 94 typeParameters.length, (i) => new TypeParameter(typeParameters[i].name),
148 growable: true); 95 growable: true);
149 var substitution = <TypeParameter, DartType>{}; 96 var map = <TypeParameter, DartType>{};
150 for (int i = 0; i < typeParameters.length; ++i) { 97 for (int i = 0; i < typeParameters.length; ++i) {
151 substitution[typeParameters[i]] = new TypeParameterType(freshParameters[i]); 98 map[typeParameters[i]] = new TypeParameterType(freshParameters[i]);
152 freshParameters[i].bound = 99 freshParameters[i].bound = substitute(typeParameters[i].bound, map);
153 substitute(typeParameters[i].bound, substitution);
154 } 100 }
155 return new FreshTypeParameters(freshParameters, substitution); 101 return new FreshTypeParameters(freshParameters, Substitution.fromMap(map));
156 } 102 }
157 103
158 class FreshTypeParameters { 104 class FreshTypeParameters {
159 final List<TypeParameter> freshTypeParameters; 105 final List<TypeParameter> freshTypeParameters;
160 final Map<TypeParameter, DartType> substitution; 106 final Substitution substitution;
161 107
162 FreshTypeParameters(this.freshTypeParameters, this.substitution); 108 FreshTypeParameters(this.freshTypeParameters, this.substitution);
163 109
164 DartType substitute(DartType type) => toplevel.substitute(type, substitution); 110 DartType substitute(DartType type) => substitution.substituteType(type);
111
112 Supertype substituteSuper(Supertype type) {
113 return substitution.substituteSupertype(type);
114 }
165 } 115 }
166 116
167 // ------------------------------------------------------------------------ 117 // ------------------------------------------------------------------------
168 // IMPLEMENTATION 118 // IMPLEMENTATION
169 // ------------------------------------------------------------------------ 119 // ------------------------------------------------------------------------
170 120
171 class _TypeSubstitutor extends DartTypeVisitor<DartType> { 121 abstract class Substitution {
172 final Map<TypeParameter, DartType> upperBounds; 122 /// Substitutes each parameter to the type it maps to in [map].
173 final Map<TypeParameter, DartType> lowerBounds; 123 static Substitution fromMap(Map<TypeParameter, DartType> map) {
124 if (map.isEmpty) return _NullSubstitution.instance;
125 return new _MapSubstitution(map, map);
126 }
127
128 /// Substitutes all occurrences of the given type parameters with the
129 /// corresponding upper or lower bound, depending on the variance of the
130 /// context where it occurs.
131 ///
132 /// For example the type `(T) => T` with the bounds `bottom <: T <: num`
133 /// becomes `(bottom) => num` (in this example, `num` is the upper bound,
134 /// and `bottom` is the lower bound).
135 ///
136 /// This is a way to obtain an upper bound for a type while eliminating all
137 /// references to certain type variables.
138 static Substitution fromUpperAndLowerBounds(
139 Map<TypeParameter, DartType> upper, Map<TypeParameter, DartType> lower) {
140 if (upper.isEmpty && lower.isEmpty) return _NullSubstitution.instance;
141 return new _MapSubstitution(upper, lower);
142 }
143
144 /// Substitutes the type parameters on the class of [supertype] with the
145 /// type arguments provided in [supertype].
146 static Substitution fromSupertype(Supertype supertype) {
147 if (supertype.typeArguments.isEmpty) return _NullSubstitution.instance;
148 return fromMap(new Map<TypeParameter, DartType>.fromIterables(
149 supertype.classNode.typeParameters, supertype.typeArguments));
150 }
151
152 /// Substitutes the type parameters on the class of [type] with the
153 /// type arguments provided in [type].
154 static Substitution fromInterfaceType(InterfaceType type) {
155 if (type.typeArguments.isEmpty) return _NullSubstitution.instance;
156 return fromMap(new Map<TypeParameter, DartType>.fromIterables(
157 type.classNode.typeParameters, type.typeArguments));
158 }
159
160 /// Substitutes the Nth parameter in [parameters] with the Nth type in
161 /// [types].
162 static Substitution fromPairs(
163 List<TypeParameter> parameters, List<DartType> types) {
164 // TODO(asgerf): Investigate if it is more efficient to implement
165 // substitution based on parallel pairwise lists instead of Maps.
166 assert(parameters.length == types.length);
167 if (parameters.isEmpty) return _NullSubstitution.instance;
168 return fromMap(
169 new Map<TypeParameter, DartType>.fromIterables(parameters, types));
170 }
171
172 DartType getSubstitute(TypeParameter parameter, bool upperBound);
173
174 DartType substituteType(DartType node) {
175 return new _TopSubstitutor(this).visit(node);
176 }
177
178 Supertype substituteSupertype(Supertype node) {
179 return new _TopSubstitutor(this).visitSupertype(node);
180 }
181 }
182
183 class _NullSubstitution extends Substitution {
184 static final _NullSubstitution instance = new _NullSubstitution();
185
186 DartType getSubstitute(TypeParameter parameter, bool upperBound) {
187 return new TypeParameterType(parameter);
188 }
189
190 @override
191 DartType substituteType(DartType node) => node;
192
193 @override
194 Supertype substituteSupertype(Supertype node) => node;
195 }
196
197 class _MapSubstitution extends Substitution {
198 final Map<TypeParameter, DartType> upper;
199 final Map<TypeParameter, DartType> lower;
200
201 _MapSubstitution(this.upper, this.lower);
202
203 DartType getSubstitute(TypeParameter parameter, bool upperBound) {
204 return upperBound ? upper[parameter] : lower[parameter];
205 }
206 }
207
208 class _TopSubstitutor extends _TypeSubstitutor {
209 final Substitution substitution;
210
211 _TopSubstitutor(this.substitution) : super(null);
212
213 DartType lookup(TypeParameter parameter, bool upperBound) {
214 return substitution.getSubstitute(parameter, upperBound);
215 }
216
217 TypeParameter freshTypeParameter(TypeParameter node) {
218 throw 'Create a fresh environment first';
219 }
220 }
221
222 class _InnerTypeSubstitutor extends _TypeSubstitutor {
223 final Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
224
225 _InnerTypeSubstitutor(_TypeSubstitutor outer) : super(outer);
226
227 DartType lookup(TypeParameter parameter, bool upperBound) {
228 return substitution[parameter];
229 }
230
231 TypeParameter freshTypeParameter(TypeParameter node) {
232 var fresh = new TypeParameter(node.name);
233 substitution[node] = new TypeParameterType(fresh);
234 fresh.bound = visit(node.bound);
235 return fresh;
236 }
237 }
238
239 abstract class _TypeSubstitutor extends DartTypeVisitor<DartType> {
174 final _TypeSubstitutor outer; 240 final _TypeSubstitutor outer;
175 bool covariantContext = true; 241 bool covariantContext = true;
176 242
243 _TypeSubstitutor(this.outer) {
244 covariantContext = outer == null ? true : outer.covariantContext;
245 }
246
247 DartType lookup(TypeParameter parameter, bool upperBound);
248
177 /// The number of times a variable from this environment has been used in 249 /// The number of times a variable from this environment has been used in
178 /// a substitution. 250 /// a substitution.
179 /// 251 ///
180 /// There is a strict requirement that we must return the same instance for 252 /// There is a strict requirement that we must return the same instance for
181 /// types that were not altered by the substitution. This counter lets us 253 /// types that were not altered by the substitution. This counter lets us
182 /// check quickly if anything happened in a substitution. 254 /// check quickly if anything happened in a substitution.
183 int useCounter = 0; 255 int useCounter = 0;
184 256
185 _TypeSubstitutor(this.upperBounds, this.lowerBounds, [this.outer]) { 257 _InnerTypeSubstitutor newInnerEnvironment() {
186 covariantContext = outer == null ? true : outer.covariantContext; 258 return new _InnerTypeSubstitutor(this);
187 }
188
189 _TypeSubstitutor newInnerEnvironment() {
190 var map = <TypeParameter, DartType>{};
191 return new _TypeSubstitutor(map, map, this);
192 } 259 }
193 260
194 void invertVariance() { 261 void invertVariance() {
195 covariantContext = !covariantContext; 262 covariantContext = !covariantContext;
196 } 263 }
197 264
265 Supertype visitSupertype(Supertype node) {
266 if (node.typeArguments.isEmpty) return node;
267 int before = useCounter;
268 var typeArguments = node.typeArguments.map(visit).toList();
269 if (useCounter == before) return node;
270 return new Supertype(node.classNode, typeArguments);
271 }
272
198 DartType visit(DartType node) => node.accept(this); 273 DartType visit(DartType node) => node.accept(this);
199 274
200 DartType visitInvalidType(InvalidType node) => node; 275 DartType visitInvalidType(InvalidType node) => node;
201 DartType visitDynamicType(DynamicType node) => node; 276 DartType visitDynamicType(DynamicType node) => node;
202 DartType visitVoidType(VoidType node) => node; 277 DartType visitVoidType(VoidType node) => node;
203 DartType visitBottomType(BottomType node) => node; 278 DartType visitBottomType(BottomType node) => node;
204 279
205 DartType visitInterfaceType(InterfaceType node) { 280 DartType visitInterfaceType(InterfaceType node) {
206 if (node.typeArguments.isEmpty) return node; 281 if (node.typeArguments.isEmpty) return node;
207 int before = useCounter; 282 int before = useCounter;
208 var typeArguments = node.typeArguments.map(visit).toList(); 283 var typeArguments = node.typeArguments.map(visit).toList();
209 if (useCounter == before) return node; 284 if (useCounter == before) return node;
210 return new InterfaceType(node.classNode, typeArguments); 285 return new InterfaceType(node.classNode, typeArguments);
211 } 286 }
212 287
213 List<TypeParameter> freshTypeParameters(List<TypeParameter> parameters) { 288 List<TypeParameter> freshTypeParameters(List<TypeParameter> parameters) {
214 if (parameters.isEmpty) return const <TypeParameter>[]; 289 if (parameters.isEmpty) return const <TypeParameter>[];
215 return parameters.map(freshTypeParameter).toList(); 290 return parameters.map(freshTypeParameter).toList();
216 } 291 }
217 292
218 TypeParameter freshTypeParameter(TypeParameter node) { 293 TypeParameter freshTypeParameter(TypeParameter node);
219 var fresh = new TypeParameter(node.name);
220 upperBounds[node] = new TypeParameterType(fresh);
221 fresh.bound = visit(node.bound);
222 return fresh;
223 }
224 294
225 DartType visitFunctionType(FunctionType node) { 295 DartType visitFunctionType(FunctionType node) {
226 assert(!node.typeParameters.any(upperBounds.containsKey));
227 // This is a bit tricky because we have to generate fresh type parameters 296 // This is a bit tricky because we have to generate fresh type parameters
228 // in order to change the bounds. At the same time, if the function type 297 // in order to change the bounds. At the same time, if the function type
229 // was unaltered, we have to return the [node] object (not a copy!). 298 // was unaltered, we have to return the [node] object (not a copy!).
230 // Substituting a type for a fresh type variable should not be confused with 299 // Substituting a type for a fresh type variable should not be confused with
231 // a "real" substitution. 300 // a "real" substitution.
232 // 301 //
233 // Create an inner environment to generate fresh type parameters. The use 302 // Create an inner environment to generate fresh type parameters. The use
234 // counter on the inner environment tells if the fresh type parameters have 303 // counter on the inner environment tells if the fresh type parameters have
235 // any uses, but does not tell if the resulting function type is distinct. 304 // any uses, but does not tell if the resulting function type is distinct.
236 // Our own use counter will get incremented if something from our 305 // Our own use counter will get incremented if something from our
(...skipping 23 matching lines...) Expand all
260 while (node != target) { 329 while (node != target) {
261 ++node.useCounter; 330 ++node.useCounter;
262 node = node.outer; 331 node = node.outer;
263 } 332 }
264 ++target.useCounter; 333 ++target.useCounter;
265 } 334 }
266 335
267 DartType getSubstitute(TypeParameter variable) { 336 DartType getSubstitute(TypeParameter variable) {
268 var environment = this; 337 var environment = this;
269 while (environment != null) { 338 while (environment != null) {
270 var replacement = covariantContext 339 var replacement = environment.lookup(variable, covariantContext);
271 ? environment.upperBounds[variable]
272 : environment.lowerBounds[variable];
273 if (replacement != null) { 340 if (replacement != null) {
274 bumpCountersUntil(environment); 341 bumpCountersUntil(environment);
275 return replacement; 342 return replacement;
276 } 343 }
277 environment = environment.outer; 344 environment = environment.outer;
278 } 345 }
279 return null; 346 return null;
280 } 347 }
281 348
282 DartType visitTypeParameterType(TypeParameterType node) { 349 DartType visitTypeParameterType(TypeParameterType node) {
283 return getSubstitute(node.parameter) ?? node; 350 return getSubstitute(node.parameter) ?? node;
284 } 351 }
285 } 352 }
286 353
287 class _DeepTypeSubstitutor extends _TypeSubstitutor { 354 class _DeepTypeSubstitutor extends _InnerTypeSubstitutor {
288 int depth = 0; 355 int depth = 0;
289 bool isInfinite = false; 356 bool isInfinite = false;
290 357
291 _DeepTypeSubstitutor(Map<TypeParameter, DartType> substitution, 358 _DeepTypeSubstitutor(Map<TypeParameter, DartType> substitution,
292 [_DeepTypeSubstitutor outer]) 359 [_DeepTypeSubstitutor outer])
293 : super(substitution, substitution, outer); 360 : super(outer) {
361 this.substitution.addAll(substitution);
362 }
294 363
295 @override 364 @override
296 _TypeSubstitutor newInnerEnvironment() { 365 _TypeSubstitutor newInnerEnvironment() {
297 return new _DeepTypeSubstitutor(<TypeParameter, DartType>{}, this); 366 return new _DeepTypeSubstitutor(<TypeParameter, DartType>{}, this);
298 } 367 }
299 368
300 @override 369 @override
301 DartType visitTypeParameterType(TypeParameterType node) { 370 DartType visitTypeParameterType(TypeParameterType node) {
302 DartType replacement = getSubstitute(node.parameter); 371 DartType replacement = getSubstitute(node.parameter);
303 if (replacement == null) return node; 372 if (replacement == null) return node;
304 if (isInfinite) return replacement; 373 if (isInfinite) return replacement;
305 ++depth; 374 ++depth;
306 if (depth > upperBounds.length) { 375 if (depth > substitution.length) {
307 isInfinite = true; 376 isInfinite = true;
308 --depth; 377 --depth;
309 return replacement; 378 return replacement;
310 } else { 379 } else {
311 replacement = visit(replacement); 380 replacement = visit(replacement);
312 // Update type to the fully fleshed-out type. 381 // Update type to the fully fleshed-out type.
313 upperBounds[node.parameter] = replacement; 382 substitution[node.parameter] = replacement;
314 --depth; 383 --depth;
315 return replacement; 384 return replacement;
316 } 385 }
317 } 386 }
318 } 387 }
319 388
320 class _TypeUnification { 389 class _TypeUnification {
321 // Acyclic invariant: There are no cycles in the map, that is, all types can 390 // Acyclic invariant: There are no cycles in the map, that is, all types can
322 // be resolved to finite types by substituting all contained type variables. 391 // be resolved to finite types by substituting all contained type variables.
323 // 392 //
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 } 576 }
508 577
509 Map<dynamic/*=K*/, dynamic/*=W*/ > _mapValues/*<K,V,W>*/( 578 Map<dynamic/*=K*/, dynamic/*=W*/ > _mapValues/*<K,V,W>*/(
510 Map<dynamic/*=K*/, dynamic/*=V*/ > map, dynamic/*=W*/ fn(dynamic/*=V*/)) { 579 Map<dynamic/*=K*/, dynamic/*=V*/ > map, dynamic/*=W*/ fn(dynamic/*=V*/)) {
511 Map<dynamic/*=K*/, dynamic/*=W*/ > result = {}; 580 Map<dynamic/*=K*/, dynamic/*=W*/ > result = {};
512 map.forEach((key, value) { 581 map.forEach((key, value) {
513 result[key] = fn(value); 582 result[key] = fn(value);
514 }); 583 });
515 return result; 584 return result;
516 } 585 }
OLDNEW
« no previous file with comments | « lib/transformations/treeshaker.dart ('k') | lib/type_propagation/builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698