diff options
Diffstat (limited to 'gcc/cp/constexpr.c')
-rw-r--r-- | gcc/cp/constexpr.c | 597 |
1 files changed, 470 insertions, 127 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 7ebdd30..f7cc413 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -133,19 +133,10 @@ ensure_literal_type_for_constexpr_object (tree decl) return decl; } -/* Representation of entries in the constexpr function definition table. */ - -struct GTY((for_user)) constexpr_fundef { - tree decl; - tree body; - tree parms; - tree result; -}; - struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef> { - static hashval_t hash (constexpr_fundef *); - static bool equal (constexpr_fundef *, 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 @@ -158,7 +149,8 @@ static GTY (()) hash_table<constexpr_fundef_hasher> *constexpr_fundef_table; same constexpr function. */ inline bool -constexpr_fundef_hasher::equal (constexpr_fundef *lhs, constexpr_fundef *rhs) +constexpr_fundef_hasher::equal (const constexpr_fundef *lhs, + const constexpr_fundef *rhs) { return lhs->decl == rhs->decl; } @@ -167,20 +159,20 @@ constexpr_fundef_hasher::equal (constexpr_fundef *lhs, constexpr_fundef *rhs) Return a hash value for the entry pointed to by Q. */ inline hashval_t -constexpr_fundef_hasher::hash (constexpr_fundef *fundef) +constexpr_fundef_hasher::hash (const constexpr_fundef *fundef) { return DECL_UID (fundef->decl); } /* Return a previously saved definition of function FUN. */ -static constexpr_fundef * +constexpr_fundef * retrieve_constexpr_fundef (tree fun) { if (constexpr_fundef_table == NULL) return NULL; - constexpr_fundef fundef = { fun, NULL, NULL, NULL }; + constexpr_fundef fundef = { fun, NULL_TREE, NULL_TREE, NULL_TREE }; return constexpr_fundef_table->find (&fundef); } @@ -669,7 +661,7 @@ get_function_named_in_call (tree t) return fun; } -/* Subroutine of register_constexpr_fundef. BODY is the body of a function +/* 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. */ @@ -738,7 +730,7 @@ constexpr_fn_retval (tree body) } } -/* Subroutine of register_constexpr_fundef. BODY is the DECL_SAVED_TREE of +/* 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. */ @@ -868,27 +860,28 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain) } /* We are processing the definition of the constexpr function FUN. - Check that its BODY fulfills the propriate requirements and - enter it in the constexpr function definition table. - For constructor BODY is actually the TREE_LIST of the - member-initializer list. */ + Check that its body fulfills the apropriate requirements and + enter it in the constexpr function definition table. */ -tree -register_constexpr_fundef (tree fun, tree body) +void +maybe_save_constexpr_fundef (tree fun) { - constexpr_fundef entry; - constexpr_fundef **slot; + if (processing_template_decl + || !DECL_DECLARED_CONSTEXPR_P (fun) + || cp_function_chain->invalid_constexpr + || DECL_CLONED_FUNCTION_P (fun)) + return; if (!is_valid_constexpr_fn (fun, !DECL_GENERATED_P (fun))) - return NULL; + return; - tree massaged = massage_constexpr_body (fun, body); + tree massaged = massage_constexpr_body (fun, DECL_SAVED_TREE (fun)); if (massaged == NULL_TREE || massaged == error_mark_node) { if (!DECL_CONSTRUCTOR_P (fun)) error ("body of %<constexpr%> function %qD not a return-statement", fun); - return NULL; + return; } bool potential = potential_rvalue_constant_expression (massaged); @@ -901,39 +894,47 @@ register_constexpr_fundef (tree fun, tree body) potential = false; if (!potential && !DECL_GENERATED_P (fun)) - return NULL; - - /* Create the constexpr function table if necessary. */ - if (constexpr_fundef_table == NULL) - constexpr_fundef_table - = hash_table<constexpr_fundef_hasher>::create_ggc (101); + return; - entry.decl = fun; - tree saved_fn = current_function_decl; + constexpr_fundef entry = {fun, NULL_TREE, NULL_TREE, NULL_TREE}; bool clear_ctx = false; - current_function_decl = fun; if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE) { clear_ctx = true; DECL_CONTEXT (DECL_RESULT (fun)) = fun; } - entry.body = copy_fn (fun, entry.parms, entry.result); + 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; - slot = constexpr_fundef_table->find_slot (&entry, INSERT); if (clear_ctx) - DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE; - + 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 = entry; - - return fun; + **slot = value; } /* FUN is a non-constexpr function called in a context that requires a @@ -1331,7 +1332,12 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, } if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND)) - return fold_builtin_source_location (EXPR_LOCATION (t)); + { + 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; @@ -1518,7 +1524,7 @@ unshare_constructor (tree t MEM_STAT_DECL) 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 (TREE_CODE (ce->value) == CONSTRUCTOR) + if (ce->value && TREE_CODE (ce->value) == CONSTRUCTOR) ptrs.safe_push (&ce->value); } return t; @@ -1602,6 +1608,11 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, 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; + /* 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. */ @@ -1987,11 +1998,20 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, 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, TREE_TYPE (obj)); + 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); @@ -2181,6 +2201,27 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, 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. */ @@ -2288,7 +2329,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { tree type = build_array_type_nelts (char_type_node, tree_to_uhwi (arg0)); - tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, + 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; @@ -2306,6 +2351,42 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, 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++; @@ -2469,11 +2550,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, depth_ok = push_cx_call_context (t); - /* Remember the object we are constructing. */ + /* Remember the object we are constructing or destructing. */ tree new_obj = NULL_TREE; - if (DECL_CONSTRUCTOR_P (fun)) + if (DECL_CONSTRUCTOR_P (fun) || DECL_DESTRUCTOR_P (fun)) { - /* In a constructor, it should be the first `this' argument. + /* 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); @@ -2586,14 +2667,14 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, 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); } - /* Don't share a CONSTRUCTOR that might be changed. This is not - redundant with the unshare just above; we also don't want to - 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); } @@ -2610,6 +2691,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, 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, @@ -2640,19 +2727,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, 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 - && CLASS_TYPE_P (TREE_TYPE (new_obj)) - && CP_TYPE_CONST_P (TREE_TYPE (new_obj))) - { - /* 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, new_obj, - /*lval*/false, - non_constant_p, - overflow_p); - if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p) - TREE_READONLY (e) = true; - } + 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. */ @@ -3091,7 +3168,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, overflow_p); else if (code == SPACESHIP_EXPR) { - r = genericize_spaceship (type, lhs, rhs); + r = genericize_spaceship (loc, type, lhs, rhs); return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, overflow_p); } @@ -3099,6 +3176,21 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, if (r == NULL_TREE) r = fold_binary_loc (loc, code, type, lhs, rhs); + if (r == NULL_TREE + && (code == LSHIFT_EXPR || code == RSHIFT_EXPR) + && TREE_CODE (lhs) == INTEGER_CST + && TREE_CODE (rhs) == INTEGER_CST + && wi::neg_p (wi::to_wide (rhs))) + { + /* For diagnostics and -fpermissive emulate previous behavior of + handling shifts by negative amount. */ + tree nrhs = const_unop (NEGATE_EXPR, TREE_TYPE (rhs), rhs); + if (nrhs) + r = fold_binary_loc (loc, + code == LSHIFT_EXPR ? RSHIFT_EXPR : LSHIFT_EXPR, + type, lhs, nrhs); + } + if (r == NULL_TREE) { if (lhs == orig_lhs && rhs == orig_rhs) @@ -3867,6 +3959,205 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, return error_mark_node; } +/* Helper for cxx_eval_bit_cast. + Check [bit.cast]/3 rules, bit_cast is constexpr only if the To and From + types and types of all subobjects have is_union_v<T>, is_pointer_v<T>, + is_member_pointer_v<T>, is_volatile_v<T> false and has no non-static + data members of reference type. */ + +static bool +check_bit_cast_type (const constexpr_ctx *ctx, location_t loc, tree type, + tree orig_type) +{ + if (TREE_CODE (type) == UNION_TYPE) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a union type", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a union type", "__builtin_bit_cast", + orig_type); + } + return true; + } + if (TREE_CODE (type) == POINTER_TYPE) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a pointer type", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a pointer type", "__builtin_bit_cast", + orig_type); + } + return true; + } + if (TREE_CODE (type) == REFERENCE_TYPE) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a reference type", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a reference type", "__builtin_bit_cast", + orig_type); + } + return true; + } + if (TYPE_PTRMEM_P (type)) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a pointer to member type", "__builtin_bit_cast", + type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a pointer to member type", + "__builtin_bit_cast", orig_type); + } + return true; + } + if (TYPE_VOLATILE (type)) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "volatile", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a volatile subobject", + "__builtin_bit_cast", orig_type); + } + return true; + } + if (TREE_CODE (type) == RECORD_TYPE) + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && check_bit_cast_type (ctx, loc, TREE_TYPE (field), orig_type)) + return true; + return false; +} + +/* Subroutine of cxx_eval_constant_expression. + Attempt to evaluate a BIT_CAST_EXPR. */ + +static tree +cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, + bool *overflow_p) +{ + if (check_bit_cast_type (ctx, EXPR_LOCATION (t), TREE_TYPE (t), + TREE_TYPE (t)) + || check_bit_cast_type (ctx, cp_expr_loc_or_loc (TREE_OPERAND (t, 0), + EXPR_LOCATION (t)), + TREE_TYPE (TREE_OPERAND (t, 0)), + TREE_TYPE (TREE_OPERAND (t, 0)))) + { + *non_constant_p = true; + return t; + } + + tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false, + non_constant_p, overflow_p); + if (*non_constant_p) + return t; + + location_t loc = EXPR_LOCATION (t); + if (BITS_PER_UNIT != 8 || CHAR_BIT != 8) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated on the target", + "__builtin_bit_cast"); + *non_constant_p = true; + return t; + } + + if (!tree_fits_shwi_p (TYPE_SIZE_UNIT (TREE_TYPE (t)))) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "type is too large", "__builtin_bit_cast"); + *non_constant_p = true; + return t; + } + + HOST_WIDE_INT len = tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (t))); + if (len < 0 || (int) len != len) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "type is too large", "__builtin_bit_cast"); + *non_constant_p = true; + return t; + } + + unsigned char buf[64]; + unsigned char *ptr, *mask; + size_t alen = (size_t) len * 2; + if (alen <= sizeof (buf)) + ptr = buf; + else + ptr = XNEWVEC (unsigned char, alen); + mask = ptr + (size_t) len; + /* At the beginning consider everything indeterminate. */ + memset (mask, ~0, (size_t) len); + + if (native_encode_initializer (op, ptr, len, 0, mask) != len) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "argument cannot be encoded", "__builtin_bit_cast"); + *non_constant_p = true; + if (ptr != buf) + XDELETE (ptr); + return t; + } + + tree r = NULL_TREE; + if (can_native_interpret_type_p (TREE_TYPE (t))) + r = native_interpret_expr (TREE_TYPE (t), ptr, len); + else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE) + { + r = native_interpret_aggregate (TREE_TYPE (t), ptr, 0, len); + if (r != NULL_TREE) + clear_type_padding_in_mask (TREE_TYPE (t), mask); + } + + if (r != NULL_TREE) + { + for (int i = 0; i < len; i++) + if (mask[i]) + { + if (!ctx->quiet) + error_at (loc, "%qs accessing uninitialized byte at offset %d", + "__builtin_bit_cast", i); + *non_constant_p = true; + r = t; + break; + } + if (ptr != buf) + XDELETE (ptr); + return r; + } + + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "argument cannot be interpreted", "__builtin_bit_cast"); + *non_constant_p = true; + if (ptr != buf) + XDELETE (ptr); + return t; +} + /* Subroutine of cxx_eval_constant_expression. Evaluate a short-circuited logical expression T in the context of a given constexpr CALL. BAILOUT_VALUE is the value for @@ -4257,7 +4548,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval, non_constant_p, overflow_p); } - if (*non_constant_p && !ctx->quiet) + if (*non_constant_p) break; if (new_ctx.ctor != ctx->ctor) { @@ -4329,11 +4620,32 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2) return same_type_ignoring_top_level_qualifiers_p (type1, type2); } +/* Try to determine the currently active union member for an expression + with UNION_TYPE. If it can be determined, return the FIELD_DECL, + otherwise return NULL_TREE. */ + +static tree +cxx_union_active_member (const constexpr_ctx *ctx, tree t) +{ + constexpr_ctx new_ctx = *ctx; + new_ctx.quiet = true; + bool non_constant_p = false, overflow_p = false; + tree ctor = cxx_eval_constant_expression (&new_ctx, t, false, + &non_constant_p, + &overflow_p); + if (TREE_CODE (ctor) == CONSTRUCTOR + && CONSTRUCTOR_NELTS (ctor) == 1 + && CONSTRUCTOR_ELT (ctor, 0)->index + && TREE_CODE (CONSTRUCTOR_ELT (ctor, 0)->index) == FIELD_DECL) + return CONSTRUCTOR_ELT (ctor, 0)->index; + return NULL_TREE; +} + /* Helper function for cxx_fold_indirect_ref_1, called recursively. */ static tree -cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op, - unsigned HOST_WIDE_INT off, bool *empty_base) +cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, + tree op, unsigned HOST_WIDE_INT off, bool *empty_base) { tree optype = TREE_TYPE (op); unsigned HOST_WIDE_INT const_nunits; @@ -4392,13 +4704,29 @@ cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op, tree index = size_int (idx + tree_to_uhwi (min_val)); op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index, NULL_TREE, NULL_TREE); - return cxx_fold_indirect_ref_1 (loc, type, op, rem, + return cxx_fold_indirect_ref_1 (ctx, loc, type, op, rem, empty_base); } } /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */ - else if (TREE_CODE (optype) == RECORD_TYPE) + else if (TREE_CODE (optype) == RECORD_TYPE + || TREE_CODE (optype) == UNION_TYPE) { + if (TREE_CODE (optype) == UNION_TYPE) + /* For unions prefer the currently active member. */ + if (tree field = cxx_union_active_member (ctx, op)) + { + unsigned HOST_WIDE_INT el_sz + = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); + if (off < el_sz) + { + tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), + op, field, NULL_TREE); + if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop, + off, empty_base)) + return ret; + } + } for (tree field = TYPE_FIELDS (optype); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL @@ -4409,13 +4737,13 @@ cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op, if (!tree_fits_uhwi_p (pos)) continue; unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos); - unsigned el_sz + unsigned HOST_WIDE_INT el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); if (upos <= off && off < upos + el_sz) { tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field, NULL_TREE); - if (tree ret = cxx_fold_indirect_ref_1 (loc, type, cop, + if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop, off - upos, empty_base)) return ret; @@ -4436,7 +4764,8 @@ cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op, with TBAA in fold_indirect_ref_1. */ static tree -cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) +cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, + tree op0, bool *empty_base) { tree sub = op0; tree subtype; @@ -4474,7 +4803,7 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) return op; } else - return cxx_fold_indirect_ref_1 (loc, type, op, 0, empty_base); + return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base); } else if (TREE_CODE (sub) == POINTER_PLUS_EXPR && tree_fits_uhwi_p (TREE_OPERAND (sub, 1))) @@ -4484,7 +4813,7 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) STRIP_NOPS (op00); if (TREE_CODE (op00) == ADDR_EXPR) - return cxx_fold_indirect_ref_1 (loc, type, TREE_OPERAND (op00, 0), + return cxx_fold_indirect_ref_1 (ctx, loc, type, TREE_OPERAND (op00, 0), tree_to_uhwi (op01), empty_base); } /* *(foo *)fooarrptr => (*fooarrptr)[0] */ @@ -4494,7 +4823,7 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) tree type_domain; tree min_val = size_zero_node; tree newsub - = cxx_fold_indirect_ref (loc, TREE_TYPE (subtype), sub, NULL); + = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL); if (newsub) sub = newsub; else @@ -4529,8 +4858,8 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, } /* First try to simplify it directly. */ - tree r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), orig_op0, - &empty_base); + tree r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), + orig_op0, &empty_base); if (!r) { /* If that didn't work, evaluate the operand first. */ @@ -4549,7 +4878,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, return t; } - r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0, + r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0, &empty_base); if (r == NULL_TREE) { @@ -4605,7 +4934,9 @@ non_const_var_error (location_t loc, tree r) auto_diagnostic_group d; tree type = TREE_TYPE (r); if (DECL_NAME (r) == heap_uninit_identifier - || DECL_NAME (r) == heap_identifier) + || DECL_NAME (r) == heap_identifier + || DECL_NAME (r) == heap_vec_uninit_identifier + || DECL_NAME (r) == heap_vec_identifier) { error_at (loc, "the content of uninitialized storage is not usable " "in a constant expression"); @@ -5971,8 +6302,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) cxx_eval_constant_expression (ctx, cleanup, false, - non_constant_p, overflow_p, - jump_target); + non_constant_p, overflow_p); } break; @@ -5983,29 +6313,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (!*non_constant_p) /* Also evaluate the cleanup. */ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true, - non_constant_p, overflow_p, - jump_target); + non_constant_p, overflow_p); break; case CLEANUP_STMT: - { - tree initial_jump_target = jump_target ? *jump_target : NULL_TREE; - r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval, - non_constant_p, overflow_p, - jump_target); - if (!CLEANUP_EH_ONLY (t) && !*non_constant_p) - { - iloc_sentinel ils (loc); - /* Also evaluate the cleanup. If we weren't skipping at the - start of the CLEANUP_BODY, change jump_target temporarily - to &initial_jump_target, so that even a return or break or - continue in the body doesn't skip the cleanup. */ - cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true, - non_constant_p, overflow_p, - jump_target ? &initial_jump_target - : NULL); - } - } + r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval, + non_constant_p, overflow_p, + jump_target); + if (!CLEANUP_EH_ONLY (t) && !*non_constant_p) + { + iloc_sentinel ils (loc); + /* Also evaluate the cleanup. */ + cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true, + non_constant_p, overflow_p); + } break; /* These differ from cxx_eval_unary_expression in that this doesn't @@ -6365,8 +6686,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, && TREE_TYPE (op) == ptr_type_node && TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR && VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0)) - && DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0), - 0)) == heap_uninit_identifier) + && (DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0), + 0)) == heap_uninit_identifier + || DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0), + 0)) == heap_vec_uninit_identifier)) { tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0); tree var_size = TYPE_SIZE_UNIT (TREE_TYPE (var)); @@ -6380,7 +6703,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, elt_type = TREE_TYPE (TREE_TYPE (fld2)); cookie_size = TYPE_SIZE_UNIT (TREE_TYPE (fld1)); } - DECL_NAME (var) = heap_identifier; + DECL_NAME (var) + = (DECL_NAME (var) == heap_uninit_identifier + ? heap_identifier : heap_vec_identifier); TREE_TYPE (var) = build_new_constexpr_heap_type (elt_type, cookie_size, var_size); @@ -6574,6 +6899,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; return t; + case BIT_CAST_EXPR: + r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p); + break; + default: if (STATEMENT_CODE_P (TREE_CODE (t))) { @@ -6651,6 +6980,8 @@ find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/) if (VAR_P (*tp) && (DECL_NAME (*tp) == heap_uninit_identifier || DECL_NAME (*tp) == heap_identifier + || DECL_NAME (*tp) == heap_vec_uninit_identifier + || DECL_NAME (*tp) == heap_vec_identifier || DECL_NAME (*tp) == heap_deleted_identifier)) return *tp; @@ -6872,6 +7203,10 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, non_constant_p = true; } + if (non_constant_p) + /* If we saw something bad, go back to our argument. The wrapping below is + only for the cases of TREE_CONSTANT argument or overflow. */ + r = t; if (!non_constant_p && overflow_p) non_constant_p = true; @@ -6888,12 +7223,6 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, return r; else if (non_constant_p && TREE_CONSTANT (r)) { - /* If __builtin_is_constant_evaluated () was evaluated to true - and the result is not a valid constant expression, we need to - punt. */ - if (manifestly_const_eval) - return cxx_eval_outermost_constant_expr (t, true, strict, - false, false, object); /* This isn't actually constant, so unset TREE_CONSTANT. Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires it to be set if it is invariant address, even when it is not @@ -7085,15 +7414,13 @@ clear_cv_cache (void) cv_cache->empty (); } -/* Dispose of the whole CV_CACHE, FOLD_CACHE, and satisfaction caches. */ +/* Dispose of the whole CV_CACHE and FOLD_CACHE. */ void -clear_cv_and_fold_caches (bool sat /*= true*/) +clear_cv_and_fold_caches () { clear_cv_cache (); clear_fold_cache (); - if (sat) - clear_satisfaction_cache (); } /* Internal function handling expressions in templates for @@ -7199,7 +7526,8 @@ maybe_fold_non_dependent_expr (tree expr, tree fold_non_dependent_init (tree t, tsubst_flags_t complain /*=tf_warning_or_error*/, - bool manifestly_const_eval /*=false*/) + bool manifestly_const_eval /*=false*/, + tree object /* = NULL_TREE */) { if (t == NULL_TREE) return NULL_TREE; @@ -7207,7 +7535,7 @@ fold_non_dependent_init (tree t, if (processing_template_decl) { t = fold_non_dependent_expr_template (t, complain, - manifestly_const_eval, NULL_TREE); + manifestly_const_eval, object); /* maybe_constant_init does this stripping, so do it here too. */ if (TREE_CODE (t) == TARGET_EXPR) { @@ -7218,7 +7546,7 @@ fold_non_dependent_init (tree t, return t; } - return maybe_constant_init (t, NULL_TREE, manifestly_const_eval); + return maybe_constant_init (t, object, manifestly_const_eval); } /* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather @@ -7445,7 +7773,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case OVERLOAD: case TEMPLATE_ID_EXPR: case LABEL_DECL: - case LABEL_EXPR: case CASE_LABEL_EXPR: case PREDICT_EXPR: case CONST_DECL: @@ -7665,6 +7992,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, } return false; } + /* Treat __PRETTY_FUNCTION__ inside a template function as + potentially-constant. */ + else if (DECL_PRETTY_FUNCTION_P (t) + && DECL_VALUE_EXPR (t) == error_mark_node) + return true; return RECUR (DECL_VALUE_EXPR (t), rval); } if (want_rval @@ -8349,9 +8681,20 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return false; } + case LABEL_EXPR: + t = LABEL_EXPR_LABEL (t); + if (DECL_ARTIFICIAL (t)) + return true; + else if (flags & tf_error) + error_at (loc, "label definition is not a constant expression"); + return false; + case ANNOTATE_EXPR: return RECUR (TREE_OPERAND (t, 0), rval); + case BIT_CAST_EXPR: + return RECUR (TREE_OPERAND (t, 0), rval); + /* Coroutine await, yield and return expressions are not. */ case CO_AWAIT_EXPR: case CO_YIELD_EXPR: @@ -8359,7 +8702,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return false; default: - if (objc_is_property_ref (t)) + if (objc_non_constant_expr_p (t)) return false; sorry ("unexpected AST of kind %s", get_tree_code_name (TREE_CODE (t))); |