mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
static inline C functions (mingw's printf and time), macOS libraries that call their host, lld's missing-library message
- a static inline function in a header is called through a pointer a small C file keeps (void *finch_inl_f = &f), built with the program's include folders and cached - --shared on macOS links with -undefined dynamic_lookup, as a .so does on Linux - "unable to find library -lx" (lld, MinGW) gives the library-not-found advice - the language server test shows the index records when alias completion fails Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GzNP6r8dg6WDxUVZy8wjL
This commit is contained in:
parent
72e96a3f71
commit
ff72f68804
8 changed files with 81 additions and 13 deletions
|
|
@ -18,6 +18,7 @@ struct CFunc {
|
||||||
ast.Type ret
|
ast.Type ret
|
||||||
[]ast.Type params
|
[]ast.Type params
|
||||||
bool variadic
|
bool variadic
|
||||||
|
bool inline // static inline in the header: called through a pointer a small C file keeps (finch_inl_name)
|
||||||
str unsupported // non-empty: why Finch can't call it (yet)
|
str unsupported // non-empty: why Finch can't call it (yet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,9 +536,7 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
||||||
}
|
}
|
||||||
f := CFunc(name: name, header: imp.header)
|
f := CFunc(name: name, header: imp.header)
|
||||||
if clang_Cursor_getStorageClass(c) == CX_SC_Static {
|
if clang_Cursor_getStorageClass(c) == CX_SC_Static {
|
||||||
f.unsupported = "is a static inline C function (it has no code to link against)"
|
f.inline = true // mingw's printf and time, header-only helpers
|
||||||
imp.out.fns[name] = f
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ft := clang_getCanonicalType(clang_getCursorType(c))
|
ft := clang_getCanonicalType(clang_getCursorType(c))
|
||||||
if ft.kind == CXType_FunctionNoProto {
|
if ft.kind == CXType_FunctionNoProto {
|
||||||
|
|
|
||||||
21
src/gen.fch
21
src/gen.fch
|
|
@ -5086,17 +5086,26 @@ fn callC(ptr[Gen] g, ast.Node c, cimport.CFunc f) -> Val {
|
||||||
tail = "..."
|
tail = "..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !g.cdone.contains("f:" + f.name) {
|
callee := "@" + f.name
|
||||||
|
sig := ""
|
||||||
|
if f.variadic {
|
||||||
|
sig = " (" + ptys.join(", ") + tail + ")"
|
||||||
|
}
|
||||||
|
if f.inline {
|
||||||
|
// a static inline function has no symbol: the linker builds a C file with void *finch_inl_name = &name;
|
||||||
|
if !g.cdone.contains("i:" + f.name) {
|
||||||
|
g.cdone.push("i:" + f.name)
|
||||||
|
g.head.push("@finch_inl_" + f.name + " = external global ptr")
|
||||||
|
}
|
||||||
|
callee = load(g, "ptr", "@finch_inl_" + f.name)
|
||||||
|
sig = " (" + ptys.join(", ") + tail + ")"
|
||||||
|
} else if !g.cdone.contains("f:" + f.name) {
|
||||||
g.cdone.push("f:" + f.name)
|
g.cdone.push("f:" + f.name)
|
||||||
if !runtimeDeclares(f.name) {
|
if !runtimeDeclares(f.name) {
|
||||||
g.head.push("declare" + retExt + " " + retLL + " @" + f.name + "(" + ptys.join(", ") + tail + ")")
|
g.head.push("declare" + retExt + " " + retLL + " @" + f.name + "(" + ptys.join(", ") + tail + ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sig := ""
|
text := "call" + retExt + " " + retLL + sig + " " + callee + "(" + args.join(", ") + ")"
|
||||||
if f.variadic {
|
|
||||||
sig = " (" + ptys.join(", ") + tail + ")"
|
|
||||||
}
|
|
||||||
text := "call" + retExt + " " + retLL + sig + " @" + f.name + "(" + args.join(", ") + ")"
|
|
||||||
if ret.kind == "void" {
|
if ret.kind == "void" {
|
||||||
emit(g, text)
|
emit(g, text)
|
||||||
releaseAll(g, temps, mark)
|
releaseAll(g, temps, mark)
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,40 @@ fn depsKey([]str files, str flags) -> str {
|
||||||
return fingerprint2(all.join("\n"))
|
return fingerprint2(all.join("\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "@finch_inl_name = external global ptr" in the IR -> a C file (in the cache) that defines them; "" if none
|
||||||
|
fn inlineFile(str ir, cimport.CImports ci) -> str {
|
||||||
|
[]str headers
|
||||||
|
[]str lines
|
||||||
|
for line in ir.split("\n") {
|
||||||
|
if line.starts_with("@finch_inl_") && line.ends_with(" = external global ptr") {
|
||||||
|
name := line.sub(11, line.find(" = "))
|
||||||
|
h := ci.fns[name].header
|
||||||
|
if !headers.contains(h) {
|
||||||
|
headers.push(h)
|
||||||
|
}
|
||||||
|
lines.push("void *finch_inl_" + name + " = (void *)&" + name + ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lines.len == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
[]str src
|
||||||
|
for h in headers {
|
||||||
|
src.push("#include \"" + h + "\"")
|
||||||
|
}
|
||||||
|
for l in lines {
|
||||||
|
src.push(l)
|
||||||
|
}
|
||||||
|
text := src.join("\n") + "\n"
|
||||||
|
dir := host.cacheDir() + "/c"
|
||||||
|
host.makeDirs(dir)
|
||||||
|
path := dir + "/inline_" + fingerprint2(text) + ".c"
|
||||||
|
if !file_exists(path) {
|
||||||
|
write_file(path, text)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
// A C file of the program (link "x.c"), built once and kept until it or a header it includes changes.
|
// A C file of the program (link "x.c"), built once and kept until it or a header it includes changes.
|
||||||
fn cObject(str src, str cc, str flags, platform.Target tg, str log) -> str! {
|
fn cObject(str src, str cc, str flags, platform.Target tg, str log) -> str! {
|
||||||
dir := host.cacheDir() + "/c"
|
dir := host.cacheDir() + "/c"
|
||||||
|
|
@ -225,6 +259,8 @@ fn linkFailed(str output, cimport.CImports ci, platform.Target tg) -> int {
|
||||||
missing.add(between(l, "unresolved external symbol ", " \n\r"))
|
missing.add(between(l, "unresolved external symbol ", " \n\r"))
|
||||||
} else if l.contains("cannot find -l") {
|
} else if l.contains("cannot find -l") {
|
||||||
libs.add(between(l, "cannot find -l", ": \n\r"))
|
libs.add(between(l, "cannot find -l", ": \n\r"))
|
||||||
|
} else if l.contains("unable to find library -l") {
|
||||||
|
libs.add(between(l, "unable to find library -l", ": \n\r")) // lld (MinGW)
|
||||||
} else if l.contains("library not found for -l") {
|
} else if l.contains("library not found for -l") {
|
||||||
libs.add(between(l, "library not found for -l", ": \n\r"))
|
libs.add(between(l, "library not found for -l", ": \n\r"))
|
||||||
} else if l.contains("ld: library '") {
|
} else if l.contains("ld: library '") {
|
||||||
|
|
@ -368,6 +404,20 @@ fn build(Build b, platform.Target tg, cimport.CImports ci) -> int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd := linkWith + " " + quote(obj) + " " + quote(rtobj) + " -o " + quote(b.out)
|
cmd := linkWith + " " + quote(obj) + " " + quote(rtobj) + " -o " + quote(b.out)
|
||||||
|
// static inline C functions the program calls: a C file that keeps a pointer to each
|
||||||
|
inl := inlineFile(b.ir, ci)
|
||||||
|
if inl.len > 0 {
|
||||||
|
pic := " -fPIC"
|
||||||
|
if tg.windows {
|
||||||
|
pic = ""
|
||||||
|
}
|
||||||
|
o := cObject(inl, cc, " -O2" + pic + b.cflags, tg, log) or {
|
||||||
|
print(err)
|
||||||
|
host.remove(log)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cmd = cmd + " " + quote(o)
|
||||||
|
}
|
||||||
if !tg.msvc {
|
if !tg.msvc {
|
||||||
cmd = cmd + " -lm"
|
cmd = cmd + " -lm"
|
||||||
}
|
}
|
||||||
|
|
@ -384,7 +434,7 @@ fn build(Build b, platform.Target tg, cimport.CImports ci) -> int {
|
||||||
}
|
}
|
||||||
if b.shared {
|
if b.shared {
|
||||||
if tg.darwin {
|
if tg.darwin {
|
||||||
cmd = cmd + " -dynamiclib"
|
cmd = cmd + " -dynamiclib -undefined dynamic_lookup" // like a .so: the host's functions are found when it is loaded
|
||||||
} else {
|
} else {
|
||||||
cmd = cmd + " -shared"
|
cmd = cmd + " -shared"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -495,8 +495,8 @@ fn main([]str args) -> int {
|
||||||
extra = extra + " -g" // or clang drops the debug info that is in the IR
|
extra = extra + " -g" // or clang drops the debug info that is in the IR
|
||||||
}
|
}
|
||||||
[]str cflagList
|
[]str cflagList
|
||||||
for d in extraDirs {
|
for d in dirs {
|
||||||
cflagList.push(" -I" + linker.quote(d))
|
cflagList.push(" -I" + linker.quote(d)) // also the program's own folders (its headers, for inline C functions)
|
||||||
}
|
}
|
||||||
cflagList.push(cflags)
|
cflagList.push(cflags)
|
||||||
if linker.build(linker.Build(out: out, ir: ir, rt: rt, opt: opt, extra: extra, shared: shared, links: links, name: absolute(file), cflags: cflagList.join(""), ldflags: ldflags), tg, ci) != 0 {
|
if linker.build(linker.Build(out: out, ir: ir, rt: rt, opt: opt, extra: extra, shared: shared, links: links, name: absolute(file), cflags: cflagList.join(""), ldflags: ldflags), tg, ci) != 0 {
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,12 @@ e3 = src3.replace(" print(", " pt.\n print(")
|
||||||
c.notify("textDocument/didChange", {"textDocument": {"uri": uri3, "version": 2}, "contentChanges": [{"text": e3}]})
|
c.notify("textDocument/didChange", {"textDocument": {"uri": uri3, "version": 2}, "contentChanges": [{"text": e3}]})
|
||||||
comp = c.request("textDocument/completion", {"textDocument": {"uri": uri3}, "position": {"line": 3, "character": 7}})
|
comp = c.request("textDocument/completion", {"textDocument": {"uri": uri3}, "position": {"line": 3, "character": 7}})
|
||||||
labels = [i["label"] for i in comp["items"]]
|
labels = [i["label"] for i in comp["items"]]
|
||||||
check("completion: a module under another name", "base" in labels and "join" in labels, labels)
|
ok3 = "base" in labels and "join" in labels
|
||||||
|
if not ok3: # show what the compiler's index says about the module, to see where the names differ
|
||||||
|
import subprocess
|
||||||
|
idx = subprocess.run([FINCH, "index", path3], capture_output=True, text=True).stdout
|
||||||
|
labels = [l for l in idx.splitlines() if l.startswith("@I") or (l.startswith("@F") and "base" in l)][:6]
|
||||||
|
check("completion: a module under another name", ok3, labels)
|
||||||
|
|
||||||
c.request("shutdown", None)
|
c.request("shutdown", None)
|
||||||
c.notify("exit", None)
|
c.notify("exit", None)
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,5 @@ fn main() {
|
||||||
print(abs(-2.5), abs(-3), min(EV_KEY, 7)) // Finch's abs, not C's int-only one
|
print(abs(-2.5), abs(-3), min(EV_KEY, 7)) // Finch's abs, not C's int-only one
|
||||||
bg := RAYWHITE // #define RAYWHITE (Color){ 245, 245, 245, 255 }
|
bg := RAYWHITE // #define RAYWHITE (Color){ 245, 245, 245, 255 }
|
||||||
print(bg.r, bg.a, DARK.g, DARK.a, HALF_UP.x, HALF_UP.y)
|
print(bg.r, bg.a, DARK.g, DARK.a, HALF_UP.x, HALF_UP.y)
|
||||||
|
print(twice(21)) // static inline in the header
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ true 32
|
||||||
0.5
|
0.5
|
||||||
2.5 3 7
|
2.5 3 7
|
||||||
245 255 20 0 0.5 -1
|
245 255 20 0 0.5 -1
|
||||||
|
42
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,6 @@ static inline int fill_key(Event *e, int key) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int poll_event(Event *e);
|
int poll_event(Event *e);
|
||||||
|
|
||||||
|
static inline int twice(int x) { return 2 * x; }
|
||||||
|
static inline int sum_all(int n, ...) { return n; }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue