diff options
| -rw-r--r-- | include/cps/CodeGen.h | 7 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 39 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/ValueTypeGen.cc | 6 | ||||
| -rw-r--r-- | tests/i/class-overload-methods/case.json | 1 | ||||
| -rw-r--r-- | tests/i/class-overload-methods/expected.txt | 7 | ||||
| -rw-r--r-- | tests/i/class-overload-methods/notes.md | 7 | ||||
| -rw-r--r-- | tests/i/class-overload-methods/program.cps | 46 | ||||
| -rw-r--r-- | tests/iii/class-overload-errors/case.json | 20 | ||||
| -rw-r--r-- | tests/iii/class-overload-errors/program.cps | 20 |
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) |
