[fidl] Initial support for extensible unions.

Implements extensible unions ("xunions") described in FTP-015.
Differences with the FTP:

* We use "xunion" as a keyword to define an extensible union, rather
  than "union".
* "union" continues to define a static union, which still exists, and is
  unchanged.
* Ordinals are calculated using FTP-020. Explicit ordinals are not
  allowed.
* Zero is an allowed ordinal, and indicates that the xunion is empty.
* No reserved field support yet, since ordinal hashing may make this
  unnecessary.

Happy to discuss any of the above points, of course!

Test: make HOST_USE_ASAN=true tools && $FUCHSIA_DIR/zircon/build-x64/host_tests/fidl-compiler-test

Change-Id: Ib07d6135099424dd7a617c6f3af8579122e92418
diff --git a/system/host/fidl/examples/types.fidl b/system/host/fidl/examples/types.fidl
index fd143a7..9ed961f 100644
--- a/system/host/fidl/examples/types.fidl
+++ b/system/host/fidl/examples/types.fidl
@@ -339,3 +339,8 @@
     request<this_is_an_interface> r;
     request<this_is_an_interface>? nullable_r;
 };
+
+xunion this_is_a_xunion {
+    string s;
+    int32 i;
+};
diff --git a/system/host/fidl/examples/xunion.fidl b/system/host/fidl/examples/xunion.fidl
new file mode 100644
index 0000000..4857c52
--- /dev/null
+++ b/system/host/fidl/examples/xunion.fidl
@@ -0,0 +1,16 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fidl.examples.xunion;
+
+table t {
+    1: string s;
+};
+
+xunion xu {
+    int32 i;
+    float32 f;
+    string s;
+    [Selector = "v2"] vector<int64> v;
+};
diff --git a/system/host/fidl/include/fidl/c_generator.h b/system/host/fidl/include/fidl/c_generator.h
index 45eb108..8093330 100644
--- a/system/host/fidl/include/fidl/c_generator.h
+++ b/system/host/fidl/include/fidl/c_generator.h
@@ -108,6 +108,11 @@
         const flat::Union& union_info;
     };
 
+    struct NamedXUnion {
+        std::string name;
+        const flat::XUnion& xunion_info;
+    };
+
     enum class StructKind {
         kMessage,
         kNonmessage,
@@ -124,6 +129,7 @@
 
     void GenerateStructDeclaration(StringView name, const std::vector<Member>& members, StructKind kind);
     void GenerateTaggedUnionDeclaration(StringView name, const std::vector<Member>& members);
+    void GenerateTaggedXUnionDeclaration(StringView name, const std::vector<Member>& members);
 
     std::map<const flat::Decl*, NamedConst>
     NameConsts(const std::vector<std::unique_ptr<flat::Const>>& const_infos);
@@ -137,6 +143,8 @@
     NameTables(const std::vector<std::unique_ptr<flat::Table>>& table_infos);
     std::map<const flat::Decl*, NamedUnion>
     NameUnions(const std::vector<std::unique_ptr<flat::Union>>& union_infos);
+    std::map<const flat::Decl*, NamedXUnion>
+    NameXUnions(const std::vector<std::unique_ptr<flat::XUnion>>& xunion_infos);
 
     void ProduceConstForwardDeclaration(const NamedConst& named_const);
     void ProduceEnumForwardDeclaration(const NamedEnum& named_enum);
@@ -144,6 +152,7 @@
     void ProduceStructForwardDeclaration(const NamedStruct& named_struct);
     void ProduceTableForwardDeclaration(const NamedTable& named_table);
     void ProduceUnionForwardDeclaration(const NamedUnion& named_union);
+    void ProduceXUnionForwardDeclaration(const NamedXUnion& named_xunion);
 
     void ProduceInterfaceExternDeclaration(const NamedInterface& named_interface);
 
@@ -153,6 +162,7 @@
     void ProduceStructDeclaration(const NamedStruct& named_struct);
     void ProduceTableDeclaration(const NamedStruct& named_struct);
     void ProduceUnionDeclaration(const NamedUnion& named_union);
+    void ProduceXUnionDeclaration(const NamedXUnion& named_xunion);
 
     void ProduceInterfaceClientDeclaration(const NamedInterface& named_interface);
     void ProduceInterfaceClientImplementation(const NamedInterface& named_interface);
diff --git a/system/host/fidl/include/fidl/coded_ast.h b/system/host/fidl/include/fidl/coded_ast.h
index 26675ab..6701117 100644
--- a/system/host/fidl/include/fidl/coded_ast.h
+++ b/system/host/fidl/include/fidl/coded_ast.h
@@ -46,6 +46,11 @@
     const uint32_t offset;
 };
 
+// This carries the same information as the XUnionField struct below and
+// arguably violates DRY, but it's useful to make it a different type to
+// distinguish its use-case in code, and also to make it easier to change later
+// if necessary. (Gotta do something at least three times before we abstract it
+// out, right?)
 struct TableField {
     TableField(const Type* type, uint32_t ordinal)
         : type(type), ordinal(ordinal) {}
@@ -54,6 +59,18 @@
     const uint32_t ordinal;
 };
 
+// This carries the same information as the TableField struct above and arguably
+// violates DRY, but it's useful to make it a different type to distinguish its
+// use-case in code, and also to make it easier to change later if necessary.
+// (Gotta do something at least three times before we abstract it out, right?)
+struct XUnionField {
+    XUnionField(const Type* type, uint32_t ordinal)
+        : type(type), ordinal(ordinal) {}
+
+    const Type* type;
+    const uint32_t ordinal;
+};
+
 struct Type {
     virtual ~Type() = default;
 
@@ -68,6 +85,8 @@
         kTablePointer,
         kUnion,
         kUnionPointer,
+        kXUnion,
+        kXUnionPointer,
         kMessage,
         kInterface,
         kArray,
@@ -181,6 +200,27 @@
     const UnionType* union_type;
 };
 
+struct XUnionType : public Type {
+    XUnionType(std::string name, std::vector<XUnionField> fields,
+               std::string pointer_name, std::string qname)
+        : Type(Kind::kXUnion, std::move(name), 24u, CodingNeeded::kNeeded), fields(std::move(fields)),
+          pointer_name(std::move(pointer_name)), qname(std::move(qname)) {
+    }
+
+    std::vector<XUnionField> fields;
+    const std::string pointer_name;
+    const std::string qname;
+    bool referenced_by_pointer = false;
+};
+
+struct XUnionPointerType : public Type {
+    XUnionPointerType(std::string name, const XUnionType* xunion_type)
+        : Type(Kind::kXUnionPointer, std::move(name), 8u, CodingNeeded::kNeeded),
+          xunion_type(xunion_type) {}
+
+    const XUnionType* const xunion_type;
+};
+
 struct MessageType : public Type {
     MessageType(std::string name, std::vector<StructField> fields, uint32_t size, std::string qname)
         : Type(Kind::kMessage, std::move(name), size, CodingNeeded::kNeeded),
diff --git a/system/host/fidl/include/fidl/flat_ast.h b/system/host/fidl/include/fidl/flat_ast.h
index ced6f9d..d4bd4de 100644
--- a/system/host/fidl/include/fidl/flat_ast.h
+++ b/system/host/fidl/include/fidl/flat_ast.h
@@ -425,6 +425,7 @@
         kStruct,
         kTable,
         kUnion,
+        kXUnion,
     };
 
     Decl(Kind kind, std::unique_ptr<raw::AttributeList> attributes, Name name)
@@ -765,6 +766,25 @@
     bool recursive = false;
 };
 
+struct XUnion : public Decl {
+    struct Member {
+        Member(std::unique_ptr<raw::Ordinal> ordinal, std::unique_ptr<Type> type, SourceLocation name, std::unique_ptr<raw::AttributeList> attributes)
+            : ordinal(std::move(ordinal)), type(std::move(type)), name(std::move(name)), attributes(std::move(attributes)) {}
+        std::unique_ptr<raw::Ordinal> ordinal;
+        std::unique_ptr<Type> type;
+        SourceLocation name;
+        std::unique_ptr<raw::AttributeList> attributes;
+        FieldShape fieldshape;
+    };
+
+    XUnion(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members)
+        : Decl(Kind::kXUnion, std::move(attributes), std::move(name)), members(std::move(members)) {}
+
+    std::vector<Member> members;
+    TypeShape typeshape;
+    bool recursive = false;
+};
+
 struct Interface : public Decl {
     struct Method {
         Method(Method&&) = default;
@@ -964,6 +984,8 @@
         kTableMember,
         kUnionDecl,
         kUnionMember,
+        kXUnionDecl,
+        kXUnionMember,
     };
 
     AttributeSchema(const std::set<Placement>& allowed_placements,
@@ -1094,6 +1116,7 @@
     bool ConsumeStructDeclaration(std::unique_ptr<raw::StructDeclaration> struct_declaration);
     bool ConsumeTableDeclaration(std::unique_ptr<raw::TableDeclaration> table_declaration);
     bool ConsumeUnionDeclaration(std::unique_ptr<raw::UnionDeclaration> union_declaration);
+    bool ConsumeXUnionDeclaration(std::unique_ptr<raw::XUnionDeclaration> xunion_declaration);
 
     bool TypeCanBeConst(const Type* type);
     const Type* TypeResolve(const Type* type);
@@ -1134,6 +1157,7 @@
     bool CompileStruct(Struct* struct_declaration);
     bool CompileTable(Table* table_declaration);
     bool CompileUnion(Union* union_declaration);
+    bool CompileXUnion(XUnion* xunion_declaration);
 
     // Compiling a type both validates the type, and computes shape
     // information for the type. In particular, we validate that
@@ -1176,6 +1200,7 @@
     std::vector<std::unique_ptr<Struct>> struct_declarations_;
     std::vector<std::unique_ptr<Table>> table_declarations_;
     std::vector<std::unique_ptr<Union>> union_declarations_;
+    std::vector<std::unique_ptr<XUnion>> xunion_declarations_;
 
     // All Decl pointers here are non-null and are owned by the
     // various foo_declarations_.
diff --git a/system/host/fidl/include/fidl/formatter.h b/system/host/fidl/include/fidl/formatter.h
index c8049e9..3a5854b 100644
--- a/system/host/fidl/include/fidl/formatter.h
+++ b/system/host/fidl/include/fidl/formatter.h
@@ -149,11 +149,24 @@
     }
 
     virtual void OnUnionMember(std::unique_ptr<UnionMember> const& element) override {
-        ScopedBool mem(is_member_decl_);
         OnBlankLineRespectingNode();
+        ScopedBool mem(is_member_decl_);
         TreeVisitor::OnUnionMember(element);
     }
 
+    virtual void OnXUnionDeclaration(std::unique_ptr<XUnionDeclaration> const& element) override {
+        OnBlankLineRequiringNode();
+        TreeVisitor::OnXUnionDeclaration(element);
+    }
+
+    virtual void OnXUnionMember(std::unique_ptr<XUnionMember> const& element) override {
+        OnBlankLineRespectingNode();
+        ScopedBool mem(is_member_decl_);
+        ScopedBool before_colon(blank_space_before_colon_, false);
+        ScopedBool after_colon(blank_space_after_colon_, true);
+        TreeVisitor::OnXUnionMember(element);
+    }
+
     virtual void OnType(std::unique_ptr<Type> const& element) override {
         ScopedIncrement si(nested_type_depth_);
         ScopedBool before_colon(blank_space_before_colon_, false);
diff --git a/system/host/fidl/include/fidl/json_generator.h b/system/host/fidl/include/fidl/json_generator.h
index 6ff50f8..8f7c1ea 100644
--- a/system/host/fidl/include/fidl/json_generator.h
+++ b/system/host/fidl/include/fidl/json_generator.h
@@ -96,6 +96,8 @@
     void Generate(const flat::Table::Member& value);
     void Generate(const flat::Union& value);
     void Generate(const flat::Union::Member& value);
+    void Generate(const flat::XUnion& value);
+    void Generate(const flat::XUnion::Member& value);
     void Generate(const flat::Library* library);
 
     void GenerateDeclarationsEntry(int count, const flat::Name& name, StringView decl);
diff --git a/system/host/fidl/include/fidl/names.h b/system/host/fidl/include/fidl/names.h
index 8f4e28a..6fc3860 100644
--- a/system/host/fidl/include/fidl/names.h
+++ b/system/host/fidl/include/fidl/names.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_C_NAMES_H_
-#define ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_C_NAMES_H_
+#ifndef ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_NAMES_H_
+#define ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_NAMES_H_
 
 #include <string>
 
@@ -34,6 +34,7 @@
 std::string NameFlatConstantKind(flat::Constant::Kind kind);
 std::string NameFlatTypeKind(flat::Type::Kind kind);
 std::string NameUnionTag(StringView union_name, const flat::Union::Member& member);
+std::string NameXUnionTag(StringView xunion_name, const flat::XUnion::Member& member);
 std::string NameFlatConstant(const flat::Constant* constant);
 std::string NameFlatType(const flat::Type* type);
 std::string NameFlatCType(const flat::Type* type, flat::Decl::Kind decl_kind);
@@ -51,6 +52,7 @@
 std::string NameCodedStruct(const flat::Struct* struct_decl);
 std::string NameCodedTable(const flat::Table* table_decl);
 std::string NameCodedUnion(const flat::Union* union_decl);
+std::string NameCodedXUnion(const flat::XUnion* xunion_decl);
 std::string NameCodedHandle(types::HandleSubtype subtype, types::Nullability nullability);
 std::string NameCodedInterfaceHandle(StringView interface_name, types::Nullability nullability);
 std::string NameCodedRequestHandle(StringView interface_name, types::Nullability nullability);
@@ -61,4 +63,4 @@
 
 } // namespace fidl
 
-#endif // ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_C_NAMES_H_
+#endif // ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_NAMES_H_
diff --git a/system/host/fidl/include/fidl/ordinals.h b/system/host/fidl/include/fidl/ordinals.h
index 7aab3c5..60b0775 100644
--- a/system/host/fidl/include/fidl/ordinals.h
+++ b/system/host/fidl/include/fidl/ordinals.h
@@ -13,19 +13,21 @@
 // Returns the Selector. If the Selector attribute is present, the
 // function returns its value; otherwise, it returns the name parameter.
 std::string GetSelector(const raw::AttributeList* attributes,
-                           SourceLocation name);
+                        SourceLocation name);
 
-// Computes the ordinal for this method per the FIDL spec.
+// Computes the ordinal for this method.
 //
 // The ordinal value is equal to
+//
 //    *((int32_t *)sha256(library_name + "." + interface_name + "/" + method_name)) & 0x7fffffff;
+//
 // If |method| has an Selector attribute, that value will be used as the
 // method_name.
 raw::Ordinal GetGeneratedOrdinal(const std::vector<StringView>& library_name,
                                  const StringView& interface_name,
                                  const raw::InterfaceMethod& method);
 
-// Retrieves the correct ordinal for this method per the FIDL spec.
+// Retrieves the correct ordinal for this method.
 //
 // If |method.ordinal| is not null, this method will return |method.ordinal|.
 // Otherwise, the ordinal value is computed with GetGeneratedOrdinal.
@@ -33,6 +35,12 @@
                         const StringView& interface_name,
                         const raw::InterfaceMethod& method);
 
+// Retrieves the correct ordinal for |xunion_member|, following the same
+// algorithm as GetOrdinal() for interface methods above.
+raw::Ordinal GetOrdinal(const std::vector<StringView>& library_name,
+                        const StringView& xunion_declaration_name,
+                        const raw::XUnionMember& xunion_member);
+
 } // namespace ordinals
 } // namespace fidl
 
diff --git a/system/host/fidl/include/fidl/parser.h b/system/host/fidl/include/fidl/parser.h
index 6b8a55d..c92e3c2 100644
--- a/system/host/fidl/include/fidl/parser.h
+++ b/system/host/fidl/include/fidl/parser.h
@@ -180,7 +180,7 @@
     std::unique_ptr<raw::TrueLiteral> ParseTrueLiteral();
     std::unique_ptr<raw::FalseLiteral> ParseFalseLiteral();
     std::unique_ptr<raw::Literal> ParseLiteral();
-    std::unique_ptr<raw::Ordinal> ParseOrdinal();
+    std::unique_ptr<raw::Ordinal> MaybeParseOrdinal();
 
     std::unique_ptr<raw::Constant> ParseConstant();
 
@@ -226,6 +226,10 @@
     std::unique_ptr<raw::UnionDeclaration>
     ParseUnionDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
 
+    std::unique_ptr<raw::XUnionMember> ParseXUnionMember();
+    std::unique_ptr<raw::XUnionDeclaration>
+    ParseXUnionDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
+
     std::unique_ptr<raw::File> ParseFile();
 
     std::map<StringView, types::HandleSubtype> handle_subtype_table_;
diff --git a/system/host/fidl/include/fidl/raw_ast.h b/system/host/fidl/include/fidl/raw_ast.h
index b845646..8ebe8ad 100644
--- a/system/host/fidl/include/fidl/raw_ast.h
+++ b/system/host/fidl/include/fidl/raw_ast.h
@@ -533,6 +533,34 @@
     std::vector<std::unique_ptr<UnionMember>> members;
 };
 
+class XUnionMember : public SourceElement {
+public:
+    XUnionMember(SourceElement const& element, std::unique_ptr<Type> type, std::unique_ptr<Identifier> identifier,
+                 std::unique_ptr<AttributeList> attributes)
+        : SourceElement(element), type(std::move(type)), identifier(std::move(identifier)), attributes(std::move(attributes)) {}
+
+    void Accept(TreeVisitor& visitor);
+
+    std::unique_ptr<Type> type;
+    std::unique_ptr<Identifier> identifier;
+    std::unique_ptr<AttributeList> attributes;
+};
+
+class XUnionDeclaration : public SourceElement {
+public:
+    XUnionDeclaration(SourceElement const& element, std::unique_ptr<AttributeList> attributes,
+                      std::unique_ptr<Identifier> identifier,
+                      std::vector<std::unique_ptr<XUnionMember>> members)
+        : SourceElement(element), attributes(std::move(attributes)), identifier(std::move(identifier)),
+          members(std::move(members)) {}
+
+    void Accept(TreeVisitor& visitor);
+
+    std::unique_ptr<AttributeList> attributes;
+    std::unique_ptr<Identifier> identifier;
+    std::vector<std::unique_ptr<XUnionMember>> members;
+};
+
 class File : public SourceElement {
 public:
     File(SourceElement const& element, Token end,
@@ -544,7 +572,8 @@
          std::vector<std::unique_ptr<InterfaceDeclaration>> interface_declaration_list,
          std::vector<std::unique_ptr<StructDeclaration>> struct_declaration_list,
          std::vector<std::unique_ptr<TableDeclaration>> table_declaration_list,
-         std::vector<std::unique_ptr<UnionDeclaration>> union_declaration_list)
+         std::vector<std::unique_ptr<UnionDeclaration>> union_declaration_list,
+         std::vector<std::unique_ptr<XUnionDeclaration>> xunion_declaration_list)
         : SourceElement(element),
           attributes(std::move(attributes)),
           library_name(std::move(library_name)),
@@ -555,6 +584,7 @@
           struct_declaration_list(std::move(struct_declaration_list)),
           table_declaration_list(std::move(table_declaration_list)),
           union_declaration_list(std::move(union_declaration_list)),
+          xunion_declaration_list(std::move(xunion_declaration_list)),
           end_(end) {}
 
     void Accept(TreeVisitor& visitor);
@@ -568,6 +598,7 @@
     std::vector<std::unique_ptr<StructDeclaration>> struct_declaration_list;
     std::vector<std::unique_ptr<TableDeclaration>> table_declaration_list;
     std::vector<std::unique_ptr<UnionDeclaration>> union_declaration_list;
+    std::vector<std::unique_ptr<XUnionDeclaration>> xunion_declaration_list;
     Token end_;
 };
 
diff --git a/system/host/fidl/include/fidl/tables_generator.h b/system/host/fidl/include/fidl/tables_generator.h
index f38f0ca..9b67ed9 100644
--- a/system/host/fidl/include/fidl/tables_generator.h
+++ b/system/host/fidl/include/fidl/tables_generator.h
@@ -48,6 +48,7 @@
     void Generate(const coded::StructType& struct_type);
     void Generate(const coded::TableType& table_type);
     void Generate(const coded::UnionType& union_type);
+    void Generate(const coded::XUnionType& xunion_type);
     void Generate(const coded::MessageType& message_type);
     void Generate(const coded::HandleType& handle_type);
     void Generate(const coded::InterfaceHandleType& interface_type);
@@ -59,14 +60,17 @@
     void Generate(const coded::Type* type);
     void Generate(const coded::StructField& field);
     void Generate(const coded::TableField& field);
+    void Generate(const coded::XUnionField& field);
 
     void GeneratePointerIfNeeded(const coded::StructType& struct_type);
     void GeneratePointerIfNeeded(const coded::TableType& table_type);
     void GeneratePointerIfNeeded(const coded::UnionType& union_type);
+    void GeneratePointerIfNeeded(const coded::XUnionType& xunion_type);
 
     void GenerateForward(const coded::StructType& struct_type);
     void GenerateForward(const coded::TableType& table_type);
     void GenerateForward(const coded::UnionType& union_type);
