You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using WIT to describe some existing interface, it is sometimes required
to have enums with specific values. But there is no way to express this in WIT
currently.
Think of bitflags,for example:
we have something simple like this (C)
#define FOO 0x2
#define BAR 0x4
int some_func(int arg) {
if (arg == 1) return FOO; else return BAR;
}
Or your function returns NEGATIVE constants (ERR_X = -1, ERR_Y =-33, ERR_Z = -22)
Or maybe your interface has some magic constants like MY_BETTER_PI=3.44 that needs to be passed around
or may be returned from functions.
I understand that WIT is about TYPES, but plain types are just not enough to describe an interface.
Because currently WIT definition is the only point where your host application can provide this data
to unknown number of its clients in unknown to the host languages.
Otherwise, we are back to days when we had to parse C includes to be able to use FFI.
Hi, this is a good question so thanks for filing an issue and sorry for the slow reply (last week was the in-person CG meeting)!
So coincidentally, with the recently-merged #336, I think we have a Component Model primitive that matches what you're describing. In particular, when defining an interface, in WAT I can write:
#336 didn't add WIT-level syntax to capture this new functionality, but we should before too long, and so you could imagine that in WIT you can write something like:
Now, an obvious improvement that you might want is a more direct typed connection between foo/bar and the return-type of some_func, which might be nice for bindings generators to, e.g., scope the constants. Now of course you can get this with an enum:
interface whatever {
enum e { foo, bar };
some_func: func(arg: u32) -> e;
}
and a C code generator can generate #defines for foo and bar so you can get the raw integer values. But now you've lost the ability to control the specific integer values. However, if we were to allow enums to declare arbitrary integral values, that opens up a tricky question: does the CABI guarantee that only valid enum values are passed? If "no", than it's not really an enum, it's really just some integer type with any allowable integer value. If "yes", then the CABI has to do some possibly-expensive validation logic and also there are legitimate reasons for an interface author to intentionally allow more values than just those explicitly declared. While I can imagine a new type that works like an integer with associated constant values, I wonder if it's worth it when it seems like the above value definitions (which are scoped, btw, by the interface) is Good Enough, at least for the time being.
I would also argue that integer mappings of enum values are not an interface concern but one of lowering to a specific language, and only for those languages that actually expose enums as ints in the first place.
Yeah, I think that's why we should consider this entirely separate from enums; if there is a new feature to consider adding here, it's some sort of specialization of an integer with a named subset of values.
I agree that if you set values in enums, it is not a enum longer and they should not be extended this way.
Still we need some way to set named values in the interface description, along with the type. How this will
be translated to target language is a task for the specific generator. Obvious candidates are named constants.
People may want to define versions, dates, cryptographic values, and other various numbers that are required to
use the declared interface.
Not sure though how far this can go - we start with numeric constants and at some day we may end up with arrays,
records and objects with constructors. Maybe this should be different language that describes data.
As this came up today as one of the helpful features for interfacing with existing C/++ code, I would like to bring up the question of the intended WIT syntax again.
Do I guess correctly that foo: u32 = 2; would be the right syntax for this?
This feature is also needed when implementing some protocols.
It is not up to me to decide what the enumeration value is, but to follow the existing protocol.
Of course, some conventions can be added:
1.If an enumeration has a value, then all enumerations must have values.
2.No enumeration value means that the enumeration value is not important, in principle, it can be assigned 0..n.
This feature is also needed when implementing some protocols.
It is not up to me to decide what the enumeration value is, but to follow the existing protocol.
Of course, some conventions can be added:
1.If an enumeration has a value, then all enumerations must have values. 2.No enumeration value means that the enumeration value is not important, in principle, it can be assigned 0..n.
I try to find a good mapping for C style enums which assign the numbers in a non-contiguous way, often seen in protocol specifications. Luke's argument that in the runtime you might not be able to quickly check whether a value is valid convinced me that indeed an enum is not the right WIT equivalent but some integer.
So likely
type port = u16;
http: port = 80;
pop3: port = 110;
connect: func(port: port) -> result<connection>;
is a good fit for this mechanism.
As for constant values, I think it is not necessary after having #235.
I feel that both are equally necessary, and originate from different binding languages: These enum like types are needed for C/C++/Rust bindings which specify the value/discriminant and the getter abstraction benefits Java(Script)/C# and similar bindings.
Can we split enums and constants into two issues, discuss them separately, and set up feature-gates separately?
I realize that they are very different, and we should focus on constants here.
I am confused, I think I proposed a constant syntax for WIT and found that they nicely solve my immediate needs for enum values.
Discussing constant syntax here and splitting off enums would recommend to rename this issue to "constant syntax in WIT" and open a new issue with this title, right?
PS: I will probably take a try at implementing above syntax in wasm-tools and wit-bindgen.
Do I guess correctly that foo: u32 = 2; would be the right syntax for this?
That looks good to me! Feel free to create a PR extending the grammar in WIT.md or, if you get something working in wit-bindgen first, let know and I can create the spec PR.
Activity
lukewagner commentedon Jun 10, 2024
Hi, this is a good question so thanks for filing an issue and sorry for the slow reply (last week was the in-person CG meeting)!
So coincidentally, with the recently-merged #336, I think we have a Component Model primitive that matches what you're describing. In particular, when defining an interface, in WAT I can write:
#336 didn't add WIT-level syntax to capture this new functionality, but we should before too long, and so you could imagine that in WIT you can write something like:
Now, an obvious improvement that you might want is a more direct typed connection between
foo/barand the return-type ofsome_func, which might be nice for bindings generators to, e.g., scope the constants. Now of course you can get this with anenum:and a C code generator can generate
#defines forfooandbarso you can get the raw integer values. But now you've lost the ability to control the specific integer values. However, if we were to allowenums to declare arbitrary integral values, that opens up a tricky question: does the CABI guarantee that only validenumvalues are passed? If "no", than it's not really anenum, it's really just some integer type with any allowable integer value. If "yes", then the CABI has to do some possibly-expensive validation logic and also there are legitimate reasons for an interface author to intentionally allow more values than just those explicitly declared. While I can imagine a new type that works like an integer with associated constant values, I wonder if it's worth it when it seems like the above value definitions (which are scoped, btw, by theinterface) is Good Enough, at least for the time being.rossberg commentedon Jun 10, 2024
I would also argue that integer mappings of enum values are not an interface concern but one of lowering to a specific language, and only for those languages that actually expose enums as ints in the first place.
lukewagner commentedon Jun 10, 2024
Yeah, I think that's why we should consider this entirely separate from
enums; if there is a new feature to consider adding here, it's some sort of specialization of an integer with a named subset of values.vlhomutov commentedon Jun 10, 2024
I agree that if you set values in enums, it is not a enum longer and they should not be extended this way.
Still we need some way to set named values in the interface description, along with the type. How this will
be translated to target language is a task for the specific generator. Obvious candidates are named constants.
People may want to define versions, dates, cryptographic values, and other various numbers that are required to
use the declared interface.
Not sure though how far this can go - we start with numeric constants and at some day we may end up with arrays,
records and objects with constructors. Maybe this should be different language that describes data.
lukewagner commentedon Jun 10, 2024
Did the middle code snippet in my first comment make sense, or were you imagining something else?
vlhomutov commentedon Jun 11, 2024
Yes, absolutely. Maybe even "u32 bar: 42", so we have control over types sizes.
lukewagner commentedon Jun 11, 2024
Oops, yes, you're right; the type would be necessary as part of the value definition in the WIT too.
cpetig commentedon Feb 4, 2025
As this came up today as one of the helpful features for interfacing with existing C/++ code, I would like to bring up the question of the intended WIT syntax again.
Do I guess correctly that
foo: u32 = 2;would be the right syntax for this?oovm commentedon Feb 5, 2025
This feature is also needed when implementing some protocols.
It is not up to me to decide what the enumeration value is, but to follow the existing protocol.
Of course, some conventions can be added:
1.If an enumeration has a value, then all enumerations must have values.
2.No enumeration value means that the enumeration value is not important, in principle, it can be assigned
0..n.oovm commentedon Feb 5, 2025
As for constant values, I think it is not necessary after having static getter.
The static getter system can also add additional properties such as
lazy,once, andthread_local.Overall, the functionality and extensibility are higher than the constant system.
cpetig commentedon Feb 5, 2025
I try to find a good mapping for C style enums which assign the numbers in a non-contiguous way, often seen in protocol specifications. Luke's argument that in the runtime you might not be able to quickly check whether a value is valid convinced me that indeed an enum is not the right WIT equivalent but some integer.
So likely
is a good fit for this mechanism.
I feel that both are equally necessary, and originate from different binding languages: These enum like types are needed for C/C++/Rust bindings which specify the value/discriminant and the getter abstraction benefits Java(Script)/C# and similar bindings.
oovm commentedon Feb 6, 2025
Can we split enums and constants into two issues, discuss them separately, and set up feature-gates separately?
I realize that they are very different, and we should focus on constants here.
cpetig commentedon Feb 6, 2025
I am confused, I think I proposed a constant syntax for WIT and found that they nicely solve my immediate needs for enum values.
Discussing constant syntax here and splitting off enums would recommend to rename this issue to "constant syntax in WIT" and open a new issue with this title, right?
PS: I will probably take a try at implementing above syntax in wasm-tools and wit-bindgen.
cpetig commentedon Feb 6, 2025
PPS: I now got it, I never really realized that the title mentions both types, do you feel that this solution isn't suitable for both?
lukewagner commentedon Feb 6, 2025
@cpetig
That looks good to me! Feel free to create a PR extending the grammar in WIT.md or, if you get something working in wit-bindgen first, let know and I can create the spec PR.