diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -14569,24 +14569,6 @@ Expr *Callee = OrigCallee->IgnoreParenCasts(); bool isPostIncDec = Second && (Op == OO_PlusPlus || Op == OO_MinusMinus); - if (First->getObjectKind() == OK_ObjCProperty) { - BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op); - if (BinaryOperator::isAssignmentOp(Opc)) - return SemaRef.checkPseudoObjectAssignment(/*Scope=*/nullptr, OpLoc, Opc, - First, Second); - ExprResult Result = SemaRef.CheckPlaceholderExpr(First); - if (Result.isInvalid()) - return ExprError(); - First = Result.get(); - } - - if (Second && Second->getObjectKind() == OK_ObjCProperty) { - ExprResult Result = SemaRef.CheckPlaceholderExpr(Second); - if (Result.isInvalid()) - return ExprError(); - Second = Result.get(); - } - // Determine whether this should be a builtin operation. if (Op == OO_Subscript) { if (!First->getType()->isOverloadableType() && @@ -14602,8 +14584,21 @@ // The argument is not of overloadable type, or this is an expression // of the form &Class::member, so try to create a built-in unary // operation. + // + // But handle pseudo-objects in the LHS, and increment and decrement + // of pseudo-object l-value (see Sema::BuildUnaryOp). UnaryOperatorKind Opc = UnaryOperator::getOverloadedOpcode(Op, isPostIncDec); + if (const BuiltinType *pty = First->getType()->getAsPlaceholderType()) { + if (pty->getKind() == BuiltinType::PseudoObject && + UnaryOperator::isIncrementDecrementOp(Opc)) + return SemaRef.checkPseudoObjectIncDec(/*Scope=*/nullptr, OpLoc, Opc, + First); + ExprResult Result = SemaRef.CheckPlaceholderExpr(First); + if (Result.isInvalid()) + return ExprError(); + First = Result.get(); + } return getSema().CreateBuiltinUnaryOp(OpLoc, Opc, First); } @@ -14612,7 +14607,26 @@ !Second->getType()->isOverloadableType()) { // Neither of the arguments is an overloadable type, so try to // create a built-in binary operation. + // + // But handle pseudo-objects in the LHS/RHS, and assignments with a + // pseudo-object l-value (see Sema::BuildBinOp). BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op); + if (const BuiltinType *pty = First->getType()->getAsPlaceholderType()) { + if (pty->getKind() == BuiltinType::PseudoObject && + BinaryOperator::isAssignmentOp(Opc)) + return SemaRef.checkPseudoObjectAssignment(/*Scope=*/nullptr, OpLoc, + Opc, First, Second); + ExprResult Result = SemaRef.CheckPlaceholderExpr(First); + if (Result.isInvalid()) + return ExprError(); + First = Result.get(); + } + if (Second->getType()->isPlaceholderType()) { + ExprResult Result = SemaRef.CheckPlaceholderExpr(Second); + if (Result.isInvalid()) + return ExprError(); + Second = Result.get(); + } ExprResult Result = SemaRef.CreateBuiltinBinOp(OpLoc, Opc, First, Second); if (Result.isInvalid()) diff --git a/clang/test/SemaCXX/PR51855.cpp b/clang/test/SemaCXX/PR51855.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/PR51855.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -S -triple %itanium_abi_triple -fms-extensions -emit-llvm %s -o - | FileCheck %s + +struct F {}; + +F operator*=(F &lhs, int rhs); + +F operator++(F &lhs); + +struct S { + short _m; + S(short _m) : _m(_m) {} + + void putM(short rhs) { _m = rhs; } + short getM() { return _m; } + + __declspec(property(get = getM, put = putM)) short theData; +}; + +int test1a(int i) { + S tmp(i); + tmp.theData *= 2; + return tmp.theData; +} + +// CHECK-LABEL: define {{.*}} i32 @_Z6test1ai(i32 %i) +// CHECK: call void @_ZN1SC1Es +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv +// CHECK: call void @_ZN1S4putMEs +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv + +template +int test1b(int i) { + T tmp(i); + tmp.theData *= 2; + return tmp.theData; +} + +template int test1b(int); + +// CHECK-LABEL: define {{.*}} i32 @_Z6test1bI1SEii(i32 %i) +// CHECK: call void @_ZN1SC1Es +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv +// CHECK: call void @_ZN1S4putMEs +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv + +int test2a(int i) { + S tmp(i); + ++tmp.theData; + return tmp.theData; +} + +// CHECK-LABEL: define {{.*}} i32 @_Z6test2ai(i32 %i) +// CHECK: call void @_ZN1SC1Es +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv +// CHECK: call void @_ZN1S4putMEs +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv + +template +int test2b(int i) { + T tmp(i); + ++tmp.theData; + return tmp.theData; +} + +template int test2b(int); + +// CHECK-LABEL: define {{.*}} i32 @_Z6test2bI1SEii(i32 %i) +// CHECK: call void @_ZN1SC1Es +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv +// CHECK: call void @_ZN1S4putMEs +// CHECK: call {{(signext )?i16}} @_ZN1S4getMEv