capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

generics.h (13158B)


      1 // Copyright (c) 2013-2020 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #pragma once
     23 
     24 #include <capnp/orphan.h>
     25 #include <capnp/compiler/grammar.capnp.h>
     26 #include <capnp/schema.capnp.h>
     27 #include <capnp/dynamic.h>
     28 #include <kj/vector.h>
     29 #include <kj/one-of.h>
     30 #include "error-reporter.h"
     31 #include "resolver.h"
     32 
     33 CAPNP_BEGIN_HEADER
     34 
     35 namespace capnp {
     36 namespace compiler {
     37 
     38 class BrandedDecl;
     39 class BrandScope;
     40 
     41 struct ImplicitParams {
     42   // Represents a set of implicit brand parameters visible in the current context.
     43   //
     44   // As of this writing, implicit parameters occur only in the context of RPC methods. That is,
     45   // like this:
     46   //
     47   //     makeBox @0 [T] (value :T) -> Box(T);
     48   //
     49   // Here, `T` is an implicit parameter.
     50 
     51   uint64_t scopeId;
     52   // If zero, then any reference to an implicit param in this context should be compiled to a
     53   // `implicitMethodParam` AnyPointer. If non-zero, it should be compiled to a `parameter`
     54   // AnyPointer using this scopeId. This comes into play when compiling the implicitly-generated
     55   // struct types corresponding to a method's params or results; these implicitly-generated types
     56   // themselves have *explicit* brand parameters corresponding to the *implicit* brand parameters
     57   // of the method.
     58   //
     59   // TODO(cleanup): Unclear why ImplicitParams is even used when compiling the implicit structs
     60   //   with explicit params. Missing abstraction?
     61 
     62   List<Declaration::BrandParameter>::Reader params;
     63   // Name and metadata about the parameter declaration.
     64 
     65   static inline ImplicitParams none() {
     66     // Convenience helper to create an empty `ImplicitParams`.
     67     return { 0, List<Declaration::BrandParameter>::Reader() };
     68   }
     69 };
     70 
     71 class BrandedDecl {
     72   // Represents a declaration possibly with generic parameter bindings.
     73 
     74 public:
     75   inline BrandedDecl(Resolver::ResolvedDecl decl,
     76                      kj::Own<BrandScope>&& brand,
     77                      Expression::Reader source)
     78       : brand(kj::mv(brand)), source(source) {
     79     // `source`, is the expression which specified this branded decl. It is provided so that errors
     80     // can be reported against it. It is acceptable to pass a default-initialized reader if there's
     81     // no source expression; errors will then be reported at 0, 0.
     82 
     83     body.init<Resolver::ResolvedDecl>(kj::mv(decl));
     84   }
     85   inline BrandedDecl(Resolver::ResolvedParameter variable, Expression::Reader source)
     86       : source(source) {
     87     body.init<Resolver::ResolvedParameter>(kj::mv(variable));
     88   }
     89   inline BrandedDecl(decltype(nullptr)) {}
     90   inline BrandedDecl() {}  // exists only for ExternalMutexGuarded<BrandedDecl> to work...
     91 
     92   static BrandedDecl implicitMethodParam(uint index) {
     93     // Get a BrandedDecl referring to an implicit method parameter.
     94     // (As a hack, we internally represent this as a ResolvedParameter. Sorry.)
     95     return BrandedDecl(Resolver::ResolvedParameter { 0, index }, Expression::Reader());
     96   }
     97 
     98   BrandedDecl(BrandedDecl& other);
     99   BrandedDecl(BrandedDecl&& other) = default;
    100 
    101   BrandedDecl& operator=(BrandedDecl& other);
    102   BrandedDecl& operator=(BrandedDecl&& other) = default;
    103 
    104   kj::Maybe<BrandedDecl> applyParams(kj::Array<BrandedDecl> params, Expression::Reader subSource);
    105   // Treat the declaration as a generic and apply it to the given parameter list.
    106 
    107   kj::Maybe<BrandedDecl> getMember(kj::StringPtr memberName, Expression::Reader subSource);
    108   // Get a member of this declaration.
    109 
    110   kj::Maybe<Declaration::Which> getKind();
    111   // Returns the kind of declaration, or null if this is an unbound generic variable.
    112 
    113   template <typename InitBrandFunc>
    114   uint64_t getIdAndFillBrand(InitBrandFunc&& initBrand);
    115   // Returns the type ID of this node. `initBrand` is a zero-arg functor which returns
    116   // schema::Brand::Builder; this will be called if this decl has brand bindings, and
    117   // the returned builder filled in to reflect those bindings.
    118   //
    119   // It is an error to call this when `getKind()` returns null.
    120 
    121   kj::Maybe<BrandedDecl&> getListParam();
    122   // Only if the kind is BUILTIN_LIST: Get the list's type parameter.
    123 
    124   Resolver::ResolvedParameter asVariable();
    125   // If this is an unbound generic variable (i.e. `getKind()` returns null), return information
    126   // about the variable.
    127   //
    128   // It is an error to call this when `getKind()` does not return null.
    129 
    130   bool compileAsType(ErrorReporter& errorReporter, schema::Type::Builder target);
    131   // Compile this decl to a schema::Type.
    132 
    133   inline void addError(ErrorReporter& errorReporter, kj::StringPtr message) {
    134     errorReporter.addErrorOn(source, message);
    135   }
    136 
    137   Resolver::ResolveResult asResolveResult(uint64_t scopeId, schema::Brand::Builder brandBuilder);
    138   // Reverse this into a ResolveResult. If necessary, use `brandBuilder` to fill in
    139   // ResolvedDecl.brand.
    140 
    141   kj::String toString();
    142   kj::String toDebugString();
    143 
    144 private:
    145   Resolver::ResolveResult body;
    146   kj::Own<BrandScope> brand;  // null if parameter
    147   Expression::Reader source;
    148 };
    149 
    150 class BrandScope: public kj::Refcounted {
    151   // Tracks the brand parameter bindings affecting the scope specified by some expression. For
    152   // example, if we are interpreting the type expression "Foo(Text).Bar", we would start with the
    153   // current scope's BrandScope, create a new child BrandScope representing "Foo", add the "(Text)"
    154   // parameter bindings to it, then create a further child scope for "Bar". Thus the BrandScope for
    155   // Bar knows that Foo's parameter list has been bound to "(Text)".
    156 
    157 public:
    158   BrandScope(ErrorReporter& errorReporter, uint64_t startingScopeId,
    159              uint startingScopeParamCount, Resolver& startingScope);
    160   // TODO(bug): Passing an `errorReporter` to the constructor of `BrandScope` turns out not to
    161   //   make a ton of sense, as an `errorReporter` is meant to report errors in a specific module,
    162   //   but `BrandScope` might be constructed while compiling one module but then used when
    163   //   compiling a different module, or not compiling a module at all. Note, though, that it DOES
    164   //   make sense for BrandedDecl to have an ErrorReporter, specifically associated with its
    165   //   `source` expression.
    166 
    167   bool isGeneric();
    168   // Returns true if this scope or any parent scope is a generic (has brand parameters).
    169 
    170   kj::Own<BrandScope> push(uint64_t typeId, uint paramCount);
    171   // Creates a new child scope with the given type ID and number of brand parameters.
    172 
    173   kj::Maybe<kj::Own<BrandScope>> setParams(
    174       kj::Array<BrandedDecl> params, Declaration::Which genericType, Expression::Reader source);
    175   // Create a new BrandScope representing the same scope, but with parameters filled in.
    176   //
    177   // This should only be called on the generic version of the scope. If called on a branded
    178   // version, an error will be reported.
    179   //
    180   // Returns null if an error occurred that prevented creating the BrandScope; the error will have
    181   // been reported to the ErrorReporter.
    182 
    183   kj::Own<BrandScope> pop(uint64_t newLeafId);
    184   // Return the parent scope.
    185 
    186   kj::Maybe<BrandedDecl> lookupParameter(Resolver& resolver, uint64_t scopeId, uint index);
    187   // Search up the scope chain for the scope matching `scopeId`, and return its `index`th parameter
    188   // binding. Returns null if the parameter is from a scope that we are currently compiling, and
    189   // hasn't otherwise been bound to any argument (see Brand.Scope.inherit in schema.capnp).
    190   //
    191   // In the case that a parameter wasn't specified, but isn't part of the current scope, this
    192   // returns the declaration for `AnyPointer`.
    193   //
    194   // TODO(cleanup): Should be called lookupArgument()?
    195 
    196   kj::Maybe<kj::ArrayPtr<BrandedDecl>> getParams(uint64_t scopeId);
    197   // Get the whole list of parameter bindings at the given scope. Returns null if the scope is
    198   // currently be compiled and the parameters are unbound.
    199   //
    200   // Note that it's possible that not all declared parameters were actually specified for a given
    201   // scope. For example, if you declare a generic `Foo(T, U)`, and then you intiantiate it
    202   // somewhere as `Foo(Text)`, then `U` is unspecified -- this is not an error, because Cap'n
    203   // Proto allows new type parameters to be added over time. `U` should be treated as `AnyPointer`
    204   // in this case, but `getParams()` doesn't know how many parameters are expected, so it will
    205   // return an array that only contains one item. Use `lookupParameter()` if you want unspecified
    206   // parameters to be filled in with `AnyPointer` automatically.
    207   //
    208   // TODO(cleanup): Should be called getArguments()?
    209 
    210   template <typename InitBrandFunc>
    211   void compile(InitBrandFunc&& initBrand);
    212   // Constructs the schema::Brand corresponding to this brand scope.
    213   //
    214   // `initBrand` is a zero-arg functor which returns an empty schema::Brand::Builder, into which
    215   // the brand is constructed. If no generics are present, then `initBrand` is never called.
    216   //
    217   // TODO(cleanup): Should this return Maybe<Orphan<schema::Brand>> instead?
    218 
    219   kj::Maybe<BrandedDecl> compileDeclExpression(
    220       Expression::Reader source, Resolver& resolver,
    221       ImplicitParams implicitMethodParams);
    222   // Interpret a type expression within this branded scope.
    223 
    224   BrandedDecl interpretResolve(
    225       Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source);
    226   // After using a Resolver to resolve a symbol, call interpretResolve() to interpret the result
    227   // within the current brand scope. For example, if a name resolved to a brand parameter, this
    228   // replaces it with the appropriate argument from the scope.
    229 
    230   inline uint64_t getScopeId() { return leafId; }
    231 
    232 private:
    233   ErrorReporter& errorReporter;
    234   kj::Maybe<kj::Own<BrandScope>> parent;
    235   uint64_t leafId;                     // zero = this is the root
    236   uint leafParamCount;                 // number of generic parameters on this leaf
    237   bool inherited;
    238   kj::Array<BrandedDecl> params;
    239 
    240   BrandScope(kj::Own<BrandScope> parent, uint64_t leafId, uint leafParamCount)
    241       : errorReporter(parent->errorReporter),
    242         parent(kj::mv(parent)), leafId(leafId), leafParamCount(leafParamCount),
    243         inherited(false) {}
    244   BrandScope(BrandScope& base, kj::Array<BrandedDecl> params)
    245       : errorReporter(base.errorReporter),
    246         leafId(base.leafId), leafParamCount(base.leafParamCount),
    247         inherited(false), params(kj::mv(params)) {
    248     KJ_IF_MAYBE(p, base.parent) {
    249       parent = kj::addRef(**p);
    250     }
    251   }
    252   BrandScope(ErrorReporter& errorReporter, uint64_t scopeId)
    253       : errorReporter(errorReporter), leafId(scopeId), leafParamCount(0), inherited(false) {}
    254 
    255   kj::Own<BrandScope> evaluateBrand(
    256       Resolver& resolver, Resolver::ResolvedDecl decl,
    257       List<schema::Brand::Scope>::Reader brand, uint index = 0);
    258 
    259   BrandedDecl decompileType(Resolver& resolver, schema::Type::Reader type);
    260 
    261   template <typename T, typename... Params>
    262   friend kj::Own<T> kj::refcounted(Params&&... params);
    263   friend class BrandedDecl;
    264 };
    265 
    266 template <typename InitBrandFunc>
    267 uint64_t BrandedDecl::getIdAndFillBrand(InitBrandFunc&& initBrand) {
    268   KJ_REQUIRE(body.is<Resolver::ResolvedDecl>());
    269 
    270   brand->compile(kj::fwd<InitBrandFunc>(initBrand));
    271   return body.get<Resolver::ResolvedDecl>().id;
    272 }
    273 
    274 template <typename InitBrandFunc>
    275 void BrandScope::compile(InitBrandFunc&& initBrand) {
    276   kj::Vector<BrandScope*> levels;
    277   BrandScope* ptr = this;
    278   for (;;) {
    279     if (ptr->params.size() > 0 || (ptr->inherited && ptr->leafParamCount > 0)) {
    280       levels.add(ptr);
    281     }
    282     KJ_IF_MAYBE(p, ptr->parent) {
    283       ptr = *p;
    284     } else {
    285       break;
    286     }
    287   }
    288 
    289   if (levels.size() > 0) {
    290     auto scopes = initBrand().initScopes(levels.size());
    291     for (uint i: kj::indices(levels)) {
    292       auto scope = scopes[i];
    293       scope.setScopeId(levels[i]->leafId);
    294 
    295       if (levels[i]->inherited) {
    296         scope.setInherit();
    297       } else {
    298         auto bindings = scope.initBind(levels[i]->params.size());
    299         for (uint j: kj::indices(bindings)) {
    300           levels[i]->params[j].compileAsType(errorReporter, bindings[j].initType());
    301         }
    302       }
    303     }
    304   }
    305 }
    306 
    307 }  // namespace compiler
    308 }  // namespace capnp
    309 
    310 CAPNP_END_HEADER