Re-commit r282556, reverted in r282564, with a fix to CallArgList::addFrom to

function correctly when targeting MS ABIs (this appears to have never mattered
prior to this change).

Update test case to always cover both 32-bit and 64-bit Windows ABIs, since
they behave somewhat differently from each other here.

Update test case to also cover operators , && and ||, which it appears are also
affected by P0145R3 (they're not explicitly called out by the design document,
but this is the emergent behavior of the existing wording).


Original commit message:

P0145R3 (C++17 evaluation order tweaks): evaluate the right-hand side of
assignment and compound-assignment operators before the left-hand side. (Even
if it's an overloaded operator.)

This completes the implementation of P0145R3 + P0400R0 for all targets except
Windows, where the evaluation order guarantees for <<, >>, and ->* are
unimplementable as the ABI requires the function arguments are evaluated from
right to left (because parameter destructors are run from left to right in the
callee).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@282619 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2016-09-28 19:09:10 +00:00
parent 6a96fd6a52
commit 7da28aef68
10 changed files with 193 additions and 64 deletions

View File

@ -76,6 +76,16 @@ public:
/// expression refers to.
OverloadedOperatorKind getOperator() const { return Operator; }
static bool isAssignmentOp(OverloadedOperatorKind Opc) {
return Opc == OO_Equal || Opc == OO_StarEqual ||
Opc == OO_SlashEqual || Opc == OO_PercentEqual ||
Opc == OO_PlusEqual || Opc == OO_MinusEqual ||
Opc == OO_LessLessEqual || Opc == OO_GreaterGreaterEqual ||
Opc == OO_AmpEqual || Opc == OO_CaretEqual ||
Opc == OO_PipeEqual;
}
bool isAssignmentOp() const { return isAssignmentOp(getOperator()); }
/// \brief Returns the location of the operator symbol in the expression.
///
/// When \c getOperator()==OO_Call, this is the location of the right

View File

@ -3129,7 +3129,7 @@ static void emitWritebackArg(CodeGenFunction &CGF, CallArgList &args,
}
void CallArgList::allocateArgumentMemory(CodeGenFunction &CGF) {
assert(!StackBase && !StackCleanup.isValid());
assert(!StackBase);
// Save the stack.
llvm::Function *F = CGF.CGM.getIntrinsic(llvm::Intrinsic::stacksave);
@ -3172,7 +3172,8 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
void CodeGenFunction::EmitCallArgs(
CallArgList &Args, ArrayRef<QualType> ArgTypes,
llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
const FunctionDecl *CalleeDecl, unsigned ParamsToSkip) {
const FunctionDecl *CalleeDecl, unsigned ParamsToSkip,
bool ForceRightToLeftEvaluation) {
assert((int)ArgTypes.size() == (ArgRange.end() - ArgRange.begin()));
auto MaybeEmitImplicitObjectSize = [&](unsigned I, const Expr *Arg) {
@ -3191,7 +3192,8 @@ void CodeGenFunction::EmitCallArgs(
// We *have* to evaluate arguments from right to left in the MS C++ ABI,
// because arguments are destroyed left to right in the callee.
if (CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
if (CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee() ||
ForceRightToLeftEvaluation) {
// Insert a stack save if we're going to need any inalloca args.
bool HasInAllocaArgs = false;
for (ArrayRef<QualType>::iterator I = ArgTypes.begin(), E = ArgTypes.end();

View File

@ -81,10 +81,19 @@ namespace CodeGen {
push_back(CallArg(rvalue, type, needscopy));
}
/// Add all the arguments from another CallArgList to this one. After doing
/// this, the old CallArgList retains its list of arguments, but must not
/// be used to emit a call.
void addFrom(const CallArgList &other) {
insert(end(), other.begin(), other.end());
Writebacks.insert(Writebacks.end(),
other.Writebacks.begin(), other.Writebacks.end());
CleanupsToDeactivate.insert(CleanupsToDeactivate.end(),
other.CleanupsToDeactivate.begin(),
other.CleanupsToDeactivate.end());
assert(!(StackBase && other.StackBase) && "can't merge stackbases");
if (!StackBase)
StackBase = other.StackBase;
}
void addWriteback(LValue srcLV, Address temporary,
@ -132,11 +141,6 @@ namespace CodeGen {
/// The stacksave call. It dominates all of the argument evaluation.
llvm::CallInst *StackBase;
/// The iterator pointing to the stack restore cleanup. We manually run and
/// deactivate this cleanup after the call in the unexceptional case because
/// it doesn't run in the normal order.
EHScopeStack::stable_iterator StackCleanup;
};
/// FunctionArgList - Type for representing both the decl and type

View File

@ -4121,8 +4121,17 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
if (Chain)
Args.add(RValue::get(Builder.CreateBitCast(Chain, CGM.VoidPtrTy)),
CGM.getContext().VoidPtrTy);
// C++17 requires that we evaluate arguments to a call using assignment syntax
// right-to-left. It also requires that we evaluate arguments to operators
// <<, >>, and ->* left-to-right, but that is not possible under the MS ABI,
// so there is no corresponding "force left-to-right" case.
bool ForceRightToLeft = false;
if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(E))
ForceRightToLeft = OCE->isAssignmentOp();
EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), E->arguments(),
E->getDirectCallee(), /*ParamsToSkip*/ 0);
E->getDirectCallee(), /*ParamsToSkip*/ 0, ForceRightToLeft);
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall(
Args, FnType, /*isChainCall=*/Chain);

View File

@ -28,7 +28,7 @@ static RequiredArgs
commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
llvm::Value *This, llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *CE,
CallArgList &Args) {
CallArgList &Args, CallArgList *RtlArgs) {
assert(CE == nullptr || isa<CXXMemberCallExpr>(CE) ||
isa<CXXOperatorCallExpr>(CE));
assert(MD->isInstance() &&
@ -61,7 +61,12 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, Args.size(), MD);
// And the rest of the call args.
if (CE) {
if (RtlArgs) {
// Special case: if the caller emitted the arguments right-to-left already
// (prior to emitting the *this argument), we're done. This happens for
// assignment operators.
Args.addFrom(*RtlArgs);
} else if (CE) {
// Special case: skip first argument of CXXOperatorCall (it is "this").
unsigned ArgsToSkip = isa<CXXOperatorCallExpr>(CE) ? 1 : 0;
CGF.EmitCallArgs(Args, FPT, drop_begin(CE->arguments(), ArgsToSkip),
@ -77,11 +82,11 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
const CallExpr *CE) {
const CallExpr *CE, CallArgList *RtlArgs) {
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
CallArgList Args;
RequiredArgs required = commonEmitCXXMemberOrOperatorCall(
*this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args);
*this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs);
return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required),
Callee, ReturnValue, Args, MD);
}
@ -92,7 +97,7 @@ RValue CodeGenFunction::EmitCXXDestructorCall(
StructorType Type) {
CallArgList Args;
commonEmitCXXMemberOrOperatorCall(*this, DD, This, ImplicitParam,
ImplicitParamTy, CE, Args);
ImplicitParamTy, CE, Args, nullptr);
return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(DD, Type),
Callee, ReturnValueSlot(), Args, DD);
}
@ -170,6 +175,19 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
}
}
// C++17 demands that we evaluate the RHS of a (possibly-compound) assignment
// operator before the LHS.
CallArgList RtlArgStorage;
CallArgList *RtlArgs = nullptr;
if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
if (OCE->isAssignmentOp()) {
RtlArgs = &RtlArgStorage;
EmitCallArgs(*RtlArgs, MD->getType()->castAs<FunctionProtoType>(),
drop_begin(CE->arguments(), 1), CE->getDirectCallee(),
/*ParamsToSkip*/0, /*ForceRightToLeftEvaluation*/true);
}
}
Address This = Address::invalid();
if (IsArrow)
This = EmitPointerWithAlignment(Base);
@ -187,10 +205,12 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
if (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) {
// We don't like to generate the trivial copy/move assignment operator
// when it isn't necessary; just produce the proper effect here.
// Special case: skip first argument of CXXOperatorCall (it is "this").
unsigned ArgsToSkip = isa<CXXOperatorCallExpr>(CE) ? 1 : 0;
Address RHS = EmitLValue(*(CE->arg_begin() + ArgsToSkip)).getAddress();
EmitAggregateAssign(This, RHS, CE->getType());
LValue RHS = isa<CXXOperatorCallExpr>(CE)
? MakeNaturalAlignAddrLValue(
(*RtlArgs)[0].RV.getScalarVal(),
(*(CE->arg_begin() + 1))->getType())
: EmitLValue(*CE->arg_begin());
EmitAggregateAssign(This, RHS.getAddress(), CE->getType());
return RValue::get(This.getPointer());
}
@ -249,7 +269,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
Callee = CGM.GetAddrOfFunction(GlobalDecl(DDtor, Dtor_Complete), Ty);
}
EmitCXXMemberOrOperatorCall(MD, Callee, ReturnValue, This.getPointer(),
/*ImplicitParam=*/nullptr, QualType(), CE);
/*ImplicitParam=*/nullptr, QualType(), CE,
nullptr);
}
return RValue::get(nullptr);
}
@ -282,7 +303,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
}
return EmitCXXMemberOrOperatorCall(MD, Callee, ReturnValue, This.getPointer(),
/*ImplicitParam=*/nullptr, QualType(), CE);
/*ImplicitParam=*/nullptr, QualType(), CE,
RtlArgs);
}
RValue

