From 036603ac59d7818be878176870b3116ee56f5574 Mon Sep 17 00:00:00 2001 From: Brian Kelley Date: Wed, 29 Mar 2017 17:31:42 +0000 Subject: [PATCH] [Objective-C] C++ Classes with __weak Members non-POD Types when using -fobjc-weak Summary: When adding an Objective-C retainable type member to a C++ class, also check the LangOpts.ObjCWeak flag and the lifetime qualifier so __weak qualified Objective-C pointer members cause the class to be a non-POD type with non-trivial special members, so the compiler always emits the necessary runtime calls for copying, moving, and destroying the weak member. Otherwise, Objective-C++ classes with weak Objective-C pointer members compiled with -fobjc-weak exhibit undefined behavior if the C++ class is classified as a POD type. Reviewers: rsmith, benlangmuir, doug.gregor, rjmccall Reviewed By: rjmccall Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D31003 llvm-svn: 299008 --- clang/lib/AST/DeclCXX.cpp | 6 +-- clang/lib/Sema/SemaDeclCXX.cpp | 7 +--- clang/test/CodeGenObjCXX/objc-weak.mm | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 clang/test/CodeGenObjCXX/objc-weak.mm diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 7418370..a1aed33 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -722,9 +722,7 @@ void CXXRecordDecl::addedMember(Decl *D) { ASTContext &Context = getASTContext(); QualType T = Context.getBaseElementType(Field->getType()); if (T->isObjCRetainableType() || T.isObjCGCStrong()) { - if (!Context.getLangOpts().ObjCAutoRefCount) { - setHasObjectMember(true); - } else if (T.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) { + if (T.hasNonTrivialObjCLifetime()) { // Objective-C Automatic Reference Counting: // If a class has a non-static data member of Objective-C pointer // type (or array thereof), it is a non-POD type and its @@ -736,6 +734,8 @@ void CXXRecordDecl::addedMember(Decl *D) { Data.PlainOldData = false; Data.HasTrivialSpecialMembers = 0; Data.HasIrrelevantDestructor = false; + } else if (!Context.getLangOpts().ObjCAutoRefCount) { + setHasObjectMember(true); } } else if (!T.isCXX98PODType(Context)) data().PlainOldData = false; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9500e6d..c369d62 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4399,11 +4399,8 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, } } - if (SemaRef.getLangOpts().ObjCAutoRefCount && - FieldBaseElementType->isObjCRetainableType() && - FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_None && - FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) { - // ARC: + if (FieldBaseElementType.hasNonTrivialObjCLifetime()) { + // ARC and Weak: // Default-initialize Objective-C pointers to NULL. CXXMemberInit = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, diff --git a/clang/test/CodeGenObjCXX/objc-weak.mm b/clang/test/CodeGenObjCXX/objc-weak.mm new file mode 100644 index 0000000..68c2d46 --- /dev/null +++ b/clang/test/CodeGenObjCXX/objc-weak.mm @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-weak -fobjc-runtime-has-weak -std=c++11 -o - %s | FileCheck %s + +struct A { __weak id x; }; + +id test0() { + A a; + A b = a; + A c(static_cast(b)); + a = c; + c = static_cast(a); + return c.x; +} + +// Copy Assignment Operator +// CHECK-LABEL: define linkonce_odr dereferenceable({{[0-9]+}}) %struct.A* @_ZN1AaSERKS_( +// CHECK: [[THISADDR:%this.*]] = alloca [[A:.*]]* +// CHECK: [[OBJECTADDR:%.*]] = alloca [[A:.*]]* +// CHECK: [[THIS:%this.*]] = load [[A]]*, [[A]]** [[THISADDR]] +// CHECK: [[OBJECT:%.*]] = load [[A]]*, [[A]]** [[OBJECTADDR]] +// CHECK: [[T0:%.*]] = getelementptr inbounds [[A]], [[A]]* [[OBJECT]], i32 0, i32 0 +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_loadWeak(i8** [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [[A]], [[A]]* [[THIS]], i32 0, i32 0 +// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]]) + +// Move Assignment Operator +// CHECK-LABEL: define linkonce_odr dereferenceable({{[0-9]+}}) %struct.A* @_ZN1AaSEOS_( +// CHECK: [[THISADDR:%this.*]] = alloca [[A:.*]]* +// CHECK: [[OBJECTADDR:%.*]] = alloca [[A:.*]]* +// CHECK: [[THIS:%this.*]] = load [[A]]*, [[A]]** [[THISADDR]] +// CHECK: [[OBJECT:%.*]] = load [[A]]*, [[A]]** [[OBJECTADDR]] +// CHECK: [[T0:%.*]] = getelementptr inbounds [[A]], [[A]]* [[OBJECT]], i32 0, i32 0 +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_loadWeak(i8** [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [[A]], [[A]]* [[THIS]], i32 0, i32 0 +// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]]) + +// Default Constructor +// CHECK-LABEL: define linkonce_odr void @_ZN1AC2Ev( +// CHECK: [[THISADDR:%this.*]] = alloca [[A:.*]]* +// CHECK: [[THIS:%this.*]] = load [[A]]*, [[A]]** [[THISADDR]] +// CHECK: [[T0:%.*]] = getelementptr inbounds [[A]], [[A]]* [[THIS]], i32 0, i32 0 +// CHECK-NEXT: store i8* null, i8** [[T0]] + +// Copy Constructor +// CHECK-LABEL: define linkonce_odr void @_ZN1AC2ERKS_( +// CHECK: [[THISADDR:%this.*]] = alloca [[A:.*]]* +// CHECK: [[OBJECTADDR:%.*]] = alloca [[A:.*]]* +// CHECK: [[THIS:%this.*]] = load [[A]]*, [[A]]** [[THISADDR]] +// CHECK: [[T0:%.*]] = getelementptr inbounds [[A]], [[A]]* [[THIS]], i32 0, i32 0 +// CHECK-NEXT: [[OBJECT:%.*]] = load [[A]]*, [[A]]** [[OBJECTADDR]] +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[A]], [[A]]* [[OBJECT]], i32 0, i32 0 +// CHECK-NEXT: call void @objc_copyWeak(i8** [[T0]], i8** [[T1]]) + +// Move Constructor +// CHECK-LABEL: define linkonce_odr void @_ZN1AC2EOS_( +// CHECK: [[THISADDR:%this.*]] = alloca [[A:.*]]* +// CHECK: [[OBJECTADDR:%.*]] = alloca [[A:.*]]* +// CHECK: [[THIS:%this.*]] = load [[A]]*, [[A]]** [[THISADDR]] +// CHECK: [[T0:%.*]] = getelementptr inbounds [[A]], [[A]]* [[THIS]], i32 0, i32 0 +// CHECK-NEXT: [[OBJECT:%.*]] = load [[A]]*, [[A]]** [[OBJECTADDR]] +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[A]], [[A]]* [[OBJECT]], i32 0, i32 0 +// CHECK-NEXT: call void @objc_moveWeak(i8** [[T0]], i8** [[T1]]) + +// Destructor +// CHECK-LABEL: define linkonce_odr void @_ZN1AD2Ev( +// CHECK: [[THISADDR:%this.*]] = alloca [[A:.*]]* +// CHECK: [[THIS:%this.*]] = load [[A]]*, [[A]]** [[THISADDR]] +// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[A]], [[A]]* [[THIS]], i32 0, i32 0 +// CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]]) + -- 2.7.4