diff options
| -rw-r--r-- | clang/lib/Sema/SemaPseudoObject.cpp | 31 | ||||
| -rw-r--r-- | clang/test/CodeGenObjCXX/property-objects.mm | 132 |
2 files changed, 145 insertions, 18 deletions
diff --git a/clang/lib/Sema/SemaPseudoObject.cpp b/clang/lib/Sema/SemaPseudoObject.cpp index fec97488f53..a7029fa07c6 100644 --- a/clang/lib/Sema/SemaPseudoObject.cpp +++ b/clang/lib/Sema/SemaPseudoObject.cpp @@ -406,19 +406,27 @@ PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, BinaryOperatorKind opcode, Expr *LHS, Expr *RHS) { assert(BinaryOperator::isAssignmentOp(opcode)); - - // Recover from user error - if (isa<UnresolvedLookupExpr>(RHS)) - return ExprError(); Expr *syntacticLHS = rebuildAndCaptureObject(LHS); OpaqueValueExpr *capturedRHS = capture(RHS); + // In some very specific cases, semantic analysis of the RHS as an + // expression may require it to be rewritten. In these cases, we + // cannot safely keep the OVE around. Fortunately, we don't really + // need to: we don't use this particular OVE in multiple places, and + // no clients rely that closely on matching up expressions in the + // semantic expression with expressions from the syntactic form. + Expr *semanticRHS = capturedRHS; + if (RHS->hasPlaceholderType() || isa<InitListExpr>(RHS)) { + semanticRHS = RHS; + Semantics.pop_back(); + } + Expr *syntactic; ExprResult result; if (opcode == BO_Assign) { - result = capturedRHS; + result = semanticRHS; syntactic = new (S.Context) BinaryOperator(syntacticLHS, capturedRHS, opcode, capturedRHS->getType(), capturedRHS->getValueKind(), @@ -430,8 +438,7 @@ PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, // Build an ordinary, non-compound operation. BinaryOperatorKind nonCompound = BinaryOperator::getOpForCompoundAssignment(opcode); - result = S.BuildBinOp(Sc, opcLoc, nonCompound, - opLHS.get(), capturedRHS); + result = S.BuildBinOp(Sc, opcLoc, nonCompound, opLHS.get(), semanticRHS); if (result.isInvalid()) return ExprError(); syntactic = @@ -745,16 +752,6 @@ ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, op = opResult.get(); assert(op && "successful assignment left argument invalid?"); } - else if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(op)) { - Expr *Initializer = OVE->getSourceExpr(); - // passing C++11 style initialized temporaries to objc++ properties - // requires special treatment by removing OpaqueValueExpr so type - // conversion takes place and adding the OpaqueValueExpr later on. - if (isa<InitListExpr>(Initializer) && - Initializer->getType()->isVoidType()) { - op = Initializer; - } - } } // Arguments. diff --git a/clang/test/CodeGenObjCXX/property-objects.mm b/clang/test/CodeGenObjCXX/property-objects.mm index 73ed21dcb7d..35450dda555 100644 --- a/clang/test/CodeGenObjCXX/property-objects.mm +++ b/clang/test/CodeGenObjCXX/property-objects.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -g -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -std=c++11 -emit-llvm -g -o - | FileCheck %s class S { public: @@ -91,3 +91,133 @@ struct X { void f(A* a) { a.x = X(); } + +// rdar://21801088 +// Ensure that pseudo-objecet expressions that require the RHS to be +// rewritten don't result in crashes or redundant emission of code. +struct B0 { long long x; }; +struct B1 { long long x; }; B1 operator+(B1, B1); +struct B2 { B1 x; }; +struct B3 { B3(); B1 x; operator B1(); }; +@interface B +@property B0 b0; +@property B1 b1; +@property B2 b2; +@property B3 b3; +@end + +int b_makeInt(); + +// Note that there's a promotion from int to long long, so +// the syntactic form of the RHS will be bogus. +void testB0(B *b) { + b.b0 = { b_makeInt() }; +} +void testB1(B *b) { + b.b1 += { b_makeInt() }; +} +// CHECK: define void @_Z6testB0P1B([[B:%.*]]* +// CHECK: [[BVAR:%.*]] = alloca [[B]]*, align 8 +// CHECK: [[TEMP:%.*]] = alloca [[B0:%.*]], align 8 +// CHECK: load [[B]]*, [[B]]** [[BVAR]] +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[B0]], [[B0]]* [[TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[T0:%.*]] = call i32 @_Z9b_makeIntv() +// CHECK-NEXT: [[T1:%.*]] = sext i32 [[T0]] to i64 +// CHECK-NEXT: store i64 [[T1]], i64* [[X]], align 8 +// CHECK-NOT: call +// CHECK: call void @llvm.memcpy +// CHECK-NOT: call +// CHECK: call void bitcast {{.*}} @objc_msgSend +// CHECK-NOT: call +// CHECK: ret void + +// CHECK: define void @_Z6testB1P1B([[B]]* +// CHECK: [[BVAR:%.*]] = alloca [[B]]*, align 8 +// CHECK: load [[B]]*, [[B]]** [[BVAR]] +// CHECK-NOT: call +// CHECK: [[T0:%.*]] = call i64 bitcast {{.*}} @objc_msgSend +// CHECK-NOT: call +// CHECK: store i64 [[T0]], +// CHECK-NOT: call +// CHECK: [[T0:%.*]] = call i32 @_Z9b_makeIntv() +// CHECK-NEXT: [[T1:%.*]] = sext i32 [[T0]] to i64 +// CHECK-NEXT: store i64 [[T1]], i64* {{.*}}, align 8 +// CHECK-NOT: call +// CHECK: [[T0:%.*]] = call i64 @_Zpl2B1S_ +// CHECK-NOT: call +// CHECK: store i64 [[T0]], +// CHECK-NOT: call +// CHECK: call void @llvm.memcpy +// CHECK-NOT: call +// CHECK: call void bitcast {{.*}} @objc_msgSend +// CHECK-NOT: call +// CHECK: ret void + +// Another example of a conversion that needs to be applied +// in the semantic form. +void testB2(B *b) { + b.b2 = { B3() }; +} + +// CHECK: define void @_Z6testB2P1B([[B]]* +// CHECK: [[BVAR:%.*]] = alloca [[B]]*, align 8 +// CHECK: load [[B]]*, [[B]]** [[BVAR]] +// CHECK-NOT: call +// CHECK: call void @_ZN2B3C1Ev( +// CHECK-NEXT: [[T0:%.*]] = call i64 @_ZN2B3cv2B1Ev( +// CHECK-NOT: call +// CHECK: store i64 [[T0]], +// CHECK-NOT: call +// CHECK: call void @llvm.memcpy +// CHECK-NOT: call +// CHECK: call void bitcast {{.*}} @objc_msgSend +// CHECK-NOT: call +// CHECK: ret void + +// A similar test to B, but using overloaded function references. +struct C1 { + int x; + friend C1 operator+(C1, void(&)()); +}; +@interface C +@property void (*c0)(); +@property C1 c1; +@end + +void c_helper(); +void c_helper(int); + +void testC0(C *c) { + c.c0 = c_helper; + c.c0 = &c_helper; +} +// CHECK: define void @_Z6testC0P1C([[C:%.*]]* +// CHECK: [[CVAR:%.*]] = alloca [[C]]*, align 8 +// CHECK: load [[C]]*, [[C]]** [[CVAR]] +// CHECK-NOT: call +// CHECK: call void bitcast {{.*}} @objc_msgSend {{.*}} @_Z8c_helperv +// CHECK-NOT: call +// CHECK: call void bitcast {{.*}} @objc_msgSend {{.*}} @_Z8c_helperv +// CHECK-NOT: call +// CHECK: ret void + +void testC1(C *c) { + c.c1 += c_helper; +} +// CHECK: define void @_Z6testC1P1C([[C]]* +// CHECK: [[CVAR:%.*]] = alloca [[C]]*, align 8 +// CHECK: load [[C]]*, [[C]]** [[CVAR]] +// CHECK-NOT: call +// CHECK: [[T0:%.*]] = call i32 bitcast {{.*}} @objc_msgSend +// CHECK-NOT: call +// CHECK: store i32 [[T0]], +// CHECK-NOT: call +// CHECK: [[T0:%.*]] = call i32 @_Zpl2C1RFvvE({{.*}} @_Z8c_helperv +// CHECK-NOT: call +// CHECK: store i32 [[T0]], +// CHECK-NOT: call +// CHECK: call void @llvm.memcpy +// CHECK-NOT: call +// CHECK: call void bitcast {{.*}} @objc_msgSend +// CHECK-NOT: call +// CHECK: ret void |

