This document is a specification of the Fuchsia Interface Definition Language (FIDL) syntax.
For more information about FIDL's overall purpose, goals, and requirements, see Overview.
Also, see a modified EBNF description of the FIDL grammar.
[TOC]
FIDL provides a syntax for declaring named bits, constants, enums, structs, tables, unions, and protocols. These declarations are collected into libraries for distribution.
FIDL declarations are stored in plain text UTF-8 files. Each file consists of a sequence of semicolon-delimited declarations. The order of declarations within a FIDL file, or among FIDL files within a library, is irrelevant. FIDL does not require (or support) forward declarations of any kind.
FIDL comments start with two (//
) or three (///
) forward slashes, continue
to the end of the line, and can contain UTF-8 content (which is, of course, ignored).
The three-forward-slash variant is a "documentation comment", and causes the comment
text to be emitted into the generated code (as a comment, escaped correctly
for the target language).
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="comments" %}
Note that documentation comments can also be provided via the @doc
attribute.
The following are keywords in FIDL.
as, bits, compose, const, enum, library, protocol,
resource, struct, table, union, using, xunion.
FIDL identifiers label declarations and their members. FIDL identifiers must
match the regex [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?
. In words: identifiers
must start with a letter, can contain letters, numbers, and underscores, but
cannot end with an underscore.
// a struct named "Foo"
type Foo = struct {};
// an enum named "enum", containing a single member
type enum = enum { WITH_A_MEMBER = 1; };
Note: While using keywords as identifiers is supported, it can lead to
confusion, and should therefore be considered on a case-by-case basis. See the
Names
section of the Style Rubric.
FIDL library names label FIDL libraries. FIDL library names
consist of one or more elements each matching the regex [a-z][a-z0-9]*
. In
words: library name elements must start with a lowercase letter, can contain
lowercase letters, and numbers (they cannot contain uppercase letters, nor
underscores). Library names are used in Qualified
Identifiers.
// a library named "foo"
library foo;
Identifiers and library names are case-sensitive.
FIDL always looks for unqualified symbols within the scope of the current library. To reference symbols in other libraries, they must be qualified by prefixing the identifier with the library name or alias thereof.
objects.fidl:
library objects;
using textures as tex;
protocol Frob {
// "Thing" refers to "Thing" in the "objects" library
// "tex.Color" refers to "Color" in the "textures" library
Paint(struct { thing Thing; color tex.Color; });
};
type Thing = struct {
name string;
};
textures.fidl:
library textures;
type Color = struct {
rgba uint32;
};
FIDL supports integer, floating point, boolean, string, and enumeration literals, using a simplified syntax familiar to C programmers (see below for examples).
FIDL supports the following constant types: bits, booleans, signed and unsigned integers, floating point values, strings, and enumerations. The syntax is similar to C:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="consts" %}
These declarations introduce a name within their scope. The constant's type must be either a primitive or an enum.
Constant expressions are either literals or the names of other constant expressions.
For greater clarity, there is no expression processing in FIDL; that is, you cannot declare a constant as having the value
6 + 5
, for example.
FIDL uses the semi-colon ';' to separate adjacent declarations within the file, much like C.
Libraries are named containers of FIDL declarations.
// library identifier separated by dots
library fuchsia.composition;
// "using" to import library "fuchsia.buffers"
using fuchsia.buffers;
// "using" to import library "fuchsia.geometry" and create a shortform called "geo"
using fuchsia.geometry as geo;
Libraries may declare that they use other libraries with a "using" declaration. This allows the library to refer to symbols defined in other libraries upon which they depend. Symbols imported this way may be accessed by:
- qualifying them with the fully qualified library name (as in "fuchsia.geometry.Rect"),
- specifying just the library name (as in "geometry.Rect"), or,
- using a library alias (as in "geo.Rect").
In the source tree, each library consists of a directory with some number of .fidl files. The name of the directory is irrelevant to the FIDL compiler but by convention it should resemble the library name itself. A directory should not contain FIDL files for more than one library.
The scope of library
and using
declarations is limited to a single file.
Each individual file within a FIDL library must restate the library
declaration together with any using
declarations needed by that file.
The library's name may be used by certain language bindings to provide scoping for symbols emitted by the code generator.
For example, the C++ bindings generator places declarations for the
FIDL library fuchsia.ui
within the C++ namespace
fuchsia::ui
. Similarly, for languages such as Dart and Rust, which
have their own module system, each FIDL library is compiled as a
module for that language.
FIDL supports a number of builtin types as well as declarations of new types (e.g. structs, unions, type aliases) and protocols.
- Simple value types.
- Never optional.
The following primitive types are supported:
- Boolean
bool
- Signed integer
int8 int16 int32 int64
- Unsigned integer
uint8 uint16 uint32 uint64
- IEEE 754 Floating-point
float32 float64
Numbers are suffixed with their size in bits, bool
is 1
byte.
We also alias byte
to mean uint8
as a built-in alias.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="primitives" %}
- Named bit types.
- Discrete subset of bit values chosen from an underlying integer primitive type.
- Never optional.
- Bits must have at least one member.
- Bits can either be
strict
orflexible
. - Bits default to
flexible
.
|
is the bitwise OR operator for bits.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="bits" %}
- Proper enumerated types.
- Discrete subset of named values chosen from an underlying integer primitive type.
- Never optional.
- Strict enums must have at least one member (flexible enums can be memberless).
- Enums can be
strict
orflexible
. - Enums default to
flexible
.
The ordinal index is required for each enum element. The underlying type of an enum must be one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64. If omitted, the underlying type is assumed to be uint32.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="enums" %}
Enum types are denoted by their identifier, which may be qualified if needed.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="enum-use" %}
- Fixed-length sequences of homogeneous elements.
- Elements can be of any type including: primitives, enums, arrays, strings, vectors, handles, structs, tables, unions.
- Never optional themselves; may contain optional types.
Arrays are denoted array<T, N>
where T can
be any FIDL type (including an array) and N is a positive
integer constant expression that specifies the number of elements in
the array.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="arrays" %}
Note that N appears as a layout parameter, which means that it affects the ABI
of the type. In other words, changing the parameter _N_
is an
ABI-breaking change.
- Variable-length sequence of UTF-8 encoded characters representing text.
- Can be optional; absent strings and empty strings are distinct.
- Can specify a maximum size, e.g.
string:40
for a maximum 40 byte string. - May contain embedded
NUL
bytes, unlike traditional C strings.
Strings are denoted as follows:
string
: required string (validation error occurs if absent)string:optional
: optional stringstring:N, string:<N, optional>
: string, and optional string, respectively, with maximum length of N bytes
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="strings" %}
Note that N appears as a constraint (it appears after the :
), which means
that it does not affect the ABI of the type. In other words, changing the
parameter _N_
is not an ABI-breaking change.
Strings should not be used to pass arbitrary binary data since bindings enforce valid UTF-8. Instead, consider
bytes
for small data orfuchsia.mem.Buffer
for blobs. See Should I use string or vector? for details.
- Variable-length sequence of homogeneous elements.
- Can be optional; absent vectors and empty vectors are distinct.
- Can specify a maximum size, e.g.
vector<T>:40
for a maximum 40 element vector. - There is no special case for vectors of bools. Each bool element takes one byte as usual.
- We have a built-in alias for
bytes
to meanvector<uint8>
, and it can be size bound in a similar fashion e.g.bytes:1024
.
Vectors are denoted as follows:
vector<T>
: required vector of element type T (validation error occurs if absent)vector<T>:optional
: optional vector of element type Tvector<T>:N, vector<T>:<N, optional>?
: vector, and optional vector, respectively, with maximum length of N elements
T can be any FIDL type.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="vectors" %}
- Transfers a Zircon capability by handle value.
- Stored as a 32-bit unsigned integer.
- Can be optional; absent handles are encoded as a zero-valued handle.
- Handles may optionally be associated with a type and set of required Zircon rights.
Handles are denoted:
zx.handle
: required Zircon handle of unspecified typezx.handle?
: optional Zircon handle of unspecified typezx.handle:H
: required Zircon handle of type Hzx.handle:<H, optional>
: optional Zircon handle of type Hzx.handle:<H, R>
: required Zircon handle of type H with rights Rzx.handle:<H, R, optional>
: optional Zircon handle of type H with rights R
H can be any object supported by
Zircon, e.g. channel
, thread
, vmo
. Please refer to the
grammar for a full list.
R can be any right supported by Zircon.
Rights are bits-typed values, defined in the zx
FIDL library, e.g. zx.rights.READ
. In both the incoming and outgoing
directions, handles are validated to have the correct Zircon object type and at
least as many rights as are specified in FIDL. If the handle has more rights
than is specified in FIDL, then its rights will be reduced by a call to
zx_handle_replace
. See Life of a handle for an example and RFC-0028: Handle
rights for further
details.
Structs, tables, and unions containing handles must be marked with the
resource
modifier.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="handles" %}
- Record type consisting of a sequence of typed fields.
- Declaration is not intended to be modified once deployed; use protocol extension instead.
- Declaration can have the
resource
modifier. - References may be
box
ed. - Structs contain zero or more members.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="structs" %}
Structs are denoted by their declared name (e.g. Circle):
Circle
: required Circlebox<Circle>
: optional Circle, stored out-of-line.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="structs-use" %}
- Record type consisting of a sequence of typed fields with ordinals.
- Declaration is intended for forward and backward compatibility in the face of schema changes.
- Declaration can have the
resource
modifier. - Tables cannot be optional. The semantics of "missing value" is expressed by an empty table i.e. where all members are absent, to avoid dealing with double optionality.
- Tables contain zero or more members.
type Profile = table {
1: locales vector<string>;
2: calendars vector<string>;
3: time_zones vector<string>;
};
Tables are denoted by their declared name (e.g. Profile):
Profile
: required Profile
Here, we show how Profile
evolves to also carry temperature units.
A client aware of the previous definition of Profile
(without temperature units)
can still send its profile to a server that has been updated to handle the larger
set of fields.
type TemperatureUnit = enum {
CELSIUS = 1;
FAHRENHEIT = 2;
};
type Profile = table {
1: locales vector<string>;
2: calendars vector<string>;
3: time_zones vector<string>;
4: temperature_unit TemperatureUnit;
};
- Record type consisting of an ordinal and an envelope.
- Ordinal indicates member selection, envelope holds contents.
- Declaration can be modified after deployment, while maintaining ABI compatibility. See the Compatibility Guide for source-compatibility considerations.
- Declaration can have the
resource
modifier. - Reference may be optional.
- Unions contain one or more members. A union with no members would have no inhabitants and thus would make little sense in a wire format.
- Unions can either be
strict
orflexible
. - Unions default to
flexible
.
{% includecode gerrit_repo="fuchsia/samples" gerrit_path="src/calculator/fidl/calculator.fidl" region_tag="union" %}
Unions are denoted by their declared name (e.g. Result) and optionality:
Result
: required ResultResult:optional
: optional Result
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="unions-use" %}
FIDL declarations can either have strict or flexible behavior:
- Bits, enums, and unions are flexible unless declared with the
strict
modifier. - Structs always have strict behavior.
- Tables always have flexible behavior.
For strict types only, serializing or deserializing a value that contains data not described in the declaration is a validation error.
In this example:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="strict-vs-flexible" %}
By virtue of being flexible, it is simpler for FlexibleEither
to evolve to
carry a third variant. A client aware of the previous definition of
FlexibleEither
without the third variant can still receive a union from a
server that has been updated to contain the larger set of variants. If the
union is of the unknown variant, bindings may expose it as unknown data (i.e. as
raw bytes and handles) to the user and allow re-encoding the unknown union (e.g.
to support proxy-like use cases). The methods provided for interacting with
unknown data for flexible types are described in detail in the bindings
reference.
More details are discussed in RFC-0033: Handling of Unknown Fields and Strictness.
Note: A type that is both flexible and a value type will not allow deserializing unknown data that contains handles.
Every FIDL type is either a value type or a resource type. Resource types include:
- handles
- protocol endpoints
- aliases of resource types
- arrays and vectors of resource types
- structs, tables, and unions marked with the
resource
modifier - optional (or boxed) references to any of the above types
All other types are value types.
Value types must not contain resource types. For example, this is incorrect:
type Foo = struct { // ERROR: must be "resource struct Foo"
h zx.handle;
};
Types can be marked with the resource
modifier even if they do not contain
handles. You should do this if you intend to add handles to the type in the
future, since adding or removing the resource
modifier requires
source-compatibility considerations. For example:
// No handles now, but we will add some in the future.
type Record = resource table {
1: str string;
};
// "Foo" must be a resource because it contains "Record", which is a resource.
type Foo = resource struct {
record Record;
};
More details are discussed in RFC-0057: Default No Handles.
-
Describe methods that can be invoked by sending messages over a channel.
-
Methods are identified by their ordinal index. The compiler calculates the ordinal by
- Taking the SHA-256 hash of the string generated by concatenating:
- The UTF-8 encoded library name, with no trailing \0 character
- '.' (ASCII 0x2e)
- The UTF-8 encoded protocol name, with no trailing \0 character
- '/' (ASCII 0x2f)
- The UTF-8 encoded method name, with no trailing \0 character
- Extracting the upper 32 bits of the hash value, and
- Setting the upper bit of that value to 0.
- To coerce the compiler into generating a different value, methods can have
a
@selector
attribute. The value of the@selector
attribute will be used in the place of the method name above.
- Taking the SHA-256 hash of the string generated by concatenating:
-
Each method declaration states its arguments and results.
- If no results are declared, then the method is one-way: no response will be generated by the server.
- If results are declared (even if empty), then the method is two-way: each invocation of the method generates a response from the server.
- If only results are declared, the method is referred to as an event. It then defines an unsolicited message from the server.
- Two-way methods may declare an error type that a server can send
instead of the response. This type must be an
int32
,uint32
, or anenum
thereof.
-
When a server of a protocol is about to close its side of the channel, it may elect to send an epitaph message to the client to indicate the disposition of the connection. The epitaph must be the last message delivered through the channel. An epitaph message includes a 32-bit int value of type zx_status_t. Negative values are reserved for system error codes. The value
ZX_OK
(0) indicates an operation was successful. Application-defined error codes (previously defined as all positivezx_status_t
values) are deprecated. For more details about epitaphs, see rejection of RFC-0031: Typed Epitaphs. For more details aboutzx_status_t
see RFC-0085: Reducing the zx_status_t space.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="calculator" %}
Protocols are denoted by their name, directionality of the channel, and optionality:
client_end:Protocol
: client endpoint of channel communicating over the FIDL protocolclient_end:<Protocol, optional>
: optional version of the aboveserver_end:Protocol
: server endpoint of a channel communicating over the FIDL protocolserver_end:<Protocol, optional>
: optional version of the above
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="endpoints" %}
A protocol can include methods from other protocols. This is called composition: you compose one protocol from other protocols.
Composition is used in the following cases:
- you have multiple protocols that all share some common behavior(s)
- you have varying levels of functionality you want to expose to different audiences
In the first case, there might be behavior that's shared across multiple protocols. For example, in a graphics system, several different protocols might all share a common need to set a background and foreground color. Rather than have each protocol define their own color setting methods, a common protocol can be defined:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="composition-base" %}
It can then be shared by other protocols:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="composition-inherit" %}
In the above, there are three protocols, SceneryController
, Drawer
, and Writer
.
Drawer
is used to draw graphical objects, like circles and squares at given locations
with given sizes.
It composes the methods SetBackground() and SetForeground() from
the SceneryController
protocol because it includes the SceneryController
protocol
(by way of the compose
keyword).
The Writer
protocol, used to write text on the display, includes the SceneryController
protocol in the same way.
Now both Drawer
and Writer
include SetBackground() and SetForeground().
This offers several advantages over having Drawer
and Writer
specify their own color
setting methods:
- the way to set background and foreground colors is the same, whether it's used to draw a circle, square, or put text on the display.
- new methods can be added to
Drawer
andWriter
without having to change their definitions, simply by adding them to theSceneryController
protocol.
The last point is particularly important, because it allows us to add functionality
to existing protocols.
For example, we might introduce an alpha-blending (or "transparency") feature to
our graphics system.
By extending the SceneryController
protocol to deal with it, perhaps like so:
protocol SceneryController {
SetBackground(struct { color Color; });
SetForeground(struct { color Color; });
SetAlphaChannel(struct { a int; });
};
we've now extended both Drawer
and Writer
to be able to support alpha blending.
Composition is not a one-to-one relationship — we can include multiple compositions into a given protocol, and not all protocols need be composed of the same mix of included protocols.
For example, we might have the ability to set font characteristics.
Fonts don't make sense for our Drawer
protocol, but they do make sense for our Writer
protocol, and perhaps other protocols.
So, we define our FontController
protocol:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="composition-multiple-1" %}
and then invite Writer
to include it, by using the compose
keyword:
protocol Writer {
compose SceneryController;
compose FontController;
Text(struct { x int; y int; message string; });
};
Here, we've extended the Writer
protocol with the FontController
protocol's methods,
without disturbing the Drawer
protocol (which doesn't need to know anything about fonts).
Protocol composition is similar to mixin. More details are discussed in RFC-0023: Compositional Model.
At the beginning of this section, we mentioned a second use for composition, namely exposing various levels of functionality to different audiences.
In this example, we have two protocols that are independently useful, a Clock
protocol
to get the current time and timezone:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="layering-clock" %}
And an Horologist
protocol that sets the time and timezone:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="layering-horologist" %}
We may not necessarily wish to expose the more privileged Horologist
protocol to just
any client, but we do want to expose it to the system clock component.
So, we create a protocol (SystemClock
) that composes both:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="layering-systemclock" %}
Type aliasing is supported. For example:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="aliasing" %}
In the above, the identifier StoryID
is an alias for the declaration of a
string
with a maximum size of MAX_SIZE
. The identifier Chapters
is an
alias for a vector declaration of five StoryId
elements.
The identifiers StoryID
and Chapters
can be used wherever their aliased
definitions can be used.
Consider:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="aliasing-usage" %}
Here, the Message
struct contains a string of MAX_SIZE
bytes called baseline
,
and a vector of up to 5
strings of MAX_SIZE
called chapters
.
Note that byte
and bytes
are built-in aliases, see below.
FIDL provides several built-ins:
- convenience types (
byte
andbytes
) zx library
see below
The types byte
and bytes
are built-in, and are conceptually
equivalent to:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference_builtin.test.fidl" region_tag="builtin" %}
When you refer to a name without specific scope, e.g.:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/language_reference.test.fidl" region_tag="builtin-aliases" %}
we treat this as builtin.byte
automatically (so long as there isn't a
more-specific name in scope).
The fidlc
compiler automatically generates an internal ZX library
for you that contains commonly used Zircon definitions.
Layouts can also be specified inline, rather than in a type
introduction
declaration. This is useful when a specific layout is only used once. For
example, the following FIDL:
type Options = table {
1: reticulate_splines bool;
};
protocol Launcher {
GenerateTerrain(struct {
options Options;
});
};
can be rewritten using an inline layout:
protocol Launcher {
GenerateTerrain(struct {
options table {
1: reticulate_splines bool;
};
});
};
When an inline layout is used, fidlc
will reserve a name for it that is
guaranteed to be unique, based on the naming context that the
layout is used in. This results in the following reserved names:
- For inline layouts used as the type of an outer layout member, the reserved
name is simply the name of the corresponding member.
- In the example above, the name
Options
is reserved for the inlinedtable
.
- In the example above, the name
- For top level request/response types,
fidlc
concatenates the protocol name, the method name, and then either"Request"
or"Response"
depending on where the type is used.- In the example above, the name
LauncherGenerateTerrainRequest
is reserved for the struct used as the request of theGenerateTerrain
method. - Note that the
"Request"
suffix denotes that the type is used to initiate communication; for this reason, event types will have the"Request"
suffix reserved instead of the"Response"
suffix.
- In the example above, the name
The name that is actually used in the generated code depends on the binding, and is described in the individual bindings references.
For inline layouts used as the type of a layout member, there are two ways to obtain a different reserved name:
- Rename the layout member.
- Override the reserved name using the
@generated_name
attribute.