Skip to content

Conversation

@dougsonos
Copy link
Contributor

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.

@dougsonos dougsonos self-assigned this Nov 2, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 2, 2025
@dougsonos dougsonos requested a review from Sirraide November 2, 2025 21:43
@llvmbot
Copy link
Member

llvmbot commented Nov 2, 2025

@llvm/pr-subscribers-clang

Author: Doug Wyatt (dougsonos)

Changes

There's a bug illustrated by this example:

template &lt;typename T&gt;
struct Holder {
	T value;
	
	T&amp; operator*() { return value; }
};

struct X {
	using Dispatch = float (X::*)() [[clang::nonblocking]];
    
	void fails(Holder&lt;Dispatch&gt;&amp; holder) [[clang::nonblocking]]
	{
		(this-&gt;*(*holder))();   &lt;&lt;&lt; the expression is incorrectly determined not to be nonblocking
	}

	void succeeds(Holder&lt;Dispatch&gt;&amp; holder) [[clang::nonblocking]]
	{
		auto func = *holder;
		(this-&gt;*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.


Full diff: https://github.com/llvm/llvm-project/pull/166101.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaFunctionEffects.cpp (+11-2)
  • (modified) clang/test/Sema/attr-nonblocking-constraints.cpp (+14-1)
diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp
index 8590ee831084f..6d7bcbf53fe0f 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1208,8 +1208,17 @@ 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..22f7e23f81cf9 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -236,9 +236,22 @@ void nb13() [[clang::nonblocking]] { nb12(); }
 struct PTMFTester {
 	typedef void (PTMFTester::*ConvertFunction)() [[clang::nonblocking]];
 
+	ConvertFunction mConvertFunc;
+
 	void convert() [[clang::nonblocking]];
 
-	ConvertFunction mConvertFunc;
+	template <typename T>
+	struct Holder {
+		T value;
+		
+		T& operator*() { return value; }
+	};
+
+
+	void ptmfInExpr(Holder<ConvertFunction>& holder) [[clang::nonblocking]]
+	{
+		(this->*(*holder))(); // This should not generate a warning.
+	}
 };
 
 void PTMFTester::convert() [[clang::nonblocking]]

Copy link
Member

@Sirraide Sirraide left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments; lgtm otherwise


void ptmfInExpr(Holder<ConvertFunction>& holder) [[clang::nonblocking]]
{
(this->*(*holder))(); // This should not generate a warning.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add

((*this).*(*holder))();

as well as a test case that shows that we do issue a warning if the member function is not nonblocking.

@dougsonos dougsonos merged commit ccc4732 into llvm:main Nov 3, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants