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:
Konstantin Shcheglov 2023-02-08 17:15:39 +00:00 committed by Commit Queue
parent e8842d862c
commit 55e0784a62
7 changed files with 232 additions and 5 deletions

View file

@ -1180,7 +1180,9 @@ mixin TypeAnalyzer<
required void Function() dispatchBody,
}) {
// Stack: ()
Type expressionType = analyzeExpression(expression, unknownType);
Type patternTypeSchema = dispatchPatternSchema(pattern);
Type expressionTypeSchema = iterableType(patternTypeSchema);
Type expressionType = analyzeExpression(expression, expressionTypeSchema);
// Stack: (Expression)
Type? elementType = operations.matchIterableType(expressionType);
@ -1950,6 +1952,9 @@ mixin TypeAnalyzer<
/// Queries whether [pattern] is a variable 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].
Type listType(Type elementType);

View file

@ -1067,10 +1067,10 @@ class MiniAstOperations
@override
Type? matchIterableType(Type type) {
if (type is PrimaryType &&
type.name == 'Iterable' &&
type.args.length == 1) {
return type.args[0];
if (type is PrimaryType && type.args.length == 1) {
if (type.name == 'Iterable' || type.name == 'List') {
return type.args[0];
}
}
return null;
}
@ -3832,6 +3832,11 @@ class _MiniAstTypeAnalyzer
@override
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);
@override

View file

@ -1634,6 +1634,26 @@ main() {
});
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:', () {
test('Iterable', () {
var x = Var('x');

View file

@ -1211,6 +1211,11 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
@override
bool isVariablePattern(AstNode pattern) => pattern is DeclaredVariablePattern;
@override
DartType iterableType(DartType elementType) {
return typeProvider.iterableType(elementType);
}
@override
DartType listType(DartType elementType) {
return typeProvider.listType(elementType);

View file

@ -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 {
await assertNoErrorsInCode(r'''
void f(List<int> x) {

View file

@ -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 {
await assertNoErrorsInCode(r'''
void f(List<int> x) {

View file

@ -9425,6 +9425,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
throw new UnimplementedError('TODO(paulberry)');
}
@override
DartType iterableType(DartType elementType) {
return new InterfaceType(coreTypes.iterableClass, Nullability.nonNullable,
<DartType>[elementType]);
}
@override
DartType listType(DartType elementType) {
return new InterfaceType(