[C2y] Support WG14 N3457, the __COUNTER__ macro (#162662)
This implements the parts of https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm which were adopted at the recent meeting in Brno. Clang already implemented `__COUNTER__`, but needed some changes for conformance. Specifically, we now diagnose when the macro is expanded more than 2147483647 times. Additionally, we now give the expected extension and pre-compat warnings for the feature. To support testing the limits, this also adds a -cc1-only option, `-finitial-counter-value=`, which lets you specify the initial value the `__COUNTER__` macro should expand to.
This commit is contained in:
parent
ef9ff15587
commit
df1d786c46
@ -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
|
||||
|
||||
@ -196,6 +196,11 @@ C2y Feature Support
|
||||
function or variable within an extern inline function is no longer a
|
||||
constraint per `WG14 N3622 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3622.txt>`_.
|
||||
- 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
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -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">;
|
||||
|
||||
@ -8445,6 +8445,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]
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -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 @@ public:
|
||||
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 &&
|
||||
|
||||
@ -198,6 +198,10 @@ public:
|
||||
/// 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) {}
|
||||
|
||||
|
||||
@ -220,8 +220,8 @@ public:
|
||||
}
|
||||
|
||||
/// 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 @@ public:
|
||||
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 @@ public:
|
||||
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
|
||||
|
||||
@ -518,14 +518,14 @@ class ASTInfoCollector : public ASTReaderListener {
|
||||
LangOptions &LangOpts;
|
||||
CodeGenOptions &CodeGenOpts;
|
||||
TargetOptions &TargetOpts;
|
||||
unsigned &Counter;
|
||||
uint32_t &Counter;
|
||||
|
||||
public:
|
||||
ASTInfoCollector(HeaderSearchOptions &HSOpts,
|
||||
std::string &SpecificModuleCachePath,
|
||||
PreprocessorOptions &PPOpts, LangOptions &LangOpts,
|
||||
CodeGenOptions &CodeGenOpts, TargetOptions &TargetOpts,
|
||||
unsigned &Counter)
|
||||
uint32_t &Counter)
|
||||
: HSOpts(HSOpts), SpecificModuleCachePath(SpecificModuleCachePath),
|
||||
PPOpts(PPOpts), LangOpts(LangOpts), CodeGenOpts(CodeGenOpts),
|
||||
TargetOpts(TargetOpts), Counter(Counter) {}
|
||||
@ -577,7 +577,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadCounter(const serialization::ModuleFile &M,
|
||||
unsigned NewCounter) override {
|
||||
uint32_t NewCounter) override {
|
||||
Counter = NewCounter;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1542,6 +1542,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.
|
||||
|
||||
@ -1735,7 +1735,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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
38
clang/test/C/C2y/n3457.c
Normal file
38
clang/test/C/C2y/n3457.c
Normal file
@ -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
clang/test/C/C2y/n3457_1.c
Normal file
20
clang/test/C/C2y/n3457_1.c
Normal file
@ -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}}
|
||||
|
||||
10
clang/test/C/C2y/n3457_2.c
Normal file
10
clang/test/C/C2y/n3457_2.c
Normal file
@ -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
|
||||
@ -329,7 +329,7 @@ conformance.</p>
|
||||
<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>
|
||||
|
||||
@ -250,6 +250,10 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||
#define BENCHMARK_RESTORE_DEPRECATED_WARNING _Pragma("GCC diagnostic pop")
|
||||
#define BENCHMARK_DISABLE_PEDANTIC_WARNING \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wpedantic\"")
|
||||
#define BENCHMARK_RESTORE_PEDANTIC_WARNING _Pragma("GCC diagnostic pop")
|
||||
#elif defined(__NVCOMPILER)
|
||||
#define BENCHMARK_BUILTIN_EXPECT(x, y) __builtin_expect(x, y)
|
||||
#define BENCHMARK_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
|
||||
@ -257,6 +261,8 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
|
||||
_Pragma("diagnostic push") \
|
||||
_Pragma("diag_suppress deprecated_entity_with_custom_message")
|
||||
#define BENCHMARK_RESTORE_DEPRECATED_WARNING _Pragma("diagnostic pop")
|
||||
#define BENCHMARK_DISABLE_PEDANTIC_WARNING
|
||||
#define BENCHMARK_RESTORE_PEDANTIC_WARNING
|
||||
#else
|
||||
#define BENCHMARK_BUILTIN_EXPECT(x, y) x
|
||||
#define BENCHMARK_DEPRECATED_MSG(msg)
|
||||
@ -265,6 +271,8 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
|
||||
__LINE__) ") : warning note: " msg))
|
||||
#define BENCHMARK_DISABLE_DEPRECATED_WARNING
|
||||
#define BENCHMARK_RESTORE_DEPRECATED_WARNING
|
||||
#define BENCHMARK_DISABLE_PEDANTIC_WARNING
|
||||
#define BENCHMARK_RESTORE_PEDANTIC_WARNING
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
@ -1462,11 +1470,13 @@ class Fixture : public internal::Benchmark {
|
||||
// Check that __COUNTER__ is defined and that __COUNTER__ increases by 1
|
||||
// every time it is expanded. X + 1 == X + 0 is used in case X is defined to be
|
||||
// empty. If X is empty the expression becomes (+1 == +0).
|
||||
BENCHMARK_DISABLE_PEDANTIC_WARNING
|
||||
#if defined(__COUNTER__) && (__COUNTER__ + 1 == __COUNTER__ + 0)
|
||||
#define BENCHMARK_PRIVATE_UNIQUE_ID __COUNTER__
|
||||
#else
|
||||
#define BENCHMARK_PRIVATE_UNIQUE_ID __LINE__
|
||||
#endif
|
||||
BENCHMARK_RESTORE_PEDANTIC_WARNING
|
||||
|
||||
// Helpers for generating unique variable names
|
||||
#ifdef BENCHMARK_HAS_CXX11
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user