-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Joe: Things will break currently if an open variant type is compiled with different instances in different compilation units. This is dumb.
- Open variants should be given a different syntactic form from closed variants, perhaps:
variant Open (A, B, ..); // Type is publicly open
variant Open (A, B, private ..); // Type is public, but only I get to extend it
variant Closed (A, B); // Type is closed
-
This will allow closed and privately-open variants to retain their current cheap representation.
-
The tags for open variants should be pointers to dummy symbols or something similar that will be unique at link time.
-
Dispatch on open variants will need to be codegenned in a way that allows other units to extend the set of cases. This may not be possible without whole-program chicanery. It should at least be a link-time error if a dispatch table is missing entries for an added member type.
Some notes on externally-open variant implementation:
-
To properly support ABI-stable open variants, the hooks for overloading variant representation and dispatch via
variantReprType,dispatchTag, anddispatchIndexwill probably need to be sealed off for open variants. Introspection primitives likeVariantMember*should be disabled or made runtime-only for open variants so that compile-time code can't introduce dependencies on the static set of variant members. -
For every type that's used as an open variant member, there'd be a global
charvariable withweak_odrlinkage. The LLVM definition would look something like this:
@"foo.Foo[bar.Bar, bas.Bas] clay variant tag" = weak_odr constant i8 0
-
The address of this global can then be used as a globally-unique tag value for variants containing values of that type, with the linker uniquing the symbol across modules because of the weak_odr linkage.
-
The layout of an open variant would be an { i8*, i8* } pair, with the first pointer being the address of a tag global, and the second being a pointer to dynamically-allocated memory storing the value. The memory buffer would be allocated/moved/copied/destroyed by the value semantics of the variant type. (There might be other approaches to variant layout to try—perhaps provide a fixed amount of internal storage, and store small member types internally and dynamically allocate larger members.)
-
Dispatch is tricky. Instead of generating a switch inline as it currently does, the jump table will need to be set up in a way that can be extended—perhaps using global arrays with appending linkage. There also needs to be some coordination between compilation units so that a compilation unit that extends a variant then provides new overloads for a dispatched-on function can instantiate the needed instance and append it to the necessary jump tables.
Another benefit of ABI-stable open variants is that a variant could be cast freely to another superset or (after checking membership) subset variant type. When open variants are declared as members of other open variants (as with exceptions), the two levels of variant could share representation without needing nesting of tags and dispatch.