This is an archive of the discontinued LLVM Phabricator instance.

Allow _Atomic to be specified on incomplete types
Needs ReviewPublic

Authored by aaron.ballman on Apr 26 2018, 3:01 AM.

Details

Reviewers
rsmith
Summary

The C standard does not prohibit the _Atomic specifier on incomplete types, which turns out to be sometimes useful.

This addresses PR37237.

Note that C++ still requires checking if the type is complete in order to support member pointers under the Microsoft ABI (because that check completes some information on the type), which is a use case we have in test\SemaCXX\atomic-type.cpp.

Diff Detail

Event Timeline

aaron.ballman created this revision.Apr 26 2018, 3:01 AM

This generally makes sense.

Need some tests to make sure we emit an appropriate error if you try to actually use atomic operators (load/store) or intrinsics (__atomic_is_lock_free etc.) with an incomplete type. And a test that code generation emits something appropriate.

lib/Sema/SemaType.cpp
8022

If you're going to allow incomplete types in C++, you'll need some code to handle the case where the type is initially incomplete, but completed later.

rsmith added inline comments.Apr 26 2018, 4:11 PM
lib/Sema/SemaType.cpp
8022

Perhaps the trivially-copyable requirement could be deferred in all cases until we reach an actual load or store. Constraints that are only enforced when a type happens to be complete are generally a bad idea (we have a mess like this for constraints on types being non-abstract, and we're still working on unsticking ourselves from that mess in the C++ standard).

In passing, I'd also note that we need the trivially-copyable requirement even outside C++, for structs containing __weak / __strong fields.

Hopefully address the review comments.

aaron.ballman added inline comments.May 6 2018, 12:34 PM
lib/Sema/SemaType.cpp
8022

I'm not quite certain I've properly captured the request here, but I took a stab at it. One thing to note is that our behavior for type checking __atomic_load() appears to diverge from GCC with regards to trivial copyability, even before my patch.

https://godbolt.org/g/g2fqLK

I think the request was that we check that a type is trivially copyable when we perform an atomic operation? I don't see the code for that anywhere.

Also needs some test coverage for atomic operations which aren't calls, like "typedef struct S S; void f(_Atomic S *s, _Atomic S *s2) { *s = *s2; };".

Addresses review comments.

Modifications based on review feedback.

I think the request was that we check that a type is trivially copyable when we perform an atomic operation? I don't see the code for that anywhere.

Sorry about that -- I didn't notice that GNU was handled while C11 was not. That's been updated now.

Also needs some test coverage for atomic operations which aren't calls, like "typedef struct S S; void f(_Atomic S *s, _Atomic S *s2) { *s = *s2; };".

Thank you for pointing this out -- that uncovered an issue where we were not properly diagnosing the types as being incomplete. I've added a new test case and rolled the contents of Sema\atomic-type.cpp (which I added in an earlier patch) into SemaCXX\atomic-type.cpp (which already existed and I missed it).

I believe the change I made to Type::isIncompleteType() is correct, but was surprised by the new behavior in SemaCXX/atomic-type.cpp that resulted. It appears that the RecordDecl for "struct inner" has IsCompleteDefinition set to false while instantiating struct atomic, but I'm not familiar enough with the template instantiation process to know whether this is reasonable or not.

rsmith added inline comments.May 12 2018, 7:58 PM
test/SemaCXX/atomic-type.cpp
5

This is a bug. You need to teach RequireCompleteType to look through atomic types when looking for a class type to instantiate.

Look through atomic types when checking type completeness during template instantiation.

aaron.ballman marked an inline comment as done.May 13 2018, 12:57 PM
aaron.ballman added inline comments.
test/SemaCXX/atomic-type.cpp
5

Thank you for the explanation -- I think I've addressed this in the latest patch.

rsmith added inline comments.May 13 2018, 2:57 PM
lib/Sema/SemaType.cpp
7604–7608

This function is duplicating the work of finding the type to complete, which was already done by isIncompleteType. Rather than unwrapping T here...

7636–7637

... these uses of T should be dyn_casting Def, which is the declaration that isIncompleteType said needed completing.

7648–7649

This would nicely address this FIXME, since isIncompleteType has already walked through arrays.

7679–7683

Likewise here, this should probably just be if (const auto *RD = dyn_cast<RecordDecl>(Def)).

aaron.ballman marked an inline comment as done.

Updated based on review comments.

aaron.ballman marked 8 inline comments as done.May 14 2018, 6:44 AM
aaron.ballman added inline comments.
lib/Sema/SemaType.cpp
7604–7608

Thank you for the explanation!

Also needs some test coverage for atomic operations which aren't calls, like "typedef struct S S; void f(_Atomic S *s, _Atomic S *s2) { *s = *s2; };".

Thank you for pointing this out -- that uncovered an issue where we were not properly diagnosing the types as being incomplete. I've added a new test case and rolled the contents of Sema\atomic-type.cpp (which I added in an earlier patch) into SemaCXX\atomic-type.cpp (which already existed and I missed it).

I don't think this has sufficiently addressed the comment. Specifically, for a case like:

struct NotTriviallyCopyable { NotTriviallyCopyable(const NotTriviallyCopyable&); };
void f(_Atomic NotTriviallyCopyable *p) { *p = *p; }

... we should reject the assignment, because it would perform an atomic operation on a non-trivially-copyable type. It would probably suffice to check the places where we form an AtomicToNonAtomic or NonAtomicToAtomic conversion.

In passing, I noticed that this crashes Clang (because we fail to create an lvalue-to-rvalue conversion for x):

struct X {};                              
void f(_Atomic X *p, X x) { *p = x; }

This will likely get in the way of your test cases unless you fix it :)

