P0145R2: Refining Expression Order for C++.

gcc/c-family/
	* c.opt (fargs-in-order): New.
	* c-opts.c (c_common_post_options): Adjust flag_args_in_order.
gcc/cp/
	* cp-tree.h (CALL_EXPR_OPERATOR_SYNTAX, CALL_EXPR_ORDERED_ARGS)
	(CALL_EXPR_REVERSE_ARGS): New.
	* call.c (build_new_op_1): Set them.
	(extract_call_expr, op_is_ordered): New.
	(build_over_call): Set CALL_EXPR_ORDERED_ARGS.
	* cp-gimplify.c (cp_gimplify_expr) [CALL_EXPR]: Handle new flags.
	* pt.c (tsubst_copy_and_build): Copy new flags.
	* semantics.c (simplify_aggr_init_expr): Likewise.
	* tree.c (build_aggr_init_expr): Likewise.
	(build_min_non_dep_op_overload): Likewise.

From-SVN: r237459
This commit is contained in:
Jason Merrill 2016-06-14 16:18:34 -04:00 committed by Jason Merrill
parent a09c81b4ba
commit 4eb24e0109
14 changed files with 403 additions and 40 deletions

View File

@ -1,3 +1,9 @@
2016-06-14 Jason Merrill <jason@redhat.com>
P0145R2: Refining Expression Order for C++.
* c.opt (fargs-in-order): New.
* c-opts.c (c_common_post_options): Adjust flag_args_in_order.
2016-06-13 Jakub Jelinek <jakub@redhat.com>
PR sanitizer/71498

View File

@ -910,6 +910,12 @@ c_common_post_options (const char **pfilename)
else if (warn_narrowing == -1)
warn_narrowing = 0;
/* C++17 requires that function arguments be evaluated left-to-right even on
PUSH_ARGS_REVERSED targets. */
if (c_dialect_cxx ()
&& flag_args_in_order == -1)
flag_args_in_order = 2 /*(cxx_dialect >= cxx1z) ? 2 : 0*/;
/* Global sized deallocation is new in C++14. */
if (flag_sized_deallocation == -1)
flag_sized_deallocation = (cxx_dialect >= cxx14);

View File

@ -1043,6 +1043,14 @@ falt-external-templates
C++ ObjC++ Ignore Warn(switch %qs is no longer supported)
No longer supported.
fargs-in-order
C++ ObjC++ Alias(fargs-in-order=, 2, 0)
Always evaluate function arguments in left-to-right order.
fargs-in-order=
C++ ObjC++ Var(flag_args_in_order) Joined UInteger Init(-1)
Always evaluate function arguments in left-to-right order.
fasm
C ObjC C++ ObjC++ Var(flag_no_asm, 0)
Recognize the \"asm\" keyword.

View File

@ -1,3 +1,17 @@
2016-06-14 Jason Merrill <jason@redhat.com>
P0145R2: Refining Expression Order for C++.
* cp-tree.h (CALL_EXPR_OPERATOR_SYNTAX, CALL_EXPR_ORDERED_ARGS)
(CALL_EXPR_REVERSE_ARGS): New.
* call.c (build_new_op_1): Set them.
(extract_call_expr, op_is_ordered): New.
(build_over_call): Set CALL_EXPR_ORDERED_ARGS.
* cp-gimplify.c (cp_gimplify_expr) [CALL_EXPR]: Handle new flags.
* pt.c (tsubst_copy_and_build): Copy new flags.
* semantics.c (simplify_aggr_init_expr): Likewise.
* tree.c (build_aggr_init_expr): Likewise.
(build_min_non_dep_op_overload): Likewise.
2016-06-14 Jakub Jelinek <jakub@redhat.com>
PR c++/71528

View File

