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

Guillot Tony via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 29 12:42:39 PDT 2024


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

>From 44d103f75aef4227262b823fd5168c081307ce23 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/4] 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/C2x/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/C2x/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 feba3c7ba8d77..70f00844caa2f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -104,7 +104,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
@@ -337,6 +337,9 @@ C23 Feature Support
 - Properly promote bit-fields of bit-precise integer types to the field's type
   rather than to ``int``. #GH87641
 
+- 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 5dc36c594bcb7..ba19fbe542176 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7781,6 +7781,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 db44cfe1288b6..89af7641e8e6e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7117,6 +7117,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/C2x/n3006.c b/clang/test/C/C2x/n3006.c
new file mode 100644
index 0000000000000..15efc0ccd6d32
--- /dev/null
+++ b/clang/test/C/C2x/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 84cd8e836006c..33bdf9107220f 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1175,7 +1175,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 1ee4821d9b0e8b5fe4d95f2f967a621959a45502 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/4] 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 89af7641e8e6e..242ba57417ac4 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7138,16 +7138,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 7a55ff72542cab200505e7af0f7fe4a2be8570ad 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/4] 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 242ba57417ac4..6f326741c21a6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7144,11 +7144,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 010732042782454631e0470c9041f54dca217ac2 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/4] Added constexpr test cases

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

diff --git a/clang/test/C/C2x/n3006.c b/clang/test/C/C2x/n3006.c
index 15efc0ccd6d32..ca9cf69e76a50 100644
--- a/clang/test/C/C2x/n3006.c
+++ b/clang/test/C/C2x/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
+}



More information about the cfe-commits mailing list