| // 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. |
| |
| // See https://fuchsia.dev/fuchsia-src/development/languages/fidl/reference/compiler#compilation |
| // for documentation |
| |
| #ifndef TOOLS_FIDL_FIDLC_INCLUDE_FIDL_FLAT_AST_H_ |
| #define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_FLAT_AST_H_ |
| |
| #include <lib/fit/function.h> |
| |
| #include <any> |
| #include <cassert> |
| #include <cstdint> |
| #include <functional> |
| #include <iostream> |
| #include <limits> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <string_view> |
| #include <type_traits> |
| #include <variant> |
| #include <vector> |
| |
| #include <safemath/checked_math.h> |
| |
| #include "attributes.h" |
| #include "experimental_flags.h" |
| #include "flat/name.h" |
| #include "flat/object.h" |
| #include "flat/types.h" |
| #include "flat/values.h" |
| #include "raw_ast.h" |
| #include "reporter.h" |
| #include "type_shape.h" |
| #include "types.h" |
| #include "virtual_source_file.h" |
| |
| namespace fidl { |
| namespace flat { |
| |
| constexpr uint32_t kHandleSameRights = 0x80000000; // ZX_HANDLE_SAME_RIGHTS |
| |
| using diagnostics::Diagnostic; |
| using diagnostics::ErrorDef; |
| using reporter::Reporter; |
| |
| template <typename T> |
| struct PtrCompare { |
| bool operator()(const T* left, const T* right) const { return *left < *right; } |
| }; |
| |
| class Typespace; |
| struct Decl; |
| class Library; |
| |
| bool HasSimpleLayout(const Decl* decl); |
| |
| // This is needed (for now) to work around declaration order issues. |
| std::string LibraryName(const Library* library, std::string_view separator); |
| |
| struct Decl { |
| virtual ~Decl() {} |
| |
| enum struct Kind { |
| kBits, |
| kConst, |
| kEnum, |
| kProtocol, |
| kResource, |
| kService, |
| kStruct, |
| kTable, |
| kUnion, |
| kTypeAlias, |
| }; |
| |
| Decl(Kind kind, std::unique_ptr<raw::AttributeList> attributes, Name name) |
| : kind(kind), attributes(std::move(attributes)), name(std::move(name)) {} |
| |
| const Kind kind; |
| |
| std::unique_ptr<raw::AttributeList> attributes; |
| const Name name; |
| |
| bool HasAttribute(std::string_view name) const; |
| std::string_view GetAttribute(std::string_view name) const; |
| std::string GetName() const; |
| |
| bool compiling = false; |
| bool compiled = false; |
| }; |
| |
| struct TypeDecl : public Decl, public Object { |
| TypeDecl(Kind kind, std::unique_ptr<raw::AttributeList> attributes, Name name) |
| : Decl(kind, std::move(attributes), std::move(name)) {} |
| |
| bool recursive = false; |
| }; |
| |
| // TODO(fxbug.dev/70247): Remove the two type constructor versions and remove |
| // the New suffix. |
| struct TypeConstructorOld; |
| struct TypeConstructorNew; |
| using TypeConstructor = |
| std::variant<std::unique_ptr<TypeConstructorNew>, std::unique_ptr<TypeConstructorOld>>; |
| |
| // Functions that recursively traverse a type constructor can't operate on |
| // const std::unique_ptr<T>& because the inner type constructors accessed through |
| // LayoutInvocation::element_type_raw are pointers. To get around this, the top |
| // level call must extract the pointers out, so that the function can be defined |
| // for and called recursively over pointers. |
| using TypeConstructorPtr = std::variant<const TypeConstructorNew*, const TypeConstructorOld*>; |
| |
| TypeConstructorPtr GetTypeCtorAsPtr(const TypeConstructor& type_ctor); |
| |
| template <typename T> |
| const Type* GetType(const T& type_ctor) { |
| return std::visit([](const auto& ptr) { return ptr->type; }, type_ctor); |
| } |
| |
| template <typename T> |
| bool IsTypeConstructorDefined(const T& type_ctor) { |
| return std::visit([](const auto& ptr) { return ptr != nullptr; }, type_ctor); |
| } |
| |
| template <typename T> |
| Name GetName(const T& type_ctor) { |
| return std::visit([](const auto& ptr) { return ptr->name; }, type_ctor); |
| } |
| |
| template <typename T> |
| const LayoutInvocation& GetLayoutInvocation(const T& type_ctor) { |
| return std::visit([](const auto& ptr) -> const LayoutInvocation& { return ptr->resolved_params; }, |
| type_ctor); |
| } |
| |
| struct TypeAlias; |
| |
| // This is a struct used to group together all data produced during compilation |
| // that might be used by consumers that are downstream from type compilation |
| // (e.g. typeshape code, declaration sorting, JSON generator), that can't be |
| // obtained by looking at a type constructor's Type. |
| // Unlike TypeConstructor::Type which will always refer to the fully resolved/ |
| // concrete (and eventually, canonicalized) type that the type constructor |
| // resolves to, this struct stores data about the actual parameters on this |
| // type constructor used to produce the type. |
| // This struct overlaps with CreateInvocation because they both store resolved |
| // parameters/constraints. This overlap will no longer exist after the old syntax |
| // and CreateInvocation are cleaned up - LayoutInvocation is in some sense the |
| // successor to CreateInvocation. |
| // These fields should be set in the same place where the parameters actually get |
| // resolved - for the new syntax, this is in Create (for layout parameters) and |
| // in ApplyConstraints (for type constraints) but in the old syntax they are all |
| // set in ResolveOldSyntaxArgs. |
| struct LayoutInvocation { |
| // set if this type constructor refers to a type alias |
| const TypeAlias* from_type_alias = nullptr; |
| |
| // Parameter data below: if a foo_resolved form is set, then its corresponding |
| // foo_raw form must be defined as well (and vice versa). |
| |
| // resolved form of this type constructor's arguments |
| const Type* element_type_resolved = nullptr; |
| const Size* size_resolved = nullptr; |
| // This has no users, probably because it's missing in the JSON IR (it is not |
| // yet generated for experimental_maybe_from_type_alias) |
| std::optional<uint32_t> subtype_resolved = std::nullopt; |
| // This has no users, probably because it's missing in the JSON IR (it is not |
| // yet generated for experimental_maybe_from_type_alias). |
| const HandleRights* rights_resolved = nullptr; |
| |
| // raw form of this type constructor's arguments |
| TypeConstructorPtr element_type_raw = {}; |
| const Constant* size_raw = nullptr; |
| // This has no users, probably because it's missing in the JSON IR (it is not |
| // yet generated for partial_type_ctors). Notably, this is probably because this |
| // is parsed as a Name in the old syntax but the consistent thing to do would |
| // be to emit it as a Constant, which made it awkward to implement (and thus |
| // was never done). This field is never set in the old syntax. |
| const Constant* subtype_raw = nullptr; |
| const Constant* rights_raw = nullptr; |
| |
| // Nullability is represented differently because there's only one degree of |
| // freedom: if it was specified, this value is equal to kNullable |
| types::Nullability nullability = types::Nullability::kNonnullable; |
| }; |
| |
| struct TypeConstructorOld final { |
| TypeConstructorOld(Name name, std::unique_ptr<TypeConstructorOld> maybe_arg_type_ctor, |
| std::optional<Name> handle_subtype_identifier, |
| std::unique_ptr<Constant> handle_rights, std::unique_ptr<Constant> maybe_size, |
| types::Nullability nullability) |
| : name(std::move(name)), |
| maybe_arg_type_ctor(std::move(maybe_arg_type_ctor)), |
| handle_subtype_identifier(std::move(handle_subtype_identifier)), |
| handle_rights(std::move(handle_rights)), |
| maybe_size(std::move(maybe_size)), |
| nullability(nullability) {} |
| |
| // Returns a type constructor for the size type (used for bounds). |
| static std::unique_ptr<TypeConstructorOld> CreateSizeType(); |
| |
| // Set during construction. |
| const Name name; |
| std::unique_ptr<TypeConstructorOld> maybe_arg_type_ctor; |
| std::optional<Name> handle_subtype_identifier; |
| std::unique_ptr<Constant> handle_rights; |
| std::unique_ptr<Constant> maybe_size; |
| types::Nullability nullability; |
| |
| // Set during compilation. |
| const Type* type = nullptr; |
| LayoutInvocation resolved_params; |
| }; |
| |
| struct LayoutParameterList; |
| struct TypeConstraints; |
| |
| struct TypeConstructorNew final { |
| TypeConstructorNew(Name name, std::unique_ptr<LayoutParameterList> parameters, |
| std::unique_ptr<TypeConstraints> constraints) |
| : name(std::move(name)), |
| parameters(std::move(parameters)), |
| constraints(std::move(constraints)) {} |
| |
| // Returns a type constructor for the size type (used for bounds). |
| static std::unique_ptr<TypeConstructorNew> CreateSizeType(); |
| |
| // Set during construction. |
| // TODO(fxbug.dev/74683): Until we settle on an approach, avoid premature |
| // optimization and do the simple thing of assuming all type constructors at |
| // this point have a named reference to an existing Decl. |
| const Name name; |
| std::unique_ptr<LayoutParameterList> parameters; |
| std::unique_ptr<TypeConstraints> constraints; |
| |
| // Set during compilation. |
| const Type* type = nullptr; |
| LayoutInvocation resolved_params; |
| }; |
| |
| struct LayoutParameter { |
| public: |
| virtual ~LayoutParameter() = default; |
| enum Kind { |
| kIdentifier, |
| kLiteral, |
| kType, |
| }; |
| |
| explicit LayoutParameter(Kind kind, SourceSpan span) : kind(kind), span(span) {} |
| |
| // TODO(fxbug.dev/75112): Providing these virtual methods rather than handling |
| // each case individually in the caller makes it harder to provide more precise |
| // error messages. For example, using this pattern we'd only know that a parameter |
| // failed to be interpreted as a type and not the specifics about why it failed |
| // (was this actually a string literal? did it look like a type but fail to |
| // resolve? did it look like a type but actually point to a constant?). |
| // Addressing the bug might involve refactoring this part of the code to move |
| // more logic into the caller. This might be acceptable when the caller is |
| // type compilation (it probably needs to know these details anyways), but |
| // less so when it's a consumer of compiled results that needs to reconstruct |
| // details about the type constructor (e.g. during declaration sorting or |
| // JSON generation). |
| |
| // TODO(fxbug.dev/75805): The return types should be optional references |
| |
| // Returns the interpretation of this layout parameter as a type if possible |
| // or nullptr otherwise. There are no guarantees that the returned type has |
| // been compiled or will actually successfully compile. |
| virtual TypeConstructorNew* AsTypeCtor() const = 0; |
| |
| // Returns the interpretation of this layout parameter as a constant if possible |
| // or nullptr otherwise. There are no guarantees that the returned constant has |
| // been compiled or will actually successfully compile. |
| virtual Constant* AsConstant() const = 0; |
| |
| const Kind kind; |
| SourceSpan span; |
| }; |
| |
| struct LiteralLayoutParameter final : public LayoutParameter { |
| explicit LiteralLayoutParameter(std::unique_ptr<LiteralConstant> literal, SourceSpan span) |
| : LayoutParameter(Kind::kLiteral, span), literal(std::move(literal)) {} |
| |
| TypeConstructorNew* AsTypeCtor() const override; |
| Constant* AsConstant() const override; |
| std::unique_ptr<LiteralConstant> literal; |
| }; |
| |
| struct TypeLayoutParameter final : public LayoutParameter { |
| explicit TypeLayoutParameter(std::unique_ptr<TypeConstructorNew> type_ctor, SourceSpan span) |
| : LayoutParameter(Kind::kType, span), type_ctor(std::move(type_ctor)) {} |
| |
| TypeConstructorNew* AsTypeCtor() const override; |
| Constant* AsConstant() const override; |
| std::unique_ptr<TypeConstructorNew> type_ctor; |
| }; |
| |
| struct IdentifierLayoutParameter final : public LayoutParameter { |
| explicit IdentifierLayoutParameter(Name name, SourceSpan span) |
| : LayoutParameter(Kind::kIdentifier, span), name(std::move(name)) {} |
| |
| TypeConstructorNew* AsTypeCtor() const override; |
| Constant* AsConstant() const override; |
| |
| // Stores an interpretation of this layout as a TypeConstructor, if asked |
| // at some point (i.e. on demand by calling AsTypeCtor). We store this to |
| // store a reference to the compiled Type and LayoutInvocation |
| mutable std::unique_ptr<TypeConstructorNew> as_type_ctor; |
| |
| // Stores an interpretation of this layout as a Constant, if asked at some |
| // point (i.e. on demand by calling AsConstant). We store this to store a |
| // reference to the compiled ConstantValue |
| mutable std::unique_ptr<Constant> as_constant; |
| const Name name; |
| }; |
| |
| struct LayoutParameterList { |
| explicit LayoutParameterList(std::vector<std::unique_ptr<LayoutParameter>> items, |
| std::optional<SourceSpan> span) |
| : items(std::move(items)), span(span) {} |
| |
| std::vector<std::unique_ptr<LayoutParameter>> items; |
| const std::optional<SourceSpan> span; |
| }; |
| |
| struct TypeConstraints { |
| explicit TypeConstraints(std::vector<std::unique_ptr<Constant>> items, |
| std::optional<SourceSpan> span) |
| : items(std::move(items)), span(span) {} |
| |
| std::vector<std::unique_ptr<Constant>> items; |
| const std::optional<SourceSpan> span; |
| }; |
| |
| struct Using final { |
| Using(Name name, const PrimitiveType* type) : name(std::move(name)), type(type) {} |
| |
| const Name name; |
| const PrimitiveType* type; |
| }; |
| |
| // Const represents the _declaration_ of a constant. (For the _use_, see |
| // Constant. For the _value_, see ConstantValue.) A Const consists of a |
| // left-hand-side Name (found in Decl) and a right-hand-side Constant. |
| struct Const final : public Decl { |
| Const(std::unique_ptr<raw::AttributeList> attributes, Name name, TypeConstructor type_ctor, |
| std::unique_ptr<Constant> value) |
| : Decl(Kind::kConst, std::move(attributes), std::move(name)), |
| type_ctor(std::move(type_ctor)), |
| value(std::move(value)) {} |
| TypeConstructor type_ctor; |
| std::unique_ptr<Constant> value; |
| }; |
| |
| struct Enum final : public TypeDecl { |
| struct Member { |
| Member(SourceSpan name, std::unique_ptr<Constant> value, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : name(name), value(std::move(value)), attributes(std::move(attributes)) {} |
| SourceSpan name; |
| std::unique_ptr<Constant> value; |
| std::unique_ptr<raw::AttributeList> attributes; |
| }; |
| |
| Enum(std::unique_ptr<raw::AttributeList> attributes, Name name, TypeConstructor subtype_ctor, |
| std::vector<Member> members, types::Strictness strictness) |
| : TypeDecl(Kind::kEnum, std::move(attributes), std::move(name)), |
| subtype_ctor(std::move(subtype_ctor)), |
| members(std::move(members)), |
| strictness(strictness) {} |
| |
| // Set during construction. |
| TypeConstructor subtype_ctor; |
| std::vector<Member> members; |
| const types::Strictness strictness; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| |
| // Set during compilation. |
| const PrimitiveType* type = nullptr; |
| // Set only for flexible enums, and either is set depending on signedness of |
| // underlying enum type. |
| std::optional<int64_t> unknown_value_signed; |
| std::optional<uint64_t> unknown_value_unsigned; |
| }; |
| |
| struct Bits final : public TypeDecl { |
| struct Member { |
| Member(SourceSpan name, std::unique_ptr<Constant> value, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : name(name), value(std::move(value)), attributes(std::move(attributes)) {} |
| SourceSpan name; |
| std::unique_ptr<Constant> value; |
| std::unique_ptr<raw::AttributeList> attributes; |
| }; |
| |
| Bits(std::unique_ptr<raw::AttributeList> attributes, Name name, TypeConstructor subtype_ctor, |
| std::vector<Member> members, types::Strictness strictness) |
| : TypeDecl(Kind::kBits, std::move(attributes), std::move(name)), |
| subtype_ctor(std::move(subtype_ctor)), |
| members(std::move(members)), |
| strictness(strictness) {} |
| |
| // Set during construction. |
| TypeConstructor subtype_ctor; |
| std::vector<Member> members; |
| const types::Strictness strictness; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| |
| // Set during compilation. |
| uint64_t mask = 0; |
| }; |
| |
| struct Service final : public TypeDecl { |
| struct Member { |
| Member(TypeConstructor type_ctor, SourceSpan name, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : type_ctor(std::move(type_ctor)), |
| name(std::move(name)), |
| attributes(std::move(attributes)) {} |
| |
| TypeConstructor type_ctor; |
| SourceSpan name; |
| std::unique_ptr<raw::AttributeList> attributes; |
| }; |
| |
| Service(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members) |
| : TypeDecl(Kind::kService, std::move(attributes), std::move(name)), |
| members(std::move(members)) {} |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| |
| std::vector<Member> members; |
| }; |
| |
| struct Struct; |
| |
| // Historically, StructMember was a nested class inside Struct named Struct::Member. However, this |
| // was made a top-level class since it's not possible to forward-declare nested classes in C++. For |
| // backward-compatibility, Struct::Member is now an alias for this top-level StructMember. |
| // TODO(fxbug.dev/37535): Move this to a nested class inside Struct. |
| struct StructMember : public Object { |
| StructMember(TypeConstructor type_ctor, SourceSpan name, |
| std::unique_ptr<Constant> maybe_default_value, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : type_ctor(std::move(type_ctor)), |
| name(std::move(name)), |
| maybe_default_value(std::move(maybe_default_value)), |
| attributes(std::move(attributes)) {} |
| TypeConstructor type_ctor; |
| SourceSpan name; |
| std::unique_ptr<Constant> maybe_default_value; |
| std::unique_ptr<raw::AttributeList> attributes; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| |
| FieldShape fieldshape(WireFormat wire_format) const; |
| |
| const Struct* parent = nullptr; |
| }; |
| |
| struct Struct final : public TypeDecl { |
| using Member = StructMember; |
| |
| Struct(std::unique_ptr<raw::AttributeList> attributes, Name name, |
| std::vector<Member> unparented_members, std::optional<types::Resourceness> resourceness, |
| bool is_request_or_response = false) |
| : TypeDecl(Kind::kStruct, std::move(attributes), std::move(name)), |
| members(std::move(unparented_members)), |
| resourceness(resourceness), |
| is_request_or_response(is_request_or_response) { |
| for (auto& member : members) { |
| member.parent = this; |
| } |
| } |
| |
| std::vector<Member> members; |
| |
| // For user-defined structs, this is set during construction. For synthesized |
| // structs (requests/responses, error result success payload) it is set during |
| // compilation based on the struct's members. |
| std::optional<types::Resourceness> resourceness; |
| |
| // This is true iff this struct is a method request/response in a transaction header. |
| const bool is_request_or_response; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| }; |
| |
| struct Table; |
| |
| // See the comment on the StructMember class for why this is a top-level class. |
| // TODO(fxbug.dev/37535): Move this to a nested class inside Table::Member. |
| struct TableMemberUsed : public Object { |
| TableMemberUsed(TypeConstructor type_ctor, SourceSpan name, |
| std::unique_ptr<Constant> maybe_default_value, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : type_ctor(std::move(type_ctor)), |
| name(std::move(name)), |
| maybe_default_value(std::move(maybe_default_value)), |
| attributes(std::move(attributes)) {} |
| TypeConstructor type_ctor; |
| SourceSpan name; |
| std::unique_ptr<Constant> maybe_default_value; |
| std::unique_ptr<raw::AttributeList> attributes; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| |
| FieldShape fieldshape(WireFormat wire_format) const; |
| }; |
| |
| // See the comment on the StructMember class for why this is a top-level class. |
| // TODO(fxbug.dev/37535): Move this to a nested class inside Table. |
| struct TableMember : public Object { |
| using Used = TableMemberUsed; |
| |
| TableMember(std::unique_ptr<raw::Ordinal64> ordinal, TypeConstructor type, SourceSpan name, |
| std::unique_ptr<Constant> maybe_default_value, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : ordinal(std::move(ordinal)), |
| maybe_used(std::make_unique<Used>(std::move(type), name, std::move(maybe_default_value), |
| std::move(attributes))) {} |
| TableMember(std::unique_ptr<raw::Ordinal64> ordinal, TypeConstructor type, SourceSpan name, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : ordinal(std::move(ordinal)), |
| maybe_used(std::make_unique<Used>(std::move(type), name, nullptr, std::move(attributes))) {} |
| TableMember(std::unique_ptr<raw::Ordinal64> ordinal, SourceSpan span) |
| : ordinal(std::move(ordinal)), span(span) {} |
| |
| std::unique_ptr<raw::Ordinal64> ordinal; |
| |
| // The span for reserved table members. |
| std::optional<SourceSpan> span; |
| |
| std::unique_ptr<Used> maybe_used; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| }; |
| |
| struct Table final : public TypeDecl { |
| using Member = TableMember; |
| |
| Table(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members, |
| types::Strictness strictness, types::Resourceness resourceness) |
| : TypeDecl(Kind::kTable, std::move(attributes), std::move(name)), |
| members(std::move(members)), |
| strictness(strictness), |
| resourceness(resourceness) {} |
| |
| std::vector<Member> members; |
| const types::Strictness strictness; |
| const types::Resourceness resourceness; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| }; |
| |
| struct Union; |
| |
| // See the comment on the StructMember class for why this is a top-level class. |
| // TODO(fxbug.dev/37535): Move this to a nested class inside Union. |
| struct UnionMemberUsed : public Object { |
| UnionMemberUsed(TypeConstructor type_ctor, SourceSpan name, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : type_ctor(std::move(type_ctor)), name(name), attributes(std::move(attributes)) {} |
| TypeConstructor type_ctor; |
| SourceSpan name; |
| std::unique_ptr<raw::AttributeList> attributes; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| |
| FieldShape fieldshape(WireFormat wire_format) const; |
| |
| const Union* parent = nullptr; |
| }; |
| |
| // See the comment on the StructMember class for why this is a top-level class. |
| // TODO(fxbug.dev/37535): Move this to a nested class inside Union. |
| struct UnionMember : public Object { |
| using Used = UnionMemberUsed; |
| |
| UnionMember(std::unique_ptr<raw::Ordinal64> ordinal, TypeConstructor type_ctor, SourceSpan name, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : ordinal(std::move(ordinal)), |
| maybe_used(std::make_unique<Used>(std::move(type_ctor), name, std::move(attributes))) {} |
| UnionMember(std::unique_ptr<raw::Ordinal64> ordinal, SourceSpan span) |
| : ordinal(std::move(ordinal)), span(span) {} |
| |
| std::unique_ptr<raw::Ordinal64> ordinal; |
| |
| // The span for reserved members. |
| std::optional<SourceSpan> span; |
| |
| std::unique_ptr<Used> maybe_used; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| }; |
| |
| struct Union final : public TypeDecl { |
| using Member = UnionMember; |
| |
| Union(std::unique_ptr<raw::AttributeList> attributes, Name name, |
| std::vector<Member> unparented_members, types::Strictness strictness, |
| std::optional<types::Resourceness> resourceness) |
| : TypeDecl(Kind::kUnion, std::move(attributes), std::move(name)), |
| members(std::move(unparented_members)), |
| strictness(strictness), |
| resourceness(resourceness) { |
| for (auto& member : members) { |
| if (member.maybe_used) { |
| member.maybe_used->parent = this; |
| } |
| } |
| } |
| |
| std::vector<Member> members; |
| const types::Strictness strictness; |
| |
| // For user-defined unions, this is set on construction. For synthesized |
| // unions (in error result responses) it is set during compilation based on |
| // the unions's members. |
| std::optional<types::Resourceness> resourceness; |
| |
| std::vector<std::reference_wrapper<const Member>> MembersSortedByXUnionOrdinal() const; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| }; |
| |
| struct Protocol final : public TypeDecl { |
| struct Method { |
| Method(Method&&) = default; |
| Method& operator=(Method&&) = default; |
| |
| Method(std::unique_ptr<raw::AttributeList> attributes, |
| std::unique_ptr<raw::Ordinal64> generated_ordinal64, SourceSpan name, |
| Struct* maybe_request, Struct* maybe_response) |
| : attributes(std::move(attributes)), |
| generated_ordinal64(std::move(generated_ordinal64)), |
| name(std::move(name)), |
| maybe_request(maybe_request), |
| maybe_response(maybe_response) { |
| assert(this->maybe_request != nullptr || this->maybe_response != nullptr); |
| } |
| |
| std::unique_ptr<raw::AttributeList> attributes; |
| std::unique_ptr<raw::Ordinal64> generated_ordinal64; |
| SourceSpan name; |
| Struct* maybe_request; |
| Struct* maybe_response; |
| // This is set to the |Protocol| instance that owns this |Method|, |
| // when the |Protocol| is constructed. |
| Protocol* owning_protocol = nullptr; |
| }; |
| |
| // Used to keep track of a all methods (i.e. including composed methods). |
| // Method pointers here are set after composed_protocols are compiled, and |
| // are owned by the corresponding composed_protocols. |
| struct MethodWithInfo { |
| MethodWithInfo(const Method* method, bool is_composed) |
| : method(method), is_composed(is_composed) {} |
| const Method* method; |
| const bool is_composed; |
| }; |
| |
| Protocol(std::unique_ptr<raw::AttributeList> attributes, Name name, |
| std::set<Name> composed_protocols, std::vector<Method> methods) |
| : TypeDecl(Kind::kProtocol, std::move(attributes), std::move(name)), |
| composed_protocols(std::move(composed_protocols)), |
| methods(std::move(methods)) { |
| for (auto& method : this->methods) { |
| method.owning_protocol = this; |
| } |
| } |
| |
| std::set<Name> composed_protocols; |
| std::vector<Method> methods; |
| std::vector<MethodWithInfo> all_methods; |
| |
| std::any AcceptAny(VisitorAny* visitor) const override; |
| }; |
| |
| struct Resource final : public Decl { |
| struct Property { |
| Property(TypeConstructor type_ctor, SourceSpan name, |
| std::unique_ptr<raw::AttributeList> attributes) |
| : type_ctor(std::move(type_ctor)), |
| name(std::move(name)), |
| attributes(std::move(attributes)) {} |
| TypeConstructor type_ctor; |
| SourceSpan name; |
| std::unique_ptr<raw::AttributeList> attributes; |
| }; |
| |
| Resource(std::unique_ptr<raw::AttributeList> attributes, Name name, TypeConstructor subtype_ctor, |
| std::vector<Property> properties) |
| : Decl(Kind::kResource, std::move(attributes), std::move(name)), |
| subtype_ctor(std::move(subtype_ctor)), |
| properties(std::move(properties)) {} |
| |
| // Set during construction. |
| TypeConstructor subtype_ctor; |
| std::vector<Property> properties; |
| |
| Property* LookupProperty(std::string_view name); |
| }; |
| |
| struct TypeAlias final : public Decl { |
| TypeAlias(std::unique_ptr<raw::AttributeList> attributes, Name name, |
| TypeConstructor partial_type_ctor) |
| : Decl(Kind::kTypeAlias, std::move(attributes), std::move(name)), |
| partial_type_ctor(std::move(partial_type_ctor)) {} |
| |
| // The shape of this type constructor is more constrained than just being a |
| // "partial" type constructor - it is either a normal type constructor |
| // referring directly to a non-type-alias with all layout parameters fully |
| // specified (e.g. alias foo = array<T, 3>), or it is a type constructor |
| // referring to another type alias that has no layout parameters (e.g. alias |
| // bar = foo). |
| // The constraints on the other hand are indeed "partial" - any type alias |
| // at any point in a "type alias chain" can specify a constraint, but any |
| // constraint can only specified once. This behavior will change in |
| // fxbug.dev/74193. |
| TypeConstructor partial_type_ctor; |
| }; |
| |
| // Wrapper class around a Library to provide specific methods to TypeTemplates. |
| // Unlike making a direct friend relationship, this approach: |
| // 1. avoids having to declare every TypeTemplate subclass as a friend |
| // 2. only exposes the methods that are needed from the Library to the TypeTemplate. |
| class LibraryMediator { |
| public: |
| explicit LibraryMediator(Library* library) : library_(library) {} |
| |
| // Top level methods for resolving layout parameters. These are used by |
| // TypeTemplates. |
| bool ResolveParamAsType(const flat::TypeTemplate* layout, |
| const std::unique_ptr<LayoutParameter>& param, |
| const Type** out_type) const; |
| bool ResolveParamAsSize(const flat::TypeTemplate* layout, |
| const std::unique_ptr<LayoutParameter>& param, |
| const Size** out_size) const; |
| |
| // Top level methods for resolving constraints. These are used by Types |
| enum class ConstraintKind { |
| kHandleSubtype, |
| kHandleRights, |
| kSize, |
| kNullability, |
| }; |
| struct ResolvedConstraint { |
| ConstraintKind kind; |
| |
| union Value { |
| uint32_t handle_subtype; |
| const HandleRights* handle_rights; |
| const Size* size; |
| // Storing a value for nullability is redundant, since there's only one possible value - if we |
| // resolved to optional, then the caller knows that the resulting value is |
| // types::Nullability::kNullable. |
| } value; |
| }; |
| // Convenience method to iterate through the possible interpretations, returning the first one |
| // that succeeds. This is valid because the interpretations are mutually exclusive, since a Name |
| // can only ever refer to one kind of thing. |
| bool ResolveConstraintAs(const std::unique_ptr<Constant>& constraint, |
| const std::vector<ConstraintKind>& interpretations, |
| Resource* resource_decl, ResolvedConstraint* out) const; |
| |
| // These methods forward their implementation to the library_. They are used |
| // by the top level methods above, as well as directly by ResolveOldSyntaxArgs |
| bool ResolveType(TypeConstructorOld* type) const; |
| bool ResolveType(TypeConstructorNew* type) const; |
| bool ResolveSizeBound(Constant* size_constant, const Size** out_size) const; |
| bool ResolveAsOptional(Constant* constant) const; |
| bool ResolveAsHandleSubtype(Resource* resource, const std::unique_ptr<Constant>& constant, |
| uint32_t* out_obj_type) const; |
| bool ResolveAsHandleRights(Resource* resource, Constant* constant, |
| const HandleRights** out_rights) const; |
| |
| template <typename... Args> |
| bool Fail(const ErrorDef<Args...>& err, const std::optional<SourceSpan>& span, |
| const Args&... args) const; |
| |
| // This is unrelated to resolving arguments: it is required in the workaround for |
| // the special handling of handles, and can be removed once resources are fully |
| // generalized (see HandleTypeTemplate::GetResource) |
| Decl* LookupDeclByName(Name::Key name) const; |
| |
| // Used specifically in TypeAliasTypeTemplates to recursively compile the next |
| // type alias. |
| bool CompileDecl(Decl* decl) const; |
| |
| private: |
| Library* library_; |
| }; |
| |
| // CreateInvocation represents a set of resolved layout parameters/constraints. |
| // It is only used in the old syntax path. |
| struct CreateInvocation { |
| CreateInvocation(const Name& name, const Type* arg_type, std::optional<uint32_t> obj_type, |
| std::optional<types::HandleSubtype> handle_subtype, |
| const HandleRights* handle_rights, const Size* size, |
| const types::Nullability nullability) |
| : name(name), |
| arg_type(arg_type), |
| obj_type(obj_type), |
| handle_subtype(handle_subtype), |
| handle_rights(handle_rights), |
| size(size), |
| nullability(nullability) {} |
| |
| const Name& name; |
| const Type* arg_type; |
| std::optional<uint32_t> obj_type; |
| std::optional<types::HandleSubtype> handle_subtype; |
| const HandleRights* handle_rights; |
| const Size* size; |
| const types::Nullability nullability; |
| }; |
| |
| class TypeTemplate { |
| public: |
| TypeTemplate(Name name, Typespace* typespace, Reporter* reporter) |
| : typespace_(typespace), name_(std::move(name)), reporter_(reporter) {} |
| |
| TypeTemplate(TypeTemplate&& type_template) = default; |
| |
| virtual ~TypeTemplate() = default; |
| |
| const Name& name() const { return name_; } |
| |
| // The set of unresolved layout arguments and constraints as they appear in the |
| // old syntax, i.e. coming directly from flat::TypeConstructor. |
| // Unlike in the more general form ArgsAndConstraintsNew, this representation |
| // can be resolved to a CreateInvocation before determining what the layout is. |
| struct OldSyntaxParamsAndConstraints { |
| const Name& name; |
| const std::unique_ptr<TypeConstructorOld>& maybe_arg_type_ctor; |
| const std::optional<Name>& handle_subtype_identifier; |
| const std::unique_ptr<Constant>& handle_rights; |
| const std::unique_ptr<Constant>& maybe_size; |
| const types::Nullability nullability; |
| }; |
| |
| virtual bool Create(const LibraryMediator& lib, const OldSyntaxParamsAndConstraints& args, |
| std::unique_ptr<Type>* out_type, LayoutInvocation* out_params) const = 0; |
| |
| bool ResolveOldSyntaxArgs(const LibraryMediator& lib, const OldSyntaxParamsAndConstraints& args, |
| std::unique_ptr<CreateInvocation>* out_args, |
| LayoutInvocation* out_params) const; |
| |
| struct NewSyntaxParamsAndConstraints { |
| // Type templates should not need to know the name of the layout and should |
| // only require a span for reporting errors, since any logic related to the |
| // name itself should be encapsulated in the layout resolution process. However, |
| // currently an exception must be made for handles so that we can double check |
| // whether the name actual refers to a Resource at runtime (see |
| // TypeTemplate::GetResource for details). |
| const Name& name; |
| const std::unique_ptr<LayoutParameterList>& parameters; |
| const std::unique_ptr<TypeConstraints>& constraints; |
| }; |
| |
| virtual bool Create(const LibraryMediator& lib, const NewSyntaxParamsAndConstraints& args, |
| std::unique_ptr<Type>* out_type, LayoutInvocation* out_params) const = 0; |
| |
| virtual bool GetResource(const LibraryMediator& lib, const Name& name, |
| Resource** out_resource) const; |
| |
| protected: |
| template <typename... Args> |
| bool Fail(const ErrorDef<const TypeTemplate*, Args...>& err, |
| const std::optional<SourceSpan>& span, const Args&... args) const; |
| template <typename... Args> |
| bool Fail(const ErrorDef<Args...>& err, const Args&... args) const; |
| |
| Typespace* typespace_; |
| |
| Name name_; |
| |
| Reporter* reporter_; |
| }; |
| |
| // Typespace provides builders for all types (e.g. array, vector, string), and |
| // ensures canonicalization, i.e. the same type is represented by one object, |
| // shared amongst all uses of said type. For instance, while the text |
| // `vector<uint8>:7` may appear multiple times in source, these all indicate |
| // the same type. |
| class Typespace { |
| public: |
| explicit Typespace(Reporter* reporter) : reporter_(reporter) {} |
| |
| bool Create(const LibraryMediator& lib, const flat::Name& name, |
| const std::unique_ptr<TypeConstructorOld>& maybe_arg_type_ctor, |
| const std::optional<Name>& handle_subtype_identifier, |
| const std::unique_ptr<Constant>& handle_rights, |
| const std::unique_ptr<Constant>& maybe_size, types::Nullability nullability, |
| const Type** out_type, LayoutInvocation* out_params); |
| bool Create(const LibraryMediator& lib, const flat::Name& name, |
| const std::unique_ptr<LayoutParameterList>& parameters, |
| const std::unique_ptr<TypeConstraints>& constraints, const Type** out_type, |
| LayoutInvocation* out_params); |
| |
| void AddTemplate(std::unique_ptr<TypeTemplate> type_template); |
| |
| // TODO(fxbug.dev/70247): this method has been made public solely for the |
| // benefit of fidlconv. Once the conversion using that tool has been |
| // completed and tool has been removed, this method should be re-privatized. |
| const TypeTemplate* LookupTemplate(const flat::Name& name) const; |
| |
| // RootTypes creates a instance with all primitive types. It is meant to be |
| // used as the top-level types lookup mechanism, providing definitional |
| // meaning to names such as `int64`, or `bool`. |
| static Typespace RootTypes(Reporter* reporter); |
| |
| private: |
| friend class TypeAliasTypeTemplate; |
| |
| bool CreateNotOwned(const LibraryMediator& lib, const flat::Name& name, |
| const std::unique_ptr<TypeConstructorOld>& maybe_arg_type_ctor, |
| const std::optional<Name>& handle_subtype_identifier, |
| const std::unique_ptr<Constant>& handle_rights, |
| const std::unique_ptr<Constant>& maybe_size, types::Nullability nullability, |
| std::unique_ptr<Type>* out_type, LayoutInvocation* out_params); |
| bool CreateNotOwned(const LibraryMediator& lib, const flat::Name& name, |
| const std::unique_ptr<LayoutParameterList>& parameters, |
| const std::unique_ptr<TypeConstraints>& constraints, |
| std::unique_ptr<Type>* out_type, LayoutInvocation* out_params); |
| |
| std::map<Name::Key, std::unique_ptr<TypeTemplate>> templates_; |
| std::vector<std::unique_ptr<Type>> types_; |
| |
| Reporter* reporter_; |
| }; |
| |
| // AttributeSchema defines a schema for attributes. This includes: |
| // - The allowed placement of an attribute (e.g. on a method, on a struct |
| // declaration); |
| // - The allowed values which an attribute can take. |
| // For attributes which may be placed on declarations (e.g. protocol, struct, |
| // union, table), a schema may additionally include: |
| // - A constraint which must be met by the declaration. |
| class AttributeSchema { |
| public: |
| using Constraint = |
| fit::function<bool(Reporter* reporter, const raw::Attribute& attribute, const Decl* decl)>; |
| |
| // Placement indicates the placement of an attribute, e.g. whether an |
| // attribute is placed on an enum declaration, method, or union |
| // member. |
| enum class Placement { |
| kBitsDecl, |
| kBitsMember, |
| kConstDecl, |
| kEnumDecl, |
| kEnumMember, |
| kProtocolDecl, |
| kLibrary, |
| kMethod, |
| kResourceDecl, |
| kResourceProperty, |
| kServiceDecl, |
| kServiceMember, |
| kStructDecl, |
| kStructMember, |
| kTableDecl, |
| kTableMember, |
| kTypeAliasDecl, |
| kUnionDecl, |
| kUnionMember, |
| kDeprecated, |
| }; |
| |
| AttributeSchema(const std::set<Placement>& allowed_placements, |
| const std::set<std::string> allowed_values, |
| Constraint constraint = NoOpConstraint); |
| |
| AttributeSchema(AttributeSchema&& schema) = default; |
| |
| static AttributeSchema Deprecated(); |
| |
| void ValidatePlacement(Reporter* reporter, const raw::Attribute& attribute, |
| Placement placement) const; |
| |
| void ValidateValue(Reporter* reporter, const raw::Attribute& attribute) const; |
| |
| void ValidateConstraint(Reporter* reporter, const raw::Attribute& attribute, |
| const Decl* decl) const; |
| |
| private: |
| static bool NoOpConstraint(Reporter* reporter, const raw::Attribute& attribute, |
| const Decl* decl) { |
| return true; |
| } |
| |
| std::set<Placement> allowed_placements_; |
| std::set<std::string> allowed_values_; |
| Constraint constraint_; |
| }; |
| |
| class Libraries { |
| public: |
| Libraries(); |
| |
| // Insert |library|. |
| bool Insert(std::unique_ptr<Library> library); |
| |
| // Lookup a library by its |library_name|. |
| bool Lookup(const std::vector<std::string_view>& library_name, Library** out_library) const; |
| |
| void AddAttributeSchema(const std::string& name, AttributeSchema schema) { |
| [[maybe_unused]] auto iter = attribute_schemas_.emplace(name, std::move(schema)); |
| assert(iter.second && "do not add schemas twice"); |
| } |
| |
| const AttributeSchema* RetrieveAttributeSchema(Reporter* reporter, |
| const raw::Attribute& attribute) const; |
| |
| std::set<std::vector<std::string_view>> Unused(const Library* target_library) const; |
| |
| private: |
| std::map<std::vector<std::string_view>, std::unique_ptr<Library>> all_libraries_; |
| std::map<std::string, AttributeSchema> attribute_schemas_; |
| }; |
| |
| class Dependencies { |
| public: |
| // Register a dependency to a library. The newly recorded dependent library |
| // will be referenced by its name, and may also be optionally be referenced |
| // by an alias. |
| bool Register(const SourceSpan& span, std::string_view filename, Library* dep_library, |
| const std::unique_ptr<raw::Identifier>& maybe_alias); |
| |
| // Returns true if this dependency set contains a library with the given name and filename. |
| bool Contains(std::string_view filename, const std::vector<std::string_view>& name); |
| |
| enum class LookupMode { |
| kSilent, |
| kUse, |
| }; |
| |
| // Looks up a dependent library by |filename| and |name|, and optionally marks |
| // it as used or not. |
| bool Lookup(std::string_view filename, const std::vector<std::string_view>& name, LookupMode mode, |
| Library** out_library) const; |
| |
| // VerifyAllDependenciesWereUsed verifies that all regisered dependencies |
| // were used, i.e. at least one lookup was made to retrieve them. |
| // Reports errors directly, and returns true if one error or more was |
| // reported. |
| bool VerifyAllDependenciesWereUsed(const Library& for_library, Reporter* reporter); |
| |
| const std::set<Library*>& dependencies() const { return dependencies_aggregate_; } |
| |
| private: |
| struct LibraryRef { |
| LibraryRef(const SourceSpan span, Library* library) : span_(span), library_(library) {} |
| |
| const SourceSpan span_; |
| Library* library_; |
| bool used_ = false; |
| }; |
| |
| bool InsertByName(std::string_view filename, const std::vector<std::string_view>& name, |
| LibraryRef* ref); |
| |
| using ByName = std::map<std::vector<std::string_view>, LibraryRef*>; |
| using ByFilename = std::map<std::string, std::unique_ptr<ByName>>; |
| |
| std::vector<std::unique_ptr<LibraryRef>> refs_; |
| ByFilename dependencies_; |
| std::set<Library*> dependencies_aggregate_; |
| }; |
| |
| class StepBase; |
| class ConsumeStep; |
| class CompileStep; |
| class VerifyResourcenessStep; |
| class VerifyAttributesStep; |
| |
| using MethodHasher = fit::function<raw::Ordinal64( |
| const std::vector<std::string_view>& library_name, const std::string_view& protocol_name, |
| const std::string_view& selector_name, const raw::SourceElement& source_element)>; |
| |
| class Library { |
| friend StepBase; |
| friend ConsumeStep; |
| friend CompileStep; |
| friend VerifyResourcenessStep; |
| friend VerifyAttributesStep; |
| |
| public: |
| Library(const Libraries* all_libraries, Reporter* reporter, Typespace* typespace, |
| MethodHasher method_hasher, ExperimentalFlags experimental_flags) |
| : all_libraries_(all_libraries), |
| reporter_(reporter), |
| typespace_(typespace), |
| method_hasher_(std::move(method_hasher)), |
| experimental_flags_(experimental_flags) {} |
| |
| bool ConsumeFile(std::unique_ptr<raw::File> file); |
| bool Compile(); |
| |
| const std::vector<std::string_view>& name() const { return library_name_; } |
| const raw::AttributeList* attributes() const { return attributes_.get(); } |
| |
| private: |
| bool Fail(std::unique_ptr<Diagnostic> err); |
| template <typename... Args> |
| bool Fail(const ErrorDef<Args...>& err, const Args&... args); |
| template <typename... Args> |
| bool Fail(const ErrorDef<Args...>& err, const std::optional<SourceSpan>& span, |
| const Args&... args); |
| template <typename... Args> |
| bool Fail(const ErrorDef<Args...>& err, const Name& name, const Args&... args) { |
| return Fail(err, name.span(), args...); |
| } |
| template <typename... Args> |
| bool Fail(const ErrorDef<Args...>& err, const Decl& decl, const Args&... args) { |
| return Fail(err, decl.name, args...); |
| } |
| |
| void ValidateAttributesPlacement(AttributeSchema::Placement placement, |
| const raw::AttributeList* attributes); |
| void ValidateAttributesConstraints(const Decl* decl, const raw::AttributeList* attributes); |
| |
| // TODO(fxbug.dev/7920): Rationalize the use of names. Here, a simple name is |
| // one that is not scoped, it is just text. An anonymous name is one that |
| // is guaranteed to be unique within the library, and a derived name is one |
| // that is library scoped but derived from the concatenated components using |
| // underscores as delimiters. |
| SourceSpan GeneratedSimpleName(const std::string& name); |
| std::string NextAnonymousName(); |
| |
| // Attempts to compile a compound identifier, and resolve it to a name |
| // within the context of a library. On success, the name is returned. |
| // On failure, no name is returned, and a failure is emitted, i.e. the |
| // caller is not responsible for reporting the resolution error. |
| std::optional<Name> CompileCompoundIdentifier(const raw::CompoundIdentifier* compound_identifier); |
| bool RegisterDecl(std::unique_ptr<Decl> decl); |
| |
| ConsumeStep StartConsumeStep(fidl::utils::Syntax syntax); |
| CompileStep StartCompileStep(); |
| VerifyResourcenessStep StartVerifyResourcenessStep(); |
| VerifyAttributesStep StartVerifyAttributesStep(); |
| |
| bool ConsumeConstant(std::unique_ptr<raw::Constant> raw_constant, |
| std::unique_ptr<Constant>* out_constant); |
| void ConsumeLiteralConstant(raw::LiteralConstant* raw_constant, |
| std::unique_ptr<LiteralConstant>* out_constant); |
| bool ConsumeTypeConstructorOld(std::unique_ptr<raw::TypeConstructorOld> raw_type_ctor, |
| std::unique_ptr<TypeConstructorOld>* out_type); |
| |
| void ConsumeUsing(std::unique_ptr<raw::Using> using_directive); |
| bool ConsumeTypeAlias(std::unique_ptr<raw::AliasDeclaration> alias_declaration); |
| void ConsumeBitsDeclaration(std::unique_ptr<raw::BitsDeclaration> bits_declaration); |
| void ConsumeConstDeclaration(std::unique_ptr<raw::ConstDeclaration> const_declaration); |
| void ConsumeEnumDeclaration(std::unique_ptr<raw::EnumDeclaration> enum_declaration); |
| void ConsumeProtocolDeclaration(std::unique_ptr<raw::ProtocolDeclaration> protocol_declaration); |
| bool ConsumeResourceDeclaration(std::unique_ptr<raw::ResourceDeclaration> resource_declaration); |
| bool ConsumeParameterList(Name name, std::unique_ptr<raw::ParameterList> parameter_list, |
| bool anonymous, Struct** out_struct_decl); |
| bool CreateMethodResult(const Name& protocol_name, SourceSpan response_span, |
| raw::ProtocolMethod* method, Struct* in_response, Struct** out_response); |
| void ConsumeServiceDeclaration(std::unique_ptr<raw::ServiceDeclaration> service_decl); |
| void ConsumeStructDeclaration(std::unique_ptr<raw::StructDeclaration> struct_declaration); |
| void ConsumeTableDeclaration(std::unique_ptr<raw::TableDeclaration> table_declaration); |
| void ConsumeUnionDeclaration(std::unique_ptr<raw::UnionDeclaration> union_declaration); |
| |
| // start new syntax |
| void ConsumeTypeDecl(std::unique_ptr<raw::TypeDecl> type_decl); |
| // TODO(fxbug.dev/74683): The context parameter is currently unused, but exists |
| // to help generate a name when implementing anonymous layouts |
| bool ConsumeTypeConstructorNew(std::unique_ptr<raw::TypeConstructorNew> raw_type_ctor, |
| const Name& context, |
| std::unique_ptr<TypeConstructorNew>* out_type); |
| bool ConsumeTypeConstructor(raw::TypeConstructor raw_type_ctor, const Name& context, |
| TypeConstructor* out_type); |
| |
| // Here, T is expected to be an ordinal-carrying flat AST class (ie, Table or |
| // Union), while M is its "Member" sub-class. |
| template <typename T, typename M> |
| bool ConsumeOrdinaledLayout(std::unique_ptr<raw::Layout>, const Name&); |
| bool ConsumeStructLayout(std::unique_ptr<raw::Layout>, const Name&); |
| |
| // Here, T is expected to be an value-carrying flat AST class (ie, Bits or |
| // Enum), while M is its "Member" sub-class. |
| template <typename T, typename M> |
| bool ConsumeValueLayout(std::unique_ptr<raw::Layout>, const Name&); |
| bool ConsumeLayout(std::unique_ptr<raw::Layout>, const Name&); |
| // end new syntax |
| |
| bool TypeCanBeConst(const Type* type); |
| const Type* TypeResolve(const Type* type); |
| // Return true if this constant refers to the built in `optional` constraint, |
| // false otherwise. |
| bool ResolveAsOptional(Constant* constant) const; |
| bool TypeIsConvertibleTo(const Type* from_type, const Type* to_type); |
| std::unique_ptr<TypeConstructorOld> IdentifierTypeForDecl(const Decl* decl, |
| types::Nullability nullability); |
| |
| bool AddConstantDependencies(const Constant* constant, std::set<const Decl*>* out_edges); |
| bool DeclDependencies(const Decl* decl, std::set<const Decl*>* out_edges); |
| |
| bool SortDeclarations(); |
| |
| bool CompileBits(Bits* bits_declaration); |
| bool CompileConst(Const* const_declaration); |
| bool CompileEnum(Enum* enum_declaration); |
| bool CompileProtocol(Protocol* protocol_declaration); |
| bool CompileResource(Resource* resource_declaration); |
| bool CompileService(Service* service_decl); |
| bool CompileStruct(Struct* struct_declaration); |
| bool CompileTable(Table* table_declaration); |
| bool CompileUnion(Union* union_declaration); |
| bool CompileTypeAlias(TypeAlias* type_alias); |
| |
| // This top level function switches behavior depending on what syntax is |
| // being read |
| bool CompileTypeConstructor(TypeConstructor* type_ctor); |
| bool CompileTypeConstructorOld(TypeConstructorOld* type_ctor); |
| bool CompileTypeConstructorNew(TypeConstructorNew* type_ctor); |
| |
| enum class AllowedCategories { |
| kTypeOrProtocol, |
| kTypeOnly, |
| kProtocolOnly, |
| // Note: there's currently no scenario where we expect a service. |
| }; |
| // Returns true if the provided type falls into one of the specified categories, |
| // and false otherwise. A span can be provided for error reporting. |
| bool VerifyTypeCategory(const Type* type, std::optional<SourceSpan> span, |
| AllowedCategories category); |
| |
| ConstantValue::Kind ConstantValuePrimitiveKind(const types::PrimitiveSubtype primitive_subtype); |
| bool ResolveHandleRightsConstant(Resource* resource, Constant* constant, |
| const HandleRights** out_rights); |
| bool ResolveHandleSubtypeIdentifier(Resource* resource, const std::unique_ptr<Constant>& constant, |
| uint32_t* out_obj_type); |
| bool ResolveSizeBound(Constant* size_constant, const Size** out_size); |
| bool ResolveOrOperatorConstant(Constant* constant, const Type* type, |
| const ConstantValue& left_operand, |
| const ConstantValue& right_operand); |
| bool ResolveConstant(Constant* constant, const Type* type); |
| bool ResolveIdentifierConstant(IdentifierConstant* identifier_constant, const Type* type); |
| bool ResolveLiteralConstant(LiteralConstant* literal_constant, const Type* type); |
| |
| // Validates a single member of a bits or enum. On success, returns nullptr, |
| // and on failure returns an error. |
| template <typename MemberType> |
| using MemberValidator = fit::function<std::unique_ptr<Diagnostic>( |
| const MemberType& member, const raw::AttributeList* attributes)>; |
| template <typename DeclType, typename MemberType> |
| bool ValidateMembers(DeclType* decl, MemberValidator<MemberType> validator); |
| template <typename MemberType> |
| bool ValidateBitsMembersAndCalcMask(Bits* bits_decl, MemberType* out_mask); |
| template <typename MemberType> |
| bool ValidateEnumMembersAndCalcUnknownValue(Enum* enum_decl, MemberType* out_unknown_value); |
| |
| void VerifyDeclAttributes(const Decl* decl); |
| bool VerifyInlineSize(const Struct* decl); |
| |
| public: |
| bool CompileDecl(Decl* decl); |
| |
| // Returns nullptr when the |name| cannot be resolved to a |
| // Name. Otherwise it returns the declaration. |
| Decl* LookupDeclByName(Name::Key name) const; |
| |
| // TODO(fxbug.dev/70247): this method has been created solely for the benefit |
| // of fidlconv. Once the conversion using that tool has been completed and |
| // and the tool has been removed, this method should be removed as well. |
| // Looks up a dependent library by |filename| and |name|. |
| bool LookupDependency(std::string_view filename, const std::vector<std::string_view>& name, |
| Library** out_library) const; |
| |
| template <typename NumericType> |
| bool ParseNumericLiteral(const raw::NumericLiteral* literal, NumericType* out_value) const; |
| |
| bool HasAttribute(std::string_view name) const; |
| |
| const std::set<Library*>& dependencies() const; |
| |
| std::vector<std::string_view> library_name_; |
| |
| std::vector<std::unique_ptr<Bits>> bits_declarations_; |
| std::vector<std::unique_ptr<Const>> const_declarations_; |
| std::vector<std::unique_ptr<Enum>> enum_declarations_; |
| std::vector<std::unique_ptr<Protocol>> protocol_declarations_; |
| std::vector<std::unique_ptr<Resource>> resource_declarations_; |
| std::vector<std::unique_ptr<Service>> service_declarations_; |
| 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<TypeAlias>> type_alias_declarations_; |
| |
| // All Decl pointers here are non-null and are owned by the |
| // various foo_declarations_. |
| std::vector<const Decl*> declaration_order_; |
| |
| private: |
| // TODO(fxbug.dev/7724): Remove when canonicalizing types. |
| const Name kSizeTypeName = Name::CreateIntrinsic("uint32"); |
| const PrimitiveType kSizeType = PrimitiveType(kSizeTypeName, types::PrimitiveSubtype::kUint32); |
| |
| std::unique_ptr<raw::AttributeList> attributes_; |
| |
| Dependencies dependencies_; |
| const Libraries* all_libraries_; |
| |
| // All Decl pointers here are non-null. They are owned by the various |
| // foo_declarations_ members of this Library object, or of one of the Library |
| // objects in dependencies_. |
| std::map<Name::Key, Decl*> declarations_; |
| |
| // This map contains a subset of declarations_ (no imported declarations) |
| // keyed by `utils::canonicalize(name.decl_name())` rather than `name.key()`. |
| std::map<std::string, Decl*> declarations_by_canonical_name_; |
| |
| Reporter* reporter_; |
| Typespace* typespace_; |
| const MethodHasher method_hasher_; |
| const ExperimentalFlags experimental_flags_; |
| |
| uint32_t anon_counter_ = 0; |
| |
| VirtualSourceFile generated_source_file_{"generated"}; |
| friend class LibraryMediator; |
| }; |
| |
| class StepBase { |
| public: |
| StepBase(Library* library) |
| : library_(library), checkpoint_(library->reporter_->Checkpoint()), done_(false) {} |
| |
| ~StepBase() { assert(done_ && "Step must be completed before destructor is called"); } |
| |
| bool Done() { |
| done_ = true; |
| return checkpoint_.NoNewErrors(); |
| } |
| |
| protected: |
| Library* library_; // link to library for which this step was created |
| |
| private: |
| Reporter::Counts checkpoint_; |
| bool done_; |
| }; |
| |
| // The input to the consume step (raw::File) stores the syntax that it is written |
| // in. This syntax needs to get propagated through the two paths that the consume |
| // step consists of: |
| // * decls that go through RegisterDecl |
| // * flat::TypeConstructors that get created in ConsumeTypeConstructor |
| // To do so, ConsumeFile passes the raw::File's syntax to the ConsumeStep class, |
| // which then passes it through to the various ConsumeFooDecl as necessary. Note |
| // that only the Consume functions that correspond to nodes present in both the |
| // old and new syntax (e.g. Service, Resource) need the extra Syntax parameter - |
| // nodes that only appear in the old or new syntax (e.g. UnionDeclaration for |
| // old, Layout for new), don't need the parameter because they can hardcode the |
| // syntax inside the implementation. |
| class ConsumeStep : public StepBase { |
| public: |
| ConsumeStep(Library* library, fidl::utils::Syntax syntax) : StepBase(library) {} |
| |
| void ForAliasDeclaration(std::unique_ptr<raw::AliasDeclaration> alias_declaration) { |
| library_->ConsumeTypeAlias(std::move(alias_declaration)); |
| } |
| void ForUsing(std::unique_ptr<raw::Using> using_directive) { |
| library_->ConsumeUsing(std::move(using_directive)); |
| } |
| void ForBitsDeclaration(std::unique_ptr<raw::BitsDeclaration> bits_declaration) { |
| library_->ConsumeBitsDeclaration(std::move(bits_declaration)); |
| } |
| void ForConstDeclaration(std::unique_ptr<raw::ConstDeclaration> const_declaration) { |
| library_->ConsumeConstDeclaration(std::move(const_declaration)); |
| } |
| void ForEnumDeclaration(std::unique_ptr<raw::EnumDeclaration> enum_declaration) { |
| library_->ConsumeEnumDeclaration(std::move(enum_declaration)); |
| } |
| void ForProtocolDeclaration(std::unique_ptr<raw::ProtocolDeclaration> protocol_declaration) { |
| library_->ConsumeProtocolDeclaration(std::move(protocol_declaration)); |
| } |
| void ForResourceDeclaration(std::unique_ptr<raw::ResourceDeclaration> resource_declaration) { |
| library_->ConsumeResourceDeclaration(std::move(resource_declaration)); |
| } |
| void ForServiceDeclaration(std::unique_ptr<raw::ServiceDeclaration> service_decl) { |
| library_->ConsumeServiceDeclaration(std::move(service_decl)); |
| } |
| void ForStructDeclaration(std::unique_ptr<raw::StructDeclaration> struct_declaration) { |
| library_->ConsumeStructDeclaration(std::move(struct_declaration)); |
| } |
| void ForTableDeclaration(std::unique_ptr<raw::TableDeclaration> table_declaration) { |
| library_->ConsumeTableDeclaration(std::move(table_declaration)); |
| } |
| void ForUnionDeclaration(std::unique_ptr<raw::UnionDeclaration> union_declaration) { |
| library_->ConsumeUnionDeclaration(std::move(union_declaration)); |
| } |
| void ForTypeDecl(std::unique_ptr<raw::TypeDecl> type_decl) { |
| library_->ConsumeTypeDecl(std::move(type_decl)); |
| } |
| }; |
| |
| class CompileStep : public StepBase { |
| public: |
| CompileStep(Library* library) : StepBase(library) {} |
| |
| void ForDecl(Decl* decl) { library_->CompileDecl(decl); } |
| }; |
| |
| class VerifyResourcenessStep : public StepBase { |
| public: |
| VerifyResourcenessStep(Library* library) : StepBase(library) {} |
| |
| void ForDecl(const Decl* decl); |
| |
| private: |
| // Returns the effective resourceness of |type|. The set of effective resource |
| // types includes (1) nominal resource types per the FTP-057 definition, and |
| // (2) declarations that have an effective resource member (or equivalently, |
| // transitively contain a nominal resource). |
| types::Resourceness EffectiveResourceness(const Type* type); |
| |
| // Map from struct/table/union declarations to their effective resourceness. A |
| // value of std::nullopt indicates that the declaration has been visited, used |
| // to prevent infinite recursion. |
| std::map<const Decl*, std::optional<types::Resourceness>> effective_resourceness_; |
| }; |
| |
| class VerifyAttributesStep : public StepBase { |
| public: |
| VerifyAttributesStep(Library* library) : StepBase(library) {} |
| |
| void ForDecl(const Decl* decl) { library_->VerifyDeclAttributes(decl); } |
| }; |
| |
| // See the comment on Object::Visitor<T> for more details. |
| struct Object::VisitorAny { |
| virtual std::any Visit(const ArrayType&) = 0; |
| virtual std::any Visit(const VectorType&) = 0; |
| virtual std::any Visit(const StringType&) = 0; |
| virtual std::any Visit(const HandleType&) = 0; |
| virtual std::any Visit(const PrimitiveType&) = 0; |
| virtual std::any Visit(const IdentifierType&) = 0; |
| virtual std::any Visit(const RequestHandleType&) = 0; |
| virtual std::any Visit(const Enum&) = 0; |
| virtual std::any Visit(const Bits&) = 0; |
| virtual std::any Visit(const Service&) = 0; |
| virtual std::any Visit(const Struct&) = 0; |
| virtual std::any Visit(const Struct::Member&) = 0; |
| virtual std::any Visit(const Table&) = 0; |
| virtual std::any Visit(const Table::Member&) = 0; |
| virtual std::any Visit(const Table::Member::Used&) = 0; |
| virtual std::any Visit(const Union&) = 0; |
| virtual std::any Visit(const Union::Member&) = 0; |
| virtual std::any Visit(const Union::Member::Used&) = 0; |
| virtual std::any Visit(const Protocol&) = 0; |
| }; |
| |
| // This Visitor<T> class is useful so that Object.Accept() can enforce that its return type |
| // matches the template type of Visitor. See the comment on Object::Visitor<T> for more |
| // details. |
| template <typename T> |
| struct Object::Visitor : public VisitorAny {}; |
| |
| template <typename T> |
| T Object::Accept(Visitor<T>* visitor) const { |
| return std::any_cast<T>(AcceptAny(visitor)); |
| } |
| |
| inline std::any ArrayType::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any VectorType::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any StringType::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any HandleType::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any PrimitiveType::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any IdentifierType::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any RequestHandleType::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any Enum::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any Bits::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any Service::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any Struct::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any Struct::Member::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any Table::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any Table::Member::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any Table::Member::Used::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any Union::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| inline std::any Union::Member::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any Union::Member::Used::AcceptAny(VisitorAny* visitor) const { |
| return visitor->Visit(*this); |
| } |
| |
| inline std::any Protocol::AcceptAny(VisitorAny* visitor) const { return visitor->Visit(*this); } |
| |
| } // namespace flat |
| } // namespace fidl |
| |
| #endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_FLAT_AST_H_ |