Skip to content

Bind an NSDMI's implicit this to the constructed object in a constructor init [GH #324] - #540

Draft
Momen-G-Ar wants to merge 1 commit into
edgcpp:mainfrom
Momen-G-Ar:gh-324-ctor-init-this
Draft

Momen-G-Ar wants to merge 1 commit into
edgcpp:mainfrom
Momen-G-Ar:gh-324-ctor-init-this

Conversation

@Momen-G-Ar

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #408 for discussion #324. #408 fixed the implicit this of a default member initializer that aggregate-initializes another object of the same class, but only when the default member initializer is used by an aggregate initialization. When it is used by a constructor, this still bound to the nested temporary:

struct Y { unsigned k = 7; unsigned n = Y{2, k}.n; };  // n = this->k
struct D : Y { D() {} };
int main() {
  if (D().n != 7) abort();  // EDG: D().n == 2
}

k here is (*this).k ([class.mfct.non-static]/3, [expr.prim.id.general] in C++23). this names the object whose non-static data member's initializer is being evaluated ([expr.prim.this]/1), and Y's constructor initializes n from its default member initializer ([class.base.init]/9, [class.default.ctor]/4). So n is 7. g++ 15 and Clang 21 give 7 in every mode.

Initialization Before After
Y y;, new Y, Y() with constant initializers (folded to {7U, 7U} by the front end) correct unchanged
Y{}, D() : Y{} {} (aggregate initialization, fixed by #408) correct unchanged
Y's constructor is called: D(), D() : Y() {}, a member Y y; of a class with a user-provided constructor, an array member, new Y[2], a class template, an aggregate with a base class this bound to the nested Y{2, k} correct
A non-constant initializer (unsigned k = seven; with volatile unsigned seven), so even Y y; runs the constructor this bound to the nested Y{2, k} correct

Same result in strict, --g++ and --clang modes, C++14 through C++23. EDG 6.5, 6.7, 6.8, 6.9 and 7.0 all have the bug too.

Root cause

lower_param_ref (lower_il.c) replaces the enk_param_ref for this in this order:

  1. the innermost matching candidate on aggregate_this_stack (Bind an NSDMI's implicit this to the right aggregate [GH #324] #408 skips an aggregate while one of its explicit elements is lowered),
  2. any matching entry on the stack, including the skipped ones,
  3. ctor_init_this, the this parameter of the constructor init being lowered (lower_ctor_init, lower_init.c).

In Y's constructor, lowering n = Y{2, k}.n pushes the temporary for Y{2, k}, and that temporary is skipped while its explicit element k is lowered. Step 1 finds nothing, and step 2 then takes the skipped temporary, so step 3 is never reached. The lowered constructor read __T.n = __T.k; instead of __T.n = this->k;. This already happened before #408.

Change

lower_param_ref now tries the this of the constructor init after the candidate search and before the search that includes the skipped aggregates (only when its type matches, as the old assertion required). While a constructor init is lowered, every entry on the stack was pushed by that constructor init (constructor inits don't nest), and a skipped entry is never what an explicit element's this refers to. So the constructor's this is right whenever no candidate matches. The search that includes the skipped aggregates stays as the last resort outside constructor inits, as before.

  • lower_il.c: reordered lower_param_ref and updated its header comment.
  • lower_init.c: comment at the not_this_candidate assignment.

Changes entry: src/pending_changes/gh-324-nsdmi-this-ctor-init.txt (passes edg-collect-changes --check). Based on main at cb7d105. The commit message passes edg-check-commit-messages.

Tests

Test Type Checks
regressions/gh-324-ctor-init.sft.cpp rp 40 checks: implicit constructor paths (local, global, static, new Y, new Y(), Y()); non-constant initializers (volatile, a function call); Y's constructor called through a derived class, : Y(), a member, an array member, a local array, new Y[2]; this->k, nested Y{2, Y{3, k}.n}, through a static member function (by value and by const &), a lambda capturing this, designated initializers (C++20); an aggregate with a base class (C++17); class templates; controls that must not change (Y{}, : Y{}, Y a{1}, the PR c++/100252 shape). The lines that were wrong on main are marked // Previously 2. Options: --c++14, --c++17, --c++20, --c++20 --g++ --gnu_version=150000, --c++20 --clang, --c++23
  • The new test fails on main (17, 18 or 19 failed checks, depending on the mode) and passes with this change on edg_x86_64 and edg_x86_64_cp, in all six option sets. g++ 15 and Clang 21 pass the same checks with -std=c++14 through c++23.
  • The recordings were created with edgy -W, and the default.* files were reviewed.
  • 138 related tests (regressions/gh-324, the NSDMI changes tests, GCC's pr79937-*, nsdmi*, nsdmi-aggr* and constexpr-nsdmi*, cmeerw/gnats-25426, regressions/gh-90-warn) match their expectations on edg_x86_64 and edg_x86_64_cp: no new failures, no unexpected passes.
  • Full suite on GitHub Actions (Build & Test CI, all four configurations).

Discussion: #324 (follow-up to #408)

…tor init [GH edgcpp#324]

When a default member initializer that aggregate-initializes another
object of the same class was used by a constructor, lower_param_ref
searched every aggregate on aggregate_this_stack, including the ones
skipped because one of their explicit elements was being lowered, before
it tried the "this" of the constructor init:

  struct Y { unsigned k = 7; unsigned n = Y{2, k}.n; };
  struct D : Y { D() {} };  // D().n was 2, not 7.

The "this" of the constructor init is now used before the skipped
aggregates are searched.
@coderabbitai

coderabbitai Bot commented Nov 14, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@edgcpp-bot

Copy link
Copy Markdown
Collaborator

AcknowlEDG review results updated for 6e2811db851c:

@edgcpp-bot

Copy link
Copy Markdown
Collaborator

AcknowlEDG review results updated for 6e2811db851c:

CI success.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants