aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 04:40:15 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 04:40:15 +0000
commit31e443264ac621b812ec5a1a5b0f4b67e4cc25e3 (patch)
tree17b80e85e00e68f715bcafd5743a749cb3a1e66b
parente1fea0e3b49f3b9b3e8a317ceb1938d3d02bf0a0 (diff)
refactor(codegen): move call marshalling and resolution into CallGen.cc
Pure move of both marshalCallArgs overloads, emitVariadicArgument and resolveCall out of CodeGen.cc; no behavior change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
-rw-r--r--AGENTS/ARCH/LAYOUT.md5
-rw-r--r--CMakeLists.txt1
-rw-r--r--lib/CodeGen/CallGen.cc277
-rw-r--r--lib/CodeGen/CodeGen.cc259
4 files changed, 281 insertions, 261 deletions
diff --git a/AGENTS/ARCH/LAYOUT.md b/AGENTS/ARCH/LAYOUT.md
index 6c70d3b..2586cf1 100644
--- a/AGENTS/ARCH/LAYOUT.md
+++ b/AGENTS/ARCH/LAYOUT.md
@@ -10,10 +10,11 @@
| `lib/Parser/ParserFile.cc` | OPENFILE/READFILE/WRITEFILE/CLOSEFILE parsing |
| `lib/Parser/ParserType.cc` | Type names, variable/array/constant declarations, TYPE/ENDTYPE and CLASS/ENDCLASS declarations, shared field and method parsing, SET/DEFINE reserved stubs |
| `include/cps/TemplateAST.h`, `lib/Parser/ParserType.cc` | TEMPLATE declarations, type parameter packs and canonical template ids; call and pack parsing also uses ParserExpression.cc/ParserFunction.cc |
-| `lib/CodeGen/CodeGen.cc` | Initialization, symbols, call arguments and main() compilation |
+| `lib/CodeGen/CodeGen.cc` | Initialization, symbols and main() compilation |
+| `lib/CodeGen/CallGen.cc` | Call-argument marshalling (`marshalCallArgs`, variadic arguments) and call resolution (`resolveCall`) |
| `lib/CodeGen/ExpressionGen.cc`, `StatementGen.cc`, `ControlFlowGen.cc` | Expression/statement dispatch and control-flow emission |
| `lib/CodeGen/OperatorGen.cc`, `include/cps/Operator.h` | Exact global/member operator selection, calls and temporary receivers; shared operator spellings |
-| `lib/CodeGen/TemplateGen.cc`, `TemplateClone.h`, `TemplateClone.cc` | Template registry validation, cached function/class instantiation, structural type substitution, pack expansion and deferred bodies; call deduction/selection lives in CodeGen.cc |
+| `lib/CodeGen/TemplateGen.cc`, `TemplateClone.h`, `TemplateClone.cc` | Template registry validation, cached function/class instantiation, structural type substitution, pack expansion and deferred bodies; call deduction/selection lives in CallGen.cc |
| `lib/CodeGen/ConstructorGen.cc` | Construction, copying, assignment, destruction and deferred object cleanup |
| `lib/CodeGen/GlobalGen.cc` | GLOBAL declarations and scalar/owning STRING initializers |
| `lib/CodeGen/ConstantGen.cc` | Literal constants, declaration conflicts and lexical binding restoration |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 32e6c1b..993c56b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,6 +56,7 @@ add_library(CPSCodeGen STATIC
lib/CodeGen/TemplateClone.cc
lib/CodeGen/TemplateGen.cc
lib/CodeGen/CodeGen.cc
+ lib/CodeGen/CallGen.cc
lib/CodeGen/ExpressionGen.cc
lib/CodeGen/OperatorGen.cc
lib/CodeGen/StatementGen.cc
diff --git a/lib/CodeGen/CallGen.cc b/lib/CodeGen/CallGen.cc
new file mode 100644
index 0000000..dfe89d6
--- /dev/null
+++ b/lib/CodeGen/CallGen.cc
@@ -0,0 +1,277 @@
+#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 <algorithm>
+#include <cstdarg>
+
+using namespace llvm;
+using namespace cps;
+
+
+// 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<std::unique_ptr<ExprAST>> &ArgExprs,
+ int Line,
+ std::vector<Value*> &Out) {
+ std::vector<ExprAST*> 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<ExprAST*> &ArgExprs,
+ int Line, std::vector<Value*> &Out) {
+ SaveAndRestore<int> 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<size_t> 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<VariableExprAST*>(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<VariableExprAST*>(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<std::unique_ptr<ExprAST>> &Args,
+ const std::vector<std::string> *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<ArgumentType> Actual;
+ std::string Key;
+ for (const auto &Arg : Args) {
+ const ArrayMetadata *Array = nullptr;
+ if (auto *Var = dynamic_cast<VariableExprAST *>(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<std::string>{});
+ SaveAndRestore<bool> 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;
+}
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 4137761..d81c4ff 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -373,262 +373,3 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) {
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<std::unique_ptr<ExprAST>> &ArgExprs,
- int Line,
- std::vector<Value*> &Out) {
- std::vector<ExprAST*> 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<ExprAST*> &ArgExprs,
- int Line, std::vector<Value*> &Out) {
- SaveAndRestore<int> 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<size_t> 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<VariableExprAST*>(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<VariableExprAST*>(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<std::unique_ptr<ExprAST>> &Args,
- const std::vector<std::string> *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<ArgumentType> Actual;
- std::string Key;
- for (const auto &Arg : Args) {
- const ArrayMetadata *Array = nullptr;
- if (auto *Var = dynamic_cast<VariableExprAST *>(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<std::string>{});
- SaveAndRestore<bool> 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;
-}