[PATCH] D125919: Drop qualifiers from return types in C (DR423)

Aaron Ballman via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 6 08:42:10 PDT 2022


aaron.ballman added a comment.

In D125919#3556754 <https://reviews.llvm.org/D125919#3556754>, @rsmith wrote:

> In D125919#3556418 <https://reviews.llvm.org/D125919#3556418>, @aaron.ballman wrote:
>
>> In D125919#3556319 <https://reviews.llvm.org/D125919#3556319>, @rjmccall wrote:
>>
>>> Now, the standard has chosen not to talk about `_Atomic` as a qualifier, I assume because there's a fair number of rules in the standard that assume that qualifiers can be freely added to pointers and don't change layout and so on, none of which apply to `_Atomic`.  But those rules also don't apply to a large number of other extended qualifiers, like address spaces and the ARC ownership qualifiers and `__ptrauth`.  The committee should probably just come to terms with the fact that it's the relatively easy-come-easy-go nature of the CVR qualifiers which is the special case.  I've thought for awhile that Clang should really be representing `_Atomic` as a qualifier instead of having to treat `AtomicType` as a special case in a million places.
>>
>> I'm not certain if we can get away with that. IIRC, Microsoft uses embedded locks on Windows in some circumstances. However, cl doesn't support `_Atomic` and so maybe we can get away with it on the assumption we have no other targets where the size/alignment would be different for an atomic type vs a non-atomic type?
>
> That assumption does not hold. Given `struct A { char c[3]; };`, `struct A` has size 3 and align 1, but `_Atomic struct A` has size 4 and align 4 across many (perhaps all?) of our targets. (This is an ABI divergence between GCC and Clang, which as far as I know the psABI owners have so far not succeeded in resolving.)

Good point!

> I saw some talk in WG14 of separating `_Atomic(T)` and `_Atomic T` so that `_Atomic T` would be a qualifier that doesn't affect size or alignment, only what code is generated to access the value, and `_Atomic(T)` would be a distinct type with potentially a different representation in order to support lock-free atomic access. Did that go anywhere? (I'd assume not, it seems to be at least a decade too late for such an idea.)

So far that's just been talk (and I'm not convinced it'll be possible to change the semantics there without silently breaking code).

In D125919#3558062 <https://reviews.llvm.org/D125919#3558062>, @rjmccall wrote:

>> I disagree with the assessment that _Atomic behaves exactly like a qualifier. It *should* (IMHO), but it doesn't. C allows the size and alignment of an atomic type be *different* from its unqualified version, to allow for embedded locks and such. Because the size and alignment can be different, to my mind, _Atomic int and int are different types in the same way as _Complex float and float -- the object representations can (must, in the case of _Complex) be different, so they're different types. The same is not true for const, volatile, or restrict qualifiers -- those are required to have the same size and alignment as the unqualified type.
>
> I think our apparent disagreement here is rooted in something quite simple: you seem to be assuming that a qualified type by definition cannot have a different size and alignment from the unqualified type, and I think that's a property that's *guaranteed* for the CVR qualifiers and merely *happens to be true* for our current set of extended qualifiers, and only really because we don't represent `_Atomic` as a qualifier.

I think you're right about the root of our disagreement. Any qualifiers other than CVR, _Atomic, and address spaces are fully implementation-defined and can do whatever they want, thus aren't really relevant to how standard qualifiers behave. Address spaces are covered by TR 18037 (which states they're extending the list of CVR qualifiers in C, thus I'd expect would follow the same rules as CVR despite predating DR423). We treat all standards-based qualifiers as a `Qualifier` in Clang, except for `_Atomic`, which I believe we treat differently because it behaves as a distinct type. I think we *want* that behavior -- these are not valid redeclarations: `void func(int a); void func(_Atomic int a);` while these are valid (and have utility): `void func(int a); void func(const int a);`.

> Many of our extended qualifiers completely change the ABI of the type they qualify, and they can change the basic semantic properties of types they're nested within.  I don't see why we'd draw a line around type layout for what counts as a qualifier and what doesn't.  For example, ObjC ARC happens to implement `__weak` references without using extra inline storage, but it would certainly be a reasonable ABI choice to make them larger than just a pointer — Swift does with its native weak references, for example.  That ABI choice wouldn't make `__weak` suddenly not a qualifier.  Type constructors like `_Atomic`, `_Complex`, `const`, and `*` are qualifiers or not based on the role and behavior of the constructed type in the language, not its ABI properties.

For our implementation-defined qualifiers, I think the design space is wide open and we can do what we want. However, I think `_Atomic` specifically is more closely related to a new type than a qualified type for the other standards-based qualifiers. The C standard goes out of its way to segregate atomic types from other types but doesn't do anything similar for any other qualifier.

All that said, I think you can see why I'm hoping to get an answer from WG14 as to what to do. Reasonable folks are disagreeing on what the standard requires here.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D125919/new/

https://reviews.llvm.org/D125919



More information about the cfe-commits mailing list