Skip to content

Commit 4150349

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Handle throw in return type inference
Closes #41156 Change-Id: I6f5360ccf3f05c2f59de5db703d90431e0d61e7c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/141100 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]>
1 parent 99f6199 commit 4150349

27 files changed

+1313
-57
lines changed

pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,17 @@ class ClosureContext {
253253
return true;
254254
}
255255

256+
void _updateInferredUnwrappedReturnOrYieldType(
257+
TypeInferrerImpl inferrer, DartType unwrappedType) {
258+
if (_inferredUnwrappedReturnOrYieldType == null) {
259+
_inferredUnwrappedReturnOrYieldType = unwrappedType;
260+
} else {
261+
_inferredUnwrappedReturnOrYieldType = inferrer.typeSchemaEnvironment
262+
.getStandardUpperBound(_inferredUnwrappedReturnOrYieldType,
263+
unwrappedType, inferrer.library.library);
264+
}
265+
}
266+
256267
/// Updates the inferred return type based on the presence of a return
257268
/// statement returning the given [type].
258269
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
@@ -289,20 +300,12 @@ class ClosureContext {
289300
if (isAsync) {
290301
unwrappedType = inferrer.typeSchemaEnvironment.unfutureType(type);
291302
}
292-
if (_inferredUnwrappedReturnOrYieldType == null) {
293-
_inferredUnwrappedReturnOrYieldType = unwrappedType;
294-
} else {
295-
_inferredUnwrappedReturnOrYieldType = inferrer.typeSchemaEnvironment
296-
.getStandardUpperBound(_inferredUnwrappedReturnOrYieldType,
297-
unwrappedType, inferrer.library.library);
298-
}
299-
return;
300-
}
301-
302-
// If we are not inferring a type we can immediately check that the return
303-
// is valid.
304-
if (checkValidReturn(inferrer, declaredReturnType, statement, type) &&
303+
_updateInferredUnwrappedReturnOrYieldType(inferrer, unwrappedType);
304+
} else if (checkValidReturn(
305+
inferrer, declaredReturnType, statement, type) &&
305306
statement.expression != null) {
307+
// If we are not inferring a type we can immediately check that the return
308+
// is valid.
306309
Expression expression = inferrer.ensureAssignable(
307310
returnOrYieldContext, type, statement.expression,
308311
fileOffset: statement.fileOffset,
@@ -340,29 +343,41 @@ class ClosureContext {
340343
: inferrer.coreTypes.iterableClass) ??
341344
type;
342345
}
343-
if (_inferredUnwrappedReturnOrYieldType == null) {
344-
_inferredUnwrappedReturnOrYieldType = unwrappedType;
345-
} else {
346-
_inferredUnwrappedReturnOrYieldType = inferrer.typeSchemaEnvironment
347-
.getStandardUpperBound(_inferredUnwrappedReturnOrYieldType,
348-
unwrappedType, inferrer.library.library);
349-
}
346+
_updateInferredUnwrappedReturnOrYieldType(inferrer, unwrappedType);
350347
}
351348
}
352349

353-
DartType inferReturnType(TypeInferrerImpl inferrer) {
350+
DartType inferReturnType(TypeInferrerImpl inferrer,
351+
{bool hasImplicitReturn}) {
354352
assert(_needToInferReturnType);
355-
DartType inferredType =
356-
inferrer.inferReturnType(_inferredUnwrappedReturnOrYieldType);
353+
assert(hasImplicitReturn != null);
354+
DartType inferredType;
355+
if (_inferredUnwrappedReturnOrYieldType != null) {
356+
// Use the types seen from the explicit return statements.
357+
inferredType = _inferredUnwrappedReturnOrYieldType;
358+
} else if (hasImplicitReturn) {
359+
// No explicit returns we have an implicit `return null`.
360+
inferredType = inferrer.typeSchemaEnvironment.nullType;
361+
} else {
362+
// No explicit return and the function doesn't complete normally; that is,
363+
// it throws.
364+
if (inferrer.isNonNullableByDefault) {
365+
inferredType = new NeverType(inferrer.library.nonNullable);
366+
} else {
367+
inferredType = inferrer.typeSchemaEnvironment.nullType;
368+
}
369+
}
370+
371+
inferredType = _wrapAsyncOrGenerator(
372+
inferrer, inferredType, inferrer.library.nonNullable);
373+
357374
if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
358375
returnOrYieldContext, SubtypeCheckMode.withNullabilities)) {
359376
// If the inferred return type isn't a subtype of the context, we use the
360377
// context.
361-
inferredType = greatestClosure(returnOrYieldContext, inferrer.bottomType);
378+
inferredType = greatestClosure(declaredReturnType, inferrer.bottomType);
362379
}
363380

