diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 6bb99c757cd19..7fdc991af3b60 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -385,7 +385,9 @@ Builtin Macros ``__COUNTER__`` Defined to an integer value that starts at zero and is incremented each time - the ``__COUNTER__`` macro is expanded. + the ``__COUNTER__`` macro is expanded. This is a standard feature in C2y but + is an extension in earlier language modes and in C++. This macro can only be + expanded 2147483647 times at most. ``__INCLUDE_LEVEL__`` Defined to an integral value that is the include depth of the file currently @@ -1821,6 +1823,7 @@ Octal literals prefixed with ``0o`` or ``0O`` C ``_Countof`` (N3369, N3469) C2y C89 ``_Generic`` with a type operand (N3260) C2y C89, C++ ``++``/``--`` on ``_Complex`` value (N3259) C2y C89, C++ +``__COUNTER__`` (N3457) C2y C89, C++ ============================================= ================================ ============= ============= Builtin type aliases diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5e9a71e1e74d6..37e02e9638296 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -181,6 +181,11 @@ C Language Changes C2y Feature Support ^^^^^^^^^^^^^^^^^^^ - Clang now supports `N3355 `_ Named Loops. +- Clang's implementation of ``__COUNTER__`` was updated to conform to + `WG14 N3457 `_. + This includes adding pedantic warnings for the feature being an extension in + other language modes as well as an error when the counter is expanded more + than 2147483647 times. C23 Feature Support ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index c7fe6e1db6d1f..417187222e448 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -90,6 +90,14 @@ def err_unterminated___pragma : Error<"missing terminating ')' character">; def err_conflict_marker : Error<"version control conflict marker in file">; +def err_counter_overflow : Error< + "'__COUNTER__' value cannot exceed 2'147'483'647">; +def ext_counter : Extension< + "'__COUNTER__' is a C2y extension">, InGroup; +def warn_counter : Warning< + "'__COUNTER__' is incompatible with standards before C2y">, + InGroup, DefaultIgnore; + def err_raw_delim_too_long : Error< "raw string delimiter longer than 16 characters" "; use PREFIX( )PREFIX to delimit raw string">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c8e96e125733c..2511f88f4e53c 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -8417,6 +8417,10 @@ def aligned_alloc_unavailable : Flag<["-"], "faligned-alloc-unavailable">, MarshallingInfoFlag>, ShouldParseIf; +def finitial_counter_value_EQ : Joined<["-"], "finitial-counter-value=">, + HelpText<"Sets the initial value for __COUNTER__, defaults to 0.">, + MarshallingInfoInt, "0">; + } // let Visibility = [CC1Option] //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 39754847a93e4..412002259c054 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -226,7 +226,7 @@ class Preprocessor { LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine; // Next __COUNTER__ value, starts at 0. - unsigned CounterValue = 0; + uint32_t CounterValue = 0; enum { /// Maximum depth of \#includes. @@ -2421,8 +2421,8 @@ class Preprocessor { bool SawDateOrTime() const { return DATELoc != SourceLocation() || TIMELoc != SourceLocation(); } - unsigned getCounterValue() const { return CounterValue; } - void setCounterValue(unsigned V) { CounterValue = V; } + uint32_t getCounterValue() const { return CounterValue; } + void setCounterValue(uint32_t V) { CounterValue = V; } LangOptions::FPEvalMethodKind getCurrentFPEvalMethod() const { assert(CurrentFPEvalMethod != LangOptions::FEM_UnsetOnCommandLine && diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h index d4c4e1ccbf2c4..1c2f6e72e1b93 100644 --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -198,6 +198,10 @@ class PreprocessorOptions { /// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH. std::optional SourceDateEpoch; + /// The initial value for __COUNTER__; typically is zero but can be set via a + /// -cc1 flag for testing purposes. + uint32_t InitialCounterValue = 0; + public: PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {} diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index af856a8097ab1..4ca45a16408a6 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -220,8 +220,8 @@ class ASTReaderListener { } /// Receives __COUNTER__ value. - virtual void ReadCounter(const serialization::ModuleFile &M, - unsigned Value) {} + virtual void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) { + } /// This is called for each AST file loaded. virtual void visitModuleFile(StringRef Filename, @@ -312,7 +312,7 @@ class ChainedASTReaderListener : public ASTReaderListener { bool Complain, std::string &SuggestedPredefines) override; - void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override; + void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) override; bool needsInputFileVisitation() override; bool needsSystemInputFileVisitation() override; void visitModuleFile(StringRef Filename, @@ -352,7 +352,7 @@ class PCHValidator : public ASTReaderListener { StringRef ModuleFilename, StringRef SpecificModuleCachePath, bool Complain) override; - void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override; + void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) override; }; /// ASTReaderListenter implementation to set SuggestedPredefines of diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index cb445682ac48b..cf1cd751b8e8d 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -520,7 +520,7 @@ class ASTInfoCollector : public ASTReaderListener { CodeGenOptions &CodeGenOpts; std::shared_ptr &TargetOpts; IntrusiveRefCntPtr &Target; - unsigned &Counter; + uint32_t &Counter; bool InitializedLanguage = false; bool InitializedHeaderSearchPaths = false; @@ -529,7 +529,7 @@ class ASTInfoCollector : public ASTReaderListener { HeaderSearchOptions &HSOpts, PreprocessorOptions &PPOpts, LangOptions &LangOpt, CodeGenOptions &CodeGenOpts, std::shared_ptr &TargetOpts, - IntrusiveRefCntPtr &Target, unsigned &Counter) + IntrusiveRefCntPtr &Target, uint32_t &Counter) : PP(PP), Context(Context), HSOpts(HSOpts), PPOpts(PPOpts), LangOpt(LangOpt), CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts), Target(Target), Counter(Counter) {} @@ -627,7 +627,7 @@ class ASTInfoCollector : public ASTReaderListener { } void ReadCounter(const serialization::ModuleFile &M, - unsigned Value) override { + uint32_t Value) override { Counter = Value; } @@ -873,7 +873,7 @@ std::unique_ptr ASTUnit::LoadFromASTFile( /*isysroot=*/"", /*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors); - unsigned Counter = 0; + uint32_t Counter = 0; AST->Reader->setListener(std::make_unique( *AST->PP, AST->Ctx.get(), *AST->HSOpts, *AST->PPOpts, *AST->LangOpts, *AST->CodeGenOpts, AST->TargetOpts, AST->Target, Counter)); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 877ab02850667..aaf01705caa5e 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -1569,6 +1569,9 @@ void clang::InitializePreprocessor(Preprocessor &PP, llvm::raw_string_ostream Predefines(PredefineBuffer); MacroBuilder Builder(Predefines); + // Ensure that the initial value of __COUNTER__ is hooked up. + PP.setCounterValue(InitOpts.InitialCounterValue); + // Emit line markers for various builtin sections of the file. The 3 here // marks as being a system header, which suppresses warnings when // the same macro is defined multiple times. diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index dec1956ea0f9a..ef49598243c5b 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1737,7 +1737,19 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Diag(getLastFPEvalPragmaLocation(), diag::note_pragma_entered_here); } } else if (II == Ident__COUNTER__) { - // __COUNTER__ expands to a simple numeric value. + Diag(Tok.getLocation(), + getLangOpts().C2y ? diag::warn_counter : diag::ext_counter); + // __COUNTER__ expands to a simple numeric value that must be less than + // 2147483647. + constexpr uint32_t MaxPosValue = std::numeric_limits::max(); + if (CounterValue > MaxPosValue) { + Diag(Tok.getLocation(), diag::err_counter_overflow); + // Retain the maximal value so we don't issue conversion-related + // diagnostics by overflowing into a long long. While this does produce + // a duplicate value, there's no way to ignore this error so there's no + // translation anyway. + CounterValue = MaxPosValue; + } OS << CounterValue++; Tok.setKind(tok::numeric_constant); } else if (II == Ident__has_feature) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 6acf79acea111..73653f64b1543 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -225,7 +225,7 @@ bool ChainedASTReaderListener::ReadPreprocessorOptions( } void ChainedASTReaderListener::ReadCounter(const serialization::ModuleFile &M, - unsigned Value) { + uint32_t Value) { First->ReadCounter(M, Value); Second->ReadCounter(M, Value); } @@ -973,7 +973,7 @@ bool PCHValidator::ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, PP.getPreprocessorOpts()); } -void PCHValidator::ReadCounter(const ModuleFile &M, unsigned Value) { +void PCHValidator::ReadCounter(const ModuleFile &M, uint32_t Value) { PP.setCounterValue(Value); } diff --git a/clang/test/C/C2y/n3457.c b/clang/test/C/C2y/n3457.c new file mode 100644 index 0000000000000..d71a3f37e1343 --- /dev/null +++ b/clang/test/C/C2y/n3457.c @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s +// RUN: %clang_cc1 -verify=ext -pedantic -x c++ %s +// RUN: %clang_cc1 -verify=pre -std=c2y -pedantic -Wpre-c2y-compat %s + +/* WG14 N3457: Clang 22 + * The __COUNTER__ predefined macro + * + * This predefined macro was supported as an extension in earlier versions of + * Clang, but the required diagnostics for the limits were not added until 22. + */ + +// Ensure that __COUNTER__ starts from 0. +static_assert(__COUNTER__ == 0); /* ext-warning {{'__COUNTER__' is a C2y extension}} + pre-warning {{'__COUNTER__' is incompatible with standards before C2y}} + */ + +// Ensure that the produced value can be used with token concatenation. +#define CAT_IMPL(a, b) a ## b +#define CAT(a, b) CAT_IMPL(a, b) +#define NAME_WITH_COUNTER(a) CAT(a, __COUNTER__) +void test() { + // Because this is the 2nd expansion, this defines test1. + int NAME_WITH_COUNTER(test); /* ext-warning {{'__COUNTER__' is a C2y extension}} + pre-warning {{'__COUNTER__' is incompatible with standards before C2y}} + */ + int other_test = test1; // Ok +} + +// Ensure that __COUNTER__ increments each time you mention it. +static_assert(__COUNTER__ == 2); /* ext-warning {{'__COUNTER__' is a C2y extension}} + pre-warning {{'__COUNTER__' is incompatible with standards before C2y}} + */ +static_assert(__COUNTER__ == 3); /* ext-warning {{'__COUNTER__' is a C2y extension}} + pre-warning {{'__COUNTER__' is incompatible with standards before C2y}} + */ +static_assert(__COUNTER__ == 4); /* ext-warning {{'__COUNTER__' is a C2y extension}} + pre-warning {{'__COUNTER__' is incompatible with standards before C2y}} + */ diff --git a/clang/test/C/C2y/n3457_1.c b/clang/test/C/C2y/n3457_1.c new file mode 100644 index 0000000000000..76c5a0b9a700f --- /dev/null +++ b/clang/test/C/C2y/n3457_1.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483646 %s + +// The value produced needs to be a type that's representable with a signed +// long. However, the actual type it expands to does *not* need to be forced to +// be signed long because that would generally mean suffixing the value with L, +// which would be very surprising for folks using this to generate unique ids. +// We'll test this by ensuring the largest value can be expanded properly and +// an assertion that signed long is always at least four bytes wide (which is +// what's required to represent that maximal value). +// +// So we set the initial counter value to 2147483646, we'll validate that, +// increment it once to get to the maximal value and ensure there's no +// diagnostic, then increment again to ensure we get the constraint violation. + +static_assert(__COUNTER__ == 2147483646); // Test and increment +static_assert(__COUNTER__ == 2147483647); // Test and increment + +// This one should fail. +signed long i = __COUNTER__; // expected-error {{'__COUNTER__' value cannot exceed 2'147'483'647}} + diff --git a/clang/test/C/C2y/n3457_2.c b/clang/test/C/C2y/n3457_2.c new file mode 100644 index 0000000000000..018c8f4390767 --- /dev/null +++ b/clang/test/C/C2y/n3457_2.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -verify=good -std=c2y -finitial-counter-value=2147483648 %s +// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483648 -DEXPAND_IT %s +// good-no-diagnostics + +// This sets the intial __COUNTER__ value to something that's too big. Setting +// the value too large is fine. Expanding to a too-large value is not. +#ifdef EXPAND_IT + // This one should fail. + signed long i = __COUNTER__; // expected-error {{'__COUNTER__' value cannot exceed 2'147'483'647}} +#endif diff --git a/clang/www/c_status.html b/clang/www/c_status.html index 380f66495a367..eeff464313245 100644 --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -329,7 +329,7 @@

C2y implementation status

The __COUNTER__ predefined macro N3457 - Unknown + Clang 22 Chasing Ghosts I: constant expressions v2