[clang] [clang][C23] N3006 Underspecified object declarations (PR #79845)

Guillot Tony via cfe-commits cfe-commits at lists.llvm.org
Sun Jul 7 13:57:17 PDT 2024


https://github.com/to268 updated https://github.com/llvm/llvm-project/pull/79845

>From 5cd7ca393c35ea31f76ceae522d9cd480f9381f1 Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Mon, 29 Jan 2024 15:14:32 +0100
Subject: [PATCH 1/5] Implementation base of N3006 Underspecified object
 declarations

---
 clang/docs/ReleaseNotes.rst                   |  5 +++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 ++
 clang/lib/Sema/SemaExpr.cpp                   | 27 +++++++++++++++++++
 clang/test/C/C23/n3006.c                      | 27 +++++++++++++++++++
 clang/test/Parser/c2x-underspecified-decls.c  | 12 +++++++++
 clang/www/c_status.html                       |  2 +-
 6 files changed, 73 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/C/C23/n3006.c
 create mode 100644 clang/test/Parser/c2x-underspecified-decls.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 838cb69f647ee..56dcf5baf6b52 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -99,7 +99,7 @@ ABI Changes in This Version
   ifuncs. Its purpose was to preserve backwards compatibility when the ".ifunc"
   suffix got removed from the name mangling. The alias interacts badly with
   GlobalOpt (see the issue #96197).
-  
+
 - Fixed Microsoft name mangling for auto non-type template arguments of pointer
   type for MSVC 1920+. This change resolves incompatibilities with code compiled
   by MSVC 1920+ but will introduce incompatibilities with code compiled by
@@ -348,6 +348,9 @@ C23 Feature Support
   but C23 added them to ``<float.h>`` in
   `WG14 N2848 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2848.pdf>`_.
 
+- Clang now diagnoses `N3006 Underspecified object declarations
+  <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm>`_.
+
 Non-comprehensive list of changes in this release
 -------------------------------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 44fd51ec9abc9..618605a5b0f2e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7779,6 +7779,8 @@ def err_attribute_arm_mve_polymorphism : Error<
   "'__clang_arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type">;
 def err_attribute_webassembly_funcref : Error<
   "'__funcref' attribute can only be applied to a function pointer type">;
+def err_c23_underspecified_object_declaration: Error<
+  "'%select{struct|<ERROR>|union|<ERROR>|enum}0 %1' is defined as an underspecified object initializer">;
 
 def warn_setter_getter_impl_required : Warning<
   "property %0 requires method %1 to be defined - "
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 852344d895ffd..c0616c3047015 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6978,6 +6978,33 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
                                            diagID))
         return ExprError();
     }
