https://github.com/AaronBallman created https://github.com/llvm/llvm-project/pull/207994
We were limiting the logic to __auto_type when it should apply to auto as well. Fixes #207466 >From 98a3e364129ac6305e6e2b763e72b3796ddc155c Mon Sep 17 00:00:00 2001 From: Aaron Ballman <[email protected]> Date: Tue, 7 Jul 2026 08:36:24 -0400 Subject: [PATCH] [C23] Allow deducing restrict and _Atomic qualifiers We were limiting the logic to __auto_type when it should apply to auto as well. Fixes #207466 --- clang/docs/ReleaseNotes.md | 3 +++ clang/lib/Sema/SemaType.cpp | 17 ++++------------- clang/test/C/C23/n3007.c | 30 ++++++++++++++++++++++++++---- clang/test/Parser/c2x-auto.c | 3 +-- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 77b6f89e39f83..6df1d8c3b8811 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -284,6 +284,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the - Clang now recognizes the C23 `H`, `D`, and `DD` length modifiers in format strings and diagnoses their use because Clang does not yet support the corresponding decimal floating-point types, `_Decimal32`, `_Decimal64`, and `_Decimal128`. (#GH116962) +- Fixed a bug with deducing qualified inferred types with `auto`. `auto` can now + be combined with `restrict` or `_Atomic` to form a properly-qualified type. (#GH207466) + ### Objective-C Language Changes diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3640bf00b77a4..1378c7baca92e 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1608,14 +1608,6 @@ 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()) @@ -1646,10 +1638,9 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, if (!EltTy->isIncompleteOrObjectType()) DiagID = diag::err_typecheck_invalid_restrict_invalid_pointee; - } 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. + } else if (!T->isDependentType() && !isa<AutoType>(T)) { + // For an inferred type, 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; EltTy = T; } @@ -10394,7 +10385,7 @@ QualType Sema::BuildUnaryTransformType(QualType BaseType, UTTKind UKind, } QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) { - if (!isDependentOrGNUAutoType(T)) { + if (!T->isDependentType() && !isa<AutoType>(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/C/C23/n3007.c b/clang/test/C/C23/n3007.c index fd81a6b9436b7..a881b89443462 100644 --- a/clang/test/C/C23/n3007.c +++ b/clang/test/C/C23/n3007.c @@ -19,25 +19,47 @@ void test_qualifiers(int x, const int y, int * restrict z) { auto yup = ci; yup = 12; + // Because inference happens after lvalue conversion, top-level qualifiers + // are stripped. So r_test will not be restrict qualified, while r_test_2 + // will be because of the explicit use of the qualifier. auto r_test = z; + auto restrict r_test_2 = z; + + // Yes, you can unfortunately infer the type when _Atomic is used as a + // qualifier. (Remember, string literals in C are not const char[], they are + // char[].) + _Atomic auto atom_int = 12; + _Atomic auto atom_float = 1.2f; + _Atomic auto atom_str = "really?"; _Static_assert(_Generic(a, int : 1)); _Static_assert(_Generic(c, unsigned long : 1)); _Static_assert(_Generic(pa, int * : 1)); _Static_assert(_Generic(pb, const int * : 1)); - _Static_assert(_Generic(r_test, int * : 1)); + _Static_assert(_Generic(&r_test, int ** : 1)); + _Static_assert(_Generic(&r_test_2, int * restrict * : 1)); + _Static_assert(_Generic(&atom_int, _Atomic(int) * : 1)); + _Static_assert(_Generic(&atom_float, _Atomic(float) * : 1)); + _Static_assert(_Generic(&atom_str, _Atomic(char *) * : 1)); } void test_atomic(void) { - _Atomic auto i = 12; // expected-error {{_Atomic cannot be applied to type 'auto' in C23}} + // As silly as this seems, it's required to accept `_Atomic auto` because + // _Atomic is a qualifier in that case. But it's required to reject + // `_Atomic(auto)` because _Atomic is a type specifier in that case and you + // cannot combine auto with other type specifiers (e.g., no `_Complex auto` + // either). + _Atomic auto i = 12; _Atomic(auto) j = 12; // expected-error {{'auto' not allowed here}} \ expected-error {{a type specifier is required for all declarations}} _Atomic(int) foo(void); auto k = foo(); - _Static_assert(_Generic(&i, _Atomic auto *: 1)); // expected-error {{_Atomic cannot be applied to type 'auto' in C23}} \ - expected-error {{'auto' not allowed here}} + // Despite being valid as a construct, it's only valid if you can actually + // deduce the type, which requires an initializer. So it cannot be used in a + // generic association. + _Static_assert(_Generic(&i, _Atomic auto *: 1)); // expected-error {{'auto' not allowed here}} _Static_assert(_Generic(k, int: 1)); } diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c index d60c96e2b168f..dc0e1f34dabdc 100644 --- a/clang/test/Parser/c2x-auto.c +++ b/clang/test/Parser/c2x-auto.c @@ -123,8 +123,7 @@ void atomic(void) { c17-error {{type name does not allow storage class to be specified}} \ c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} - _Atomic auto atom2 = 12; // c23-error {{_Atomic cannot be applied to type 'auto' in C23}} \ - c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} + _Atomic auto atom2 = 12; // c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} } void attributes(void) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