View File

@ -2867,7 +2867,8 @@ public:
EmitCXXMemberOrOperatorCall(const CXXMethodDecl *MD, llvm::Value *Callee,
ReturnValueSlot ReturnValue, llvm::Value *This,
llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *E);
QualType ImplicitParamTy, const CallExpr *E,
CallArgList *RtlArgs);
RValue EmitCXXDestructorCall(const CXXDestructorDecl *DD, llvm::Value *Callee,
llvm::Value *This, llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *E,
@ -3322,7 +3323,8 @@ public:
void EmitCallArgs(CallArgList &Args, const T *CallArgTypeInfo,
llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
const FunctionDecl *CalleeDecl = nullptr,
unsigned ParamsToSkip = 0) {
unsigned ParamsToSkip = 0,
bool ForceRightToLeftEvaluation = false) {
SmallVector<QualType, 16> ArgTypes;
CallExpr::const_arg_iterator Arg = ArgRange.begin();
@ -3362,13 +3364,15 @@ public:
for (auto *A : llvm::make_range(Arg, ArgRange.end()))
ArgTypes.push_back(getVarArgType(A));
EmitCallArgs(Args, ArgTypes, ArgRange, CalleeDecl, ParamsToSkip);
EmitCallArgs(Args, ArgTypes, ArgRange, CalleeDecl, ParamsToSkip,
ForceRightToLeftEvaluation);
}
void EmitCallArgs(CallArgList &Args, ArrayRef<QualType> ArgTypes,
llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
const FunctionDecl *CalleeDecl = nullptr,
unsigned ParamsToSkip = 0);
unsigned ParamsToSkip = 0,
bool ForceRightToLeftEvaluation = false);
/// EmitPointerWithAlignment - Given an expression with a pointer
/// type, emit the value and compute our best estimate of the

