Skip to content

Commit 653502e

Browse files
johnniwintherCommit Queue
authored andcommitted
[analyzer] Compute default types for local functions
Default types are used in the exhaustiveness computation but were not computed for local functions, leading to a crash during constant evaluation. This adds computation of default types to FunctionDeclaration and FunctionExpression. Closes #54388 Closes #60264 Change-Id: I95eb2ae5b42e4fb5ab77088c915fe6088a1d4060 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427702 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 651206e commit 653502e

File tree

12 files changed

+287
-63
lines changed

12 files changed

+287
-63
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
sealed class Option<T> {
6+
const Option();
7+
}
8+
9+
class Some<T> extends Option<T> {
10+
final T value;
11+
const Some(this.value);
12+
}
13+
14+
class None extends Option<Never> {
15+
// T can be any type
16+
const None();
17+
18+
/// Placeholder get, to use it in switch in this example
19+
bool get noneProp => false;
20+
}
21+
22+
/// Global const.
23+
const none = None();
24+
25+
/// Works as expected
26+
void fun<T>(Option<T> option) {
27+
/*
28+
checkingOrder={Option<T>,Some<T>,None},
29+
subtypes={Some<T>,None},
30+
type=Option<T>
31+
*/
32+
switch (option) {
33+
/*space=Some<T>*/
34+
case Some():
35+
final _ = option.value;
36+
/*space=None*/
37+
case None():
38+
final _ = option.noneProp;
39+
}
40+
}
41+
42+
/// Works as expected
43+
class MyClass {
44+
void fun<T>(Option<T> option) {
45+
/*
46+
checkingOrder={Option<T>,Some<T>,None},
47+
subtypes={Some<T>,None},
48+
type=Option<T>
49+
*/
50+
switch (option) {
51+
/*space=Some<T>*/
52+
case Some():
53+
final _ = option.value;
54+
/*space=None*/
55+
case None():
56+
final _ = option.noneProp;
57+
}
58+
}
59+
}
60+
61+
void globalFunWithNestedFun() {
62+
void fun<T>(Option<T> option) {
63+
/*
64+
checkingOrder={Option<T>,Some<T>,None},
65+
subtypes={Some<T>,None},
66+
type=Option<T>
67+
*/
68+
switch (option) {
69+
/*space=Some<T>*/
70+
case Some():
71+
final _ = option.value;
72+
/*space=None*/
73+
case None():
74+
final _ = option.noneProp;
75+
}
76+
}
77+
78+
var f = <T>(Option<T> option) {
79+
/*
80+
checkingOrder={Option<T>,Some<T>,None},
81+
subtypes={Some<T>,None},
82+
type=Option<T>
83+
*/
84+
switch (option) {
85+
/*space=Some<T>*/
86+
case Some():
87+
final _ = option.value;
88+
/*space=None*/
89+
case None():
90+
final _ = option.noneProp;
91+
}
92+
};
93+
}

pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analyzer/dart/element/element.dart';
56
import 'package:analyzer/dart/element/nullability_suffix.dart';
67
import 'package:analyzer/dart/element/type.dart';
78
import 'package:analyzer/src/dart/ast/ast.dart';
89
import 'package:analyzer/src/dart/element/element.dart';
910
import 'package:analyzer/src/dart/element/type.dart';
1011
import 'package:analyzer/src/dart/element/type_schema.dart';
1112
import 'package:analyzer/src/dart/element/type_system.dart';
13+
import 'package:analyzer/src/generated/error_verifier.dart';
1214
import 'package:analyzer/src/generated/resolver.dart';
15+
import 'package:analyzer/src/summary2/default_types_builder.dart';
1316

1417
class FunctionExpressionResolver {
1518
final ResolverVisitor _resolver;
@@ -66,6 +69,25 @@ class FunctionExpressionResolver {
6669
_resolver.flowAnalysis.flow?.functionExpression_end();
6770
_resolver.nullSafetyDeadCodeVerifier.flowEnd(node);
6871
}
72+
73+
var typeParameterList = node.typeParameters;
74+
if (typeParameterList != null) {
75+
// Computing the default types for type parameters will normalize the
76+
// recursive bounds, so we need to check for recursion first.
77+
//
78+
// This is only needed for local functions because top-level and
79+
checkForTypeParameterBoundRecursion(
80+
_resolver.errorReporter,
81+
typeParameterList.typeParameters,
82+
);
83+
var map = <Fragment, TypeParameter>{};
84+
for (var typeParameter in typeParameterList.typeParameters) {
85+
map[typeParameter.declaredFragment!] = typeParameter;
86+
}
87+
DefaultTypesBuilder(
88+
getTypeParameterNode: (fragment) => map[fragment],
89+
).build([node]);
90+
}
6991
}
7092

7193
/// Infer types of implicitly typed formal parameters.

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,49 @@ import 'package:analyzer/src/utilities/extensions/object.dart';
5757
import 'package:analyzer/src/utilities/extensions/string.dart';
5858
import 'package:collection/collection.dart';
5959

