Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/cmd_line.c
Original file line number Diff line number Diff line change
Expand Up @@ -4301,6 +4301,13 @@ default mode (e.g., exception handling).
std_override_modifiers_enabled = value;
constexpr_implies_const = value;
register_is_deprecated = TRUE;
if (value && rvalue_references_enabled &&
(cpp14_mode || gpp_mode || clang_mode) &&
!gpp_version_is(<90000) && !clangcpp_version_is(<40000)) {
/* A field selection on a class prvalue is an xvalue (CWG 616). GCC 9+
and Clang 4+ also do that in C++11 mode. */
selection_from_prvalue_is_xvalue = TRUE;
} /* if */
if (cpp14_mode) {
/* Features enabled in C++14 mode. */
if (auto_type_specifier_enabled) {
Expand All @@ -4317,9 +4324,6 @@ default mode (e.g., exception handling).
if (!option_kind_used[(int)optk_digit_separators]) {
digit_separators_enabled = TRUE;
} /* if */
if (rvalue_references_enabled && !gpp_mode && !clang_mode) {
selection_from_prvalue_is_xvalue = TRUE;
} /* if */
relaxed_constexpr_enabled = TRUE;
sized_deallocation_enabled = RUNTIME_SUPPORTS_SIZED_DEALLOCATION;
if (gpp_mode && !clang_mode && gnu_version < 50000) {
Expand Down
3 changes: 2 additions & 1 deletion src/cmd_line.h
Original file line number Diff line number Diff line change
Expand Up @@ -1742,7 +1742,8 @@ EXTERN_THREAD a_boolean
EXTERN_THREAD a_boolean
selection_from_prvalue_is_xvalue;
/* TRUE if a field selection on a prvalue class object
produces an xvalue (C++14 / CWG 616). When FALSE,
produces an xvalue (C++14 / CWG 616; GCC 9 and
Clang 4 also apply it in C++11 mode). When FALSE,
the selection is a prvalue. */

EXTERN_THREAD a_boolean
Expand Down
7 changes: 7 additions & 0 deletions src/exprutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -25481,6 +25481,13 @@ underlying entity is a temporary, return *is_temp set to TRUE.
clear_expr_or_stmt_traversal_block(&tblock);
tblock.process_expr = examine_expr_for_auto_object;
tblock.follow_addressing_path = TRUE;
/* A pointer into a prvalue.field xvalue (CWG 616) addresses a
subobject of the class prvalue, which is a temporary even when it
is not represented by an explicit temporary (e.g., for a trivially
destructible class). */
tblock.follow_class_rvalue_addressing_path =
selection_from_prvalue_is_xvalue;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
tblock.look_for_temporaries = selection_from_prvalue_is_xvalue;
traverse_expr(expr, &tblock);
is_addr_of_auto = tblock.result;
*is_temp = tblock.is_temp;
Expand Down
16 changes: 8 additions & 8 deletions src/il_walk.c
Original file line number Diff line number Diff line change
Expand Up @@ -3267,10 +3267,10 @@ Tips for proper use of the follow_addressing_path mode:
switch (op) {
case eok_dot_field:
/* x.y: Follow x. */
/* We don't have to test for the prvalue.field case here because its
result is a prvalue and therefore wouldn't get here, unless
follow_class_rvalue_addressing_path is TRUE, in which case
we want to keep going anyway. */
/* We don't have to test for the prvalue.field case here: it gets
here only if follow_class_rvalue_addressing_path is TRUE or if
its result is an xvalue (selection_from_prvalue_is_xvalue,
CWG 616), and in both cases we want to keep going. */
traverse_expr(operand1, tblock);
break;
case eok_points_to_field:
Expand All @@ -3279,10 +3279,10 @@ Tips for proper use of the follow_addressing_path mode:
break;
case eok_pm_field:
/* x.*pm: Follow x. */
/* We don't have to test for the prvalue.*pm case here because its
result is a prvalue and therefore wouldn't get here, unless
follow_class_rvalue_addressing_path is TRUE, in which case
we want to keep going anyway. */
/* We don't have to test for the prvalue.*pm case here: it gets
here only if follow_class_rvalue_addressing_path is TRUE or if
its result is an xvalue (selection_from_prvalue_is_xvalue,
CWG 616), and in both cases we want to keep going. */
traverse_expr(operand1, tblock);
break;
case eok_pm_points_to_field:
Expand Down
3 changes: 2 additions & 1 deletion src/il_walk.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,8 @@ typedef struct an_expr_or_stmt_traversal_block {
/* TRUE if expr_complete_object_type was called
to determine the object type for a virtual function
call. */
/* Fields used by is_glvalue_for_auto_object: */
/* Fields used by is_glvalue_for_auto_object and
is_address_of_auto_object: */
a_boolean is_temp;
/* TRUE if the underlying object is a temporary. */
a_boolean look_for_temporaries;
Expand Down
109 changes: 99 additions & 10 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -4887,6 +4887,36 @@ object) is initialized.
} /* subobject_is_initialized */


#if GNU_VECTOR_TYPES_ALLOWED
static a_boolean vector_elements_are_initialized(
an_interpreter_state *ips,
a_type_ptr tp,
a_constexpr_address *cap)
/*
Return TRUE if each element of the vector of type tp at the address described
by *cap is initialized (or if the enclosing complete object is fully
initialized). The elements of a vector are tracked individually, so a whole
vector value can only be read if none of them is uninitialized.
*/
{
a_boolean result = TRUE;

if (!complete_object_is_initialized(cap->complete_object)) {
a_type_ptr etp = skip_typerefs(tp->variant.vector.element_type);
a_targ_size_t k, n_elems = num_vector_elements(tp);
a_byte_count elem_size = value_bytes_for_type(ips, etp, &result);
for (k = 0; result && k<n_elems; k += 1) {
if (!subobject_is_initialized(cap->address+k*elem_size,
cap->complete_object)) {
result = FALSE;
} /* if */
} /* for */
} /* if */
return result;
} /* vector_elements_are_initialized */
#endif /* GNU_VECTOR_TYPES_ALLOWED */


static a_boolean mark_mutable_members_not_initialized(
an_interpreter_state *ips,
a_byte *subobj,
Expand Down Expand Up @@ -7483,6 +7513,15 @@ END_DISABLE_GCC_WARNING_STR_OVERFLOW
unmap_stack_bytes(ips, dst_addr.address);
} /* if */
mark_subobject_initialized(dst_addr.address, complete_object);
#if GNU_VECTOR_TYPES_ALLOWED
if (type_is(ftp, tk_vector)) {
/* A vector element can be accessed by itself (e.g., by
subscripting the member), so mark each element
initialized. */
mark_whole_subobject_initialized(ips, dst_addr.address, ftp,
complete_object);
} /* if */
#endif /* GNU_VECTOR_TYPES_ALLOWED */
elem_con = elem_con->next;
} /* if */
if (this_bytes != NULL) {
Expand Down Expand Up @@ -7818,6 +7857,25 @@ any subobject that is not initialized (the diagnostic is associated with pos).
} /* if */
}
break;
#if GNU_VECTOR_TYPES_ALLOWED
case tk_vector:
{ /* The elements of a vector are tracked individually: Handle each
element after the first (which shares the address of the vector
and was handled above). */
a_type_ptr etp = skip_typerefs(tp->variant.vector.element_type);
a_targ_size_t k, n_elems = num_vector_elements(tp);
a_byte_count elem_size = value_bytes_for_type(ips, etp, &result);
for (k = 1; result && k<n_elems; ++k) {
if (subobject_is_initialized(src_bytes+k*elem_size, complete_src)) {
mark_subobject_initialized(dst_bytes+k*elem_size, complete_dst);
} else {
do_constexpr_fail(result);
info_with_pos(ec_object_not_initialized, pos, ips);
} /* if */
} /* for */
}
break;
#endif /* GNU_VECTOR_TYPES_ALLOWED */
default:
/* Nothing more to do. */
break;
Expand Down Expand Up @@ -23985,10 +24043,21 @@ after the constructor body has been interpreted.
p_arg_ptr += 1;
arg_size += 1;
} /* for */
if (dip->variant.constructor.value_initialization) {
/* If this is for value initialization, clear the storage first.
Do not, however, overwrite the first word (which may record the
inheritance hierarchy). */
if (!dip->variant.constructor.value_initialization) {
/* Nothing to do. */
} else if (!type_has_user_provided_default_constructor(class_type)) {
/* Value initialization of a class without a user-provided default
constructor zero-initializes the object before calling the
constructor, which need not initialize every subobject (e.g., a
member of a class whose implicit default constructor calls a
user-provided base class constructor). Mark the subobjects as
initialized, so that reading them is valid. */
init_subobject_to_zero(ips, result_storage, class_type,
complete_object);
} else {
/* Otherwise, the object is not zero-initialized, but clear the
storage anyway. Do not, however, overwrite the first word (which
may record the inheritance hierarchy). */
a_byte_count n_class_bytes;
a_boolean dummy = TRUE;
n_class_bytes = f_value_bytes_for_type(ips, class_type, &dummy);
Expand Down Expand Up @@ -25465,6 +25534,14 @@ conversion to an rvalue is forced externally.
} else if (!is_initialized(cap) && !is_immediate_class_type(tp)) {
do_constexpr_fail(result);
info_with_pos(ec_object_not_initialized, &expr->position, ips);
#if GNU_VECTOR_TYPES_ALLOWED
} else if (type_is(tp, tk_vector) &&
!vector_elements_are_initialized(ips, tp, cap)) {
/* A whole vector is read, but some of its elements are not
initialized. */
do_constexpr_fail(result);
info_with_pos(ec_object_not_initialized, &expr->position, ips);
#endif /* GNU_VECTOR_TYPES_ALLOWED */
} else if (is_variant_path(cap) &&
!check_variant_path(ips, cap, &expr->position)) {
/* An attempt to dereference an inactive variant path. */
Expand Down Expand Up @@ -32204,7 +32281,7 @@ the value representation of the integer value.
break;
#if GNU_VECTOR_TYPES_ALLOWED
case eok_vector_subscript:
/* The first operand is a vector (lvalue or rvalue), and the
/* The first operand is a vector (glvalue or prvalue), and the
second operand is an integer. */
{ a_host_large_integer len = (a_host_large_integer)
num_vector_elements(opnd1_type);
Expand All @@ -32223,7 +32300,7 @@ the value representation of the integer value.
etp = skip_typerefs(opnd1_type->variant.vector.element_type);
esize = value_bytes_for_type(ips, etp, &result);
if (!result) break;
if (opnd1->is_lvalue) {
if (opnd1->is_lvalue || opnd1->is_xvalue) {
a_constexpr_address
result_addr = *(a_constexpr_address*)opnd1_value;
result_addr.address += host_int_val*esize;
Expand All @@ -32250,8 +32327,9 @@ the value representation of the integer value.
result_addr = *(a_constexpr_address*)opnd1_value;
} else {
/* The first operand is a class rvalue: Create an address for
it. */
mark_complete_object_initialized(opnd1_value);
it. Do not mark the object as fully initialized: since
C++20, a constexpr constructor can leave members
uninitialized, and reading those must be diagnosed. */
clear_address(&result_addr, opnd1_value);
result_addr.alloc_seq_number = ips->storage_stack.alloc_seq();
} /* if */
Expand Down Expand Up @@ -32375,8 +32453,9 @@ the value representation of the integer value.
result_addr = *(a_constexpr_address*)opnd1_value;
} else {
/* The first operand is a class rvalue: Create an address for
it. */
mark_complete_object_initialized(opnd1_value);
it. Do not mark the object as fully initialized: since
C++20, a constexpr constructor can leave members
uninitialized, and reading those must be diagnosed. */
clear_address(&result_addr, opnd1_value);
result_addr.alloc_seq_number = ips->storage_stack.alloc_seq();
} /* if */
Expand Down Expand Up @@ -33129,6 +33208,16 @@ the value representation of the integer value.
&expr->position, ips);
} /* switch */
done:
#if GNU_VECTOR_TYPES_ALLOWED
if (pop_when_done && result && type_is(tp, tk_vector) &&
!is_glvalue_node(expr)) {
/* The whole vector value has been stored, and its elements can be
accessed individually (e.g., by subscripting an lvalue for it), so
mark each of them initialized. */
mark_whole_subobject_initialized(ips, result_storage, tp,
complete_object);
} /* if */
#endif /* GNU_VECTOR_TYPES_ALLOWED */
end_work_visit(ips, result, pop_when_done);
#undef SET_result_val_from_operand_address
#undef CHECK_int_range
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[GH #90]
GNU and Clang C++ modes: lifetime extension through a member of a temporary

When emulating GCC 9 or later or Clang 4 or later (in C++11 and later
modes), selecting a member of a class prvalue now produces an xvalue (Core
issue 616), as it already did in standard C++14 and later modes. Binding a
reference to the member now extends the lifetime of the whole temporary.
For example, with --g++ --gnu_version=150000:

struct A { int b; ~A(); };
const int &r = A().b; // A() was previously destroyed here.

Returning a pointer into a member of a class prvalue is now diagnosed also
when the class is trivially destructible. For example, with --c++20:

struct T { int b; int arr[2]; };
T make();
const int *f() { return &make().arr[1]; } // Warning.

Constant evaluation also no longer treats every member of a class prvalue
as initialized when one is selected with "." or ".*". Since C++20, a
constexpr constructor can leave members uninitialized, and reading such a
member is now diagnosed instead of producing an indeterminate value. For
example, with --c++20:

struct W { int n; constexpr W() {} };
static_assert(W().n == 0); // Error: n is uninitialized.

Constant evaluation now also marks each element of a GNU vector as
initialized when a whole vector value is stored (e.g., by a mem-initializer),
and copies the state of each element when an object containing a vector is
copied. Subscripting such a vector could previously give a spurious "access
to uninitialized object" error. Conversely, reading or copying a whole
vector subobject now requires each of its elements to be initialized. For
example, with --g++ --c++20:

typedef int V4 __attribute__((vector_size(16)));
struct Y { V4 v; constexpr Y(V4 a) : v(a) {} };
constexpr int f() { V4 a = {1, 2, 3, 4}; Y y(a); return y.v[3]; }
static_assert(f() == 4, ""); // Previously rejected.
12 changes: 12 additions & 0 deletions src/pending_changes/gh-90-value-init-zeroed-members.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[GH #90]
Constexpr reads of zero-initialized members after value initialization

Value initialization of a class without a user-provided default constructor
zero-initializes the object before calling the constructor. During
constant evaluation, the zeroed members were not treated as initialized, so
reading one that the constructor does not set was rejected. For example,
with --c++20:

struct A { constexpr A() {} int x; };
struct B : A { int y; };
static_assert(B().x == 0); // Previously rejected.
3 changes: 0 additions & 3 deletions tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -6601,7 +6601,6 @@ gnu/cpp/cpp0x/constexpr-typeid/test.mft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp0x/conv-tmpl1.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp0x/conv-tmpl6.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp0x/decltype41/test.mft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp0x/decltype72.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp0x/decltype80.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp0x/decltype83.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:ABORT:
gnu/cpp/cpp0x/decltype83a.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:ABORT:
Expand Down Expand Up @@ -7066,7 +7065,6 @@ gnu/cpp/cpp2a/consteval13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval25.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval32.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-99287.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-init18.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-inst1.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
gnu/cpp/cpp2a/constexpr-inst2.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp2a/constexpr-new11/test.mft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
Expand Down Expand Up @@ -7742,7 +7740,6 @@ gnu/cpp/init/new22.sft.cpp:fn:-DTEST_NUMBER=1:FAIL:
gnu/cpp/init/new39.sft.cpp:rp:-DTEST_NUMBER=1:FAIL:
gnu/cpp/init/new40.sft.cpp:rp:-DTEST_NUMBER=1:FAIL:
gnu/cpp/init/null1.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/init/ref19.sft.cpp:rp:-DTEST_NUMBER=1:FAIL:
gnu/cpp/init/ref23.sft.cpp:fp:-DTEST_NUMBER=1:FAIL:
gnu/cpp/init/reference2.sft.cpp:fn:-DTEST_NUMBER=1:FAIL:
gnu/cpp/init/self1.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL:
Expand Down
1 change: 1 addition & 0 deletions tests/tests/cwg/.cwg616.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++14 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/cwg/.cwg616.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=2 --c++14 --g++ --gnu_version=150000 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/cwg/.cwg616.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=3 --c++14 --clang --clang_version=190100 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/cwg/.cwg616.rto/default.4.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=4 --c++11 --g++ --gnu_version=90000 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/cwg/.cwg616.rto/default.5.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=5 --c++11 --clang --clang_version=40000 Test_name.c
13 changes: 13 additions & 0 deletions tests/tests/cwg/cwg616.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//type:fp
//options:--c++14:--c++14 --g++ --gnu_version=150000:--c++14 --clang --clang_version=190100:--c++11 --g++ --gnu_version=90000:--c++11 --clang --clang_version=40000
template <class T> struct is_xvalue { static const bool value = false; };
template <class T> struct is_xvalue<T &&> { static const bool value = true; };
struct A { int b; };
// Selecting a member of a class prvalue produces an xvalue.
static_assert(is_xvalue<decltype((A().b))>::value, "");
static_assert(is_xvalue<decltype((A().*&A::b))>::value, "");

//cwg: 616
//title: Definition of "indeterminate value"
//meeting: Bristol 4/13
//edg_status: Passes
8 changes: 0 additions & 8 deletions tests/tests/imported/clang/c/C/C11/.n1285.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
fe_only -DTEST_NUMBER=3 --clang -e 200 --no_wrap_diagnostics --clang_version 999999 --c++11 --c++ Test_name.c
"Test_name.c", line 44: warning: variable "p" was set but never used
int *p = (1 ? (struct X){ 0 } : f()).a; // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
^

"Test_name.c", line 45: warning: variable "r" was declared but never referenced
int *r = (1 ? (union U){ 0 } : g()).a; // expected-warning {{temporary whose address is used as value of local variable 'r' will be destroyed at the end of the full-expression}}
^

"Test_name.c", line 64: warning: variable "p" was set but never used
int *p = ((void)0, x).a; // c-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
^
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_version 999999 --c++20 Test_name.c
"Test_name.c", line 16: error: expression must have a constant value
static_assert(B().x == 0);
^
"Test_name.c", line 16: note: access to uninitialized object
static_assert(B().x == 0);
^

"Test_name.c", line 27: error: class "P0960R3::A" has no suitable copy constructor
A a1(va);
^
Expand Down Expand Up @@ -144,4 +137,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
D d8 = {vdd}; // expected-error {{no matching constructor for initialization of 'D'}}
^

31 errors detected in the compilation of "Test_name.c".
30 errors detected in the compilation of "Test_name.c".
Loading
Loading