[C23] Allow deducing restrict and _Atomic qualifiers (#207994)

We were limiting the logic to __auto_type when it should apply to auto
as well.

Fixes #207466
This commit is contained in:
Aaron Ballman authored and GitHub committed 2026-07-07 13:22:14 +00:00
1 parent 5343b20425
commit 849d90b64e
4 files changed
+34 -19

No files matched your search

+3
View File
@@ -299,6 +299,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
+4 -13
View File
@@ -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))
+26 -4
View File
@@ -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));
}
+1 -2
View File
@@ -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) {