+    void GenerateForward(const coded::XUnionType& xunion_type);
 
     // Returns a pointer owned by coded_types_.
     const coded::Type* CompileType(const flat::Type* type);
diff --git a/system/host/fidl/include/fidl/token_definitions.inc b/system/host/fidl/include/fidl/token_definitions.inc
index 910666f..a9cb6aa 100644
--- a/system/host/fidl/include/fidl/token_definitions.inc
+++ b/system/host/fidl/include/fidl/token_definitions.inc
@@ -61,6 +61,7 @@
 KEYWORD(Struct, "struct")
 KEYWORD(Table, "table")
 KEYWORD(Union, "union")
+KEYWORD(XUnion, "xunion")
 
 KEYWORD(True, "true")
 KEYWORD(False, "false")
diff --git a/system/host/fidl/include/fidl/tree_visitor.h b/system/host/fidl/include/fidl/tree_visitor.h
index e5066f2..c901983 100644
--- a/system/host/fidl/include/fidl/tree_visitor.h
+++ b/system/host/fidl/include/fidl/tree_visitor.h
@@ -211,6 +211,12 @@
     virtual void OnUnionDeclaration(std::unique_ptr<UnionDeclaration> const& element) {
         element->Accept(*this);
     }
+    virtual void OnXUnionMember(std::unique_ptr<XUnionMember> const& element) {
+        element->Accept(*this);
+    }
+    virtual void OnXUnionDeclaration(std::unique_ptr<XUnionDeclaration> const& element) {
+        element->Accept(*this);
+    }
     virtual void OnFile(std::unique_ptr<File> const& element) {
         element->Accept(*this);
     }
diff --git a/system/host/fidl/include/fidl/type_shape.h b/system/host/fidl/include/fidl/type_shape.h
index fa0c87a..dfd369b 100644
--- a/system/host/fidl/include/fidl/type_shape.h
+++ b/system/host/fidl/include/fidl/type_shape.h
@@ -39,7 +39,7 @@
         : FieldShape(TypeShape()) {}
 
     TypeShape& Typeshape() { return typeshape_; }
-    TypeShape Typeshape() const { return typeshape_; }
+    const TypeShape& Typeshape() const { return typeshape_; }
 
     uint32_t Size() const { return typeshape_.Size(); }
     uint32_t Alignment() const { return typeshape_.Alignment(); }
diff --git a/system/host/fidl/lib/c_generator.cpp b/system/host/fidl/lib/c_generator.cpp
index b4f5f54..e90fd6c 100644
--- a/system/host/fidl/lib/c_generator.cpp
+++ b/system/host/fidl/lib/c_generator.cpp
@@ -46,15 +46,15 @@
 }
 
 CGenerator::Member EmptyStructMember() {
-  return {
-    .kind = flat::Type::Kind::kPrimitive,
-    .type = NamePrimitiveCType(types::PrimitiveSubtype::kUint8),
+    return {
+        .kind = flat::Type::Kind::kPrimitive,
+        .type = NamePrimitiveCType(types::PrimitiveSubtype::kUint8),
 
-    // Prepend the reserved uint8_t field with a single underscore, which is
-    // for reserved identifiers (see ISO C standard, section 7.1.3
-    // <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>).
-    .name = "_reserved",
-  };
+        // Prepend the reserved uint8_t field with a single underscore, which is
+        // for reserved identifiers (see ISO C standard, section 7.1.3
+        // <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>).
+        .name = "_reserved",
+    };
 }
 
 CGenerator::Transport ParseTransport(StringView view) {
@@ -137,6 +137,7 @@
         case flat::Decl::Kind::kStruct:
         case flat::Decl::Kind::kTable:
         case flat::Decl::Kind::kUnion:
+        case flat::Decl::Kind::kXUnion:
             switch (member.nullability) {
             case types::Nullability::kNullable:
                 *file << "const " << member.type << " " << member.name;
@@ -186,6 +187,7 @@
         case flat::Decl::Kind::kStruct:
         case flat::Decl::Kind::kTable:
         case flat::Decl::Kind::kUnion:
+        case flat::Decl::Kind::kXUnion:
             switch (member.nullability) {
             case types::Nullability::kNullable:
                 *file << member.type << " out_" << member.name;
@@ -257,7 +259,7 @@
         return true;
     if (member.kind == flat::Type::Kind::kIdentifier) {
         return member.nullability == types::Nullability::kNullable &&
-               (member.decl_kind == flat::Decl::Kind::kStruct || member.decl_kind == flat::Decl::Kind::kUnion);
+               (member.decl_kind == flat::Decl::Kind::kStruct || member.decl_kind == flat::Decl::Kind::kUnion || member.decl_kind == flat::Decl::Kind::kXUnion);
     }
     return false;
 }
@@ -277,7 +279,8 @@
 void EmitParameterSizeValidation(std::ostream* file,
                                  const std::vector<CGenerator::Member>& params) {
     for (const auto& member : params) {
-        if (member.max_num_elements == std::numeric_limits<uint32_t>::max()) continue;
+        if (member.max_num_elements == std::numeric_limits<uint32_t>::max())
+            continue;
         std::string param_name;
         if (member.kind == flat::Type::Kind::kVector) {
             param_name = member.name + "_count";
@@ -287,7 +290,7 @@
             assert(false && "only vector/string has size limit");
         }
         *file << kIndent
-            << "if (" << param_name << " > " << member.max_num_elements << ") {\n";
+              << "if (" << param_name << " > " << member.max_num_elements << ") {\n";
         *file << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
         *file << kIndent << "}\n";
     }
@@ -380,6 +383,9 @@
             case flat::Decl::Kind::kTable:
                 assert(false && "c-codegen for tables not yet implemented");
                 break;
+            case flat::Decl::Kind::kXUnion:
+                assert(false && "c-codegen for extensible unions not yet implemented");
+                break;
             case flat::Decl::Kind::kStruct:
             case flat::Decl::Kind::kUnion:
                 switch (member.nullability) {
@@ -561,6 +567,17 @@
     return members;
 }
 
+std::vector<CGenerator::Member>
+GenerateMembers(const flat::Library* library,
+                const std::vector<flat::XUnion::Member>& xunion_members) {
+    std::vector<CGenerator::Member> members;
+    members.reserve(xunion_members.size());
+    for (const auto& xunion_member : xunion_members) {
+        members.push_back(CreateMember(library, xunion_member));
+    }
+    return members;
+}
+
 void GetMethodParameters(const flat::Library* library,
                          const CGenerator::NamedMethod& method_info,
                          std::vector<CGenerator::Member>* request,
@@ -698,6 +715,11 @@
     file_ << "};\n";
 }
 
+void CGenerator::GenerateTaggedXUnionDeclaration(StringView name,
+                                                 const std::vector<Member>& members) {
+    // XUnions are (intentionally) unimplemented for C bindings.
+}
+
 // TODO(TO-702) These should maybe check for global name
 // collisions? Otherwise, is there some other way they should fail?
 std::map<const flat::Decl*, CGenerator::NamedConst>
@@ -807,6 +829,16 @@
     return named_unions;
 }
 
+std::map<const flat::Decl*, CGenerator::NamedXUnion>
+CGenerator::NameXUnions(const std::vector<std::unique_ptr<flat::XUnion>>& xunion_infos) {
+    std::map<const flat::Decl*, NamedXUnion> named_xunions;
+    for (const auto& xunion_info : xunion_infos) {
+        std::string xunion_name = NameName(xunion_info->name, "_", "_");
+        named_xunions.emplace(xunion_info.get(), NamedXUnion{std::move(xunion_name), *xunion_info});
+    }
+    return named_xunions;
+}
+
 void CGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) {
     // TODO(TO-702)
 }
@@ -853,6 +885,10 @@
     GenerateStructTypedef(named_union.name);
 }
 
+void CGenerator::ProduceXUnionForwardDeclaration(const NamedXUnion& named_xunion) {
+    GenerateStructTypedef(named_xunion.name);
+}
+
 void CGenerator::ProduceInterfaceExternDeclaration(const NamedInterface& named_interface) {
     for (const auto& method_info : named_interface.methods) {
         if (method_info.request)
@@ -943,6 +979,24 @@
     EmitBlank(&file_);
 }
 
+void CGenerator::ProduceXUnionDeclaration(const NamedXUnion& named_xunion) {
+    std::vector<CGenerator::Member> members =
+        GenerateMembers(library_, named_xunion.xunion_info.members);
+    GenerateTaggedXUnionDeclaration(named_xunion.name, members);
+
+    uint32_t tag = 0u;
+    for (const auto& member : named_xunion.xunion_info.members) {
+        std::string tag_name = NameXUnionTag(named_xunion.name, member);
+        auto xunion_tag_type = types::PrimitiveSubtype::kUint32;
+        std::ostringstream value;
+        value << tag;
+        GenerateIntegerDefine(std::move(tag_name), xunion_tag_type, value.str());
+        ++tag;
+    }
+
+    EmitBlank(&file_);
+}
+
 void CGenerator::ProduceInterfaceClientDeclaration(const NamedInterface& named_interface) {
     for (const auto& method_info : named_interface.methods) {
         if (!method_info.request)
@@ -1158,6 +1212,9 @@
                     case flat::Decl::Kind::kTable:
                         assert(false && "c-codegen for tables not yet implemented");
                         break;
+                    case flat::Decl::Kind::kXUnion:
+                        assert(false && "c-codegen for extensible unions not yet implemented");
+                        break;
                     case flat::Decl::Kind::kStruct:
                     case flat::Decl::Kind::kUnion:
                         switch (member.nullability) {
@@ -1273,6 +1330,9 @@
                 case flat::Decl::Kind::kTable:
                     assert(false && "c-codegen for tables not yet implemented");
                     break;
+                case flat::Decl::Kind::kXUnion:
+                    assert(false && "c-codegen for extensible unions not yet implemented");
+                    break;
                 case flat::Decl::Kind::kStruct:
                 case flat::Decl::Kind::kUnion:
                     switch (member.nullability) {
@@ -1380,6 +1440,8 @@
         NameTables(library_->table_declarations_);
     std::map<const flat::Decl*, NamedUnion> named_unions =
         NameUnions(library_->union_declarations_);
+    std::map<const flat::Decl*, NamedXUnion> named_xunions =
+        NameXUnions(library_->xunion_declarations_);
 
     file_ << "\n// Forward declarations\n\n";
 
@@ -1427,6 +1489,13 @@
             }
             break;
         }
+        case flat::Decl::Kind::kXUnion: {
+            auto iter = named_xunions.find(decl);
+            if (iter != named_xunions.end()) {
+                ProduceXUnionForwardDeclaration(iter->second);
+            }
+            break;
+        }
         default:
             abort();
         }
@@ -1441,6 +1510,7 @@
         case flat::Decl::Kind::kStruct:
         case flat::Decl::Kind::kTable:
         case flat::Decl::Kind::kUnion:
+        case flat::Decl::Kind::kXUnion:
             // Only messages have extern fidl_type_t declarations.
             break;
         case flat::Decl::Kind::kInterface: {
@@ -1495,6 +1565,13 @@
             }
             break;
         }
+        case flat::Decl::Kind::kXUnion: {
+            auto iter = named_xunions.find(decl);
+            if (iter != named_xunions.end()) {
+                ProduceXUnionDeclaration(iter->second);
+            }
+            break;
+        }
         default:
             abort();
         }
@@ -1509,6 +1586,7 @@
         case flat::Decl::Kind::kStruct:
         case flat::Decl::Kind::kTable:
         case flat::Decl::Kind::kUnion:
+        case flat::Decl::Kind::kXUnion:
             // Only interfaces have client declarations.
             break;
         case flat::Decl::Kind::kInterface: {
@@ -1550,6 +1628,7 @@
         case flat::Decl::Kind::kStruct:
         case flat::Decl::Kind::kTable:
         case flat::Decl::Kind::kUnion:
+        case flat::Decl::Kind::kXUnion:
             // Only interfaces have client implementations.
             break;
         case flat::Decl::Kind::kInterface: {
@@ -1587,6 +1666,7 @@
         case flat::Decl::Kind::kStruct:
         case flat::Decl::Kind::kTable:
         case flat::Decl::Kind::kUnion:
+        case flat::Decl::Kind::kXUnion:
             // Only interfaces have client implementations.
             break;
         case flat::Decl::Kind::kInterface: {
diff --git a/system/host/fidl/lib/flat_ast.cpp b/system/host/fidl/lib/flat_ast.cpp
index daf54bd..7ef4253 100644
--- a/system/host/fidl/lib/flat_ast.cpp
+++ b/system/host/fidl/lib/flat_ast.cpp
@@ -19,6 +19,8 @@
 #include "fidl/raw_ast.h"
 #include "fidl/utils.h"
 
+#include "zircon/fidl.h"
+
 namespace fidl {
 namespace flat {
 
@@ -28,8 +30,8 @@
 
 class ScopeInsertResult {
 public:
-    explicit ScopeInsertResult(std::unique_ptr<SourceLocation> previous_occurance)
-        : previous_occurance_(std::move(previous_occurance)) {}
+    explicit ScopeInsertResult(std::unique_ptr<SourceLocation> previous_occurrence)
+        : previous_occurrence_(std::move(previous_occurrence)) {}
 
     static ScopeInsertResult Ok() { return ScopeInsertResult(nullptr); }
     static ScopeInsertResult FailureAt(SourceLocation previous) {
@@ -37,16 +39,16 @@
     }
 
     bool ok() const {
-        return previous_occurance_ == nullptr;
+        return previous_occurrence_ == nullptr;
     }
 
-    const SourceLocation& previous_occurance() const {
+    const SourceLocation& previous_occurrence() const {
         assert(!ok());
-        return *previous_occurance_;
+        return *previous_occurrence_;
     }
 
 private:
-    std::unique_ptr<SourceLocation> previous_occurance_;
+    std::unique_ptr<SourceLocation> previous_occurrence_;
 };
 
 template <typename T>
@@ -191,11 +193,11 @@
 }
 
 TypeShape FidlMessageTypeShape(std::vector<FieldShape*>* fields) {
-    auto struct_shape =  FidlStructTypeShape(fields);
+    auto struct_shape = FidlStructTypeShape(fields);
     return AlignTypeshape(struct_shape, kMessageAlign);
 }
 
-TypeShape PointerTypeShape(TypeShape element, uint32_t max_element_count = 1u) {
+TypeShape PointerTypeShape(const TypeShape& element, uint32_t max_element_count = 1u) {
     // Because FIDL supports recursive data structures, we might not have
     // computed the TypeShape for the element we're pointing to. In that case,
     // the size will be zero and we'll use |numeric_limits<uint32_t>::max()| as
@@ -222,7 +224,7 @@
     return TypeShape(8u, 8u, depth, max_handles, max_out_of_line);
 }
 
-TypeShape CEnvelopeTypeShape(TypeShape contained_type) {
+TypeShape CEnvelopeTypeShape(const TypeShape& contained_type) {
     auto packed_sizes_field = FieldShape(kUint64TypeShape);
     auto pointer_type = FieldShape(PointerTypeShape(contained_type));
     std::vector<FieldShape*> header{&packed_sizes_field, &pointer_type};
@@ -253,6 +255,22 @@
     return CStructTypeShape(&header, extra_handles);
 }
 
+TypeShape CXUnionTypeShape(const std::vector<flat::XUnion::Member>& members, uint32_t extra_handles = 0u) {
+    uint32_t depth = 0u;
+    uint32_t max_handles = 0u;
+    uint32_t max_out_of_line = 0u;
+
+    for (const auto& member : members) {
+        const auto& envelope = CEnvelopeTypeShape(member.fieldshape.Typeshape());
+
+        depth = ClampedAdd(depth, envelope.Depth());
+        max_handles = ClampedAdd(max_handles, envelope.MaxHandles());
+        max_out_of_line = std::max(max_out_of_line, envelope.MaxOutOfLine());
+    }
+
+    return TypeShape(sizeof(fidl_xunion_t), 8u, depth, max_handles, max_out_of_line);
+}
+
 TypeShape ArrayTypeShape(TypeShape element, uint32_t count) {
     // TODO(FIDL-345): once TypeShape builders are done and methods can fail, do a
     // __builtin_mul_overflow and fail on overflow instead of ClampedMultiply(element.Size(), count)
@@ -403,6 +421,12 @@
     }
 }
 
+Type* Typespace::CreateForwardDeclaredType(const flat::Name& name, types::Nullability nullability) {
+    // TODO(pascallouis): Implement :-).
+
+    return nullptr;
+}
+
 bool NoOpConstraint(ErrorReporter* error_reporter,
                     const raw::Attribute* attribute,
                     const Decl* decl) {
@@ -412,9 +436,9 @@
 AttributeSchema::AttributeSchema(const std::set<Placement>& allowed_placements,
                                  const std::set<std::string> allowed_values,
                                  Constraint constraint)
-        : allowed_placements_(allowed_placements),
-          allowed_values_(allowed_values),
-          constraint_(std::move(constraint)) {}
+    : allowed_placements_(allowed_placements),
+      allowed_values_(allowed_values),
+      constraint_(std::move(constraint)) {}
 
 void AttributeSchema::ValidatePlacement(ErrorReporter* error_reporter,
                                         const raw::Attribute* attribute,
@@ -517,24 +541,29 @@
         return false;
     uint32_t max_bytes = std::numeric_limits<uint32_t>::max();
     switch (decl->kind) {
-        case Decl::Kind::kStruct: {
-            auto struct_decl = static_cast<const Struct*>(decl);
-            max_bytes = struct_decl->typeshape.Size() + struct_decl->typeshape.MaxOutOfLine();
-            break;
-        }
-        case Decl::Kind::kTable: {
-            auto table_decl = static_cast<const Table*>(decl);
-            max_bytes = table_decl->typeshape.Size() + table_decl->typeshape.MaxOutOfLine();
-            break;
-        }
-        case Decl::Kind::kUnion: {
-            auto union_decl = static_cast<const Union*>(decl);
-            max_bytes = union_decl->typeshape.Size() + union_decl->typeshape.MaxOutOfLine();
-            break;
-        }
-        default:
-            assert(false && "unexpected kind");
-            return false;
+    case Decl::Kind::kStruct: {
+        auto struct_decl = static_cast<const Struct*>(decl);
+        max_bytes = struct_decl->typeshape.Size() + struct_decl->typeshape.MaxOutOfLine();
+        break;
+    }
+    case Decl::Kind::kTable: {
+        auto table_decl = static_cast<const Table*>(decl);
+        max_bytes = table_decl->typeshape.Size() + table_decl->typeshape.MaxOutOfLine();
+        break;
+    }
+    case Decl::Kind::kUnion: {
+        auto union_decl = static_cast<const Union*>(decl);
+        max_bytes = union_decl->typeshape.Size() + union_decl->typeshape.MaxOutOfLine();
+        break;
+    }
+    case Decl::Kind::kXUnion: {
+        auto xunion_decl = static_cast<const XUnion*>(decl);
+        max_bytes = xunion_decl->typeshape.Size() + xunion_decl->typeshape.MaxOutOfLine();
+        break;
+    }
+    default:
+        assert(false && "unexpected kind");
+        return false;
     }
     if (max_bytes > bound) {
         std::ostringstream message;
@@ -557,24 +586,29 @@
         return false;
     uint32_t max_handles = std::numeric_limits<uint32_t>::max();
     switch (decl->kind) {
-        case Decl::Kind::kStruct: {
-            auto struct_decl = static_cast<const Struct*>(decl);
-            max_handles = struct_decl->typeshape.MaxHandles();
-            break;
-        }
-        case Decl::Kind::kTable: {
-            auto table_decl = static_cast<const Table*>(decl);
-            max_handles = table_decl->typeshape.MaxHandles();
-            break;
-        }
-        case Decl::Kind::kUnion: {
-            auto union_decl = static_cast<const Union*>(decl);
-            max_handles = union_decl->typeshape.MaxHandles();
-            break;
-        }
-        default:
-            assert(false && "unexpected kind");
-            return false;
+    case Decl::Kind::kStruct: {
+        auto struct_decl = static_cast<const Struct*>(decl);
+        max_handles = struct_decl->typeshape.MaxHandles();
+        break;
+    }
+    case Decl::Kind::kTable: {
+        auto table_decl = static_cast<const Table*>(decl);
+        max_handles = table_decl->typeshape.MaxHandles();
+        break;
+    }
+    case Decl::Kind::kUnion: {
+        auto union_decl = static_cast<const Union*>(decl);
+        max_handles = union_decl->typeshape.MaxHandles();
+        break;
+    }
+    case Decl::Kind::kXUnion: {
+        auto xunion_decl = static_cast<const XUnion*>(decl);
+        max_handles = xunion_decl->typeshape.MaxHandles();
+        break;
+    }
+    default:
+        assert(false && "unexpected kind");
+        return false;
     }
     if (max_handles > bound) {
         std::ostringstream message;
@@ -590,6 +624,7 @@
 }
 
 Libraries::Libraries() {
+    // clang-format off
     AddAttributeSchema("Discoverable", AttributeSchema({
         AttributeSchema::Placement::kInterfaceDecl,
     }, {
@@ -617,6 +652,7 @@
         AttributeSchema::Placement::kStructDecl,
         AttributeSchema::Placement::kTableDecl,
         AttributeSchema::Placement::kUnionDecl,
+        AttributeSchema::Placement::kXUnionDecl,
     }, {
         /* any value */
     },
@@ -627,12 +663,14 @@
         AttributeSchema::Placement::kStructDecl,
         AttributeSchema::Placement::kTableDecl,
         AttributeSchema::Placement::kUnionDecl,
+        AttributeSchema::Placement::kXUnionDecl,
     }, {
         /* any value */
     },
     MaxHandlesConstraint));
     AddAttributeSchema("Selector", AttributeSchema({
         AttributeSchema::Placement::kMethod,
+        AttributeSchema::Placement::kXUnionMember,
     }, {
         /* any value */
     }));
@@ -643,6 +681,7 @@
         "SocketControl",
         "OvernetStream",
     }));
+    // clang-format on
 }
 
 bool Libraries::Insert(std::unique_ptr<Library> library) {
@@ -665,8 +704,8 @@
 size_t EditDistance(const std::string& sequence1, const std::string& sequence2) {
     size_t s1_length = sequence1.length();
     size_t s2_length = sequence2.length();
-    size_t row1[s1_length+1];
-    size_t row2[s1_length+1];
+    size_t row1[s1_length + 1];
+    size_t row2[s1_length + 1];
     size_t* last_row = row1;
     size_t* this_row = row2;
     for (size_t i = 0; i <= s1_length; i++)
@@ -677,7 +716,8 @@
         for (size_t i = 1; i <= s1_length; i++) {
             auto s1c = sequence1[i - 1];
             this_row[i] = std::min(std::min(
-                last_row[i] + 1, this_row[i - 1] + 1), last_row[i - 1] + (s1c == s2c ? 0 : 1));
+                                       last_row[i] + 1, this_row[i - 1] + 1),
+                                   last_row[i - 1] + (s1c == s2c ? 0 : 1));
         }
         std::swap(last_row, this_row);
     }
@@ -1244,6 +1284,31 @@
     return RegisterDecl(union_declarations_.back().get());
 }
 
+bool Library::ConsumeXUnionDeclaration(std::unique_ptr<raw::XUnionDeclaration> xunion_declaration) {
+    auto name = Name(this, xunion_declaration->identifier->location());
+
+    std::vector<XUnion::Member> members;
+    for (auto& member : xunion_declaration->members) {
+        std::unique_ptr<raw::Ordinal> ordinal =
+            std::make_unique<raw::Ordinal>(fidl::ordinals::GetOrdinal(library_name_, name.name_part(), *member));
+
+        auto location = member->identifier->location();
+        std::unique_ptr<Type> type;
+        if (!ConsumeType(std::move(member->type), location, &type))
+            return false;
+
+        if (type->nullability != types::Nullability::kNonnullable) {
+            return Fail(member->location(), "Extensible union members cannot be nullable");
+        }
+
+        members.emplace_back(std::move(ordinal), std::move(type), location, std::move(member->attributes));
+    }
+
+    xunion_declarations_.push_back(
+        std::make_unique<XUnion>(std::move(xunion_declaration->attributes), std::move(name), std::move(members)));
+    return RegisterDecl(xunion_declarations_.back().get());
+}
+
 bool Library::ConsumeFile(std::unique_ptr<raw::File> file) {
     if (file->attributes) {
         ValidateAttributesPlacement(AttributeSchema::Placement::kLibrary, file->attributes.get());
@@ -1323,6 +1388,13 @@
         }
     }
 
+    auto xunion_declaration_list = std::move(file->xunion_declaration_list);
+    for (auto& xunion_declaration : xunion_declaration_list) {
+        if (!ConsumeXUnionDeclaration(std::move(xunion_declaration))) {
+            return false;
+        }
+    }
+
     return true;
 }
 
@@ -1762,8 +1834,8 @@
 // of D1 before the declaration of D2. For instance, given the fidl
 //     struct D2 { D1 d; };
 //     struct D1 { int32 x; };
-// D1 has an edge pointing to D2. Note that struct and union pointers,
-// unlike inline structs or unions, do not have dependency edges.
+// D1 has an edge pointing to D2. Note that struct, union, and xunion pointers,
+// unlike inline structs/unions/xunions, do not have dependency edges.
 bool Library::DeclDependencies(Decl* decl, std::set<Decl*>* out_edges) {
     std::set<Decl*> edges;
     auto maybe_add_decl = [this, &edges](const Type* type, LookupOption option) {
@@ -1858,6 +1930,13 @@
         }
         break;
     }
+    case Decl::Kind::kXUnion: {
+        auto xunion_decl = static_cast<const XUnion*>(decl);
+        for (const auto& member : xunion_decl->members) {
+            maybe_add_decl(member.type.get(), LookupOption::kIgnoreNullable);
+        }
+        break;
+    }
     }
     *out_edges = std::move(edges);
     return true;
@@ -2073,7 +2152,7 @@
             if (!name_result.ok())
                 return Fail(method.name,
                             "Multiple methods with the same name in an interface; last occurrence was at " +
-                                name_result.previous_occurance().position());
+                                name_result.previous_occurrence().position());
             auto ordinal_result = method_scope.ordinals.Insert(method.ordinal->value, method.name);
             if (method.ordinal->value == 0)
                 return Fail(method.ordinal->location(), "Ordinal value 0 disallowed.");
