Skip to content

Commit 4e7920c

Browse files
committed
Handle dynamic as bottom inside of function type reps.
This changes the way we handle dynamic at runtime to be more correct and cleaner. We now simply emit dynamic as dynamic instead of as core.Object. There are now two ways to construct a function type: one can construct a fuzzy function type (the default), or a definite function type. The constructor for a fuzzy function type replaces all uses of dynamic with bottom. This function type is used for all type annotations. Definite function types do not replace dynamic with bottom. These only occur as the runtime type of actual functions, for which we really know the type. Because we now eagerly sort this out when we create the function type, the subtyping code doesn't need to deal with this. This allows some additional subtyping: closures which actually are typed to take dynamic would previously not have been allowed to be cast to something with a concrete argument type. Now this is allowed (see the change in runtime_tests.js for an example of this). This fixes #107. BUG= [email protected] Review URL: https://codereview.chromium.org/1195523002
1 parent 8d76f5f commit 4e7920c

32 files changed

+859
-765
lines changed

pkg/dev_compiler/lib/runtime/_classes.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,6 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
118118
throwError('requires ' + length + ' or 0 type arguments');
119119
}
120120
let args = slice.call(arguments);
121-
// TODO(leafp): This should really be core.Object for
122-
// consistency, but Object is not attached to core
123-
// until the entire core library has been processed,
124-
// which is too late.
125121
while (args.length < length) args.push(types.dynamic);
126122

127123
let value = resultMap;
@@ -175,7 +171,7 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
175171
if (sigObj === void 0) return void 0;
176172
let parts = sigObj[name];
177173
if (parts === void 0) return void 0;
178-
return types.functionType.apply(null, parts);
174+
return types.definiteFunctionType.apply(null, parts);
179175
}
180176

181177
/// Get the type of a constructor from a class using the stored signature
@@ -189,7 +185,7 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
189185
if (sigCtor === void 0) return void 0;
190186
let parts = sigCtor[name];
191187
if (parts === void 0) return void 0;
192-
return types.functionType.apply(null, parts);
188+
return types.definiteFunctionType.apply(null, parts);
193189
}
194190
exports.classGetConstructorType = _getConstructorType;
195191

@@ -230,7 +226,7 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
230226
for (let name of names) {
231227
rtti.tagMemoized(f[name], function() {
232228
let parts = f[_staticSig][name];
233-
return types.functionType.apply(null, parts);
229+
return types.definiteFunctionType.apply(null, parts);
234230
})
235231
}
236232
}

pkg/dev_compiler/lib/runtime/_rtti.js

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,38 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[
1919

2020
const slice = [].slice;
2121

22+
/**
23+
* Runtime type information. This module defines the mapping from
24+
* runtime objects to their runtime type information. See the types
25+
* module for the definition of how type information is represented.
26+
*
27+
* Runtime objects fall into four main categories:
28+
*
29+
* - Things represented by javascript primitives, such as
30+
* null, numbers, booleans, strings, and symbols. For these
31+
* we map directly from the javascript type (given by typeof)
32+
* to the appropriate class type from core, which serves as their
33+
* rtti.
34+
*
35+
* - Functions, which are represented by javascript functions.
36+
* Representations of Dart functions always have a
37+
* _runtimeType property attached to them with the appropriate
38+
* rtti.
39+
*
40+
* - Objects (instances) which are represented by instances of
41+
* javascript (ES6) classes. Their types are given by their
42+
* classes, and the rtti is accessed by projecting out their
43+
* constructor field.
44+
*
45+
* - Types objects, which are represented as described in the types
46+
* module. Types always have a _runtimeType property attached to
47+
* them with the appropriate rtti. The rtti for these is always
48+
* core.Type. TODO(leafp): consider the possibility that we can
49+
* reliably recognize type objects and map directly to core.Type
50+
* rather than attaching this property everywhere.
51+
*
52+
*/
53+
2254
/**
2355
*Tag a closure with a type, using one of three forms:
2456
* dart.fn(cls) marks cls has having no optional or named
@@ -27,6 +59,9 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[
2759
* runtime type as computed by func()
2860
* dart.fn(cls, rType, argsT, extras) marks cls as having the
2961
* runtime type dart.functionType(rType, argsT, extras)
62+
*
63+
* Note that since we are producing a type for a concrete function,
64+
* it is sound to use the definite arrow type.
3065
*/
3166
function fn(closure/* ...args*/) {
3267
// Closure and a lazy type constructor
@@ -38,18 +73,13 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[
3873
if (arguments.length == 1) {
3974
// No type arguments, it's all dynamic
4075
let len = closure.length;
41-
let build = () => {
42-
let args = Array.apply(null, new Array(len)).map(() => core.Object);
43-
return types.functionType(core.Object, args);
44-
};
45-
// We could be called before Object is defined.
46-
if (core.Object === void 0) return fn(closure, build);
47-
t = build();
76+
let args = Array.apply(null, new Array(len)).map(() => types.dynamic);
77+
t = types.definiteFunctionType(types.dynamic, args);
4878
} else {
4979
// We're passed the piecewise components of the function type,
5080
// construct it.
5181
let args = slice.call(arguments, 1);
52-
t = types.functionType.apply(null, args);
82+
t = types.definiteFunctionType.apply(null, args);
5383
}
5484
tag(closure, t);
5585
return closure;
@@ -87,8 +117,9 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[
87117

88118
function getFunctionType(obj) {
89119
// TODO(vsm): Encode this properly on the function for Dart-generated code.
90-
let args = Array.apply(null, new Array(obj.length)).map(() => core.Object);
91-
return types.functionType(types.bottom, args);
120+
let args =
121+
Array.apply(null, new Array(obj.length)).map(() => types.dynamic);
122+
return types.definiteFunctionType(types.bottom, args);
92123
}
93124

94125
/**

pkg/dev_compiler/lib/runtime/_types.js

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,32 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
1919
const copyProperties = dart_utils.copyProperties;
2020
const safeGetOwnProperty = dart_utils.safeGetOwnProperty;
2121

22+
/**
23+
* Types in dart are represented at runtime as follows.
24+
* - Normal nominal types, produced from classes, are represented
25+
* at runtime by the JS class of which they are an instance.
26+
* If the type is the result of instantiating a generic class,
27+
* then the "classes" module manages the association between the
28+
* instantiated class and the original class declaration
29+
* and the type arguments with which it was instantiated. This
30+
* assocation can be queried via the "classes" module".
31+
*
32+
* - All other types are represented as instances of class TypeRep,
33+
* defined in this module.
34+
* - Dynamic, Void, and Bottom are singleton instances of sentinal
35+
* classes.
36+
* - Function types are instances of subclasses of AbstractFunctionType.
37+
*
38+
* Function types are represented in one of two ways:
39+
* - As an instance of FunctionType. These are eagerly computed.
40+
* - As an instance of TypeDef. The TypeDef representation lazily
41+
* computes an instance of FunctionType, and delegates to that instance.
42+
*
43+
* All types satisfy the following interface:
44+
* get String name;
45+
* String toString();
46+
*
47+
*/
2248
class TypeRep extends rtti.LazyTagged(() => core.Type) {
2349
get name() {return this.toString();}
2450
}
@@ -90,12 +116,51 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
90116
}
91117

92118
class FunctionType extends AbstractFunctionType {
93-
constructor(returnType, args, optionals, named) {
119+
/**
120+
* Construct a function type. There are two arrow constructors,
121+
* distinguished by the "definite" flag.
122+
*
123+
* The fuzzy arrow (definite is false) treats any arguments
124+
* of type dynamic as having type bottom, and will always be
125+
* called with a dynamic invoke.
126+
*
127+
* The definite arrow (definite is true) leaves arguments unchanged.
128+
*
129+
* We eagerly canonize the argument types to avoid having to deal with
130+
* this logic in multiple places.
131+
*
132+
* TODO(leafp): Figure out how to present this to the user. How
133+
* should these be printed out?
134+
*/
135+
constructor(definite, returnType, args, optionals, named) {
94136
super();
137+
this.definite = definite;
95138
this.returnType = returnType;
96139
this.args = args;
97140
this.optionals = optionals;
98141
this.named = named;
142+
this._canonize();
143+
}
144+
_canonize() {
145+
if (this.definite) return;
146+
147+
function replace(a) {
148+
return (a == dynamicR) ? bottomR : a;
149+
}
150+
151+
this.args = this.args.map(replace);
152+
153+
if (this.optionals.length > 0) {
154+
this.optionals = this.optionals.map(replace);
155+
}
156+
157+
if (Object.keys(this.named).length > 0) {
158+
let r = {};
159+
for (let name of getOwnPropertyNames(this.named)) {
160+
r[name] = replace(this.named[name]);
161+
}
162+
this.named = r;
163+
}
99164
}
100165
}
101166

@@ -107,6 +172,10 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
107172
this._functionType = null;
108173
}
109174

175+
get definite() {
176+
return this._functionType.definite;
177+
}
178+
110179
get name() {
111180
return this._name;
112181
}
@@ -135,7 +204,7 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
135204
}
136205
}
137206

138-
function functionType(returnType, args, extra) {
207+
function _functionType(definite, returnType, args, extra) {
139208
// TODO(vsm): Cache / memomize?
140209
let optionals;
141210
let named;
@@ -149,10 +218,27 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
149218
optionals = [];
150219
named = extra;
151220
}
152-
return new FunctionType(returnType, args, optionals, named);
221+
return new FunctionType(definite, returnType, args, optionals, named);
222+
}
223+
224+
/**
225+
* Create a "fuzzy" function type. If any arguments are dynamic
226+
* they will be replaced with bottom.
227+
*/
228+
function functionType(returnType, args, extra) {
229+
return _functionType(false, returnType, args, extra);
153230
}
154231
exports.functionType = functionType;
155232

233+
/**
234+
* Create a definite function type. No substitution of dynamic for
235+
* bottom occurs.
236+
*/
237+
function definiteFunctionType(returnType, args, extra) {
238+
return _functionType(true, returnType, args, extra);
239+
}
240+
exports.definiteFunctionType = definiteFunctionType;
241+
156242
function typedef(name, closure) {
157243
return new Typedef(name, closure);
158244
}
@@ -218,7 +304,7 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
218304
}
219305

