[libc-commits] [clang] [libc] [libc] Fix modular printf attributes (PR #194003)
Volodymyr Turanskyy via libc-commits
libc-commits at lists.llvm.org
Thu May 7 03:48:59 PDT 2026
https://github.com/voltur01 updated https://github.com/llvm/llvm-project/pull/194003
>From e36c9d6b9c1f0df50a577442a1c7460327efd1e3 Mon Sep 17 00:00:00 2001
From: Volodymyr Turanskyy <volodymyr.turanskyy at arm.com>
Date: Fri, 24 Apr 2026 17:04:26 +0100
Subject: [PATCH 1/3] Fix modular printf attributes
This fixes declarations of modular printf attributes by adding the format
attribute required by clang and documented here
https://github.com/llvm/llvm-project/blob/deb84db5b4056eed1457a6b03148a486bbadd8ea/libc/docs/dev/modular_format.rst?plain=1#L26
---
libc/include/llvm-libc-macros/CMakeLists.txt | 2 +-
.../_LIBC_MODULAR_FORMAT_PRINTF-disable.h | 3 ++-
.../_LIBC_MODULAR_FORMAT_PRINTF.h | 4 +++-
libc/include/stdio.yaml | 16 ++++++++--------
4 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index 1f34257c57e01..a923b979e71c9 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -400,7 +400,7 @@ add_macro_header(
sysexits-macros.h
)
-if (LIBC_CONF_MODULAR_FORMAT)
+if (LIBC_CONF_PRINTF_MODULAR)
add_macro_header(
_LIBC_MODULAR_FORMAT_PRINTF
HDR
diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
index e3238161b3808..ecec03c2142ce 100644
--- a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
+++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
@@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
-#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN)
+#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN, FORMAT_INDEX, \
+ FIRST_TO_CHECK)
#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
index 918241ab8f2ec..e7349afe94371 100644
--- a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
+++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
@@ -9,7 +9,9 @@
#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
-#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN) \
+#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN, FORMAT_INDEX, \
+ FIRST_TO_CHECK) \
+ __attribute__((format(printf, FORMAT_INDEX, FIRST_TO_CHECK))) \
__attribute__((modular_format(MODULAR_IMPL_FN, "__printf", "float")))
#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml
index 24890b4b25670..ce7738e8e0648 100644
--- a/libc/include/stdio.yaml
+++ b/libc/include/stdio.yaml
@@ -51,7 +51,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__asprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__asprintf_modular, 2, 3)
- name: clearerr
standards:
- stdc
@@ -304,7 +304,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__printf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__printf_modular, 1, 2)
- name: putc
standards:
- stdc
@@ -377,7 +377,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__snprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__snprintf_modular, 3, 4)
- name: sprintf
standards:
- stdc
@@ -387,7 +387,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__sprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__sprintf_modular, 2, 3)
- name: sscanf
standards:
- stdc
@@ -412,7 +412,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vasprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vasprintf_modular, 2, 0)
- name: vfprintf
standards:
- stdc
@@ -429,7 +429,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vprintf_modular, 1, 0)
- name: vsnprintf
standards:
- stdc
@@ -440,7 +440,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vsnprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vsnprintf_modular, 3, 0)
- name: vsprintf
standards:
- stdc
@@ -450,7 +450,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vsprintf_modular)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vsprintf_modular, 2, 0)
- name: vsscanf
standards:
- stdc
>From 57d4eda3a867c060823c873b5b6d0e420dad06db Mon Sep 17 00:00:00 2001
From: Volodymyr Turanskyy <volodymyr.turanskyy at arm.com>
Date: Thu, 7 May 2026 11:44:40 +0100
Subject: [PATCH 2/3] Remove hardcoded format attributes
---
.../_LIBC_MODULAR_FORMAT_PRINTF-disable.h | 3 +--
.../_LIBC_MODULAR_FORMAT_PRINTF.h | 4 +---
libc/include/stdio.yaml | 16 ++++++++--------
3 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
index ecec03c2142ce..e3238161b3808 100644
--- a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
+++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
@@ -9,7 +9,6 @@
#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
-#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN, FORMAT_INDEX, \
- FIRST_TO_CHECK)
+#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN)
#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
index e7349afe94371..918241ab8f2ec 100644
--- a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
+++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
@@ -9,9 +9,7 @@
#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
-#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN, FORMAT_INDEX, \
- FIRST_TO_CHECK) \
- __attribute__((format(printf, FORMAT_INDEX, FIRST_TO_CHECK))) \
+#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN) \
__attribute__((modular_format(MODULAR_IMPL_FN, "__printf", "float")))
#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml
index ce7738e8e0648..24890b4b25670 100644
--- a/libc/include/stdio.yaml
+++ b/libc/include/stdio.yaml
@@ -51,7 +51,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__asprintf_modular, 2, 3)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__asprintf_modular)
- name: clearerr
standards:
- stdc
@@ -304,7 +304,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__printf_modular, 1, 2)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__printf_modular)
- name: putc
standards:
- stdc
@@ -377,7 +377,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__snprintf_modular, 3, 4)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__snprintf_modular)
- name: sprintf
standards:
- stdc
@@ -387,7 +387,7 @@ functions:
- type: const char *__restrict
- type: '...'
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__sprintf_modular, 2, 3)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__sprintf_modular)
- name: sscanf
standards:
- stdc
@@ -412,7 +412,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vasprintf_modular, 2, 0)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vasprintf_modular)
- name: vfprintf
standards:
- stdc
@@ -429,7 +429,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vprintf_modular, 1, 0)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vprintf_modular)
- name: vsnprintf
standards:
- stdc
@@ -440,7 +440,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vsnprintf_modular, 3, 0)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vsnprintf_modular)
- name: vsprintf
standards:
- stdc
@@ -450,7 +450,7 @@ functions:
- type: const char *__restrict
- type: va_list
attributes:
- - _LIBC_MODULAR_FORMAT_PRINTF(__vsprintf_modular, 2, 0)
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vsprintf_modular)
- name: vsscanf
standards:
- stdc
>From b07c293b86039f0afdc95034341ce6decf2d955d Mon Sep 17 00:00:00 2001
From: Volodymyr Turanskyy <volodymyr.turanskyy at arm.com>
Date: Thu, 7 May 2026 11:47:11 +0100
Subject: [PATCH 3/3] Make checkModularFormatAttr() aware of format attribute
for builtins
Make checkModularFormatAttr() check aware of the format attribute added automatically for printf family of functions.
Add a C++ test as the builtin handling behavior is different from C.
---
clang/lib/Sema/SemaDecl.cpp | 67 ++++++++++++++++++----
clang/test/SemaCXX/attr-modular-format.cpp | 18 ++++++
2 files changed, 74 insertions(+), 11 deletions(-)
create mode 100644 clang/test/SemaCXX/attr-modular-format.cpp
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 36538e18f297c..3ddb2de611f33 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7244,9 +7244,55 @@ static void checkLifetimeBoundAttr(Sema &S, NamedDecl &ND) {
}
}
+static bool isPrintfScanfLikeBuiltin(Sema &S, const FunctionDecl &FD) {
+ if (unsigned BuiltinID = FD.getBuiltinID()) {
+ unsigned FormatIdx;
+ bool HasVAListArg;
+
+ return S.Context.BuiltinInfo.isPrintfLike(BuiltinID, FormatIdx,
+ HasVAListArg) ||
+ S.Context.BuiltinInfo.isScanfLike(BuiltinID, FormatIdx,
+ HasVAListArg);
+ }
+
+ return false;
+}
+
+static bool isPrintfLikeFunction(Sema &S, const FunctionDecl &FD,
+ unsigned &FormatIdx, unsigned &FirstArg) {
+ IdentifierInfo *Name = FD.getIdentifier();
+ if (!Name)
+ return false;
+
+ if ((!S.getLangOpts().CPlusPlus &&
+ FD.getDeclContext()->isTranslationUnit()) ||
+ (isa<LinkageSpecDecl>(FD.getDeclContext()) &&
+ cast<LinkageSpecDecl>(FD.getDeclContext())->getLanguage() ==
+ LinkageSpecLanguageIDs::C)) {
+ if (Name->isStr("asprintf") || Name->isStr("vasprintf")) {
+ FormatIdx = 2;
+ FirstArg = Name->isStr("vasprintf") ? 0 : 3;
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void checkModularFormatAttr(Sema &S, NamedDecl &ND) {
- if (ND.hasAttr<ModularFormatAttr>() && !ND.hasAttr<FormatAttr>())
- S.Diag(ND.getLocation(), diag::err_modular_format_attribute_no_format);
+ if (!ND.hasAttr<ModularFormatAttr>())
+ return;
+
+ if (isa<FunctionDecl>(ND)) {
+ FunctionDecl *FD = cast<FunctionDecl>(&ND);
+ unsigned FormatIdx, FirstArg;
+
+ if (FD->hasAttr<FormatAttr>() || isPrintfScanfLikeBuiltin(S, *FD) ||
+ isPrintfLikeFunction(S, *FD, FormatIdx, FirstArg))
+ return;
+ }
+
+ S.Diag(ND.getLocation(), diag::err_modular_format_attribute_no_format);
}
static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
@@ -17556,15 +17602,14 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
} else
return;
- if (Name->isStr("asprintf") || Name->isStr("vasprintf")) {
- // FIXME: asprintf and vasprintf aren't C99 functions. Should they be
- // target-specific builtins, perhaps?
- if (!FD->hasAttr<FormatAttr>())
- FD->addAttr(FormatAttr::CreateImplicit(Context,
- &Context.Idents.get("printf"), 2,
- Name->isStr("vasprintf") ? 0 : 3,
- FD->getLocation()));
- }
+ // FIXME: asprintf and vasprintf aren't C99 functions. Should they be
+ // target-specific builtins, perhaps?
+ unsigned FormatIdx, FirstArg;
+ if (!FD->hasAttr<FormatAttr>() &&
+ isPrintfLikeFunction(*this, *FD, FormatIdx, FirstArg))
+ FD->addAttr(
+ FormatAttr::CreateImplicit(Context, &Context.Idents.get("printf"),
+ FormatIdx, FirstArg, FD->getLocation()));
if (Name->isStr("__CFStringMakeConstantString")) {
// We already have a __builtin___CFStringMakeConstantString,
diff --git a/clang/test/SemaCXX/attr-modular-format.cpp b/clang/test/SemaCXX/attr-modular-format.cpp
new file mode 100644
index 0000000000000..9427d7ef79eb8
--- /dev/null
+++ b/clang/test/SemaCXX/attr-modular-format.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s
+
+extern "C" int printf(const char *fmt, ...)
+ __attribute__((modular_format(__modular_printf, "__printf", "float")));
+extern "C" int asprintf(char **buf, const char *fmt, ...)
+ __attribute__((modular_format(__asprintf_modular, "__printf", "float")));
+extern "C" int vasprintf(char **buf, const char *fmt, __builtin_va_list ap)
+ __attribute__((modular_format(__vasprintf_modular, "__printf", "float")));
+
+int myprintf(const char *fmt, ...) __attribute__((
+ modular_format(__modular_printf, "__printf", "float")));
+// expected-error at -2 {{'modular_format' attribute requires 'format' attribute}}
+
+namespace ns {
+int asprintf(char **buf, const char *fmt, ...)
+ __attribute__((modular_format(__asprintf_modular, "__printf", "float")));
+// expected-error at -2 {{'modular_format' attribute requires 'format' attribute}}
+} // namespace ns
More information about the libc-commits
mailing list