@ -5372,6 +5372,40 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
}
}
/* Returns 1 if P0145R2 says that the LHS of operator CODE is evaluated first,
-1 if the RHS is evaluated first, or 0 if the order is unspecified. */
static int
op_is_ordered (tree_code code)
{
if (!flag_args_in_order)
return 0;
switch (code)
{
// 5. b @= a
case MODIFY_EXPR:
return -1;
// 1. a.b
// Not overloadable (yet).
// 2. a->b
// Only one argument.
// 3. a->*b
case MEMBER_REF:
// 6. a[b]
case ARRAY_REF:
// 7. a << b
case LSHIFT_EXPR:
// 8. a >> b
case RSHIFT_EXPR:
return 1;
default:
return 0;
}
}
static tree
build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
tree arg2, tree arg3, tree *overload, tsubst_flags_t complain)
@ -5660,17 +5694,33 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
else
result = build_over_call (cand, LOOKUP_NORMAL, complain);
if (processing_template_decl
&& result != NULL_TREE
&& result != error_mark_node
&& DECL_HIDDEN_FRIEND_P (cand->fn))
if (trivial_fn_p (cand->fn))
/* There won't be a CALL_EXPR. */;
else if (result && result != error_mark_node)
{
tree call = result;
if (REFERENCE_REF_P (call))
call = TREE_OPERAND (call, 0);
/* This prevents build_new_function_call from discarding this
function during instantiation of the enclosing template. */
KOENIG_LOOKUP_P (call) = 1;
tree call = extract_call_expr (result);
CALL_EXPR_OPERATOR_SYNTAX (call) = true;
if (processing_template_decl && DECL_HIDDEN_FRIEND_P (cand->fn))
/* This prevents build_new_function_call from discarding this
function during instantiation of the enclosing template. */
KOENIG_LOOKUP_P (call) = 1;
/* Specify evaluation order as per P0145R2. */
CALL_EXPR_ORDERED_ARGS (call) = false;
switch (op_is_ordered (code))
{
case -1:
CALL_EXPR_REVERSE_ARGS (call) = true;
break;
case 1:
CALL_EXPR_ORDERED_ARGS (call) = true;
break;
default:
break;
}
}
}
else
@ -5846,6 +5896,25 @@ build_new_op (location_t loc, enum tree_code code, int flags,
return ret;
}
/* CALL was returned by some call-building function; extract the actual
CALL_EXPR from any bits that have been tacked on, e.g. by
convert_from_reference. */
tree
extract_call_expr (tree call)
{
while (TREE_CODE (call) == COMPOUND_EXPR)
call = TREE_OPERAND (call, 1);
if (REFERENCE_REF_P (call))
call = TREE_OPERAND (call, 0);
if (TREE_CODE (call) == TARGET_EXPR)
call = TARGET_EXPR_INITIAL (call);
gcc_assert (TREE_CODE (call) == CALL_EXPR
|| TREE_CODE (call) == AGGR_INIT_EXPR
|| call == error_mark_node);
return call;
}
/* Returns true if FN has two parameters, of which the second has type
size_t. */
@ -7533,10 +7602,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
}
/* Ellipsis */
int magic = magic_varargs_p (fn);
for (; arg_index < vec_safe_length (args); ++arg_index)
{
tree a = (*args)[arg_index];
int magic = magic_varargs_p (fn);
if (magic == 2)
{
/* Do no conversions for certain magic varargs. */
@ -7666,9 +7735,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
if (is_really_empty_class (type))
{
/* Avoid copying empty classes. */
val = build2 (COMPOUND_EXPR, void_type_node, to, arg);
TREE_NO_WARNING (val) = 1;
val = build2 (COMPOUND_EXPR, type, val, to);
val = build2 (COMPOUND_EXPR, type, arg, to);
TREE_NO_WARNING (val) = 1;
}
else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
@ -7756,9 +7823,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
}
tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
if (TREE_CODE (call) == CALL_EXPR
&& (cand->flags & LOOKUP_LIST_INIT_CTOR))
CALL_EXPR_LIST_INIT_P (call) = true;
if (call != error_mark_node
&& !magic
&& (flag_args_in_order > 1
|| (cand->flags & LOOKUP_LIST_INIT_CTOR)))
{
tree c = extract_call_expr (call);
/* build_new_op_1 will clear this when appropriate. */
CALL_EXPR_ORDERED_ARGS (c) = true;
}
return call;
}

View File

