From 31e443264ac621b812ec5a1a5b0f4b67e4cc25e3 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Thu, 22 Oct 2026 04:40:15 +0000 Subject: 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 --- lib/CodeGen/CallGen.cc | 277 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/CodeGen/CodeGen.cc | 259 --------------------------------------------- 2 files changed, 277 insertions(+), 259 deletions(-) create mode 100644 lib/CodeGen/CallGen.cc (limited to 'lib') 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 +#include + +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> &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; +} 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 &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> &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; -} -- cgit v1.3.1-4-g156e