Skip to content

Commit a504897

Browse files
committed
[Sema][ObjC] Allow declaring ObjC pointer members with non-trivial
ownership qualifications in C++ unions under ARC. An ObjC pointer member with non-trivial ownership qualifications causes all of the defaulted special functions of the enclosing union 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 Differential Revision: https://reviews.llvm.org/D57438 llvm-svn: 352949
1 parent f82d892 commit a504897

File tree

6 files changed

+213
-8
lines changed

6 files changed

+213
-8
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4649,13 +4649,15 @@ def note_deleted_special_member_class_subobject : Note<
46494649
"copy assignment operator of|move assignment operator of|destructor of|"
46504650
"constructor inherited by}0 "
46514651
"%1 is implicitly deleted because "
4652-
"%select{base class %3|%select{||||variant }4field %3}2 has "
4652+
"%select{base class %3|%select{||||variant }4field %3}2 "
4653+
"%select{has "
46534654
"%select{no|a deleted|multiple|an inaccessible|a non-trivial}4 "
46544655
"%select{%select{default constructor|copy constructor|move constructor|copy "
46554656
"assignment operator|move assignment operator|destructor|"
46564657
"%select{default|corresponding|default|default|default}4 constructor}0|"
46574658
"destructor}5"
4658-
"%select{||s||}4">;
4659+
"%select{||s||}4"
4660+
"|is an ObjC pointer}6">;
46594661
def note_deleted_default_ctor_uninit_field : Note<
46604662
"%select{default constructor of|constructor inherited by}0 "
46614663
"%1 is implicitly deleted because field %2 of "

clang/lib/AST/DeclCXX.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,17 @@ void CXXRecordDecl::addedMember(Decl *D) {
991991
setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
992992

993993
Data.HasIrrelevantDestructor = false;
994+
995+
if (isUnion()) {
996+
data().DefaultedCopyConstructorIsDeleted = true;
997+
data().DefaultedMoveConstructorIsDeleted = true;
998+
data().DefaultedMoveAssignmentIsDeleted = true;
999+
data().DefaultedDestructorIsDeleted = true;
1000+
data().NeedOverloadResolutionForCopyConstructor = true;
1001+
data().NeedOverloadResolutionForMoveConstructor = true;
1002+
data().NeedOverloadResolutionForMoveAssignment = true;
1003+
data().NeedOverloadResolutionForDestructor = true;
1004+
}
9941005
} else if (!Context.getLangOpts().ObjCAutoRefCount) {
9951006
setHasObjectMember(true);
9961007
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15923,7 +15923,8 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
1592315923
QualType T = Context.getObjCObjectPointerType(FD->getType());
1592415924
FD->setType(T);
1592515925
} else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() &&
15926-
Record && !ObjCFieldLifetimeErrReported && Record->isUnion()) {
15926+
Record && !ObjCFieldLifetimeErrReported && Record->isUnion() &&
15927+
!getLangOpts().CPlusPlus) {
1592715928
// It's an error in ARC or Weak if a field has lifetime.
1592815929
// We don't want to report this in a system header, though,
1592915930
// so we just make the field unavailable.

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6891,6 +6891,8 @@ struct SpecialMemberDeletionInfo
68916891
return ICI ? Sema::CXXInvalid : CSM;
68926892
}
68936893

6894+
bool shouldDeleteForVariantObjCPtrMember(FieldDecl *FD, QualType FieldType);
6895+
68946896
bool visitBase(CXXBaseSpecifier *Base) { return shouldDeleteForBase(Base); }
68956897
bool visitField(FieldDecl *Field) { return shouldDeleteForField(Field); }
68966898

