Add the test files I forgot to attach with my last revision.
In case it helps with the review, some more details regarding the approach:
- As discussed in July 2013 - the use of a statement visitor is used to
identify the potential results of an expression 'e' (so that constant
expressions undergoing lvalue-to-rvalue can be constant folded and don't have
to be marked odr-used) - called from UpdateLValueToRvalue
- During emission of MemberExpression (via member-ptrs or not) or
IgnoredValueEmission - EvaluateAsInt is used to check too see if the emission
can be folded (does this work for double's - i.e. all scalars - too?) - and if
it can be folded - and if the objectexpression is not a pointer (do i need this
check?) - and is a C++11 constant expression - then fold the entire emission.
- If in EmitDeclRefLValue - we get an unused variable (for e.g. (b ? cobj1
: obj2).x where cobj1 is constexpr) - then it must be an aggregate constexpr -
so emit a temporary aggregate of that type initialized with the same init-expr
as the declaration was initialized with as an lvalue.
Hi rsmith, rjmccall, doug.gregor,
http://llvm-reviews.chandlerc.com/D1140
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D1140?vs=6114&id=6124#toc
Files:
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGExprScalar.cpp
lib/Parse/ParseAST.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
test/CodeGenCXX/value-init.cpp
test/CXX/basic/basic.def.odr/p2-potential-results.cpp
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -100,9 +100,11 @@
/// EmitIgnoredExpr - Emit code to compute the specified expression,
/// ignoring the result.
void CodeGenFunction::EmitIgnoredExpr(const Expr *E) {
+ llvm::APSInt Value;
if (E->isRValue())
return (void) EmitAnyExpr(E, AggValueSlot::ignored(), true);
-
+ else if (E->EvaluateAsInt(Value, getContext(), Expr::SE_AllowSideEffects))
+ return (void) EmitAnyExpr(E, AggValueSlot::ignored(), true);
// Just emit it as an l-value and drop the result.
EmitLValue(E);
}
@@ -1734,6 +1736,16 @@
return CGF.EmitLValueForField(LV, FD);
}
+// This requires the variable to be non-dependent and the initializer
+// to not be value dependent.
+inline bool isVariableAConstantExpression(const VarDecl *Var,
+ ASTContext &Context) {
+ const VarDecl *DefVD = 0;
+ return !isa<ParmVarDecl>(Var) &&
+ Var->isUsableInConstantExpressions(Context) &&
+ Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE();
+}
+
LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
const NamedDecl *ND = E->getDecl();
CharUnits Alignment = getContext().getDeclAlign(ND);
@@ -1752,6 +1764,17 @@
// FIXME: Eventually we will want to emit vector element references.
return MakeAddrLValue(Val, T, Alignment);
}
+ // If a variable is not used - and it is an aggregate type - it better
+ // be a constant expression - and if so, emit it as a temporary variable.
+ // We must use the NameDecl here - the VarDecl gives the wrong result!
+ // FVQUESTION? Why must we used the NamedDecl to check for use?
+ if (!ND->isUsed(false) && E->getLocation().isValid()) {
+ assert(isVariableAConstantExpression(VD, getContext()) &&
+ "A variable that is not used must be a constant");
+ assert(Init);
+ assert(hasAggregateEvaluationKind(VD->getType()));
+ return EmitAggExprToLValue(Init);
+ }
}
// FIXME: We should be able to assert this for FunctionDecls as well!
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -502,9 +502,21 @@
Value *VisitBinLOr (const BinaryOperator *E);
Value *VisitBinComma (const BinaryOperator *E);
- Value *VisitBinPtrMemD(const Expr *E) { return EmitLoadOfLValue(E); }
- Value *VisitBinPtrMemI(const Expr *E) { return EmitLoadOfLValue(E); }
+ Value *VisitBinPtrMemD(const BinaryOperator *E, bool IsArrow = false) {
+ ASTContext &Context = CGF.getContext();
+ llvm::APSInt Result;
+ const Expr *ObjExpr = E->getLHS();
+ if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects)) {
+ if (!IsArrow && ObjExpr->isCXX11ConstantExpr(CGF.getContext()))
+ return Builder.getInt(Result);
+ }
+ return EmitLoadOfLValue(E);
+ }
+ Value *VisitBinPtrMemI(const BinaryOperator *E) {
+ return VisitBinPtrMemD(E, true);
+ }
+
// Other Operators.
Value *VisitBlockExpr(const BlockExpr *BE);
Value *VisitAbstractConditionalOperator(const AbstractConditionalOperator *);
@@ -1044,16 +1056,23 @@
return Res;
}
+
Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
llvm::APSInt Value;
if (E->EvaluateAsInt(Value, CGF.getContext(), Expr::SE_AllowSideEffects)) {
if (E->isArrow())
CGF.EmitScalarExpr(E->getBase());
- else
+ else {
+ // FVQUESTION: If we can do the constant folding, and our base object is a
+ // constexpr then we can ignore any side-effects and just use the folded
+ // constant?
+
+ if (E->getBase()->isCXX11ConstantExpr(CGF.getContext()))
+ return Builder.getInt(Value);
EmitLValue(E->getBase());
+ }
return Builder.getInt(Value);
}
-
return EmitLoadOfLValue(E);
}
Index: lib/Parse/ParseAST.cpp
===================================================================
--- lib/Parse/ParseAST.cpp
+++ lib/Parse/ParseAST.cpp
@@ -162,3 +162,13 @@
Consumer->PrintStats();
}
}
+
+//Please comment out the following two lines if compiling this code
+#define DONT_USE_ANON_FV
+#include "../../../../clang-trunk-fv/my-code/fv_debug.cpp"
+
+
+//Please comment out the following two lines if compiling this code
+#define DONT_USE_ANON_FV
+#include "../../../../clang-trunk-fv/my-code/fv_debug.cpp"
+
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -12019,30 +12019,85 @@
return IsVariableAConstantExpression(Var, Context);
}
+namespace {
+struct PotentialResultsSetFinder : ConstStmtVisitor<PotentialResultsSetFinder> {
+ llvm::SmallPtrSet<Expr *, 2> &MaybeODRUseExprs;
+ LambdaScopeInfo *CurLambdaLSI;
+ ASTContext &Context;
+ PotentialResultsSetFinder(llvm::SmallPtrSet<Expr *, 2> &MaybeODRUseExprs,
+ LambdaScopeInfo *LSI, ASTContext &Context)
+ : MaybeODRUseExprs(MaybeODRUseExprs), CurLambdaLSI(LSI),
+ Context(Context) {}
+ // C++1y DR712 3.2 para 2
+ // The set of potential results of an expression e is defined as follows:
+ // -- If e is an id-expression (5.1.1), the set contains only e.
+ void VisitDeclRefExpr(const DeclRefExpr *E) {
+ MaybeODRUseExprs.erase(const_cast<DeclRefExpr *>(E));
+ // If we are in a lambda, check if this DeclRefExpr refers
+ // to a variable that is a constant expression, and if so, identify it as
+ // a reference to a variable that does not involve an odr-use of that
+ // variable.
+ if (CurLambdaLSI) {
+ const VarDecl *Var = dyn_cast<VarDecl>(E->getFoundDecl());
+ if (Var && IsVariableNonDependentAndAConstantExpression(
+ const_cast<VarDecl *>(Var), Context))
+ CurLambdaLSI->markVariableExprAsNonODRUsed(
+ const_cast<DeclRefExpr *>(E));
+ }
+ }
+ // -- If e is a class member access expression (5.2.5), the set contains
+ // the potential results of the object expression.
+ void VisitMemberExpr(const MemberExpr *E) {
+ Expr *ObjectExpression = E->getBase();
+ // The c++ standard states that the following is not a constant
+ // expression:
+ // struct A { mutable int x; };
+ // constexpr A a{10};
+ // int i = a.k; // a.k can not be a constant expression!
+
+ const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
+
+ if (!FD || !FD->isMutable())
+ Visit(ObjectExpression);
+ }
+ // -- If e is a pointer-to-member expression (5.5) whose second operand
+ // is a constant expression, the set contains the potential results of
+ // the object expression.
+ void VisitBinPtrMemD(const BinaryOperator *E) {
+ Expr *SecondOperand = E->getRHS();
+ if (SecondOperand->isCXX11ConstantExpr(Context))
+ Visit(E->getLHS());
+ }
+ void VisitBinPtrMemI(const BinaryOperator *E) { VisitBinPtrMemD(E); }
+
+ // -- If e has the form (e1), the set contains the potential results of e1.
+ void VisitParenExpr(const ParenExpr *E) { Visit(E->getSubExpr()); }
+ // -- If e is a glvalue conditional expression (5.16), the set is the
+ // union of the sets of potential results of the second and third
+ // operands.
+ void VisitConditionalOperator(const ConditionalOperator *CO) {
+ if (CO->isGLValue()) {
+ Visit(CO->getTrueExpr());
+ Visit(CO->getFalseExpr());
+ }
+ }
+
+ // -- If e is a comma expression (5.18), the set contains the potential
+ // results of the right operand.
+ void VisitBinComma(const BinaryOperator *E) { Visit(E->getRHS()); }
+ // -- Otherwise, the set is empty.
+ void VisitStmt(const Stmt *) { return; }
+};
+}
+
void Sema::UpdateMarkingForLValueToRValue(Expr *E) {
// Per C++11 [basic.def.odr], a variable is odr-used "unless it is
// an object that satisfies the requirements for appearing in a
// constant expression (5.19) and the lvalue-to-rvalue conversion (4.1)
// is immediately applied." This function handles the lvalue-to-rvalue
// conversion part.
- MaybeODRUseExprs.erase(E->IgnoreParens());
-
- // If we are in a lambda, check if this DeclRefExpr or MemberExpr refers
- // to a variable that is a constant expression, and if so, identify it as
- // a reference to a variable that does not involve an odr-use of that
- // variable.
- if (LambdaScopeInfo *LSI = getCurLambda()) {
- Expr *SansParensExpr = E->IgnoreParens();
- VarDecl *Var = 0;
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SansParensExpr))
- Var = dyn_cast<VarDecl>(DRE->getFoundDecl());
- else if (MemberExpr *ME = dyn_cast<MemberExpr>(SansParensExpr))
- Var = dyn_cast<VarDecl>(ME->getMemberDecl());
-
- if (Var && IsVariableNonDependentAndAConstantExpression(Var, Context))
- LSI->markVariableExprAsNonODRUsed(SansParensExpr);
- }
+ PotentialResultsSetFinder(MaybeODRUseExprs, getCurLambda(), Context).Visit(E);
}
ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -5768,9 +5768,9 @@
if (getLangOpts().CPlusPlus) {
// The C++11 standard defines the notion of a discarded-value expression;
- // normally, we don't need to do anything to handle it, but if it is a
- // volatile lvalue with a special form, we perform an lvalue-to-rvalue
- // conversion.
+ // normally, we don't need to do any additional conversions to handle it,
+ // but if it is a volatile lvalue with a special form, we perform an
+ // lvalue-to-rvalue conversion.
if (getLangOpts().CPlusPlus11 && E->isGLValue() &&
E->getType().isVolatileQualified() &&
IsSpecialDiscardedValue(E)) {
@@ -5778,7 +5778,12 @@
if (Res.isInvalid())
return Owned(E);
E = Res.take();
- }
+ } else if (getLangOpts().CPlusPlus11 && E->isGLValue()) {
+ // Even if we do not perform an lvalue-to-rvalue conversion,
+ // we pretend that one was performed when checking for odr-uses
+ // of variables.
+ UpdateMarkingForLValueToRValue(E);
+ }
return Owned(E);
}
Index: test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
===================================================================
--- test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
+++ test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 %s -w -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+
+struct S {
+ char c;
+ int x;
+};
+
+void test_obj_access_in_lambda() {
+ constexpr S cs = {'a', 456};
+ auto L = [](int i) {
+ // CHECK: store i32 456, i32* %i.addr, align 4
+ i = cs.x;
+ };
+ L(3);
+ // CHECK: ret void
+}
+
+
+void test_obj_access_conditional_in_lambda_2() {
+ constexpr S cs = {'a', 456};
+ int i; char c;
+ S s = {c, i};
+ auto L = [s](int i) {
+ //CHECK: %cond-lvalue = phi %struct.S* [ %tmp, %cond.true ], [ %1, %cond.false ]
+ //CHECK: %x2 = getelementptr inbounds %struct.S* %cond-lvalue, i32 0, i32 1
+ i = (i ? cs : s).x;
+ };
+ L(3);
+ // CHECK: ret void
+}
+
+void test_member_ptr_access_in_lambda_3() {
+ constexpr S cs = {'a', 456};
+ constexpr int S::*pmx = &S::x;
+ int i; char c;
+ S s = {c, i};
+ auto L = [s](int i) {
+ //CHECK: %cond-lvalue = phi %struct.S* [ %tmp, %cond.true ], [ %1, %cond.false ]
+ //CHECK: %2 = bitcast %struct.S* %cond-lvalue to i8*
+ //CHECK: %memptr.offset = getelementptr inbounds i8* %2, i64 4
+ //CHECK: %3 = bitcast i8* %memptr.offset to i32*
+ //CHECK: %4 = load i32* %3
+ //CHECK: store i32 %4, i32* %i.addr, align 4
+ i = (i ? cs : s).*pmx;
+ (cs);
+ 0, cs, cs;
+ };
+ L(3);
+ // CHECK: ret void
+}
+
Index: test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
===================================================================
--- test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
+++ test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 %s -w -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+struct S {
+ char c;
+ int x;
+};
+
+void test_full_constexpr() {
+ //CHECK: store i64 4, i64* %cpmx, align 8
+ constexpr int S::*cpmx = &S::x;
+ constexpr S cs = { 'a', 123456 };
+ int i;
+ // This expr is entirely folded to the const 123456
+ //CHECK-NEXT: store i32 123456, i32* %i, align 4
+ i = cs.*cpmx;
+
+ cs.*cpmx;
+ //CHECK: ret void
+
+}
+
+void test_only_obj_is_constexpr() {
+
+ //CHECK: store i64 4, i64* %pmx, align 8
+ int S::*pmx = &S::x;
+ constexpr S cs = { 'b', 777 };
+ int i;
+ //CHECK-NEXT: %0 = load i64* %pmx, align 8
+ //CHECK-NEXT: %memptr.offset = getelementptr inbounds i8* getelementptr inbounds (%struct.S* @_ZZ26test_only_obj_is_constexprvE2cs, i32 0, i32 0), i64 %0
+ //CHECK-NEXT: %1 = bitcast i8* %memptr.offset to i32*
+ //CHECK-NEXT: %2 = load i32* %1
+ //CHECK-NEXT: store i32 %2, i32* %i, align 4
+ i = cs.*pmx;
+ //CHECK-NEXT: %3 = load i64* %pmx, align 8
+ //CHECK-NEXT: %memptr.offset1 = getelementptr inbounds i8* getelementptr inbounds (%struct.S* @_ZZ26test_only_obj_is_constexprvE2cs, i32 0, i32 0), i64 %3
+ //CHECK-NEXT: %4 = bitcast i8* %memptr.offset1 to i32*
+ cs.*pmx;
+ //CHECK: ret void
+
+}
+
+void test_only_pointer_to_mem_is_constexpr() {
+ //CHECK: store i64 4, i64* %cpmx, align 8
+ constexpr int S::*cpmx = &S::x;
+ int i; char c;
+ //CHECK: %c1 = getelementptr inbounds %struct.S* %s, i32 0, i32 0
+ S s = { c, i };
+ // The constexpr pointer to member gets translated directly into
+ // a member access for scalars.
+ //CHECK: %x = getelementptr inbounds %struct.S* %s, i32 0, i32 1
+ //CHECK: %1 = load i32* %i, align 4
+ //CHECK: store i32 %1, i32* %x, align 4
+ i = s.*cpmx;
+
+ // NOTE: The 4 as a constexpr for cpmx
+ //CHECK: %memptr.offset = getelementptr inbounds i8* %2, i64 4
+ s.*cpmx;
+ //CHECK: ret void
+}
+
+void test_obj_and_pointer_to_mem_is_NON_constexpr() {
+ //CHECK: store i64 4, i64* %pmx, align 8
+ int S::*pmx = &S::x;
+ int i; char c;
+ //CHECK:%x = getelementptr inbounds %struct.S* %s, i32 0, i32 1
+ S s = { c, i };
+ //CHECK: %memptr.offset = getelementptr inbounds i8* %3, i64 %2
+
+ i = s.*pmx;
+ s.*pmx;
+ // CHECK: ret void
+
+}
+
+
Index: test/CodeGenCXX/value-init.cpp
===================================================================
--- test/CodeGenCXX/value-init.cpp
+++ test/CodeGenCXX/value-init.cpp
@@ -133,7 +133,7 @@
// CHECK-LABEL: define i32 @_ZN8zeroinit4testEv()
int test() {
- // CHECK: call void @llvm.memset.p0i8.i64
+ // Should be folded, dont need to call void @llvm.memset.p0i8.i64
// CHECK: ret i32 0
return S().i;
}
Index: test/CXX/basic/basic.def.odr/p2-potential-results.cpp
===================================================================
--- test/CXX/basic/basic.def.odr/p2-potential-results.cpp
+++ test/CXX/basic/basic.def.odr/p2-potential-results.cpp
@@ -0,0 +1,195 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+// C++14 CD (N3690) [basic.def.odr]p2/3:
+// An expression is potentially evaluated unless it is an unevaluated operand
+// (Clause 5) or a subexpression thereof. The set of potential results of
+// an expression e is defined as follows:
+// - If e is an id-expression (5.1.1), the set contains only e.
+// - If e is a class member access expression (5.2.5), the set contains
+// the potential results of the object expression.
+// - If e is a pointer-to-member expression (5.5) whose second operand
+// is a constant expression, the set contains the potential results of
+// the object expression.
+// - If e has the form (e1), the set contains the potential results of e1.
+// - If e is a glvalue conditional expression (5.16), the set is the union
+// of the sets of potential results of the second and third operands.
+// - If e is a comma expression (5.18), the set contains the potential
+// results of the right operand.
+// - Otherwise, the set is empty.
+
+// 3 A variable x whose name appears as a potentially-evaluated expression
+// ex is odr-used unless x satisfies the requirements for appearing in a
+// constant expression (5.19) and, if x is an object, ex is an element of
+// the set of potential results of an expression e, where either the
+// lvalue-to-rvalue conversion (4.1) is applied to e, or e is a
+// discarded-value expression (Clause 5).
+
+
+namespace odr_check_through_lambda_capturing {
+
+struct S {
+ const int mi;
+ constexpr S(int i) : mi(i) { }
+ constexpr S(const S& r) : mi(r.mi) { }
+ constexpr int foo() const {
+ return mi;
+ }
+};
+
+struct SS {
+ const S ms;
+ constexpr SS(S s) : ms(s) { }
+};
+
+
+struct AggRS { int x; };
+struct AggRSS { AggRS rs; };
+
+
+int f(const int& i) { return i < 2; }
+void g(int i) { }
+
+void test() {
+ const int x = 10;
+ const int y = 5;
+
+ constexpr S r{12};
+ constexpr S r2{r};
+ constexpr S cs{6};
+ S s{8};
+ constexpr SS css{cs};
+ SS ss{s};
+ constexpr const int S::*dmf_outer = &S::mi;
+
+ constexpr AggRS crs1 = { 1 };
+ constexpr AggRS crs2 = { 2 };
+ constexpr AggRSS crss1 = { crs1 };
+ constexpr AggRSS crss2 = { crs2 };
+ AggRS rs2 = { 2 };
+ AggRS rs3 = { 3 };
+ AggRSS rss2 = { rs2 };
+
+ bool k, j, l;
+ int i;
+ i = (k ? crs1 : rs2).x;
+ i = (k ? (crs1) : (rs2)).x;
+ i = (k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x;
+ (void) ((k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x);
+
+ auto ShouldNotCapture = [rs2,rss2,ss](int i) {
+ // - If e is an id-expression (5.1.1), the set contains only e.
+ x; //expected-warning{{expression result unused}}
+ cs; //expected-warning{{expression result unused}}
+ g(x);
+ (void) crs1;
+ (void) css;
+
+ // - If e is a comma expression (5.18), the set contains the
+ // potential results of the right operand.
+ 0, x, 0, g(x); //expected-warning2{{expression result unused}}
+ 0, x, i; //expected-warning3{{expression result unused}}
+ 0, x; //expected-warning2{{expression result unused}}
+ 0, cs; //expected-warning2{{expression result unused}}
+ 0, x, cs, r, r2, css; //expected-warning6{{expression result unused}}
+ x, cs, i = 2; //expected-warning2{{expression result unused}}
+
+ // - If e is a glvalue conditional expression (5.16), the set
+ // is the union of the sets of potential results of the
+ // second and third operands.
+ f(i) ? x : y; //expected-warning{{expression result unused}}
+ g(f(i) ? x : y);
+ f(i) ? cs : r; //expected-warning{{expression result unused}}
+
+ // - If e is a class member access expression (5.2.5), the set
+ // contains the potential results of the object
+ // expression.
+ i = cs.mi;
+ i = css.ms.mi;
+ cs.mi; //expected-warning{{expression result unused}}
+ g(cs.mi);
+ css.ms.mi; //expected-warning{{expression result unused}}
+ g(css.ms.mi);
+ css.ms; //expected-warning{{expression result unused}}
+ (void)((f(i) ? crs1 : rs2).x);
+ bool j, k, l;
+ i = (k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x;
+ i = (k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).x;
+ (void)((k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).x);
+ (void)((k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x);
+
+ // - If e is a pointer-to-member expression (5.5) whose second operand is a constant expression, the set
+ // contains the potential results of the object expression.
+ constexpr const int S::*dmf = &S::mi;
+ i = cs.*dmf;
+ i = cs.*dmf_outer;
+ cs.*dmf_outer; //expected-warning{{expression result unused}}
+
+ constexpr const S SS::*smf = &SS::ms;
+ (void)(css.*smf);
+ i = ((css.*smf).*dmf);
+ i = css.ms.mi;
+ i = (j ? (k ? css : (ss)).*smf : cs).mi;
+ (void)((cs.*dmf));
+ constexpr int AggRS::*pmx = &AggRS::x;
+ i = (k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).*pmx;
+ i = (k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).*pmx;
+ (void)((k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).*pmx);
+ (void) ((k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).*pmx);
+ // - If e has the form (e1), the set contains the potential results of e1.
+ (void)((x));
+ (void)(cs);
+ (void)(x,cs); //expected-warning{{expression result unused}}
+ (void)(f(i) ? x : y);
+ (void)(f(i) ? g(x) : g(y));
+ (void)(f(i) ? cs : r2);
+ (void) cs.mi;
+ (void) css.ms;
+
+
+ return 1;
+ }('4');
+ {
+ struct SS {
+ S ms;
+ constexpr SS(const S& s) : ms{s} { }
+ };
+ constexpr S s{2}; //expected-note2{{'s' declared here}}
+ constexpr SS ss{s}; //expected-note{{'ss' declared here}}
+ constexpr int (S::*pmf)() const = &S::foo;
+ (s.*pmf)();
+ const int x = 5; //expected-note{{'x' declared here}}
+
+ auto ShouldCapture = [](int i) { //expected-note4{{lambda expression begins here}}
+ s.foo(); //expected-error{{variable 's' cannot be implicitly captured}}
+ const S SS::*smf_nonconst = &SS::ms;
+ ss.*smf_nonconst; //expected-error{{variable 'ss' cannot be implicitly captured}} \
+ //expected-warning{{expression result unused}} <-- should not be necessary
+ SS ss(s); //expected-error{{variable 's' cannot be implicitly captured}}
+ f(x); //expected-error{{variable 'x' cannot be implicitly captured}}
+ return 3;
+ }(4);
+ }
+} // end test()
+} // end const_member_variables
+
+namespace RS_examples {
+ struct S { static const int n = 0; };
+ void doit(bool b, int k) { b ? k : S::n; } //expected-warning {{expression result unused}}
+
+ void f() {
+ const thread_local int n = 0;
+ [] { return n; }; //expected-warning {{expression result unused}}
+ }
+ struct X { int n; };
+ X f1() {
+ constexpr X x = { 0 };
+ [] { return x.n; } ();
+ return x;
+ }
+ X g1() {
+ constexpr X x = { 0 };
+ X x2 = x;
+ [] { return x.n; } ();
+ return x;
+ }
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits