From 377a1fd2eadd2a70cd27f13e2fee6e96c8706a51 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 10:32:30 +0000 Subject: feat(codegen): overload global functions and procedures Co-Authored-By: Claude Opus 5.5 --- include/cps/ArrayHandler.h | 2 +- include/cps/CodeGen.h | 6 +- include/cps/FunctionGen.h | 15 ++++- lib/CodeGen/CodeGen.cc | 69 +++++++++++++++++----- lib/CodeGen/ConstantGen.cc | 2 +- lib/CodeGen/Designator.cc | 5 +- lib/CodeGen/ExpressionGen.cc | 5 +- lib/CodeGen/FunctionGen.cc | 67 ++++++++++++++------- lib/CodeGen/GlobalGen.cc | 2 +- lib/CodeGen/StatementGen.cc | 2 +- lib/CodeGen/ValueTypeGen.cc | 15 +---- tests/i/multifile-overloads/case.json | 7 +++ tests/i/multifile-overloads/expected.txt | 2 + tests/i/multifile-overloads/integer.cps | 3 + tests/i/multifile-overloads/notes.md | 7 +++ tests/i/multifile-overloads/program.cps | 4 ++ tests/i/multifile-overloads/real.cps | 3 + tests/i/overload-functions/case.json | 1 + tests/i/overload-functions/expected.txt | 10 ++++ tests/i/overload-functions/notes.md | 9 +++ tests/i/overload-functions/program.cps | 49 +++++++++++++++ tests/iii/multifile-overload-duplicate/case.json | 14 +++++ tests/iii/multifile-overload-duplicate/first.cps | 2 + tests/iii/multifile-overload-duplicate/program.cps | 1 + tests/iii/multifile-overload-duplicate/second.cps | 2 + tests/iii/multifile-overload-visibility/case.json | 14 +++++ .../iii/multifile-overload-visibility/integer.cps | 3 + .../iii/multifile-overload-visibility/program.cps | 2 + tests/iii/multifile-overload-visibility/real.cps | 3 + tests/iii/overload-errors/case.json | 36 +++++++++++ tests/iii/overload-errors/program.cps | 29 +++++++++ tests/iii/overload-main/case.json | 8 +++ tests/iii/overload-main/program.cps | 3 + 33 files changed, 342 insertions(+), 60 deletions(-) create mode 100644 tests/i/multifile-overloads/case.json create mode 100644 tests/i/multifile-overloads/expected.txt create mode 100644 tests/i/multifile-overloads/integer.cps create mode 100644 tests/i/multifile-overloads/notes.md create mode 100644 tests/i/multifile-overloads/program.cps create mode 100644 tests/i/multifile-overloads/real.cps create mode 100644 tests/i/overload-functions/case.json create mode 100644 tests/i/overload-functions/expected.txt create mode 100644 tests/i/overload-functions/notes.md create mode 100644 tests/i/overload-functions/program.cps create mode 100644 tests/iii/multifile-overload-duplicate/case.json create mode 100644 tests/iii/multifile-overload-duplicate/first.cps create mode 100644 tests/iii/multifile-overload-duplicate/program.cps create mode 100644 tests/iii/multifile-overload-duplicate/second.cps create mode 100644 tests/iii/multifile-overload-visibility/case.json create mode 100644 tests/iii/multifile-overload-visibility/integer.cps create mode 100644 tests/iii/multifile-overload-visibility/program.cps create mode 100644 tests/iii/multifile-overload-visibility/real.cps create mode 100644 tests/iii/overload-errors/case.json create mode 100644 tests/iii/overload-errors/program.cps create mode 100644 tests/iii/overload-main/case.json create mode 100644 tests/iii/overload-main/program.cps diff --git a/include/cps/ArrayHandler.h b/include/cps/ArrayHandler.h index 843bdc8..e678af4 100644 --- a/include/cps/ArrayHandler.h +++ b/include/cps/ArrayHandler.h @@ -40,7 +40,6 @@ class ArrayHandler { llvm::Value *computeFlatIndex(const std::string &Name, const std::vector &Indices); llvm::Value *getArrayBasePointer(const std::string &Name); llvm::Value *getElementPointer(const std::string &Name, llvm::Value *Offset); - const ArrayMetadata *getMetadata(const std::string &Name) const; // Shared bounds math: DimSize_i = (Upper_i - Lower_i) + 1, row-major // multipliers back-to-front, TotalElements = product of the sizes. void computeDimsAndMultipliers(const std::string &Name, @@ -68,6 +67,7 @@ public: TypeSystem &TS, RuntimeCheck &RC); + const ArrayMetadata *getMetadata(const std::string &Name) const; void setupExternalFunctions(); void emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG); diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index d624ca6..463c613 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -120,7 +120,7 @@ class CodeGen { void registerSymbol(const std::string &Name, llvm::Value *Storage, const std::string &TypeName, bool IsArray = false); const SymbolInfo *getSymbolInfo(const std::string &Name) const; bool isWholeArrayVar(ExprAST *Expr) const; - const TypeInfo *getExprTypeInfo(ExprAST *Expr) const; + const TypeInfo *getExprTypeInfo(ExprAST *Expr); llvm::Value *coerceValueToType(llvm::Value *Val, const TypeInfo *TargetInfo); llvm::Value *convertNumericValue(llvm::Value *Val, const TypeInfo *Source, const TypeInfo *Target); @@ -150,7 +150,9 @@ class CodeGen { llvm::Value *emitAllocationSize(ExprAST *Expr); llvm::Value *emitCall(const std::string &Callee, const std::vector> &ArgExprs, int Line); - bool marshalCallArgs(const std::string &Callee, + const FuncSig *resolveCall(const std::string &Name, + const std::vector> &Args); + bool marshalCallArgs(const FuncSig &Signature, const std::vector> &ArgExprs, int Line, std::vector &Out); diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h index eb4a839..c168e1c 100644 --- a/include/cps/FunctionGen.h +++ b/include/cps/FunctionGen.h @@ -36,6 +36,10 @@ struct ParamSig { // back from LLVM types. struct FuncSig { std::string Name; + std::string LinkName; + std::string ParamKey; + bool IsOverload = false; + const PrototypeAST *Prototype = nullptr; std::string ReturnTypeName; std::vector Params; size_t OwnerUnit = 0; @@ -63,7 +67,10 @@ private: int &DiagnosticLine; void reportError(const char *Fmt, ...); - IdentifierMap Signatures; + IdentifierMap> Signatures; + IdentifierMap> LinkIndex; + std::map PrototypeLinks; + std::map UnitPaths; size_t CurrentUnit = 0; std::function Visible; ArrayParamBinder BindArrayParam; @@ -89,9 +96,11 @@ public: RegisterCopy = std::move(Registrar); } - void setCurrentUnit(size_t Unit) { CurrentUnit = Unit; } + void setCurrentUnit(size_t Unit, const std::string &Path) { CurrentUnit = Unit; UnitPaths[Unit] = Path; } void setVisibility(std::function Predicate) { Visible = std::move(Predicate); } - const FuncSig *getSignature(const std::string &Name) const; + std::vector getOverloads(const std::string &Name) const; + bool hasFunction(const std::string &Name) const { return !getOverloads(Name).empty(); } + const FuncSig *getSignatureByLink(const std::string &Name) const; llvm::Function *emitPrototype(PrototypeAST *Proto); diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index b81fe2b..9f59a4d 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -160,7 +160,7 @@ bool CodeGen::isVisible(size_t Owner) const { void CodeGen::selectUnit(size_t Unit) { CurrentUnit = Unit; DiagnosticSource = Units->size() > 1 ? (*Units)[Unit].Path : ""; - FuncGen->setCurrentUnit(Unit); + FuncGen->setCurrentUnit(Unit, (*Units)[Unit].Path); } void CodeGen::importSymbols() { @@ -187,15 +187,17 @@ void CodeGen::compile(const std::vector &Sources) { }); FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); }); - IdentifierMap Definitions; + struct Definition { size_t Unit; bool IsFunction; bool IsOverload; }; + IdentifierMap Definitions; std::optional CodeUnit; FunctionDefAST *Entry = nullptr; size_t EntryUnit = 0; - auto Reserve = [&](const std::string &Name) { - auto [It, Added] = Definitions.emplace(Name, CurrentUnit); - if (!Added && It->second != CurrentUnit) + auto Reserve = [&](const std::string &Name, bool IsFunction = false, bool IsOverload = false) { + auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload}); + bool Compatible = IsFunction && IsOverload && It->second.IsFunction && It->second.IsOverload; + if (!Added && It->second.Unit != CurrentUnit && !Compatible) reportError("Duplicate definition %s in %s and %s", Name.c_str(), - Sources[It->second].Path.c_str(), Sources[CurrentUnit].Path.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); @@ -203,7 +205,7 @@ void CodeGen::compile(const std::vector &Sources) { SaveAndRestore Location(DiagnosticLine, S->getLine()); std::string TypeName; if (auto *F = dynamic_cast(S.get())) { - Reserve(F->getProto()->getName()); + 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()); @@ -326,7 +328,7 @@ void CodeGen::compile(const std::vector &Sources) { if (!Builder->GetInsertBlock()->hasTerminator()) { Value *Status = ConstantInt::get(*TheContext, APInt(32, 0)); - if (Entry) { + if (Entry && !HadError) { Value *Result = Exceptions->emitCall(TheModule->getFunction("__cps_main"), {}, "main_result"); Status = Builder->CreateTrunc(Result, Type::getInt32Ty(*TheContext)); } @@ -356,16 +358,13 @@ void CodeGen::print(raw_ostream &Output) { // 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 std::string &Callee, +bool CodeGen::marshalCallArgs(const FuncSig &Signature, const std::vector> &ArgExprs, int Line, std::vector &Out) { SaveAndRestore Location(DiagnosticLine, Line ? Line : DiagnosticLine); - const FuncSig *Sig = FuncGen->getSignature(Callee); - if (!Sig) { - reportError("Call to undefined function %s", Callee.c_str()); - return false; - } + const FuncSig *Sig = &Signature; + const std::string &Callee = Sig->Name; if (ArgExprs.size() != Sig->Params.size()) { reportError("Incorrect # arguments passed to %s", Callee.c_str()); return false; @@ -424,3 +423,45 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, } return true; } + +// Resolve using static source types only; argument evaluation belongs to marshalling. +const FuncSig *CodeGen::resolveCall(const std::string &Name, + const std::vector> &Args) { + auto Overloads = FuncGen->getOverloads(Name); + if (Overloads.empty()) { + reportError("Call to undefined function %s", Name.c_str()); + return nullptr; + } + if (Overloads.size() == 1 && !Overloads.front()->IsOverload) 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 : "?"; + } + for (const FuncSig *Sig : Overloads) { + 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) return Sig; + } + reportError("No matching overload for %s(%s)", Name.c_str(), Key.c_str()); + return nullptr; +} diff --git a/lib/CodeGen/ConstantGen.cc b/lib/CodeGen/ConstantGen.cc index 6e1ec57..fc5d178 100644 --- a/lib/CodeGen/ConstantGen.cc +++ b/lib/CodeGen/ConstantGen.cc @@ -60,7 +60,7 @@ void CodeGen::emitConstantStmt(ConstantStmtAST *Stmt) { return; } if (ConstantScopes.back().ID == 0 && - (FuncGen->getSignature(Name) || isBuiltinName(Name))) { + (FuncGen->hasFunction(Name) || isBuiltinName(Name))) { reportError("Constant %s conflicts with a function name", Name.c_str()); return; } diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index b31492b..540059d 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -97,8 +97,9 @@ Value *CodeGen::emitMethodCall(const std::string &Class, Value *Receiver, return nullptr; } std::vector Args = {Receiver}; - if (!marshalCallArgs(Method->getLinkName(), ArgExprs, Line, Args)) return nullptr; - return FuncGen->emitCall(Method->getLinkName(), Args); + const FuncSig *Sig = resolveCall(Method->getLinkName(), ArgExprs); + if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr; + return FuncGen->emitCall(Sig->LinkName, Args); } // Resolves a designator (or legacy variable/array-access node) to the address diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index ba5e329..b667e6a 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -299,8 +299,9 @@ Value *CodeGen::emitCall(const std::string &Callee, } std::vector Args; - if (!marshalCallArgs(Name, ArgExprs, Line, Args)) return nullptr; - return FuncGen->emitCall(Name, Args); + const FuncSig *Sig = resolveCall(Name, ArgExprs); + if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr; + return FuncGen->emitCall(Sig->LinkName, Args); } Value *CodeGen::emitAllocationSize(ExprAST *Expr) { diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index d985bce..b4b37d3 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -104,13 +104,28 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vector FunctionGen::getOverloads(const std::string &Name) const { + std::vector Result; auto It = Signatures.find(sameIdentifier(Name, "MAIN") ? "__cps_main" : Name); - return It == Signatures.end() || (Visible && !Visible(It->second.OwnerUnit)) - ? nullptr : &It->second; + if (It != Signatures.end()) + for (const FuncSig &Sig : It->second) + if (!Visible || Visible(Sig.OwnerUnit)) Result.push_back(&Sig); + return Result; +} + +const FuncSig *FunctionGen::getSignatureByLink(const std::string &Name) const { + auto It = LinkIndex.find(Name); + if (It == LinkIndex.end()) return nullptr; + return &Signatures.at(It->second.first)[It->second.second]; } Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { + auto Registered = PrototypeLinks.find(Proto); + if (Registered != PrototypeLinks.end()) return Module.getFunction(Registered->second); + if (Proto->isOverload() && Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN")) { + reportError("MAIN cannot be declared OVERLOAD"); + return nullptr; + } if (Proto->getOwnerClass().empty()) { if (isReservedRuntimeName(Proto->getName())) { reportError("'%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name", @@ -137,6 +152,8 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { FuncSig Sig; Sig.OwnerUnit = CurrentUnit; Sig.Name = Proto->getLinkName(); + Sig.IsOverload = Proto->isOverload(); + Sig.Prototype = Proto; Sig.ReturnTypeName = Proto->getReturnType(); std::vector ArgTypes; @@ -167,29 +184,38 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { } ArgTypes.push_back(T); } + if (!Sig.ParamKey.empty()) Sig.ParamKey += ","; + if (P.IsArray) Sig.ParamKey += "ARRAY" + std::to_string(P.Rank) + " OF "; + Sig.ParamKey += Info ? (Info->isC() ? "C:" + Info->CIdentity : Info->Name) : P.TypeName; Sig.Params.push_back(std::move(PS)); } Type *RetType = getLLVMType(Proto->getReturnType()); FunctionType *FT = FunctionType::get(RetType, ArgTypes, false); - const FuncSig *Existing = getSignature(Proto->getLinkName()); - Function *F = Module.getFunction(Existing ? Existing->Name : Proto->getLinkName()); - if (F && F->getFunctionType() != FT) { - reportError("Function %s conflicts with an existing declaration of a different type", - Proto->getName().c_str()); - return nullptr; - } - if (!F) { - F = Function::Create(FT, - Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN") - ? Function::InternalLinkage : Function::ExternalLinkage, - Proto->getLinkName(), &Module); + auto &Overloads = Signatures[Sig.Name]; + for (const FuncSig &Existing : Overloads) { + if (!Sig.IsOverload || !Existing.IsOverload) { + reportError("Function %s cannot be redefined. Multiple definition of %s; every overload must be declared OVERLOAD", + Proto->getName().c_str(), Proto->getName().c_str()); + return nullptr; + } + if (sameIdentifier(Existing.ParamKey, Sig.ParamKey)) { + if (Existing.OwnerUnit != CurrentUnit) + reportError("Duplicate definition %s in %s and %s", Sig.Name.c_str(), + UnitPaths[Existing.OwnerUnit].c_str(), UnitPaths[CurrentUnit].c_str()); + else reportError("Function %s cannot be redefined", Proto->getName().c_str()); + return nullptr; + } } + Sig.LinkName = Sig.Name + (Sig.IsOverload ? "(" + Sig.ParamKey + ")" : ""); + Function *F = Function::Create(FT, + Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN") + ? Function::InternalLinkage : Function::ExternalLinkage, + Sig.LinkName, &Module); Exceptions.prepareFunction(F); - - // Keep the first registration: on a duplicate definition the first one - // wins and the second is rejected by emitFunctionDef. - Signatures.emplace(Proto->getLinkName(), std::move(Sig)); + PrototypeLinks.emplace(Proto, Sig.LinkName); + LinkIndex.emplace(Sig.LinkName, std::make_pair(Sig.Name, Overloads.size())); + Overloads.push_back(std::move(Sig)); Function::arg_iterator AI = F->arg_begin(); if (!Proto->getOwnerClass().empty()) (AI++)->setName("this"); @@ -280,8 +306,7 @@ static Value *GenerateCall(llvm::Module &Module, } llvm::Value *FunctionGen::emitCall(const std::string &Callee, const std::vector &Args) { - const FuncSig *Sig = getSignature(Callee); - return GenerateCall(Module, Exceptions, Sig ? Sig->Name : Callee, Args, HadError, DiagnosticLine); + return GenerateCall(Module, Exceptions, Callee, Args, HadError, DiagnosticLine); } void FunctionGen::emitReturn(ReturnStmtAST *Ret, llvm::Value *RetVal) { diff --git a/lib/CodeGen/GlobalGen.cc b/lib/CodeGen/GlobalGen.cc index 659ae85..46b6f0c 100644 --- a/lib/CodeGen/GlobalGen.cc +++ b/lib/CodeGen/GlobalGen.cc @@ -34,7 +34,7 @@ void CodeGen::runGlobalPrePass(const SourceUnit &Unit, int Phase) { continue; } if (Exports[CurrentUnit].count(Name) || Types->resolve(Name) || - Types->lookupEnumConstant(Name) || FuncGen->getSignature(Name) || + Types->lookupEnumConstant(Name) || FuncGen->hasFunction(Name) || isBuiltinName(Name)) { reportError("Global %s conflicts with an existing definition", Name.c_str()); continue; diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc index 1a9c285..8783cde 100644 --- a/lib/CodeGen/StatementGen.cc +++ b/lib/CodeGen/StatementGen.cc @@ -100,7 +100,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) { if (auto *Ret = dynamic_cast(Stmt)) { Function *F = Builder->GetInsertBlock()->getParent(); const FuncSig *Sig = F->getName() == "main" ? nullptr - : FuncGen->getSignature(F->getName().str()); + : FuncGen->getSignatureByLink(F->getName().str()); // The generated wrapper has no signature; user MAIN uses __cps_main. if (!Sig) { reportError("RETURN is only allowed inside a FUNCTION or PROCEDURE"); diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc index 5e926cf..d2bba03 100644 --- a/lib/CodeGen/ValueTypeGen.cc +++ b/lib/CodeGen/ValueTypeGen.cc @@ -8,7 +8,7 @@ using namespace cps; using namespace cps::detail; -const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { +const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) { if (!Expr) return nullptr; if (dynamic_cast(Expr)) return resolveType("INTEGER"); @@ -129,20 +129,11 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { if (const BuiltinInfo *B = findBuiltin(Name)) { return resolveType(B->ReturnTypeName); } - if (const FuncSig *Sig = FuncGen->getSignature(Name)) { + if (const FuncSig *Sig = resolveCall(Name, Call->getArgs())) { return resolveType(Sig->ReturnTypeName); } - Function *CalleeF = TheModule->getFunction(Name); - if (!CalleeF) return resolveType("INTEGER"); - - Type *RetTy = CalleeF->getReturnType(); - if (RetTy->isVoidTy()) return resolveType("VOID"); - if (RetTy->isDoubleTy()) return resolveType("REAL"); - if (RetTy->isIntegerTy(1)) return resolveType("BOOLEAN"); - if (RetTy->isIntegerTy(8)) return resolveType("CHAR"); - if (RetTy->isPointerTy()) return resolveType("STRING"); - return resolveType("INTEGER"); + return nullptr; } return nullptr; diff --git a/tests/i/multifile-overloads/case.json b/tests/i/multifile-overloads/case.json new file mode 100644 index 0000000..dcbd7a4 --- /dev/null +++ b/tests/i/multifile-overloads/case.json @@ -0,0 +1,7 @@ +{ + "sources": [ + "program.cps", + "integer.cps", + "real.cps" + ] +} diff --git a/tests/i/multifile-overloads/expected.txt b/tests/i/multifile-overloads/expected.txt new file mode 100644 index 0000000..6cf72e1 --- /dev/null +++ b/tests/i/multifile-overloads/expected.txt @@ -0,0 +1,2 @@ +6 +TRUE diff --git a/tests/i/multifile-overloads/integer.cps b/tests/i/multifile-overloads/integer.cps new file mode 100644 index 0000000..d2fd6d4 --- /dev/null +++ b/tests/i/multifile-overloads/integer.cps @@ -0,0 +1,3 @@ +OVERLOAD FUNCTION Area(x : INTEGER) RETURNS INTEGER +RETURN x * 2 +ENDFUNCTION diff --git a/tests/i/multifile-overloads/notes.md b/tests/i/multifile-overloads/notes.md new file mode 100644 index 0000000..9a13837 --- /dev/null +++ b/tests/i/multifile-overloads/notes.md @@ -0,0 +1,7 @@ +# multifile-overloads + +Human review: pending. Expected output was independently traced by AI, not captured from cpsc. + +Basis: OVERLOAD is a project extension. + +Trace: Both libraries are visible: INTEGER 3 doubles to 6; REAL 2.0 compares greater than 1.0 and returns TRUE. diff --git a/tests/i/multifile-overloads/program.cps b/tests/i/multifile-overloads/program.cps new file mode 100644 index 0000000..35a9555 --- /dev/null +++ b/tests/i/multifile-overloads/program.cps @@ -0,0 +1,4 @@ +INCLUDE "integer.cps" +INCLUDE "real.cps" +OUTPUT Area(3) +OUTPUT Area(2.0) diff --git a/tests/i/multifile-overloads/real.cps b/tests/i/multifile-overloads/real.cps new file mode 100644 index 0000000..3b5116a --- /dev/null +++ b/tests/i/multifile-overloads/real.cps @@ -0,0 +1,3 @@ +OVERLOAD FUNCTION Area(x : REAL) RETURNS BOOLEAN +RETURN x > 1.0 +ENDFUNCTION diff --git a/tests/i/overload-functions/case.json b/tests/i/overload-functions/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/overload-functions/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/overload-functions/expected.txt b/tests/i/overload-functions/expected.txt new file mode 100644 index 0000000..65c760e --- /dev/null +++ b/tests/i/overload-functions/expected.txt @@ -0,0 +1,10 @@ +3 +TRUE +pair +text +7 +9 +TRUE +3 +5 +TRUE diff --git a/tests/i/overload-functions/notes.md b/tests/i/overload-functions/notes.md new file mode 100644 index 0000000..dcfdde3 --- /dev/null +++ b/tests/i/overload-functions/notes.md @@ -0,0 +1,9 @@ +# overload-functions + +Human review: pending. Expected output was independently traced by AI, not captured from cpsc. + +Basis: OVERLOAD is a project extension. + +Trace: INTEGER adds one; positive REAL returns TRUE; two INTEGERs return pair; STRING prints text. Rank-one INTEGER, rank-two INTEGER and REAL arrays select distinct overloads and yield 7, 9 and TRUE. Nested Pick(1) returns INTEGER 2, so the outer call yields 3. + +Bump selects INTEGER and mutates 4 to 5 through BYREF. The ordinary Legacy function still converts INTEGER 1 to REAL and returns TRUE. diff --git a/tests/i/overload-functions/program.cps b/tests/i/overload-functions/program.cps new file mode 100644 index 0000000..e76637d --- /dev/null +++ b/tests/i/overload-functions/program.cps @@ -0,0 +1,49 @@ +OVERLOAD FUNCTION Pick(x : INTEGER) RETURNS INTEGER +RETURN x + 1 +ENDFUNCTION +OVERLOAD FUNCTION Pick(x : REAL) RETURNS BOOLEAN +RETURN x > 0 +ENDFUNCTION +OVERLOAD FUNCTION Pick(x : INTEGER, y : INTEGER) RETURNS STRING +RETURN "pair" +ENDFUNCTION +OVERLOAD PROCEDURE Pick(x : STRING) +OUTPUT x +ENDPROCEDURE +OVERLOAD FUNCTION Pick(a : ARRAY OF INTEGER) RETURNS INTEGER +RETURN a[1] +ENDFUNCTION +OVERLOAD FUNCTION Pick(a : ARRAY[1:2,1:2] OF INTEGER) RETURNS INTEGER +RETURN a[2,2] +ENDFUNCTION +OVERLOAD FUNCTION Pick(a : ARRAY OF REAL) RETURNS BOOLEAN +RETURN a[1] > 0 +ENDFUNCTION +DECLARE a : ARRAY[1:2] OF INTEGER +DECLARE b : ARRAY[1:2,1:2] OF INTEGER +DECLARE c : ARRAY[1:2] OF REAL +a[1] <- 7 +b[2,2] <- 9 +c[1] <- 1.0 +OUTPUT pIcK(2) +OUTPUT Pick(2.0) +OUTPUT Pick(1, 2) +CALL Pick("text") +OUTPUT Pick(a) +OUTPUT Pick(b) +OUTPUT Pick(c) +OUTPUT Pick(Pick(1)) +OVERLOAD PROCEDURE Bump(BYREF x : INTEGER) +x <- x + 1 +ENDPROCEDURE +OVERLOAD PROCEDURE Bump(BYREF x : REAL) +x <- x + 2.0 +ENDPROCEDURE +FUNCTION Legacy(x : REAL) RETURNS BOOLEAN +RETURN x > 0 +ENDFUNCTION +DECLARE n : INTEGER +n <- 4 +CALL Bump(n) +OUTPUT n +OUTPUT Legacy(1) diff --git a/tests/iii/multifile-overload-duplicate/case.json b/tests/iii/multifile-overload-duplicate/case.json new file mode 100644 index 0000000..bb02048 --- /dev/null +++ b/tests/iii/multifile-overload-duplicate/case.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "reason": "(?i)Duplicate definition Same in", + "line": 1, + "file": "second.cps" + } + ], + "sources": [ + "program.cps", + "first.cps", + "second.cps" + ] +} diff --git a/tests/iii/multifile-overload-duplicate/first.cps b/tests/iii/multifile-overload-duplicate/first.cps new file mode 100644 index 0000000..4ec6b09 --- /dev/null +++ b/tests/iii/multifile-overload-duplicate/first.cps @@ -0,0 +1,2 @@ +OVERLOAD PROCEDURE Same(a : ARRAY[1:2] OF INTEGER) +ENDPROCEDURE diff --git a/tests/iii/multifile-overload-duplicate/program.cps b/tests/iii/multifile-overload-duplicate/program.cps new file mode 100644 index 0000000..e5d0515 --- /dev/null +++ b/tests/iii/multifile-overload-duplicate/program.cps @@ -0,0 +1 @@ +OUTPUT 0 diff --git a/tests/iii/multifile-overload-duplicate/second.cps b/tests/iii/multifile-overload-duplicate/second.cps new file mode 100644 index 0000000..f26810a --- /dev/null +++ b/tests/iii/multifile-overload-duplicate/second.cps @@ -0,0 +1,2 @@ +OVERLOAD PROCEDURE same(BYREF a : ARRAY[3:4] OF INTEGER) +ENDPROCEDURE diff --git a/tests/iii/multifile-overload-visibility/case.json b/tests/iii/multifile-overload-visibility/case.json new file mode 100644 index 0000000..6c60472 --- /dev/null +++ b/tests/iii/multifile-overload-visibility/case.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "reason": "No matching overload for Area\\(REAL\\)", + "line": 2, + "file": "program.cps" + } + ], + "sources": [ + "program.cps", + "integer.cps", + "real.cps" + ] +} diff --git a/tests/iii/multifile-overload-visibility/integer.cps b/tests/iii/multifile-overload-visibility/integer.cps new file mode 100644 index 0000000..b87740a --- /dev/null +++ b/tests/iii/multifile-overload-visibility/integer.cps @@ -0,0 +1,3 @@ +OVERLOAD FUNCTION Area(x : INTEGER) RETURNS INTEGER +RETURN x +ENDFUNCTION diff --git a/tests/iii/multifile-overload-visibility/program.cps b/tests/iii/multifile-overload-visibility/program.cps new file mode 100644 index 0000000..8f47c4a --- /dev/null +++ b/tests/iii/multifile-overload-visibility/program.cps @@ -0,0 +1,2 @@ +INCLUDE "integer.cps" +OUTPUT Area(2.0) diff --git a/tests/iii/multifile-overload-visibility/real.cps b/tests/iii/multifile-overload-visibility/real.cps new file mode 100644 index 0000000..a16261a --- /dev/null +++ b/tests/iii/multifile-overload-visibility/real.cps @@ -0,0 +1,3 @@ +OVERLOAD FUNCTION Area(x : REAL) RETURNS REAL +RETURN x +ENDFUNCTION diff --git a/tests/iii/overload-errors/case.json b/tests/iii/overload-errors/case.json new file mode 100644 index 0000000..b31c666 --- /dev/null +++ b/tests/iii/overload-errors/case.json @@ -0,0 +1,36 @@ +{ + "errors": [ + { + "reason": "every overload must be declared OVERLOAD", + "line": 4 + }, + { + "reason": "Function Ret cannot be redefined", + "line": 10 + }, + { + "reason": "Function Mode cannot be redefined", + "line": 15 + }, + { + "reason": "No matching overload for Exact\\(INTEGER\\)", + "line": 20 + }, + { + "reason": "Function Bounds cannot be redefined", + "line": 23 + }, + { + "reason": "built-in function and cannot be redefined", + "line": 25 + }, + { + "reason": "No matching overload for Exact\\(STRING\\)", + "line": 28 + }, + { + "reason": "No matching overload for Exact\\(\\)", + "line": 29 + } + ] +} diff --git a/tests/iii/overload-errors/program.cps b/tests/iii/overload-errors/program.cps new file mode 100644 index 0000000..13d4692 --- /dev/null +++ b/tests/iii/overload-errors/program.cps @@ -0,0 +1,29 @@ +OVERLOAD FUNCTION Mixed(x : INTEGER) RETURNS INTEGER +RETURN x +ENDFUNCTION +FUNCTION Mixed(x : REAL) RETURNS REAL +RETURN x +ENDFUNCTION +OVERLOAD FUNCTION Ret(x : INTEGER) RETURNS INTEGER +RETURN x +ENDFUNCTION +OVERLOAD FUNCTION Ret(x : INTEGER) RETURNS REAL +RETURN x +ENDFUNCTION +OVERLOAD PROCEDURE Mode(BYREF x : INTEGER) +ENDPROCEDURE +OVERLOAD PROCEDURE Mode(BYVAL x : INTEGER) +ENDPROCEDURE +OVERLOAD FUNCTION Exact(x : REAL) RETURNS REAL +RETURN x +ENDFUNCTION +OUTPUT Exact(1) +OVERLOAD PROCEDURE Bounds(a : ARRAY[1:2] OF INTEGER) +ENDPROCEDURE +OVERLOAD PROCEDURE Bounds(a : ARRAY[3:4] OF INTEGER) +ENDPROCEDURE +OVERLOAD FUNCTION LENGTH(x : INTEGER) RETURNS INTEGER +RETURN x +ENDFUNCTION +OUTPUT Exact("text") +OUTPUT Exact() diff --git a/tests/iii/overload-main/case.json b/tests/iii/overload-main/case.json new file mode 100644 index 0000000..c2ffe3f --- /dev/null +++ b/tests/iii/overload-main/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "MAIN cannot be declared OVERLOAD", + "line": 1 + } + ] +} diff --git a/tests/iii/overload-main/program.cps b/tests/iii/overload-main/program.cps new file mode 100644 index 0000000..709c905 --- /dev/null +++ b/tests/iii/overload-main/program.cps @@ -0,0 +1,3 @@ +OVERLOAD FUNCTION MAIN() RETURNS INTEGER +RETURN 0 +ENDFUNCTION -- cgit v1.3.1-4-g156e