+  } else if (LangOpts.C23 &&
+             (literalType->isRecordType() || literalType->isEnumeralType())) {
+    // C23 6.2.1p7: Structure, union, and enumeration tags have scope that
+    // begins just after the appearance of the tag in a type specifier that
+    // declares the tag.
+    // [...]
+    // An ordinary identifier that has an underspecified definition has scope
+    // that starts when the definition is completed; if the same ordinary
+    // identifier declares another entity with a scope that encloses the current
+    // block, that declaration is hidden as soon as the inner declarator is
+    // completed*.)
+    // [...]
+    // *) That means, that the outer declaration is not visible for the
+    // initializer.
+    auto Range = SourceRange(LParenLoc, RParenLoc);
+    const auto *Tag = literalType->castAs<TagType>();
+    const auto &TagRange = Tag->getDecl()->getSourceRange();
+
+    // We should diagnose underspecified declaration, unless the identifier has
+    // been diagnosed as being a redefinition, since the tag is made anonymous.
+    if (Range.fullyContains(TagRange) && Tag->getDecl()->getIdentifier()) {
+      Diag(TagRange.getBegin(),
+           diag::err_c23_underspecified_object_declaration)
+          << (unsigned)Tag->getDecl()->getTagKind()
+          << Tag->getDecl()->getName() << TagRange;
+      return ExprError();
+    }
   } else if (!literalType->isDependentType() &&
              RequireCompleteType(LParenLoc, literalType,
                diag::err_typecheck_decl_incomplete_type,
diff --git a/clang/test/C/C23/n3006.c b/clang/test/C/C23/n3006.c
new file mode 100644
index 0000000000000..15efc0ccd6d32
--- /dev/null
+++ b/clang/test/C/C23/n3006.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c2x -verify %s
+
+/* WG14 N3006: Full
+ * Underspecified object declarations
+ */
+
+struct S1 { int x, y; };        // expected-note {{previous definition is here}}
+union U1 { int a; double b; };  // expected-note {{previous definition is here}}
+enum E1 { FOO, BAR };           // expected-note {{previous definition is here}}
+
+auto normal_struct = (struct S1){ 1, 2 };
+auto underspecified_struct = (struct S2 { int x, y; }){ 1, 2 };           // expected-error {{'struct S2' is defined as an underspecified object initializer}}
+auto underspecified_struct_redef = (struct S1 { char x, y; }){ 'A', 'B'}; // expected-error {{redefinition of 'S1'}}
+auto underspecified_empty_struct = (struct S3 { }){ };                    // expected-error {{'struct S3' is defined as an underspecified object initializer}}
+
+auto normal_union_int = (union U1){ .a = 12 };
+auto normal_union_double = (union U1){ .b = 2.4 };
+auto underspecified_union = (union U2 { int a; double b; }){ .a = 34 };         // expected-error {{'union U2' is defined as an underspecified object initializer}}
+auto underspecified_union_redef = (union U1 { char a; double b; }){ .a = 'A' }; // expected-error {{redefinition of 'U1'}}
+auto underspecified_empty_union = (union U3 {  }){  };                          // expected-error {{'union U3' is defined as an underspecified object initializer}}
+
+auto normal_enum_foo = (enum E1){ FOO };
+auto normal_enum_bar = (enum E1){ BAR };
+auto underspecified_enum = (enum E2 { BAZ, QUX }){ BAZ };       // expected-error {{'enum E2' is defined as an underspecified object initializer}}
+auto underspecified_enum_redef = (enum E1 { ONE, TWO }){ ONE }; // expected-error {{redefinition of 'E1'}}
+auto underspecified_empty_enum = (enum E3 {  }){ };             // expected-error {{'enum E3' is defined as an underspecified object initializer}} \
+                                                                   expected-error {{use of empty enum}}
diff --git a/clang/test/Parser/c2x-underspecified-decls.c b/clang/test/Parser/c2x-underspecified-decls.c
new file mode 100644
index 0000000000000..5a7e935cda544
--- /dev/null
+++ b/clang/test/Parser/c2x-underspecified-decls.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c23 -std=c23 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c17 -std=c17 %s
+
+auto underspecified_struct = (struct S1 { int x, y; }){ 1, 2 };         // c23-error {{'struct S1' is defined as an underspecified object initializer}} \
+                                                                           c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+                                                                           c17-error {{illegal storage class on file-scoped variable}}
+auto underspecified_union = (union U1 { int a; double b; }){ .a = 34 }; // c23-error {{'union U1' is defined as an underspecified object initializer}} \
+                                                                           c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+                                                                           c17-error {{illegal storage class on file-scoped variable}}
+auto underspecified_enum = (enum E1 { FOO, BAR }){ BAR };               // c23-error {{'enum E1' is defined as an underspecified object initializer}} \
+                                                                           c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+                                                                           c17-error {{illegal storage class on file-scoped variable}}
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 3fb1efc1989e8..fc491035d9781 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1180,7 +1180,7 @@ <h2 id="c2x">C23 implementation status</h2>
     <tr>
       <td>Underspecified object definitions</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm">N3006</a></td>
-      <td class="none" align="center">No</td>
+      <td class="unreleased" align="center">Clang 19</td>
     </tr>
     <tr>
       <td>Type inference for object declarations</td>

>From e558cca8f0ca9365d2aa7259b5e2388aefd6366a Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Mon, 29 Jan 2024 16:11:02 +0100
Subject: [PATCH 2/5] Fidex formatting

---
 clang/lib/Sema/SemaExpr.cpp | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c0616c3047015..cd0d06e24d403 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6999,16 +6999,17 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
     // We should diagnose underspecified declaration, unless the identifier has
     // been diagnosed as being a redefinition, since the tag is made anonymous.
     if (Range.fullyContains(TagRange) && Tag->getDecl()->getIdentifier()) {
-      Diag(TagRange.getBegin(),
-           diag::err_c23_underspecified_object_declaration)
-          << (unsigned)Tag->getDecl()->getTagKind()
-          << Tag->getDecl()->getName() << TagRange;
+      Diag(TagRange.getBegin(), diag::err_c23_underspecified_object_declaration)
+          << (unsigned)Tag->getDecl()->getTagKind() << Tag->getDecl()->getName()
+          << TagRange;
       return ExprError();
     }
   } else if (!literalType->isDependentType() &&
-             RequireCompleteType(LParenLoc, literalType,
-               diag::err_typecheck_decl_incomplete_type,
-               SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
+             RequireCompleteType(
+                 LParenLoc, literalType,
+                 diag::err_typecheck_decl_incomplete_type,
+                 SourceRange(LParenLoc,
+                             LiteralExpr->getSourceRange().getEnd())))
     return ExprError();
 
   InitializedEntity Entity

>From d8e186f40454bdc8e71e9e57e33dfe92df7713ac Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Mon, 18 Mar 2024 08:00:47 +0100
Subject: [PATCH 3/5] Revert "Fidex formatting"

This reverts commit de90e3ddfe38e2a5b6ae5d5938d9c9fc3ae21228.
---
 clang/lib/Sema/SemaExpr.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index cd0d06e24d403..9a3b23b4c49bb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7005,11 +7005,9 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
       return ExprError();
     }
   } else if (!literalType->isDependentType() &&
-             RequireCompleteType(
-                 LParenLoc, literalType,
-                 diag::err_typecheck_decl_incomplete_type,
-                 SourceRange(LParenLoc,
-                             LiteralExpr->getSourceRange().getEnd())))
+             RequireCompleteType(LParenLoc, literalType,
+               diag::err_typecheck_decl_incomplete_type,
+               SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
     return ExprError();
 
   InitializedEntity Entity

>From 3b9b9e8cb48c460e6044737f73b1abc3e22b8590 Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Sat, 29 Jun 2024 19:27:23 +0200
Subject: [PATCH 4/5] Added constexpr test cases

---
 clang/test/C/C23/n3006.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/clang/test/C/C23/n3006.c b/clang/test/C/C23/n3006.c
index 15efc0ccd6d32..ca9cf69e76a50 100644
--- a/clang/test/C/C23/n3006.c
+++ b/clang/test/C/C23/n3006.c
@@ -9,6 +9,7 @@ union U1 { int a; double b; };  // expected-note {{previous definition is here}}
 enum E1 { FOO, BAR };           // expected-note {{previous definition is here}}
 
 auto normal_struct = (struct S1){ 1, 2 };
+auto normal_struct2 = (struct S1) { .x = 1, .y = 2 };
 auto underspecified_struct = (struct S2 { int x, y; }){ 1, 2 };           // expected-error {{'struct S2' is defined as an underspecified object initializer}}
 auto underspecified_struct_redef = (struct S1 { char x, y; }){ 'A', 'B'}; // expected-error {{redefinition of 'S1'}}
 auto underspecified_empty_struct = (struct S3 { }){ };                    // expected-error {{'struct S3' is defined as an underspecified object initializer}}
@@ -25,3 +26,31 @@ auto underspecified_enum = (enum E2 { BAZ, QUX }){ BAZ };       // expected-erro
 auto underspecified_enum_redef = (enum E1 { ONE, TWO }){ ONE }; // expected-error {{redefinition of 'E1'}}
 auto underspecified_empty_enum = (enum E3 {  }){ };             // expected-error {{'enum E3' is defined as an underspecified object initializer}} \
                                                                    expected-error {{use of empty enum}}
+
+// Constexpr tests
+constexpr auto ce_struct = (struct S1){ 1, 2 };
+constexpr auto ce_union = (union U1){ .a = 12 };
+constexpr auto ce_enum = (enum E1){ FOO };
+
+int func() {
+  struct S { int x, y; };
+  constexpr int i = (struct T { int a, b; }){0, 1}.a;
+
+  struct T t = { 1, 2 };
+}
+
+void func2() {
+  int x = (struct Foo { int x; }){ 0 }.x;
+}
+
+void func3() {
+  constexpr int x = (struct Foo { int x; }){ 0 }.x;
+}
+
+void test() {
+    constexpr typeof(struct s *) x = 0; // declares `s` which is not an ordinary identifier
+    constexpr struct S { int a, b; } y = { 0 }; // declares `S`, `a`, and `b`, none of which are ordinary identifiers
+    constexpr int a = 0, b = 0; // declares `a` and `b` as ordinary identifiers
+    auto c = (struct T { int x, y; }){0, 0}; // declares `T`, `x`, and `y`, none of which are ordinary identifiers
+    constexpr int (*fp)(struct X { int x; } val) = 0; // declares `X` and `x` which are not ordinary identifiers
+}

>From bcda3a600a91fa9c4e73e63ffa7f49dd085f62e8 Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Sun, 7 Jul 2024 18:53:09 +0200
Subject: [PATCH 5/5] Updated test cases and annoted what should be worked on

---
 clang/docs/ReleaseNotes.rst |  2 +-
 clang/test/C/C23/n3006.c    | 50 ++++++++++++++++++++++---------------
 2 files changed, 31 insertions(+), 21 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 56dcf5baf6b52..87ff1fbd3e83f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -348,7 +348,7 @@ C23 Feature Support
   but C23 added them to ``<float.h>`` in
   `WG14 N2848 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2848.pdf>`_.
 
-- Clang now diagnoses `N3006 Underspecified object declarations
+- Clang now diagnoses `N3006 Underspecified object declarations`
   <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm>`_.
 
 Non-comprehensive list of changes in this release
diff --git a/clang/test/C/C23/n3006.c b/clang/test/C/C23/n3006.c
index ca9cf69e76a50..85b36f45289a9 100644
--- a/clang/test/C/C23/n3006.c
+++ b/clang/test/C/C23/n3006.c
@@ -25,32 +25,42 @@ auto normal_enum_bar = (enum E1){ BAR };
 auto underspecified_enum = (enum E2 { BAZ, QUX }){ BAZ };       // expected-error {{'enum E2' is defined as an underspecified object initializer}}
 auto underspecified_enum_redef = (enum E1 { ONE, TWO }){ ONE }; // expected-error {{redefinition of 'E1'}}
 auto underspecified_empty_enum = (enum E3 {  }){ };             // expected-error {{'enum E3' is defined as an underspecified object initializer}} \
-                                                                   expected-error {{use of empty enum}}
-
-// Constexpr tests
-constexpr auto ce_struct = (struct S1){ 1, 2 };
-constexpr auto ce_union = (union U1){ .a = 12 };
-constexpr auto ce_enum = (enum E1){ FOO };
+                                                                     expected-error {{use of empty enum}}
+void constexpr_test() {
+  constexpr auto ce_struct = (struct S1){ 1, 2 };
+  constexpr auto ce_union = (union U1){ .a = 12 };
+  constexpr auto ce_enum = (enum E1){ FOO };
+}
 
-int func() {
-  struct S { int x, y; };
-  constexpr int i = (struct T { int a, b; }){0, 1}.a;
+void trivial_test() {
+  constexpr int i = i;  // expected-error {{constexpr variable 'i' must be initialized by a constant expression}} \
+                           expected-note {{read of object outside its lifetime is not allowed in a constant expression}}
+  auto j = j;           // expected-error {{variable 'j' declared with deduced type 'auto' cannot appear in its own initializer}}
+}
 
-  struct T t = { 1, 2 };
+void double_definition_test() {
+  const struct S { int x; } s;  // expected-note {{previous definition is here}}
+  constexpr struct S s = {0};   // expected-error {{redefinition of 's'}}
 }
 
-void func2() {
-  int x = (struct Foo { int x; }){ 0 }.x;
+void declaring_an_underspecified_defied_object_test() {
+  struct S { int x, y; };
+  constexpr int i = (struct T { int a, b; }){0, 1}.a;  // expected-error {{'struct T' is defined as an underspecified object initializer}} \
+                                                          FIXME: `constexpr variable 'i' must be initialized by a constant expression` shoud appear
+
+  struct T t = { 1, 2 };                         // TODO: Should this be diagnosed as an invalid declaration?
 }
 
-void func3() {
-  constexpr int x = (struct Foo { int x; }){ 0 }.x;
+void constexpr_complience_test() {
+  int x = (struct Foo { int x; }){ 0 }.x;           // expected-error {{'struct Foo' is defined as an underspecified object initializer}}
+  constexpr int y = (struct Bar { int x; }){ 0 }.x; // expected-error {{'struct Bar' is defined as an underspecified object initializer}}
 }
 
-void test() {
-    constexpr typeof(struct s *) x = 0; // declares `s` which is not an ordinary identifier
-    constexpr struct S { int a, b; } y = { 0 }; // declares `S`, `a`, and `b`, none of which are ordinary identifiers
-    constexpr int a = 0, b = 0; // declares `a` and `b` as ordinary identifiers
-    auto c = (struct T { int x, y; }){0, 0}; // declares `T`, `x`, and `y`, none of which are ordinary identifiers
-    constexpr int (*fp)(struct X { int x; } val) = 0; // declares `X` and `x` which are not ordinary identifiers
+void special_test() {
+  constexpr typeof(struct s *) x = 0;               // FIXME: declares `s` which is not an ordinary identifier
+  constexpr struct S { int a, b; } y = { 0 };    // FIXME: declares `S`, `a`, and `b`, none of which are ordinary identifiers
+  constexpr int a = 0, b = 0;
+  auto c = (struct T { int x, y; }){0, 0};          // expected-error {{'struct T' is defined as an underspecified object initializer}}
+  constexpr int (*fp)(struct X { int x; } val) = 0; // expected-warning {{declaration of 'struct X' will not be visible outside of this function}} \
+                                                       FIXME: declares `X` and `x` which are not ordinary identifiers
 }



More information about the cfe-commits mailing list