Author: Doug Wyatt Date: 2025-11-03T12:26:49-08:00 New Revision: ccc473254fd2d0da01921e8402fbd4f678ff46f1
URL: https://github.com/llvm/llvm-project/commit/ccc473254fd2d0da01921e8402fbd4f678ff46f1 DIFF: https://github.com/llvm/llvm-project/commit/ccc473254fd2d0da01921e8402fbd4f678ff46f1.diff LOG: [Clang] FunctionEffects: properly extract the type of a bound member member function from a CallExpr. (#166101) There's a bug illustrated by this example: ``` template <typename T> struct Holder { T value; T& operator*() { return value; } }; struct X { using Dispatch = float (X::*)() [[clang::nonblocking]]; void fails(Holder<Dispatch>& holder) [[clang::nonblocking]] { (this->*(*holder))(); <<< the expression is incorrectly determined not to be nonblocking } void succeeds(Holder<Dispatch>& holder) [[clang::nonblocking]] { auto func = *holder; (this->*func)(); } }; ``` In both cases we have a `CXXMemberCallExpr`. In `succeeds`, the expression refers to a `Decl` (`func`) and gets a useful PTMF type. In `fails`, the expression does not refer to a `Decl` and its type is special, printed as `bound member function`. `Expr` provides a method for extracting the true type so we can use that in this situation. --------- Co-authored-by: Doug Wyatt <[email protected]> Co-authored-by: Sirraide <[email protected]> Added: Modified: clang/lib/Sema/SemaFunctionEffects.cpp clang/test/Sema/attr-nonblocking-constraints.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp index 8590ee831084f..5459861ec349d 100644 --- a/clang/lib/Sema/SemaFunctionEffects.cpp +++ b/clang/lib/Sema/SemaFunctionEffects.cpp @@ -1208,8 +1208,16 @@ class Analyzer { return true; } - // No Decl, just an Expr. Just check based on its type. - checkIndirectCall(Call, CalleeExpr->getType()); + // No Decl, just an Expr. Just check based on its type. Bound member + // functions are a special expression type and need to be specially + // unpacked. + QualType CalleeExprQT = CalleeExpr->getType(); + if (CalleeExpr->isBoundMemberFunction(Outer.S.getASTContext())) { + QualType QT = Expr::findBoundMemberType(CalleeExpr); + if (!QT.isNull()) + CalleeExprQT = QT; + } + checkIndirectCall(Call, CalleeExprQT); return true; } diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp b/clang/test/Sema/attr-nonblocking-constraints.cpp index b26a945843696..0d2dbb4947dc8 100644 --- a/clang/test/Sema/attr-nonblocking-constraints.cpp +++ b/clang/test/Sema/attr-nonblocking-constraints.cpp @@ -235,16 +235,35 @@ void nb13() [[clang::nonblocking]] { nb12(); } // C++ member function pointers struct PTMFTester { typedef void (PTMFTester::*ConvertFunction)() [[clang::nonblocking]]; - - void convert() [[clang::nonblocking]]; + typedef void (PTMFTester::*BlockingFunction)(); ConvertFunction mConvertFunc; -}; -void PTMFTester::convert() [[clang::nonblocking]] -{ - (this->*mConvertFunc)(); -} + void convert() [[clang::nonblocking]] + { + (this->*mConvertFunc)(); // This should not generate a warning. + } + + template <typename T> + struct Holder { + T value; + + T& operator*() { return value; } + }; + + + void ptmfInExpr(Holder<ConvertFunction>& holder) [[clang::nonblocking]] + { + (this->*(*holder))(); // Should not generate a warning. + ((*this).*(*holder))(); // Should not generate a warning. + } + + void ptmfInExpr(Holder<BlockingFunction>& holder) [[clang::nonblocking]] + { + (this->*(*holder))(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' expression}} + ((*this).*(*holder))(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' expression}} + } +}; // Allow implicit conversion from array to pointer. void nb14(unsigned idx) [[clang::nonblocking]] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
