blob: fbdbddb488d903a3edb635f78b3d2060952bd72c [file] [log] [blame]
// Copyright (c) 2013-2020 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include <capnp/orphan.h>
#include <capnp/compiler/grammar.capnp.h>
#include <capnp/schema.capnp.h>
#include <capnp/dynamic.h>
#include <kj/vector.h>
#include <kj/one-of.h>
#include "error-reporter.h"
#include "resolver.h"
CAPNP_BEGIN_HEADER
namespace capnp {
namespace compiler {
class BrandedDecl;
class BrandScope;
struct ImplicitParams {
// Represents a set of implicit brand parameters visible in the current context.
//
// As of this writing, implicit parameters occur only in the context of RPC methods. That is,
// like this:
//
// makeBox @0 [T] (value :T) -> Box(T);
//
// Here, `T` is an implicit parameter.
uint64_t scopeId;
// If zero, then any reference to an implicit param in this context should be compiled to a
// `implicitMethodParam` AnyPointer. If non-zero, it should be compiled to a `parameter`
// AnyPointer using this scopeId. This comes into play when compiling the implicitly-generated
// struct types corresponding to a method's params or results; these implicitly-generated types
// themselves have *explicit* brand parameters corresponding to the *implicit* brand parameters
// of the method.
//
// TODO(cleanup): Unclear why ImplicitParams is even used when compiling the implicit structs
// with explicit params. Missing abstraction?
List<Declaration::BrandParameter>::Reader params;
// Name and metadata about the parameter declaration.
static inline ImplicitParams none() {
// Convenience helper to create an empty `ImplicitParams`.
return { 0, List<Declaration::BrandParameter>::Reader() };
}
};
class BrandedDecl {
// Represents a declaration possibly with generic parameter bindings.
public:
inline BrandedDecl(Resolver::ResolvedDecl decl,
kj::Own<BrandScope>&& brand,
Expression::Reader source)
: brand(kj::mv(brand)), source(source) {
// `source`, is the expression which specified this branded decl. It is provided so that errors
// can be reported against it. It is acceptable to pass a default-initialized reader if there's
// no source expression; errors will then be reported at 0, 0.
body.init<Resolver::ResolvedDecl>(kj::mv(decl));
}
inline BrandedDecl(Resolver::ResolvedParameter variable, Expression::Reader source)
: source(source) {
body.init<Resolver::ResolvedParameter>(kj::mv(variable));
}
inline BrandedDecl(decltype(nullptr)) {}
inline BrandedDecl() {} // exists only for ExternalMutexGuarded<BrandedDecl> to work...
static BrandedDecl implicitMethodParam(uint index) {
// Get a BrandedDecl referring to an implicit method parameter.
// (As a hack, we internally represent this as a ResolvedParameter. Sorry.)
return BrandedDecl(Resolver::ResolvedParameter { 0, index }, Expression::Reader());
}
BrandedDecl(BrandedDecl& other);
BrandedDecl(BrandedDecl&& other) = default;
BrandedDecl& operator=(BrandedDecl& other);
BrandedDecl& operator=(BrandedDecl&& other) = default;
kj::Maybe<BrandedDecl> applyParams(kj::Array<BrandedDecl> params, Expression::Reader subSource);
// Treat the declaration as a generic and apply it to the given parameter list.
kj::Maybe<BrandedDecl> getMember(kj::StringPtr memberName, Expression::Reader subSource);
// Get a member of this declaration.
kj::Maybe<Declaration::Which> getKind();
// Returns the kind of declaration, or null if this is an unbound generic variable.
template <typename InitBrandFunc>
uint64_t getIdAndFillBrand(InitBrandFunc&& initBrand);
// Returns the type ID of this node. `initBrand` is a zero-arg functor which returns
// schema::Brand::Builder; this will be called if this decl has brand bindings, and
// the returned builder filled in to reflect those bindings.
//
// It is an error to call this when `getKind()` returns null.
kj::Maybe<BrandedDecl&> getListParam();
// Only if the kind is BUILTIN_LIST: Get the list's type parameter.
Resolver::ResolvedParameter asVariable();
// If this is an unbound generic variable (i.e. `getKind()` returns null), return information
// about the variable.
//
// It is an error to call this when `getKind()` does not return null.
bool compileAsType(ErrorReporter& errorReporter, schema::Type::Builder target);
// Compile this decl to a schema::Type.
inline void addError(ErrorReporter& errorReporter, kj::StringPtr message) {
errorReporter.addErrorOn(source, message);
}
Resolver::ResolveResult asResolveResult(uint64_t scopeId, schema::Brand::Builder brandBuilder);
// Reverse this into a ResolveResult. If necessary, use `brandBuilder` to fill in
// ResolvedDecl.brand.
kj::String toString();
kj::String toDebugString();
private:
Resolver::ResolveResult body;
kj::Own<BrandScope> brand; // null if parameter
Expression::Reader source;
};
class BrandScope: public kj::Refcounted {
// Tracks the brand parameter bindings affecting the scope specified by some expression. For
// example, if we are interpreting the type expression "Foo(Text).Bar", we would start with the
// current scope's BrandScope, create a new child BrandScope representing "Foo", add the "(Text)"
// parameter bindings to it, then create a further child scope for "Bar". Thus the BrandScope for
// Bar knows that Foo's parameter list has been bound to "(Text)".
public:
BrandScope(ErrorReporter& errorReporter, uint64_t startingScopeId,
uint startingScopeParamCount, Resolver& startingScope);
// TODO(bug): Passing an `errorReporter` to the constructor of `BrandScope` turns out not to
// make a ton of sense, as an `errorReporter` is meant to report errors in a specific module,
// but `BrandScope` might be constructed while compiling one module but then used when
// compiling a different module, or not compiling a module at all. Note, though, that it DOES
// make sense for BrandedDecl to have an ErrorReporter, specifically associated with its
// `source` expression.
bool isGeneric();
// Returns true if this scope or any parent scope is a generic (has brand parameters).
kj::Own<BrandScope> push(uint64_t typeId, uint paramCount);
// Creates a new child scope with the given type ID and number of brand parameters.
kj::Maybe<kj::Own<BrandScope>> setParams(
kj::Array<BrandedDecl> params, Declaration::Which genericType, Expression::Reader source);
// Create a new BrandScope representing the same scope, but with parameters filled in.
//
// This should only be called on the generic version of the scope. If called on a branded
// version, an error will be reported.
//
// Returns null if an error occurred that prevented creating the BrandScope; the error will have
// been reported to the ErrorReporter.
kj::Own<BrandScope> pop(uint64_t newLeafId);
// Return the parent scope.
kj::Maybe<BrandedDecl> lookupParameter(Resolver& resolver, uint64_t scopeId, uint index);
// Search up the scope chain for the scope matching `scopeId`, and return its `index`th parameter
// binding. Returns null if the parameter is from a scope that we are currently compiling, and
// hasn't otherwise been bound to any argument (see Brand.Scope.inherit in schema.capnp).
//
// In the case that a parameter wasn't specified, but isn't part of the current scope, this
// returns the declaration for `AnyPointer`.
//
// TODO(cleanup): Should be called lookupArgument()?
kj::Maybe<kj::ArrayPtr<BrandedDecl>> getParams(uint64_t scopeId);
// Get the whole list of parameter bindings at the given scope. Returns null if the scope is
// currently be compiled and the parameters are unbound.
//
// Note that it's possible that not all declared parameters were actually specified for a given
// scope. For example, if you declare a generic `Foo(T, U)`, and then you intiantiate it
// somewhere as `Foo(Text)`, then `U` is unspecified -- this is not an error, because Cap'n
// Proto allows new type parameters to be added over time. `U` should be treated as `AnyPointer`
// in this case, but `getParams()` doesn't know how many parameters are expected, so it will
// return an array that only contains one item. Use `lookupParameter()` if you want unspecified
// parameters to be filled in with `AnyPointer` automatically.
//
// TODO(cleanup): Should be called getArguments()?
template <typename InitBrandFunc>
void compile(InitBrandFunc&& initBrand);
// Constructs the schema::Brand corresponding to this brand scope.
//
// `initBrand` is a zero-arg functor which returns an empty schema::Brand::Builder, into which
// the brand is constructed. If no generics are present, then `initBrand` is never called.
//
// TODO(cleanup): Should this return Maybe<Orphan<schema::Brand>> instead?
kj::Maybe<BrandedDecl> compileDeclExpression(
Expression::Reader source, Resolver& resolver,
ImplicitParams implicitMethodParams);
// Interpret a type expression within this branded scope.
BrandedDecl interpretResolve(
Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source);
// After using a Resolver to resolve a symbol, call interpretResolve() to interpret the result
// within the current brand scope. For example, if a name resolved to a brand parameter, this
// replaces it with the appropriate argument from the scope.
inline uint64_t getScopeId() { return leafId; }
private:
ErrorReporter& errorReporter;
kj::Maybe<kj::Own<BrandScope>> parent;
uint64_t leafId; // zero = this is the root
uint leafParamCount; // number of generic parameters on this leaf
bool inherited;
kj::Array<BrandedDecl> params;
BrandScope(kj::Own<BrandScope> parent, uint64_t leafId, uint leafParamCount)
: errorReporter(parent->errorReporter),
parent(kj::mv(parent)), leafId(leafId), leafParamCount(leafParamCount),
inherited(false) {}
BrandScope(BrandScope& base, kj::Array<BrandedDecl> params)
: errorReporter(base.errorReporter),
leafId(base.leafId), leafParamCount(base.leafParamCount),
inherited(false), params(kj::mv(params)) {
KJ_IF_MAYBE(p, base.parent) {
parent = kj::addRef(**p);
}
}
BrandScope(ErrorReporter& errorReporter, uint64_t scopeId)
: errorReporter(errorReporter), leafId(scopeId), leafParamCount(0), inherited(false) {}
kj::Own<BrandScope> evaluateBrand(
Resolver& resolver, Resolver::ResolvedDecl decl,
List<schema::Brand::Scope>::Reader brand, uint index = 0);
BrandedDecl decompileType(Resolver& resolver, schema::Type::Reader type);
template <typename T, typename... Params>
friend kj::Own<T> kj::refcounted(Params&&... params);
friend class BrandedDecl;
};
template <typename InitBrandFunc>
uint64_t BrandedDecl::getIdAndFillBrand(InitBrandFunc&& initBrand) {
KJ_REQUIRE(body.is<Resolver::ResolvedDecl>());
brand->compile(kj::fwd<InitBrandFunc>(initBrand));
return body.get<Resolver::ResolvedDecl>().id;
}
template <typename InitBrandFunc>
void BrandScope::compile(InitBrandFunc&& initBrand) {
kj::Vector<BrandScope*> levels;
BrandScope* ptr = this;
for (;;) {
if (ptr->params.size() > 0 || (ptr->inherited && ptr->leafParamCount > 0)) {
levels.add(ptr);
}
KJ_IF_MAYBE(p, ptr->parent) {
ptr = *p;
} else {
break;
}
}
if (levels.size() > 0) {
auto scopes = initBrand().initScopes(levels.size());
for (uint i: kj::indices(levels)) {
auto scope = scopes[i];
scope.setScopeId(levels[i]->leafId);
if (levels[i]->inherited) {
scope.setInherit();
} else {
auto bindings = scope.initBind(levels[i]->params.size());
for (uint j: kj::indices(bindings)) {
levels[i]->params[j].compileAsType(errorReporter, bindings[j].initType());
}
}
}
}
}
} // namespace compiler
} // namespace capnp
CAPNP_END_HEADER