@@ -2083,7 +2162,7 @@
                 replacement_method.push_back('_');
                 return Fail(method.ordinal->location(),
                             "Multiple methods with the same ordinal in an interface; previous was at " +
-                                ordinal_result.previous_occurance().position() + ". If these " +
+                                ordinal_result.previous_occurrence().position() + ". If these " +
                                 "were automatically generated, consider using attribute " +
                                 "[Selector=\"" + replacement_method + "\"] to change the " +
                                 "name used to calculate the ordinal.");
@@ -2179,7 +2258,7 @@
         if (!name_result.ok())
             return Fail(member.name,
                         "Multiple struct fields with the same name; previous was at " +
-                            name_result.previous_occurance().position());
+                            name_result.previous_occurrence().position());
         if (!CompileType(member.type.get(), &member.fieldshape.Typeshape()))
             return false;
         fidl_struct.push_back(&member.fieldshape);
@@ -2225,13 +2304,13 @@
         if (!ordinal_result.ok())
             return Fail(member.ordinal->location(),
                         "Multiple table fields with the same ordinal; previous was at " +
-                            ordinal_result.previous_occurance().position());
+                            ordinal_result.previous_occurrence().position());
         if (member.maybe_used) {
             auto name_result = name_scope.Insert(member.maybe_used->name.data(), member.maybe_used->name);
             if (!name_result.ok())
                 return Fail(member.maybe_used->name,
                             "Multiple table fields with the same name; previous was at " +
-                                name_result.previous_occurance().position());
+                                name_result.previous_occurrence().position());
             if (!CompileType(member.maybe_used->type.get(), &member.maybe_used->typeshape))
                 return false;
         }
@@ -2292,7 +2371,7 @@
         if (!name_result.ok())
             return Fail(member.name,
                         "Multiple union members with the same name; previous was at " +
-                            name_result.previous_occurance().position());
+                            name_result.previous_occurrence().position());
         if (!CompileType(member.type.get(), &member.fieldshape.Typeshape()))
             return false;
     }
@@ -2333,6 +2412,58 @@
     return true;
 }
 
+bool Library::CompileXUnion(XUnion* xunion_declaration) {
+    Compiling guard(xunion_declaration);
+    Scope<StringView> scope;
+    Scope<uint32_t> ordinal_scope;
+
+    for (auto& member : xunion_declaration->members) {
+        auto ordinal_result = ordinal_scope.Insert(member.ordinal->value, member.ordinal->location());
+        if (!ordinal_result.ok())
+            return Fail(member.ordinal->location(),
+                        "Multiple xunion fields with the same ordinal; previous was at " +
+                            ordinal_result.previous_occurrence().position());
+
+        auto name_result = scope.Insert(member.name.data(), member.name);
+        if (!name_result.ok())
+            return Fail(member.name,
+                        "Multiple xunion members with the same name; previous was at " +
+                            name_result.previous_occurrence().position());
+
+        if (!CompileType(member.type.get(), &member.fieldshape.Typeshape()))
+            return false;
+    }
+
+    uint32_t max_member_handles;
+    if (xunion_declaration->recursive) {
+        max_member_handles = std::numeric_limits<uint32_t>::max();
+    } else {
+        // Member handles will be counted by CXUnionTypeShape.
+        max_member_handles = 0u;
+    }
+
+    xunion_declaration->typeshape = CXUnionTypeShape(xunion_declaration->members, max_member_handles);
+
+    auto placement_ok = error_reporter_->Checkpoint();
+    // Attributes: check placement.
+    ValidateAttributesPlacement(
+        AttributeSchema::Placement::kXUnionDecl,
+        xunion_declaration->attributes.get());
+    for (const auto& member : xunion_declaration->members) {
+        ValidateAttributesPlacement(
+            AttributeSchema::Placement::kXUnionMember,
+            member.attributes.get());
+    }
+    if (placement_ok.NoNewErrors()) {
+        // Attributes: check constraint.
+        ValidateAttributesConstraints(
+            xunion_declaration,
+            xunion_declaration->attributes.get());
+    }
+
+    return true;
+}
+
 bool Library::CompileLibraryName() {
     const std::regex pattern("^[a-z][a-z0-9]*$");
     for (const auto& part_view : library_name_) {
@@ -2405,6 +2536,13 @@
             }
             break;
         }
+        case Decl::Kind::kXUnion: {
+            auto xunion_decl = static_cast<XUnion*>(decl);
+            if (!CompileXUnion(xunion_decl)) {
+                return false;
+            }
+            break;
+        }
         default:
             abort();
         }
@@ -2566,6 +2704,22 @@
             typeshape = PointerTypeShape(typeshape);
         break;
     }
+    case Decl::Kind::kXUnion: {
+        XUnion* xunion_decl = static_cast<XUnion*>(named_decl);
+        if (!xunion_decl->compiled) {
+            if (xunion_decl->compiling) {
+                xunion_decl->recursive = true;
+            } else {
+                if (!CompileXUnion(xunion_decl)) {
+                    return false;
+                }
+            }
+        }
+        typeshape = xunion_decl->typeshape;
+        if (identifier_type->nullability == types::Nullability::kNullable)
+            typeshape = PointerTypeShape(typeshape);
+        break;
+    }
     default: {
         abort();
     }
@@ -2651,7 +2805,7 @@
             std::ostringstream msg_stream;
             msg_stream << "value of member " << name;
             msg_stream << " conflicts with previously declared member ";
-            msg_stream << NameIdentifier(value_result.previous_occurance()) << " in the enum ";
+            msg_stream << NameIdentifier(value_result.previous_occurrence()) << " in the enum ";
             msg_stream << enum_decl->GetName();
 
             // We can log the error and then continue validating other members for other bugs
diff --git a/system/host/fidl/lib/json_generator.cpp b/system/host/fidl/lib/json_generator.cpp
index 071c0af..f14b150 100644
--- a/system/host/fidl/lib/json_generator.cpp
+++ b/system/host/fidl/lib/json_generator.cpp
@@ -526,6 +526,33 @@
     });
 }
 
+void JSONGenerator::Generate(const flat::XUnion& value) {
+    GenerateObject([&]() {
+        GenerateObjectMember("name", value.name, Position::kFirst);
+        if (value.attributes)
+            GenerateObjectMember("maybe_attributes", value.attributes);
+        GenerateObjectMember("members", value.members);
+        GenerateObjectMember("size", value.typeshape.Size());
+        GenerateObjectMember("max_out_of_line", value.typeshape.MaxOutOfLine());
+        GenerateObjectMember("alignment", value.typeshape.Alignment());
+        GenerateObjectMember("max_handles", value.typeshape.MaxHandles());
+    });
+}
+
+void JSONGenerator::Generate(const flat::XUnion::Member& value) {
+    GenerateObject([&]() {
+        GenerateObjectMember("ordinal", value.ordinal, Position::kFirst);
+        GenerateObjectMember("type", value.type);
+        GenerateObjectMember("name", value.name);
+        if (value.attributes)
+            GenerateObjectMember("maybe_attributes", value.attributes);
+        GenerateObjectMember("size", value.fieldshape.Size());
+        GenerateObjectMember("max_out_of_line", value.fieldshape.MaxOutOfLine());
+        GenerateObjectMember("alignment", value.fieldshape.Alignment());
+        GenerateObjectMember("offset", value.fieldshape.Offset());
+    });
+}
+
 void JSONGenerator::Generate(const flat::Library* library) {
     GenerateObject([&]() {
         auto library_name = flat::LibraryName(library, ".");
@@ -568,6 +595,9 @@
 
         for (const auto& decl : library->union_declarations_)
             GenerateDeclarationsEntry(count++, decl->name, "union");
+
+        for (const auto& decl : library->xunion_declarations_)
+            GenerateDeclarationsEntry(count++, decl->name, "xunion");
     });
 }
 
@@ -595,6 +625,7 @@
         GenerateObjectMember("struct_declarations", library_->struct_declarations_);
         GenerateObjectMember("table_declarations", library_->table_declarations_);
         GenerateObjectMember("union_declarations", library_->union_declarations_);
+        GenerateObjectMember("xunion_declarations", library_->xunion_declarations_);
 
         // The library's declaration_order_ contains all the declarations for all
         // transitive dependencies. The backend only needs the declaration order
diff --git a/system/host/fidl/lib/names.cpp b/system/host/fidl/lib/names.cpp
index 744533f..8fc78ea 100644
--- a/system/host/fidl/lib/names.cpp
+++ b/system/host/fidl/lib/names.cpp
@@ -255,6 +255,10 @@
     return std::string(union_name) + "Tag_" + NameIdentifier(member.name);
 }
 
+std::string NameXUnionTag(StringView xunion_name, const flat::XUnion::Member& member) {
+    return std::string(xunion_name) + "Tag_" + NameIdentifier(member.name);
+}
+
 std::string NameFlatConstant(const flat::Constant* constant) {
     switch (constant->kind) {
     case flat::Constant::Kind::kLiteral: {
@@ -376,7 +380,8 @@
             case flat::Decl::Kind::kEnum:
             case flat::Decl::Kind::kStruct:
             case flat::Decl::Kind::kTable:
-            case flat::Decl::Kind::kUnion: {
+            case flat::Decl::Kind::kUnion:
+            case flat::Decl::Kind::kXUnion: {
                 std::string name = NameName(identifier_type->name, "_", "_");
                 if (identifier_type->nullability == types::Nullability::kNullable) {
                     name.push_back('*');
@@ -483,6 +488,10 @@
     return NameName(union_decl->name, "_", "_");
 }
 
+std::string NameCodedXUnion(const flat::XUnion* xunion_decl) {
+    return NameName(xunion_decl->name, "_", "_");
+}
+
 std::string NameCodedHandle(types::HandleSubtype subtype, types::Nullability nullability) {
     std::string name("Handle");
     name += NameHandleSubtype(subtype);
diff --git a/system/host/fidl/lib/ordinals.cpp b/system/host/fidl/lib/ordinals.cpp
index 70d6b9c..3716459 100644
--- a/system/host/fidl/lib/ordinals.cpp
+++ b/system/host/fidl/lib/ordinals.cpp
@@ -12,7 +12,8 @@
 namespace fidl {
 namespace ordinals {
 
-std::string GetSelector(const raw::AttributeList* attributes, SourceLocation name) {
+std::string GetSelector(const raw::AttributeList* attributes,
+                        SourceLocation name) {
     if (attributes != nullptr) {
         const size_t size = attributes->attributes.size();
         for (int i = 0; i < size; i++) {
@@ -24,11 +25,27 @@
     return std::string(name.data().data(), name.data().size());
 }
 
-raw::Ordinal GetGeneratedOrdinal(
-    const std::vector<StringView>& library_name,
-    const StringView& interface_name,
-    const raw::InterfaceMethod& method) {
-    std::string method_name = GetSelector(method.attributes.get(), method.identifier->location());
+raw::Ordinal GetGeneratedOrdinal(const StringView& full_name,
+                                 const raw::SourceElement& source_element) {
+    uint8_t digest[SHA256_DIGEST_LENGTH];
+    SHA256(reinterpret_cast<const uint8_t*>(full_name.data()), full_name.size(), digest);
+    // The following dance ensures that we treat the bytes as a little-endian
+    // int32 regardless of host byte order.
+    uint32_t ordinal = static_cast<uint32_t>(digest[0]) |
+                       static_cast<uint32_t>(digest[1]) << 8 |
+                       static_cast<uint32_t>(digest[2]) << 16 |
+                       static_cast<uint32_t>(digest[3]) << 24;
+
+    ordinal &= 0x7fffffff;
+    return raw::Ordinal(source_element, ordinal);
+}
+
+raw::Ordinal GetGeneratedOrdinal(const std::vector<StringView>& library_name,
+                                 const StringView& container_name,
+                                 const raw::AttributeList* attributes,
+                                 SourceLocation name,
+                                 const raw::SourceElement& source_element) {
+    std::string method_name = GetSelector(attributes, name);
     std::string full_name;
     bool once = false;
     for (StringView id : library_name) {
@@ -40,21 +57,17 @@
         full_name.append(id.data(), id.size());
     }
     full_name.append(".");
-    full_name.append(interface_name.data(), interface_name.size());
+    full_name.append(container_name.data(), container_name.size());
     full_name.append("/");
     full_name.append(method_name);
 
-    uint8_t digest[SHA256_DIGEST_LENGTH];
-    SHA256(reinterpret_cast<const uint8_t*>(full_name.data()), full_name.size(), digest);
-    // The following dance ensures that we treat the bytes as a little-endian
-    // int32 regardless of host byte order.
-    uint32_t ordinal = static_cast<uint32_t>(digest[0]) |
-                       static_cast<uint32_t>(digest[1]) << 8 |
-                       static_cast<uint32_t>(digest[2]) << 16 |
-                       static_cast<uint32_t>(digest[3]) << 24;
+    return GetGeneratedOrdinal(StringView(full_name), source_element);
+}
 
-    ordinal &= 0x7fffffff;
-    return raw::Ordinal(*method.identifier, ordinal);
+raw::Ordinal GetGeneratedOrdinal(const std::vector<StringView>& library_name,
+                                 const StringView& interface_name,
+                                 const raw::InterfaceMethod& method) {
+    return GetGeneratedOrdinal(library_name, interface_name, method.attributes.get(), method.identifier->location(), method);
 }
 
 raw::Ordinal GetOrdinal(const std::vector<StringView>& library_name,
@@ -62,8 +75,17 @@
                         const raw::InterfaceMethod& method) {
     if (method.ordinal != nullptr)
         return *method.ordinal;
+
     return GetGeneratedOrdinal(library_name, interface_name, method);
 }
 
+raw::Ordinal GetOrdinal(const std::vector<StringView>& library_name,
+                        const StringView& xunion_declaration_name,
+                        const raw::XUnionMember& xunion_member) {
+    // Note that this ordinal hashing for xunion members uses the same ordinal
+    // hashing algorithm as for FIDL methods, which results in 31 bits, not 32.
+    return GetGeneratedOrdinal(library_name, xunion_declaration_name, xunion_member.attributes.get(), xunion_member.identifier->location(), xunion_member);
+}
+
 } // namespace ordinals
 } // namespace fidl
diff --git a/system/host/fidl/lib/parser.cpp b/system/host/fidl/lib/parser.cpp
index eabed11..feb592a 100644
--- a/system/host/fidl/lib/parser.cpp
+++ b/system/host/fidl/lib/parser.cpp
@@ -4,8 +4,8 @@
 
 #include <errno.h>
 
-#include "fidl/parser.h"
 #include "fidl/attributes.h"
+#include "fidl/parser.h"
 
 namespace fidl {
 
@@ -146,7 +146,7 @@
     return std::make_unique<raw::NumericLiteral>(scope.GetSourceElement());
 }
 
-std::unique_ptr<raw::Ordinal> Parser::ParseOrdinal() {
+std::unique_ptr<raw::Ordinal> Parser::MaybeParseOrdinal() {
     ASTScope scope(this);
 
     if (Peek().kind() != Token::Kind::kNumericLiteral) {
@@ -683,7 +683,7 @@
 }
 
 std::unique_ptr<raw::InterfaceMethod> Parser::ParseInterfaceMethod(std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) {
-    auto ordinal = ParseOrdinal();
+    auto ordinal = MaybeParseOrdinal();
     if (!Ok())
         return Fail();
 
@@ -875,7 +875,7 @@
     if (!Ok())
         return Fail();
 
-    auto ordinal = ParseOrdinal();
+    auto ordinal = MaybeParseOrdinal();
     if (!Ok())
         return Fail();
 
@@ -1014,6 +1014,75 @@
                                                    std::move(members));
 }
 
+std::unique_ptr<raw::XUnionMember> Parser::ParseXUnionMember() {
+    ASTScope scope(this);
+
+    auto attributes = MaybeParseAttributeList();
+    if (!Ok())
+        return Fail();
+
+    auto type = ParseType();
+    if (!Ok())
+        return Fail();
+
+    auto identifier = ParseIdentifier();
+    if (!Ok())
+        return Fail();
+
+    return std::make_unique<raw::XUnionMember>(scope.GetSourceElement(),
+                                               std::move(type),
+                                               std::move(identifier),
+                                               std::move(attributes));
+}
+
+std::unique_ptr<raw::XUnionDeclaration>
+Parser::ParseXUnionDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) {
+    std::vector<std::unique_ptr<raw::XUnionMember>> members;
+
+    ConsumeToken(IdentifierOfSubkind(Token::Subkind::kXUnion));
+    if (!Ok())
+        return Fail();
+
+    auto identifier = ParseIdentifier();
+    if (!Ok())
+        return Fail();
+
+    ConsumeToken(OfKind(Token::Kind::kLeftCurly));
+    if (!Ok())
+        return Fail();
+
+    auto parse_member = [&]() {
+        switch (Peek().combined()) {
+        default:
+            ConsumeToken(OfKind(Token::Kind::kRightCurly));
+            return Done;
+
+        case Token::Kind::kNumericLiteral: // Ordinal
+        TOKEN_ATTR_CASES:
+            // intentional fallthrough for attribute parsing
+        TOKEN_TYPE_CASES:
+            members.emplace_back(ParseXUnionMember());
+            return More;
+        }
+    };
+
+    while (parse_member() == More) {
+        if (!Ok())
+            Fail();
+
+        ConsumeToken(OfKind(Token::Kind::kSemicolon));
+        if (!Ok())
+            return Fail();
+    }
+
+    if (!Ok())
+        Fail();
+
+    return std::make_unique<raw::XUnionDeclaration>(scope.GetSourceElement(),
+                                                    std::move(attributes), std::move(identifier),
+                                                    std::move(members));
+}
+
 std::unique_ptr<raw::File> Parser::ParseFile() {
     ASTScope scope(this);
     std::vector<std::unique_ptr<raw::Using>> using_list;
@@ -1023,6 +1092,7 @@
     std::vector<std::unique_ptr<raw::StructDeclaration>> struct_declaration_list;
     std::vector<std::unique_ptr<raw::TableDeclaration>> table_declaration_list;
     std::vector<std::unique_ptr<raw::UnionDeclaration>> union_declaration_list;
+    std::vector<std::unique_ptr<raw::XUnionDeclaration>> xunion_declaration_list;
 
     auto attributes = MaybeParseAttributeList();
     if (!Ok())
@@ -1058,7 +1128,7 @@
 
     auto parse_declaration = [&const_declaration_list, &enum_declaration_list,
                               &interface_declaration_list, &struct_declaration_list,
-                              &table_declaration_list, &union_declaration_list, this]() {
+                              &table_declaration_list, &union_declaration_list, &xunion_declaration_list, this]() {
         ASTScope scope(this);
         std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList();
         if (!Ok())
@@ -1092,6 +1162,10 @@
         case CASE_IDENTIFIER(Token::Subkind::kUnion):
             union_declaration_list.emplace_back(ParseUnionDeclaration(std::move(attributes), scope));
             return More;
+
+        case CASE_IDENTIFIER(Token::Subkind::kXUnion):
+            xunion_declaration_list.emplace_back(ParseXUnionDeclaration(std::move(attributes), scope));
+            return More;
         }
     };
 
@@ -1111,7 +1185,7 @@
         scope.GetSourceElement(), end,
         std::move(attributes), std::move(library_name), std::move(using_list), std::move(const_declaration_list),
         std::move(enum_declaration_list), std::move(interface_declaration_list),
-        std::move(struct_declaration_list), std::move(table_declaration_list), std::move(union_declaration_list));
+        std::move(struct_declaration_list), std::move(table_declaration_list), std::move(union_declaration_list), std::move(xunion_declaration_list));
 }
 
 } // namespace fidl
diff --git a/system/host/fidl/lib/raw_ast.cpp b/system/host/fidl/lib/raw_ast.cpp
index a87364f..2a4c494 100644
--- a/system/host/fidl/lib/raw_ast.cpp
+++ b/system/host/fidl/lib/raw_ast.cpp
@@ -24,8 +24,8 @@
 
 void CompoundIdentifier::Accept(TreeVisitor& visitor) {
     SourceElementMark sem(visitor, *this);
-    for (auto i = components.begin(); i != components.end(); ++i) {
-        visitor.OnIdentifier(*i);
+    for (auto& i : components) {
+        visitor.OnIdentifier(i);
     }
 }
 
@@ -65,8 +65,8 @@
 
 void AttributeList::Accept(TreeVisitor& visitor) {
     SourceElementMark sem(visitor, *this);
-    for (auto i = attributes.begin(); i != attributes.end(); ++i) {
-        visitor.OnAttribute(*i);
+    for (auto& i : attributes) {
+        visitor.OnAttribute(i);
     }
 }
 
@@ -281,43 +281,53 @@
     }
 }
 
+void XUnionMember::Accept(TreeVisitor& visitor) {
+    SourceElementMark sem(visitor, *this);
+    if (attributes != nullptr) {
+        visitor.OnAttributeList(attributes);
+    }
+
+    visitor.OnType(type);
+    visitor.OnIdentifier(identifier);
+}
+
+void XUnionDeclaration::Accept(TreeVisitor& visitor) {
+    SourceElementMark sem(visitor, *this);
+    if (attributes != nullptr) {
+        visitor.OnAttributeList(attributes);
+    }
+    visitor.OnIdentifier(identifier);
+    for (auto& member : members) {
+        visitor.OnXUnionMember(member);
+    }
+}
+
 void File::Accept(TreeVisitor& visitor) {
     SourceElementMark sem(visitor, *this);
     visitor.OnCompoundIdentifier(library_name);
-    for (auto i = using_list.begin();
-         i != using_list.end();
-         ++i) {
-        visitor.OnUsing(*i);
+    for (auto& i : using_list) {
+        visitor.OnUsing(i);
     }
-    for (auto i = const_declaration_list.begin();
-         i != const_declaration_list.end();
-         ++i) {
-        visitor.OnConstDeclaration(*i);
+    for (auto& i : const_declaration_list) {
+        visitor.OnConstDeclaration(i);
     }
-    for (auto i = enum_declaration_list.begin();
-         i != enum_declaration_list.end();
-         ++i) {
-        visitor.OnEnumDeclaration(*i);
+    for (auto& i : enum_declaration_list) {
+        visitor.OnEnumDeclaration(i);
     }
-    for (auto i = interface_declaration_list.begin();
-         i != interface_declaration_list.end();
-         ++i) {
-        visitor.OnInterfaceDeclaration(*i);
+    for (auto& i : interface_declaration_list) {
+        visitor.OnInterfaceDeclaration(i);
     }
-    for (auto i = struct_declaration_list.begin();
-         i != struct_declaration_list.end();
-         ++i) {
-        visitor.OnStructDeclaration(*i);
+    for (auto& i : struct_declaration_list) {
+        visitor.OnStructDeclaration(i);
     }
-    for (auto i = table_declaration_list.begin();
-         i != table_declaration_list.end();
-         ++i) {
-        visitor.OnTableDeclaration(*i);
+    for (auto& i : table_declaration_list) {
+        visitor.OnTableDeclaration(i);
     }
-    for (auto i = union_declaration_list.begin();
-         i != union_declaration_list.end();
-         ++i) {
-        visitor.OnUnionDeclaration(*i);
+    for (auto& i : union_declaration_list) {
+        visitor.OnUnionDeclaration(i);
+    }
+    for (auto& i : xunion_declaration_list) {
+        visitor.OnXUnionDeclaration(i);
     }
 }
 
diff --git a/system/host/fidl/lib/tables_generator.cpp b/system/host/fidl/lib/tables_generator.cpp
index 16c90a9..9bed931 100644
--- a/system/host/fidl/lib/tables_generator.cpp
+++ b/system/host/fidl/lib/tables_generator.cpp
@@ -98,7 +98,7 @@
 }
 
 void TablesGenerator::Generate(const coded::StructType& struct_type) {
-    Emit(&tables_file_, "static const ::fidl::FidlField ");
+    Emit(&tables_file_, "static const ::fidl::FidlStructField ");
     Emit(&tables_file_, NameFields(struct_type.coded_name));
     Emit(&tables_file_, "[] = ");
     GenerateArray(struct_type.fields);
@@ -157,12 +157,30 @@
     Emit(&tables_file_, "\"));\n\n");
 }
 
+void TablesGenerator::Generate(const coded::XUnionType& xunion_type) {
+    Emit(&tables_file_, "static const ::fidl::FidlXUnionField ");
+    Emit(&tables_file_, NameFields(xunion_type.coded_name));
+    Emit(&tables_file_, "[] = ");
+    GenerateArray(xunion_type.fields);
+    Emit(&tables_file_, ";\n");
+
+    Emit(&tables_file_, "const fidl_type_t ");
+    Emit(&tables_file_, NameTable(xunion_type.coded_name));
+    Emit(&tables_file_, " = fidl_type_t(::fidl::FidlCodedXUnion(");
+    Emit(&tables_file_, static_cast<uint32_t>(xunion_type.fields.size()));
+    Emit(&tables_file_, ", ");
+    Emit(&tables_file_, NameFields(xunion_type.coded_name));
+    Emit(&tables_file_, ", \"");
+    Emit(&tables_file_, xunion_type.qname);
+    Emit(&tables_file_, "\"));\n\n");
+}
+
 void TablesGenerator::Generate(const coded::MessageType& message_type) {
     Emit(&tables_file_, "extern const fidl_type_t ");
     Emit(&tables_file_, NameTable(message_type.coded_name));
     Emit(&tables_file_, ";\n");
 
-    Emit(&tables_file_, "static const ::fidl::FidlField ");
+    Emit(&tables_file_, "static const ::fidl::FidlStructField ");
     Emit(&tables_file_, NameFields(message_type.coded_name));
     Emit(&tables_file_, "[] = ");
     GenerateArray(message_type.fields);
@@ -262,7 +280,7 @@
 }
 
 void TablesGenerator::Generate(const coded::StructField& field) {
-    Emit(&tables_file_, "::fidl::FidlField(&");
+    Emit(&tables_file_, "::fidl::FidlStructField(&");
     Emit(&tables_file_, NameTable(field.type->coded_name));
     Emit(&tables_file_, ", ");
     Emit(&tables_file_, field.offset);
@@ -277,6 +295,14 @@
     Emit(&tables_file_, ")");
 }
 
+void TablesGenerator::Generate(const coded::XUnionField& field) {
+    Emit(&tables_file_, "::fidl::FidlXUnionField(&");
+    Emit(&tables_file_, NameTable(field.type->coded_name));
+    Emit(&tables_file_, ",");
+    Emit(&tables_file_, field.ordinal);
+    Emit(&tables_file_, ")");
+}
+
 void TablesGenerator::GeneratePointerIfNeeded(const coded::StructType& struct_type) {
     if (struct_type.referenced_by_pointer) {
         Emit(&tables_file_, "static const fidl_type_t ");
@@ -307,6 +333,16 @@
     }
 }
 
+void TablesGenerator::GeneratePointerIfNeeded(const coded::XUnionType& xunion_type) {
+    if (xunion_type.referenced_by_pointer) {
+        Emit(&tables_file_, "static const fidl_type_t ");
+        Emit(&tables_file_, NameTable(xunion_type.pointer_name));
+        Emit(&tables_file_, " = fidl_type_t(::fidl::FidlCodedXUnionPointer(&");
+        Emit(&tables_file_, NameTable(xunion_type.coded_name));
+        Emit(&tables_file_, ".coded_xunion));\n");
+    }
+}
+
 void TablesGenerator::GenerateForward(const coded::StructType& struct_type) {
     Emit(&tables_file_, "extern const fidl_type_t ");
     Emit(&tables_file_, NameTable(struct_type.coded_name));
@@ -325,6 +361,12 @@
     Emit(&tables_file_, ";\n");
 }
 
+void TablesGenerator::GenerateForward(const coded::XUnionType& xunion_type) {
+    Emit(&tables_file_, "extern const fidl_type_t ");
+    Emit(&tables_file_, NameTable(xunion_type.coded_name));
+    Emit(&tables_file_, ";\n");
+}
+
 const coded::Type* TablesGenerator::CompileType(const flat::Type* type) {
     switch (type->kind) {
     case flat::Type::Kind::kArray: {
@@ -416,7 +458,7 @@
         if (iter == named_coded_types_.end()) {
             assert(false && "unknown type in named type map!");
         }
-        // We may need to set the emit-pointer bit on structs and unions now.
+        // We may need to set the emit-pointer bit on structs, unions, and xunions now.
         auto coded_type = iter->second.get();
         switch (coded_type->kind) {
         case coded::Type::Kind::kStruct: {
@@ -452,6 +494,17 @@
                 coded_union_type->pointer_name, coded_union_type));
             return coded_types_.back().get();
         }
+        case coded::Type::Kind::kXUnion: {
+            // XUnions were compiled as part of decl compilation,
+            // but we may now need to generate the XUnionPointer.
+            if (identifier_type->nullability != types::Nullability::kNullable)
+                break;
+            auto coded_xunion_type = static_cast<coded::XUnionType*>(coded_type);
+            coded_xunion_type->referenced_by_pointer = true;
+            coded_types_.push_back(std::make_unique<coded::XUnionPointerType>(
+                coded_xunion_type->pointer_name, coded_xunion_type));
+            return coded_types_.back().get();
+        }
         case coded::Type::Kind::kInterface: {
             auto iter = interface_type_map_.find(identifier_type);
             if (iter != interface_type_map_.end())
@@ -471,6 +524,7 @@
         case coded::Type::Kind::kStructPointer:
         case coded::Type::Kind::kTablePointer:
         case coded::Type::Kind::kUnionPointer:
+        case coded::Type::Kind::kXUnionPointer:
         case coded::Type::Kind::kMessage:
         case coded::Type::Kind::kRequestHandle:
         case coded::Type::Kind::kHandle:
@@ -555,6 +609,27 @@
         }
         break;
     }
+    case flat::Decl::Kind::kXUnion: {
+        auto xunion_decl = static_cast<const flat::XUnion*>(decl);
+        auto coded_xunion =
+            static_cast<coded::XUnionType*>(named_coded_types_[&decl->name].get());
+
+        std::map<uint32_t, const flat::XUnion::Member*> members;
+        for (const auto& member : xunion_decl->members) {
+            if (!members.emplace(member.ordinal->value, &member).second) {
+                assert(false && "Duplicate ordinal found in table generation");
+            }
+        }
+
+        for (const auto& member_pair : members) {
+            const auto& member = *member_pair.second;
+            auto coded_member_type = CompileType(member.type.get());
+            if (coded_member_type->coding_needed == coded::CodingNeeded::kNeeded) {
+                coded_xunion->fields.emplace_back(coded_member_type, member.ordinal->value);
+            }
+        }
+        break;
+    }
     case flat::Decl::Kind::kTable: {
         auto table_decl = static_cast<const flat::Table*>(decl);
         coded::TableType* coded_table =
@@ -578,7 +653,9 @@
         }
         break;
     }
-    default: { break; }
+    default: {
+        break;
+    }
     }
 }
 
@@ -662,6 +739,16 @@
                              std::move(pointer_name), NameName(union_decl->name, ".", "/")));
         break;
     }
+    case flat::Decl::Kind::kXUnion: {
+        auto xunion_decl = static_cast<const flat::XUnion*>(decl);
+        std::string xunion_name = NameCodedXUnion(xunion_decl);
+        std::string pointer_name = NamePointer(xunion_name);
+        named_coded_types_.emplace(
+            &decl->name, std::make_unique<coded::XUnionType>(
+                             std::move(xunion_name), std::vector<coded::XUnionField>(),
+                             std::move(pointer_name), NameName(xunion_decl->name, ".", "/")));
+        break;
+    }
     }
 }
 
@@ -692,6 +779,9 @@
         case coded::Type::Kind::kUnion:
             GenerateForward(*static_cast<const coded::UnionType*>(coded_type));
             break;
+        case coded::Type::Kind::kXUnion:
+            GenerateForward(*static_cast<const coded::XUnionType*>(coded_type));
+            break;
         default:
             break;
         }
@@ -713,6 +803,9 @@
         case coded::Type::Kind::kUnion:
             GeneratePointerIfNeeded(*static_cast<const coded::UnionType*>(coded_type));
             break;
+        case coded::Type::Kind::kXUnion:
+            GeneratePointerIfNeeded(*static_cast<const coded::XUnionType*>(coded_type));
+            break;
         default:
             break;
         }
@@ -731,6 +824,8 @@
         case coded::Type::Kind::kTablePointer:
         case coded::Type::Kind::kUnion:
         case coded::Type::Kind::kUnionPointer:
+        case coded::Type::Kind::kXUnion:
+        case coded::Type::Kind::kXUnionPointer:
             // These are generated in the next phase.
             break;
         case coded::Type::Kind::kInterface:
@@ -784,6 +879,9 @@
         case coded::Type::Kind::kUnion:
             Generate(*static_cast<const coded::UnionType*>(coded_type));
             break;
+        case coded::Type::Kind::kXUnion:
+            Generate(*static_cast<const coded::XUnionType*>(coded_type));
+            break;
         default:
             continue;
         }
diff --git a/system/host/fidl/lib/tree_visitor.cpp b/system/host/fidl/lib/tree_visitor.cpp
index e7eecd0..44020ba 100644
--- a/system/host/fidl/lib/tree_visitor.cpp
+++ b/system/host/fidl/lib/tree_visitor.cpp
@@ -26,6 +26,7 @@
     auto struct_decls_it = element->struct_declaration_list.begin();
     auto table_decls_it = element->table_declaration_list.begin();
     auto union_decls_it = element->union_declaration_list.begin();
+    auto xunion_decls_it = element->xunion_declaration_list.begin();
 
     enum Next {
         const_t,
@@ -33,7 +34,8 @@
         interface_t,
         struct_t,
         table_t,
-        union_t
+        union_t,
+        xunion_t,
     };
 
     std::map<const char*, Next> m;
@@ -69,6 +71,9 @@
         if (union_decls_it != element->union_declaration_list.end()) {
             m[(*union_decls_it)->start_.previous_end().data().data()] = union_t;
         }
+        if (xunion_decls_it != element->xunion_declaration_list.end()) {
+            m[(*xunion_decls_it)->start_.previous_end().data().data()] = xunion_t;
+        }
         if (m.size() == 0)
             break;
 
@@ -98,6 +103,10 @@
             OnUnionDeclaration(*union_decls_it);
             ++union_decls_it;
             break;
+        case xunion_t:
+            OnXUnionDeclaration(*xunion_decls_it);
+            ++xunion_decls_it;
+            break;
         }
     } while (1);
     OnSourceElementEnd(*element);
diff --git a/system/host/fidl/schema.json b/system/host/fidl/schema.json
index 0ccae74..cc6bf3b 100644
--- a/system/host/fidl/schema.json
+++ b/system/host/fidl/schema.json
@@ -11,6 +11,7 @@
         "struct_declarations",
         "table_declarations",
         "union_declarations",
+        "xunion_declarations",
         "declaration_order",
         "declarations",
         "library_dependencies"
@@ -66,6 +67,13 @@
                 "$ref": "#/definitions/union"
             }
         },
+        "xunion_declarations": {
+            "description": "List of extensible unions",
+            "type": "array",
+            "items": {
+                "$ref": "#/definitions/xunion"
+            }
+        },
         "declaration_order": {
             "description": "List of declarations in declaration order",
             "type": "array",
@@ -555,6 +563,97 @@
                 }
             }
         },
+        "xunion": {
+            "description": "Definition of an extensible union",
+            "type": "object",
+            "required": [
+                "name",
+                "members",
+                "size",
+                "max_out_of_line",
+                "alignment"
+            ],
+            "properties": {
+                "max_handles": {
+                    "description": "Maximum number of handles present in the extensible union",
+                    "$ref": "#/definitions/count"
+                },
+                "maybe_attributes": {
+                    "description": "Optional list of extensible union attributes",
+                    "$ref": "#/definitions/attributes-list"
+                },
+                "name": {
+                    "description": "Name of extensible union",
+                    "$ref": "#/definitions/compound-identifier"
+                },
+                "members": {
+                    "description": "List of extensible union members",
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/xunion-member"
+                    }
+                },
+                "size": {
+                    "description": "Size of extensible union",
+                    "$ref": "#/definitions/count"
+                },
+                "max_out_of_line": {
+                    "description": "Maximum size of the out-of-line payload",
+                    "$ref": "#/definitions/count"
+                },
+                "alignment": {
+                    "description": "Alignment of extensible union",
+                    "$ref": "#/definitions/count"
+                }
+            }
+        },
+        "xunion-member": {
+            "description": "Definition of an extensible union member",
+            "type": "object",
+            "required": [
+                "ordinal",
+                "type",
+                "name",
+                "size",
+                "max_out_of_line",
+                "alignment",
+                "offset"
+            ],
+            "properties": {
+                "ordinal": {
+                    "description": "Ordinal value of extensible union member",
+                    "$ref": "#/definitions/ordinal"
+                },
+                "type": {
+                    "description": "Type of extensible union member",
+                    "$ref": "#/definitions/type"
+                },
+                "name": {
+                    "description": "Name of extensible union member",
+                    "$ref": "#/definitions/identifier"
+                },
+                "size": {
+                    "description": "Size of extensible union member",
+                    "$ref": "#/definitions/count"
+                },
+                "max_out_of_line": {
+                    "description": "Maximum size of the out-of-line payload",
+                    "$ref": "#/definitions/count"
+                },
+                "alignment": {
+                    "description": "Alignment of extensible union member",
+                    "$ref": "#/definitions/count"
+                },
+                "offset": {
+                    "description": "Offset of extensible union member",
+                    "$ref": "#/definitions/count"
+                },
+                "maybe_attributes": {
+                    "description": "Optional list of extensible union member attributes",
+                    "$ref": "#/definitions/attributes-list"
+                }
+            }
+        },
         "declarations-map": {
             "description": "Definition of declarations of a library",
             "type": "object",
@@ -567,7 +666,8 @@
                         "interface",
                         "struct",
                         "table",
-                        "union"
+                        "union",
+                        "xunion"
                     ]
                 }
             }
diff --git a/system/public/zircon/fidl.h b/system/public/zircon/fidl.h
index 5209ea5..e863a96 100644
--- a/system/public/zircon/fidl.h
+++ b/system/public/zircon/fidl.h
@@ -5,9 +5,9 @@
 #ifndef ZIRCON_FIDL_H_
 #define ZIRCON_FIDL_H_
 
-#include <assert.h>
-#include <stdalign.h>
-#include <stdint.h>
+#include <assert.h>   // NOLINT(modernize-deprecated-headers, foobar)
+#include <stdalign.h> // NOLINT(modernize-deprecated-headers)
+#include <stdint.h>   // NOLINT(modernize-*)
 
 #include <zircon/compiler.h>
 #include <zircon/types.h>
@@ -15,7 +15,7 @@
 __BEGIN_CDECLS
 
 // Fidl data types have a representation in a wire format. This wire
-// format is shared by all language bindings, including C11 and C++14.
+// format is shared by all language bindings, including C11 and C++.
 //
 // The C bindings also define a representation of fidl data types. For
 // a given type, the size and alignment of all parts of the type agree
@@ -199,7 +199,8 @@
 // - Stores a variable size uninterpreted payload out-of-line.
 // - Payload may contain an arbitrary number of bytes and handles.
 // - Allows for encapsulation of one FIDL message inside of another.
-// - Building block for derived structures such as Tables.
+// - Building block for extensible structures such as tables & extensible
+//   unions.
 
 // When encoded for transfer, |data| indicates presence of content:
 // - FIDL_ALLOC_ABSENT : envelope is null
@@ -208,16 +209,24 @@
 // - nullptr : envelope is null
 // - <valid pointer> : envelope is non-null, |data| is at indicated memory address
 
-typedef struct fidl_envelope {
-    // Number of bytes in the envelope.
-    // Always a multiple of 8; must be zero if envelope is null.
+typedef struct {
+    // The size of the entire envelope contents, including any additional
+    // out-of-line objects that the envelope may contain. For example, a
+    // vector<string>'s num_bytes for ["hello", "world"] would include the
+    // string contents in the size, not just the outer vector. Always a multiple
+    // of 8; must be zero if envelope is null.
     uint32_t num_bytes;
 
-    // Number of handles in envelope; must be zero if envelope is null.
+    // The number of handles in the envelope, including any additional
+    // out-of-line objects that the envelope contains. Must be zero if envelope is null.
     uint32_t num_handles;
 
-    // Pointer to out-of-line data.
-    void* data;
+    // A pointer to the out-of-line envelope data in decoded form, or
+    // FIDL_ALLOC_(ABSENT|PRESENT) in encoded form.
+    union {
+        void* data;
+        uintptr_t presence;
+    };
 } fidl_envelope_t;
 
 // Handle types.
@@ -305,6 +314,22 @@
 //                                                   union_foo, or else
 //                                                   FIDL_ALLOC_ABSENT.
 
+// Extensible unions.
+
+// Extensible unions, or "xunions" (colloquially pronounced "zoo-nions") are
+// similar to unions, except that storage for union members are out-of-line
+// rather than inline. This enables union members to be added and removed while
+// preserving ABI compatibility with the existing xunion definition. Like
+// unions, xunions have a 4-byte tag, and may be nullable.
+
+typedef uint32_t fidl_xunion_tag_t;
+
+typedef struct {
+    fidl_xunion_tag_t tag;
+    uint32_t padding; // Should always be zero.
+    fidl_envelope_t envelope;
+} fidl_xunion_t;
+
 // Messages.
 
 // All fidl messages share a common 16 byte header.
diff --git a/system/ulib/fidl/buffer_walker.h b/system/ulib/fidl/buffer_walker.h
index 0f85ece..93d9484 100644
--- a/system/ulib/fidl/buffer_walker.h
+++ b/system/ulib/fidl/buffer_walker.h
@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#pragma once
+#ifndef ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
+#define ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
+
+#include <lib/fidl/internal.h>
+#include <cstdlib>
 
 #include <cstdint>
 #include <cstdlib>
@@ -125,7 +129,7 @@
                     Pop();
                     continue;
                 }
-                const fidl::FidlField& field = frame->struct_state.fields[field_index];
+                const fidl::FidlStructField& field = frame->struct_state.fields[field_index];
                 const fidl_type_t* field_type = field.type;
                 const uint32_t field_offset = frame->offset + field.offset;
                 if (!Push(Frame(field_type, field_offset))) {
@@ -235,12 +239,12 @@
                 const uint32_t table_bytes = static_cast<uint32_t>(packed_sizes & 0xffffffffu);
                 const uint32_t table_handles = static_cast<uint32_t>(packed_sizes >> 32);
                 if (add_overflow(out_of_line_offset_, table_bytes, &offset) || offset > num_bytes()) {
-                    SetError("integer overflow decoding table field");
+                    SetError("table is larger than expected");
                     FIDL_POP_AND_CONTINUE_OR_RETURN;
                 }
                 if (add_overflow(handle_idx_, table_handles, &handles) ||
                     handles > num_handles()) {
-                    SetError("integer overflow decoding table handles");
+                    SetError("table has more handles than expected");
                     FIDL_POP_AND_CONTINUE_OR_RETURN;
                 }
                 frame->table_state.end_offset = offset;
@@ -295,6 +299,125 @@
                 *frame = Frame(coded_table, frame->offset);
                 continue;
             }
+            case Frame::kStateXUnion: {
+                auto xunion = TypedAt<fidl_xunion_t>(frame->offset);
+
+                if (xunion->padding != 0) {
+                    SetError("xunion padding after discriminant are non-zero");
+                    FIDL_POP_AND_CONTINUE_OR_RETURN;
+                }
+
+                uint32_t end_offset;
+                if (add_overflow(out_of_line_offset_, xunion->envelope.num_bytes, &end_offset) || end_offset > num_bytes()) {
+                    SetError("xunion size is larger than expected");
+                    FIDL_POP_AND_CONTINUE_OR_RETURN;
+                }
+
+                uint32_t total_handle_count;
+                if (add_overflow(handle_idx_, xunion->envelope.num_handles, &total_handle_count) ||
+                    total_handle_count > num_handles()) {
+                    SetError("xunion has more handles than expected");
+                    FIDL_POP_AND_CONTINUE_OR_RETURN;
+                }
+
+                switch (GetPointerState(&xunion->envelope.data)) {
+                case PointerState::PRESENT:
+                    if (xunion->tag == 0) {
+                        SetError("xunion has zero discriminant but envelope is present");
+                        FIDL_POP_AND_CONTINUE_OR_RETURN;
+                    }
+                    break;
+                case PointerState::ABSENT:
+                    if (xunion->tag != 0) {
+                        SetError("xunion has non-zero discriminant but no data");
+                        FIDL_POP_AND_CONTINUE_OR_RETURN;
+                    }
+
+                    // Empty xunion.
+                    frame->offset += static_cast<uint32_t>(sizeof(fidl_xunion_t));
+                    Pop();
+                    continue;
+                case PointerState::INVALID:
+                default:
+                    SetError("xunion has invalid envelope pointer");
+                    FIDL_POP_AND_CONTINUE_OR_RETURN;
+                    break;
+                }
+
+                const FidlXUnionField* known_field = nullptr;
+                for (size_t i = 0; i < frame->xunion_state.field_count; i++) {
+                    const auto field = frame->xunion_state.fields + i;
+
+                    if (field->ordinal == xunion->tag) {
+                        known_field = field;
+                        break;
+                    }
+                }
+
+                auto claim_data = [&](uint32_t known_size, uint32_t* envelope_offset) {
+                    if (!ClaimOutOfLineStorage(known_size, xunion->envelope.data, envelope_offset)) {
+                        SetError("xunion out-of-line storage claim couldn't be validated");
+                        return false;
+                    }
+
+                    if (*envelope_offset + xunion->envelope.num_bytes != end_offset) {
+                        SetError("expected xunion end offset doesn't match envelope offset + recursive size");
+                        return false;
+                    }
+
+                    UpdatePointer(&xunion->envelope.data, TypedAt<void>(*envelope_offset));
+
+                    return true;
+                };
+
+                uint32_t envelope_offset;
+                if (known_field != nullptr) {
+                    if (!claim_data(TypeSize(known_field->type), &envelope_offset)) {
+                        FIDL_POP_AND_CONTINUE_OR_RETURN;
+                    }
+
+                    frame->offset = envelope_offset;
+                    *frame = Frame(known_field->type, envelope_offset);
+                } else {
+                    if (!claim_data(xunion->envelope.num_bytes, &envelope_offset)) {
+                        FIDL_POP_AND_CONTINUE_OR_RETURN;
+                    }
+
+                    for (uint32_t i = 0; i < xunion->envelope.num_handles; i++) {
+                        if (!ClaimHandle(nullptr)) {
+                            SetError("expected handle not present");
+                            FIDL_POP_AND_CONTINUE_OR_RETURN;
+                        }
+                    }
+
+                    frame->offset = end_offset;
+                    Pop();
+                }
+
+                continue;
+            }
+            case Frame::kStateXUnionPointer: {
+                auto xunion_ptr_ptr = TypedAt<fidl_xunion_tag_t*>(frame->offset);
+                switch (GetPointerState(TypedAt<void>(frame->offset))) {
+                case PointerState::PRESENT:
+                    break;
+                case PointerState::ABSENT:
+                    Pop();
+                    continue;
+                default:
+                    SetError("Tried to decode a bad xunion pointer");
+                    FIDL_POP_AND_CONTINUE_OR_RETURN;
+                }
+                if (!ClaimOutOfLineStorage(sizeof(fidl_xunion_t), *xunion_ptr_ptr,
+                                           &frame->offset)) {
+                    SetError("message wanted to store too large of a nullable xunion");
+                    FIDL_POP_AND_CONTINUE_OR_RETURN;
+                }
+                UpdatePointer(xunion_ptr_ptr, TypedAt<fidl_xunion_tag_t>(frame->offset));
+                const fidl::FidlCodedXUnion* coded_xunion = frame->xunion_pointer_state.xunion_type;
+                *frame = Frame(coded_xunion, frame->offset);
+                continue;
+            }
             case Frame::kStateUnion: {
                 fidl_union_tag_t union_tag = *TypedAt<fidl_union_tag_t>(frame->offset);
                 if (union_tag >= frame->union_state.type_count) {
@@ -403,11 +526,13 @@
                     }
                     Pop();
                     continue;
-                default:
+                case HandleState::INVALID:
+                default: {
                     // The value at the handle was garbage.
                     SetError("message tried to decode a garbage handle");
                     FIDL_POP_AND_CONTINUE_OR_RETURN;
                 }
+                }
             }
             case Frame::kStateVector: {
                 auto vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
@@ -578,6 +703,7 @@
         case fidl::kFidlTypeStructPointer:
         case fidl::kFidlTypeTablePointer:
         case fidl::kFidlTypeUnionPointer:
+        case fidl::kFidlTypeXUnionPointer:
             return sizeof(uint64_t);
         case fidl::kFidlTypeHandle:
             return sizeof(zx_handle_t);
@@ -587,6 +713,8 @@
             return sizeof(fidl_vector_t);
         case fidl::kFidlTypeUnion:
             return type->coded_union.size;
+        case fidl::kFidlTypeXUnion:
+            return sizeof(fidl_xunion_t);
         case fidl::kFidlTypeString:
             return sizeof(fidl_string_t);
         case fidl::kFidlTypeArray:
@@ -632,6 +760,15 @@
                 state = kStateUnionPointer;
                 union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type;
                 break;
+            case fidl::kFidlTypeXUnion:
+                state = kStateXUnion;
+                xunion_state.fields = fidl_type->coded_xunion.fields;
+                xunion_state.field_count = fidl_type->coded_xunion.field_count;
+                break;
+            case fidl::kFidlTypeXUnionPointer:
+                state = kStateXUnionPointer;
+                xunion_pointer_state.xunion_type = fidl_type->coded_xunion_pointer.xunion_type;
+                break;
             case fidl::kFidlTypeArray:
                 state = kStateArray;
                 array_state.element = fidl_type->coded_array.element;
@@ -679,6 +816,15 @@
             union_state.data_offset = coded_union->data_offset;
         }
 
+        Frame(const fidl::FidlCodedXUnion* coded_xunion, uint32_t offset)
+            : state(kStateXUnion), offset(offset) {
+            // This initialization is done in the ctor body instead of in an
+            // initialization list since we need to set fields in unions, which
+            // is much more involved in a ctor initialization list.
+            xunion_state.fields = coded_xunion->fields;
+            xunion_state.field_count = coded_xunion->field_count;
+        }
+
         Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size,
               uint32_t offset)
             : offset(offset) {
@@ -724,6 +870,8 @@
             kStateString,
             kStateHandle,
             kStateVector,
+            kStateXUnion,
+            kStateXUnionPointer,
 
             kStateDone,
         } state;
@@ -735,7 +883,7 @@
         // example, struct sizes do not need to be present here.
         union {
             struct {
-                const fidl::FidlField* fields;
+                const fidl::FidlStructField* fields;
                 uint32_t field_count;
             } struct_state;
             struct {
@@ -761,6 +909,13 @@
                 const fidl::FidlCodedUnion* union_type;
             } union_pointer_state;
             struct {
+                const fidl::FidlXUnionField* fields;
+                uint32_t field_count;
+            } xunion_state;
+            struct {
+                const fidl::FidlCodedXUnion* xunion_type;
+            } xunion_pointer_state;
+            struct {
                 const fidl_type_t* element;
                 uint32_t array_size;
                 uint32_t element_size;
@@ -817,3 +972,5 @@
 
 } // namespace internal
 } // namespace fidl
+
+#endif // ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
diff --git a/system/ulib/fidl/include/lib/fidl/internal.h b/system/ulib/fidl/include/lib/fidl/internal.h
index efef16e..b814fd9 100644
--- a/system/ulib/fidl/include/lib/fidl/internal.h
+++ b/system/ulib/fidl/include/lib/fidl/internal.h
@@ -35,14 +35,18 @@
     return (offset + alignment_mask) & ~alignment_mask;
 }
 
-struct FidlField {
+struct FidlStructField {
     const fidl_type* type;
     uint32_t offset;
 
-    constexpr FidlField(const fidl_type* type, uint32_t offset)
+    constexpr FidlStructField(const fidl_type* type, uint32_t offset)
         : type(type), offset(offset) {}
 };
 
+// TODO(apang): Remove this backward-compatible typedef after all dependencies
+// have been updated from FidlField to FidlStructField.
+using FidlField = FidlStructField;
+
 struct FidlTableField {
     const fidl_type* type;
     uint32_t ordinal;
@@ -51,28 +55,38 @@
         : type(type), ordinal(ordinal) {}
 };
 
+struct FidlXUnionField {
+    const fidl_type* type;
+    uint32_t ordinal;
+
+    constexpr FidlXUnionField(const fidl_type* type, uint32_t ordinal)
+        : type(type), ordinal(ordinal) {}
+};
+
 enum FidlTypeTag : uint32_t {
-    kFidlTypeStruct = 0u,
-    kFidlTypeStructPointer = 1u,
-    kFidlTypeTable = 8u,
-    kFidlTypeTablePointer = 9u,
-    kFidlTypeUnion = 2u,
-    kFidlTypeUnionPointer = 3u,
-    kFidlTypeArray = 4u,
-    kFidlTypeString = 5u,
-    kFidlTypeHandle = 6u,
-    kFidlTypeVector = 7u,
+    kFidlTypeStruct,
+    kFidlTypeStructPointer,
+    kFidlTypeUnion,
+    kFidlTypeUnionPointer,
+    kFidlTypeArray,
+    kFidlTypeString,
+    kFidlTypeHandle,
+    kFidlTypeVector,
+    kFidlTypeTable,
+    kFidlTypeTablePointer,
+    kFidlTypeXUnion,
+    kFidlTypeXUnionPointer,
 };
 
 // Though the |size| is implied by the fields, computing that information is not the purview of this
 // library. It's easier for the compiler to stash it.
 struct FidlCodedStruct {
-    const FidlField* const fields;
+    const FidlStructField* const fields;
     const uint32_t field_count;
     const uint32_t size;
     const char* name; // may be nullptr if omitted at compile time
 
-    constexpr FidlCodedStruct(const FidlField* fields, uint32_t field_count, uint32_t size,
+    constexpr FidlCodedStruct(const FidlStructField* fields, uint32_t field_count, uint32_t size,
                               const char* name)
         : fields(fields), field_count(field_count), size(size), name(name) {}
 };
@@ -101,8 +115,8 @@
         : table_type(table_type) {}
 };
 
-// Unlike structs, union members do not have different offsets, so this points to an array of
-// |fidl_type*| rather than |FidlField|.
+// Unlike structs, union members do not have different offsets, so this points
+// to an array of |fidl_type*| rather than |FidlStructField|.
 //
 // On-the-wire unions begin with a tag which is an index into |types|.
 // |data_offset| is the offset of the data in the wire format (tag + padding).
@@ -125,6 +139,22 @@
         : union_type(union_type) {}
 };
 
+struct FidlCodedXUnion {
+    const uint32_t field_count;
+    const FidlXUnionField* const fields;
+    const char* name; // may be nullptr if omitted at compile time
+
+    constexpr FidlCodedXUnion(uint32_t field_count, const FidlXUnionField* fields, const char* name)
+        : field_count(field_count), fields(fields), name(name) {}
+};
+
+struct FidlCodedXUnionPointer {
+    const FidlCodedXUnion* const xunion_type;
+
+    constexpr explicit FidlCodedXUnionPointer(const FidlCodedXUnion* xunion_type)
+        : xunion_type(xunion_type) {}
+};
+
 // An array is essentially a struct with |array_size / element_size| of the same field, named at
 // |element|.
 struct FidlCodedArray {
@@ -202,6 +232,8 @@
         const fidl::FidlCodedTablePointer coded_table_pointer;
         const fidl::FidlCodedUnion coded_union;
         const fidl::FidlCodedUnionPointer coded_union_pointer;
+        const fidl::FidlCodedXUnion coded_xunion;
+        const fidl::FidlCodedXUnionPointer coded_xunion_pointer;
         const fidl::FidlCodedHandle coded_handle;
         const fidl::FidlCodedString coded_string;
         const fidl::FidlCodedArray coded_array;
@@ -226,6 +258,12 @@
     constexpr fidl_type(fidl::FidlCodedUnionPointer coded_union_pointer)
         : type_tag(fidl::kFidlTypeUnionPointer), coded_union_pointer(coded_union_pointer) {}
 
+    constexpr fidl_type(fidl::FidlCodedXUnion coded_xunion)
+        : type_tag(fidl::kFidlTypeXUnion), coded_xunion(coded_xunion) {}
+
+    constexpr fidl_type(fidl::FidlCodedXUnionPointer coded_xunion_pointer)
+        : type_tag(fidl::kFidlTypeXUnionPointer), coded_xunion_pointer(coded_xunion_pointer) {}
+
     constexpr fidl_type(fidl::FidlCodedHandle coded_handle)
         : type_tag(fidl::kFidlTypeHandle), coded_handle(coded_handle) {}
 
diff --git a/system/ulib/fidl/walker.h b/system/ulib/fidl/walker.h
index 67658fa..68d2066 100644
--- a/system/ulib/fidl/walker.h
+++ b/system/ulib/fidl/walker.h
@@ -42,6 +42,7 @@
         case fidl::kFidlTypeStructPointer:
         case fidl::kFidlTypeTablePointer:
         case fidl::kFidlTypeUnionPointer:
+        case fidl::kFidlTypeXUnionPointer:
             return sizeof(uint64_t);
         case fidl::kFidlTypeHandle:
             return sizeof(zx_handle_t);
@@ -51,15 +52,16 @@
             return sizeof(fidl_vector_t);
         case fidl::kFidlTypeUnion:
             return type->coded_union.size;
+        case fidl::kFidlTypeXUnion:
+            return sizeof(fidl_xunion_t);
         case fidl::kFidlTypeString:
             return sizeof(fidl_string_t);
         case fidl::kFidlTypeArray:
             return type->coded_array.array_size;
         case fidl::kFidlTypeVector:
             return sizeof(fidl_vector_t);
-        default:
-            __builtin_unreachable();
     }
+    __builtin_unreachable();
 }
 
 // The Walker class traverses through a FIDL message by following its encoding table and
@@ -134,6 +136,15 @@
                     state = kStateUnionPointer;
                     union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type;
                     break;
+                case fidl::kFidlTypeXUnion:
+                    state = kStateXUnion;
+                    xunion_state.fields = fidl_type->coded_xunion.fields;
+                    xunion_state.field_count = fidl_type->coded_xunion.field_count;
+                    break;
+                case fidl::kFidlTypeXUnionPointer:
+                    state = kStateXUnionPointer;
+                    xunion_pointer_state.xunion_type = fidl_type->coded_xunion_pointer.xunion_type;
+                    break;
                 case fidl::kFidlTypeArray:
                     state = kStateArray;
                     array_state.element = fidl_type->coded_array.element;
@@ -185,6 +196,15 @@
             union_state.data_offset = coded_union->data_offset;
         }
 
+        Frame(const fidl::FidlCodedXUnion* coded_xunion, Position position)
+            : state(kStateXUnion), position(position) {
+            // This initialization is done in the ctor body instead of in an
+            // initialization list since we need to set fields in unions, which
+            // is much more involved in a ctor initialization list.
+            xunion_state.fields = coded_xunion->fields;
+            xunion_state.field_count = coded_xunion->field_count;
+        }
+
         Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size,
               Position position)
             : position(position) {
@@ -227,6 +247,8 @@
             kStateTablePointer,
             kStateUnion,
             kStateUnionPointer,
+            kStateXUnion,
+            kStateXUnionPointer,
             kStateArray,
             kStateString,
             kStateHandle,
@@ -282,6 +304,13 @@
                 const fidl::FidlCodedUnion* union_type;
             } union_pointer_state;
             struct {
+                const fidl::FidlXUnionField* fields;
+                uint32_t field_count;
+            } xunion_state;
+            struct {
+                const fidl::FidlCodedXUnion* xunion_type;
+            } xunion_pointer_state;
+            struct {
                 const fidl_type_t* element;
                 // Size of the entire array in bytes
                 uint32_t array_size;
@@ -527,6 +556,14 @@
                 *frame = Frame(coded_union, frame->position);
                 continue;
             }
+            case Frame::kStateXUnion: {
+                assert(false && "xunions not implemented yet");
+                continue;
+            }
+            case Frame::kStateXUnionPointer: {
+                assert(false && "xunion pointers not implemented yet");
+                continue;
+            }
             case Frame::kStateArray: {
                 const uint32_t element_offset = frame->NextArrayOffset();
                 if (element_offset == frame->array_state.array_size) {
diff --git a/system/ulib/trace-provider/trace_provider.fidl.tables.cpp b/system/ulib/trace-provider/trace_provider.fidl.tables.cpp
index d8c2bb8..0bcef6d 100644
--- a/system/ulib/trace-provider/trace_provider.fidl.tables.cpp
+++ b/system/ulib/trace-provider/trace_provider.fidl.tables.cpp
@@ -10,7 +10,7 @@
 //   --files system/fidl/fuchsia-tracelink/tracelink.fidl
 //
 // and then this header (up to "End of additions ...") manually inserted.
-//   
+//
 // There are *no* differences with the machine generated version except the
 // addition of this text.
 // Minimizing differences with generated code is critical to lessening the
@@ -29,8 +29,6 @@
 
 extern "C" {
 
-
-
 static const fidl_type_t HandlevmononnullableTable = fidl_type_t(::fidl::FidlCodedHandle(ZX_OBJ_TYPE_VMO, ::fidl::kNonnullable));
 
 static const fidl_type_t HandlefifononnullableTable = fidl_type_t(::fidl::FidlCodedHandle(ZX_OBJ_TYPE_FIFO, ::fidl::kNonnullable));
@@ -40,41 +38,37 @@
 static const fidl_type_t VectorString100nonnullable100nonnullableTable = fidl_type_t(::fidl::FidlCodedVector(&String100nonnullableTable, 100, 16, ::fidl::kNonnullable));
 
 extern const fidl_type_t fuchsia_tracelink_ProviderStartRequestTable;
-static const ::fidl::FidlField fuchsia_tracelink_ProviderStartRequestFields[] = {
-    ::fidl::FidlField(&HandlevmononnullableTable, 20),
-    ::fidl::FidlField(&HandlefifononnullableTable, 24),
-    ::fidl::FidlField(&VectorString100nonnullable100nonnullableTable, 32)
-};
+static const ::fidl::FidlStructField fuchsia_tracelink_ProviderStartRequestFields[] = {
+    ::fidl::FidlStructField(&HandlevmononnullableTable, 20),
+    ::fidl::FidlStructField(&HandlefifononnullableTable, 24),
+    ::fidl::FidlStructField(&VectorString100nonnullable100nonnullableTable, 32)};
 const fidl_type_t fuchsia_tracelink_ProviderStartRequestTable = fidl_type_t(::fidl::FidlCodedStruct(fuchsia_tracelink_ProviderStartRequestFields, 3, 48, "fuchsia.tracelink/ProviderStartRequest"));
 
 extern const fidl_type_t fuchsia_tracelink_ProviderStopRequestTable;
-static const ::fidl::FidlField fuchsia_tracelink_ProviderStopRequestFields[] = {};
+static const ::fidl::FidlStructField fuchsia_tracelink_ProviderStopRequestFields[] = {};
 const fidl_type_t fuchsia_tracelink_ProviderStopRequestTable = fidl_type_t(::fidl::FidlCodedStruct(fuchsia_tracelink_ProviderStopRequestFields, 0, 16, "fuchsia.tracelink/ProviderStopRequest"));
 
 static const fidl_type_t fuchsia_tracelink_ProviderInterfacenonnullableTable = fidl_type_t(::fidl::FidlCodedHandle(ZX_OBJ_TYPE_CHANNEL, ::fidl::kNonnullable));
 
 extern const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderDeprecatedRequestTable;
-static const ::fidl::FidlField fuchsia_tracelink_RegistryRegisterTraceProviderDeprecatedRequestFields[] = {
-    ::fidl::FidlField(&fuchsia_tracelink_ProviderInterfacenonnullableTable, 16)
-};
+static const ::fidl::FidlStructField fuchsia_tracelink_RegistryRegisterTraceProviderDeprecatedRequestFields[] = {
+    ::fidl::FidlStructField(&fuchsia_tracelink_ProviderInterfacenonnullableTable, 16)};
 const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderDeprecatedRequestTable = fidl_type_t(::fidl::FidlCodedStruct(fuchsia_tracelink_RegistryRegisterTraceProviderDeprecatedRequestFields, 1, 20, "fuchsia.tracelink/RegistryRegisterTraceProviderDeprecatedRequest"));
 
 extern const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderRequestTable;
-static const ::fidl::FidlField fuchsia_tracelink_RegistryRegisterTraceProviderRequestFields[] = {
-    ::fidl::FidlField(&fuchsia_tracelink_ProviderInterfacenonnullableTable, 16),
-    ::fidl::FidlField(&String100nonnullableTable, 32)
-};
+static const ::fidl::FidlStructField fuchsia_tracelink_RegistryRegisterTraceProviderRequestFields[] = {
+    ::fidl::FidlStructField(&fuchsia_tracelink_ProviderInterfacenonnullableTable, 16),
+    ::fidl::FidlStructField(&String100nonnullableTable, 32)};
 const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderRequestTable = fidl_type_t(::fidl::FidlCodedStruct(fuchsia_tracelink_RegistryRegisterTraceProviderRequestFields, 2, 48, "fuchsia.tracelink/RegistryRegisterTraceProviderRequest"));
 
 extern const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyRequestTable;
-static const ::fidl::FidlField fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyRequestFields[] = {
-    ::fidl::FidlField(&fuchsia_tracelink_ProviderInterfacenonnullableTable, 16),
-    ::fidl::FidlField(&String100nonnullableTable, 32)
-};
+static const ::fidl::FidlStructField fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyRequestFields[] = {
+    ::fidl::FidlStructField(&fuchsia_tracelink_ProviderInterfacenonnullableTable, 16),
+    ::fidl::FidlStructField(&String100nonnullableTable, 32)};
 const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyRequestTable = fidl_type_t(::fidl::FidlCodedStruct(fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyRequestFields, 2, 48, "fuchsia.tracelink/RegistryRegisterTraceProviderSynchronouslyRequest"));
 
 extern const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyResponseTable;
-static const ::fidl::FidlField fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyResponseFields[] = {};
+static const ::fidl::FidlStructField fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyResponseFields[] = {};
 const fidl_type_t fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyResponseTable = fidl_type_t(::fidl::FidlCodedStruct(fuchsia_tracelink_RegistryRegisterTraceProviderSynchronouslyResponseFields, 0, 24, "fuchsia.tracelink/RegistryRegisterTraceProviderSynchronouslyResponse"));
 
 } // extern "C"
diff --git a/system/utest/fidl-compiler/attributes_tests.cpp b/system/utest/fidl-compiler/attributes_tests.cpp
index b2419b6..4b7c748 100644
--- a/system/utest/fidl-compiler/attributes_tests.cpp
+++ b/system/utest/fidl-compiler/attributes_tests.cpp
@@ -368,6 +368,29 @@
     END_TEST;
 }
 
+bool selector_incorrect_placement() {
+    BEGIN_TEST;
+
+    TestLibrary library(R"FIDL(
+library fidl.test;
+
+[Selector = "Nonsense"]
+union MyUnion {
+  uint8 hello;
+};
+
+)FIDL");
+    EXPECT_FALSE(library.Compile());
+    auto errors = library.errors();
+    ASSERT_EQ(errors.size(), 1);
+    ASSERT_STR_STR(errors[0].c_str(),
+        "placement of attribute");
+    ASSERT_STR_STR(errors[0].c_str(),
+        "disallowed here");
+
+    END_TEST;
+}
+
 } // namespace
 
 BEGIN_TEST_CASE(attributes_tests);
@@ -385,4 +408,5 @@
 RUN_TEST(constraint_only_three_members_on_interface);
 RUN_TEST(max_bytes);
 RUN_TEST(max_handles);
+RUN_TEST(selector_incorrect_placement);
 END_TEST_CASE(attributes_tests);
diff --git a/system/utest/fidl-compiler/json_generator_tests.cpp b/system/utest/fidl-compiler/json_generator_tests.cpp
index 2df3f82..6143085 100644
--- a/system/utest/fidl-compiler/json_generator_tests.cpp
+++ b/system/utest/fidl-compiler/json_generator_tests.cpp
@@ -119,6 +119,7 @@
   ],
   "table_declarations": [],
   "union_declarations": [],
+  "xunion_declarations": [],
   "declaration_order": [
     "fidl.test.json/Simple"
   ],
@@ -264,6 +265,7 @@
   ],
   "table_declarations": [],
   "union_declarations": [],
+  "xunion_declarations": [],
   "declaration_order": [
     "fidl.test.json/Empty",
     "fidl.test.json/EmptyInterface"
@@ -344,6 +346,7 @@
     }
   ],
   "union_declarations": [],
+  "xunion_declarations": [],
   "declaration_order": [
     "fidl.test.json/Simple"
   ],
@@ -474,6 +477,7 @@
       "max_handles": 0
     }
   ],
+  "xunion_declarations": [],
   "declaration_order": [
     "fidl.test.json/Pizza",
     "fidl.test.json/Pasta",
@@ -491,6 +495,78 @@
     END_TEST;
 }
 
+bool json_generator_test_xunion() {
+    BEGIN_TEST;
+
+    for (int i = 0; i < kRepeatTestCount; i++) {
+        EXPECT_TRUE(checkJSONGenerator(R"FIDL(
+library fidl.test.json;
+
+xunion xu {
+  string s;
+  int32 i;
+};
+
+)FIDL",
+                                       R"JSON(
+{
+  "version": "0.0.1",
+  "name": "fidl.test.json",
+  "library_dependencies": [],
+  "const_declarations": [],
+  "enum_declarations": [],
+  "interface_declarations": [],
+  "struct_declarations": [],
+  "table_declarations": [],
+  "union_declarations": [],
+  "xunion_declarations": [
+    {
+      "name": "fidl.test.json/xu",
+      "members": [
+        {
+          "ordinal": 730795057,
+          "type": {
+            "kind": "string",
+            "nullable": false
+          },
+          "name": "s",
+          "size": 16,
+          "max_out_of_line": 4294967295,
+          "alignment": 8,
+          "offset": 0
+        },
+        {
+          "ordinal": 243975053,
+          "type": {
+            "kind": "primitive",
+            "subtype": "int32"
+          },
+          "name": "i",
+          "size": 4,
+          "max_out_of_line": 0,
+          "alignment": 4,
+          "offset": 0
+        }
+      ],
+      "size": 24,
+      "max_out_of_line": 4294967295,
+      "alignment": 8,
+      "max_handles": 0
+    }
+  ],
+  "declaration_order": [
+    "fidl.test.json/xu"
+  ],
+  "declarations": {
+    "fidl.test.json/xu": "xunion"
+  }
+}
+)JSON"));
+    }
+
+    END_TEST;
+}
+
 // This test ensures that inherited methods have the same ordinal / signature /
 // etc as the method from which they are inheriting.
 bool json_generator_test_inheritance() {
@@ -614,6 +690,7 @@
   "struct_declarations": [],
   "table_declarations": [],
   "union_declarations": [],
+  "xunion_declarations": [],
   "declaration_order": [
     "fidl.test.json/super",
     "fidl.test.json/sub"
@@ -744,6 +821,7 @@
   "struct_declarations": [],
   "table_declarations": [],
   "union_declarations": [],
+  "xunion_declarations": [],
   "declaration_order": [
     "fidl.test.json/Parent",
     "fidl.test.json/Child"
@@ -764,6 +842,7 @@
 RUN_TEST(json_generator_test_struct);
 RUN_TEST(json_generator_test_table);
 RUN_TEST(json_generator_test_union);
+RUN_TEST(json_generator_test_xunion);
 RUN_TEST(json_generator_test_inheritance);
 RUN_TEST(json_generator_test_inheritance_with_recursive_decl);
 END_TEST_CASE(json_generator_tests);
diff --git a/system/utest/fidl-compiler/max_bytes_tests.cpp b/system/utest/fidl-compiler/max_bytes_tests.cpp
index 7c26e36..717191d 100644
--- a/system/utest/fidl-compiler/max_bytes_tests.cpp
+++ b/system/utest/fidl-compiler/max_bytes_tests.cpp
@@ -168,10 +168,44 @@
   1: array<int64>:5 a;
 };
 
+xunion EmptyXUnion {
+};
+
+xunion XUnionWithOneBool {
+  bool b;
+};
+
+xunion XUnionWithBoolAndU32 {
+  bool b;
+  uint32 u;
+};
+
+xunion XUnionWithBoundedOutOfLineObject {
+  // smaller than |v| below, so will not be selected for max-out-of-line
+  // calculation.
+  bool b;
+
+  // 1. vector<int32>:5 = 20 bytes
+  //                    = 24 bytes for 8-byte boundary alignment
+  //                    +  8 bytes for vector element count
+  //                    +  8 bytes for data pointer
+  //                    = 40 bytes total
+  // 1. vector<vector<int32>:5>:6 = vector<int32>:5 (40) * 6
+  //                              = 240 bytes
+  //                              +   8 bytes for vector element count
+  //                              +   8 bytes for data pointer
+  //                              = 256 bytes total
+  vector<vector<int32>:5>:6 v;
+};
+
+xunion XUnionWithUnboundedOutOfLineObject {
+  string s;
+};
+
 )FIDL") {}
 };
 
-static bool simple_structs(void) {
+static bool simple_structs() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -200,7 +234,7 @@
     END_TEST;
 }
 
-static bool simple_tables(void) {
+static bool simple_tables() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -229,7 +263,7 @@
     END_TEST;
 }
 
-static bool optional_structs(void) {
+static bool optional_structs() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -258,7 +292,7 @@
     END_TEST;
 }
 
-static bool optional_tables(void) {
+static bool optional_tables() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -307,7 +341,7 @@
     END_TEST;
 }
 
-static bool unions(void) {
+static bool unions() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -331,7 +365,7 @@
     END_TEST;
 }
 
-static bool vectors(void) {
+static bool vectors() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -370,7 +404,7 @@
     END_TEST;
 }
 
-static bool strings(void) {
+static bool strings() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -399,7 +433,7 @@
     END_TEST;
 }
 
-static bool arrays(void) {
+static bool arrays() {
     BEGIN_TEST;
 
     MaxBytesLibrary test_library;
@@ -418,6 +452,33 @@
     END_TEST;
 }
 
+static bool xunions() {
+    BEGIN_TEST;
+
+    MaxBytesLibrary test_library;
+    EXPECT_TRUE(test_library.Compile());
+
+    auto empty = test_library.LookupXUnion("EmptyXUnion");
+    EXPECT_EQ(empty->typeshape.Size(), 24);
+    EXPECT_EQ(empty->typeshape.MaxOutOfLine(), 0);
+
+    auto one_bool = test_library.LookupXUnion("XUnionWithOneBool");
+    EXPECT_EQ(one_bool->typeshape.Size(), 24);
+    EXPECT_EQ(one_bool->typeshape.MaxOutOfLine(), 8);
+
+    auto xu = test_library.LookupXUnion("XUnionWithBoundedOutOfLineObject");
+    EXPECT_EQ(xu->typeshape.Size(), 24);
+    EXPECT_EQ(xu->typeshape.MaxOutOfLine(), 256);
+
+    auto unbounded = test_library.LookupXUnion("XUnionWithUnboundedOutOfLineObject");
+    EXPECT_EQ(unbounded->typeshape.Size(), 24);
+    EXPECT_EQ(unbounded->typeshape.MaxOutOfLine(), std::numeric_limits<uint32_t>::max());
+
+    // TODO(apang): More tests here
+
+    END_TEST;
+}
+
 } // namespace
 
 BEGIN_TEST_CASE(max_bytes_tests);
@@ -429,4 +490,5 @@
 RUN_TEST(vectors);
 RUN_TEST(strings);
 RUN_TEST(arrays);
+RUN_TEST(xunions);
 END_TEST_CASE(max_bytes_tests);
diff --git a/system/utest/fidl-compiler/rules.mk b/system/utest/fidl-compiler/rules.mk
index a5a162e..1f2a4ca 100644
--- a/system/utest/fidl-compiler/rules.mk
+++ b/system/utest/fidl-compiler/rules.mk
@@ -81,6 +81,7 @@
     $(LOCAL_DIR)/types_tests.cpp \
     $(LOCAL_DIR)/using_tests.cpp \
     $(LOCAL_DIR)/visitor_unittests.cpp \
+    $(LOCAL_DIR)/xunion_tests.cpp \
     $(BUILDGEN_DIR)/examples.cpp \
 
 MODULE_COMPILEFLAGS := \
diff --git a/system/utest/fidl-compiler/test_library.h b/system/utest/fidl-compiler/test_library.h
index 3348cc4..e7cdb75 100644
--- a/system/utest/fidl-compiler/test_library.h
+++ b/system/utest/fidl-compiler/test_library.h
@@ -101,6 +101,15 @@
         return nullptr;
     }
 
