ValueArray Alias::selfTemplateArgs() const { ValueArray templateArgs; templateArgs.reserve(templateVariables().size()); for (const auto templateVar: templateVariables()) { // Refer to the template variables of this type alias. templateArgs.push_back(templateVar->selfRefValue()); } return templateArgs; }
bool MethodSetElement::operator==(const MethodSetElement& methodSetElement) const { return templateVariables() == methodSetElement.templateVariables() && constPredicate() == methodSetElement.constPredicate() && noexceptPredicate() == methodSetElement.noexceptPredicate() && requirePredicate() == methodSetElement.requirePredicate() && returnType() == methodSetElement.returnType() && parameterTypes() == methodSetElement.parameterTypes() && isStatic() == methodSetElement.isStatic(); }
Value Alias::selfRefValue(ValueArray templateArguments) const { assert(templateArguments.size() == templateVariables().size()); if (type()->isBuiltInTypename()) { const auto aliasRef = selfRefType(std::move(templateArguments)); return Value::TypeRef(aliasRef, type()->createStaticRefType(aliasRef)); } else { return Value::Alias(*this, std::move(templateArguments)); } }
std::size_t MethodSetElement::hash() const { std::size_t seed = 0; boost::hash_combine(seed, templateVariables().hash()); boost::hash_combine(seed, constPredicate().hash()); boost::hash_combine(seed, noexceptPredicate().hash()); boost::hash_combine(seed, requirePredicate().hash()); boost::hash_combine(seed, isStatic()); boost::hash_combine(seed, returnType()); for (const auto& parameterType: parameterTypes()) { boost::hash_combine(seed, parameterType); } return seed; }
SEM::TypeInstance* AddTypeInstance(Context& context, const AST::Node<AST::TypeInstance>& astTypeInstanceNode, const SEM::ModuleScope& moduleScope) { const auto parentNamespace = context.scopeStack().back().nameSpace(); const auto& typeInstanceName = astTypeInstanceNode->name; const Name fullTypeName = parentNamespace->name() + typeInstanceName; // Check if there's anything with the same name. const auto iterator = parentNamespace->items().find(typeInstanceName); if (iterator != parentNamespace->items().end()) { const auto& existingTypeInstance = iterator->second.typeInstance(); const auto& debugInfo = *(existingTypeInstance.debugInfo()); throw ErrorException(makeString("Type instance name '%s', at position %s, clashes with existing name, at position %s.", fullTypeName.toString().c_str(), astTypeInstanceNode.location().toString().c_str(), debugInfo.location.toString().c_str())); } const auto typeInstanceKind = ConvertTypeInstanceKind(astTypeInstanceNode->kind); // Create a placeholder type instance. std::unique_ptr<SEM::TypeInstance> semTypeInstance(new SEM::TypeInstance(context.semContext(), fullTypeName.copy(), typeInstanceKind, moduleScope.copy())); if (semTypeInstance->isPrimitive()) { semTypeInstance->setPrimitiveID(context.sharedMaps().primitiveIDMap().getPrimitiveID(typeInstanceName)); } switch (moduleScope.kind()) { case SEM::ModuleScope::INTERNAL: { if (semTypeInstance->isClassDecl()) { throw ErrorException(makeString("Definition required for internal class '%s', at location %s.", fullTypeName.toString().c_str(), astTypeInstanceNode.location().toString().c_str())); } break; } case SEM::ModuleScope::IMPORT: { if (semTypeInstance->isClassDef()) { throw ErrorException(makeString("Implementation not allowed of imported class '%s', at location %s.", fullTypeName.toString().c_str(), astTypeInstanceNode.location().toString().c_str())); } break; } case SEM::ModuleScope::EXPORT: { if (semTypeInstance->isClassDecl()) { throw ErrorException(makeString("Definition required for exported class '%s', at location %s.", fullTypeName.toString().c_str(), astTypeInstanceNode.location().toString().c_str())); } break; } } semTypeInstance->setDebugInfo(Debug::TypeInstanceInfo(astTypeInstanceNode.location())); // Add template variables. size_t templateVarIndex = 0; for (const auto& astTemplateVarNode: *(astTypeInstanceNode->templateVariables)) { const auto& templateVarName = astTemplateVarNode->name; // TODO! const bool isVirtual = (typeInstanceName == "__ref"); const auto semTemplateVar = new SEM::TemplateVar(context.semContext(), fullTypeName + templateVarName, templateVarIndex++, isVirtual); const auto templateVarIterator = semTypeInstance->namedTemplateVariables().find(templateVarName); if (templateVarIterator != semTypeInstance->namedTemplateVariables().end()) { throw ErrorException(makeString("More than one template variable shares name '%s' in type '%s', at location %s.", templateVarName.c_str(), fullTypeName.toString().c_str(), astTemplateVarNode.location().toString().c_str())); } semTemplateVar->setDebugInfo(makeTemplateVarInfo(astTemplateVarNode)); semTypeInstance->templateVariables().push_back(semTemplateVar); semTypeInstance->namedTemplateVariables().insert(std::make_pair(templateVarName, semTemplateVar)); } if (semTypeInstance->isUnionDatatype()) { for (auto& astVariantNode: *(astTypeInstanceNode->variants)) { const auto variantTypeInstance = AddTypeInstance(context, astVariantNode, moduleScope); variantTypeInstance->setParent(semTypeInstance.get()); variantTypeInstance->templateVariables() = semTypeInstance->templateVariables().copy(); variantTypeInstance->namedTemplateVariables() = semTypeInstance->namedTemplateVariables().copy(); semTypeInstance->variants().push_back(variantTypeInstance); } } if (!astTypeInstanceNode->noTagSet.isNull()) { SEM::TemplateVarArray noTagSet; for (const auto& astNoTagName: *(astTypeInstanceNode->noTagSet)) { const auto templateVarIterator = semTypeInstance->namedTemplateVariables().find(astNoTagName); if (templateVarIterator == semTypeInstance->namedTemplateVariables().end()) { throw ErrorException(makeString("Can't find template variable '%s' in notag() set in type '%s', at location %s.", astNoTagName.c_str(), fullTypeName.toString().c_str(), astTypeInstanceNode->noTagSet.location().toString().c_str())); } noTagSet.push_back(templateVarIterator->second); } semTypeInstance->setNoTagSet(std::move(noTagSet)); } const auto typeInstancePtr = semTypeInstance.get(); parentNamespace->items().insert(std::make_pair(typeInstanceName, SEM::NamespaceItem::TypeInstance(std::move(semTypeInstance)))); return typeInstancePtr; }
const Type* Alias::selfRefType(ValueArray templateArguments) const { assert(templateArguments.size() == templateVariables().size()); return Type::Alias(*this, std::move(templateArguments)); }
SEM::Value GetTemplatedMethodWithoutResolution(Context& context, SEM::Value value, const SEM::Type* const type, const String& methodName, SEM::ValueArray templateArguments, const Debug::SourceLocation& location) { assert(value.type()->isRef() && value.type()->isBuiltInReference()); if (!type->isObjectOrTemplateVar()) { throw ErrorException(makeString("Cannot get method '%s' for non-object type '%s' at position %s.", methodName.c_str(), type->toString().c_str(), location.toString().c_str())); } const auto methodSet = getTypeMethodSet(context, type); const auto& objectConstPredicate = methodSet->constPredicate(); const auto canonicalMethodName = CanonicalizeMethodName(methodName); const auto methodIterator = methodSet->find(canonicalMethodName); if (methodIterator == methodSet->end()) { throw ErrorException(makeString("Cannot find method '%s' for type '%s' at position %s.", methodName.c_str(), type->toString().c_str(), location.toString().c_str())); } const auto& methodElement = methodIterator->second; if (methodElement.isStatic()) { throw ErrorException(makeString("Cannot access static method '%s' for value of type '%s' at position %s.", methodName.c_str(), type->toString().c_str(), location.toString().c_str())); } auto templateVariableAssignments = type->generateTemplateVarMap(); const auto function = type->isObject() ? type->getObjectType()->functions().at(canonicalMethodName).get() : nullptr; if (function != nullptr) { const auto& templateVariables = function->templateVariables(); if (templateVariables.size() != templateArguments.size()) { // Try to apply some basic deduction... if (templateVariables.size() == 1 && templateArguments.size() == 0 && function->constPredicate().isVariable() && function->constPredicate().variableTemplateVar() == templateVariables[0] ) { const auto boolType = getBuiltInType(context, context.getCString("bool"), {}); templateArguments.push_back(SEM::Value::PredicateExpr(objectConstPredicate.copy(), boolType)); } else { throw ErrorException(makeString("Incorrect number of template " "arguments provided for method '%s'; %llu were required, " "but %llu were provided at position %s.", function->name().toString().c_str(), (unsigned long long) templateVariables.size(), (unsigned long long) templateArguments.size(), location.toString().c_str())); } } // Add function template variable => argument mapping. for (size_t i = 0; i < templateArguments.size(); i++) { const auto templateVariable = templateVariables.at(i); const auto& templateValue = templateArguments.at(i); if (templateValue.isTypeRef()) { const auto templateTypeValue = templateValue.typeRefType()->resolveAliases(); if (!templateTypeValue->isObjectOrTemplateVar() || templateTypeValue->isInterface()) { throw ErrorException(makeString("Invalid type '%s' passed " "as template parameter '%s' for method '%s' at position %s.", templateTypeValue->toString().c_str(), templateVariable->name().toString().c_str(), function->name().toString().c_str(), location.toString().c_str())); } templateVariableAssignments.insert(std::make_pair(templateVariable, SEM::Value::TypeRef(templateTypeValue, templateValue.type()))); } else { templateVariableAssignments.insert(std::make_pair(templateVariable, templateValue.copy())); } } } else { assert(templateArguments.empty()); } const auto methodConstPredicate = methodElement.constPredicate().substitute(templateVariableAssignments); if (!objectConstPredicate.implies(methodConstPredicate)) { throw ErrorException(makeString("Cannot refer to mutator method '%s' from const object of type '%s' at position %s.", methodName.c_str(), type->toString().c_str(), location.toString().c_str())); } // Now check the template arguments satisfy the requires predicate. const auto& requirePredicate = methodElement.requirePredicate(); // Conservatively assume require predicate is not satisified if result is undetermined. const bool satisfiesRequireDefault = false; if (!evaluatePredicateWithDefault(context, requirePredicate, templateVariableAssignments, satisfiesRequireDefault)) { throw ErrorException(makeString("Template arguments do not satisfy " "require predicate '%s' of method '%s' at position %s.", requirePredicate.substitute(templateVariableAssignments).toString().c_str(), methodName.c_str(), location.toString().c_str())); } if (function != nullptr) { const auto functionType = simplifyFunctionType(context, function->type().substitute(templateVariableAssignments)); const auto functionRefType = createFunctionPointerType(context, functionType); auto functionRef = addDebugInfo(SEM::Value::FunctionRef(type, function, std::move(templateArguments), functionRefType), location); if (type->isInterface()) { const auto interfaceMethodType = createInterfaceMethodType(context, functionType); return addDebugInfo(SEM::Value::InterfaceMethodObject(std::move(functionRef), std::move(value), interfaceMethodType), location); } else { const auto methodType = createMethodType(context, functionType); return addDebugInfo(SEM::Value::MethodObject(std::move(functionRef), std::move(value), methodType), location); } } else { const bool isTemplated = true; const auto functionType = methodElement.createFunctionType(isTemplated); const auto functionRefType = createFunctionPointerType(context, functionType); auto functionRef = addDebugInfo(SEM::Value::TemplateFunctionRef(type, methodName, functionRefType), location); const auto methodType = createMethodType(context, functionType); return addDebugInfo(SEM::Value::MethodObject(std::move(functionRef), std::move(value), methodType), location); } }
MethodSetElement MethodSetElement::withNoExceptPredicate(Predicate newNoExceptPredicate) const { return MethodSetElement(templateVariables().copy(), constPredicate().copy(), std::move(newNoExceptPredicate), requirePredicate().copy(), isStatic(), returnType(), parameterTypes().copy()); }
MethodSetElement MethodSetElement::withRequirement(Predicate requirement) const { return MethodSetElement(templateVariables().copy(), constPredicate().copy(), noexceptPredicate().copy(), Predicate::And(requirePredicate().copy(), std::move(requirement)), isStatic(), returnType(), parameterTypes().copy()); }
MethodSetElement MethodSetElement::copy() const { return MethodSetElement(templateVariables().copy(), constPredicate().copy(), noexceptPredicate().copy(), requirePredicate().copy(), isStatic(), returnType(), parameterTypes().copy()); }