From 7837cc397473399ad1221f267b7d413c43718d4d Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Fri, 10 Oct 2025 19:09:16 +0200 Subject: [PATCH 1/7] [CIR] Upstream CIR Dialect TryOp with Catch Attrs --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 15 ++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 63 ++++++++- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 132 ++++++++++++++++++ clang/test/CIR/IR/try-catch.cir | 84 +++++++++++ 4 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/IR/try-catch.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 610e349717e12..e739378e3969c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -968,4 +968,19 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> { }]; } +//===----------------------------------------------------------------------===// +// CatchAllAttr & CatchUnwindAttr +//===----------------------------------------------------------------------===// + +// Represents the unwind region where unwind continues or +// the program std::terminate's. +def CIR_CatchUnwindAttr : CIR_UnitAttr<"CatchUnwind", "unwind"> { + let storageType = [{ CatchUnwind }]; +} + +// Represents the catch_all region. +def CIR_CatchAllAttr : CIR_UnitAttr<"CatchAll", "all"> { + let storageType = [{ CatchAllAttr }]; +} + #endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index baab156726a2b..44e8363ee3f45 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -644,7 +644,7 @@ def CIR_StoreOp : CIR_Op<"store", [ defvar CIR_ReturnableScopes = [ "FuncOp", "ScopeOp", "IfOp", "SwitchOp", "CaseOp", - "DoWhileOp", "WhileOp", "ForOp" + "DoWhileOp", "WhileOp", "ForOp", "TryOp" ]; def CIR_ReturnOp : CIR_Op<"return", [ @@ -791,7 +791,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [ defvar CIR_YieldableScopes = [ "ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp", - "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp" + "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp" ]; def CIR_YieldOp : CIR_Op<"yield", [ @@ -4325,6 +4325,65 @@ def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> { }]; } +//===----------------------------------------------------------------------===// +// TryOp +//===----------------------------------------------------------------------===// + +def CIR_TryOp : CIR_Op<"try",[ + DeclareOpInterfaceMethods, + RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments +]> { + let summary = "C++ try block"; + let description = [{ + Holds the lexical scope of `try {}`. Note that resources used on catch + clauses are usually allocated in the same parent as `cir.try`. + + `synthetic`: use `cir.try` to represent try/catches not originally + present in the source code (e.g. `g = new Class` under `-fexceptions`). + + `cleanup`: signal to targets (LLVM for now) that this try/catch, needs + to specially tag their landing pads as needing "cleanup". + + Example: + + ```mlir + %0 = cir.alloc.exception 16 -> !cir.ptr + %1 = cir.get_global @d2 : !cir.ptr + cir.try synthetic cleanup { + cir.call exception @_ZN7test2_DC1ERKS_(%0, %1) + : (!cir.ptr, !cir.ptr) -> () cleanup { + %2 = cir.cast bitcast %0 : !cir.ptr -> !cir.ptr + cir.free.exception %2 + cir.yield + } + ... + } + ``` + }]; + + let arguments = (ins UnitAttr:$synthetic, UnitAttr:$cleanup, + OptionalAttr:$catch_types); + let regions = (region AnyRegion:$try_region, + VariadicRegion:$catch_regions); + + let assemblyFormat = [{ + (`synthetic` $synthetic^)? + (`cleanup` $cleanup^)? + $try_region + custom($catch_regions, $catch_types) + attr-dict + }]; + + // Everything already covered elsewhere. + let builders = [OpBuilder<(ins + "llvm::function_ref":$tryBuilder, + "llvm::function_ref":$catchBuilder)>]; + + let hasLLVMLowering = false; +} + //===----------------------------------------------------------------------===// // Atomic operations //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 7af3dc1f84955..2f5a93450ab88 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2914,6 +2914,138 @@ LogicalResult cir::TypeInfoAttr::verify( return success(); } +//===----------------------------------------------------------------------===// +// TryOp +//===----------------------------------------------------------------------===// + +void cir::TryOp::build( + OpBuilder &builder, OperationState &result, + function_ref tryBuilder, + function_ref catchBuilder) { + assert(tryBuilder && "expected builder callback for 'cir.try' body"); + assert(catchBuilder && "expected builder callback for 'catch' body"); + + OpBuilder::InsertionGuard guard(builder); + + // Try body region + Region *tryBodyRegion = result.addRegion(); + + // Create try body region and set insertion point + builder.createBlock(tryBodyRegion); + tryBuilder(builder, result.location); + catchBuilder(builder, result.location, result); +} + +void cir::TryOp::getSuccessorRegions( + mlir::RegionBranchPoint point, SmallVectorImpl ®ions) { + // If any index all the underlying regions branch back to the parent + // operation. + if (!point.isParent()) { + regions.push_back(RegionSuccessor()); + return; + } + + // If the condition isn't constant, both regions may be executed. + regions.push_back(RegionSuccessor(&getTryRegion())); + + // FIXME: optimize, ideas include: + // - If we know a target function never throws a specific type, we can + // remove the catch handler. + for (mlir::Region &r : this->getCatchRegions()) + regions.push_back(RegionSuccessor(&r)); +} + +static void printCatchRegions(OpAsmPrinter &printer, cir::TryOp op, + mlir::MutableArrayRef<::mlir::Region> regions, + mlir::ArrayAttr catchersAttr) { + if (!catchersAttr) + return; + + int currCatchIdx = 0; + printer << "catch ["; + llvm::interleaveComma(catchersAttr, printer, [&](const Attribute &a) { + if (mlir::isa(a)) { + printer.printAttribute(a); + printer << " "; + } else if (!a) { + printer << "all"; + } else { + printer << "type "; + printer.printAttribute(a); + printer << " "; + } + printer.printRegion(regions[currCatchIdx], /*printEntryBLockArgs=*/false, + /*printBlockTerminators=*/true); + currCatchIdx++; + }); + + printer << "]"; +} + +static ParseResult parseCatchRegions( + OpAsmParser &parser, + llvm::SmallVectorImpl> ®ions, + ::mlir::ArrayAttr &catchersAttr) { + if (parser.parseKeyword("catch").failed()) + return parser.emitError(parser.getCurrentLocation(), + "expected 'catch' keyword here"); + + auto parseAndCheckRegion = [&]() -> ParseResult { + // Parse region attached to catch + regions.emplace_back(new Region); + Region &currRegion = *regions.back(); + SMLoc parserLoc = parser.getCurrentLocation(); + if (parser.parseRegion(currRegion)) { + regions.clear(); + return failure(); + } + + if (currRegion.empty()) { + return parser.emitError(parser.getCurrentLocation(), + "catch region shall not be empty"); + } + + if (!(currRegion.back().mightHaveTerminator() && + currRegion.back().getTerminator())) + return parser.emitError( + parserLoc, "blocks are expected to be explicitly terminated"); + + return success(); + }; + + llvm::SmallVector catchList; + auto parseCatchEntry = [&]() -> ParseResult { + mlir::Attribute exceptionTypeInfo; + + if (parser.parseOptionalAttribute(exceptionTypeInfo).has_value()) { + catchList.push_back(exceptionTypeInfo); + } else { + ::llvm::StringRef attrStr; + if (parser.parseOptionalKeyword(&attrStr, {"all"}).succeeded()) { + // "all" keyword found, exceptionTypeInfo remains null + } else if (parser.parseOptionalKeyword("type").succeeded()) { + if (parser.parseAttribute(exceptionTypeInfo).failed()) + return parser.emitError(parser.getCurrentLocation(), + "expected valid RTTI info attribute"); + } else { + return parser.emitError(parser.getCurrentLocation(), + "expected attribute, 'all', or 'type' keyword"); + } + catchList.push_back(exceptionTypeInfo); + } + return parseAndCheckRegion(); + }; + + if (parser + .parseCommaSeparatedList(OpAsmParser::Delimiter::Square, + parseCatchEntry, " in catch list") + .failed()) + return failure(); + + catchersAttr = parser.getBuilder().getArrayAttr(catchList); + return ::mlir::success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir new file mode 100644 index 0000000000000..7bc71ff84d4ae --- /dev/null +++ b/clang/test/CIR/IR/try-catch.cir @@ -0,0 +1,84 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!u8i = !cir.int + +module { + +cir.global "private" constant external @_ZTIi : !cir.ptr +cir.global "private" constant external @_ZTIPKc : !cir.ptr + +cir.func dso_local @empty_try_block_with_catch_all() { + cir.scope { + cir.try { + cir.yield + } catch [type #cir.all { + cir.yield + }] + } + cir.return +} + +// CHECK: cir.func dso_local @empty_try_block_with_catch_all() { +// CHECK: cir.scope { +// CHECK: cir.try { +// CHECK: cir.yield +// CHECK: } catch [type #cir.all { +// CHECK: cir.yield +// CHECK: }] +// CHECK: } +// CHECK: cir.return +// CHECK: } + +cir.func dso_local @empty_try_block_with_catch_unwind() { + cir.scope { + cir.try { + cir.yield + } catch [#cir.unwind { + cir.yield + }] + } + cir.return +} + +// CHECK: cir.func dso_local @empty_try_block_with_catch_unwind() { +// CHECK: cir.scope { +// CHECK: cir.try { +// CHECK: cir.yield +// CHECK: } catch [#cir.unwind { +// CHECK: cir.yield +// CHECK: }] +// CHECK: } +// CHECK: cir.return +// CHECK: } + +cir.func dso_local @empty_try_block_with_catch_ist() { + cir.scope { + cir.try { + cir.yield + } catch [type #cir.global_view<@_ZTIi> : !cir.ptr { + cir.yield + }, type #cir.global_view<@_ZTIPKc> : !cir.ptr { + cir.yield + }, #cir.unwind { + cir.yield + }] + } + cir.return +} + +// CHECK: cir.func dso_local @empty_try_block_with_catch_ist() { +// CHECK: cir.scope { +// CHECK: cir.try { +// CHECK: cir.yield +// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr { +// CHECK: cir.yield +// CHECK: }, type #cir.global_view<@_ZTIPKc> : !cir.ptr { +// CHECK: cir.yield +// CHECK: }, #cir.unwind { +// CHECK: cir.yield +// CHECK: }] +// CHECK: } +// CHECK: cir.return +// CHECK: } + +} From 05e1171d8553f1e810d9d60ca413144e1e13026b Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Fri, 10 Oct 2025 21:14:15 +0200 Subject: [PATCH 2/7] Address code review comments --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 20 +++++- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18 ----- clang/test/CIR/IR/invalid-try-catch.cir | 75 ++++++++++++++++++++ 3 files changed, 93 insertions(+), 20 deletions(-) create mode 100644 clang/test/CIR/IR/invalid-try-catch.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 44e8363ee3f45..11097299212d8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4375,11 +4375,27 @@ def CIR_TryOp : CIR_Op<"try",[ }]; // Everything already covered elsewhere. - let builders = [OpBuilder<(ins + let builders = [ + OpBuilder<(ins "llvm::function_ref":$tryBuilder, "llvm::function_ref":$catchBuilder)>]; + "mlir::OperationState &)>":$catchBuilder), + [{ + assert(tryBuilder && "expected builder callback for 'cir.try' body"); + assert(catchBuilder && "expected builder callback for 'catch' body"); + + OpBuilder::InsertionGuard guard($_builder); + + // Try body region + Region *tryBodyRegion = $_state.addRegion(); + + // Create try body region and set insertion point + $_builder.createBlock(tryBodyRegion); + tryBuilder($_builder, $_state.location); + catchBuilder($_builder, $_state.location, $_state); + }]> + ]; let hasLLVMLowering = false; } diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2f5a93450ab88..40587fef47f7b 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2918,24 +2918,6 @@ LogicalResult cir::TypeInfoAttr::verify( // TryOp //===----------------------------------------------------------------------===// -void cir::TryOp::build( - OpBuilder &builder, OperationState &result, - function_ref tryBuilder, - function_ref catchBuilder) { - assert(tryBuilder && "expected builder callback for 'cir.try' body"); - assert(catchBuilder && "expected builder callback for 'catch' body"); - - OpBuilder::InsertionGuard guard(builder); - - // Try body region - Region *tryBodyRegion = result.addRegion(); - - // Create try body region and set insertion point - builder.createBlock(tryBodyRegion); - tryBuilder(builder, result.location); - catchBuilder(builder, result.location, result); -} - void cir::TryOp::getSuccessorRegions( mlir::RegionBranchPoint point, SmallVectorImpl ®ions) { // If any index all the underlying regions branch back to the parent diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir new file mode 100644 index 0000000000000..57863af13d20b --- /dev/null +++ b/clang/test/CIR/IR/invalid-try-catch.cir @@ -0,0 +1,75 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +module { + +cir.func dso_local @invalid_catch_without_all_or_type() { + cir.scope { + cir.try { + cir.yield + // expected-error @below {{'cir.try' expected attribute, 'all', or 'type' keyword}} + } catch [invalid_keyword { + cir.yield + }] + } + cir.return +} + +} + +// ----- + +module { + +cir.func dso_local @invalid_catch_rtti_type() { + cir.scope { + cir.try { + cir.yield + // expected-error @below {{expected attribute value}} + // expected-error @below {{'cir.try' expected valid RTTI info attribute}} + } catch [type invalid_type { + cir.yield + }] + } + cir.return +} + +} + +// ----- + +module { + +cir.func dso_local @invalid_catch_empty_block() { + cir.scope { + cir.try { + cir.yield + } catch [type #cir.all { + // expected-error @below {{'cir.try' catch region shall not be empty}} + }] + } + cir.return +} + +} + +// ----- + +!s32i = !cir.int + +module { + +cir.func dso_local @invalid_catch_not_terminated() { + %a = cir.alloca !s32i, !cir.ptr, ["a", init] + cir.scope { + cir.try { + cir.yield + } + // expected-error @below {{'cir.try' blocks are expected to be explicitly terminated}} + catch [type #cir.all { + %tmp_a = cir.load %a : !cir.ptr, !s32i + }] + } + cir.return +} + +} From 77397f86a820186d9675a3ebcd1ec947b610acf5 Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Sat, 11 Oct 2025 00:05:22 +0200 Subject: [PATCH 3/7] Address code review comments --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 11097299212d8..d4b335d023c02 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4339,24 +4339,26 @@ def CIR_TryOp : CIR_Op<"try",[ clauses are usually allocated in the same parent as `cir.try`. `synthetic`: use `cir.try` to represent try/catches not originally - present in the source code (e.g. `g = new Class` under `-fexceptions`). + present in the source code. For example, a synthetic `cir.try` region + is created around the constructor call when `operator new` is used + so that the memory allocated will be freed if the constructor throws + an exception. - `cleanup`: signal to targets (LLVM for now) that this try/catch, needs - to specially tag their landing pads as needing "cleanup". + `cleanup`: indicates that there are cleanups that must be performed + when exiting the try region via exception, even if the exception is not + caught. Example: ```mlir - %0 = cir.alloc.exception 16 -> !cir.ptr - %1 = cir.get_global @d2 : !cir.ptr - cir.try synthetic cleanup { - cir.call exception @_ZN7test2_DC1ERKS_(%0, %1) - : (!cir.ptr, !cir.ptr) -> () cleanup { - %2 = cir.cast bitcast %0 : !cir.ptr -> !cir.ptr - cir.free.exception %2 - cir.yield - } + cir.try { + cir.call exception @function() : () -> () + cir.yield + } catch [type #cir.global_view<@_ZTIPf> : !cir.ptr] { ... + cir.yield + } unwind { + cir.resume } ``` }]; From 4cdea3f26bab4f3b0ddede1e4c5d0086f418c050 Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Sat, 11 Oct 2025 13:25:55 +0200 Subject: [PATCH 4/7] TryOp & Catchers Parsers and printers --- .../CIR/Dialect/IR/CIRAttrConstraints.td | 30 +++- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 14 +- clang/include/clang/CIR/Dialect/IR/CIROps.td | 17 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 152 +++++++++--------- clang/test/CIR/IR/invalid-try-catch.cir | 82 ++++++++-- clang/test/CIR/IR/try-catch.cir | 60 +++---- 6 files changed, 223 insertions(+), 132 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td index 8f72ff4d754ad..bab847ef621bf 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td @@ -38,14 +38,32 @@ def CIR_AnyIntOrFloatAttr : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyFPAttr], string cppType = "::mlir::TypedAttr"; } +//===----------------------------------------------------------------------===// +// Exceptions constraints +//===----------------------------------------------------------------------===// + +def CIR_AnyCatchAllAttr + : CIR_AttrConstraint<"::cir::CatchAllAttr", "catch all attribute">; + +def CIR_AnyUnwindAttr + : CIR_AttrConstraint<"::cir::UnwindAttr", "unwind attribute">; + //===----------------------------------------------------------------------===// // GlobalViewAttr constraints //===----------------------------------------------------------------------===// -def CIR_AnyGlobalViewAttr : CIR_AttrConstraint<"::cir::GlobalViewAttr", "GlobalView attribute">; +def CIR_AnyGlobalViewAttr + : CIR_AttrConstraint<"::cir::GlobalViewAttr", "GlobalView attribute">; -def CIR_AnyIntOrGlobalViewAttr : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyGlobalViewAttr], - "integer or global view attribute"> { +def CIR_AnyIntOrGlobalViewAttr + : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyGlobalViewAttr], + "integer or global view attribute"> { + string cppType = "::mlir::TypedAttr"; +} + +def CIR_AnyGlobalViewOrCatchAllOrUnwindAttr + : AnyAttrOf<[CIR_AnyGlobalViewAttr, CIR_AnyCatchAllAttr, CIR_AnyUnwindAttr], + "catch all or unwind or global view attribute"> { string cppType = "::mlir::TypedAttr"; } @@ -61,4 +79,8 @@ def CIR_IntOrGlobalViewArrayAttr : TypedArrayAttrBase; + +#endif // CLANG_CIR_DIALECT_IR_CIRATTRCONSTRAINTS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index e739378e3969c..69dbad3ba1cde 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -969,18 +969,18 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> { } //===----------------------------------------------------------------------===// -// CatchAllAttr & CatchUnwindAttr +// CatchAllAttr & UnwindAttr //===----------------------------------------------------------------------===// -// Represents the unwind region where unwind continues or -// the program std::terminate's. -def CIR_CatchUnwindAttr : CIR_UnitAttr<"CatchUnwind", "unwind"> { - let storageType = [{ CatchUnwind }]; -} - // Represents the catch_all region. def CIR_CatchAllAttr : CIR_UnitAttr<"CatchAll", "all"> { let storageType = [{ CatchAllAttr }]; } +// Represents the unwind region where unwind continues or +// the program std::terminate's. +def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> { + let storageType = [{ CatchUnwind }]; +} + #endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index d4b335d023c02..a321ef2ceb41d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4363,10 +4363,16 @@ def CIR_TryOp : CIR_Op<"try",[ ``` }]; - let arguments = (ins UnitAttr:$synthetic, UnitAttr:$cleanup, - OptionalAttr:$catch_types); - let regions = (region AnyRegion:$try_region, - VariadicRegion:$catch_regions); + let arguments = (ins + UnitAttr:$synthetic, + UnitAttr:$cleanup, + CIR_GlobalViewOrCatchAllOrUnwindArrayAttr:$catch_types + ); + + let regions = (region + AnyRegion:$try_region, + VariadicRegion>:$catch_regions + ); let assemblyFormat = [{ (`synthetic` $synthetic^)? @@ -4376,7 +4382,6 @@ def CIR_TryOp : CIR_Op<"try",[ attr-dict }]; - // Everything already covered elsewhere. let builders = [ OpBuilder<(ins "llvm::function_ref ®ions) { - // If any index all the underlying regions branch back to the parent - // operation. + mlir::RegionBranchPoint point, + llvm::SmallVectorImpl ®ions) { + // The `try` and the `catchers` region branch back to the parent operation. if (!point.isParent()) { - regions.push_back(RegionSuccessor()); + regions.push_back(mlir::RegionSuccessor()); return; } - // If the condition isn't constant, both regions may be executed. - regions.push_back(RegionSuccessor(&getTryRegion())); + regions.push_back(mlir::RegionSuccessor(&getTryRegion())); - // FIXME: optimize, ideas include: - // - If we know a target function never throws a specific type, we can - // remove the catch handler. - for (mlir::Region &r : this->getCatchRegions()) - regions.push_back(RegionSuccessor(&r)); + // TODO(CIR): If we know a target function never throws a specific type, we + // can remove the catch handler. + for (mlir::Region ®ion : this->getCatchRegions()) + regions.push_back(mlir::RegionSuccessor(®ion)); } -static void printCatchRegions(OpAsmPrinter &printer, cir::TryOp op, - mlir::MutableArrayRef<::mlir::Region> regions, +static void printCatchRegions(mlir::OpAsmPrinter &printer, cir::TryOp op, + mlir::MutableArrayRef regions, mlir::ArrayAttr catchersAttr) { if (!catchersAttr) return; - int currCatchIdx = 0; - printer << "catch ["; - llvm::interleaveComma(catchersAttr, printer, [&](const Attribute &a) { - if (mlir::isa(a)) { - printer.printAttribute(a); + for (const auto [catcherIdx, catcherAttr] : llvm::enumerate(catchersAttr)) { + if (catcherIdx) printer << " "; - } else if (!a) { - printer << "all"; + + if (mlir::isa(catcherAttr)) { + printer << "catch all "; + } else if (mlir::isa(catcherAttr)) { + printer << "unwind "; } else { - printer << "type "; - printer.printAttribute(a); - printer << " "; + printer << "catch [type "; + printer.printAttribute(catcherAttr); + printer << "] "; } - printer.printRegion(regions[currCatchIdx], /*printEntryBLockArgs=*/false, - /*printBlockTerminators=*/true); - currCatchIdx++; - }); - printer << "]"; + printer.printRegion(regions[catcherIdx], /*printEntryBLockArgs=*/false, + /*printBlockTerminators=*/true); + } } -static ParseResult parseCatchRegions( - OpAsmParser &parser, - llvm::SmallVectorImpl> ®ions, - ::mlir::ArrayAttr &catchersAttr) { - if (parser.parseKeyword("catch").failed()) - return parser.emitError(parser.getCurrentLocation(), - "expected 'catch' keyword here"); +static mlir::ParseResult +parseCatchRegions(mlir::OpAsmParser &parser, + llvm::SmallVectorImpl> ®ions, + mlir::ArrayAttr &catchersAttr) { - auto parseAndCheckRegion = [&]() -> ParseResult { - // Parse region attached to catch - regions.emplace_back(new Region); - Region &currRegion = *regions.back(); - SMLoc parserLoc = parser.getCurrentLocation(); + auto parseCheckedCatcherRegion = [&]() -> mlir::ParseResult { + regions.emplace_back(new mlir::Region); + + mlir::Region &currRegion = *regions.back(); + mlir::SMLoc regionLoc = parser.getCurrentLocation(); if (parser.parseRegion(currRegion)) { regions.clear(); return failure(); } - if (currRegion.empty()) { - return parser.emitError(parser.getCurrentLocation(), - "catch region shall not be empty"); - } - - if (!(currRegion.back().mightHaveTerminator() && - currRegion.back().getTerminator())) + if (!currRegion.empty() && !(currRegion.back().mightHaveTerminator() && + currRegion.back().getTerminator())) return parser.emitError( - parserLoc, "blocks are expected to be explicitly terminated"); + regionLoc, "blocks are expected to be explicitly terminated"); return success(); }; - llvm::SmallVector catchList; - auto parseCatchEntry = [&]() -> ParseResult { - mlir::Attribute exceptionTypeInfo; + bool hasCatchAll = false; + llvm::SmallVector catcherAttrs; + while (parser.parseOptionalKeyword("catch").succeeded()) { + bool hasLSquare = parser.parseOptionalLSquare().succeeded(); - if (parser.parseOptionalAttribute(exceptionTypeInfo).has_value()) { - catchList.push_back(exceptionTypeInfo); - } else { - ::llvm::StringRef attrStr; - if (parser.parseOptionalKeyword(&attrStr, {"all"}).succeeded()) { - // "all" keyword found, exceptionTypeInfo remains null - } else if (parser.parseOptionalKeyword("type").succeeded()) { - if (parser.parseAttribute(exceptionTypeInfo).failed()) - return parser.emitError(parser.getCurrentLocation(), - "expected valid RTTI info attribute"); - } else { + llvm::StringRef attrStr; + if (parser.parseOptionalKeyword(&attrStr, {"all", "type"}).failed()) + return parser.emitError(parser.getCurrentLocation(), + "expected 'all' or 'type' keyword"); + + bool isCatchAll = attrStr == "all"; + if (isCatchAll) { + if (hasCatchAll) return parser.emitError(parser.getCurrentLocation(), - "expected attribute, 'all', or 'type' keyword"); - } - catchList.push_back(exceptionTypeInfo); + "can't have more than one catch all"); + hasCatchAll = true; } - return parseAndCheckRegion(); - }; - if (parser - .parseCommaSeparatedList(OpAsmParser::Delimiter::Square, - parseCatchEntry, " in catch list") - .failed()) - return failure(); + mlir::Attribute exceptionRTTIAttr; + if (!isCatchAll && parser.parseAttribute(exceptionRTTIAttr).failed()) + return parser.emitError(parser.getCurrentLocation(), + "expected valid RTTI info attribute"); - catchersAttr = parser.getBuilder().getArrayAttr(catchList); - return ::mlir::success(); + catcherAttrs.push_back(isCatchAll + ? cir::CatchAllAttr::get(parser.getContext()) + : exceptionRTTIAttr); + + if (hasLSquare && isCatchAll) + return parser.emitError(parser.getCurrentLocation(), + "catch all dosen't need RTTI info attribute"); + + if (hasLSquare && parser.parseRSquare().failed()) + return parser.emitError(parser.getCurrentLocation(), + "expected `]` after RTTI info attribute"); + + if (parseCheckedCatcherRegion().failed()) + return mlir::failure(); + } + + if (parser.parseOptionalKeyword("unwind").succeeded()) { + catcherAttrs.push_back(cir::UnwindAttr::get(parser.getContext())); + if (parseCheckedCatcherRegion().failed()) + return mlir::failure(); + } + + catchersAttr = parser.getBuilder().getArrayAttr(catcherAttrs); + return mlir::success(); } //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir index 57863af13d20b..40eaa7b06e3c3 100644 --- a/clang/test/CIR/IR/invalid-try-catch.cir +++ b/clang/test/CIR/IR/invalid-try-catch.cir @@ -6,10 +6,10 @@ cir.func dso_local @invalid_catch_without_all_or_type() { cir.scope { cir.try { cir.yield - // expected-error @below {{'cir.try' expected attribute, 'all', or 'type' keyword}} + // expected-error @below {{'cir.try' expected 'all' or 'type' keyword}} } catch [invalid_keyword { cir.yield - }] + } } cir.return } @@ -22,13 +22,12 @@ module { cir.func dso_local @invalid_catch_rtti_type() { cir.scope { + // expected-error @below {{'cir.try' op attribute 'catch_types' failed to satisfy constraint: catch all or unwind or global view array attribute}} cir.try { cir.yield - // expected-error @below {{expected attribute value}} - // expected-error @below {{'cir.try' expected valid RTTI info attribute}} - } catch [type invalid_type { + } catch [type #cir.undef] { cir.yield - }] + } } cir.return } @@ -41,11 +40,11 @@ module { cir.func dso_local @invalid_catch_empty_block() { cir.scope { + // expected-error @below {{'cir.try' op region #1 ('catch_regions') failed to verify constraint: region with at least 1 blocks}} cir.try { cir.yield - } catch [type #cir.all { - // expected-error @below {{'cir.try' catch region shall not be empty}} - }] + } catch all { + } } cir.return } @@ -65,11 +64,72 @@ cir.func dso_local @invalid_catch_not_terminated() { cir.yield } // expected-error @below {{'cir.try' blocks are expected to be explicitly terminated}} - catch [type #cir.all { + catch all { %tmp_a = cir.load %a : !cir.ptr, !s32i - }] + } } cir.return } } + +// ----- + +module { + +cir.func dso_local @invalid_catch_multiple_catch_all() { + cir.scope { + cir.try { + cir.yield + } catch all { + cir.yield + } + // expected-error @below {{op 'cir.try' can't have more than one catch all}} + catch all { + cir.yield + } + } + cir.return +} + +} + +// ----- + +module { + +cir.func dso_local @invalid_catch_without_type_info() { + cir.scope { + cir.try { + cir.yield + } + // expected-error @below {{expected attribute value}} + // expected-error @below {{op 'cir.try' expected valid RTTI info attribute}} + catch [type] { + cir.yield + } + } + cir.return +} + +} + +// ----- + +module { + +cir.func dso_local @invalid_catch_all_with_type_info() { + cir.scope { + cir.try { + cir.yield + } + // expected-error @below {{op 'cir.try' catch all dosen't need RTTI info attribute}} + catch [all] { + cir.yield + } + } + cir.return +} + +} + diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir index 7bc71ff84d4ae..7becd0b559f5e 100644 --- a/clang/test/CIR/IR/try-catch.cir +++ b/clang/test/CIR/IR/try-catch.cir @@ -11,57 +11,57 @@ cir.func dso_local @empty_try_block_with_catch_all() { cir.scope { cir.try { cir.yield - } catch [type #cir.all { + } catch all { cir.yield - }] + } } cir.return } -// CHECK: cir.func dso_local @empty_try_block_with_catch_all() { -// CHECK: cir.scope { -// CHECK: cir.try { -// CHECK: cir.yield -// CHECK: } catch [type #cir.all { -// CHECK: cir.yield -// CHECK: }] -// CHECK: } -// CHECK: cir.return -// CHECK: } +// CHECK: cir.func dso_local @empty_try_block_with_catch_all() { +// CHECK: cir.scope { +// CHECK: cir.try { +// CHECK: cir.yield +// CHECK: } catch all { +// CHECK: cir.yield +// CHECK: } +// CHECK: } +// CHECK: cir.return +// CHECK: } cir.func dso_local @empty_try_block_with_catch_unwind() { cir.scope { cir.try { cir.yield - } catch [#cir.unwind { + } unwind { cir.yield - }] + } } cir.return } // CHECK: cir.func dso_local @empty_try_block_with_catch_unwind() { -// CHECK: cir.scope { -// CHECK: cir.try { -// CHECK: cir.yield -// CHECK: } catch [#cir.unwind { -// CHECK: cir.yield -// CHECK: }] -// CHECK: } -// CHECK: cir.return +// CHECK: cir.scope { +// CHECK: cir.try { +// CHECK: cir.yield +// CHECK: } unwind { +// CHECK: cir.yield +// CHECK: } +// CHECK: } +// CHECK: cir.return // CHECK: } cir.func dso_local @empty_try_block_with_catch_ist() { cir.scope { cir.try { cir.yield - } catch [type #cir.global_view<@_ZTIi> : !cir.ptr { + } catch [type #cir.global_view<@_ZTIi> : !cir.ptr] { cir.yield - }, type #cir.global_view<@_ZTIPKc> : !cir.ptr { + } catch [type #cir.global_view<@_ZTIPKc> : !cir.ptr] { cir.yield - }, #cir.unwind { + } unwind { cir.yield - }] + } } cir.return } @@ -70,13 +70,13 @@ cir.func dso_local @empty_try_block_with_catch_ist() { // CHECK: cir.scope { // CHECK: cir.try { // CHECK: cir.yield -// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr { +// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr] { // CHECK: cir.yield -// CHECK: }, type #cir.global_view<@_ZTIPKc> : !cir.ptr { +// CHECK: } catch [type #cir.global_view<@_ZTIPKc> : !cir.ptr] { // CHECK: cir.yield -// CHECK: }, #cir.unwind { +// CHECK: } unwind { // CHECK: cir.yield -// CHECK: }] +// CHECK: } // CHECK: } // CHECK: cir.return // CHECK: } From 53690902122b9e1d69dc579e5e5eaf6a8a69800f Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Sun, 12 Oct 2025 17:44:44 +0200 Subject: [PATCH 5/7] Add error message for using unwind with catch all --- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 4 ++++ clang/test/CIR/IR/invalid-try-catch.cir | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 1ff0a784e4eda..3733ca1cbd777 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -3023,6 +3023,10 @@ parseCatchRegions(mlir::OpAsmParser &parser, } if (parser.parseOptionalKeyword("unwind").succeeded()) { + if (hasCatchAll) + return parser.emitError(parser.getCurrentLocation(), + "unwind can't be used with catch all"); + catcherAttrs.push_back(cir::UnwindAttr::get(parser.getContext())); if (parseCheckedCatcherRegion().failed()) return mlir::failure(); diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir index 40eaa7b06e3c3..4c4187d346846 100644 --- a/clang/test/CIR/IR/invalid-try-catch.cir +++ b/clang/test/CIR/IR/invalid-try-catch.cir @@ -122,7 +122,7 @@ cir.func dso_local @invalid_catch_all_with_type_info() { cir.scope { cir.try { cir.yield - } + } // expected-error @below {{op 'cir.try' catch all dosen't need RTTI info attribute}} catch [all] { cir.yield @@ -133,3 +133,24 @@ cir.func dso_local @invalid_catch_all_with_type_info() { } +// ----- + +module { + +cir.func dso_local @invalid_unwind_with_catch_all() { + cir.scope { + cir.try { + cir.yield + } + catch all { + cir.yield + } + // expected-error @below {{op 'cir.try' unwind can't be used with catch all}} + unwind { + + } + } + cir.return +} + +} From 01e63fb8456a669f972ce571d6934c2077bb7b90 Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Mon, 13 Oct 2025 19:55:58 +0200 Subject: [PATCH 6/7] Rename catch_regions to handlers to act as general eh regions --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 4 ++-- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index a321ef2ceb41d..28cf3292c083d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4371,14 +4371,14 @@ def CIR_TryOp : CIR_Op<"try",[ let regions = (region AnyRegion:$try_region, - VariadicRegion>:$catch_regions + VariadicRegion>:$handlers ); let assemblyFormat = [{ (`synthetic` $synthetic^)? (`cleanup` $cleanup^)? $try_region - custom($catch_regions, $catch_types) + custom($handlers, $catch_types) attr-dict }]; diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 3733ca1cbd777..ab2a1156c9f22 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2931,8 +2931,8 @@ void cir::TryOp::getSuccessorRegions( // TODO(CIR): If we know a target function never throws a specific type, we // can remove the catch handler. - for (mlir::Region ®ion : this->getCatchRegions()) - regions.push_back(mlir::RegionSuccessor(®ion)); + for (mlir::Region &handler : this->getHandlers()) + regions.push_back(mlir::RegionSuccessor(&handler)); } static void printCatchRegions(mlir::OpAsmPrinter &printer, cir::TryOp op, From 905b0dbc8e71f6ee07690ca61b64f1d49401f060 Mon Sep 17 00:00:00 2001 From: Amr Hesham Date: Wed, 15 Oct 2025 18:55:47 +0200 Subject: [PATCH 7/7] Address code review comments --- .../CIR/Dialect/IR/CIRAttrConstraints.td | 5 +-- clang/include/clang/CIR/Dialect/IR/CIROps.td | 13 +++--- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 42 ++++++++++--------- clang/test/CIR/IR/invalid-try-catch.cir | 4 +- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td index bab847ef621bf..2548d464fb07f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td @@ -61,7 +61,7 @@ def CIR_AnyIntOrGlobalViewAttr string cppType = "::mlir::TypedAttr"; } -def CIR_AnyGlobalViewOrCatchAllOrUnwindAttr +def CIR_TryHandlerAttr : AnyAttrOf<[CIR_AnyGlobalViewAttr, CIR_AnyCatchAllAttr, CIR_AnyUnwindAttr], "catch all or unwind or global view attribute"> { string cppType = "::mlir::TypedAttr"; @@ -79,8 +79,7 @@ def CIR_IntOrGlobalViewArrayAttr : TypedArrayAttrBase; #endif // CLANG_CIR_DIALECT_IR_CIRATTRCONSTRAINTS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 28cf3292c083d..4b26f814cbbe9 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4366,19 +4366,19 @@ def CIR_TryOp : CIR_Op<"try",[ let arguments = (ins UnitAttr:$synthetic, UnitAttr:$cleanup, - CIR_GlobalViewOrCatchAllOrUnwindArrayAttr:$catch_types + CIR_TryHandlerArrayAttr:$handler_types ); let regions = (region AnyRegion:$try_region, - VariadicRegion>:$handlers + VariadicRegion>:$handler_regions ); let assemblyFormat = [{ (`synthetic` $synthetic^)? (`cleanup` $cleanup^)? $try_region - custom($handlers, $catch_types) + custom($handler_regions, $handler_types) attr-dict }]; @@ -4387,10 +4387,11 @@ def CIR_TryOp : CIR_Op<"try",[ "llvm::function_ref":$tryBuilder, "llvm::function_ref":$catchBuilder), + "mlir::OperationState &)>":$handlersBuilder), [{ assert(tryBuilder && "expected builder callback for 'cir.try' body"); - assert(catchBuilder && "expected builder callback for 'catch' body"); + assert(handlersBuilder + && "expected builder callback for 'handlers' body"); OpBuilder::InsertionGuard guard($_builder); @@ -4400,7 +4401,7 @@ def CIR_TryOp : CIR_Op<"try",[ // Create try body region and set insertion point $_builder.createBlock(tryBodyRegion); tryBuilder($_builder, $_state.location); - catchBuilder($_builder, $_state.location, $_state); + handlersBuilder($_builder, $_state.location, $_state); }]> ]; diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index ab2a1156c9f22..0712de2d2f182 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2931,47 +2931,49 @@ void cir::TryOp::getSuccessorRegions( // TODO(CIR): If we know a target function never throws a specific type, we // can remove the catch handler. - for (mlir::Region &handler : this->getHandlers()) - regions.push_back(mlir::RegionSuccessor(&handler)); + for (mlir::Region &handlerRegion : this->getHandlerRegions()) + regions.push_back(mlir::RegionSuccessor(&handlerRegion)); } -static void printCatchRegions(mlir::OpAsmPrinter &printer, cir::TryOp op, - mlir::MutableArrayRef regions, - mlir::ArrayAttr catchersAttr) { - if (!catchersAttr) +static void +printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op, + mlir::MutableArrayRef handlerRegions, + mlir::ArrayAttr handlerTypes) { + if (!handlerTypes) return; - for (const auto [catcherIdx, catcherAttr] : llvm::enumerate(catchersAttr)) { - if (catcherIdx) + for (const auto [typeIdx, typeAttr] : llvm::enumerate(handlerTypes)) { + if (typeIdx) printer << " "; - if (mlir::isa(catcherAttr)) { + if (mlir::isa(typeAttr)) { printer << "catch all "; - } else if (mlir::isa(catcherAttr)) { + } else if (mlir::isa(typeAttr)) { printer << "unwind "; } else { printer << "catch [type "; - printer.printAttribute(catcherAttr); + printer.printAttribute(typeAttr); printer << "] "; } - printer.printRegion(regions[catcherIdx], /*printEntryBLockArgs=*/false, + printer.printRegion(handlerRegions[typeIdx], + /*printEntryBLockArgs=*/false, /*printBlockTerminators=*/true); } } -static mlir::ParseResult -parseCatchRegions(mlir::OpAsmParser &parser, - llvm::SmallVectorImpl> ®ions, - mlir::ArrayAttr &catchersAttr) { +static mlir::ParseResult parseTryHandlerRegions( + mlir::OpAsmParser &parser, + llvm::SmallVectorImpl> &handlerRegions, + mlir::ArrayAttr &handlerTypes) { auto parseCheckedCatcherRegion = [&]() -> mlir::ParseResult { - regions.emplace_back(new mlir::Region); + handlerRegions.emplace_back(new mlir::Region); - mlir::Region &currRegion = *regions.back(); + mlir::Region &currRegion = *handlerRegions.back(); mlir::SMLoc regionLoc = parser.getCurrentLocation(); if (parser.parseRegion(currRegion)) { - regions.clear(); + handlerRegions.clear(); return failure(); } @@ -3032,7 +3034,7 @@ parseCatchRegions(mlir::OpAsmParser &parser, return mlir::failure(); } - catchersAttr = parser.getBuilder().getArrayAttr(catcherAttrs); + handlerTypes = parser.getBuilder().getArrayAttr(catcherAttrs); return mlir::success(); } diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir index 4c4187d346846..04a4d2543b8e1 100644 --- a/clang/test/CIR/IR/invalid-try-catch.cir +++ b/clang/test/CIR/IR/invalid-try-catch.cir @@ -22,7 +22,7 @@ module { cir.func dso_local @invalid_catch_rtti_type() { cir.scope { - // expected-error @below {{'cir.try' op attribute 'catch_types' failed to satisfy constraint: catch all or unwind or global view array attribute}} + // expected-error @below {{'cir.try' op attribute 'handler_types' failed to satisfy constraint: catch all or unwind or global view array attribute}} cir.try { cir.yield } catch [type #cir.undef] { @@ -40,7 +40,7 @@ module { cir.func dso_local @invalid_catch_empty_block() { cir.scope { - // expected-error @below {{'cir.try' op region #1 ('catch_regions') failed to verify constraint: region with at least 1 blocks}} + // expected-error @below {{'cir.try' op region #1 ('handler_regions') failed to verify constraint: region with at least 1 blocks}} cir.try { cir.yield } catch all {