| // 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. | |
| #include "generics.h" | |
| #include "parser.h" // for expressionString() | |
| namespace capnp { | |
| namespace compiler { | |
| BrandedDecl::BrandedDecl(BrandedDecl& other) | |
| : body(other.body), | |
| source(other.source) { | |
| if (body.is<Resolver::ResolvedDecl>()) { | |
| brand = kj::addRef(*other.brand); | |
| } | |
| } | |
| BrandedDecl& BrandedDecl::operator=(BrandedDecl& other) { | |
| body = other.body; | |
| source = other.source; | |
| if (body.is<Resolver::ResolvedDecl>()) { | |
| brand = kj::addRef(*other.brand); | |
| } | |
| return *this; | |
| } | |
| kj::Maybe<BrandedDecl> BrandedDecl::applyParams( | |
| kj::Array<BrandedDecl> params, Expression::Reader subSource) { | |
| if (body.is<Resolver::ResolvedParameter>()) { | |
| return nullptr; | |
| } else { | |
| return brand->setParams(kj::mv(params), body.get<Resolver::ResolvedDecl>().kind, subSource) | |
| .map([&](kj::Own<BrandScope>&& scope) { | |
| BrandedDecl result = *this; | |
| result.brand = kj::mv(scope); | |
| result.source = subSource; | |
| return result; | |
| }); | |
| } | |
| } | |
| kj::Maybe<BrandedDecl> BrandedDecl::getMember( | |
| kj::StringPtr memberName, Expression::Reader subSource) { | |
| if (body.is<Resolver::ResolvedParameter>()) { | |
| return nullptr; | |
| } else KJ_IF_MAYBE(r, body.get<Resolver::ResolvedDecl>().resolver->resolveMember(memberName)) { | |
| return brand->interpretResolve(*body.get<Resolver::ResolvedDecl>().resolver, *r, subSource); | |
| } else { | |
| return nullptr; | |
| } | |
| } | |
| kj::Maybe<Declaration::Which> BrandedDecl::getKind() { | |
| if (body.is<Resolver::ResolvedParameter>()) { | |
| return nullptr; | |
| } else { | |
| return body.get<Resolver::ResolvedDecl>().kind; | |
| } | |
| } | |
| kj::Maybe<BrandedDecl&> BrandedDecl::getListParam() { | |
| KJ_REQUIRE(body.is<Resolver::ResolvedDecl>()); | |
| auto& decl = body.get<Resolver::ResolvedDecl>(); | |
| KJ_REQUIRE(decl.kind == Declaration::BUILTIN_LIST); | |
| auto params = KJ_ASSERT_NONNULL(brand->getParams(decl.id)); | |
| if (params.size() != 1) { | |
| return nullptr; | |
| } else { | |
| return params[0]; | |
| } | |
| } | |
| Resolver::ResolvedParameter BrandedDecl::asVariable() { | |
| KJ_REQUIRE(body.is<Resolver::ResolvedParameter>()); | |
| return body.get<Resolver::ResolvedParameter>(); | |
| } | |
| bool BrandedDecl::compileAsType( | |
| ErrorReporter& errorReporter, schema::Type::Builder target) { | |
| KJ_IF_MAYBE(kind, getKind()) { | |
| switch (*kind) { | |
| case Declaration::ENUM: { | |
| auto enum_ = target.initEnum(); | |
| enum_.setTypeId(getIdAndFillBrand([&]() { return enum_.initBrand(); })); | |
| return true; | |
| } | |
| case Declaration::STRUCT: { | |
| auto struct_ = target.initStruct(); | |
| struct_.setTypeId(getIdAndFillBrand([&]() { return struct_.initBrand(); })); | |
| return true; | |
| } | |
| case Declaration::INTERFACE: { | |
| auto interface = target.initInterface(); | |
| interface.setTypeId(getIdAndFillBrand([&]() { return interface.initBrand(); })); | |
| return true; | |
| } | |
| case Declaration::BUILTIN_LIST: { | |
| auto elementType = target.initList().initElementType(); | |
| KJ_IF_MAYBE(param, getListParam()) { | |
| if (!param->compileAsType(errorReporter, elementType)) { | |
| return false; | |
| } | |
| } else { | |
| addError(errorReporter, "'List' requires exactly one parameter."); | |
| return false; | |
| } | |
| if (elementType.isAnyPointer()) { | |
| auto unconstrained = elementType.getAnyPointer().getUnconstrained(); | |
| if (unconstrained.isAnyKind()) { | |
| addError(errorReporter, "'List(AnyPointer)' is not supported."); | |
| // Seeing List(AnyPointer) later can mess things up, so change the type to Void. | |
| elementType.setVoid(); | |
| return false; | |
| } else if (unconstrained.isStruct()) { | |
| addError(errorReporter, "'List(AnyStruct)' is not supported."); | |
| // Seeing List(AnyStruct) later can mess things up, so change the type to Void. | |
| elementType.setVoid(); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| case Declaration::BUILTIN_VOID: target.setVoid(); return true; | |
| case Declaration::BUILTIN_BOOL: target.setBool(); return true; | |
| case Declaration::BUILTIN_INT8: target.setInt8(); return true; | |
| case Declaration::BUILTIN_INT16: target.setInt16(); return true; | |
| case Declaration::BUILTIN_INT32: target.setInt32(); return true; | |
| case Declaration::BUILTIN_INT64: target.setInt64(); return true; | |
| case Declaration::BUILTIN_U_INT8: target.setUint8(); return true; | |
| case Declaration::BUILTIN_U_INT16: target.setUint16(); return true; | |
| case Declaration::BUILTIN_U_INT32: target.setUint32(); return true; | |
| case Declaration::BUILTIN_U_INT64: target.setUint64(); return true; | |
| case Declaration::BUILTIN_FLOAT32: target.setFloat32(); return true; | |
| case Declaration::BUILTIN_FLOAT64: target.setFloat64(); return true; | |
| case Declaration::BUILTIN_TEXT: target.setText(); return true; | |
| case Declaration::BUILTIN_DATA: target.setData(); return true; | |
| case Declaration::BUILTIN_OBJECT: | |
| addError(errorReporter, | |
| "As of Cap'n Proto 0.4, 'Object' has been renamed to 'AnyPointer'. Sorry for the " | |
| "inconvenience, and thanks for being an early adopter. :)"); | |
| KJ_FALLTHROUGH; | |
| case Declaration::BUILTIN_ANY_POINTER: | |
| target.initAnyPointer().initUnconstrained().setAnyKind(); | |
| return true; | |
| case Declaration::BUILTIN_ANY_STRUCT: | |
| target.initAnyPointer().initUnconstrained().setStruct(); | |
| return true; | |
| case Declaration::BUILTIN_ANY_LIST: | |
| target.initAnyPointer().initUnconstrained().setList(); | |
| return true; | |
| case Declaration::BUILTIN_CAPABILITY: | |
| target.initAnyPointer().initUnconstrained().setCapability(); | |
| return true; | |
| case Declaration::FILE: | |
| case Declaration::USING: | |
| case Declaration::CONST: | |
| case Declaration::ENUMERANT: | |
| case Declaration::FIELD: | |
| case Declaration::UNION: | |
| case Declaration::GROUP: | |
| case Declaration::METHOD: | |
| case Declaration::ANNOTATION: | |
| case Declaration::NAKED_ID: | |
| case Declaration::NAKED_ANNOTATION: | |
| addError(errorReporter, kj::str("'", toString(), "' is not a type.")); | |
| return false; | |
| } | |
| KJ_UNREACHABLE; | |
| } else { | |
| // Oh, this is a type variable. | |
| auto var = asVariable(); | |
| if (var.id == 0) { | |
| // This is actually a method implicit parameter. | |
| auto builder = target.initAnyPointer().initImplicitMethodParameter(); | |
| builder.setParameterIndex(var.index); | |
| return true; | |
| } else { | |
| auto builder = target.initAnyPointer().initParameter(); | |
| builder.setScopeId(var.id); | |
| builder.setParameterIndex(var.index); | |
| return true; | |
| } | |
| } | |
| } | |
| Resolver::ResolveResult BrandedDecl::asResolveResult( | |
| uint64_t scopeId, schema::Brand::Builder brandBuilder) { | |
| auto result = body; | |
| if (result.is<Resolver::ResolvedDecl>()) { | |
| // May need to compile our context as the "brand". | |
| result.get<Resolver::ResolvedDecl>().scopeId = scopeId; | |
| getIdAndFillBrand([&]() { | |
| result.get<Resolver::ResolvedDecl>().brand = brandBuilder.asReader(); | |
| return brandBuilder; | |
| }); | |
| } | |
| return result; | |
| } | |
| kj::String BrandedDecl::toString() { | |
| return expressionString(source); | |
| } | |
| kj::String BrandedDecl::toDebugString() { | |
| if (body.is<Resolver::ResolvedParameter>()) { | |
| auto variable = body.get<Resolver::ResolvedParameter>(); | |
| return kj::str("variable(", variable.id, ", ", variable.index, ")"); | |
| } else { | |
| auto decl = body.get<Resolver::ResolvedDecl>(); | |
| return kj::str("decl(", decl.id, ", ", (uint)decl.kind, "')"); | |
| } | |
| } | |
| BrandScope::BrandScope(ErrorReporter& errorReporter, uint64_t startingScopeId, | |
| uint startingScopeParamCount, Resolver& startingScope) | |
| : errorReporter(errorReporter), parent(nullptr), leafId(startingScopeId), | |
| leafParamCount(startingScopeParamCount), inherited(true) { | |
| // Create all lexical parent scopes, all with no brand bindings. | |
| KJ_IF_MAYBE(p, startingScope.getParent()) { | |
| parent = kj::refcounted<BrandScope>( | |
| errorReporter, p->id, p->genericParamCount, *p->resolver); | |
| } | |
| } | |
| bool BrandScope::isGeneric() { | |
| if (leafParamCount > 0) return true; | |
| KJ_IF_MAYBE(p, parent) { | |
| return p->get()->isGeneric(); | |
| } else { | |
| return false; | |
| } | |
| } | |
| kj::Own<BrandScope> BrandScope::push(uint64_t typeId, uint paramCount) { | |
| return kj::refcounted<BrandScope>(kj::addRef(*this), typeId, paramCount); | |
| } | |
| kj::Maybe<kj::Own<BrandScope>> BrandScope::setParams( | |
| kj::Array<BrandedDecl> params, Declaration::Which genericType, Expression::Reader source) { | |
| if (this->params.size() != 0) { | |
| errorReporter.addErrorOn(source, "Double-application of generic parameters."); | |
| return nullptr; | |
| } else if (params.size() > leafParamCount) { | |
| if (leafParamCount == 0) { | |
| errorReporter.addErrorOn(source, "Declaration does not accept generic parameters."); | |
| } else { | |
| errorReporter.addErrorOn(source, "Too many generic parameters."); | |
| } | |
| return nullptr; | |
| } else if (params.size() < leafParamCount) { | |
| errorReporter.addErrorOn(source, "Not enough generic parameters."); | |
| return nullptr; | |
| } else { | |
| if (genericType != Declaration::BUILTIN_LIST) { | |
| for (auto& param: params) { | |
| KJ_IF_MAYBE(kind, param.getKind()) { | |
| switch (*kind) { | |
| case Declaration::BUILTIN_LIST: | |
| case Declaration::BUILTIN_TEXT: | |
| case Declaration::BUILTIN_DATA: | |
| case Declaration::BUILTIN_ANY_POINTER: | |
| case Declaration::STRUCT: | |
| case Declaration::INTERFACE: | |
| break; | |
| default: | |
| param.addError(errorReporter, | |
| "Sorry, only pointer types can be used as generic parameters."); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return kj::refcounted<BrandScope>(*this, kj::mv(params)); | |
| } | |
| } | |
| kj::Own<BrandScope> BrandScope::pop(uint64_t newLeafId) { | |
| if (leafId == newLeafId) { | |
| return kj::addRef(*this); | |
| } | |
| KJ_IF_MAYBE(p, parent) { | |
| return (*p)->pop(newLeafId); | |
| } else { | |
| // Looks like we're moving into a whole top-level scope. | |
| return kj::refcounted<BrandScope>(errorReporter, newLeafId); | |
| } | |
| } | |
| kj::Maybe<BrandedDecl> BrandScope::lookupParameter( | |
| Resolver& resolver, uint64_t scopeId, uint index) { | |
| // Returns null if the param should be inherited from the client scope. | |
| if (scopeId == leafId) { | |
| if (index < params.size()) { | |
| return params[index]; | |
| } else if (inherited) { | |
| return nullptr; | |
| } else { | |
| // Unbound and not inherited, so return AnyPointer. | |
| auto decl = resolver.resolveBuiltin(Declaration::BUILTIN_ANY_POINTER); | |
| return BrandedDecl(decl, | |
| evaluateBrand(resolver, decl, List<schema::Brand::Scope>::Reader()), | |
| Expression::Reader()); | |
| } | |
| } else KJ_IF_MAYBE(p, parent) { | |
| return p->get()->lookupParameter(resolver, scopeId, index); | |
| } else { | |
| KJ_FAIL_REQUIRE("scope is not a parent"); | |
| } | |
| } | |
| kj::Maybe<kj::ArrayPtr<BrandedDecl>> BrandScope::getParams(uint64_t scopeId) { | |
| // Returns null if params at the requested scope should be inherited from the client scope. | |
| if (scopeId == leafId) { | |
| if (inherited) { | |
| return nullptr; | |
| } else { | |
| return params.asPtr(); | |
| } | |
| } else KJ_IF_MAYBE(p, parent) { | |
| return p->get()->getParams(scopeId); | |
| } else { | |
| KJ_FAIL_REQUIRE("scope is not a parent"); | |
| } | |
| } | |
| BrandedDecl BrandScope::interpretResolve( | |
| Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source) { | |
| if (result.is<Resolver::ResolvedDecl>()) { | |
| auto& decl = result.get<Resolver::ResolvedDecl>(); | |
| auto scope = pop(decl.scopeId); | |
| KJ_IF_MAYBE(brand, decl.brand) { | |
| scope = scope->evaluateBrand(resolver, decl, brand->getScopes()); | |
| } else { | |
| scope = scope->push(decl.id, decl.genericParamCount); | |
| } | |
| return BrandedDecl(decl, kj::mv(scope), source); | |
| } else { | |
| auto& param = result.get<Resolver::ResolvedParameter>(); | |
| KJ_IF_MAYBE(p, lookupParameter(resolver, param.id, param.index)) { | |
| return *p; | |
| } else { | |
| return BrandedDecl(param, source); | |
| } | |
| } | |
| } | |
| kj::Own<BrandScope> BrandScope::evaluateBrand( | |
| Resolver& resolver, Resolver::ResolvedDecl decl, | |
| List<schema::Brand::Scope>::Reader brand, uint index) { | |
| auto result = kj::refcounted<BrandScope>(errorReporter, decl.id); | |
| result->leafParamCount = decl.genericParamCount; | |
| // Fill in `params`. | |
| if (index < brand.size()) { | |
| auto nextScope = brand[index]; | |
| if (decl.id == nextScope.getScopeId()) { | |
| // Initialize our parameters. | |
| switch (nextScope.which()) { | |
| case schema::Brand::Scope::BIND: { | |
| auto bindings = nextScope.getBind(); | |
| auto params = kj::heapArrayBuilder<BrandedDecl>(bindings.size()); | |
| for (auto binding: bindings) { | |
| switch (binding.which()) { | |
| case schema::Brand::Binding::UNBOUND: { | |
| // Build an AnyPointer-equivalent. | |
| auto anyPointerDecl = resolver.resolveBuiltin(Declaration::BUILTIN_ANY_POINTER); | |
| params.add(BrandedDecl(anyPointerDecl, | |
| kj::refcounted<BrandScope>(errorReporter, anyPointerDecl.scopeId), | |
| Expression::Reader())); | |
| break; | |
| } | |
| case schema::Brand::Binding::TYPE: | |
| // Reverse this schema::Type back into a BrandedDecl. | |
| params.add(decompileType(resolver, binding.getType())); | |
| break; | |
| } | |
| } | |
| result->params = params.finish(); | |
| break; | |
| } | |
| case schema::Brand::Scope::INHERIT: | |
| KJ_IF_MAYBE(p, getParams(decl.id)) { | |
| result->params = kj::heapArray(*p); | |
| } else { | |
| result->inherited = true; | |
| } | |
| break; | |
| } | |
| // Parent should start one level deeper in the list. | |
| ++index; | |
| } | |
| } | |
| // Fill in `parent`. | |
| KJ_IF_MAYBE(parent, decl.resolver->getParent()) { | |
| result->parent = evaluateBrand(resolver, *parent, brand, index); | |
| } | |
| return result; | |
| } | |
| BrandedDecl BrandScope::decompileType( | |
| Resolver& resolver, schema::Type::Reader type) { | |
| auto builtin = [&](Declaration::Which which) -> BrandedDecl { | |
| auto decl = resolver.resolveBuiltin(which); | |
| return BrandedDecl(decl, | |
| evaluateBrand(resolver, decl, List<schema::Brand::Scope>::Reader()), | |
| Expression::Reader()); | |
| }; | |
| switch (type.which()) { | |
| case schema::Type::VOID: return builtin(Declaration::BUILTIN_VOID); | |
| case schema::Type::BOOL: return builtin(Declaration::BUILTIN_BOOL); | |
| case schema::Type::INT8: return builtin(Declaration::BUILTIN_INT8); | |
| case schema::Type::INT16: return builtin(Declaration::BUILTIN_INT16); | |
| case schema::Type::INT32: return builtin(Declaration::BUILTIN_INT32); | |
| case schema::Type::INT64: return builtin(Declaration::BUILTIN_INT64); | |
| case schema::Type::UINT8: return builtin(Declaration::BUILTIN_U_INT8); | |
| case schema::Type::UINT16: return builtin(Declaration::BUILTIN_U_INT16); | |
| case schema::Type::UINT32: return builtin(Declaration::BUILTIN_U_INT32); | |
| case schema::Type::UINT64: return builtin(Declaration::BUILTIN_U_INT64); | |
| case schema::Type::FLOAT32: return builtin(Declaration::BUILTIN_FLOAT32); | |
| case schema::Type::FLOAT64: return builtin(Declaration::BUILTIN_FLOAT64); | |
| case schema::Type::TEXT: return builtin(Declaration::BUILTIN_TEXT); | |
| case schema::Type::DATA: return builtin(Declaration::BUILTIN_DATA); | |
| case schema::Type::ENUM: { | |
| auto enumType = type.getEnum(); | |
| Resolver::ResolvedDecl decl = resolver.resolveId(enumType.getTypeId()); | |
| return BrandedDecl(decl, | |
| evaluateBrand(resolver, decl, enumType.getBrand().getScopes()), | |
| Expression::Reader()); | |
| } | |
| case schema::Type::INTERFACE: { | |
| auto interfaceType = type.getInterface(); | |
| Resolver::ResolvedDecl decl = resolver.resolveId(interfaceType.getTypeId()); | |
| return BrandedDecl(decl, | |
| evaluateBrand(resolver, decl, interfaceType.getBrand().getScopes()), | |
| Expression::Reader()); | |
| } | |
| case schema::Type::STRUCT: { | |
| auto structType = type.getStruct(); | |
| Resolver::ResolvedDecl decl = resolver.resolveId(structType.getTypeId()); | |
| return BrandedDecl(decl, | |
| evaluateBrand(resolver, decl, structType.getBrand().getScopes()), | |
| Expression::Reader()); | |
| } | |
| case schema::Type::LIST: { | |
| auto elementType = decompileType(resolver, type.getList().getElementType()); | |
| return KJ_ASSERT_NONNULL(builtin(Declaration::BUILTIN_LIST) | |
| .applyParams(kj::heapArray(&elementType, 1), Expression::Reader())); | |
| } | |
| case schema::Type::ANY_POINTER: { | |
| auto anyPointer = type.getAnyPointer(); | |
| switch (anyPointer.which()) { | |
| case schema::Type::AnyPointer::UNCONSTRAINED: | |
| return builtin(Declaration::BUILTIN_ANY_POINTER); | |
| case schema::Type::AnyPointer::PARAMETER: { | |
| auto param = anyPointer.getParameter(); | |
| auto id = param.getScopeId(); | |
| uint index = param.getParameterIndex(); | |
| KJ_IF_MAYBE(binding, lookupParameter(resolver, id, index)) { | |
| return *binding; | |
| } else { | |
| return BrandedDecl(Resolver::ResolvedParameter {id, index}, Expression::Reader()); | |
| } | |
| } | |
| case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER: | |
| KJ_FAIL_ASSERT("Alias pointed to implicit method type parameter?"); | |
| } | |
| KJ_UNREACHABLE; | |
| } | |
| } | |
| KJ_UNREACHABLE; | |
| } | |
| kj::Maybe<BrandedDecl> BrandScope::compileDeclExpression( | |
| Expression::Reader source, Resolver& resolver, | |
| ImplicitParams implicitMethodParams) { | |
| switch (source.which()) { | |
| case Expression::UNKNOWN: | |
| // Error reported earlier. | |
| return nullptr; | |
| case Expression::POSITIVE_INT: | |
| case Expression::NEGATIVE_INT: | |
| case Expression::FLOAT: | |
| case Expression::STRING: | |
| case Expression::BINARY: | |
| case Expression::LIST: | |
| case Expression::TUPLE: | |
| case Expression::EMBED: | |
| errorReporter.addErrorOn(source, "Expected name."); | |
| return nullptr; | |
| case Expression::RELATIVE_NAME: { | |
| auto name = source.getRelativeName(); | |
| auto nameValue = name.getValue(); | |
| // Check implicit method params first. | |
| for (auto i: kj::indices(implicitMethodParams.params)) { | |
| if (implicitMethodParams.params[i].getName() == nameValue) { | |
| if (implicitMethodParams.scopeId == 0) { | |
| return BrandedDecl::implicitMethodParam(i); | |
| } else { | |
| return BrandedDecl(Resolver::ResolvedParameter { | |
| implicitMethodParams.scopeId, static_cast<uint16_t>(i) }, | |
| Expression::Reader()); | |
| } | |
| } | |
| } | |
| KJ_IF_MAYBE(r, resolver.resolve(nameValue)) { | |
| return interpretResolve(resolver, *r, source); | |
| } else { | |
| errorReporter.addErrorOn(name, kj::str("Not defined: ", nameValue)); | |
| return nullptr; | |
| } | |
| } | |
| case Expression::ABSOLUTE_NAME: { | |
| auto name = source.getAbsoluteName(); | |
| KJ_IF_MAYBE(r, resolver.getTopScope().resolver->resolveMember(name.getValue())) { | |
| return interpretResolve(resolver, *r, source); | |
| } else { | |
| errorReporter.addErrorOn(name, kj::str("Not defined: ", name.getValue())); | |
| return nullptr; | |
| } | |
| } | |
| case Expression::IMPORT: { | |
| auto filename = source.getImport(); | |
| KJ_IF_MAYBE(decl, resolver.resolveImport(filename.getValue())) { | |
| // Import is always a root scope, so create a fresh BrandScope. | |
| return BrandedDecl(*decl, kj::refcounted<BrandScope>( | |
| errorReporter, decl->id, decl->genericParamCount, *decl->resolver), source); | |
| } else { | |
| errorReporter.addErrorOn(filename, kj::str("Import failed: ", filename.getValue())); | |
| return nullptr; | |
| } | |
| } | |
| case Expression::APPLICATION: { | |
| auto app = source.getApplication(); | |
| KJ_IF_MAYBE(decl, compileDeclExpression(app.getFunction(), resolver, implicitMethodParams)) { | |
| // Compile all params. | |
| auto params = app.getParams(); | |
| auto compiledParams = kj::heapArrayBuilder<BrandedDecl>(params.size()); | |
| bool paramFailed = false; | |
| for (auto param: params) { | |
| if (param.isNamed()) { | |
| errorReporter.addErrorOn(param.getNamed(), "Named parameter not allowed here."); | |
| } | |
| KJ_IF_MAYBE(d, compileDeclExpression(param.getValue(), resolver, implicitMethodParams)) { | |
| compiledParams.add(kj::mv(*d)); | |
| } else { | |
| // Param failed to compile. Error was already reported. | |
| paramFailed = true; | |
| } | |
| }; | |
| if (paramFailed) { | |
| return kj::mv(*decl); | |
| } | |
| // Add the parameters to the brand. | |
| KJ_IF_MAYBE(applied, decl->applyParams(compiledParams.finish(), source)) { | |
| return kj::mv(*applied); | |
| } else { | |
| // Error already reported. Ignore parameters. | |
| return kj::mv(*decl); | |
| } | |
| } else { | |
| // error already reported | |
| return nullptr; | |
| } | |
| } | |
| case Expression::MEMBER: { | |
| auto member = source.getMember(); | |
| KJ_IF_MAYBE(decl, compileDeclExpression(member.getParent(), resolver, implicitMethodParams)) { | |
| auto name = member.getName(); | |
| KJ_IF_MAYBE(memberDecl, decl->getMember(name.getValue(), source)) { | |
| return kj::mv(*memberDecl); | |
| } else { | |
| errorReporter.addErrorOn(name, kj::str( | |
| "'", expressionString(member.getParent()), | |
| "' has no member named '", name.getValue(), "'")); | |
| return nullptr; | |
| } | |
| } else { | |
| // error already reported | |
| return nullptr; | |
| } | |
| } | |
| } | |
| KJ_UNREACHABLE; | |
| } | |
| } // namespace compiler | |
| } // namespace capnp |