From c56877035d6600b463a9cbf0e339f7548c0b46fd Mon Sep 17 00:00:00 2001 From: _ov4 Date: Wed, 21 Oct 2026 05:54:26 +0000 Subject: feat(codegen): instantiate function templates by deduction Co-Authored-By: Claude Opus 5.5 --- lib/CodeGen/TemplateClone.cc | 238 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 lib/CodeGen/TemplateClone.cc (limited to 'lib/CodeGen/TemplateClone.cc') diff --git a/lib/CodeGen/TemplateClone.cc b/lib/CodeGen/TemplateClone.cc new file mode 100644 index 0000000..7374ee8 --- /dev/null +++ b/lib/CodeGen/TemplateClone.cc @@ -0,0 +1,238 @@ +#include "TemplateClone.h" +#include "cps/FileAST.h" +#include + +using namespace cps; +using namespace cps::detail; + +namespace { +template std::unique_ptr located(std::unique_ptr Copy, const U *Source) { + Copy->setLine(Source->getLine()); + return Copy; +} +} + +void TemplateClone::fail(int Line, const std::string &Message) { + Failed = true; + Error(Line, Message); +} + +std::string TemplateClone::type(const std::string &Name) { + size_t Position = 0; + auto Join = [](const std::vector &Parts) { + std::string Result; + for (const auto &Part : Parts) { + if (!Result.empty()) Result += ','; + Result += Part; + } + return Result; + }; + std::function()> Parse = [&]() -> std::vector { + size_t Start = Position; + while (Position < Name.size() && + (std::isalnum(static_cast(Name[Position])) || Name[Position] == '_')) ++Position; + if (Position == Start) { + fail(0, "Invalid canonical template type " + Name); + Position = Name.size(); + return {}; + } + std::string Base = Name.substr(Start, Position - Start); + bool HasArguments = Position < Name.size() && Name[Position] == '<'; + if (HasArguments) { + ++Position; + std::vector Arguments; + while (Position < Name.size() && Name[Position] != '>') { + auto Expanded = Parse(); + Arguments.insert(Arguments.end(), Expanded.begin(), Expanded.end()); + if (Position == Name.size() || Name[Position] != ',') break; + ++Position; + } + if (Position == Name.size() || Name[Position] != '>') { + fail(0, "Invalid canonical template type " + Name); + return {}; + } + ++Position; + Base += "<" + Join(Arguments) + ">"; + } + bool Expand = Name.compare(Position, 3, "...") == 0; + if (Expand) Position += 3; + auto Binding = Bindings.find(Base); + if (Binding == Bindings.end()) return {Base}; + if (Expand) return Binding->second; + if (Binding->second.size() != 1) { + fail(0, "A type pack requires an expansion"); + return {}; + } + return Binding->second; + }; + auto Types = Parse(); + if (Position != Name.size()) fail(0, "Invalid canonical template type " + Name); + return Join(Types); +} + +std::vector> TemplateClone::arguments( + const std::vector> &Args) { + std::vector> Result; + for (const auto &Arg : Args) { + if (auto *Pack = dynamic_cast(Arg.get())) { + auto Names = PackNames.find(Pack->getName()); + if (Names == PackNames.end()) fail(Pack->getLine(), "Unknown parameter pack " + Pack->getName()); + else for (const auto &Name : Names->second) + Result.push_back(located(std::make_unique(Name), Pack)); + } else Result.push_back(expression(Arg.get())); + } + return Result; +} + +std::vector> TemplateClone::body( + const std::vector> &Body) { + std::vector> Result; + for (const auto &Stmt : Body) Result.push_back(statement(Stmt.get())); + return Result; +} + +std::unique_ptr TemplateClone::expression(ExprAST *Expr) { + if (!Expr) return nullptr; + std::unique_ptr Copy; + if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getVal()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getVal()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getVal()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getVal()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getVal(), E->isCString()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getName()); + else if (auto *E = dynamic_cast(Expr)) { + std::vector> Indices; + for (const auto &Index : E->getIndices()) Indices.push_back(expression(Index.get())); + Copy = std::make_unique(E->getName(), std::move(Indices), E->getLine()); + } else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getOp(), expression(E->getOperand())); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(E->getOp(), expression(E->getLHS()), expression(E->getRHS()), E->getLine()); + else if (auto *E = dynamic_cast(Expr)) { + std::vector Types; + for (const auto &Arg : E->getTemplateArgs()) { + std::string Substituted = type(Arg); + // A template argument pack expands into separate argument slots. + size_t Start = 0, Depth = 0; + for (size_t I = 0; I <= Substituted.size(); ++I) { + if (I < Substituted.size() && Substituted[I] == '<') ++Depth; + if (I < Substituted.size() && Substituted[I] == '>') --Depth; + if (I == Substituted.size() || (!Depth && Substituted[I] == ',')) { + if (I > Start) Types.push_back(Substituted.substr(Start, I - Start)); + Start = I + 1; + } + } + } + Copy = std::make_unique(E->getCallee(), arguments(E->getArgs()), E->getLine(), std::move(Types)); + } else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(expression(E->getReceiver()), E->getMethod(), arguments(E->getArgs()), E->getLine()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(type(E->getClassName()), arguments(E->getArgs()), E->getLine()); + else if (auto *E = dynamic_cast(Expr)) Copy = std::make_unique(type(E->getTypeName()), expression(E->getOperand())); + else if (auto *E = dynamic_cast(Expr)) { + std::vector Accesses; + for (const auto &A : E->getAccesses()) { + DesignatorAccess CopyAccess{A.Kind, {}, A.FieldName}; + for (const auto &Index : A.Indices) CopyAccess.Indices.push_back(expression(Index.get())); + Accesses.push_back(std::move(CopyAccess)); + } + Copy = std::make_unique(E->getBaseName(), std::move(Accesses), E->getLine()); + } else if (auto *E = dynamic_cast(Expr)) { + auto Target = expression(E->getTarget()); + Copy = std::make_unique(std::unique_ptr(static_cast(Target.release()))); + } else if (dynamic_cast(Expr)) fail(Expr->getLine(), "A parameter pack expansion is only allowed in an argument list"); + else fail(Expr->getLine(), "Unsupported expression in template body"); + if (Copy) Copy->setLine(Expr->getLine()); + return Copy; +} + +std::unique_ptr TemplateClone::statement(StmtAST *Stmt) { + std::unique_ptr Copy; + if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(S->getName(), type(S->getType()), arguments(S->getArgs()), S->hasParens()); + else if (auto *S = dynamic_cast(Stmt)) { + std::vector, std::unique_ptr>> Bounds; + for (const auto &B : S->getBounds()) Bounds.emplace_back(expression(B.first.get()), expression(B.second.get())); + Copy = std::make_unique(S->getName(), std::move(Bounds), type(S->getType())); + } else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(S->getName(), expression(S->getValue())); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(S->getName(), expression(S->getExpr())); + else if (auto *S = dynamic_cast(Stmt)) { + std::vector> Indices; + for (const auto &Index : S->getIndices()) Indices.push_back(expression(Index.get())); + Copy = std::make_unique(S->getName(), std::move(Indices), expression(S->getExpr()), S->getLine()); + } else if (auto *S = dynamic_cast(Stmt)) { + auto Target = expression(S->getTarget()); + Copy = std::make_unique(std::unique_ptr(static_cast(Target.release())), expression(S->getExpr())); + } else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getTarget())); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getTarget())); + else if (auto *S = dynamic_cast(Stmt)) { + std::vector> Exprs; + for (const auto &E : S->getExprs()) Exprs.push_back(expression(E.get())); + Copy = std::make_unique(std::move(Exprs)); + } else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getRetVal())); + else if (auto *S = dynamic_cast(Stmt)) { + CallExprAST Call(S->getCallee(), {}, S->getLine(), S->getTemplateArgs()); + auto Expr = expression(&Call); + auto *Cloned = static_cast(Expr.get()); + Copy = std::make_unique(S->getCallee(), arguments(S->getArgs()), S->getLine(), Cloned->getTemplateArgs()); + } else if (auto *S = dynamic_cast(Stmt)) { + auto Call = expression(S->getCall()); + Copy = std::make_unique(std::unique_ptr(static_cast(Call.release()))); + } else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getCond()), body(S->getThenStmts()), body(S->getElseStmts())); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getCond()), body(S->getBody())); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(body(S->getBody()), expression(S->getCond())); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(S->getVarName(), expression(S->getStart()), expression(S->getEnd()), expression(S->getStep()), body(S->getBody())); + else if (auto *S = dynamic_cast(Stmt)) { + std::vector Branches; + for (const auto &B : S->getBranches()) Branches.push_back({expression(B.Lower.get()), expression(B.Upper.get()), body(B.Body), B.Line}); + Copy = std::make_unique(S->getSelector(), std::move(Branches)); + } else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(body(S->getBody()), body(S->getCatchBody())); + else if (dynamic_cast(Stmt)) Copy = std::make_unique(); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getFileName()), S->getMode(), S->getLine()); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getFileName()), S->getVarName(), S->getLine()); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getFileName()), expression(S->getData()), S->getLine()); + else if (auto *S = dynamic_cast(Stmt)) Copy = std::make_unique(expression(S->getFileName()), S->getLine()); + else if (dynamic_cast(Stmt)) fail(Stmt->getLine(), "Nested FUNCTION or PROCEDURE definitions are not allowed in template bodies"); + else fail(Stmt->getLine(), "Unsupported declaration in template body"); + if (Copy) Copy->setLine(Stmt->getLine()); + return Copy; +} + +std::unique_ptr TemplateClone::function(const FunctionDefAST *Source, + const std::string &Name, const std::string &OwnerClass) { + auto SavedNames = PackNames; + const auto *Proto = Source->getProto(); + std::vector Params; + for (const auto &P : Proto->getParams()) { + if (P.IsPack) { + auto Binding = Bindings.find(P.TypeName); + if (Binding == Bindings.end()) { fail(P.Line, "Unbound template parameter pack"); continue; } + auto &Names = PackNames[P.Name]; + Names.clear(); + for (size_t I = 0; I < Binding->second.size(); ++I) { + ParamDecl Copy = P; + Copy.Name += "#" + std::to_string(I + 1); + Copy.TypeName = Binding->second[I]; + Copy.IsPack = false; + Names.push_back(Copy.Name); + Params.push_back(std::move(Copy)); + } + } else { + Params.push_back(P); + Params.back().TypeName = type(P.TypeName); + } + } + auto CopyProto = std::make_unique(Name, std::move(Params), type(Proto->getReturnType()), + Proto->isExternal(), OwnerClass, Proto->isPublic(), Proto->isOverload()); + CopyProto->setOperator(Proto->getOperator()); + CopyProto->setVariadic(Proto->isVariadic()); + auto Copy = located(std::make_unique(std::move(CopyProto), body(Source->getBody())), Source); + PackNames = std::move(SavedNames); + return Failed ? nullptr : std::move(Copy); +} + +std::unique_ptr TemplateClone::klass(const ClassTypeDeclAST *Source, + const std::string &Name) { + std::vector Fields = Source->getFields(); + for (auto &Field : Fields) Field.TypeName = type(Field.TypeName); + std::vector> Methods; + for (const auto &Method : Source->getMethods()) + Methods.push_back(function(Method.get(), Method->getProto()->getName(), Name)); + auto Copy = located(std::make_unique(Name, std::move(Fields), std::move(Methods)), Source); + return Failed ? nullptr : std::move(Copy); +} -- cgit v1.3.1-4-g156e