From 6d7b4213c70d813735b828ed45a51aff743d41d7 Mon Sep 17 00:00:00 2001 From: Cem Dervis Date: Tue, 3 Feb 2026 00:08:47 +0100 Subject: [PATCH] Add C2y features and explanations for some of them --- content/c-alignof-incomplete.md | 31 +++ content/c-case-range.md | 41 ++++ content/c-complex-inc-dec.md | 35 ++++ content/c-complex-literals.md | 35 ++++ content/c-const-constexpr.md | 35 ++++ content/c-counter-macro.md | 34 +++ content/c-countof.md | 33 +++ content/c-generic-type-operand.md | 39 ++++ content/c-if-declarations.md | 38 ++++ content/c-named-loops.md | 41 ++++ content/c-octal-literals.md | 37 ++++ features_c2y.yaml | 332 ++++++++++++++++++++++++++++++ 12 files changed, 731 insertions(+) create mode 100644 content/c-alignof-incomplete.md create mode 100644 content/c-case-range.md create mode 100644 content/c-complex-inc-dec.md create mode 100644 content/c-complex-literals.md create mode 100644 content/c-const-constexpr.md create mode 100644 content/c-counter-macro.md create mode 100644 content/c-countof.md create mode 100644 content/c-generic-type-operand.md create mode 100644 content/c-if-declarations.md create mode 100644 content/c-named-loops.md create mode 100644 content/c-octal-literals.md create mode 100644 features_c2y.yaml diff --git a/content/c-alignof-incomplete.md b/content/c-alignof-incomplete.md new file mode 100644 index 0000000..743ab0f --- /dev/null +++ b/content/c-alignof-incomplete.md @@ -0,0 +1,31 @@ +## What It Does + +The `alignof` operator can now be applied to incomplete array types. +The alignment of an incomplete array type is the same as the alignment of its element type. + +## Why It Matters + +Previously, `alignof` required a complete type, which prevented querying the alignment +of array types with unknown bounds at the point of use. +Since array alignment depends only on the element type, this restriction was unnecessary. + +## Example + +```c +#include +#include + +extern int incomplete_array[]; + +int main(void) { + // Alignment of incomplete array equals element alignment + printf("alignof(int[]) = %zu\n", alignof(int[])); + printf("alignof(int) = %zu\n", alignof(int)); + + printf("alignof(double[]) = %zu\n", alignof(double[])); + printf("alignof(double) = %zu\n", alignof(double)); + + // Works with extern declarations + printf("alignof(incomplete_array) = %zu\n", alignof(incomplete_array)); +} +``` diff --git a/content/c-case-range.md b/content/c-case-range.md new file mode 100644 index 0000000..23c4675 --- /dev/null +++ b/content/c-case-range.md @@ -0,0 +1,41 @@ +## What It Does + +Case range expressions allow a `case` label to match a contiguous range of integer values +using the syntax `case low ... high:`. +Both endpoints are inclusive, and the range covers all integer values from `low` to `high`. + +## Why It Matters + +Matching multiple consecutive values in a `switch` previously required listing each value +as a separate `case` label. +Case ranges express the intent directly and reduce repetition when handling contiguous value sets. + +## Example + +```c +#include + +const char *classify_char(char c) { + switch (c) { + case 'a' ... 'z': + return "lowercase letter"; + case 'A' ... 'Z': + return "uppercase letter"; + case '0' ... '9': + return "digit"; + case ' ': + case '\t': + case '\n': + return "whitespace"; + default: + return "other"; + } +} + +int main(void) { + printf("'g' is a %s\n", classify_char('g')); + printf("'M' is a %s\n", classify_char('M')); + printf("'7' is a %s\n", classify_char('7')); + printf("'@' is a %s\n", classify_char('@')); +} +``` diff --git a/content/c-complex-inc-dec.md b/content/c-complex-inc-dec.md new file mode 100644 index 0000000..609be29 --- /dev/null +++ b/content/c-complex-inc-dec.md @@ -0,0 +1,35 @@ +## What It Does + +The `++` and `--` operators can now be applied to complex number values. +Incrementing a complex number adds 1 to its real part, and decrementing subtracts 1 from its real part. +The imaginary part remains unchanged. + +## Why It Matters + +Increment and decrement operators were not defined for complex types in previous C standards. +This addition provides consistency with arithmetic operations on complex numbers, where adding +an integer affects only the real component. + +## Example + +```c +#include +#include + +int main(void) { + double complex z = 3.0 + 4.0i; + + printf("Initial: %.1f + %.1fi\n", creal(z), cimag(z)); + + ++z; // Adds 1 to real part + printf("After ++z: %.1f + %.1fi\n", creal(z), cimag(z)); + + z--; // Subtracts 1 from real part + printf("After z--: %.1f + %.1fi\n", creal(z), cimag(z)); + + // Works in expressions + double complex w = 1.0 + 2.0i; + double complex result = ++w + z; + printf("Result: %.1f + %.1fi\n", creal(result), cimag(result)); +} +``` diff --git a/content/c-complex-literals.md b/content/c-complex-literals.md new file mode 100644 index 0000000..6840350 --- /dev/null +++ b/content/c-complex-literals.md @@ -0,0 +1,35 @@ +## What It Does + +Complex literals allow imaginary numbers to be written directly using the +`i`, `I`, `j`, or `J` suffix on floating-point constants. +The suffix indicates an imaginary value with a real part of zero. +Combined with real constants via addition, this forms complete complex numbers. + +## Why It Matters + +Constructing complex numbers previously required the `CMPLX` macro or arithmetic operations. +Complex literals enable direct notation that matches mathematical convention and can appear in +contexts requiring constant expressions, such as static initializers. + +## Example + +```c +#include +#include + +int main(void) { + // Imaginary literals + double complex z1 = 3.14i; // 0 + 3.14i + float complex z2 = 2.0fi; // 0 + 2.0i (float) + + // Full complex numbers via addition + double complex z3 = 2.0 + 3.0i; // 2 + 3i + float complex z4 = 1.5f + 0.5fi; // 1.5 + 0.5i (float) + + printf("z1 = %.2f + %.2fi\n", creal(z1), cimag(z1)); + printf("z3 = %.2f + %.2fi\n", creal(z3), cimag(z3)); + + // Constant expressions allowed + static double complex table[] = {1.0 + 2.0i, 3.0 + 4.0i}; +} +``` diff --git a/content/c-const-constexpr.md b/content/c-const-constexpr.md new file mode 100644 index 0000000..4d51905 --- /dev/null +++ b/content/c-const-constexpr.md @@ -0,0 +1,35 @@ +## What It Does + +Const integer declarations with constant expression initializers are implicitly treated +as `constexpr`. A `const`-qualified integer variable initialized with a constant expression +in the same declaration becomes a compile-time constant, usable in contexts requiring +constant expressions such as array bounds and `_Static_assert`. + +## Why It Matters + +In C23, `const int n = 10; int arr[n];` created a variable-length array because `n` was +not a constant expression despite its constant initializer. +This conflicted with decades of existing practice where compilers treated such declarations +as compile-time constants. The change aligns the standard with existing implementations. + +## Example + +```c +#include + +int main(void) { + const int size = 10; // Implicitly constexpr + int array[size]; // Fixed-size array, not VLA + + const int doubled = size * 2; // Also constexpr + _Static_assert(doubled == 20, "should be 20"); + + // Chained constants work + const int a = 1; + const int b = a + 1; + const int c = b + 1; + _Static_assert(c == 3, "should be 3"); + + printf("Array has %d elements\n", size); +} +``` diff --git a/content/c-counter-macro.md b/content/c-counter-macro.md new file mode 100644 index 0000000..10c160b --- /dev/null +++ b/content/c-counter-macro.md @@ -0,0 +1,34 @@ +## What It Does + +The `__COUNTER__` predefined macro expands to an integer literal that starts at 0 +and increments by 1 each time it is expanded within a translation unit. +Each expansion produces a unique value, enabling generation of distinct identifiers or values at compile time. + +## Why It Matters + +Generating unique identifiers in macros previously required manual numbering or reliance on +`__LINE__`, which fails when multiple expansions occur on the same line. +`__COUNTER__` provides a reliable mechanism for creating unique names in macro-generated code. + +## Example + +```c +#include + +#define CONCAT_IMPL(a, b) a ## b +#define CONCAT(a, b) CONCAT_IMPL(a, b) +#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__) + +#define MAKE_TEMP(value) \ + int UNIQUE_NAME(temp_) = (value) + +int main(void) { + MAKE_TEMP(10); // Creates temp_0 + MAKE_TEMP(20); // Creates temp_1 + MAKE_TEMP(30); // Creates temp_2 + + printf("temp_0 = %d\n", temp_0); + printf("temp_1 = %d\n", temp_1); + printf("temp_2 = %d\n", temp_2); +} +``` diff --git a/content/c-countof.md b/content/c-countof.md new file mode 100644 index 0000000..926d38e --- /dev/null +++ b/content/c-countof.md @@ -0,0 +1,33 @@ +## What It Does + +The `_Countof` operator returns the number of elements in an array. +It takes either an array expression or an array type as its operand and evaluates to a constant +of type `size_t` representing the array's element count. + +## Why It Matters + +Computing array length previously required the idiom `sizeof(arr) / sizeof(arr[0])`, which +produces incorrect results when applied to a pointer instead of an array. +`_Countof` only accepts array types, providing a compile-time error when misused with pointers, +and directly expresses the intent of obtaining element count. + +## Example + +```c +#include + +int main(void) { + int numbers[10]; + char message[] = "Hello"; + + printf("numbers has %zu elements\n", _Countof(numbers)); // 10 + printf("message has %zu elements\n", _Countof(message)); // 6 (includes '\0') + + // Works with typedefs + typedef double vec3[3]; + printf("vec3 has %zu elements\n", _Countof(vec3)); // 3 + + // Compile-time constant + _Static_assert(_Countof(numbers) == 10, "expected 10 elements"); +} +``` diff --git a/content/c-generic-type-operand.md b/content/c-generic-type-operand.md new file mode 100644 index 0000000..2cfba2c --- /dev/null +++ b/content/c-generic-type-operand.md @@ -0,0 +1,39 @@ +## What It Does + +Generic selection with a type operand extends `_Generic` to accept a type name directly, +in addition to the existing expression form. +The syntax `_Generic(type-name, ...)` selects an association based on the provided type +without requiring a value of that type. + +## Why It Matters + +The original `_Generic` required an expression operand, forcing macros to manufacture a +value (often via a cast or compound literal) when only type-based dispatch was needed. +Type operands enable `_Generic` selection based purely on type information, supporting +metaprogramming patterns that operate on types rather than values. + +## Example + +```c +#include + +#define type_name(T) _Generic(T, \ + int: "int", \ + unsigned int: "unsigned int", \ + float: "float", \ + double: "double", \ + default: "unknown") + +#define alignment_of(T) _Generic(T, \ + char: 1, \ + short: _Alignof(short), \ + int: _Alignof(int), \ + double: _Alignof(double), \ + default: 0) + +int main(void) { + printf("Type name: %s\n", type_name(int)); + printf("Type name: %s\n", type_name(double)); + printf("Alignment of int: %d\n", alignment_of(int)); +} +``` diff --git a/content/c-if-declarations.md b/content/c-if-declarations.md new file mode 100644 index 0000000..ae82871 --- /dev/null +++ b/content/c-if-declarations.md @@ -0,0 +1,38 @@ +## What It Does + +If declarations allow a variable declaration with initialization in the condition of an `if` statement. +The declared variable's scope extends through the entire `if`/`else` block, +and its initialized value serves as the controlling expression. + +## Why It Matters + +Declaring a variable and testing it previously required either a separate declaration before the +`if` or an additional scope block. +If declarations reduce variable scope to where it is actually needed and eliminate the pattern of +declaring, initializing, and testing in three separate steps. + +## Example + +```c +#include +#include + +int get_value(void) { + return 42; +} + +int main(void) { + // Variable 'x' exists only within the if/else block + if (int x = get_value()) { + printf("Got non-zero value: %d\n", x); + } else { + printf("Got zero\n"); + } + // 'x' is not accessible here + + // Works with pointers too + if (char *env = getenv("HOME")) { + printf("HOME = %s\n", env); + } +} +``` diff --git a/content/c-named-loops.md b/content/c-named-loops.md new file mode 100644 index 0000000..4b40abf --- /dev/null +++ b/content/c-named-loops.md @@ -0,0 +1,41 @@ +## What It Does + +Named loops allow `break` and `continue` statements to target a specific enclosing loop or `switch` by name. +A label placed before an iteration statement or `switch` becomes a target that can be referenced by +`break label;` or `continue label;` to exit or continue that specific statement, even from deeply nested code. + +## Why It Matters + +Breaking out of multiple nested loops previously required a `goto` statement or additional flag variables. +Named loops provide a structured way to exit or continue outer loops directly, matching functionality +available in Java, JavaScript, and Rust. + +## Example + +```c +#include +#include + +int main(void) { + int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + int target = 5; + bool found = false; + int row = -1, col = -1; + + search: + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (matrix[i][j] == target) { + row = i; + col = j; + found = true; + break search; // exits both loops + } + } + } + + if (found) { + printf("Found %d at [%d][%d]\n", target, row, col); + } +} +``` diff --git a/content/c-octal-literals.md b/content/c-octal-literals.md new file mode 100644 index 0000000..e6016c4 --- /dev/null +++ b/content/c-octal-literals.md @@ -0,0 +1,37 @@ +## What It Does + +This feature introduces the `0o` and `0O` prefixes for octal integer literals as the preferred +syntax, marking the traditional leading-zero octal notation (e.g., `0755`) as obsolescent. +It also adds delimited escape sequences using braces, such as `\x{1F600}` for hexadecimal and `\o{755}` for octal escapes. + +## Why It Matters + +The traditional octal syntax where a leading zero indicates base 8 (e.g., `0777`) is a common source of +errors, as decimal numbers with leading zeros are misinterpreted. +The explicit `0o` prefix matches the binary `0b` prefix and indicates base 8 unambiguously. +Delimited escape sequences resolve parsing ambiguity when escape sequences are followed by valid hex or octal digits. + +## Example + +```c +#include + +int main(void) { + // Explicit octal prefix (preferred) + int permissions = 0o755; + int old_style = 0755; // Obsolescent, but still valid + + printf("permissions = %d (octal %o)\n", permissions, permissions); + + // Delimited escape sequences + char hex_delim[] = "\x{48}ello"; // 'H' via hex + char octal_delim[] = "\o{110}ello"; // 'H' via octal + + printf("%s\n", hex_delim); + printf("%s\n", octal_delim); + + // Resolves ambiguity: \x1 followed by 'F', not \x1F + char ambiguous[] = "\x{1}F"; + printf("Length: %zu\n", sizeof(ambiguous) - 1); // 2 characters +} +``` diff --git a/features_c2y.yaml b/features_c2y.yaml new file mode 100644 index 0000000..9eeb3fd --- /dev/null +++ b/features_c2y.yaml @@ -0,0 +1,332 @@ +--- +features: + - desc: "Round-trip rounding" + paper: N3232 + lib: true + support: + - GCC ? + - Clang + - Xcode ? + + - desc: "Slay Some Earthly Demons I" + paper: N3244 + support: + - GCC ? + - Clang (partial) + - Xcode ? + hints: + - target: Clang + msg: "Clang does not document the implementation-defined behavior for decay of an array with the register storage class specifier. Clang does not diagnose an extern inline function with no definition in the TU. Clang accepts and rejects redeclarations with/without an alignment specifier, depending on the order of the declarations." + + - desc: "Accessing byte arrays" + paper: N3254 + support: + - GCC 15 + - Clang 18 + - Xcode 16.4 + + - desc: "Support `++` and `--` on complex values" + paper: N3259 + content: c-complex-inc-dec.md + support: + - GCC 3 + - Clang 18 + - Xcode 16.4 + + - desc: "Generic selection expression with a type operand" + paper: N3260 + content: c-generic-type-operand.md + support: + - GCC 15 + - Clang 17 + - Xcode 16.4 + + - desc: "`alignof` of an incomplete array type" + paper: N3273 + content: c-alignof-incomplete.md + support: + - GCC 15 + - Clang 3.5 + + - desc: "Remove imaginary types" + paper: N3274 + support: + - Clang + - Xcode ? + + - desc: "Floating-point exceptions for macro replacements" + paper: N3286 + lib: true + support: + - Clang + - Xcode 16.4 + + - desc: "Introduce complex literals" + paper: N3298 + content: c-complex-literals.md + support: + - GCC 2.5 + - Clang + - Xcode 16.4 + + - desc: "Allowing stricter alignment for atomic types" + paper: N3312 + support: + - Clang + - Xcode 16.4 + + - desc: "Allow zero length operations on null pointers" + paper: N3322 + support: + - GCC 15 + - Clang + - Xcode 16.4 + + - desc: "How do you add one to something?" + paper: N3323 + support: + - Clang + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons III" + paper: N3341 + support: + - Clang + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons IV" + paper: N3342 + support: + - Clang + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons VI" + paper: N3344 + support: + - Clang 20 + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons VII" + paper: N3345 + support: + - Clang + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons VIII" + paper: N3346 + support: + - Clang + - Xcode ? + + - desc: "Slay Some Earthly Demons IX" + paper: N3347 + support: + - Clang + - Xcode 16.4 + + - desc: "Obsolete implicitly octal literals and delimited escape sequences" + paper: N3353 + content: c-octal-literals.md + support: + - GCC 15 + - Clang 21 + - Xcode ? + + - desc: "Named loops" + paper: N3355 + content: c-named-loops.md + support: + - GCC 15 + - Clang 22 + - Xcode ? + + - desc: "`if` declarations" + paper: N3356 + content: c-if-declarations.md + support: + - GCC 15 + + - desc: "Consistent wording for SNAN initialization" + paper: N3364 + support: + - Clang + - Xcode 16.4 + + - desc: "Case range expressions (`case x ... y`)" + paper: N3370 + content: c-case-range.md + support: + - GCC 2 + - Clang + - Xcode 16.4 + + - desc: "`stdarg.h` wording improvements" + paper: N3363 + support: + - Clang 5 + - Xcode 16.4 + + - desc: "`_Countof` operator" + paper: + - N3369 + - N3469 + content: c-countof.md + support: + - GCC 16 + - Clang 21 + + - desc: "Restartable Functions for Efficient Character Conversions" + paper: N3366 + support: + - Clang ? + - MSVC ? + - Xcode ? + + - desc: "Abs Without Undefined Behavior" + paper: N3349 + support: + - Clang ? + - MSVC ? + - Xcode ? + + - desc: "More Modern Bit Utilities" + paper: N3367 + support: + - Clang ? + - MSVC ? + - Xcode ? + + - desc: "Error conditions in ``" + paper: N3405 + lib: true + + - desc: "Slay Some Earthly Demons X" + paper: N3409 + support: + - Clang 21 + + - desc: "Slay Some Earthly Demons XI" + paper: N3410 + support: + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons XII" + paper: N3411 + support: + - Clang 21 + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons XIV" + paper: N3418 + + - desc: "Const integer declarations as implicit `constexpr`" + paper: N3443 + content: c-const-constexpr.md + + - desc: "Anonymous structure/union initialization" + paper: N3451 + support: + - Clang + + - desc: "The `__COUNTER__` predefined macro" + paper: N3457 + content: c-counter-macro.md + support: + - Clang 22 + + - desc: "Complex operators" + paper: N3460 + support: + - Clang 12 + - Xcode 16.4 + + - desc: "Range error definition followup" + paper: N3461 + lib: true + + - desc: "Preprocessor integer expressions" + paper: + - N3465 + - N3505 + support: + - Clang + + - desc: "Null pointer clarifications in library" + paper: N3466 + lib: true + support: + - GCC 16 + - Xcode 16.4 + + - desc: "`auto` as placeholder type for parameters" + paper: N3472 + + - desc: "Slay Some Earthly Demons XIII" + paper: N3478 + support: + - Clang + + - desc: "Slay Some Earthly Demons XVI" + paper: N3481 + support: + - Clang + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons XVII" + paper: N3482 + support: + - Clang + - Xcode 16.4 + + - desc: "Slay Some Earthly Demons V" + paper: N3484 + support: + - Xcode 16.4 + + - desc: "Error conditions for narrow type rounding" + paper: N3492 + lib: true + + - desc: "Clarify width macros specification" + paper: N3496 + support: + - Clang 20 + - Xcode 16.4 + + - desc: "`static_assert` without UB" + paper: N3525 + support: + - Clang + - Xcode ? + + - desc: "Member access of an incomplete object" + paper: N3532 + support: + - Clang + - Xcode 16.4 + + - desc: "Chasing Ghosts I: constant expressions" + paper: N3558 + support: + - GCC ? + - MSVC ? + - Clang ? + - Xcode ? + + - desc: "Generic replacement (quasi-literals)" + paper: N3605 + support: + - Clang + - Xcode ? + + - desc: "Allow calling `static inline` within `extern inline`" + paper: N3622 + support: + - Clang 22 + - Xcode ? + + - desc: "Earthly Demon XV: Definition of Main" + paper: N3623 + support: + - Clang + - Xcode ?