aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 10:32:30 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 10:32:30 +0000
commit377a1fd2eadd2a70cd27f13e2fee6e96c8706a51 (patch)
treec05665f26b3434359cf4ae7720e8f9111a322522
parentf5246db6d045ffb86d6717e1b1c37f03bcb2b75b (diff)
feat(codegen): overload global functions and procedures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--include/cps/ArrayHandler.h2
-rw-r--r--include/cps/CodeGen.h6
-rw-r--r--include/cps/FunctionGen.h15
-rw-r--r--lib/CodeGen/CodeGen.cc69
-rw-r--r--lib/CodeGen/ConstantGen.cc2
-rw-r--r--lib/CodeGen/Designator.cc5
-rw-r--r--lib/CodeGen/ExpressionGen.cc5
-rw-r--r--lib/CodeGen/FunctionGen.cc67
-rw-r--r--lib/CodeGen/GlobalGen.cc2
-rw-r--r--lib/CodeGen/StatementGen.cc2
-rw-r--r--lib/CodeGen/ValueTypeGen.cc15
-rw-r--r--tests/i/multifile-overloads/case.json7
-rw-r--r--tests/i/multifile-overloads/expected.txt2
-rw-r--r--tests/i/multifile-overloads/integer.cps3
-rw-r--r--tests/i/multifile-overloads/notes.md7
-rw-r--r--tests/i/multifile-overloads/program.cps4
-rw-r--r--tests/i/multifile-overloads/real.cps3
-rw-r--r--tests/i/overload-functions/case.json1
-rw-r--r--tests/i/overload-functions/expected.txt10
-rw-r--r--tests/i/overload-functions/notes.md9
-rw-r--r--tests/i/overload-functions/program.cps49
-rw-r--r--tests/iii/multifile-overload-duplicate/case.json14
-rw-r--r--tests/iii/multifile-overload-duplicate/first.cps2
-rw-r--r--tests/iii/multifile-overload-duplicate/program.cps1
-rw-r--r--tests/iii/multifile-overload-duplicate/second.cps2
-rw-r--r--tests/iii/multifile-overload-visibility/case.json14
-rw-r--r--tests/iii/multifile-overload-visibility/integer.cps3
-rw-r--r--tests/iii/multifile-overload-visibility/program.cps2
-rw-r--r--tests/iii/multifile-overload-visibility/real.cps3
-rw-r--r--tests/iii/overload-errors/case.json36
-rw-r--r--tests/iii/overload-errors/program.cps29
-rw-r--r--tests/iii/overload-main/case.json8
-rw-r--r--tests/iii/overload-main/program.cps3
33 files changed, 342 insertions, 60 deletions
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<llvm::Value*> &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<std::unique_ptr<ExprAST>> &ArgExprs, int Line);
- bool marshalCallArgs(const std::string &Callee,
+ const FuncSig *resolveCall(const std::string &Name,
+ const std::vector<std::unique_ptr<ExprAST>> &Args);
+ bool marshalCallArgs(const FuncSig &Signature,
const std::vector<std::unique_ptr<ExprAST>> &ArgExprs,
int Line,
std::vector<llvm::Value*> &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<ParamSig> Params;
size_t OwnerUnit = 0;
@@ -63,7 +67,10 @@ private:
int &DiagnosticLine;
void reportError(const char *Fmt, ...);
- IdentifierMap<FuncSig> Signatures;
+ IdentifierMap<std::vector<FuncSig>> Signatures;
+ IdentifierMap<std::pair<std::string, size_t>> LinkIndex;
+ std::map<const PrototypeAST *, std::string> PrototypeLinks;
+ std::map<size_t, std::string> UnitPaths;
size_t CurrentUnit = 0;
std::function<bool(size_t)> 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<bool(size_t)> Predicate) { Visible = std::move(Predicate); }
- const FuncSig *getSignature(const std::string &Name) const;
+ std::vector<const FuncSig *> 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<SourceUnit> &Sources) {
});
FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); });
- IdentifierMap<size_t> Definitions;
+ struct Definition { size_t Unit; bool IsFunction; bool IsOverload; };
+ IdentifierMap<Definition> Definitions;
std::optional<size_t> 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<SourceUnit> &Sources) {
SaveAndRestore<int> Location(DiagnosticLine, S->getLine());
std::string TypeName;
if (auto *F = dynamic_cast<FunctionDefAST*>(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<SourceUnit> &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<std::unique_ptr<ExprAST>> &ArgExprs,
int Line,
std::vector<Value*> &Out) {
SaveAndRestore<int> 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<std::unique_ptr<ExprAST>> &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<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 : "?";
+ }
+ 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<Value*> 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<Value*> 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<ParamDecl
}
}
-const FuncSig *FunctionGen::getSignature(const std::string &Name) const {
+std::vector<const FuncSig *> FunctionGen::getOverloads(const std::string &Name) const {
+ std::vector<const FuncSig *> 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<Type*> 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<llvm::Value*> &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<ReturnStmtAST*>(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<IntegerExprAST*>(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