364-
inferredType = _wrapAsyncOrGenerator(
365-
inferrer, inferredType, inferrer.library.nonNullable);
366381
for (int i = 0; i < returnStatements.length; ++i) {
367382
checkValidReturn(inferrer, inferredType, returnStatements[i],
368383
returnExpressionTypes[i]);
@@ -2567,7 +2582,8 @@ class TypeInferrerImpl implements TypeInferrer {
25672582
// or `void` if `B’` contains no `return` expressions.
25682583
DartType inferredReturnType;
25692584
if (needToSetReturnType) {
2570-
inferredReturnType = closureContext.inferReturnType(this);
2585+
inferredReturnType = closureContext.inferReturnType(this,
2586+
hasImplicitReturn: flowAnalysis.isReachable);
25712587
}
25722588

25732589
// Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B` with
@@ -3302,11 +3318,6 @@ class TypeInferrerImpl implements TypeInferrer {
33023318
return new ExpressionInferenceResult(inferredType, expression);
33033319
}
33043320

3305-
/// Modifies a type as appropriate when inferring a closure return type.
3306-
DartType inferReturnType(DartType returnType) {
3307-
return returnType ?? typeSchemaEnvironment.nullType;
3308-
}
3309-
33103321
/// Performs type inference on the given [statement].
33113322
///
33123323
/// Derived classes should override this method with logic that dispatches on
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
Never throwing() => throw '';
6+
7+
void main() {
8+
String Function(int) x1 = (int v) => throw v; /* ok */
9+
String Function(int) x2 = (int v) /* ok */ {
10+
throw v;
11+
};
12+
String Function(int) x3 = (int v) /* ok */ {
13+
return throw v;
14+
};
15+
String Function(int) x4 = (int v) => throwing(); /* ok */
16+
String Function(int) x5 = (int v) /* ok */ {
17+
throwing();
18+
};
19+
String Function(int) x6 = (int v) /* ok */ {
20+
return throwing();
21+
};
22+
Future<String> Function(int) y1 = (int v) async => throw v; /* ok */
23+
Future<String> Function(int) y2 = (int v) async /* ok */ {
24+
throw v;
25+
};
26+
Future<String> Function(int) y3 = (int v) async /* ok */ {
27+
return throw v;
28+
};
29+
Future<String> Function(int) y4 = (int v) async => throwing(); /* ok */
30+
Future<String> Function(int) y5 = (int v) async /* ok */ {
31+
throwing();
32+
};
33+
Future<String> Function(int) y6 = (int v) async /* ok */ {
34+
return throwing();
35+
};
36+
}
37+
38+
void errors() async {
39+
String Function(int) x2 = (int v) /* error */ {
40+
try {
41+
throw v;
42+
} catch (_) {}
43+
};
44+
String Function(int) x3 = (int v) /* error */ {
45+
try {
46+
return throw v;
47+
} catch (_) {}
48+
};
49+
String Function(int) x5 = (int v) /* error */ {
50+
try {
51+
throwing();
52+
} catch (_) {}
53+
};
54+
String Function(int) x6 = (int v) /* error */ {
55+
try {
56+
return throwing();
57+
} catch (_) {}
58+
};
59+
Future<String> Function(int) y2 = (int v) async /* error */ {
60+
try {
61+
throw v;
62+
} catch (_) {}
63+
};
64+
Future<String> Function(int) y3 = (int v) async /* error */ {
65+
try {
66+
return throw v;
67+
} catch (_) {}
68+
};
69+
Future<String> Function(int) y5 = (int v) async /* error */ {
70+
try {
71+
throwing();
72+
} catch (_) {}
73+
};
74+
Future<String> Function(int) y6 = (int v) async /* error */ {
75+
try {
76+
return throwing();
77+
} catch (_) {}
78+
};
79+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
4+
static method throwing() → Never
5+
;
6+
static method main() → void
7+
;
8+
static method errors() → void
9+
;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/nnbd/issue41156.dart:39:29: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
6+
// String Function(int) x2 = (int v) /* error */ {
7+
// ^
8+
//
9+
// pkg/front_end/testcases/nnbd/issue41156.dart:44:29: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
10+
// String Function(int) x3 = (int v) /* error */ {
11+
// ^
12+
//
13+
// pkg/front_end/testcases/nnbd/issue41156.dart:49:29: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
14+
// String Function(int) x5 = (int v) /* error */ {
15+
// ^
16+
//
17+
// pkg/front_end/testcases/nnbd/issue41156.dart:54:29: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
18+
// String Function(int) x6 = (int v) /* error */ {
19+
// ^
20+
//
21+
// pkg/front_end/testcases/nnbd/issue41156.dart:59:37: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
22+
// Future<String> Function(int) y2 = (int v) async /* error */ {
23+
// ^
24+
//
25+
// pkg/front_end/testcases/nnbd/issue41156.dart:64:37: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
26+
// Future<String> Function(int) y3 = (int v) async /* error */ {
27+
// ^
28+
//
29+
// pkg/front_end/testcases/nnbd/issue41156.dart:69:37: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
30+
// Future<String> Function(int) y5 = (int v) async /* error */ {
31+
// ^
32+
//
33+
// pkg/front_end/testcases/nnbd/issue41156.dart:74:37: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
34+
// Future<String> Function(int) y6 = (int v) async /* error */ {
35+
// ^
36+
//
37+
import self as self;
38+
import "dart:core" as core;
39+
import "dart:async" as asy;
40+
41+
static method throwing() → Never
42+
return throw "";
43+
static method main() → void {
44+
(core::int) → core::String x1 = (core::int v) → Never => throw v;
45+
(core::int) → core::String x2 = (core::int v) → Never {
46+
throw v;
47+
};
48+
(core::int) → core::String x3 = (core::int v) → Never {
49+
return throw v;
50+
};
51+
(core::int) → core::String x4 = (core::int v) → Never => self::throwing();
52+
(core::int) → core::String x5 = (core::int v) → Never {
53+
self::throwing();
54+
};
55+
(core::int) → core::String x6 = (core::int v) → Never {
56+
return self::throwing();
57+
};
58+
(core::int) → asy::Future<core::String> y1 = (core::int v) → asy::Future<Never> async => throw v;
59+
(core::int) → asy::Future<core::String> y2 = (core::int v) → asy::Future<Never> async {
60+
throw v;
61+
};
62+
(core::int) → asy::Future<core::String> y3 = (core::int v) → asy::Future<Never> async {
63+
return throw v;
64+
};
65+
(core::int) → asy::Future<core::String> y4 = (core::int v) → asy::Future<Never> async => self::throwing();
66+
(core::int) → asy::Future<core::String> y5 = (core::int v) → asy::Future<Never> async {
67+
self::throwing();
68+
};
69+
(core::int) → asy::Future<core::String> y6 = (core::int v) → asy::Future<Never> async {
70+
return self::throwing();
71+
};
72+
}
73+
static method errors() → void async {
74+
(core::int) → core::String x2 = (core::int v) → core::String {
75+
try {
76+
throw v;
77+
}
78+
on dynamic catch(final dynamic _) {
79+
}
80+
return let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:39:29: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
81+
String Function(int) x2 = (int v) /* error */ {
82+
^" in null;
83+
};
84+
(core::int) → core::String x3 = (core::int v) → Never {
85+
try {
86+
return throw v;
87+
}
88+
on dynamic catch(final dynamic _) {
89+
}
90+
return let final<BottomType> #t2 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:44:29: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
91+
String Function(int) x3 = (int v) /* error */ {
92+
^" in null;
93+
};
94+
(core::int) → core::String x5 = (core::int v) → core::String {
95+
try {
96+
self::throwing();
97+
}
98+
on dynamic catch(final dynamic _) {
99+
}
100+
return let final<BottomType> #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:49:29: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
101+
String Function(int) x5 = (int v) /* error */ {
102+
^" in null;
103+
};
104+
(core::int) → core::String x6 = (core::int v) → Never {
105+
try {
106+
return self::throwing();
107+
}
108+
on dynamic catch(final dynamic _) {
109+
}
110+
return let final<BottomType> #t4 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:54:29: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
111+
String Function(int) x6 = (int v) /* error */ {
112+
^" in null;
113+
};
114+
(core::int) → asy::Future<core::String> y2 = (core::int v) → asy::Future<core::String> async {
115+
try {
116+
throw v;
117+
}
118+
on dynamic catch(final dynamic _) {
119+
}
120+
return let final<BottomType> #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:59:37: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
121+
Future<String> Function(int) y2 = (int v) async /* error */ {
122+
^" in null;
123+
};
124+
(core::int) → asy::Future<core::String> y3 = (core::int v) → asy::Future<Never> async {
125+
try {
126+
return throw v;
127+
}
128+
on dynamic catch(final dynamic _) {
129+
}
130+
return let final<BottomType> #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:64:37: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
131+
Future<String> Function(int) y3 = (int v) async /* error */ {
132+
^" in null;
133+
};
134+
(core::int) → asy::Future<core::String> y5 = (core::int v) → asy::Future<core::String> async {
135+
try {
136+
self::throwing();
137+
}
138+
on dynamic catch(final dynamic _) {
139+
}
140+
return let final<BottomType> #t7 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:69:37: Error: A non-null value must be returned since the return type 'String' doesn't allow null.
141+
Future<String> Function(int) y5 = (int v) async /* error */ {
142+
^" in null;
143+
};
144+
(core::int) → asy::Future<core::String> y6 = (core::int v) → asy::Future<Never> async {
145+
try {
146+
return self::throwing();
147+
}
148+
on dynamic catch(final dynamic _) {
149+
}
150+
return let final<BottomType> #t8 = invalid-expression "pkg/front_end/testcases/nnbd/issue41156.dart:74:37: Error: A non-null value must be returned since the return type 'Never' doesn't allow null.
151+
Future<String> Function(int) y6 = (int v) async /* error */ {
152+
^" in null;
153+
};
154+
}

0 commit comments

Comments
 (0)