mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
Use pattern type schema for expression inference.
0277f03586
Change-Id: Ie305303ad85c0c7c920f9a28f4357abd9a02de3f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281580
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
e8842d862c
commit
55e0784a62
7 changed files with 232 additions and 5 deletions
|
@ -1180,7 +1180,9 @@ mixin TypeAnalyzer<
|
||||||
required void Function() dispatchBody,
|
required void Function() dispatchBody,
|
||||||
}) {
|
}) {
|
||||||
// Stack: ()
|
// Stack: ()
|
||||||
Type expressionType = analyzeExpression(expression, unknownType);
|
Type patternTypeSchema = dispatchPatternSchema(pattern);
|
||||||
|
Type expressionTypeSchema = iterableType(patternTypeSchema);
|
||||||
|
Type expressionType = analyzeExpression(expression, expressionTypeSchema);
|
||||||
// Stack: (Expression)
|
// Stack: (Expression)
|
||||||
|
|
||||||
Type? elementType = operations.matchIterableType(expressionType);
|
Type? elementType = operations.matchIterableType(expressionType);
|
||||||
|
@ -1950,6 +1952,9 @@ mixin TypeAnalyzer<
|
||||||
/// Queries whether [pattern] is a variable pattern.
|
/// Queries whether [pattern] is a variable pattern.
|
||||||
bool isVariablePattern(Node pattern);
|
bool isVariablePattern(Node pattern);
|
||||||
|
|
||||||
|
/// Returns the type `Iterable`, with type argument [elementType].
|
||||||
|
Type iterableType(Type elementType);
|
||||||
|
|
||||||
/// Returns the type `List`, with type argument [elementType].
|
/// Returns the type `List`, with type argument [elementType].
|
||||||
Type listType(Type elementType);
|
Type listType(Type elementType);
|
||||||
|
|
||||||
|
|
|
@ -1067,11 +1067,11 @@ class MiniAstOperations
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Type? matchIterableType(Type type) {
|
Type? matchIterableType(Type type) {
|
||||||
if (type is PrimaryType &&
|
if (type is PrimaryType && type.args.length == 1) {
|
||||||
type.name == 'Iterable' &&
|
if (type.name == 'Iterable' || type.name == 'List') {
|
||||||
type.args.length == 1) {
|
|
||||||
return type.args[0];
|
return type.args[0];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3832,6 +3832,11 @@ class _MiniAstTypeAnalyzer
|
||||||
@override
|
@override
|
||||||
bool isVariablePattern(Node pattern) => pattern is _VariablePattern;
|
bool isVariablePattern(Node pattern) => pattern is _VariablePattern;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Type iterableType(Type elementType) {
|
||||||
|
return PrimaryType('Iterable', args: [elementType]);
|
||||||
|
}
|
||||||
|
|
||||||
Type leastUpperBound(Type t1, Type t2) => _harness._operations._lub(t1, t2);
|
Type leastUpperBound(Type t1, Type t2) => _harness._operations._lub(t1, t2);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1634,6 +1634,26 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Pattern-for-in:', () {
|
group('Pattern-for-in:', () {
|
||||||
|
group('Expression context type schema:', () {
|
||||||
|
test('Pattern has type', () {
|
||||||
|
var x = Var('x');
|
||||||
|
h.run([
|
||||||
|
patternForIn(x.pattern(type: 'num'),
|
||||||
|
expr('List<int>').checkContext('Iterable<num>'), [])
|
||||||
|
.checkIr('forEach(expr(List<int>), varPattern(x, '
|
||||||
|
'matchedType: int, staticType: num), block())'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
test('Pattern does not have type', () {
|
||||||
|
var x = Var('x');
|
||||||
|
h.run([
|
||||||
|
patternForIn(x.pattern(),
|
||||||
|
expr('List<int>').checkContext('Iterable<?>'), [])
|
||||||
|
.checkIr('forEach(expr(List<int>), varPattern(x, '
|
||||||
|
'matchedType: int, staticType: int), block())'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
group('Expression type:', () {
|
group('Expression type:', () {
|
||||||
test('Iterable', () {
|
test('Iterable', () {
|
||||||
var x = Var('x');
|
var x = Var('x');
|
||||||
|
|
|
@ -1211,6 +1211,11 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
||||||
@override
|
@override
|
||||||
bool isVariablePattern(AstNode pattern) => pattern is DeclaredVariablePattern;
|
bool isVariablePattern(AstNode pattern) => pattern is DeclaredVariablePattern;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DartType iterableType(DartType elementType) {
|
||||||
|
return typeProvider.iterableType(elementType);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DartType listType(DartType elementType) {
|
DartType listType(DartType elementType) {
|
||||||
return typeProvider.listType(elementType);
|
return typeProvider.listType(elementType);
|
||||||
|
|
|
@ -176,6 +176,98 @@ ForElement
|
||||||
''');
|
''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_iterableContextType_patternVariable_typed() async {
|
||||||
|
await assertNoErrorsInCode(r'''
|
||||||
|
void f() {
|
||||||
|
[for (var (int a) in g()) a];
|
||||||
|
}
|
||||||
|
|
||||||
|
T g<T>() => throw 0;
|
||||||
|
''');
|
||||||
|
var node = findNode.singleForElement;
|
||||||
|
assertResolvedNodeText(node, r'''
|
||||||
|
ForElement
|
||||||
|
forKeyword: for
|
||||||
|
leftParenthesis: (
|
||||||
|
forLoopParts: ForEachPartsWithPattern
|
||||||
|
keyword: var
|
||||||
|
pattern: ParenthesizedPattern
|
||||||
|
leftParenthesis: (
|
||||||
|
pattern: DeclaredVariablePattern
|
||||||
|
type: NamedType
|
||||||
|
name: SimpleIdentifier
|
||||||
|
token: int
|
||||||
|
staticElement: dart:core::@class::int
|
||||||
|
staticType: null
|
||||||
|
type: int
|
||||||
|
name: a
|
||||||
|
declaredElement: a@28
|
||||||
|
type: int
|
||||||
|
rightParenthesis: )
|
||||||
|
inKeyword: in
|
||||||
|
iterable: MethodInvocation
|
||||||
|
methodName: SimpleIdentifier
|
||||||
|
token: g
|
||||||
|
staticElement: self::@function::g
|
||||||
|
staticType: T Function<T>()
|
||||||
|
argumentList: ArgumentList
|
||||||
|
leftParenthesis: (
|
||||||
|
rightParenthesis: )
|
||||||
|
staticInvokeType: Iterable<int> Function()
|
||||||
|
staticType: Iterable<int>
|
||||||
|
typeArgumentTypes
|
||||||
|
Iterable<int>
|
||||||
|
rightParenthesis: )
|
||||||
|
body: SimpleIdentifier
|
||||||
|
token: a
|
||||||
|
staticElement: a@28
|
||||||
|
staticType: int
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
|
test_iterableContextType_patternVariable_untyped() async {
|
||||||
|
await assertNoErrorsInCode(r'''
|
||||||
|
void f() {
|
||||||
|
[for (var (a) in g()) a];
|
||||||
|
}
|
||||||
|
|
||||||
|
T g<T>() => throw 0;
|
||||||
|
''');
|
||||||
|
var node = findNode.singleForElement;
|
||||||
|
assertResolvedNodeText(node, r'''
|
||||||
|
ForElement
|
||||||
|
forKeyword: for
|
||||||
|
leftParenthesis: (
|
||||||
|
forLoopParts: ForEachPartsWithPattern
|
||||||
|
keyword: var
|
||||||
|
pattern: ParenthesizedPattern
|
||||||
|
leftParenthesis: (
|
||||||
|
pattern: DeclaredVariablePattern
|
||||||
|
name: a
|
||||||
|
declaredElement: hasImplicitType a@24
|
||||||
|
type: Object?
|
||||||
|
rightParenthesis: )
|
||||||
|
inKeyword: in
|
||||||
|
iterable: MethodInvocation
|
||||||
|
methodName: SimpleIdentifier
|
||||||
|
token: g
|
||||||
|
staticElement: self::@function::g
|
||||||
|
staticType: T Function<T>()
|
||||||
|
argumentList: ArgumentList
|
||||||
|
leftParenthesis: (
|
||||||
|
rightParenthesis: )
|
||||||
|
staticInvokeType: Iterable<Object?> Function()
|
||||||
|
staticType: Iterable<Object?>
|
||||||
|
typeArgumentTypes
|
||||||
|
Iterable<Object?>
|
||||||
|
rightParenthesis: )
|
||||||
|
body: SimpleIdentifier
|
||||||
|
token: a
|
||||||
|
staticElement: a@24
|
||||||
|
staticType: Object?
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
test_keyword_final_patternVariable() async {
|
test_keyword_final_patternVariable() async {
|
||||||
await assertNoErrorsInCode(r'''
|
await assertNoErrorsInCode(r'''
|
||||||
void f(List<int> x) {
|
void f(List<int> x) {
|
||||||
|
|
|
@ -532,6 +532,100 @@ ForStatement
|
||||||
''');
|
''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_iterableContextType_patternVariable_typed() async {
|
||||||
|
await assertErrorsInCode(r'''
|
||||||
|
void f() {
|
||||||
|
for (var (int a) in g()) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
T g<T>() => throw 0;
|
||||||
|
''', [
|
||||||
|
error(HintCode.UNUSED_LOCAL_VARIABLE, 27, 1),
|
||||||
|
]);
|
||||||
|
var node = findNode.forStatement('for');
|
||||||
|
assertResolvedNodeText(node, r'''
|
||||||
|
ForStatement
|
||||||
|
forKeyword: for
|
||||||
|
leftParenthesis: (
|
||||||
|
forLoopParts: ForEachPartsWithPattern
|
||||||
|
keyword: var
|
||||||
|
pattern: ParenthesizedPattern
|
||||||
|
leftParenthesis: (
|
||||||
|
pattern: DeclaredVariablePattern
|
||||||
|
type: NamedType
|
||||||
|
name: SimpleIdentifier
|
||||||
|
token: int
|
||||||
|
staticElement: dart:core::@class::int
|
||||||
|
staticType: null
|
||||||
|
type: int
|
||||||
|
name: a
|
||||||
|
declaredElement: a@27
|
||||||
|
type: int
|
||||||
|
rightParenthesis: )
|
||||||
|
inKeyword: in
|
||||||
|
iterable: MethodInvocation
|
||||||
|
methodName: SimpleIdentifier
|
||||||
|
token: g
|
||||||
|
staticElement: self::@function::g
|
||||||
|
staticType: T Function<T>()
|
||||||
|
argumentList: ArgumentList
|
||||||
|
leftParenthesis: (
|
||||||
|
rightParenthesis: )
|
||||||
|
staticInvokeType: Iterable<int> Function()
|
||||||
|
staticType: Iterable<int>
|
||||||
|
typeArgumentTypes
|
||||||
|
Iterable<int>
|
||||||
|
rightParenthesis: )
|
||||||
|
body: Block
|
||||||
|
leftBracket: {
|
||||||
|
rightBracket: }
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
|
test_iterableContextType_patternVariable_untyped() async {
|
||||||
|
await assertErrorsInCode(r'''
|
||||||
|
void f() {
|
||||||
|
for (var (a) in g()) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
T g<T>() => throw 0;
|
||||||
|
''', [
|
||||||
|
error(HintCode.UNUSED_LOCAL_VARIABLE, 23, 1),
|
||||||
|
]);
|
||||||
|
var node = findNode.forStatement('for');
|
||||||
|
assertResolvedNodeText(node, r'''
|
||||||
|
ForStatement
|
||||||
|
forKeyword: for
|
||||||
|
leftParenthesis: (
|
||||||
|
forLoopParts: ForEachPartsWithPattern
|
||||||
|
keyword: var
|
||||||
|
pattern: ParenthesizedPattern
|
||||||
|
leftParenthesis: (
|
||||||
|
pattern: DeclaredVariablePattern
|
||||||
|
name: a
|
||||||
|
declaredElement: hasImplicitType a@23
|
||||||
|
type: Object?
|
||||||
|
rightParenthesis: )
|
||||||
|
inKeyword: in
|
||||||
|
iterable: MethodInvocation
|
||||||
|
methodName: SimpleIdentifier
|
||||||
|
token: g
|
||||||
|
staticElement: self::@function::g
|
||||||
|
staticType: T Function<T>()
|
||||||
|
argumentList: ArgumentList
|
||||||
|
leftParenthesis: (
|
||||||
|
rightParenthesis: )
|
||||||
|
staticInvokeType: Iterable<Object?> Function()
|
||||||
|
staticType: Iterable<Object?>
|
||||||
|
typeArgumentTypes
|
||||||
|
Iterable<Object?>
|
||||||
|
rightParenthesis: )
|
||||||
|
body: Block
|
||||||
|
leftBracket: {
|
||||||
|
rightBracket: }
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
test_keyword_final_patternVariable() async {
|
test_keyword_final_patternVariable() async {
|
||||||
await assertNoErrorsInCode(r'''
|
await assertNoErrorsInCode(r'''
|
||||||
void f(List<int> x) {
|
void f(List<int> x) {
|
||||||
|
|
|
@ -9425,6 +9425,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||||
throw new UnimplementedError('TODO(paulberry)');
|
throw new UnimplementedError('TODO(paulberry)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
DartType iterableType(DartType elementType) {
|
||||||
|
return new InterfaceType(coreTypes.iterableClass, Nullability.nonNullable,
|
||||||
|
<DartType>[elementType]);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DartType listType(DartType elementType) {
|
DartType listType(DartType elementType) {
|
||||||
return new InterfaceType(
|
return new InterfaceType(
|
||||||
|
|
Loading…
Reference in a new issue