diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -368,6 +368,8 @@ def warn_cxx11_compat_decltype_auto_type_specifier : Warning< "'decltype(auto)' type specifier is incompatible with C++ standards before " "C++14">, InGroup, DefaultIgnore; +def err_c2x_auto_compound_literal_not_allowed: Error< + "'auto' is not allowed in a compound literal">; def ext_auto_type : Extension< "'__auto_type' is a GNU extension">, InGroup; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3796,7 +3796,7 @@ isStorageClass = true; break; case tok::kw_auto: - if (getLangOpts().CPlusPlus11) { + if (getLangOpts().CPlusPlus11 || getLangOpts().C2x) { if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1507,6 +1507,17 @@ } [[fallthrough]]; + case tok::kw_auto: { + // We are trying to detect if the second next token is '{' + // in this case it's must be a compound literal + // This is a temporary fix while we don't support C2x 6.5.2.5p4 + if (getLangOpts().C2x && GetLookAheadToken(2).getKind() == tok::l_brace) { + Diag(Tok, diag::err_c2x_auto_compound_literal_not_allowed); + return ExprError(); + } + } + [[fallthrough]]; + case tok::annot_decltype: case tok::kw_char: case tok::kw_wchar_t: @@ -1531,7 +1542,6 @@ case tok::kw___float128: case tok::kw___ibm128: case tok::kw_void: - case tok::kw_auto: case tok::kw_typename: case tok::kw_typeof: case tok::kw___vector: diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1360,8 +1360,9 @@ StorageClassSpecLoc = SourceLocation(); } // Diagnose if we've recovered from an ill-formed 'auto' storage class - // specifier in a pre-C++11 dialect of C++. - if (!S.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto) + // specifier in a pre-C++11 dialect of C++ or in a C2X dialect of C. + if ((!S.getLangOpts().CPlusPlus11 && !S.getLangOpts().C2x) && + TypeSpecType == TST_auto) S.Diag(TSTLoc, diag::ext_auto_type_specifier); if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 && StorageClassSpec == SCS_auto) diff --git a/clang/test/C/C2x/n3007.c b/clang/test/C/C2x/n3007.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n3007.c @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -std=c2x -verify %s + +/* WG14 N3007: Yes + * Type Inference for object definitions + */ +void test_qualifiers(int x, const int y) { + const auto a = x; + auto b = y; + static auto c = 1UL; + int* pa = &a; // expected-warning {{initializing 'int *' with an expression of type 'const int *' discards qualifiers}} + const int* pb = &b; + int* pc = &c; // expected-warning {{incompatible pointer types initializing 'int *' with an expression of type 'unsigned long *'}} + + const int ci = 12; + auto yup = ci; + yup = 12; +} + +void test_double(void) { + double A[3] = { 0 }; + auto pA = A; + auto qA = &A; + auto pi = 3.14; +} + +int test_auto_param(auto a) { // expected-error {{'auto' not allowed in function prototype}} + return (int)(a * 2); +} + +auto test_auto_return(float a, int b) { // expected-error {{'auto' not allowed in function return type}} + return ((a * b) * (a / b)); +} + +void test_sizeof_alignas(void) { + auto auto_size = sizeof(auto); // expected-error {{expected expression}} + _Alignas(4) auto b[4]; // expected-error {{'b' declared as array of 'auto'}} +} + +void test_arrary(void) { + auto a[4]; // expected-error {{'a' declared as array of 'auto'}} + auto b[] = {1, 2}; // expected-error {{'b' declared as array of 'auto'}} +} + +void test_structs(void) { + auto a = (struct { int a; } *)0; + struct B { auto b; }; // expected-error {{'auto' not allowed in struct member}} +} + +void test_misc(void) { + auto something; // expected-error {{declaration of variable 'something' with deduced type 'auto' requires an initializer}} + auto test_char = 'A'; + auto test_char_ptr = "test"; + auto auto_size = sizeof(auto); // expected-error {{expected expression}} + typedef auto auto_type; // expected-error {{'auto' not allowed in typedef}} + + _Static_assert(_Generic(test_char_ptr, const char * : 1, char * : 2) == 2, "C is weird"); +} + +void test_loop(void) { + auto j = 4; + for (auto i = j; i < 2 * j; i++); +} + +#define AUTO_MACRO(_NAME, ARG, ARG2, ARG3) \ + auto _NAME = ARG + (ARG2 / ARG3); + +// This macro should only work with integers due to the usage of binary operators +#define AUTO_INT_MACRO(_NAME, ARG, ARG2, ARG3) \ + auto _NAME = (ARG ^ ARG2) & ARG3; + +int test_macros(int in_int) { + auto a = in_int + 1; + AUTO_MACRO(b, 1.3, 2.5f, 3); + AUTO_INT_MACRO(c, 64, 23, 0xff); + AUTO_INT_MACRO(not_valid, 51.5, 25, 0xff); // expected-error {{invalid operands to binary expression ('double' and 'int')}} + return (a + (int)b) - c; +} diff --git a/clang/test/CodeGen/auto.c b/clang/test/CodeGen/auto.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/auto.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -std=c2x -emit-llvm %s -o - | FileCheck %s + +void basic_types(void) { + auto nb = 4; // CHECK: %nb = alloca i32, align 4 + auto dbl = 4.3; // CHECK: %dbl = alloca double, align 8 + auto lng = 4UL; // CHECK: %lng = alloca i{{32|64}}, align {{4|8}} + auto bl = true; // CHECK: %bl = alloca i8, align 1 + auto chr = 'A'; // CHECK: %chr = alloca i32, align 4 + auto str = "Test"; // CHECK: %str = alloca ptr, align 8 +} + +void misc_declarations(void) { + auto strct_ptr = (struct { int a; } *)0; // CHECK: %strct_ptr = alloca ptr, align 8 + auto int_cl = (int){13}; // CHECK: %int_cl = alloca i32, align 4 + auto double_cl = (double){2.5}; // CHECK: %double_cl = alloca double, align 8 + + auto se = ({ // CHECK: %se = alloca i32, align 4 + auto snb = 12; // CHECK: %snb = alloca i32, align 4 + snb; + }); +} + +void loop(void) { + auto j = 4; // CHECK: %j = alloca i32, align 4 + for (auto i = j; i < 2 * j; i++); // CHECK: %i = alloca i32, align 4 +} + +#define AUTO_MACRO(_NAME, ARG, ARG2, ARG3) \ + auto _NAME = ARG + (ARG2 / ARG3); + +#define AUTO_INT_MACRO(_NAME, ARG, ARG2, ARG3) \ + auto _NAME = (ARG ^ ARG2) & ARG3; + +int macros(int in_int) { + auto a = in_int + 1; // CHECK: %a = alloca i32, align 4 + AUTO_MACRO(b, 1.3, 2.5f, 3); // CHECK: %b = alloca double, align 8 + AUTO_INT_MACRO(c, 64, 23, 0xff); // CHECK: %c = alloca i32, align 4 + return (a + (int)b) - c; // CHECK: ret i32 %sub +} diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c new file mode 100644 --- /dev/null +++ b/clang/test/Parser/c2x-auto.c @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=expected,c2x -std=c2x %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,c17 -std=c17 %s + +#define AUTO_MACRO(_NAME, ARG, ARG2, ARG3) \ + auto _NAME = ARG + (ARG2 / ARG3); + +struct S { + int a; + auto b; // c2x-error {{'auto' not allowed in struct member}} \ + c17-error {{type name does not allow storage class to be specified}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + union { + char c; + auto smth; // c2x-error {{'auto' not allowed in union member}} \ + c17-error {{type name does not allow storage class to be specified}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + } u; +}; + +enum E : auto { // c2x-error {{'auto' not allowed here}} \ + c17-error {{expected a type}} \ + c17-error {{type name does not allow storage class to be specified}} + One, + Two, + Tree, +}; + +auto basic_usage(auto auto) { // c2x-error {{'auto' not allowed in function prototype}} \ + c2x-error {{'auto' not allowed in function return type}} \ + c2x-error {{cannot combine with previous 'auto' declaration specifier}} \ + c17-error {{invalid storage class specifier in function declarator}} \ + c17-error {{illegal storage class on function}} \ + c17-warning {{duplicate 'auto' declaration specifier}} \ + c17-warning {{omitting the parameter name in a function definition is a C2x extension}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + auto = 4; // expected-error {{expected identifier or '('}} + + auto a = 4; // c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + auto auto aa = 12; // c2x-error {{cannot combine with previous 'auto' declaration specifier}} \ + c17-warning {{duplicate 'auto' declaration specifier}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + auto b[4]; // c2x-error {{'b' declared as array of 'auto'}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + auto array[auto]; // expected-error {{expected expression}} \ + c2x-error {{declaration of variable 'array' with deduced type 'auto' requires an initializer}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + AUTO_MACRO(auto, 1, 2, 3); // c2x-error {{cannot combine with previous 'auto' declaration specifier}} \ + expected-error {{expected identifier or '('}} \ + c17-warning {{duplicate 'auto' declaration specifier}} + + auto c = (auto)a; // expected-error {{expected expression}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + return c; +} + +void structs(void) { + struct s_auto { auto a; }; // c2x-error {{'auto' not allowed in struct member}} \ + c17-error {{type name does not allow storage class to be specified}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + auto s_int = (struct { int a; } *)0; // c17-error {{incompatible pointer to integer conversion initializing 'int' with an expression of type}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + typedef auto auto_type; // c2x-error {{'auto' not allowed in typedef}} \ + c17-error {{cannot combine with previous 'typedef' declaration specifier}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} +} + +void sizeof_alignas(void) { + auto auto_size = sizeof(auto); // expected-error {{expected expression}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + _Alignas(4) auto b[4]; // c2x-error {{'b' declared as array of 'auto'}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} +} + +void generic_alignof_alignas(void) { + int g; + _Generic(g, auto : 0); // c2x-error {{'auto' not allowed here}} \ + c17-error {{expected a type}} \ + c17-error {{type name does not allow storage class to be specified}} + + _Alignof(auto); // expected-error {{expected expression}} \ + expected-warning {{'_Alignof' applied to an expression is a GNU extension}} + + _Alignas(auto); // expected-error {{expected expression}} \ + expected-warning {{declaration does not declare anything}} +} + +void test_function_pointers(void) { + extern auto auto_ret_func(void); // c2x-error {{'auto' not allowed in function return type}} \ + c17-error {{cannot combine with previous 'extern' declaration specifier}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + extern void auto_param_func(auto); // c2x-error {{'auto' not allowed in function prototype}} \ + c17-error {{invalid storage class specifier in function declarator}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + auto (auto_ret_func)(void); // c2x-error {{'auto' not allowed in function return type}} \ + c17-error {{illegal storage class on function}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + void (auto_param_func)(auto); // c2x-error {{'auto' not allowed in function prototype}} \ + c17-error {{invalid storage class specifier in function declarator}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} +} + +void atomic(void) { + _Atomic(auto) atom1 = 12; // c2x-error {{'auto' not allowed here}} \ + c2x-error {{a type specifier is required for all declarations}} \ + c17-error {{expected a type}} \ + c17-error {{type name does not allow storage class to be specified}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + + _Atomic auto atom2 = 12; // c2x-error {{_Atomic cannot be applied to type 'auto' which is not trivially copyable}} \ + c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} +} diff --git a/clang/test/Sema/c2x-auto.c b/clang/test/Sema/c2x-auto.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/c2x-auto.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c2x %s + +void test_basic_types(void) { + auto undefined; // expected-error {{declaration of variable 'undefined' with deduced type 'auto' requires an initializer}} + auto auto_int = 4; + auto auto_long = 4UL; +} + +void test_sizeof_typeof(void) { + auto auto_size = sizeof(auto); // expected-error {{expected expression}} + typeof(auto) tpof = 4; // expected-error {{expected expression}} +} + +void test_casts(void) { + auto int_cast = (int)(4 + 3); + auto double_cast = (double)(1 / 3); + auto long_cast = (long)(4UL + 3UL); + auto auto_cast = (auto)(4 + 3); // expected-error {{expected expression}} +} + +void test_compound_literral(void) { + auto int_cl = (int){13}; + auto double_cl = (double){2.5}; + + // FIXME: This should be accepted per C2x 6.5.2.5p4 + auto auto_cl = (auto){13}; // expected-error {{'auto' is not allowed in a compound literal}} + auto array[] = { 1, 2, 3 }; // expected-error {{'array' declared as array of 'auto'}} +} + +void test_qualifiers(int x, const int y) { + const auto a = x; + auto b = y; + static auto c = 1UL; + int* pa = &a; // expected-warning {{initializing 'int *' with an expression of type 'const int *' discards qualifiers}} + const int* pb = &b; + int* pc = &c; // expected-warning {{incompatible pointer types initializing 'int *' with an expression of type 'unsigned long *'}} +} + +void test_scopes(void) { + { + auto a = 7; + auto b = 9; + auto c = a + b; + } + { + auto d = a * b; // expected-error {{use of undeclared identifier 'a'}} \ + expected-error {{use of undeclared identifier 'b'}} + { + auto e = d + c; // expected-error {{use of undeclared identifier 'c'}} + } + } +} diff --git a/clang/www/c_status.html b/clang/www/c_status.html --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -1193,7 +1193,7 @@ Type inference for object declarations N3007 - No + Clang 16 constexpr for object definitions