blob: 2eb1e10fe782e03b67dde1ef5b3892a36c914118 [file] [log] [blame] [view]
# [FTP](../README.md)-025: Bit Flags
_Just a Little Bit_
Field | Value
----------|--------------------------
Status | Accepted
Authors | kulakowski@google.com
Submitted | 2019-01-09
Reviewed | 2019-01-24
# Summary
Extend the FIDL language with bit flag declarations.
# Motivation
There are several use cases for describing a set of flags over an integer in
FIDL.
Currently, users of FIDL are advised to make a set of constants of the same
underlying type.
Because these are all independent, creation of invalid values cannot be
detected by the bindings at runtime.
# Design
## Source language Changes
This proposal adds the `bits` keyword to FIDL.
`bits` introduce a top-level declaration, similar to `enum`s.
Formally, the productions in the grammar are as follows:
```
bits-declaration = ( attribute-list ) , "bits" , IDENTIFIER , ( ":" , type-constructor ) ,
"{" , ( bits-or-enum-member , ";" )+ , "}" ; [NOTE 1]
bits-or-enum-member = ( attribute-list ) , IDENTIFIER , ( "=" , bits-or-enum-member-value ) ;
bits-or-enum-member-value = IDENTIFIER | literal ; [NOTE 2]
```
Notes:
1. The `bits-declaration` allows the more liberal `type-constructor` in the grammar, but
the compiler limits this to unsigned integer types, see [primitives].
2. The `bits-or-enum-member-value` allows the more liberal `literal` in the grammar, but the compiler limits this to:
* A `NUMERIC-LITERAL` in the context of an `enum`;
* A `NUMERIC-LITERAL` which must be a power of two, in the context of a `bits`.
Each member in a `bits` declaration is a power of two.
This proposal suggests not allowing more complicated expressions in the `bits`
declaration itself, nor allowing them to be ORed together in `bits` constant
expressions, for the sake of simplicity.
They could be added to `bits` declarations in the future.
An example of a `bits` declaration, taken from constants currently in the
fuchsia.io library:
```fidl
bits OpenRights : uint32 {
READABLE = 0x00000001;
WRITABLE = 0x00000002;
ADMIN = 0x00000004;
};
```
Furthermore, this proposal adds a binary literal syntax like so:
```fidl
bits OpenRights : uint32 {
READABLE = 0b0001;
WRITABLE = 0b0010;
ADMIN = 0b0100;
};
```
## Semantics
Overflowing the underlying integer type is a compilation error.
Each `bits` member value must be distinct.
Serializing or deserializing a `bits` value which has a bit set that is not a
member of the `bits` declaration is a validation error.
The semantics of `bits` are distinct from `enum`.
An `enum` value must be exactly one of the values declared in FIDL, while
`bits` may not.
For instance, if `OpenRights` were an `enum`, the only valid values to send
would be `1`, `2`, and `4`.
As a `bits` type, though, `0`, `3`, `5`, `6`, and `7` are all also valid.
## Bindings
Each language binding will be extended to handle these values idiomatically.
At worst, this simply generates a constant for each `bits` member as though it
were an integral constant of the underlying type.
The wire format for a `bits` value is the same as the underlying integral value.
Serializing and deserializing code should ensure that the value is a subset of
the described bits.
For instance, attempting to use 8 as an `OpenRights` value should fail validation.
## Signal and Rights Constants
I also propose adding signal and handle right values to the `zx` library.
This includes a `bits` declaration of all the signal value and rights, and
possibly a set of constants with default rights for each handle type.
# Implementation strategy
## Phase 1
Add all of the source changes to the FIDL compiler, including parser tests.
## Phase 2
Add support to all language bindings, and to the compatibility test suite.
## Phase 3
Migrate existing pile-of-int-constants to `bits`.
# Ergonomics
This change makes FIDL more ergonomic, by letting users explicitly express
their intention, and readers see the explicit grouping.
# Documentation and examples
This proposal describes changes to the FIDL grammar above.
I would tweak the FIDL tutorial to include an example of this pattern.
# Backwards compatibility
This is a backwards compatible change to FIDL source.
The wire format is backwards compatible, in the sense that the value of `(2 |
4)`, or `6`, is the same on the wire whether sent as a `uint32` or a `bits Bits :
uint32`.
# Performance
Changing a type in FIDL from a `uint32` to a `bits` value adds some
minor overhead in the bindings of checking that the serialized or deserialized
value is valid.
This is a bitmask and a branch, and unlikely to be noticable.
# Security
I do not see a security downside.
The better type safety is perhaps a minor upside.
# Testing
`fidlc` host unit tests will exercise the FIDL parser.
The compatibility test suite will be extended with `bits` types of various sizes
and values to exercise the sending and receiving paths of all supported
bindings.
# Drawbacks, alternatives, and unknowns
This proposal suggests only allowing bits for unsigned integer types.
I believe it would be possible to allow it for signed underlying types, but
with more care than desirable necessary in all of the language bindings.
I'd rather not have us accidentally shifting bits too far in C/C++ in
particular.
More general bitfield patterns seem more complicated than worthwhile for this
proposal.
By this I mean carving an integer type up into ranges of several bits, and
giving each range of bits a name as a field.
`&` and `~` expressions feel unnecessary, at least at first.
Target languages could optionally support such arithmetic expressions on bit
flag values, but I do not yet see a need for them in FIDL constants directly.
# Prior art and references
The hesitancy to do anything too complicated with member values, and to avoid
signed types, is based on eternal confusion with those concepts in C and C++,
whose bindings must support this concept.
<!-- xrefs -->
[primitives]: /docs/reference/fidl/language/language.md#primitives