blob: 1d9543a80d62aeed6b07b5f9e398a3afbe16f2a0 [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.
#include "tools/fidl/fidlc/src/flat_ast.h"
#include <zircon/assert.h>
#include "tools/fidl/fidlc/src/diagnostics.h"
#include "tools/fidl/fidlc/src/reporter.h"
namespace fidlc {
bool Element::IsDecl() const {
switch (kind) {
case Kind::kBits:
case Kind::kBuiltin:
case Kind::kConst:
case Kind::kEnum:
case Kind::kProtocol:
case Kind::kResource:
case Kind::kService:
case Kind::kStruct:
case Kind::kTable:
case Kind::kAlias:
case Kind::kUnion:
case Kind::kNewType:
case Kind::kOverlay:
return true;
case Kind::kLibrary:
case Kind::kBitsMember:
case Kind::kEnumMember:
case Kind::kProtocolCompose:
case Kind::kProtocolMethod:
case Kind::kResourceProperty:
case Kind::kServiceMember:
case Kind::kStructMember:
case Kind::kTableMember:
case Kind::kUnionMember:
case Kind::kOverlayMember:
return false;
}
}
Decl* Element::AsDecl() {
ZX_ASSERT(IsDecl());
return static_cast<Decl*>(this);
}
bool Element::IsAnonymousLayout() const {
switch (kind) {
case Element::Kind::kBits:
case Element::Kind::kEnum:
case Element::Kind::kStruct:
case Element::Kind::kTable:
case Element::Kind::kUnion:
case Element::Kind::kOverlay:
return static_cast<const Decl*>(this)->name.as_anonymous() != nullptr;
default:
return false;
}
}
std::string_view Element::GetName() const {
switch (kind) {
case Kind::kLibrary:
ZX_PANIC("should not call GetName() on a library element");
case Kind::kBits:
case Kind::kBuiltin:
case Kind::kConst:
case Kind::kEnum:
case Kind::kProtocol:
case Kind::kResource:
case Kind::kService:
case Kind::kStruct:
case Kind::kTable:
case Kind::kAlias:
case Kind::kUnion:
case Kind::kNewType:
case Kind::kOverlay:
return static_cast<const Decl*>(this)->name.decl_name();
case Kind::kBitsMember:
return static_cast<const Bits::Member*>(this)->name.data();
case Kind::kEnumMember:
return static_cast<const Enum::Member*>(this)->name.data();
case Kind::kProtocolCompose:
return static_cast<const Protocol::ComposedProtocol*>(this)->reference.span().data();
case Kind::kProtocolMethod:
return static_cast<const Protocol::Method*>(this)->name.data();
case Kind::kResourceProperty:
return static_cast<const Resource::Property*>(this)->name.data();
case Kind::kServiceMember:
return static_cast<const Service::Member*>(this)->name.data();
case Kind::kStructMember:
return static_cast<const Struct::Member*>(this)->name.data();
case Kind::kTableMember:
return static_cast<const Table::Member*>(this)->name.data();
case Kind::kUnionMember:
return static_cast<const Union::Member*>(this)->name.data();
case Kind::kOverlayMember:
return static_cast<const Overlay::Member*>(this)->name.data();
}
}
SourceSpan Element::GetNameSource() const {
switch (kind) {
case Kind::kLibrary:
ZX_PANIC("should not call GetName() on a library element");
case Kind::kBits:
case Kind::kBuiltin:
case Kind::kConst:
case Kind::kEnum:
case Kind::kProtocol:
case Kind::kResource:
case Kind::kService:
case Kind::kStruct:
case Kind::kTable:
case Kind::kAlias:
case Kind::kUnion:
case Kind::kNewType:
case Kind::kOverlay:
return static_cast<const Decl*>(this)->name.span().value();
case Kind::kBitsMember:
return static_cast<const Bits::Member*>(this)->name;
case Kind::kEnumMember:
return static_cast<const Enum::Member*>(this)->name;
case Kind::kProtocolCompose:
return static_cast<const Protocol::ComposedProtocol*>(this)->reference.span();
case Kind::kProtocolMethod:
return static_cast<const Protocol::Method*>(this)->name;
case Kind::kResourceProperty:
return static_cast<const Resource::Property*>(this)->name;
case Kind::kServiceMember:
return static_cast<const Service::Member*>(this)->name;
case Kind::kStructMember:
return static_cast<const Struct::Member*>(this)->name;
case Kind::kTableMember:
return static_cast<const Table::Member*>(this)->name;
case Kind::kUnionMember:
return static_cast<const Union::Member*>(this)->name;
case Kind::kOverlayMember:
return static_cast<const Overlay::Member*>(this)->name;
}
}
bool Builtin::IsInternal() const {
switch (id) {
case Identity::kBool:
case Identity::kInt8:
case Identity::kInt16:
case Identity::kInt32:
case Identity::kInt64:
case Identity::kUint8:
case Identity::kZxUchar:
case Identity::kUint16:
case Identity::kUint32:
case Identity::kUint64:
case Identity::kZxUsize64:
case Identity::kZxUintptr64:
case Identity::kFloat32:
case Identity::kFloat64:
case Identity::kString:
case Identity::kBox:
case Identity::kArray:
case Identity::kStringArray:
case Identity::kVector:
case Identity::kZxExperimentalPointer:
case Identity::kClientEnd:
case Identity::kServerEnd:
case Identity::kByte:
case Identity::kOptional:
case Identity::kMax:
case Identity::kHead:
return false;
case Identity::kFrameworkErr:
return true;
}
}
Resource::Property* Resource::LookupProperty(std::string_view name) {
for (Property& property : properties) {
if (property.name.data() == name.data()) {
return &property;
}
}
return nullptr;
}
Dependencies::RegisterResult Dependencies::Register(
const SourceSpan& span, std::string_view filename, Library* dep_library,
const std::unique_ptr<RawIdentifier>& maybe_alias) {
refs_.push_back(std::make_unique<LibraryRef>(span, dep_library));
LibraryRef* ref = refs_.back().get();
const std::vector<std::string_view> name =
maybe_alias ? std::vector{maybe_alias->span().data()} : dep_library->name;
auto iter = by_filename_.find(filename);
if (iter == by_filename_.end()) {
iter = by_filename_.emplace(filename, std::make_unique<PerFile>()).first;
}
PerFile& per_file = *iter->second;
if (!per_file.libraries.insert(dep_library).second) {
return RegisterResult::kDuplicate;
}
if (!per_file.refs.emplace(name, ref).second) {
return RegisterResult::kCollision;
}
dependencies_aggregate_.insert(dep_library);
return RegisterResult::kSuccess;
}
bool Dependencies::Contains(std::string_view filename, const std::vector<std::string_view>& name) {
const auto iter = by_filename_.find(filename);
if (iter == by_filename_.end()) {
return false;
}
const PerFile& per_file = *iter->second;
return per_file.refs.find(name) != per_file.refs.end();
}
Library* Dependencies::LookupAndMarkUsed(std::string_view filename,
const std::vector<std::string_view>& name) const {
auto iter1 = by_filename_.find(filename);
if (iter1 == by_filename_.end()) {
return nullptr;
}
auto iter2 = iter1->second->refs.find(name);
if (iter2 == iter1->second->refs.end()) {
return nullptr;
}
auto ref = iter2->second;
ref->used = true;
return ref->library;
}
void Dependencies::VerifyAllDependenciesWereUsed(const Library& for_library, Reporter* reporter) {
for (const auto& [filename, per_file] : by_filename_) {
for (const auto& [name, ref] : per_file->refs) {
if (!ref->used) {
reporter->Fail(ErrUnusedImport, ref->span, for_library.name, ref->library->name);
}
}
}
}
std::string LibraryName(const std::vector<std::string_view>& components,
std::string_view separator) {
return StringJoin(components, separator);
}
// static
std::unique_ptr<Library> Library::CreateRootLibrary() {
// TODO(https://fxbug.dev/42146818): Because this library doesn't get compiled, we have
// to simulate what AvailabilityStep would do (set the platform, inherit the
// availabilities). Perhaps we could make the root library less special and
// compile it as well. That would require addressing circularity issues.
auto library = std::make_unique<Library>();
library->name = {"fidl"};
library->platform = Platform::Unversioned();
library->availability.Init({.added = Version::Head()});
library->availability.Inherit(Availability::Unbounded());
auto insert = [&](const char* name, Builtin::Identity id) {
auto decl = std::make_unique<Builtin>(id, Name::CreateIntrinsic(library.get(), name));
decl->availability.Init({});
decl->availability.Inherit(library->availability);
library->declarations.Insert(std::move(decl));
};
// An assertion in Declarations::Insert ensures that these insertions
// stays in sync with the order of Builtin::Identity.
insert("bool", Builtin::Identity::kBool);
insert("int8", Builtin::Identity::kInt8);
insert("int16", Builtin::Identity::kInt16);
insert("int32", Builtin::Identity::kInt32);
insert("int64", Builtin::Identity::kInt64);
insert("uint8", Builtin::Identity::kUint8);
insert("uchar", Builtin::Identity::kZxUchar);
insert("uint16", Builtin::Identity::kUint16);
insert("uint32", Builtin::Identity::kUint32);
insert("uint64", Builtin::Identity::kUint64);
insert("usize64", Builtin::Identity::kZxUsize64);
insert("uintptr64", Builtin::Identity::kZxUintptr64);
insert("float32", Builtin::Identity::kFloat32);
insert("float64", Builtin::Identity::kFloat64);
insert("string", Builtin::Identity::kString);
insert("box", Builtin::Identity::kBox);
insert("array", Builtin::Identity::kArray);
insert("string_array", Builtin::Identity::kStringArray);
insert("vector", Builtin::Identity::kVector);
insert("experimental_pointer", Builtin::Identity::kZxExperimentalPointer);
insert("client_end", Builtin::Identity::kClientEnd);
insert("server_end", Builtin::Identity::kServerEnd);
insert("byte", Builtin::Identity::kByte);
insert("FrameworkErr", Builtin::Identity::kFrameworkErr);
insert("optional", Builtin::Identity::kOptional);
insert("MAX", Builtin::Identity::kMax);
insert("HEAD", Builtin::Identity::kHead);
// Simulate narrowing availabilities to maintain the invariant that they
// always reach kNarrowed (except for the availability of `library`).
library->TraverseElements([](Element* element) {
element->availability.Narrow(VersionRange(Version::Head(), Version::PosInf()));
});
return library;
}
void Library::TraverseElements(const fit::function<void(Element*)>& fn) {
fn(this);
for (auto& [name, decl] : declarations.all) {
fn(decl);
decl->ForEachMember(fn);
}
}
void Decl::ForEachMember(const fit::function<void(Element*)>& fn) {
switch (kind) {
case Decl::Kind::kBuiltin:
case Decl::Kind::kConst:
case Decl::Kind::kAlias:
case Decl::Kind::kNewType:
break;
case Decl::Kind::kBits:
for (auto& member : static_cast<Bits*>(this)->members) {
fn(&member);
}
break;
case Decl::Kind::kEnum:
for (auto& member : static_cast<Enum*>(this)->members) {
fn(&member);
}
break;
case Decl::Kind::kProtocol:
for (auto& composed_protocol : static_cast<Protocol*>(this)->composed_protocols) {
fn(&composed_protocol);
}
for (auto& method : static_cast<Protocol*>(this)->methods) {
fn(&method);
}
break;
case Decl::Kind::kResource:
for (auto& member : static_cast<Resource*>(this)->properties) {
fn(&member);
}
break;
case Decl::Kind::kService:
for (auto& member : static_cast<Service*>(this)->members) {
fn(&member);
}
break;
case Decl::Kind::kStruct:
for (auto& member : static_cast<Struct*>(this)->members) {
fn(&member);
}
break;
case Decl::Kind::kTable:
for (auto& member : static_cast<Table*>(this)->members) {
fn(&member);
}
break;
case Decl::Kind::kUnion:
for (auto& member : static_cast<Union*>(this)->members) {
fn(&member);
}
break;
case Decl::Kind::kOverlay:
for (auto& member : static_cast<Overlay*>(this)->members) {
fn(&member);
}
break;
} // switch
}
template <typename T>
static T* StoreDecl(std::unique_ptr<Decl> decl, std::multimap<std::string_view, Decl*>* all,
std::vector<std::unique_ptr<T>>* declarations) {
auto ptr = static_cast<T*>(decl.release());
all->emplace(ptr->name.decl_name(), ptr);
declarations->emplace_back(ptr);
return ptr;
}
Decl* Library::Declarations::Insert(std::unique_ptr<Decl> decl) {
switch (decl->kind) {
case Decl::Kind::kBuiltin: {
auto index = static_cast<size_t>(static_cast<Builtin*>(decl.get())->id);
ZX_ASSERT_MSG(index == builtins.size(), "inserted builtin out of order");
return StoreDecl(std::move(decl), &all, &builtins);
}
case Decl::Kind::kBits:
return StoreDecl(std::move(decl), &all, &bits);
case Decl::Kind::kConst:
return StoreDecl(std::move(decl), &all, &consts);
case Decl::Kind::kEnum:
return StoreDecl(std::move(decl), &all, &enums);
case Decl::Kind::kProtocol:
return StoreDecl(std::move(decl), &all, &protocols);
case Decl::Kind::kResource:
return StoreDecl(std::move(decl), &all, &resources);
case Decl::Kind::kService:
return StoreDecl(std::move(decl), &all, &services);
case Decl::Kind::kStruct:
return StoreDecl(std::move(decl), &all, &structs);
case Decl::Kind::kTable:
return StoreDecl(std::move(decl), &all, &tables);
case Decl::Kind::kAlias:
return StoreDecl(std::move(decl), &all, &aliases);
case Decl::Kind::kUnion:
return StoreDecl(std::move(decl), &all, &unions);
case Decl::Kind::kOverlay:
return StoreDecl(std::move(decl), &all, &overlays);
case Decl::Kind::kNewType:
return StoreDecl(std::move(decl), &all, &new_types);
}
}
Builtin* Library::Declarations::LookupBuiltin(Builtin::Identity id) const {
auto index = static_cast<size_t>(id);
ZX_ASSERT_MSG(index < builtins.size(), "builtin id out of range");
auto builtin = builtins[index].get();
ZX_ASSERT_MSG(builtin->id == id, "builtin's id does not match index");
return builtin;
}
std::unique_ptr<TypeConstructor> TypeConstructor::Clone() const {
return std::make_unique<TypeConstructor>(span, layout, parameters->Clone(), constraints->Clone());
}
std::unique_ptr<LayoutParameterList> LayoutParameterList::Clone() const {
return std::make_unique<LayoutParameterList>(MapClone(items), span);
}
std::unique_ptr<TypeConstraints> TypeConstraints::Clone() const {
return std::make_unique<TypeConstraints>(MapClone(items), span);
}
TypeConstructor* LiteralLayoutParameter::AsTypeCtor() const { return nullptr; }
TypeConstructor* TypeLayoutParameter::AsTypeCtor() const { return type_ctor.get(); }
TypeConstructor* IdentifierLayoutParameter::AsTypeCtor() const { return as_type_ctor.get(); }
Constant* LiteralLayoutParameter::AsConstant() const { return literal.get(); }
Constant* TypeLayoutParameter::AsConstant() const { return nullptr; }
Constant* IdentifierLayoutParameter::AsConstant() const { return as_constant.get(); }
std::unique_ptr<LayoutParameter> LiteralLayoutParameter::Clone() const {
return std::make_unique<LiteralLayoutParameter>(literal->CloneLiteralConstant(), span);
}
std::unique_ptr<LayoutParameter> TypeLayoutParameter::Clone() const {
return std::make_unique<TypeLayoutParameter>(type_ctor->Clone(), span);
}
std::unique_ptr<LayoutParameter> IdentifierLayoutParameter::Clone() const {
ZX_ASSERT_MSG(!(as_constant || as_type_ctor), "Clone() is not allowed after Disambiguate()");
return std::make_unique<IdentifierLayoutParameter>(reference, span);
}
void IdentifierLayoutParameter::Disambiguate() {
switch (reference.resolved().element()->kind) {
case Element::Kind::kConst:
case Element::Kind::kBitsMember:
case Element::Kind::kEnumMember: {
as_constant = std::make_unique<IdentifierConstant>(reference, span);
break;
}
default: {
as_type_ctor = std::make_unique<TypeConstructor>(span, reference,
std::make_unique<LayoutParameterList>(),
std::make_unique<TypeConstraints>());
break;
}
}
}
std::unique_ptr<Decl> Decl::Split(VersionRange range) const {
auto decl = SplitImpl(range);
decl->availability = availability;
decl->availability.Narrow(range);
return decl;
}
// For a decl member type T that has a Copy() method, takes a vector<T> and
// returns a vector of copies filtered to only include those that intersect with
// the given range, and narrows their availabilities to that range.
template <typename T>
static std::vector<T> FilterMembers(const std::vector<T>& all, VersionRange range) {
std::vector<T> result;
for (auto& member : all) {
if (VersionSet::Intersect(VersionSet(range), member.availability.set())) {
result.push_back(member.Clone());
result.back().availability = member.availability;
result.back().availability.Narrow(range);
}
}
return result;
}
// Like FilterMembers, but for members with ordinals (table and union members).
// In addition to filtering, sorts the result by ordinal.
template <typename T>
static std::vector<T> FilterOrdinaledMembers(const std::vector<T>& all, VersionRange range) {
std::vector<T> result = FilterMembers(all, range);
std::sort(result.begin(), result.end(),
[](const T& lhs, const T& rhs) { return lhs.ordinal->value < rhs.ordinal->value; });
return result;
}
std::unique_ptr<Decl> Builtin::SplitImpl(VersionRange range) const {
ZX_PANIC("splitting builtins not allowed");
}
std::unique_ptr<Decl> Const::SplitImpl(VersionRange range) const {
return std::make_unique<Const>(attributes->Clone(), name, type_ctor->Clone(), value->Clone());
}
std::unique_ptr<Decl> Enum::SplitImpl(VersionRange range) const {
return std::make_unique<Enum>(attributes->Clone(), name, subtype_ctor->Clone(),
FilterMembers(members, range), strictness);
}
std::unique_ptr<Decl> Bits::SplitImpl(VersionRange range) const {
return std::make_unique<Bits>(attributes->Clone(), name, subtype_ctor->Clone(),
FilterMembers(members, range), strictness);
}
std::unique_ptr<Decl> Service::SplitImpl(VersionRange range) const {
return std::make_unique<Service>(attributes->Clone(), name, FilterMembers(members, range));
}
std::unique_ptr<Decl> Struct::SplitImpl(VersionRange range) const {
return std::make_unique<Struct>(attributes->Clone(), name, FilterMembers(members, range),
resourceness);
}
std::unique_ptr<Decl> Table::SplitImpl(VersionRange range) const {
return std::make_unique<Table>(attributes->Clone(), name, FilterOrdinaledMembers(members, range),
strictness, resourceness);
}
std::unique_ptr<Decl> Union::SplitImpl(VersionRange range) const {
return std::make_unique<Union>(attributes->Clone(), name, FilterOrdinaledMembers(members, range),
strictness, resourceness);
}
std::unique_ptr<Decl> Overlay::SplitImpl(VersionRange range) const {
return std::make_unique<Overlay>(
attributes->Clone(), name, FilterOrdinaledMembers(members, range), strictness, resourceness);
}
std::unique_ptr<Decl> Protocol::SplitImpl(VersionRange range) const {
return std::make_unique<Protocol>(attributes->Clone(), openness, name,
FilterMembers(composed_protocols, range),
FilterMembers(methods, range));
}
std::unique_ptr<Decl> Resource::SplitImpl(VersionRange range) const {
return std::make_unique<Resource>(attributes->Clone(), name, subtype_ctor->Clone(),
FilterMembers(properties, range));
}
std::unique_ptr<Decl> Alias::SplitImpl(VersionRange range) const {
return std::make_unique<Alias>(attributes->Clone(), name, partial_type_ctor->Clone());
}
std::unique_ptr<Decl> NewType::SplitImpl(VersionRange range) const {
return std::make_unique<NewType>(attributes->Clone(), name, type_ctor->Clone());
}
Enum::Member Enum::Member::Clone() const {
return Member(name, value->Clone(), attributes->Clone());
}
Bits::Member Bits::Member::Clone() const {
return Member(name, value->Clone(), attributes->Clone());
}
Service::Member Service::Member::Clone() const {
return Member(type_ctor->Clone(), name, attributes->Clone());
}
Struct::Member Struct::Member::Clone() const {
return Member(type_ctor->Clone(), name,
maybe_default_value ? maybe_default_value->Clone() : nullptr, attributes->Clone());
}
Table::Member Table::Member::Clone() const {
return Member(ordinal, type_ctor->Clone(), name, attributes->Clone());
}
Union::Member Union::Member::Clone() const {
return Member(ordinal, type_ctor->Clone(), name, attributes->Clone());
}
Overlay::Member Overlay::Member::Clone() const {
return Member(ordinal, type_ctor->Clone(), name, attributes->Clone());
}
Protocol::Method Protocol::Method::Clone() const {
return Method(attributes->Clone(), strictness, identifier, name, has_request,
maybe_request ? maybe_request->Clone() : nullptr, has_response,
maybe_response ? maybe_response->Clone() : nullptr, has_error);
}
Protocol::ComposedProtocol Protocol::ComposedProtocol::Clone() const {
return ComposedProtocol(attributes->Clone(), reference);
}
Resource::Property Resource::Property::Clone() const {
return Property(type_ctor->Clone(), name, attributes->Clone());
}
} // namespace fidlc