From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2122) id 8BBFE3858412; Fri, 15 Oct 2021 01:40:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8BBFE3858412 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jason Merrill To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r12-4425] c++: instantiate less for constant folding X-Act-Checkin: gcc X-Git-Author: Jason Merrill X-Git-Refname: refs/heads/master X-Git-Oldrev: 5bb1e518b4a5b772665a22cc27245ece5bf82750 X-Git-Newrev: 1595fe44e11a969d8ae462212886fb0a87b46226 Message-Id: <20211015014021.8BBFE3858412@sourceware.org> Date: Fri, 15 Oct 2021 01:40:21 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Oct 2021 01:40:21 -0000 https://gcc.gnu.org/g:1595fe44e11a969d8ae462212886fb0a87b46226 commit r12-4425-g1595fe44e11a969d8ae462212886fb0a87b46226 Author: Jason Merrill Date: Wed Oct 13 22:04:53 2021 -0400 c++: instantiate less for constant folding I've been experimenting with a change to make all inline functions implicitly constexpr; this revealed that we are instantiating too aggressively for speculative constant evaluation, leading to ordering difficulties with e.g. is_a_helper::test. This patch tries to avoid such instantiation until we actually need the function definition to determine whether a call is constant, by limiting the initial instantiation of all used functions to manifestly-constant-evaluated expressions, and checking whether the function arguments are constant before instantiating the function. This change resulted in a change in the diagnostics for a few library tests due to instantiating the function with the static_assert later (during constant evaluation) than we did before (during instantiation of the intermediate function). gcc/cp/ChangeLog: * constexpr.c (cxx_bind_parameters_in_call): Replace new_call parameter with fun. (cxx_eval_call_expression): Call it before instantiation. (cxx_eval_outermost_constant_expr): Only instantiate fns when manifestly_const_eval. * typeck2.c (check_narrowing): This context is manifestly constant-evaluated. libstdc++-v3/ChangeLog: * testsuite/20_util/integer_comparisons/greater_equal_neg.cc: * testsuite/20_util/integer_comparisons/greater_neg.cc: * testsuite/20_util/integer_comparisons/less_equal_neg.cc: Adjust expected message. * testsuite/lib/prune.exp: Prune 'in constexpr expansion'. gcc/testsuite/ChangeLog: * g++.dg/ext/vla22.C: Don't expect a narrowing error. * g++.dg/cpp0x/constexpr-inst1.C: New test. Diff: --- gcc/cp/constexpr.c | 58 ++++++++++++---------- gcc/cp/typeck2.c | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C | 17 +++++++ gcc/testsuite/g++.dg/ext/vla22.C | 2 +- .../integer_comparisons/greater_equal_neg.cc | 24 ++++----- .../20_util/integer_comparisons/greater_neg.cc | 24 ++++----- .../20_util/integer_comparisons/less_equal_neg.cc | 24 ++++----- libstdc++-v3/testsuite/lib/prune.exp | 1 + 8 files changed, 87 insertions(+), 65 deletions(-) diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 08f8514d08d..c5f01b95470 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1609,24 +1609,24 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data) all arguments and bind their values to correspondings parameters, making up the NEW_CALL context. */ -static void -cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, - constexpr_call *new_call, +static tree +cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, bool *non_constant_p, bool *overflow_p, bool *non_constant_args) { const int nargs = call_expr_nargs (t); - tree fun = new_call->fundef->decl; - tree parms = new_call->fundef->parms; + tree parms = DECL_ARGUMENTS (fun); int i; /* We don't record ellipsis args below. */ int nparms = list_length (parms); int nbinds = nargs < nparms ? nargs : nparms; - tree binds = new_call->bindings = make_tree_vec (nbinds); + tree binds = make_tree_vec (nbinds); for (i = 0; i < nargs; ++i) { tree x, arg; tree type = parms ? TREE_TYPE (parms) : void_type_node; + if (parms && DECL_BY_REFERENCE (parms)) + type = TREE_TYPE (type); x = get_nth_callarg (t, i); /* For member function, the first argument is a pointer to the implied object. For a constructor, it might still be a dummy object, in @@ -1647,7 +1647,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p); /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p && ctx->quiet) - return; + break; /* Just discard ellipsis args after checking their constantitude. */ if (!parms) continue; @@ -1698,6 +1698,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, } parms = TREE_CHAIN (parms); } + + return binds; } /* Variables and functions to manage constexpr call expansion context. @@ -2564,6 +2566,26 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, } } + bool non_constant_args = false; + new_call.bindings + = cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p, + overflow_p, &non_constant_args); + + /* 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 + this function exits. */ + class free_bindings + { + tree *bindings; + public: + free_bindings (tree &b): bindings (&b) { } + ~free_bindings () { if (bindings) ggc_free (*bindings); } + void preserve () { bindings = NULL; } + } fb (new_call.bindings); + + if (*non_constant_p) + return t; + /* We can't defer instantiating the function any longer. */ if (!DECL_INITIAL (fun) && DECL_TEMPLOID_INSTANTIATION (fun) @@ -2615,25 +2637,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, } } - bool non_constant_args = false; - cxx_bind_parameters_in_call (ctx, t, &new_call, - non_constant_p, overflow_p, &non_constant_args); - - /* 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 - this function exits. */ - class free_bindings - { - tree *bindings; - public: - free_bindings (tree &b): bindings (&b) { } - ~free_bindings () { if (bindings) ggc_free (*bindings); } - void preserve () { bindings = NULL; } - } fb (new_call.bindings); - - if (*non_constant_p) - return t; - depth_ok = push_cx_call_context (t); /* Remember the object we are constructing or destructing. */ @@ -7394,7 +7397,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, auto_vec cleanups; global_ctx.cleanups = &cleanups; - instantiate_constexpr_fns (r); + if (manifestly_const_eval) + instantiate_constexpr_fns (r); r = cxx_eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index abfd7dabd60..c01f2f8ced4 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -892,7 +892,7 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain, /* Even non-dependent expressions can still have template codes like CAST_EXPR, so use *_non_dependent_expr to cope. */ - init = fold_non_dependent_expr (init, complain); + init = fold_non_dependent_expr (init, complain, /*manifest*/true); if (init == error_mark_node) return ok; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C new file mode 100644 index 00000000000..3ce513d6e25 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C @@ -0,0 +1,17 @@ +// Test that we don't uselessly instantiate f immediately while parsing g. +// Doing so is permitted by the standard, but has no benefit and breaks code +// unnecessarily. + +// { dg-do compile { target c++11 } } + +// -O activates the call to maybe_constant_value in cp_fold. +// { dg-additional-options -O } + +template +constexpr int f (const T* p) { return p->i; } + +constexpr int g(const struct A* p) { return f(p); } + +struct A { int i; }; + +// Instantiating f at EOF works fine. diff --git a/gcc/testsuite/g++.dg/ext/vla22.C b/gcc/testsuite/g++.dg/ext/vla22.C index 967adb9ab67..2308ee748df 100644 --- a/gcc/testsuite/g++.dg/ext/vla22.C +++ b/gcc/testsuite/g++.dg/ext/vla22.C @@ -6,4 +6,4 @@ void f () { const int tbl[(long) "h"] = { 12 }; // { dg-error "size of array .tbl. is not an integral constant-expression" } -} // { dg-warning "narrowing conversion" "" { target c++11 } .-1 } +} diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc index 62633262948..8fc6179c07d 100644 --- a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc @@ -20,17 +20,17 @@ #include -bool a = std::cmp_greater_equal('1', 49); // { dg-error "here" } -bool b = std::cmp_greater_equal(50, '2'); // { dg-error "here" } -bool c = std::cmp_greater_equal(2, L'2'); // { dg-error "here" } -bool d = std::cmp_greater_equal(L'2', 2); // { dg-error "here" } -bool e = std::cmp_greater_equal(true, 1); // { dg-error "here" } -bool f = std::cmp_greater_equal(0, false); // { dg-error "here" } -bool g = std::cmp_greater_equal(97, u8'a'); // { dg-error "here" } -bool h = std::cmp_greater_equal(u8'a', 97); // { dg-error "here" } -bool i = std::cmp_greater_equal(97, u'a'); // { dg-error "here" } -bool j = std::cmp_greater_equal(u'a', 97); // { dg-error "here" } -bool k = std::cmp_greater_equal(97, U'a'); // { dg-error "here" } -bool l = std::cmp_greater_equal(U'a', 97); // { dg-error "here" } +bool a = std::cmp_greater_equal('1', 49); // { dg-error "constexpr" } +bool b = std::cmp_greater_equal(50, '2'); // { dg-error "constexpr" } +bool c = std::cmp_greater_equal(2, L'2'); // { dg-error "constexpr" } +bool d = std::cmp_greater_equal(L'2', 2); // { dg-error "constexpr" } +bool e = std::cmp_greater_equal(true, 1); // { dg-error "constexpr" } +bool f = std::cmp_greater_equal(0, false); // { dg-error "constexpr" } +bool g = std::cmp_greater_equal(97, u8'a'); // { dg-error "constexpr" } +bool h = std::cmp_greater_equal(u8'a', 97); // { dg-error "constexpr" } +bool i = std::cmp_greater_equal(97, u'a'); // { dg-error "constexpr" } +bool j = std::cmp_greater_equal(u'a', 97); // { dg-error "constexpr" } +bool k = std::cmp_greater_equal(97, U'a'); // { dg-error "constexpr" } +bool l = std::cmp_greater_equal(U'a', 97); // { dg-error "constexpr" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc index 48cb64d5676..d5524b86c26 100644 --- a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc @@ -20,17 +20,17 @@ #include -bool a = std::cmp_greater('1', 49); // { dg-error "here" } -bool b = std::cmp_greater(50, '2'); // { dg-error "here" } -bool c = std::cmp_greater(2, L'2'); // { dg-error "here" } -bool d = std::cmp_greater(L'2', 2); // { dg-error "here" } -bool e = std::cmp_greater(true, 1); // { dg-error "here" } -bool f = std::cmp_greater(0, false); // { dg-error "here" } -bool g = std::cmp_greater(97, u8'a'); // { dg-error "here" } -bool h = std::cmp_greater(u8'a', 97); // { dg-error "here" } -bool i = std::cmp_greater(97, u'a'); // { dg-error "here" } -bool j = std::cmp_greater(u'a', 97); // { dg-error "here" } -bool k = std::cmp_greater(97, U'a'); // { dg-error "here" } -bool l = std::cmp_greater(U'a', 97); // { dg-error "here" } +bool a = std::cmp_greater('1', 49); // { dg-error "constexpr" } +bool b = std::cmp_greater(50, '2'); // { dg-error "constexpr" } +bool c = std::cmp_greater(2, L'2'); // { dg-error "constexpr" } +bool d = std::cmp_greater(L'2', 2); // { dg-error "constexpr" } +bool e = std::cmp_greater(true, 1); // { dg-error "constexpr" } +bool f = std::cmp_greater(0, false); // { dg-error "constexpr" } +bool g = std::cmp_greater(97, u8'a'); // { dg-error "constexpr" } +bool h = std::cmp_greater(u8'a', 97); // { dg-error "constexpr" } +bool i = std::cmp_greater(97, u'a'); // { dg-error "constexpr" } +bool j = std::cmp_greater(u'a', 97); // { dg-error "constexpr" } +bool k = std::cmp_greater(97, U'a'); // { dg-error "constexpr" } +bool l = std::cmp_greater(U'a', 97); // { dg-error "constexpr" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc index a16b36a83c9..61a987561da 100644 --- a/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc @@ -20,17 +20,17 @@ #include -bool a = std::cmp_less_equal('1', 49); // { dg-error "here" } -bool b = std::cmp_less_equal(50, '2'); // { dg-error "here" } -bool c = std::cmp_less_equal(2, L'2'); // { dg-error "here" } -bool d = std::cmp_less_equal(L'2', 2); // { dg-error "here" } -bool e = std::cmp_less_equal(true, 1); // { dg-error "here" } -bool f = std::cmp_less_equal(0, false); // { dg-error "here" } -bool g = std::cmp_less_equal(97, u8'a'); // { dg-error "here" } -bool h = std::cmp_less_equal(u8'a', 97); // { dg-error "here" } -bool i = std::cmp_less_equal(97, u'a'); // { dg-error "here" } -bool j = std::cmp_less_equal(u'a', 97); // { dg-error "here" } -bool k = std::cmp_less_equal(97, U'a'); // { dg-error "here" } -bool l = std::cmp_less_equal(U'a', 97); // { dg-error "here" } +bool a = std::cmp_less_equal('1', 49); // { dg-error "constexpr" } +bool b = std::cmp_less_equal(50, '2'); // { dg-error "constexpr" } +bool c = std::cmp_less_equal(2, L'2'); // { dg-error "constexpr" } +bool d = std::cmp_less_equal(L'2', 2); // { dg-error "constexpr" } +bool e = std::cmp_less_equal(true, 1); // { dg-error "constexpr" } +bool f = std::cmp_less_equal(0, false); // { dg-error "constexpr" } +bool g = std::cmp_less_equal(97, u8'a'); // { dg-error "constexpr" } +bool h = std::cmp_less_equal(u8'a', 97); // { dg-error "constexpr" } +bool i = std::cmp_less_equal(97, u'a'); // { dg-error "constexpr" } +bool j = std::cmp_less_equal(u'a', 97); // { dg-error "constexpr" } +bool k = std::cmp_less_equal(97, U'a'); // { dg-error "constexpr" } +bool l = std::cmp_less_equal(U'a', 97); // { dg-error "constexpr" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/lib/prune.exp b/libstdc++-v3/testsuite/lib/prune.exp index fd4a0ac7f97..334f8218d16 100644 --- a/libstdc++-v3/testsuite/lib/prune.exp +++ b/libstdc++-v3/testsuite/lib/prune.exp @@ -46,6 +46,7 @@ proc libstdc++-dg-prune { system text } { regsub -all "(^|\n)\[^\n\]*(: )?At (top level|global scope):\[^\n\]*" $text "" text regsub -all "(^|\n)\[^\n\]*: (recursively )?required \[^\n\]*" $text "" text regsub -all "(^|\n)\[^\n\]*: . skipping \[0-9\]* instantiation contexts \[^\n\]*" $text "" text + regsub -all "(^|\n)\[^\n\]*: in .constexpr. expansion \[^\n\]*" $text "" text regsub -all "(^|\n) inlined from \[^\n\]*" $text "" text # Why doesn't GCC need these to strip header context? regsub -all "(^|\n)In file included from \[^\n\]*" $text "" text