https://github.com/mysterymath updated https://github.com/llvm/llvm-project/pull/147431
>From 6101f2a38371cb6f9dd8c4c0e58447e787182fff Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 10 Jun 2025 14:06:53 -0700 Subject: [PATCH 01/22] [clang] "modular_format" attribute for functions using format strings This provides a C language version of the new IR modular-format attribute. This, in concert with the format attribute, allows a library function to declare that a modular version of its implementation is available. See issue #146159 for context. --- clang/include/clang/Basic/Attr.td | 11 +++++++++++ clang/include/clang/Basic/AttrDocs.td | 25 +++++++++++++++++++++++++ clang/lib/CodeGen/CGCall.cpp | 12 ++++++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8e5f7ef0bb82d..e40e46f2846c8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5323,3 +5323,14 @@ def NonString : InheritableAttr { let Subjects = SubjectList<[Var, Field]>; let Documentation = [NonStringDocs]; } + +def ModularFormat : InheritableAttr { + let Spellings = [Clang<"modular_format">]; + let Args = [ + IdentifierArgument<"ModularImplFn">, + StringArgument<"ImplName">, + VariadicStringArgument<"Aspects"> + ]; + let Subjects = SubjectList<[Function]>; + let Documentation = [ModularFormatDocs]; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index c1b1510f363d4..f24674d7355ac 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9630,3 +9630,28 @@ silence diagnostics with code like: __attribute__((nonstring)) char NotAStr[3] = "foo"; // Not diagnosed }]; } + +def ModularFormatDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``modular_format`` attribute can be applied to a function that bears the +``format`` attribute to indicate that the implementation is modular on the +format string argument. When the format argument for a given call is constant, +the compiler may redirect the call to the symbol given as the first argument to +the attribute (the modular implementation function). + +The second argument is a implementation name, and the remaining arguments are +aspects of the format string for the compiler to report. If the compiler does +not understand a aspect, it must summarily report that the format string has +that aspect. + +The compiler reports an aspect by issing a relocation for the symbol +`<impl_name>_<aspect>``. This arranges for code and data needed to support the +aspect of the implementation to be brought into the link to satisfy weak +references in the modular implemenation function. + +The following aspects are currently supported: + +- ``float``: The call has a floating point argument + }]; +} diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index efacb3cc04c01..0a3ef36acbbc2 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2559,6 +2559,18 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, if (TargetDecl->hasAttr<ArmLocallyStreamingAttr>()) FuncAttrs.addAttribute("aarch64_pstate_sm_body"); + + if (auto *ModularFormat = TargetDecl->getAttr<ModularFormatAttr>()) { + // TODO: Error checking + FormatAttr *Format = TargetDecl->getAttr<FormatAttr>(); + std::string FormatIdx = std::to_string(Format->getFormatIdx()); + std::string FirstArg = std::to_string(Format->getFirstArg()); + SmallVector<StringRef> Args = { + FormatIdx, FirstArg, ModularFormat->getModularImplFn()->getName(), + ModularFormat->getImplName()}; + llvm::append_range(Args, ModularFormat->aspects()); + FuncAttrs.addAttribute("modular-format", llvm::join(Args, ",")); + } } // Attach "no-builtins" attributes to: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index e3af5023c74d0..485b6876aa95f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6973,6 +6973,29 @@ static void handleVTablePointerAuthentication(Sema &S, Decl *D, CustomDiscriminationValue)); } +static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef ImplName; + if (!S.checkStringLiteralArgumentAttr(AL, 1, ImplName)) + return; + SmallVector<StringRef> Aspects; + for (unsigned I = 2, E = AL.getNumArgs(); I != E; ++I) { + StringRef Aspect; + if (!S.checkStringLiteralArgumentAttr(AL, I, Aspect)) + return; + Aspects.push_back(Aspect); + } + + // Store aspects sorted and without duplicates. + llvm::sort(Aspects); + Aspects.erase(llvm::unique(Aspects), Aspects.end()); + + // TODO: Type checking on identifier + // TODO: Merge attributes + D->addAttr(::new (S.Context) ModularFormatAttr( + S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName, + Aspects.data(), Aspects.size())); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -7910,6 +7933,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_VTablePointerAuthentication: handleVTablePointerAuthentication(S, D, AL); break; + + case ParsedAttr::AT_ModularFormat: + handleModularFormat(S, D, AL); + break; } } >From 906b0b4c0b74d52fe120e0823354cb1d3cadb683 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 15 Jul 2025 11:28:20 -0700 Subject: [PATCH 02/22] Update docs to account for clang inferring format attribute --- clang/include/clang/Basic/AttrDocs.td | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index f24674d7355ac..31db4484c69d5 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9635,10 +9635,11 @@ def ModularFormatDocs : Documentation { let Category = DocCatFunction; let Content = [{ The ``modular_format`` attribute can be applied to a function that bears the -``format`` attribute to indicate that the implementation is modular on the -format string argument. When the format argument for a given call is constant, -the compiler may redirect the call to the symbol given as the first argument to -the attribute (the modular implementation function). +``format`` attribute (or standard library functions) to indicate that the +implementation is modular on the format string argument. When the format string +for a given call is constant, the compiler may redirect the call to the symbol +given as the first argument to the attribute (the modular implementation +function). The second argument is a implementation name, and the remaining arguments are aspects of the format string for the compiler to report. If the compiler does >From 4aaaa778fd8a159d95493ed253914aff7ef20ce4 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Wed, 16 Jul 2025 15:19:37 -0700 Subject: [PATCH 03/22] Add an example to clang attr doc --- clang/include/clang/Basic/AttrDocs.td | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 31db4484c69d5..1f25c90eb13d4 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9647,10 +9647,18 @@ not understand a aspect, it must summarily report that the format string has that aspect. The compiler reports an aspect by issing a relocation for the symbol -`<impl_name>_<aspect>``. This arranges for code and data needed to support the +``<impl_name>_<aspect>``. This arranges for code and data needed to support the aspect of the implementation to be brought into the link to satisfy weak references in the modular implemenation function. +For example, say ``printf`` is annotated with +``modular_format(__modular_printf, __printf, float)``. Then, a call to +``printf(var, 42)`` would be untouched. A call to ``printf("%d", 42)`` would +become a call to ``__modular_printf`` with the same arguments, as would +``printf("%f", 42.0)``. The latter would be accompanied with a strong +relocation against the symbol ``__printf_float``, which would bring floating +point support for ``printf`` into the link. + The following aspects are currently supported: - ``float``: The call has a floating point argument >From 5ad6e9acb7a51f5c6ebac4189640d4edf343f14d Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 22 Jul 2025 13:35:46 -0700 Subject: [PATCH 04/22] Emit the new type arg from format attr --- clang/lib/CodeGen/CGCall.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 0a3ef36acbbc2..3b85a9db15bcf 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2563,10 +2563,12 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, if (auto *ModularFormat = TargetDecl->getAttr<ModularFormatAttr>()) { // TODO: Error checking FormatAttr *Format = TargetDecl->getAttr<FormatAttr>(); + StringRef Type = Format->getType()->getName(); std::string FormatIdx = std::to_string(Format->getFormatIdx()); std::string FirstArg = std::to_string(Format->getFirstArg()); SmallVector<StringRef> Args = { - FormatIdx, FirstArg, ModularFormat->getModularImplFn()->getName(), + Type, FormatIdx, FirstArg, + ModularFormat->getModularImplFn()->getName(), ModularFormat->getImplName()}; llvm::append_range(Args, ModularFormat->aspects()); FuncAttrs.addAttribute("modular-format", llvm::join(Args, ",")); >From 8d9246f594899c7f04bc18674e7b7c8b444987e7 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 22 Jul 2025 15:01:56 -0700 Subject: [PATCH 05/22] Correct typos --- clang/include/clang/Basic/AttrDocs.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 1f25c90eb13d4..7e8cd3d93772e 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9643,10 +9643,10 @@ function). The second argument is a implementation name, and the remaining arguments are aspects of the format string for the compiler to report. If the compiler does -not understand a aspect, it must summarily report that the format string has +not understand an aspect, it must summarily report that the format string has that aspect. -The compiler reports an aspect by issing a relocation for the symbol +The compiler reports an aspect by issuing a relocation for the symbol ``<impl_name>_<aspect>``. This arranges for code and data needed to support the aspect of the implementation to be brought into the link to satisfy weak references in the modular implemenation function. >From 4f1d07c3330f89c7e042a8f1cab77c13bc30d091 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 17 Jul 2025 15:56:10 -0700 Subject: [PATCH 06/22] Tests for successful format string passthrough --- clang/test/CodeGen/attr-modular-format.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 clang/test/CodeGen/attr-modular-format.c diff --git a/clang/test/CodeGen/attr-modular-format.c b/clang/test/CodeGen/attr-modular-format.c new file mode 100644 index 0000000000000..7d0580def41e9 --- /dev/null +++ b/clang/test/CodeGen/attr-modular-format.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s + +int printf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); +int myprintf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2))); + +// CHECK-LABEL: define dso_local void @test_inferred_format( +// CHECK: {{.*}} = call i32 (ptr, ...) @printf(ptr noundef @.str) #[[ATTR:[0-9]+]] +void test_inferred_format(void) { + printf("hello"); +} + +// CHECK-LABEL: define dso_local void @test_explicit_format( +// CHECK: {{.*}} = call i32 (ptr, ...) @myprintf(ptr noundef @.str) #[[ATTR:[0-9]+]] +void test_explicit_format(void) { + myprintf("hello"); +} + +// CHECK: attributes #[[ATTR]] = { "modular-format"="printf,1,2,__modular_printf,__printf,float" } >From ad4e4258818db3c1c48ff0e197a6bab6d31e2bcf Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Fri, 5 Sep 2025 17:10:37 -0700 Subject: [PATCH 07/22] Add redeclaration test --- clang/lib/Sema/SemaDeclAttr.cpp | 1 - clang/test/CodeGen/attr-modular-format.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 485b6876aa95f..b897ed807e48c 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6990,7 +6990,6 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { Aspects.erase(llvm::unique(Aspects), Aspects.end()); // TODO: Type checking on identifier - // TODO: Merge attributes D->addAttr(::new (S.Context) ModularFormatAttr( S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName, Aspects.data(), Aspects.size())); diff --git a/clang/test/CodeGen/attr-modular-format.c b/clang/test/CodeGen/attr-modular-format.c index 7d0580def41e9..2c647214b3bca 100644 --- a/clang/test/CodeGen/attr-modular-format.c +++ b/clang/test/CodeGen/attr-modular-format.c @@ -15,4 +15,14 @@ void test_explicit_format(void) { myprintf("hello"); } +int redecl(const char *fmt, ...) __attribute__((modular_format(__first_impl, "__first", "one"), format(printf, 1, 2))); +int redecl(const char *fmt, ...) __attribute__((modular_format(__second_impl, "__second", "two", "three"))); + +// CHECK-LABEL: define dso_local void @test_redecl( +// CHECK: {{.*}} = call i32 (ptr, ...) @redecl(ptr noundef @.str) #[[ATTR_REDECL:[0-9]+]] +void test_redecl(void) { + redecl("hello"); +} + // CHECK: attributes #[[ATTR]] = { "modular-format"="printf,1,2,__modular_printf,__printf,float" } +// CHECK: attributes #[[ATTR_REDECL]] = { "modular-format"="printf,1,2,__second_impl,__second,three,two" } >From ad78cf89fc6d1ef25e0379b32747581c1da00f70 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 3 Nov 2025 16:48:55 -0800 Subject: [PATCH 08/22] Clarify and correct docs --- clang/include/clang/Basic/AttrDocs.td | 23 ++++++++++++----------- clang/lib/Sema/SemaDeclAttr.cpp | 1 - 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 7e8cd3d93772e..db231d6b9ae3b 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9636,23 +9636,24 @@ def ModularFormatDocs : Documentation { let Content = [{ The ``modular_format`` attribute can be applied to a function that bears the ``format`` attribute (or standard library functions) to indicate that the -implementation is modular on the format string argument. When the format string -for a given call is constant, the compiler may redirect the call to the symbol -given as the first argument to the attribute (the modular implementation -function). +implementation is "modular", that is, that the implemenation is logically +divided into a number of named aspects. When the compiler can determine that +not all aspects of the implementation are needed for a given call, the compiler +may redirect the call to the identifier given as the first argument to the +attribute (the modular implementation function). The second argument is a implementation name, and the remaining arguments are aspects of the format string for the compiler to report. If the compiler does -not understand an aspect, it must summarily report that the format string has -that aspect. +not understand an aspect, it must summarily consider any call to require that +aspect. -The compiler reports an aspect by issuing a relocation for the symbol -``<impl_name>_<aspect>``. This arranges for code and data needed to support the -aspect of the implementation to be brought into the link to satisfy weak -references in the modular implemenation function. +The compiler reports that a call requires an aspect by issuing a relocation for +the symbol ``<impl_name>_<aspect>`` at the point of the call. This arranges for +code and data needed to support the aspect of the implementation to be brought +into the link to satisfy weak references in the modular implemenation function. For example, say ``printf`` is annotated with -``modular_format(__modular_printf, __printf, float)``. Then, a call to +``modular_format(__modular_printf, "__printf", "float")``. Then, a call to ``printf(var, 42)`` would be untouched. A call to ``printf("%d", 42)`` would become a call to ``__modular_printf`` with the same arguments, as would ``printf("%f", 42.0)``. The latter would be accompanied with a strong diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index b897ed807e48c..0c93106596c46 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6989,7 +6989,6 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { llvm::sort(Aspects); Aspects.erase(llvm::unique(Aspects), Aspects.end()); - // TODO: Type checking on identifier D->addAttr(::new (S.Context) ModularFormatAttr( S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName, Aspects.data(), Aspects.size())); >From 0afe987f9c9a427398eb8f4528c06143ccbd8653 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 4 Nov 2025 13:17:47 -0800 Subject: [PATCH 09/22] Emit error for missing format attribute --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ clang/lib/Sema/SemaDecl.cpp | 6 ++++++ clang/test/Sema/attr-modular-format.c | 7 +++++++ 3 files changed, 16 insertions(+) create mode 100644 clang/test/Sema/attr-modular-format.c diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 53aa86a7dabde..b8386eda89cb3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13063,6 +13063,9 @@ def err_get_vtable_pointer_requires_complete_type : Error<"__builtin_get_vtable_pointer requires an argument with a complete " "type, but %0 is incomplete">; +def err_modular_format_attribute_no_format : Error< + "'modular_format' attribute requires 'format' attribute">; + // SYCL-specific diagnostics def warn_sycl_kernel_num_of_template_params : Warning< "'sycl_kernel' attribute only applies to a function template with at least" diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 651437a6f4c30..89485e2850381 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7217,6 +7217,11 @@ static void checkLifetimeBoundAttr(Sema &S, NamedDecl &ND) { } } +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); +} + static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) { // Ensure that an auto decl is deduced otherwise the checks below might cache // the wrong linkage. @@ -7229,6 +7234,7 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) { checkHybridPatchableAttr(S, ND); checkInheritableAttr(S, ND); checkLifetimeBoundAttr(S, ND); + checkModularFormatAttr(S, ND); } static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl, diff --git a/clang/test/Sema/attr-modular-format.c b/clang/test/Sema/attr-modular-format.c new file mode 100644 index 0000000000000..46811b980f854 --- /dev/null +++ b/clang/test/Sema/attr-modular-format.c @@ -0,0 +1,7 @@ +//RUN: %clang_cc1 -fsyntax-only -verify %s + +#include <stdarg.h> + +int printf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // no-error +int myprintf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // expected-error {{'modular_format' attribute requires 'format' attribute}} + >From afd92f69dd97478953a890374083f5e7034e7aae Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 4 Nov 2025 16:38:55 -0800 Subject: [PATCH 10/22] Clarify semantics --- clang/include/clang/Basic/AttrDocs.td | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index db231d6b9ae3b..4f9efc39e5115 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9643,14 +9643,15 @@ may redirect the call to the identifier given as the first argument to the attribute (the modular implementation function). The second argument is a implementation name, and the remaining arguments are -aspects of the format string for the compiler to report. If the compiler does -not understand an aspect, it must summarily consider any call to require that -aspect. +aspects of the format string for the compiler to report. The implementation +name is an unevaluted identifier be in the C namespace. The compiler reports that a call requires an aspect by issuing a relocation for the symbol ``<impl_name>_<aspect>`` at the point of the call. This arranges for code and data needed to support the aspect of the implementation to be brought into the link to satisfy weak references in the modular implemenation function. +If the compiler does not understand an aspect, it must summarily consider any +call to require that aspect. For example, say ``printf`` is annotated with ``modular_format(__modular_printf, "__printf", "float")``. Then, a call to >From 56183a3bbe8979628b38fce5729dc6124c29336a Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 4 Nov 2025 16:40:11 -0800 Subject: [PATCH 11/22] clang-format --- clang/include/clang/Basic/Attr.td | 7 ++----- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index e40e46f2846c8..73b6b2688cb48 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5326,11 +5326,8 @@ def NonString : InheritableAttr { def ModularFormat : InheritableAttr { let Spellings = [Clang<"modular_format">]; - let Args = [ - IdentifierArgument<"ModularImplFn">, - StringArgument<"ImplName">, - VariadicStringArgument<"Aspects"> - ]; + let Args = [IdentifierArgument<"ModularImplFn">, StringArgument<"ImplName">, + VariadicStringArgument<"Aspects">]; let Subjects = SubjectList<[Function]>; let Documentation = [ModularFormatDocs]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b8386eda89cb3..2bb86ffef17da 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13063,8 +13063,8 @@ def err_get_vtable_pointer_requires_complete_type : Error<"__builtin_get_vtable_pointer requires an argument with a complete " "type, but %0 is incomplete">; -def err_modular_format_attribute_no_format : Error< - "'modular_format' attribute requires 'format' attribute">; +def err_modular_format_attribute_no_format + : Error<"'modular_format' attribute requires 'format' attribute">; // SYCL-specific diagnostics def warn_sycl_kernel_num_of_template_params : Warning< >From 46f88d58be82c248e92a4f6412f14ead51073ab3 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 4 Nov 2025 16:44:44 -0800 Subject: [PATCH 12/22] Add a blurb to the release notes --- clang/docs/ReleaseNotes.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 51f07256c5d9f..3fb4aa54e75f9 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -356,6 +356,11 @@ Attribute Changes in Clang attribute, but `malloc_span` applies not to functions returning pointers, but to functions returning span-like structures (i.e. those that contain a pointer field and a size integer field or two pointers). +- New attribute ``modular_format`` to allow dynamically selecting at link time + which aspects of a statically linked libc's printf (et al) implementation are + required. This can reduce code size without requiring e.g. multilibs for + printf features. Requires cooperation with the libc implementation. + Improvements to Clang's diagnostics ----------------------------------- - Diagnostics messages now refer to ``structured binding`` instead of ``decomposition``, >From 5e234479ab25879d17d6020363d21eb96e9b29bd Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 6 Nov 2025 14:31:24 -0800 Subject: [PATCH 13/22] Update newly-failing test from merge --- clang/test/Misc/pragma-attribute-supported-attributes-list.test | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 747eb17446c87..1737516e2e734 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -110,6 +110,7 @@ // CHECK-NEXT: Mips16 (SubjectMatchRule_function) // CHECK-NEXT: MipsLongCall (SubjectMatchRule_function) // CHECK-NEXT: MipsShortCall (SubjectMatchRule_function) +// CHECK-NEXT: ModularFormat (SubjectMatchRule_function) // CHECK-NEXT: NSConsumed (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: NSConsumesSelf (SubjectMatchRule_objc_method) // CHECK-NEXT: NSErrorDomain (SubjectMatchRule_enum) >From 25675a6d88ec648acee09c4f8069e74d1f132d25 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Wed, 12 Nov 2025 13:21:45 -0800 Subject: [PATCH 14/22] Correct typos and misc nits --- clang/docs/ReleaseNotes.rst | 9 +++++---- clang/include/clang/Basic/AttrDocs.td | 4 ++-- clang/lib/CodeGen/CGCall.cpp | 1 - clang/test/Sema/attr-modular-format.c | 2 -- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3fb4aa54e75f9..21500db27ae0c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -356,10 +356,11 @@ Attribute Changes in Clang attribute, but `malloc_span` applies not to functions returning pointers, but to functions returning span-like structures (i.e. those that contain a pointer field and a size integer field or two pointers). -- New attribute ``modular_format`` to allow dynamically selecting at link time - which aspects of a statically linked libc's printf (et al) implementation are - required. This can reduce code size without requiring e.g. multilibs for - printf features. Requires cooperation with the libc implementation. +- Added new attribute ``modular_format`` to allow dynamically selecting at link + time which aspects of a statically linked libc's printf (et al) + implementation are required. This can reduce code size without requiring e.g. + multilibs for printf features. Requires cooperation with the libc + implementation. Improvements to Clang's diagnostics ----------------------------------- diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 4f9efc39e5115..51f009fba8b46 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9636,7 +9636,7 @@ def ModularFormatDocs : Documentation { let Content = [{ The ``modular_format`` attribute can be applied to a function that bears the ``format`` attribute (or standard library functions) to indicate that the -implementation is "modular", that is, that the implemenation is logically +implementation is "modular", that is, that the implementation is logically divided into a number of named aspects. When the compiler can determine that not all aspects of the implementation are needed for a given call, the compiler may redirect the call to the identifier given as the first argument to the @@ -9644,7 +9644,7 @@ attribute (the modular implementation function). The second argument is a implementation name, and the remaining arguments are aspects of the format string for the compiler to report. The implementation -name is an unevaluted identifier be in the C namespace. +name is an unevaluated identifier be in the C namespace. The compiler reports that a call requires an aspect by issuing a relocation for the symbol ``<impl_name>_<aspect>`` at the point of the call. This arranges for diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 3b85a9db15bcf..4a9025b6e0b0f 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2561,7 +2561,6 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, FuncAttrs.addAttribute("aarch64_pstate_sm_body"); if (auto *ModularFormat = TargetDecl->getAttr<ModularFormatAttr>()) { - // TODO: Error checking FormatAttr *Format = TargetDecl->getAttr<FormatAttr>(); StringRef Type = Format->getType()->getName(); std::string FormatIdx = std::to_string(Format->getFormatIdx()); diff --git a/clang/test/Sema/attr-modular-format.c b/clang/test/Sema/attr-modular-format.c index 46811b980f854..3cc6fac29def7 100644 --- a/clang/test/Sema/attr-modular-format.c +++ b/clang/test/Sema/attr-modular-format.c @@ -1,7 +1,5 @@ //RUN: %clang_cc1 -fsyntax-only -verify %s -#include <stdarg.h> - int printf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // no-error int myprintf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // expected-error {{'modular_format' attribute requires 'format' attribute}} >From 36b4e84b48cc616757f8728f8e8eff185256a195 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 1 Dec 2025 12:31:31 -0800 Subject: [PATCH 15/22] Add modular_format attribute sorting test --- clang/test/CodeGen/attr-modular-format.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clang/test/CodeGen/attr-modular-format.c b/clang/test/CodeGen/attr-modular-format.c index 2c647214b3bca..bda958e83a3dd 100644 --- a/clang/test/CodeGen/attr-modular-format.c +++ b/clang/test/CodeGen/attr-modular-format.c @@ -24,5 +24,17 @@ void test_redecl(void) { redecl("hello"); } +int order1(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "a", "b"), format(printf, 1, 2))); +int order2(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "b", "a"), format(printf, 1, 2))); + +// CHECK-LABEL: define dso_local void @test_order( +// CHECK: {{.*}} = call i32 (ptr, ...) @order1(ptr noundef @.str) #[[ATTR_ORDER:[0-9]+]] +// CHECK: {{.*}} = call i32 (ptr, ...) @order2(ptr noundef @.str) #[[ATTR_ORDER]] +void test_order(void) { + order1("hello"); + order2("hello"); +} + // CHECK: attributes #[[ATTR]] = { "modular-format"="printf,1,2,__modular_printf,__printf,float" } // CHECK: attributes #[[ATTR_REDECL]] = { "modular-format"="printf,1,2,__second_impl,__second,three,two" } +// CHECK: attributes #[[ATTR_ORDER]] = { "modular-format"="printf,1,2,__modular_printf,__printf,a,b" } >From 299c3640e02fc2478a50d4960a38937b304f9ea9 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 1 Dec 2025 12:37:50 -0800 Subject: [PATCH 16/22] Add error for duplicate modular_format aspects and tests --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ clang/lib/Sema/SemaDeclAttr.cpp | 15 +++++++++++++-- clang/test/Sema/attr-modular-format.c | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2bb86ffef17da..e14bb5cad2564 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13066,6 +13066,9 @@ def err_get_vtable_pointer_requires_complete_type def err_modular_format_attribute_no_format : Error<"'modular_format' attribute requires 'format' attribute">; +def err_modular_format_duplicate_aspect + : Error<"duplicate aspect '%0' in 'modular_format' attribute">; + // SYCL-specific diagnostics def warn_sycl_kernel_num_of_template_params : Warning< "'sycl_kernel' attribute only applies to a function template with at least" diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 0c93106596c46..9ee8e3f37c520 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6978,16 +6978,27 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { if (!S.checkStringLiteralArgumentAttr(AL, 1, ImplName)) return; SmallVector<StringRef> Aspects; + llvm::DenseSet<StringRef> SeenAspects; + bool HasDuplicate = false; for (unsigned I = 2, E = AL.getNumArgs(); I != E; ++I) { StringRef Aspect; if (!S.checkStringLiteralArgumentAttr(AL, I, Aspect)) return; + if (!SeenAspects.insert(Aspect).second) { + S.Diag(AL.getArgAsExpr(I)->getExprLoc(), + diag::err_modular_format_duplicate_aspect) + << Aspect; + HasDuplicate = true; + continue; + } Aspects.push_back(Aspect); } - // Store aspects sorted and without duplicates. + if (HasDuplicate) + return; + + // Store aspects sorted. llvm::sort(Aspects); - Aspects.erase(llvm::unique(Aspects), Aspects.end()); D->addAttr(::new (S.Context) ModularFormatAttr( S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName, diff --git a/clang/test/Sema/attr-modular-format.c b/clang/test/Sema/attr-modular-format.c index 3cc6fac29def7..fdaf3a25c182b 100644 --- a/clang/test/Sema/attr-modular-format.c +++ b/clang/test/Sema/attr-modular-format.c @@ -3,3 +3,7 @@ int printf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // no-error int myprintf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // expected-error {{'modular_format' attribute requires 'format' attribute}} +int dupe(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int", "float"), format(printf, 1, 2))); // expected-error {{duplicate aspect 'float' in 'modular_format' attribute}} +int multi_dupe(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int", "float", "int"), format(printf, 1, 2))); // expected-error {{duplicate aspect 'float' in 'modular_format' attribute}} \ + // expected-error {{duplicate aspect 'int' in 'modular_format' attribute}} + >From 7272717427988a6759bb79ce27da226d64501b14 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 1 Dec 2025 14:48:40 -0800 Subject: [PATCH 17/22] Warn about duplicate attributes --- clang/lib/Sema/SemaDeclAttr.cpp | 30 ++++++++++++++++++++++++++- clang/test/Sema/attr-modular-format.c | 12 +++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9ee8e3f37c520..76bea8c3990f4 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6973,6 +6973,24 @@ static void handleVTablePointerAuthentication(Sema &S, Decl *D, CustomDiscriminationValue)); } +static bool modularFormatIsSame(const ModularFormatAttr *Existing, + IdentifierInfo *ModularImplFn, + StringRef ImplName, + ArrayRef<StringRef> Aspects) { + if (Existing->getModularImplFn() != ModularImplFn) + return false; + if (Existing->getImplName() != ImplName) + return false; + if (Existing->aspects_size() != Aspects.size()) + return false; + unsigned I = 0; + for (const auto &ExistingAspect : Existing->aspects()) { + if (ExistingAspect != Aspects[I++]) + return false; + } + return true; +} + static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { StringRef ImplName; if (!S.checkStringLiteralArgumentAttr(AL, 1, ImplName)) @@ -7000,8 +7018,18 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { // Store aspects sorted. llvm::sort(Aspects); + IdentifierInfo *ModularImplFn = AL.getArgAsIdent(0)->getIdentifierInfo(); + + if (const auto *Existing = D->getAttr<ModularFormatAttr>()) { + if (!modularFormatIsSame(Existing, ModularImplFn, ImplName, Aspects)) { + S.Diag(AL.getLoc(), diag::warn_duplicate_attribute) << AL; + S.Diag(Existing->getLocation(), diag::note_conflicting_attribute); + } + D->dropAttr<ModularFormatAttr>(); + } + D->addAttr(::new (S.Context) ModularFormatAttr( - S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName, + S.Context, AL, ModularImplFn, ImplName, Aspects.data(), Aspects.size())); } diff --git a/clang/test/Sema/attr-modular-format.c b/clang/test/Sema/attr-modular-format.c index fdaf3a25c182b..d05d0ed581e74 100644 --- a/clang/test/Sema/attr-modular-format.c +++ b/clang/test/Sema/attr-modular-format.c @@ -7,3 +7,15 @@ int dupe(const char *fmt, ...) __attribute__((modular_format(__modular_printf, int multi_dupe(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int", "float", "int"), format(printf, 1, 2))); // expected-error {{duplicate aspect 'float' in 'modular_format' attribute}} \ // expected-error {{duplicate aspect 'int' in 'modular_format' attribute}} +// Test with multiple identical attributes on the same declaration. +int same_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2))); // no-warning + +// Test with multiple different attributes on the same declaration. +int diff_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int"))); // expected-warning {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} + +int diff_attr2(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__other", "float"))); // expected-warning {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} + +int diff_attr3(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__other, "__printf", "float"))); // expected-warning {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} + +// Test with same attributes but different aspect order. +int diff_order(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int", "float"))); // no-warning >From c067ccea6c8717a5caff7a5831851e76c816a836 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 1 Dec 2025 16:24:21 -0800 Subject: [PATCH 18/22] clang-format --- clang/lib/Sema/SemaDeclAttr.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 76bea8c3990f4..f39de42184c2d 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7029,8 +7029,7 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { } D->addAttr(::new (S.Context) ModularFormatAttr( - S.Context, AL, ModularImplFn, ImplName, - Aspects.data(), Aspects.size())); + S.Context, AL, ModularImplFn, ImplName, Aspects.data(), Aspects.size())); } //===----------------------------------------------------------------------===// >From a1c9f84de7436af5bce3be7e95f3a050db2c1d28 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 1 Dec 2025 16:28:44 -0800 Subject: [PATCH 19/22] Minor cleanup --- clang/lib/Sema/SemaDeclAttr.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index f39de42184c2d..c8d816a03f0b5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7012,9 +7012,6 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { Aspects.push_back(Aspect); } - if (HasDuplicate) - return; - // Store aspects sorted. llvm::sort(Aspects); @@ -7028,6 +7025,9 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { D->dropAttr<ModularFormatAttr>(); } + if (HasDuplicate) + return; + D->addAttr(::new (S.Context) ModularFormatAttr( S.Context, AL, ModularImplFn, ImplName, Aspects.data(), Aspects.size())); } >From 7cbf5b7da351f541a754369c926f364db2db79f5 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Mon, 1 Dec 2025 16:31:44 -0800 Subject: [PATCH 20/22] Add a codegen test for the overwrite behavior --- clang/test/CodeGen/attr-modular-format.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clang/test/CodeGen/attr-modular-format.c b/clang/test/CodeGen/attr-modular-format.c index bda958e83a3dd..e38287ee4a6a6 100644 --- a/clang/test/CodeGen/attr-modular-format.c +++ b/clang/test/CodeGen/attr-modular-format.c @@ -35,6 +35,15 @@ void test_order(void) { order2("hello"); } +int overwrite(const char *fmt, ...) __attribute__((modular_format(__impl1, "__name1", "1"), modular_format(__impl2, "__name2", "2"), format(printf, 1, 2))); + +// CHECK-LABEL: define dso_local void @test_overwrite( +// CHECK: {{.*}} = call i32 (ptr, ...) @overwrite(ptr noundef @.str) #[[ATTR_OVERWRITE:[0-9]+]] +void test_overwrite(void) { + overwrite("hello"); +} + // CHECK: attributes #[[ATTR]] = { "modular-format"="printf,1,2,__modular_printf,__printf,float" } // CHECK: attributes #[[ATTR_REDECL]] = { "modular-format"="printf,1,2,__second_impl,__second,three,two" } // CHECK: attributes #[[ATTR_ORDER]] = { "modular-format"="printf,1,2,__modular_printf,__printf,a,b" } +// CHECK: attributes #[[ATTR_OVERWRITE]] = { "modular-format"="printf,1,2,__impl2,__name2,2" } >From ffc8742ff1a624c18a8ca143146efe109def179d Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 2 Dec 2025 10:31:52 -0800 Subject: [PATCH 21/22] Allow only duplicates, whether on the same decl or redecls --- clang/include/clang/Basic/AttrDocs.td | 4 ++ .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 5 ++ clang/lib/Sema/SemaDecl.cpp | 5 ++ clang/lib/Sema/SemaDeclAttr.cpp | 60 +++++++++++-------- clang/test/CodeGen/attr-modular-format.c | 20 +++---- clang/test/Sema/attr-modular-format.c | 13 ++-- 7 files changed, 69 insertions(+), 40 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 51f009fba8b46..f8248f295d665 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9661,6 +9661,10 @@ become a call to ``__modular_printf`` with the same arguments, as would relocation against the symbol ``__printf_float``, which would bring floating point support for ``printf`` into the link. +If the attribute appears more than once on a declaration, or across a chain of +redeclarations, it is an error for the attributes to have different arguments, +excepting that the aspects may be in any order. + The following aspects are currently supported: - ``float``: The call has a floating point argument diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e14bb5cad2564..2b85148c35bde 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11270,6 +11270,8 @@ def warn_duplicate_attribute_exact : Warning< def warn_duplicate_attribute : Warning< "attribute %0 is already applied with different arguments">, InGroup<IgnoredAttributes>; +def err_duplicate_attribute + : Error<"attribute %0 is already applied with different arguments">; def err_disallowed_duplicate_attribute : Error< "attribute %0 cannot appear more than once on a declaration">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ae500139ee6f7..7dbf0fa654047 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4957,6 +4957,11 @@ class Sema final : public SemaBase { IdentifierInfo *Format, int FormatIdx, StringLiteral *FormatStr); + ModularFormatAttr *mergeModularFormatAttr(Decl *D, + const AttributeCommonInfo &CI, + IdentifierInfo *ModularImplFn, + StringRef ImplName, + MutableArrayRef<StringRef> Aspects); /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 89485e2850381..f07c187804af0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -58,6 +58,7 @@ #include "clang/Sema/SemaSwift.h" #include "clang/Sema/SemaWasm.h" #include "clang/Sema/Template.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallPtrSet.h" @@ -2901,6 +2902,10 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, else if (const auto *FMA = dyn_cast<FormatMatchesAttr>(Attr)) NewAttr = S.mergeFormatMatchesAttr( D, *FMA, FMA->getType(), FMA->getFormatIdx(), FMA->getFormatString()); + else if (const auto *MFA = dyn_cast<ModularFormatAttr>(Attr)) + NewAttr = S.mergeModularFormatAttr( + D, *MFA, MFA->getModularImplFn(), MFA->getImplName(), + MutableArrayRef<StringRef>{MFA->aspects_begin(), MFA->aspects_size()}); else if (const auto *SA = dyn_cast<SectionAttr>(Attr)) NewAttr = S.mergeSectionAttr(D, *SA, SA->getName()); else if (const auto *CSA = dyn_cast<CodeSegAttr>(Attr)) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index c8d816a03f0b5..5a9ad7abc56c5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6973,31 +6973,40 @@ static void handleVTablePointerAuthentication(Sema &S, Decl *D, CustomDiscriminationValue)); } -static bool modularFormatIsSame(const ModularFormatAttr *Existing, - IdentifierInfo *ModularImplFn, - StringRef ImplName, - ArrayRef<StringRef> Aspects) { - if (Existing->getModularImplFn() != ModularImplFn) - return false; - if (Existing->getImplName() != ImplName) - return false; - if (Existing->aspects_size() != Aspects.size()) - return false; - unsigned I = 0; - for (const auto &ExistingAspect : Existing->aspects()) { - if (ExistingAspect != Aspects[I++]) - return false; +static bool modularFormatAttrsEquiv(const ModularFormatAttr *Existing, + IdentifierInfo *ModularImplFn, + StringRef ImplName, + ArrayRef<StringRef> Aspects) { + return Existing->getModularImplFn() == ModularImplFn && + Existing->getImplName() == ImplName && + Existing->aspects_size() == Aspects.size() && + llvm::equal(Existing->aspects(), Aspects); +} + +ModularFormatAttr * +Sema::mergeModularFormatAttr(Decl *D, const AttributeCommonInfo &CI, + IdentifierInfo *ModularImplFn, StringRef ImplName, + MutableArrayRef<StringRef> Aspects) { + if (const auto *Existing = D->getAttr<ModularFormatAttr>()) { + if (!modularFormatAttrsEquiv(Existing, ModularImplFn, ImplName, Aspects)) { + Diag(Existing->getLocation(), diag::err_duplicate_attribute) << *Existing; + Diag(CI.getLoc(), diag::note_conflicting_attribute); + } + // Drop the existing attribute on the declaration in favor of the newly + // inherited one. + D->dropAttr<ModularFormatAttr>(); } - return true; + return ::new (Context) ModularFormatAttr(Context, CI, ModularImplFn, ImplName, + Aspects.data(), Aspects.size()); } static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { + bool Valid = true; StringRef ImplName; if (!S.checkStringLiteralArgumentAttr(AL, 1, ImplName)) - return; + Valid = false; SmallVector<StringRef> Aspects; llvm::DenseSet<StringRef> SeenAspects; - bool HasDuplicate = false; for (unsigned I = 2, E = AL.getNumArgs(); I != E; ++I) { StringRef Aspect; if (!S.checkStringLiteralArgumentAttr(AL, I, Aspect)) @@ -7006,27 +7015,26 @@ static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { S.Diag(AL.getArgAsExpr(I)->getExprLoc(), diag::err_modular_format_duplicate_aspect) << Aspect; - HasDuplicate = true; + Valid = false; continue; } Aspects.push_back(Aspect); } + if (!Valid) + return; // Store aspects sorted. llvm::sort(Aspects); - IdentifierInfo *ModularImplFn = AL.getArgAsIdent(0)->getIdentifierInfo(); if (const auto *Existing = D->getAttr<ModularFormatAttr>()) { - if (!modularFormatIsSame(Existing, ModularImplFn, ImplName, Aspects)) { - S.Diag(AL.getLoc(), diag::warn_duplicate_attribute) << AL; - S.Diag(Existing->getLocation(), diag::note_conflicting_attribute); + if (!modularFormatAttrsEquiv(Existing, ModularImplFn, ImplName, Aspects)) { + S.Diag(AL.getLoc(), diag::err_duplicate_attribute) << *Existing; + S.Diag(Existing->getLoc(), diag::note_conflicting_attribute); } - D->dropAttr<ModularFormatAttr>(); - } - - if (HasDuplicate) + // Ignore the later declaration in favor of the earlier one. return; + } D->addAttr(::new (S.Context) ModularFormatAttr( S.Context, AL, ModularImplFn, ImplName, Aspects.data(), Aspects.size())); diff --git a/clang/test/CodeGen/attr-modular-format.c b/clang/test/CodeGen/attr-modular-format.c index e38287ee4a6a6..5474ce361fbc2 100644 --- a/clang/test/CodeGen/attr-modular-format.c +++ b/clang/test/CodeGen/attr-modular-format.c @@ -15,11 +15,12 @@ void test_explicit_format(void) { myprintf("hello"); } -int redecl(const char *fmt, ...) __attribute__((modular_format(__first_impl, "__first", "one"), format(printf, 1, 2))); -int redecl(const char *fmt, ...) __attribute__((modular_format(__second_impl, "__second", "two", "three"))); +int redecl(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +int redecl(const char *fmt, ...) __attribute__((modular_format(__dupe_impl, "__dupe", "1"))); +int redecl(const char *fmt, ...) __attribute__((modular_format(__dupe_impl, "__dupe", "1"))); // CHECK-LABEL: define dso_local void @test_redecl( -// CHECK: {{.*}} = call i32 (ptr, ...) @redecl(ptr noundef @.str) #[[ATTR_REDECL:[0-9]+]] +// CHECK: {{.*}} = call i32 (ptr, ...) @redecl(ptr noundef @.str) #[[ATTR_DUPE_IDENTICAL:[0-9]+]] void test_redecl(void) { redecl("hello"); } @@ -35,15 +36,14 @@ void test_order(void) { order2("hello"); } -int overwrite(const char *fmt, ...) __attribute__((modular_format(__impl1, "__name1", "1"), modular_format(__impl2, "__name2", "2"), format(printf, 1, 2))); +int duplicate_identical(const char *fmt, ...) __attribute__((modular_format(__dupe_impl, "__dupe", "1"), modular_format(__dupe_impl, "__dupe", "1"), format(printf, 1, 2))); -// CHECK-LABEL: define dso_local void @test_overwrite( -// CHECK: {{.*}} = call i32 (ptr, ...) @overwrite(ptr noundef @.str) #[[ATTR_OVERWRITE:[0-9]+]] -void test_overwrite(void) { - overwrite("hello"); +// CHECK-LABEL: define dso_local void @test_duplicate_identical( +// CHECK: {{.*}} = call i32 (ptr, ...) @duplicate_identical(ptr noundef @.str) #[[ATTR_DUPE_IDENTICAL]] +void test_duplicate_identical(void) { + duplicate_identical("hello"); } // CHECK: attributes #[[ATTR]] = { "modular-format"="printf,1,2,__modular_printf,__printf,float" } -// CHECK: attributes #[[ATTR_REDECL]] = { "modular-format"="printf,1,2,__second_impl,__second,three,two" } +// CHECK: attributes #[[ATTR_DUPE_IDENTICAL]] = { "modular-format"="printf,1,2,__dupe_impl,__dupe,1" } // CHECK: attributes #[[ATTR_ORDER]] = { "modular-format"="printf,1,2,__modular_printf,__printf,a,b" } -// CHECK: attributes #[[ATTR_OVERWRITE]] = { "modular-format"="printf,1,2,__impl2,__name2,2" } diff --git a/clang/test/Sema/attr-modular-format.c b/clang/test/Sema/attr-modular-format.c index d05d0ed581e74..fc5b28b0b88be 100644 --- a/clang/test/Sema/attr-modular-format.c +++ b/clang/test/Sema/attr-modular-format.c @@ -11,11 +11,16 @@ int multi_dupe(const char *fmt, ...) __attribute__((modular_format(__modular_pr int same_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2))); // no-warning // Test with multiple different attributes on the same declaration. -int diff_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int"))); // expected-warning {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} +int diff_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} -int diff_attr2(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__other", "float"))); // expected-warning {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} +int diff_attr2(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__other", "float"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} -int diff_attr3(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__other, "__printf", "float"))); // expected-warning {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} +int diff_attr3(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__other, "__printf", "float"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} // Test with same attributes but different aspect order. -int diff_order(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int", "float"))); // no-warning +int diff_order(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int", "float"))); // no-error + +// Test with multiple different attributes on a declaration and a redeclaration +int redecl(const char *fmt, ...) __attribute__((format(printf, 1, 2))); // no-error +int redecl(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // expected-note {{conflicting attribute is here}} +int redecl(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "int"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} >From f951d6d08a98fe841714f8eee3ff8b53161a86b3 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 2 Dec 2025 16:05:00 -0800 Subject: [PATCH 22/22] Return nullptr in mergeModularFormatAttr --- clang/lib/Sema/SemaDeclAttr.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 5a9ad7abc56c5..fcdb22d086396 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6992,9 +6992,7 @@ Sema::mergeModularFormatAttr(Decl *D, const AttributeCommonInfo &CI, Diag(Existing->getLocation(), diag::err_duplicate_attribute) << *Existing; Diag(CI.getLoc(), diag::note_conflicting_attribute); } - // Drop the existing attribute on the declaration in favor of the newly - // inherited one. - D->dropAttr<ModularFormatAttr>(); + return nullptr; } return ::new (Context) ModularFormatAttr(Context, CI, ModularImplFn, ImplName, Aspects.data(), Aspects.size()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
