Infer the return types of local functions where appropriate.

Note that we do this in order to be consistent with type inference of
function expressions.  See https://codereview.chromium.org/2209293002.

R=sigmund@google.com

Review-Url: https://codereview.chromium.org/2950213002 .
This commit is contained in:
Paul Berry 2017-06-22 12:17:08 -07:00
parent ec8505fc79
commit dd0a00f581
13 changed files with 255 additions and 175 deletions

View file

@ -369,6 +369,30 @@ class _InstrumentationVisitor extends RecursiveAstVisitor<Null> {
} }
} }
@override
visitFunctionDeclaration(FunctionDeclaration node) {
super.visitFunctionDeclaration(node);
if (node.element is LocalElement &&
node.element.enclosingElement is! CompilationUnitElement) {
if (node.returnType == null) {
_instrumentation.record(
uri,
node.name.offset,
'returnType',
new _InstrumentationValueForType(
node.element.returnType, elementNamer));
}
var parameters = node.functionExpression.parameters;
for (var parameter in parameters.parameters) {
// Note: it's tempting to check `parameter.type == null`, but that
// doesn't work because of function-typed formal parameter syntax.
if (parameter.element.hasImplicitType) {
_recordType(parameter.identifier.offset, parameter.element.type);
}
}
}
}
visitFunctionExpression(FunctionExpression node) { visitFunctionExpression(FunctionExpression node) {
super.visitFunctionExpression(node); super.visitFunctionExpression(node);
if (node.parent is! FunctionDeclaration) { if (node.parent is! FunctionDeclaration) {

View file

@ -2432,10 +2432,14 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
FunctionNode function = pop(); FunctionNode function = pop();
exitLocalScope(); exitLocalScope();
var declaration = pop(); var declaration = pop();
var returnType = pop() ?? const DynamicType(); var returnType = pop();
var hasImplicitReturnType = returnType == null;
returnType ??= const DynamicType();
pop(); // Modifiers. pop(); // Modifiers.
exitFunction(); exitFunction();
if (declaration is FunctionDeclaration) { if (declaration is FunctionDeclaration) {
KernelFunctionDeclaration.setHasImplicitReturnType(
declaration, hasImplicitReturnType);
function.returnType = returnType; function.returnType = returnType;
declaration.variable.type = function.functionType; declaration.variable.type = function.functionType;
declaration.function = function; declaration.function = function;

View file

@ -25,11 +25,9 @@ import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
import 'package:front_end/src/fasta/type_inference/type_promotion.dart'; import 'package:front_end/src/fasta/type_inference/type_promotion.dart';
import 'package:front_end/src/fasta/type_inference/type_schema.dart'; import 'package:front_end/src/fasta/type_inference/type_schema.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart'; import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
import 'package:kernel/ast.dart' import 'package:kernel/ast.dart'
hide InvalidExpression, InvalidInitializer, InvalidStatement; hide InvalidExpression, InvalidInitializer, InvalidStatement;
import 'package:kernel/frontend/accessors.dart'; import 'package:kernel/frontend/accessors.dart';
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart'; import 'package:kernel/type_environment.dart';
import '../errors.dart' show internalError; import '../errors.dart' show internalError;
@ -715,31 +713,24 @@ class KernelForInStatement extends ForInStatement implements KernelStatement {
/// form. /// form.
class KernelFunctionDeclaration extends FunctionDeclaration class KernelFunctionDeclaration extends FunctionDeclaration
implements KernelStatement { implements KernelStatement {
bool _hasImplicitReturnType = false;
KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function) KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function)
: super(variable, function); : super(variable, function);
@override @override
void _inferStatement(KernelTypeInferrer inferrer) { void _inferStatement(KernelTypeInferrer inferrer) {
inferrer.listener.functionDeclarationEnter(this); inferrer.listener.functionDeclarationEnter(this);
for (var parameter in function.positionalParameters) { inferrer.inferLocalFunction(function, null, false, fileOffset,
if (parameter.initializer != null) { _hasImplicitReturnType ? null : function.returnType, true);
inferrer.inferExpression(parameter.initializer, parameter.type, false); variable.type = function.functionType;
}
}
for (var parameter in function.namedParameters) {
if (parameter.initializer != null) {
inferrer.inferExpression(parameter.initializer, parameter.type, false);
}
}
if (!inferrer.isTopLevel) {
var oldClosureContext = inferrer.closureContext;
inferrer.closureContext = new ClosureContext(
inferrer, function.asyncMarker, function.returnType);
inferrer.inferStatement(function.body);
inferrer.closureContext = oldClosureContext;
}
inferrer.listener.functionDeclarationExit(this); inferrer.listener.functionDeclarationExit(this);
} }
static void setHasImplicitReturnType(
KernelFunctionDeclaration declaration, bool hasImplicitReturnType) {
declaration._hasImplicitReturnType = hasImplicitReturnType;
}
} }
/// Concrete shadow object representing a function expression in kernel form. /// Concrete shadow object representing a function expression in kernel form.
@ -774,137 +765,8 @@ class KernelFunctionExpression extends FunctionExpression
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) || typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) ||
typeNeeded; typeNeeded;
var inferredType = inferrer.inferLocalFunction(
if (!inferrer.isTopLevel) { function, typeContext, typeNeeded, fileOffset, null, false);
for (var parameter in function.positionalParameters) {
if (parameter.initializer != null) {
inferrer.inferExpression(
parameter.initializer, parameter.type, false);
}
}
for (var parameter in function.namedParameters) {
if (parameter.initializer != null) {
inferrer.inferExpression(
parameter.initializer, parameter.type, false);
}
}
}
// Let `<T0, ..., Tn>` be the set of type parameters of the closure (with
// `n`=0 if there are no type parameters).
List<TypeParameter> typeParameters = function.typeParameters;
// Let `(P0 x0, ..., Pm xm)` be the set of formal parameters of the closure
// (including required, positional optional, and named optional parameters).
// If any type `Pi` is missing, denote it as `_`.
List<VariableDeclaration> formals = function.positionalParameters.toList()
..addAll(function.namedParameters);
// Let `B` denote the closure body. If `B` is an expression function body
// (`=> e`), treat it as equivalent to a block function body containing a
// single `return` statement (`{ return e; }`).
// Attempt to match `K` as a function type compatible with the closure (that
// is, one having n type parameters and a compatible set of formal
// parameters). If there is a successful match, let `<S0, ..., Sn>` be the
// set of matched type parameters and `(Q0, ..., Qm)` be the set of matched
// formal parameter types, and let `N` be the return type.
Substitution substitution;
List<DartType> formalTypesFromContext =
new List<DartType>.filled(formals.length, null);
DartType returnContext;
if (inferrer.strongMode && typeContext is FunctionType) {
for (int i = 0; i < formals.length; i++) {
if (i < function.positionalParameters.length) {
formalTypesFromContext[i] =
getPositionalParameterType(typeContext, i);
} else {
formalTypesFromContext[i] =
getNamedParameterType(typeContext, formals[i].name);
}
}
returnContext = typeContext.returnType;
// Let `[T/S]` denote the type substitution where each `Si` is replaced with
// the corresponding `Ti`.
var substitutionMap = <TypeParameter, DartType>{};
for (int i = 0; i < typeContext.typeParameters.length; i++) {
substitutionMap[typeContext.typeParameters[i]] =
i < typeParameters.length
? new TypeParameterType(typeParameters[i])
: const DynamicType();
}
substitution = Substitution.fromMap(substitutionMap);
} else {
// If the match is not successful because `K` is `_`, let all `Si`, all
// `Qi`, and `N` all be `_`.
// If the match is not successful for any other reason, this will result in
// a type error, so the implementation is free to choose the best error
// recovery path.
substitution = Substitution.empty;
}
// Define `Ri` as follows: if `Pi` is not `_`, let `Ri` be `Pi`.
// Otherwise, if `Qi` is not `_`, let `Ri` be the greatest closure of
// `Qi[T/S]` with respect to `?`. Otherwise, let `Ri` be `dynamic`.
for (int i = 0; i < formals.length; i++) {
KernelVariableDeclaration formal = formals[i];
if (KernelVariableDeclaration.isImplicitlyTyped(formal)) {
DartType inferredType;
if (formalTypesFromContext[i] != null) {
inferredType = greatestClosure(inferrer.coreTypes,
substitution.substituteType(formalTypesFromContext[i]));
} else {
inferredType = const DynamicType();
}
inferrer.instrumentation?.record(
Uri.parse(inferrer.uri),
formal.fileOffset,
'type',
new InstrumentationValueForType(inferredType));
formal.type = inferredType;
}
}
// Let `N'` be `N[T/S]`. The [ClosureContext] constructor will adjust
// accordingly if the closure is declared with `async`, `async*`, or
// `sync*`.
if (returnContext != null) {
returnContext = substitution.substituteType(returnContext);
}
// Apply type inference to `B` in return context `N`, with any references
// to `xi` in `B` having type `Pi`. This produces `B`.
bool isExpressionFunction = function.body is ReturnStatement;
bool needToSetReturnType = isExpressionFunction || inferrer.strongMode;
ClosureContext oldClosureContext = inferrer.closureContext;
ClosureContext closureContext =
new ClosureContext(inferrer, function.asyncMarker, returnContext);
inferrer.closureContext = closureContext;
inferrer.inferStatement(function.body);
// If the closure is declared with `async*` or `sync*`, let `M` be the least
// upper bound of the types of the `yield` expressions in `B`, or `void` if
// `B` contains no `yield` expressions. Otherwise, let `M` be the least
// upper bound of the types of the `return` expressions in `B`, or `void`
// if `B` contains no `return` expressions.
DartType inferredReturnType;
if (needToSetReturnType || typeNeeded) {
inferredReturnType =
closureContext.inferReturnType(inferrer, isExpressionFunction);
}
// Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B` with
// type `<T0, ..., Tn>(R0, ..., Rn) -> M` (with some of the `Ri` and `xi`
// denoted as optional or named parameters, if appropriate).
if (needToSetReturnType) {
inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
'returnType', new InstrumentationValueForType(inferredReturnType));
function.returnType = inferredReturnType;
}
inferrer.closureContext = oldClosureContext;
var inferredType = typeNeeded ? function.functionType : null;
inferrer.listener.functionExpressionExit(this, inferredType); inferrer.listener.functionExpressionExit(this, inferredType);
return inferredType; return inferredType;
} }

