#include "cps/Diagnostics.h" #include "cps/CodeGen.h" #include "cps/ArrayHandler.h" #include "cps/FileHandler.h" #include "cps/MemoryHandler.h" #include "cps/ExceptionHandler.h" #include "Builtins.h" #include "TemplateClone.h" #include "cps/TypeAST.h" #include "cps/TemplateAST.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Verifier.h" #include #include #include using namespace llvm; using namespace cps; using namespace cps::detail; CodeGen::~CodeGen() = default; bool CodeGen::isBuiltinName(const std::string &Name) { return findBuiltin(Name) != nullptr; } void CodeGen::reportError(const char *Fmt, ...) { va_list Args; va_start(Args, Fmt); cps::vsourceDiagnostic(cps::Severity::Error, DiagnosticLine, Fmt, Args); va_end(Args); HadError = true; } CodeGen::CodeGen() { TheContext = std::make_unique(); TheModule = std::make_unique("cps_module", *TheContext); Builder = std::make_unique>(*TheContext); Types = std::make_unique(*TheContext); Exceptions = std::make_unique(*TheContext, *Builder, *TheModule); RuntimeChecker = std::make_unique(*TheModule, *TheContext, *Builder, *Exceptions); Arrays = std::make_unique(*TheContext, *Builder, *TheModule, Symbols, *Types, *RuntimeChecker); FuncGen = std::make_unique(*TheContext, *TheModule, *Builder, *Types, *Exceptions, Symbols, HadError, DiagnosticLine); ArithHandler = std::make_unique(*TheContext, *Builder, HadError, *RuntimeChecker); Files = std::make_unique(*TheContext, *Builder, *TheModule, *RuntimeChecker, *Exceptions); Memory = std::make_unique(*TheContext, *Builder, *TheModule, *Types, *RuntimeChecker, *Exceptions); // Array parameters are bound into ArrayHandler through this inversion so // FunctionGen never sees the array subsystem. FuncGen->setArrayParamBinder( [this](const ParamDecl &P, Value *DataPtr, const std::vector &LBs, const std::vector &UBs) { Arrays->bindArrayParameter(P.Name, P.TypeName, DataPtr, LBs, UBs, /*MakeCopy=*/P.Mode == PassMode::ByVal, *this); }); FuncGen->setCopyRegistrar([this](Value *Addr, const std::string &TypeName, int Line) { activateStackObject(registerStackObject(Addr, TypeName, Line, false, true)); }); SetupExternalFunctions(); } void CodeGen::SetupExternalFunctions() { std::vector PrintfArgs; PrintfArgs.push_back(PointerType::getUnqual(*TheContext)); FunctionType *PrintfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true); PrintfFunc = TheModule->getOrInsertFunction("printf", PrintfType); FunctionType *ScanfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true); ScanfFunc = TheModule->getOrInsertFunction("scanf", ScanfType); PrintfFormatStr = Builder->CreateGlobalString("%lld\n", "fmt_nl", 0, TheModule.get()); PrintfFloatFormatStr = Builder->CreateGlobalString("%f\n", "fmt_flt", 0, TheModule.get()); PrintfStringFormatStr = Builder->CreateGlobalString("%s\n", "fmt_str", 0, TheModule.get()); PrintfCharFormatStr = Builder->CreateGlobalString("%c\n", "fmt_chr", 0, TheModule.get()); ScanfFormatStr = Builder->CreateGlobalString("%lld", "fmt_in", 0, TheModule.get()); ScanfFloatFormatStr = Builder->CreateGlobalString("%lf", "fmt_in_flt", 0, TheModule.get()); TrueStr = Builder->CreateGlobalString("TRUE", "str_true", 0, TheModule.get()); FalseStr = Builder->CreateGlobalString("FALSE", "str_false", 0, TheModule.get()); EmptyStringStr = Builder->CreateGlobalString("", "str_empty", 0, TheModule.get()); Arrays->setupExternalFunctions(); } AllocaInst *CodeGen::CreateEntryBlockAlloca(Function *TheFunction, const std::string &VarName) { return CreateEntryBlockAlloca(TheFunction, Type::getInt64Ty(*TheContext), VarName); } AllocaInst *CodeGen::CreateEntryBlockAlloca(Function *TheFunction, Type *AllocType, const std::string &VarName) { IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin()); return TmpB.CreateAlloca(AllocType, nullptr, VarName); } void CodeGen::registerSymbol(const std::string &Name, Value *Storage, const std::string &TypeName, bool IsArray) { Symbols[Name] = {Storage, TypeName, IsArray, nullptr, ConstantScopes.back().ID}; } const SymbolInfo *CodeGen::getSymbolInfo(const std::string &Name) const { auto It = Symbols.find(Name); if (It == Symbols.end()) { return nullptr; } return &It->second; } // A whole-array variable reference evaluates to the raw buffer pointer, which // the pointer->STRING coercion would accept unchanged; file-statement operands // must reject it explicitly or garbage gets treated as a C string. bool CodeGen::isWholeArrayVar(ExprAST *Expr) const { auto *Var = dynamic_cast(Expr); if (!Var) return false; const SymbolInfo *Info = getSymbolInfo(Var->getName()); return Info && Info->IsArray; } const TypeInfo *CodeGen::resolveType(const std::string &TypeName) const { return Types->resolve(TypeName); } bool CodeGen::isVisible(size_t Owner) const { return !Units || (*Units)[VisibilityUnit.value_or(CurrentUnit)].Visible[Owner]; } void CodeGen::selectUnit(size_t Unit) { CurrentUnit = Unit; DiagnosticSource = (*Units)[Unit].Path; FuncGen->setCurrentUnit(Unit, (*Units)[Unit].Path); RuntimeChecker->setLibraryMode((*Units)[Unit].IsLibrary); FuncGen->setCallHook([this](const FuncSig &Sig) { if (!(*Units)[CurrentUnit].IsLibrary && (*Units)[Sig.OwnerUnit].IsLibrary) RuntimeChecker->setUserLine(DiagnosticLine); }); } void CodeGen::importSymbols() { Symbols.clear(); for (size_t I = 0; I < Exports.size(); ++I) { if (!isVisible(I)) continue; for (const auto &[Name, Info] : Exports[I]) if (I != CurrentUnit || Info.IsGlobal) Symbols.emplace(Name, Info); } } void CodeGen::compile(const std::vector> &Statements) { compile(std::vector{{"", Statements, {true}}}); } void CodeGen::compile(const std::vector &Sources) { SaveAndRestore *> SourceContext(Units, &Sources); SaveAndRestore SourceName(DiagnosticSource); if (Sources.empty()) { reportError("no main program"); return; } Exports.resize(Sources.size()); Types->setVisibility([this](const std::string &Name) { if (FailedTemplateTypes.count(Name)) return false; auto It = TypeOwners.find(Name); return It == TypeOwners.end() || isVisible(It->second); }); Types->setResolveMiss([this](const std::string &Name) { return instantiateClass(Name); }); FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); }); struct Definition { size_t Unit; bool IsFunction; bool IsOverload; bool IsExtern; bool IsTemplate; int Line; }; IdentifierMap Definitions; std::optional CodeUnit; FunctionDefAST *Entry = nullptr; size_t EntryUnit = 0; auto Reserve = [&](const std::string &Name, bool IsFunction = false, bool IsOverload = false, bool IsExtern = false, bool IsTemplate = false) { auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload, IsExtern, IsTemplate, DiagnosticLine}); bool Compatible = (IsFunction && IsOverload && It->second.IsFunction && It->second.IsOverload) || (IsExtern && It->second.IsExtern); if (!Added && !Compatible && (It->second.Unit != CurrentUnit || ((IsTemplate || It->second.IsTemplate) && !(IsFunction && It->second.IsFunction)))) { // Default library units follow user inputs; point at the user's conflicting definition. bool UserConflict = Sources[CurrentUnit].IsLibrary && !Sources[It->second.Unit].IsLibrary; SaveAndRestore Location(DiagnosticLine, UserConflict ? It->second.Line : DiagnosticLine); SaveAndRestore Source(DiagnosticSource, UserConflict ? Sources[It->second.Unit].Path : DiagnosticSource); reportError("Duplicate definition %s in %s and %s", Name.c_str(), Sources[It->second.Unit].Path.c_str(), Sources[CurrentUnit].Path.c_str()); } }; for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); for (const auto &S : Sources[I].Statements) { SaveAndRestore Location(DiagnosticLine, S->getLine()); std::string TypeName; if (auto *T = dynamic_cast(S.get())) { auto *Function = T->getFunction(); Reserve(T->getName(), Function != nullptr, Function && Function->getProto()->isOverload(), false, true); Templates[T->getName()].push_back({T, I}); } else if (auto *F = dynamic_cast(S.get())) { Reserve(F->getProto()->getName(), true, F->getProto()->isOverload()); if (sameIdentifier(F->getProto()->getName(), "MAIN")) { if (Entry) reportError("Multiple MAIN definitions in %s and %s", Sources[EntryUnit].Path.c_str(), Sources[I].Path.c_str()); Entry = F; EntryUnit = I; if (!F->getProto()->getParams().empty() || !sameIdentifier(F->getProto()->getReturnType(), "INTEGER")) reportError("MAIN must be a FUNCTION with no parameters returning INTEGER"); } } else if (auto *X = dynamic_cast(S.get())) { Reserve(X->getProto()->getName(), true, false, true); } else if (dynamic_cast(S.get())) { } else if (auto *E = dynamic_cast(S.get())) { TypeName = E->getName(); for (const auto &V : E->getValueNames()) Reserve(V); } else if (auto *R = dynamic_cast(S.get())) { TypeName = R->getName(); } else if (auto *P = dynamic_cast(S.get())) { TypeName = P->getName(); } else if (auto *C = dynamic_cast(S.get())) { TypeName = C->getName(); } else if (auto *C = dynamic_cast(S.get())) { Reserve(C->getName()); } else if (auto *G = dynamic_cast(S.get())) { for (const auto &Body : G->getBody()) if (auto *D = dynamic_cast(Body.get())) Reserve(D->getName()); } else if (!dynamic_cast(S.get())) { if (CodeUnit && *CodeUnit != I) { reportError("Top-level code in both %s and %s", Sources[*CodeUnit].Path.c_str(), Sources[I].Path.c_str()); return; } CodeUnit = I; } if (!TypeName.empty()) { Reserve(TypeName); TypeOwners.emplace(TypeName, I); } } } if (HadError) return; if (Entry && CodeUnit) { selectUnit(EntryUnit); SaveAndRestore Location(DiagnosticLine, Entry->getLine()); reportError("FUNCTION MAIN in %s cannot coexist with top-level code in %s", Sources[EntryUnit].Path.c_str(), Sources[*CodeUnit].Path.c_str()); return; } MainUnit = Entry ? EntryUnit : CodeUnit.value_or(0); FunctionType *FT = FunctionType::get(Type::getInt32Ty(*TheContext), false); Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule.get()); BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", F); Builder->SetInsertPoint(BB); Exceptions->prepareFunction(F); BasicBlock *UnhandledBB = BasicBlock::Create(*TheContext, "unhandled_exception", F); Exceptions->pushHandler(UnhandledBB); TypePrePass = true; // Reserve names across every unit before resolving any type payload. for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); runTypePrePass(Sources[I].Statements, 0); } // Complete included units first. The visited set makes include cycles harmless. std::vector Visited(Sources.size(), false); std::function DefineTypes = [&](size_t I) { if (Visited[I]) return; Visited[I] = true; for (size_t J = 0; J < Sources.size(); ++J) if (J != I && Sources[I].Visible[J]) DefineTypes(J); selectUnit(I); runTypePrePass(Sources[I].Statements, 1); runTypePrePass(Sources[I].Statements, 2); }; for (size_t I = 0; I < Sources.size(); ++I) DefineTypes(I); TypePrePass = false; emitDeferredClassPrototypes(); for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); for (const auto &Stmt : Sources[I].Statements) { SaveAndRestore Location(DiagnosticLine, Stmt->getLine()); if (auto *FuncDef = dynamic_cast(Stmt.get())) FuncGen->emitPrototype(FuncDef->getProto()); if (auto *Extern = dynamic_cast(Stmt.get())) FuncGen->emitExternPrototype(Extern->getProto()); if (auto *Class = dynamic_cast(Stmt.get())) { emitClassPrototypes(Class); } } } validateTemplates(); // Constants are literal exports. Own constants are still installed only // when their declaration is encountered during body emission. for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); Symbols.clear(); for (const auto &Stmt : Sources[I].Statements) { if (auto *Constant = dynamic_cast(Stmt.get())) { SaveAndRestore Location(DiagnosticLine, Stmt->getLine()); emitConstantStmt(Constant); } } Exports[I] = Symbols; } for (int Phase = 0; Phase < 2; ++Phase) { for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); importSymbols(); runGlobalPrePass(Sources[I], Phase); } } captureTemplateSymbols(); for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); importSymbols(); for (const auto &Stmt : Sources[I].Statements) emitStmt(Stmt.get()); } drainTemplateBodies(); selectUnit(MainUnit); if (!CodeUnit && !Entry && !HadError) { SaveAndRestore Location(DiagnosticLine, Sources.size() > 1 ? 1 : 0); reportError("no main program"); } if (!Builder->GetInsertBlock()->hasTerminator()) { Value *Status = ConstantInt::get(*TheContext, APInt(32, 0)); if (Entry && !HadError) { Value *Result = Exceptions->emitCall(TheModule->getFunction("__cps_main"), {}, "main_result"); Status = Builder->CreateTrunc(Result, Type::getInt32Ty(*TheContext)); } Exceptions->recordExit(Builder->CreateRet(Status)); } finishStackObjects(F); auto *Exception = Exceptions->popHandler(); Builder->SetInsertPoint(UnhandledBB); Exceptions->beginCatch(Exception); Exceptions->endCatch(); Value *Msg = Builder->CreateGlobalString("Unhandled exception. Program terminated.\n", "err_unhandled", 0, TheModule.get()); RuntimeChecker->emitStderr(Msg); Builder->CreateRet(ConstantInt::get(Type::getInt32Ty(*TheContext), 1)); if (verifyModule(*TheModule, &errs())) { errs() << cps::diagnosticPrefix(cps::Severity::Fatal, "") << "LLVM module verification failed\n"; HadError = true; } } void CodeGen::print(raw_ostream &Output) { TheModule->print(Output, nullptr); } // Collects the arguments for a call to a user FUNCTION/PROCEDURE, dispatching // on the *declared* signature: BYREF parameters take the variable's alloca, // BYVAL parameters are evaluated and coerced to the declared parameter type. bool CodeGen::marshalCallArgs(const FuncSig &Signature, const std::vector> &ArgExprs, int Line, std::vector &Out) { std::vector Args; for (const auto &Arg : ArgExprs) Args.push_back(Arg.get()); return marshalCallArgs(Signature, Args, Line, Out); } bool CodeGen::marshalCallArgs(const FuncSig &Signature, const std::vector &ArgExprs, int Line, std::vector &Out) { SaveAndRestore Location(DiagnosticLine, Line ? Line : DiagnosticLine); const FuncSig *Sig = &Signature; const std::string &Callee = Sig->Name; if (Sig->IsVariadic ? ArgExprs.size() < Sig->Params.size() : ArgExprs.size() != Sig->Params.size()) { reportError("Incorrect # arguments passed to %s", Callee.c_str()); return false; } std::vector Transfers; for (size_t i = 0; i < ArgExprs.size(); ++i) { if (i >= Sig->Params.size()) { Value *V = emitVariadicArgument(ArgExprs[i], Callee, i + 1); if (!V) return false; Out.push_back(V); continue; } const ParamSig &P = Sig->Params[i]; const std::string &TypeName = P.TypeName; bool IsByRef = P.IsByRef; ExprAST *ArgExpr = ArgExprs[i]; if (P.IsArray) { if (!Arrays->emitArrayArgument(ArgExpr, P.TypeName, P.Rank, P.DeclaredBounds, Callee, Line, *this, Out)) { return false; } continue; } // A whole-array argument against a scalar parameter is always a user // error (and would smash the data pointer if let through). if (auto *Var = dynamic_cast(ArgExpr)) { const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (Info && Info->IsArray) { reportError("Cannot pass whole array %s to %s; the parameter is not an array (declare it ARRAY OF %s)", Var->getName().c_str(), Callee.c_str(), TypeName.c_str()); return false; } } if (IsByRef) { auto *Var = dynamic_cast(ArgExpr); bool InternalBinding = Signature.Prototype && (sameIdentifier(Signature.Prototype->getName(), "NEW") || Signature.Prototype->getOperator() == tok_assign); if (!Var && InternalBinding) { const TypeInfo *Actual = getExprTypeInfo(ArgExpr); if (!Actual || !Actual->sameType(resolveType(TypeName))) { reportError("BYREF argument to %s has an incompatible type", Callee.c_str()); return false; } if (isObjectLValue(ArgExpr)) { LValueInfo LV; if (!emitLValue(ArgExpr, LV)) return false; Out.push_back(LV.Addr); } else { Value *Storage = materializeOperatorReceiver(emitExpr(ArgExpr), Actual); if (!Storage) return false; Out.push_back(Storage); } continue; } if (!Var) { reportError("BYREF argument to %s must be a variable", Callee.c_str()); return false; } const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (!Info) { reportError("Unknown variable %s in BYREF call", Var->getName().c_str()); return false; } const TypeInfo *ParameterType = resolveType(TypeName); if (!(InternalBinding && ParameterType && ParameterType->isString()) && !requireWritable(*Info, Var->getName())) return false; // BYREF aliases the storage, so the types must match exactly. if (!Types->sameType(Info->TypeName, TypeName)) { reportError("BYREF argument %s to %s must be of type %s (got %s)", Var->getName().c_str(), Callee.c_str(), TypeName.c_str(), Info->TypeName.c_str()); return false; } Out.push_back(symbolAddress(*Info)); } else { Value *V = emitOwnedValue(ArgExpr, resolveType(TypeName), Transfers); if (!V) return false; Out.push_back(V); } } transferObjects(Transfers); return true; } // C default argument promotions for the variadic part of an EXTERN call; only // C values are accepted, matching the explicit-cast rule for C/pseudocode. Value *CodeGen::emitVariadicArgument(ExprAST *Expr, const std::string &Callee, size_t Position) { const TypeInfo *Info = isWholeArrayVar(Expr) ? nullptr : getExprTypeInfo(Expr); if (!Info || !Info->isC() || Info->isVoid()) { reportError("Variadic argument %zu to %s must be a C value; cast it to a C type", Position, Callee.c_str()); return nullptr; } Value *V = emitExpr(Expr); if (!V || Info->Kind != TypeKind::CScalar) return V; if (Info->isReal()) { const TypeInfo *Double = resolveType("C_DOUBLE"); return Info->CRank < Double->CRank ? convertNumericValue(V, Info, Double) : V; } return convertNumericValue(V, Info, Types->promoteCScalar(Info)); } // Resolve using static source types only; argument evaluation belongs to marshalling. const FuncSig *CodeGen::resolveCall(const std::string &Name, const std::vector> &Args, const std::vector *Explicit) { auto Overloads = FuncGen->getOverloads(Name); bool HasTemplates = hasFunctionTemplate(Name); bool HasExplicit = Explicit && !Explicit->empty(); if (HasExplicit && !HasTemplates) { reportError("Explicit template arguments require a function template: %s", Name.c_str()); return nullptr; } if (Overloads.empty() && !HasTemplates) { reportError("Call to undefined function %s", Name.c_str()); return nullptr; } if (!HasTemplates && Overloads.size() == 1 && !Overloads.front()->IsOverload) return Overloads.front(); // Visible EXTERN redeclarations are identical by construction. if (!HasTemplates && Overloads.front()->IsExternal) return Overloads.front(); struct ArgumentType { const TypeInfo *Type; int Rank; }; std::vector Actual; std::string Key; for (const auto &Arg : Args) { const ArrayMetadata *Array = nullptr; if (auto *Var = dynamic_cast(Arg.get())) { const SymbolInfo *Symbol = getSymbolInfo(Var->getName()); if (Symbol && Symbol->IsArray) Array = Arrays->getMetadata(Var->getName()); } const TypeInfo *Type = Array ? Types->resolve(Array->ElementTypeName) : getExprTypeInfo(Arg.get()); int Rank = Array ? Array->Rank : 0; Actual.push_back({Type, Rank}); if (!Key.empty()) Key += ", "; if (Rank) Key += "ARRAY" + std::to_string(Rank) + " OF "; Key += Type ? Type->Name : "?"; } const FuncSig *Ordinary = nullptr; for (const FuncSig *Sig : Overloads) { if (HasExplicit) break; if (Sig->Params.size() != Actual.size()) continue; bool Match = true; for (size_t I = 0; I < Actual.size(); ++I) { const ParamSig &P = Sig->Params[I]; if (Actual[I].Rank != (P.IsArray ? P.Rank : 0) || !Actual[I].Type || !Actual[I].Type->sameType(Types->resolve(P.TypeName))) { Match = false; break; } } if (Match) { if (Ordinary && HasTemplates) { reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str()); return nullptr; } Ordinary = Sig; if (!HasTemplates) return Sig; } } if (Ordinary) return Ordinary; if (HasTemplates) { // A matching non-variadic template wins before any variadic candidate. for (bool Variadic : {false, true}) { const TemplateDefinition *Selected = nullptr; detail::TemplateBindings SelectedBindings; for (const auto &Definition : Templates.at(Name)) { auto *Function = Definition.Decl->getFunction(); if (!Function || Definition.Decl->isVariadic() != Variadic || !isVisible(Definition.OwnerUnit)) continue; const auto &Params = Function->getProto()->getParams(); const auto &TemplateParams = Definition.Decl->getParams(); bool HasPackParam = !Params.empty() && Params.back().IsPack; size_t FixedCount = Params.size() - HasPackParam; if (HasPackParam ? Actual.size() < FixedCount : Actual.size() != FixedCount) continue; detail::TemplateBindings Bindings; for (const auto &Param : TemplateParams) Bindings.emplace(Param.Name, std::vector{}); SaveAndRestore Speculation(SpeculativeTemplateResolution, true); bool Match = true; if (HasExplicit) { size_t FixedTypes = TemplateParams.size() - Variadic; if (!Variadic && Explicit->size() > FixedTypes) continue; for (size_t I = 0; I < Explicit->size(); ++I) { auto Type = canonicalTemplateArgument((*Explicit)[I]); if (!Type) { Match = false; break; } Bindings.at(TemplateParams[std::min(I, FixedTypes)].Name).push_back(*Type); } } if (!Match) continue; for (size_t I = 0; I < FixedCount; ++I) { const auto &P = Params[I]; const TypeInfo *Type = Actual[I].Type; if (!Type || Type->isVoid() || Actual[I].Rank != (P.IsArray ? P.Rank : 0)) { Match = false; break; } auto Binding = Bindings.find(P.TypeName); if (Binding != Bindings.end()) { if (Binding->second.empty()) Binding->second.push_back(Type->Name); else if (!Type->sameType(resolveType(Binding->second.front()))) { Match = false; break; } } } if (HasPackParam) { auto &Pack = Bindings.at(Params.back().TypeName); // Explicit pack types constrain a prefix; deduction fills the rest. if (Pack.size() > Actual.size() - FixedCount) Match = false; for (size_t I = FixedCount; Match && I < Actual.size(); ++I) { const TypeInfo *Type = Actual[I].Type; size_t Index = I - FixedCount; if (!Type || Type->isVoid() || Actual[I].Rank) { Match = false; break; } if (Index < Pack.size()) { if (!Type->sameType(resolveType(Pack[Index]))) Match = false; } else Pack.push_back(Type->Name); } } for (const auto &Param : TemplateParams) if (!Param.IsPack && Bindings.at(Param.Name).empty()) Match = false; if (!Match) continue; // Composite parameter types can use bindings deduced at later positions, // but do not themselves contribute deductions. detail::TemplateClone Substitute(Bindings, [&](int, const std::string &) { Match = false; }); for (size_t I = 0; I < FixedCount; ++I) { const auto &TypeName = Params[I].TypeName; auto Binding = Bindings.find(TypeName); std::string Expected = Binding != Bindings.end() ? Binding->second.front() : TypeName.find('<') != std::string::npos ? Substitute.type(TypeName) : TypeName; if (!Match || !Actual[I].Type->sameType(resolveType(Expected))) { Match = false; break; } } if (!Match) continue; if (Selected) { reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str()); return nullptr; } Selected = &Definition; SelectedBindings = std::move(Bindings); } if (Selected) return instantiateFunction(*Selected, SelectedBindings); } } reportError("No matching overload for %s(%s)", Name.c_str(), Key.c_str()); return nullptr; }