[clang] [Clang] Fix assertion failure when classifying a dependent call to a builtin (PR #210524)
Akshay K via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 19 07:23:39 PDT 2026
https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/210524
>From 8a893a6fd8083dea7ea57b8eabb3696477efd227 Mon Sep 17 00:00:00 2001
From: AkshayK <iit.akshay at gmail.com>
Date: Sat, 18 Jul 2026 10:14:42 -0400
Subject: [PATCH 1/2] [Clang] Fix crash when classifying a dependent call to a
builtin
A call to a builtin with dependent arguments takes the dependent path
in Sema::BuildCallExpr, so its callee is not rewritten with
CK_BuiltinFnToFnPtr until instantiation and keeps the BuiltinFn
placeholder type. CallExpr::getCallReturnType had no case for this
placeholder and fell through to castAs<FunctionType>, asserting on
valid code such as:
template <auto> struct S {};
template <typename T>
using Alias = S<__builtin_constant_p(T::x)>;
where the call is classified via Expr::ClassifyImpl while deducing the
auto non-type template parameter.
Return DependentTy for BuiltinFn callees, mirroring the existing
overload, bound-member, and record-callee cases.
---
clang/docs/ReleaseNotes.md | 3 ++
clang/lib/AST/Expr.cpp | 4 +-
clang/test/SemaCXX/builtin-dependent-call.cpp | 18 ++++++++
clang/unittests/Tooling/SourceCodeTest.cpp | 42 +++++++++++++++++++
4 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 clang/test/SemaCXX/builtin-dependent-call.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a39bc2dfc5b6d..8e910a697845c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -302,6 +302,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
#### Bug Fixes to Compiler Builtins
+- Fixed a crash when classifying a call to a builtin with dependent arguments,
+ such as when the call is used as an `auto` non-type template argument.
+
#### Bug Fixes to Attribute Support
- The `counted_by`/`counted_by_or_null` diagnostic that rejects a pointer whose
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 0bb9c6aa01c39..9a9a76e265f6a 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1628,7 +1628,9 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const {
// dependent call to the call operator of that type.
return Ctx.DependentTy;
} else if (CalleeType->isDependentType() ||
- CalleeType->isSpecificPlaceholderType(BuiltinType::Overload)) {
+ CalleeType->isSpecificPlaceholderType(BuiltinType::Overload) ||
+ CalleeType->isSpecificPlaceholderType(BuiltinType::BuiltinFn)) {
+ // Dependent builtin calls keep their placeholder until instantiation.
return Ctx.DependentTy;
}
diff --git a/clang/test/SemaCXX/builtin-dependent-call.cpp b/clang/test/SemaCXX/builtin-dependent-call.cpp
new file mode 100644
index 0000000000000..e84bb78cfea29
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-dependent-call.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// Dependent builtin calls used to assert during auto NTTP deduction.
+
+template <auto> struct S {};
+
+template <typename T> using Alias = S<__builtin_constant_p(T::x)>;
+template <typename T> using Alias2 = S<__builtin_ffs(T::x)>;
+
+struct HasX {
+ static constexpr int x = 42;
+};
+
+using Inst = Alias<HasX>;
+using Inst2 = Alias2<HasX>;
+
+using Control = S<__builtin_constant_p(3)>;
diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp b/clang/unittests/Tooling/SourceCodeTest.cpp
index d0eba43b0bb1c..db454945286ad 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -824,4 +824,46 @@ void f2() {
Visitor.runOver(Code.code(), CallsVisitor::Lang_CXX14);
}
+TEST(SourceCodeTest, GetCallReturnType_DependentBuiltinCall) {
+ // Dependent builtin calls keep their BuiltinFn placeholder until
+ // instantiation. The alias also covers the parse-time regression.
+ llvm::Annotations Code{R"cpp(
+template <auto> struct S {};
+
+template <typename T>
+using Alias = S<__builtin_constant_p(T::x)>;
+
+template <typename T>
+void templ(const T &t) {
+ $test1[[__builtin_constant_p(t.x)]];
+ $test2[[__builtin_ffs(t.x)]];
+}
+)cpp"};
+
+ llvm::Annotations::Range R1 = Code.range("test1");
+ llvm::Annotations::Range R2 = Code.range("test2");
+
+ bool SawBuiltinFnCallee = false;
+ CallsVisitor Visitor;
+ Visitor.OnCall = [&R1, &R2, &SawBuiltinFnCallee](CallExpr *Expr,
+ ASTContext *Context) {
+ unsigned Begin = Context->getSourceManager().getFileOffset(
+ Expr->getSourceRange().getBegin());
+ unsigned End = Context->getSourceManager().getFileOffset(
+ Expr->getSourceRange().getEnd());
+ llvm::Annotations::Range R{Begin, End + 1};
+
+ QualType CalleeType = Expr->getCallee()->getType();
+ if (R == R1 || R == R2) {
+ SawBuiltinFnCallee = true;
+ ASSERT_FALSE(CalleeType->isDependentType());
+ ASSERT_TRUE(
+ CalleeType->isSpecificPlaceholderType(BuiltinType::BuiltinFn));
+ EXPECT_EQ(Expr->getCallReturnType(*Context), Context->DependentTy);
+ }
+ };
+ Visitor.runOver(Code.code(), CallsVisitor::Lang_CXX17);
+ EXPECT_TRUE(SawBuiltinFnCallee);
+}
+
} // end anonymous namespace
>From 7d0ace21be5d9c471d9c2c9fb74f42a73153b2dc Mon Sep 17 00:00:00 2001
From: AkshayK <iit.akshay at gmail.com>
Date: Sat, 18 Jul 2026 15:45:05 -0400
Subject: [PATCH 2/2] [Clang] Pass -fno-delayed-template-parsing in
dependent-builtin-call unittest
On MSVC-compatible triples delayed template parsing is on by default, so
the uninstantiated body of the function template in
GetCallReturnType_DependentBuiltinCall is never parsed and the dependent
builtin calls never enter the AST, tripping the SawBuiltinFnCallee check
on Windows CI. Bypass TestVisitor::runOver and pass
-fno-delayed-template-parsing explicitly, following the existing pattern
in this file and in RecursiveASTVisitorTests/OffsetOfExpr.cpp.
---
clang/unittests/Tooling/SourceCodeTest.cpp | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp b/clang/unittests/Tooling/SourceCodeTest.cpp
index db454945286ad..33aeb7a132d55 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -45,6 +45,9 @@ struct IntLitVisitor : TestVisitor {
};
struct CallsVisitor : TestVisitor {
+ // Expose the protected helper so a test can supply extra compiler args.
+ using TestVisitor::CreateTestAction;
+
bool VisitCallExpr(CallExpr *Expr) override {
OnCall(Expr, Context);
return true;
@@ -862,7 +865,12 @@ void templ(const T &t) {
EXPECT_EQ(Expr->getCallReturnType(*Context), Context->DependentTy);
}
};
- Visitor.runOver(Code.code(), CallsVisitor::Lang_CXX17);
+ // Bypass TestVisitor::runOver so we can pass -fno-delayed-template-parsing.
+ // Otherwise on MSVC-compatible triples the uninstantiated body of templ is
+ // not parsed and the dependent builtin calls never enter the AST.
+ EXPECT_TRUE(tooling::runToolOnCodeWithArgs(
+ Visitor.CreateTestAction(), Code.code(),
+ {"-std=c++17", "-fno-delayed-template-parsing"}));
EXPECT_TRUE(SawBuiltinFnCallee);
}
More information about the cfe-commits
mailing list