View file

@ -22,6 +22,7 @@ import 'package:kernel/ast.dart'
DynamicType, DynamicType,
Expression, Expression,
Field, Field,
FunctionNode,
FunctionType, FunctionType,
Initializer, Initializer,
InterfaceType, InterfaceType,
@ -33,10 +34,12 @@ import 'package:kernel/ast.dart'
ProcedureKind, ProcedureKind,
PropertyGet, PropertyGet,
PropertySet, PropertySet,
ReturnStatement,
Statement, Statement,
SuperMethodInvocation, SuperMethodInvocation,
SuperPropertyGet, SuperPropertyGet,
SuperPropertySet, SuperPropertySet,
TypeParameter,
TypeParameterType, TypeParameterType,
VariableDeclaration, VariableDeclaration,
VoidType; VoidType;
@ -637,6 +640,136 @@ abstract class TypeInferrerImpl extends TypeInferrer {
return inferredType; return inferredType;
} }
DartType inferLocalFunction(FunctionNode function, DartType typeContext,
bool typeNeeded, int fileOffset, DartType returnContext, bool isNamed) {
bool hasImplicitReturnType = returnContext == null;
if (!isTopLevel) {
for (var parameter in function.positionalParameters) {
if (parameter.initializer != null) {
inferExpression(parameter.initializer, parameter.type, false);
}
}
for (var parameter in function.namedParameters) {
if (parameter.initializer != null) {
inferExpression(parameter.initializer, parameter.type, false);
}
}
}
// Let `<T0, ..., Tn>` be the set of type parameters of the closure (with
// `n`=0 if there are no type parameters).
List<TypeParameter> typeParameters = function.typeParameters;
// Let `(P0 x0, ..., Pm xm)` be the set of formal parameters of the closure
// (including required, positional optional, and named optional parameters).
// If any type `Pi` is missing, denote it as `_`.
List<VariableDeclaration> formals = function.positionalParameters.toList()
..addAll(function.namedParameters);
// Let `B` denote the closure body. If `B` is an expression function body
// (`=> e`), treat it as equivalent to a block function body containing a
// single `return` statement (`{ return e; }`).
// Attempt to match `K` as a function type compatible with the closure (that
// is, one having n type parameters and a compatible set of formal
// parameters). If there is a successful match, let `<S0, ..., Sn>` be the
// set of matched type parameters and `(Q0, ..., Qm)` be the set of matched
// formal parameter types, and let `N` be the return type.
Substitution substitution;
List<DartType> formalTypesFromContext =
new List<DartType>.filled(formals.length, null);
if (strongMode && typeContext is FunctionType) {
for (int i = 0; i < formals.length; i++) {
if (i < function.positionalParameters.length) {
formalTypesFromContext[i] =
getPositionalParameterType(typeContext, i);
} else {
formalTypesFromContext[i] =
getNamedParameterType(typeContext, formals[i].name);
}
}
returnContext = typeContext.returnType;
// Let `[T/S]` denote the type substitution where each `Si` is replaced
// with the corresponding `Ti`.
var substitutionMap = <TypeParameter, DartType>{};
for (int i = 0; i < typeContext.typeParameters.length; i++) {
substitutionMap[typeContext.typeParameters[i]] =
i < typeParameters.length
? new TypeParameterType(typeParameters[i])
: const DynamicType();
}
substitution = Substitution.fromMap(substitutionMap);
} else {
// If the match is not successful because `K` is `_`, let all `Si`, all
// `Qi`, and `N` all be `_`.
// If the match is not successful for any other reason, this will result in
// a type error, so the implementation is free to choose the best error
// recovery path.
substitution = Substitution.empty;
}
// Define `Ri` as follows: if `Pi` is not `_`, let `Ri` be `Pi`.
// Otherwise, if `Qi` is not `_`, let `Ri` be the greatest closure of
// `Qi[T/S]` with respect to `?`. Otherwise, let `Ri` be `dynamic`.
for (int i = 0; i < formals.length; i++) {
KernelVariableDeclaration formal = formals[i];
if (KernelVariableDeclaration.isImplicitlyTyped(formal)) {
DartType inferredType;
if (formalTypesFromContext[i] != null) {
inferredType = greatestClosure(coreTypes,
substitution.substituteType(formalTypesFromContext[i]));
} else {
inferredType = const DynamicType();
}
instrumentation?.record(Uri.parse(uri), formal.fileOffset, 'type',
new InstrumentationValueForType(inferredType));
formal.type = inferredType;
}
}
// Let `N'` be `N[T/S]`. The [ClosureContext] constructor will adjust
// accordingly if the closure is declared with `async`, `async*`, or
// `sync*`.
if (returnContext != null) {
returnContext = substitution.substituteType(returnContext);
}
// Apply type inference to `B` in return context `N`, with any references
// to `xi` in `B` having type `Pi`. This produces `B`.
bool isExpressionFunction = function.body is ReturnStatement;
bool needToSetReturnType = hasImplicitReturnType &&
((isExpressionFunction && !isNamed) || strongMode);
ClosureContext oldClosureContext = this.closureContext;
ClosureContext closureContext =
new ClosureContext(this, function.asyncMarker, returnContext);
this.closureContext = closureContext;
inferStatement(function.body);
// If the closure is declared with `async*` or `sync*`, let `M` be the least
// upper bound of the types of the `yield` expressions in `B`, or `void` if
// `B` contains no `yield` expressions. Otherwise, let `M` be the least
// upper bound of the types of the `return` expressions in `B`, or `void`
// if `B` contains no `return` expressions.
DartType inferredReturnType;
if (needToSetReturnType || typeNeeded) {
inferredReturnType =
closureContext.inferReturnType(this, isExpressionFunction);
}
// Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B` with
// type `<T0, ..., Tn>(R0, ..., Rn) -> M` (with some of the `Ri` and `xi`
// denoted as optional or named parameters, if appropriate).
if (needToSetReturnType) {
instrumentation?.record(Uri.parse(uri), fileOffset, 'returnType',
new InstrumentationValueForType(inferredReturnType));
function.returnType = inferredReturnType;
}
this.closureContext = oldClosureContext;
return typeNeeded ? function.functionType : null;
}
/// Performs the core type inference algorithm for method invocations (this /// Performs the core type inference algorithm for method invocations (this
/// handles both null-aware and non-null-aware method invocations). /// handles both null-aware and non-null-aware method invocations).
DartType inferMethodInvocation( DartType inferMethodInvocation(

View file

@ -244,6 +244,7 @@ inference/infer_generic_method_type_required: Crash
inference/infer_getter_cross_to_setter: Crash inference/infer_getter_cross_to_setter: Crash
inference/infer_getter_from_later_inferred_getter: Crash inference/infer_getter_from_later_inferred_getter: Crash
inference/infer_list_literal_nested_in_map_literal: Crash inference/infer_list_literal_nested_in_map_literal: Crash
inference/infer_local_function_referenced_before_declaration: Crash
inference/infer_local_function_return_type: Crash inference/infer_local_function_return_type: Crash
inference/infer_method_function_typed: Crash inference/infer_method_function_typed: Crash
inference/infer_method_missing_params: Crash inference/infer_method_missing_params: Crash

View file

@ -59,7 +59,6 @@ inference/downwards_inference_on_function_of_t_using_the_t: Fail
inference/future_then_explicit_future: Fail inference/future_then_explicit_future: Fail
inference/generic_functions_return_typedef: Fail inference/generic_functions_return_typedef: Fail
inference/generic_methods_infer_js_builtin: Fail inference/generic_methods_infer_js_builtin: Fail
inference/infer_local_function_return_type: Fail
inference/infer_types_on_loop_indices_for_loop_with_inference: Fail inference/infer_types_on_loop_indices_for_loop_with_inference: Fail
inference/infer_use_of_void: Fail inference/infer_use_of_void: Fail
inference/list_literals_can_infer_null_top_level: Fail inference/list_literals_can_infer_null_top_level: Fail

View file

@ -0,0 +1,20 @@
// 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;
test() {
/*@returnType=dynamic*/ f() => /*error:REFERENCED_BEFORE_DECLARATION*/ g();
// Ignore inference for g since Fasta doesn't infer it due to the circularity,
// and that's ok.
/*@testedFeatures=none*/
g() => 0;
/*@testedFeatures=inference*/
var /*@type=() -> dynamic*/ v = f;
}
main() {}

View file

@ -0,0 +1,12 @@
library test;
import self as self;
import "dart:core" as core;
const field dynamic #errors = const <dynamic>["pkg/front_end/testcases/inference/infer_local_function_referenced_before_declaration.dart:9:74: Error: Previous use of 'g'.\n /*@returnType=dynamic*/ f() => /*error:REFERENCED_BEFORE_DECLARATION*/ g();\n ^"]/* from null */;
static method test() → dynamic {
function f() → dynamic
return throw new core::NoSuchMethodError::_withType(null, #g, 32, <dynamic>[].toList(growable: false), <dynamic, dynamic>{}, null);
const core::_ConstantExpressionError::•()._throw(new core::_CompileTimeError::•("pkg/front_end/testcases/inference/infer_local_function_referenced_before_declaration.dart:14:3: Error: Can't declare 'g' because it was already used in this scope.\n g() => 0;\n ^"));
dynamic v = f;
}
static method main() → dynamic {}

View file

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

View file

@ -0,0 +1,12 @@
library test;
import self as self;
import "dart:core" as core;
const field dynamic #errors = const <dynamic>["pkg/front_end/testcases/inference/infer_local_function_referenced_before_declaration.dart:9:74: Error: Previous use of 'g'.\n /*@returnType=dynamic*/ f() => /*error:REFERENCED_BEFORE_DECLARATION*/ g();\n ^"]/* from null */;
static method test() → dynamic {
function f() → dynamic
return throw new core::NoSuchMethodError::_withType(null, #g, 32, <dynamic>[].toList(growable: false), <dynamic, dynamic>{}, null);
const core::_ConstantExpressionError::•()._throw(new core::_CompileTimeError::•("pkg/front_end/testcases/inference/infer_local_function_referenced_before_declaration.dart:14:3: Error: Can't declare 'g' because it was already used in this scope.\n g() => 0;\n ^"));
() → dynamic v = f;
}
static method main() → dynamic {}

View file

@ -6,30 +6,29 @@
library test; library test;
test() { test() {
f0() => 42; /*@returnType=int*/ f0() => 42;
f1() async => 42; /*@returnType=Future<int>*/ f1() async => 42;
f2() { /*@returnType=int*/ f2() {
return 42; return 42;
} }
f3() async { /*@returnType=Future<int>*/ f3() async {
return 42; return 42;
} }
f4() sync* { /*@returnType=Iterable<int>*/ f4() sync* {
yield 42; yield 42;
} }
f5() async* { /*@returnType=Stream<int>*/ f5() async* {
yield 42; yield 42;
} }
num f6() => 42; num f6() => 42;
f7() => f7(); /*@returnType=dynamic*/ f7() => f7();
f8() => /*error:REFERENCED_BEFORE_DECLARATION*/ f9(); /*@returnType=Stream<int>*/ f8() => f5();
f9() => f5();
var /*@type=() -> int*/ v0 = f0; var /*@type=() -> int*/ v0 = f0;
var /*@type=() -> Future<int>*/ v1 = f1; var /*@type=() -> Future<int>*/ v1 = f1;
@ -39,8 +38,7 @@ test() {
var /*@type=() -> Stream<int>*/ v5 = f5; var /*@type=() -> Stream<int>*/ v5 = f5;
var /*@type=() -> num*/ v6 = f6; var /*@type=() -> num*/ v6 = f6;
var /*@type=() -> dynamic*/ v7 = f7; var /*@type=() -> dynamic*/ v7 = f7;
var /*@type=() -> dynamic*/ v8 = f8; var /*@type=() -> Stream<int>*/ v8 = f8;
var /*@type=() -> Stream<int>*/ v9 = f9;
} }
main() {} main() {}

View file

@ -1,31 +1,39 @@
library test; library test;
import self as self; import self as self;
import "dart:core" as core; import "dart:core" as core;
import "dart:async" as asy;
const field dynamic #errors = const <dynamic>["pkg/front_end/testcases/inference/infer_local_function_return_type.dart:31:51: Error: Previous use of 'f9'.\n f8() => /*error:REFERENCED_BEFORE_DECLARATION*/ f9();\n ^"]/* from null */;
static method test() → dynamic { static method test() → dynamic {
function f0() → dynamic function f0() → core::int
return 42; return 42;
function f1() → dynamic async function f1() → asy::Future<core::int> async
return 42; return 42;
function f2() → dynamic { function f2() → core::int {
return 42; return 42;
} }
function f3() → dynamic async { function f3() → asy::Future<core::int> async {
return 42; return 42;
} }
function f4() → dynamic sync* { function f4() → core::Iterable<core::int> sync* {
yield 42; yield 42;
} }
function f5() → dynamic async* { function f5() → asy::Stream<core::int> async* {
yield 42; yield 42;
} }
function f6() → core::num function f6() → core::num
return 42; return 42;
function f7() → dynamic function f7() → dynamic
return f7.call(); return f7.call();
function f8() → dynamic function f8() → asy::Stream<core::int>
return throw new core::NoSuchMethodError::_withType(null, #f9, 32, <dynamic>[].toList(growable: false), <dynamic, dynamic>{}, null); return f5.call();
const core::_ConstantExpressionError::•()._throw(new core::_CompileTimeError::•("pkg/front_end/testcases/inference/infer_local_function_return_type.dart:32:3: Error: Can't declare 'f9' because it was already used in this scope.\n f9() => f5();\n ^")); () → core::int v0 = f0;
() → asy::Future<core::int> v1 = f1;
() → core::int v2 = f2;
() → asy::Future<core::int> v3 = f3;
() → core::Iterable<core::int> v4 = f4;
() → asy::Stream<core::int> v5 = f5;
() → core::num v6 = f6;
() → dynamic v7 = f7;
() → asy::Stream<core::int> v8 = f8;
} }
static method main() → dynamic {} static method main() → dynamic {}

View file

@ -15,8 +15,8 @@ void optional_toplevel([x = /*@typeArgs=int*/ const [0]]) {}
void named_toplevel({x: /*@typeArgs=int*/ const [0]}) {} void named_toplevel({x: /*@typeArgs=int*/ const [0]}) {}
main() { main() {
void optional_local([x = /*@typeArgs=int*/ const [0]]) {} void optional_local([/*@type=dynamic*/ x = /*@typeArgs=int*/ const [0]]) {}
void named_local({x: /*@typeArgs=int*/ const [0]}) {} void named_local({/*@type=dynamic*/ x: /*@typeArgs=int*/ const [0]}) {}
var /*@type=C<dynamic>*/ c_optional_toplevel = var /*@type=C<dynamic>*/ c_optional_toplevel =
new /*@typeArgs=dynamic*/ C.optional(optional_toplevel); new /*@typeArgs=dynamic*/ C.optional(optional_toplevel);
var /*@type=C<dynamic>*/ c_named_toplevel = var /*@type=C<dynamic>*/ c_named_toplevel =