| // 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 |