@ -565,6 +565,7 @@ int
cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
{
int saved_stmts_are_full_exprs_p = 0;
location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
enum tree_code code = TREE_CODE (*expr_p);
enum gimplify_status ret;
@ -752,18 +753,26 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
cilk_cp_gimplify_call_params_in_spawned_fn (expr_p, pre_p, post_p);
return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
}
/* DR 1030 says that we need to evaluate the elements of an
initializer-list in forward order even when it's used as arguments to
a constructor. So if the target wants to evaluate them in reverse
order and there's more than one argument other than 'this', gimplify
them in order. */
ret = GS_OK;
if (PUSH_ARGS_REVERSED && CALL_EXPR_LIST_INIT_P (*expr_p)
&& call_expr_nargs (*expr_p) > 2)
if (!CALL_EXPR_FN (*expr_p))
/* Internal function call. */;
else if (CALL_EXPR_REVERSE_ARGS (*expr_p))
{
int nargs = call_expr_nargs (*expr_p);
location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
for (int i = 1; i < nargs; ++i)
/* This is a call to a (compound) assignment operator that used
the operator syntax; gimplify the RHS first. */
gcc_assert (call_expr_nargs (*expr_p) == 2);
gcc_assert (!CALL_EXPR_ORDERED_ARGS (*expr_p));
enum gimplify_status t
= gimplify_arg (&CALL_EXPR_ARG (*expr_p, 1), pre_p, loc);
if (t == GS_ERROR)
ret = GS_ERROR;
}
else if (CALL_EXPR_ORDERED_ARGS (*expr_p))
{
/* Leave the last argument for gimplify_call_expr, to avoid problems
with __builtin_va_arg_pack(). */
int nargs = call_expr_nargs (*expr_p) - 1;
for (int i = 0; i < nargs; ++i)
{
enum gimplify_status t
= gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, loc);
@ -771,6 +780,22 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
ret = GS_ERROR;
}
}
else if (flag_args_in_order == 1
&& !CALL_EXPR_OPERATOR_SYNTAX (*expr_p))
{
/* If flag_args_in_order == 1, we don't force an order on all
function arguments, but do evaluate the object argument first. */
tree fntype = TREE_TYPE (CALL_EXPR_FN (*expr_p));
if (POINTER_TYPE_P (fntype))
fntype = TREE_TYPE (fntype);
if (TREE_CODE (fntype) == METHOD_TYPE)
{
enum gimplify_status t
= gimplify_arg (&CALL_EXPR_ARG (*expr_p, 0), pre_p, loc);
if (t == GS_ERROR)
ret = GS_ERROR;
}
}
break;
case RETURN_EXPR:

View File