lib/Sema/SemaChecking.cpp
3202

The (pre-existing) isScalarType() check here looks wrong to me. If we had a non-trivially-copyable scalar type (which I think do not currently exist in our type system), I think we should reject it here.

lib/Sema/SemaType.cpp
7662

May as well cast directly to CXXRecordDecl here; none of the code controlled by this if does anything in C.

aaron.ballman marked 4 inline comments as done.

Corrected some of the review comments.

Also needs some test coverage for atomic operations which aren't calls, like "typedef struct S S; void f(_Atomic S *s, _Atomic S *s2) { *s = *s2; };".

Thank you for pointing this out -- that uncovered an issue where we were not properly diagnosing the types as being incomplete. I've added a new test case and rolled the contents of Sema\atomic-type.cpp (which I added in an earlier patch) into SemaCXX\atomic-type.cpp (which already existed and I missed it).

I don't think this has sufficiently addressed the comment. Specifically, for a case like:

struct NotTriviallyCopyable { NotTriviallyCopyable(const NotTriviallyCopyable&); };
void f(_Atomic NotTriviallyCopyable *p) { *p = *p; }

... we should reject the assignment, because it would perform an atomic operation on a non-trivially-copyable type. It would probably suffice to check the places where we form an AtomicToNonAtomic or NonAtomicToAtomic conversion.

In passing, I noticed that this crashes Clang (because we fail to create an lvalue-to-rvalue conversion for x):

struct X {};                              
void f(_Atomic X *p, X x) { *p = x; }

This will likely get in the way of your test cases unless you fix it :)

It only gets in the way for C++ whereas my primary concern for this patch is C. I tried spending a few hours on this today and got nowhere -- we have a lot of FIXME comments surrounding atomic type qualifiers in C++. I've run out of time to be able to work on this patch and may have to abandon it. I'd hate to lose the forward progress already made, so I'm wondering if the C bits are sufficiently baked that even more FIXMEs around atomics in C++ would suffice?

If you don't want to spend too much time on C++, fine; could you add a short Objective-C test instead to make sure the trivially-copyable checks are working?

What are the changes to Sema::RequireCompleteTypeImpl supposed to do?

test/Sema/atomic-type.c
30

This error message is terrible; yes, technically 'void' isn't trivially copyable, but that isn't really helpful.

jfb added a subscriber: jfb.Jul 17 2018, 4:46 PM

Also needs some test coverage for atomic operations which aren't calls, like "typedef struct S S; void f(_Atomic S *s, _Atomic S *s2) { *s = *s2; };".

Thank you for pointing this out -- that uncovered an issue where we were not properly diagnosing the types as being incomplete. I've added a new test case and rolled the contents of Sema\atomic-type.cpp (which I added in an earlier patch) into SemaCXX\atomic-type.cpp (which already existed and I missed it).

I don't think this has sufficiently addressed the comment. Specifically, for a case like:

struct NotTriviallyCopyable { NotTriviallyCopyable(const NotTriviallyCopyable&); };
void f(_Atomic NotTriviallyCopyable *p) { *p = *p; }

... we should reject the assignment, because it would perform an atomic operation on a non-trivially-copyable type. It would probably suffice to check the places where we form an AtomicToNonAtomic or NonAtomicToAtomic conversion.

In passing, I noticed that this crashes Clang (because we fail to create an lvalue-to-rvalue conversion for x):

struct X {};                              
void f(_Atomic X *p, X x) { *p = x; }

This will likely get in the way of your test cases unless you fix it :)

It only gets in the way for C++ whereas my primary concern for this patch is C. I tried spending a few hours on this today and got nowhere -- we have a lot of FIXME comments surrounding atomic type qualifiers in C++. I've run out of time to be able to work on this patch and may have to abandon it. I'd hate to lose the forward progress already made, so I'm wondering if the C bits are sufficiently baked that even more FIXMEs around atomics in C++ would suffice?

FYI I ran into this here: https://reviews.llvm.org/D49458