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:
Paul Berry 2018-03-29 17:16:39 +00:00 committed by commit-bot@chromium.org
parent 494b861d00
commit 506ac7f953
9 changed files with 682 additions and 895 deletions

View file

@ -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);
''');
}

View file

@ -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);
}

View file

@ -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

View file

@ -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();
}

View file

@ -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];

View file

@ -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() {

View file

@ -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> {}

View file

@ -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