https://gcc.gnu.org/g:bee204863e7b49e445ce68d8edcd567fe5c4cc55
commit r16-4861-gbee204863e7b49e445ce68d8edcd567fe5c4cc55 Author: Philip Herron <[email protected]> Date: Mon Sep 15 17:00:58 2025 +0100 gccrs: Fix ICE handling division by zero in const eval This pulls over more constexpr support from C++ front-end. Fixes Rust-GCC#4146 gcc/rust/ChangeLog: * backend/rust-constexpr.cc (eval_constant_expression): port over missing bits (eval_store_expression): likewise (eval_call_expression): likewise (eval_binary_expression): likewise (eval_bit_field_ref): likewise (eval_check_shift_p): likewise (fold_pointer_plus_expression): likewise (maybe_fold_addr_pointer_plus): likewise (fold_expr): likewise (union_active_member): likewise (fold_indirect_ref_1): likewise (rs_fold_indirect_ref): likewise (rs_eval_indirect_ref): likewise (eval_logical_expression): likewise (eval_vector_conditional_expression): likewise (eval_bare_aggregate): likewise (cxx_eval_trinary_expression): likewise (eval_and_check_array_index): likewise (eval_array_reference): likewise (eval_component_reference): likewise (rs_bind_parameters_in_call): likewise (eval_builtin_function_call): likewise (constexpr_fn_retval): likewise (verify_constant): likewise (get_array_or_vector_nelts): likewise (eval_conditional_expression): likewise (eval_switch_expr): likewise (eval_unary_expression): likewise (cxx_eval_outermost_constant_expr): likewise (potential_constant_expression_1): likewise gcc/testsuite/ChangeLog: * rust/compile/issue-2394.rs: Update test case * rust/compile/issue-4146.rs: New test. Signed-off-by: Philip Herron <[email protected]> Diff: --- gcc/rust/backend/rust-constexpr.cc | 555 ++++++++++++++++++++++++------- gcc/testsuite/rust/compile/issue-2394.rs | 1 + gcc/testsuite/rust/compile/issue-4146.rs | 3 + 3 files changed, 431 insertions(+), 128 deletions(-) diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc index 0dbfe895f649..d04f8643306e 100644 --- a/gcc/rust/backend/rust-constexpr.cc +++ b/gcc/rust/backend/rust-constexpr.cc @@ -505,18 +505,18 @@ tree decl_constant_value (tree decl, bool unshare_p); static void non_const_var_error (location_t loc, tree r); static tree eval_constant_expression (const constexpr_ctx *ctx, tree, bool, - bool *, bool *, tree * = NULL); + bool *, bool *, tree *jump_target); static tree constexpr_fn_retval (const constexpr_ctx *ctx, tree r); static tree eval_store_expression (const constexpr_ctx *ctx, tree r, bool, - bool *, bool *); + bool *, bool *, tree *jump_target); static tree eval_call_expression (const constexpr_ctx *ctx, tree r, bool, - bool *, bool *); + bool *, bool *, tree *); static tree eval_binary_expression (const constexpr_ctx *ctx, tree r, bool, - bool *, bool *); + bool *, bool *, tree *jump_target); static tree get_function_named_in_call (tree t); @@ -531,7 +531,8 @@ static tree eval_conditional_expression (const constexpr_ctx *ctx, tree t, bool *overflow_p, tree *jump_target); static tree eval_bit_field_ref (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p); + bool *non_constant_p, bool *overflow_p, + tree *jump_target); static tree eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, @@ -543,7 +544,15 @@ static tree eval_switch_expr (const constexpr_ctx *ctx, tree t, static tree eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/, bool *non_constant_p, - bool *overflow_p); + bool *overflow_p, tree *jump_target); +static bool eval_check_shift_p (location_t loc, const constexpr_ctx *ctx, + enum tree_code code, tree type, tree lhs, + tree rhs); +static tree fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, + tree lhs, tree rhs, + bool *non_constant_p, + bool *overflow_p, tree *jump_target); +static tree maybe_fold_addr_pointer_plus (tree t); /* Variables and functions to manage constexpr call expansion context. These do not need to be marked for PCH or GC. */ @@ -605,8 +614,9 @@ fold_expr (tree expr) bool non_constant_p = false; bool overflow_p = false; + tree jump_target = NULL_TREE; tree folded = eval_constant_expression (&ctx, expr, false, &non_constant_p, - &overflow_p); + &overflow_p, &jump_target); rust_assert (folded != NULL_TREE); // more logic here to possibly port @@ -632,13 +642,13 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2) otherwise return NULL_TREE. */ static tree -union_active_member (const constexpr_ctx *ctx, tree t) +union_active_member (const constexpr_ctx *ctx, tree t, tree *jump_target) { constexpr_ctx new_ctx = *ctx; new_ctx.quiet = true; bool non_constant_p = false, overflow_p = false; tree ctor = eval_constant_expression (&new_ctx, t, false, &non_constant_p, - &overflow_p); + &overflow_p, jump_target); if (TREE_CODE (ctor) == CONSTRUCTOR && CONSTRUCTOR_NELTS (ctor) == 1 && CONSTRUCTOR_ELT (ctor, 0)->index && TREE_CODE (CONSTRUCTOR_ELT (ctor, 0)->index) == FIELD_DECL) @@ -650,7 +660,8 @@ union_active_member (const constexpr_ctx *ctx, tree t) static tree fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, - tree op, unsigned HOST_WIDE_INT off, bool *empty_base) + tree op, unsigned HOST_WIDE_INT off, bool *empty_base, + tree *jump_target) { tree optype = TREE_TYPE (op); unsigned HOST_WIDE_INT const_nunits; @@ -697,7 +708,8 @@ fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, tree index = size_int (idx + tree_to_uhwi (min_val)); op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index, NULL_TREE, NULL_TREE); - return fold_indirect_ref_1 (ctx, loc, type, op, rem, empty_base); + return fold_indirect_ref_1 (ctx, loc, type, op, rem, empty_base, + jump_target); } } /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */ @@ -706,7 +718,7 @@ fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, { if (TREE_CODE (optype) == UNION_TYPE) /* For unions prefer the currently active member. */ - if (tree field = union_active_member (ctx, op)) + if (tree field = union_active_member (ctx, op, jump_target)) { unsigned HOST_WIDE_INT el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); @@ -715,7 +727,7 @@ fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field, NULL_TREE); if (tree ret = fold_indirect_ref_1 (ctx, loc, type, cop, off, - empty_base)) + empty_base, jump_target)) return ret; } } @@ -734,8 +746,9 @@ fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, { tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field, NULL_TREE); - if (tree ret = fold_indirect_ref_1 (ctx, loc, type, cop, - off - upos, empty_base)) + if (tree ret + = fold_indirect_ref_1 (ctx, loc, type, cop, off - upos, + empty_base, jump_target)) return ret; } } @@ -764,7 +777,7 @@ fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, static tree rs_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, - tree op0, bool *empty_base) + tree op0, bool *empty_base, tree *jump_target) { tree sub = op0; tree subtype; @@ -823,7 +836,8 @@ rs_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree off = integer_zero_node; canonicalize_obj_off (op, off); gcc_assert (integer_zerop (off)); - return fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base); + return fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base, + jump_target); } } else if (TREE_CODE (sub) == POINTER_PLUS_EXPR @@ -838,7 +852,7 @@ rs_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree obj = TREE_OPERAND (op00, 0); canonicalize_obj_off (obj, off); return fold_indirect_ref_1 (ctx, loc, type, obj, tree_to_uhwi (off), - empty_base); + empty_base, jump_target); } } /* *(foo *)fooarrptr => (*fooarrptr)[0] */ @@ -847,8 +861,8 @@ rs_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, { tree type_domain; tree min_val = size_zero_node; - tree newsub - = rs_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL); + tree newsub = rs_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, + NULL, jump_target); if (newsub) sub = newsub; else @@ -867,7 +881,7 @@ rs_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, static tree rs_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree orig_op0 = TREE_OPERAND (t, 0); bool empty_base = false; @@ -885,13 +899,13 @@ rs_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval, /* First try to simplify it directly. */ tree r = rs_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), - orig_op0, &empty_base); + orig_op0, &empty_base, jump_target); if (!r) { /* If that didn't work, evaluate the operand first. */ - tree op0 - = eval_constant_expression (ctx, orig_op0, - /*lval*/ false, non_constant_p, overflow_p); + tree op0 = eval_constant_expression (ctx, orig_op0, + /*lval*/ false, non_constant_p, + overflow_p, jump_target); /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -905,7 +919,7 @@ rs_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval, } r = rs_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0, - &empty_base); + &empty_base, jump_target); if (r == NULL_TREE) { /* We couldn't fold to a constant value. Make sure it's not @@ -934,7 +948,8 @@ rs_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval, } } - r = eval_constant_expression (ctx, r, lval, non_constant_p, overflow_p); + r = eval_constant_expression (ctx, r, lval, non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; @@ -960,17 +975,17 @@ rs_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval, static tree eval_logical_expression (const constexpr_ctx *ctx, tree t, tree bailout_value, tree continue_value, bool lval, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { tree r; tree lhs = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); VERIFY_CONSTANT (lhs); if (tree_int_cst_equal (lhs, bailout_value)) return lhs; gcc_assert (tree_int_cst_equal (lhs, continue_value)); r = eval_constant_expression (ctx, TREE_OPERAND (t, 1), lval, non_constant_p, - overflow_p); + overflow_p, jump_target); VERIFY_CONSTANT (r); return r; } @@ -1286,19 +1301,20 @@ get_or_insert_ctor_field (tree ctor, tree index, int pos_hint = -1) static tree eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { - tree arg1 - = eval_constant_expression (ctx, TREE_OPERAND (t, 0), - /*lval*/ false, non_constant_p, overflow_p); + tree arg1 = eval_constant_expression (ctx, TREE_OPERAND (t, 0), + /*lval*/ false, non_constant_p, + overflow_p, jump_target); VERIFY_CONSTANT (arg1); - tree arg2 - = eval_constant_expression (ctx, TREE_OPERAND (t, 1), - /*lval*/ false, non_constant_p, overflow_p); + tree arg2 = eval_constant_expression (ctx, TREE_OPERAND (t, 1), + /*lval*/ false, non_constant_p, + overflow_p, jump_target); VERIFY_CONSTANT (arg2); - tree arg3 - = eval_constant_expression (ctx, TREE_OPERAND (t, 2), - /*lval*/ false, non_constant_p, overflow_p); + tree arg3 = eval_constant_expression (ctx, TREE_OPERAND (t, 2), + /*lval*/ false, non_constant_p, + overflow_p, jump_target); VERIFY_CONSTANT (arg3); location_t loc = EXPR_LOCATION (t); tree type = TREE_TYPE (t); @@ -1323,7 +1339,7 @@ eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t, static tree eval_bare_aggregate (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t); bool changed = false; @@ -1374,8 +1390,9 @@ eval_bare_aggregate (const constexpr_ctx *ctx, tree t, bool lval, the active union member now so that we can later detect and diagnose if its initializer attempts to activate another member. */ get_or_insert_ctor_field (ctx->ctor, index); - tree elt = eval_constant_expression (&new_ctx, value, lval, - non_constant_p, overflow_p); + tree elt + = eval_constant_expression (&new_ctx, value, lval, non_constant_p, + overflow_p, jump_target); /* Don't VERIFY_CONSTANT here. */ if (ctx->quiet && *non_constant_p) break; @@ -1444,7 +1461,8 @@ eval_bare_aggregate (const constexpr_ctx *ctx, tree t, bool lval, static tree cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { int i; tree args[3]; @@ -1452,8 +1470,9 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, bool lval, for (i = 0; i < 3; i++) { - args[i] = eval_constant_expression (ctx, TREE_OPERAND (t, i), lval, - non_constant_p, overflow_p); + args[i] + = eval_constant_expression (ctx, TREE_OPERAND (t, i), lval, + non_constant_p, overflow_p, jump_target); VERIFY_CONSTANT (args[i]); } @@ -1598,7 +1617,8 @@ free_constructor (tree t) static tree eval_and_check_array_index (const constexpr_ctx *ctx, tree t, bool allow_one_past, - bool *non_constant_p, bool *overflow_p); + bool *non_constant_p, bool *overflow_p, + tree *jump_target); // forked from gcc/cp/constexpr.cc cxx_eval_array_reference @@ -1607,11 +1627,11 @@ static tree eval_and_check_array_index (const constexpr_ctx *ctx, tree t, static tree eval_array_reference (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree oldary = TREE_OPERAND (t, 0); - tree ary - = eval_constant_expression (ctx, oldary, lval, non_constant_p, overflow_p); + tree ary = eval_constant_expression (ctx, oldary, lval, non_constant_p, + overflow_p, jump_target); if (*non_constant_p) return t; if (!lval && TREE_CODE (ary) == VIEW_CONVERT_EXPR @@ -1620,8 +1640,8 @@ eval_array_reference (const constexpr_ctx *ctx, tree t, bool lval, ary = TREE_OPERAND (ary, 0); tree oldidx = TREE_OPERAND (t, 1); - tree index - = eval_and_check_array_index (ctx, t, lval, non_constant_p, overflow_p); + tree index = eval_and_check_array_index (ctx, t, lval, non_constant_p, + overflow_p, jump_target); if (*non_constant_p) return t; @@ -1723,7 +1743,8 @@ eval_array_reference (const constexpr_ctx *ctx, tree t, bool lval, new_ctx.ctor = build_constructor (elem_type, NULL); ctx = &new_ctx; } - t = eval_constant_expression (ctx, val, lval, non_constant_p, overflow_p); + t = eval_constant_expression (ctx, val, lval, non_constant_p, overflow_p, + jump_target); if (!SCALAR_TYPE_P (elem_type) && t != ctx->ctor) free_constructor (ctx->ctor); return t; @@ -1736,7 +1757,8 @@ eval_array_reference (const constexpr_ctx *ctx, tree t, bool lval, static tree eval_component_reference (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { unsigned HOST_WIDE_INT i; tree field; @@ -1744,7 +1766,7 @@ eval_component_reference (const constexpr_ctx *ctx, tree t, bool lval, tree part = TREE_OPERAND (t, 1); tree orig_whole = TREE_OPERAND (t, 0); tree whole = eval_constant_expression (ctx, orig_whole, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); if (INDIRECT_REF_P (whole) && integer_zerop (TREE_OPERAND (whole, 0))) { if (!ctx->quiet) @@ -1833,8 +1855,8 @@ eval_component_reference (const constexpr_ctx *ctx, tree t, bool lval, // back to handle this to assign suitable value to value before sending it in // eval_constant_expression below // value = build_value_init (TREE_TYPE (t), tf_warning_or_error); - return eval_constant_expression (ctx, value, lval, non_constant_p, - overflow_p); + return eval_constant_expression (ctx, value, lval, non_constant_p, overflow_p, + jump_target); } /* Subroutine of cxx_eval_statement_list. Determine whether the statement @@ -1893,7 +1915,7 @@ label_matches (const constexpr_ctx *ctx, tree *jump_target, tree stmt) static tree eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, bool *non_constant_p, bool *overflow_p, - tree *jump_target /* = NULL */) + tree *jump_target) { if (jump_target && *jump_target) { @@ -1964,7 +1986,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, { r = DECL_VALUE_EXPR (t); return eval_constant_expression (ctx, r, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } /* fall through */ case CONST_DECL: @@ -2067,7 +2089,8 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, case LTGT_EXPR: case RANGE_EXPR: case COMPLEX_EXPR: - r = eval_binary_expression (ctx, t, lval, non_constant_p, overflow_p); + r = eval_binary_expression (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; /* fold can introduce non-IF versions of these; still treat them as @@ -2076,14 +2099,14 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, case TRUTH_ANDIF_EXPR: r = eval_logical_expression (ctx, t, boolean_false_node, boolean_true_node, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); break; case TRUTH_OR_EXPR: case TRUTH_ORIF_EXPR: r = eval_logical_expression (ctx, t, boolean_true_node, boolean_false_node, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); break; case TARGET_EXPR: @@ -2132,7 +2155,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, /* Pass false for 'lval' because this indicates initialization of a temporary. */ r = eval_constant_expression (ctx, TREE_OPERAND (t, 1), false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); if (*non_constant_p) break; /* Adjust the type of the result to the type of the temporary. */ @@ -2149,13 +2172,14 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, break; case CALL_EXPR: - r = eval_call_expression (ctx, t, lval, non_constant_p, overflow_p); + r = eval_call_expression (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case RETURN_EXPR: if (TREE_OPERAND (t, 0) != NULL_TREE) r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* FALLTHRU */ case BREAK_STMT: case CONTINUE_STMT: @@ -2188,7 +2212,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, if (tree init = DECL_INITIAL (r)) { init = eval_constant_expression (ctx, init, false, non_constant_p, - overflow_p); + overflow_p, jump_target); /* Don't share a CONSTRUCTOR that might be changed. */ init = unshare_constructor (init); /* Remember that a constant object's constructor has already @@ -2209,12 +2233,13 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, constant without its operand being, and vice versa. */ case MEM_REF: case INDIRECT_REF: - r = rs_eval_indirect_ref (ctx, t, lval, non_constant_p, overflow_p); + r = rs_eval_indirect_ref (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case VEC_PERM_EXPR: - r = cxx_eval_trinary_expression (ctx, t, lval, non_constant_p, - overflow_p); + r = cxx_eval_trinary_expression (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case PAREN_EXPR: @@ -2222,11 +2247,12 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, /* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in constant expressions since it's unaffected by -fassociative-math. */ r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case MODIFY_EXPR: - r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p); + r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p, + jump_target); break; case STATEMENT_LIST: @@ -2242,13 +2268,14 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, case OBJ_TYPE_REF: /* Virtual function lookup. We don't need to do anything fancy. */ return eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); case EXIT_EXPR: { tree cond = TREE_OPERAND (t, 0); - cond = eval_constant_expression (ctx, cond, /*lval*/ false, - non_constant_p, overflow_p); + cond + = eval_constant_expression (ctx, cond, /*lval*/ false, non_constant_p, + overflow_p, jump_target); VERIFY_CONSTANT (cond); if (integer_nonzerop (cond)) *jump_target = t; @@ -2278,7 +2305,8 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, else { r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) break; ctx->global->values.put (t, r); @@ -2292,7 +2320,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, tree oldop = TREE_OPERAND (t, 0); tree op = eval_constant_expression (ctx, oldop, /*lval*/ true, non_constant_p, - overflow_p); + overflow_p, jump_target); /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -2337,7 +2365,8 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, if (lval) { r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (r == error_mark_node) ; else if (r == TREE_OPERAND (t, 0)) @@ -2356,7 +2385,8 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, case BIT_NOT_EXPR: case TRUTH_NOT_EXPR: case FIXED_CONVERT_EXPR: - r = eval_unary_expression (ctx, t, lval, non_constant_p, overflow_p); + r = eval_unary_expression (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case LOOP_EXPR: @@ -2371,7 +2401,8 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, break; case ARRAY_REF: - r = eval_array_reference (ctx, t, lval, non_constant_p, overflow_p); + r = eval_array_reference (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case COMPONENT_REF: @@ -2385,11 +2416,13 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, *non_constant_p = true; return t; } - r = eval_component_reference (ctx, t, lval, non_constant_p, overflow_p); + r = eval_component_reference (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case BIT_FIELD_REF: - r = eval_bit_field_ref (ctx, t, lval, non_constant_p, overflow_p); + r = eval_bit_field_ref (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; case COND_EXPR: @@ -2434,7 +2467,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, case VEC_COND_EXPR: r = eval_vector_conditional_expression (ctx, t, non_constant_p, - overflow_p); + overflow_p, jump_target); break; case TRY_CATCH_EXPR: @@ -2460,7 +2493,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) eval_constant_expression (ctx, cleanup, false, non_constant_p, - overflow_p); + overflow_p, jump_target); } break; @@ -2470,7 +2503,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, if (!*non_constant_p) /* Also evaluate the cleanup. */ eval_constant_expression (ctx, TREE_OPERAND (t, 1), true, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case CONSTRUCTOR: @@ -2482,7 +2515,8 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, if (TREE_CONSTANT (t)) return fold (t); } - r = eval_bare_aggregate (ctx, t, lval, non_constant_p, overflow_p); + r = eval_bare_aggregate (ctx, t, lval, non_constant_p, overflow_p, + jump_target); break; /* FALLTHROUGH. */ @@ -2493,7 +2527,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, tree oldop = TREE_OPERAND (t, 0); tree op = eval_constant_expression (ctx, oldop, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); if (*non_constant_p) return t; tree type = TREE_TYPE (t); @@ -2624,7 +2658,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, return ctor; else return eval_constant_expression (ctx, ctor, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } /* A placeholder without a referent. We can get here when checking whether NSDMIs are noexcept, or in massage_init_elt; @@ -2685,7 +2719,8 @@ is_empty_field (tree decl) static tree eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { constexpr_ctx new_ctx = *ctx; @@ -2706,7 +2741,7 @@ eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, if (!SCALAR_TYPE_P (type)) new_ctx.ctor = new_ctx.object = NULL_TREE; init = eval_constant_expression (&new_ctx, init, false, non_constant_p, - overflow_p); + overflow_p, jump_target); if (*non_constant_p) return t; } @@ -2718,7 +2753,7 @@ eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, as a whole; otherwise, only evaluate the innermost piece to avoid building up unnecessary *_REFs. */ target = eval_constant_expression (ctx, target, true, non_constant_p, - overflow_p); + overflow_p, jump_target); evaluated = true; if (*non_constant_p) return t; @@ -2747,7 +2782,8 @@ eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, if (TREE_CODE (probe) == ARRAY_REF) { elt = eval_and_check_array_index (ctx, probe, false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; } @@ -2771,8 +2807,9 @@ eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, object = probe; else { - probe = eval_constant_expression (ctx, probe, true, - non_constant_p, overflow_p); + probe + = eval_constant_expression (ctx, probe, true, non_constant_p, + overflow_p, jump_target); evaluated = true; if (*non_constant_p) return t; @@ -2963,7 +3000,7 @@ eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, if (tree tinit = TARGET_EXPR_INITIAL (init)) init = tinit; init = eval_constant_expression (&new_ctx, init, false, non_constant_p, - overflow_p); + overflow_p, jump_target); /* The hash table might have moved since the get earlier, and the initializer might have mutated the underlying CONSTRUCTORs, so we must recompute VALP. */ @@ -3065,22 +3102,115 @@ eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, Like cxx_eval_unary_expression, except for binary expressions. */ static tree eval_binary_expression (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { + tree r = NULL_TREE; tree orig_lhs = TREE_OPERAND (t, 0); tree orig_rhs = TREE_OPERAND (t, 1); tree lhs, rhs; - lhs = eval_constant_expression (ctx, orig_lhs, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); + /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer + subtraction. */ + if (*non_constant_p) + return t; + if (*jump_target) + return NULL_TREE; + rhs = eval_constant_expression (ctx, orig_rhs, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*non_constant_p) + return t; + if (*jump_target) + return NULL_TREE; location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); tree type = TREE_TYPE (t); - return fold_binary_loc (loc, code, type, lhs, rhs); + if (code == EQ_EXPR || code == NE_EXPR) + { + bool is_code_eq = (code == EQ_EXPR); + + if (TREE_CODE (lhs) == PTRMEM_CST && TREE_CODE (rhs) == PTRMEM_CST) + { + tree lmem = PTRMEM_CST_MEMBER (lhs); + tree rmem = PTRMEM_CST_MEMBER (rhs); + bool eq; + if (TREE_CODE (lmem) == TREE_CODE (rmem) + && TREE_CODE (lmem) == FIELD_DECL + && TREE_CODE (DECL_CONTEXT (lmem)) == UNION_TYPE + && same_type_p (DECL_CONTEXT (lmem), DECL_CONTEXT (rmem))) + /* If both refer to (possibly different) members of the same union + (12.3), they compare equal. */ + eq = true; + // else + // eq = cp_tree_equal (lhs, rhs); + r = constant_boolean_node (eq == is_code_eq, type); + } + else if ((TREE_CODE (lhs) == PTRMEM_CST || TREE_CODE (rhs) == PTRMEM_CST) + && (null_member_pointer_value_p (lhs) + || null_member_pointer_value_p (rhs))) + r = constant_boolean_node (!is_code_eq, type); + } + if (r == NULL_TREE && TREE_CODE_CLASS (code) == tcc_comparison + && POINTER_TYPE_P (TREE_TYPE (lhs))) + { + if (tree lhso = maybe_fold_addr_pointer_plus (lhs)) + lhs = fold_convert (TREE_TYPE (lhs), lhso); + if (tree rhso = maybe_fold_addr_pointer_plus (rhs)) + rhs = fold_convert (TREE_TYPE (rhs), rhso); + } + if (code == POINTER_PLUS_EXPR && !*non_constant_p && integer_zerop (lhs) + && !integer_zerop (rhs)) + { + if (!ctx->quiet) + error ("arithmetic involving a null pointer in %qE", lhs); + *non_constant_p = true; + return t; + } + else if (code == POINTER_PLUS_EXPR) + { + r = fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p, + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; + } + + if (r == NULL_TREE) + { + r = fold_binary_loc (loc, code, type, lhs, rhs); + } + + if (r == NULL_TREE && (code == LSHIFT_EXPR || code == RSHIFT_EXPR) + && TREE_CODE (lhs) == INTEGER_CST && TREE_CODE (rhs) == INTEGER_CST + && wi::neg_p (wi::to_wide (rhs))) + { + /* For diagnostics and -fpermissive emulate previous behavior of + handling shifts by negative amount. */ + tree nrhs = const_unop (NEGATE_EXPR, TREE_TYPE (rhs), rhs); + if (nrhs) + r = fold_binary_loc (loc, + code == LSHIFT_EXPR ? RSHIFT_EXPR : LSHIFT_EXPR, + type, lhs, nrhs); + } + + if (r == NULL_TREE) + { + if (lhs == orig_lhs && rhs == orig_rhs) + r = t; + else + r = build2_loc (loc, code, type, lhs, rhs); + } + else if (eval_check_shift_p (loc, ctx, code, type, lhs, rhs)) + *non_constant_p = true; + /* Don't VERIFY_CONSTANT if this might be dealing with a pointer to + a local array in a constexpr function. */ + bool ptr = INDIRECT_TYPE_P (TREE_TYPE (lhs)); + if (!ptr) + VERIFY_CONSTANT (r); + return r; } /* Helper function of cxx_bind_parameters_in_call. Return non-NULL @@ -3118,7 +3248,7 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data) static tree rs_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, bool *non_constant_p, bool *overflow_p, - bool *non_constant_args) + bool *non_constant_args, tree *jump_target) { const int nargs = call_expr_nargs (t); tree parms = DECL_ARGUMENTS (fun); @@ -3142,7 +3272,7 @@ rs_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, such as this, but here we do the elision differently: we keep the TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm. */ arg = eval_constant_expression (ctx, x, /*lval=*/false, non_constant_p, - overflow_p); + overflow_p, jump_target); /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p && ctx->quiet) break; @@ -3208,7 +3338,8 @@ rs_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, static tree eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, - bool lval, bool *non_constant_p, bool *overflow_p) + bool lval, bool *non_constant_p, bool *overflow_p, + tree *jump_target) { const int nargs = call_expr_nargs (t); tree *args = (tree *) alloca (nargs * sizeof (tree)); @@ -3311,8 +3442,9 @@ eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, || potential_constant_expression (arg)) { bool dummy1 = false, dummy2 = false; - arg - = eval_constant_expression (&new_ctx, arg, false, &dummy1, &dummy2); + tree dummy_jump_target = NULL_TREE; + arg = eval_constant_expression (&new_ctx, arg, false, &dummy1, + &dummy2, &dummy_jump_target); } if (bi_const_p) @@ -3408,7 +3540,7 @@ eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, } return eval_constant_expression (&new_ctx, new_call, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } // Subroutine of cxx_eval_constant_expression. @@ -3416,7 +3548,7 @@ eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, // evaluation. static tree eval_call_expression (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { location_t loc = EXPR_LOCATION (t); tree fun = get_function_named_in_call (t); @@ -3445,12 +3577,12 @@ eval_call_expression (const constexpr_ctx *ctx, tree t, bool lval, if (fndecl_built_in_p (fun)) return eval_builtin_function_call (ctx, t, fun, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); bool non_constant_args = false; new_call.bindings = rs_bind_parameters_in_call (ctx, t, fun, non_constant_p, overflow_p, - &non_constant_args); + &non_constant_args, jump_target); /* We build up the bindings list before we know whether we already have this call cached. If we don't end up saving these bindings, ggc_free them when @@ -4013,7 +4145,7 @@ constexpr_fn_retval (const constexpr_ctx *ctx, tree body) bool non_constant_p = false; bool overflow_p = false; return eval_constant_expression (ctx, body, false, &non_constant_p, - &overflow_p); + &overflow_p, NULL); } case DECL_EXPR: { @@ -4409,7 +4541,7 @@ verify_constant (tree t, bool allow_non_constant, bool *non_constant_p, if (!*non_constant_p && !reduced_constant_expression_p (t) && t != void_node) { if (!allow_non_constant) - error ("%q+E is not a constant expression", t); + error_at (EXPR_LOCATION (t), "is not a constant expression"); *non_constant_p = true; } if (TREE_OVERFLOW_P (t)) @@ -4514,7 +4646,8 @@ diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, static tree get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree nelts; if (TREE_CODE (type) == ARRAY_TYPE) @@ -4530,8 +4663,8 @@ get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, rust_unreachable (); /* For VLAs, the number of elements won't be an integer constant. */ - nelts - = eval_constant_expression (ctx, nelts, false, non_constant_p, overflow_p); + nelts = eval_constant_expression (ctx, nelts, false, non_constant_p, + overflow_p, jump_target); return nelts; } @@ -4543,13 +4676,13 @@ get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, static tree eval_and_check_array_index (const constexpr_ctx *ctx, tree t, bool allow_one_past, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { location_t loc = rs_expr_loc_or_input_loc (t); tree ary = TREE_OPERAND (t, 0); t = TREE_OPERAND (t, 1); tree index = eval_constant_expression (ctx, t, allow_one_past, non_constant_p, - overflow_p); + overflow_p, jump_target); VERIFY_CONSTANT (index); if (!tree_fits_shwi_p (index) || tree_int_cst_sgn (index) < 0) @@ -4560,7 +4693,7 @@ eval_and_check_array_index (const constexpr_ctx *ctx, tree t, } tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p, - overflow_p); + overflow_p, jump_target); VERIFY_CONSTANT (nelts); if (allow_one_past ? !tree_int_cst_le (index, nelts) : !tree_int_cst_lt (index, nelts)) @@ -4770,9 +4903,9 @@ eval_conditional_expression (const constexpr_ctx *ctx, tree t, bool lval, bool *non_constant_p, bool *overflow_p, tree *jump_target) { - tree val - = eval_constant_expression (ctx, TREE_OPERAND (t, 0), - /*lval*/ false, non_constant_p, overflow_p); + tree val = eval_constant_expression (ctx, TREE_OPERAND (t, 0), + /*lval*/ false, non_constant_p, + overflow_p, jump_target); VERIFY_CONSTANT (val); if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t)) { @@ -4809,14 +4942,14 @@ eval_conditional_expression (const constexpr_ctx *ctx, tree t, bool lval, static tree eval_bit_field_ref (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree orig_whole = TREE_OPERAND (t, 0); tree retval, fldval, utype, mask; bool fld_seen = false; HOST_WIDE_INT istart, isize; tree whole = eval_constant_expression (ctx, orig_whole, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); tree start, field, value; unsigned HOST_WIDE_INT i; @@ -5047,8 +5180,8 @@ eval_switch_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, { tree cond = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t); - cond - = eval_constant_expression (ctx, cond, false, non_constant_p, overflow_p); + cond = eval_constant_expression (ctx, cond, false, non_constant_p, overflow_p, + jump_target); VERIFY_CONSTANT (cond); *jump_target = cond; @@ -5082,12 +5215,13 @@ eval_switch_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, static tree eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r; tree orig_arg = TREE_OPERAND (t, 0); tree arg = eval_constant_expression (ctx, orig_arg, /*lval*/ false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); VERIFY_CONSTANT (arg); location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); @@ -5223,7 +5357,9 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (manifestly_const_eval) instantiate_constexpr_fns (r); - r = eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); + tree jump_target = NULL_TREE; + r = eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p, + &jump_target); if (!constexpr_dtor) verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); @@ -5235,7 +5371,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) eval_constant_expression (&ctx, cleanup, false, &non_constant_p, - &overflow_p); + &overflow_p, NULL); /* Mutable logic is a bit tricky: we want to allow initialization of constexpr variables with mutable members, but we can't copy those @@ -5995,7 +6131,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, /* A pointer-to-member constant. */ return true; - // handle_addr_expr: #if 0 /* FIXME adjust when issue 1197 is fully resolved. For now don't do any checking here, as we might dereference the pointer later. If @@ -6571,6 +6706,170 @@ fold_non_dependent_init (tree t, tsubst_flags_t /*=tf_warning_or_error*/, return maybe_constant_init (t, object, manifestly_const_eval); } +/* Check whether the shift operation with code CODE and type TYPE on LHS + and RHS is undefined. If it is, give an error with an explanation, + and return true; return false otherwise. */ + +static bool +eval_check_shift_p (location_t loc, const constexpr_ctx *ctx, + enum tree_code code, tree type, tree lhs, tree rhs) +{ + if ((code != LSHIFT_EXPR && code != RSHIFT_EXPR) + || TREE_CODE (lhs) != INTEGER_CST || TREE_CODE (rhs) != INTEGER_CST) + return false; + + tree lhstype = TREE_TYPE (lhs); + unsigned HOST_WIDE_INT uprec = TYPE_PRECISION (TREE_TYPE (lhs)); + + /* [expr.shift] The behavior is undefined if the right operand + is negative, or greater than or equal to the length in bits + of the promoted left operand. */ + if (tree_int_cst_sgn (rhs) == -1) + { + if (!ctx->quiet) + permerror (loc, "right operand of shift expression %q+E is negative", + build2_loc (loc, code, type, lhs, rhs)); + return (!flag_permissive || ctx->quiet); + } + if (compare_tree_int (rhs, uprec) >= 0) + { + if (!ctx->quiet) + permerror (loc, + "right operand of shift expression %q+E is greater " + "than or equal to the precision %wu of the left operand", + build2_loc (loc, code, type, lhs, rhs), uprec); + return (!flag_permissive || ctx->quiet); + } + + /* The value of E1 << E2 is E1 left-shifted E2 bit positions; [...] + if E1 has a signed type and non-negative value, and E1x2^E2 is + representable in the corresponding unsigned type of the result type, + then that value, converted to the result type, is the resulting value; + otherwise, the behavior is undefined. + For C++20: + The value of E1 << E2 is the unique value congruent to E1 x 2^E2 modulo + 2^N, where N is the range exponent of the type of the result. */ + if (code == LSHIFT_EXPR && !TYPE_OVERFLOW_WRAPS (lhstype)) + { + if (tree_int_cst_sgn (lhs) == -1) + { + if (!ctx->quiet) + permerror (loc, "left operand of shift expression %q+E is negative", + build2_loc (loc, code, type, lhs, rhs)); + return (!flag_permissive || ctx->quiet); + } + /* For signed x << y the following: + (unsigned) x >> ((prec (lhs) - 1) - y) + if > 1, is undefined. The right-hand side of this formula + is the highest bit of the LHS that can be set (starting from 0), + so that the shift doesn't overflow. We then right-shift the LHS + to see whether any other bit is set making the original shift + undefined -- the result is not representable in the corresponding + unsigned type. */ + tree t = build_int_cst (unsigned_type_node, uprec - 1); + t = fold_build2 (MINUS_EXPR, unsigned_type_node, t, rhs); + tree ulhs = fold_convert (unsigned_type_for (lhstype), lhs); + t = fold_build2 (RSHIFT_EXPR, TREE_TYPE (ulhs), ulhs, t); + if (tree_int_cst_lt (integer_one_node, t)) + { + if (!ctx->quiet) + permerror (loc, "shift expression %q+E overflows", + build2_loc (loc, code, type, lhs, rhs)); + return (!flag_permissive || ctx->quiet); + } + } + return false; +} + +/* Helper function for cxx_eval_binary_expression. Try to optimize + original POINTER_PLUS_EXPR T, LHS p+ RHS, return NULL_TREE if the + generic folding should be used. */ + +static tree +fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, tree lhs, + tree rhs, bool *non_constant_p, bool *overflow_p, + tree *jump_target) +{ + STRIP_NOPS (lhs); + if (TREE_CODE (lhs) != ADDR_EXPR) + return NULL_TREE; + + lhs = TREE_OPERAND (lhs, 0); + + /* &A[i] p+ j => &A[i + j] */ + if (TREE_CODE (lhs) == ARRAY_REF + && TREE_CODE (TREE_OPERAND (lhs, 1)) == INTEGER_CST + && TREE_CODE (rhs) == INTEGER_CST && TYPE_SIZE_UNIT (TREE_TYPE (lhs)) + && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (lhs))) == INTEGER_CST) + { + tree orig_type = TREE_TYPE (t); + location_t loc = EXPR_LOCATION (t); + tree type = TREE_TYPE (lhs); + + t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1)); + tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0))); + nelts = eval_constant_expression (ctx, nelts, true, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p) + return NULL_TREE; + if (*jump_target) + return NULL_TREE; + /* Don't fold an out-of-bound access. */ + if (!tree_int_cst_le (t, nelts)) + return NULL_TREE; + rhs = fold_convert (ssizetype, rhs); + /* Don't fold if rhs can't be divided exactly by TYPE_SIZE_UNIT. + constexpr int A[1]; ... (char *)&A[0] + 1 */ + if (!integer_zerop (fold_build2_loc (loc, TRUNC_MOD_EXPR, sizetype, rhs, + TYPE_SIZE_UNIT (type)))) + return NULL_TREE; + /* Make sure to treat the second operand of POINTER_PLUS_EXPR + as signed. */ + rhs = fold_build2_loc (loc, EXACT_DIV_EXPR, ssizetype, rhs, + TYPE_SIZE_UNIT (type)); + t = size_binop_loc (loc, PLUS_EXPR, rhs, t); + t = build4_loc (loc, ARRAY_REF, type, TREE_OPERAND (lhs, 0), t, NULL_TREE, + NULL_TREE); + t = build_fold_addr_expr (t); + t = fold_convert (orig_type, t); + return eval_constant_expression (ctx, t, true, non_constant_p, overflow_p, + jump_target); + } + + return NULL_TREE; +} + +/* Try to fold expressions like + (struct S *) (&a[0].D.2378 + 12) + into + &MEM <struct T> [(void *)&a + 12B] + This is something normally done by gimple_fold_stmt_to_constant_1 + on GIMPLE, but is undesirable on GENERIC if we are e.g. going to + dereference the address because some details are lost. + For pointer comparisons we want such folding though so that + match.pd address_compare optimization works. */ + +static tree +maybe_fold_addr_pointer_plus (tree t) +{ + while (CONVERT_EXPR_P (t) && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0)))) + t = TREE_OPERAND (t, 0); + if (TREE_CODE (t) != POINTER_PLUS_EXPR) + return NULL_TREE; + tree op0 = TREE_OPERAND (t, 0); + tree op1 = TREE_OPERAND (t, 1); + if (TREE_CODE (op1) != INTEGER_CST) + return NULL_TREE; + while (CONVERT_EXPR_P (op0) + && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (op0, 0)))) + op0 = TREE_OPERAND (op0, 0); + if (TREE_CODE (op0) != ADDR_EXPR) + return NULL_TREE; + op1 = fold_convert (ptr_type_node, op1); + tree r = fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (op0)), op0, op1); + return build1_loc (EXPR_LOCATION (t), ADDR_EXPR, TREE_TYPE (op0), r); +} + } // namespace Compile } // namespace Rust diff --git a/gcc/testsuite/rust/compile/issue-2394.rs b/gcc/testsuite/rust/compile/issue-2394.rs index 92f7afc6507a..b5b5394e834e 100644 --- a/gcc/testsuite/rust/compile/issue-2394.rs +++ b/gcc/testsuite/rust/compile/issue-2394.rs @@ -1,5 +1,6 @@ const A: i32 = (1 / 0); // { dg-error "division by zero" "" { target *-*-* } .-1 } +// { dg-error "is not a constant expression" "" { target *-*-* } .-2 } fn main() { let a = 1 / 0; diff --git a/gcc/testsuite/rust/compile/issue-4146.rs b/gcc/testsuite/rust/compile/issue-4146.rs new file mode 100644 index 000000000000..efb3ee2556f5 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-4146.rs @@ -0,0 +1,3 @@ +const _NISIZE_DIV_P: &isize = &(1isize / 0); +// { dg-error "division by zero" "" { target *-*-* } .-1 } +// { dg-error "is not a constant expression" "" { target *-*-* } .-2 }
