mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Rework linker type inference using ExprBuilder.
This CL changes the way linker type inference works. Instead of walking the operations in an UnlinkedExpr in order and inferring types directly, it first builds an AST for the expression using ExprBuilder, then uses ResolverVisitor to resolve the AST and perform type inference. This helps ensure that summary type inference and ordinary AST-based type inference produce the same result, since they now use the same core algorithm. In particular, this means that summary type inference now does downwards inference properly. Since the ResolverVisitor makes greater use of the element model than the rest of the linker, this required implementing additional methods in the linker's element model. This CL includes some minor fixes to ExprBuilder that were uncovered during testing. It also contains some minor changes to ResolverVisitor to make it work during linking. Fixes #32525. Fixes #32394. Change-Id: I5ec9b2bf5565ad30b8cc856475334323dc118da8 Reviewed-on: https://dart-review.googlesource.com/48741 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
494b861d00
commit
506ac7f953
|
@ -7043,15 +7043,16 @@ set speed2(int ms) {}
|
|||
}
|
||||
|
||||
test_removeTypeAnnotation_avoidTypesOnClosureParameters_FunctionTypedFormalParameter() async {
|
||||
// Note: explicit type `Function` to work around dartbug.com/32708.
|
||||
String src = '''
|
||||
var functionWithFunction = (/*LINT*/int f(int x)) => f(0);
|
||||
Function functionWithFunction = (/*LINT*/int f(int x)) => f(0);
|
||||
''';
|
||||
await findLint(src, LintNames.avoid_types_on_closure_parameters);
|
||||
|
||||
await applyFix(DartFixKind.REPLACE_WITH_IDENTIFIER);
|
||||
|
||||
verifyResult('''
|
||||
var functionWithFunction = (f) => f(0);
|
||||
Function functionWithFunction = (f) => f(0);
|
||||
''');
|
||||
}
|
||||
|
||||
|
|
|
@ -4999,7 +4999,8 @@ class ResolverVisitor extends ScopedVisitor {
|
|||
}
|
||||
this.inferenceContext = new InferenceContext._(
|
||||
typeProvider, typeSystem, strongModeHints, errorReporter);
|
||||
this.typeAnalyzer = new StaticTypeAnalyzer(this);
|
||||
this.typeAnalyzer =
|
||||
new StaticTypeAnalyzer(this, propagateTypes: propagateTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5723,7 +5724,8 @@ class ResolverVisitor extends ScopedVisitor {
|
|||
}
|
||||
// Clone the ASTs for default formal parameters, so that we can use them
|
||||
// during constant evaluation.
|
||||
if (!_hasSerializedConstantInitializer(element)) {
|
||||
if (element is ConstVariableElement &&
|
||||
!_hasSerializedConstantInitializer(element)) {
|
||||
(element as ConstVariableElement).constantInitializer =
|
||||
_createCloner().cloneNode(node.defaultValue);
|
||||
}
|
||||
|
|
|
@ -432,7 +432,6 @@ class ExprBuilder {
|
|||
simpleParam.identifier.staticElement = param;
|
||||
simpleParam.element = param;
|
||||
var unlinkedParam = param.unlinkedParam;
|
||||
FormalParameter paramAst;
|
||||
if (unlinkedParam.kind == UnlinkedParamKind.positional) {
|
||||
return AstTestFactory.positionalFormalParameter(simpleParam, null);
|
||||
} else if (unlinkedParam.kind == UnlinkedParamKind.named) {
|
||||
|
@ -570,13 +569,17 @@ class ExprBuilder {
|
|||
ReferenceInfo info = resynthesizer.getReferenceInfo(ref.reference);
|
||||
Expression node = _buildIdentifierSequence(info);
|
||||
TypeArgumentList typeArguments = _buildTypeArguments();
|
||||
var period = TokenFactory.tokenFromType(TokenType.PERIOD);
|
||||
var argumentList = AstTestFactory.argumentList(arguments);
|
||||
if (node is SimpleIdentifier) {
|
||||
_push(astFactory.methodInvocation(
|
||||
null,
|
||||
TokenFactory.tokenFromType(TokenType.PERIOD),
|
||||
node,
|
||||
typeArguments,
|
||||
AstTestFactory.argumentList(arguments)));
|
||||
null, period, node, typeArguments, argumentList));
|
||||
} else if (node is PropertyAccess) {
|
||||
_push(astFactory.methodInvocation(
|
||||
node.target, period, node.propertyName, typeArguments, argumentList));
|
||||
} else if (node is PrefixedIdentifier) {
|
||||
_push(astFactory.methodInvocation(
|
||||
node.prefix, period, node.identifier, typeArguments, argumentList));
|
||||
} else {
|
||||
throw new UnimplementedError('For ${node?.runtimeType}: $node');
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,4 +31,7 @@ class NonErrorResolverTest_Driver extends NonErrorResolverTest {
|
|||
@override // Passes with driver
|
||||
test_infer_mixin_with_substitution_functionType() =>
|
||||
super.test_infer_mixin_with_substitution_functionType();
|
||||
|
||||
@override // Passes with driver
|
||||
test_issue_32394() => super.test_issue_32394();
|
||||
}
|
||||
|
|
|
@ -3619,6 +3619,26 @@ f(S s) async {
|
|||
verify([source]);
|
||||
}
|
||||
|
||||
@failingTest // Fails with the old task model
|
||||
test_issue_32394() async {
|
||||
Source source = addSource('''
|
||||
var x = y.map((a) => a.toString());
|
||||
var y = [3];
|
||||
var z = x.toList();
|
||||
|
||||
void main() {
|
||||
String p = z;
|
||||
}
|
||||
''');
|
||||
var result = await computeAnalysisResult(source);
|
||||
var z = result.unit.element.topLevelVariables
|
||||
.where((e) => e.name == 'z')
|
||||
.single;
|
||||
expect(z.type.toString(), 'List<String>');
|
||||
assertErrors(source, [StaticTypeWarningCode.INVALID_ASSIGNMENT]);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
test_listElementTypeNotAssignable() async {
|
||||
Source source = addSource(r'''
|
||||
var v1 = <int> [42];
|
||||
|
|
|
@ -339,7 +339,7 @@ var y = x;
|
|||
''');
|
||||
LibraryElementForLink library = linker.getLibrary(linkerInputs.testDartUri);
|
||||
expect(_getVariable(library.getContainedName('y')).inferredType.toString(),
|
||||
'() → dynamic');
|
||||
'() → Null');
|
||||
}
|
||||
|
||||
void test_inferredType_closure_fromBundle_identifierSequence() {
|
||||
|
@ -399,7 +399,7 @@ class C {
|
|||
ClassElementForLink_Class cls = library.getContainedName('C');
|
||||
expect(cls.fields, hasLength(1));
|
||||
var field = cls.fields[0];
|
||||
expect(field.type.toString(), '(<bottom>) → dynamic');
|
||||
expect(field.type.toString(), '(<bottom>) → int');
|
||||
}
|
||||
|
||||
void test_inferredType_instanceField_dynamic() {
|
||||
|
|
|
@ -2919,25 +2919,11 @@ const dynamic V = const
|
|||
class C {}
|
||||
const V = const C.named();
|
||||
''', allowErrors: true);
|
||||
if (isSharedFrontEnd) {
|
||||
checkElementText(library, r'''
|
||||
checkElementText(library, r'''
|
||||
class C {
|
||||
}
|
||||
const dynamic V = #invalidConst;
|
||||
''');
|
||||
} else if (isStrongMode) {
|
||||
checkElementText(library, r'''
|
||||
class C {
|
||||
}
|
||||
const C V = #invalidConst;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, r'''
|
||||
class C {
|
||||
}
|
||||
const dynamic V = #invalidConst;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_const_invokeConstructor_named_unresolved2() async {
|
||||
|
@ -2960,22 +2946,10 @@ class C {
|
|||
import 'a.dart' as p;
|
||||
const V = const p.C.named();
|
||||
''', allowErrors: true);
|
||||
if (isSharedFrontEnd) {
|
||||
checkElementText(library, r'''
|
||||
checkElementText(library, r'''
|
||||
import 'a.dart' as p;
|
||||
const dynamic V = #invalidConst;
|
||||
''');
|
||||
} else if (isStrongMode) {
|
||||
checkElementText(library, r'''
|
||||
import 'a.dart' as p;
|
||||
const C V = #invalidConst;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, r'''
|
||||
import 'a.dart' as p;
|
||||
const dynamic V = #invalidConst;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_const_invokeConstructor_named_unresolved4() async {
|
||||
|
@ -3007,25 +2981,11 @@ const dynamic V = #invalidConst;
|
|||
class C<T> {}
|
||||
const V = const C.named();
|
||||
''', allowErrors: true);
|
||||
if (isSharedFrontEnd) {
|
||||
checkElementText(library, r'''
|
||||
checkElementText(library, r'''
|
||||
class C<T> {
|
||||
}
|
||||
const dynamic V = #invalidConst;
|
||||
''');
|
||||
} else if (isStrongMode) {
|
||||
checkElementText(library, r'''
|
||||
class C<T> {
|
||||
}
|
||||
const C<dynamic> V = #invalidConst;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, r'''
|
||||
class C<T> {
|
||||
}
|
||||
const dynamic V = #invalidConst;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_const_invokeConstructor_unnamed() async {
|
||||
|
@ -6455,6 +6415,19 @@ dynamic get y {}
|
|||
''');
|
||||
}
|
||||
|
||||
@failingTest
|
||||
test_implicitConstructor_named_const() async {
|
||||
// TODO(paulberry, scheglov): get this to pass
|
||||
var library = await checkLibrary('''
|
||||
class C {
|
||||
final Object x;
|
||||
const C.named(this.x);
|
||||
}
|
||||
const x = C.named(42);
|
||||
''');
|
||||
checkElementText(library, 'TODO(paulberry, scheglov)');
|
||||
}
|
||||
|
||||
test_implicitTopLevelVariable_getterFirst() async {
|
||||
var library =
|
||||
await checkLibrary('int get x => 0; void set x(int value) {}');
|
||||
|
@ -6633,6 +6606,158 @@ D d;
|
|||
''');
|
||||
}
|
||||
|
||||
@failingTest
|
||||
void test_infer_generic_typedef_complex() async {
|
||||
// TODO(paulberry, scheglov): get this test to pass.
|
||||
var library = await checkLibrary('''
|
||||
typedef F<T> = D<T,U> Function<U>();
|
||||
class C<V> {
|
||||
const C(F<V> f);
|
||||
}
|
||||
class D<T,U> {}
|
||||
D<int,U> f<U>() => null;
|
||||
const x = const C(f);
|
||||
''');
|
||||
checkElementText(library, '''TODO(paulberry, scheglov)''');
|
||||
}
|
||||
|
||||
void test_infer_generic_typedef_simple() async {
|
||||
var library = await checkLibrary('''
|
||||
typedef F = D<T> Function<T>();
|
||||
class C {
|
||||
const C(F f);
|
||||
}
|
||||
class D<T> {}
|
||||
D<T> f<T>() => null;
|
||||
const x = const C(f);
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
typedef F = D<T> Function<T>();
|
||||
class C {
|
||||
const C(<T>() → D<T> f);
|
||||
}
|
||||
class D<T> {
|
||||
}
|
||||
const C x = const
|
||||
C/*location: test.dart;C*/(
|
||||
f/*location: test.dart;f*/);
|
||||
D<T> f<T>() {}
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
typedef F = D<T> Function<T>();
|
||||
class C {
|
||||
const C(<T>() → D<T> f);
|
||||
}
|
||||
class D<T> {
|
||||
}
|
||||
const dynamic x = const
|
||||
C/*location: test.dart;C*/(
|
||||
f/*location: test.dart;f*/);
|
||||
D<T> f<T>() {}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_infer_property_set() async {
|
||||
var library = await checkLibrary('''
|
||||
class A {
|
||||
B b;
|
||||
}
|
||||
class B {
|
||||
C get c => null;
|
||||
void set c(C value) {}
|
||||
}
|
||||
class C {}
|
||||
class D extends C {}
|
||||
var a = new A();
|
||||
var x = a.b.c ??= new D();
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
class A {
|
||||
B b;
|
||||
}
|
||||
class B {
|
||||
C get c {}
|
||||
void set c(C value) {}
|
||||
}
|
||||
class C {
|
||||
}
|
||||
class D extends C {
|
||||
}
|
||||
A a;
|
||||
C x;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
class A {
|
||||
B b;
|
||||
}
|
||||
class B {
|
||||
C get c {}
|
||||
void set c(C value) {}
|
||||
}
|
||||
class C {
|
||||
}
|
||||
class D extends C {
|
||||
}
|
||||
dynamic a;
|
||||
dynamic x;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_inference_issue_32394() async {
|
||||
// Test the type inference involed in dartbug.com/32394
|
||||
var library = await checkLibrary('''
|
||||
var x = y.map((a) => a.toString());
|
||||
var y = [3];
|
||||
var z = x.toList();
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
Iterable<String> x;
|
||||
List<int> y;
|
||||
List<String> z;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
dynamic x;
|
||||
dynamic y;
|
||||
dynamic z;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_inference_map() async {
|
||||
var library = await checkLibrary('''
|
||||
class C {
|
||||
int p;
|
||||
}
|
||||
var x = <C>[];
|
||||
var y = x.map((c) => c.p);
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
class C {
|
||||
int p;
|
||||
}
|
||||
List<C> x;
|
||||
Iterable<int> y;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
class C {
|
||||
int p;
|
||||
}
|
||||
dynamic x;
|
||||
dynamic y;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_inferred_function_type_for_variable_in_generic_function() async {
|
||||
// In the code below, `x` has an inferred type of `() => int`, with 2
|
||||
// (unused) type parameters from the enclosing top level function.
|
||||
|
@ -9178,6 +9303,114 @@ dynamic d;
|
|||
''');
|
||||
}
|
||||
|
||||
test_type_inference_based_on_loadLibrary() async {
|
||||
addLibrarySource('/a.dart', '');
|
||||
var library = await checkLibrary('''
|
||||
import 'a.dart' deferred as a;
|
||||
var x = a.loadLibrary;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
import 'a.dart' deferred as a;
|
||||
() → Future<dynamic> x;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
import 'a.dart' deferred as a;
|
||||
dynamic x;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@failingTest
|
||||
test_type_inference_closure_with_function_typed_parameter() async {
|
||||
// TODO(paulberry, scheglov): get this test to pass. See dartbug.com/32708.
|
||||
var library = await checkLibrary('''
|
||||
var x = (int f(String x)) => 0;
|
||||
''');
|
||||
checkElementText(library, '''TODO(paulberry, scheglov)''');
|
||||
}
|
||||
|
||||
test_type_inference_closure_with_function_typed_parameter_new() async {
|
||||
var library = await checkLibrary('''
|
||||
var x = (int Function(String) f) => 0;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
((String) → int) → int x;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
dynamic x;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_type_inference_depends_on_exported_variable() async {
|
||||
addLibrarySource('/a.dart', 'export "b.dart";');
|
||||
addLibrarySource('/b.dart', 'var x = 0;');
|
||||
var library = await checkLibrary('''
|
||||
import 'a.dart';
|
||||
var y = x;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
import 'a.dart';
|
||||
int y;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
import 'a.dart';
|
||||
dynamic y;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_type_inference_nested_function() async {
|
||||
var library = await checkLibrary('''
|
||||
var x = (t) => (u) => t + u;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
(dynamic) → (dynamic) → dynamic x;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
dynamic x;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_type_inference_nested_function_with_parameter_types() async {
|
||||
var library = await checkLibrary('''
|
||||
var x = (int t) => (int u) => t + u;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
(int) → (int) → int x;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
dynamic x;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_type_inference_of_closure_with_default_value() async {
|
||||
var library = await checkLibrary('''
|
||||
var x = ([y: 0]) => y;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, '''
|
||||
([dynamic]) → dynamic x;
|
||||
''');
|
||||
} else {
|
||||
checkElementText(library, '''
|
||||
dynamic x;
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
test_type_invalid_topLevelVariableElement_asType() async {
|
||||
var library = await checkLibrary('''
|
||||
class C<T extends V> {}
|
||||
|
|
|
@ -1251,7 +1251,6 @@ string_unicode4_negative_test: CompileTimeError
|
|||
super_bound_closure_test/none: CompileTimeError
|
||||
super_setter_test: StaticWarning # Issue 28823
|
||||
switch_case_test/none: CompileTimeError
|
||||
type_inference_accessor_ref_test/06: MissingCompileTimeError
|
||||
type_promotion_functions_test/01: Pass
|
||||
type_promotion_functions_test/05: Pass
|
||||
type_promotion_functions_test/06: Pass
|
||||
|
|
Loading…
Reference in a new issue