[clang] [clang][Sema] support block pointers as non-type template parameters (PR #191694)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 12 02:22:13 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Serosh (Serosh-commits)
<details>
<summary>Changes</summary>
allow block pointers to be used as template parameters in C++20.
Add mangling for block literals in template arguments.
Let ODR hashing see block expressions so they don't cause a crash.
Fix a crash by making sure block names decay to pointers.
Allow matching block pointer types even if they have different const qualifiers.
---
Full diff: https://github.com/llvm/llvm-project/pull/191694.diff
5 Files Affected:
- (modified) clang/lib/AST/ItaniumMangle.cpp (+8-4)
- (modified) clang/lib/AST/ODRHash.cpp (+7-2)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+20-13)
- (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+1)
- (added) clang/test/SemaCXX/nttp-blockpointer.cpp (+53)
``````````diff
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index f58faa03bfa8c..af2b1b6ae0f20 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5009,7 +5009,11 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
}
// FIXME: invent manglings for all these.
- case Expr::BlockExprClass:
+ case Expr::BlockExprClass: {
+ NotPrimaryExpr();
+ mangleUnqualifiedBlock(cast<BlockExpr>(E)->getBlockDecl());
+ break;
+ }
case Expr::ChooseExprClass:
case Expr::CompoundLiteralExprClass:
case Expr::ExtVectorElementExprClass:
@@ -6775,7 +6779,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
case APValue::LValue: {
// Proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
- assert((T->isPointerOrReferenceType()) &&
+ assert((T->isPointerOrReferenceType() || T->isBlockPointerType()) &&
"unexpected type for LValue template arg");
if (V.isNullPointer()) {
@@ -6844,7 +6848,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
Out << "cv";
mangleType(T);
}
- if (T->isPointerType())
+ if (T->isPointerType() || T->isBlockPointerType())
Out << "ad";
Out << "so";
mangleType(T->isVoidPointerType()
@@ -6859,7 +6863,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
Out << "cv";
mangleType(T);
}
- if (T->isPointerType()) {
+ if (T->isPointerType() || T->isBlockPointerType()) {
NotPrimaryExpr();
Out << "ad";
}
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..0f883b240f30c 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -1270,8 +1270,13 @@ void ODRHash::AddStructuralValue(const APValue &Value) {
break;
}
- assert(Base.is<const ValueDecl *>());
- AddDecl(Base.get<const ValueDecl *>());
+ if (auto *VD = Base.dyn_cast<const ValueDecl *>()) {
+ AddDecl(VD);
+ } else if (auto *E = Base.dyn_cast<const Expr *>()) {
+ AddStmt(E);
+ } else {
+ llvm_unreachable("unexpected LValue base kind in structural value");
+ }
ID.AddInteger(Value.getLValueOffset().getQuantity());
bool OnePastTheEnd = Value.isLValueOnePastTheEnd();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..2851c68c62c7e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1459,11 +1459,6 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
return QualType();
}
- if (T->isBlockPointerType()) {
- Diag(Loc, diag::err_template_nontype_parm_bad_type) << T;
- return QualType();
- }
-
// C++ [temp.param]p4:
//
// A non-type template-parameter shall have one of the following
@@ -1473,6 +1468,8 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
if (T->isIntegralOrEnumerationType() ||
// -- pointer to object or pointer to function,
T->isPointerType() ||
+ // -- block pointer type,
+ (getLangOpts().CPlusPlus20 && T->isBlockPointerType()) ||
// -- lvalue reference to object or lvalue reference to function,
T->isLValueReferenceType() ||
// -- pointer to member,
@@ -6670,8 +6667,8 @@ CheckTemplateArgumentIsCompatibleWithParameter(Sema &S, NamedDecl *Param,
QualType ParamType, Expr *ArgIn,
Expr *Arg, QualType ArgType) {
bool ObjCLifetimeConversion;
- if (ParamType->isPointerType() &&
- !ParamType->castAs<PointerType>()->getPointeeType()->isFunctionType() &&
+ if ((ParamType->isPointerType() || ParamType->isBlockPointerType()) &&
+ !ParamType->getPointeeType()->isFunctionType() &&
S.IsQualificationConversion(ArgType, ParamType, false,
ObjCLifetimeConversion)) {
// For pointer-to-object types, qualification conversions are
@@ -6821,7 +6818,8 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
Entity = CUE->getGuidDecl();
// If our parameter has pointer type, check for a null template value.
- if (ParamType->isPointerType() || ParamType->isNullPtrType()) {
+ if (ParamType->isPointerType() || ParamType->isBlockPointerType() ||
+ ParamType->isNullPtrType()) {
switch (isNullPointerValueTemplateArgument(S, Param, ParamType, ArgIn,
Entity)) {
case NPV_NullPointer:
@@ -7355,17 +7353,19 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
if (Value.isLValue()) {
APValue::LValueBase Base = Value.getLValueBase();
auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>());
+ auto *E = const_cast<Expr *>(Base.dyn_cast<const Expr *>());
// For a non-type template-parameter of pointer or reference type,
// the value of the constant expression shall not refer to
assert(ParamType->isPointerOrReferenceType() ||
- ParamType->isNullPtrType());
+ ParamType->isBlockPointerType() || ParamType->isNullPtrType());
// -- a temporary object
// -- a string literal
// -- the result of a typeid expression, or
// -- a predefined __func__ variable
if (Base &&
(!VD ||
- isa<LifetimeExtendedTemporaryDecl, UnnamedGlobalConstantDecl>(VD))) {
+ isa<LifetimeExtendedTemporaryDecl, UnnamedGlobalConstantDecl>(VD)) &&
+ (!E || !isa<BlockExpr>(E))) {
Diag(Arg->getBeginLoc(), diag::err_template_arg_not_decl_ref)
<< Arg->getSourceRange();
return ExprError();
@@ -7663,13 +7663,16 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
return Arg;
}
- if (ParamType->isPointerType()) {
+ if (ParamType->isPointerType() || ParamType->isBlockPointerType()) {
// -- for a non-type template-parameter of type pointer to
// object, qualification conversions (4.4) and the
// array-to-pointer conversion (4.2) are applied.
// C++0x also allows a value of std::nullptr_t.
- assert(ParamType->getPointeeType()->isIncompleteOrObjectType() &&
- "Only object pointers allowed here");
+ if (ParamType->isPointerType())
+ assert(ParamType->getPointeeType()->isIncompleteOrObjectType() &&
+ "Only object pointers allowed here");
+ else
+ assert(ParamType->isBlockPointerType() && "Expected block pointer");
if (CheckTemplateArgumentAddressOfObjectOrFunction(
*this, Param, ParamType, Arg, SugaredConverted, CanonicalConverted))
@@ -7976,6 +7979,10 @@ ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
RefExpr = DefaultFunctionArrayConversion(RefExpr.get());
if (RefExpr.isInvalid())
return ExprError();
+ } else if (ParamType->isBlockPointerType()) {
+ RefExpr = DefaultLvalueConversion(RefExpr.get());
+ if (RefExpr.isInvalid())
+ return ExprError();
} else if (ParamType->isPointerType() || ParamType->isMemberPointerType()) {
// For any other pointer, take the address (or form a pointer-to-member).
RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get());
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c71c40526ccdc..66bbc7b1353e1 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4986,6 +4986,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
// both P and A are pointers or member pointers. In this case, we
// just ignore cv-qualifiers completely).
if ((P->isPointerType() && A->isPointerType()) ||
+ (P->isBlockPointerType() && A->isBlockPointerType()) ||
(P->isMemberPointerType() && A->isMemberPointerType()))
TDF |= TDF_IgnoreQualifiers;
diff --git a/clang/test/SemaCXX/nttp-blockpointer.cpp b/clang/test/SemaCXX/nttp-blockpointer.cpp
new file mode 100644
index 0000000000000..eb91f87e5bd5a
--- /dev/null
+++ b/clang/test/SemaCXX/nttp-blockpointer.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -std=c++20 -fblocks -triple x86_64-apple-darwin %s -verify
+
+template<void (^B)()>
+struct A {
+ void call() { B(); }
+};
+
+constexpr void (^global_block)() = ^{};
+void test_global() {
+ A<global_block> a;
+ a.call();
+}
+
+void test_literal() {
+ A<^{}> a;
+ a.call();
+}
+
+void test_null() {
+ A<nullptr> a;
+}
+
+void test_capturing(int x) {
+ A<^{ (void)x; }> a; // expected-error {{non-type template argument is not a constant expression}}
+}
+
+template<void (^B)()>
+void deduce(A<B> a) {}
+
+void test_deduce() {
+ A<global_block> a;
+ deduce(a);
+}
+
+constexpr void (^another_block)() = ^{};
+static_assert(!__is_same(A<global_block>, A<another_block>));
+
+template<int (^B)(int)>
+struct BFunc {
+ int call(int x) { return B(x); }
+};
+
+void test_params() {
+ BFunc<^(int x) { return x + 1; }> b;
+ (void)b.call(1);
+}
+
+template<auto B>
+struct AutoBlock {};
+
+void test_auto() {
+ AutoBlock<global_block> a;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/191694
More information about the cfe-commits
mailing list