[clang] e4a42c5 - Change __auto_type behavior with qualifiers to match GCC behavior

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 23 10:25:42 PDT 2022


Author: Aaron Ballman
Date: 2022-03-23T13:25:31-04:00
New Revision: e4a42c5b64d044ae28d9483b0ebd12038d5b5917

URL: https://github.com/llvm/llvm-project/commit/e4a42c5b64d044ae28d9483b0ebd12038d5b5917
DIFF: https://github.com/llvm/llvm-project/commit/e4a42c5b64d044ae28d9483b0ebd12038d5b5917.diff

LOG: Change __auto_type behavior with qualifiers to match GCC behavior

Currently, Clang handles some qualifiers correctly for __auto_type, but
it does not handle the restrict or _Atomic qualifiers in the same way
that GCC does. This patch handles those qualifiers so that they attach
to the deduced type the same as const and volatile already do.

This fixes https://github.com/llvm/llvm-project/issues/53652

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/Type.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaType.cpp
    clang/test/Sema/auto-type.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ebbed4b5ce008..83d5dbdda2607 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -73,6 +73,11 @@ Bug Fixes
   Now fixed by setting identifiers for them.
   This fixes `Issue 28475 (PR28101) <https://github.com/llvm/llvm-project/issues/28475>`_.
 
+- Now allow the `restrict` and `_Atomic` qualifiers to be used in conjunction
+  with `__auto_type` to match the behavior in GCC. This fixes
+  `Issue 53652 <https://github.com/llvm/llvm-project/issues/53652>`_.
+
+
 Improvements to Clang's diagnostics
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - ``-Wliteral-range`` will warn on floating-point equality comparisons with

diff  --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 448dd2fdd8e49..bc66ffbf735e2 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -5083,6 +5083,10 @@ class alignas(8) AutoType : public DeducedType, public llvm::FoldingSetNode {
     return getKeyword() == AutoTypeKeyword::DecltypeAuto;
   }
 
+  bool isGNUAutoType() const {
+    return getKeyword() == AutoTypeKeyword::GNUAutoType;
+  }
+
   AutoTypeKeyword getKeyword() const {
     return (AutoTypeKeyword)AutoTypeBits.Keyword;
   }

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 3ba9f40a52a36..77b9edc9628fe 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -10285,7 +10285,16 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
       if (RHS->isObjCIdType() && LHS->isBlockPointerType())
         return RHS;
     }
-
+    // Allow __auto_type to match anything; it merges to the type with more
+    // information.
+    if (const auto *AT = LHS->getAs<AutoType>()) {
+      if (AT->isGNUAutoType())
+        return RHS;
+    }
+    if (const auto *AT = RHS->getAs<AutoType>()) {
+      if (AT->isGNUAutoType())
+        return LHS;
+    }
     return {};
   }
 

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 23f7625f1c969..9ad279788660b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9399,6 +9399,15 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
     return Compatible;
   }
 
+  // If the LHS has an __auto_type, there are no additional type constraints
+  // to be worried about.
+  if (const auto *AT = dyn_cast<AutoType>(LHSType)) {
+    if (AT->isGNUAutoType()) {
+      Kind = CK_NoOp;
+      return Compatible;
+    }
+  }
+
   // If we have an atomic type, try a non-atomic assignment, then just add an
   // atomic qualification step.
   if (const AtomicType *AtomicTy = dyn_cast<AtomicType>(LHSType)) {

diff  --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index ad38738d4daba..419ccbb437ad2 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1880,6 +1880,14 @@ static std::string getPrintableNameForEntity(DeclarationName Entity) {
   return "type name";
 }
 
+static bool isDependentOrGNUAutoType(QualType T) {
+  if (T->isDependentType())
+    return true;
+
+  const auto *AT = dyn_cast<AutoType>(T);
+  return AT && AT->isGNUAutoType();
+}
+
 QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc,
                                   Qualifiers Qs, const DeclSpec *DS) {
   if (T.isNull())
@@ -1913,7 +1921,10 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc,
         DiagID = diag::err_typecheck_invalid_restrict_invalid_pointee;
         ProblemTy = EltTy;
       }
-    } else if (!T->isDependentType()) {
+    } else if (!isDependentOrGNUAutoType(T)) {
+      // For an __auto_type variable, we may not have seen the initializer yet
+      // and so have no idea whether the underlying type is a pointer type or
+      // not.
       DiagID = diag::err_typecheck_invalid_restrict_not_pointer;
       ProblemTy = T;
     }