@@ -6962,13 +6964,14 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
69626964
S.Diag(Field->getLocation(),
69636965
diag::note_deleted_special_member_class_subobject)
69646966
<< getEffectiveCSM() << MD->getParent() << /*IsField*/true
6965-
<< Field << DiagKind << IsDtorCallInCtor;
6967+
<< Field << DiagKind << IsDtorCallInCtor << /*IsObjCPtr*/false;
69666968
} else {
69676969
CXXBaseSpecifier *Base = Subobj.get<CXXBaseSpecifier*>();
69686970
S.Diag(Base->getBeginLoc(),
69696971
diag::note_deleted_special_member_class_subobject)
69706972
<< getEffectiveCSM() << MD->getParent() << /*IsField*/ false
6971-
<< Base->getType() << DiagKind << IsDtorCallInCtor;
6973+
<< Base->getType() << DiagKind << IsDtorCallInCtor
6974+
<< /*IsObjCPtr*/false;
69726975
}
69736976

69746977
if (DiagKind == 1)
@@ -7020,6 +7023,30 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
70207023
return false;
70217024
}
70227025

7026+
bool SpecialMemberDeletionInfo::shouldDeleteForVariantObjCPtrMember(
7027+
FieldDecl *FD, QualType FieldType) {
7028+
// The defaulted special functions are defined as deleted if this is a variant
7029+
// member with a non-trivial ownership type, e.g., ObjC __strong or __weak
7030+
// type under ARC.
7031+
if (!FieldType.hasNonTrivialObjCLifetime())
7032+
return false;
7033+
7034+
// Don't make the defaulted default constructor defined as deleted if the
7035+
// member has an in-class initializer.
7036+
if (CSM == Sema::CXXDefaultConstructor && FD->hasInClassInitializer())
7037+
return false;
7038+
7039+
if (Diagnose) {
7040+
auto *ParentClass = cast<CXXRecordDecl>(FD->getParent());
7041+
S.Diag(FD->getLocation(),
7042+
diag::note_deleted_special_member_class_subobject)
7043+
<< getEffectiveCSM() << ParentClass << /*IsField*/true
7044+
<< FD << 4 << /*IsDtorCallInCtor*/false << /*IsObjCPtr*/true;
7045+
}
7046+
7047+
return true;
7048+
}
7049+
70237050
/// Check whether we should delete a special member function due to the class
70247051
/// having a particular direct or virtual base class.
70257052
bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) {
@@ -7040,7 +7067,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) {
70407067
S.Diag(Base->getBeginLoc(),
70417068
diag::note_deleted_special_member_class_subobject)
70427069
<< getEffectiveCSM() << MD->getParent() << /*IsField*/ false
7043-
<< Base->getType() << /*Deleted*/ 1 << /*IsDtorCallInCtor*/ false;
7070+
<< Base->getType() << /*Deleted*/ 1 << /*IsDtorCallInCtor*/ false
7071+
<< /*IsObjCPtr*/false;
70447072
S.NoteDeletedFunction(BaseCtor);
70457073
}
70467074
return BaseCtor->isDeleted();
@@ -7054,6 +7082,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
70547082
QualType FieldType = S.Context.getBaseElementType(FD->getType());
70557083
CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
70567084

7085+
if (inUnion() && shouldDeleteForVariantObjCPtrMember(FD, FieldType))
7086+
return true;
7087+
70577088
if (CSM == Sema::CXXDefaultConstructor) {
70587089
// For a default constructor, all references must be initialized in-class
70597090
// and, if a union, it must have a non-const member.
@@ -7115,6 +7146,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
71157146
for (auto *UI : FieldRecord->fields()) {
71167147
QualType UnionFieldType = S.Context.getBaseElementType(UI->getType());
71177148

7149+
if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType))
7150+
return true;
7151+
71187152
if (!UnionFieldType.isConstQualified())
71197153
AllVariantFieldsAreConst = false;
71207154

clang/test/SemaObjCXX/arc-0x.mm

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -verify -fblocks -fobjc-exceptions %s
1+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -fobjc-weak -verify -fblocks -fobjc-exceptions %s
22

