Author: Yuxuan Chen Date: 2026-02-02T08:44:11-08:00 New Revision: 98e83adc0104025b6127a128124d7d5079c4e4cb
URL: https://github.com/llvm/llvm-project/commit/98e83adc0104025b6127a128124d7d5079c4e4cb DIFF: https://github.com/llvm/llvm-project/commit/98e83adc0104025b6127a128124d7d5079c4e4cb.diff LOG: [Clang] Fix coro_await_elidable breaking with parenthesized expressions (#178495) The `applySafeElideContext` function used `IgnoreImplicit()` to find the underlying CallExpr, but this didn't strip `ParenExpr` nodes. When code like `co_await (fn(leaf()))` was parsed, the operand was wrapped in a `ParenExpr`, causing safe elide attribution to fail in Clang stage. This fix chains `IgnoreImplicit()->IgnoreParens()->IgnoreImplicit()` to handle both orderings of implicit nodes and parentheses in the AST. Fixes https://github.com/llvm/llvm-project/issues/178256. Except that `IgnoreParen()` should be a better suggestion than `IgnoreParenImpCast()`. Added: Modified: clang/lib/Sema/SemaCoroutine.cpp clang/test/CodeGenCoroutines/coro-await-elidable.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index c0aba832dba94..90af7340c4614 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -18,6 +18,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/IgnoreExpr.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Lex/Preprocessor.h" @@ -841,7 +842,11 @@ static bool isAttributedCoroAwaitElidable(const QualType &QT) { } static void applySafeElideContext(Expr *Operand) { - auto *Call = dyn_cast<CallExpr>(Operand->IgnoreImplicit()); + // Strip both implicit nodes and parentheses to find the underlying CallExpr. + // The AST may have these in either order, so we apply both transformations + // iteratively until reaching a fixed point. + auto *Call = dyn_cast<CallExpr>(IgnoreExprNodes( + Operand, IgnoreImplicitSingleStep, IgnoreParensSingleStep)); if (!Call || !Call->isPRValue()) return; diff --git a/clang/test/CodeGenCoroutines/coro-await-elidable.cpp b/clang/test/CodeGenCoroutines/coro-await-elidable.cpp index deb19b4a50043..71c56f310b344 100644 --- a/clang/test/CodeGenCoroutines/coro-await-elidable.cpp +++ b/clang/test/CodeGenCoroutines/coro-await-elidable.cpp @@ -124,4 +124,54 @@ Task<int> elidableWithPackRecursive() { co_return co_await sumAll(addTasks(returnSame(1), returnSame(2)), returnSame(3)); } +// Test that parenthesized expressions don't break HALO +// CHECK-LABEL: define{{.*}} @_Z14withParenthesev{{.*}} { +Task<int> withParenthese() { + // CHECK: call void @_Z6calleev(ptr {{.*}}) #[[ELIDE_SAFE]] + co_return co_await (callee()); +} + +// Test nested parentheses +// CHECK-LABEL: define{{.*}} @_Z20withNestedParenthesev{{.*}} { +Task<int> withNestedParenthese() { + // CHECK: call void @_Z6calleev(ptr {{.*}}) #[[ELIDE_SAFE]] + co_return co_await ((callee())); +} + +// Test parentheses with elidable argument +// CHECK-LABEL: define{{.*}} @_Z21withParenArgsElidablev{{.*}} { +Task<int> withParenArgsElidable() { + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 2) #[[ELIDE_SAFE]] + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 3){{$}} + co_return co_await (addTasks(returnSame(2), returnSame(3))); +} + +// Test parentheses around elidable argument expressions +// CHECK-LABEL: define{{.*}} @_Z24withParenInsideArgsFirstv{{.*}} { +Task<int> withParenInsideArgsFirst() { + // Argument wrapped in parens should still be elidable + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 4) #[[ELIDE_SAFE]] + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 5){{$}} + co_return co_await addTasks((returnSame(4)), returnSame(5)); +} + +// CHECK-LABEL: define{{.*}} @_Z25withParenInsideArgsSecondv{{.*}} { +Task<int> withParenInsideArgsSecond() { + // Both arguments wrapped in parens, first should still be elidable + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 6) #[[ELIDE_SAFE]] + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 7){{$}} + co_return co_await addTasks((returnSame(6)), (returnSame(7))); +} + +// Test operator overloading scenario (like the `operator|` case) +Task<int> operator|(int, [[clang::coro_await_elidable_argument]] Task<int> &&t) { + co_return co_await t; +} + +// CHECK-LABEL: define{{.*}} @_Z15withOperatorOldv{{.*}} { +Task<int> withOperatorOld() { + // CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 8) #[[ELIDE_SAFE]] + co_return co_await (0 | returnSame(8)); +} + // CHECK: attributes #[[ELIDE_SAFE]] = { coro_elide_safe } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
