@@ -36,6 +36,7 @@ import 'package:kernel/ast.dart'
36
36
ProcedureKind,
37
37
PropertyGet,
38
38
PropertySet,
39
+ ReturnStatement,
39
40
StaticGet,
40
41
SuperMethodInvocation,
41
42
SuperPropertyGet,
@@ -74,6 +75,7 @@ import '../fasta_codes.dart'
74
75
Message,
75
76
Template,
76
77
messageReturnFromVoidFunction,
78
+ messageReturnWithoutExpression,
77
79
messageVoidExpression,
78
80
noLength,
79
81
templateArgumentTypeNotAssignable,
@@ -203,10 +205,24 @@ class ClosureContext {
203
205
/// wrapping this type in `Stream` or `Iterator` , as appropriate.
204
206
DartType _inferredUnwrappedReturnOrYieldType;
205
207
208
+ /// Whether the function is an arrow function.
209
+ bool isArrow;
210
+
211
+ /// A list of return statements in functions whose return type is being
212
+ /// inferred.
213
+ ///
214
+ /// The returns are checked for validity after the return type is inferred.
215
+ List <ReturnStatement > returnStatements;
216
+
217
+ /// A list of return expression types in functions whose return type is
218
+ /// being inferred.
219
+ List <DartType > returnExpressionTypes;
220
+
206
221
factory ClosureContext (TypeInferrerImpl inferrer, AsyncMarker asyncMarker,
207
222
DartType returnContext, bool needToInferReturnType) {
208
223
assert (returnContext != null );
209
- DartType declaredReturnType = returnContext;
224
+ DartType declaredReturnType =
225
+ greatestClosure (inferrer.coreTypes, returnContext);
210
226
bool isAsync = asyncMarker == AsyncMarker .Async ||
211
227
asyncMarker == AsyncMarker .AsyncStar ;
212
228
bool isGenerator = asyncMarker == AsyncMarker .SyncStar ||
@@ -228,23 +244,86 @@ class ClosureContext {
228
244
}
229
245
230
246
ClosureContext ._(this .isAsync, this .isGenerator, this .returnOrYieldContext,
231
- this .declaredReturnType, this ._needToInferReturnType) {}
247
+ this .declaredReturnType, this ._needToInferReturnType) {
248
+ if (_needToInferReturnType) {
249
+ returnStatements = [];
250
+ returnExpressionTypes = [];
251
+ }
252
+ }
253
+
254
+ bool checkValidReturn (TypeInferrerImpl inferrer, DartType returnType,
255
+ ReturnStatement statement, DartType expressionType) {
256
+ if (statement.expression == null ) {
257
+ if (isAsync) {
258
+ returnType = inferrer.typeSchemaEnvironment.unfutureType (returnType);
259
+ }
260
+ if (returnType is ! VoidType &&
261
+ returnType is ! DynamicType &&
262
+ returnType != inferrer.coreTypes.nullClass.rawType) {
263
+ statement.expression = inferrer.helper.wrapInProblem (
264
+ new NullLiteral ()..fileOffset = statement.fileOffset,
265
+ messageReturnWithoutExpression,
266
+ noLength)
267
+ ..parent = statement;
268
+ return false ;
269
+ }
270
+ } else {
271
+ if (isAsync) {
272
+ returnType = inferrer.typeSchemaEnvironment.unfutureType (returnType);
273
+ expressionType =
274
+ inferrer.typeSchemaEnvironment.unfutureType (expressionType);
275
+ }
276
+ if (! isArrow && returnType is VoidType ) {
277
+ if (expressionType is ! VoidType &&
278
+ expressionType is ! DynamicType &&
279
+ expressionType != inferrer.coreTypes.nullClass.rawType) {
280
+ statement.expression = inferrer.helper.wrapInProblem (
281
+ statement.expression, messageReturnFromVoidFunction, noLength)
282
+ ..parent = statement;
283
+ return false ;
284
+ }
285
+ } else if (expressionType is VoidType ) {
286
+ if (returnType is ! VoidType &&
287
+ returnType is ! DynamicType &&
288
+ returnType != inferrer.coreTypes.nullClass.rawType) {
289
+ statement.expression = inferrer.helper.wrapInProblem (
290
+ statement.expression, messageVoidExpression, noLength)
291
+ ..parent = statement;
292
+ return false ;
293
+ }
294
+ }
295
+ }
296
+ return true ;
297
+ }
232
298
233
299
/// Updates the inferred return type based on the presence of a return
234
300
/// statement returning the given [type] .
235
- void handleReturn (TypeInferrerImpl inferrer, DartType type ,
236
- Expression expression, int fileOffset , bool isArrow) {
301
+ void handleReturn (TypeInferrerImpl inferrer, ReturnStatement statement ,
302
+ DartType type , bool isArrow) {
237
303
if (isGenerator) return ;
238
- if (inferrer.ensureAssignable (
239
- returnOrYieldContext, type, expression, fileOffset,
240
- isReturnFromAsync: isAsync,
241
- isReturn: true ,
242
- declaredReturnType: declaredReturnType,
243
- isArrow: isArrow) !=
244
- null ) {
245
- type = greatestClosure (inferrer.coreTypes, returnOrYieldContext);
304
+ // The first return we see tells us if we have an arrow function.
305
+ if (this .isArrow == null ) {
306
+ this .isArrow = isArrow;
307
+ } else {
308
+ assert (this .isArrow == isArrow);
246
309
}
310
+
247
311
if (_needToInferReturnType) {
312
+ // Add the return to a list to be checked for validity after we've
313
+ // inferred the return type.
314
+ returnStatements.add (statement);
315
+ returnExpressionTypes.add (type);
316
+
317
+ // The return expression has to be assignable to the return type
318
+ // expectation from the downwards inference context.
319
+ if (statement.expression != null &&
320
+ inferrer.ensureAssignable (returnOrYieldContext, type,
321
+ statement.expression, statement.fileOffset,
322
+ isReturnFromAsync: isAsync, isVoidAllowed: true ) !=
323
+ null ) {
324
+ // Not assignable, use the expectation.
325
+ type = greatestClosure (inferrer.coreTypes, returnOrYieldContext);
326
+ }
248
327
var unwrappedType = type;
249
328
if (isAsync) {
250
329
unwrappedType = inferrer.typeSchemaEnvironment.unfutureType (type);
@@ -256,6 +335,16 @@ class ClosureContext {
256
335
.getStandardUpperBound (
257
336
_inferredUnwrappedReturnOrYieldType, unwrappedType);
258
337
}
338
+ return ;
339
+ }
340
+
341
+ // If we are not inferring a type we can immediately check that the return
342
+ // is valid.
343
+ if (checkValidReturn (inferrer, declaredReturnType, statement, type) &&
344
+ statement.expression != null ) {
345
+ inferrer.ensureAssignable (returnOrYieldContext, type,
346
+ statement.expression, statement.fileOffset,
347
+ isReturnFromAsync: isAsync, isVoidAllowed: true );
259
348
}
260
349
}
261
350
@@ -294,13 +383,20 @@ class ClosureContext {
294
383
assert (_needToInferReturnType);
295
384
DartType inferredType =
296
385
inferrer.inferReturnType (_inferredUnwrappedReturnOrYieldType);
297
- if (! _analyzerSubtypeOf (inferrer, inferredType, returnOrYieldContext)) {
386
+ if (! inferrer.typeSchemaEnvironment
387
+ .isSubtypeOf (inferredType, returnOrYieldContext)) {
298
388
// If the inferred return type isn't a subtype of the context, we use the
299
389
// context.
300
390
inferredType = greatestClosure (inferrer.coreTypes, returnOrYieldContext);
301
391
}
302
392
303
- return _wrapAsyncOrGenerator (inferrer, inferredType);
393
+ inferredType = _wrapAsyncOrGenerator (inferrer, inferredType);
394
+ for (int i = 0 ; i < returnStatements.length; ++ i) {
395
+ checkValidReturn (inferrer, inferredType, returnStatements[i],
396
+ returnExpressionTypes[i]);
397
+ }
398
+
399
+ return inferredType;
304
400
}
305
401
306
402
DartType _wrapAsyncOrGenerator (TypeInferrerImpl inferrer, DartType type) {
@@ -316,19 +412,6 @@ class ClosureContext {
316
412
return type;
317
413
}
318
414
}
319
-
320
- static bool _analyzerSubtypeOf (
321
- TypeInferrerImpl inferrer, DartType subtype, DartType supertype) {
322
- if (supertype is VoidType ) {
323
- if (subtype is VoidType ) return true ;
324
- if (subtype is InterfaceType &&
325
- identical (subtype.classNode, inferrer.coreTypes.nullClass)) {
326
- return true ;
327
- }
328
- return false ;
329
- }
330
- return inferrer.typeSchemaEnvironment.isSubtypeOf (subtype, supertype);
331
- }
332
415
}
333
416
334
417
/// Enum denoting the kinds of contravariance check that might need to be
@@ -585,22 +668,9 @@ abstract class TypeInferrerImpl extends TypeInferrer {
585
668
Expression ensureAssignable (DartType expectedType, DartType actualType,
586
669
Expression expression, int fileOffset,
587
670
{bool isReturnFromAsync: false ,
588
- bool isReturn: false ,
589
- bool isVoidAllowed,
590
- bool isArrow: false ,
591
- DartType declaredReturnType,
671
+ bool isVoidAllowed: false ,
592
672
Template <Message Function (DartType , DartType )> template}) {
593
- isVoidAllowed ?? = isArrow;
594
673
assert (expectedType != null );
595
- if (isReturn &&
596
- ! isArrow &&
597
- ! isValidReturn (declaredReturnType, actualType, isReturnFromAsync)) {
598
- TreeNode parent = expression.parent;
599
- Expression errorNode = helper.wrapInProblem (
600
- expression, messageReturnFromVoidFunction, noLength);
601
- parent? .replaceChild (expression, errorNode);
602
- return errorNode;
603
- }
604
674
expectedType = greatestClosure (coreTypes, expectedType);
605
675
606
676
DartType initialExpectedType = expectedType;
@@ -617,28 +687,6 @@ abstract class TypeInferrerImpl extends TypeInferrer {
617
687
expectedType = futuredExpectedType;
618
688
}
619
689
}
620
- if (isReturn && ! isArrow) {
621
- if (expectedType is VoidType ) {
622
- isVoidAllowed = true ;
623
- if (actualType is ! VoidType &&
624
- actualType is ! DynamicType &&
625
- ! isNull (actualType)) {
626
- // Error: not assignable. Perform error recovery.
627
- TreeNode parent = expression.parent;
628
- Expression errorNode = helper.wrapInProblem (
629
- expression, messageReturnFromVoidFunction, noLength);
630
- parent? .replaceChild (expression, errorNode);
631
- return errorNode;
632
- }
633
- } else {
634
- DartType flattened = typeSchemaEnvironment.unfutureType (expectedType);
635
- if (flattened is VoidType ) {
636
- isVoidAllowed = true ;
637
- } else {
638
- isVoidAllowed = expectedType is DynamicType ;
639
- }
640
- }
641
- }
642
690
643
691
// We don't need to insert assignability checks when doing top level type
644
692
// inference since top level type inference only cares about the type that
@@ -721,63 +769,6 @@ abstract class TypeInferrerImpl extends TypeInferrer {
721
769
}
722
770
}
723
771
724
- bool isValidReturn (
725
- DartType returnType, DartType expressionType, bool isAsync) {
726
- final DartType t = returnType;
727
- final DartType s = expressionType;
728
- if (! isAsync) {
729
- if (t is DynamicType ) {
730
- // * `return exp;` where `exp` has static type `S` is a valid return if:
731
- // * `T` is `dynamic`
732
- return true ;
733
- }
734
-
735
- if (t is VoidType ) {
736
- // * `return exp;` where `exp` has static type `S` is a valid return if:
737
- // * `T` is `void`
738
- // * and `S` is `void` or `dynamic` or `Null`
739
- return s is VoidType || s is DynamicType || isNull (s);
740
- } else {
741
- // * `return exp;` where `exp` has static type `S` is a valid return if:
742
- // * `T` is not `void`
743
- // * and `S` is not `void`
744
- // * and `S` is assignable to `T`
745
- return s is ! VoidType ;
746
- }
747
- }
748
- final DartType flattenT = typeSchemaEnvironment.unfutureType (t);
749
-
750
- // * `return exp;` where `exp` has static type `S` is a valid return if:
751
- // * `flatten(T)` is `dynamic` or `Null`
752
- if (flattenT is DynamicType || isNull (flattenT)) return true ;
753
-
754
- // * `return exp;` where `exp` has static type `S` is a valid return if:
755
- // * `T` is `void`
756
- // * and `S` is `void`, `dynamic` or `Null`
757
- if (t is VoidType ) {
758
- if (s is VoidType || s is DynamicType || isNull (s)) return true ;
759
- } else {
760
- final DartType flattenS = typeSchemaEnvironment.unfutureType (s);
761
- // * `return exp;` where `exp` has static type `S` is a valid return if:
762
- // * `T` is not `void`
763
- // * `flatten(T)` is `void`
764
- // * and `flatten(S)` is `void`, `dynamic` or `Null`
765
- if (flattenT is VoidType ) {
766
- if (flattenS is VoidType ||
767
- flattenS is DynamicType ||
768
- isNull (flattenS)) {
769
- return true ;
770
- }
771
- }
772
-
773
- // * `return exp;` where `exp` has static type `S` is a valid return if:
774
- // * `T` is not `void`
775
- // * and `flatten(S)` is not `void`
776
- if (flattenS is ! VoidType ) return true ;
777
- }
778
- return false ;
779
- }
780
-
781
772
bool isNull (DartType type) {
782
773
return type is InterfaceType && type.classNode == coreTypes.nullClass;
783
774
}
@@ -1215,10 +1206,12 @@ abstract class TypeInferrerImpl extends TypeInferrer {
1215
1206
assert (closureContext == null );
1216
1207
this .helper = helper;
1217
1208
var actualType = inferExpression (
1218
- initializer, declaredType ?? const UnknownType (), declaredType != null );
1209
+ initializer, declaredType ?? const UnknownType (), declaredType != null ,
1210
+ isVoidAllowed: true );
1219
1211
if (declaredType != null ) {
1220
1212
ensureAssignable (
1221
- declaredType, actualType, initializer, initializer.fileOffset);
1213
+ declaredType, actualType, initializer, initializer.fileOffset,
1214
+ isVoidAllowed: declaredType is VoidType );
1222
1215
}
1223
1216
this .helper = null ;
1224
1217
}
0 commit comments