Skip to content

Commit 5d78b78

Browse files
to268AaronBallman
authored andcommitted
[C2X] N3007 Type inference for object definitions
This patches implements the auto keyword from the N3007 standard specification. This allows deducing the type of the variable like in C++: ``` auto nb = 1; auto chr = 'A'; auto str = "String"; ``` The list of statements which allows the usage of auto: * Basic variables declarations (int, float, double, char, char*...) * Macros declaring a variable with the auto type The list of statements which will not work with the auto keyword: * auto arrays * sizeof(), alignas() * auto parameters, auto return type * auto as a struct/typedef member * uninitialized auto variables * auto in an union * auto as a enum type specifier * auto casts * auto in an compound literals Differential Revision: https://reviews.llvm.org/D133289
1 parent 58678d3 commit 5d78b78

File tree

11 files changed

+542
-11
lines changed

11 files changed

+542
-11
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ def ext_imaginary_constant : Extension<
240240
"imaginary constants are a GNU extension">, InGroup<GNUImaginaryConstant>;
241241
def ext_integer_complex : Extension<
242242
"complex integer types are a GNU extension">, InGroup<GNUComplexInteger>;
243+
def ext_c23_auto_non_plain_identifier : Extension<
244+
"type inference of a declaration other than a plain identifier with optional "
245+
"trailing attributes is a Clang extension">,
246+
InGroup<DiagGroup<"auto-decl-extensions">>;
243247

244248
def err_invalid_saturation_spec : Error<"'_Sat' specifier is only valid on "
245249
"'_Fract' or '_Accum', not '%0'">;
@@ -2388,7 +2392,8 @@ def err_auto_not_allowed : Error<
23882392
"|in conversion function type|here|in lambda parameter"
23892393
"|in type allocated by 'new'|in K&R-style function parameter"
23902394
"|in template parameter|in friend declaration|in function prototype that is "
2391-
"not a function declaration|in requires expression parameter}1">;
2395+
"not a function declaration|in requires expression parameter"
2396+
"|in array declaration}1">;
23922397
def err_dependent_deduced_tst : Error<
23932398
"typename specifier refers to "
23942399
"%select{class template|function template|variable template|alias template|"
@@ -2461,7 +2466,8 @@ def err_implied_std_initializer_list_not_found : Error<
24612466
def err_malformed_std_initializer_list : Error<
24622467
"std::initializer_list must be a class template with a single type parameter">;
24632468
def err_auto_init_list_from_c : Error<
2464-
"cannot use __auto_type with initializer list in C">;
2469+
"cannot use %select{'auto'|<ERROR>|'__auto_type'}0 with "
2470+
"%select{initializer list|array}1 in C">;
24652471
def err_auto_bitfield : Error<
24662472
"cannot pass bit-field as __auto_type initializer in C">;
24672473

@@ -6664,8 +6670,8 @@ def err_func_def_incomplete_result : Error<
66646670
def err_atomic_specifier_bad_type
66656671
: Error<"_Atomic cannot be applied to "
66666672
"%select{incomplete |array |function |reference |atomic |qualified "
6667-
"|sizeless ||integer }0type "
6668-
"%1 %select{|||||||which is not trivially copyable|}0">;
6673+
"|sizeless ||integer |}0type "
6674+
"%1 %select{|||||||which is not trivially copyable||in C23}0">;
66696675
def warn_atomic_member_access : Warning<
66706676
"accessing a member of an atomic structure or union is undefined behavior">,
66716677
InGroup<DiagGroup<"atomic-access">>, DefaultError;

clang/lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4038,7 +4038,7 @@ void Parser::ParseDeclarationSpecifiers(
40384038
isStorageClass = true;
40394039
break;
40404040
case tok::kw_auto:
4041-
if (getLangOpts().CPlusPlus11) {
4041+
if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
40424042
if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
40434043
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
40444044
PrevSpec, DiagID, Policy);

clang/lib/Sema/DeclSpec.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,8 +1375,9 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
13751375
StorageClassSpecLoc = SourceLocation();
13761376
}
13771377
// Diagnose if we've recovered from an ill-formed 'auto' storage class
1378-
// specifier in a pre-C++11 dialect of C++.
1379-
if (!S.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto)
1378+
// specifier in a pre-C++11 dialect of C++ or in a pre-C23 dialect of C.
1379+
if (!S.getLangOpts().CPlusPlus11 && !S.getLangOpts().C23 &&
1380+
TypeSpecType == TST_auto)
13801381
S.Diag(TSTLoc, diag::ext_auto_type_specifier);
13811382
if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 &&
13821383
StorageClassSpec == SCS_auto)

clang/lib/Sema/SemaDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12863,6 +12863,15 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl,
1286312863
DeducedType *Deduced = Type->getContainedDeducedType();
1286412864
assert(Deduced && "deduceVarTypeFromInitializer for non-deduced type");
1286512865

