mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
C import: unions (SDL_Event) and their fields; #defines that are expressions, casts or text; typedef'd structs no function mentions; FLAG_A | FLAG_B fits a u64
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6142e47e71
commit
d0666cf788
7 changed files with 314 additions and 13 deletions
165
src/cimport.fch
165
src/cimport.fch
|
|
@ -21,11 +21,13 @@ struct CFunc {
|
||||||
str unsupported // non-empty: why Finch can't call it (yet)
|
str unsupported // non-empty: why Finch can't call it (yet)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CConst { // enum values and simple #define numbers
|
struct CConst { // enum values and #define constants
|
||||||
bool isFloat
|
bool isFloat
|
||||||
int i
|
int i
|
||||||
float f
|
float f
|
||||||
str text // a float as written, so no digits are lost: 3.14159265358979323846
|
str text // a float as written, so no digits are lost: 3.14159265358979323846
|
||||||
|
bool isStr // #define NAME "text"
|
||||||
|
str s
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CField {
|
struct CField {
|
||||||
|
|
@ -44,6 +46,7 @@ struct CStruct {
|
||||||
int align
|
int align
|
||||||
str unsupported // why it can't be used by value (pointers to it still work)
|
str unsupported // why it can't be used by value (pointers to it still work)
|
||||||
bool hasHidden
|
bool hasHidden
|
||||||
|
bool isUnion // every field starts at byte 0
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CImports {
|
struct CImports {
|
||||||
|
|
@ -75,6 +78,7 @@ struct Importer {
|
||||||
map[str]str recordNames // record USR -> Finch name (a typedef name wins)
|
map[str]str recordNames // record USR -> Finch name (a typedef name wins)
|
||||||
map[str]bool defining // USR -> true while its fields are being mapped
|
map[str]bool defining // USR -> true while its fields are being mapped
|
||||||
int anon
|
int anon
|
||||||
|
[]str exprMacros // #defines that are expressions: clang works out their values
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text(CXString s) -> str {
|
fn text(CXString s) -> str {
|
||||||
|
|
@ -169,6 +173,7 @@ fn defineRecord(ptr[Importer] imp, CXType type) -> str {
|
||||||
s := CStruct(name: name, size: size, align: int(clang_Type_getAlignOf(t)))
|
s := CStruct(name: name, size: size, align: int(clang_Type_getAlignOf(t)))
|
||||||
if clang_getCursorKind(decl) == CXCursor_UnionDecl {
|
if clang_getCursorKind(decl) == CXCursor_UnionDecl {
|
||||||
s.unsupported = "is a C union"
|
s.unsupported = "is a C union"
|
||||||
|
s.isUnion = true
|
||||||
}
|
}
|
||||||
// the fields are collected by a callback that gets the importer and the struct through one pointer
|
// the fields are collected by a callback that gets the importer and the struct through one pointer
|
||||||
ctx := FieldCtx(imp: imp, s: addr(s))
|
ctx := FieldCtx(imp: imp, s: addr(s))
|
||||||
|
|
@ -194,9 +199,10 @@ fn visitField(CXCursor field, ptr data) -> i32 {
|
||||||
if clang_Cursor_isBitField(field) != 0 {
|
if clang_Cursor_isBitField(field) != 0 {
|
||||||
c.s.unsupported = "has bit fields"
|
c.s.unsupported = "has bit fields"
|
||||||
f.hidden = true
|
f.hidden = true
|
||||||
} else if ft.kind == CXType_Record && clang_getCursorKind(clang_getTypeDeclaration(ft)) == CXCursor_UnionDecl {
|
|
||||||
f.hidden = true // unions are kept as opaque bytes
|
|
||||||
} else {
|
} else {
|
||||||
|
if ft.kind == CXType_Record && clang_getCursorKind(clang_getTypeDeclaration(ft)) == CXCursor_UnionDecl {
|
||||||
|
c.s.unsupported = "has a union inside" // its fields can be used, but not passed by value
|
||||||
|
}
|
||||||
m := mapType(c.imp, ft, false, false)
|
m := mapType(c.imp, ft, false, false)
|
||||||
if m.ok {
|
if m.ok {
|
||||||
f.ty = m.ty
|
f.ty = m.ty
|
||||||
|
|
@ -293,9 +299,6 @@ fn mapType(ptr[Importer] imp, CXType type, bool signature, bool param) -> Mapped
|
||||||
return ok(ast.ptrTo(inner.ty))
|
return ok(ast.ptrTo(inner.ty))
|
||||||
}
|
}
|
||||||
if k == CXType_Record {
|
if k == CXType_Record {
|
||||||
if clang_getCursorKind(clang_getTypeDeclaration(t)) == CXCursor_UnionDecl {
|
|
||||||
return no("uses a C union by value")
|
|
||||||
}
|
|
||||||
name := defineRecord(imp, t)
|
name := defineRecord(imp, t)
|
||||||
if name.len == 0 {
|
if name.len == 0 {
|
||||||
return no("uses an incomplete C struct by value")
|
return no("uses an incomplete C struct by value")
|
||||||
|
|
@ -366,19 +369,93 @@ fn parseInt(str lit) -> Number {
|
||||||
return Number(c: CConst(i: n), ok: true)
|
return Number(c: CConst(i: n), ok: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple #defines: a single number, maybe negative, maybe in parentheses.
|
// The tokens after a #define's name; `safe` is false if any of them could not be part of a
|
||||||
fn macroValue(ptr[Importer] imp, CXCursor c) -> Number {
|
// constant expression (braces, semicolons, statements), so it is never handed to clang.
|
||||||
|
struct MacroTokens {
|
||||||
|
[]str parts
|
||||||
|
bool safe
|
||||||
|
}
|
||||||
|
|
||||||
|
fn macroTokens(ptr[Importer] imp, CXCursor c) -> MacroTokens {
|
||||||
ptr[CXToken] tokens = null
|
ptr[CXToken] tokens = null
|
||||||
u32 n = 0
|
u32 n = 0
|
||||||
clang_tokenize(imp.tu, clang_getCursorExtent(c), addr(tokens), addr(n))
|
clang_tokenize(imp.tu, clang_getCursorExtent(c), addr(tokens), addr(n))
|
||||||
[]str parts
|
out := MacroTokens(safe: true)
|
||||||
|
allowed := ["(", ")", "+", "-", "*", "/", "%", "<<", ">>", "|", "&", "^", "~", "!", "<", ">", "<=", ">=", "==", "!=", "&&", "||", "?", ":", ","]
|
||||||
|
words := ["sizeof", "unsigned", "signed", "int", "long", "short", "char", "float", "double", "const", "_Bool"]
|
||||||
|
depth := 0
|
||||||
for k in 1..int(n) { // token 0 is the macro's name
|
for k in 1..int(n) { // token 0 is the macro's name
|
||||||
if clang_getTokenKind(tokens[k]) == CXToken_Comment {
|
kind := clang_getTokenKind(tokens[k])
|
||||||
|
if kind == CXToken_Comment {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parts.push(text(clang_getTokenSpelling(imp.tu, tokens[k])))
|
t := text(clang_getTokenSpelling(imp.tu, tokens[k]))
|
||||||
|
out.parts.push(t)
|
||||||
|
if kind == CXToken_Punctuation {
|
||||||
|
if !allowed.contains(t) {
|
||||||
|
out.safe = false
|
||||||
|
}
|
||||||
|
if t == "(" {
|
||||||
|
depth += 1
|
||||||
|
} else if t == ")" {
|
||||||
|
depth -= 1
|
||||||
|
}
|
||||||
|
} else if kind == CXToken_Keyword && !words.contains(t) {
|
||||||
|
out.safe = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clang_disposeTokens(imp.tu, tokens, n)
|
clang_disposeTokens(imp.tu, tokens, n)
|
||||||
|
if depth != 0 {
|
||||||
|
out.safe = false
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// "text" as C writes it -> the text: \n \t \" \\ \0 and \xNN; "a" "b" are joined
|
||||||
|
fn cString([]str parts) -> Number {
|
||||||
|
out := ""
|
||||||
|
for p in parts {
|
||||||
|
if p.len < 2 || p[0] != '"' || p[p.len - 1] != '"' {
|
||||||
|
return Number()
|
||||||
|
}
|
||||||
|
i := 1
|
||||||
|
while i < p.len - 1 {
|
||||||
|
c := p[i]
|
||||||
|
if c == '\\' && i + 1 < p.len - 1 {
|
||||||
|
i += 1
|
||||||
|
e := p[i]
|
||||||
|
if e == 'n' {
|
||||||
|
c = '\n'
|
||||||
|
} else if e == 't' {
|
||||||
|
c = '\t'
|
||||||
|
} else if e == 'r' {
|
||||||
|
c = '\r'
|
||||||
|
} else if e == '0' {
|
||||||
|
c = char(0)
|
||||||
|
} else if e == 'x' {
|
||||||
|
v := 0
|
||||||
|
while i + 1 < p.len - 1 && "0123456789abcdefABCDEF".contains(str(p[i + 1])) {
|
||||||
|
i += 1
|
||||||
|
v = v * 16 + parseInt("0x" + str(p[i])).c.i
|
||||||
|
}
|
||||||
|
c = char(v & 255)
|
||||||
|
} else {
|
||||||
|
c = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = out + str(c)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Number(c: CConst(isStr: true, s: out), ok: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple #defines: a single number, maybe negative, maybe in parentheses; or a "text".
|
||||||
|
fn macroValue(ptr[Importer] imp, CXCursor c) -> Number {
|
||||||
|
parts := macroTokens(imp, c).parts
|
||||||
|
if parts.len > 0 && parts[0].starts_with("\"") {
|
||||||
|
return cString(parts)
|
||||||
|
}
|
||||||
|
|
||||||
while parts.len >= 2 && parts[0] == "(" && parts[parts.len - 1] == ")" {
|
while parts.len >= 2 && parts[0] == "(" && parts[parts.len - 1] == ")" {
|
||||||
parts.remove(0)
|
parts.remove(0)
|
||||||
|
|
@ -438,6 +515,7 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
||||||
if !used && !imp.out.structs.has(name) {
|
if !used && !imp.out.structs.has(name) {
|
||||||
imp.recordNames[usr] = name
|
imp.recordNames[usr] = name
|
||||||
}
|
}
|
||||||
|
defineRecord(imp, u) // usable even if no function mentions it: Motion m
|
||||||
}
|
}
|
||||||
} else if kind == CXCursor_FunctionDecl {
|
} else if kind == CXCursor_FunctionDecl {
|
||||||
if imp.out.fns.has(name) {
|
if imp.out.fns.has(name) {
|
||||||
|
|
@ -483,7 +561,7 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
||||||
if g.ok {
|
if g.ok {
|
||||||
imp.out.globals[name] = g.ty
|
imp.out.globals[name] = g.ty
|
||||||
}
|
}
|
||||||
} else if kind == CXCursor_StructDecl {
|
} else if kind == CXCursor_StructDecl || kind == CXCursor_UnionDecl {
|
||||||
if clang_isCursorDefinition(c) != 0 && name.len > 0 && !name.contains(" ") {
|
if clang_isCursorDefinition(c) != 0 && name.len > 0 && !name.contains(" ") {
|
||||||
usr := text(clang_getCursorUSR(c))
|
usr := text(clang_getCursorUSR(c))
|
||||||
if !imp.recordNames.has(usr) {
|
if !imp.recordNames.has(usr) {
|
||||||
|
|
@ -500,9 +578,17 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
||||||
if clang_Cursor_isMacroBuiltin(c) != 0 || clang_Cursor_isMacroFunctionLike(c) != 0 {
|
if clang_Cursor_isMacroBuiltin(c) != 0 || clang_Cursor_isMacroFunctionLike(c) != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if imp.out.consts.has(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
v := macroValue(imp, c)
|
v := macroValue(imp, c)
|
||||||
if v.ok && !imp.out.consts.has(name) {
|
if v.ok {
|
||||||
imp.out.consts[name] = v.c
|
imp.out.consts[name] = v.c
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mt := macroTokens(imp, c)
|
||||||
|
if mt.safe && mt.parts.len > 0 {
|
||||||
|
imp.exprMacros.push(name) // (1 << 3), (FLAG_A | FLAG_B), ((Uint64)1): asked of clang later
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -563,7 +649,60 @@ fn importHeaders([]Header headers, []str dirs, str target, bool windows, str ccF
|
||||||
imp.header = h.path
|
imp.header = h.path
|
||||||
clang_visitChildren(clang_getTranslationUnitCursor(tu), addr(visitTop), addr(imp))
|
clang_visitChildren(clang_getTranslationUnitCursor(tu), addr(visitTop), addr(imp))
|
||||||
clang_disposeTranslationUnit(tu)
|
clang_disposeTranslationUnit(tu)
|
||||||
|
evalMacros(addr(imp), index, h, argv)
|
||||||
}
|
}
|
||||||
clang_disposeIndex(index)
|
clang_disposeIndex(index)
|
||||||
return imp.out
|
return imp.out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #defines that are expressions: each becomes `static const __auto_type __finch_m_NAME = (NAME);`
|
||||||
|
// in a second look at the header, and clang works out the value. One that isn't a constant
|
||||||
|
// (a type, a function call) just gives clang an error there, and is left out.
|
||||||
|
fn evalMacros(ptr[Importer] imp, ptr index, Header h, []ptr[char] argv) {
|
||||||
|
if imp.exprMacros.len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
src := "#include \"" + h.path + "\"\n"
|
||||||
|
for m in imp.exprMacros {
|
||||||
|
src = src + "static const __auto_type __finch_m_" + m + " = (" + m + ");\n"
|
||||||
|
}
|
||||||
|
[]str none
|
||||||
|
imp.exprMacros = none
|
||||||
|
file := host.unsavedFile("finch_macros.c", src)
|
||||||
|
ptr tu = null
|
||||||
|
err := clang_parseTranslationUnit2(index, "finch_macros.c", argv.ptr, i32(argv.len), addr(file), 1, u32(CXTranslationUnit_SkipFunctionBodies), addr(tu))
|
||||||
|
if err != CXError_Success || tu == null {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clang_visitChildren(clang_getTranslationUnitCursor(tu), addr(visitMacroVar), imp)
|
||||||
|
clang_disposeTranslationUnit(tu)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visitMacroVar(CXCursor c, CXCursor parent, ptr data) -> i32 {
|
||||||
|
ptr[Importer] imp = data
|
||||||
|
if clang_getCursorKind(c) != CXCursor_VarDecl {
|
||||||
|
return CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
name := text(clang_getCursorSpelling(c))
|
||||||
|
if !name.starts_with("__finch_m_") {
|
||||||
|
return CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
name = name.sub(10, name.len)
|
||||||
|
r := clang_Cursor_Evaluate(c)
|
||||||
|
if r == null || imp.out.consts.has(name) {
|
||||||
|
return CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
k := clang_EvalResult_getKind(r)
|
||||||
|
if k == CXEval_Int {
|
||||||
|
imp.out.consts[name] = CConst(i: int(clang_EvalResult_getAsLongLong(r)))
|
||||||
|
} else if k == CXEval_Float {
|
||||||
|
v := clang_EvalResult_getAsDouble(r)
|
||||||
|
if v == v && v - v == 0.0 { // not nan or inf
|
||||||
|
imp.out.consts[name] = CConst(isFloat: true, f: v, text: str(v))
|
||||||
|
}
|
||||||
|
} else if k == CXEval_StrLiteral {
|
||||||
|
imp.out.consts[name] = CConst(isStr: true, s: clang_EvalResult_getAsStr(r))
|
||||||
|
}
|
||||||
|
clang_EvalResult_dispose(r)
|
||||||
|
return CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
|
||||||
89
src/gen.fch
89
src/gen.fch
|
|
@ -140,6 +140,7 @@ struct StructDef {
|
||||||
int index
|
int index
|
||||||
int state // 0 new, 1 resolving, 2 done
|
int state // 0 new, 1 resolving, 2 done
|
||||||
bool owning
|
bool owning
|
||||||
|
bool isUnion // C: a union, every field at byte 0
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Val {
|
struct Val {
|
||||||
|
|
@ -1857,6 +1858,10 @@ fn genericCall(ptr[Gen] g, ast.Node c, int ti, int first, str shown) -> Val {
|
||||||
fn resolveCStruct(ptr[Gen] g, int i) {
|
fn resolveCStruct(ptr[Gen] g, int i) {
|
||||||
cs := g.ci.structs[g.structs[i].name]
|
cs := g.ci.structs[g.structs[i].name]
|
||||||
g.structs[i].state = 1
|
g.structs[i].state = 1
|
||||||
|
if cs.isUnion {
|
||||||
|
resolveCUnion(g, i, cs)
|
||||||
|
return
|
||||||
|
}
|
||||||
cur := 0
|
cur := 0
|
||||||
[]str elems
|
[]str elems
|
||||||
for f in cs.fields {
|
for f in cs.fields {
|
||||||
|
|
@ -1890,6 +1895,37 @@ fn resolveCStruct(ptr[Gen] g, int i) {
|
||||||
g.head.push("%S.C." + cs.name + " = type { " + elems.join(", ") + " }")
|
g.head.push("%S.C." + cs.name + " = type { " + elems.join(", ") + " }")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A C union: room for its biggest field, aligned like C aligns it; every field is at byte 0.
|
||||||
|
fn resolveCUnion(ptr[Gen] g, int i, cimport.CStruct cs) {
|
||||||
|
for f in cs.fields {
|
||||||
|
if f.hidden {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t := resolve(g, f.ty, ast.Node())
|
||||||
|
if t.kind == "struct" {
|
||||||
|
resolveStruct(g, findStruct(g, t.module, t.name))
|
||||||
|
}
|
||||||
|
g.structs[i].fnames.push(f.name)
|
||||||
|
g.structs[i].ftypes.push(t)
|
||||||
|
g.structs[i].lidx.push(0)
|
||||||
|
g.structs[i].offs.push(0)
|
||||||
|
}
|
||||||
|
a := cs.align
|
||||||
|
if a > 8 {
|
||||||
|
a = 8
|
||||||
|
}
|
||||||
|
if a < 1 {
|
||||||
|
a = 1
|
||||||
|
}
|
||||||
|
n := (cs.size + a - 1) / a
|
||||||
|
g.structs[i].unsupported = cs.unsupported
|
||||||
|
g.structs[i].isUnion = true
|
||||||
|
g.structs[i].csize = cs.size
|
||||||
|
g.structs[i].calign = cs.align
|
||||||
|
g.structs[i].state = 2
|
||||||
|
g.head.push("%S.C." + cs.name + " = type { [" + str(n) + " x i" + str(a * 8) + "] }")
|
||||||
|
}
|
||||||
|
|
||||||
fn resolveStruct(ptr[Gen] g, int i) {
|
fn resolveStruct(ptr[Gen] g, int i) {
|
||||||
if g.structs[i].state == 2 {
|
if g.structs[i].state == 2 {
|
||||||
return
|
return
|
||||||
|
|
@ -3567,6 +3603,14 @@ fn arith(ptr[Gen] g, str o, Val l, Val r, ast.Node at) -> Val {
|
||||||
panicIf(g, op(g, "icmp eq " + lt + " " + r.v + ", 0"), "division by zero", at)
|
panicIf(g, op(g, "icmp eq " + lt + " " + r.v + ", 0"), "division by zero", at)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if l.lit && r.lit && l.ty.kind == "int" {
|
||||||
|
// numbers written in the code stay such a number: FLAG_A | FLAG_B fits a u64 or a u32
|
||||||
|
a := int(l.v) or 0
|
||||||
|
b := int(r.v) or 0
|
||||||
|
if (o != "<<" && o != ">>") || (b >= 0 && b < 63) {
|
||||||
|
return Val(v: str(foldInt(o, a, b)), ty: l.ty, lit: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
return Val(v: op(g, insts[k] + " " + lt + " " + l.v + ", " + r.v), ty: l.ty)
|
return Val(v: op(g, insts[k] + " " + lt + " " + l.v + ", " + r.v), ty: l.ty)
|
||||||
}
|
}
|
||||||
if isFloat(l.ty) {
|
if isFloat(l.ty) {
|
||||||
|
|
@ -3582,6 +3626,29 @@ fn arith(ptr[Gen] g, str o, Val l, Val r, ast.Node at) -> Val {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn foldInt(str o, int a, int b) -> int {
|
||||||
|
if o == "+" {
|
||||||
|
return a + b
|
||||||
|
} else if o == "-" {
|
||||||
|
return a - b
|
||||||
|
} else if o == "*" {
|
||||||
|
return a * b
|
||||||
|
} else if o == "/" {
|
||||||
|
return a / b
|
||||||
|
} else if o == "%" {
|
||||||
|
return a % b
|
||||||
|
} else if o == "&" {
|
||||||
|
return a & b
|
||||||
|
} else if o == "|" {
|
||||||
|
return a | b
|
||||||
|
} else if o == "^" {
|
||||||
|
return a ^ b
|
||||||
|
} else if o == "<<" {
|
||||||
|
return a << b
|
||||||
|
}
|
||||||
|
return a >> b
|
||||||
|
}
|
||||||
|
|
||||||
fn compare(ptr[Gen] g, str o, Val l, Val r, ast.Node at) -> Val {
|
fn compare(ptr[Gen] g, str o, Val l, Val r, ast.Node at) -> Val {
|
||||||
eq := o == "==" || o == "!="
|
eq := o == "==" || o == "!="
|
||||||
if l.ty.kind == "ptr" || r.ty.kind == "ptr" || l.ty.kind == "null" || r.ty.kind == "null" {
|
if l.ty.kind == "ptr" || r.ty.kind == "ptr" || l.ty.kind == "null" || r.ty.kind == "null" {
|
||||||
|
|
@ -3747,6 +3814,9 @@ fn ref(ptr[Gen] g, ast.Node e, bool write) -> Ref {
|
||||||
id := lookup(g, e.name)
|
id := lookup(g, e.name)
|
||||||
if id < 0 && g.ci.consts.has(e.name) {
|
if id < 0 && g.ci.consts.has(e.name) {
|
||||||
c := g.ci.consts[e.name]
|
c := g.ci.consts[e.name]
|
||||||
|
if c.isStr {
|
||||||
|
return Ref(isPlace: false, val: strConst(g, c.s))
|
||||||
|
}
|
||||||
if c.isFloat {
|
if c.isFloat {
|
||||||
return Ref(isPlace: false, val: Val(v: llFloat(c.text), ty: T("float"), lit: true))
|
return Ref(isPlace: false, val: Val(v: llFloat(c.text), ty: T("float"), lit: true))
|
||||||
}
|
}
|
||||||
|
|
@ -4017,6 +4087,10 @@ fn member(ptr[Gen] g, ast.Node m, bool write) -> Ref {
|
||||||
fp := op(g, "getelementptr inbounds " + llty(g, t) + ", ptr " + obj.pl.addr + ", i32 0, i32 " + str(sd.lidx[k]))
|
fp := op(g, "getelementptr inbounds " + llty(g, t) + ", ptr " + obj.pl.addr + ", i32 0, i32 " + str(sd.lidx[k]))
|
||||||
return Ref(isPlace: true, pl: Place(addr: fp, ty: ft))
|
return Ref(isPlace: true, pl: Place(addr: fp, ty: ft))
|
||||||
}
|
}
|
||||||
|
if sd.isUnion {
|
||||||
|
u := spill(g, obj.val)
|
||||||
|
return Ref(isPlace: false, val: Val(v: load(g, llty(g, ft), u), ty: ft))
|
||||||
|
}
|
||||||
fv := op(g, "extractvalue " + llty(g, t) + " " + obj.val.v + ", " + str(sd.lidx[k]))
|
fv := op(g, "extractvalue " + llty(g, t) + " " + obj.val.v + ", " + str(sd.lidx[k]))
|
||||||
out := Val(v: fv, ty: ft)
|
out := Val(v: fv, ty: ft)
|
||||||
if owning(g, ft) && obj.val.fresh {
|
if owning(g, ft) && obj.val.fresh {
|
||||||
|
|
@ -5197,6 +5271,21 @@ fn construct(ptr[Gen] g, int si, []ast.Node args, []str names, ast.Node at) -> V
|
||||||
t := ast.Type(kind: "struct", name: sd.name, module: sd.module)
|
t := ast.Type(kind: "struct", name: sd.name, module: sd.module)
|
||||||
st := llty(g, t)
|
st := llty(g, t)
|
||||||
named := names.len > 0 && names[0].len > 0
|
named := names.len > 0 && names[0].len > 0
|
||||||
|
if sd.isUnion && args.len > 0 {
|
||||||
|
// a union holds one field at a time: Ev(key: k)
|
||||||
|
if args.len != 1 || !named {
|
||||||
|
fail(g, at, sd.name + " is a C union: give one field by name, like " + sd.name + "(" + sd.fnames[0] + ": ...), or none")
|
||||||
|
}
|
||||||
|
k := sd.fnames.find(names[0])
|
||||||
|
if k < 0 {
|
||||||
|
fail(g, args[0], sd.name + " has no field '" + names[0] + "'")
|
||||||
|
}
|
||||||
|
u := alloca(g, st)
|
||||||
|
store(g, st, "zeroinitializer", u)
|
||||||
|
fv := coerce(g, exprWant(g, args[0], sd.ftypes[k]), sd.ftypes[k], args[0], "the field '" + names[0] + "'")
|
||||||
|
store(g, llty(g, sd.ftypes[k]), fv.v, u)
|
||||||
|
return Val(v: load(g, st, u), ty: t)
|
||||||
|
}
|
||||||
[]int given
|
[]int given
|
||||||
for k in 0..sd.fnames.len {
|
for k in 0..sd.fnames.len {
|
||||||
given.push(-1)
|
given.push(-1)
|
||||||
|
|
|
||||||
25
tests/new/c_unions_macros.fch
Normal file
25
tests/new/c_unions_macros.fch
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// C unions (SDL_Event-style) and #defines that are expressions, strings or casts
|
||||||
|
import "cunion.h"
|
||||||
|
link "cunion.c"
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
print(EV_MOTION, EV_KEY, FLAG_BIG, FLAGS_BOTH, HALF, GREETING, LONG_NAME)
|
||||||
|
u64 flags = FLAG_BIG | FLAG_GL
|
||||||
|
u32 small = EV_KEY
|
||||||
|
print(flags, small)
|
||||||
|
|
||||||
|
Event e
|
||||||
|
if poll_event(addr(e)) != 0 && e.type == EV_MOTION {
|
||||||
|
print("motion", e.motion.x, e.motion.y)
|
||||||
|
}
|
||||||
|
k := Event(key: Key(type: EV_KEY, key: 32))
|
||||||
|
print(k.type == EV_KEY, k.key.key)
|
||||||
|
[]Event queue
|
||||||
|
queue.push(e)
|
||||||
|
queue.push(k)
|
||||||
|
print(queue[1].key.key, queue.len)
|
||||||
|
|
||||||
|
Tagged t
|
||||||
|
t.as.f = 0.5
|
||||||
|
print(t.as.f)
|
||||||
|
}
|
||||||
6
tests/new/c_unions_macros.out
Normal file
6
tests/new/c_unions_macros.out
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
1024 1025 1099511627776 1099511627778 0.5 hi there ab
|
||||||
|
1099511627778 1025
|
||||||
|
motion 1.5 -2
|
||||||
|
true 32
|
||||||
|
32 2
|
||||||
|
0.5
|
||||||
7
tests/new/cunion.c
Normal file
7
tests/new/cunion.c
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "cunion.h"
|
||||||
|
int poll_event(Event *e) {
|
||||||
|
e->motion.type = EV_MOTION;
|
||||||
|
e->motion.x = 1.5f;
|
||||||
|
e->motion.y = -2.0f;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
30
tests/new/cunion.h
Normal file
30
tests/new/cunion.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// C unions and #define constants that are expressions (tests/new/c_unions_macros.fch)
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct { uint32_t type; float x, y; } Motion;
|
||||||
|
typedef struct { uint32_t type; int32_t key; } Key;
|
||||||
|
typedef union {
|
||||||
|
uint32_t type;
|
||||||
|
Motion motion;
|
||||||
|
Key key;
|
||||||
|
uint8_t padding[64];
|
||||||
|
} Event;
|
||||||
|
|
||||||
|
typedef struct { int kind; union { int i; float f; } as; } Tagged;
|
||||||
|
|
||||||
|
#define EV_MOTION 0x400u
|
||||||
|
#define EV_KEY (EV_MOTION + 1)
|
||||||
|
#define FLAG_BIG ((uint64_t)1 << 40)
|
||||||
|
#define FLAG_GL 0x2ULL
|
||||||
|
#define FLAGS_BOTH (FLAG_BIG | FLAG_GL)
|
||||||
|
#define HALF (1.0 / 2)
|
||||||
|
#define GREETING "hi\tthere"
|
||||||
|
#define LONG_NAME "a" "b"
|
||||||
|
#define NOT_A_CONSTANT do { } while (0)
|
||||||
|
|
||||||
|
static inline int fill_key(Event *e, int key) {
|
||||||
|
e->key.type = EV_KEY;
|
||||||
|
e->key.key = key;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int poll_event(Event *e);
|
||||||
5
tests/new/fail/union_two_fields.fch
Normal file
5
tests/new/fail/union_two_fields.fch
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// expect: Event is a C union: give one field by name
|
||||||
|
import "../cunion.h"
|
||||||
|
fn main() {
|
||||||
|
e := Event(1, 2)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue