From 00fa429b5d58a2378861673fa8544a830901a3d2 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Tue, 13 Nov 2012 23:16:33 +0000 Subject: [PATCH] Don't try to save the assigned value in a Objective-C property assignment if the type of the value is a non-trivial class type. Fixes PR14318. (There's a minor ObjC++ language change here: given that we can't save the value, the type of the assignment expression is void in such cases.) llvm-svn: 167884 --- clang/lib/Sema/SemaPseudoObject.cpp | 22 +++++++++++++++------- clang/test/CodeGenObjCXX/property-objects.mm | 22 ++++++++++++++++------ clang/test/SemaObjCXX/properties.mm | 23 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/clang/lib/Sema/SemaPseudoObject.cpp b/clang/lib/Sema/SemaPseudoObject.cpp index 37d9e77..a8d75b2 100644 --- a/clang/lib/Sema/SemaPseudoObject.cpp +++ b/clang/lib/Sema/SemaPseudoObject.cpp @@ -198,7 +198,14 @@ namespace { } /// Return true if assignments have a non-void result. - virtual bool assignmentsHaveResult() { return true; } + bool CanCaptureValueOfType(QualType ty) { + assert(!ty->isIncompleteType()); + assert(!ty->isDependentType()); + + if (const CXXRecordDecl *ClassDecl = ty->getAsCXXRecordDecl()) + return ClassDecl->isTriviallyCopyable(); + return true; + } virtual Expr *rebuildAndCaptureObject(Expr *) = 0; virtual ExprResult buildGet() = 0; @@ -380,7 +387,7 @@ PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, // The result of the assignment, if not void, is the value set into // the l-value. - result = buildSet(result.take(), opcLoc, assignmentsHaveResult()); + result = buildSet(result.take(), opcLoc, /*captureSetValueAsResult*/ true); if (result.isInvalid()) return ExprError(); addSemanticExpr(result.take()); @@ -404,7 +411,7 @@ PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc, QualType resultType = result.get()->getType(); // That's the postfix result. - if (UnaryOperator::isPostfix(opcode) && assignmentsHaveResult()) { + if (UnaryOperator::isPostfix(opcode) && CanCaptureValueOfType(resultType)) { result = capture(result.take()); setResultToLastSemantic(); } @@ -423,8 +430,7 @@ PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc, // Store that back into the result. The value stored is the result // of a prefix operation. - result = buildSet(result.take(), opcLoc, - UnaryOperator::isPrefix(opcode) && assignmentsHaveResult()); + result = buildSet(result.take(), opcLoc, UnaryOperator::isPrefix(opcode)); if (result.isInvalid()) return ExprError(); addSemanticExpr(result.take()); @@ -696,7 +702,8 @@ ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, ObjCMessageExpr *msgExpr = cast(msg.get()->IgnoreImplicit()); Expr *arg = msgExpr->getArg(0); - msgExpr->setArg(0, captureValueAsResult(arg)); + if (CanCaptureValueOfType(arg->getType())) + msgExpr->setArg(0, captureValueAsResult(arg)); } return msg; @@ -1312,7 +1319,8 @@ ExprResult ObjCSubscriptOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, ObjCMessageExpr *msgExpr = cast(msg.get()->IgnoreImplicit()); Expr *arg = msgExpr->getArg(0); - msgExpr->setArg(0, captureValueAsResult(arg)); + if (CanCaptureValueOfType(arg->getType())) + msgExpr->setArg(0, captureValueAsResult(arg)); } return msg; diff --git a/clang/test/CodeGenObjCXX/property-objects.mm b/clang/test/CodeGenObjCXX/property-objects.mm index 6dfcc27..a3c2ed3 100644 --- a/clang/test/CodeGenObjCXX/property-objects.mm +++ b/clang/test/CodeGenObjCXX/property-objects.mm @@ -1,8 +1,4 @@ // RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s -// CHECK-NOT: callq _objc_msgSend_stret -// CHECK: call void @_ZN1SC1ERKS_ -// CHECK: call %class.S* @_ZN1SaSERKS_ -// CHECK: call %struct.CGRect* @_ZN6CGRectaSERKS_ class S { public: @@ -19,13 +15,14 @@ struct CGRect { S position; CGRect bounds; } + @property(assign, nonatomic) S position; @property CGRect bounds; @property CGRect frame; - (void)setFrame:(CGRect)frameRect; - (CGRect)frame; - (void) initWithOwner; -- (struct CGRect)extent; +- (CGRect)extent; - (void)dealloc; @end @@ -33,6 +30,11 @@ struct CGRect { @synthesize position; @synthesize bounds; @synthesize frame; + +// CHECK: define internal void @"\01-[I setPosition:]" +// CHECK: call %class.S* @_ZN1SaSERKS_ +// CHECK-NEXT: ret void + - (void)setFrame:(CGRect)frameRect {} - (CGRect)frame {return bounds;} @@ -42,14 +44,20 @@ struct CGRect { labelLayerFrame = self.bounds; _labelLayer.frame = labelLayerFrame; } + // rdar://8366604 - (void)dealloc { CGRect cgrect = self.extent; } - (struct CGRect)extent {return bounds;} + @end +// CHECK: define i32 @main +// CHECK: call void @_ZN1SC1ERKS_(%class.S* [[AGGTMP:%[a-zA-Z0-9\.]+]], %class.S* {{%[a-zA-Z0-9\.]+}}) +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %class.S*)*)(i8* {{%[a-zA-Z0-9\.]+}}, i8* {{%[a-zA-Z0-9\.]+}}, %class.S* [[AGGTMP]]) +// CHECK-NEXT: ret i32 0 int main() { I *i; S s1; @@ -59,7 +67,9 @@ int main() { // rdar://8379892 // CHECK: define void @_Z1fP1A -// CHECK: @objc_msgSend to void +// CHECK: call void @_ZN1XC1Ev(%struct.X* [[LVTEMP:%[a-zA-Z0-9\.]+]]) +// CHECK: call void @_ZN1XC1ERKS_(%struct.X* [[AGGTMP:%[a-zA-Z0-9\.]+]], %struct.X* [[LVTEMP]]) +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %struct.X*)*)({{.*}} %struct.X* [[AGGTMP]]) struct X { X(); X(const X&); diff --git a/clang/test/SemaObjCXX/properties.mm b/clang/test/SemaObjCXX/properties.mm index 3c6b138..0783eeb 100644 --- a/clang/test/SemaObjCXX/properties.mm +++ b/clang/test/SemaObjCXX/properties.mm @@ -106,3 +106,26 @@ void test7(Test7 *ptr) { delete ptr.implicit_struct_property; delete ptr.explicit_struct_property; } + +// Make sure the returned value from property assignment is void, +// because there isn't any other viable way to handle it for +// non-trivial classes. +class NonTrivial1 { +public: + ~NonTrivial1(); +}; +class NonTrivial2 { +public: + NonTrivial2(); + NonTrivial2(const NonTrivial2&); +}; +@interface TestNonTrivial +@property(assign, nonatomic) NonTrivial1 p1; +@property(assign, nonatomic) NonTrivial2 p2; +@end +TestNonTrivial *TestNonTrivialObj; + +extern void* VoidType; +extern decltype(TestNonTrivialObj.p1 = NonTrivial1())* VoidType; +extern decltype(TestNonTrivialObj.p2 = NonTrivial2())* VoidType; + -- 2.7.4