aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 10:34:37 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 10:34:37 +0000
commite572d278a44d286c8713d0ee4af9c72a3d1ca73c (patch)
tree2fd37a936d1d96869776b2fd4577a121eee974c2
parent377a1fd2eadd2a70cd27f13e2fee6e96c8706a51 (diff)
feat(classes): overload methods and NEW constructors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--include/cps/CodeGen.h7
-rw-r--r--lib/CodeGen/CodeGen.cc2
-rw-r--r--lib/CodeGen/Designator.cc39
-rw-r--r--lib/CodeGen/StatementGen.cc2
-rw-r--r--lib/CodeGen/ValueTypeGen.cc6
-rw-r--r--tests/i/class-overload-methods/case.json1
-rw-r--r--tests/i/class-overload-methods/expected.txt7
-rw-r--r--tests/i/class-overload-methods/notes.md7
-rw-r--r--tests/i/class-overload-methods/program.cps46
-rw-r--r--tests/iii/class-overload-errors/case.json20
-rw-r--r--tests/iii/class-overload-errors/program.cps20
11 files changed, 139 insertions, 18 deletions
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h
index 463c613..04bf25c 100644
--- a/include/cps/CodeGen.h
+++ b/include/cps/CodeGen.h
@@ -43,9 +43,12 @@ class CodeGen {
void importSymbols();
void runGlobalPrePass(const SourceUnit &Unit, int Phase);
IdentifierMap<SymbolInfo> Symbols;
- IdentifierMap<IdentifierMap<FunctionDefAST*>> ClassMethods;
+ IdentifierMap<IdentifierMap<std::vector<FunctionDefAST*>>> ClassMethods;
std::string CurrentClass;
- const PrototypeAST *findMethod(const std::string &Class, const std::string &Name) const;
+ const std::vector<FunctionDefAST *> *findMethod(const std::string &Class, const std::string &Name) const;
+ bool isRegisteredMethod(const std::string &Class, const PrototypeAST *Method) const;
+ const FuncSig *resolveMethod(const std::string &Class, const std::string &Name,
+ const std::vector<std::unique_ptr<ExprAST>> &Args);
llvm::Value *emitMemberCall(MemberCallExprAST *Call);
llvm::Value *emitMethodCall(const std::string &Class, llvm::Value *Receiver,
const std::string &Name,
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 9f59a4d..92a52e8 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -288,7 +288,7 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) {
if (auto *Class = dynamic_cast<ClassTypeDeclAST*>(Stmt.get())) {
for (const auto &Method : Class->getMethods()) {
SaveAndRestore<int> MethodLocation(DiagnosticLine, Method->getLine());
- if (findMethod(Class->getName(), Method->getProto()->getName()) == Method->getProto())
+ if (isRegisteredMethod(Class->getName(), Method->getProto()))
FuncGen->emitPrototype(Method->getProto());
}
}
diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc
index 540059d..467f788 100644
--- a/lib/CodeGen/Designator.cc
+++ b/lib/CodeGen/Designator.cc
@@ -59,20 +59,40 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem
const std::string &Name = Method->getProto()->getName();
if (sameIdentifier(Name, "this")) {
reportError("A method cannot be named this");
- } else if (!Names.insert(Name).second) {
+ } else if (Names.count(Name) ||
+ (!Methods[Name].empty() && (!Method->getProto()->isOverload() ||
+ !Methods[Name].front()->getProto()->isOverload()))) {
reportError("Duplicate member %s in class %s", Name.c_str(), Class->getName().c_str());
} else {
- Methods.emplace(Name, Method.get());
+ Methods[Name].push_back(Method.get());
}
}
}
}
-const PrototypeAST *CodeGen::findMethod(const std::string &Class, const std::string &Name) const {
+const std::vector<FunctionDefAST *> *CodeGen::findMethod(const std::string &Class, const std::string &Name) const {
auto C = ClassMethods.find(Class);
if (C == ClassMethods.end()) return nullptr;
auto M = C->second.find(Name);
- return M == C->second.end() ? nullptr : M->second->getProto();
+ return M == C->second.end() || M->second.empty() ? nullptr : &M->second;
+}
+
+bool CodeGen::isRegisteredMethod(const std::string &Class, const PrototypeAST *Method) const {
+ const auto *Methods = findMethod(Class, Method->getName());
+ if (Methods)
+ for (const auto *Candidate : *Methods)
+ if (Candidate->getProto() == Method) return true;
+ return false;
+}
+
+const FuncSig *CodeGen::resolveMethod(const std::string &Class, const std::string &Name,
+ const std::vector<std::unique_ptr<ExprAST>> &Args) {
+ const auto *Methods = findMethod(Class, Name);
+ if (!Methods) {
+ reportError("Type %s has no method %s", Class.c_str(), Name.c_str());
+ return nullptr;
+ }
+ return resolveCall(Methods->front()->getProto()->getLinkName(), Args);
}
Value *CodeGen::emitMemberCall(MemberCallExprAST *Call) {
@@ -87,18 +107,15 @@ Value *CodeGen::emitMethodCall(const std::string &Class, Value *Receiver,
const std::string &Name,
const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line) {
SaveAndRestore<int> Location(DiagnosticLine, Line);
- const PrototypeAST *Method = findMethod(Class, Name);
- if (!Method) {
- reportError("Type %s has no method %s", Class.c_str(), Name.c_str());
- return nullptr;
- }
+ const FuncSig *Sig = resolveMethod(Class, Name, ArgExprs);
+ if (!Sig) return nullptr;
+ const PrototypeAST *Method = Sig->Prototype;
if (!Method->isPublic() && !sameIdentifier(CurrentClass, Class)) {
reportError("Cannot access private method %s of type %s", Name.c_str(), Class.c_str());
return nullptr;
}
std::vector<Value*> Args = {Receiver};
- const FuncSig *Sig = resolveCall(Method->getLinkName(), ArgExprs);
- if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr;
+ if (!marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr;
return FuncGen->emitCall(Sig->LinkName, Args);
}
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index 8783cde..022c550 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -19,7 +19,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
if (auto *Class = dynamic_cast<ClassTypeDeclAST*>(Stmt)) {
for (const auto &Method : Class->getMethods()) {
// Invalid or duplicate members have already been diagnosed.
- if (findMethod(Class->getName(), Method->getProto()->getName()) == Method->getProto())
+ if (isRegisteredMethod(Class->getName(), Method->getProto()))
emitStmt(Method.get());
}
return;
diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc
index d2bba03..3c1fc06 100644
--- a/lib/CodeGen/ValueTypeGen.cc
+++ b/lib/CodeGen/ValueTypeGen.cc
@@ -119,9 +119,9 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) {
if (auto *Call = dynamic_cast<MemberCallExprAST*>(Expr)) {
const TypeInfo *Receiver = getExprTypeInfo(Call->getReceiver());
- const PrototypeAST *Method = Receiver ? findMethod(Receiver->Name, Call->getMethod()) : nullptr;
- if (!Method || (!Method->isPublic() && !sameIdentifier(CurrentClass, Receiver->Name))) return nullptr;
- return resolveType(Method->getReturnType());
+ const FuncSig *Sig = Receiver ? resolveMethod(Receiver->Name, Call->getMethod(), Call->getArgs()) : nullptr;
+ if (!Sig || (!Sig->Prototype->isPublic() && !sameIdentifier(CurrentClass, Receiver->Name))) return nullptr;
+ return resolveType(Sig->ReturnTypeName);
}
if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) {
diff --git a/tests/i/class-overload-methods/case.json b/tests/i/class-overload-methods/case.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/tests/i/class-overload-methods/case.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/i/class-overload-methods/expected.txt b/tests/i/class-overload-methods/expected.txt
new file mode 100644
index 0000000..d7fa519
--- /dev/null
+++ b/tests/i/class-overload-methods/expected.txt
@@ -0,0 +1,7 @@
+174
+11
+9
+ok!
+3
+8
+1
diff --git a/tests/i/class-overload-methods/notes.md b/tests/i/class-overload-methods/notes.md
new file mode 100644
index 0000000..accde5e
--- /dev/null
+++ b/tests/i/class-overload-methods/notes.md
@@ -0,0 +1,7 @@
+# class-overload-methods
+
+Human review: pending. Expected output was independently traced by AI, not captured from cpsc.
+
+Basis: OVERLOAD is a project extension.
+
+Trace: The default constructor sets 1, INTEGER sets 7, STRING length sets 4. Holder.Item and both array elements default to 1. Get(INTEGER) adds 2 to 7; Get(STRING) appends !. Set(STRING) stores length 3. Keyword NEW stores 8; explicit NEW() resets to 1.
diff --git a/tests/i/class-overload-methods/program.cps b/tests/i/class-overload-methods/program.cps
new file mode 100644
index 0000000..e68d416
--- /dev/null
+++ b/tests/i/class-overload-methods/program.cps
@@ -0,0 +1,46 @@
+CLASS Box
+Value : INTEGER
+PUBLIC OVERLOAD PROCEDURE NEW()
+Value <- 1
+ENDPROCEDURE
+PUBLIC OVERLOAD PROCEDURE NEW(x : INTEGER)
+Value <- x
+ENDPROCEDURE
+PUBLIC OVERLOAD PROCEDURE NEW(x : STRING)
+Value <- LENGTH(x)
+ENDPROCEDURE
+OVERLOAD FUNCTION Get() RETURNS INTEGER
+RETURN Value
+ENDFUNCTION
+OVERLOAD FUNCTION Get(x : INTEGER) RETURNS INTEGER
+RETURN Value + x
+ENDFUNCTION
+OVERLOAD FUNCTION Get(x : STRING) RETURNS STRING
+RETURN x & "!"
+ENDFUNCTION
+OVERLOAD PROCEDURE Set(x : INTEGER)
+Value <- x
+ENDPROCEDURE
+OVERLOAD PROCEDURE Set(x : STRING)
+Value <- LENGTH(x)
+ENDPROCEDURE
+ENDCLASS
+TYPE Holder
+DECLARE Item : Box
+ENDTYPE
+DECLARE a : Box
+DECLARE b : Box(7)
+DECLARE c : Box("four")
+DECLARE h : Holder
+DECLARE items : ARRAY[1:2] OF Box
+OUTPUT a.Get(), b.Get(), c.Get()
+OUTPUT h.Item.Get(), items[2].Get()
+OUTPUT b.Get(2)
+OUTPUT b.Get("ok")
+CALL a.Set("abc")
+OUTPUT a.Get()
+a <- NEW Box(8)
+OUTPUT a.Get()
+CALL a.NEW()
+OUTPUT a.Get()
+DELETE a
diff --git a/tests/iii/class-overload-errors/case.json b/tests/iii/class-overload-errors/case.json
new file mode 100644
index 0000000..afc0e6a
--- /dev/null
+++ b/tests/iii/class-overload-errors/case.json
@@ -0,0 +1,20 @@
+{
+ "errors": [
+ {
+ "reason": "Duplicate member Set in class Mixed",
+ "line": 4
+ },
+ {
+ "reason": "No matching overload for Required.NEW\\(\\)",
+ "line": 17
+ },
+ {
+ "reason": "Cannot access private method Set of type Secret",
+ "line": 19
+ },
+ {
+ "reason": "No matching overload for Secret.Set\\(BOOLEAN\\)",
+ "line": 20
+ }
+ ]
+}
diff --git a/tests/iii/class-overload-errors/program.cps b/tests/iii/class-overload-errors/program.cps
new file mode 100644
index 0000000..586d47d
--- /dev/null
+++ b/tests/iii/class-overload-errors/program.cps
@@ -0,0 +1,20 @@
+CLASS Mixed
+OVERLOAD PROCEDURE Set(x : INTEGER)
+ENDPROCEDURE
+PROCEDURE Set(x : STRING)
+ENDPROCEDURE
+ENDCLASS
+CLASS Required
+OVERLOAD PROCEDURE NEW(x : INTEGER)
+ENDPROCEDURE
+ENDCLASS
+CLASS Secret
+PRIVATE OVERLOAD PROCEDURE Set(x : INTEGER)
+ENDPROCEDURE
+PUBLIC OVERLOAD PROCEDURE Set(x : STRING)
+ENDPROCEDURE
+ENDCLASS
+DECLARE item : Required
+DECLARE obj : Secret
+CALL obj.Set(1)
+CALL obj.Set(TRUE)