chipsalliance / firrtl-spec

The specification for the FIRRTL language
38 stars 27 forks source link

are zero field/variant enum/bundle types legal #208

Open programmerjake opened 4 months ago

programmerjake commented 4 months ago

afaict the specification doesn't specify if you can have an enumeration type with no variants {||} (the grammar allows it, but I'm not sure if that's intentional, since uninhabited types behave very oddly). The specification says you can have a bundle type with no fields but the grammar doesn't allow it:

https://github.com/chipsalliance/firrtl-spec/blob/9b0357e91d5a26c816e7b488fa20491ee2d93aeb/spec.md?plain=1#L4406-L4415

mmaloney-sf commented 4 months ago

This is a fair point.

Presumably, it should work as a 0-width type. (Just like UInt<0>). But I don't know where the firtool implementation sits with this.

jackkoenig commented 4 months ago

While a Bundle with no fields is a zero-width type, an enumeration would be a zero-width type with one variant. I don't know if firtool implements it either but it's a bit different than UInt<0> since the numerical value of this 0-bit value is user-defined rather than 0 (I know you @mmaloney-sf are just saying it should work, not that it's the same, I just suspect the correct behavior isn't implemented yet). I'm not sure what a zero-variant enumeration is since it would need log2(0) bits to represent it 🤯.

mmaloney-sf commented 4 months ago

Ah. I made a mistake here.

The bitwidth of a type should be log2 of the number of values it supports. Eg, a UInt<1> has two values, UInt<1>(0) and UInt<1>(1), and thus has a bitwidth of log2(2) = 1.

So the bitwidth of an empty type (such as an empty enum) should be undefined, as log2(0) is undefined.

So yeah. Scratch what I said earlier.

programmerjake commented 4 months ago

In Rust, zero-variant enums are generally size zero (though not currently guaranteed). they are types which can not ever have any values of that type, so they're used for code paths that never return (not very applicable to firrtl) or can't happen (somewhat useful in firrtl).

e.g. in Rust you can have Result<MyTy, Infallible> to indicate a type which can never be the error variant. the firrtl equivalent would be {|Ok: MyTy, Err: {||}|}

see docs for Result and Infallible

that kind of enum would be useful in generic code where you instantiate some generic module that can handle an error, e.g. by propagating the error value to some output, but in your specific use it never will handle an error so supplying an empty enum type as the error type can help by making firrtl automatically remove all error handling code and data.