[clang] [C11] Generic selection expressions and qualified rvalues (PR #96913)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 27 07:28:49 PDT 2024


https://github.com/AaronBallman created https://github.com/llvm/llvm-project/pull/96913

It is possible to get a qualified rvalue in C. Consider:

  struct { cont int i; } foo();
  _Generic(foo().i, ...);

foo() returns an rvalue for the anonymous structure, the member access
expression then results in an rvalue of type const int. The lvalue to
rvalue conversion we perform on the controlling expression of the
generic selection expression then fails to strip any qualifiers because
the expression is already an rvalue.

Discussion on the WG14 reflectors suggested that the qualifiers should
still be stripped from the type of the controlling expression; the
standard should be corrected to make this more clear.

Fixes https://github.com/llvm/llvm-project/issues/96713

>From 5c4426f573ed860e50cd7fc781daad8ec40d17cd Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 27 Jun 2024 10:23:30 -0400
Subject: [PATCH] [C11] Generic selection expressions and qualified rvalues

It is possible to get a qualified rvalue in C. Consider:

  struct { cont int i; } foo();
  _Generic(foo().i, ...);

foo() returns an rvalue for the anonymous structure, the member access
expression then results in an rvalue of type const int. The lvalue to
rvalue conversion we perform on the controlling expression of the
generic selection expression then fails to strip any qualifiers because
the expression is already an rvalue.

Discussion on the WG14 reflectors suggested that the qualifiers should
still be stripped from the type of the controlling expression; the
standard should be corrected to make this more clear.

Fixes #96713
---
 clang/docs/ReleaseNotes.rst         |  4 ++++
 clang/lib/Sema/SemaExpr.cpp         | 26 +++++++++++++++++---------
 clang/test/Sema/generic-selection.c | 10 ++++++++++
 3 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index df579ae398c5e..2b730ef0e3de3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -739,6 +739,10 @@ Bug Fixes in This Version
   negatives where the analysis failed to detect unchecked access to guarded
   data.
 
+- When passing a qualified rvalue as the controlling expression of a
+  ``_Generic`` selection expression, Clang now properly strips the qualifiers.
+  Fixes #GH96713
+
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4a2f3a65eac96..e548362c10e9d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1828,18 +1828,26 @@ ExprResult Sema::CreateGenericSelectionExpr(
   // Look at the canonical type of the controlling expression in case it was a
   // deduced type like __auto_type. However, when issuing diagnostics, use the
   // type the user wrote in source rather than the canonical one.
+  //
+  // Note: there is an edge case for this in C where you can get a
+  // qualified rvalue and the qualifiers are not stripped by the lvalue
+  // conversion applied above. e.g.,
+  //
+  //   struct { const int i; } foo();
+  //   _Generic(foo().i, ...);
+  //
+  // Therefore, in C, we will still ask for the non-atomic, unqualified
+  // type despite those qualifiers generally being stripped above.
+  QualType ControllingTypeToUse =
+      ControllingExpr ? ControllingExpr->getType() : ControllingType->getType();
+  ControllingTypeToUse = ControllingTypeToUse.getCanonicalType();
+  if (!getLangOpts().CPlusPlus && ControllingExpr)
+    ControllingTypeToUse = ControllingTypeToUse.getAtomicUnqualifiedType();
   for (unsigned i = 0; i < NumAssocs; ++i) {
     if (!Types[i])
       DefaultIndex = i;
-    else if (ControllingExpr &&
-             Context.typesAreCompatible(
-                 ControllingExpr->getType().getCanonicalType(),
-                 Types[i]->getType()))
-      CompatIndices.push_back(i);
-    else if (ControllingType &&
-             Context.typesAreCompatible(
-                 ControllingType->getType().getCanonicalType(),
-                 Types[i]->getType()))
+    else if (Context.typesAreCompatible(ControllingTypeToUse,
+                                        Types[i]->getType()))
       CompatIndices.push_back(i);
   }
 
diff --git a/clang/test/Sema/generic-selection.c b/clang/test/Sema/generic-selection.c
index 1f17896ca4cda..ff2fe6ec44eff 100644
--- a/clang/test/Sema/generic-selection.c
+++ b/clang/test/Sema/generic-selection.c
@@ -86,3 +86,13 @@ void GH55562(void) {
   struct S s = { 0 };
   int i = s.a;
 }
+
+struct { const int i; } GH96713_1(void);
+void GH96713_2(void) {
+  _Static_assert(           // ext-warning {{'_Static_assert' is a C11 extension}}
+    _Generic(GH96713_1().i, // ext-warning {{'_Generic' is a C11 extension}}
+      int : 1,
+      const int : 0         // expected-warning {{due to lvalue conversion of the controlling expression, association of type 'const int' will never be selected because it is qualified}}
+    ), ""
+  );
+}



More information about the cfe-commits mailing list