Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ C Language Changes
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
- Clang now supports `N3355 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm>`_ Named Loops.
- Clang's implementation of ``__COUNTER__`` was updated to conform to
`WG14 N3457 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm>`_.
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
^^^^^^^^^^^^^^^^^^^
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<C2y>;
def warn_counter : Warning<
"'__COUNTER__' is incompatible with standards before C2y">,
InGroup<CPre2yCompat>, DefaultIgnore;

def err_raw_delim_too_long : Error<
"raw string delimiter longer than 16 characters"
"; use PREFIX( )PREFIX to delimit raw string">;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -8417,6 +8417,10 @@ def aligned_alloc_unavailable : Flag<["-"], "faligned-alloc-unavailable">,
MarshallingInfoFlag<LangOpts<"AlignedAllocationUnavailable">>,
ShouldParseIf<faligned_allocation.KeyPath>;

def finitial_counter_value_EQ : Joined<["-"], "finitial-counter-value=">,
HelpText<"Sets the initial value for __COUNTER__, defaults to 0.">,
MarshallingInfoInt<PreprocessorOpts<"InitialCounterValue">, "0">;

} // let Visibility = [CC1Option]

//===----------------------------------------------------------------------===//
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 &&
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Lex/PreprocessorOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ class PreprocessorOptions {
/// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH.
std::optional<uint64_t> 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) {}

Expand Down
8 changes: 4 additions & 4 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Frontend/ASTUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ class ASTInfoCollector : public ASTReaderListener {
CodeGenOptions &CodeGenOpts;
std::shared_ptr<TargetOptions> &TargetOpts;
IntrusiveRefCntPtr<TargetInfo> &Target;
unsigned &Counter;
uint32_t &Counter;
bool InitializedLanguage = false;
bool InitializedHeaderSearchPaths = false;

Expand All @@ -529,7 +529,7 @@ class ASTInfoCollector : public ASTReaderListener {
HeaderSearchOptions &HSOpts, PreprocessorOptions &PPOpts,
LangOptions &LangOpt, CodeGenOptions &CodeGenOpts,
std::shared_ptr<TargetOptions> &TargetOpts,
IntrusiveRefCntPtr<TargetInfo> &Target, unsigned &Counter)
IntrusiveRefCntPtr<TargetInfo> &Target, uint32_t &Counter)
: PP(PP), Context(Context), HSOpts(HSOpts), PPOpts(PPOpts),
LangOpt(LangOpt), CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts),
Target(Target), Counter(Counter) {}
Expand Down Expand Up @@ -627,7 +627,7 @@ class ASTInfoCollector : public ASTReaderListener {
}

void ReadCounter(const serialization::ModuleFile &M,
unsigned Value) override {
uint32_t Value) override {
Counter = Value;
}

Expand Down Expand Up @@ -873,7 +873,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
/*isysroot=*/"",
/*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors);

unsigned Counter = 0;
uint32_t Counter = 0;
AST->Reader->setListener(std::make_unique<ASTInfoCollector>(
*AST->PP, AST->Ctx.get(), *AST->HSOpts, *AST->PPOpts, *AST->LangOpts,
*AST->CodeGenOpts, AST->TargetOpts, AST->Target, Counter));
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <built-in> as being a system header, which suppresses warnings when
// the same macro is defined multiple times.
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/Lex/PPMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int32_t>::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) {
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}

Expand Down
38 changes: 38 additions & 0 deletions clang/test/C/C2y/n3457.c
Original file line number Diff line number Diff line change
@@ -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}}
*/
20 changes: 20 additions & 0 deletions clang/test/C/C2y/n3457_1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483646 %s
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if we set -finitial-counter-value to something larger than unsinged 32 max? Do we obtain a diagnostic?


// 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}}

10 changes: 10 additions & 0 deletions clang/test/C/C2y/n3457_2.c
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion clang/www/c_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ <h2 id="c2y">C2y implementation status</h2>
<tr>
<td>The __COUNTER__ predefined macro</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm">N3457</a></td>
<td class="unknown" align="center">Unknown</td>
<td class="unreleased" align="center">Clang 22</td>
</tr>
<tr>
<td>Chasing Ghosts I: constant expressions v2</td>
Expand Down
Loading