mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 11:11:05 +00:00
Store inferred type arguments for tear-off(s).
This change passes pre-submit run over google3. https://test.corp.google.com/ui#id=OCL:260993915:BASE:261043007:1564632838252:e80ace42 Change-Id: Ibef739f241fdb60a574da971961b1abe6abde9c8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/111402 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
5456641022
commit
a05ef7c0cc
|
@ -4647,6 +4647,18 @@ abstract class SimpleIdentifier implements Identifier {
|
|||
/// information to the given [element].
|
||||
void set staticElement(Element element);
|
||||
|
||||
/// If the identifier is a tear-off, return the inferred type arguments
|
||||
/// applied to the function type of the element to produce its [staticType].
|
||||
///
|
||||
/// Return an empty list if the function type does not have type parameters.
|
||||
///
|
||||
/// Return an empty list if the context type has type parameters.
|
||||
///
|
||||
/// Return `null` if not a tear-off.
|
||||
///
|
||||
/// Return `null` if the AST structure has not been resolved.
|
||||
List<DartType> get tearOffTypeArgumentTypes;
|
||||
|
||||
/// Return the token representing the identifier.
|
||||
Token get token;
|
||||
|
||||
|
|
|
@ -8902,6 +8902,9 @@ class SimpleIdentifierImpl extends IdentifierImpl implements SimpleIdentifier {
|
|||
/// getter context.
|
||||
AuxiliaryElements auxiliaryElements = null;
|
||||
|
||||
@override
|
||||
List<DartType> tearOffTypeArgumentTypes;
|
||||
|
||||
/// Initialize a newly created identifier.
|
||||
SimpleIdentifierImpl(this.token);
|
||||
|
||||
|
|
|
@ -6386,6 +6386,8 @@ class ResolutionCopier implements AstVisitor<bool> {
|
|||
toNode.staticElement = node.staticElement;
|
||||
toNode.staticType = node.staticType;
|
||||
toNode.auxiliaryElements = node.auxiliaryElements;
|
||||
(toNode as SimpleIdentifierImpl).tearOffTypeArgumentTypes =
|
||||
node.tearOffTypeArgumentTypes;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -831,7 +831,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
staticType = staticElement.type;
|
||||
}
|
||||
|
||||
staticType = _inferGenericInstantiationFromContext(node, staticType);
|
||||
staticType = _inferTearOff(node, node.identifier, staticType);
|
||||
if (!_inferObjectAccess(node, staticType, prefixedIdentifier)) {
|
||||
_recordStaticType(prefixedIdentifier, staticType);
|
||||
_recordStaticType(node, staticType);
|
||||
|
@ -924,7 +924,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
_nonNullableEnabled) {
|
||||
staticType = _typeSystem.makeNullable(staticType);
|
||||
}
|
||||
staticType = _inferGenericInstantiationFromContext(node, staticType);
|
||||
staticType = _inferTearOff(node, node.propertyName, staticType);
|
||||
|
||||
if (!_inferObjectAccess(node, staticType, propertyName)) {
|
||||
_recordStaticType(propertyName, staticType);
|
||||
|
@ -1074,7 +1074,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
} else {
|
||||
staticType = _dynamicType;
|
||||
}
|
||||
staticType = _inferGenericInstantiationFromContext(node, staticType);
|
||||
staticType = _inferTearOff(node, node, staticType);
|
||||
_recordStaticType(node, staticType);
|
||||
}
|
||||
|
||||
|
@ -1567,23 +1567,6 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an uninstantiated generic function type, try to infer the
|
||||
* instantiated generic function type from the surrounding context.
|
||||
*/
|
||||
DartType _inferGenericInstantiationFromContext(AstNode node, DartType type) {
|
||||
TypeSystem ts = _typeSystem;
|
||||
var context = InferenceContext.getContext(node);
|
||||
if (context is FunctionType &&
|
||||
type is FunctionType &&
|
||||
ts is Dart2TypeSystem) {
|
||||
// TODO(scheglov) Also store type arguments for identifiers.
|
||||
return ts.inferFunctionTypeInstantiation(context, type,
|
||||
errorReporter: _resolver.errorReporter, errorNode: node);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a possibly generic invocation like `o.m(args)` or `(f)(args)` try to
|
||||
* infer the instantiated generic function type.
|
||||
|
@ -1929,6 +1912,36 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an uninstantiated generic function type, referenced by the
|
||||
* [identifier] in the tear-off [expression], try to infer the instantiated
|
||||
* generic function type from the surrounding context.
|
||||
*/
|
||||
DartType _inferTearOff(
|
||||
Expression expression,
|
||||
SimpleIdentifier identifier,
|
||||
DartType tearOffType,
|
||||
) {
|
||||
TypeSystem ts = _typeSystem;
|
||||
var context = InferenceContext.getContext(expression);
|
||||
if (context is FunctionType &&
|
||||
tearOffType is FunctionType &&
|
||||
ts is Dart2TypeSystem) {
|
||||
var typeArguments = ts.inferFunctionTypeInstantiation(
|
||||
context,
|
||||
tearOffType,
|
||||
errorReporter: _resolver.errorReporter,
|
||||
errorNode: expression,
|
||||
);
|
||||
(identifier as SimpleIdentifierImpl).tearOffTypeArgumentTypes =
|
||||
typeArguments;
|
||||
if (typeArguments.isNotEmpty) {
|
||||
return tearOffType.instantiate(typeArguments);
|
||||
}
|
||||
}
|
||||
return tearOffType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the given [node] is not a type literal.
|
||||
*/
|
||||
|
|
|
@ -202,18 +202,18 @@ class Dart2TypeSystem extends TypeSystem {
|
|||
* Given a generic function type `F<T0, T1, ... Tn>` and a context type C,
|
||||
* infer an instantiation of F, such that `F<S0, S1, ..., Sn>` <: C.
|
||||
*
|
||||
* This is similar to [inferGenericFunctionOrType], but the return type is also
|
||||
* considered as part of the solution.
|
||||
* This is similar to [inferGenericFunctionOrType2], but the return type is
|
||||
* also considered as part of the solution.
|
||||
*
|
||||
* If this function is called with a [contextType] that is also
|
||||
* uninstantiated, or a [fnType] that is already instantiated, it will have
|
||||
* no effect and return [fnType].
|
||||
* no effect and return `null`.
|
||||
*/
|
||||
FunctionType inferFunctionTypeInstantiation(
|
||||
List<DartType> inferFunctionTypeInstantiation(
|
||||
FunctionType contextType, FunctionType fnType,
|
||||
{ErrorReporter errorReporter, AstNode errorNode}) {
|
||||
if (contextType.typeFormals.isNotEmpty || fnType.typeFormals.isEmpty) {
|
||||
return fnType;
|
||||
return const <DartType>[];
|
||||
}
|
||||
|
||||
// Create a TypeSystem that will allow certain type parameters to be
|
||||
|
@ -224,12 +224,11 @@ class Dart2TypeSystem extends TypeSystem {
|
|||
inferrer.constrainGenericFunctionInContext(fnType, contextType);
|
||||
|
||||
// Infer and instantiate the resulting type.
|
||||
var inferredTypes = inferrer.infer(
|
||||
return inferrer.infer(
|
||||
fnType.typeFormals,
|
||||
errorReporter: errorReporter,
|
||||
errorNode: errorNode,
|
||||
);
|
||||
return fnType.instantiate(inferredTypes);
|
||||
}
|
||||
|
||||
/// Infers a generic type, function, method, or list/map literal
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../driver_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(TearOffTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class TearOffTest extends DriverResolutionTest {
|
||||
test_empty_contextNotInstantiated() async {
|
||||
await assertNoErrorsInCode('''
|
||||
T f<T>(T x) => x;
|
||||
|
||||
void test() {
|
||||
U Function<U>(U) context;
|
||||
context = f; // 1
|
||||
}
|
||||
''');
|
||||
_assertTearOff(
|
||||
'f; // 1',
|
||||
findElement.topFunction('f'),
|
||||
'T Function<T>(T)',
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
test_empty_notGeneric() async {
|
||||
await assertNoErrorsInCode('''
|
||||
int f(int x) => x;
|
||||
|
||||
void test() {
|
||||
int Function(int) context;
|
||||
context = f; // 1
|
||||
}
|
||||
''');
|
||||
_assertTearOff(
|
||||
'f; // 1',
|
||||
findElement.topFunction('f'),
|
||||
'int Function(int)',
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
test_notEmpty() async {
|
||||
await assertNoErrorsInCode('''
|
||||
T f<T>(T x) => x;
|
||||
|
||||
class C {
|
||||
T f<T>(T x) => x;
|
||||
static T g<T>(T x) => x;
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
void test() {
|
||||
int Function(int) func;
|
||||
func = super.f; // 1
|
||||
}
|
||||
}
|
||||
|
||||
void test() {
|
||||
T h<T>(T x) => x;
|
||||
int Function(int) func;
|
||||
func = f; // 2
|
||||
func = new C().f; // 3
|
||||
func = C.g; // 4
|
||||
func = h; // 5
|
||||
}
|
||||
''');
|
||||
_assertTearOff(
|
||||
'f; // 1',
|
||||
findElement.method('f', of: 'C'),
|
||||
'int Function(int)',
|
||||
['int'],
|
||||
);
|
||||
_assertTearOff(
|
||||
'f; // 2',
|
||||
findElement.topFunction('f'),
|
||||
'int Function(int)',
|
||||
['int'],
|
||||
);
|
||||
_assertTearOff(
|
||||
'f; // 3',
|
||||
findElement.method('f', of: 'C'),
|
||||
'int Function(int)',
|
||||
['int'],
|
||||
);
|
||||
_assertTearOff(
|
||||
'g; // 4',
|
||||
findElement.method('g', of: 'C'),
|
||||
'int Function(int)',
|
||||
['int'],
|
||||
);
|
||||
_assertTearOff(
|
||||
'h; // 5',
|
||||
findElement.localFunction('h'),
|
||||
'int Function(int)',
|
||||
['int'],
|
||||
);
|
||||
}
|
||||
|
||||
test_null_notTearOff() async {
|
||||
await assertNoErrorsInCode('''
|
||||
T f<T>(T x) => x;
|
||||
|
||||
void test() {
|
||||
f(0);
|
||||
}
|
||||
''');
|
||||
_assertTearOff(
|
||||
'f(0);',
|
||||
findElement.topFunction('f'),
|
||||
'T Function<T>(T)',
|
||||
null,
|
||||
);
|
||||
assertInvokeType(
|
||||
findNode.methodInvocation('f(0)'),
|
||||
'int Function(int)',
|
||||
);
|
||||
}
|
||||
|
||||
void _assertTearOff(
|
||||
String search,
|
||||
ExecutableElement element,
|
||||
String type,
|
||||
List<String> typeArguments,
|
||||
) {
|
||||
var id = findNode.simple(search);
|
||||
assertElement(id, element);
|
||||
assertType(id, type);
|
||||
if (typeArguments != null) {
|
||||
assertElementTypeStrings(id.tearOffTypeArgumentTypes, typeArguments);
|
||||
} else {
|
||||
expect(id.tearOffTypeArgumentTypes, isNull);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import 'map_literal_test.dart' as map_literal;
|
|||
import 'prefix_expressions_test.dart' as prefix_expressions;
|
||||
import 'set_literal_test.dart' as set_literal;
|
||||
import 'statements_test.dart' as statements;
|
||||
import 'tear_off_test.dart' as tear_off;
|
||||
import 'throw_test.dart' as throw_expression;
|
||||
import 'type_test_expressions_test.dart' as type_test_expressions;
|
||||
|
||||
|
@ -27,6 +28,7 @@ main() {
|
|||
prefix_expressions.main();
|
||||
set_literal.main();
|
||||
statements.main();
|
||||
tear_off.main();
|
||||
throw_expression.main();
|
||||
type_test_expressions.main();
|
||||
}, name: 'type inference');
|
||||
|
|
|
@ -2999,7 +2999,7 @@ class CodeGenerator extends Object
|
|||
@override
|
||||
js_ast.Expression visitSimpleIdentifier(SimpleIdentifier node,
|
||||
[PrefixedIdentifier prefix]) {
|
||||
var typeArgs = _getTypeArgs(node.staticElement, node.staticType);
|
||||
var typeArgs = _emitFunctionTypeArguments(node.tearOffTypeArgumentTypes);
|
||||
var simpleId = _emitSimpleIdentifier(node, prefix)
|
||||
..sourceInformation = _nodeSpan(node);
|
||||
if (prefix != null &&
|
||||
|
@ -3991,57 +3991,18 @@ class CodeGenerator extends Object
|
|||
if (function is Identifier && !_reifyGeneric(function.staticElement)) {
|
||||
return null;
|
||||
}
|
||||
return _emitFunctionTypeArguments(
|
||||
node, function.staticType, node.staticInvokeType, node.typeArguments);
|
||||
if (node.typeArgumentTypes.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return _emitFunctionTypeArguments(node.typeArgumentTypes);
|
||||
}
|
||||
|
||||
/// If `g` is a generic function type, and `f` is an instantiation of it,
|
||||
/// then this will return the type arguments to apply, otherwise null.
|
||||
/// If has [typeArguments], emit them, otherwise return `null`.
|
||||
List<js_ast.Expression> _emitFunctionTypeArguments(
|
||||
AstNode node, DartType g, DartType f,
|
||||
[TypeArgumentList typeArgs]) {
|
||||
if (node is InvocationExpression) {
|
||||
if (g is! FunctionType && typeArgs == null) {
|
||||
return null;
|
||||
}
|
||||
var typeArguments = node.typeArgumentTypes;
|
||||
return typeArguments.map(_emitType).toList(growable: false);
|
||||
}
|
||||
|
||||
if (g is FunctionType &&
|
||||
g.typeFormals.isNotEmpty &&
|
||||
f is FunctionType &&
|
||||
f.typeFormals.isEmpty) {
|
||||
var typeArguments = _recoverTypeArguments(g, f);
|
||||
return typeArguments.map(_emitType).toList(growable: false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Given a generic function type [g] and an instantiated function type [f],
|
||||
/// find a list of type arguments TArgs such that `g<TArgs> == f`,
|
||||
/// and return TArgs.
|
||||
///
|
||||
/// This function must be called with type [f] that was instantiated from [g].
|
||||
Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) {
|
||||
// TODO(jmesserly): this design is a bit unfortunate. It would be nice if
|
||||
// resolution could simply create a synthetic type argument list.
|
||||
assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty);
|
||||
assert(g.typeFormals.length <= f.typeArguments.length);
|
||||
|
||||
// Instantiation in Analyzer works like this:
|
||||
// Given:
|
||||
// {U/T} <S> T -> S
|
||||
// Where {U/T} represents the typeArguments (U) and typeParameters (T) list,
|
||||
// and <S> represents the typeFormals.
|
||||
//
|
||||
// Now instantiate([V]), and the result should be:
|
||||
// {U/T, V/S} T -> S.
|
||||
//
|
||||
// Therefore, we can recover the typeArguments from our instantiated
|
||||
// function.
|
||||
return f.typeArguments.skip(f.typeArguments.length - g.typeFormals.length);
|
||||
List<DartType> typeArguments) {
|
||||
if (typeArguments == null) return null;
|
||||
if (typeArguments.isEmpty) return null;
|
||||
return typeArguments.map(_emitType).toList(growable: false);
|
||||
}
|
||||
|
||||
/// Emits code for the `JS(...)` macro.
|
||||
|
@ -5260,23 +5221,9 @@ class CodeGenerator extends Object
|
|||
}
|
||||
}
|
||||
|
||||
List<js_ast.Expression> _getTypeArgs(Element member, DartType instantiated) {
|
||||
DartType type;
|
||||
if (member is ExecutableElement) {
|
||||
type = member.type;
|
||||
} else if (member is VariableElement) {
|
||||
type = member.type;
|
||||
}
|
||||
|
||||
// TODO(jmesserly): handle explicitly passed type args.
|
||||
if (type == null) return null;
|
||||
return _emitFunctionTypeArguments(null, type, instantiated);
|
||||
}
|
||||
|
||||
/// Shared code for [PrefixedIdentifier] and [PropertyAccess].
|
||||
js_ast.Expression _emitPropertyGet(
|
||||
Expression receiver, SimpleIdentifier memberId, Expression accessNode) {
|
||||
var resultType = accessNode.staticType;
|
||||
var accessor = memberId.staticElement;
|
||||
var memberName = memberId.name;
|
||||
var receiverType = getStaticType(receiver);
|
||||
|
@ -5329,7 +5276,9 @@ class CodeGenerator extends Object
|
|||
result = _emitTargetAccess(jsTarget, jsName, accessor, memberId);
|
||||
}
|
||||
|
||||
var typeArgs = _getTypeArgs(accessor, resultType);
|
||||
var typeArgs = _emitFunctionTypeArguments(
|
||||
memberId.tearOffTypeArgumentTypes,
|
||||
);
|
||||
return typeArgs == null
|
||||
? result
|
||||
: runtimeCall('gbind(#, #)', [result, typeArgs]);
|
||||
|
|
Loading…
Reference in a new issue