From 2b9e79db9e1f4d1f74c6ca99c794ba4886e9f003 Mon Sep 17 00:00:00 2001 From: zinc Date: Thu, 15 Oct 2026 14:58:36 +0330 Subject: analyzer: infer .u from returns of fresh objects in the body (prescan guess, then checked as .u) --- src/compiler/base.zc | 2 +- src/compiler/decl.zc | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'src/compiler') diff --git a/src/compiler/base.zc b/src/compiler/base.zc index de699aa..7d06f8e 100644 --- a/src/compiler/base.zc +++ b/src/compiler/base.zc @@ -192,7 +192,7 @@ int bincap = bprec_BASE - bname_BASE, uncap = useq_BASE - uname_BASE, asgcap = a const int T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4, T_FLT = 5, T_BSTR = 6; const int K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5, K_ENH = 6, K_TYPE = 7, K_DOC = 8; -const int F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128, F_PRE = 256, F_O3 = 512, F_EXT = 1024, F_TPL = 2048, F_STATIC = 4096, F_DECL = 8192, F_RETOWN = 16384, F_EARLY = 32768; +const int F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128, F_PRE = 256, F_O3 = 512, F_EXT = 1024, F_TPL = 2048, F_STATIC = 4096, F_DECL = 8192, F_RETOWN = 16384, F_EARLY = 32768, F_GUESS = 65536; // variable states for the lifetime analyzer const int S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; // expression classes: plain word, a new object, or a bare variable (C_VAR + index) diff --git a/src/compiler/decl.zc b/src/compiler/decl.zc index 52d08c7..6bc8245 100644 --- a/src/compiler/decl.zc +++ b/src/compiler/decl.zc @@ -81,8 +81,44 @@ int prescan_fn(int ret, int n, int o3) { sflag[s] = sflag[s] | F_SIG | F_PRE; } if (o3) sflag[s] = sflag[s] | F_O3; + if (!ret && is_op(s_lb) && prescan_owned(n)) { sflag[s] = sflag[s] | F_GUESS; if (sflag[s] & F_PRE) ssig[s] = ssig[s] | 1; } return 0; } +// true for a token that makes a fresh owned object when called: new or heap. +// nothing else: a call to the function itself is no evidence. +int prescan_maker() { + return tk == T_ID && (rt.eq_w(tv, "rt.new") || rt.eq_w(tv, "new") || rt.eq_w(tv, "heap")); +} +// prescan: at the { of an untagged function's body, guess whether it returns an +// owned object: a return of a fresh object, or of a local last set from one. +// the guess only makes the function compile as if declared .u; its body is then +// checked against that, so a wrong guess is an error, never a silent leak. +int prescan_owned(int self) { + int depth = 0, n, k, found = 0, nown = 0; let owned[16]; + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; if (!depth) { next(); return found; } } + else if (tk == T_ID && rt.eq_w(tv, "return")) { + next(); + if (tk == T_ID) { + n = tv; peek(); + if (pk_tk == T_OP && pk_tv == s_lp && prescan_maker()) found = 1; + else if (pk_tk == T_OP && pk_tv == s_semi) { k = 0; while (k < nown) { if (owned[k] == n) found = 1; k++; } } + } + continue; + } + else if (tk == T_ID) { + n = tv; peek(); + if (pk_tk == T_OP && pk_tv == s_eq) { + next(); next(); + if (prescan_maker()) { peek(); if (pk_tk == T_OP && pk_tv == s_lp && nown < 16) { owned[nown] = n; nown++; } } + continue; + } + } + next(); + } + return found; +} // scan ahead, without emitting code, to learn what a block of tokens contains. int prescan(int kind, int obj) { int depth = 0, sp = panic, sn = nerr, pend = 0, po3 = 0, n, dep; char* oldmodule = module_name; word oldimports = module_imports; @@ -359,6 +395,7 @@ int fndef(int chk, int rty, int ret, int n) { if (amb & 1) { t = lookup(pn[i]); if (t >= 0 && skind[t] == K_TYPE) { pty[i] = sval[t]; typed = 1; } } amb = amb >> 1; i++; } + if (!ret && (sflag[s] & F_GUESS)) ret = 1; // prescan saw it return an owned object: compile it as .u sig = ret + np + np + tags * 64; lo = borrows_clause(pn, np, 1); // a definition after a prototype keeps the prototype's borrows clause -- cgit v1.2.3