blob: fc2a987c6a278127a13ac8e6cca504454a77b730 [file] [log] [blame]
// 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.
#ifndef ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_FLAT_AST_H_
#define ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_FLAT_AST_H_
#include <assert.h>
#include <stdint.h>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <type_traits>
#include <vector>
#include <lib/fit/function.h>
#include "attributes.h"
#include "error_reporter.h"
#include "raw_ast.h"
#include "type_shape.h"
#include "virtual_source_file.h"
// TODO(FIDL-487, ZX-3415): Decide if all cases of NumericConstantValue::Convert() are safe.
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#endif
namespace fidl {
namespace flat {
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, StringView separator);
// Name represents a scope name, i.e. a name within the context of a library
// or in the 'global' context. Names either reference (or name) things which
// appear in source, or are synthesized by the compiler (e.g. an anonymous
// struct name).
struct Name {
Name() {}
Name(const Library* library, const SourceLocation name)
: library_(library),
name_from_source_(std::make_unique<SourceLocation>(name)) {}
Name(const Library* library, const std::string& name)
: library_(library),
anonymous_name_(std::make_unique<std::string>(name)) {}
Name(Name&&) = default;
Name& operator=(Name&&) = default;
const Library* library() const { return library_; }
const SourceLocation* maybe_location() const {
return name_from_source_.get();
}
const StringView name_part() const {
if (!name_from_source_)
return *anonymous_name_.get();
return name_from_source_->data();
}
bool operator==(const Name& other) const {
// can't use the library name yet, not necesserily compiled!
auto library_ptr = reinterpret_cast<uintptr_t>(library_);
auto other_library_ptr = reinterpret_cast<uintptr_t>(other.library_);
if (library_ptr != other_library_ptr)
return false;
return name_part() == other.name_part();
}
bool operator!=(const Name& other) const { return !operator==(other); }
bool operator<(const Name& other) const {
// can't use the library name yet, not necesserily compiled!
auto library_ptr = reinterpret_cast<uintptr_t>(library_);
auto other_library_ptr = reinterpret_cast<uintptr_t>(other.library_);
if (library_ptr != other_library_ptr)
return library_ptr < other_library_ptr;
return name_part() < other.name_part();
}
private:
const Library* library_ = nullptr;
std::unique_ptr<SourceLocation> name_from_source_;
std::unique_ptr<std::string> anonymous_name_;
};
struct ConstantValue {
virtual ~ConstantValue() {}
enum struct Kind {
kInt8,
kInt16,
kInt32,
kInt64,
kUint8,
kUint16,
kUint32,
kUint64,
kFloat32,
kFloat64,
kBool,
kString,
};
virtual bool Convert(Kind kind, std::unique_ptr<ConstantValue>* out_value) const = 0;
const Kind kind;
protected:
explicit ConstantValue(Kind kind)
: kind(kind) {}
};
template <typename ValueType>
struct NumericConstantValue : ConstantValue {
static_assert(std::is_arithmetic<ValueType>::value && !std::is_same<ValueType, bool>::value,
"NumericConstantValue can only be used with a numeric ValueType!");
NumericConstantValue(ValueType value)
: ConstantValue(GetKind()), value(value) {}
operator ValueType() const { return value; }
friend bool operator==(const NumericConstantValue<ValueType>& l,
const NumericConstantValue<ValueType>& r) {
return l.value == r.value;
}
friend bool operator<(const NumericConstantValue<ValueType>& l,
const NumericConstantValue<ValueType>& r) {
return l.value < r.value;
}
friend bool operator>(const NumericConstantValue<ValueType>& l,
const NumericConstantValue<ValueType>& r) {
return l.value > r.value;
}
friend bool operator!=(const NumericConstantValue<ValueType>& l,
const NumericConstantValue<ValueType>& r) {
return l.value != r.value;
}
friend bool operator<=(const NumericConstantValue<ValueType>& l,
const NumericConstantValue<ValueType>& r) {
return l.value <= r.value;
}
friend bool operator>=(const NumericConstantValue<ValueType>& l,
const NumericConstantValue<ValueType>& r) {
return l.value >= r.value;
}
friend std::ostream& operator<<(std::ostream& os, const NumericConstantValue<ValueType>& v) {
if constexpr (GetKind() == Kind::kInt8)
os << static_cast<int>(v.value);
else if constexpr (GetKind() == Kind::kUint8)
os << static_cast<unsigned>(v.value);
else
os << v.value;
return os;
}
virtual bool Convert(Kind kind, std::unique_ptr<ConstantValue>* out_value) const override {
assert(out_value != nullptr);
switch (kind) {
case Kind::kInt8: {
if (std::is_floating_point<ValueType>::value ||
value < std::numeric_limits<int8_t>::lowest() ||
value > std::numeric_limits<int8_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<int8_t>>(
static_cast<int8_t>(value));
return true;
}
case Kind::kInt16: {
if (std::is_floating_point<ValueType>::value ||
value < std::numeric_limits<int16_t>::lowest() ||
value > std::numeric_limits<int16_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<int16_t>>(
static_cast<int16_t>(value));
return true;
}
case Kind::kInt32: {
if (std::is_floating_point<ValueType>::value ||
value < std::numeric_limits<int32_t>::lowest() ||
value > std::numeric_limits<int32_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<int32_t>>(
static_cast<int32_t>(value));
return true;
}
case Kind::kInt64: {
if (std::is_floating_point<ValueType>::value ||
value < std::numeric_limits<int64_t>::lowest() ||
value > std::numeric_limits<int64_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<int64_t>>(
static_cast<int64_t>(value));
return true;
}
case Kind::kUint8: {
if (std::is_floating_point<ValueType>::value ||
value < 0 || value > std::numeric_limits<uint8_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<uint8_t>>(
static_cast<uint8_t>(value));
return true;
}
case Kind::kUint16: {
if (std::is_floating_point<ValueType>::value ||
value < 0 || value > std::numeric_limits<uint16_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<uint16_t>>(
static_cast<uint16_t>(value));
return true;
}
case Kind::kUint32: {
if (std::is_floating_point<ValueType>::value ||
value < 0 || value > std::numeric_limits<uint32_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<uint32_t>>(
static_cast<uint32_t>(value));
return true;
}
case Kind::kUint64: {
if (std::is_floating_point<ValueType>::value ||
value < 0 || value > std::numeric_limits<uint64_t>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<uint64_t>>(
static_cast<uint64_t>(value));
return true;
}
case Kind::kFloat32: {
if (!std::is_floating_point<ValueType>::value ||
value < std::numeric_limits<float>::lowest() ||
value > std::numeric_limits<float>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<float>>(static_cast<float>(value));
return true;
}
case Kind::kFloat64: {
if (!std::is_floating_point<ValueType>::value ||
value < std::numeric_limits<double>::lowest() ||
value > std::numeric_limits<double>::max()) {
return false;
}
*out_value = std::make_unique<NumericConstantValue<double>>(static_cast<double>(value));
return true;
}
case Kind::kString:
case Kind::kBool:
return false;
}
}
static NumericConstantValue<ValueType> Min() {
return NumericConstantValue<ValueType>(std::numeric_limits<ValueType>::lowest());
}
static NumericConstantValue<ValueType> Max() {
return NumericConstantValue<ValueType>(std::numeric_limits<ValueType>::max());
}
ValueType value;
private:
constexpr static Kind GetKind() {
if constexpr (std::is_same_v<ValueType, uint64_t>)
return Kind::kUint64;
if constexpr (std::is_same_v<ValueType, int64_t>)
return Kind::kInt64;
if constexpr (std::is_same_v<ValueType, uint32_t>)
return Kind::kUint32;
if constexpr (std::is_same_v<ValueType, int32_t>)
return Kind::kInt32;
if constexpr (std::is_same_v<ValueType, uint16_t>)
return Kind::kUint16;
if constexpr (std::is_same_v<ValueType, int16_t>)
return Kind::kInt16;
if constexpr (std::is_same_v<ValueType, uint8_t>)
return Kind::kUint8;
if constexpr (std::is_same_v<ValueType, int8_t>)
return Kind::kInt8;
if constexpr (std::is_same_v<ValueType, double>)
return Kind::kFloat64;
if constexpr (std::is_same_v<ValueType, float>)
return Kind::kFloat32;
}
};
using Size = NumericConstantValue<uint32_t>;
struct BoolConstantValue : ConstantValue {
BoolConstantValue(bool value)
: ConstantValue(ConstantValue::Kind::kBool), value(value) {}
operator bool() const { return value; }
friend bool operator==(const BoolConstantValue& l, const BoolConstantValue& r) {
return l.value == r.value;
}
friend bool operator!=(const BoolConstantValue& l, const BoolConstantValue& r) {
return l.value != r.value;
}
friend std::ostream& operator<<(std::ostream& os, const BoolConstantValue& v) {
os << v.value;
return os;
}
virtual bool Convert(Kind kind, std::unique_ptr<ConstantValue>* out_value) const override {
assert(out_value != nullptr);
switch (kind) {
case Kind::kBool:
*out_value = std::make_unique<BoolConstantValue>(value);
return true;
default:
return false;
}
}
bool value;
};
struct StringConstantValue : ConstantValue {
explicit StringConstantValue(StringView value)
: ConstantValue(ConstantValue::Kind::kString), value(value) {}
friend std::ostream& operator<<(std::ostream& os, const StringConstantValue& v) {
os << v.value.data();
return os;
}
virtual bool Convert(Kind kind, std::unique_ptr<ConstantValue>* out_value) const override {
assert(out_value != nullptr);
switch (kind) {
case Kind::kString:
*out_value = std::make_unique<StringConstantValue>(StringView(value));
return true;
default:
return false;
}
}
StringView value;
};
struct Constant {
virtual ~Constant() {}
enum struct Kind {
kIdentifier,
kLiteral,
kSynthesized,
};
explicit Constant(Kind kind)
: kind(kind), value_(nullptr) {}
bool IsResolved() const { return value_ != nullptr; }
void ResolveTo(std::unique_ptr<ConstantValue> value) {
assert(value != nullptr);
assert(!IsResolved() && "Constants should only be resolved once!");
value_ = std::move(value);
}
const ConstantValue& Value() const {
assert(IsResolved() && "Accessing the value of an unresolved Constant!");
return *value_;
}
const Kind kind;
protected:
std::unique_ptr<ConstantValue> value_;
};
struct IdentifierConstant : Constant {
explicit IdentifierConstant(Name name)
: Constant(Kind::kIdentifier), name(std::move(name)) {}
Name name;
};
struct LiteralConstant : Constant {
explicit LiteralConstant(std::unique_ptr<raw::Literal> literal)
: Constant(Kind::kLiteral), literal(std::move(literal)) {}
std::unique_ptr<raw::Literal> literal;
};
struct SynthesizedConstant : Constant {
explicit SynthesizedConstant(std::unique_ptr<ConstantValue> value)
: Constant(Kind::kSynthesized) {
ResolveTo(std::move(value));
}
};
struct Decl {
virtual ~Decl() {}
enum struct Kind {
kBits,
kConst,
kEnum,
kInterface,
kStruct,
kTable,
kUnion,
kXUnion,
};
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(fidl::StringView name) const;
fidl::StringView GetAttribute(fidl::StringView name) const;
std::string GetName() const;
bool compiling = false;
bool compiled = false;
};
struct TypeDecl : public Decl {
TypeDecl(Kind kind, std::unique_ptr<raw::AttributeList> attributes, Name name)
: Decl(kind, std::move(attributes), std::move(name)) {}
TypeShape typeshape;
bool recursive = false;
};
struct Type {
virtual ~Type() {}
enum struct Kind {
kArray,
kVector,
kString,
kHandle,
kRequestHandle,
kPrimitive,
kIdentifier,
};
explicit Type(Kind kind, types::Nullability nullability, TypeShape shape)
: kind(kind), nullability(nullability), shape(shape) {}
const Kind kind;
const types::Nullability nullability;
TypeShape shape;
// Comparison helper object.
class Comparison {
public:
Comparison() = default;
template <class T>
Comparison Compare(const T& a, const T& b) const {
if (result_ != 0)
return Comparison(result_);
if (a < b)
return Comparison(-1);
if (b < a)
return Comparison(1);
return Comparison(0);
}
bool IsLessThan() const {
return result_ < 0;
}
private:
Comparison(int result)
: result_(result) {}
const int result_ = 0;
};
bool operator<(const Type& other) const {
if (kind != other.kind)
return kind < other.kind;
return Compare(other).IsLessThan();
}
// Compare this object against 'other'.
// It's guaranteed that this->kind == other.kind.
// Return <0 if *this < other, ==0 if *this == other, and >0 if *this > other.
// Derived types should override this, but also call this implementation.
virtual Comparison Compare(const Type& other) const {
assert(kind == other.kind);
return Comparison()
.Compare(nullability, other.nullability);
}
};
struct ArrayType : public Type {
ArrayType(const Type* element_type, const Size* element_count)
: Type(
Kind::kArray,
types::Nullability::kNonnullable,
Shape(element_type->shape, element_count->value)),
element_type(element_type), element_count(element_count) {}
const Type* element_type;
const Size* element_count;
Comparison Compare(const Type& other) const override {
const auto& o = static_cast<const ArrayType&>(other);
return Type::Compare(o)
.Compare(element_count->value, o.element_count->value)
.Compare(*element_type, *o.element_type);
}
static TypeShape Shape(TypeShape element, uint32_t count);
};
struct VectorType : public Type {
VectorType(const Type* element_type, const Size* element_count, types::Nullability nullability)
: Type(Kind::kVector, nullability, Shape(element_type->shape, element_count->value)),
element_type(element_type), element_count(element_count) {}
const Type* element_type;
const Size* element_count;
Comparison Compare(const Type& other) const override {
const auto& o = static_cast<const VectorType&>(other);
return Type::Compare(o)
.Compare(element_count->value, o.element_count->value)
.Compare(*element_type, *o.element_type);
}
static TypeShape Shape(TypeShape element, uint32_t max_element_count);
};
struct StringType : public Type {
StringType(const Size* max_size, types::Nullability nullability)
: Type(Kind::kString, nullability, Shape(max_size->value)), max_size(max_size) {}
const Size* max_size;
Comparison Compare(const Type& other) const override {
const auto& o = static_cast<const StringType&>(other);
return Type::Compare(o)
.Compare(max_size->value, o.max_size->value);
}
static TypeShape Shape(uint32_t max_length);
};
struct HandleType : public Type {
HandleType(types::HandleSubtype subtype, types::Nullability nullability)
: Type(Kind::kHandle, nullability, Shape()),
subtype(subtype) {}
const types::HandleSubtype subtype;
Comparison Compare(const Type& other) const override {
const auto& o = *static_cast<const HandleType*>(&other);
return Type::Compare(o)
.Compare(subtype, o.subtype);
}
static TypeShape Shape();
};
struct PrimitiveType : public Type {
explicit PrimitiveType(types::PrimitiveSubtype subtype)
: Type(
Kind::kPrimitive,
types::Nullability::kNonnullable,
Shape(subtype)),
subtype(subtype) {}
types::PrimitiveSubtype subtype;
Comparison Compare(const Type& other) const override {
const auto& o = static_cast<const PrimitiveType&>(other);
return Type::Compare(o)
.Compare(subtype, o.subtype);
}
static TypeShape Shape(types::PrimitiveSubtype subtype);
static uint32_t SubtypeSize(types::PrimitiveSubtype subtype);
};
struct IdentifierType : public Type {
IdentifierType(Name name, types::Nullability nullability, const TypeDecl* type_decl, TypeShape shape)
: Type(Kind::kIdentifier, nullability, shape),
name(std::move(name)), type_decl(type_decl) {}
Name name;
const TypeDecl* type_decl;
Comparison Compare(const Type& other) const override {
const auto& o = static_cast<const IdentifierType&>(other);
return Type::Compare(o)
.Compare(name, o.name);
}
};
struct RequestHandleType : public Type {
RequestHandleType(const IdentifierType* interface_type, types::Nullability nullability)
: Type(Kind::kRequestHandle, nullability, HandleType::Shape()),
interface_type(interface_type) {}
const IdentifierType* interface_type;
Comparison Compare(const Type& other) const override {
const auto& o = static_cast<const RequestHandleType&>(other);
return Type::Compare(o)
.Compare(*interface_type, *o.interface_type);
}
};
struct TypeConstructor {
TypeConstructor(Name name, std::unique_ptr<TypeConstructor> maybe_arg_type_ctor,
std::unique_ptr<types::HandleSubtype> maybe_handle_subtype,
std::unique_ptr<Constant> maybe_size, types::Nullability nullability)
: name(std::move(name)), maybe_arg_type_ctor(std::move(maybe_arg_type_ctor)),
maybe_handle_subtype(std::move(maybe_handle_subtype)),
maybe_size(std::move(maybe_size)), nullability(nullability) {}
// Set during construction.
const Name name;
const std::unique_ptr<TypeConstructor> maybe_arg_type_ctor;
const std::unique_ptr<types::HandleSubtype> maybe_handle_subtype;
const std::unique_ptr<Constant> maybe_size;
const types::Nullability nullability;
// Set during compilation.
bool compiling = false;
bool compiled = false;
const Type* type = nullptr;
};
struct Using {
Using(Name name, const PrimitiveType* type)
: name(std::move(name)), type(type) {}
const Name name;
const PrimitiveType* type;
};
struct Const : public Decl {
Const(std::unique_ptr<raw::AttributeList> attributes, Name name,
std::unique_ptr<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)) {}
std::unique_ptr<TypeConstructor> type_ctor;
std::unique_ptr<Constant> value;
};
struct Enum : public TypeDecl {
struct Member {
Member(SourceLocation name, std::unique_ptr<Constant> value, std::unique_ptr<raw::AttributeList> attributes)
: name(name), value(std::move(value)), attributes(std::move(attributes)) {}
SourceLocation name;
std::unique_ptr<Constant> value;
std::unique_ptr<raw::AttributeList> attributes;
};
Enum(std::unique_ptr<raw::AttributeList> attributes, Name name,
std::unique_ptr<TypeConstructor> subtype_ctor,
std::vector<Member> members)
: TypeDecl(Kind::kEnum, std::move(attributes), std::move(name)),
subtype_ctor(std::move(subtype_ctor)),
members(std::move(members)) {}
// Set during construction.
std::unique_ptr<TypeConstructor> subtype_ctor;
std::vector<Member> members;
// Set during compilation.
const PrimitiveType* type = nullptr;
};
struct Bits : public TypeDecl {
struct Member {
Member(SourceLocation name, std::unique_ptr<Constant> value, std::unique_ptr<raw::AttributeList> attributes)
: name(name), value(std::move(value)), attributes(std::move(attributes)) {}
SourceLocation name;
std::unique_ptr<Constant> value;
std::unique_ptr<raw::AttributeList> attributes;
};
Bits(std::unique_ptr<raw::AttributeList> attributes, Name name,
std::unique_ptr<TypeConstructor> subtype_ctor,
std::vector<Member> members)
: TypeDecl(Kind::kBits, std::move(attributes), std::move(name)),
subtype_ctor(std::move(subtype_ctor)),
members(std::move(members)) {}
std::unique_ptr<TypeConstructor> subtype_ctor;
std::vector<Member> members;
};
struct Struct : public TypeDecl {
struct Member {
Member(std::unique_ptr<TypeConstructor> type_ctor, SourceLocation 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)) {}
std::unique_ptr<TypeConstructor> type_ctor;
SourceLocation name;
std::unique_ptr<Constant> maybe_default_value;
std::unique_ptr<raw::AttributeList> attributes;
FieldShape fieldshape;
};
Struct(std::unique_ptr<raw::AttributeList> attributes, Name name,
std::vector<Member> members, bool anonymous = false)
: TypeDecl(Kind::kStruct, std::move(attributes), std::move(name)),
members(std::move(members)), anonymous(anonymous) {
}
std::vector<Member> members;
const bool anonymous;
static TypeShape Shape(std::vector<FieldShape*>* fields, uint32_t extra_handles = 0u);
};
struct Table : public TypeDecl {
struct Member {
Member(std::unique_ptr<raw::Ordinal> ordinal, std::unique_ptr<TypeConstructor> type,
SourceLocation 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), std::move(name),
std::move(maybe_default_value),
std::move(attributes))) {}
Member(std::unique_ptr<raw::Ordinal> ordinal, SourceLocation location)
: ordinal(std::move(ordinal)),
maybe_location(std::make_unique<SourceLocation>(location)) {}
std::unique_ptr<raw::Ordinal> ordinal;
// The location for reserved table members.
std::unique_ptr<SourceLocation> maybe_location;
struct Used {
Used(std::unique_ptr<TypeConstructor> type_ctor, SourceLocation 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)) {}
std::unique_ptr<TypeConstructor> type_ctor;
SourceLocation name;
std::unique_ptr<Constant> maybe_default_value;
std::unique_ptr<raw::AttributeList> attributes;
TypeShape typeshape;
};
std::unique_ptr<Used> maybe_used;
};
Table(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members)
: TypeDecl(Kind::kTable, std::move(attributes), std::move(name)),
members(std::move(members)) {}
std::vector<Member> members;
static TypeShape Shape(std::vector<TypeShape*>* fields, uint32_t extra_handles = 0u);
};
struct Union : public TypeDecl {
struct Member {
Member(std::unique_ptr<TypeConstructor> type_ctor, SourceLocation name,
std::unique_ptr<raw::AttributeList> attributes)
: type_ctor(std::move(type_ctor)), name(std::move(name)),
attributes(std::move(attributes)) {}
std::unique_ptr<TypeConstructor> type_ctor;
SourceLocation name;
std::unique_ptr<raw::AttributeList> attributes;
FieldShape fieldshape;
};
Union(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members)
: TypeDecl(Kind::kUnion, std::move(attributes), std::move(name)),
members(std::move(members)) {}
std::vector<Member> members;
// The offset of each of the union members is the same, so store
// it here as well.
FieldShape membershape;
static TypeShape Shape(const std::vector<flat::Union::Member>& members);
};
struct XUnion : public TypeDecl {
struct Member {
Member(std::unique_ptr<raw::Ordinal> ordinal, std::unique_ptr<TypeConstructor> type_ctor,
SourceLocation name, std::unique_ptr<raw::AttributeList> attributes)
: ordinal(std::move(ordinal)), type_ctor(std::move(type_ctor)), name(std::move(name)),
attributes(std::move(attributes)) {}
std::unique_ptr<raw::Ordinal> ordinal;
std::unique_ptr<TypeConstructor> type_ctor;
SourceLocation name;
std::unique_ptr<raw::AttributeList> attributes;
FieldShape fieldshape;
};
XUnion(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members)
: TypeDecl(Kind::kXUnion, std::move(attributes), std::move(name)), members(std::move(members)) {}
std::vector<Member> members;
static TypeShape Shape(const std::vector<flat::XUnion::Member>& members, uint32_t extra_handles = 0u);
};
struct Interface : public TypeDecl {
struct Method {
Method(Method&&) = default;
Method& operator=(Method&&) = default;
Method(std::unique_ptr<raw::AttributeList> attributes,
std::unique_ptr<raw::Ordinal> ordinal,
std::unique_ptr<raw::Ordinal> generated_ordinal,
SourceLocation name,
Struct* maybe_request,
Struct* maybe_response)
: attributes(std::move(attributes)),
ordinal(std::move(ordinal)),
generated_ordinal(std::move(generated_ordinal)),
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::Ordinal> ordinal;
std::unique_ptr<raw::Ordinal> generated_ordinal;
SourceLocation name;
Struct* maybe_request;
Struct* maybe_response;
};
Interface(std::unique_ptr<raw::AttributeList> attributes, Name name,
std::set<Name> superinterfaces, std::vector<Method> methods)
: TypeDecl(Kind::kInterface, std::move(attributes), std::move(name)),
superinterfaces(std::move(superinterfaces)), methods(std::move(methods)) {}
std::set<Name> superinterfaces;
std::vector<Method> methods;
// Pointers here are set after superinterfaces are compiled, and
// are owned by the correspending superinterface.
std::vector<const Method*> all_methods;
};
class TypeTemplate {
public:
TypeTemplate(Name name, Typespace* typespace, ErrorReporter* error_reporter)
: typespace_(typespace), name_(std::move(name)), error_reporter_(error_reporter) {}
TypeTemplate(TypeTemplate&& type_template) = default;
virtual ~TypeTemplate() = default;
const Name* name() const { return &name_; }
virtual bool Create(const SourceLocation* maybe_location,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const = 0;
protected:
bool MustBeParameterized(const SourceLocation* maybe_location) const {
return Fail(maybe_location, "must be parametrized");
}
bool MustHaveSize(const SourceLocation* maybe_location) const {
return Fail(maybe_location, "must have size");
}
bool CannotBeParameterized(const SourceLocation* maybe_location) const {
return Fail(maybe_location, "cannot be parametrized");
}
bool CannotHaveSize(const SourceLocation* maybe_location) const {
return Fail(maybe_location, "cannot have size");
}
bool CannotBeNullable(const SourceLocation* maybe_location) const {
return Fail(maybe_location, "cannot be nullable");
}
bool Fail(const SourceLocation* maybe_location, const std::string& content) const;
Typespace* typespace_;
private:
Name name_;
ErrorReporter* error_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(ErrorReporter* error_reporter)
: error_reporter_(error_reporter) {}
bool Create(const flat::Name& name,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
const Type** out_type);
void AddTemplate(std::unique_ptr<TypeTemplate> type_template);
// BoostrapRootTypes 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(ErrorReporter* error_reporter);
private:
friend class TypeAliasTypeTemplate;
bool CreateNotOwned(const flat::Name& name,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type);
const TypeTemplate* LookupTemplate(const flat::Name& name) const;
struct cmpName {
bool operator()(const flat::Name* a, const flat::Name* b) const {
return *a < *b;
}
};
std::map<const flat::Name*, std::unique_ptr<TypeTemplate>, cmpName> templates_;
std::vector<std::unique_ptr<Type>> types_;
ErrorReporter* error_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. interface, 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(ErrorReporter* error_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,
kInterfaceDecl,
kLibrary,
kMethod,
kStructDecl,
kStructMember,
kTableDecl,
kTableMember,
kUnionDecl,
kUnionMember,
kXUnionDecl,
kXUnionMember,
};
AttributeSchema(const std::set<Placement>& allowed_placements,
const std::set<std::string> allowed_values,
Constraint constraint = NoOpConstraint);
AttributeSchema(AttributeSchema&& schema) = default;
void ValidatePlacement(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
Placement placement) const;
void ValidateValue(ErrorReporter* error_reporter,
const raw::Attribute& attribute) const;
void ValidateConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) const;
private:
static bool NoOpConstraint(ErrorReporter* error_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<StringView>& 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(ErrorReporter* error_reporter,
const raw::Attribute& attribute) const;
private:
std::map<std::vector<StringView>, 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(StringView filename, Library* dep_library,
const std::unique_ptr<raw::Identifier>& maybe_alias);
// Lookup a dependent library by |filename| and |name|.
bool Lookup(StringView filename, const std::vector<StringView>& name,
Library** out_library);
const std::set<Library*>& dependencies() const { return dependencies_aggregate_; }
private:
bool InsertByName(StringView filename, const std::vector<StringView>& name,
Library* library);
using ByName = std::map<std::vector<StringView>, Library*>;
using ByFilename = std::map<std::string, std::unique_ptr<ByName>>;
ByFilename dependencies_;
std::set<Library*> dependencies_aggregate_;
};
class Library {
public:
Library(const Libraries* all_libraries, ErrorReporter* error_reporter, Typespace* typespace)
: all_libraries_(all_libraries), error_reporter_(error_reporter), typespace_(typespace) {}
bool ConsumeFile(std::unique_ptr<raw::File> file);
bool Compile();
const std::vector<StringView>& name() const { return library_name_; }
const std::vector<std::string>& errors() const { return error_reporter_->errors(); }
private:
friend class TypeAliasTypeTemplate;
bool Fail(StringView message);
bool Fail(const SourceLocation& location, StringView message) {
return Fail(&location, message);
}
bool Fail(const SourceLocation* maybe_location, StringView message);
bool Fail(const Name& name, StringView message) {
return Fail(name.maybe_location(), message);
}
bool Fail(const Decl& decl, StringView message) { return Fail(decl.name, message); }
void ValidateAttributesPlacement(AttributeSchema::Placement placement,
const raw::AttributeList* attributes);
void ValidateAttributesConstraints(const Decl* decl,
const raw::AttributeList* attributes);
// TODO(FIDL-596): 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.
SourceLocation GeneratedSimpleName(const std::string& name);
Name NextAnonymousName();
Name DerivedName(const std::vector<StringView>& components);
bool CompileCompoundIdentifier(const raw::CompoundIdentifier* compound_identifier,
SourceLocation location, Name* out_name);
void RegisterConst(Const* decl);
bool RegisterDecl(Decl* decl);
bool ConsumeConstant(std::unique_ptr<raw::Constant> raw_constant, SourceLocation location,
std::unique_ptr<Constant>* out_constant);
bool ConsumeTypeConstructor(std::unique_ptr<raw::TypeConstructor> raw_type_ctor,
SourceLocation location,
std::unique_ptr<TypeConstructor>* out_type);
bool ConsumeUsing(std::unique_ptr<raw::Using> using_directive);
bool ConsumeTypeAlias(std::unique_ptr<raw::Using> using_directive);
bool ConsumeBitsDeclaration(std::unique_ptr<raw::BitsDeclaration> bits_declaration);
bool ConsumeConstDeclaration(std::unique_ptr<raw::ConstDeclaration> const_declaration);
bool ConsumeEnumDeclaration(std::unique_ptr<raw::EnumDeclaration> enum_declaration);
bool
ConsumeInterfaceDeclaration(std::unique_ptr<raw::InterfaceDeclaration> interface_declaration);
bool ConsumeParameterList(Name name, std::unique_ptr<raw::ParameterList> parameter_list,
bool anonymous, Struct** out_struct_decl);
bool CreateMethodResult(const Name& interface_name, raw::InterfaceMethod* method, Struct* in_response, Struct** out_response);
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);
bool TypeIsConvertibleTo(const Type* from_type, const Type* to_type);
std::unique_ptr<TypeConstructor> IdentifierTypeForDecl(const Decl* decl, types::Nullability nullability);
// Given a const declaration of the form
// const type foo = name;
// return the declaration corresponding to name.
Decl* LookupConstant(const TypeConstructor* type_ctor, const Name& name);
bool DeclDependencies(Decl* decl, std::set<Decl*>* out_edges);
bool SortDeclarations();
bool CompileLibraryName();
bool CompileBits(Bits* bits_declaration);
bool CompileConst(Const* const_declaration);
bool CompileEnum(Enum* enum_declaration);
bool CompileInterface(Interface* interface_declaration);
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
// optional identifier types refer to things that can in fact be
// nullable (ie not enums).
bool CompileTypeConstructor(TypeConstructor* type, TypeShape* out_type_metadata);
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 failure,
// returns false and places an error message in the out parameter.
template <typename MemberType>
using MemberValidator = fit::function<bool(const MemberType& member, std::string* out_error)>;
template <typename DeclType, typename MemberType>
bool ValidateMembers(DeclType* decl, MemberValidator<MemberType> validator);
template <typename MemberType>
bool ValidateBitsMembers(Bits* bits_decl);
template <typename MemberType>
bool ValidateEnumMembers(Enum* enum_decl);
bool VerifyDeclAttributes(Decl* decl);
public:
bool CompileDecl(Decl* decl);
// Returns nullptr when the |name| cannot be resolved to a
// Name. Otherwise it returns the declaration.
Decl* LookupDeclByName(const Name& name) const;
template <typename NumericType>
bool ParseNumericLiteral(const raw::NumericLiteral* literal, NumericType* out_value) const;
bool HasAttribute(fidl::StringView name) const;
const std::set<Library*>& dependencies() const;
std::vector<StringView> 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<Interface>> interface_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<XUnion>> xunion_declarations_;
// All Decl pointers here are non-null and are owned by the
// various foo_declarations_.
std::vector<Decl*> declaration_order_;
private:
const PrimitiveType kSizeType = PrimitiveType(types::PrimitiveSubtype::kUint32);
std::unique_ptr<raw::AttributeList> attributes_;
Dependencies dependencies_;
const Libraries* all_libraries_;
// All Name, Constant, Using, and Decl pointers here are non-null and are
// owned by the various foo_declarations_.
std::map<const Name*, Decl*, PtrCompare<Name>> declarations_;
std::map<const Name*, Const*, PtrCompare<Name>> constants_;
ErrorReporter* error_reporter_;
Typespace* typespace_;
uint32_t anon_counter_ = 0;
VirtualSourceFile generated_source_file_{"generated"};
};
} // namespace flat
} // namespace fidl
// TODO(FIDL-487, ZX-3415): Decide if all cases of NumericConstantValue::Convert() are safe.
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_FLAT_AST_H_