#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); // Pointers to C structs pass unchanged; records by value are not supported yet. if (!Info || Info->isRecord() || !Types->isExternType(Info)) { 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; }