Skip to content

Commit f3c0bc1

Browse files
committed
[C2y] Add octal prefixes, deprecate unprefixed octals
WG14 N3353 added support for 0o and 0O as octal literal prefixes. It also deprecates use of octal literals without a prefix, except for the literal 0. This feature is being exposed as an extension in older C language modes as well as in all C++ language modes.
1 parent e0223fa commit f3c0bc1

File tree

7 files changed

+134
-2
lines changed

7 files changed

+134
-2
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,7 @@ Designated initializers (N494) C
16521652
Array & element qualification (N2607) C23 C89
16531653
Attributes (N2335) C23 C89
16541654
``#embed`` (N3017) C23 C89, C++
1655+
Octal literals prefixed with ``0o`` or ``0O`` C2y C89, C++
16551656
============================================= ================================ ============= =============
16561657

16571658
Builtin type aliases

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ C2y Feature Support
120120
- Implemented `WG14 N3411 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf>`_
121121
which allows a source file to not end with a newline character. This is still
122122
reported as a conforming extension in earlier language modes.
123+
- Implemented `WG14 N3353 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3353.htm>_`
124+
which adds the new ``0o`` and ``0O`` ocal literal prefixes and deprecates
125+
octal literals other than ``0`` which do not start with the new prefix. This
126+
feature is exposed in earlier language modes and in C++ as an extension.
123127

124128
C23 Feature Support
125129
^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ def EnumCompare : DiagGroup<"enum-compare", [EnumCompareSwitch,
9292
def DeprecatedAnonEnumEnumConversion : DiagGroup<"deprecated-anon-enum-enum-conversion">;
9393
def DeprecatedEnumEnumConversion : DiagGroup<"deprecated-enum-enum-conversion">;
9494
def DeprecatedEnumFloatConversion : DiagGroup<"deprecated-enum-float-conversion">;
95+
def DeprecatedOctalLiterals : DiagGroup<"deprecated-octal-literals">;
9596
def AnonEnumEnumConversion : DiagGroup<"anon-enum-enum-conversion",
9697
[DeprecatedAnonEnumEnumConversion]>;
9798
def EnumEnumConversion : DiagGroup<"enum-enum-conversion",
@@ -235,7 +236,8 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
235236
DeprecatedVolatile,
236237
DeprecatedWritableStr,
237238
DeprecatedRedundantConstexprStaticDef,
238-
DeprecatedMissingCommaVariadicParam
239+
DeprecatedMissingCommaVariadicParam,
240+
DeprecatedOctalLiterals
239241
]>,
240242
DiagCategory<"Deprecations">;
241243

clang/include/clang/Basic/DiagnosticLexKinds.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,14 @@ def warn_cxx17_hex_literal : Warning<
256256
"hexadecimal floating literals are incompatible with "
257257
"C++ standards before C++17">,
258258
InGroup<CXXPre17CompatPedantic>, DefaultIgnore;
259+
def ext_octal_literal : Extension<
260+
"octal integer literals are a C2y extension">, InGroup<C2y>;
261+
def warn_c2y_compat_octal_literal : Warning<
262+
"octal integer literals are incompatible with standards before C2y">,
263+
InGroup<CPre2yCompat>, DefaultIgnore;
264+
def warn_unprefixed_octal_deprecated : Warning<
265+
"octal literals without a '0o' prefix are deprecated">,
266+
InGroup<DeprecatedOctalLiterals>;
259267
def ext_binary_literal : Extension<
260268
"binary integer literals are a C23 extension">, InGroup<C23>;
261269
def warn_c23_compat_binary_literal : Warning<

clang/lib/Lex/LiteralSupport.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "clang/Lex/Preprocessor.h"
2222
#include "clang/Lex/Token.h"
2323
#include "llvm/ADT/APInt.h"
24+
#include "llvm/ADT/ScopeExit.h"
2425
#include "llvm/ADT/SmallVector.h"
2526
#include "llvm/ADT/StringExtras.h"
2627
#include "llvm/ADT/StringSwitch.h"
@@ -1423,6 +1424,27 @@ void NumericLiteralParser::ParseNumberStartingWithZero(SourceLocation TokLoc) {
14231424
return;
14241425
}
14251426

1427+
// Parse a potential octal literal prefix.
1428+
bool SawOctalPrefix = false;
1429+
if ((c1 == 'O' || c1 == 'o') && (s[1] >= '0' && s[1] <= '7')) {
1430+
unsigned DiagId;
1431+
if (LangOpts.C2y)
1432+
DiagId = diag::warn_c2y_compat_octal_literal;
1433+
else
1434+
DiagId = diag::ext_octal_literal;
1435+
Diags.Report(TokLoc, DiagId);
1436+
++s;
1437+
DigitsBegin = s;
1438+
SawOctalPrefix = true;
1439+
}
1440+
1441+
auto _ = llvm::make_scope_exit([&] {
1442+
// If we still have an octal value but we did not see an octal prefix,
1443+
// diagnose as being an obsolescent feature starting in C2y.
1444+
if (radix == 8 && LangOpts.C2y && !SawOctalPrefix && !hadError)
1445+
Diags.Report(TokLoc, diag::warn_unprefixed_octal_deprecated);
1446+
});
1447+
14261448
// For now, the radix is set to 8. If we discover that we have a
14271449
// floating point constant, the radix will change to 10. Octal floating
14281450
// point constants are not permitted (only decimal and hexadecimal).

