| /* 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) |
| { |
|