[cfe][InternalNodes] Add InternalCatch

This is a step towards separating Variable from InternalVariable.

Change-Id: I5941148e55ae7105a5798df557db512fce3d8255
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/509941
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther
2026-06-08 05:45:44 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 8492bfd7ac
commit 19db32f041
6 changed files with 182 additions and 55 deletions
@@ -5935,18 +5935,18 @@ class BodyBuilderImpl extends StackListenerImpl
assignedVariables.deferNode(),
);
}
List<Catch>? catchBlocks;
List<InternalCatch>? catchBlocks;
List<Statement>? compileTimeErrors;
if (catchCount != 0) {
List<Object?> catchBlocksAndErrors = const FixedNullableList<Object?>()
.pop(stack, catchCount * 2)!;
catchBlocks = new List<Catch>.filled(
catchBlocks = new List<InternalCatch>.filled(
catchCount,
dummyCatch,
dummyInternalCatch,
growable: true,
);
for (int i = 0; i < catchCount; i++) {
catchBlocks[i] = catchBlocksAndErrors[i * 2] as Catch;
catchBlocks[i] = catchBlocksAndErrors[i * 2] as InternalCatch;
Statement? error = catchBlocksAndErrors[i * 2 + 1] as Statement?;
if (error != null) {
compileTimeErrors ??= <Statement>[];
@@ -1341,3 +1341,16 @@ WildcardPattern createWildcardPattern({
}) {
return new WildcardPattern(type)..fileOffset = fileOffset;
}
Catch createCatch({
required DartType guard,
required Variable? exception,
required Variable? stackTrace,
required Statement body,
required Scope? scope,
required int fileOffset,
}) {
return new Catch(exception, body, guard: guard, stackTrace: stackTrace)
..scope = scope
..fileOffset = fileOffset;
}
+105 -4
View File
@@ -108,7 +108,7 @@ abstract class InternalStatement extends AuxiliaryStatement {
class TryStatement extends InternalStatement {
Statement tryBlock;
List<Catch> catchBlocks;
List<InternalCatch> catchBlocks;
Statement? finallyBlock;
new(this.tryBlock, this.catchBlocks, this.finallyBlock) {
@@ -132,9 +132,9 @@ class TryStatement extends InternalStatement {
void toTextInternal(AstPrinter printer) {
printer.write('try ');
printer.writeStatement(tryBlock);
for (Catch catchBlock in catchBlocks) {
for (InternalCatch catchBlock in catchBlocks) {
printer.write(' ');
printer.writeCatch(catchBlock);
catchBlock.toTextInternal(printer);
}
if (finallyBlock != null) {
printer.write(' finally ');
@@ -6981,7 +6981,6 @@ class InternalFunctionDeclaration extends InternalStatement {
}
}
// Coverage-ignore(suite): Not run.
sealed class InternalPattern extends TreeNode with InternalTreeNode {
/// Returns the variable name that this pattern defines, if any.
///
@@ -7000,27 +6999,33 @@ sealed class InternalPattern extends TreeNode with InternalTreeNode {
List<InternalVariable> get declaredVariables;
@override
// Coverage-ignore(suite): Not run.
R accept<R>(TreeVisitor<R> v) =>
unsupported("${runtimeType}.accept", -1, null);
@override
// Coverage-ignore(suite): Not run.
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
unsupported("${runtimeType}.accept", -1, null);
@override
// Coverage-ignore(suite): Not run.
void replaceChild(TreeNode child, TreeNode replacement) {
// Do nothing. The node should not be part of the resulting AST, anyway.
}
@override
// Coverage-ignore(suite): Not run.
void visitChildren(Visitor<dynamic> v) =>
unsupported("${runtimeType}.visitChildren", -1, null);
@override
// Coverage-ignore(suite): Not run.
void transformChildren(Transformer v) =>
unsupported("${runtimeType}.transformChildren", -1, null);
@override
// Coverage-ignore(suite): Not run.
void transformOrRemoveChildren(RemovingTransformer v) {
unsupported("${runtimeType}.transformOrRemoveChildren", -1, null);
}
@@ -8233,6 +8238,89 @@ class InternalContinueSwitchStatement extends InternalStatement {
}
}
class InternalCatch extends TreeNode with InternalTreeNode {
final DartType guard; // Not null, defaults to dynamic.
final InternalVariable? exception;
final InternalVariable? stackTrace;
final Statement body;
new({
required this.exception,
required this.body,
this.guard = const DynamicType(),
this.stackTrace,
required int fileOffset,
}) {
exception?.parent = this;
stackTrace?.parent = this;
body.parent = this;
this.fileOffset = fileOffset;
}
@override
// Coverage-ignore(suite): Not run.
R accept<R>(TreeVisitor<R> v) {
unsupported("${runtimeType}.accept on ${v.runtimeType}", -1, null);
}
@override
// Coverage-ignore(suite): Not run.
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
unsupported("${runtimeType}.accept1 on ${v.runtimeType}", -1, null);
}
@override
// Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
bool isImplicitType(DartType type) {
if (type is DynamicType) {
return true;
}
if (type is InterfaceType &&
type.classReference.node != null &&
type.classNode.name == 'Object') {
Uri uri = type.classNode.enclosingLibrary.importUri;
return uri.isScheme('dart') &&
uri.path == 'core' &&
type.nullability == Nullability.nonNullable;
}
return false;
}
if (exception != null) {
if (!isImplicitType(guard)) {
printer.write('on ');
printer.writeType(guard);
printer.write(' ');
}
printer.write('catch (');
printer.writeVariableInitialization(
exception!.astVariable,
includeModifiersAndType: false,
includeInitializer: false,
);
if (stackTrace != null) {
printer.write(', ');
printer.writeVariableInitialization(
stackTrace!.astVariable,
includeModifiersAndType: false,
);
}
printer.write(') ');
} else {
printer.write('on ');
printer.writeType(guard);
printer.write(' ');
}
printer.writeStatement(body);
}
@override
String toString() {
return "$runtimeType(${toStringInternal()})";
}
}
final InternalPattern dummyInternalPattern = new InternalConstantPattern(
expression: dummyExpression,
fileOffset: TreeNode.noOffset,
@@ -8261,3 +8349,16 @@ final InternalSwitchCase dummyInternalSwitchCase =
labels: null,
fileOffset: TreeNode.noOffset,
);
final InternalCatch dummyInternalCatch = new InternalCatch(
exception: dummyInternalVariable,
body: dummyStatement,
stackTrace: dummyInternalVariable,
fileOffset: TreeNode.noOffset,
);
final InternalVariable dummyInternalVariable = new VariableDeclarationImpl(
null,
fileOffset: TreeNode.noOffset,
isSynthesized: true,
);
@@ -172,7 +172,7 @@ InternalPattern createCastPattern(
}
/// Return a representation of a catch clause.
Catch createCatch(
InternalCatch createCatch(
int fileOffset,
DartType exceptionType,
InternalVariable? exceptionParameter,
@@ -180,12 +180,13 @@ Catch createCatch(
DartType stackTraceType,
Statement body,
) {
return new Catch(
exceptionParameter?.asVariableDeclaration,
body,
return new InternalCatch(
exception: exceptionParameter,
body: body,
guard: exceptionType,
stackTrace: stackTraceParameter?.asVariableDeclaration,
)..fileOffset = fileOffset;
stackTrace: stackTraceParameter,
fileOffset: fileOffset,
);
}
InternalVariable createCatchVariable({
@@ -1583,10 +1584,10 @@ Expression createThrow(int fileOffset, Expression expression) {
Statement createTryStatement(
int fileOffset,
Statement tryBlock,
List<Catch>? catchBlocks,
List<InternalCatch>? catchBlocks,
Statement? finallyBlock,
) {
return new TryStatement(tryBlock, catchBlocks ?? <Catch>[], finallyBlock)
return new TryStatement(tryBlock, catchBlocks ?? [], finallyBlock)
..fileOffset = fileOffset;
}
@@ -13521,46 +13521,50 @@ class InferenceVisitorImpl extends InferenceVisitorBase
return new ExpressionInferenceResult(const NeverType.nonNullable(), node);
}
void visitCatch(Catch node) {
Catch visitCatch(InternalCatch node) {
ScopeProviderInfo? scopeProviderInfo;
Variable? exception = node.exception?.astVariable;
Variable? stackTrace = node.stackTrace?.astVariable;
if (isClosureContextLoweringEnabled) {
scopeProviderInfo = _contextAllocationStrategy.enterScopeProvider(
scopeProviderInfoKind: ScopeProviderInfoKind.Catch,
);
if (node.exception case CatchVariable exceptionCatchVariable?) {
if (exception != null) {
// TODO(62401): Remove the casts when the flow analysis uses
// [InternalExpressionVariable]s.
exceptionCatchVariable =
(exceptionCatchVariable as InternalVariable).astVariable
as CatchVariable;
_contextAllocationStrategy.handleDeclarationOfVariable(
exceptionCatchVariable,
captureKind: _captureKindForVariable(exceptionCatchVariable),
exception,
captureKind: _captureKindForVariable(exception),
);
node.exception = exceptionCatchVariable;
}
if (node.stackTrace case CatchVariable stackTraceCatchVariable?) {
if (stackTrace != null) {
// TODO(62401): Remove the casts when the flow analysis uses
// [InternalExpressionVariable]s.
stackTraceCatchVariable =
(stackTraceCatchVariable as InternalVariable).astVariable
as CatchVariable;
_contextAllocationStrategy.handleDeclarationOfVariable(
stackTraceCatchVariable,
captureKind: _captureKindForVariable(stackTraceCatchVariable),
stackTrace,
captureKind: _captureKindForVariable(stackTrace),
);
node.stackTrace = stackTraceCatchVariable;
}
}
StatementInferenceResult bodyResult = inferStatement(node.body);
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = bodyResult.statement..parent = node;
}
Statement body = bodyResult.hasChanged
?
// Coverage-ignore(suite): Not run.
bodyResult.statement
: node.body;
Scope? scope;
if (scopeProviderInfo != null) {
_contextAllocationStrategy.exitScopeProvider(scopeProviderInfo);
node.scope = scopeProviderInfo.scope;
scope = scopeProviderInfo.scope;
}
return extern.createCatch(
guard: node.guard,
exception: exception,
stackTrace: stackTrace,
body: body,
scope: scope,
fileOffset: node.fileOffset,
);
}
StatementInferenceResult visitTryStatement(TryStatement node) {
@@ -13576,16 +13580,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
StatementInferenceResult tryBlockResult = inferStatement(node.tryBlock);
List<Catch>? catchBlocks;
if (node.catchBlocks.isNotEmpty) {
catchBlocks = [];
flowAnalysis.tryCatchStatement_bodyEnd(tryBodyWithAssignedInfo);
for (Catch catchBlock in node.catchBlocks) {
for (InternalCatch catchBlock in node.catchBlocks) {
// TODO(62401): Remove the casts when the flow analysis uses
// [InternalExpressionVariable]s.
flowAnalysis.tryCatchStatement_catchBegin(
(catchBlock.exception as InternalVariable?)?.astVariable,
(catchBlock.stackTrace as InternalVariable?)?.astVariable,
catchBlock.exception?.astVariable,
catchBlock.stackTrace?.astVariable,
);
visitCatch(catchBlock);
catchBlocks.add(visitCatch(catchBlock));
flowAnalysis.tryCatchStatement_catchEnd();
}
flowAnalysis.tryCatchStatement_end();
@@ -13605,9 +13611,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
Statement result = tryBlockResult.hasChanged
? tryBlockResult.statement
: node.tryBlock;
if (node.catchBlocks.isNotEmpty) {
result = new TryCatch(result, node.catchBlocks)
..fileOffset = node.fileOffset;
if (catchBlocks != null) {
result = new TryCatch(result, catchBlocks)..fileOffset = node.fileOffset;
}
if (node.finallyBlock != null) {
result = new TryFinally(
@@ -248,20 +248,27 @@ void _testTryStatement() {
Block emptyBlock2 = new Block([]);
Block returnBlock1 = new Block([new ReturnStatement()]);
Block returnBlock2 = new Block([new ReturnStatement()]);
Catch emptyCatchBlock = new Catch(new Variable('e'), new Block([]));
Catch emptyCatchBlockOnVoid = new Catch(
new Variable('e'),
new Block([]),
guard: const VoidType(),
InternalCatch emptyCatchBlock = new InternalCatch(
exception: new VariableDeclarationImpl('e', fileOffset: TreeNode.noOffset),
body: new Block([]),
fileOffset: TreeNode.noOffset,
);
Catch returnCatchBlock = new Catch(
new Variable('e'),
new Block([new ReturnStatement()]),
);
Catch returnCatchBlockOnVoid = new Catch(
new Variable('e'),
new Block([new ReturnStatement()]),
InternalCatch emptyCatchBlockOnVoid = new InternalCatch(
exception: new VariableDeclarationImpl('e', fileOffset: TreeNode.noOffset),
body: new Block([]),
guard: const VoidType(),
fileOffset: TreeNode.noOffset,
);
InternalCatch returnCatchBlock = new InternalCatch(
exception: new VariableDeclarationImpl('e', fileOffset: TreeNode.noOffset),
body: new Block([new ReturnStatement()]),
fileOffset: TreeNode.noOffset,
);
InternalCatch returnCatchBlockOnVoid = new InternalCatch(
exception: new VariableDeclarationImpl('e', fileOffset: TreeNode.noOffset),
body: new Block([new ReturnStatement()]),
guard: const VoidType(),
fileOffset: TreeNode.noOffset,
);
testStatement(new TryStatement(emptyBlock1, [], emptyBlock2), '''