mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-28 14:24:43 +08:00
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.
This commit is contained in:
parent
5bb1e518b4
commit
1595fe44e1
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
17
gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C
Normal file
17
gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C
Normal file
@ -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.
|
@ -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 }
|
||||
}
|
||||
|
@ -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 }
|
||||
|
@ -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 }
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user