Repository navigation
Take no .init of the element type in ElementType, nor of the range in each - #11143
ehudt-weka wants to merge 2 commits into
Conversation
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. The range primitives traits, std.traits (CommonType, isIterable, ForeachType, hasElaborateAssign, the *TypeOf alias-this traits, ...), and constraints in std.algorithm, std.array and std.typecons used it only to form an expression of type `T`. They now use lvalueOf and rvalueOf, so their compile-time memory and time no longer grow with the size of `T`. CommonType keeps `.init` for operands that are not a struct or union, since null and array literals merge differently in `?:` than values of the type do. Answers are unchanged, except for a type that declares a member named `init`: `T.init` named that member, so the traits answered for the member instead of the type.
…in each ElementType and isBidirectionalRange read the element type as `typeof(R.init.front.init)` / `typeof(R.init.back.init)`, and each's constraint tested its function on `R.init.front`. They now use `frontOf(lvalueOf!R)` / `backOf(lvalueOf!R)`, private functions returning `r.front` / `r.back`, and `lvalueOf!R.front`, so no default-initializer literal of the range or element type is built. This changes three kinds of answers, listed in the changelog: each with a `ref` parameter on a forward range whose `front` is a field, ElementType of an enum whose base type is a non-string array, and types that declare a member named `init`.
|
Why are you pursuing this instead of optimising |
|
I prototyped that in dmd. When It changes no answers: 64,860 trait queries over 629 types give the same output as master, and so do dmd's 3,642 It does not replace #11119, though. Only 2 of the 49 uses #11119 replaces have the form If you would rather have the dmd change, I can open it and reduce this PR to |
|
Ok, you have established that phobos needs to change. Although I wouldn't say no to also having the optimisation, but not required. |
…683364 Brings in the review fixes of dlang#11119 and dlang#11143. `ifTestable` on a struct or union: when `pred` does not take a `T`, try an rvalue of each other qualifier of `T`, since the unique literal `T.init` converted to all of them. A struct with indirections and a `const` or `immutable` predicate parameter is testable again, as before this change. Tests for `isIterable`/`ForeachType` (`ref @safe` opApply, non-copyable range), the range traits without `@property`, a member named `init`, an enum based on an array, `each` with a `ref` parameter on a field `front`, `Chunks` over an infinite range in `@safe pure` code, and qualified `isTuple`. Comments reworded.
Stacked on #11119: the first commit is #11119's, and this PR's change is the second commit.
This PR holds the replacements of
.initthat change answers, split out of #11119 as asked inreview there.
Rationale
After #11119, three
.inituses remain in the range traits andeach:ElementTypereads the element type astypeof(lvalueOf!R.front.init), andtypeof(R.init.front.init)for an enum.isBidirectionalRangecomparestypeof(lvalueOf!R.back.init)with it.eachtests its function onR.init.front.For a struct or a static array,
.initbuilds the type's whole default-initializer literal,so these grow with the size of the element type, or of the range for
each. This PR replacesthem:
ElementTypeandisBidirectionalRangeread the element type through two private helpers,frontOfandbackOf, which areauto reffunctions returningr.frontandr.back. In areturn statement a non-
@propertyfront()is called; a baretypeof(lvalueOf!R.front)would yield the function type instead. A field or
refresult is returned by reference, soa non-copyable element type still gets an answer.
eachtests its function onlvalueOf!R.front.Changed answers
The differential check described in #11119 (629 types, 64,860 answers) finds 210 answers that
differ between #11119 and this PR, in three groups. The changelog entry documents all three.
eachwith arefparameter on a forward range whosefrontis a field now compiles, asit already did on arrays and input ranges (3 answers).
R.init.frontis an rvalue onlybecause
R.initis a literal;lvalueOf!R.frontis an lvalue.ElementTypeandElementEncodingTypeof an enum whose base type is a non-string array(
enum E : int[],enum E : int[3],enum E : const(int)[]) are nowvoid(45 answers,with the qualifier variants). They were
inout(int), a type no value ofEproduces: avariable of
Ehas nofront, andisInputRange!Eis false on both.foreachoverE,ForeachType!E(int) andeachwith an untyped function do not change. Code that namesElementType!E, such as the check'seachwith animmutable ElementType!Eparameter, nolonger compiles.
Where the element type declares a member named
init,ElementTypewas the type of thatmember:
void(int v)for a member function,intfor a fieldint init. It is now theelement type. What builds on
ElementTypefollows (162 answers):mapover it has
opIndex.appender!(S[])now compiles; its element type comes fromElementEncodingType.Swith a fieldint init,isInputRange!(S[], int)is now false.ElementTypeof such an array, such as a function with animmutable ElementType!Rparameter passed tomap,eachorminElement, now compiles.Where the range type itself declares a member named
init,eachnow accepts it, sinceits constraint no longer reads
R.init.front.Measurements
Same setup and benchmarks as in #11119 (x86_64 dmd 7023163b4, same druntime imports, peak RSS,
3 runs each). Only the rows that change are listed; time does not change beyond run-to-run
noise outside the large types.
a.map!(e => e.x).sum, N = 2^22minElement+maxElement, N = 2^22appender!(Huge[])put, N = 2^22On ordinary code, peak RSS over #11119: Phobos (the dmd perf-check workload) 587–589 MiB
against 587–588 MiB,
ctRegexunittests unchanged, and-unittest -o-of 16 Phobos modules+0.0% to +0.9% (
std.range), median +0.2%. The increase comes from thefrontOfandbackOfinstances, one per inspected type.
Testing
.initof the inspected type in traits #11119:eachwith arefparameter on a forward range whosefrontis a field.ElementTypeandisRandomAccessRangeon an array of a struct whoseinitmember comesfrom a mixin template.
ElementTypeof an enum whose base type isint[].make unittest(release, x86_64) on macOS under Rosetta 2, as described in Take no.initof the inspected type in traits #11119: the same119 modules pass and the same 10 fail as on master, and all touched modules pass.
Pre-review checklist
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. The self-review checked above is the agent's own (the tests,
the differential check and the measurements), not a human code review.