@ -179,19 +179,21 @@ operator == (const cp_expr &lhs, tree rhs)
IDENTIFIER_CTOR_OR_DTOR_P (in IDENTIFIER_NODE)
BIND_EXPR_BODY_BLOCK (in BIND_EXPR)
DECL_NON_TRIVIALLY_INITIALIZED_P (in VAR_DECL)
CALL_EXPR_LIST_INIT_P (in CALL_EXPR, AGGR_INIT_EXPR)
CALL_EXPR_ORDERED_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
4: TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
or FIELD_DECL).
CALL_EXPR, or FIELD_DECL).
IDENTIFIER_TYPENAME_P (in IDENTIFIER_NODE)
DECL_TINFO_P (in VAR_DECL)
FUNCTION_REF_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
5: C_IS_RESERVED_WORD (in IDENTIFIER_NODE)
DECL_VTABLE_OR_VTT_P (in VAR_DECL)
FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
TYPE_MARKED_P (in _TYPE)
RANGE_FOR_IVDEP (in RANGE_FOR_STMT)
CALL_EXPR_OPERATOR_SYNTAX (in CALL_EXPR, AGGR_INIT_EXPR)
Usage of TYPE_LANG_FLAG_?:
0: TYPE_DEPENDENT_P
@ -3379,6 +3381,9 @@ extern void decl_shadowed_for_var_insert (tree, tree);
#define DELETE_EXPR_USE_VEC(NODE) \
TREE_LANG_FLAG_1 (DELETE_EXPR_CHECK (NODE))
#define CALL_OR_AGGR_INIT_CHECK(NODE) \
TREE_CHECK2 ((NODE), CALL_EXPR, AGGR_INIT_EXPR)
/* Indicates that this is a non-dependent COMPOUND_EXPR which will
resolve to a function call. */
#define COMPOUND_EXPR_OVERLOADED(NODE) \
@ -3388,9 +3393,20 @@ extern void decl_shadowed_for_var_insert (tree, tree);
should be performed at instantiation time. */
#define KOENIG_LOOKUP_P(NODE) TREE_LANG_FLAG_0 (CALL_EXPR_CHECK (NODE))
/* True if CALL_EXPR expresses list-initialization of an object. */
#define CALL_EXPR_LIST_INIT_P(NODE) \
TREE_LANG_FLAG_3 (TREE_CHECK2 ((NODE),CALL_EXPR,AGGR_INIT_EXPR))
/* True if the arguments to NODE should be evaluated in left-to-right
order regardless of PUSH_ARGS_REVERSED. */
#define CALL_EXPR_ORDERED_ARGS(NODE) \
TREE_LANG_FLAG_3 (CALL_OR_AGGR_INIT_CHECK (NODE))
/* True if the arguments to NODE should be evaluated in right-to-left
order regardless of PUSH_ARGS_REVERSED. */
#define CALL_EXPR_REVERSE_ARGS(NODE) \
TREE_LANG_FLAG_5 (CALL_OR_AGGR_INIT_CHECK (NODE))
/* True if CALL_EXPR was written as an operator expression, not a function
call. */
#define CALL_EXPR_OPERATOR_SYNTAX(NODE) \
TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE))
/* Indicates whether a string literal has been parenthesized. Such
usages are disallowed in certain circumstances. */
@ -5542,6 +5558,7 @@ extern bool null_ptr_cst_p (tree);
extern bool null_member_pointer_value_p (tree);
extern bool sufficient_parms_p (const_tree);
extern tree type_decays_to (tree);
extern tree extract_call_expr (tree);
extern tree build_user_type_conversion (tree, tree, int,
tsubst_flags_t);
extern tree build_new_function_call (tree, vec<tree, va_gc> **, bool,

View File

@ -16652,6 +16652,20 @@ tsubst_copy_and_build (tree t,
release_tree_vector (call_args);
if (ret != error_mark_node)
{
bool op = CALL_EXPR_OPERATOR_SYNTAX (t);
bool ord = CALL_EXPR_ORDERED_ARGS (t);
bool rev = CALL_EXPR_REVERSE_ARGS (t);
if (op || ord || rev)
{
function = extract_call_expr (ret);
CALL_EXPR_OPERATOR_SYNTAX (function) = op;
CALL_EXPR_ORDERED_ARGS (function) = ord;
CALL_EXPR_REVERSE_ARGS (function) = rev;
}
}
RETURN (ret);
}

View File

@ -4057,8 +4057,11 @@ simplify_aggr_init_expr (tree *tp)
aggr_init_expr_nargs (aggr_init_expr),
AGGR_INIT_EXPR_ARGP (aggr_init_expr));
TREE_NOTHROW (call_expr) = TREE_NOTHROW (aggr_init_expr);
CALL_EXPR_LIST_INIT_P (call_expr) = CALL_EXPR_LIST_INIT_P (aggr_init_expr);
CALL_FROM_THUNK_P (call_expr) = AGGR_INIT_FROM_THUNK_P (aggr_init_expr);
CALL_EXPR_OPERATOR_SYNTAX (call_expr)
= CALL_EXPR_OPERATOR_SYNTAX (aggr_init_expr);
CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (aggr_init_expr);
CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (aggr_init_expr);
if (style == ctor)
{

View File

@ -524,7 +524,9 @@ build_aggr_init_expr (tree type, tree init)
TREE_SIDE_EFFECTS (rval) = 1;
AGGR_INIT_VIA_CTOR_P (rval) = is_ctor;
TREE_NOTHROW (rval) = TREE_NOTHROW (init);
CALL_EXPR_LIST_INIT_P (rval) = CALL_EXPR_LIST_INIT_P (init);
CALL_EXPR_OPERATOR_SYNTAX (rval) = CALL_EXPR_OPERATOR_SYNTAX (init);
CALL_EXPR_ORDERED_ARGS (rval) = CALL_EXPR_ORDERED_ARGS (init);
CALL_EXPR_REVERSE_ARGS (rval) = CALL_EXPR_REVERSE_ARGS (init);
}
else
rval = init;
@ -2854,8 +2856,7 @@ build_min_non_dep_op_overload (enum tree_code op,
tree fn, call;
vec<tree, va_gc> *args;
if (REFERENCE_REF_P (non_dep))
non_dep = TREE_OPERAND (non_dep, 0);
non_dep = extract_call_expr (non_dep);
nargs = call_expr_nargs (non_dep);
@ -2897,10 +2898,11 @@ build_min_non_dep_op_overload (enum tree_code op,
call = build_min_non_dep_call_vec (non_dep, fn, args);
release_tree_vector (args);
tree call_expr = call;
if (REFERENCE_REF_P (call_expr))
call_expr = TREE_OPERAND (call_expr, 0);
tree call_expr = extract_call_expr (call);
KOENIG_LOOKUP_P (call_expr) = KOENIG_LOOKUP_P (non_dep);
CALL_EXPR_OPERATOR_SYNTAX (call_expr) = true;
CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (non_dep);
CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (non_dep);
return call;
}

View File

@ -189,7 +189,8 @@ in the following sections.
@item C++ Language Options
@xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
@gccoptlist{-fabi-version=@var{n} -fno-access-control -fcheck-new @gol
@gccoptlist{-fabi-version=@var{n} -fno-access-control @gol
-fargs-in-order=@var{n} -fcheck-new @gol
-fconstexpr-depth=@var{n} -ffriend-injection @gol
-fno-elide-constructors @gol
-fno-enforce-eh-specs @gol
@ -2233,6 +2234,14 @@ option is used for the warning.
Turn off all access checking. This switch is mainly useful for working
around bugs in the access control code.
@item -fargs-in-order
@opindex fargs-in-order
Evaluate function arguments and operands of some binary expressions in
left-to-right order, and evaluate the right side of an assignment
before the left side, as proposed in P0145R2. Enabled by default with
@option{-std=c++1z}. @option{-fargs-in-order=1} implements all of the
ordering requirements except function arguments.
@item -fcheck-new
@opindex fcheck-new
Check that the pointer returned by @code{operator new} is non-null

View File

@ -0,0 +1,21 @@
// P0145R2: Refining Expression Order for C++
// { dg-do run }
// { dg-options "-std=c++1z" }
extern "C" int printf (const char *, ...);
void sink(...) { }
int last = 0;
int f(int i)
{
if (i < last)
__builtin_abort ();
last = i;
return i;
}
int main()
{
sink(f(1), f(2));
sink(f(3), f(4), f(5));
}

View File

@ -0,0 +1,15 @@
// P0145R2: Refining Expression Order for C++
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <string>
#define assert(X) if (!(X)) __builtin_abort();
int main()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
.replace( s.find( " don't" ), 6, "" );
assert( s == "I have heard it works only if you believe in it" ) ;
}

View File

@ -0,0 +1,150 @@
// P0145R2: Refining Expression Order for C++
// { dg-do run }
// { dg-options "-std=c++1z -fargs-in-order=1" }
extern "C" int printf (const char *, ...);
void sink(...) { }
int last = 0;
int f(int i)
{
if (i < last)
__builtin_abort ();
last = i;
return i;
}
int& g(int i)
{
static int dummy;
f(i);
return dummy;
}
struct A
{
int _i;
A(int i): _i(f(i)) { }
A& memfn(int i, int j) { f(j); return *this; }
int operator<<(int i) { }
A& operator=(const A&) { return *this; }
A& operator+=(int i) { return *this; }
};
int operator>>(A&, int i) { }
A a(0);
A* afn(int i)
{
f(i);
return &a;
}
A& aref(int i)
{
f(i);
return a;
}
static int si;
int* ip (int i)
{
f(i);
return &si;
}
int& iref(int i)
{
f(i);
return si;
}
auto pmff(int i) {
f(i);
return &A::memfn;
}
template <class T> void f()
{
// a.b
A(1).memfn(f(2),3).memfn(f(4),5);
aref(6).memfn(f(7),8);
(aref(9).*pmff(10))(f(11),12);
last = 0;
// a->b
afn(12)->memfn(f(13),14);
// a->*b
(afn(15)->*pmff(16))(f(17),18);
last = 0;
// a(b)
// covered in eval-order1.C
// b @= a
aref(19)=A(18);
//iref(21)=f(20);
aref(23)+=f(22);
last = 0;
// a[b]
afn(20)[f(21)-21].memfn(f(22),23);
ip(24)[f(25)-25] = 0;
last=0;
// a << b
aref(24) << f(25);
iref(26) << f(27);
last=0;
// a >> b
aref(26) >> f(27);
iref(28) >> f(29);
}
void g()
{
// a.b
A(1).memfn(f(2),3).memfn(f(4),5);
aref(6).memfn(f(7),8);
(aref(9).*pmff(10))(f(11),12);
last = 0;
// a->b
afn(12)->memfn(f(13),14);
// a->*b
(afn(15)->*pmff(16))(f(17),18);
last = 0;
// a(b)
// covered in eval-order1.C
// b @= a
aref(19)=A(18);
//iref(21)=f(20);
aref(23)+=f(22);
last = 0;
// a[b]
afn(20)[f(21)-21].memfn(f(22),23);
ip(24)[f(25)-25] = 0;
last=0;
// a << b
aref(24) << f(25);
iref(26) << f(27);
last=0;
// a >> b
aref(26) >> f(27);
iref(28) >> f(29);
}
int main()
{
g();
last = 0;
f<int>();
}