Skip to content

Commit 266b6df

Browse files
committed
1 parent bd5ab4b commit 266b6df

33 files changed

+549
-1115
lines changed

pkg/dev_compiler/lib/src/checker/checker.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,19 @@ class CodeChecker extends RecursiveAstVisitor {
468468
var initializer = variable.initializer;
469469
if (initializer != null) {
470470
variable.initializer = checkAssignment(initializer, dartType);
471+
} else if (_rules.maybePrimitiveType(dartType)) {
472+
var element = variable.element;
473+
if (element is FieldElement && !element.isStatic) {
474+
// Initialized - possibly implicitly - during construction.
475+
// Handle this via a runtime check during code generation.
476+
477+
// TODO(vsm): Detect statically whether this can fail and
478+
// report a static error (must fail) or warning (can fail).
479+
} else {
480+
var staticInfo =
481+
new InvalidVariableDeclaration(_rules, variable, dartType);
482+
_recordMessage(staticInfo);
483+
}
471484
}
472485
}
473486
}

pkg/dev_compiler/lib/src/checker/rules.dart

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ abstract class TypeRules {
2828
bool isNumType(DartType t) => t == provider.intType.superclass;
2929
bool isStringType(DartType t) => t == provider.stringType;
3030
bool isPrimitiveType(DartType t) => false;
31+
bool maybePrimitiveType(DartType t) => false;
3132

3233
StaticInfo checkAssignment(Expression expr, DartType t);
3334

@@ -84,10 +85,12 @@ class RestrictedRules extends TypeRules {
8485
final CheckerReporter _reporter;
8586
final bool covariantGenerics;
8687
final bool relaxedCasts;
88+
final List<DartType> _primitives;
8789

8890
RestrictedRules(TypeProvider provider, this._reporter,
8991
{this.covariantGenerics: true, this.relaxedCasts: true})
90-
: super(provider);
92+
: _primitives = [provider.intType, provider.doubleType],
93+
super(provider) {}
9194

9295
DartType getStaticType(Expression expr) {
9396
var type = expr.staticType;
@@ -96,7 +99,24 @@ class RestrictedRules extends TypeRules {
9699
return provider.dynamicType;
97100
}
98101

99-
bool isPrimitiveType(DartType t) => isIntType(t) || isDoubleType(t);
102+
bool isPrimitiveType(DartType t) => _primitives.contains(t);
103+
104+
bool maybePrimitiveType(DartType t) {
105+
// Return true iff t *may* be a primitive type.
106+
// If t is a generic type parameter, return true if it may be
107+
// instantiated as a primitive.
108+
if (isPrimitiveType(t)) {
109+
return true;
110+
} else if (t is TypeParameterType) {
111+
var bound = t.element.bound;
112+
if (bound == null) {
113+
return true;
114+
}
115+
return _primitives.any((DartType p) => isSubTypeOf(p, bound));
116+
} else {
117+
return false;
118+
}
119+
}
100120

101121
// TODO(leafp): Revisit this.
102122
bool isGroundType(DartType t) {
@@ -230,7 +250,10 @@ class RestrictedRules extends TypeRules {
230250

231251
// Null can be assigned to anything non-primitive.
232252
// FIXME: Can this be anything besides null?
233-
if (t1.isBottom) return !isPrimitiveType(t2);
253+
if (t1.isBottom) {
254+
// Return false iff t2 *may* be a primitive type.
255+
return !maybePrimitiveType(t2);
256+
}
234257
if (t2.isBottom) return false;
235258

236259
if (t2.isDynamic) return true;

pkg/dev_compiler/lib/src/codegen/js_codegen.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,11 @@ $name.prototype[Symbol.iterator] = function() {
473473
NodeList<ConstructorInitializer> initializers]) {
474474

475475
// Run field initializers if they can have side-effects.
476-
var unsetFields = new Map<String, Expression>();
476+
var unsetFields = new Map<String, VariableDeclaration>();
477477
for (var declaration in fields) {
478478
for (var field in declaration.fields.variables) {
479479
if (_isFieldInitConstant(field)) {
480-
unsetFields[field.name.name] = field.initializer;
480+
unsetFields[field.name.name] = field;
481481
} else {
482482
_visitNode(field, suffix: ';\n');
483483
}
@@ -510,12 +510,20 @@ $name.prototype[Symbol.iterator] = function() {
510510
}
511511

512512
// Initialize all remaining fields
513-
unsetFields.forEach((name, expression) {
513+
unsetFields.forEach((name, field) {
514514
out.write('this.$name = ');
515+
var expression = field.initializer;
515516
if (expression != null) {
516517
expression.accept(this);
517518
} else {
518-
out.write('null');
519+
var type = rules.elementType(field.element);
520+
if (rules.maybePrimitiveType(type)) {
521+
out.write('dart.as(null, ');
522+
_writeTypeName(type);
523+
out.write(')');
524+
} else {
525+
out.write('null');
526+
}
519527
}
520528
out.write(';\n');
521529
});

pkg/dev_compiler/lib/src/info.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,16 @@ class StaticTypeError extends StaticError {
354354

355355
String get message =>
356356
'Type check failed: $node ($baseType) is not of type $expectedType';
357+
}
357358

358-
Level get level => Level.SEVERE;
359+
class InvalidVariableDeclaration extends StaticError {
360+
final DartType expectedType;
361+
362+
InvalidVariableDeclaration(
363+
TypeRules rules, VariableDeclaration declaration, this.expectedType)
364+
: super(declaration);
365+
366+
String get message => 'Type check failed: null is not of type $expectedType';
359367
}
360368

361369
class InvalidRuntimeCheckError extends StaticError {

pkg/dev_compiler/test/checker/checker_test.dart

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,23 @@ main() {
3232
test('Primitives', () {
3333
testChecker({
3434
'/main.dart': '''
35+
int /*severe:InvalidVariableDeclaration*/a;
36+
double /*severe:InvalidVariableDeclaration*/b;
37+
num c;
38+
39+
class A {
40+
int a;
41+
double b;
42+
num c;
43+
44+
static int /*severe:InvalidVariableDeclaration*/x;
45+
static double /*severe:InvalidVariableDeclaration*/y;
46+
static num z;
47+
}
48+
3549
void main() {
36-
// TODO(vsm): This declaration should be an error.
37-
int x;
38-
// TODO(vsm): This declaration should be an error.
39-
double y;
50+
int /*severe:InvalidVariableDeclaration*/x;
51+
double /*severe:InvalidVariableDeclaration*/y;
4052
num z;
4153
bool b;
4254
@@ -66,6 +78,45 @@ main() {
6678
});
6779
});
6880

81+
test('Primitives and generics', () {
82+
testChecker({
83+
'/main.dart': '''
84+
class A<T> {
85+
// TODO(vsm): This needs a static info indicating a runtime
86+
// check at construction.
87+
T x;
88+
89+
// TODO(vsm): Should this be a different type of DownCast?
90+
T foo() => /*warning:DownCastLiteral*/null;
91+
92+
void bar() {
93+
int /*severe:InvalidVariableDeclaration*/x;
94+
num y;
95+
// TODO(vsm): This should be a runtime check:
96+
// Transformed to: T z = cast(null, T)
97+
T /*severe:InvalidVariableDeclaration*/z;
98+
}
99+
}
100+
101+
class B<T extends List> {
102+
T x;
103+
104+
// T cannot be primitive.
105+
T foo() => null;
106+
}
107+
108+
class C<T extends num> {
109+
// TODO(vsm): This needs a static info indicating a runtime
110+
// check at construction.
111+
T x;
112+
113+
// TODO(vsm): Should this be a different type of DownCast?
114+
T foo() => /*warning:DownCastLiteral*/null;
115+
}
116+
'''
117+
});
118+
});
119+
69120
test('Unbound variable', () {
70121
testChecker({
71122
'/main.dart': '''
@@ -96,8 +147,8 @@ main() {
96147
void main() {
97148
dynamic y;
98149
Object o;
99-
int i;
100-
double d;
150+
int i = 0;
151+
double d = 0.0;
101152
num n;
102153
A a;
103154
B b;
@@ -122,8 +173,8 @@ main() {
122173
void main() {
123174
dynamic y;
124175
Object o;
125-
int i;
126-
double d;
176+
int i = 0;
177+
double d = 0.0;
127178
num n;
128179
A a;
129180
B b;
@@ -148,8 +199,8 @@ main() {
148199
void main() {
149200
dynamic y;
150201
Object o;
151-
int i;
152-
double d;
202+
int i = 0;
203+
double d = 0.0;
153204
num n;
154205
A a;
155206
B b;
@@ -176,8 +227,8 @@ main() {
176227
void main() {
177228
dynamic y;
178229
Object o;
179-
int i;
180-
double d;
230+
int i = 0;
231+
double d = 0.0;
181232
num n;
182233
A a;
183234
B b;

pkg/dev_compiler/test/checker/inferred_type_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ main() {
8686
test('infer type on var from field', () {
8787
testChecker({
8888
'/main.dart': '''
89-
int x;
89+
int x = 0;
9090
9191
test1() {
9292
var a = x;
@@ -100,7 +100,7 @@ main() {
100100
c = 4;
101101
}
102102
103-
int y; // field def after use
103+
int y = 0; // field def after use
104104
final z = 42; // should infer `int`
105105
'''
106106
});

pkg/dev_compiler/test/codegen/constructors.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class I {
4747
}
4848

4949
class J {
50-
int nonInitialized;
50+
num nonInitialized;
5151
bool initialized;
5252

5353
J() : initialized = true;

pkg/dev_compiler/test/codegen/expect/_internal/_internal.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ var _internal;
9292
}
9393
singleWhere(test) {
9494
let length = this.length;
95-
let match = null;
95+
let match = dart.as(null, E);
9696
let matchFound = false;
9797
for (let i = 0; i < length; i++) {
9898
let element = this.elementAt(i);
@@ -282,7 +282,7 @@ var _internal;
282282
this._iterable = iterable;
283283
this._length = iterable.length;
284284
this._index = 0;
285-
this._current = null;
285+
this._current = dart.as(null, E);
286286
}
287287
get current() { return this._current; }
288288
moveNext() {
@@ -291,7 +291,7 @@ var _internal;
291291
throw new core.ConcurrentModificationError(this._iterable);
292292
}
293293
if (this._index >= length) {
294-
this._current = null;
294+
this._current = dart.as(null, E);
295295
return false;
296296
}
297297
this._current = this._iterable.elementAt(this._index);
@@ -344,15 +344,15 @@ var _internal;
344344
constructor(_iterator, _f) {
345345
this._iterator = _iterator;
346346
this._f = _f;
347-
this._current = null;
347+
this._current = dart.as(null, T);
348348
super();
349349
}
350350
moveNext() {
351351
if (this._iterator.moveNext()) {
352352
this._current = this._f(this._iterator.current);
353353
return true;
354354
}
355-
this._current = null;
355+
this._current = dart.as(null, T);
356356
return false;
357357
}
358358
get current() { return this._current; }
@@ -428,15 +428,15 @@ var _internal;
428428
this._iterator = _iterator;
429429
this._f = _f;
430430
this._currentExpansion = dart.as(new EmptyIterator(), core.Iterator$(T));
431-
this._current = null;
431+
this._current = dart.as(null, T);
432432
}
433433
_nextExpansion() {
434434
}
435435
get current() { return this._current; }
436436
moveNext() {
437437
if (this._currentExpansion === null) return false;
438438
while (!this._currentExpansion.moveNext()) {
439-
this._current = null;
439+
this._current = dart.as(null, T);
440440
if (this._iterator.moveNext()) {
441441
this._currentExpansion = null;
442442
this._currentExpansion = dart.as(this._f(this._iterator.current).iterator, core.Iterator$(T));
@@ -509,7 +509,7 @@ var _internal;
509509
return false;
510510
}
511511
get current() {
512-
if (this._remaining < 0) return null;
512+
if (this._remaining < 0) return dart.as(null, E);
513513
return this._iterator.current;
514514
}
515515
}
@@ -549,7 +549,7 @@ var _internal;
549549
return true;
550550
}
551551
get current() {
552-
if (this._isFinished) return null;
552+
if (this._isFinished) return dart.as(null, E);
553553
return this._iterator.current;
554554
}
555555
}
@@ -739,7 +739,7 @@ var _internal;
739739
constructor() {
740740
}
741741
moveNext() { return false; }
742-
get current() { return null; }
742+
get current() { return dart.as(null, E); }
743743
}
744744
return EmptyIterator;
745745
});

0 commit comments

Comments
 (0)