blob: 47d5113ace26791031dd55619c546589bc75bb4d [file] [log] [blame]
/* Perform -*- C++ -*- constant expression evaluation, including calls to
constexpr functions. These routines are used both during actual parsing
and during the instantiation of template functions.
Copyright (C) 1998-2022 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
#include "varasm.h"
#include "c-family/c-objc.h"
#include "tree-iterator.h"
#include "gimplify.h"
#include "builtins.h"
#include "tree-inline.h"
#include "ubsan.h"
#include "gimple-fold.h"
#include "timevar.h"
#include "fold-const-call.h"
#include "stor-layout.h"
#include "cgraph.h"
#include "opts.h"
#include "stringpool.h"
#include "attribs.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
do { \
if (verify_constant ((X), ctx->quiet, non_constant_p, overflow_p)) \
return t; \
} while (0)
static HOST_WIDE_INT find_array_ctor_elt (tree ary, tree dindex,
bool insert = false);
static int array_index_cmp (tree key, tree index);
/* Returns true iff FUN is an instantiation of a constexpr function
template or a defaulted constexpr function. */
bool
is_instantiation_of_constexpr (tree fun)
{
return ((DECL_TEMPLOID_INSTANTIATION (fun)
&& DECL_DECLARED_CONSTEXPR_P (DECL_TI_TEMPLATE (fun)))
|| (DECL_DEFAULTED_FN (fun)
&& DECL_DECLARED_CONSTEXPR_P (fun)));
}
/* Return true if T is a literal type. */
bool
literal_type_p (tree t)
{
if (SCALAR_TYPE_P (t)
|| VECTOR_TYPE_P (t)
|| TYPE_REF_P (t)
|| (VOID_TYPE_P (t) && cxx_dialect >= cxx14))
return true;
if (CLASS_TYPE_P (t))
{
t = complete_type (t);
gcc_assert (COMPLETE_TYPE_P (t) || errorcount);
return CLASSTYPE_LITERAL_P (t);
}
if (TREE_CODE (t) == ARRAY_TYPE)
return literal_type_p (strip_array_types (t));
return false;
}
/* If DECL is a variable declared `constexpr', require its type
be literal. Return error_mark_node if we give an error, the
DECL otherwise. */
tree
ensure_literal_type_for_constexpr_object (tree decl)
{
tree type = TREE_TYPE (decl);
if (VAR_P (decl)
&& (DECL_DECLARED_CONSTEXPR_P (decl)
|| var_in_constexpr_fn (decl))
&& !processing_template_decl)
{
tree stype = strip_array_types (type);
if (CLASS_TYPE_P (stype) && !COMPLETE_TYPE_P (complete_type (stype)))
/* Don't complain here, we'll complain about incompleteness
when we try to initialize the variable. */;
else if (!literal_type_p (type))
{
if (DECL_DECLARED_CONSTEXPR_P (decl))
{
auto_diagnostic_group d;
error_at (DECL_SOURCE_LOCATION (decl),
"the type %qT of %<constexpr%> variable %qD "
"is not literal", type, decl);
explain_non_literal_class (type);
decl = error_mark_node;
}
else if (cxx_dialect < cxx23)
{
if (!is_instantiation_of_constexpr (current_function_decl))
{
auto_diagnostic_group d;
error_at (DECL_SOURCE_LOCATION (decl),
"variable %qD of non-literal type %qT in "
"%<constexpr%> function only available with "
"%<-std=c++2b%> or %<-std=gnu++2b%>", decl, type);
explain_non_literal_class (type);
decl = error_mark_node;
}
cp_function_chain->invalid_constexpr = true;
}
}
else if (DECL_DECLARED_CONSTEXPR_P (decl)
&& variably_modified_type_p (type, NULL_TREE))
{
error_at (DECL_SOURCE_LOCATION (decl),
"%<constexpr%> variable %qD has variably-modified "
"type %qT", decl, type);
decl = error_mark_node;
}
}
return decl;
}
struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
{
static hashval_t hash (const constexpr_fundef *);
static bool equal (const constexpr_fundef *, const constexpr_fundef *);
};
/* This table holds all constexpr function definitions seen in
the current translation unit. */
static GTY (()) hash_table<constexpr_fundef_hasher> *constexpr_fundef_table;
/* Utility function used for managing the constexpr function table.
Return true if the entries pointed to by P and Q are for the
same constexpr function. */
inline bool
constexpr_fundef_hasher::equal (const constexpr_fundef *lhs,
const constexpr_fundef *rhs)
{
return lhs->decl == rhs->decl;
}
/* Utility function used for managing the constexpr function table.
Return a hash value for the entry pointed to by Q. */
inline hashval_t
constexpr_fundef_hasher::hash (const constexpr_fundef *fundef)
{
return DECL_UID (fundef->decl);
}
/* Return a previously saved definition of function FUN. */
constexpr_fundef *
retrieve_constexpr_fundef (tree fun)
{
if (constexpr_fundef_table == NULL)
return NULL;
constexpr_fundef fundef = { fun, NULL_TREE, NULL_TREE, NULL_TREE };
return constexpr_fundef_table->find (&fundef);
}
/* Check whether the parameter and return types of FUN are valid for a
constexpr function, and complain if COMPLAIN. */
bool
is_valid_constexpr_fn (tree fun, bool complain)
{
bool ret = true;
if (DECL_INHERITED_CTOR (fun)
&& TREE_CODE (fun) == TEMPLATE_DECL)
{
ret = false;
if (complain)
error ("inherited constructor %qD is not %<constexpr%>",
DECL_INHERITED_CTOR (fun));
}
else
{
for (tree parm = FUNCTION_FIRST_USER_PARM (fun);
parm != NULL_TREE; parm = TREE_CHAIN (parm))
if (!literal_type_p (TREE_TYPE (parm)))
{
ret = false;
if (complain)
{
auto_diagnostic_group d;
error ("invalid type for parameter %d of %<constexpr%> "
"function %q+#D", DECL_PARM_INDEX (parm), fun);
explain_non_literal_class (TREE_TYPE (parm));
}
}
}
if (LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun)) && cxx_dialect < cxx17)
{
ret = false;
if (complain)
inform (DECL_SOURCE_LOCATION (fun),
"lambdas are implicitly %<constexpr%> only in C++17 and later");
}
else if (DECL_DESTRUCTOR_P (fun))
{
if (cxx_dialect < cxx20)
{
ret = false;
if (complain)
error_at (DECL_SOURCE_LOCATION (fun),
"%<constexpr%> destructors only available"
" with %<-std=c++20%> or %<-std=gnu++20%>");
}
}
else if (!DECL_CONSTRUCTOR_P (fun))
{
tree rettype = TREE_TYPE (TREE_TYPE (fun));
if (!literal_type_p (rettype))
{
ret = false;
if (complain)
{
auto_diagnostic_group d;
error ("invalid return type %qT of %<constexpr%> function %q+D",
rettype, fun);
explain_non_literal_class (rettype);
}
}
/* C++14 DR 1684 removed this restriction. */
if (cxx_dialect < cxx14
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)
&& !CLASSTYPE_LITERAL_P (DECL_CONTEXT (fun)))
{
ret = false;
if (complain)
{
auto_diagnostic_group d;
if (pedwarn (DECL_SOURCE_LOCATION (fun), OPT_Wpedantic,
"enclosing class of %<constexpr%> non-static"
" member function %q+#D is not a literal type",
fun))
explain_non_literal_class (DECL_CONTEXT (fun));
}
}
}
else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)))
{
ret = false;
if (complain)
error ("%q#T has virtual base classes", DECL_CONTEXT (fun));
}
return ret;
}
/* Subroutine of build_data_member_initialization. MEMBER is a COMPONENT_REF
for a member of an anonymous aggregate, INIT is the initializer for that
member, and VEC_OUTER is the vector of constructor elements for the class
whose constructor we are processing. Add the initializer to the vector
and return true to indicate success. */
static bool
build_anon_member_initialization (tree member, tree init,
vec<constructor_elt, va_gc> **vec_outer)
{
/* MEMBER presents the relevant fields from the inside out, but we need
to build up the initializer from the outside in so that we can reuse
previously built CONSTRUCTORs if this is, say, the second field in an
anonymous struct. So we use a vec as a stack. */
auto_vec<tree, 2> fields;
do
{
fields.safe_push (TREE_OPERAND (member, 1));
member = TREE_OPERAND (member, 0);
}
while (ANON_AGGR_TYPE_P (TREE_TYPE (member))
&& TREE_CODE (member) == COMPONENT_REF);
/* VEC has the constructor elements vector for the context of FIELD.
If FIELD is an anonymous aggregate, we will push inside it. */
vec<constructor_elt, va_gc> **vec = vec_outer;
tree field;
while (field = fields.pop(),
ANON_AGGR_TYPE_P (TREE_TYPE (field)))
{
tree ctor;
/* If there is already an outer constructor entry for the anonymous
aggregate FIELD, use it; otherwise, insert one. */
if (vec_safe_is_empty (*vec)
|| (*vec)->last().index != field)
{
ctor = build_constructor (TREE_TYPE (field), NULL);
CONSTRUCTOR_APPEND_ELT (*vec, field, ctor);
}
else
ctor = (*vec)->last().value;
vec = &CONSTRUCTOR_ELTS (ctor);
}
/* Now we're at the innermost field, the one that isn't an anonymous
aggregate. Add its initializer to the CONSTRUCTOR and we're done. */
gcc_assert (fields.is_empty());
CONSTRUCTOR_APPEND_ELT (*vec, field, init);
return true;
}
/* Subroutine of build_constexpr_constructor_member_initializers.
The expression tree T represents a data member initialization
in a (constexpr) constructor definition. Build a pairing of
the data member with its initializer, and prepend that pair
to the existing initialization pair INITS. */
static bool
build_data_member_initialization (tree t, vec<constructor_elt, va_gc> **vec)
{
tree member, init;
if (TREE_CODE (t) == CLEANUP_POINT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == EXPR_STMT)
t = TREE_OPERAND (t, 0);
if (t == error_mark_node)
return false;
if (TREE_CODE (t) == STATEMENT_LIST)
{
for (tree stmt : tsi_range (t))
if (! build_data_member_initialization (stmt, vec))
return false;
return true;
}
if (TREE_CODE (t) == CLEANUP_STMT)
{
/* We can't see a CLEANUP_STMT in a constructor for a literal class,
but we can in a constexpr constructor for a non-literal class. Just
ignore it; either all the initialization will be constant, in which
case the cleanup can't run, or it can't be constexpr.
Still recurse into CLEANUP_BODY. */
return build_data_member_initialization (CLEANUP_BODY (t), vec);
}
if (TREE_CODE (t) == CONVERT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == INIT_EXPR
/* vptr initialization shows up as a MODIFY_EXPR. In C++14 we only
use what this function builds for cx_check_missing_mem_inits, and
assignment in the ctor body doesn't count. */
|| (cxx_dialect < cxx14 && TREE_CODE (t) == MODIFY_EXPR))
{
member = TREE_OPERAND (t, 0);
init = break_out_target_exprs (TREE_OPERAND (t, 1));
}
else if (TREE_CODE (t) == CALL_EXPR)
{
tree fn = get_callee_fndecl (t);
if (!fn || !DECL_CONSTRUCTOR_P (fn))
/* We're only interested in calls to subobject constructors. */
return true;
member = CALL_EXPR_ARG (t, 0);
/* We don't use build_cplus_new here because it complains about
abstract bases. Leaving the call unwrapped means that it has the
wrong type, but cxx_eval_constant_expression doesn't care. */
init = break_out_target_exprs (t);
}
else if (TREE_CODE (t) == BIND_EXPR)
return build_data_member_initialization (BIND_EXPR_BODY (t), vec);
else
/* Don't add anything else to the CONSTRUCTOR. */
return true;
if (INDIRECT_REF_P (member))
member = TREE_OPERAND (member, 0);
if (TREE_CODE (member) == NOP_EXPR)
{
tree op = member;
STRIP_NOPS (op);
if (TREE_CODE (op) == ADDR_EXPR)
{
gcc_assert (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (TREE_TYPE (op)),
TREE_TYPE (TREE_TYPE (member))));
/* Initializing a cv-qualified member; we need to look through
the const_cast. */
member = op;
}
else if (op == current_class_ptr
&& (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (TREE_TYPE (member)),
current_class_type)))
/* Delegating constructor. */
member = op;
else
{
/* This is an initializer for an empty base; keep it for now so
we can check it in cxx_eval_bare_aggregate. */
gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (member))));
}
}
if (TREE_CODE (member) == ADDR_EXPR)
member = TREE_OPERAND (member, 0);
if (TREE_CODE (member) == COMPONENT_REF)
{
tree aggr = TREE_OPERAND (member, 0);
if (TREE_CODE (aggr) == VAR_DECL)
/* Initializing a local variable, don't add anything. */
return true;
if (TREE_CODE (aggr) != COMPONENT_REF)
/* Normal member initialization. */
member = TREE_OPERAND (member, 1);
else if (ANON_AGGR_TYPE_P (TREE_TYPE (aggr)))
/* Initializing a member of an anonymous union. */
return build_anon_member_initialization (member, init, vec);
else
/* We're initializing a vtable pointer in a base. Leave it as
COMPONENT_REF so we remember the path to get to the vfield. */
gcc_assert (TREE_TYPE (member) == vtbl_ptr_type_node);
}
/* Value-initialization can produce multiple initializers for the
same field; use the last one. */
if (!vec_safe_is_empty (*vec) && (*vec)->last().index == member)
(*vec)->last().value = init;
else
CONSTRUCTOR_APPEND_ELT (*vec, member, init);
return true;
}
/* Subroutine of check_constexpr_ctor_body_1 and constexpr_fn_retval.
In C++11 mode checks that the TYPE_DECLs in the BIND_EXPR_VARS of a
BIND_EXPR conform to 7.1.5/3/4 on typedef and alias declarations. */
static bool
check_constexpr_bind_expr_vars (tree t)
{
gcc_assert (TREE_CODE (t) == BIND_EXPR);
for (tree var = BIND_EXPR_VARS (t); var; var = DECL_CHAIN (var))
if (TREE_CODE (var) == TYPE_DECL
&& DECL_IMPLICIT_TYPEDEF_P (var)
&& !LAMBDA_TYPE_P (TREE_TYPE (var)))
return false;
return true;
}
/* Subroutine of check_constexpr_ctor_body. */
static bool
check_constexpr_ctor_body_1 (tree last, tree list)
{
switch (TREE_CODE (list))
{
case DECL_EXPR:
if (TREE_CODE (DECL_EXPR_DECL (list)) == USING_DECL
|| TREE_CODE (DECL_EXPR_DECL (list)) == TYPE_DECL)
return true;
return false;
case CLEANUP_POINT_EXPR:
return check_constexpr_ctor_body (last, TREE_OPERAND (list, 0),
/*complain=*/false);
case BIND_EXPR:
if (!check_constexpr_bind_expr_vars (list)
|| !check_constexpr_ctor_body (last, BIND_EXPR_BODY (list),
/*complain=*/false))
return false;
return true;
case USING_STMT:
case STATIC_ASSERT:
case DEBUG_BEGIN_STMT:
return true;
default:
return false;
}
}
/* Make sure that there are no statements after LAST in the constructor
body represented by LIST. */
bool
check_constexpr_ctor_body (tree last, tree list, bool complain)
{
/* C++14 doesn't require a constexpr ctor to have an empty body. */
if (cxx_dialect >= cxx14)
return true;
bool ok = true;
if (TREE_CODE (list) == STATEMENT_LIST)
{
tree_stmt_iterator i = tsi_last (list);
for (; !tsi_end_p (i); tsi_prev (&i))
{
tree t = tsi_stmt (i);
if (t == last)
break;
if (!check_constexpr_ctor_body_1 (last, t))
{
ok = false;
break;
}
}
}
else if (list != last
&& !check_constexpr_ctor_body_1 (last, list))
ok = false;
if (!ok)
{
if (complain)
error ("%<constexpr%> constructor does not have empty body");
DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false;
}
return ok;
}
/* V is a vector of constructor elements built up for the base and member
initializers of a constructor for TYPE. They need to be in increasing
offset order, which they might not be yet if TYPE has a primary base
which is not first in the base-clause or a vptr and at least one base
all of which are non-primary. */
static vec<constructor_elt, va_gc> *
sort_constexpr_mem_initializers (tree type, vec<constructor_elt, va_gc> *v)
{
tree pri = CLASSTYPE_PRIMARY_BINFO (type);
tree field_type;
unsigned i;
constructor_elt *ce;
if (pri)
field_type = BINFO_TYPE (pri);
else if (TYPE_CONTAINS_VPTR_P (type))
field_type = vtbl_ptr_type_node;
else
return v;
/* Find the element for the primary base or vptr and move it to the
beginning of the vec. */
for (i = 0; vec_safe_iterate (v, i, &ce); ++i)
if (TREE_TYPE (ce->index) == field_type)
break;
if (i > 0 && i < vec_safe_length (v))
{
vec<constructor_elt, va_gc> &vref = *v;
constructor_elt elt = vref[i];
for (; i > 0; --i)
vref[i] = vref[i-1];
vref[0] = elt;
}
return v;
}
/* Build compile-time evalable representations of member-initializer list
for a constexpr constructor. */
static tree
build_constexpr_constructor_member_initializers (tree type, tree body)
{
vec<constructor_elt, va_gc> *vec = NULL;
bool ok = true;
while (true)
switch (TREE_CODE (body))
{
case MUST_NOT_THROW_EXPR:
case EH_SPEC_BLOCK:
body = TREE_OPERAND (body, 0);
break;
case STATEMENT_LIST:
for (tree stmt : tsi_range (body))
{
body = stmt;
if (TREE_CODE (body) == BIND_EXPR)
break;
}
break;
case BIND_EXPR:
body = BIND_EXPR_BODY (body);
goto found;
default:
gcc_unreachable ();
}
found:
if (TREE_CODE (body) == TRY_BLOCK)
{
body = TREE_OPERAND (body, 0);
if (TREE_CODE (body) == BIND_EXPR)
body = BIND_EXPR_BODY (body);
}
if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
{
body = TREE_OPERAND (body, 0);
if (TREE_CODE (body) == EXPR_STMT)
body = TREE_OPERAND (body, 0);
if (TREE_CODE (body) == INIT_EXPR
&& (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (TREE_OPERAND (body, 0)),
current_class_type)))
{
/* Trivial copy. */
return TREE_OPERAND (body, 1);
}
ok = build_data_member_initialization (body, &vec);
}
else if (TREE_CODE (body) == STATEMENT_LIST)
{
for (tree stmt : tsi_range (body))
{
ok = build_data_member_initialization (stmt, &vec);
if (!ok)
break;
}
}
else if (EXPR_P (body))
ok = build_data_member_initialization (body, &vec);
else
gcc_assert (errorcount > 0);
if (ok)
{
if (vec_safe_length (vec) > 0)
{
/* In a delegating constructor, return the target. */
constructor_elt *ce = &(*vec)[0];
if (ce->index == current_class_ptr)
{
body = ce->value;
vec_free (vec);
return body;
}
}
vec = sort_constexpr_mem_initializers (type, vec);
return build_constructor (type, vec);
}
else
return error_mark_node;
}
/* We have an expression tree T that represents a call, either CALL_EXPR
or AGGR_INIT_EXPR. If the call is lexically to a named function,
retrun the _DECL for that function. */
static tree
get_function_named_in_call (tree t)
{
tree fun = cp_get_callee (t);
if (fun && TREE_CODE (fun) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL)
fun = TREE_OPERAND (fun, 0);
return fun;
}
/* Subroutine of check_constexpr_fundef. BODY is the body of a function
declared to be constexpr, or a sub-statement thereof. Returns the
return value if suitable, error_mark_node for a statement not allowed in
a constexpr function, or NULL_TREE if no return value was found. */
tree
constexpr_fn_retval (tree body)
{
switch (TREE_CODE (body))
{
case STATEMENT_LIST:
{
tree expr = NULL_TREE;
for (tree stmt : tsi_range (body))
{
tree s = constexpr_fn_retval (stmt);
if (s == error_mark_node)
return error_mark_node;
else if (s == NULL_TREE)
/* Keep iterating. */;
else if (expr)
/* Multiple return statements. */
return error_mark_node;
else
expr = s;
}
return expr;
}
case RETURN_EXPR:
return break_out_target_exprs (TREE_OPERAND (body, 0));
case DECL_EXPR:
{
tree decl = DECL_EXPR_DECL (body);
if (TREE_CODE (decl) == USING_DECL
/* Accept __func__, __FUNCTION__, and __PRETTY_FUNCTION__. */
|| DECL_ARTIFICIAL (decl))
return NULL_TREE;
return error_mark_node;
}
case CLEANUP_POINT_EXPR:
return constexpr_fn_retval (TREE_OPERAND (body, 0));
case BIND_EXPR:
if (!check_constexpr_bind_expr_vars (body))
return error_mark_node;
return constexpr_fn_retval (BIND_EXPR_BODY (body));
case USING_STMT:
case DEBUG_BEGIN_STMT:
return NULL_TREE;
case CALL_EXPR:
{
tree fun = get_function_named_in_call (body);
if (fun != NULL_TREE
&& fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE))
return NULL_TREE;
}
/* Fallthru. */
default:
return error_mark_node;
}
}
/* Subroutine of check_constexpr_fundef. BODY is the DECL_SAVED_TREE of
FUN; do the necessary transformations to turn it into a single expression
that we can store in the hash table. */
static tree
massage_constexpr_body (tree fun, tree body)
{
if (DECL_CONSTRUCTOR_P (fun))
body = build_constexpr_constructor_member_initializers
(DECL_CONTEXT (fun), body);
else if (cxx_dialect < cxx14)
{
if (TREE_CODE (body) == EH_SPEC_BLOCK)
body = EH_SPEC_STMTS (body);
if (TREE_CODE (body) == MUST_NOT_THROW_EXPR)
body = TREE_OPERAND (body, 0);
body = constexpr_fn_retval (body);
}
return body;
}
/* CTYPE is a type constructed from BODY. Return true if some
bases/fields are uninitialized, and complain if COMPLAIN. */
static bool
cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
{
/* We allow uninitialized bases/fields in C++20. */
if (cxx_dialect >= cxx20)
return false;
unsigned nelts = 0;
if (body)
{
if (TREE_CODE (body) != CONSTRUCTOR)
return false;
nelts = CONSTRUCTOR_NELTS (body);
}
tree field = TYPE_FIELDS (ctype);
if (TREE_CODE (ctype) == UNION_TYPE)
{
if (nelts == 0 && next_initializable_field (field))
{
if (complain)
error ("%<constexpr%> constructor for union %qT must "
"initialize exactly one non-static data member", ctype);
return true;
}
return false;
}
/* Iterate over the CONSTRUCTOR, checking any missing fields don't
need an explicit initialization. */
bool bad = false;
for (unsigned i = 0; i <= nelts; ++i)
{
tree index = NULL_TREE;
if (i < nelts)
{
index = CONSTRUCTOR_ELT (body, i)->index;
/* Skip base and vtable inits. */
if (TREE_CODE (index) != FIELD_DECL
|| DECL_ARTIFICIAL (index))
continue;
}
for (; field != index; field = DECL_CHAIN (field))
{
tree ftype;
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (DECL_UNNAMED_BIT_FIELD (field))
continue;
if (DECL_ARTIFICIAL (field))
continue;
if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
{
/* Recurse to check the anonymous aggregate member. */
bad |= cx_check_missing_mem_inits
(TREE_TYPE (field), NULL_TREE, complain);
if (bad && !complain)
return true;
continue;
}
ftype = TREE_TYPE (field);
if (!ftype || !TYPE_P (ftype) || !COMPLETE_TYPE_P (ftype))
/* A flexible array can't be intialized here, so don't complain
that it isn't. */
continue;
if (is_empty_field (field))
/* An empty field doesn't need an initializer. */
continue;
ftype = strip_array_types (ftype);
if (type_has_constexpr_default_constructor (ftype))
{
/* It's OK to skip a member with a trivial constexpr ctor.
A constexpr ctor that isn't trivial should have been
added in by now. */
gcc_checking_assert (!TYPE_HAS_COMPLEX_DFLT (ftype)
|| errorcount != 0);
continue;
}
if (!complain)
return true;
auto_diagnostic_group d;
error ("member %qD must be initialized by mem-initializer "
"in %<constexpr%> constructor", field);
inform (DECL_SOURCE_LOCATION (field), "declared here");
bad = true;
}
if (field == NULL_TREE)
break;
if (ANON_AGGR_TYPE_P (TREE_TYPE (index)))
{
/* Check the anonymous aggregate initializer is valid. */
bad |= cx_check_missing_mem_inits
(TREE_TYPE (index), CONSTRUCTOR_ELT (body, i)->value, complain);
if (bad && !complain)
return true;
}
field = DECL_CHAIN (field);
}
return bad;
}
/* We are processing the definition of the constexpr function FUN.
Check that its body fulfills the apropriate requirements and
enter it in the constexpr function definition table. */
void
maybe_save_constexpr_fundef (tree fun)
{
if (processing_template_decl
|| cp_function_chain->invalid_constexpr
|| (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun)))
return;
/* With -fimplicit-constexpr, try to make inlines constexpr. We'll
actually set DECL_DECLARED_CONSTEXPR_P below if the checks pass. */
bool implicit = false;
if (flag_implicit_constexpr)
{
if (DECL_DELETING_DESTRUCTOR_P (fun)
&& decl_implicit_constexpr_p (DECL_CLONED_FUNCTION (fun)))
/* Don't inherit implicit constexpr from the non-deleting
destructor. */
DECL_DECLARED_CONSTEXPR_P (fun) = false;
if (!DECL_DECLARED_CONSTEXPR_P (fun)
&& DECL_DECLARED_INLINE_P (fun)
&& !lookup_attribute ("noinline", DECL_ATTRIBUTES (fun)))
implicit = true;
}
if (!DECL_DECLARED_CONSTEXPR_P (fun) && !implicit)
return;
bool complain = !DECL_GENERATED_P (fun) && !implicit;
if (!is_valid_constexpr_fn (fun, complain))
return;
tree massaged = massage_constexpr_body (fun, DECL_SAVED_TREE (fun));
if (massaged == NULL_TREE || massaged == error_mark_node)
{
if (!DECL_CONSTRUCTOR_P (fun) && complain)
error ("body of %<constexpr%> function %qD not a return-statement",
fun);
return;
}
bool potential = potential_rvalue_constant_expression (massaged);
if (!potential && complain)
require_potential_rvalue_constant_expression (massaged);
if (DECL_CONSTRUCTOR_P (fun) && potential
&& !DECL_DEFAULTED_FN (fun))
{
if (cx_check_missing_mem_inits (DECL_CONTEXT (fun),
massaged, complain))
potential = false;
else if (cxx_dialect > cxx11)
{
/* What we got from massage_constexpr_body is pretty much just the
ctor-initializer, also check the body. */
massaged = DECL_SAVED_TREE (fun);
potential = potential_rvalue_constant_expression (massaged);
if (!potential && complain)
require_potential_rvalue_constant_expression (massaged);
}
}
if (!potential && complain)
return;
if (implicit)
{
if (potential)
{
DECL_DECLARED_CONSTEXPR_P (fun) = true;
DECL_LANG_SPECIFIC (fun)->u.fn.implicit_constexpr = true;
if (DECL_CONSTRUCTOR_P (fun))
TYPE_HAS_CONSTEXPR_CTOR (DECL_CONTEXT (fun)) = true;
}
else
/* Don't bother keeping the pre-generic body of unsuitable functions
not explicitly declared constexpr. */
return;
}
constexpr_fundef entry = {fun, NULL_TREE, NULL_TREE, NULL_TREE};
bool clear_ctx = false;
if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
{
clear_ctx = true;
DECL_CONTEXT (DECL_RESULT (fun)) = fun;
}
tree saved_fn = current_function_decl;
current_function_decl = fun;
entry.body = copy_fn (entry.decl, entry.parms, entry.result);
current_function_decl = saved_fn;
if (clear_ctx)
DECL_CONTEXT (DECL_RESULT (entry.decl)) = NULL_TREE;
if (!potential)
/* For a template instantiation, we want to remember the pre-generic body
for explain_invalid_constexpr_fn, but do tell cxx_eval_call_expression
that it doesn't need to bother trying to expand the function. */
entry.result = error_mark_node;
register_constexpr_fundef (entry);
}
/* BODY is a validated and massaged definition of a constexpr
function. Register it in the hash table. */
void
register_constexpr_fundef (const constexpr_fundef &value)
{
/* Create the constexpr function table if necessary. */
if (constexpr_fundef_table == NULL)
constexpr_fundef_table
= hash_table<constexpr_fundef_hasher>::create_ggc (101);
constexpr_fundef **slot = constexpr_fundef_table->find_slot
(const_cast<constexpr_fundef *> (&value), INSERT);
gcc_assert (*slot == NULL);
*slot = ggc_alloc<constexpr_fundef> ();
**slot = value;
}
/* FUN is a non-constexpr function called in a context that requires a
constant expression. If it comes from a constexpr template, explain why
the instantiation isn't constexpr. */
void
explain_invalid_constexpr_fn (tree fun)
{
static hash_set<tree> *diagnosed;
tree body;
/* Only diagnose defaulted functions, lambdas, or instantiations. */
if (!DECL_DEFAULTED_FN (fun)
&& !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
&& !is_instantiation_of_constexpr (fun))
{
inform (DECL_SOURCE_LOCATION (fun), "%qD declared here", fun);
return;
}
if (diagnosed == NULL)
diagnosed = new hash_set<tree>;
if (diagnosed->add (fun))
/* Already explained. */
return;
iloc_sentinel ils = input_location;
if (!lambda_static_thunk_p (fun))
{
/* Diagnostics should completely ignore the static thunk, so leave
input_location set to our caller's location. */
input_location = DECL_SOURCE_LOCATION (fun);
inform (input_location,
"%qD is not usable as a %<constexpr%> function because:", fun);
}
/* First check the declaration. */
if (is_valid_constexpr_fn (fun, true))
{
/* Then if it's OK, the body. */
if (!DECL_DECLARED_CONSTEXPR_P (fun)
&& DECL_DEFAULTED_FN (fun))
explain_implicit_non_constexpr (fun);
else
{
if (constexpr_fundef *fd = retrieve_constexpr_fundef (fun))
body = fd->body;
else
body = DECL_SAVED_TREE (fun);
body = massage_constexpr_body (fun, body);
require_potential_rvalue_constant_expression (body);
if (DECL_CONSTRUCTOR_P (fun))
cx_check_missing_mem_inits (DECL_CONTEXT (fun), body, true);
}
}
}
/* Objects of this type represent calls to constexpr functions
along with the bindings of parameters to their arguments, for
the purpose of compile time evaluation. */
struct GTY((for_user)) constexpr_call {
/* Description of the constexpr function definition. */
constexpr_fundef *fundef;
/* Parameter bindings environment. A TREE_VEC of arguments. */
tree bindings;
/* Result of the call.
NULL means the call is being evaluated.
error_mark_node means that the evaluation was erroneous;
otherwise, the actuall value of the call. */
tree result;
/* The hash of this call; we remember it here to avoid having to
recalculate it when expanding the hash table. */
hashval_t hash;
/* Whether __builtin_is_constant_evaluated() should evaluate to true. */
bool manifestly_const_eval;
};
struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
{
static hashval_t hash (constexpr_call *);
static bool equal (constexpr_call *, constexpr_call *);
};
enum constexpr_switch_state {
/* Used when processing a switch for the first time by cxx_eval_switch_expr
and default: label for that switch has not been seen yet. */
css_default_not_seen,
/* Used when processing a switch for the first time by cxx_eval_switch_expr
and default: label for that switch has been seen already. */
css_default_seen,
/* Used when processing a switch for the second time by
cxx_eval_switch_expr, where default: label should match. */
css_default_processing
};
/* The constexpr expansion context part which needs one instance per
cxx_eval_outermost_constant_expr invocation. VALUES is a map of values of
variables initialized within the expression. */
struct constexpr_global_ctx {
/* Values for any temporaries or local variables within the
constant-expression. */
hash_map<tree,tree> values;
/* Number of cxx_eval_constant_expression calls (except skipped ones,
on simple constants or location wrappers) encountered during current
cxx_eval_outermost_constant_expr call. */
HOST_WIDE_INT constexpr_ops_count;
/* Heap VAR_DECLs created during the evaluation of the outermost constant
expression. */
auto_vec<tree, 16> heap_vars;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *cleanups;
/* Number of heap VAR_DECL deallocations. */
unsigned heap_dealloc_count;
/* Constructor. */
constexpr_global_ctx ()
: constexpr_ops_count (0), cleanups (NULL), heap_dealloc_count (0) {}
};
/* The constexpr expansion context. CALL is the current function
expansion, CTOR is the current aggregate initializer, OBJECT is the
object being initialized by CTOR, either a VAR_DECL or a _REF. */
struct constexpr_ctx {
/* The part of the context that needs to be unique to the whole
cxx_eval_outermost_constant_expr invocation. */
constexpr_global_ctx *global;
/* The innermost call we're evaluating. */
constexpr_call *call;
/* SAVE_EXPRs and TARGET_EXPR_SLOT vars of TARGET_EXPRs that we've seen
within the current LOOP_EXPR. NULL if we aren't inside a loop. */
vec<tree> *save_exprs;
/* The CONSTRUCTOR we're currently building up for an aggregate
initializer. */
tree ctor;
/* The object we're building the CONSTRUCTOR for. */
tree object;
/* If inside SWITCH_EXPR. */
constexpr_switch_state *css_state;
/* The aggregate initialization context inside which this one is nested. This
is used by lookup_placeholder to resolve PLACEHOLDER_EXPRs. */
const constexpr_ctx *parent;
/* Whether we should error on a non-constant expression or fail quietly.
This flag needs to be here, but some of the others could move to global
if they get larger than a word. */
bool quiet;
/* Whether we are strictly conforming to constant expression rules or
trying harder to get a constant value. */
bool strict;
/* Whether __builtin_is_constant_evaluated () should be true. */
bool manifestly_const_eval;
};
/* This internal flag controls whether we should avoid doing anything during
constexpr evaluation that would cause extra DECL_UID generation, such as
template instantiation and function body copying. */
static bool uid_sensitive_constexpr_evaluation_value;
/* An internal counter that keeps track of the number of times
uid_sensitive_constexpr_evaluation_p returned true. */
static unsigned uid_sensitive_constexpr_evaluation_true_counter;
/* The accessor for uid_sensitive_constexpr_evaluation_value which also
increments the corresponding counter. */
static bool
uid_sensitive_constexpr_evaluation_p ()
{
if (uid_sensitive_constexpr_evaluation_value)
{
++uid_sensitive_constexpr_evaluation_true_counter;
return true;
}
else
return false;
}
/* The default constructor for uid_sensitive_constexpr_evaluation_sentinel
enables the internal flag for uid_sensitive_constexpr_evaluation_p
during the lifetime of the sentinel object. Upon its destruction, the
previous value of uid_sensitive_constexpr_evaluation_p is restored. */
uid_sensitive_constexpr_evaluation_sentinel
::uid_sensitive_constexpr_evaluation_sentinel ()
: ovr (uid_sensitive_constexpr_evaluation_value, true)
{
}
/* The default constructor for uid_sensitive_constexpr_evaluation_checker
records the current number of times that uid_sensitive_constexpr_evaluation_p
has been called and returned true. */
uid_sensitive_constexpr_evaluation_checker
::uid_sensitive_constexpr_evaluation_checker ()
: saved_counter (uid_sensitive_constexpr_evaluation_true_counter)
{
}
/* Returns true iff uid_sensitive_constexpr_evaluation_p is true, and
some constexpr evaluation was restricted due to u_s_c_e_p being called
and returning true during the lifetime of this checker object. */
bool
uid_sensitive_constexpr_evaluation_checker::evaluation_restricted_p () const
{
return (uid_sensitive_constexpr_evaluation_value
&& saved_counter != uid_sensitive_constexpr_evaluation_true_counter);
}
/* A table of all constexpr calls that have been evaluated by the
compiler in this translation unit. */
static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
bool, bool *, bool *, tree * = NULL);
/* Compute a hash value for a constexpr call representation. */
inline hashval_t
constexpr_call_hasher::hash (constexpr_call *info)
{
return info->hash;
}
/* Return true if the objects pointed to by P and Q represent calls
to the same constexpr function with the same arguments.
Otherwise, return false. */
bool
constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
{
if (lhs == rhs)
return true;
if (lhs->hash != rhs->hash)
return false;
if (lhs->manifestly_const_eval != rhs->manifestly_const_eval)
return false;
if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef))
return false;
return cp_tree_equal (lhs->bindings, rhs->bindings);
}
/* Initialize the constexpr call table, if needed. */
static void
maybe_initialize_constexpr_call_table (void)
{
if (constexpr_call_table == NULL)
constexpr_call_table = hash_table<constexpr_call_hasher>::create_ggc (101);
}
/* During constexpr CALL_EXPR evaluation, to avoid issues with sharing when
a function happens to get called recursively, we unshare the callee
function's body and evaluate this unshared copy instead of evaluating the
original body.
FUNDEF_COPIES_TABLE is a per-function freelist of these unshared function
copies. The underlying data structure of FUNDEF_COPIES_TABLE is a hash_map
that's keyed off of the original FUNCTION_DECL and whose value is a
TREE_LIST of this function's unused copies awaiting reuse.
This is not GC-deletable to avoid GC affecting UID generation. */
static GTY(()) decl_tree_map *fundef_copies_table;
/* Reuse a copy or create a new unshared copy of the function FUN.
Return this copy. We use a TREE_LIST whose PURPOSE is body, VALUE
is parms, TYPE is result. */
static tree
get_fundef_copy (constexpr_fundef *fundef)
{
tree copy;
bool existed;
tree *slot = &(hash_map_safe_get_or_insert<hm_ggc>
(fundef_copies_table, fundef->decl, &existed, 127));
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 (fundef->body, fundef->parms);
TREE_TYPE (copy) = fundef->result;
}
else if (*slot == NULL_TREE)
{
if (uid_sensitive_constexpr_evaluation_p ())
return NULL_TREE;
/* We've already used the function itself, so make a copy. */
copy = build_tree_list (NULL, NULL);
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
{
/* We have a cached function available. */
copy = *slot;
*slot = TREE_CHAIN (copy);
}
return copy;
}
/* Save the copy COPY of function FUN for later reuse by
get_fundef_copy(). By construction, there will always be an entry
to find. */
static void
save_fundef_copy (tree fun, tree copy)
{
tree *slot = fundef_copies_table->get (fun);
TREE_CHAIN (copy) = *slot;
*slot = copy;
}
/* We have an expression tree T that represents a call, either CALL_EXPR
or AGGR_INIT_EXPR. Return the Nth argument. */
static inline tree
get_nth_callarg (tree t, int n)
{
switch (TREE_CODE (t))
{
case CALL_EXPR:
return CALL_EXPR_ARG (t, n);
case AGGR_INIT_EXPR:
return AGGR_INIT_EXPR_ARG (t, n);
default:
gcc_unreachable ();
return NULL;
}
}
/* Attempt to evaluate T which represents a call to a builtin function.
We assume here that all builtin functions evaluate to scalar types
represented by _CST nodes. */
static tree
cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
bool lval,
bool *non_constant_p, bool *overflow_p)
{
const int nargs = call_expr_nargs (t);
tree *args = (tree *) alloca (nargs * sizeof (tree));
tree new_call;
int i;
/* Don't fold __builtin_constant_p within a constexpr function. */
bool bi_const_p = DECL_IS_BUILTIN_CONSTANT_P (fun);
/* If we aren't requiring a constant expression, defer __builtin_constant_p
in a constexpr function until we have values for the parameters. */
if (bi_const_p
&& !ctx->manifestly_const_eval
&& current_function_decl
&& DECL_DECLARED_CONSTEXPR_P (current_function_decl))
{
*non_constant_p = true;
return t;
}
/* For __builtin_is_constant_evaluated, defer it if not
ctx->manifestly_const_eval (as sometimes we try to constant evaluate
without manifestly_const_eval even expressions or parts thereof which
will later be manifestly const_eval evaluated), otherwise fold it to
true. */
if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
BUILT_IN_FRONTEND))
{
if (!ctx->manifestly_const_eval)
{
*non_constant_p = true;
return t;
}
return boolean_true_node;
}
if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND))
{
temp_override<tree> ovr (current_function_decl);
if (ctx->call && ctx->call->fundef)
current_function_decl = ctx->call->fundef->decl;
return fold_builtin_source_location (EXPR_LOCATION (t));
}
int strops = 0;
int strret = 0;
if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
switch (DECL_FUNCTION_CODE (fun))
{
case BUILT_IN_STRLEN:
case BUILT_IN_STRNLEN:
strops = 1;
break;
case BUILT_IN_MEMCHR:
case BUILT_IN_STRCHR:
case BUILT_IN_STRRCHR:
strops = 1;
strret = 1;
break;
case BUILT_IN_MEMCMP:
case BUILT_IN_STRCMP:
strops = 2;
break;
case BUILT_IN_STRSTR:
strops = 2;
strret = 1;
break;
case BUILT_IN_ASAN_POINTER_COMPARE:
case BUILT_IN_ASAN_POINTER_SUBTRACT:
/* These builtins shall be ignored during constant expression
evaluation. */
return void_node;
default:
break;
}
/* Be permissive for arguments to built-ins; __builtin_constant_p should
return constant false for a non-constant argument. */
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
for (i = 0; i < nargs; ++i)
{
tree arg = CALL_EXPR_ARG (t, i);
tree oarg = arg;
/* To handle string built-ins we need to pass ADDR_EXPR<STRING_CST> since
expand_builtin doesn't know how to look in the values table. */
bool strop = i < strops;
if (strop)
{
STRIP_NOPS (arg);
if (TREE_CODE (arg) == ADDR_EXPR)
arg = TREE_OPERAND (arg, 0);
else
strop = false;
}
/* If builtin_valid_in_constant_expr_p is true,
potential_constant_expression_1 has not recursed into the arguments
of the builtin, verify it here. */
if (!builtin_valid_in_constant_expr_p (fun)
|| potential_constant_expression (arg))
{
bool dummy1 = false, dummy2 = false;
arg = cxx_eval_constant_expression (&new_ctx, arg, false,
&dummy1, &dummy2);
}
if (bi_const_p)
/* For __builtin_constant_p, fold all expressions with constant values
even if they aren't C++ constant-expressions. */
arg = cp_fold_rvalue (arg);
else if (strop)
{
if (TREE_CODE (arg) == CONSTRUCTOR)
arg = braced_lists_to_strings (TREE_TYPE (arg), arg);
if (TREE_CODE (arg) == STRING_CST)
arg = build_address (arg);
else
arg = oarg;
}
args[i] = arg;
}
bool save_ffbcp = force_folding_builtin_constant_p;
force_folding_builtin_constant_p |= ctx->manifestly_const_eval;
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;
if (fndecl_built_in_p (fun,
CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
BUILT_IN_FRONTEND))
{
location_t loc = EXPR_LOCATION (t);
if (nargs >= 1)
VERIFY_CONSTANT (args[0]);
new_call
= fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs,
args);
}
else if (fndecl_built_in_p (fun,
CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
BUILT_IN_FRONTEND))
{
location_t loc = EXPR_LOCATION (t);
if (nargs >= 2)
{
VERIFY_CONSTANT (args[0]);
VERIFY_CONSTANT (args[1]);
}
new_call = fold_builtin_is_corresponding_member (loc, nargs, args);
}
else
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)
{
if (!*non_constant_p && !ctx->quiet)
{
/* Do not allow__builtin_unreachable in constexpr function.
The __builtin_unreachable call with BUILTINS_LOCATION
comes from cp_maybe_instrument_return. */
if (fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE)
&& EXPR_LOCATION (t) == BUILTINS_LOCATION)
error ("%<constexpr%> call flows off the end of the function");
else
{
new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t),
CALL_EXPR_FN (t), nargs, args);
error ("%q+E is not a constant expression", new_call);
}
}
*non_constant_p = true;
return t;
}
if (!potential_constant_expression (new_call))
{
if (!*non_constant_p && !ctx->quiet)
error ("%q+E is not a constant expression", new_call);
*non_constant_p = true;
return t;
}
if (strret)
{
/* memchr returns a pointer into the first argument, but we replaced the
argument above with a STRING_CST; put it back it now. */
tree op = CALL_EXPR_ARG (t, strret-1);
STRIP_NOPS (new_call);
if (TREE_CODE (new_call) == POINTER_PLUS_EXPR)
TREE_OPERAND (new_call, 0) = op;
else if (TREE_CODE (new_call) == ADDR_EXPR)
new_call = op;
}
return cxx_eval_constant_expression (&new_ctx, new_call, lval,
non_constant_p, overflow_p);
}
/* TEMP is the constant value of a temporary object of type TYPE. Adjust
the type of the value to match. */
static tree
adjust_temp_type (tree type, tree temp)
{
if (same_type_p (TREE_TYPE (temp), type))
return temp;
/* Avoid wrapping an aggregate value in a NOP_EXPR. */
if (TREE_CODE (temp) == CONSTRUCTOR)
{
/* build_constructor wouldn't retain various CONSTRUCTOR flags. */
tree t = copy_node (temp);
TREE_TYPE (t) = type;
return t;
}
if (TREE_CODE (temp) == EMPTY_CLASS_EXPR)
return build0 (EMPTY_CLASS_EXPR, type);
gcc_assert (scalarish_type_p (type));
/* Now we know we're dealing with a scalar, and a prvalue of non-class
type is cv-unqualified. */
return cp_fold_convert (cv_unqualified (type), temp);
}
/* If T is a CONSTRUCTOR, return an unshared copy of T and any
sub-CONSTRUCTORs. Otherwise return T.
We use this whenever we initialize an object as a whole, whether it's a
parameter, a local variable, or a subobject, so that subsequent
modifications don't affect other places where it was used. */
tree
unshare_constructor (tree t MEM_STAT_DECL)
{
if (!t || TREE_CODE (t) != CONSTRUCTOR)
return t;
auto_vec <tree*, 4> ptrs;
ptrs.safe_push (&t);
while (!ptrs.is_empty ())
{
tree *p = ptrs.pop ();
tree n = copy_node (*p PASS_MEM_STAT);
CONSTRUCTOR_ELTS (n) = vec_safe_copy (CONSTRUCTOR_ELTS (*p) PASS_MEM_STAT);
*p = n;
vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (n);
constructor_elt *ce;
for (HOST_WIDE_INT i = 0; vec_safe_iterate (v, i, &ce); ++i)
if (ce->value && TREE_CODE (ce->value) == CONSTRUCTOR)
ptrs.safe_push (&ce->value);
}
return t;
}
/* If T is a CONSTRUCTOR, ggc_free T and any sub-CONSTRUCTORs. */
static void
free_constructor (tree t)
{
if (!t || TREE_CODE (t) != CONSTRUCTOR)
return;
releasing_vec ctors;
vec_safe_push (ctors, t);
while (!ctors->is_empty ())
{
tree c = ctors->pop ();
if (vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (c))
{
constructor_elt *ce;
for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
if (TREE_CODE (ce->value) == CONSTRUCTOR)
vec_safe_push (ctors, ce->value);
ggc_free (elts);
}
ggc_free (c);
}
}
/* Helper function of cxx_bind_parameters_in_call. Return non-NULL
if *TP is address of a static variable (or part of it) currently being
constructed or of a heap artificial variable. */
static tree
addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data)
{
if (TREE_CODE (*tp) == ADDR_EXPR)
if (tree var = get_base_address (TREE_OPERAND (*tp, 0)))
if (VAR_P (var) && TREE_STATIC (var))
{
if (DECL_NAME (var) == heap_uninit_identifier
|| DECL_NAME (var) == heap_identifier
|| DECL_NAME (var) == heap_vec_uninit_identifier
|| DECL_NAME (var) == heap_vec_identifier)
return var;
constexpr_global_ctx *global = (constexpr_global_ctx *) data;
if (global->values.get (var))
return var;
}
if (TYPE_P (*tp))
*walk_subtrees = false;
return NULL_TREE;
}
/* Subroutine of cxx_eval_call_expression.
We are processing a call expression (either CALL_EXPR or
AGGR_INIT_EXPR) in the context of CTX. Evaluate
all arguments and bind their values to correspondings
parameters, making up the NEW_CALL context. */
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 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 = 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
which case we get the real argument from ctx. */
if (i == 0 && DECL_CONSTRUCTOR_P (fun)
&& is_dummy_object (x))
{
x = ctx->object;
x = build_address (x);
}
if (TREE_ADDRESSABLE (type))
/* Undo convert_for_arg_passing work here. */
x = convert_from_reference (x);
/* Normally we would strip a TARGET_EXPR in an initialization context
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 = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
non_constant_p, overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p && ctx->quiet)
break;
/* Just discard ellipsis args after checking their constantitude. */
if (!parms)
continue;
if (!*non_constant_p)
{
/* Make sure the binding has the same type as the parm. But
only for constant args. */
if (!TYPE_REF_P (type))
arg = adjust_temp_type (type, arg);
if (!TREE_CONSTANT (arg))
*non_constant_args = true;
else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
/* The destructor needs to see any modifications the callee makes
to the argument. */
*non_constant_args = true;
/* If arg is or contains address of a heap artificial variable or
of a static variable being constructed, avoid caching the
function call, as those variables might be modified by the
function, or might be modified by the callers in between
the cached function and just read by the function. */
else if (!*non_constant_args
&& cp_walk_tree (&arg, addr_of_non_const_var, ctx->global,
NULL))
*non_constant_args = true;
/* For virtual calls, adjust the this argument, so that it is
the object on which the method is called, rather than
one of its bases. */
if (i == 0 && DECL_VIRTUAL_P (fun))
{
tree addr = arg;
STRIP_NOPS (addr);
if (TREE_CODE (addr) == ADDR_EXPR)
{
tree obj = TREE_OPERAND (addr, 0);
while (TREE_CODE (obj) == COMPONENT_REF
&& DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1))
&& !same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (obj), DECL_CONTEXT (fun)))
obj = TREE_OPERAND (obj, 0);
if (obj != TREE_OPERAND (addr, 0))
arg = build_fold_addr_expr_with_type (obj,
TREE_TYPE (arg));
}
}
TREE_VEC_ELT (binds, i) = arg;
}
parms = TREE_CHAIN (parms);
}
return binds;
}
/* Variables and functions to manage constexpr call expansion context.
These do not need to be marked for PCH or GC. */
/* FIXME remember and print actual constant arguments. */
static vec<tree> call_stack;
static int call_stack_tick;
static int last_cx_error_tick;
static int
push_cx_call_context (tree call)
{
++call_stack_tick;
if (!EXPR_HAS_LOCATION (call))
SET_EXPR_LOCATION (call, input_location);
call_stack.safe_push (call);
int len = call_stack.length ();
if (len > max_constexpr_depth)
return false;
return len;
}
static void
pop_cx_call_context (void)
{
++call_stack_tick;
call_stack.pop ();
}
vec<tree>
cx_error_context (void)
{
vec<tree> r = vNULL;
if (call_stack_tick != last_cx_error_tick
&& !call_stack.is_empty ())
r = call_stack;
last_cx_error_tick = call_stack_tick;
return r;
}
/* Evaluate a call T to a GCC internal function when possible and return
the evaluated result or, under the control of CTX, give an error, set
NON_CONSTANT_P, and return the unevaluated call T otherwise. */
static tree
cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
bool lval,
bool *non_constant_p, bool *overflow_p)
{
enum tree_code opcode = ERROR_MARK;
switch (CALL_EXPR_IFN (t))
{
case IFN_UBSAN_NULL:
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
case IFN_FALLTHROUGH:
return void_node;
case IFN_ADD_OVERFLOW:
opcode = PLUS_EXPR;
break;
case IFN_SUB_OVERFLOW:
opcode = MINUS_EXPR;
break;
case IFN_MUL_OVERFLOW:
opcode = MULT_EXPR;
break;
case IFN_LAUNDER:
return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
false, non_constant_p, overflow_p);
case IFN_VEC_CONVERT:
{
tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
false, non_constant_p,
overflow_p);
if (TREE_CODE (arg) == VECTOR_CST)
if (tree r = fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg))
return r;
}
/* FALLTHRU */
default:
if (!ctx->quiet)
error_at (cp_expr_loc_or_input_loc (t),
"call to internal function %qE", t);
*non_constant_p = true;
return t;
}
/* Evaluate constant arguments using OPCODE and return a complex
number containing the result and the overflow bit. */
tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
non_constant_p, overflow_p);
tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
non_constant_p, overflow_p);
if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
{
location_t loc = cp_expr_loc_or_input_loc (t);
tree type = TREE_TYPE (TREE_TYPE (t));
tree result = fold_binary_loc (loc, opcode, type,
fold_convert_loc (loc, type, arg0),
fold_convert_loc (loc, type, arg1));
tree ovf
= build_int_cst (type, arith_overflowed_p (opcode, type, arg0, arg1));
/* Reset TREE_OVERFLOW to avoid warnings for the overflow. */
if (TREE_OVERFLOW (result))
TREE_OVERFLOW (result) = 0;
return build_complex (TREE_TYPE (t), result, ovf);
}
*non_constant_p = true;
return t;
}
/* Clean CONSTRUCTOR_NO_CLEARING from CTOR and its sub-aggregates. */
static void
clear_no_implicit_zero (tree ctor)
{
if (CONSTRUCTOR_NO_CLEARING (ctor))
{
CONSTRUCTOR_NO_CLEARING (ctor) = false;
for (auto &e: CONSTRUCTOR_ELTS (ctor))
if (TREE_CODE (e.value) == CONSTRUCTOR)
clear_no_implicit_zero (e.value);
}
}
/* Complain about a const object OBJ being modified in a constant expression.
EXPR is the MODIFY_EXPR expression performing the modification. */
static void
modifying_const_object_error (tree expr, tree obj)
{
location_t loc = cp_expr_loc_or_input_loc (expr);
auto_diagnostic_group d;
error_at (loc, "modifying a const object %qE is not allowed in "
"a constant expression", TREE_OPERAND (expr, 0));
inform (location_of (obj), "originally declared %<const%> here");
}
/* Return true if FNDECL is a replaceable global allocation function that
should be useable during constant expression evaluation. */
static inline bool
cxx_replaceable_global_alloc_fn (tree fndecl)
{
return (cxx_dialect >= cxx20
&& IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fndecl))
&& CP_DECL_CONTEXT (fndecl) == global_namespace
&& (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fndecl)
|| DECL_IS_OPERATOR_DELETE_P (fndecl)));
}
/* Return true if FNDECL is a placement new function that should be
useable during constant expression evaluation of std::construct_at. */
static inline bool
cxx_placement_new_fn (tree fndecl)
{
if (cxx_dialect >= cxx20
&& IDENTIFIER_NEW_OP_P (DECL_NAME (fndecl))
&& CP_DECL_CONTEXT (fndecl) == global_namespace
&& !DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fndecl)
&& TREE_CODE (TREE_TYPE (fndecl)) == FUNCTION_TYPE)
{
tree first_arg = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
if (TREE_VALUE (first_arg) == ptr_type_node
&& TREE_CHAIN (first_arg) == void_list_node)
return true;
}
return false;
}
/* Return true if FNDECL is std::construct_at. */
static inline bool
is_std_construct_at (tree fndecl)
{
if (!decl_in_std_namespace_p (fndecl))
return false;
tree name = DECL_NAME (fndecl);
return name && id_equal (name, "construct_at");
}
/* Overload for the above taking constexpr_call*. */
static inline bool
is_std_construct_at (const constexpr_call *call)
{
return (call
&& call->fundef
&& is_std_construct_at (call->fundef->decl));
}
/* Return true if FNDECL is std::allocator<T>::{,de}allocate. */
static inline bool
is_std_allocator_allocate (tree fndecl)
{
tree name = DECL_NAME (fndecl);
if (name == NULL_TREE
|| !(id_equal (name, "allocate") || id_equal (name, "deallocate")))
return false;
tree ctx = DECL_CONTEXT (fndecl);
if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx))
return false;
tree decl = TYPE_MAIN_DECL (ctx);
name = DECL_NAME (decl);
if (name == NULL_TREE || !id_equal (name, "allocator"))
return false;
return decl_in_std_namespace_p (decl);
}
/* Overload for the above taking constexpr_call*. */
static inline bool
is_std_allocator_allocate (const constexpr_call *call)
{
return (call
&& call->fundef
&& is_std_allocator_allocate (call->fundef->decl));
}
/* Return true if FNDECL is __dynamic_cast. */
static inline bool
cxx_dynamic_cast_fn_p (tree fndecl)
{
return (cxx_dialect >= cxx20
&& id_equal (DECL_NAME (fndecl), "__dynamic_cast")
&& CP_DECL_CONTEXT (fndecl) == global_namespace);
}
/* Often, we have an expression in the form of address + offset, e.g.
"&_ZTV1A + 16". Extract the object from it, i.e. "_ZTV1A". */
static tree
extract_obj_from_addr_offset (tree expr)
{
if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
expr = TREE_OPERAND (expr, 0);
STRIP_NOPS (expr);
if (TREE_CODE (expr) == ADDR_EXPR)
expr = TREE_OPERAND (expr, 0);
return expr;
}
/* Given a PATH like
g.D.2181.D.2154.D.2102.D.2093
find a component with type TYPE. Return NULL_TREE if not found, and
error_mark_node if the component is not accessible. If STOP is non-null,
this function will return NULL_TREE if STOP is found before TYPE. */
static tree
get_component_with_type (tree path, tree type, tree stop)
{
while (true)
{
if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
/* Found it. */
return path;
else if (stop
&& (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path),
stop)))
return NULL_TREE;
else if (TREE_CODE (path) == COMPONENT_REF
&& DECL_FIELD_IS_BASE (TREE_OPERAND (path, 1)))
{
/* We need to check that the component we're accessing is in fact
accessible. */
if (TREE_PRIVATE (TREE_OPERAND (path, 1))
|| TREE_PROTECTED (TREE_OPERAND (path, 1)))
return error_mark_node;
path = TREE_OPERAND (path, 0);
}
else
return NULL_TREE;
}
}
/* Evaluate a call to __dynamic_cast (permitted by P1327R1).
The declaration of __dynamic_cast is:
void* __dynamic_cast (const void* __src_ptr,
const __class_type_info* __src_type,
const __class_type_info* __dst_type,
ptrdiff_t __src2dst);
where src2dst has the following possible values
>-1: src_type is a unique public non-virtual base of dst_type
dst_ptr + src2dst == src_ptr
-1: unspecified relationship
-2: src_type is not a public base of dst_type
-3: src_type is a multiple public non-virtual base of dst_type
Since literal types can't have virtual bases, we only expect hint >=0,
-2, or -3. */
static tree
cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
bool *non_constant_p, bool *overflow_p)
{
/* T will be something like
__dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
dismantle it. */
gcc_assert (call_expr_nargs (call) == 4);
tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error;
tree obj = CALL_EXPR_ARG (call, 0);
tree type = CALL_EXPR_ARG (call, 2);
HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3));
location_t loc = cp_expr_loc_or_input_loc (call);
/* Get the target type of the dynamic_cast. */
gcc_assert (TREE_CODE (type) == ADDR_EXPR);
type = TREE_OPERAND (type, 0);
type = TREE_TYPE (DECL_NAME (type));
/* TYPE can only be either T* or T&. We can't know which of these it
is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
and something like "(T*)(T&)(T*) x" in the second case. */
bool reference_p = false;
while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
{
reference_p |= TYPE_REF_P (TREE_TYPE (obj));
obj = TREE_OPERAND (obj, 0);
}
/* Evaluate the object so that we know its dynamic type. */
obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p,
overflow_p);
if (*non_constant_p)
return call;
/* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
but when HINT is > 0, it can also be something like
&d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET. */
obj = extract_obj_from_addr_offset (obj);
const tree objtype = TREE_TYPE (obj);
/* If OBJ doesn't refer to a base field, we're done. */
if (tree t = (TREE_CODE (obj) == COMPONENT_REF
? TREE_OPERAND (obj, 1) : obj))
if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
{
if (reference_p)
{
if (!ctx->quiet)
{
error_at (loc, "reference %<dynamic_cast%> failed");
inform (loc, "dynamic type %qT of its operand does "
"not have a base class of type %qT",
objtype, type);
}
*non_constant_p = true;
}
return integer_zero_node;
}
/* [class.cdtor] When a dynamic_cast is used in a constructor ...
or in a destructor ... if the operand of the dynamic_cast refers
to the object under construction or destruction, this object is
considered to be a most derived object that has the type of the
constructor or destructor's class. */
tree vtable = build_vfield_ref (obj, objtype);
vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
non_constant_p, overflow_p);
if (*non_constant_p)
return call;
/* With -fsanitize=vptr, we initialize all vtable pointers to null,
so it's possible that we got a null pointer now. */
if (integer_zerop (vtable))
{
if (!ctx->quiet)
error_at (loc, "virtual table pointer is used uninitialized");
*non_constant_p = true;
return integer_zero_node;
}
/* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A. */
vtable = extract_obj_from_addr_offset (vtable);
const tree mdtype = DECL_CONTEXT (vtable);
/* Given dynamic_cast<T>(v),
[expr.dynamic.cast] If C is the class type to which T points or refers,
the runtime check logically executes as follows:
If, in the most derived object pointed (referred) to by v, v points
(refers) to a public base class subobject of a C object, and if only
one object of type C is derived from the subobject pointed (referred)
to by v the result points (refers) to that C object.
In this case, HINT >= 0 or -3. */
if (hint >= 0 || hint == -3)
{
/* Look for a component with type TYPE. */
tree t = get_component_with_type (obj, type, mdtype);
/* If not accessible, give an error. */
if (t == error_mark_node)
{
if (reference_p)
{
if (!ctx->quiet)
{
error_at (loc, "reference %<dynamic_cast%> failed");
inform (loc, "static type %qT of its operand is a "
"non-public base class of dynamic type %qT",
objtype, type);
}
*non_constant_p = true;
}
return integer_zero_node;
}
else if (t)
/* The result points to the TYPE object. */
return cp_build_addr_expr (t, complain);
/* Else, TYPE was not found, because the HINT turned out to be wrong.
Fall through to the normal processing. */
}
/* Otherwise, if v points (refers) to a public base class subobject of the
most derived object, and the type of the most derived object has a base
class, of type C, that is unambiguous and public, the result points
(refers) to the C subobject of the most derived object.
But it can also be an invalid case. */
/* Get the most derived object. */
obj = get_component_with_type (obj, mdtype, NULL_TREE);
if (obj == error_mark_node)
{
if (reference_p)
{
if (!ctx->quiet)
{
error_at (loc, "reference %<dynamic_cast%> failed");
inform (loc, "static type %qT of its operand is a non-public"
" base class of dynamic type %qT", objtype, mdtype);
}
*non_constant_p = true;
}
return integer_zero_node;
}
else
gcc_assert (obj);
/* Check that the type of the most derived object has a base class
of type TYPE that is unambiguous and public. */
base_kind b_kind;
tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none);
if (!binfo || binfo == error_mark_node)
{
if (reference_p)
{
if (!ctx->quiet)
{
error_at (loc, "reference %<dynamic_cast%> failed");
if (b_kind == bk_ambig)
inform (loc, "%qT is an ambiguous base class of dynamic "
"type %qT of its operand", type, mdtype);
else
inform (loc, "dynamic type %qT of its operand does not "
"have an unambiguous public base class %qT",
mdtype, type);
}
*non_constant_p = true;
}
return integer_zero_node;
}
/* If so, return the TYPE subobject of the most derived object. */
obj = convert_to_base_statically (obj, binfo);
return cp_build_addr_expr (obj, complain);
}
/* Data structure used by replace_decl and replace_decl_r. */
struct replace_decl_data
{
/* The _DECL we want to replace. */
tree decl;
/* The replacement for DECL. */
tree replacement;
/* Trees we've visited. */
hash_set<tree> *pset;
/* Whether we've performed any replacements. */
bool changed;
};
/* Helper function for replace_decl, called through cp_walk_tree. */
static tree
replace_decl_r (tree *tp, int *walk_subtrees, void *data)
{
replace_decl_data *d = (replace_decl_data *) data;
if (*tp == d->decl)
{
*tp = unshare_expr (d->replacement);
d->changed = true;
*walk_subtrees = 0;
}
else if (TYPE_P (*tp)
|| d->pset->add (*tp))
*walk_subtrees = 0;
return NULL_TREE;
}
/* Replace every occurrence of DECL with (an unshared copy of)
REPLACEMENT within the expression *TP. Returns true iff a
replacement was performed. */
bool
replace_decl (tree *tp, tree decl, tree replacement)
{
gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (decl), TREE_TYPE (replacement)));
hash_set<tree> pset;
replace_decl_data data = { decl, replacement, &pset, false };
cp_walk_tree (tp, replace_decl_r, &data, NULL);
return data.changed;
}
/* Evaluate the call T to virtual function thunk THUNK_FNDECL. */
static tree
cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
bool lval,
bool *non_constant_p, bool *overflow_p)
{
tree function = THUNK_TARGET (thunk_fndecl);
if (THUNK_VIRTUAL_OFFSET (thunk_fndecl))
{
if (!ctx->quiet)
{
if (!DECL_DECLARED_CONSTEXPR_P (function))
{
error ("call to non-%<constexpr%> function %qD", function);
explain_invalid_constexpr_fn (function);
}
else
/* virtual_offset is only set for virtual bases, which make the
class non-literal, so we don't need to handle it here. */
error ("calling constexpr member function %qD through virtual "
"base subobject", function);
}
*non_constant_p = true;
return t;
}
tree new_call = copy_node (t);
CALL_EXPR_FN (new_call) = function;
TREE_TYPE (new_call) = TREE_TYPE (TREE_TYPE (function));
tree offset = size_int (THUNK_FIXED_OFFSET (thunk_fndecl));
if (DECL_THIS_THUNK_P (thunk_fndecl))
{
/* 'this'-adjusting thunk. */
tree this_arg = CALL_EXPR_ARG (t, 0);
this_arg = build2 (POINTER_PLUS_EXPR, TREE_TYPE (this_arg),
this_arg, offset);
CALL_EXPR_ARG (new_call, 0) = this_arg;
}
else
/* Return-adjusting thunk. */
new_call = build2 (POINTER_PLUS_EXPR, TREE_TYPE (new_call),
new_call, offset);
return cxx_eval_constant_expression (ctx, new_call, lval,
non_constant_p, overflow_p);
}
/* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set
its TREE_READONLY flag according to READONLY_P. Used for constexpr
'tors to detect modifying const objects in a constexpr context. */
static void
cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
bool readonly_p, bool *non_constant_p,
bool *overflow_p)
{
if (CLASS_TYPE_P (TREE_TYPE (object))
&& CP_TYPE_CONST_P (TREE_TYPE (object)))
{
/* Subobjects might not be stored in ctx->global->values but we
can get its CONSTRUCTOR by evaluating *this. */
tree e = cxx_eval_constant_expression (ctx, object, /*lval*/false,
non_constant_p, overflow_p);
if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p)
TREE_READONLY (e) = readonly_p;
}
}
/* Subroutine of cxx_eval_constant_expression.
Evaluate the call expression tree T in the context of OLD_CALL expression
evaluation. */
static tree
cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
bool lval,
bool *non_constant_p, bool *overflow_p)
{
/* Handle concept checks separately. */
if (concept_check_p (t))
return evaluate_concept_check (t);
location_t loc = cp_expr_loc_or_input_loc (t);
tree fun = get_function_named_in_call (t);
constexpr_call new_call
= { NULL, NULL, NULL, 0, ctx->manifestly_const_eval };
int depth_ok;
if (fun == NULL_TREE)
return cxx_eval_internal_function (ctx, t, lval,
non_constant_p, overflow_p);
if (TREE_CODE (fun) != FUNCTION_DECL)
{
/* Might be a constexpr function pointer. */
fun = cxx_eval_constant_expression (ctx, fun,
/*lval*/false, non_constant_p,
overflow_p);
STRIP_NOPS (fun);
if (TREE_CODE (fun) == ADDR_EXPR)
fun = TREE_OPERAND (fun, 0);
/* For TARGET_VTABLE_USES_DESCRIPTORS targets, there is no
indirection, the called expression is a pointer into the
virtual table which should contain FDESC_EXPR. Extract the
FUNCTION_DECL from there. */
else if (TARGET_VTABLE_USES_DESCRIPTORS
&& TREE_CODE (fun) == POINTER_PLUS_EXPR
&& TREE_CODE (TREE_OPERAND (fun, 0)) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fun, 1)) == INTEGER_CST)
{
tree d = TREE_OPERAND (TREE_OPERAND (fun, 0), 0);
if (VAR_P (d)
&& DECL_VTABLE_OR_VTT_P (d)
&& TREE_CODE (TREE_TYPE (d)) == ARRAY_TYPE
&& TREE_TYPE (TREE_TYPE (d)) == vtable_entry_type
&& DECL_INITIAL (d)
&& TREE_CODE (DECL_INITIAL (d)) == CONSTRUCTOR)
{
tree i = int_const_binop (TRUNC_DIV_EXPR, TREE_OPERAND (fun, 1),
TYPE_SIZE_UNIT (vtable_entry_type));
HOST_WIDE_INT idx = find_array_ctor_elt (DECL_INITIAL (d), i);
if (idx >= 0)
{
tree fdesc
= (*CONSTRUCTOR_ELTS (DECL_INITIAL (d)))[idx].value;
if (TREE_CODE (fdesc) == FDESC_EXPR
&& integer_zerop (TREE_OPERAND (fdesc, 1)))
fun = TREE_OPERAND (fdesc, 0);
}
}
}
}
if (TREE_CODE (fun) != FUNCTION_DECL)
{
if (!ctx->quiet && !*non_constant_p)
error_at (loc, "expression %qE does not designate a %<constexpr%> "
"function", fun);
*non_constant_p = true;
return t;
}
if (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun))
fun = DECL_CLONED_FUNCTION (fun);
if (is_ubsan_builtin_p (fun))
return void_node;
if (fndecl_built_in_p (fun))
return cxx_eval_builtin_function_call (ctx, t, fun,
lval, non_constant_p, overflow_p);
if (DECL_THUNK_P (fun))
return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p);
if (!maybe_constexpr_fn (fun))
{
if (TREE_CODE (t) == CALL_EXPR
&& cxx_replaceable_global_alloc_fn (fun)
&& (CALL_FROM_NEW_OR_DELETE_P (t)
|| is_std_allocator_allocate (ctx->call)))
{
const int nargs = call_expr_nargs (t);
tree arg0 = NULL_TREE;
for (int i = 0; i < nargs; ++i)
{
tree arg = CALL_EXPR_ARG (t, i);
arg = cxx_eval_constant_expression (ctx, arg, false,
non_constant_p, overflow_p);
VERIFY_CONSTANT (arg);
if (i == 0)
arg0 = arg;
}
gcc_assert (arg0);
if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
{
tree type = build_array_type_nelts (char_type_node,
tree_to_uhwi (arg0));
tree var = build_decl (loc, VAR_DECL,
(IDENTIFIER_OVL_OP_FLAGS (DECL_NAME (fun))
& OVL_OP_FLAG_VEC)
? heap_vec_uninit_identifier
: heap_uninit_identifier,
type);
DECL_ARTIFICIAL (var) = 1;
TREE_STATIC (var) = 1;
// Temporarily register the artificial var in varpool,
// so that comparisons of its address against NULL are folded
// through nonzero_address even with
// -fno-delete-null-pointer-checks or that comparison of
// addresses of different heap artificial vars is folded too.
// See PR98988 and PR99031.
varpool_node::finalize_decl (var);
ctx->global->heap_vars.safe_push (var);
ctx->global->values.put (var, NULL_TREE);
return fold_convert (ptr_type_node, build_address (var));
}
else
{
STRIP_NOPS (arg0);
if (TREE_CODE (arg0) == ADDR_EXPR
&& VAR_P (TREE_OPERAND (arg0, 0)))
{
tree var = TREE_OPERAND (arg0, 0);
if (DECL_NAME (var) == heap_uninit_identifier
|| DECL_NAME (var) == heap_identifier)
{
if (IDENTIFIER_OVL_OP_FLAGS (DECL_NAME (fun))
& OVL_OP_FLAG_VEC)
{
if (!ctx->quiet)
{
error_at (loc, "array deallocation of object "
"allocated with non-array "
"allocation");
inform (DECL_SOURCE_LOCATION (var),
"allocation performed here");
}
*non_constant_p = true;
return t;
}
DECL_NAME (var) = heap_deleted_identifier;
ctx->global->values.remove (var);
ctx->global->heap_dealloc_count++;
return void_node;
}
else if (DECL_NAME (var) == heap_vec_uninit_identifier
|| DECL_NAME (var) == heap_vec_identifier)
{
if ((IDENTIFIER_OVL_OP_FLAGS (DECL_NAME (fun))
& OVL_OP_FLAG_VEC) == 0)
{
if (!ctx->quiet)
{
error_at (loc, "non-array deallocation of "
"object allocated with array "
"allocation");
inform (DECL_SOURCE_LOCATION (var),
"allocation performed here");
}
*non_constant_p = true;
return t;
}
DECL_NAME (var) = heap_deleted_identifier;
ctx->global->values.remove (var);
ctx->global->heap_dealloc_count++;
return void_node;
}
else if (DECL_NAME (var) == heap_deleted_identifier)
{
if (!ctx->quiet)
error_at (loc, "deallocation of already deallocated "
"storage");
*non_constant_p = true;
return t;
}
}
if (!ctx->quiet)
error_at (loc, "deallocation of storage that was "
"not previously allocated");
*non_constant_p = true;
return t;
}
}
/* Allow placement new in std::construct_at, just return the second
argument. */
if (TREE_CODE (t) == CALL_EXPR
&& cxx_placement_new_fn (fun)
&& is_std_construct_at (ctx->call))
{
const int nargs = call_expr_nargs (t);
tree arg1 = NULL_TREE;
for (int i = 0; i < nargs; ++i)
{
tree arg = CALL_EXPR_ARG (t, i);
arg = cxx_eval_constant_expression (ctx, arg, false,
non_constant_p, overflow_p);
if (i == 1)
arg1 = arg;
else
VERIFY_CONSTANT (arg);
}
gcc_assert (arg1);
return arg1;
}
else if (cxx_dynamic_cast_fn_p (fun))
return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
if (!ctx->quiet)
{
if (!lambda_static_thunk_p (fun))
error_at (loc, "call to non-%<constexpr%> function %qD", fun);
explain_invalid_constexpr_fn (fun);
}
*non_constant_p = true;
return t;
}
constexpr_ctx new_ctx = *ctx;
if (DECL_CONSTRUCTOR_P (fun) && !ctx->object
&& TREE_CODE (t) == AGGR_INIT_EXPR)
{
/* We want to have an initialization target for an AGGR_INIT_EXPR.
If we don't already have one in CTX, use the AGGR_INIT_EXPR_SLOT. */
new_ctx.object = AGGR_INIT_EXPR_SLOT (t);
tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
CONSTRUCTOR_NO_CLEARING (ctor) = true;
ctx->global->values.put (new_ctx.object, ctor);
ctx = &new_ctx;
}
/* Shortcut trivial constructor/op=. */
if (trivial_fn_p (fun))
{
tree init = NULL_TREE;
if (call_expr_nargs (t) == 2)
init = convert_from_reference (get_nth_callarg (t, 1));
else if (TREE_CODE (t) == AGGR_INIT_EXPR
&& AGGR_INIT_ZERO_FIRST (t))
init = build_zero_init (DECL_CONTEXT (fun), NULL_TREE, false);
if (init)
{
tree op = get_nth_callarg (t, 0);
if (is_dummy_object (op))
op = ctx->object;
else
op = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (op)), op);
tree set = build2 (MODIFY_EXPR, TREE_TYPE (op), op, init);
new_ctx.call = &new_call;
return cxx_eval_constant_expression (&new_ctx, set, lval,
non_constant_p, overflow_p);
}
}
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)
&& !uid_sensitive_constexpr_evaluation_p ())
{
location_t save_loc = input_location;
input_location = loc;
++function_depth;
if (ctx->manifestly_const_eval)
FNDECL_MANIFESTLY_CONST_EVALUATED (fun) = true;
instantiate_decl (fun, /*defer_ok*/false, /*expl_inst*/false);
--function_depth;
input_location = save_loc;
}
/* If in direct recursive call, optimize definition search. */
if (ctx && ctx->call && ctx->call->fundef && ctx->call->fundef->decl == fun)
new_call.fundef = ctx->call->fundef;
else
{
new_call.fundef = retrieve_constexpr_fundef (fun);
if (new_call.fundef == NULL || new_call.fundef->body == NULL
|| new_call.fundef->result == error_mark_node
|| fun == current_function_decl)
{
if (!ctx->quiet)
{
/* We need to check for current_function_decl here in case we're
being called during cp_fold_function, because at that point
DECL_INITIAL is set properly and we have a fundef but we
haven't lowered invisirefs yet (c++/70344). */
if (DECL_INITIAL (fun) == error_mark_node
|| fun == current_function_decl)
error_at (loc, "%qD called in a constant expression before its "
"definition is complete", fun);
else if (DECL_INITIAL (fun))
{
/* The definition of fun was somehow unsuitable. But pretend
that lambda static thunks don't exist. */
if (!lambda_static_thunk_p (fun))
error_at (loc, "%qD called in a constant expression", fun);
explain_invalid_constexpr_fn (fun);
}
else
error_at (loc, "%qD used before its definition", fun);
}
*non_constant_p = true;
return t;
}
}
depth_ok = push_cx_call_context (t);
/* Remember the object we are constructing or destructing. */
tree new_obj = NULL_TREE;
if (DECL_CONSTRUCTOR_P (fun) || DECL_DESTRUCTOR_P (fun))
{
/* In a cdtor, it should be the first `this' argument.
At this point it has already been evaluated in the call
to cxx_bind_parameters_in_call. */
new_obj = TREE_VEC_ELT (new_call.bindings, 0);
STRIP_NOPS (new_obj);
if (TREE_CODE (new_obj) == ADDR_EXPR)
new_obj = TREE_OPERAND (new_obj, 0);
if (ctx->call && ctx->call->fundef
&& DECL_CONSTRUCTOR_P (ctx->call->fundef->decl))
{
tree cur_obj = TREE_VEC_ELT (ctx->call->bindings, 0);
STRIP_NOPS (cur_obj);
if (TREE_CODE (cur_obj) == ADDR_EXPR)
cur_obj = TREE_OPERAND (cur_obj, 0);
if (new_obj == cur_obj)
/* We're calling the target constructor of a delegating
constructor, or accessing a base subobject through a
NOP_EXPR as part of a call to a base constructor, so
there is no new (sub)object. */
new_obj = NULL_TREE;
}
}
tree result = NULL_TREE;
constexpr_call *entry = NULL;
if (depth_ok && !non_constant_args && ctx->strict)
{
new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef);
new_call.hash
= iterative_hash_template_arg (new_call.bindings, new_call.hash);
new_call.hash
= iterative_hash_object (ctx->manifestly_const_eval, new_call.hash);
/* If we have seen this call before, we are done. */
maybe_initialize_constexpr_call_table ();
constexpr_call **slot
= constexpr_call_table->find_slot (&new_call, INSERT);
entry = *slot;
if (entry == NULL)
{
/* Only cache up to constexpr_cache_depth to limit memory use. */
if (depth_ok < constexpr_cache_depth)
{
/* We need to keep a pointer to the entry, not just the slot, as
the slot can move during evaluation of the body. */
*slot = entry = ggc_alloc<constexpr_call> ();
*entry = new_call;
fb.preserve ();
}
}
/* Calls that are in progress have their result set to NULL, so that we
can detect circular dependencies. Now that we only cache up to
constexpr_cache_depth this won't catch circular dependencies that
start deeper, but they'll hit the recursion or ops limit. */
else if (entry->result == NULL)
{
if (!ctx->quiet)
error ("call has circular dependency");
*non_constant_p = true;
entry->result = result = error_mark_node;
}
else
result = entry->result;
}
if (!depth_ok)
{
if (!ctx->quiet)
error ("%<constexpr%> evaluation depth exceeds maximum of %d (use "
"%<-fconstexpr-depth=%> to increase the maximum)",
max_constexpr_depth);
*non_constant_p = true;
result = error_mark_node;
}
else
{
bool cacheable = true;
if (result && result != error_mark_node)
/* OK */;
else if (!DECL_SAVED_TREE (fun))
{
/* When at_eof >= 2, cgraph has started throwing away
DECL_SAVED_TREE, so fail quietly. FIXME we get here because of
late code generation for VEC_INIT_EXPR, which needs to be
completely reconsidered. */
gcc_assert (at_eof >= 2 && ctx->quiet);
*non_constant_p = true;
}
else if (tree copy = get_fundef_copy (new_call.fundef))
{
tree body, parms, res;
releasing_vec ctors;
/* Reuse or create a new unshared copy of this function's body. */
body = TREE_PURPOSE (copy);
parms = TREE_VALUE (copy);
res = TREE_TYPE (copy);
/* Associate the bindings with the remapped parms. */
tree bound = new_call.bindings;
tree remapped = parms;
for (int i = 0; i < TREE_VEC_LENGTH (bound); ++i)
{
tree arg = TREE_VEC_ELT (bound, i);
if (entry)
{
/* Unshare args going into the hash table to separate them
from the caller's context, for better GC and to avoid
problems with verify_gimple. */
arg = unshare_expr_without_location (arg);
TREE_VEC_ELT (bound, i) = arg;
/* And then unshare again so the callee doesn't change the
argument values in the hash table. XXX Could we unshare
lazily in cxx_eval_store_expression? */
arg = unshare_constructor (arg);
if (TREE_CODE (arg) == CONSTRUCTOR)
vec_safe_push (ctors, arg);
}
ctx->global->values.put (remapped, arg);
remapped = DECL_CHAIN (remapped);
}
/* Add the RESULT_DECL to the values map, too. */
gcc_assert (!DECL_BY_REFERENCE (res));
ctx->global->values.put (res, NULL_TREE);
/* Track the callee's evaluated SAVE_EXPRs and TARGET_EXPRs so that
we can forget their values after the call. */
constexpr_ctx ctx_with_save_exprs = *ctx;
auto_vec<tree, 10> save_exprs;
ctx_with_save_exprs.save_exprs = &save_exprs;
ctx_with_save_exprs.call = &new_call;
unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
/* If this is a constexpr destructor, the object's const and volatile
semantics are no longer in effect; see [class.dtor]p5. */
if (new_obj && DECL_DESTRUCTOR_P (fun))
cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false,
non_constant_p, overflow_p);
tree jump_target = NULL_TREE;
cxx_eval_constant_expression (&ctx_with_save_exprs, body,
lval, non_constant_p, overflow_p,
&jump_target);
if (DECL_CONSTRUCTOR_P (fun))
{
/* This can be null for a subobject constructor call, in
which case what we care about is the initialization
side-effects rather than the value. We could get at the
value by evaluating *this, but we don't bother; there's
no need to put such a call in the hash table. */
result = lval ? ctx->object : ctx->ctor;
/* If we've just evaluated a subobject constructor call for an
empty union member, it might not have produced a side effect
that actually activated the union member. So produce such a
side effect now to ensure the union appears initialized. */
if (!result && new_obj
&& TREE_CODE (new_obj) == COMPONENT_REF
&& TREE_CODE (TREE_TYPE
(TREE_OPERAND (new_obj, 0))) == UNION_TYPE
&& is_really_empty_class (TREE_TYPE (new_obj),
/*ignore_vptr*/false))
{
tree activate = build2 (MODIFY_EXPR, TREE_TYPE (new_obj),
new_obj,
build_constructor (TREE_TYPE (new_obj),
NULL));
cxx_eval_constant_expression (ctx, activate, lval,
non_constant_p, overflow_p);
ggc_free (activate);
}
}
else if (VOID_TYPE_P (TREE_TYPE (res)))
result = void_node;
else
{
result = *ctx->global->values.get (res);
if (result == NULL_TREE && !*non_constant_p
&& !DECL_DESTRUCTOR_P (fun))
{
if (!ctx->quiet)
error ("%<constexpr%> call flows off the end "
"of the function");
*non_constant_p = true;
}
}
/* At this point, the object's constructor will have run, so
the object is no longer under construction, and its possible
'const' semantics now apply. Make a note of this fact by
marking the CONSTRUCTOR TREE_READONLY. */
if (new_obj && DECL_CONSTRUCTOR_P (fun))
cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true,
non_constant_p, overflow_p);
/* Forget the saved values of the callee's SAVE_EXPRs and
TARGET_EXPRs. */
for (tree save_expr : save_exprs)
ctx->global->values.remove (save_expr);
/* Remove the parms/result from the values map. Is it worth
bothering to do this when the map itself is only live for
one constexpr evaluation? If so, maybe also clear out
other vars from call, maybe in BIND_EXPR handling? */
ctx->global->values.remove (res);
for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
ctx->global->values.remove (parm);
/* Free any parameter CONSTRUCTORs we aren't returning directly. */
while (!ctors->is_empty ())
{
tree c = ctors->pop ();
if (c != result)
free_constructor (c);
}
/* Make the unshared function copy we used available for re-use. */
save_fundef_copy (fun, copy);
/* If the call allocated some heap object that hasn't been
deallocated during the call, or if it deallocated some heap
object it has not allocated, the call isn't really stateless
for the constexpr evaluation and should not be cached.
It is fine if the call allocates something and deallocates it
too. */
if (entry
&& (save_heap_alloc_count != ctx->global->heap_vars.length ()
|| (save_heap_dealloc_count
!= ctx->global->heap_dealloc_count)))
{
tree heap_var;
unsigned int i;
if ((ctx->global->heap_vars.length ()
- ctx->global->heap_dealloc_count)
!= save_heap_alloc_count - save_heap_dealloc_count)
cacheable = false;
else
FOR_EACH_VEC_ELT_FROM (ctx->global->heap_vars, i, heap_var,
save_heap_alloc_count)
if (DECL_NAME (heap_var) != heap_deleted_identifier)
{
cacheable = false;
break;
}
}
/* Rewrite all occurrences of the function's RESULT_DECL with the
current object under construction. */
if (!*non_constant_p && ctx->object
&& CLASS_TYPE_P (TREE_TYPE (res))
&& !is_empty_class (TREE_TYPE (res)))
if (replace_decl (&result, res, ctx->object))
cacheable = false;
}
else
/* Couldn't get a function copy to evaluate. */
*non_constant_p = true;
if (result == error_mark_node)
*non_constant_p = true;
if (*non_constant_p || *overflow_p)
result = error_mark_node;
else if (!result)
result = void_node;
if (entry)
entry->result = cacheable ? result : error_mark_node;
}
/* The result of a constexpr function must be completely initialized.
However, in C++20, a constexpr constructor doesn't necessarily have
to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
in order to detect reading an unitialized object in constexpr instead
of value-initializing it. (reduced_constant_expression_p is expected to
take care of clearing the flag.) */
if (TREE_CODE (result) == CONSTRUCTOR
&& (cxx_dialect < cxx20
|| !DECL_CONSTRUCTOR_P (fun)))
clear_no_implicit_zero (result);
pop_cx_call_context ();
return result;
}
/* Return true if T is a valid constant initializer. If a CONSTRUCTOR
initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
cleared.
FIXME speed this up, it's taking 16% of compile time on sieve testcase. */
bool
reduced_constant_expression_p (tree t)
{
if (t == NULL_TREE)
return false;
switch (TREE_CODE (t))
{
case PTRMEM_CST:
/* Even if we can't lower this yet, it's constant. */
return true;
case CONSTRUCTOR:
/* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */
tree field;
if (CONSTRUCTOR_NO_CLEARING (t))
{
if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
/* An initialized vector would have a VECTOR_CST. */
return false;
else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
{
/* There must be a valid constant initializer at every array
index. */
tree min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
tree cursor = min;
for (auto &e: CONSTRUCTOR_ELTS (t))
{
if (!reduced_constant_expression_p (e.value))
return false;
if (array_index_cmp (cursor, e.index) != 0)
return false;
if (TREE_CODE (e.index) == RANGE_EXPR)
cursor = TREE_OPERAND (e.index, 1);
cursor = int_const_binop (PLUS_EXPR, cursor, size_one_node);
}
if (find_array_ctor_elt (t, max) == -1)
return false;
goto ok;
}
else if (cxx_dialect >= cxx20
&& TREE_CODE (TREE_TYPE (t)) == UNION_TYPE)
{
if (CONSTRUCTOR_NELTS (t) == 0)
/* An initialized union has a constructor element. */
return false;
/* And it only initializes one member. */
field = NULL_TREE;
}
else
field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
}
else
field = NULL_TREE;
for (auto &e: CONSTRUCTOR_ELTS (t))
{
/* If VAL is null, we're in the middle of initializing this
element. */
if (!reduced_constant_expression_p (e.value))
return false;
/* Empty class field may or may not have an initializer. */
for (; field && e.index != field;
field = next_initializable_field (DECL_CHAIN (field)))
if (!is_really_empty_class (TREE_TYPE (field),
/*ignore_vptr*/false))
return false;
if (field)
field = next_initializable_field (DECL_CHAIN (field));
}
/* There could be a non-empty field at the end. */
for (; field; field = next_initializable_field (DECL_CHAIN (field)))
if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
return false;
ok:
if (CONSTRUCTOR_NO_CLEARING (t))
/* All the fields are initialized. */
CONSTRUCTOR_NO_CLEARING (t) = false;
return true;
default:
/* FIXME are we calling this too much? */
return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE;
}
}
/* Some expressions may have constant operands but are not constant
themselves, such as 1/0. Call this function to check for that
condition.
We only call this in places that require an arithmetic constant, not in
places where we might have a non-constant expression that can be a
component of a constant expression, such as the address of a constexpr
variable that might be dereferenced later. */
static bool
verify_constant (tree t, bool allow_non_constant, bool *non_constant_p,
bool *overflow_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);
*non_constant_p = true;
}
if (TREE_OVERFLOW_P (t))
{
if (!allow_non_constant)
{
permerror (input_location, "overflow in constant expression");
/* If we're being permissive (and are in an enforcing
context), ignore the overflow. */
if (flag_permissive)
return *non_constant_p;
}
*overflow_p = true;
}
return *non_constant_p;
}
/* 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
cxx_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)
{