From: Jason Merrill Date: Tue, 14 Jun 2016 20:18:34 +0000 (-0400) Subject: P0145R2: Refining Expression Order for C++. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4eb24e010993b2d9152c2038566f0114fd65fac7;p=gcc.git 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 --- diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index a3e1fd8958b..08302e1c5bd 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2016-06-14 Jason Merrill + + 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 PR sanitizer/71498 diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c index fec58bcf91f..ff6339c44b6 100644 --- a/gcc/c-family/c-opts.c +++ b/gcc/c-family/c-opts.c @@ -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); diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 761a83bcfb3..83fd84ccc0f 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -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. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index cd5996bdd1d..54e934af572 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,17 @@ +2016-06-14 Jason Merrill + + 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 PR c++/71528 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 729b7eb4ba3..e2b89b8a2c4 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -5372,6 +5372,40 @@ add_candidates (tree fns, tree first_arg, const vec *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; } diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index dcb0fa6215b..97b043acbf2 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -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: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 14ba12073ee..6c6ad10804e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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 **, bool, diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 3a3d9b8f439..11b5d822a35 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -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); } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 536509127e4..9b0cff8c310 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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) { diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index abda6e4f729..9ab964d0b13 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -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 *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; } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 223fd86ac95..21053511ded 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order1.C b/gcc/testsuite/g++.dg/cpp1z/eval-order1.C new file mode 100644 index 00000000000..278990d6f8e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/eval-order1.C @@ -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)); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order2.C b/gcc/testsuite/g++.dg/cpp1z/eval-order2.C new file mode 100644 index 00000000000..2a741d687b5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/eval-order2.C @@ -0,0 +1,15 @@ +// P0145R2: Refining Expression Order for C++ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include +#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" ) ; +} diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order3.C b/gcc/testsuite/g++.dg/cpp1z/eval-order3.C new file mode 100644 index 00000000000..15df9038107 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/eval-order3.C @@ -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 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(); +}