+    const fidl::flat::XUnion* LookupXUnion(const std::string& name) {
+        for (const auto& xunion_decl : library_->xunion_declarations_) {
+            if (xunion_decl->GetName() == name) {
+                return xunion_decl.get();
+            }
+        }
+        return nullptr;
+    }
+
     const fidl::flat::Interface* LookupInterface(const std::string& name) {
         for (const auto& interface_decl : library_->interface_declarations_) {
             if (interface_decl->GetName() == name) {
diff --git a/system/utest/fidl-compiler/xunion_tests.cpp b/system/utest/fidl-compiler/xunion_tests.cpp
new file mode 100644
index 0000000..be84725
--- /dev/null
+++ b/system/utest/fidl-compiler/xunion_tests.cpp
@@ -0,0 +1,77 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <unittest/unittest.h>
+
+#include <fidl/flat_ast.h>
+#include <fidl/lexer.h>
+#include <fidl/parser.h>
+#include <fidl/source_file.h>
+
+#include "test_library.h"
+
+namespace {
+
+static bool Compiles(const std::string& source_code) {
+    return TestLibrary("test.fidl", source_code).Compile();
+}
+
+static bool compiling() {
+    BEGIN_TEST;
+
+    // Populated fields.
+    EXPECT_TRUE(Compiles(R"FIDL(
+library fidl.test.xunions;
+
+xunion Foo {
+    int64 i;
+};
+)FIDL"));
+
+    // Empty xunions are allowed.
+    EXPECT_TRUE(Compiles(R"FIDL(
+library fidl.test.xunions;
+
+xunion Foo {
+};
+)FIDL"));
+
+    // Explicit ordinals are invalid.
+    EXPECT_FALSE(Compiles(R"FIDL(
+library fidl.test.xunions;
+
+xunion Foo {
+    1: int64 x;
+};
+)FIDL"));
+
+    // Attributes on fields.
+    EXPECT_TRUE(Compiles(R"FIDL(
+library fidl.test.xunions;
+
+xunion Foo {
+    [FooAttr="bar"] int64 x;
+    [BarAttr] bool bar;
+};
+)FIDL"));
+
+    // Attributes on xunions.
+    EXPECT_TRUE(Compiles(R"FIDL(
+library fidl.test.xunions;
+
+[FooAttr="bar"]
+xunion Foo {
+    int64 x;
+    bool please;
+};
+)FIDL"));
+
+    END_TEST;
+}
+
+} // namespace
+
+BEGIN_TEST_CASE(xunion_tests);
+RUN_TEST(compiling);
+END_TEST_CASE(xunion_tests);
diff --git a/system/utest/fidl/abi_tests.cpp b/system/utest/fidl/abi_tests.cpp
index 16c7a0d..c90dc8c 100644
--- a/system/utest/fidl/abi_tests.cpp
+++ b/system/utest/fidl/abi_tests.cpp
@@ -12,7 +12,7 @@
 
 // All the data in coding tables should be pure data.
 static_assert(std::is_standard_layout<fidl_type>::value, "");
-static_assert(std::is_standard_layout<FidlField>::value, "");
+static_assert(std::is_standard_layout<FidlStructField>::value, "");
 static_assert(std::is_standard_layout<FidlTableField>::value, "");
 static_assert(std::is_standard_layout<FidlTypeTag>::value, "");
 static_assert(std::is_standard_layout<FidlCodedStruct>::value, "");
diff --git a/system/utest/fidl/fidl_coded_types.cpp b/system/utest/fidl/fidl_coded_types.cpp
index 758ce44..530ff87 100644
--- a/system/utest/fidl/fidl_coded_types.cpp
+++ b/system/utest/fidl/fidl_coded_types.cpp
@@ -92,31 +92,31 @@
     fidl::FidlCodedVector(nullptr, 2, sizeof(uint32_t), fidl::kNullable));
 
 // Handle messages.
-static const fidl::FidlField nonnullable_handle_message_fields[] = {
-    fidl::FidlField(&nonnullable_handle,
-                    offsetof(nonnullable_handle_message_layout, inline_struct.handle)),
+static const fidl::FidlStructField nonnullable_handle_message_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle,
+                          offsetof(nonnullable_handle_message_layout, inline_struct.handle)),
 };
 const fidl_type_t nonnullable_handle_message_type = fidl_type_t(fidl::FidlCodedStruct(
     nonnullable_handle_message_fields, ArrayCount(nonnullable_handle_message_fields),
     sizeof(nonnullable_handle_inline_data),
     "nonnullable_handle_message"));
 
-static const fidl::FidlField multiple_nonnullable_handles_fields[] = {
-    fidl::FidlField(&nonnullable_handle,
-                    offsetof(multiple_nonnullable_handles_message_layout, inline_struct.handle_0)),
-    fidl::FidlField(&nonnullable_channel_handle,
-                    offsetof(multiple_nonnullable_handles_message_layout, inline_struct.handle_1)),
-    fidl::FidlField(&nonnullable_vmo_handle,
-                    offsetof(multiple_nonnullable_handles_message_layout, inline_struct.handle_2)),
+static const fidl::FidlStructField multiple_nonnullable_handles_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle,
+                          offsetof(multiple_nonnullable_handles_message_layout, inline_struct.handle_0)),
+    fidl::FidlStructField(&nonnullable_channel_handle,
+                          offsetof(multiple_nonnullable_handles_message_layout, inline_struct.handle_1)),
+    fidl::FidlStructField(&nonnullable_vmo_handle,
+                          offsetof(multiple_nonnullable_handles_message_layout, inline_struct.handle_2)),
 };
 const fidl_type_t multiple_nonnullable_handles_message_type = fidl_type_t(fidl::FidlCodedStruct(
     multiple_nonnullable_handles_fields, ArrayCount(multiple_nonnullable_handles_fields),
     sizeof(multiple_nonnullable_handles_inline_data),
     "multiple_nonnullable_handles_message"));
 
-static const fidl::FidlField nullable_handle_fields[] = {
-    fidl::FidlField(&nullable_handle,
-                    offsetof(nullable_handle_message_layout, inline_struct.handle)),
+static const fidl::FidlStructField nullable_handle_fields[] = {
+    fidl::FidlStructField(&nullable_handle,
+                          offsetof(nullable_handle_message_layout, inline_struct.handle)),
 
 };
 const fidl_type_t nullable_handle_message_type =
@@ -124,13 +124,13 @@
                                       sizeof(nullable_handle_inline_data),
                                       "nullable_handle_message"));
 
-static const fidl::FidlField multiple_nullable_handles_fields[] = {
-    fidl::FidlField(&nullable_handle,
-                    offsetof(multiple_nullable_handles_message_layout, inline_struct.handle_0)),
-    fidl::FidlField(&nullable_channel_handle,
-                    offsetof(multiple_nullable_handles_message_layout, inline_struct.handle_1)),
-    fidl::FidlField(&nullable_vmo_handle,
-                    offsetof(multiple_nullable_handles_message_layout, inline_struct.handle_2)),
+static const fidl::FidlStructField multiple_nullable_handles_fields[] = {
+    fidl::FidlStructField(&nullable_handle,
+                          offsetof(multiple_nullable_handles_message_layout, inline_struct.handle_0)),
+    fidl::FidlStructField(&nullable_channel_handle,
+                          offsetof(multiple_nullable_handles_message_layout, inline_struct.handle_1)),
+    fidl::FidlStructField(&nullable_vmo_handle,
+                          offsetof(multiple_nullable_handles_message_layout, inline_struct.handle_2)),
 };
 const fidl_type_t multiple_nullable_handles_message_type = fidl_type_t(fidl::FidlCodedStruct(
     multiple_nullable_handles_fields, ArrayCount(multiple_nullable_handles_fields),
@@ -138,26 +138,26 @@
     "multiple_nullable_handles_message"));
 
 // Array messages.
-static const fidl::FidlField array_of_nonnullable_handles_fields[] = {
-    fidl::FidlField(&array_of_four_nonnullable_handles,
-                    offsetof(array_of_nonnullable_handles_message_layout, inline_struct.handles)),
+static const fidl::FidlStructField array_of_nonnullable_handles_fields[] = {
+    fidl::FidlStructField(&array_of_four_nonnullable_handles,
+                          offsetof(array_of_nonnullable_handles_message_layout, inline_struct.handles)),
 };
 const fidl_type_t array_of_nonnullable_handles_message_type = fidl_type_t(fidl::FidlCodedStruct(
     array_of_nonnullable_handles_fields, ArrayCount(array_of_nonnullable_handles_fields),
     sizeof(array_of_nonnullable_handles_inline_data),
     "array_of_nonnullable_handles_message"));
 
-static const fidl::FidlField array_of_nullable_handles_fields[] = {
-    fidl::FidlField(&array_of_five_nullable_handles,
-                    offsetof(array_of_nullable_handles_message_layout, inline_struct.handles)),
+static const fidl::FidlStructField array_of_nullable_handles_fields[] = {
+    fidl::FidlStructField(&array_of_five_nullable_handles,
+                          offsetof(array_of_nullable_handles_message_layout, inline_struct.handles)),
 };
 const fidl_type_t array_of_nullable_handles_message_type = fidl_type_t(fidl::FidlCodedStruct(
     array_of_nullable_handles_fields, ArrayCount(array_of_nullable_handles_fields),
     sizeof(array_of_nullable_handles_inline_data),
     "array_of_nullable_handles_message"));
 
-static const fidl::FidlField array_of_array_of_nonnullable_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField array_of_array_of_nonnullable_handles_fields[] = {
+    fidl::FidlStructField(
         &array_of_three_arrays_of_four_nonnullable_handles,
         offsetof(array_of_array_of_nonnullable_handles_message_layout, inline_struct.handles)),
 };
@@ -167,9 +167,9 @@
                                       sizeof(array_of_array_of_nonnullable_handles_inline_data),
                                       "array_of_array_of_nonnullable_handles_message"));
 
-static const fidl::FidlField out_of_line_fields[] = {
-    fidl::FidlField(&array_of_four_nonnullable_handles,
-                    offsetof(array_of_nonnullable_handles, handles)),
+static const fidl::FidlStructField out_of_line_fields[] = {
+    fidl::FidlStructField(&array_of_four_nonnullable_handles,
+                          offsetof(array_of_nonnullable_handles, handles)),
 
 };
 static const fidl_type_t out_of_line_type = fidl_type_t(fidl::FidlCodedStruct(
@@ -178,10 +178,10 @@
 static const fidl_type_t out_of_line_pointer_type =
     fidl_type_t(fidl::FidlCodedStructPointer(&out_of_line_type.coded_struct));
 
-static const fidl::FidlField out_of_line_array_of_nonnullable_handles_fields[] = {
-    fidl::FidlField(&out_of_line_pointer_type,
-                    offsetof(out_of_line_array_of_nonnullable_handles_message_layout,
-                             inline_struct.maybe_array)),
+static const fidl::FidlStructField out_of_line_array_of_nonnullable_handles_fields[] = {
+    fidl::FidlStructField(&out_of_line_pointer_type,
+                          offsetof(out_of_line_array_of_nonnullable_handles_message_layout,
+                                   inline_struct.maybe_array)),
 };
 const fidl_type_t out_of_line_array_of_nonnullable_handles_message_type = fidl_type_t(
     fidl::FidlCodedStruct(out_of_line_array_of_nonnullable_handles_fields,
@@ -190,9 +190,9 @@
                           "out_of_line_array_of_nonnullable_handles_message"));
 
 // String messages.
-static const fidl::FidlField unbounded_nonnullable_string_fields[] = {
-    fidl::FidlField(&unbounded_nonnullable_string,
-                    offsetof(unbounded_nonnullable_string_message_layout, inline_struct.string)),
+static const fidl::FidlStructField unbounded_nonnullable_string_fields[] = {
+    fidl::FidlStructField(&unbounded_nonnullable_string,
+                          offsetof(unbounded_nonnullable_string_message_layout, inline_struct.string)),
 
 };
 const fidl_type_t unbounded_nonnullable_string_message_type = fidl_type_t(fidl::FidlCodedStruct(
@@ -200,9 +200,9 @@
     sizeof(unbounded_nonnullable_string_inline_data),
     "unbounded_nonnullable_string_message"));
 
-static const fidl::FidlField unbounded_nullable_string_fields[] = {
-    fidl::FidlField(&unbounded_nullable_string,
-                    offsetof(unbounded_nullable_string_message_layout, inline_struct.string)),
+static const fidl::FidlStructField unbounded_nullable_string_fields[] = {
+    fidl::FidlStructField(&unbounded_nullable_string,
+                          offsetof(unbounded_nullable_string_message_layout, inline_struct.string)),
 
 };
 const fidl_type_t unbounded_nullable_string_message_type = fidl_type_t(fidl::FidlCodedStruct(
@@ -210,9 +210,9 @@
     sizeof(unbounded_nullable_string_inline_data),
     "unbounded_nullable_string_message"));
 
-static const fidl::FidlField bounded_32_nonnullable_string_fields[] = {
-    fidl::FidlField(&bounded_32_nonnullable_string,
-                    offsetof(bounded_32_nonnullable_string_message_layout, inline_struct.string)),
+static const fidl::FidlStructField bounded_32_nonnullable_string_fields[] = {
+    fidl::FidlStructField(&bounded_32_nonnullable_string,
+                          offsetof(bounded_32_nonnullable_string_message_layout, inline_struct.string)),
 
 };
 const fidl_type_t bounded_32_nonnullable_string_message_type = fidl_type_t(fidl::FidlCodedStruct(
@@ -220,45 +220,45 @@
     sizeof(bounded_32_nonnullable_string_inline_data),
     "bounded_32_nonnullable_string_message"));
 
-static const fidl::FidlField bounded_32_nullable_string_fields[] = {
-    fidl::FidlField(&bounded_32_nullable_string,
-                    offsetof(bounded_32_nullable_string_message_layout, inline_struct.string)),
+static const fidl::FidlStructField bounded_32_nullable_string_fields[] = {
+    fidl::FidlStructField(&bounded_32_nullable_string,
+                          offsetof(bounded_32_nullable_string_message_layout, inline_struct.string)),
 };
 const fidl_type_t bounded_32_nullable_string_message_type = fidl_type_t(fidl::FidlCodedStruct(
     bounded_32_nullable_string_fields, ArrayCount(bounded_32_nullable_string_fields),
     sizeof(bounded_32_nullable_string_inline_data),
     "bounded_32_nullable_string_message"));
 
-static const fidl::FidlField multiple_nonnullable_strings_fields[] = {
-    fidl::FidlField(&bounded_32_nonnullable_string,
-                    offsetof(multiple_nonnullable_strings_message_layout, inline_struct.string)),
+static const fidl::FidlStructField multiple_nonnullable_strings_fields[] = {
+    fidl::FidlStructField(&bounded_32_nonnullable_string,
+                          offsetof(multiple_nonnullable_strings_message_layout, inline_struct.string)),
 
-    fidl::FidlField(&bounded_32_nonnullable_string,
-                    offsetof(multiple_nonnullable_strings_message_layout, inline_struct.string2)),
+    fidl::FidlStructField(&bounded_32_nonnullable_string,
+                          offsetof(multiple_nonnullable_strings_message_layout, inline_struct.string2)),
 };
 const fidl_type_t multiple_nonnullable_strings_message_type = fidl_type_t(fidl::FidlCodedStruct(
     multiple_nonnullable_strings_fields, ArrayCount(multiple_nonnullable_strings_fields),
     sizeof(multiple_nonnullable_strings_inline_data),
     "multiple_nonnullable_strings_message"));
 
-static const fidl::FidlField multiple_nullable_strings_fields[] = {
-    fidl::FidlField(&bounded_32_nullable_string,
-                    offsetof(multiple_nullable_strings_message_layout, inline_struct.string)),
+static const fidl::FidlStructField multiple_nullable_strings_fields[] = {
+    fidl::FidlStructField(&bounded_32_nullable_string,
+                          offsetof(multiple_nullable_strings_message_layout, inline_struct.string)),
 
-    fidl::FidlField(&bounded_32_nullable_string,
-                    offsetof(multiple_nullable_strings_message_layout, inline_struct.string2)),
+    fidl::FidlStructField(&bounded_32_nullable_string,
+                          offsetof(multiple_nullable_strings_message_layout, inline_struct.string2)),
 };
 const fidl_type_t multiple_nullable_strings_message_type = fidl_type_t(fidl::FidlCodedStruct(
     multiple_nullable_strings_fields, ArrayCount(multiple_nullable_strings_fields),
     sizeof(multiple_nullable_strings_inline_data),
     "multiple_nullable_strings_message"));
 
-static const fidl::FidlField multiple_short_nonnullable_strings_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField multiple_short_nonnullable_strings_fields[] = {
+    fidl::FidlStructField(
         &bounded_4_nonnullable_string,
         offsetof(multiple_short_nonnullable_strings_message_layout, inline_struct.string)),
 
-    fidl::FidlField(
+    fidl::FidlStructField(
         &bounded_32_nonnullable_string,
         offsetof(multiple_short_nonnullable_strings_message_layout, inline_struct.string2)),
 };
@@ -268,11 +268,11 @@
                                       sizeof(multiple_short_nonnullable_strings_inline_data),
                                       "multiple_short_nonnullable_strings_message"));
 
-static const fidl::FidlField multiple_short_nullable_strings_fields[] = {
-    fidl::FidlField(&bounded_4_nullable_string,
-                    offsetof(multiple_short_nullable_strings_message_layout, inline_struct.string)),
+static const fidl::FidlStructField multiple_short_nullable_strings_fields[] = {
+    fidl::FidlStructField(&bounded_4_nullable_string,
+                          offsetof(multiple_short_nullable_strings_message_layout, inline_struct.string)),
 
-    fidl::FidlField(
+    fidl::FidlStructField(
         &bounded_32_nullable_string,
         offsetof(multiple_short_nullable_strings_message_layout, inline_struct.string2)),
 };
@@ -282,8 +282,8 @@
     "multiple_short_nullable_strings_message"));
 
 // Vector messages.
-static const fidl::FidlField unbounded_nonnullable_vector_of_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField unbounded_nonnullable_vector_of_handles_fields[] = {
+    fidl::FidlStructField(
         &unbounded_nonnullable_vector_of_handles,
         offsetof(unbounded_nonnullable_vector_of_handles_message_layout, inline_struct.vector)),
 
@@ -294,8 +294,8 @@
                                       sizeof(unbounded_nonnullable_vector_of_handles_inline_data),
                                       "unbounded_nonnullable_vector_of_handles_message"));
 
-static const fidl::FidlField unbounded_nullable_vector_of_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField unbounded_nullable_vector_of_handles_fields[] = {
+    fidl::FidlStructField(
         &unbounded_nullable_vector_of_handles,
         offsetof(unbounded_nullable_vector_of_handles_message_layout, inline_struct.vector)),
 
@@ -306,8 +306,8 @@
                                       sizeof(unbounded_nullable_vector_of_handles_inline_data),
                                       "unbounded_nullable_vector_of_handles_message"));
 
-static const fidl::FidlField bounded_32_nonnullable_vector_of_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField bounded_32_nonnullable_vector_of_handles_fields[] = {
+    fidl::FidlStructField(
         &bounded_32_nonnullable_vector_of_handles,
         offsetof(bounded_32_nonnullable_vector_of_handles_message_layout, inline_struct.vector)),
 
@@ -318,8 +318,8 @@
                           sizeof(bounded_32_nonnullable_vector_of_handles_inline_data),
                           "bounded_32_nonnullable_vector_of_handles_message"));
 
-static const fidl::FidlField bounded_32_nullable_vector_of_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField bounded_32_nullable_vector_of_handles_fields[] = {
+    fidl::FidlStructField(
         &bounded_32_nullable_vector_of_handles,
         offsetof(bounded_32_nullable_vector_of_handles_message_layout, inline_struct.vector)),
 
@@ -330,11 +330,11 @@
                                       sizeof(bounded_32_nullable_vector_of_handles_inline_data),
                                       "bounded_32_nullable_vector_of_handles_message"));
 
-static const fidl::FidlField multiple_nonnullable_vectors_of_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField multiple_nonnullable_vectors_of_handles_fields[] = {
+    fidl::FidlStructField(
         &bounded_2_nonnullable_vector_of_handles,
         offsetof(multiple_nonnullable_vectors_of_handles_message_layout, inline_struct.vector)),
-    fidl::FidlField(
+    fidl::FidlStructField(
         &unbounded_nonnullable_vector_of_handles,
         offsetof(multiple_nonnullable_vectors_of_handles_message_layout, inline_struct.vector2)),
 };
@@ -344,11 +344,11 @@
                                       sizeof(multiple_nonnullable_vectors_of_handles_inline_data),
                                       "multiple_nonnullable_vectors_of_handles_message"));
 
-static const fidl::FidlField multiple_nullable_vectors_of_handles_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField multiple_nullable_vectors_of_handles_fields[] = {
+    fidl::FidlStructField(
         &bounded_2_nullable_vector_of_handles,
         offsetof(multiple_nullable_vectors_of_handles_message_layout, inline_struct.vector)),
-    fidl::FidlField(
+    fidl::FidlStructField(
         &unbounded_nullable_vector_of_handles,
         offsetof(multiple_nullable_vectors_of_handles_message_layout, inline_struct.vector2)),
 };
@@ -358,8 +358,8 @@
                                       sizeof(multiple_nullable_vectors_of_handles_inline_data),
                                       "multiple_nullable_vectors_of_handles_message"));
 
-static const fidl::FidlField unbounded_nonnullable_vector_of_uint32_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField unbounded_nonnullable_vector_of_uint32_fields[] = {
+    fidl::FidlStructField(
         &unbounded_nonnullable_vector_of_uint32,
         offsetof(unbounded_nonnullable_vector_of_uint32_message_layout, inline_struct.vector)),
 
@@ -370,8 +370,8 @@
                                       sizeof(unbounded_nonnullable_vector_of_uint32_inline_data),
                                       "unbounded_nonnullable_vector_of_uint32_message"));
 
-static const fidl::FidlField unbounded_nullable_vector_of_uint32_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField unbounded_nullable_vector_of_uint32_fields[] = {
+    fidl::FidlStructField(
         &unbounded_nullable_vector_of_uint32,
         offsetof(unbounded_nullable_vector_of_uint32_message_layout, inline_struct.vector)),
 
@@ -382,8 +382,8 @@
                                       sizeof(unbounded_nullable_vector_of_uint32_inline_data),
                                       "unbounded_nullable_vector_of_uint32_message"));
 
-static const fidl::FidlField bounded_32_nonnullable_vector_of_uint32_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField bounded_32_nonnullable_vector_of_uint32_fields[] = {
+    fidl::FidlStructField(
         &bounded_32_nonnullable_vector_of_uint32,
         offsetof(bounded_32_nonnullable_vector_of_uint32_message_layout, inline_struct.vector)),
 
@@ -394,8 +394,8 @@
                           sizeof(bounded_32_nonnullable_vector_of_uint32_inline_data),
                           "bounded_32_nonnullable_vector_of_uint32_message"));
 
-static const fidl::FidlField bounded_32_nullable_vector_of_uint32_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField bounded_32_nullable_vector_of_uint32_fields[] = {
+    fidl::FidlStructField(
         &bounded_32_nullable_vector_of_uint32,
         offsetof(bounded_32_nullable_vector_of_uint32_message_layout, inline_struct.vector)),
 
@@ -406,11 +406,11 @@
                                       sizeof(bounded_32_nullable_vector_of_uint32_inline_data),
                                       "bounded_32_nullable_vector_of_uint32_message"));
 
-static const fidl::FidlField multiple_nonnullable_vectors_of_uint32_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField multiple_nonnullable_vectors_of_uint32_fields[] = {
+    fidl::FidlStructField(
         &bounded_2_nonnullable_vector_of_uint32,
         offsetof(multiple_nonnullable_vectors_of_uint32_message_layout, inline_struct.vector)),
-    fidl::FidlField(
+    fidl::FidlStructField(
         &unbounded_nonnullable_vector_of_uint32,
         offsetof(multiple_nonnullable_vectors_of_uint32_message_layout, inline_struct.vector2)),
 };
@@ -420,11 +420,11 @@
                                       sizeof(multiple_nonnullable_vectors_of_uint32_inline_data),
                                       "multiple_nonnullable_vectors_of_uint32_message"));
 
-static const fidl::FidlField multiple_nullable_vectors_of_uint32_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField multiple_nullable_vectors_of_uint32_fields[] = {
+    fidl::FidlStructField(
         &bounded_2_nullable_vector_of_uint32,
         offsetof(multiple_nullable_vectors_of_uint32_message_layout, inline_struct.vector)),
-    fidl::FidlField(
+    fidl::FidlStructField(
         &unbounded_nullable_vector_of_uint32,
         offsetof(multiple_nullable_vectors_of_uint32_message_layout, inline_struct.vector2)),
 };
@@ -444,9 +444,9 @@
     offsetof(nonnullable_handle_union, handle),
     sizeof(nonnullable_handle_union),
     "nonnullable_handle_union"));
-static const fidl::FidlField nonnullable_handle_union_fields[] = {
-    fidl::FidlField(&nonnullable_handle_union_type,
-                    offsetof(nonnullable_handle_union_message_layout, inline_struct.data)),
+static const fidl::FidlStructField nonnullable_handle_union_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle_union_type,
+                          offsetof(nonnullable_handle_union_message_layout, inline_struct.data)),
 };
 const fidl_type_t nonnullable_handle_union_message_type = fidl_type_t(fidl::FidlCodedStruct(
     nonnullable_handle_union_fields, ArrayCount(nonnullable_handle_union_fields),
@@ -464,8 +464,8 @@
                          offsetof(array_of_nonnullable_handles_union, handle),
                          sizeof(array_of_nonnullable_handles_union),
                          "array_of_nonnullable_handles_union"));
-static const fidl::FidlField array_of_nonnullable_handles_union_fields[] = {
-    fidl::FidlField(
+static const fidl::FidlStructField array_of_nonnullable_handles_union_fields[] = {
+    fidl::FidlStructField(
         &array_of_nonnullable_handles_union_type,
         offsetof(array_of_nonnullable_handles_union_message_layout, inline_struct.data)),
 
@@ -479,9 +479,9 @@
 // Union pointer messages.
 const fidl_type_t nonnullable_handle_union_ptr =
     fidl_type_t(fidl::FidlCodedUnionPointer(&nonnullable_handle_union_type.coded_union));
-static const fidl::FidlField nonnullable_handle_union_ptr_fields[] = {
-    fidl::FidlField(&nonnullable_handle_union_ptr,
-                    offsetof(nonnullable_handle_union_ptr_inline_data, data)),
+static const fidl::FidlStructField nonnullable_handle_union_ptr_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle_union_ptr,
+                          offsetof(nonnullable_handle_union_ptr_inline_data, data)),
 };
 const fidl_type_t nonnullable_handle_union_ptr_message_type = fidl_type_t(fidl::FidlCodedStruct(
     nonnullable_handle_union_ptr_fields, ArrayCount(nonnullable_handle_union_ptr_fields),
@@ -490,9 +490,9 @@
 
 static const fidl_type_t array_of_nonnullable_handles_union_ptr =
     fidl_type_t(fidl::FidlCodedUnionPointer(&array_of_nonnullable_handles_union_type.coded_union));
-static const fidl::FidlField array_of_nonnullable_handles_union_ptr_fields[] = {
-    fidl::FidlField(&array_of_nonnullable_handles_union_ptr,
-                    offsetof(array_of_nonnullable_handles_union_ptr_inline_data, data)),
+static const fidl::FidlStructField array_of_nonnullable_handles_union_ptr_fields[] = {
+    fidl::FidlStructField(&array_of_nonnullable_handles_union_ptr,
+                          offsetof(array_of_nonnullable_handles_union_ptr_inline_data, data)),
 };
 const fidl_type_t array_of_nonnullable_handles_union_ptr_message_type =
     fidl_type_t(fidl::FidlCodedStruct(array_of_nonnullable_handles_union_ptr_fields,
@@ -501,36 +501,36 @@
                                       "array_of_nonnullable_handles_union_ptr_message"));
 
 // Struct messages.
-static const fidl::FidlField struct_level_3_fields[] = {
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_level_3, handle_3)),
+static const fidl::FidlStructField struct_level_3_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_level_3, handle_3)),
 };
 static const fidl_type_t struct_level_3_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_level_3_fields, ArrayCount(struct_level_3_fields), sizeof(struct_level_3),
     "struct_level_3"));