@@ -9101,7 +9112,7 @@ QualType Sema::BuildUnaryTransformType(QualType BaseType,
 }
 
 QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
-  if (!T->isDependentType()) {
+  if (!isDependentOrGNUAutoType(T)) {
     // FIXME: It isn't entirely clear whether incomplete atomic types
     // are allowed or not; for simplicity, ban them for the moment.
     if (RequireCompleteType(Loc, T, diag::err_atomic_specifier_bad_type, 0))

diff  --git a/clang/test/Sema/auto-type.c b/clang/test/Sema/auto-type.c
index 65f53f43dae7e..7faa8a6984e7a 100644
--- a/clang/test/Sema/auto-type.c
+++ b/clang/test/Sema/auto-type.c
@@ -24,3 +24,57 @@ int i() {
 int k(l)
 __auto_type l; // expected-error {{'__auto_type' not allowed in K&R-style function parameter}}
 {}
+
+void Issue53652(void) {
+  // Ensure that qualifiers all work the same way as GCC.
+  const __auto_type cat = a;
+  const __auto_type pcat = &a;
+  volatile __auto_type vat = a;
+  volatile __auto_type pvat = &a;
+  restrict __auto_type rat = &a;
+  _Atomic __auto_type aat1 = a;
+  _Atomic __auto_type paat = &a;
+
+  // GCC does not accept this either, for the same reason.
+  _Atomic(__auto_type) aat2 = a; // expected-error {{'__auto_type' not allowed here}} \
+                                 // expected-warning {{type specifier missing, defaults to 'int'}}
+
+  // Ensure the types are what we expect them to be, regardless of order we
+  // pass the types.
+  _Static_assert(__builtin_types_compatible_p(__typeof(cat), const int), "");
+  _Static_assert(__builtin_types_compatible_p(const int, __typeof(cat)), "");
+  _Static_assert(__builtin_types_compatible_p(__typeof(pcat), int *const), "");
+  _Static_assert(__builtin_types_compatible_p(int *const, __typeof(pcat)), "");
+  _Static_assert(__builtin_types_compatible_p(__typeof(vat), volatile int), "");
+  _Static_assert(__builtin_types_compatible_p(volatile int, __typeof(vat)), "");
+  _Static_assert(__builtin_types_compatible_p(__typeof(pvat), int *volatile), "");
+  _Static_assert(__builtin_types_compatible_p(int *volatile, __typeof(pvat)), "");
+  _Static_assert(__builtin_types_compatible_p(__typeof(rat), int *restrict), "");
+  _Static_assert(__builtin_types_compatible_p(int *restrict, __typeof(rat)), "");
+  _Static_assert(__builtin_types_compatible_p(__typeof(aat1), _Atomic int), "");
+  _Static_assert(__builtin_types_compatible_p(_Atomic int, __typeof(aat1)), "");
+  _Static_assert(__builtin_types_compatible_p(__typeof(paat), _Atomic(int *)), "");
+  _Static_assert(__builtin_types_compatible_p(_Atomic(int *), __typeof(paat)), "");
+
+  // Ensure the types also work in generic selection expressions. Remember, the
+  // type of the expression argument to _Generic is treated as-if it undergoes
+  // lvalue to rvalue conversion, which drops qualifiers. We're making sure the
+  // use of __auto_type doesn't impact that.
+  (void)_Generic(cat, int : 0);
+  (void)_Generic(pcat, int * : 0);
+  (void)_Generic(vat, int : 0);
+  (void)_Generic(pvat, int * : 0);
+  (void)_Generic(rat, int * : 0);
+  (void)_Generic(aat1, int : 0);
+  (void)_Generic(paat, int * : 0);
+
+  // Ensure that trying to merge two 
diff erent __auto_type types does not
+  // decide that they are both the same type when they're actually 
diff erent,
+  // and that we reject when the types are the same.
+  __auto_type i = 12;
+  __auto_type f = 1.2f;
+  (void)_Generic(a, __typeof__(i) : 0, __typeof__(f) : 1);
+  (void)_Generic(a,
+                 __typeof__(i) : 0,   // expected-note {{compatible type 'typeof (i)' (aka 'int') specified here}}
+                 __typeof__(a) : 1);  // expected-error {{type 'typeof (a)' (aka 'int') in generic association compatible with previously specified type 'typeof (i)' (aka 'int')}}
+}


        


More information about the cfe-commits mailing list