+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
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);
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.
+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
}
}
+/* 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)
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
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. */
}
/* 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. */
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)))
}
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;
}
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;
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);
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:
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
#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) \
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. */
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,
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);
}
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)
{
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;
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);
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;
}
@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
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
--- /dev/null
+// 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));
+}
--- /dev/null
+// 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" ) ;
+}
--- /dev/null
+// 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>();
+}