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 params
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -535,9 +536,7 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
|||
}
|
||||
f := CFunc(name: name, header: imp.header)
|
||||
if clang_Cursor_getStorageClass(c) == CX_SC_Static {
|
||||
f.unsupported = "is a static inline C function (it has no code to link against)"
|
||||
imp.out.fns[name] = f
|
||||
return
|
||||
f.inline = true // mingw's printf and time, header-only helpers
|
||||
}
|
||||
ft := clang_getCanonicalType(clang_getCursorType(c))
|
||||
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 = "..."
|
||||
}
|
||||
}
|
||||
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)
|
||||
if !runtimeDeclares(f.name) {
|
||||
g.head.push("declare" + retExt + " " + retLL + " @" + f.name + "(" + ptys.join(", ") + tail + ")")
|
||||
}
|
||||
}
|
||||
sig := ""
|
||||
if f.variadic {
|
||||
sig = " (" + ptys.join(", ") + tail + ")"
|
||||
}
|
||||
text := "call" + retExt + " " + retLL + sig + " @" + f.name + "(" + args.join(", ") + ")"
|
||||
text := "call" + retExt + " " + retLL + sig + " " + callee + "(" + args.join(", ") + ")"
|
||||
if ret.kind == "void" {
|
||||
emit(g, text)
|
||||
releaseAll(g, temps, mark)
|
||||
|
|
|
|||
|
|
@ -98,6 +98,40 @@ fn depsKey([]str files, str flags) -> str {
|
|||
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.
|
||||
fn cObject(str src, str cc, str flags, platform.Target tg, str log) -> str! {
|
||||
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"))
|
||||
} else if l.contains("cannot find -l") {
|
||||
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") {
|
||||
libs.add(between(l, "library not found for -l", ": \n\r"))
|
||||
} 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)
|
||||
// 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 {
|
||||
cmd = cmd + " -lm"
|
||||
}
|
||||
|
|
@ -384,7 +434,7 @@ fn build(Build b, platform.Target tg, cimport.CImports ci) -> int {
|
|||
}
|
||||
if b.shared {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
[]str cflagList
|
||||
for d in extraDirs {
|
||||
cflagList.push(" -I" + linker.quote(d))
|
||||
for d in dirs {
|
||||
cflagList.push(" -I" + linker.quote(d)) // also the program's own folders (its headers, for inline C functions)
|
||||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -204,7 +204,12 @@ e3 = src3.replace(" print(", " pt.\n print(")
|
|||
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}})
|
||||
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.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
|
||||
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(twice(21)) // static inline in the header
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ true 32
|
|||
0.5
|
||||
2.5 3 7
|
||||
245 255 20 0 0.5 -1
|
||||
42
|
||||
|
|
|
|||
|
|
@ -36,3 +36,6 @@ static inline int fill_key(Event *e, int key) {
|
|||
return 1;
|
||||
}
|
||||
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