Finch 2.4.0: methods, maps, errors as values; str() of any value; macOS and ARM64 docs; Kate support

- fn Struct.method(...) with self by reference, also on C structs
- map[K]V: insertion-ordered hash maps, literals, m[k] += 1, has/get/remove/keys/values
- -> T! / -> !, return error(...), or <value>, or { err }, try; fallible int/float/read_file/write_file/delete_file
- for i, x in list / for k, v in map
- print and str() share one text formatter
- LSP: method and map completion, method signatures; grammar and snippets for VS Code and Kate
- tests: 74 (methods, errors, maps + 11 failure cases); docs at all three levels, en and pl

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-09 01:11:12 +02:00
commit b872be3176
55 changed files with 3400 additions and 336 deletions

View file

@ -80,6 +80,9 @@ struct Fn {
llvm::Function *cThunk = nullptr;
FType ret; // resolved types
std::vector<FType> params;
StructInfo *recv = nullptr; // a method of this struct: the first LLVM parameter is `self`'s address
bool mutatesSelf = false; // the method changes `self`
bool fallible = false; // -> T!: returns { i1 ok, T value, str error }
};
struct Loop {
@ -139,6 +142,13 @@ public:
int deferLimit = -1; // while emitting a defer: hide variables declared after it
int inDefer = 0;
std::map<std::string, llvm::Function *> helpers; // drop/copy/print per type
std::map<StructInfo *, std::map<std::string, Fn>> methods;
std::set<std::string> mutatingMethods; // names of methods that change self (for the mutation analysis)
// errors as values: the call that `or` / `try` is handling reports here whether it failed
const Expr *fallibleTarget = nullptr;
llvm::Value *fallibleFailed = nullptr; // i1
llvm::Value *fallibleMsg = nullptr; // str
// debug info (-g)
bool debug;
@ -164,6 +174,9 @@ public:
void forStmt(const ForStmt &s);
void forEachStmt(const ForEachStmt &s);
void returnStmt(const ReturnStmt &s);
llvm::Type *retType(const Fn &f);
void retValue(llvm::Value *v); // return from the current function (wrapped as success if it can fail)
void retError(llvm::Value *msg); // fail the current function with the str `msg`
void jump(bool isBreak, Pos p);
llvm::Value *condition(const Expr &e);
void continueAt(llvm::BasicBlock *bb, llvm::BasicBlock *deadEnd);
@ -194,6 +207,10 @@ public:
LRef member(const MemberExpr &m, bool forWrite);
LRef index(const IndexExpr &e, bool forWrite);
Value_ arrayLit(const ArrayLitExpr &a, const FType *want);
Value_ fallible(const Expr &call, llvm::Value *&failed, llvm::Value *&msg);
Value_ tryExpr(const TryExpr &t);
Value_ orElse(const OrElseExpr &e, bool discard = false); // discard: a statement, the value isn't used
FType structType(StructInfo *s);
std::string rootVar(const Expr &e);
bool mutates(const std::string &name, const Stmt &s);
bool mutatesExpr(const std::string &name, const Expr &e);
@ -225,7 +242,9 @@ public:
std::string typeKey(const FType &t);
llvm::Value *tmp(llvm::Value *v); // a stack copy of v, to pass by address
void emitPrint(llvm::Value *v, const FType &t, bool quoted);
llvm::Function *printFn(const FType &t);
void emitFormat(llvm::Value *buf, llvm::Value *v, const FType &t, bool quoted); // append v as text to the str at buf
void bufText(llvm::Value *buf, const std::string &text);
llvm::Function *formatFn(const FType &t);
void printf_(const std::string &fmt, std::vector<llvm::Value *> args);
llvm::Value *strConst(const std::string &s);
llvm::Value *cstr(llvm::Value *strValue); // str -> char* for C
@ -236,13 +255,26 @@ public:
Value_ call(const CallExpr &c);
Value_ method(const MethodExpr &m);
Value_ callFinch(const Fn &fn, const std::vector<ExprPtr> &args, const std::vector<std::string> &names, Pos p,
const std::string &shownName);
const std::string &shownName, const Expr *site, llvm::Value *self = nullptr,
const std::string &selfRoot = "");
Value_ construct(StructInfo *s, const std::vector<ExprPtr> &args, const std::vector<std::string> &names, Pos p);
Value_ callC(const CFunc &f, const std::vector<ExprPtr> &args, Pos p);
Value_ convert(const FType &to, const std::vector<ExprPtr> &args, Pos p, const std::string &name);
Value_ convert(const FType &to, const std::vector<ExprPtr> &args, Pos p, const std::string &name, bool canFail = false);
Value_ print(const std::vector<ExprPtr> &args, Pos p);
Value_ addrOf(const std::vector<ExprPtr> &args, Pos p);
Value_ arrayMethod(const MethodExpr &m, Value_ *recv);
Value_ structMethod(const MethodExpr &m, LRef &r, const FType &t);
// ---------- codegen_map.cpp: maps ----------
llvm::StructType *mapEntryTy(const FType &t);
Value_ mapLit(const MapLitExpr &e, const FType *want);
LRef mapIndex(const IndexExpr &e, LRef &obj, const FType &t, bool forWrite);
Value_ mapMethod(const MethodExpr &m, Value_ *recv);
std::vector<llvm::Value *> mapArgs(llvm::Value *map, llvm::Value *keyAddr, const FType &t);
llvm::Value *mapSlot(llvm::Value *map, const FType &t, const Value_ &key, Pos p, bool fill);
llvm::Value *mapEntry(llvm::Value *map, const FType &t, llvm::Value *i);
void forEachEntry(llvm::Value *map, const FType &t, const std::function<void(llvm::Value *i, llvm::Value *entry)> &body);
llvm::Value *keyText(const Value_ &key);
bool isBuiltin(const std::string &n);
std::string headerHint(const std::string &name);
void checkArgs(const std::vector<ExprPtr> &args, const std::vector<std::string> &names, size_t want, Pos p,