clang/test/C/C2y/n3353.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// RUN: %clang_cc1 -verify=expected,c2y -pedantic -std=c2y %s
2+
// RUN: %clang_cc1 -verify=expected,c2y,compat -Wpre-c2y-compat -std=c2y %s
3+
// RUN: %clang_cc1 -verify=expected,ext -pedantic -std=c23 %s
4+
// RUN: %clang_cc1 -verify=expected,ext -pedantic -x c++ -Wno-c11-extensions %s
5+
6+
7+
/* WG14 N3353: Clang 21
8+
* Obsolete implicitly octal literals and add delimited escape sequences
9+
*/
10+
11+
constexpr int i = 0234; // c2y-warning {{octal literals without a '0o' prefix are deprecated}}
12+
constexpr int j = 0o234; /* ext-warning {{octal integer literals are a C2y extension}}
13+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
14+
*/
15+
16+
static_assert(i == 156);
17+
static_assert(j == 156);
18+
19+
// Show that 0O is the same as Oo (tested above)
20+
static_assert(0O1234 == 0o1234); /* ext-warning 2 {{octal integer literals are a C2y extension}}
21+
compat-warning 2 {{octal integer literals are incompatible with standards before C2y}}
22+
*/
23+
24+
// Demonstrate that it works fine in the preprocessor.
25+
#if 0o123 != 0x53 /* ext-warning {{octal integer literals are a C2y extension}}
26+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
27+
*/
28+
#error "oh no, math stopped working!"
29+
#endif
30+
31+
// 0 by itself is not deprecated, of course.
32+
int k = 0;
33+
34+
// Make sure there are no surprises with auto and type deduction. Promotion
35+
// turns this into an 'int', and 'constexpr' implies 'const'.
36+
constexpr auto l = 0o1234567; /* ext-warning {{octal integer literals are a C2y extension}}
37+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
38+
*/
39+
static_assert(l == 0x53977);
40+
static_assert(__extension__ _Generic(typeof(0o1), typeof(01) : 1, default : 0)); /* c2y-warning {{octal literals without a '0o' prefix are deprecated}}
41+
compat-warning {{passing a type argument as the first operand to '_Generic' is incompatible with C standards before C2y}}
42+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
43+
*/
44+
static_assert(__extension__ _Generic(typeof(l), const int : 1, default : 0)); // compat-warning {{passing a type argument as the first operand to '_Generic' is incompatible with C standards before C2y}}
45+
46+
// Note that 0o by itself is an invalid literal.
47+
int m = 0o; /* expected-error {{invalid suffix 'o' on integer constant}}
48+
c2y-warning {{octal literals without a '0o' prefix are deprecated}}
49+
*/
50+
51+
// Ensure negation works as expected.
52+
static_assert(-0o1234 == -668); /* ext-warning {{octal integer literals are a C2y extension}}
53+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
54+
*/
55+
56+
// FIXME: it would be better to not diagnose the compat and ext warnings when
57+
// the octal literal is invalid.
58+
// We expect diagnostics for non-octal digits.
59+
int n = 0o18; /* expected-error {{invalid digit '8' in octal constant}}
60+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
61+
ext-warning {{octal integer literals are a C2y extension}}
62+
*/
63+
int o1 = 0o8; /* expected-error {{invalid suffix 'o8' on integer constant}}
64+
c2y-warning {{octal literals without a '0o' prefix are deprecated}}
65+
*/
66+
// FIXME: however, it matches the behavior for hex literals in terms of the
67+
// error reported. Unfortunately, we then go on to think 0 is an octal literal
68+
// without a prefix, which is again a bit confusing.
69+
int o2 = 0xG; /* expected-error {{invalid suffix 'xG' on integer constant}}
70+
c2y-warning {{octal literals without a '0o' prefix are deprecated}}
71+
*/
72+
73+
// Ensure digit separators work as expected.
74+
constexpr int p = 0o0'1'2'3'4'5'6'7; /* compat-warning {{octal integer literals are incompatible with standards before C2y}}
75+
ext-warning {{octal integer literals are a C2y extension}}
76+
*/
77+
static_assert(p == 01234567); // c2y-warning {{octal literals without a '0o' prefix are deprecated}}
78+
int q = 0o'0'1; /* expected-error {{invalid suffix 'o'0'1' on integer constant}}
79+
c2y-warning {{octal literals without a '0o' prefix are deprecated}}
80+
*/
81+
82+
#ifdef __cplusplus
83+
template <unsigned N>
84+
struct S {
85+
static_assert(N == 0o567); /* ext-warning {{octal integer literals are a C2y extension}}
86+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
87+
*/
88+
};
89+
90+
void foo() {
91+
S<0o567> s; /* ext-warning {{octal integer literals are a C2y extension}}
92+
compat-warning {{octal integer literals are incompatible with standards before C2y}}
93+
*/
94+
}
95+
#endif

clang/www/c_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ <h2 id="c2y">C2y implementation status</h2>
176176
<tr>
177177
<td>Obsolete implicitly octal literals and add delimited escape sequences</td>
178178
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3353.htm">N3353</a></td>
179-
<td class="none" align="center">No</td>
179+
<td class="unreleased" align="center">Clang 21</td>
180180
</tr>
181181
<tr>
182182
<td>'if' declarations, v2</td>

0 commit comments

Comments
 (0)