Implement type inference for try/catch blocks.

Note that the types of the exception and stack trace variables are set
in the BodyBuilder since they do not depend on inference of other
parts of the function.

R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2954823002 .
This commit is contained in:
Paul Berry 2017-06-26 08:56:58 -07:00
parent b94fdceadc
commit 7553b2e75c
23 changed files with 500 additions and 6 deletions

View file

@ -1940,9 +1940,11 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
if (catchParameters != null) {
if (catchParameters.required.length > 0) {
exception = catchParameters.required[0];
exception.type = type;
}
if (catchParameters.required.length > 1) {
stackTrace = catchParameters.required[1];
stackTrace.type = coreTypes.stackTraceClass.rawType;
}
if (catchParameters.required.length > 2 ||
catchParameters.optional != null) {
@ -1962,10 +1964,10 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
Statement tryBlock = popStatement();
if (compileTimeErrorInTry == null) {
if (catches != null) {
tryBlock = new TryCatch(tryBlock, catches);
tryBlock = new KernelTryCatch(tryBlock, catches);
}
if (finallyBlock != null) {
tryBlock = new TryFinally(tryBlock, finallyBlock);
tryBlock = new KernelTryFinally(tryBlock, finallyBlock);
}
push(tryBlock);
} else {

View file

@ -1874,6 +1874,35 @@ class KernelThrow extends Throw implements KernelExpression {
}
}
/// Concrete shadow object representing a try-catch block in kernel form.
class KernelTryCatch extends TryCatch implements KernelStatement {
KernelTryCatch(Statement body, List<Catch> catches) : super(body, catches);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.tryCatchEnter(this);
inferrer.inferStatement(body);
for (var catch_ in catches) {
inferrer.inferStatement(catch_.body);
}
inferrer.listener.tryCatchExit(this);
}
}
/// Concrete shadow object representing a try-finally block in kernel form.
class KernelTryFinally extends TryFinally implements KernelStatement {
KernelTryFinally(Statement body, Statement finalizer)
: super(body, finalizer);
@override
void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.tryFinallyEnter(this);
inferrer.inferStatement(body);
inferrer.inferStatement(finalizer);
inferrer.listener.tryFinallyExit(this);
}
}
/// Concrete implementation of [TypeInferenceEngine] specialized to work with
/// kernel objects.
class KernelTypeInferenceEngine extends TypeInferenceEngineImpl {

View file

@ -334,6 +334,18 @@ class TypeInferenceListener
void throwExit(Throw expression, DartType inferredType) =>
debugExpressionExit('throw', expression, inferredType);
void tryCatchEnter(TryCatch statement) =>
debugStatementEnter('tryCatch', statement);
void tryCatchExit(TryCatch statement) =>
debugStatementExit('tryCatch', statement);
void tryFinallyEnter(TryFinally statement) =>
debugStatementEnter('tryFinally', statement);
void tryFinallyExit(TryFinally statement) =>
debugStatementExit('tryFinally', statement);
bool variableAssignEnter(Expression expression, DartType typeContext) =>
debugExpressionEnter("variableAssign", expression, typeContext);

View file

@ -14,7 +14,7 @@ class C extends core::Object {
core::print(this.onlySetter);
throw "No error thrown";
}
on core::NoSuchMethodError catch(final dynamic e) {
on core::NoSuchMethodError catch(final core::NoSuchMethodError e) {
core::print("Expected error: ${e}");
}
this.onlySetter = "hest";
@ -42,7 +42,7 @@ static method main() → dynamic {
core::print(throw new core::NoSuchMethodError::_withType(null, #onlySetter, 33, <dynamic>[].toList(growable: false), <dynamic, dynamic>{}, null));
throw "No error thrown";
}
on core::NoSuchMethodError catch(final dynamic e) {
on core::NoSuchMethodError catch(final core::NoSuchMethodError e) {
core::print("Expected error: ${e}");
}
self::onlySetter = "fisk";

View file

@ -21,7 +21,7 @@ static method main() → dynamic {
try {
throw "fisk";
}
on core::String catch(final dynamic e, final dynamic s) {
on core::String catch(final core::String e, final core::StackTrace s) {
core::print(e);
if(!s.==(null))
core::print(s);
@ -72,7 +72,7 @@ static method main() → dynamic {
core::print(throw new core::NoSuchMethodError::_withType(null, #toString, 32, <dynamic>[].toList(growable: false), <dynamic, dynamic>{}, null));
throw "Shouldn't work";
}
on core::NoSuchMethodError catch(final dynamic e) {
on core::NoSuchMethodError catch(final core::NoSuchMethodError e) {
core::print("As expected: ${e}");
}
core::print(core::int::parse("42"));

View file

@ -0,0 +1,31 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/*@testedFeatures=inference*/
library test;
class C {}
class D {}
class E {}
void test(void f()) {
try {
var /*@type=int*/ x = 0;
f();
} on C {
var /*@type=int*/ x = 0;
} on D catch (x) {
var /*@type=D*/ x2 = x;
} on E catch (x, y) {
var /*@type=E*/ x2 = x;
var /*@type=StackTrace*/ y2 = y;
} catch (x, y) {
var /*@type=dynamic*/ x2 = x;
var /*@type=StackTrace*/ y2 = y;
}
}
main() {}

View file

@ -0,0 +1,40 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class D extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class E extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
static method test(() → void f) → void {
try {
dynamic x = 0;
f.call();
}
on self::C catch(no-exception-var) {
dynamic x = 0;
}
on self::D catch(final self::D x) {
dynamic x2 = x;
}
on self::E catch(final self::E x, final core::StackTrace y) {
dynamic x2 = x;
dynamic y2 = y;
}
on dynamic catch(final dynamic x, final core::StackTrace y) {
dynamic x2 = x;
dynamic y2 = y;
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,20 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
;
}
class D extends core::Object {
constructor •() → void
;
}
class E extends core::Object {
constructor •() → void
;
}
static method test(() → void f) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,40 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class D extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class E extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
static method test(() → void f) → void {
try {
core::int x = 0;
f.call();
}
on self::C catch(no-exception-var) {
core::int x = 0;
}
on self::D catch(final self::D x) {
self::D x2 = x;
}
on self::E catch(final self::E x, final core::StackTrace y) {
self::E x2 = x;
core::StackTrace y2 = y;
}
on dynamic catch(final dynamic x, final core::StackTrace y) {
dynamic x2 = x;
core::StackTrace y2 = y;
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/*@testedFeatures=inference*/
library test;
class C {}
class D {}
class E {}
void test(void f()) {
try {
var /*@type=int*/ x = 0;
f();
} on C {
var /*@type=int*/ x = 0;
} on D catch (x) {
var /*@type=D*/ x2 = x;
} on E catch (x, y) {
var /*@type=E*/ x2 = x;
var /*@type=StackTrace*/ y2 = y;
} catch (x, y) {
var /*@type=dynamic*/ x2 = x;
var /*@type=StackTrace*/ y2 = y;
} finally {
var /*@type=int*/ x = 0;
}
}
main() {}

View file

@ -0,0 +1,44 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class D extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class E extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
static method test(() → void f) → void {
try
try {
dynamic x = 0;
f.call();
}
on self::C catch(no-exception-var) {
dynamic x = 0;
}
on self::D catch(final self::D x) {
dynamic x2 = x;
}
on self::E catch(final self::E x, final core::StackTrace y) {
dynamic x2 = x;
dynamic y2 = y;
}
on dynamic catch(final dynamic x, final core::StackTrace y) {
dynamic x2 = x;
dynamic y2 = y;
}
finally {
dynamic x = 0;
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,20 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
;
}
class D extends core::Object {
constructor •() → void
;
}
class E extends core::Object {
constructor •() → void
;
}
static method test(() → void f) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,44 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class D extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class E extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
static method test(() → void f) → void {
try
try {
core::int x = 0;
f.call();
}
on self::C catch(no-exception-var) {
core::int x = 0;
}
on self::D catch(final self::D x) {
self::D x2 = x;
}
on self::E catch(final self::E x, final core::StackTrace y) {
self::E x2 = x;
core::StackTrace y2 = y;
}
on dynamic catch(final dynamic x, final core::StackTrace y) {
dynamic x2 = x;
core::StackTrace y2 = y;
}
finally {
core::int x = 0;
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,29 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/*@testedFeatures=inference*/
library test;
class C {}
class D extends C {}
class E extends StackTrace {}
void test(void f()) {
try {
f();
} on C catch (x, y) {
var /*@type=C*/ x1 = x;
var /*@type=StackTrace*/ y1 = y;
if (x is D) {
var /*@type=D*/ x2 = /*@promotedType=D*/ x;
}
if (y is E) {
var /*@type=E*/ y2 = /*@promotedType=E*/ y;
}
}
}
main() {}

View file

@ -0,0 +1,35 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class D extends self::C {
constructor •() → void
: super self::C::•()
;
}
class E extends core::StackTrace {
constructor •() → void
: super core::StackTrace::•()
;
}
static method test(() → void f) → void {
try {
f.call();
}
on self::C catch(final self::C x, final core::StackTrace y) {
dynamic x1 = x;
dynamic y1 = y;
if(x is self::D) {
dynamic x2 = x{self::D};
}
if(y is self::E) {
dynamic y2 = y{self::E};
}
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,20 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
;
}
class D extends self::C {
constructor •() → void
;
}
class E extends core::StackTrace {
constructor •() → void
;
}
static method test(() → void f) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,35 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
}
class D extends self::C {
constructor •() → void
: super self::C::•()
;
}
class E extends core::StackTrace {
constructor •() → void
: super core::StackTrace::•()
;
}
static method test(() → void f) → void {
try {
f.call();
}
on self::C catch(final self::C x, final core::StackTrace y) {
self::C x1 = x;
core::StackTrace y1 = y;
if(x is self::D) {
self::D x2 = x{self::D};
}
if(y is self::E) {
self::E y2 = y{self::E};
}
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,17 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/*@testedFeatures=inference*/
library test;
void test(void f()) {
try {
var /*@type=int*/ x = 0;
f();
} finally {
var /*@type=int*/ x = 0;
}
}
main() {}

View file

@ -0,0 +1,13 @@
library test;
import self as self;
static method test(() → void f) → void {
try {
dynamic x = 0;
f.call();
}
finally {
dynamic x = 0;
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,7 @@
library test;
import self as self;
static method test(() → void f) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,14 @@
library test;
import self as self;
import "dart:core" as core;
static method test(() → void f) → void {
try {
core::int x = 0;
f.call();
}
finally {
core::int x = 0;
}
}
static method main() → dynamic {}

View file

@ -362,6 +362,10 @@ inference/subexpressions_of_explicitly_typed_fields: Crash
inference/this_reference: Crash
inference/top_level_return_and_yield: Crash
inference/toplevel_inference_toplevel_var: Crash
inference/try_catch: Crash
inference/try_catch_finally: Crash
inference/try_catch_promotion: Crash
inference/try_finally: Crash
inference/type_cast: Crash
inference/type_promotion_ignores_local_functions: Crash
inference/type_promotion_not_and_not: Crash

View file

@ -71,6 +71,7 @@ class CoreTypes {
Library _asyncLibrary;
Class _futureClass;
Class _stackTraceClass;
Class _streamClass;
Class _completerClass;
Class _futureOrClass;
@ -235,6 +236,10 @@ class CoreTypes {
return _printProcedure ??= _index.getTopLevelMember('dart:core', 'print');
}
Class get stackTraceClass {
return _stackTraceClass ??= _index.getClass('dart:core', 'StackTrace');
}
Class get streamClass {
return _streamClass ??= _index.getClass('dart:async', 'Stream');
}