12866+
// Diagnose auto array declarations in C23, unless it's a supported extension.
12867+
if (getLangOpts().C23 && Type->isArrayType() &&
12868+
!isa_and_present<StringLiteral, InitListExpr>(Init)) {
12869+
Diag(Range.getBegin(), diag::err_auto_not_allowed)
12870+
<< (int)Deduced->getContainedAutoType()->getKeyword()
12871+
<< /*in array decl*/ 23 << Range;
12872+
return QualType();
12873+
}
12874+
1286612875
// C++11 [dcl.spec.auto]p3
1286712876
if (!Init) {
1286812877
assert(VDecl && "no init for init capture deduction?");

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4842,9 +4842,25 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
48424842
return TDK_Success;
48434843
}
48444844

4845+
// Make sure that we treat 'char[]' equaly as 'char*' in C23 mode.
4846+
auto *String = dyn_cast<StringLiteral>(Init);
4847+
if (getLangOpts().C23 && String && Type.getType()->isArrayType()) {
4848+
Diag(Type.getBeginLoc(), diag::ext_c23_auto_non_plain_identifier);
4849+
TypeLoc TL = TypeLoc(Init->getType(), Type.getOpaqueData());
4850+
Result = SubstituteDeducedTypeTransform(*this, DependentResult).Apply(TL);
4851+
assert(!Result.isNull() && "substituting DependentTy can't fail");
4852+
return TDK_Success;
4853+
}
4854+
4855+
// Emit a warning if 'auto*' is used in pedantic and in C23 mode.
4856+
if (getLangOpts().C23 && Type.getType()->isPointerType()) {
4857+
Diag(Type.getBeginLoc(), diag::ext_c23_auto_non_plain_identifier);
4858+
}
4859+
48454860
auto *InitList = dyn_cast<InitListExpr>(Init);
48464861
if (!getLangOpts().CPlusPlus && InitList) {
4847-
Diag(Init->getBeginLoc(), diag::err_auto_init_list_from_c);
4862+
Diag(Init->getBeginLoc(), diag::err_auto_init_list_from_c)
4863+
<< (int)AT->getKeyword() << getLangOpts().C23;
48484864
return TDK_AlreadyDiagnosed;
48494865
}
48504866

clang/lib/Sema/SemaType.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9880,11 +9880,14 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
98809880
DisallowedKind = 5;
98819881
else if (T->isSizelessType())
98829882
DisallowedKind = 6;
9883-
else if (!T.isTriviallyCopyableType(Context))
9883+
else if (!T.isTriviallyCopyableType(Context) && getLangOpts().CPlusPlus)
98849884
// Some other non-trivially-copyable type (probably a C++ class)
98859885
DisallowedKind = 7;
98869886
else if (T->isBitIntType())
98879887
DisallowedKind = 8;
9888+
else if (getLangOpts().C23 && T->isUndeducedAutoType())
9889+
// _Atomic auto is prohibited in C23
9890+
DisallowedKind = 9;
98889891