View File

@ -1456,7 +1456,8 @@ void ItaniumCXXABI::EmitDestructorCall(CodeGenFunction &CGF,
Callee = CGM.getAddrOfCXXStructor(DD, getFromDtorType(Type));
CGF.EmitCXXMemberOrOperatorCall(DD, Callee, ReturnValueSlot(),
This.getPointer(), VTT, VTTTy, nullptr);
This.getPointer(), VTT, VTTTy,
nullptr, nullptr);
}
void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
@ -1636,7 +1637,7 @@ llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(),
This.getPointer(), /*ImplicitParam=*/nullptr,
QualType(), CE);
QualType(), CE, nullptr);
return nullptr;
}

View File

@ -1,5 +1,6 @@
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple %ms_abi_triple | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple i686-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple x86_64-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS
struct B;
struct A {
@ -18,15 +19,25 @@ struct B {
struct C {
operator int *();
A *operator->();
void operator->*(B);
void operator->*(A);
friend void operator->*(C, B);
friend void operator<<(C, B);
friend void operator>>(C, B);
void operator<<(A);
void operator>>(A);
void operator=(B);
void operator+=(B);
void operator=(A);
void operator+=(A);
friend void operator+=(C, B);
void operator,(A);
friend void operator,(C, B);
void operator&&(A);
void operator||(A);
friend void operator&&(C, B);
friend void operator||(C, B);
};
A make_a();
@ -137,7 +148,19 @@ int dotstar_lhs_before_rhs() {
int b = make_a_ptr()->*make_mem_ptr_a();
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
make_c()->*make_a();
// FIXME: The corresponding case for Windows ABIs is unimplementable if the
// operand has a non-trivially-destructible type, because the order of
// construction of function arguments is defined by the ABI there (it's the
// reverse of the order in which the parameters are destroyed in the callee).
// But we could follow the C++17 rule in the case where the operand type is
// trivially-destructible.
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}(
make_c()->*make_b();
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
@ -154,61 +177,106 @@ int dotstar_lhs_before_rhs() {
// CHECK: }
}
#if 0
// CHECKDISABLED-LABEL: define {{.*}}@{{.*}}assign_lhs_before_rhs{{.*}}(
// CHECK-LABEL: define {{.*}}@{{.*}}assign_rhs_before_lhs{{.*}}(
void assign_rhs_before_lhs() {
extern int &lhs_ref(), rhs();
// CHECKDISABLED: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}lhs_ref{{.*}}(
// CHECK: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}lhs_ref{{.*}}(
lhs_ref() = rhs();
// CHECKDISABLED: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}lhs_ref{{.*}}(
// CHECK: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}lhs_ref{{.*}}(
lhs_ref() += rhs();
// CHECKDISABLED: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}lhs_ref{{.*}}(
// CHECK: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}lhs_ref{{.*}}(
lhs_ref() %= rhs();
// CHECKDISABLED: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() = make_b();
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() = make_a();
// CHECKDISABLED: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() += make_a();
// CHECK: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() += make_b();
// CHECKDISABLED: }
// CHECK: }
}
#endif
#if 0
// CHECKDISABLED-LABEL: define {{.*}}@{{.*}}shift_lhs_before_rhs{{.*}}(
// CHECK-LABEL: define {{.*}}@{{.*}}shift_lhs_before_rhs{{.*}}(
void shift_lhs_before_rhs() {
extern int lhs(), rhs();
// CHECKDISABLED: call {{.*}}@{{.*}}lhs{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}lhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}rhs{{.*}}(
(void)(lhs() << rhs());
// CHECKDISABLED: call {{.*}}@{{.*}}lhs{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}rhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}lhs{{.*}}(
// CHECK: call {{.*}}@{{.*}}rhs{{.*}}(
(void)(lhs() >> rhs());
// CHECKDISABLED: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}make_a{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
make_c() << make_a();
// CHECKDISABLED: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}make_a{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
make_c() >> make_a();
// CHECKDISABLED: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}make_b{{.*}}(
// FIXME: This is unimplementable for Windows ABIs, see above.
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() << make_b();
// CHECKDISABLED: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECKDISABLED: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() >> make_b();
// CHECKDISABLED: }
// CHECK: }
}
// CHECK-LABEL: define {{.*}}@{{.*}}comma_lhs_before_rhs{{.*}}(
void comma_lhs_before_rhs() {
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
make_c() , make_a();
// FIXME: This is unimplementable for Windows ABIs, see above.
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() , make_b();
}
// CHECK-LABEL: define {{.*}}@{{.*}}andor_lhs_before_rhs{{.*}}(
void andor_lhs_before_rhs() {
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
make_c() && make_a();
// CHECK: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK: call {{.*}}@{{.*}}make_a{{.*}}(
make_c() || make_a();
// FIXME: This is unimplementable for Windows ABIs, see above.
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() && make_b();
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}(
// CHECK-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}(
// CHECK-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}(
make_c() || make_b();
}
#endif

View File

@ -44,8 +44,8 @@ struct TCPPObject
// CHECK: ret void
// CHECK-LABEL: define internal void @__assign_helper_atomic_property_(%struct.TCPPObject*, %struct.TCPPObject*) #
// CHECK: [[TWO:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR:%.*]], align 8
// CHECK: [[THREE:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR1:%.*]], align 8
// CHECK: [[TWO:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR:%.*]], align 8
// CHECK: [[CALL:%.*]] = call dereferenceable({{[0-9]+}}) %struct.TCPPObject* @_ZN10TCPPObjectaSERKS_(%struct.TCPPObject* [[TWO]], %struct.TCPPObject* dereferenceable({{[0-9]+}}) [[THREE]])
// CHECK: ret void

View File

@ -701,7 +701,7 @@ as the draft C++1z standard evolves.</p>
<tr>
<td rowspan=2>Stricter expression evaluation order</td>
<td><a href="http://wg21.link/p0145r3">P0145R3</a></td>
<td class="none" align="center" rowspan=2>No</td>
<td class="svn" align="center" rowspan=2>SVN <a href="#p0145">(10)</a></td>
</tr>
<tr>
<td><a href="http://wg21.link/p0400r0">P0400R0</a></td>
@ -741,6 +741,15 @@ In Clang 3.7, a warning is emitted for all cases that would change meaning.
</span><br>
<span id="p0136">(9): This is the resolution to a Defect Report, so is applied
to all language versions supporting inheriting constructors.
</span><br>
<span id="p0145">(10): Under the MS ABI, this feature is not fully implementable,
because the calling convention requires that function parameters are destroyed
from left to right in the callee. In order to guarantee that destruction order
is reverse construction order, the operands of overloaded
<tt>operator&lt;&lt;</tt>, <tt>operator&gt;&gt;</tt>, <tt>operator-&gt;*</tt>,
<tt>operator&amp;&amp;</tt>, <tt>operator||</tt>, and <tt>operator,</tt>
functions are evaluated right-to-left under the MS ABI when called using operator
syntax, not left-to-right as P0145R3 requires.
</span>
</p>