Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -62,6 +62,11 @@ either return ``None`` or a ``llvm::Optional`` wrapping a valid ``Expr*``. This fixes `Issue 53742 `_. +- 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 `_. + + Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ``-Wliteral-range`` will warn on floating-point equality comparisons with Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -5085,6 +5085,10 @@ return getKeyword() == AutoTypeKeyword::DecltypeAuto; } + bool isGNUAutoType() const { + return getKeyword() == AutoTypeKeyword::GNUAutoType; + } + AutoTypeKeyword getKeyword() const { return (AutoTypeKeyword)AutoTypeBits.Keyword; } Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -10285,7 +10285,16 @@ 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()) { + if (AT->isGNUAutoType()) + return RHS; + } + if (const auto *AT = RHS->getAs()) { + if (AT->isGNUAutoType()) + return LHS; + } return {}; } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -9399,6 +9399,15 @@ 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(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(LHSType)) { Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -1880,6 +1880,14 @@ return "type name"; } +static bool isDependentOrGNUAutoType(QualType T) { + if (T->isDependentType()) + return true; + + const auto *AT = dyn_cast(T); + return AT && AT->isGNUAutoType(); +} + QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qualifiers Qs, const DeclSpec *DS) { if (T.isNull()) @@ -1913,7 +1921,10 @@ 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::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)) Index: clang/test/Sema/auto-type.c =================================================================== --- clang/test/Sema/auto-type.c +++ clang/test/Sema/auto-type.c @@ -24,3 +24,57 @@ 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 different __auto_type types does not + // decide that they are both the same type when they're actually different, + // 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')}} +}