This is an archive of the discontinued LLVM Phabricator instance.

[Sema][ObjC] Allow declaring ObjC pointer members in C++ unions under ARC
ClosedPublic

Authored by ahatanak on Jan 29 2019, 10:06 PM.

Details

Summary

ObjC pointer members are currently not allowed in unions in either C or C++ mode. This patch lifts the restriction in C++ mode.

This patch essentially treats ObjC pointer members the same way a non-static data member of a class type that has non-trivial special functions is treated. The ObjC pointer member causes all of the defaulted special functions of the union that directly contains the member to be defined as deleted, except when the member has an in-class initializer, the default constructor isn't defined as deleted.

rdar://problem/34213306

Diff Detail

Repository
rC Clang

Event Timeline

ahatanak created this revision.Jan 29 2019, 10:06 PM
ahatanak marked an inline comment as done.Jan 29 2019, 10:16 PM
ahatanak added inline comments.
test/SemaObjCXX/arc-0x.mm
164

The diagnostic message here should say the special function is deleted because the anonymous union's corresponding special function is deleted, but when diagnosing a deleted copy assignment operator, it says the anonymous union's special function is non-trivial. I'm not sure this is a bug, but I see the same diagnostic message when I compile the following non-ObjC code:

struct S0 {
  S0(const S0 &);
  S0 &operator=(const S0 &);
  int *p;
};

struct S1 {
  union {
    union { // copy assignment operator of 'S1' is implicitly deleted because variant field '' has a non-trivial copy assignment operator
      S0 s10;
      int b;
    };
    int c;
  };
  ~S1();
};

S1 *x0;

void testC1(S1 *a0) {
  *a0 = *x0; // error: object of type 'S1' cannot be assigned because its copy assignment operator is implicitly deleted
  *a0 = static_cast<S1&&>(*x0); // error: object of type 'S1' cannot be assigned because its copy assignment operator is implicitly deleted
}

It seems that this happens because the following code in Sema::ShouldDeleteSpecialMember is preventing the method declaration from being marked as deleted:

// For an anonymous struct or union, the copy and assignment special members
// will never be used, so skip the check. For an anonymous union declared at
// namespace scope, the constructor and destructor are used.
if (CSM != CXXDefaultConstructor && CSM != CXXDestructor &&
    RD->isAnonymousStructOrUnion())
  return false;
rjmccall added inline comments.Jan 31 2019, 12:34 PM
lib/Sema/SemaDeclCXX.cpp
7030

This comment should be talking about non-trivial ownership rather than just general ObjC pointer-ish-ness.

7033

I feel like this would be clearer as separate if statements; you can put separate comments on each.

7085

I believe the right check for variant-ness is inUnion(), not FD->getParent()->isUnion(), since the latter can miss cases with e.g. anonymous structs.

test/SemaObjCXX/arc-0x.mm
164

Well, if it's not different from the ordinary C++ treatment, I think we can justify just improving QoI there separately.

ahatanak updated this revision to Diff 184665.Jan 31 2019, 6:34 PM
ahatanak marked 3 inline comments as done.

Address review comments.

test/SemaObjCXX/arc-0x.mm
164

I filed PR40555 to fix the C++ case.

rjmccall added inline comments.Jan 31 2019, 8:33 PM
lib/Sema/SemaDeclCXX.cpp
7085

Can you try adding a test case here for an anonymous struct nested within an anonymous union?

ahatanak marked 2 inline comments as done.Feb 1 2019, 2:48 PM
ahatanak added inline comments.
lib/Sema/SemaDeclCXX.cpp
7085

Added a test case for an anonymous struct nested within an anonymous union (struct S2 in arc-0x.mm).

I found out that the diagnostic messages clang prints for arc-0x.mm are the same whether inUnion() or FD->getParent()->isUnion() is called. This is what I discovered:

SpecialMemberDeletionInfo is used in two cases:

  1. To find the deletedness of the special functions in the AST after a class is parsed.
  1. When a deleted special function is used, provide more details on why the function is deleted and print note diagnostics.

Since the nested classes are parsed before the enclosing classes, we have all the information we need about the members that are directly contained to correctly infer the deletedness of a class' special functions. So the first case should work fine regardless of which method is called.

It doesn't make a difference for the second case either because SpecialMemberDeletionInfo doesn't try to find out which member of an anonymous struct is causing the special function to be deleted; it only does so for anonymous unions (which happens inside the if statement near comment "// Some additional restrictions exist on the variant members." at line 7140).

ahatanak updated this revision to Diff 184851.Feb 1 2019, 2:50 PM
ahatanak marked an inline comment as done.

Add a test case for an anonymous struct nested inside an anonymous union.

Asking for a minor tweak, but feel free to commit with that.

lib/Sema/SemaDeclCXX.cpp
7085

Alright. Another place where we could generally improve diagnostics, then.

test/SemaObjCXX/arc-0x.mm
174

Worth adding a FIXME saying that this note should go on f1?

This revision was not accepted when it landed; it landed in state Needs Review.Feb 1 2019, 6:23 PM
This revision was automatically updated to reflect the committed changes.
ahatanak marked an inline comment as done.