-static const fidl::FidlField struct_level_2_fields[] = {
-    fidl::FidlField(&struct_level_3_struct, offsetof(struct_level_2, l3)),
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_level_2, handle_2)),
+static const fidl::FidlStructField struct_level_2_fields[] = {
+    fidl::FidlStructField(&struct_level_3_struct, offsetof(struct_level_2, l3)),
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_level_2, handle_2)),
 };
 static const fidl_type_t struct_level_2_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_level_2_fields, ArrayCount(struct_level_2_fields), sizeof(struct_level_2),
     "struct_level_2"));
-static const fidl::FidlField struct_level_1_fields[] = {
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_level_1, handle_1)),
-    fidl::FidlField(&struct_level_2_struct, offsetof(struct_level_1, l2)),
+static const fidl::FidlStructField struct_level_1_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_level_1, handle_1)),
+    fidl::FidlStructField(&struct_level_2_struct, offsetof(struct_level_1, l2)),
 };
 static const fidl_type_t struct_level_1_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_level_1_fields, ArrayCount(struct_level_1_fields), sizeof(struct_level_1),
     "struct_level_1"));
-static const fidl::FidlField struct_level_0_fields[] = {
-    fidl::FidlField(&struct_level_1_struct, offsetof(struct_level_0, l1)),
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_level_0, handle_0)),
+static const fidl::FidlStructField struct_level_0_fields[] = {
+    fidl::FidlStructField(&struct_level_1_struct, offsetof(struct_level_0, l1)),
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_level_0, handle_0)),
 };
 const fidl_type_t struct_level_0_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_level_0_fields, ArrayCount(struct_level_1_fields), sizeof(struct_level_0),
     "struct_level_0"));
-static const fidl::FidlField nested_structs_fields[] = {
-    fidl::FidlField(&struct_level_0_struct,
-                    offsetof(nested_structs_message_layout, inline_struct.l0)),
+static const fidl::FidlStructField nested_structs_fields[] = {
+    fidl::FidlStructField(&struct_level_0_struct,
+                          offsetof(nested_structs_message_layout, inline_struct.l0)),
 
 };
 const fidl_type_t nested_structs_message_type = fidl_type_t(fidl::FidlCodedStruct(
@@ -538,8 +538,8 @@
     "nested_structs_message"));
 
 // Struct pointer messages.
-static const fidl::FidlField struct_ptr_level_3_fields[] = {
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_ptr_level_3, handle_3)),
+static const fidl::FidlStructField struct_ptr_level_3_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_ptr_level_3, handle_3)),
 
 };
 static const fidl_type_t struct_ptr_level_3_struct = fidl_type_t(fidl::FidlCodedStruct(
@@ -547,46 +547,46 @@
     "struct_ptr_level_3"));
 static const fidl_type_t struct_ptr_level_3_struct_pointer =
     fidl_type_t(fidl::FidlCodedStructPointer(&struct_ptr_level_3_struct.coded_struct));
-static const fidl::FidlField struct_ptr_level_2_fields[] = {
-    fidl::FidlField(&struct_ptr_level_3_struct_pointer, offsetof(struct_ptr_level_2, l3_present)),
-    fidl::FidlField(&struct_ptr_level_3_struct_pointer, offsetof(struct_ptr_level_2, l3_absent)),
-    fidl::FidlField(&struct_ptr_level_3_struct, offsetof(struct_ptr_level_2, l3_inline)),
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_ptr_level_2, handle_2)),
+static const fidl::FidlStructField struct_ptr_level_2_fields[] = {
+    fidl::FidlStructField(&struct_ptr_level_3_struct_pointer, offsetof(struct_ptr_level_2, l3_present)),
+    fidl::FidlStructField(&struct_ptr_level_3_struct_pointer, offsetof(struct_ptr_level_2, l3_absent)),
+    fidl::FidlStructField(&struct_ptr_level_3_struct, offsetof(struct_ptr_level_2, l3_inline)),
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_ptr_level_2, handle_2)),
 };
 static const fidl_type_t struct_ptr_level_2_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_ptr_level_2_fields, ArrayCount(struct_ptr_level_2_fields), sizeof(struct_ptr_level_2),
     "struct_ptr_level_2"));
 static const fidl_type_t struct_ptr_level_2_struct_pointer =
     fidl_type_t(fidl::FidlCodedStructPointer(&struct_ptr_level_2_struct.coded_struct));
-static const fidl::FidlField struct_ptr_level_1_fields[] = {
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_ptr_level_1, handle_1)),
-    fidl::FidlField(&struct_ptr_level_2_struct_pointer, offsetof(struct_ptr_level_1, l2_present)),
-    fidl::FidlField(&struct_ptr_level_2_struct, offsetof(struct_ptr_level_1, l2_inline)),
-    fidl::FidlField(&struct_ptr_level_2_struct_pointer, offsetof(struct_ptr_level_1, l2_absent)),
+static const fidl::FidlStructField struct_ptr_level_1_fields[] = {
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_ptr_level_1, handle_1)),
+    fidl::FidlStructField(&struct_ptr_level_2_struct_pointer, offsetof(struct_ptr_level_1, l2_present)),
+    fidl::FidlStructField(&struct_ptr_level_2_struct, offsetof(struct_ptr_level_1, l2_inline)),
+    fidl::FidlStructField(&struct_ptr_level_2_struct_pointer, offsetof(struct_ptr_level_1, l2_absent)),
 };
 static const fidl_type_t struct_ptr_level_1_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_ptr_level_1_fields, ArrayCount(struct_ptr_level_1_fields), sizeof(struct_ptr_level_1),
     "struct_ptr_level_1"));
 static const fidl_type_t struct_ptr_level_1_struct_pointer =
     fidl_type_t(fidl::FidlCodedStructPointer(&struct_ptr_level_1_struct.coded_struct));
-static const fidl::FidlField struct_ptr_level_0_fields[] = {
-    fidl::FidlField(&struct_ptr_level_1_struct_pointer, offsetof(struct_ptr_level_0, l1_absent)),
-    fidl::FidlField(&struct_ptr_level_1_struct, offsetof(struct_ptr_level_0, l1_inline)),
-    fidl::FidlField(&nonnullable_handle, offsetof(struct_ptr_level_0, handle_0)),
-    fidl::FidlField(&struct_ptr_level_1_struct_pointer, offsetof(struct_ptr_level_0, l1_present)),
+static const fidl::FidlStructField struct_ptr_level_0_fields[] = {
+    fidl::FidlStructField(&struct_ptr_level_1_struct_pointer, offsetof(struct_ptr_level_0, l1_absent)),
+    fidl::FidlStructField(&struct_ptr_level_1_struct, offsetof(struct_ptr_level_0, l1_inline)),
+    fidl::FidlStructField(&nonnullable_handle, offsetof(struct_ptr_level_0, handle_0)),
+    fidl::FidlStructField(&struct_ptr_level_1_struct_pointer, offsetof(struct_ptr_level_0, l1_present)),
 };
 static const fidl_type_t struct_ptr_level_0_struct = fidl_type_t(fidl::FidlCodedStruct(
     struct_ptr_level_0_fields, ArrayCount(struct_ptr_level_0_fields), sizeof(struct_ptr_level_0),
     "struct_ptr_level_0"));
 const fidl_type_t struct_ptr_level_0_struct_pointer =
     fidl_type_t(fidl::FidlCodedStructPointer(&struct_ptr_level_0_struct.coded_struct));
-static const fidl::FidlField nested_struct_ptrs_fields[] = {
-    fidl::FidlField(&struct_ptr_level_0_struct,
-                    offsetof(nested_struct_ptrs_inline_data, l0_inline)),
-    fidl::FidlField(&struct_ptr_level_0_struct_pointer,
-                    offsetof(nested_struct_ptrs_inline_data, l0_absent)),
-    fidl::FidlField(&struct_ptr_level_0_struct_pointer,
-                    offsetof(nested_struct_ptrs_inline_data, l0_present)),
+static const fidl::FidlStructField nested_struct_ptrs_fields[] = {
+    fidl::FidlStructField(&struct_ptr_level_0_struct,
+                          offsetof(nested_struct_ptrs_inline_data, l0_inline)),
+    fidl::FidlStructField(&struct_ptr_level_0_struct_pointer,
+                          offsetof(nested_struct_ptrs_inline_data, l0_absent)),
+    fidl::FidlStructField(&struct_ptr_level_0_struct_pointer,
+                          offsetof(nested_struct_ptrs_inline_data, l0_present)),
 };
 const fidl_type_t nested_struct_ptrs_message_type = fidl_type_t(
     fidl::FidlCodedStruct(nested_struct_ptrs_fields, ArrayCount(nested_struct_ptrs_fields),
@@ -603,8 +603,8 @@
 const fidl_type_t maybe_recurse_type = fidl_type_t(fidl::FidlCodedUnion(
     maybe_recurse_union_members, ArrayCount(maybe_recurse_union_members),
     offsetof(maybe_recurse, handle), sizeof(maybe_recurse), "maybe_recurse"));
-static const fidl::FidlField recursion_fields[] = {
-    fidl::FidlField(&maybe_recurse_type, offsetof(recursion_inline_data, inline_union)),
+static const fidl::FidlStructField recursion_fields[] = {
+    fidl::FidlStructField(&maybe_recurse_type, offsetof(recursion_inline_data, inline_union)),
 };
 const fidl_type_t recursion_message_type = fidl_type_t(fidl::FidlCodedStruct(
     recursion_fields, ArrayCount(recursion_fields), sizeof(recursion_inline_data),
diff --git a/system/utest/fidl/handle_closing_tests.cpp b/system/utest/fidl/handle_closing_tests.cpp
index 57e73c2..46d39a3 100644
--- a/system/utest/fidl/handle_closing_tests.cpp
+++ b/system/utest/fidl/handle_closing_tests.cpp
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include <limits.h>
-#include <stddef.h>
 #include <new>
+#include <stddef.h>
 
 #include <lib/fidl/coding.h>
 #include <lib/fidl/internal.h>
@@ -269,8 +269,8 @@
 const fidl_type_t unbounded_too_large_nullable_vector_of_handles =
     fidl_type_t(fidl::FidlCodedVector(&nullable_handle, FIDL_MAX_SIZE, sizeof(zx_handle_t),
                                       fidl::kNullable));
-static const ::fidl::FidlField unbounded_too_large_nullable_vector_of_handles_fields[] = {
-    ::fidl::FidlField(
+static const ::fidl::FidlStructField unbounded_too_large_nullable_vector_of_handles_fields[] = {
+    ::fidl::FidlStructField(
         &unbounded_too_large_nullable_vector_of_handles,
         offsetof(unbounded_too_large_nullable_vector_of_handles_message_layout,
                  inline_struct.vector)),
diff --git a/system/utest/fidl/llcpp_types_tests.cpp b/system/utest/fidl/llcpp_types_tests.cpp
index 9b60db9..76c3d6e 100644
--- a/system/utest/fidl/llcpp_types_tests.cpp
+++ b/system/utest/fidl/llcpp_types_tests.cpp
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <stdalign.h>
 #include <utility>
-#include <memory>
 
 #include <lib/fidl/internal.h>
 #include <lib/fidl/llcpp/array_wrapper.h>
@@ -22,7 +22,7 @@
 
 struct NonnullableChannelMessage {
     alignas(FIDL_ALIGNMENT)
-    fidl_message_header_t header;
+        fidl_message_header_t header;
     zx::channel channel;
 
     static constexpr uint32_t MaxNumHandles = 1;
@@ -38,28 +38,28 @@
 
 const fidl_type_t NonnullableChannelType =
     fidl_type_t(fidl::FidlCodedHandle(ZX_OBJ_TYPE_CHANNEL, fidl::kNonnullable));
-const fidl::FidlField NonnullableChannelMessageFields[] = {
-    fidl::FidlField(&NonnullableChannelType,
-                    offsetof(NonnullableChannelMessage, channel)),
+const fidl::FidlStructField NonnullableChannelMessageFields[] = {
+    fidl::FidlStructField(&NonnullableChannelType,
+                          offsetof(NonnullableChannelMessage, channel)),
 };
 const fidl_type_t NonnullableChannelMessageType = fidl_type_t(fidl::FidlCodedStruct(
     NonnullableChannelMessageFields, /* field_count */ 1,
     sizeof(NonnullableChannelMessage),
     "NonnullableChannelMessage"));
-}
+} // namespace
 
 namespace fidl {
 
 // Manually specialize the templates.
 // These will match the llcpp codegen output.
 
-template<>
+template <>
 struct IsFidlType<NonnullableChannelMessage> : public std::true_type {};
 
-template<>
+template <>
 struct IsFidlMessage<NonnullableChannelMessage> : public std::true_type {};
 
-}
+} // namespace fidl
 
 namespace {
 
@@ -67,7 +67,7 @@
 // Zircon system call instead of calling a destructor, we indirectly test for handle closure
 // via the ZX_ERR_PEER_CLOSED error message.
 
-bool HelperExpectPeerValid(zx::channel &channel) {
+bool HelperExpectPeerValid(zx::channel& channel) {
     BEGIN_HELPER;
 
     const char* foo = "A";
@@ -76,7 +76,7 @@
     END_HELPER;
 }
 
-bool HelperExpectPeerInvalid(zx::channel &channel) {
+bool HelperExpectPeerInvalid(zx::channel& channel) {
     BEGIN_HELPER;
 
     const char* foo = "A";
@@ -98,8 +98,8 @@
 
     {
         fidl::EncodedMessage<NonnullableChannelMessage> encoded_message;
-        encoded_message.Initialize([&buf, &channel_1] (fidl::BytePart& msg_bytes,
-                                                       fidl::HandlePart& msg_handles) {
+        encoded_message.Initialize([&buf, &channel_1](fidl::BytePart& msg_bytes,
+                                                      fidl::HandlePart& msg_handles) {
             msg_bytes = fidl::BytePart(buf, sizeof(buf), sizeof(buf));
             zx_handle_t* handle = msg_handles.data();
 
@@ -169,10 +169,9 @@
         new fidl::EncodedMessage<NonnullableChannelMessage>();
     zx_handle_t unsafe_handle_backup;
 
-    encoded_message->Initialize([&buf, &channel_1, &unsafe_handle_backup] (
-        fidl::BytePart& msg_bytes,
-        fidl::HandlePart& msg_handles
-    ) {
+    encoded_message->Initialize([&buf, &channel_1, &unsafe_handle_backup](
+                                    fidl::BytePart& msg_bytes,
+                                    fidl::HandlePart& msg_handles) {
         msg_bytes = fidl::BytePart(buf, sizeof(buf), sizeof(buf));
         zx_handle_t* handle = msg_handles.data();
 
@@ -189,13 +188,12 @@
     });
 
     uint8_t golden_encoded[] = {
-        10, 0, 0, 0, // txid
-        0, 0, 0, 0, // reserved
-        0, 0, 0, 0, // flags
-        42, 0, 0, 0, // ordinal
+        10, 0, 0, 0,        // txid
+        0, 0, 0, 0,         // reserved
+        0, 0, 0, 0,         // flags
+        42, 0, 0, 0,        // ordinal
         255, 255, 255, 255, // handle present
-        0, 0, 0, 0
-    };
+        0, 0, 0, 0};
 
     // Byte-accurate comparison
     EXPECT_EQ(memcmp(golden_encoded, buf, sizeof(buf)), 0);
@@ -254,11 +252,11 @@
     END_TEST;
 }
 
-}  // namespace
+} // namespace
 
 BEGIN_TEST_CASE(llcpp_types_tests)
-    RUN_NAMED_TEST("EncodedMessage test", EncodedMessageTest)
-    RUN_NAMED_TEST("DecodedMessage test", DecodedMessageTest)
-    RUN_NAMED_TEST("Round trip test", RoundTripTest)
-    RUN_NAMED_TEST("Array layout test", ArrayLayoutTest)
+RUN_NAMED_TEST("EncodedMessage test", EncodedMessageTest)
+RUN_NAMED_TEST("DecodedMessage test", DecodedMessageTest)
+RUN_NAMED_TEST("Round trip test", RoundTripTest)
+RUN_NAMED_TEST("Array layout test", ArrayLayoutTest)
 END_TEST_CASE(llcpp_types_tests);