cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false,
non_constant_p, overflow_p);
+ /* If this is a constructor, we are beginning the lifetime of the
+ object we are initializing. */
+ if (new_obj
+ && DECL_CONSTRUCTOR_P (fun)
+ && TREE_CODE (new_obj) == COMPONENT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (new_obj, 0))) == UNION_TYPE)
+ {
+ tree activate = build2 (INIT_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);
+ }
+
tree jump_target = NULL_TREE;
cxx_eval_constant_expression (&call_ctx, body,
vc_discard, 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);
- }
- }
+ /* 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;
else if (VOID_TYPE_P (TREE_TYPE (res)))
result = void_node;
else
*non_constant_p = true;
return t;
}
+
bool pmf = TYPE_PTRMEMFUNC_P (TREE_TYPE (whole));
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value)
{
break;
}
}
- if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE
- && CONSTRUCTOR_NELTS (whole) > 0)
+ if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE)
{
- /* DR 1188 says we don't have to deal with this. */
- if (!ctx->quiet)
+ if (CONSTRUCTOR_NELTS (whole) > 0)
{
- constructor_elt *cep = CONSTRUCTOR_ELT (whole, 0);
- if (cep->value == NULL_TREE)
- error ("accessing uninitialized member %qD", part);
- else
- error ("accessing %qD member instead of initialized %qD member in "
- "constant expression", part, cep->index);
+ /* DR 1188 says we don't have to deal with this. */
+ if (!ctx->quiet)
+ {
+ constructor_elt *cep = CONSTRUCTOR_ELT (whole, 0);
+ if (cep->value == NULL_TREE)
+ error ("accessing uninitialized member %qD", part);
+ else
+ error ("accessing %qD member instead of initialized %qD member "
+ "in constant expression", part, cep->index);
+ }
+ *non_constant_p = true;
+ return t;
+ }
+ else if (!CONSTRUCTOR_NO_CLEARING (whole))
+ {
+ /* Value-initialized union, check if looking at the first member. */
+ tree first = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (whole)));
+ if (first != part)
+ {
+ if (!ctx->quiet)
+ error ("accessing %qD member instead of initialized %qD "
+ "member in constant expression", part, first);
+ *non_constant_p = true;
+ return t;
+ }
}
- *non_constant_p = true;
- return t;
}
/* We only create a CONSTRUCTOR for a subobject when we modify it, so empty
mutable_p)
&& const_object_being_modified == NULL_TREE)
const_object_being_modified = probe;
+
+ /* Track named member accesses for unions to validate modifications
+ that change active member. */
+ if (!evaluated && TREE_CODE (probe) == COMPONENT_REF)
+ vec_safe_push (refs, probe);
+ else
+ vec_safe_push (refs, NULL_TREE);
+
vec_safe_push (refs, elt);
vec_safe_push (refs, TREE_TYPE (probe));
probe = ob;
case REALPART_EXPR:
gcc_assert (probe == target);
+ vec_safe_push (refs, NULL_TREE);
vec_safe_push (refs, probe);
vec_safe_push (refs, TREE_TYPE (probe));
probe = TREE_OPERAND (probe, 0);
case IMAGPART_EXPR:
gcc_assert (probe == target);
+ vec_safe_push (refs, NULL_TREE);
vec_safe_push (refs, probe);
vec_safe_push (refs, TREE_TYPE (probe));
probe = TREE_OPERAND (probe, 0);
enum tree_code code = TREE_CODE (type);
tree reftype = refs->pop();
tree index = refs->pop();
+ bool is_access_expr = refs->pop() != NULL_TREE;
if (code == COMPLEX_TYPE)
{
break;
}
- type = reftype;
+ /* If a union is zero-initialized, its first non-static named data member
+ is zero-initialized (and therefore active). */
+ if (code == UNION_TYPE
+ && !no_zero_init
+ && CONSTRUCTOR_NELTS (*valp) == 0)
+ if (tree first = next_aggregate_field (TYPE_FIELDS (type)))
+ CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (*valp), first, NULL_TREE);
- if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp)
- && CONSTRUCTOR_ELT (*valp, 0)->index != index)
+ /* Check for implicit change of active member for a union. */
+ if (code == UNION_TYPE
+ && (CONSTRUCTOR_NELTS (*valp) == 0
+ || CONSTRUCTOR_ELT (*valp, 0)->index != index)
+ /* An INIT_EXPR of the last member in an access chain is always OK,
+ but still check implicit change of members earlier on; see
+ cpp2a/constexpr-union6.C. */
+ && !(TREE_CODE (t) == INIT_EXPR && refs->is_empty ()))
{
- if (cxx_dialect < cxx20)
+ bool has_active_member = CONSTRUCTOR_NELTS (*valp) != 0;
+ tree inner = strip_array_types (reftype);
+
+ if (has_active_member && cxx_dialect < cxx20)
{
if (!ctx->quiet)
error_at (cp_expr_loc_or_input_loc (t),
"change of the active member of a union "
- "from %qD to %qD",
+ "from %qD to %qD is not a constant expression "
+ "before C++20",
CONSTRUCTOR_ELT (*valp, 0)->index,
index);
*non_constant_p = true;
}
- else if (TREE_CODE (t) == MODIFY_EXPR
- && CONSTRUCTOR_NO_CLEARING (*valp))
+ else if (!is_access_expr
+ || (TREE_CODE (t) == MODIFY_EXPR
+ && CLASS_TYPE_P (inner)
+ && !type_has_non_deleted_trivial_default_ctor (inner)))
+ {
+ /* Diagnose changing active union member after initialization
+ without a valid member access expression, as described in
+ [class.union.general] p5. */
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ if (has_active_member)
+ error_at (cp_expr_loc_or_input_loc (t),
+ "accessing %qD member instead of initialized "
+ "%qD member in constant expression",
+ index, CONSTRUCTOR_ELT (*valp, 0)->index);
+ else
+ error_at (cp_expr_loc_or_input_loc (t),
+ "accessing uninitialized member %qD",
+ index);
+ if (is_access_expr)
+ inform (DECL_SOURCE_LOCATION (index),
+ "%qD does not implicitly begin its lifetime "
+ "because %qT does not have a non-deleted "
+ "trivial default constructor, use "
+ "%<std::construct_at%> instead",
+ index, inner);
+ else
+ inform (DECL_SOURCE_LOCATION (index),
+ "initializing %qD requires a member access "
+ "expression as the left operand of the assignment",
+ index);
+ }
+ *non_constant_p = true;
+ }
+ else if (has_active_member && CONSTRUCTOR_NO_CLEARING (*valp))
{
/* Diagnose changing the active union member while the union
is in the process of being initialized. */
activated_union_member_p = true;
valp = &cep->value;
+ type = reftype;
}
/* For initialization of an empty base, the original target will be