-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Proposal: Reify dotbraces as bundle-of-locals types #7887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for writing this up, but I don't think this idea is ready for a proposal yet. I do think there's a good idea in here, but this version has some problems. Without pointers, there's no way to pass a mutable record into a function. There's a way to build a pseudo-pointer to a record (by bundling pointers to each field into a tuple), and we could use that as the pointer-to-record type, but it's incompatible with other pointer traits like alignment. Most notably, this means that Which brings me to the last point, this issue is lacking proper motivation. As much as quotes from me are convincing to me, I don't think they will convince the community at large ;). A vague notion that "optimization is easier" isn't really enough to make a decision on. For this idea to be properly considered, there need to be specific examples of optimizations which it enables, and some analysis to show that the current language constructs are unable to bring forth these optimizations. There also need to be examples of where this construct makes sense in real code, and where existing code would be improved with this feature. I do think this idea has a lot of potential. If done right, this could be the type we use to represent optionals, error unions, and maybe even slices. It has the potential to solve the padding problem with optionals, and maybe even to allow certain forms of slices in extern structs, which would be a huge benefit. But it needs some more work before it's ready. |
Aside from dead field elimination, there is also the issue of AoS/SoA: a vector of records could be transformed internally into independent vectors, with no need to manually turn the structure inside-out and no confusion about layout.
Hmm. This is harder. My first thought would be to introduce a bit of comptime's pointer/reference distinction at runtime (i.e. disallow casting pointer-to-record to integer), and have the function return (internally) the new values of mutated fields. We may even be able to apply a similar optimisation to non-record locals if their pointers are never cast to integers. The cost is more work at the callsite to relocate the results correctly, potentially stack strain to pass through all the values, requiring deep introspection to figure out what actually needs to be passed, and a bit of mind-bending; I think it might be feasible, but not a thing to do blindly. |
What I meant by this was to ask for specific arguments about why the current semantics can't accomplish these optimizations, preferably accompanied by disassembly proving that the current semantics cannot manifest these optimizations. Compilers normally try to do destructuring, breaking structs apart into a bunch of fields, like the proposed record. Under what situations does that destructuring fail? Do those situations occur often in real code? Why is this record type the best way to address those problems? Those are the questions that need to be answered to provide a motivation for adding this complexity to the language.
I'm actually working on a proposal like this to try to solve some pointer provenance problems 😄 |
According to @SpexGuy on Discord, struct layout has one of three levels of importance:
2 and 3 are covered exactly by
struct
andpacked struct
respectively, but 1 is left to be covered by 2. This is unfortunate, as@sizeOf
,@offsetOf
and co. return comptime values unless they may be elided in specific circumstances, meaning in many cases layout and size must be computed before semantic analysis is complete -- so, they never have a chance to be optimised.Optimisation could be made much easier if there were a way to explicitly reject reflection and pointer operations, effectively having a way to collectively refer to a disjointed set of values which could be anywhere in memory or even left out entirely if not used. Such a construct would have to be at least as easy to type as
struct
to encourage use, but also a minimal change such that it could be "upgraded" if necessary, as is likely with refactoring.In accordance with #7764, I propose that this construct should be known as
record
, and be written like so:A record has no corresponding
*
type, it has no defined@size
, and its fields have no defined@offsetOf
; attempting any of these is a compile error. The only allowed operations are access, assignment,@field
, passing to/returning from functions, and coercing to structs/records with a strict superset of fields and defaults for all not covered.Records may be passed across function boundaries; the compiler determines how best to do this, and there are no guarantees -- for instance,when returning a record, it may decide to return one member in a register and write another to the result location, or it may use only registers, or use the result location as an ad hoc struct, or accept multiple result locations, or whatever else. This effectively gives us multiple return values, without the semantic and maintenance headache that comes with a naive solution.
A dotbrace with labels is a record with an in-place type. A record may coerce to a struct of any kind if that struct has a superset of its fields, all of the record's field types are coercible to the corresponding struct fields' types, and all other fields of the struct have default values. The compiler is allowed to write the record fields directly to the struct fields if it can determine this will always be done; such is the advantage of having no defined layout. This gives literals a place in the type system without bizarre special casing. (While we're at it, we may consider doing something similar for dotbraces without fields. Let's call those ones
tuple
.)This will allow us to maintain maximum organisation without accidentally preventing optimisation. It slightly increases language entropy in places, but decreases it in others (dotbraces are now well-defined without being overbearing). I feel that this is a worthwhile change.
The text was updated successfully, but these errors were encountered: