Skip to content

druntime: take no .init of the inspected type in traits - #24072

Merged
thewilsonator merged 1 commit into
dlang:masterfrom
ehudt-weka:no-init-in-traits
Oct 18, 2026
Merged

thewilsonator merged 1 commit into
dlang:masterfrom
ehudt-weka:no-init-in-traits

Conversation

@ehudt-weka

Copy link
Copy Markdown
Contributor

Rationale

For a struct or a static array, an expression T.init makes the compiler build the type's
whole default-initializer literal, with one element expression per static-array element. A
trait that writes S.init.opAssign(...) only to get an expression of type S therefore
costs memory and time in proportion to the size of S. With a ubyte[1 << 20] field, a few
druntime trait queries take 0.85 s and 303 MiB.

lvalueOf!T from core.internal.traits is a declared, never-defined function returning
ref T. It gives an expression of the same type without building any literal. This PR uses it
at every druntime site where .init of the inspected type served only that purpose:

File Site Before After
core/internal/traits.d hasElaborateMove S.init.opPostMove(...) lvalueOf!S.opPostMove(...)
core/internal/traits.d hasElaborateAssign S.init.opAssign(...) lvalueOf!S.opAssign(...)
core/internal/traits.d AliasThisTypeOf (used by DynamicArrayTypeOf) __traits(getMember, T.init, ...) __traits(getMember, lvalueOf!T, ...)
core/internal/moving.d __move_post_blt, opPostMove signature check S.init.opPostMove(...) lvalueOf!S.opPostMove(...)
core/lifetime.d hasContextPointers (static arrays) typeof(T.init[0]) typeof(lvalueOf!T[0])

No trait answer changes. The companion Phobos PR, dlang/phobos#11119, makes the same change in
std.traits, std.range.primitives and their users. The two PRs do not depend on each other.

Measurements

These were measured before a rebase onto current master that left the diff unchanged. An
x86_64 dmd built from master e64474adc was used for both trees (macOS, -o-, peak RSS from
/usr/bin/time -l).

hasElaborateAssign, hasElaborateMove and core.lifetime.move on structs holding a
ubyte[N]:

N master this PR
2^16 0.43 s, 48 MiB 0.06 s, 35 MiB
2^20 0.85 s, 303 MiB 0.07 s, 36 MiB
2^22 not run 0.07 s, 40 MiB

On ordinary code there is no measurable effect. On the two Phobos workloads of the dmd perf
check, with Phobos master and only druntime changed, peak RSS was 581–582 MiB before and after
for dmd -c -i=std -preview=dip1000 std/package.d. For the ctRegex unittests it was
2051–2054 MiB before and 2053–2054 MiB after. Time was equal within run-to-run noise (3 runs
each).

Testing

All of this ran at the current revision, with an x86_64 dmd built from master 7023163b4
(macOS, under Rosetta 2).

  • New unittest for DynamicArrayTypeOf, which goes through AliasThisTypeOf. It covers a
    plain slice, alias this to a slice, a const alias-this struct, nested alias this, and
    alias this to a static array, which is rejected.
  • make -C druntime unittest (release, x86_64): all 76 modules pass on master and on this PR.
    The test/ directories pass on both trees, test/traits and test/lifetime among them,
    except seven that fail identically on both because of this environment (GNU make 3.81
    shipped with macOS, the local C++ toolchain).
  • I wrote a differential check that prints the answer of every changed trait for about 100
    types, each in 5 qualifier variants: structs with and without indirections, destructors,
    postblits, copy constructors, disabled copy, default construction and assignment, alias
    this to every builtin kind, unions, enums, classes, and ranges. Compiled against master and
    against this PR (with the Phobos PR applied), hasElaborateAssign, hasElaborateMove,
    DynamicArrayTypeOf, move and moveEmplace give the same answers on both trees. The
    Phobos traits that the companion PR changes are covered in that PR.

A regression test that bounds compile-time memory is not practical in the test suite, so no
test exercises the size directly.

LLM/AI disclosure

This PR was written entirely by an LLM/AI.

An AI coding agent (Claude, by Anthropic) wrote the code, the tests and this description, ran
the checks and measurements in it, and opened this PR at @ehudt-weka's request, from that
account; "I" above is the agent. Its only review so far is the agent's own (the tests, the
differential check and the measurements), not a human code review.

For a struct or a static array, `T.init` makes the compiler build the
type's whole default-initializer literal, one entry per static-array
element. hasElaborateAssign, hasElaborateMove, AliasThisTypeOf (behind
DynamicArrayTypeOf), __move_post_blt's opPostMove check and
core.lifetime.hasContextPointers used it only to form an expression of
type `T`. They now use lvalueOf, so their compile-time memory and time no
longer grow with the size of `T`.
@dkorpel

dkorpel commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Please refer to principle 2 in https://drive.google.com/file/d/1Z1RA5o8jPkQ3UQ7umimKI3akxx7nKgBy/view

Human understanding and review. Code must not be merged merely because an AI system produced
something that appears to work. The contributor and reviewers must be able to understand, evaluate, test,
and maintain it.

@thewilsonator

Copy link
Copy Markdown
Contributor

Please refer to principle 2 in

Are you referring to me or Ehudt here?
I understand perfectly well what this is doing and why.
Also there is no change of behaviour where unlike the phobos one dlang/phobos#11119 which is still in draft.

@dkorpel

dkorpel commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Referring to

Its only review so far is the agent's own (...), not a human code review.

If you take responsibility for understanding and maintaining it, then that's fine by me

@thewilsonator
thewilsonator merged commit b5c7166 into dlang:master Oct 18, 2026
55 of 57 checks passed
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.

3 participants