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