mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 16:54:25 +00:00
Warnings for big copies: a struct with 256+ bytes of plain data as a parameter or in for x in list
- on stderr (finch ir stays the IR), as yellow warnings in editors (@W in the index), once per place - str, arrays and maps inside a struct don't count: passing it copies only their handles - none in the compiler, the examples or the tests other than the new one - docs: the warning instead of "not planned"; what was and wasn't checked for Android Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GzNP6r8dg6WDxUVZy8wjL
This commit is contained in:
parent
961ba951ec
commit
d557b5505e
15 changed files with 210 additions and 18 deletions
66
src/gen.fch
66
src/gen.fch
|
|
@ -7,6 +7,7 @@ import ast
|
|||
import docs
|
||||
import cimport
|
||||
import platform
|
||||
import "stdio.h"
|
||||
|
||||
struct Var {
|
||||
str name
|
||||
|
|
@ -166,6 +167,7 @@ struct Ref {
|
|||
|
||||
struct Gen {
|
||||
[]ast.Program progs
|
||||
[]str warned // file:line:col of the warnings given
|
||||
bool trace // each function keeps a frame { prev, name, file, line } in @finch_trace
|
||||
str frame // the current function's frame ("" in helpers)
|
||||
str frameLine // where its line goes
|
||||
|
|
@ -337,6 +339,58 @@ fn fail(ptr[Gen] g, ast.Node n, str msg) {
|
|||
exit(1)
|
||||
}
|
||||
|
||||
// A warning: the program is still built. On stderr (finch ir's output stays the IR); an @W record for editors.
|
||||
fn warn(ptr[Gen] g, ast.Node n, str msg) {
|
||||
where := g.files[n.file] + ":" + str(n.line) + ":" + str(n.col)
|
||||
if g.warned.contains(where) {
|
||||
return // once per place (a generic function is built more than once)
|
||||
}
|
||||
g.warned.push(where)
|
||||
if g.index {
|
||||
g.idx.push("@W\t" + g.files[n.file] + "\t" + str(n.line) + "\t" + str(n.col) + "\t" + msg)
|
||||
return
|
||||
}
|
||||
fputs(where + ": warning: " + msg + "\n", stderr)
|
||||
}
|
||||
|
||||
// About how many bytes of plain data (numbers, structs of them, C's [N]T) a value has, for the copy warnings
|
||||
fn byteSize(ptr[Gen] g, ast.Type t) -> int {
|
||||
k := t.kind
|
||||
if k == "struct" {
|
||||
sd := g.structs[findStruct(g, t.module, t.name)]
|
||||
if sd.isC && sd.csize > 0 {
|
||||
return sd.csize
|
||||
}
|
||||
n := 0
|
||||
for ft in sd.ftypes {
|
||||
n += byteSize(g, ft)
|
||||
}
|
||||
return n
|
||||
}
|
||||
if k == "fixed" && t.elem.len > 0 {
|
||||
return t.count * byteSize(g, t.elem[0])
|
||||
}
|
||||
if k == "str" || k == "array" || k == "map" {
|
||||
return 0 // only its { ptr, len, cap } is copied when it is passed: not counted
|
||||
}
|
||||
if k == "ptr" || k == "enum" || k == "int" || k == "float" {
|
||||
return 8
|
||||
}
|
||||
return bitsOf(t) / 8 + int(bitsOf(t) == 1)
|
||||
}
|
||||
|
||||
// a struct this big passed or looped over by value is copied byte by byte each time
|
||||
fn bigCopy(ptr[Gen] g, ast.Type t) -> int {
|
||||
if t.kind != "struct" {
|
||||
return 0
|
||||
}
|
||||
n := byteSize(g, t)
|
||||
if n >= 256 {
|
||||
return n
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn emit(ptr[Gen] g, str line) {
|
||||
// with -g every call in a function that has debug info has to say where it is
|
||||
if g.dbgScope >= 0 && line.contains("call ") {
|
||||
|
|
@ -2306,6 +2360,10 @@ fn define(ptr[Gen] g, int si) {
|
|||
}
|
||||
for k in 0..g.cur.params.len {
|
||||
pt := g.cur.params[k]
|
||||
big := bigCopy(g, pt)
|
||||
if big > 0 {
|
||||
warn(g, f.kids[k], "'" + g.cur.pnames[k] + "' is a " + ast.show(pt) + " (" + str(big) + " bytes), copied on every call to " + f.name + "; take ptr[" + ast.show(pt) + "] to share it instead (callers can still pass the variable as it is)")
|
||||
}
|
||||
params.push(llty(g, pt) + " %p" + str(k))
|
||||
v := "%p" + str(k)
|
||||
if owning(g, pt) && !g.cur.borrow[k] {
|
||||
|
|
@ -2714,6 +2772,14 @@ fn eachStmt(ptr[Gen] g, ast.Node s) {
|
|||
et = lt.elem[0]
|
||||
}
|
||||
copyElems := r.isPlace && owning(g, et) && mutates(g, rootVar(s.kids[0]), s.kids[1])
|
||||
big := bigCopy(g, et)
|
||||
if big > 0 && s.text.len == 0 {
|
||||
list := "list"
|
||||
if s.kids[0].kind == "var" {
|
||||
list = s.kids[0].name
|
||||
}
|
||||
warn(g, s, "each " + ast.show(et) + " (" + str(big) + " bytes) is copied into '" + s.name + "'; for i in 0.." + list + ".len with " + list + "[i] works on it in place")
|
||||
}
|
||||
iv := alloca(g, "i64")
|
||||
store(g, "i64", "0", iv)
|
||||
cond := newLabel(g, "each.cond")
|
||||
|
|
|
|||
24
src/lsp.fch
24
src/lsp.fch
|
|
@ -83,6 +83,7 @@ struct Analysis {
|
|||
[]str imports
|
||||
[]str importKeys // the module each import is: btn -> ui/button
|
||||
Diag diag
|
||||
[]Diag warnings // shown in yellow; the program still builds
|
||||
}
|
||||
|
||||
struct Server {
|
||||
|
|
@ -324,6 +325,8 @@ fn parseOutput(str output) -> Analysis {
|
|||
a.structs[k].types.push(f[4])
|
||||
} else if tag == "@E" && f.len >= 7 {
|
||||
a.enums.push(EnumRec(module: f[1], name: f[2], file: f[3], line: intOf(f[4]), col: intOf(f[5]), variants: f[6].split(",")))
|
||||
} else if tag == "@W" && f.len >= 5 {
|
||||
a.warnings.push(Diag(has: true, file: f[1], line: intOf(f[2]), col: intOf(f[3]), message: f[4]))
|
||||
} else if tag == "@I" && f.len >= 2 {
|
||||
a.imports.push(f[1])
|
||||
if f.len >= 3 {
|
||||
|
|
@ -399,6 +402,27 @@ fn Server.publish(str path, Analysis a) {
|
|||
list.push(d)
|
||||
now.push(f)
|
||||
}
|
||||
for w in a.warnings {
|
||||
if w.file == f {
|
||||
d := json.new_object()
|
||||
wl := w.line - 1
|
||||
if wl < 0 {
|
||||
wl = 0
|
||||
}
|
||||
wc := w.col - 1
|
||||
if wc < 0 {
|
||||
wc = 0
|
||||
}
|
||||
d.set("range", range(wl, wc, wc + 1))
|
||||
d.set("severity", num(2))
|
||||
d.set("source", json.new_text("finch"))
|
||||
d.set("message", json.new_text(w.message))
|
||||
list.push(d)
|
||||
if !now.contains(f) {
|
||||
now.push(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
p := json.new_object()
|
||||
p.set("uri", json.new_text(pathToUri(f)))
|
||||
p.set("diagnostics", list)
|
||||
|
|
|
|||
Loading…
Reference in a new issue