Skip to content

Commit e6d9621

Browse files
Momen-G-Ardaveedvdv-nvidia
authored andcommitted
Bind an NSDMI's implicit this to the right aggregate [GH #324]
When lowering a non-constant aggregate initialization, lower_param_ref replaced the enk_param_ref for "this" with the innermost aggregate on aggregate_this_stack whose type matched. A default member initializer that aggregate-initializes another object of the same class then bound the implicit "this" of its explicit elements to that nested object: struct Y { unsigned k; unsigned n = Y{2, k}.n; }; Y *p = new Y{1}; // p->n was 2, not 1. Only an element that comes from a default member initializer (marked implicit_aggr_element) can refer to the aggregate it initializes, which is also the rule the constexpr interpreter uses. An aggregate is now skipped in the search while one of its explicit elements is lowered; if nothing else matches, the previous search is used.
1 parent 34b1655 commit e6d9621

12 files changed

Lines changed: 210 additions & 49 deletions

File tree

‎src/lower_il.c‎

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15971,6 +15971,73 @@ lowering). Note that eok_class_rvalue_adjust operations are not modified here
1597115971
} /* remove_qualifiers_from_expr_type_if_needed */
1597215972

1597315973

15974+
static a_boolean replace_param_ref_with_aggregate_this(
15975+
an_expr_node_ptr expr,
15976+
a_boolean candidates_only)
15977+
/*
15978+
expr is an enk_param_ref node that represents "this" while lowering an
15979+
aggregate constant. Go backwards through aggregate_this_stack, the stack of
15980+
nested potential "this" pointers, looking for the innermost entry that
15981+
matches the type of expr. If candidates_only is TRUE, skip the entries
15982+
whose not_this_candidate flag is set (see
15983+
lower_dynamic_init_aggregate_constant). If an entry is found, replace expr
15984+
by an expression that points to the beginning of that aggregate and return
15985+
TRUE; otherwise, return FALSE. Note that the code below is inefficient in
15986+
that it creates an expression representation for the initialization position
15987+
description just to find out its type (and discards expression trees that
15988+
don't match), but the type is difficult to determine (because of potential
15989+
modifiers) and this case doesn't occur frequently.
15990+
*/
15991+
{
15992+
a_boolean found = FALSE;
15993+
an_expr_node_ptr new_expr = NULL;
15994+
an_init_pos_descr_ptr ipdp;
15995+
a_type_ptr new_type,
15996+
old_type= skip_typerefs(type_pointed_to(expr->type));
15997+
15998+
for (ipdp = aggregate_this_stack; ipdp != NULL; ipdp = ipdp->next) {
15999+
a_type_ptr subobj_type;
16000+
if (candidates_only && ipdp->not_this_candidate) {
16001+
/* An explicitly specified element of this aggregate is being lowered,
16002+
so this aggregate is not what "this" refers to. */
16003+
continue;
16004+
} /* if */
16005+
new_expr = make_address_of_init_entity_node(ipdp,
16006+
/*using_as_dest=*/FALSE);
16007+
new_type = skip_typerefs(type_pointed_to(new_expr->type));
16008+
if (identical_types_ignoring_qualifiers(new_type, old_type) ||
16009+
(aggregate_classes_can_have_bases &&
16010+
!class_type_supp(old_type)->is_lambda_closure_class &&
16011+
!class_type_supp(new_type)->is_lambda_closure_class &&
16012+
(subobj_type = subobject_for_class(old_type),
16013+
identical_types_ignoring_qualifiers(new_type, subobj_type)))) {
16014+
found = TRUE;
16015+
new_expr = add_cast_if_necessary(new_expr, expr->type);
16016+
overwrite_node(expr, new_expr);
16017+
break;
16018+
} else if (aggregate_classes_can_have_bases &&
16019+
ipdp->base_class_subobject) {
16020+
/* See if this initialization position has been modified to point
16021+
to a subobject base class. If so, temporarily remove the modifier
16022+
to see if the non-modified type is the "this" type that we're
16023+
looking for. */
16024+
an_init_pos_modifier_ptr save_modifiers = ipdp->modifiers;
16025+
ipdp->modifiers = NULL;
16026+
new_expr = make_address_of_init_entity_node(ipdp,
16027+
/*using_as_dest=*/FALSE);
16028+
new_type = type_pointed_to(new_expr->type);
16029+
ipdp->modifiers = save_modifiers;
16030+
if (identical_types_ignoring_qualifiers(new_type, old_type)) {
16031+
found = TRUE;
16032+
overwrite_node(expr, new_expr);
16033+
break;
16034+
} /* if */
16035+
} /* if */
16036+
} /* for */
16037+
return found;
16038+
} /* replace_param_ref_with_aggregate_this */
16039+
16040+
1597416041
static void lower_param_ref(an_expr_node_ptr expr)
1597516042
/*
1597616043
The front end uses enk_param_refs to represent "this" in two particular
@@ -16001,60 +16068,26 @@ as in this case:
1600116068
} a;
1600216069

1600316070
If we're in a non-constant aggregate initialization, search for that first.
16071+
Several nested aggregates can have the type of "this", as in:
16072+
16073+
struct Y { unsigned k; unsigned n = Y{2, k}.n; };
16074+
16075+
where "k" refers to the Y being initialized, not to the temporary
16076+
initialized by "Y{2, k}". The aggregates for which an explicitly specified
16077+
element is being lowered are therefore skipped.
1600416078
*/
1600516079
{
1600616080
a_boolean found = FALSE;
1600716081

1600816082
check_assertion(expr->kind == (an_expr_node_kind)enk_param_ref &&
1600916083
expr->variant.param_ref.param_num == 0);
1601016084
if (aggregate_this_stack != NULL) {
16011-
/* We must be lowering an aggregate constant; go backwards through the
16012-
stack of nested potential "this" pointers looking for one that matches
16013-
the type of the enk_param_ref node (there can only be one) and
16014-
create an expression that points to the beginning of that aggregate.
16015-
Note that the code below is inefficient in that it creates an
16016-
expression representation for the initialization position description
16017-
just to find out its type (and discards expression trees that don't
16018-
match), but the type is difficult to determine (because of potential
16019-
modifiers) and this case doesn't occur frequently. */
16020-
an_expr_node_ptr new_expr = NULL;
16021-
an_init_pos_descr_ptr ipdp;
16022-
a_type_ptr new_type,
16023-
old_type= skip_typerefs(type_pointed_to(expr->type));
16024-
for (ipdp = aggregate_this_stack; ipdp != NULL; ipdp = ipdp->next) {
16025-
a_type_ptr subobj_type;
16026-
new_expr = make_address_of_init_entity_node(ipdp,
16027-
/*using_as_dest=*/FALSE);
16028-
new_type = skip_typerefs(type_pointed_to(new_expr->type));
16029-
if (identical_types_ignoring_qualifiers(new_type, old_type) ||
16030-
(aggregate_classes_can_have_bases &&
16031-
!class_type_supp(old_type)->is_lambda_closure_class &&
16032-
!class_type_supp(new_type)->is_lambda_closure_class &&
16033-
(subobj_type = subobject_for_class(old_type),
16034-
identical_types_ignoring_qualifiers(new_type, subobj_type)))) {
16035-
found = TRUE;
16036-
new_expr = add_cast_if_necessary(new_expr, expr->type);
16037-
overwrite_node(expr, new_expr);
16038-
break;
16039-
} else if (aggregate_classes_can_have_bases &&
16040-
ipdp->base_class_subobject) {
16041-
/* See if this initialization position has been modified to point
16042-
to a subobject base class. If so, temporarily remove the modifier
16043-
to see if the non-modified type is the "this" type that we're
16044-
looking for. */
16045-
an_init_pos_modifier_ptr save_modifiers = ipdp->modifiers;
16046-
ipdp->modifiers = NULL;
16047-
new_expr = make_address_of_init_entity_node(ipdp,
16048-
/*using_as_dest=*/FALSE);
16049-
new_type = type_pointed_to(new_expr->type);
16050-
ipdp->modifiers = save_modifiers;
16051-
if (identical_types_ignoring_qualifiers(new_type, old_type)) {
16052-
found = TRUE;
16053-
overwrite_node(expr, new_expr);
16054-
break;
16055-
} /* if */
16056-
} /* if */
16057-
} /* for */
16085+
/* We must be lowering an aggregate constant. If no candidate aggregate
16086+
matches, fall back on all the aggregates on the stack. */
16087+
found = replace_param_ref_with_aggregate_this(expr,
16088+
/*candidates_only=*/TRUE) ||
16089+
replace_param_ref_with_aggregate_this(expr,
16090+
/*candidates_only=*/FALSE);
1605816091
} /* if */
1605916092
if (!found) {
1606016093
if (ctor_init_this != NULL) {

‎src/lower_il.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@ typedef struct an_init_pos_descr {
220220
to indicate that the derived class is a complete
221221
object, which allows more efficient addressing of
222222
virtual base classes. */
223+
a_byte_boolean
224+
not_this_candidate;
225+
/* TRUE while an explicitly specified element of the
226+
aggregate that begins at this position is being
227+
lowered. Such an element cannot refer to that
228+
aggregate through an implicit "this" (see
229+
lower_param_ref). */
223230
a_type_ptr base_type;
224231
/* Base entity type. */
225232
an_init_pos_modifier_ptr

‎src/lower_init.c‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,7 @@ Clear an initialization position description entry to default values.
11261126
ipdp->array_element_sequence = FALSE;
11271127
ipdp->base_class_subobject = FALSE;
11281128
ipdp->base_of_complete_object = FALSE;
1129+
ipdp->not_this_candidate = FALSE;
11291130
ipdp->base_type = NULL;
11301131
ipdp->modifiers = NULL;
11311132
ipdp->array_element_count = 0;
@@ -6301,6 +6302,18 @@ Pop the top entry from aggregate_this_stack.
63016302
} /* pop_aggregate_this */
63026303

63036304

6305+
static a_boolean is_anonymous_union_class(a_type_ptr type)
6306+
/*
6307+
Return TRUE if type is the class type of an anonymous union.
6308+
*/
6309+
{
6310+
type = skip_typerefs(type);
6311+
return is_immediate_class_type(type) &&
6312+
class_type_supp(type)->anonymous_union_kind !=
6313+
(an_anonymous_union_kind)auk_none;
6314+
} /* is_anonymous_union_class */
6315+
6316+
63046317
static a_boolean has_aggregate_with_dynamic(a_constant_ptr con)
63056318
/*
63066319
Returns TRUE if the constant includes (at some level) an aggregate that
@@ -6730,6 +6743,17 @@ dealt with).
67306743
ipmp->curr_field = curr_field;
67316744
ipmp->type = curr_field->type;
67326745
} /* if */
6746+
/* An implicit "this" in the initializer of an element refers to this
6747+
aggregate only if the element comes from a default member
6748+
initializer (i.e., it is implicit); an explicitly specified element
6749+
refers to the "this" of the enclosing context, as in:
6750+
struct Y { unsigned k; unsigned n = Y{2, k}.n; };
6751+
where "k" in "Y{2, k}" designates the outer Y, not the temporary.
6752+
The default member initializers of the members of an anonymous
6753+
union refer to the enclosing class, so an anonymous union does not
6754+
affect the search. */
6755+
ipdp->not_this_candidate = !con_ptr->implicit_aggr_element &&
6756+
!is_anonymous_union_class(aggr_type);
67336757
} /* if */
67346758
/* Initialize one member of the aggregate (of type ipmp->type) with
67356759
one constant (con_ptr). */
@@ -7051,7 +7075,10 @@ dealt with).
70517075
if (!aggr_const->is_partially_initialized) {
70527076
(void)recompute_partially_initialized_flag(aggr_const, aggr_type);
70537077
} /* if */
7054-
if (!array_or_vector) pop_aggregate_this();
7078+
if (!array_or_vector) {
7079+
ipdp->not_this_candidate = FALSE;
7080+
pop_aggregate_this();
7081+
} /* if */
70557082
} /* lower_dynamic_init_aggregate_constant */
70567083

