diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-18 06:09:01 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-18 06:09:01 +0000 |
| commit | b17f101cfc75565f9991c856ec62f1e17b0ab104 (patch) | |
| tree | 6c925f06c45dbaa557a0451cfca33f17be71ec67 | |
| parent | 16231050058131860f710073c17d3bca7c6637eb (diff) | |
feat(operators): add member and unary overloads with explicit calls
Select member and global candidates together, enforce private access, and
reuse argument marshalling. Materialize temporary receivers for chaining
and register them for the existing object cleanup mechanism.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | include/cps/CodeGen.h | 7 | ||||
| -rw-r--r-- | include/cps/Operator.h | 2 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 12 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 22 | ||||
| -rw-r--r-- | lib/CodeGen/OperatorGen.cc | 71 | ||||
| -rw-r--r-- | lib/CodeGen/ValueTypeGen.cc | 6 | ||||
| -rw-r--r-- | lib/Parser/Parser.cc | 15 | ||||
| -rw-r--r-- | lib/Parser/ParserExpression.cc | 25 | ||||
| -rw-r--r-- | lib/Parser/ParserFunction.cc | 17 | ||||
| -rw-r--r-- | tests/i/operator-members/case.json | 1 | ||||
| -rw-r--r-- | tests/i/operator-members/expected.txt | 12 | ||||
| -rw-r--r-- | tests/i/operator-members/notes.md | 7 | ||||
| -rw-r--r-- | tests/i/operator-members/program.cps | 66 | ||||
| -rw-r--r-- | tests/iii/operator-member-errors/case.json | 20 | ||||
| -rw-r--r-- | tests/iii/operator-member-errors/notes.md | 3 | ||||
| -rw-r--r-- | tests/iii/operator-member-errors/program.cps | 18 |
16 files changed, 282 insertions, 22 deletions
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index 30e6d06..9b2d6c5 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -160,11 +160,18 @@ class CodeGen { bool Ambiguous = false; }; OperatorResolution resolveOperator(int Op, const TypeInfo *Left, const TypeInfo *Right = nullptr); + bool checkOperatorAccess(const FuncSig &Signature); + llvm::Value *materializeOperatorReceiver(llvm::Value *Value, const TypeInfo *Type); + llvm::Value *emitOperatorCall(const FuncSig &Signature, ExprAST *Left, ExprAST *Right = nullptr); + llvm::Value *emitOperatorValues(const FuncSig &Signature, llvm::Value *Left, + const TypeInfo *LeftType, llvm::Value *Right); llvm::Value *emitVariadicArgument(ExprAST *Expr, const std::string &Callee, size_t Position); bool marshalCallArgs(const FuncSig &Signature, const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line, std::vector<llvm::Value*> &Out); + bool marshalCallArgs(const FuncSig &Signature, const std::vector<ExprAST*> &ArgExprs, + int Line, std::vector<llvm::Value*> &Out); llvm::Value *symbolAddress(const SymbolInfo &Info); void emitAssignment(const LValueInfo &Target, ExprAST *Expr, const SymbolInfo *Symbol = nullptr); diff --git a/include/cps/Operator.h b/include/cps/Operator.h index 7cbb5d9..304793e 100644 --- a/include/cps/Operator.h +++ b/include/cps/Operator.h @@ -22,7 +22,7 @@ inline std::string operatorSpelling(int Op) { } inline std::string operatorName(int Op) { - return "OPERATOR" + operatorSpelling(Op); + return "OPERATOR " + operatorSpelling(Op); } } // namespace cps diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 61fd837..f7c4de6 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -369,6 +369,14 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature, const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line, std::vector<Value*> &Out) { + std::vector<ExprAST*> Args; + for (const auto &Arg : ArgExprs) Args.push_back(Arg.get()); + return marshalCallArgs(Signature, Args, Line, Out); +} + +bool CodeGen::marshalCallArgs(const FuncSig &Signature, + const std::vector<ExprAST*> &ArgExprs, + int Line, std::vector<Value*> &Out) { SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine); const FuncSig *Sig = &Signature; const std::string &Callee = Sig->Name; @@ -379,7 +387,7 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature, for (size_t i = 0; i < ArgExprs.size(); ++i) { if (i >= Sig->Params.size()) { - Value *V = emitVariadicArgument(ArgExprs[i].get(), Callee, i + 1); + Value *V = emitVariadicArgument(ArgExprs[i], Callee, i + 1); if (!V) return false; Out.push_back(V); continue; @@ -387,7 +395,7 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature, const ParamSig &P = Sig->Params[i]; const std::string &TypeName = P.TypeName; bool IsByRef = P.IsByRef; - ExprAST *ArgExpr = ArgExprs[i].get(); + ExprAST *ArgExpr = ArgExprs[i]; if (P.IsArray) { if (!Arrays->emitArrayArgument(ArgExpr, P.TypeName, P.Rank, P.DeclaredBounds, diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index 8fb13c9..a01cab2 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -73,6 +73,16 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { } if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { + if (isWholeArrayVar(Unary->getOperand())) { + reportError("A whole array cannot be used as an operand; index it"); + return nullptr; + } + auto Overload = resolveOperator(Unary->getOp(), getExprTypeInfo(Unary->getOperand())); + if (Overload.Ambiguous) { + reportError("Ambiguous overload for %s", operatorName(Unary->getOp()).c_str()); + return nullptr; + } + if (Overload.Signature) return emitOperatorCall(*Overload.Signature, Unary->getOperand()); if (Unary->getOp() == tok_not) { const TypeInfo *Info = getExprTypeInfo(Unary->getOperand()); Value *Operand = Info && Info->isC() @@ -123,6 +133,16 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { // '&' path: their admissible operations are decided by name here. const TypeInfo *LT = getExprTypeInfo(Bin->getLHS()); const TypeInfo *RT = getExprTypeInfo(Bin->getRHS()); + if (LT && RT) { + auto Overload = resolveOperator(Bin->getOp(), LT, RT); + if (Overload.Ambiguous) { + reportError("Ambiguous overload for %s(%s, %s)", operatorName(Bin->getOp()).c_str(), + LT->Name.c_str(), RT->Name.c_str()); + return nullptr; + } + if (Overload.Signature) + return emitOperatorCall(*Overload.Signature, Bin->getLHS(), Bin->getRHS()); + } Value *L = emitExpr(Bin->getLHS()); Value *R = emitExpr(Bin->getRHS()); return emitBinaryOp(Bin->getOp(), L, LT, R, RT, Bin->getLine()); @@ -165,7 +185,7 @@ Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT, reportError("Ambiguous overload for %s(%s, %s)", operatorName(Op).c_str(), LT->Name.c_str(), RT->Name.c_str()); return nullptr; } - if (Overload.Signature) return FuncGen->emitCall(*Overload.Signature, {L, R}); + if (Overload.Signature) return emitOperatorValues(*Overload.Signature, L, LT, R); if ((LT && LT->isC()) || (RT && RT->isC())) return emitCBinaryOp(Op, L, LT, R, RT, Line); if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) diff --git a/lib/CodeGen/OperatorGen.cc b/lib/CodeGen/OperatorGen.cc index 83c515b..d340ed7 100644 --- a/lib/CodeGen/OperatorGen.cc +++ b/lib/CodeGen/OperatorGen.cc @@ -1,7 +1,9 @@ #include "cps/CodeGen.h" #include "cps/Operator.h" +#include "cps/TypeAST.h" using namespace cps; +using namespace llvm; // Probing candidates must not evaluate operands or diagnose absent overloads: // both static typing and emission use this same selection. @@ -9,12 +11,73 @@ CodeGen::OperatorResolution CodeGen::resolveOperator( int Op, const TypeInfo *Left, const TypeInfo *Right) { OperatorResolution Result; if (!Left) return Result; - for (const FuncSig *Sig : FuncGen->getOverloads(operatorName(Op))) { - if (Sig->Prototype->getOperator() != Op || Sig->Params.size() != (Right ? 2u : 1u)) continue; - if (!Left->sameType(resolveType(Sig->Params[0].TypeName))) continue; - if (Right && !Right->sameType(resolveType(Sig->Params[1].TypeName))) continue; + auto Candidates = FuncGen->getOverloads(operatorName(Op)); + if (Left->IsClass) { + auto Members = FuncGen->getOverloads(Left->Name + "." + operatorName(Op)); + Candidates.insert(Candidates.end(), Members.begin(), Members.end()); + } + for (const FuncSig *Sig : Candidates) { + bool Member = !Sig->Prototype->getOwnerClass().empty(); + if (Sig->Prototype->getOperator() != Op || Sig->Params.size() != (Right ? 2u : 1u) - Member) continue; + if (!Member && !Left->sameType(resolveType(Sig->Params[0].TypeName))) continue; + if (Right && !Right->sameType(resolveType(Sig->Params[Member ? 0 : 1].TypeName))) continue; if (Result.Signature) Result.Ambiguous = true; else Result.Signature = Sig; } return Result; } + +bool CodeGen::checkOperatorAccess(const FuncSig &Signature) { + const PrototypeAST *Proto = Signature.Prototype; + if (!Proto->getOwnerClass().empty() && !Proto->isPublic() && + !sameIdentifier(CurrentClass, Proto->getOwnerClass())) { + reportError("Cannot access private method %s of type %s", + Proto->getName().c_str(), Proto->getOwnerClass().c_str()); + return false; + } + return true; +} + +Value *CodeGen::materializeOperatorReceiver(Value *Val, const TypeInfo *Type) { + if (!Val || !Type) return nullptr; + auto *Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), + Type->LLVMType, "operator_receiver"); + size_t Object = registerStackObject(Storage, Type->Name, DiagnosticLine, false, true); + Builder->CreateStore(Val, Storage); + activateStackObject(Object); + return Storage; +} + +Value *CodeGen::emitOperatorCall(const FuncSig &Signature, ExprAST *Left, ExprAST *Right) { + if (!checkOperatorAccess(Signature)) return nullptr; + std::vector<Value*> Args; + std::vector<ExprAST*> Explicit; + if (Signature.Prototype->getOwnerClass().empty()) { + Explicit.push_back(Left); + } else { + Value *Receiver = nullptr; + if (dynamic_cast<VariableExprAST*>(Left) || dynamic_cast<ArrayAccessExprAST*>(Left) || + dynamic_cast<DesignatorExprAST*>(Left)) { + LValueInfo LV; + if (!emitLValue(Left, LV)) return nullptr; + Receiver = LV.Addr; + } else { + Receiver = materializeOperatorReceiver(emitExpr(Left), getExprTypeInfo(Left)); + } + if (!Receiver) return nullptr; + Args.push_back(Receiver); + } + if (Right) Explicit.push_back(Right); + if (!marshalCallArgs(Signature, Explicit, DiagnosticLine, Args)) return nullptr; + return FuncGen->emitCall(Signature, Args); +} + +// CASE has already evaluated and captured its selector and label values. +Value *CodeGen::emitOperatorValues(const FuncSig &Signature, Value *Left, + const TypeInfo *LeftType, Value *Right) { + if (!checkOperatorAccess(Signature)) return nullptr; + if (!Signature.Prototype->getOwnerClass().empty()) + Left = materializeOperatorReceiver(Left, LeftType); + if (!Left) return nullptr; + return FuncGen->emitCall(Signature, {Left, Right}); +} diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc index b09a5d9..e7a3f77 100644 --- a/lib/CodeGen/ValueTypeGen.cc +++ b/lib/CodeGen/ValueTypeGen.cc @@ -70,10 +70,14 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) { if (dynamic_cast<AddrOfExprAST*>(Expr)) return nullptr; if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { + if (isWholeArrayVar(Unary->getOperand())) return nullptr; + const TypeInfo *Operand = getExprTypeInfo(Unary->getOperand()); + auto Overload = resolveOperator(Unary->getOp(), Operand); + if (Overload.Ambiguous) return nullptr; + if (Overload.Signature) return resolveType(Overload.Signature->ReturnTypeName); if (Unary->getOp() == tok_not) { return resolveType("BOOLEAN"); } - const TypeInfo *Operand = getExprTypeInfo(Unary->getOperand()); if (Operand && Operand->isC()) return Types->promoteCScalar(Operand); if (Operand && !Operand->isC() && !Operand->isUserKind()) return Operand->isReal() ? resolveType("REAL") : resolveType("INTEGER"); diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index f22e3c6..afe0e80 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -48,7 +48,7 @@ void Parser::syncToStatementStart() { switch (CurTok) { case tok_include: case tok_global: case tok_endglobal: case tok_include_obj: case tok_extern: - case tok_delete: case tok_constant: case tok_declare: case tok_identifier: case tok_input: case tok_output: + case tok_delete: case tok_constant: case tok_declare: case tok_identifier: case tok_operator: case tok_input: case tok_output: case tok_case: case tok_endcase: case tok_otherwise: case tok_if: case tok_while: case tok_repeat: case tok_for: case tok_overload: case tok_function: case tok_procedure: case tok_call: case tok_return: @@ -163,7 +163,7 @@ std::unique_ptr<StmtAST> Parser::ParseStatementImpl() { if (CurTok == tok_declare) { return ParseDeclare(); } - else if (CurTok == tok_identifier) { + else if (CurTok == tok_identifier || CurTok == tok_operator) { return ParseAssignStmt(); } else if (CurTok == tok_input) { @@ -284,9 +284,16 @@ std::unique_ptr<StmtAST> Parser::ParseStatementImpl() { // Bare-name and single-index targets degrade to their legacy nodes so the // existing assignment code paths stay untouched. std::unique_ptr<StmtAST> Parser::ParseAssignStmt() { - std::string Name = Lex.IdentifierStr; int Line = Lex.getLine(); - getNextToken(); + std::string Name; + if (CurTok == tok_operator) { + int Op; + Name = ParseOperatorName(Op); + if (Name.empty()) return nullptr; + } else { + Name = Lex.IdentifierStr; + getNextToken(); + } if (CurTok == '(') { getNextToken(); diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index a4f4fcc..6cde5bf 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -41,9 +41,16 @@ std::unique_ptr<ExprAST> Parser::ParseNumberExpr() { } std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { - std::string IdName = Lex.IdentifierStr; int Line = Lex.getLine(); - getNextToken(); + std::string IdName; + if (CurTok == tok_operator) { + int Op; + IdName = ParseOperatorName(Op); + if (IdName.empty()) return nullptr; + } else { + IdName = Lex.IdentifierStr; + getNextToken(); + } if (CurTok == '(' && !atCaseBoundary()) { getNextToken(); @@ -110,15 +117,21 @@ bool Parser::ParseDesignatorSuffix(std::vector<DesignatorAccess> &Out) { Out.push_back(std::move(A)); } else if (CurTok == '.') { getNextToken(); - if (CurTok != tok_identifier && CurTok != tok_new && CurTok != tok_delete) { + if (CurTok != tok_identifier && CurTok != tok_new && CurTok != tok_delete && CurTok != tok_operator) { cps::sourceDiagnostic(Lex.getLine(), "error: Expected field name after '.' at line %d\n", Lex.getLine()); return false; } DesignatorAccess A; A.Kind = AccessKind::Field; - A.FieldName = CurTok == tok_new ? "NEW" : CurTok == tok_delete ? "DELETE" : Lex.IdentifierStr; + if (CurTok == tok_operator) { + int Op; + A.FieldName = ParseOperatorName(Op); + if (A.FieldName.empty()) return false; + } else { + A.FieldName = CurTok == tok_new ? "NEW" : CurTok == tok_delete ? "DELETE" : Lex.IdentifierStr; + getNextToken(); + } Out.push_back(std::move(A)); - getNextToken(); } else if (CurTok == '^') { getNextToken(); DesignatorAccess A; @@ -172,7 +185,7 @@ std::unique_ptr<ExprAST> Parser::ParsePrimaryImpl() { case tok_new: cps::sourceDiagnostic(Lex.getLine(), "error: NEW is only allowed on the right of an assignment at line %d\n", Lex.getLine()); return nullptr; - case tok_identifier: return ParseIdentifierExpr(); + case tok_identifier: case tok_operator: return ParseIdentifierExpr(); case tok_number_int: return ParseNumberExpr(); case tok_number_real: return ParseNumberExpr(); case tok_string_literal: { diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index 3ff54da..dfb689a 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -140,6 +140,10 @@ std::string Parser::ParseOperatorName(int &Op) { return {}; } getNextToken(); + if (CurTok != '(') { + sourceDiagnostic(Lex.getLine(), "error: Expected '(' after operator name at line %d\n", Lex.getLine()); + return {}; + } return operatorName(Op); } @@ -339,13 +343,20 @@ std::unique_ptr<StmtAST> Parser::ParseExternDecl() { std::unique_ptr<StmtAST> Parser::ParseCallStmt() { int Line = Lex.getLine(); getNextToken(); - if (CurTok != tok_identifier) { + if (CurTok != tok_identifier && CurTok != tok_operator) { cps::sourceDiagnostic(Lex.getLine(), "error: Expected callee name after CALL\n"); return nullptr; } - std::string Callee = Lex.IdentifierStr; - getNextToken(); + std::string Callee; + if (CurTok == tok_operator) { + int Op; + Callee = ParseOperatorName(Op); + if (Callee.empty()) return nullptr; + } else { + Callee = Lex.IdentifierStr; + getNextToken(); + } std::vector<DesignatorAccess> Accesses; if (!ParseDesignatorSuffix(Accesses)) return nullptr; diff --git a/tests/i/operator-members/case.json b/tests/i/operator-members/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/operator-members/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/operator-members/expected.txt b/tests/i/operator-members/expected.txt new file mode 100644 index 0000000..24ab52b --- /dev/null +++ b/tests/i/operator-members/expected.txt @@ -0,0 +1,12 @@ +9 +-3 +negated +3! +8 +8 +8 +6 +make +number +6 +caught diff --git a/tests/i/operator-members/notes.md b/tests/i/operator-members/notes.md new file mode 100644 index 0000000..2b741c9 --- /dev/null +++ b/tests/i/operator-members/notes.md @@ -0,0 +1,7 @@ +# operator-members + +Human review: pending. Expected output was independently traced by AI, not captured from cpsc. + +Basis: operator overloading is a project extension; arguments match exact types. + +Trace: chained additions produce 3 + 2 + 4 = 9. Unary minus returns -3; NOT deliberately returns negated. A method can use its private concatenation operator, yielding 3!. Box addition modifies the original receiver from 3 to 8. Explicit member addition then global subtraction yields 8; explicit global subtraction yields 6. Discarded unary/global calls produce no output. MakeBox prints make exactly once, then Number prints number exactly once; their result has value 4 + 2 = 6. A throwing global multiplication reaches CATCH. diff --git a/tests/i/operator-members/program.cps b/tests/i/operator-members/program.cps new file mode 100644 index 0000000..d17184c --- /dev/null +++ b/tests/i/operator-members/program.cps @@ -0,0 +1,66 @@ +CLASS Box +Value : INTEGER +OVERLOAD FUNCTION OPERATOR+(rhs : INTEGER) RETURNS Box +DECLARE result : Box +result.Value <- Value + rhs +RETURN result +ENDFUNCTION +OVERLOAD FUNCTION OPERATOR+(rhs : Box) RETURNS INTEGER +Value <- Value + rhs.Value +RETURN Value +ENDFUNCTION +OVERLOAD FUNCTION OPERATOR-() RETURNS INTEGER +RETURN -Value +ENDFUNCTION +OVERLOAD FUNCTION OPERATOR NOT() RETURNS STRING +RETURN "negated" +ENDFUNCTION +PRIVATE OVERLOAD FUNCTION OPERATOR&(rhs : STRING) RETURNS STRING +RETURN NUM_TO_STR(Value) & rhs +ENDFUNCTION +FUNCTION Describe() RETURNS STRING +RETURN this & "!" +ENDFUNCTION +ENDCLASS +OVERLOAD FUNCTION OPERATOR-(lhs : Box, rhs : INTEGER) RETURNS INTEGER +RETURN lhs.Value - rhs +ENDFUNCTION +OVERLOAD FUNCTION OPERATOR*(lhs : Box, rhs : Box) RETURNS INTEGER +THROW +RETURN 0 +ENDFUNCTION +FUNCTION MakeBox() RETURNS Box +OUTPUT "make" +DECLARE result : Box +result.Value <- 4 +RETURN result +ENDFUNCTION +FUNCTION Number() RETURNS INTEGER +OUTPUT "number" +RETURN 2 +ENDFUNCTION +DECLARE a : Box +DECLARE b : Box +DECLARE result : Box +a.Value <- 3 +b.Value <- 5 +result <- (a + 2) + 4 +OUTPUT result.Value +OUTPUT -a +OUTPUT NOT a +OUTPUT a.Describe() +OUTPUT a + b +OUTPUT a.Value +OUTPUT a.OPERATOR+(1) - 1 +OUTPUT OPERATOR-(a, 2) +CALL a.OPERATOR-( ) +a.OPERATOR-( ) +CALL OPERATOR-(a, 1) +OPERATOR-(a, 1) +result <- MakeBox() + Number() +OUTPUT result.Value +TRY +OUTPUT a * b +CATCH +OUTPUT "caught" +ENDTRY diff --git a/tests/iii/operator-member-errors/case.json b/tests/iii/operator-member-errors/case.json new file mode 100644 index 0000000..1e8f8ae --- /dev/null +++ b/tests/iii/operator-member-errors/case.json @@ -0,0 +1,20 @@ +{ + "errors": [ + { + "reason": "Ambiguous overload", + "line": 14 + }, + { + "reason": "Cannot access private method", + "line": 15 + }, + { + "reason": "Cannot access private method", + "line": 16 + }, + { + "reason": "whole array cannot be used as an operand", + "line": 18 + } + ] +} diff --git a/tests/iii/operator-member-errors/notes.md b/tests/iii/operator-member-errors/notes.md new file mode 100644 index 0000000..204ab43 --- /dev/null +++ b/tests/iii/operator-member-errors/notes.md @@ -0,0 +1,3 @@ +# operator-member-errors + +Reject equally matching member/global candidates, implicit and explicit private access, and unary operations on whole arrays. diff --git a/tests/iii/operator-member-errors/program.cps b/tests/iii/operator-member-errors/program.cps new file mode 100644 index 0000000..9b48135 --- /dev/null +++ b/tests/iii/operator-member-errors/program.cps @@ -0,0 +1,18 @@ +CLASS Box +Value : INTEGER +OVERLOAD FUNCTION OPERATOR+(rhs : Box) RETURNS INTEGER +RETURN 1 +ENDFUNCTION +PRIVATE OVERLOAD FUNCTION OPERATOR&(rhs : STRING) RETURNS STRING +RETURN rhs +ENDFUNCTION +ENDCLASS +OVERLOAD FUNCTION OPERATOR+(lhs : Box, rhs : Box) RETURNS INTEGER +RETURN 2 +ENDFUNCTION +DECLARE a : Box +OUTPUT a + a +OUTPUT a & "x" +OUTPUT a.OPERATOR&("x") +DECLARE items : ARRAY[1:2] OF Box +OUTPUT NOT items |
