[clang] [CIR] Add verifier for CIR try op (PR #181419)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 12:54:49 PST 2026
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/181419
This adds a verifier to enforce the requirement that every catch handler in a cir.try operation must begin with a cir.catch_param operation.
>From dceb54570a59b0af3401ffb951cf3e0349c9bf09 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 12 Feb 2026 18:09:17 -0800
Subject: [PATCH] [CIR] Add verifier for CIR try op
This adds a verifier to enforce the requirement that every catch handler
in a cir.try operation must begin with a cir.catch_param operation.
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 1 +
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 32 ++++++++++++++
clang/test/CIR/IR/invalid-try-catch.cir | 45 ++++++++++++++++++++
clang/test/CIR/IR/try-catch.cir | 8 ++++
4 files changed, 86 insertions(+)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 7085580d99718..496033d34d137 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -6279,6 +6279,7 @@ def CIR_TryOp : CIR_Op<"try",[
}]>
];
+ let hasVerifier = 1;
let hasLLVMLowering = false;
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 4a25407d0f3cf..a8ef838111c29 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3774,6 +3774,38 @@ mlir::ValueRange cir::TryOp::getSuccessorInputs(RegionSuccessor successor) {
: ValueRange();
}
+LogicalResult cir::TryOp::verify() {
+ mlir::ArrayAttr handlerTypes = getHandlerTypes();
+ if (!handlerTypes) {
+ if (!getHandlerRegions().empty())
+ return emitOpError(
+ "handler regions must be empty when no handler types are present");
+ return success();
+ }
+
+ mlir::MutableArrayRef<mlir::Region> handlerRegions = getHandlerRegions();
+
+ // The parser and builder won't allow this to happen, but the loop below
+ // relies on the sizes being the same, so we check it here.
+ if (handlerRegions.size() != handlerTypes.size())
+ return emitOpError(
+ "number of handler regions and handler types must match");
+
+ for (const auto &[typeAttr, handlerRegion] :
+ llvm::zip(handlerTypes, handlerRegions)) {
+ // The unwind region does not require a cir.catch_param.
+ if (mlir::isa<cir::UnwindAttr>(typeAttr))
+ continue;
+
+ mlir::Block &entryBlock = handlerRegion.front();
+ if (entryBlock.empty() || !mlir::isa<cir::CatchParamOp>(entryBlock.front()))
+ return emitOpError(
+ "catch handler region must start with 'cir.catch_param'");
+ }
+
+ return success();
+}
+
static void
printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op,
mlir::MutableArrayRef<mlir::Region> handlerRegions,
diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir
index c11abb9dd449b..18002e6db4467 100644
--- a/clang/test/CIR/IR/invalid-try-catch.cir
+++ b/clang/test/CIR/IR/invalid-try-catch.cir
@@ -136,6 +136,51 @@ cir.func dso_local @invalid_catch_all_with_type_info() {
// -----
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
+
+cir.func dso_local @catch_handler_missing_catch_param() {
+ cir.scope {
+ // expected-error @below {{catch handler region must start with 'cir.catch_param'}}
+ cir.try {
+ cir.yield
+ } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
+ cir.yield
+ } unwind {
+ cir.resume
+ }
+ }
+ cir.return
+}
+
+}
+
+// -----
+
+!void = !cir.void
+
+module {
+
+cir.func dso_local @catch_all_handler_missing_catch_param() {
+ cir.scope {
+ // expected-error @below {{catch handler region must start with 'cir.catch_param'}}
+ cir.try {
+ cir.yield
+ } catch all {
+ cir.yield
+ }
+ }
+ cir.return
+}
+
+}
+
+// -----
+
module {
cir.func dso_local @invalid_unwind_with_catch_all() {
diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir
index 8ffce067ba043..3a5fe1a1573cc 100644
--- a/clang/test/CIR/IR/try-catch.cir
+++ b/clang/test/CIR/IR/try-catch.cir
@@ -1,6 +1,8 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
!u8i = !cir.int<u, 8>
+!s32i = !cir.int<s, 32>
+!void = !cir.void
module {
@@ -12,6 +14,7 @@ cir.func dso_local @empty_try_block_with_catch_all() {
cir.try {
cir.yield
} catch all {
+ %0 = cir.catch_param : !cir.ptr<!void>
cir.yield
}
}
@@ -23,6 +26,7 @@ cir.func dso_local @empty_try_block_with_catch_all() {
// CHECK: cir.try {
// CHECK: cir.yield
// CHECK: } catch all {
+// CHECK: %[[CP:.*]] = cir.catch_param : !cir.ptr<!void>
// CHECK: cir.yield
// CHECK: }
// CHECK: }
@@ -56,8 +60,10 @@ cir.func dso_local @empty_try_block_with_catch_ist() {
cir.try {
cir.yield
} catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
+ %0 = cir.catch_param : !cir.ptr<!s32i>
cir.yield
} catch [type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i>] {
+ %0 = cir.catch_param : !cir.ptr<!cir.ptr<!u8i>>
cir.yield
} unwind {
cir.yield
@@ -71,8 +77,10 @@ cir.func dso_local @empty_try_block_with_catch_ist() {
// CHECK: cir.try {
// CHECK: cir.yield
// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
+// CHECK: %[[CP1:.*]] = cir.catch_param : !cir.ptr<!s32i>
// CHECK: cir.yield
// CHECK: } catch [type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i>] {
+// CHECK: %[[CP2:.*]] = cir.catch_param : !cir.ptr<!cir.ptr<!u8i>>
// CHECK: cir.yield
// CHECK: } unwind {
// CHECK: cir.yield
More information about the cfe-commits
mailing list