70577084
#if USE_PATCH_INIT_STARTUP
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[GH #324]
2+
Implicit "this" in a default member initializer with a nested aggregate
3+
4+
When the default member initializer of an aggregate class contained an
5+
aggregate initialization of another object of the same class, an implicit use
6+
of "this" in the elements of that nested initialization referred to the
7+
nested object instead of to the object being initialized. This affected
8+
initializations that are not folded to constants, such as those in
9+
new-expressions or return statements. For example:
10+
11+
struct Y { unsigned k; unsigned n = Y{2, k}.n; };
12+
Y *p = new Y{1}; // p->n is now 1 (previously 2).
13+
14+
This is now fixed.

‎tests/expectations/edg_x86_64/imported.log‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6814,7 +6814,6 @@ gnu/cpp/cpp1y/pr61636-3.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
68146814
gnu/cpp/cpp1y/pr68180.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
68156815
gnu/cpp/cpp1y/pr79435.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
68166816
gnu/cpp/cpp1y/pr79463.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
6817-
gnu/cpp/cpp1y/pr79937-4.sft.cpp:rp:-DTEST_NUMBER=1 --c++14:FAIL:
68186817
gnu/cpp/cpp1y/sized-dealloc2/test.mft.cpp:fp:-DTEST_NUMBER=1:FAIL:
68196818
gnu/cpp/cpp1y/udlit-char-template-sfinae-neg.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:ABORT:
68206819
gnu/cpp/cpp1y/var-templ20.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eccp -DTEST_NUMBER=1 --c++20 Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./a.out
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eccp -DTEST_NUMBER=2 --c++20 --g++ --gnu_version=150000 Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./a.out
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eccp -DTEST_NUMBER=3 --c++20 --clang Test_name.c

0 commit comments

Comments
 (0)