33
// "Move" semantics, trivial version.
44
void move_it(__strong id &&from) {
@@ -111,3 +111,160 @@ void test() {
111111
func(^(A *a[]){}); // expected-error{{must explicitly describe intended ownership of an object array parameter}}
112112
}
113113
}
114+
115+
namespace test_union {
116+
// Implicitly-declared special functions of a union are deleted by default if
117+
// ARC is enabled and the union has an ObjC pointer field.
118+
union U0 {
119+
id f0; // expected-note 6 {{'U0' is implicitly deleted because variant field 'f0' is an ObjC pointer}}
120+
};
121+
122+
union U1 {
123+
__weak id f0; // expected-note 12 {{'U1' is implicitly deleted because variant field 'f0' is an ObjC pointer}}
124+
U1() = default; // expected-warning {{explicitly defaulted default constructor is implicitly deleted}} expected-note {{explicitly defaulted function was implicitly deleted here}}
125+
~U1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}} expected-note {{explicitly defaulted function was implicitly deleted here}}
126+
U1(const U1 &) = default; // expected-warning {{explicitly defaulted copy constructor is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}}
127+
U1(U1 &&) = default; // expected-warning {{explicitly defaulted move constructor is implicitly deleted}}
128+
U1 & operator=(const U1 &) = default; // expected-warning {{explicitly defaulted copy assignment operator is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}}
129+
U1 & operator=(U1 &&) = default; // expected-warning {{explicitly defaulted move assignment operator is implicitly deleted}}
130+
};
131+
132+
id getStrong();
133+
134+
// If the ObjC pointer field of a union has a default member initializer, the
135+
// implicitly-declared default constructor of the union is not deleted by
136+
// default.
137+
union U2 {
138+
id f0 = getStrong(); // expected-note 4 {{'U2' is implicitly deleted because variant field 'f0' is an ObjC pointer}}
139+
~U2();
140+
};
141+
142+
// It's fine if the user has explicitly defined the special functions.
143+
union U3 {
144+
id f0;
145+
U3();
146+
~U3();
147+
U3(const U3 &);
148+
U3(U3 &&);
149+
U3 & operator=(const U3 &);
150+
U3 & operator=(U3 &&);
151+
};
152+
153+
// ObjC pointer fields in anonymous union fields delete the defaulted special
154+
// functions of the containing class.
155+
struct S0 {
156+
union {
157+
id f0; // expected-note 6 {{'' is implicitly deleted because variant field 'f0' is an ObjC pointer}}
158+
char f1;
159+
};
160+
};
161+
162+
struct S1 {
163+
union {
164+
union { // expected-note 2 {{'S1' is implicitly deleted because variant field '' has a non-trivial}} expected-note 4 {{'S1' is implicitly deleted because field '' has a deleted}}
165+
id f0; // expected-note 2 {{'' is implicitly deleted because variant field 'f0' is an ObjC pointer}}
166+
char f1;
167+
};
168+
int f2;
169+
};
170+
};
171+
172+
struct S2 {
173+
union {
174+
// FIXME: the note should say 'f0' is causing the special functions to be deleted.
175+
struct { // expected-note 6 {{'S2' is implicitly deleted because variant field '' has a non-trivial}}
176+
id f0;
177+
int f1;
178+
};
179+
int f2;
180+
};
181+
int f3;
182+
};
183+
184+
U0 *x0;
185+
U1 *x1;
186+
U2 *x2;
187+
U3 *x3;
188+
S0 *x4;
189+
S1 *x5;
190+
S2 *x6;
191+
192+
static union { // expected-error {{call to implicitly-deleted default constructor of}}
193+
id g0; // expected-note {{default constructor of '' is implicitly deleted because variant field 'g0' is an ObjC pointer}}
194+
};
195+
196+
static union { // expected-error {{call to implicitly-deleted default constructor of}}
197+
union { // expected-note {{default constructor of '' is implicitly deleted because field '' has a deleted default constructor}}
198+
union { // expected-note {{default constructor of '' is implicitly deleted because field '' has a deleted default constructor}}
199+
__weak id g1; // expected-note {{default constructor of '' is implicitly deleted because variant field 'g1' is an ObjC pointer}}
200+
int g2;
201+
};
202+
int g3;
203+
};
204+
int g4;
205+
};
206+
207+
void testDefaultConstructor() {
208+
U0 t0; // expected-error {{call to implicitly-deleted default constructor}}
209+
U1 t1; // expected-error {{call to implicitly-deleted default constructor}}
210+
U2 t2;
211+
U3 t3;
212+
S0 t4; // expected-error {{call to implicitly-deleted default constructor}}
213+
S1 t5; // expected-error {{call to implicitly-deleted default constructor}}
214+
S2 t6; // expected-error {{call to implicitly-deleted default constructor}}
215+
}
216+
217+
void testDestructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) {
218+
delete u0; // expected-error {{attempt to use a deleted function}}
219+
delete u1; // expected-error {{attempt to use a deleted function}}
220+
delete u2;
221+
delete u3;
222+
delete s0; // expected-error {{attempt to use a deleted function}}
223+
delete s1; // expected-error {{attempt to use a deleted function}}
224+
delete s2; // expected-error {{attempt to use a deleted function}}
225+
}
226+
227+
void testCopyConstructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) {
228+
U0 t0(*u0); // expected-error {{call to implicitly-deleted copy constructor}}
229+
U1 t1(*u1); // expected-error {{call to implicitly-deleted copy constructor}}
230+
U2 t2(*u2); // expected-error {{call to implicitly-deleted copy constructor}}
231+
U3 t3(*u3);
232+
S0 t4(*s0); // expected-error {{call to implicitly-deleted copy constructor}}
233+
S1 t5(*s1); // expected-error {{call to implicitly-deleted copy constructor}}
234+
S2 t6(*s2); // expected-error {{call to implicitly-deleted copy constructor}}
235+
}
236+
237+
void testCopyAssignment(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) {
238+
*x0 = *u0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
239+
*x1 = *u1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
240+
*x2 = *u2; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
241+
*x3 = *u3;
242+
*x4 = *s0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
243+
*x5 = *s1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
244+
*x6 = *s2; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
245+
}
246+
247+
// The diagnostics below refer to the deleted copy constructors and assignment
248+
// operators since defaulted move constructors and assignment operators that are
249+
// defined as deleted are ignored by overload resolution.
250+
251+
void testMoveConstructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) {
252+
U0 t0(static_cast<U0 &&>(*u0)); // expected-error {{call to implicitly-deleted copy constructor}}
253+
U1 t1(static_cast<U1 &&>(*u1)); // expected-error {{call to implicitly-deleted copy constructor}}
254+
U2 t2(static_cast<U2 &&>(*u2)); // expected-error {{call to implicitly-deleted copy constructor}}
255+
U3 t3(static_cast<U3 &&>(*u3));
256+
S0 t4(static_cast<S0 &&>(*s0)); // expected-error {{call to implicitly-deleted copy constructor}}
257+
S1 t5(static_cast<S1 &&>(*s1)); // expected-error {{call to implicitly-deleted copy constructor}}
258+
S2 t6(static_cast<S2 &&>(*s2)); // expected-error {{call to implicitly-deleted copy constructor}}
259+
}
260+
261+
void testMoveAssignment(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) {
262+
*x0 = static_cast<U0 &&>(*u0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
263+
*x1 = static_cast<U1 &&>(*u1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
264+
*x2 = static_cast<U2 &&>(*u2); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
265+
*x3 = static_cast<U3 &&>(*u3);
266+
*x4 = static_cast<S0 &&>(*s0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
267+
*x5 = static_cast<S1 &&>(*s1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
268+
*x6 = static_cast<S2 &&>(*s2); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
269+
}
270+
}

clang/test/SemaObjCXX/objc-weak.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ @interface NOWEAK : AnObject // expected-note 2 {{class is declared here}}
1313
};
1414

1515
union U {
16-
__weak id a; // expected-error {{ARC forbids Objective-C objects in union}}
16+
__weak id a;
1717
S b; // expected-error {{union member 'b' has a non-trivial copy constructor}}
1818
};
1919

0 commit comments

Comments
 (0)