to268 updated this revision to Diff 463415.
to268 marked 3 inline comments as done.
to268 added a comment.
I've fixed the diagnostic message and all of the bad TODOs comments from the
Sema test.
I've explained in details why i'm only able to handle `auto` in compound
literals.
In D133289#3784117 <https://reviews.llvm.org/D133289#3784117>, @aaron.ballman
wrote:
> One thought that occurred to me is that the way C has this specified is
> awfully effectively the same way implicit int worked. It may be worth
> exploring a change to our implicit int functionality to instead generate an
> implicit `auto` type when in C2x mode.
That's something is was trying to do a few weeks earlier, so i'll work on that
too.
In D133289#3784117 <https://reviews.llvm.org/D133289#3784117>, @aaron.ballman
wrote:
> I've been thinking about this more and I'm starting to make myself a bit
> uncomfortable with the current approach, at least in terms of how we're
> handling it on the parsing side of things. I think it's reasonable for us to
> form an `AutoType` when we eventually get down to forming the type. But I'm
> uncomfortable with not modeling the language when parsing. e.g., I think we
> want to parse `auto` as a storage class specifier rather than a type
> specifier, and we want to rely on the lack of a type specifier coupled with
> use of `auto` as a storage class specifier to determine the situations where
> we want to infer a type. The resulting semantics should be basically
> equivalent, but this ensures that we're properly parsing as the language
> expects which helps us be forwards-compatible with future changes in C that
> build on top of this being a storage class specifier rather than a type.
I agree, that's a better approach to design it like that, i'll change my
approach when i'll be able to generate an implicit `auto`.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D133289/new/
https://reviews.llvm.org/D133289
Files:
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/DeclSpec.cpp
clang/test/C/C2x/n3007.c
clang/test/CodeGen/auto.c
clang/test/Parser/c2x-auto.c
clang/test/Sema/c2x-auto.c
clang/www/c_status.html
Index: clang/www/c_status.html
===================================================================
--- clang/www/c_status.html
+++ clang/www/c_status.html
@@ -1193,7 +1193,7 @@
<tr>
<td>Type inference for object declarations</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3007.htm">N3007</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 16</td>
</tr>
<tr>
<td>constexpr for object definitions</td>
Index: clang/test/Sema/c2x-auto.c
===================================================================
--- /dev/null
+++ 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'}}
+ }
+ }
+}
Index: clang/test/Parser/c2x-auto.c
===================================================================
--- /dev/null
+++ 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}}
+}
Index: clang/test/CodeGen/auto.c
===================================================================
--- /dev/null
+++ 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
+}
Index: clang/test/C/C2x/n3007.c
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/lib/Sema/DeclSpec.cpp
===================================================================
--- clang/lib/Sema/DeclSpec.cpp
+++ 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)
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ 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:
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ 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);
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ 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<CXXPre14Compat>, 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<GNUAutoType>;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits