aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2021-10-13 22:04:53 -0400
committerJason Merrill <jason@redhat.com>2021-10-14 21:40:11 -0400
commit1595fe44e11a969d8ae462212886fb0a87b46226 (patch)
tree597c4fe89c15dd41e714e99532514947bc5add95
parent5bb1e518b4a5b772665a22cc27245ece5bf82750 (diff)
downloadgcc-1595fe44e11a969d8ae462212886fb0a87b46226.zip
gcc-1595fe44e11a969d8ae462212886fb0a87b46226.tar.gz
gcc-1595fe44e11a969d8ae462212886fb0a87b46226.tar.bz2
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<cgraph_node*>::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.
-rw-r--r--gcc/cp/constexpr.c58
-rw-r--r--gcc/cp/typeck2.c2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C17
-rw-r--r--gcc/testsuite/g++.dg/ext/vla22.C2
-rw-r--r--libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc24
-rw-r--r--libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc24
-rw-r--r--libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc24
-rw-r--r--libstdc++-v3/testsuite/lib/prune.exp1
8 files changed, 87 insertions, 65 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 08f8514..c5f01b9 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<tree, 16> 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 abfd7da..c01f2f8 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 0000000..3ce513d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C
@@ -0,0 +1,17 @@
+// Test that we don't uselessly instantiate f<A> 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 <class T>
+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<A> at EOF works fine.
diff --git a/gcc/testsuite/g++.dg/ext/vla22.C b/gcc/testsuite/g++.dg/ext/vla22.C
index 967adb9..2308ee7 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 6263326..8fc6179 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 <utility>
-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 48cb64d..d5524b8 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 <utility>
-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 a16b36a..61a9875 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 <utility>
-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 fd4a0ac..334f821 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