60+
/// Check that none of the type [parameters] references itself in its bound.
61+
///
62+
/// See [CompileTimeErrorCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND].
63+
void checkForTypeParameterBoundRecursion(
64+
ErrorReporter errorReporter,
65+
List<TypeParameter> parameters,
66+
) {
67+
Map<TypeParameterElement, TypeParameter>? elementToNode;
68+
for (var parameter in parameters) {
69+
if (parameter.bound != null) {
70+
if (elementToNode == null) {
71+
elementToNode = {};
72+
for (var parameter in parameters) {
73+
elementToNode[parameter.declaredFragment!.element] = parameter;
74+
}
75+
}
76+
77+
TypeParameter? current = parameter;
78+
for (var step = 0; current != null; step++) {
79+
var boundNode = current.bound;
80+
if (boundNode is NamedType) {
81+
var boundType = boundNode.typeOrThrow;
82+
boundType = boundType.extensionTypeErasure;
83+
current = elementToNode[boundType.element3];
84+
} else {
85+
current = null;
86+
}
87+
if (step == parameters.length) {
88+
var element = parameter.declaredFragment!.element;
89+
// This error can only occur if there is a bound, so we can safely
90+
// assume `element.bound` is non-`null`.
91+
errorReporter.atToken(
92+
parameter.name,
93+
CompileTimeErrorCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND,
94+
arguments: [element.displayName, element.bound!],
95+
);
96+
break;
97+
}
98+
}
99+
}
100+
}
101+
}
102+
60103
class EnclosingExecutableContext {
61104
final ExecutableElement2OrMember? element;
62105
final bool isAsynchronous;
@@ -5300,40 +5343,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
53005343
///
53015344
/// See [CompileTimeErrorCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND].
53025345
void _checkForTypeParameterBoundRecursion(List<TypeParameter> parameters) {
5303-
Map<TypeParameterElement, TypeParameter>? elementToNode;
5304-
for (var parameter in parameters) {
5305-
if (parameter.bound != null) {
5306-
if (elementToNode == null) {
5307-
elementToNode = {};
5308-
for (var parameter in parameters) {
5309-
elementToNode[parameter.declaredFragment!.element] = parameter;
5310-
}
5311-
}
5312-
5313-
TypeParameter? current = parameter;
5314-
for (var step = 0; current != null; step++) {
5315-
var boundNode = current.bound;
5316-
if (boundNode is NamedType) {
5317-
var boundType = boundNode.typeOrThrow;
5318-
boundType = boundType.extensionTypeErasure;
5319-
current = elementToNode[boundType.element3];
5320-
} else {
5321-
current = null;
5322-
}
5323-
if (step == parameters.length) {
5324-
var element = parameter.declaredFragment!.element;
5325-
// This error can only occur if there is a bound, so we can safely
5326-
// assume `element.bound` is non-`null`.
5327-
errorReporter.atToken(
5328-
parameter.name,
5329-
CompileTimeErrorCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND,
5330-
arguments: [element.displayName, element.bound!],
5331-
);
5332-
break;
5333-
}
5334-
}
5335-
}
5336-
}
5346+
checkForTypeParameterBoundRecursion(errorReporter, parameters);
53375347
}
53385348

53395349
void _checkForTypeParameterReferencedByStatic({

pkg/analyzer/lib/src/summary2/default_types_builder.dart

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,23 @@ import 'package:analyzer/src/dart/element/element.dart';
1111
import 'package:analyzer/src/dart/element/replacement_visitor.dart';
1212
import 'package:analyzer/src/dart/element/type.dart';
1313
import 'package:analyzer/src/summary2/function_type_builder.dart';
14-
import 'package:analyzer/src/summary2/link.dart';
1514
import 'package:analyzer/src/summary2/named_type_builder.dart';
1615
import 'package:analyzer/src/summary2/type_builder.dart';
1716
import 'package:analyzer/src/util/graph.dart';
1817

18+
/// Function that returns the [AstNode] corresponding to the type parameter
19+
/// [fragment].
20+
///
21+
/// If the [fragment] is not part of a library currently being linked or
22+
/// resolved, `null` is returned.
23+
typedef GetTypeParameterNodeFunction = AstNode? Function(Fragment fragment);
24+
1925
class DefaultTypesBuilder {
20-
final Linker _linker;
26+
final GetTypeParameterNodeFunction _getTypeParameterNode;
2127

22-
DefaultTypesBuilder(this._linker);
28+
DefaultTypesBuilder({
29+
required GetTypeParameterNodeFunction getTypeParameterNode,
30+
}) : _getTypeParameterNode = getTypeParameterNode;
2331

2432
void build(List<AstNode> nodes) {
2533
for (var node in nodes) {
@@ -73,6 +81,11 @@ class DefaultTypesBuilder {
7381
_breakSelfCycles(node.functionExpression.typeParameters);
7482
_breakRawTypeCycles(element, node.functionExpression.typeParameters);
7583
_computeBounds(element, node.functionExpression.typeParameters);
84+
} else if (node is FunctionExpressionImpl) {
85+
var element = node.declaredFragment!.element;
86+
_breakSelfCycles(node.typeParameters);
87+
_breakRawTypeCycles(element, node.typeParameters);
88+
_computeBounds(element, node.typeParameters);
7689
}
7790
}
7891
for (var node in nodes) {
@@ -94,6 +107,8 @@ class DefaultTypesBuilder {
94107
_build(node.typeParameters);
95108
} else if (node is FunctionDeclarationImpl) {
96109
_build(node.functionExpression.typeParameters);
110+
} else if (node is FunctionExpressionImpl) {
111+
_build(node.typeParameters);
97112
} else if (node is MethodDeclarationImpl) {
98113
_build(node.typeParameters);
99114
}
@@ -274,7 +289,7 @@ class DefaultTypesBuilder {
274289
} else if (visited.add(startType.element3)) {
275290
void recurseParameters(List<TypeParameterElement> parameters) {
276291
for (var parameter in parameters) {
277-
var parameterNode = _linker.getLinkingNode2(
292+
var parameterNode = _getTypeParameterNode(
278293
parameter.firstFragment,
279294
);
280295
if (parameterNode is TypeParameter) {

pkg/analyzer/lib/src/summary2/types_builder.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ class TypesBuilder {
8181

8282
/// Build types for all type annotations, and set types for declarations.
8383
void build(NodesToBuildType nodes) {
84-
DefaultTypesBuilder(_linker).build(nodes.declarations);
84+
DefaultTypesBuilder(
85+
getTypeParameterNode: _linker.getLinkingNode2,
86+
).build(nodes.declarations);
8587

8688
for (var builder in nodes.typeBuilders) {
8789
builder.build();

pkg/analyzer/test/src/dart/resolution/function_declaration_statement_test.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ FunctionDeclarationStatement
4242
TypeParameter
4343
name: T
4444
declaredElement: T@17
45-
defaultType: null
45+
defaultType: dynamic
4646
TypeParameter
4747
name: U
4848
declaredElement: U@20
49-
defaultType: null
49+
defaultType: dynamic
5050
rightBracket: >
5151
parameters: FormalParameterList
5252
leftParenthesis: (
@@ -113,11 +113,11 @@ FunctionDeclarationStatement
113113
element2: U@33
114114
type: U
115115
declaredElement: T@20
116-
defaultType: null
116+
defaultType: dynamic
117117
TypeParameter
118118
name: U
119119
declaredElement: U@33
120-
defaultType: null
120+
defaultType: dynamic
121121
TypeParameter
122122
name: V
123123
extendsKeyword: extends
@@ -126,7 +126,7 @@ FunctionDeclarationStatement
126126
element2: U@33
127127
type: U
128128
declaredElement: V@36
129-
defaultType: null
129+
defaultType: dynamic
130130
rightBracket: >
131131
parameters: FormalParameterList
132132
leftParenthesis: (
@@ -193,7 +193,7 @@ FunctionDeclarationStatement
193193
TypeParameter
194194
name: T
195195
declaredElement: T@20
196-
defaultType: null
196+
defaultType: dynamic
197197
rightBracket: >
198198
parameters: FormalParameterList
199199
leftParenthesis: (
@@ -250,7 +250,7 @@ FunctionDeclarationStatement
250250
TypeParameter
251251
name: T
252252
declaredElement: T@20
253-
defaultType: null
253+
defaultType: dynamic
254254
rightBracket: >
255255
parameters: FormalParameterList
256256
leftParenthesis: (
@@ -307,7 +307,7 @@ FunctionDeclarationStatement
307307
TypeParameter
308308
name: T
309309
declaredElement: T@20
310-
defaultType: null
310+
defaultType: dynamic
311311
rightBracket: >
312312
parameters: FormalParameterList
313313
leftParenthesis: (
@@ -365,7 +365,7 @@ FunctionDeclarationStatement
365365
TypeParameter
366366
name: T
367367
declaredElement: T@20
368-
defaultType: null
368+
defaultType: dynamic
369369
rightBracket: >
370370
parameters: FormalParameterList
371371
leftParenthesis: (

pkg/analyzer/test/src/dart/resolution/function_declaration_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ FunctionDeclaration
288288
element2: dart:core::@class::List
289289
type: List<T>
290290
declaredElement: T@20
291-
defaultType: null
291+
defaultType: List<dynamic>
292292
rightBracket: >
293293
parameters: FormalParameterList
294294
leftParenthesis: (
@@ -335,7 +335,7 @@ FunctionDeclaration
335335
element2: dart:core::@class::num
336336
type: num
337337
declaredElement: T@20
338-
defaultType: null
338+
defaultType: num
339339
rightBracket: >
340340
parameters: FormalParameterList
341341
leftParenthesis: (

0 commit comments

Comments
 (0)