[cfe][Contexts][InternalNodes] Add internal nodes for FunctionNode, FunctionExpression and FunctionDeclaration

This adds internal nodes for FunctionNode, FunctionExpression and FunctionDeclaration which require InternalVariable rather than Variable. This is in preparation for using the new variable model by default.

Change-Id: I3c29954bcaae555c20b37e6a3ba1e70d959f1559
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506941
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther
2026-05-29 02:41:29 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 8c42f67d35
commit 02046380d1
15 changed files with 603 additions and 263 deletions
+4 -4
View File
@@ -298,23 +298,23 @@ B0 = EntryBlock()
DynamicCall call(v1, v4, v5)
ClosureCall(v7, v2, v9, v10)
v12 = AllocateClosure()
DirectCall closure FunctionDeclarationImpl(void func3(int x) => print(x);) at closureCalls(v12, v15)
DirectCall closure FunctionDeclaration(void func3(int x) => print(x);) at closureCalls(v12, v15)
v17 = AllocateClosure()
DirectCall closure FunctionDeclarationImpl(void func4<T extends Object?>(T% x) => print(x);) at closureCalls(v19, v17, v21)
DirectCall closure FunctionDeclaration(void func4<T extends Object?>(T% x) => print(x);) at closureCalls(v19, v17, v21)
v23 = AllocateClosure()
DirectCall closure FunctionExpression(Null () {
print("hey");
}) at closureCalls(v23)
Return(v25)
--- closure FunctionDeclarationImpl(void func3(int x) => print(x);) at closureCalls
--- closure FunctionDeclaration(void func3(int x) => print(x);) at closureCalls
B0 = EntryBlock()
Parameter(#closure)
v2 = Parameter(x)
v4 = DirectCall print(v2)
Return(v4)
--- closure FunctionDeclarationImpl(void func4<T extends Object?>(T% x) => print(x);) at closureCalls
--- closure FunctionDeclaration(void func4<T extends Object?>(T% x) => print(x);) at closureCalls
B0 = EntryBlock()
v1 = Parameter(#functionTypeParameters)
Parameter(#closure)
+29 -31
View File
@@ -7787,7 +7787,7 @@ class BodyBuilderImpl extends StackListenerImpl
identifierName = createWildcardVariableName(wildcardVariableIndex);
wildcardVariableIndex++;
}
Variable variable = new VariableDeclarationImpl(
InternalVariable variable = new VariableDeclarationImpl(
identifierName,
forSyntheticToken: nameToken.isSynthetic,
isFinal: true,
@@ -7796,11 +7796,10 @@ class BodyBuilderImpl extends StackListenerImpl
fileOffset: name.nameOffset,
);
push(
new FunctionDeclarationImpl(
variable,
// The real function node is created later.
dummyFunctionNode,
)..fileOffset = beginToken.charOffset,
intern.createFunctionDeclaration(
variable: variable,
fileOffset: beginToken.charOffset,
),
);
if (!(libraryFeatures.wildcardVariables.isEnabled && variable.isWildcard)) {
// The local scope stack contains a type parameter scope for the local
@@ -7815,7 +7814,7 @@ class BodyBuilderImpl extends StackListenerImpl
LocalScope scope = isFunctionExpression
? _localScope
: _localScopes.previous;
declareVariable(variable, scope);
declareVariable(variable.astVariable, scope);
}
}
@@ -7890,26 +7889,25 @@ class BodyBuilderImpl extends StackListenerImpl
if (!isFunctionExpression) {
annotations = pop() as List<Expression>?; // Metadata.
}
FunctionNode function = formals.buildFunctionNode(
libraryBuilder,
returnType,
typeParameters,
asyncModifier,
body,
token.charOffset,
InternalFunctionNode function = formals.buildFunctionNode(
libraryBuilder: libraryBuilder,
returnTypeBuilder: returnType,
typeParameterBuilders: typeParameters,
asyncModifier: asyncModifier,
body: body,
// TODO(johnniwinther): Shouldn't we provide the start offset here?
fileOffset: TreeNode.noOffset,
fileEndOffset: token.charOffset,
);
if (declaration is FunctionDeclaration) {
Variable variable = declaration.variable;
if (declaration is InternalFunctionDeclaration) {
InternalVariable variable = declaration.variable;
if (annotations != null) {
for (Expression annotation in annotations) {
variable.addAnnotation(annotation);
}
}
FunctionDeclarationImpl.setHasImplicitReturnType(
declaration as FunctionDeclarationImpl,
hasImplicitReturnType,
);
declaration.hasImplicitReturnType = hasImplicitReturnType;
if (!hasImplicitReturnType) {
problemReporting.checkAsyncReturnType(
libraryBuilder: libraryBuilder,
@@ -7921,10 +7919,9 @@ class BodyBuilderImpl extends StackListenerImpl
);
}
variable.type = function.computeFunctionType(Nullability.nonNullable);
variable.type = function.computeFunctionType();
declaration.function = function;
function.parent = declaration;
Statement statement;
if (variable.initializer != null) {
// This must have been a compile-time error.
@@ -8021,15 +8018,16 @@ class BodyBuilderImpl extends StackListenerImpl
exitFunction();
List<NominalParameterBuilder>? typeParameters =
pop() as List<NominalParameterBuilder>?;
FunctionNode function = formals.buildFunctionNode(
libraryBuilder,
null,
typeParameters,
asyncModifier,
body,
InternalFunctionNode function = formals.buildFunctionNode(
libraryBuilder: libraryBuilder,
returnTypeBuilder: null,
typeParameterBuilders: typeParameters,
asyncModifier: asyncModifier,
body: body,
fileOffset: beginToken.charOffset,
// TODO(jensj): Is this the offset we want?
endToken.next!.charOffset,
)..fileOffset = beginToken.charOffset;
fileEndOffset: endToken.next!.charOffset,
);
Expression result;
if (constantContext != ConstantContext.none) {
@@ -8041,7 +8039,7 @@ class BodyBuilderImpl extends StackListenerImpl
);
} else {
result = intern.createFunctionExpression(
function,
function: function,
fileOffset: offsetForToken(beginToken),
);
}
@@ -280,32 +280,34 @@ class FormalParameters extends Parameters {
}
}
FunctionNode buildFunctionNode(
SourceLibraryBuilder library,
TypeBuilder? returnTypeBuilder,
List<NominalParameterBuilder>? typeParameterBuilders,
AsyncModifier asyncModifier,
Statement body,
int fileEndOffset,
) {
DartType returnType =
returnTypeBuilder?.build(library, TypeUse.returnType) ??
const DynamicType();
InternalFunctionNode buildFunctionNode({
required SourceLibraryBuilder libraryBuilder,
required TypeBuilder? returnTypeBuilder,
required List<NominalParameterBuilder>? typeParameterBuilders,
required AsyncModifier asyncModifier,
required Statement body,
required int fileOffset,
required int fileEndOffset,
}) {
DartType? returnType = returnTypeBuilder?.build(
libraryBuilder,
TypeUse.returnType,
);
int requiredParameterCount = 0;
List<Variable> positionalParameters = <Variable>[];
List<Variable> namedParameters = <Variable>[];
List<InternalVariable> positionalParameters = [];
List<InternalVariable> namedParameters = [];
if (parameters != null) {
for (FormalParameterBuilder formal in parameters!) {
Variable parameter = formal.build(library);
Variable parameter = formal.build(libraryBuilder);
if (formal.isPositional) {
positionalParameters.add(parameter);
positionalParameters.add(parameter as InternalVariable);
if (formal.isRequiredPositional) requiredParameterCount++;
} else if (formal.isNamed) {
namedParameters.add(parameter);
namedParameters.add(parameter as InternalVariable);
}
}
namedParameters.sort((Variable a, Variable b) {
return a.name!.compareTo(b.name!);
namedParameters.sort((InternalVariable a, InternalVariable b) {
return a.cosmeticName!.compareTo(b.cosmeticName!);
});
}
@@ -315,11 +317,11 @@ class FormalParameters extends Parameters {
for (NominalParameterBuilder t in typeParameterBuilders) {
typeParameters.add(t.parameter);
// Build the bound to detect cycles in typedefs.
t.bound?.build(library, TypeUse.typeParameterBound);
t.bound?.build(libraryBuilder, TypeUse.typeParameterBound);
}
}
return extern.createFunctionNode(
body,
return intern.createFunctionNode(
body: body,
typeParameters: typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
@@ -283,6 +283,14 @@ FileUriExpression createFileUriExpression({
return new FileUriExpression(expression, fileUri)..fileOffset = fileOffset;
}
FunctionDeclaration createFunctionDeclaration({
required Variable variable,
required FunctionNode function,
required int fileOffset,
}) {
return new FunctionDeclaration(variable, function)..fileOffset = fileOffset;
}
FunctionExpression createFunctionExpression(
FunctionNode function, {
required int fileOffset,
@@ -301,6 +309,9 @@ FunctionNode createFunctionNode(
int? fileEndOffset,
AsyncMarker asyncMarker = AsyncMarker.Sync,
AsyncMarker? dartAsyncMarker,
DartType? emittedValueType,
Scope? scope,
List<VariableContext>? capturedContexts,
}) {
return new FunctionNode(
body,
@@ -312,6 +323,9 @@ FunctionNode createFunctionNode(
asyncMarker: asyncMarker,
dartAsyncMarker: dartAsyncMarker,
)
..emittedValueType = emittedValueType
..scope = scope
..capturedContexts = capturedContexts
..fileOffset = fileOffset
..fileEndOffset = fileEndOffset ?? fileOffset;
}
+183 -20
View File
@@ -791,26 +791,6 @@ class TypeAliasedFactoryInvocation extends InternalExpression {
}
}
/// Front end specific implementation of [FunctionDeclaration].
class FunctionDeclarationImpl extends FunctionDeclaration {
bool hasImplicitReturnType = false;
FunctionDeclarationImpl(Variable variable, FunctionNode function)
: super(variable, function);
static void setHasImplicitReturnType(
FunctionDeclarationImpl declaration,
bool hasImplicitReturnType,
) {
declaration.hasImplicitReturnType = hasImplicitReturnType;
}
@override
String toString() {
return "FunctionDeclarationImpl(${toStringInternal()})";
}
}
/// Internal expression representing an if-null expression.
///
/// An if-null expression of the form `a ?? b` is encoded as:
@@ -1255,6 +1235,7 @@ class InternalLateVariable extends TreeNode
int binaryOffsetNoTag = -1;
@override
// Coverage-ignore(suite): Not run.
List<VariableContext>? get capturedContexts =>
variableDeclaration?.capturedContexts;
@@ -1319,6 +1300,7 @@ class InternalPositionalParameter extends TreeNode
}
@override
// Coverage-ignore(suite): Not run.
R accept<R>(VariableVisitor<R> v) => v.visitPositionalParameter(astVariable);
@override
@@ -1425,6 +1407,7 @@ class InternalNamedParameter extends TreeNode
}
@override
// Coverage-ignore(suite): Not run.
R accept<R>(VariableVisitor<R> v) => v.visitNamedParameter(astVariable);
@override
@@ -1667,6 +1650,7 @@ mixin DelegatingVariableMixin on InternalVariableMixin
}
@override
// Coverage-ignore(suite): Not run.
bool get isCovariantByClass => astVariable.isCovariantByClass;
@override
@@ -1676,6 +1660,7 @@ mixin DelegatingVariableMixin on InternalVariableMixin
}
@override
// Coverage-ignore(suite): Not run.
bool get isCovariantByDeclaration => astVariable.isCovariantByDeclaration;
@override
@@ -1685,6 +1670,7 @@ mixin DelegatingVariableMixin on InternalVariableMixin
}
@override
// Coverage-ignore(suite): Not run.
bool get isErroneouslyInitialized => astVariable.isErroneouslyInitialized;
@override
@@ -1731,6 +1717,7 @@ mixin DelegatingVariableMixin on InternalVariableMixin
}
@override
// Coverage-ignore(suite): Not run.
bool get isLowered => astVariable.isLowered;
@override
@@ -1798,12 +1785,15 @@ mixin DelegatingVariableMixin on InternalVariableMixin
bool get isAssignable => astVariable.isAssignable;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsFinal => astVariable.hasIsFinal;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsConst => astVariable.hasIsConst;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsLate => astVariable.hasIsLate;
@override
@@ -1811,9 +1801,11 @@ mixin DelegatingVariableMixin on InternalVariableMixin
bool get hasIsInitializingFormal => astVariable.hasIsInitializingFormal;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsSynthesized => astVariable.hasIsSynthesized;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsHoisted => astVariable.hasIsHoisted;
@override
@@ -1821,19 +1813,24 @@ mixin DelegatingVariableMixin on InternalVariableMixin
bool get hasHasDeclaredInitializer => astVariable.hasHasDeclaredInitializer;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsCovariantByClass => astVariable.hasIsCovariantByClass;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsRequired => astVariable.hasIsRequired;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsCovariantByDeclaration =>
astVariable.hasIsCovariantByDeclaration;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsLowered => astVariable.hasIsLowered;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsWildcard => astVariable.hasIsWildcard;
@override
@@ -1842,6 +1839,7 @@ mixin DelegatingVariableMixin on InternalVariableMixin
astVariable.hasIsSuperInitializingFormal;
@override
// Coverage-ignore(suite): Not run.
bool get hasIsErroneouslyInitialized =>
astVariable.hasIsErroneouslyInitialized;
@@ -1853,6 +1851,7 @@ mixin DelegatingVariableMixin on InternalVariableMixin
astVariable.fileOffset = value;
}
// Coverage-ignore(suite): Not run.
int get flags => astVariable.flags;
// Coverage-ignore(suite): Not run.
@@ -6701,3 +6700,167 @@ class InternalVariableSet extends InternalExpression {
return "InternalVariableSet(${toStringInternal()})";
}
}
class InternalFunctionNode {
final DartType? returnType;
final List<TypeParameter> typeParameters;
final List<InternalVariable> positionalParameters;
final List<InternalVariable> namedParameters;
final int requiredParameterCount;
final AsyncMarker asyncMarker;
final Statement? body;
final int fileOffset;
final int fileEndOffset;
InternalFunctionNode({
required this.returnType,
required this.typeParameters,
required this.positionalParameters,
required this.namedParameters,
required this.requiredParameterCount,
required this.asyncMarker,
required this.body,
required this.fileOffset,
required this.fileEndOffset,
});
FunctionType computeFunctionType() {
return FunctionNode.computeFunctionTypeFromData(
returnType: returnType ?? const DynamicType(),
typeParameters: typeParameters,
// TODO(johnniwinther): Can we avoid creating a list of ast variables?
positionalParameters: [
for (InternalVariable parameter in positionalParameters)
parameter.astVariable,
],
namedParameters: [
for (InternalVariable parameter in namedParameters)
parameter.astVariable,
],
nullability: Nullability.nonNullable,
requiredParameterCount: requiredParameterCount,
);
}
// Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer, {String name = ''}) {
if (returnType != null) {
printer.writeType(returnType!);
printer.write(' ');
}
printer.write(name);
if (typeParameters.isNotEmpty) {
printer.write('<');
for (int index = 0; index < typeParameters.length; index++) {
if (index > 0) {
printer.write(', ');
}
printer.write(typeParameters[index].name ?? '');
printer.write(' extends ');
printer.writeType(typeParameters[index].bound);
}
printer.write('>');
}
printer.write('(');
for (int index = 0; index < positionalParameters.length; index++) {
if (index > 0) {
printer.write(', ');
}
if (index == requiredParameterCount) {
printer.write('[');
}
positionalParameters[index].toTextInternal(printer);
}
if (requiredParameterCount < positionalParameters.length) {
printer.write(']');
}
if (namedParameters.isNotEmpty) {
if (positionalParameters.isNotEmpty) {
printer.write(', ');
}
printer.write('{');
for (int index = 0; index < namedParameters.length; index++) {
if (index > 0) {
printer.write(', ');
}
namedParameters[index].toTextInternal(printer);
}
printer.write('}');
}
printer.write(')');
Statement? body = this.body;
if (body != null) {
if (body is ReturnStatement) {
printer.write(' => ');
printer.writeExpression(body.expression!);
} else {
printer.write(' ');
printer.writeStatement(body);
}
} else {
printer.write(';');
}
}
}
class InternalFunctionExpression extends InternalExpression {
final InternalFunctionNode function;
InternalFunctionExpression({
required this.function,
required int fileOffset,
}) {
this.fileOffset = fileOffset;
}
@override
ExpressionInferenceResult acceptInference(
InferenceVisitorImpl visitor,
DartType typeContext,
) {
return visitor.visitInternalFunctionExpression(this, typeContext);
}
@override
// Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
function.toTextInternal(printer);
}
@override
String toString() {
return "$runtimeType(${toStringInternal()}";
}
}
class InternalFunctionDeclaration extends InternalStatement {
final InternalVariable variable;
late final InternalFunctionNode function;
late final bool hasImplicitReturnType;
InternalFunctionDeclaration({
required this.variable,
required int fileOffset,
}) {
this.fileOffset = fileOffset;
}
@override
StatementInferenceResult acceptInference(InferenceVisitorImpl visitor) {
return visitor.visitInternalFunctionDeclaration(this);
}
@override
// Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
function.toTextInternal(printer, name: variable.cosmeticName ?? '');
if (function.body is ReturnStatement) {
printer.write(';');
}
}
@override
String toString() {
return "$runtimeType(${toStringInternal()}";
}
}
@@ -391,11 +391,48 @@ Statement createForStatement(
)..fileOffset = fileOffset;
}
FunctionExpression createFunctionExpression(
FunctionNode function, {
InternalFunctionNode createFunctionNode({
required Statement? body,
required List<TypeParameter>? typeParameters,
required List<InternalVariable> positionalParameters,
required List<InternalVariable> namedParameters,
required int? requiredParameterCount,
required DartType? returnType,
required int fileOffset,
required int? fileEndOffset,
required AsyncMarker asyncMarker,
}) {
return new InternalFunctionNode(
returnType: returnType,
typeParameters: typeParameters ?? [],
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount: requiredParameterCount ?? 0,
asyncMarker: asyncMarker,
body: body,
fileOffset: fileOffset,
fileEndOffset: fileEndOffset ?? TreeNode.noOffset,
);
}
Statement createFunctionDeclaration({
required InternalVariable variable,
required int fileOffset,
}) {
return new FunctionExpression(function)..fileOffset = fileOffset;
return new InternalFunctionDeclaration(
variable: variable,
fileOffset: fileOffset,
);
}
Expression createFunctionExpression({
required InternalFunctionNode function,
required int fileOffset,
}) {
return new InternalFunctionExpression(
function: function,
fileOffset: fileOffset,
);
}
Expression createIfCaseElement(
@@ -285,10 +285,14 @@ extension CheckHelper on ProblemReporting {
required SourceLibraryBuilder libraryBuilder,
required TypeEnvironment typeEnvironment,
required AsyncModifier asyncModifier,
required DartType returnType,
required DartType? returnType,
required TypeBuilder returnTypeBuilder,
required Uri fileUri,
}) {
if (returnType == null) {
return;
}
// For async, async*, and sync* functions with declared return types, we
// need to determine whether those types are valid.
// We use the same trick in each case below. For example to decide whether
@@ -155,14 +155,15 @@ abstract class ContextAllocationStrategy<Info extends ScopeProviderInfo> {
required CaptureKind captureKind,
});
void handleVariablesCapturedByNode(
ContextConsumer node,
List<VariableContext> computeVariablesCapturedByNode(
List<VariableBase> variables,
) {
Set<VariableContext> contexts = {
if (variables.isEmpty) {
return [];
}
return {
for (VariableBase variable in variables) variable.context!,
};
(node.capturedContexts ??= []).addAll(contexts);
}.toList();
}
ThisVariable get thisVariable {
@@ -546,7 +546,6 @@ class InferenceVisitorImpl extends InferenceVisitorBase
// therefore doesn't specify the type context for the subterms.
StatementInferenceResult bodyResult = inferStatement(node.body);
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = (bodyResult.statement as Block)..parent = node;
}
ExpressionInferenceResult valueResult = inferExpression(
@@ -3513,29 +3512,38 @@ class InferenceVisitorImpl extends InferenceVisitorBase
return const StatementInferenceResult();
}
FunctionType visitFunctionNode(
FunctionNode node,
DartType? typeContext,
DartType? returnContext,
int returnTypeInstrumentationOffset,
) {
LocalFunctionResult _visitInternalFunctionNode(
InternalFunctionNode node, {
required DartType? typeContext,
required DartType? returnType,
required int implicitReturnOffset,
}) {
return inferLocalFunction(
this,
node,
typeContext,
returnTypeInstrumentationOffset,
returnContext,
typeContext: typeContext,
implicitReturnOffset: implicitReturnOffset,
returnType: returnType,
);
}
@override
StatementInferenceResult visitFunctionDeclaration(
covariant FunctionDeclarationImpl node,
// Coverage-ignore(suite): Not run.
StatementInferenceResult visitFunctionDeclaration(FunctionDeclaration node) {
return _unhandledStatement(node);
}
StatementInferenceResult visitInternalFunctionDeclaration(
InternalFunctionDeclaration node,
) {
InternalFunctionNode function = node.function;
ScopeProviderInfo? scopeProviderInfo;
List<VariableContext>? capturedContexts;
if (isClosureContextLoweringEnabled) {
_contextAllocationStrategy.handleVariablesCapturedByNode(
node.function,
_capturedVariablesForNode(node),
capturedContexts = _contextAllocationStrategy
.computeVariablesCapturedByNode(_capturedVariablesForNode(node));
scopeProviderInfo = _contextAllocationStrategy.enterScopeProvider(
scopeProviderInfoKind: ScopeProviderInfoKind.Loop,
);
_handleDeclarationsOfParameters([
...node.function.positionalParameters,
@@ -3545,18 +3553,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
bool oldInTryOrLocalFunction = _inTryOrLocalFunction;
_inTryOrLocalFunction = true;
Variable variable = node.variable;
InternalVariable variable = node.variable;
flowAnalysis.functionExpression_begin(node);
_returnContexts.push(const StandardReturnContext());
inferMetadata(this, variable);
DartType? returnContext = node.hasImplicitReturnType
? null
: node.function.returnType;
FunctionType inferredType = visitFunctionNode(
node.function,
null,
returnContext,
node.fileOffset,
LocalFunctionResult localFunctionResult = _visitInternalFunctionNode(
function,
typeContext: null,
returnType: function.returnType,
implicitReturnOffset: node.fileOffset,
);
FunctionType inferredType = localFunctionResult.computeInferredType(
function,
);
if (dataForTesting != null &&
// Coverage-ignore(suite): Not run.
@@ -3567,14 +3575,31 @@ class InferenceVisitorImpl extends InferenceVisitorBase
}
variable.type = inferredType;
flowAnalysis.declare(
variable,
variable.astVariable,
new SharedTypeView(variable.type),
initialized: true,
);
flowAnalysis.functionExpression_end();
_returnContexts.pop();
_inTryOrLocalFunction = oldInTryOrLocalFunction;
return const StatementInferenceResult();
Scope? scope;
if (scopeProviderInfo != null) {
_contextAllocationStrategy.exitScopeProvider(scopeProviderInfo);
scope = scopeProviderInfo.scope;
}
FunctionDeclaration replacement = extern.createFunctionDeclaration(
variable: variable.astVariable,
function: localFunctionResult.computeFunctionNode(
function: function,
scope: scope,
capturedContexts: capturedContexts,
),
fileOffset: node.fileOffset,
);
libraryBuilder.loader.dataForTesting
// Coverage-ignore(suite): Not run.
?.registerAlias(node, replacement);
return new StatementInferenceResult.single(replacement);
}
@override
@@ -3594,7 +3619,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase
captureKind: _captureKindForVariable(internalThisVariable),
);
}
_handleDeclarationsOfParameters(parameters);
_handleDeclarationsOfParameters([
for (Variable parameter in parameters) parameter as InternalVariable,
]);
return scopeProviderInfo;
}
@@ -3603,12 +3630,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase
_contextAllocationStrategy.exitScopeProvider(scopeProviderInfo);
}
void _handleDeclarationsOfParameters(List<Variable> parameters) {
for (Variable parameter in parameters) {
// TODO(62401): Remove the cast when the flow analysis uses
// [InternalExpressionVariable]s.
Variable parameterAstVariable =
(parameter as InternalVariable).astVariable;
void _handleDeclarationsOfParameters(List<InternalVariable> parameters) {
for (InternalVariable parameter in parameters) {
Variable parameterAstVariable = parameter.astVariable;
_contextAllocationStrategy.handleDeclarationOfVariable(
parameterAstVariable,
captureKind: _captureKindForVariable(parameterAstVariable),
@@ -3616,23 +3640,22 @@ class InferenceVisitorImpl extends InferenceVisitorBase
}
}
@override
ExpressionInferenceResult visitFunctionExpression(
FunctionExpression node,
ExpressionInferenceResult visitInternalFunctionExpression(
InternalFunctionExpression node,
DartType typeContext,
) {
InternalFunctionNode function = node.function;
ScopeProviderInfo? scopeProviderInfo;
List<VariableContext>? capturedContexts;
if (isClosureContextLoweringEnabled) {
_contextAllocationStrategy.handleVariablesCapturedByNode(
node.function,
_capturedVariablesForNode(node),
);
capturedContexts = _contextAllocationStrategy
.computeVariablesCapturedByNode(_capturedVariablesForNode(node));
scopeProviderInfo = _contextAllocationStrategy.enterScopeProvider(
scopeProviderInfoKind: ScopeProviderInfoKind.Loop,
);
_handleDeclarationsOfParameters([
...node.function.positionalParameters,
...node.function.namedParameters,
...function.positionalParameters,
...function.namedParameters,
]);
}
@@ -3640,11 +3663,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
_inTryOrLocalFunction = true;
flowAnalysis.functionExpression_begin(node);
_returnContexts.push(const StandardReturnContext());
FunctionType inferredType = visitFunctionNode(
node.function,
typeContext,
null,
node.fileOffset,
LocalFunctionResult localFunctionResult = _visitInternalFunctionNode(
function,
typeContext: typeContext,
returnType: function.returnType,
implicitReturnOffset: node.fileOffset,
);
FunctionType inferredType = localFunctionResult.computeInferredType(
function,
);
if (dataForTesting != null) {
// Coverage-ignore-block(suite): Not run.
@@ -3654,11 +3680,33 @@ class InferenceVisitorImpl extends InferenceVisitorBase
flowAnalysis.functionExpression_end();
_returnContexts.pop();
_inTryOrLocalFunction = oldInTryOrLocalFunction;
Scope? scope;
if (scopeProviderInfo != null) {
_contextAllocationStrategy.exitScopeProvider(scopeProviderInfo);
node.function.scope = scopeProviderInfo.scope;
scope = scopeProviderInfo.scope;
}
return new ExpressionInferenceResult(inferredType, node);
Expression replacement = extern.createFunctionExpression(
localFunctionResult.computeFunctionNode(
function: function,
scope: scope,
capturedContexts: capturedContexts,
),
fileOffset: node.fileOffset,
);
libraryBuilder.loader.dataForTesting
// Coverage-ignore(suite): Not run.
?.registerAlias(node, replacement);
return new ExpressionInferenceResult(inferredType, replacement);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitFunctionExpression(
FunctionExpression node,
DartType typeContext,
) {
return _unhandledExpression(node, typeContext);
}
ExpressionInferenceResult visitIfNullExpression(
@@ -3783,7 +3831,6 @@ class InferenceVisitorImpl extends InferenceVisitorBase
node.otherwise!,
);
if (otherwiseResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.otherwise = otherwiseResult.statement..parent = node;
}
}
@@ -13694,7 +13741,6 @@ class InferenceVisitorImpl extends InferenceVisitorBase
);
StatementInferenceResult bodyResult = inferStatement(node.body);
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = bodyResult.statement..parent = node;
}
flowAnalysis.whileStatement_end();
@@ -16961,10 +17007,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
// [InternalExpressionVariable]s.
Variable variable = (node.variable as InternalVariable).astVariable;
if (isClosureContextLoweringEnabled) {
_contextAllocationStrategy.handleVariablesCapturedByNode(
node,
_capturedVariablesForNode(variable),
);
node.capturedContexts = _contextAllocationStrategy
.computeVariablesCapturedByNode(
_capturedVariablesForNode(variable),
);
}
flowAnalysis.lateInitializer_begin(variable);
}
@@ -32,6 +32,7 @@ import '../builder/member_builder.dart';
import '../kernel/assigned_variables_impl.dart';
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/external_ast_helper.dart';
import '../kernel/external_ast_helper.dart' as extern;
import '../kernel/hierarchy/class_member.dart';
import '../kernel/internal_ast.dart';
import '../kernel/kernel_helper.dart';
@@ -59,26 +60,27 @@ import 'type_schema_environment.dart'
AllTypeParameterEliminator,
TypeSchemaEnvironment;
/// Given a [FunctionExpression], computes a set whose elements consist of (a)
/// an integer corresponding to the zero-based index of each positional
/// Given an [InternalFunctionExpression], computes a set whose elements consist
/// of (a) an integer corresponding to the zero-based index of each positional
/// parameter of the function expression that has an explicit type annotation,
/// and (b) a string corresponding to the name of each named parameter of the
/// function expression that has an explicit type annotation.
Set<Object> _computeExplicitlyTypedParameterSet(
FunctionExpression functionExpression,
InternalFunctionExpression functionExpression,
) {
Set<Object> result = {};
int unnamedParameterIndex = 0;
for (Variable positionalParameter
for (InternalVariable positionalParameter
in functionExpression.function.positionalParameters) {
int key = unnamedParameterIndex++;
if (!(positionalParameter as InternalVariable).isImplicitlyTyped) {
if (!positionalParameter.isImplicitlyTyped) {
result.add(key);
}
}
for (Variable namedParameter in functionExpression.function.namedParameters) {
String key = namedParameter.name!;
if (!(namedParameter as InternalVariable).isImplicitlyTyped) {
for (InternalVariable namedParameter
in functionExpression.function.namedParameters) {
String key = namedParameter.cosmeticName!;
if (!namedParameter.isImplicitlyTyped) {
result.add(key);
}
}
@@ -1919,7 +1921,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
unparenthesizedExpression = unparenthesizedExpression.expression;
}
if (isInferenceUpdate1Enabled &&
unparenthesizedExpression is FunctionExpression) {
unparenthesizedExpression is InternalFunctionExpression) {
_DeferredArgumentInfo argumentInfo = new _DeferredArgumentInfo(
argument: argument,
formalType: formalType,
@@ -2232,18 +2234,14 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
);
}
FunctionType inferLocalFunction(
LocalFunctionResult inferLocalFunction(
InferenceVisitor visitor,
FunctionNode function,
DartType? typeContext,
int fileOffset,
DartType? returnContext,
) {
bool hasImplicitReturnType = false;
if (returnContext == null) {
hasImplicitReturnType = true;
returnContext = const UnknownType();
}
InternalFunctionNode function, {
required DartType? typeContext,
required DartType? returnType,
required int implicitReturnOffset,
}) {
DartType? returnContext = returnType ?? const UnknownType();
// Let `<T0, ..., Tn>` be the set of type parameters of the closure (with
// `n`=0 if there are no type parameters).
@@ -2252,7 +2250,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
// Let `(P0 x0, ..., Pm xm)` be the set of formal parameters of the closure
// (including required, positional optional, and named optional parameters).
// If any type `Pi` is missing, denote it as `_`.
List<Variable> formals = [
List<InternalVariable> formals = [
...function.positionalParameters,
...function.namedParameters,
];
@@ -2282,7 +2280,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
} else {
formalTypesFromContext[i] = getNamedParameterType(
typeContext,
formals[i].name!,
formals[i].cosmeticName!,
);
}
}
@@ -2311,7 +2309,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
// Otherwise, if `Qi` is not `_`, let `Ri` be the greatest closure of
// `Qi[T/S]` with respect to `?`. Otherwise, let `Ri` be `dynamic`.
for (int i = 0; i < formals.length; i++) {
InternalVariable formal = formals[i] as InternalVariable;
InternalVariable formal = formals[i];
if (formal.isImplicitlyTyped) {
DartType inferredType;
if (formalTypesFromContext[i] != null) {
@@ -2360,16 +2358,10 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
}
}
List<Variable> positionalParameters = function.positionalParameters;
for (int i = 0; i < positionalParameters.length; i++) {
Variable parameter = positionalParameters[i];
// TODO(62401): Remove the cast when the flow analysis uses
// [InternalExpressionVariable]s.
Variable parameterAstVariable =
(parameter as InternalVariable).astVariable;
for (InternalVariable parameter in function.positionalParameters) {
flowAnalysis.declare(
parameterAstVariable,
new SharedTypeView(parameterAstVariable.type),
parameter.astVariable,
new SharedTypeView(parameter.type),
initialized: true,
);
inferMetadata(visitor, parameter);
@@ -2382,14 +2374,10 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
..parent = parameter;
}
}
for (Variable parameter in function.namedParameters) {
// TODO(62401): Remove the cast when the flow analysis uses
// [InternalExpressionVariable]s.
Variable parameterAstVariable =
(parameter as InternalVariable).astVariable;
for (InternalVariable parameter in function.namedParameters) {
flowAnalysis.declare(
parameterAstVariable,
new SharedTypeView(parameterAstVariable.type),
parameter.astVariable,
new SharedTypeView(parameter.type),
initialized: true,
);
inferMetadata(visitor, parameter);
@@ -2403,8 +2391,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
}
}
for (Variable parameter in function.namedParameters) {
InternalVariable formal = parameter as InternalVariable;
for (InternalVariable formal in function.namedParameters) {
// Required named parameters shouldn't have initializers.
if (formal.isRequired && formal.hasDeclaredInitializer) {
libraryBuilder.addProblem(
@@ -2427,12 +2414,11 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
// Apply type inference to `B` in return context `N`, with any references
// to `xi` in `B` having type `Pi`. This produces `B`.
bool needToSetReturnType = hasImplicitReturnType;
BodyInferenceContext bodyContext = new BodyInferenceContext(
this,
function.asyncMarker,
returnContext,
needToInferReturnType: needToSetReturnType,
needToInferReturnType: returnType == null,
isRoot: false,
);
StatementInferenceResult bodyResult = visitor.inferStatement(
@@ -2445,7 +2431,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
// `void` if `B` contains no `yield` expressions. Otherwise, let `M` be
// the least upper bound of the types of the `return` expressions in `B`,
// or `void` if `B` contains no `return` expressions.
if (needToSetReturnType) {
if (returnType == null) {
DartType inferredReturnType = bodyContext.inferReturnType(
this,
hasImplicitReturn: flowAnalysis.isReachable,
@@ -2454,20 +2440,29 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
// Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B`
// with type `<T0, ..., Tn>(R0, ..., Rn) -> M` (with some of the `Ri` and
// `xi` denoted as optional or named parameters, if appropriate).
function.returnType = inferredReturnType;
returnType = inferredReturnType;
}
bodyResult = bodyContext.handleImplicitReturn(
this,
function.body!,
bodyResult,
fileOffset,
implicitReturnOffset,
);
List<Variable> positionalParameters = [
for (InternalVariable parameter in function.positionalParameters)
parameter.astVariable,
];
List<Variable> namedParameters = [
for (InternalVariable parameter in function.namedParameters)
parameter.astVariable,
];
return new LocalFunctionResult(
returnType: returnType,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
body: bodyResult.hasChanged ? bodyResult.statement : function.body,
emittedValueType: bodyContext.emittedValueType,
);
function.emittedValueType = bodyContext.emittedValueType;
if (bodyResult.hasChanged) {
function.body = bodyResult.statement..parent = function;
}
return function.computeFunctionType(Nullability.nonNullable);
}
/// Infers the [annotations].
@@ -6009,7 +6004,7 @@ class ImplicitInstantiation {
/// feature is enabled.
class _DeferredArgumentInfo extends _ArgumentInfo {
/// The unparenthesized argument expression.
final FunctionExpression unparenthesizedExpression;
final InternalFunctionExpression unparenthesizedExpression;
_DeferredArgumentInfo({
required super.argument,
@@ -6414,3 +6409,51 @@ class PatternForInData {
required this.computePatternVariableDeclaration,
});
}
class LocalFunctionResult {
final DartType returnType;
final List<Variable> positionalParameters;
final List<Variable> namedParameters;
final Statement? body;
final DartType? emittedValueType;
LocalFunctionResult({
required this.returnType,
required this.positionalParameters,
required this.namedParameters,
required this.body,
required this.emittedValueType,
});
FunctionType computeInferredType(InternalFunctionNode function) {
return FunctionNode.computeFunctionTypeFromData(
returnType: returnType,
typeParameters: function.typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
nullability: Nullability.nonNullable,
requiredParameterCount: function.requiredParameterCount,
);
}
FunctionNode computeFunctionNode({
required InternalFunctionNode function,
required Scope? scope,
required List<VariableContext>? capturedContexts,
}) {
return extern.createFunctionNode(
body,
returnType: returnType,
typeParameters: function.typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount: function.requiredParameterCount,
fileOffset: function.fileOffset,
fileEndOffset: function.fileEndOffset,
asyncMarker: function.asyncMarker,
emittedValueType: emittedValueType,
scope: scope,
capturedContexts: capturedContexts,
);
}
}
@@ -7,6 +7,7 @@ import 'dart:io' show Directory, Platform;
import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart'
show DataInterpreter, runTests;
import 'package:front_end/src/source/source_loader.dart';
import 'package:front_end/src/source/source_member_builder.dart';
import 'package:front_end/src/testing/id_testing_helper.dart';
import 'package:front_end/src/testing/id_testing_utils.dart';
@@ -65,18 +66,22 @@ class InferredVariableTypesDataComputer extends CfeDataComputer<DartType> {
}
class InferredTypeArgumentDataExtractor extends CfeDataExtractor<DartType> {
final SourceLoaderDataForTesting _sourceLoaderDataForTesting;
final TypeInferenceResultForTesting typeInferenceResult;
InferredTypeArgumentDataExtractor(
InternalCompilerResult compilerResult,
this.typeInferenceResult,
Map<Id, ActualData<DartType>> actualMap,
) : super(compilerResult, actualMap);
) : _sourceLoaderDataForTesting =
compilerResult.kernelTargetForTesting!.loader.dataForTesting!,
super(compilerResult, actualMap);
@override
DartType? computeNodeValue(Id id, TreeNode node) {
if (node is Variable || node is LocalFunction) {
return typeInferenceResult.inferredVariableTypes[node];
TreeNode? alias = _sourceLoaderDataForTesting.toOriginal(node);
return typeInferenceResult.inferredVariableTypes[alias];
}
return null;
}
@@ -1202,10 +1202,24 @@ const library test:dummy::Typedef<void>.foo(0, bar: 1)''',
void _testFunctionDeclarationImpl() {
testStatement(
new FunctionDeclarationImpl(
new VariableDeclarationImpl('foo', fileOffset: TreeNode.noOffset),
new FunctionNode(new Block([])),
),
new InternalFunctionDeclaration(
variable: new VariableDeclarationImpl(
'foo',
fileOffset: TreeNode.noOffset,
),
fileOffset: TreeNode.noOffset,
)
..function = new InternalFunctionNode(
returnType: const DynamicType(),
typeParameters: [],
positionalParameters: [],
namedParameters: [],
requiredParameterCount: 0,
asyncMarker: AsyncMarker.Sync,
body: new Block([]),
fileOffset: TreeNode.noOffset,
fileEndOffset: TreeNode.noOffset,
),
'''
dynamic foo() {}''',
);
+1 -1
View File
@@ -32,7 +32,7 @@ closure_context_lowering/late_field_initializers: Crash
closure_context_lowering/late_variable_initializers: ExpectationFileMismatchSerialized
closure_context_lowering/local_variables: ExpectationFileMismatchSerialized
closure_context_lowering/loop_depth_strategy: ExpectationFileMismatchSerialized
closure_context_lowering/parameters: Crash
closure_context_lowering/parameters: ExpectationFileMismatchSerialized
closure_context_lowering/redirecting_factories: ExpectationFileMismatchSerialized
closure_context_lowering/super_initializing_formal: ExpectationFileMismatchSerialized
closure_context_lowering/synthetic_variables: ExpectationFileMismatchSerialized
+1 -1
View File
@@ -274,7 +274,7 @@ closure_context_lowering/late_field_initializers: Crash
closure_context_lowering/late_variable_initializers: ExpectationFileMismatchSerialized
closure_context_lowering/local_variables: ExpectationFileMismatchSerialized
closure_context_lowering/loop_depth_strategy: ExpectationFileMismatchSerialized
closure_context_lowering/parameters: Crash
closure_context_lowering/parameters: ExpectationFileMismatchSerialized
closure_context_lowering/redirecting_factories: ExpectationFileMismatchSerialized
closure_context_lowering/super_initializing_formal: ExpectationFileMismatchSerialized
closure_context_lowering/synthetic_variables: ExpectationFileMismatchSerialized
+75 -62
View File
@@ -145,77 +145,21 @@ class FunctionNode extends TreeNode implements ScopeProvider, ContextConsumer {
/// is useful in some contexts, especially when reasoning about the function
/// type of the enclosing generic function and in combination with
/// [FunctionType.withoutTypeParameters].
FunctionType computeThisFunctionType(
Nullability nullability, {
bool reuseTypeParameters = false,
}) {
FunctionType computeThisFunctionType(Nullability nullability) {
TreeNode? parent = this.parent;
List<StructuralParameter> structuralParameters;
List<TypeParameter> typeParametersToCopy = parent is Constructor
? parent.enclosingClass.typeParameters
: typeParameters;
DartType returnType;
List<DartType> positionalParameters;
List<NamedType> namedParameters;
if (typeParametersToCopy.isEmpty || reuseTypeParameters) {
structuralParameters = const <StructuralParameter>[];
returnType = this.returnType;
List<Variable> thisPositionals = this.positionalParameters;
positionalParameters = List.generate(
thisPositionals.length,
(index) => _getTypeOfVariable(thisPositionals[index]),
growable: false,
);
List<Variable> thisNamed = this.namedParameters;
if (thisNamed.isEmpty) {
namedParameters = const <NamedType>[];
} else {
namedParameters = List.generate(
thisNamed.length,
(index) => _getNamedTypeOfVariable(thisNamed[index]),
growable: false,
);
namedParameters.sort();
}
} else {
// We need create a copy of the list of type parameters, otherwise
// transformations like erasure don't work.
FreshStructuralParametersFromTypeParameters freshStructuralParameters =
getFreshStructuralParametersFromTypeParameters(typeParametersToCopy);
structuralParameters = freshStructuralParameters.freshTypeParameters;
Substitution substitution = freshStructuralParameters.substitution;
returnType = substitution.substituteType(this.returnType);
List<Variable> thisPositionals = this.positionalParameters;
positionalParameters = List.generate(
thisPositionals.length,
(index) => substitution.substituteType(
_getTypeOfVariable(thisPositionals[index]),
),
growable: false,
);
List<Variable> thisNamed = this.namedParameters;
if (thisNamed.isEmpty) {
namedParameters = const <NamedType>[];
} else {
namedParameters = List.generate(
thisNamed.length,
(index) => _getNamedTypeOfVariable(thisNamed[index], substitution),
growable: false,
);
namedParameters.sort();
}
}
// TODO(johnniwinther,cstefantsova): Cache the function type here and use
// [DartType.withDeclaredNullability] to handle the variants.
return new FunctionType(
positionalParameters,
returnType,
nullability,
return computeFunctionTypeFromData(
returnType: returnType,
typeParameters: typeParametersToCopy,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
typeParameters: structuralParameters,
nullability: nullability,
requiredParameterCount: requiredParameterCount,
);
}
@@ -233,6 +177,75 @@ class FunctionNode extends TreeNode implements ScopeProvider, ContextConsumer {
return computeThisFunctionType(nullability);
}
static FunctionType computeFunctionTypeFromData({
required DartType returnType,
required List<TypeParameter> typeParameters,
required List<Variable> positionalParameters,
required List<Variable> namedParameters,
required Nullability nullability,
required int requiredParameterCount,
}) {
List<StructuralParameter> structuralParameters;
DartType functionReturnType;
List<DartType> positionalParameterTypes;
List<NamedType> namedParameterTypes;
if (typeParameters.isEmpty) {
structuralParameters = const <StructuralParameter>[];
functionReturnType = returnType;
positionalParameterTypes = List.generate(
positionalParameters.length,
(index) => _getTypeOfVariable(positionalParameters[index]),
growable: false,
);
if (namedParameters.isEmpty) {
namedParameterTypes = const <NamedType>[];
} else {
namedParameterTypes = List.generate(
namedParameters.length,
(index) => _getNamedTypeOfVariable(namedParameters[index]),
growable: false,
);
namedParameterTypes.sort();
}
} else {
// We need create a copy of the list of type parameters, otherwise
// transformations like erasure don't work.
FreshStructuralParametersFromTypeParameters freshStructuralParameters =
getFreshStructuralParametersFromTypeParameters(typeParameters);
structuralParameters = freshStructuralParameters.freshTypeParameters;
Substitution substitution = freshStructuralParameters.substitution;
functionReturnType = substitution.substituteType(returnType);
positionalParameterTypes = List.generate(
positionalParameters.length,
(index) => substitution.substituteType(
_getTypeOfVariable(positionalParameters[index]),
),
growable: false,
);
if (namedParameters.isEmpty) {
namedParameterTypes = const <NamedType>[];
} else {
namedParameterTypes = List.generate(
namedParameters.length,
(index) =>
_getNamedTypeOfVariable(namedParameters[index], substitution),
growable: false,
);
namedParameterTypes.sort();
}
}
return new FunctionType(
positionalParameterTypes,
functionReturnType,
nullability,
namedParameters: namedParameterTypes,
typeParameters: structuralParameters,
requiredParameterCount: requiredParameterCount,
);
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitFunctionNode(this);