98899892
if (DisallowedKind != -1) {
98909893
Diag(Loc, diag::err_atomic_specifier_bad_type) << DisallowedKind << T;

clang/test/C/C2x/n3007.c

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// RUN: %clang_cc1 -std=c2x -verify -pedantic -Wno-comments %s
2+
3+
/* WG14 N3007: Yes
4+
* Type Inference for object definitions
5+
*/
6+
void test_qualifiers(int x, const int y, int * restrict z) {
7+
const auto a = x;
8+
auto b = y;
9+
static auto c = 1UL;
10+
int* pa = &a; // expected-warning {{initializing 'int *' with an expression of type 'const int *' discards qualifiers}}
11+
const int* pb = &b;
12+
int* pc = &c; // expected-warning {{incompatible pointer types initializing 'int *' with an expression of type 'unsigned long *'}}
13+
14+
const int ci = 12;
15+
auto yup = ci;
16+
yup = 12;
17+
18+
auto r_test = z;
19+
20+
_Static_assert(_Generic(a, int : 1));
21+
_Static_assert(_Generic(c, unsigned long : 1));
22+
_Static_assert(_Generic(pa, int * : 1));
23+
_Static_assert(_Generic(pb, const int * : 1));
24+
_Static_assert(_Generic(r_test, int * : 1));
25+
}
26+
27+
void test_atomic(void) {
28+
_Atomic auto i = 12; // expected-error {{_Atomic cannot be applied to type 'auto' in C23}}
29+
_Atomic(auto) j = 12; // expected-error {{'auto' not allowed here}} \
30+
expected-error {{a type specifier is required for all declarations}}
31+
32+
_Atomic(int) foo(void);
33+
auto k = foo();
34+
35+
_Static_assert(_Generic(&i, _Atomic auto *: 1)); // expected-error {{_Atomic cannot be applied to type 'auto' in C23}} \
36+
expected-error {{'auto' not allowed here}}
37+
_Static_assert(_Generic(k, int: 1));
38+
}
39+
40+
void test_double(void) {
41+
double A[3] = { 0 };
42+
auto pA = A;
43+
auto qA = &A;
44+
auto pi = 3.14;
45+
46+
_Static_assert(_Generic(A, double * : 1));
47+
_Static_assert(_Generic(pA, double * : 1));
48+
_Static_assert(_Generic(qA, double (*)[3] : 1));
49+
_Static_assert(_Generic(pi, double : 1));
50+
}
51+
52+
int test_auto_param(auto a) { // expected-error {{'auto' not allowed in function prototype}}
53+
return (int)(a * 2);
54+
}
55+
56+
auto test_auto_return(float a, int b) { // expected-error {{'auto' not allowed in function return type}}
57+
return ((a * b) * (a / b));
58+
}
59+
60+
[[clang::overloadable]] auto test(auto x) { // expected-error {{'auto' not allowed in function prototype}} \
61+
expected-error {{'auto' not allowed in function return type}}
62+
return x;
63+
}
64+
65+
void test_sizeof_alignas(void) {
66+
(void)sizeof(auto); // expected-error {{expected expression}}
67+
_Alignas(auto) int a[4]; // expected-error {{expected expression}}
68+
}
69+
70+
void test_arrary(void) {
71+
auto a[4]; // expected-error {{'auto' not allowed in array declaration}}
72+
auto b[] = {1, 2}; // expected-error {{cannot use 'auto' with array in C}}
73+
}
74+
75+
void test_initializer_list(void) {
76+
auto a = {}; // expected-error {{cannot use 'auto' with array in C}}
77+
auto b = { 0 }; // expected-error {{cannot use 'auto' with array in C}}
78+
auto c = { 1, }; // expected-error {{cannot use 'auto' with array in C}}
79+
auto d = { 1 , 2 }; // expected-error {{cannot use 'auto' with array in C}}
80+
auto e = (int [3]){ 1, 2, 3 };
81+
}
82+
83+
void test_structs(void) {
84+
// FIXME: Both of these should be diagnosed as invalid underspecified
85+
// declarations as described in N3006.
86+
auto p1 = (struct { int a; } *)0;
87+
struct s;
88+
auto p2 = (struct s { int a; } *)0;
89+
90+
struct B { auto b; }; // expected-error {{'auto' not allowed in struct member}}
91+
}
92+
93+
void test_typedefs(void) {
94+
typedef auto auto_type; // expected-error {{'auto' not allowed in typedef}}
95+
96+
typedef auto (*fp)(void); // expected-error {{'auto' not allowed in typedef}}
97+
typedef void (*fp)(auto); // expected-error {{'auto' not allowed in function prototype}}
98+
99+
_Generic(0, auto : 1); // expected-error {{'auto' not allowed here}}
100+
}
101+
102+
void test_misc(void) {
103+
auto something; // expected-error {{declaration of variable 'something' with deduced type 'auto' requires an initializer}}
104+
auto test_char = 'A';
105+
auto test_char_ptr = "test";
106+
auto test_char_ptr2[] = "another test"; // expected-warning {{type inference of a declaration other than a plain identifier with optional trailing attributes is a Clang extension}}
107+
auto auto_size = sizeof(auto); // expected-error {{expected expression}}
108+
109+
_Static_assert(_Generic(test_char, int : 1));
110+
_Static_assert(_Generic(test_char_ptr, char * : 1));
111+
_Static_assert(_Generic(test_char_ptr2, char * : 1));
112+
}
113+
114+
void test_no_integer_promotions(void) {
115+
short s;
116+
auto a = s;
117+
_Generic(a, int : 1); // expected-error {{controlling expression type 'short' not compatible with any generic association type}}
118+
}
119+
120+
void test_compound_literals(void) {
121+
auto a = (int){};
122+
auto b = (int){ 0 };
123+
auto c = (int){ 0, };
124+
auto d = (int){ 0, 1 }; // expected-warning {{excess elements in scalar initializer}}
125+
126+
auto auto_cl = (auto){13}; // expected-error {{expected expression}}
127+
128+
_Static_assert(_Generic(a, int : 1));
129+
_Static_assert(_Generic(b, int : 1));
130+
_Static_assert(_Generic(c, int : 1));
131+
}
132+
133+
void test_pointers(void) {
134+
int a;
135+
auto *ptr = &a; // expected-warning {{type inference of a declaration other than a plain identifier with optional trailing attributes is a Clang extension}}
136+
auto *ptr2 = a; // expected-error {{variable 'ptr2' with type 'auto *' has incompatible initializer of type 'int'}} \
137+
expected-warning {{type inference of a declaration other than a plain identifier with optional trailing attributes is a Clang extension}}
138+
auto nptr = nullptr;
139+
140+
_Static_assert(_Generic(ptr, int * : 1));
141+
_Static_assert(_Generic(ptr2, int * : 1));
142+
}
143+
144+
void test_scopes(void) {
145+
double a = 7;
146+
double b = 9;
147+
{
148+
auto a = a * a; // expected-error {{variable 'a' declared with deduced type 'auto' cannot appear in its own initializer}} \
149+
expected-error {{variable 'a' declared with deduced type 'auto' cannot appear in its own initializer}}
150+
}
151+
{
152+
auto b = a * a;
153+
auto a = b;
154+
155+
_Static_assert(_Generic(a, double : 1));
156+
_Static_assert(_Generic(b, double : 1));
157+
}
158+
}
159+
160+
void test_loop(void) {
161+
auto j = 4;
162+
for (auto i = j; i < 2 * j; i++);
163+
164+
_Static_assert(_Generic(j, int : 1));
165+
}
166+
167+
#define AUTO_MACRO(_NAME, ARG, ARG2, ARG3) \
168+
auto _NAME = ARG + (ARG2 / ARG3);
169+
170+
// This macro should only work with integers due to the usage of binary operators
171+
#define AUTO_INT_MACRO(_NAME, ARG, ARG2, ARG3) \
172+
auto _NAME = (ARG ^ ARG2) & ARG3;
173+
174+
void test_macros(int in_int) {
175+
auto a = in_int + 1;
176+
AUTO_MACRO(b, 1.3, 2.5f, 3);
177+
AUTO_INT_MACRO(c, 64, 23, 0xff);
178+
AUTO_INT_MACRO(not_valid, 51.5, 25, 0xff); // expected-error {{invalid operands to binary expression ('double' and 'int')}}
179+
180+
auto result = (a + (int)b) - c;
181+
182+
_Static_assert(_Generic(a, int : 1));
183+
_Static_assert(_Generic(b, double : 1));
184+
_Static_assert(_Generic(c, int : 1));
185+
_Static_assert(_Generic(result, int : 1));
186+
}

clang/test/CodeGen/auto.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %clang_cc1 -std=c2x -emit-llvm %s -o - | FileCheck %s
2+
3+
void basic_types(void) {
4+
auto nb = 4; // CHECK: %nb = alloca i32, align 4
5+
auto dbl = 4.3; // CHECK: %dbl = alloca double, align 8
6+
auto lng = 4UL; // CHECK: %lng = alloca i{{32|64}}, align {{4|8}}
7+
auto bl = true; // CHECK: %bl = alloca i8, align 1
8+
auto chr = 'A'; // CHECK: %chr = alloca i32, align 4
9+
auto str = "Test"; // CHECK: %str = alloca ptr, align 8
10+
auto str2[] = "Test"; // CHECK: %str2 = alloca [5 x i8], align 1
11+
auto nptr = nullptr; // CHECK: %nptr = alloca ptr, align 8
12+
}
13+
14+
void misc_declarations(void) {
15+
// FIXME: this should end up being rejected when we implement underspecified
16+
// declarations in N3006.
17+
auto strct_ptr = (struct { int a; } *)0; // CHECK: %strct_ptr = alloca ptr, align 8
18+
auto int_cl = (int){13}; // CHECK: %int_cl = alloca i32, align 4
19+
auto double_cl = (double){2.5}; // CHECK: %double_cl = alloca double, align 8
20+
21+
auto se = ({ // CHECK: %se = alloca i32, align 4
22+
auto snb = 12; // CHECK: %snb = alloca i32, align 4
23+
snb;
24+
});
25+
}
26+
27+
void loop(void) {
28+
auto j = 4; // CHECK: %j = alloca i32, align 4
29+
for (auto i = j; i < 2 * j; i++); // CHECK: %i = alloca i32, align 4
30+
}
31+
32+
#define AUTO_MACRO(_NAME, ARG, ARG2, ARG3) auto _NAME = ARG + (ARG2 / ARG3);
33+
34+
#define AUTO_INT_MACRO(_NAME, ARG, ARG2, ARG3) auto _NAME = (ARG ^ ARG2) & ARG3;
35+
36+
int macros(int in_int) {
37+
auto a = in_int + 1; // CHECK: %a = alloca i32, align 4
38+
AUTO_MACRO(b, 1.3, 2.5f, 3); // CHECK: %b = alloca double, align 8
39+
AUTO_INT_MACRO(c, 64, 23, 0xff); // CHECK: %c = alloca i32, align 4
40+
return (a + (int)b) - c; // CHECK: ret i32 %sub
41+
}

0 commit comments

Comments
 (0)