mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
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:
parent
ec8505fc79
commit
dd0a00f581
|
@ -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) {
|
||||
super.visitFunctionExpression(node);
|
||||
if (node.parent is! FunctionDeclaration) {
|
||||
|
|
|
@ -2432,10 +2432,14 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
|||
FunctionNode function = pop();
|
||||
exitLocalScope();
|
||||
var declaration = pop();
|
||||
var returnType = pop() ?? const DynamicType();
|
||||
var returnType = pop();
|
||||
var hasImplicitReturnType = returnType == null;
|
||||
returnType ??= const DynamicType();
|
||||
pop(); // Modifiers.
|
||||
exitFunction();
|
||||
if (declaration is FunctionDeclaration) {
|
||||
KernelFunctionDeclaration.setHasImplicitReturnType(
|
||||
declaration, hasImplicitReturnType);
|
||||
function.returnType = returnType;
|
||||
declaration.variable.type = function.functionType;
|
||||
declaration.function = function;
|
||||
|
|
|
@ -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_schema.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'
|
||||
hide InvalidExpression, InvalidInitializer, InvalidStatement;
|
||||
import 'package:kernel/frontend/accessors.dart';
|
||||
import 'package:kernel/type_algebra.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
|
||||
import '../errors.dart' show internalError;
|
||||
|
@ -715,31 +713,24 @@ class KernelForInStatement extends ForInStatement implements KernelStatement {
|
|||
/// form.
|
||||
class KernelFunctionDeclaration extends FunctionDeclaration
|
||||
implements KernelStatement {
|
||||
bool _hasImplicitReturnType = false;
|
||||
|
||||
KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function)
|
||||
: super(variable, function);
|
||||
|
||||
@override
|
||||
void _inferStatement(KernelTypeInferrer inferrer) {
|
||||
inferrer.listener.functionDeclarationEnter(this);
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (!inferrer.isTopLevel) {
|
||||
var oldClosureContext = inferrer.closureContext;
|
||||
inferrer.closureContext = new ClosureContext(
|
||||
inferrer, function.asyncMarker, function.returnType);
|
||||
inferrer.inferStatement(function.body);
|
||||
inferrer.closureContext = oldClosureContext;
|
||||
}
|
||||
inferrer.inferLocalFunction(function, null, false, fileOffset,
|
||||
_hasImplicitReturnType ? null : function.returnType, true);
|
||||
variable.type = function.functionType;
|
||||
inferrer.listener.functionDeclarationExit(this);
|
||||
}
|
||||
|
||||
static void setHasImplicitReturnType(
|
||||
KernelFunctionDeclaration declaration, bool hasImplicitReturnType) {
|
||||
declaration._hasImplicitReturnType = hasImplicitReturnType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Concrete shadow object representing a function expression in kernel form.
|
||||
|
@ -774,137 +765,8 @@ class KernelFunctionExpression extends FunctionExpression
|
|||
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
|
||||
typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) ||
|
||||
typeNeeded;
|
||||
|
||||
if (!inferrer.isTopLevel) {
|
||||
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;
|
||||
var inferredType = inferrer.inferLocalFunction(
|
||||
function, typeContext, typeNeeded, fileOffset, null, false);
|
||||
inferrer.listener.functionExpressionExit(this, inferredType);
|
||||
return inferredType;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import 'package:kernel/ast.dart'
|
|||
DynamicType,
|
||||
Expression,
|
||||
Field,
|
||||
FunctionNode,
|
||||
FunctionType,
|
||||
Initializer,
|
||||
InterfaceType,
|
||||
|
@ -33,10 +34,12 @@ import 'package:kernel/ast.dart'
|
|||
ProcedureKind,
|
||||
PropertyGet,
|
||||
PropertySet,
|
||||
ReturnStatement,
|
||||
Statement,
|
||||
SuperMethodInvocation,
|
||||
SuperPropertyGet,
|
||||
SuperPropertySet,
|
||||
TypeParameter,
|
||||
TypeParameterType,
|
||||
VariableDeclaration,
|
||||
VoidType;
|
||||
|
@ -637,6 +640,136 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
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
|
||||
/// handles both null-aware and non-null-aware method invocations).
|
||||
DartType inferMethodInvocation(
|
||||
|
|
|
@ -244,6 +244,7 @@ inference/infer_generic_method_type_required: Crash
|
|||
inference/infer_getter_cross_to_setter: Crash
|
||||
inference/infer_getter_from_later_inferred_getter: 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_method_function_typed: Crash
|
||||
inference/infer_method_missing_params: Crash
|
||||
|
|
|
@ -59,7 +59,6 @@ inference/downwards_inference_on_function_of_t_using_the_t: Fail
|
|||
inference/future_then_explicit_future: Fail
|
||||
inference/generic_functions_return_typedef: 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_use_of_void: Fail
|
||||
inference/list_literals_can_infer_null_top_level: Fail
|
||||
|
|
|
@ -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() {}
|
|
@ -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 {}
|
|
@ -0,0 +1,7 @@
|
|||
library test;
|
||||
import self as self;
|
||||
|
||||
static method test() → dynamic
|
||||
;
|
||||
static method main() → dynamic
|
||||
;
|
|
@ -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 {}
|
|
@ -6,30 +6,29 @@
|
|||
library test;
|
||||
|
||||
test() {
|
||||
f0() => 42;
|
||||
f1() async => 42;
|
||||
/*@returnType=int*/ f0() => 42;
|
||||
/*@returnType=Future<int>*/ f1() async => 42;
|
||||
|
||||
f2() {
|
||||
/*@returnType=int*/ f2() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
f3() async {
|
||||
/*@returnType=Future<int>*/ f3() async {
|
||||
return 42;
|
||||
}
|
||||
|
||||
f4() sync* {
|
||||
/*@returnType=Iterable<int>*/ f4() sync* {
|
||||
yield 42;
|
||||
}
|
||||
|
||||
f5() async* {
|
||||
/*@returnType=Stream<int>*/ f5() async* {
|
||||
yield 42;
|
||||
}
|
||||
|
||||
num f6() => 42;
|
||||
|
||||
f7() => f7();
|
||||
f8() => /*error:REFERENCED_BEFORE_DECLARATION*/ f9();
|
||||
f9() => f5();
|
||||
/*@returnType=dynamic*/ f7() => f7();
|
||||
/*@returnType=Stream<int>*/ f8() => f5();
|
||||
|
||||
var /*@type=() -> int*/ v0 = f0;
|
||||
var /*@type=() -> Future<int>*/ v1 = f1;
|
||||
|
@ -39,8 +38,7 @@ test() {
|
|||
var /*@type=() -> Stream<int>*/ v5 = f5;
|
||||
var /*@type=() -> num*/ v6 = f6;
|
||||
var /*@type=() -> dynamic*/ v7 = f7;
|
||||
var /*@type=() -> dynamic*/ v8 = f8;
|
||||
var /*@type=() -> Stream<int>*/ v9 = f9;
|
||||
var /*@type=() -> Stream<int>*/ v8 = f8;
|
||||
}
|
||||
|
||||
main() {}
|
||||
|
|
|
@ -1,31 +1,39 @@
|
|||
library test;
|
||||
import self as self;
|
||||
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 {
|
||||
function f0() → dynamic
|
||||
function f0() → core::int
|
||||
return 42;
|
||||
function f1() → dynamic async
|
||||
function f1() → asy::Future<core::int> async
|
||||
return 42;
|
||||
function f2() → dynamic {
|
||||
function f2() → core::int {
|
||||
return 42;
|
||||
}
|
||||
function f3() → dynamic async {
|
||||
function f3() → asy::Future<core::int> async {
|
||||
return 42;
|
||||
}
|
||||
function f4() → dynamic sync* {
|
||||
function f4() → core::Iterable<core::int> sync* {
|
||||
yield 42;
|
||||
}
|
||||
function f5() → dynamic async* {
|
||||
function f5() → asy::Stream<core::int> async* {
|
||||
yield 42;
|
||||
}
|
||||
function f6() → core::num
|
||||
return 42;
|
||||
function f7() → dynamic
|
||||
return f7.call();
|
||||
function f8() → dynamic
|
||||
return throw new core::NoSuchMethodError::_withType(null, #f9, 32, <dynamic>[].toList(growable: false), <dynamic, dynamic>{}, null);
|
||||
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 ^"));
|
||||
function f8() → asy::Stream<core::int>
|
||||
return f5.call();
|
||||
() → 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 {}
|
||||
|
|
|
@ -15,8 +15,8 @@ void optional_toplevel([x = /*@typeArgs=int*/ const [0]]) {}
|
|||
void named_toplevel({x: /*@typeArgs=int*/ const [0]}) {}
|
||||
|
||||
main() {
|
||||
void optional_local([x = /*@typeArgs=int*/ const [0]]) {}
|
||||
void named_local({x: /*@typeArgs=int*/ const [0]}) {}
|
||||
void optional_local([/*@type=dynamic*/ x = /*@typeArgs=int*/ const [0]]) {}
|
||||
void named_local({/*@type=dynamic*/ x: /*@typeArgs=int*/ const [0]}) {}
|
||||
var /*@type=C<dynamic>*/ c_optional_toplevel =
|
||||
new /*@typeArgs=dynamic*/ C.optional(optional_toplevel);
|
||||
var /*@type=C<dynamic>*/ c_named_toplevel =
|
||||
|
|
Loading…
Reference in a new issue