220306
for (let i = 0; i < args1.length; ++i) {
221-
if (!isSubtype_(args2[i], args1[i], true)) {
307+
if (!isSubtype_(args2[i], args1[i])) {
222308
return false;
223309
}
224310
}
@@ -232,13 +318,13 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
232318

233319
let j = 0;
234320
for (let i = args1.length; i < args2.length; ++i, ++j) {
235-
if (!isSubtype_(args2[i], optionals1[j], true)) {
321+
if (!isSubtype_(args2[i], optionals1[j])) {
236322
return false;
237323
}
238324
}
239325

240326
for (let i = 0; i < optionals2.length; ++i, ++j) {
241-
if (!isSubtype_(optionals2[i], optionals1[j], true)) {
327+
if (!isSubtype_(optionals2[i], optionals1[j])) {
242328
return false;
243329
}
244330
}
@@ -254,7 +340,7 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
254340
if (n1 === void 0) {
255341
return false;
256342
}
257-
if (!isSubtype_(n2, n1, true)) {
343+
if (!isSubtype_(n2, n1)) {
258344
return false;
259345
}
260346
}
@@ -298,33 +384,26 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
298384
}
299385
exports.isSubtype = isSubtype;
300386

301-
function _isBottom(type, dynamicIsBottom) {
302-
return (type == dynamicR && dynamicIsBottom) || type == bottomR;
387+
function _isBottom(type) {
388+
return type == bottomR;
303389
}
304390

305-
function _isTop(type, dynamicIsBottom) {
306-
return type == core.Object || (type == dynamicR && !dynamicIsBottom);
391+
function _isTop(type) {
392+
return type == core.Object || (type == dynamicR);
307393
}
308394

309-
function isSubtype_(t1, t2, opt_dynamicIsBottom) {
310-
let dynamicIsBottom =
311-
opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom;
312-
395+
function isSubtype_(t1, t2) {
313396
t1 = canonicalType(t1);
314397
t2 = canonicalType(t2);
315398
if (t1 == t2) return true;
316399

317-
// In Dart, dynamic is effectively both top and bottom.
318-
// Here, we treat dynamic as one or the other depending on context,
319-
// but not both.
320-
321400
// Trivially true.
322-
if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) {
401+
if (_isTop(t2) || _isBottom(t1)) {
323402
return true;
324403
}
325404

326405
// Trivially false.
327-
if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) {
406+
if (_isTop(t1) || _isBottom(t2)) {
328407
return false;
329408
}
330409

@@ -413,16 +492,16 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[
413492
// TODO(vsm): Cache this if we start using it at runtime.
414493

415494
if (type instanceof AbstractFunctionType) {
416-
if (!_isTop(type.returnType, false)) return false;
495+
if (!_isTop(type.returnType)) return false;
417496
for (let i = 0; i < type.args.length; ++i) {
418-
if (!_isBottom(type.args[i], true)) return false;
497+
if (!_isBottom(type.args[i])) return false;
419498
}
420499
for (let i = 0; i < type.optionals.length; ++i) {
421-
if (!_isBottom(type.optionals[i], true)) return false;
500+
if (!_isBottom(type.optionals[i])) return false;
422501
}
423502
let names = getOwnPropertyNames(type.named);
424503
for (let i = 0; i < names.length; ++i) {
425-
if (!_isBottom(type.named[names[i]], true)) return false;
504+
if (!_isBottom(type.named[names[i]])) return false;
426505
}
427506
return true;
428507
}

pkg/dev_compiler/lib/runtime/dart/_foreign_helper.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,23 @@ dart_library.library('dart/_foreign_helper', null, /* Imports */[
3131
if (arg11 === void 0)
3232
arg11 = null;
3333
}
34-
dart.fn(JS, core.Object, [core.String, core.String], [core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object]);
34+
dart.fn(JS, dart.dynamic, [core.String, core.String], [dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic]);
3535
function JS_CURRENT_ISOLATE_CONTEXT() {
3636
}
37-
dart.fn(JS_CURRENT_ISOLATE_CONTEXT, () => dart.functionType(IsolateContext, []));
37+
dart.fn(JS_CURRENT_ISOLATE_CONTEXT, () => dart.definiteFunctionType(IsolateContext, []));
3838
class IsolateContext extends core.Object {}
3939
function JS_CALL_IN_ISOLATE(isolate, func) {
4040
}
41-
dart.fn(JS_CALL_IN_ISOLATE, core.Object, [core.Object, core.Function]);
41+
dart.fn(JS_CALL_IN_ISOLATE, dart.dynamic, [dart.dynamic, core.Function]);
4242
function DART_CLOSURE_TO_JS(func) {
4343
}
44-
dart.fn(DART_CLOSURE_TO_JS, core.Object, [core.Function]);
44+
dart.fn(DART_CLOSURE_TO_JS, dart.dynamic, [core.Function]);
4545
function RAW_DART_FUNCTION_REF(func) {
4646
}
47-
dart.fn(RAW_DART_FUNCTION_REF, core.Object, [core.Function]);
47+
dart.fn(RAW_DART_FUNCTION_REF, dart.dynamic, [core.Function]);
4848
function JS_SET_CURRENT_ISOLATE(isolate) {
4949
}
50-
dart.fn(JS_SET_CURRENT_ISOLATE, dart.void, [core.Object]);
50+
dart.fn(JS_SET_CURRENT_ISOLATE, dart.void, [dart.dynamic]);
5151
function JS_CREATE_ISOLATE() {
5252
}
5353
dart.fn(JS_CREATE_ISOLATE);
@@ -56,7 +56,7 @@ dart_library.library('dart/_foreign_helper', null, /* Imports */[
5656
dart.fn(JS_DART_OBJECT_CONSTRUCTOR);
5757
function JS_INTERCEPTOR_CONSTANT(type) {
5858
}
59-
dart.fn(JS_INTERCEPTOR_CONSTANT, core.Object, [core.Type]);
59+
dart.fn(JS_INTERCEPTOR_CONSTANT, dart.dynamic, [core.Type]);
6060
function JS_OPERATOR_IS_PREFIX() {
6161
}
6262
dart.fn(JS_OPERATOR_IS_PREFIX, core.String, []);
@@ -107,7 +107,7 @@ dart_library.library('dart/_foreign_helper', null, /* Imports */[
107107
dart.fn(JS_GET_NAME, core.String, [core.String]);
108108
function JS_EMBEDDED_GLOBAL(typeDescription, name) {
109109
}
110-
dart.fn(JS_EMBEDDED_GLOBAL, core.Object, [core.String, core.String]);
110+
dart.fn(JS_EMBEDDED_GLOBAL, dart.dynamic, [core.String, core.String]);
111111
function JS_GET_FLAG(name) {
112112
}
113113
dart.fn(JS_GET_FLAG, core.bool, [core.String]);

0 commit comments

Comments
 (0)