[clang] [llvm] [Clang] Fix assertion failure when passing wide string literal to __builtin_nanf (PR #205452)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 30 10:53:27 PDT 2026
https://github.com/divyansh-1009 updated https://github.com/llvm/llvm-project/pull/205452
>From 8ddb4b1a09af7c11b744bc18d3ea8a2ba4a6fb84 Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Wed, 24 Jun 2026 04:28:01 +0530
Subject: [PATCH 1/5] [Clang] Fix assertion failure when passing wide string
literal to __builtin_nanf
Fixes #205306.
---
clang/lib/AST/ExprConstant.cpp | 2 ++
clang/test/Sema/builtin-nan-wide-string.c | 3 +++
2 files changed, 5 insertions(+)
create mode 100644 clang/test/Sema/builtin-nan-wide-string.c
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 563d6b3bb0cf9..26dc1e84babc3 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20007,6 +20007,8 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context,
const StringLiteral *S = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
if (!S) return false;
+ if (!S->isOrdinary() && !S->isUTF8()) return false;
+
const llvm::fltSemantics &Sem = Context.getFloatTypeSemantics(ResultTy);
llvm::APInt fill;
diff --git a/clang/test/Sema/builtin-nan-wide-string.c b/clang/test/Sema/builtin-nan-wide-string.c
new file mode 100644
index 0000000000000..52e1b4a17db94
--- /dev/null
+++ b/clang/test/Sema/builtin-nan-wide-string.c
@@ -0,0 +1,3 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+char hello = __builtin_nanf(L""); // expected-error {{incompatible pointer types passing 'int[1]' to parameter of type 'const char *'}}
>From 20ae46b3a50a5d556dd41cf8344683dbc99c4a28 Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Wed, 24 Jun 2026 21:53:06 +0530
Subject: [PATCH 2/5] update release notes, add comprehensive test coverages,
restrict support to ordinary string literals, add run directives for C and
C++
---
clang/docs/ReleaseNotes.md | 1 +
clang/lib/AST/ExprConstant.cpp | 2 +-
clang/test/Sema/builtin-nan-wide-string.c | 47 ++++++++++++++++++++++-
clang/test/Sema/constant-builtins-2.c | 4 ++
test_nan_u8.c | 1 +
test_nan_u8.cpp | 1 +
test_nan_wide.c | 1 +
test_nan_wide2.c | 1 +
8 files changed, 55 insertions(+), 3 deletions(-)
create mode 100644 test_nan_u8.c
create mode 100644 test_nan_u8.cpp
create mode 100644 test_nan_wide.c
create mode 100644 test_nan_wide2.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 31e7979612a76..d566c5f5b135a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -734,6 +734,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
crash when using it with `-fms-extensions` on other platforms. (#GH184318)
- Fixed a compiler crash due to an unresolved overloaded function type when
calling `__builtin_bit_cast`. (#GH200112)
+- Fixed an assertion failure when passing a wide string literal to `__builtin_nan` and related builtins. (#GH205306)
- Clang now SFINAE friendly when the ``__reference_meows_from_temporary`` builtins
should SFINAE friendly when the 1st type is not a reference type. (#GH206524)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 26dc1e84babc3..afc2f2d2dd8de 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20007,7 +20007,7 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context,
const StringLiteral *S = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
if (!S) return false;
- if (!S->isOrdinary() && !S->isUTF8()) return false;
+ if (!S->isOrdinary()) return false;
const llvm::fltSemantics &Sem = Context.getFloatTypeSemantics(ResultTy);
diff --git a/clang/test/Sema/builtin-nan-wide-string.c b/clang/test/Sema/builtin-nan-wide-string.c
index 52e1b4a17db94..d97af1c7ad898 100644
--- a/clang/test/Sema/builtin-nan-wide-string.c
+++ b/clang/test/Sema/builtin-nan-wide-string.c
@@ -1,3 +1,46 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify=c -x c %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
+// RUN: %clang_cc1 -fsyntax-only -verify=c -x c -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ -fexperimental-new-constant-interpreter %s
-char hello = __builtin_nanf(L""); // expected-error {{incompatible pointer types passing 'int[1]' to parameter of type 'const char *'}}
+#ifdef __cplusplus
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+CONSTEXPR float f1 = __builtin_nanf(L"");
+// c-warning at -1 {{incompatible pointer types passing 'int[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const wchar_t[1]'}}
+
+CONSTEXPR double d1 = __builtin_nan(L"");
+// c-warning at -1 {{incompatible pointer types passing 'int[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const wchar_t[1]'}}
+
+CONSTEXPR long double ld1 = __builtin_nanl(L"");
+// c-warning at -1 {{incompatible pointer types passing 'int[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const wchar_t[1]'}}
+
+CONSTEXPR float f2 = __builtin_nanf(u"");
+// c-warning at -1 {{incompatible pointer types passing 'unsigned short[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char16_t[1]'}}
+
+CONSTEXPR double d2 = __builtin_nan(u"");
+// c-warning at -1 {{incompatible pointer types passing 'unsigned short[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char16_t[1]'}}
+
+CONSTEXPR long double ld2 = __builtin_nanl(u"");
+// c-warning at -1 {{incompatible pointer types passing 'unsigned short[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char16_t[1]'}}
+
+CONSTEXPR float f3 = __builtin_nanf(U"");
+// c-warning at -1 {{incompatible pointer types passing 'unsigned int[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char32_t[1]'}}
+
+CONSTEXPR double d3 = __builtin_nan(U"");
+// c-warning at -1 {{incompatible pointer types passing 'unsigned int[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char32_t[1]'}}
+
+CONSTEXPR long double ld3 = __builtin_nanl(U"");
+// c-warning at -1 {{incompatible pointer types passing 'unsigned int[1]' to parameter of type 'const char *'}}
+// cxx-error at -2 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char32_t[1]'}}
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index fd3643bbdb7c8..4890ff0ecc1cc 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -19,6 +19,10 @@ __float128 g5_2 = __builtin_inff128();
double g6 = __builtin_nan("");
float g7 = __builtin_nanf("");
long double g8 = __builtin_nanl("");
+
+double g6_u8 = __builtin_nan(u8"");
+float g7_u8 = __builtin_nanf(u8"");
+long double g8_u8 = __builtin_nanl(u8"");
#if defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__)
__float128 g8_2 = __builtin_nanf128("");
#endif
diff --git a/test_nan_u8.c b/test_nan_u8.c
new file mode 100644
index 0000000000000..6859499504725
--- /dev/null
+++ b/test_nan_u8.c
@@ -0,0 +1 @@
+float g8 = __builtin_nanf(u8"");
diff --git a/test_nan_u8.cpp b/test_nan_u8.cpp
new file mode 100644
index 0000000000000..1016b5b948ef3
--- /dev/null
+++ b/test_nan_u8.cpp
@@ -0,0 +1 @@
+constexpr float g8 = __builtin_nanf(u8"");
diff --git a/test_nan_wide.c b/test_nan_wide.c
new file mode 100644
index 0000000000000..6c8abecf78d31
--- /dev/null
+++ b/test_nan_wide.c
@@ -0,0 +1 @@
+float g8 = __builtin_nanf(L"");
diff --git a/test_nan_wide2.c b/test_nan_wide2.c
new file mode 100644
index 0000000000000..657ef0ac7ad19
--- /dev/null
+++ b/test_nan_wide2.c
@@ -0,0 +1 @@
+char hello = __builtin_nanf(L"");
>From f8d13ab6808ef94b77d5453a6fd2c0cf4983f274 Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Thu, 25 Jun 2026 00:02:05 +0530
Subject: [PATCH 3/5] fix: validate that arguments to __builtin_nan functions
are ordinary string literals
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 ++
clang/lib/Sema/SemaChecking.cpp | 23 +++++++++++++++++++
test_cxx17_u8.cpp | 1 +
test_nan_all.c | 5 ++++
test_nan_u8_c.c | 1 +
5 files changed, 32 insertions(+)
create mode 100644 test_cxx17_u8.cpp
create mode 100644 test_nan_all.c
create mode 100644 test_nan_u8_c.c
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7e4a9338d5b1f..bd69f08350cc0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -172,6 +172,8 @@ def err_ice_too_large : Error<
"integer constant expression evaluates to value %0 that cannot be "
"represented in a %1-bit %select{signed|unsigned}2 integer type">;
def err_expr_not_string_literal : Error<"expression is not a string literal">;
+def err_expected_ordinary_string_literal : Error<
+ "argument to %0 must be an ordinary string literal">;
def note_constexpr_assert_failed : Note<
"assertion failed during evaluation of constant expression">;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 01b1f4c26f017..b0dadc83d3e93 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3037,6 +3037,29 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
FPOptions FPO;
switch (BuiltinID) {
+ case Builtin::BI__builtin_nan:
+ case Builtin::BI__builtin_nanf:
+ case Builtin::BI__builtin_nanl:
+ case Builtin::BI__builtin_nanf16:
+ case Builtin::BI__builtin_nanf128:
+ case Builtin::BI__builtin_nans:
+ case Builtin::BI__builtin_nansf:
+ case Builtin::BI__builtin_nansl:
+ case Builtin::BI__builtin_nansf16:
+ case Builtin::BI__builtin_nansf128: {
+ if (TheCall->getNumArgs() > 0) {
+ Expr *Arg = TheCall->getArg(0)->IgnoreParenCasts();
+ if (StringLiteral *S = dyn_cast<StringLiteral>(Arg)) {
+ if (!S->isOrdinary()) {
+ Diag(Arg->getBeginLoc(), diag::err_expected_ordinary_string_literal)
+ << Context.BuiltinInfo.getQuotedName(BuiltinID)
+ << Arg->getSourceRange();
+ return ExprError();
+ }
+ }
+ }
+ break;
+ }
case Builtin::BI__builtin___get_unsafe_stack_start:
case Builtin::BI__builtin___get_unsafe_stack_bottom:
Diag(TheCall->getBeginLoc(), diag::warn_deprecated_builtin)
diff --git a/test_cxx17_u8.cpp b/test_cxx17_u8.cpp
new file mode 100644
index 0000000000000..1016b5b948ef3
--- /dev/null
+++ b/test_cxx17_u8.cpp
@@ -0,0 +1 @@
+constexpr float g8 = __builtin_nanf(u8"");
diff --git a/test_nan_all.c b/test_nan_all.c
new file mode 100644
index 0000000000000..3c9dddda8597a
--- /dev/null
+++ b/test_nan_all.c
@@ -0,0 +1,5 @@
+float f1 = __builtin_nanf(u8"");
+float f2 = __builtin_nanf(L"");
+float f3 = __builtin_nanf(u"");
+float f4 = __builtin_nanf(U"");
+float f5 = __builtin_nanf("");
diff --git a/test_nan_u8_c.c b/test_nan_u8_c.c
new file mode 100644
index 0000000000000..6859499504725
--- /dev/null
+++ b/test_nan_u8_c.c
@@ -0,0 +1 @@
+float g8 = __builtin_nanf(u8"");
>From 7906d2452adfced45574cf9ab264daeded86f557 Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Fri, 26 Jun 2026 00:16:14 +0530
Subject: [PATCH 4/5] Revert changes related to disallowing u8 string literals
This focuses the PR exclusively on fixing the failed assertion for wide strings,
keeping the changes for u8 literals for a separate PR. Also updates the new
constant evaluator to properly handle unsigned 8-bit character arrays.
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 --
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 12 ++++++++--
clang/lib/AST/ExprConstant.cpp | 2 +-
clang/lib/Sema/SemaChecking.cpp | 23 -------------------
test_cxx17_u8.cpp | 1 -
test_nan_all.c | 5 ----
test_nan_u8_c.c | 1 -
7 files changed, 11 insertions(+), 35 deletions(-)
delete mode 100644 test_cxx17_u8.cpp
delete mode 100644 test_nan_all.c
delete mode 100644 test_nan_u8_c.c
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bd69f08350cc0..7e4a9338d5b1f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -172,8 +172,6 @@ def err_ice_too_large : Error<
"integer constant expression evaluates to value %0 that cannot be "
"represented in a %1-bit %select{signed|unsigned}2 integer type">;
def err_expr_not_string_literal : Error<"expression is not a string literal">;
-def err_expected_ordinary_string_literal : Error<
- "argument to %0 must be an ordinary string literal">;
def note_constexpr_assert_failed : Note<
"assertion failed during evaluation of constant expression">;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 73952e032f1eb..48275680466b4 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -441,11 +441,19 @@ static bool interp__builtin_nan(InterpState &S, CodePtr OpPC,
if (!Arg.isElementInitialized(I))
return false;
- if (Arg.elem<int8_t>(I) == 0) {
+ char C;
+ if (Arg.getFieldDesc()->getPrimType() == PT_Sint8)
+ C = Arg.elem<int8_t>(I);
+ else if (Arg.getFieldDesc()->getPrimType() == PT_Uint8)
+ C = Arg.elem<uint8_t>(I);
+ else
+ return false;
+
+ if (C == 0) {
FoundZero = true;
break;
}
- Str += Arg.elem<char>(I);
+ Str += C;
}
// If we didn't find a NUL byte, diagnose as a one-past-the-end read.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index afc2f2d2dd8de..26dc1e84babc3 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20007,7 +20007,7 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context,
const StringLiteral *S = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
if (!S) return false;
- if (!S->isOrdinary()) return false;
+ if (!S->isOrdinary() && !S->isUTF8()) return false;
const llvm::fltSemantics &Sem = Context.getFloatTypeSemantics(ResultTy);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b0dadc83d3e93..01b1f4c26f017 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3037,29 +3037,6 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
FPOptions FPO;
switch (BuiltinID) {
- case Builtin::BI__builtin_nan:
- case Builtin::BI__builtin_nanf:
- case Builtin::BI__builtin_nanl:
- case Builtin::BI__builtin_nanf16:
- case Builtin::BI__builtin_nanf128:
- case Builtin::BI__builtin_nans:
- case Builtin::BI__builtin_nansf:
- case Builtin::BI__builtin_nansl:
- case Builtin::BI__builtin_nansf16:
- case Builtin::BI__builtin_nansf128: {
- if (TheCall->getNumArgs() > 0) {
- Expr *Arg = TheCall->getArg(0)->IgnoreParenCasts();
- if (StringLiteral *S = dyn_cast<StringLiteral>(Arg)) {
- if (!S->isOrdinary()) {
- Diag(Arg->getBeginLoc(), diag::err_expected_ordinary_string_literal)
- << Context.BuiltinInfo.getQuotedName(BuiltinID)
- << Arg->getSourceRange();
- return ExprError();
- }
- }
- }
- break;
- }
case Builtin::BI__builtin___get_unsafe_stack_start:
case Builtin::BI__builtin___get_unsafe_stack_bottom:
Diag(TheCall->getBeginLoc(), diag::warn_deprecated_builtin)
diff --git a/test_cxx17_u8.cpp b/test_cxx17_u8.cpp
deleted file mode 100644
index 1016b5b948ef3..0000000000000
--- a/test_cxx17_u8.cpp
+++ /dev/null
@@ -1 +0,0 @@
-constexpr float g8 = __builtin_nanf(u8"");
diff --git a/test_nan_all.c b/test_nan_all.c
deleted file mode 100644
index 3c9dddda8597a..0000000000000
--- a/test_nan_all.c
+++ /dev/null
@@ -1,5 +0,0 @@
-float f1 = __builtin_nanf(u8"");
-float f2 = __builtin_nanf(L"");
-float f3 = __builtin_nanf(u"");
-float f4 = __builtin_nanf(U"");
-float f5 = __builtin_nanf("");
diff --git a/test_nan_u8_c.c b/test_nan_u8_c.c
deleted file mode 100644
index 6859499504725..0000000000000
--- a/test_nan_u8_c.c
+++ /dev/null
@@ -1 +0,0 @@
-float g8 = __builtin_nanf(u8"");
>From a48f4d6305c3749b34aaf65b51429d3c0c90b0c1 Mon Sep 17 00:00:00 2001
From: Divyansh <anshmcs at gmail.com>
Date: Tue, 30 Jun 2026 23:21:15 +0530
Subject: [PATCH 5/5] [Clang] Add explicit target triple to
builtin-nan-wide-string.c to fix Windows CI
---
clang/test/Sema/builtin-nan-wide-string.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/Sema/builtin-nan-wide-string.c b/clang/test/Sema/builtin-nan-wide-string.c
index d97af1c7ad898..c3ec698462872 100644
--- a/clang/test/Sema/builtin-nan-wide-string.c
+++ b/clang/test/Sema/builtin-nan-wide-string.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify=c -x c %s
-// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
-// RUN: %clang_cc1 -fsyntax-only -verify=c -x c -fexperimental-new-constant-interpreter %s
-// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=c -x c %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=cxx -x c++ %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=c -x c -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify=cxx -x c++ -fexperimental-new-constant-interpreter %s
#ifdef __cplusplus
#define CONSTEXPR constexpr
More information about the cfe-commits
mailing list