From patchwork Wed Feb 20 07:46:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1045192 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-496682-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="I/TJ75/i"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4448nG1l6Pz9s7h for ; Wed, 20 Feb 2019 18:46:38 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=bO1uaOpC2vZeJRvlY gNUznxq9Yv8UpyPjFc5qGvzNm+vgzkUocy7B/opBbIE/i2AMQ29rRMW8DX4k1mDn xVvsAwNVoQiBmlgbP0XirnG7eir/DV0pEoc1PaGfuF8zVR7z+XQc1GpgxDDU0/hY mP+Zl/oakZd9/BkqNFJdR4OYmA= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:references:mime-version :content-type:in-reply-to; s=default; bh=E+FNGonNl/sUwMt5NLn9YSe 45hY=; b=I/TJ75/iJmiulynheCUEqqekQ2m6HdSMEi0+77lSSDm8Uws7CRVSsRV h7ekTIAImJoB2l7Ed3+USV4c2cn9y7L+50m2y844XW2g9nTIO+5GCph2o7J91zdP dkH8P6W2d6DD9rMcOpk1DR1RNsa7kN+QzQaO6sXum/L1bu7oatyw= Received: (qmail 26499 invoked by alias); 20 Feb 2019 07:46:28 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 26474 invoked by uid 89); 20 Feb 2019 07:46:28 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-9.2 required=5.0 tests=BAYES_50, GIT_PATCH_2, GIT_PATCH_3, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=tre, using_decl, *iter, USING_DECL X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 20 Feb 2019 07:46:24 +0000 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 43F3859469 for ; Wed, 20 Feb 2019 07:46:23 +0000 (UTC) Received: from tucnak.zalov.cz (ovpn-116-188.ams2.redhat.com [10.36.116.188]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8C33817CC3; Wed, 20 Feb 2019 07:46:20 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.15.2/8.15.2) with ESMTP id x1K7kIEK030580; Wed, 20 Feb 2019 08:46:19 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.15.2/8.15.2/Submit) id x1K7kGJW030579; Wed, 20 Feb 2019 08:46:16 +0100 Date: Wed, 20 Feb 2019 08:46:16 +0100 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 2) Message-ID: <20190220074616.GA2135@tucnak> Reply-To: Jakub Jelinek References: <20190218224551.GO2135@tucnak> <9f6bae87-cbce-f8a5-3020-54550b64e4b6@redhat.com> <20190219043128.GT2135@tucnak> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20190219043128.GT2135@tucnak> User-Agent: Mutt/1.10.1 (2018-07-13) X-IsSubscribed: yes Hi! On Tue, Feb 19, 2019 at 05:31:28AM +0100, Jakub Jelinek wrote: > > > + /* These builtins will fold into > > > + (cast) > > > + ((something = __real__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>), > > > + __imag__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>) > > > + which fails is_constant_expression. */ This part is now simplified by builtins.c change, so it is actually just (something = cst1), cst2 but still the !is_constant_expression issue applies. > > > @@ -1358,6 +1433,9 @@ cxx_bind_parameters_in_call (const const > > > x = ctx->object; > > > x = build_address (x); > > > } > > > + if (TREE_ADDRESSABLE (type) && TYPE_REF_P (TREE_TYPE (x))) > > > + /* Undo convert_for_arg_passing work here. */ > > > + x = build_fold_indirect_ref_loc (EXPR_LOCATION (x), x); > > > > Not convert_from_reference? > > Will change. Changed. > > > @@ -4036,6 +4113,10 @@ label_matches (const constexpr_ctx *ctx, > > > } > > > break; > > > + case BREAK_STMT: > > > + case CONTINUE_STMT: > > > + break; > > > + > > > > Let's add a comment that these are handled directly in cxx_eval_loop_expr. > > Ok, will do. Likewise. Here is an updated version with those changes, bootstrapped/regtested on x86_64-linux/i686-linux. 2019-02-20 Jakub Jelinek PR c++/89285 * builtins.c (fold_builtin_arith_overflow): If first two args are INTEGER_CSTs, set intres and ovfres to constants rather than calls to ifn. * constexpr.c (struct constexpr_fundef): Add parms and result members. (retrieve_constexpr_fundef): Adjust for the above change. (register_constexpr_fundef): Save constexpr body with copy_fn, temporarily set DECL_CONTEXT on DECL_RESULT before that. (get_fundef_copy): Change FUN argument to FUNDEF with constexpr_fundef * type, grab body and parms/result out of constexpr_fundef struct and temporarily change it for copy_fn calls too. (cxx_eval_builtin_function_call): For __builtin_FUNCTION temporarily adjust current_function_decl from ctx->call context. For arith overflow builtins, don't test is_constant_expression on the result, instead test if arguments are suitable constant expressions. (cxx_bind_parameters_in_call): Grab parameters from new_call. Undo convert_for_arg_passing changes for TREE_ADDRESSABLE type passing. (cxx_eval_call_expression): Adjust get_fundef_copy caller. (cxx_eval_conditional_expression): For IF_STMT, allow then or else operands to be NULL. (label_matches): Handle BREAK_STMT and CONTINUE_STMT. (cxx_eval_loop_expr): Add support for FOR_STMT, WHILE_STMT and DO_STMT. (cxx_eval_switch_expr): Add support for SWITCH_STMT. (cxx_eval_constant_expression): Handle IF_STMT, FOR_STMT, WHILE_STMT, DO_STMT, CONTINUE_STMT, SWITCH_STMT, BREAK_STMT and CONTINUE_STMT. For SIZEOF_EXPR, recurse on the result of fold_sizeof_expr. Ignore DECL_EXPR with USING_DECL operand. * lambda.c (maybe_add_lambda_conv_op): Build thisarg using build_int_cst to make it a valid constant expression. * g++.dg/ubsan/vptr-4.C: Expect reinterpret_cast errors. * g++.dg/cpp1y/constexpr-84192.C (f2): Adjust expected diagnostics. * g++.dg/cpp1y/constexpr-70265-2.C (foo): Adjust expected line of diagnostics. * g++.dg/cpp1y/constexpr-89285.C: New test. Jakub --- gcc/builtins.c.jj 2019-02-16 12:19:37.599192312 +0100 +++ gcc/builtins.c 2019-02-19 10:20:01.103606684 +0100 @@ -9302,8 +9302,7 @@ fold_builtin_arith_overflow (location_t tree arg0, tree arg1, tree arg2) { enum internal_fn ifn = IFN_LAST; - /* The code of the expression corresponding to the type-generic - built-in, or ERROR_MARK for the type-specific ones. */ + /* The code of the expression corresponding to the built-in. */ enum tree_code opcode = ERROR_MARK; bool ovf_only = false; @@ -9313,42 +9312,39 @@ fold_builtin_arith_overflow (location_t ovf_only = true; /* FALLTHRU */ case BUILT_IN_ADD_OVERFLOW: - opcode = PLUS_EXPR; - /* FALLTHRU */ case BUILT_IN_SADD_OVERFLOW: case BUILT_IN_SADDL_OVERFLOW: case BUILT_IN_SADDLL_OVERFLOW: case BUILT_IN_UADD_OVERFLOW: case BUILT_IN_UADDL_OVERFLOW: case BUILT_IN_UADDLL_OVERFLOW: + opcode = PLUS_EXPR; ifn = IFN_ADD_OVERFLOW; break; case BUILT_IN_SUB_OVERFLOW_P: ovf_only = true; /* FALLTHRU */ case BUILT_IN_SUB_OVERFLOW: - opcode = MINUS_EXPR; - /* FALLTHRU */ case BUILT_IN_SSUB_OVERFLOW: case BUILT_IN_SSUBL_OVERFLOW: case BUILT_IN_SSUBLL_OVERFLOW: case BUILT_IN_USUB_OVERFLOW: case BUILT_IN_USUBL_OVERFLOW: case BUILT_IN_USUBLL_OVERFLOW: + opcode = MINUS_EXPR; ifn = IFN_SUB_OVERFLOW; break; case BUILT_IN_MUL_OVERFLOW_P: ovf_only = true; /* FALLTHRU */ case BUILT_IN_MUL_OVERFLOW: - opcode = MULT_EXPR; - /* FALLTHRU */ case BUILT_IN_SMUL_OVERFLOW: case BUILT_IN_SMULL_OVERFLOW: case BUILT_IN_SMULLL_OVERFLOW: case BUILT_IN_UMUL_OVERFLOW: case BUILT_IN_UMULL_OVERFLOW: case BUILT_IN_UMULLL_OVERFLOW: + opcode = MULT_EXPR; ifn = IFN_MUL_OVERFLOW; break; default: @@ -9373,13 +9369,27 @@ fold_builtin_arith_overflow (location_t ? boolean_true_node : boolean_false_node, arg2); - tree ctype = build_complex_type (type); - tree call = build_call_expr_internal_loc (loc, ifn, ctype, - 2, arg0, arg1); - tree tgt = save_expr (call); - tree intres = build1_loc (loc, REALPART_EXPR, type, tgt); - tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); - ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + tree intres, ovfres; + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + intres = fold_binary_loc (loc, opcode, type, + fold_convert_loc (loc, type, arg0), + fold_convert_loc (loc, type, arg1)); + if (TREE_OVERFLOW (intres)) + intres = drop_tree_overflow (intres); + ovfres = (arith_overflowed_p (opcode, type, arg0, arg1) + ? boolean_true_node : boolean_false_node); + } + else + { + tree ctype = build_complex_type (type); + tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, + arg0, arg1); + tree tgt = save_expr (call); + intres = build1_loc (loc, REALPART_EXPR, type, tgt); + ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); + ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + } if (ovf_only) return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2); --- gcc/cp/constexpr.c.jj 2019-02-19 09:33:15.499185224 +0100 +++ gcc/cp/constexpr.c 2019-02-19 10:26:09.567962395 +0100 @@ -139,6 +139,8 @@ ensure_literal_type_for_constexpr_object struct GTY((for_user)) constexpr_fundef { tree decl; tree body; + tree parms; + tree result; }; struct constexpr_fundef_hasher : ggc_ptr_hash @@ -176,11 +178,10 @@ constexpr_fundef_hasher::hash (constexpr static constexpr_fundef * retrieve_constexpr_fundef (tree fun) { - constexpr_fundef fundef = { NULL, NULL }; if (constexpr_fundef_table == NULL) return NULL; - fundef.decl = fun; + constexpr_fundef fundef = { fun, NULL, NULL, NULL }; return constexpr_fundef_table->find (&fundef); } @@ -897,8 +898,19 @@ register_constexpr_fundef (tree fun, tre = hash_table::create_ggc (101); entry.decl = fun; - entry.body = body; + tree saved_fn = current_function_decl; + bool clear_ctx = false; + current_function_decl = fun; + if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE) + { + clear_ctx = true; + DECL_CONTEXT (DECL_RESULT (fun)) = fun; + } + entry.body = copy_fn (fun, entry.parms, entry.result); + current_function_decl = saved_fn; slot = constexpr_fundef_table->find_slot (&entry, INSERT); + if (clear_ctx) + DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE; gcc_assert (*slot == NULL); *slot = ggc_alloc (); @@ -1114,27 +1126,40 @@ maybe_initialize_fundef_copies_table () is parms, TYPE is result. */ static tree -get_fundef_copy (tree fun) +get_fundef_copy (constexpr_fundef *fundef) { maybe_initialize_fundef_copies_table (); tree copy; bool existed; - tree *slot = &fundef_copies_table->get_or_insert (fun, &existed); + tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed); if (!existed) { /* There is no cached function available, or in use. We can use the function directly. That the slot is now created records that this function is now in use. */ - copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun)); - TREE_TYPE (copy) = DECL_RESULT (fun); + copy = build_tree_list (fundef->body, fundef->parms); + TREE_TYPE (copy) = fundef->result; } else if (*slot == NULL_TREE) { /* We've already used the function itself, so make a copy. */ copy = build_tree_list (NULL, NULL); - TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy)); + tree saved_body = DECL_SAVED_TREE (fundef->decl); + tree saved_parms = DECL_ARGUMENTS (fundef->decl); + tree saved_result = DECL_RESULT (fundef->decl); + tree saved_fn = current_function_decl; + DECL_SAVED_TREE (fundef->decl) = fundef->body; + DECL_ARGUMENTS (fundef->decl) = fundef->parms; + DECL_RESULT (fundef->decl) = fundef->result; + current_function_decl = fundef->decl; + TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy), + TREE_TYPE (copy)); + current_function_decl = saved_fn; + DECL_RESULT (fundef->decl) = saved_result; + DECL_ARGUMENTS (fundef->decl) = saved_parms; + DECL_SAVED_TREE (fundef->decl) = saved_body; } else { @@ -1245,8 +1270,15 @@ cxx_eval_builtin_function_call (const co bool save_ffbcp = force_folding_builtin_constant_p; force_folding_builtin_constant_p = true; + tree save_cur_fn = current_function_decl; + /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION (). */ + if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION) + && ctx->call + && ctx->call->fundef) + current_function_decl = ctx->call->fundef->decl; new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t), CALL_EXPR_FN (t), nargs, args); + current_function_decl = save_cur_fn; force_folding_builtin_constant_p = save_ffbcp; if (new_call == NULL) { @@ -1269,6 +1301,46 @@ cxx_eval_builtin_function_call (const co return t; } + if (fndecl_built_in_p (fun, BUILT_IN_NORMAL)) + switch (DECL_FUNCTION_CODE (fun)) + { + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These builtins will fold into ((something = cst1), cst2) + which fails is_constant_expression. */ + if (TREE_CODE (args[0]) != INTEGER_CST + || TREE_CODE (args[1]) != INTEGER_CST + || !potential_constant_expression (args[2])) + { + if (!*non_constant_p && !ctx->quiet) + error ("%q+E is not a constant expression", new_call); + *non_constant_p = true; + return t; + } + return cxx_eval_constant_expression (&new_ctx, new_call, lval, + non_constant_p, overflow_p); + default: + break; + } if (!is_constant_expression (new_call)) { if (!*non_constant_p && !ctx->quiet) @@ -1341,7 +1413,7 @@ cxx_bind_parameters_in_call (const const { const int nargs = call_expr_nargs (t); tree fun = new_call->fundef->decl; - tree parms = DECL_ARGUMENTS (fun); + tree parms = new_call->fundef->parms; int i; tree *p = &new_call->bindings; for (i = 0; i < nargs; ++i) @@ -1358,6 +1430,9 @@ cxx_bind_parameters_in_call (const const x = ctx->object; x = build_address (x); } + if (TREE_ADDRESSABLE (type)) + /* Undo convert_for_arg_passing work here. */ + x = convert_from_reference (x); arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false, non_constant_p, overflow_p); /* Don't VERIFY_CONSTANT here. */ @@ -1761,7 +1836,7 @@ cxx_eval_call_expression (const constexp tree body, parms, res; /* Reuse or create a new unshared copy of this function's body. */ - tree copy = get_fundef_copy (fun); + tree copy = get_fundef_copy (new_call.fundef); body = TREE_PURPOSE (copy); parms = TREE_VALUE (copy); res = TREE_TYPE (copy); @@ -2223,14 +2298,13 @@ cxx_eval_conditional_expression (const c VERIFY_CONSTANT (val); /* Don't VERIFY_CONSTANT the other operands. */ if (integer_zerop (val)) - return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2), - lval, - non_constant_p, overflow_p, - jump_target); - return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), - lval, - non_constant_p, overflow_p, - jump_target); + val = TREE_OPERAND (t, 2); + else + val = TREE_OPERAND (t, 1); + if (TREE_CODE (t) == IF_STMT && !val) + val = void_node; + return cxx_eval_constant_expression (ctx, val, lval, non_constant_p, + overflow_p, jump_target); } /* Subroutine of cxx_eval_constant_expression. @@ -4047,6 +4121,12 @@ label_matches (const constexpr_ctx *ctx, } break; + case BREAK_STMT: + case CONTINUE_STMT: + /* These two are handled directly in cxx_eval_loop_expr by testing + breaks (jump_target) or continues (jump_target). */ + break; + default: gcc_unreachable (); } @@ -4107,20 +4187,84 @@ cxx_eval_loop_expr (const constexpr_ctx { constexpr_ctx new_ctx = *ctx; - tree body = TREE_OPERAND (t, 0); + tree body, cond = NULL_TREE, expr = NULL_TREE; int count = 0; + switch (TREE_CODE (t)) + { + case LOOP_EXPR: + body = LOOP_EXPR_BODY (t); + break; + case DO_STMT: + body = DO_BODY (t); + cond = DO_COND (t); + break; + case WHILE_STMT: + body = WHILE_BODY (t); + cond = WHILE_COND (t); + count = -1; + break; + case FOR_STMT: + if (FOR_INIT_STMT (t)) + cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false, + non_constant_p, overflow_p, jump_target); + if (*non_constant_p) + return NULL_TREE; + body = FOR_BODY (t); + cond = FOR_COND (t); + expr = FOR_EXPR (t); + count = -1; + break; + default: + gcc_unreachable (); + } + hash_set save_exprs; + new_ctx.save_exprs = &save_exprs; do { - hash_set save_exprs; - new_ctx.save_exprs = &save_exprs; + if (count != -1) + { + if (body) + cxx_eval_constant_expression (&new_ctx, body, /*lval*/false, + non_constant_p, overflow_p, + jump_target); + if (breaks (jump_target)) + { + *jump_target = NULL_TREE; + break; + } - cxx_eval_constant_expression (&new_ctx, body, /*lval*/false, - non_constant_p, overflow_p, jump_target); + if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target)) + *jump_target = NULL_TREE; + + if (expr) + cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false, + non_constant_p, overflow_p, + jump_target); + } + + if (cond) + { + tree res + = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false, + non_constant_p, overflow_p, + jump_target); + if (res) + { + if (verify_constant (res, ctx->quiet, non_constant_p, + overflow_p)) + break; + if (integer_zerop (res)) + break; + } + else + gcc_assert (*jump_target); + } /* Forget saved values of SAVE_EXPRs. */ for (hash_set::iterator iter = save_exprs.begin(); iter != save_exprs.end(); ++iter) new_ctx.values->remove (*iter); + if (++count >= constexpr_loop_limit) { if (!ctx->quiet) @@ -4134,11 +4278,14 @@ cxx_eval_loop_expr (const constexpr_ctx } while (!returns (jump_target) && !breaks (jump_target) - && !switches (jump_target) + && !continues (jump_target) + && (!switches (jump_target) || count == 0) && !*non_constant_p); - if (breaks (jump_target)) - *jump_target = NULL_TREE; + /* Forget saved values of SAVE_EXPRs. */ + for (hash_set::iterator iter = save_exprs.begin(); + iter != save_exprs.end(); ++iter) + new_ctx.values->remove (*iter); return NULL_TREE; } @@ -4151,13 +4298,15 @@ cxx_eval_switch_expr (const constexpr_ct bool *non_constant_p, bool *overflow_p, tree *jump_target) { - tree cond = SWITCH_COND (t); + tree cond + = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t); cond = cxx_eval_constant_expression (ctx, cond, false, non_constant_p, overflow_p); VERIFY_CONSTANT (cond); *jump_target = cond; - tree body = SWITCH_BODY (t); + tree body + = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t); constexpr_ctx new_ctx = *ctx; constexpr_switch_state css = css_default_not_seen; new_ctx.css_state = &css; @@ -4233,6 +4382,10 @@ cxx_eval_constant_expression (const cons case STATEMENT_LIST: case LOOP_EXPR: case COND_EXPR: + case IF_STMT: + case DO_STMT: + case WHILE_STMT: + case FOR_STMT: break; case LABEL_EXPR: case CASE_LABEL_EXPR: @@ -4377,6 +4530,11 @@ cxx_eval_constant_expression (const cons case DECL_EXPR: { r = DECL_EXPR_DECL (t); + if (TREE_CODE (r) == USING_DECL) + { + r = void_node; + break; + } if (AGGREGATE_TYPE_P (TREE_TYPE (r)) || VECTOR_TYPE_P (TREE_TYPE (r))) { @@ -4464,6 +4622,9 @@ cxx_eval_constant_expression (const cons r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p); + /* FALLTHRU */ + case BREAK_STMT: + case CONTINUE_STMT: if (jump_target) *jump_target = t; else @@ -4514,6 +4675,17 @@ cxx_eval_constant_expression (const cons jump_target); break; + case CLEANUP_STMT: + r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval, + non_constant_p, overflow_p, + jump_target); + if (!CLEANUP_EH_ONLY (t) && !*non_constant_p) + /* Also evaluate the cleanup. */ + cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true, + non_constant_p, overflow_p, + jump_target); + break; + /* These differ from cxx_eval_unary_expression in that this doesn't check for a constant operand or result; an address can be constant without its operand being, and vice versa. */ @@ -4569,8 +4741,9 @@ cxx_eval_constant_expression (const cons break; case SIZEOF_EXPR: - r = fold_sizeof_expr (t); - VERIFY_CONSTANT (r); + r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval, + non_constant_p, overflow_p, + jump_target); break; case COMPOUND_EXPR: @@ -4690,16 +4863,18 @@ cxx_eval_constant_expression (const cons break; case COND_EXPR: + case IF_STMT: if (jump_target && *jump_target) { tree orig_jump = *jump_target; + tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1)) + ? TREE_OPERAND (t, 1) : void_node); /* When jumping to a label, the label might be either in the then or else blocks, so process then block first in skipping mode first, and if we are still in the skipping mode at its end, process the else block too. */ - r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), - lval, non_constant_p, overflow_p, - jump_target); + r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p, + overflow_p, jump_target); /* It's possible that we found the label in the then block. But it could have been followed by another jumping statement, e.g. say we're looking for case 1: @@ -4713,9 +4888,12 @@ cxx_eval_constant_expression (const cons in which case we need not go looking to the else block. (goto is not allowed in a constexpr function.) */ if (*jump_target == orig_jump) - r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2), - lval, non_constant_p, overflow_p, - jump_target); + { + arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2)) + ? TREE_OPERAND (t, 2) : void_node); + r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p, + overflow_p, jump_target); + } break; } r = cxx_eval_conditional_expression (ctx, t, lval, @@ -4959,11 +5137,15 @@ cxx_eval_constant_expression (const cons break; case LOOP_EXPR: + case DO_STMT: + case WHILE_STMT: + case FOR_STMT: cxx_eval_loop_expr (ctx, t, non_constant_p, overflow_p, jump_target); break; case SWITCH_EXPR: + case SWITCH_STMT: cxx_eval_switch_expr (ctx, t, non_constant_p, overflow_p, jump_target); break; --- gcc/cp/lambda.c.jj 2019-02-19 09:41:58.515496370 +0100 +++ gcc/cp/lambda.c 2019-02-19 10:11:09.463430637 +0100 @@ -1087,8 +1087,7 @@ maybe_add_lambda_conv_op (tree type) tree optype = TREE_TYPE (callop); tree fn_result = TREE_TYPE (optype); - tree thisarg = build_nop (TREE_TYPE (DECL_ARGUMENTS (callop)), - null_pointer_node); + tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0); if (generic_lambda_p) { ++processing_template_decl; --- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj 2019-02-19 09:33:03.190389710 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-4.C 2019-02-19 10:11:09.463430637 +0100 @@ -15,11 +15,11 @@ struct T : S { int b; int g() { return 0; } virtual int v() { return 1; } - constexpr const T *foo() { return (const T *) reinterpret_cast (this); } + constexpr const T *foo() { return (const T *) reinterpret_cast (this); } // { dg-error "is not a constant expression" } }; constexpr T t; -constexpr const T *p = t.foo (); +constexpr const T *p = t.foo (); // { dg-message "expansion of" } template struct V { @@ -39,16 +39,17 @@ struct W : V { }; constexpr W w; -constexpr const W *s = w.foo (); +constexpr const W *s = w.foo (); // { dg-error "is not a constant expression" } +// { dg-message "expansion of" "" { target *-*-* } .-1 } template int foo (void) { static constexpr T t; - static constexpr const T *p = t.foo (); + static constexpr const T *p = t.foo (); // { dg-message "expansion of" } static constexpr W w; - static constexpr const W *s = w.foo (); - return t.b + w.b; + static constexpr const W *s = w.foo (); // { dg-error "is not a constant expression" } + return t.b + w.b; // { dg-message "expansion of" "" { target *-*-* } .-1 } } int x = foo (); --- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj 2019-02-19 09:33:03.099391222 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C 2019-02-19 10:11:09.463430637 +0100 @@ -12,7 +12,7 @@ void f2 () { for (;;) - constexpr bool b = ({ break; false; }) && false; // { dg-error "statement is not a constant expression" } + constexpr bool b = ({ break; false; }) && false; // { dg-error "is not a constant expression" } } constexpr bool --- gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C.jj 2019-02-19 09:33:03.131390690 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C 2019-02-19 10:11:09.463430637 +0100 @@ -5,8 +5,8 @@ constexpr int foo (int p) { int t = 0; - while (1) - t = 0; // { dg-error "count exceeds" } + while (1) // { dg-error "count exceeds" } + t = 0; return t; } --- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C.jj 2019-02-19 10:11:09.464430621 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C 2019-02-19 10:11:09.464430621 +0100 @@ -0,0 +1,20 @@ +// PR c++/89285 +// { dg-do compile { target c++14 } } + +struct A { + int a {}; +}; +struct B { + int b {}; + constexpr B (A *x) { + int *c = &x->a; + while (*c) + c = reinterpret_cast((reinterpret_cast(c) + *c)); + *c = reinterpret_cast(this) - reinterpret_cast(c); // { dg-error "reinterpret_cast" } + } +}; +struct C : A { + B bar {this}; +}; + +constexpr C foo {}; // { dg-message "expansion of" }