WebAssembly / interface-types

Other
641 stars 57 forks source link

Define a "flags" interface type? #125

Closed lachlansneff closed 3 years ago

lachlansneff commented 3 years ago

I'm not sure how widely used this would be outside of apis like WASI, but it might be very useful in those cases.

Suggestion: Define a "flags" interface type that represents a strongly-typed bitflag.

intertype ::= f32 | f64
            | s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64
            | char
            | (list <intertype>)
            | (record (field <name> <id>? <intertype>)*)
            | (variant (case <name> <id>? <intertype>?)*)
            | (flags (flag <name>)*)

There would need to be specific instructions for lifting and lowering flags. They'd probably be stored on either side as integers with bitflags, but wouldn't necessarily have to be. This would also avoid backwards-compatibility issues because there could be any number of flags without deciding on a common representation. (e.g. one side could lower from a list of strings and the other side could lift into a bitflag.)

fgmccabe commented 3 years ago

Perhaps for a later post MAP* release? It is quite possible today to model this as a record of boolean fields.

J0eCool commented 3 years ago

In particular it should be possible to have the C++

enum Opt {
  A = 1 << 0,
  B = 1 << 1,
  C = 1 << 2,
};

translate to an IT

(type $Opt
  (record
    (field $A bool)
    (field $B bool)
    (field $C bool)
  )
)

and be lifted/lowered with bitwise logic in the adapters, so a C++ function signature of

Opt foo(Opt);

would work just fine.

Designing flags as a first-class IT type might be tricky though, because of edge cases I haven't thought of. How do adapters handle out-of-range flags? Can we represent A | B constructs? Should we? I'm not sure at this time.

lukewagner commented 3 years ago

I was thinking about the WASI use case too, but it fell off my radar, so thanks for bringing it up! I think this use case could be addressed with minimal effort by expressing flags as an abbreviation:

(flags <name>*) ≡ (record (field <name> bool)*)

Thus, standard record.lift/record.lower could be used to efficiently unpack/pack an i32 (or i64, or whatever other core tuple of types the compiler wants).

lachlansneff commented 3 years ago

@lukewagner That seems fine to me. I keep forgetting that the interface types don't actually "exist" and simply enforce that that data must fit roughly into a certain structure.

lukewagner commented 3 years ago

Hah, yes, they exist only to be eliminated by fusion :)

lukewagner commented 3 years ago

(Oops, I didn't mean to let this drop.) Given that I think there is a clear use case for this in WASI and it's a low-cost addition (via abbreviation), I tentatively added flags. In any case, we can slice-and-dice what exactly is in the MAP closer to a release.