feat(stdlib): generic Iter_Map/Filter/Fold with func monomorphization

Add Iter_Map<T,U>, Filter, Fold, Any, All, ForEach over fat function
pointers; keep Iter_MapInt and friends as thin aliases.

Compiler fixes required for non-int returns and capturing closures:
- bootstrap: resolve fat-func call return type under mono typeSubst
- bootstrap: type-check args of Foo<T>(...) so closures capture correctly
- selfhost: substitute type params inside tekFunc (BuxFn_U_T → concrete)
- selfhost: emit cstr fat typedefs with #ifndef redefinition guards

Example: examples/iter_generic.bux (int↔String map, fold, closures).
Selfhost-loop remains binary-identical.
This commit is contained in:
2026-07-18 01:07:41 +03:00
parent 26631252c0
commit eac78f28c1
9 changed files with 267 additions and 87 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ SRC := bootstrap/main.nim
OUT := buxc OUT := buxc
BUILD_DIR := build BUILD_DIR := build
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic
.PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors selfhost-loop lsp .PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors selfhost-loop lsp
+7 -1
View File
@@ -548,9 +548,15 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
return makeUnknown() return makeUnknown()
else: return ctx.resolveExprType(expr.exprUnaryOperand) else: return ctx.resolveExprType(expr.exprUnaryOperand)
of ekCall: of ekCall:
# Local / param fat-func values (after monomorphization typeSubst) — e.g. f: func(T)->U
# Must run before the global-only lookup so generic HOFs get the correct return type.
if expr.exprCallCallee.kind in {ekIdent, ekPath}:
let calType = ctx.resolveExprType(expr.exprCallCallee)
if calType != nil and calType.kind == tkFunc and calType.inner.len > 0:
return calType.inner[^1]
if expr.exprCallCallee.kind == ekIdent: if expr.exprCallCallee.kind == ekIdent:
let sym = ctx.globalScope.lookup(expr.exprCallCallee.exprIdent) let sym = ctx.globalScope.lookup(expr.exprCallCallee.exprIdent)
if sym != nil and sym.typ != nil and sym.typ.kind == tkFunc: if sym != nil and sym.typ != nil and sym.typ.kind == tkFunc and sym.typ.inner.len > 0:
return sym.typ.inner[^1] return sym.typ.inner[^1]
if expr.exprCallCallee.kind == ekField: if expr.exprCallCallee.kind == ekField:
let recvType = ctx.resolveExprType(expr.exprCallCallee.exprFieldObj) let recvType = ctx.resolveExprType(expr.exprCallCallee.exprFieldObj)
+13 -10
View File
@@ -1063,31 +1063,34 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
sema.emitError(expr.loc, "internal error: nil callee in call expression") sema.emitError(expr.loc, "internal error: nil callee in call expression")
return makeUnknown() return makeUnknown()
# Check for generic function call: Max<int>(10, 20) # Check for generic function call: Max<int>(10, 20) or Iter_Map<int, String>(…)
if expr.exprCallCallee.kind == ekGenericCall: if expr.exprCallCallee.kind == ekGenericCall:
let sym = scope.lookup(expr.exprCallCallee.exprGenericCallee) let sym = scope.lookup(expr.exprCallCallee.exprGenericCallee)
if sym == nil: if sym == nil:
sema.emitError(expr.loc, &"undeclared identifier '{expr.exprCallCallee.exprGenericCallee}'") sema.emitError(expr.loc, &"undeclared identifier '{expr.exprCallCallee.exprGenericCallee}'")
return makeUnknown() return makeUnknown()
if sym.typ != nil and sym.typ.kind == tkFunc: # Still type-check args (closures need capture analysis, etc.)
let retType = sym.typ.inner[^1] # Bind type params while checking so `func(T)->U` params resolve.
let sym2 = sema.globalScope.lookup(expr.exprCallCallee.exprGenericCallee) let sym2 = sema.globalScope.lookup(expr.exprCallCallee.exprGenericCallee)
if sym2 != nil and sym2.decl != nil and sym2.decl.kind == dkFunc and
sym2.decl.declFuncTypeParams.len > 0 and
sym2.decl.declFuncReturnType != nil:
let typeParams = sym2.decl.declFuncTypeParams
var added: seq[string] = @[] var added: seq[string] = @[]
if sym2 != nil and sym2.decl != nil and sym2.decl.kind == dkFunc and
sym2.decl.declFuncTypeParams.len > 0:
let typeParams = sym2.decl.declFuncTypeParams
for i, tp in typeParams: for i, tp in typeParams:
if i < expr.exprCallCallee.exprGenericTypeArgs.len: if i < expr.exprCallCallee.exprGenericTypeArgs.len:
let concrete = sema.resolveType(expr.exprCallCallee.exprGenericTypeArgs[i]) let concrete = sema.resolveType(expr.exprCallCallee.exprGenericTypeArgs[i])
sema.typeTable[tp.name] = concrete sema.typeTable[tp.name] = concrete
added.add(tp.name) added.add(tp.name)
let resolvedRet = sema.resolveType(sym2.decl.declFuncReturnType) discard sema.checkExprList(expr.exprCallArgs, scope)
var resolvedRet = makeUnknown()
if sym2 != nil and sym2.decl != nil and sym2.decl.kind == dkFunc and
sym2.decl.declFuncReturnType != nil:
resolvedRet = sema.resolveType(sym2.decl.declFuncReturnType)
elif sym.typ != nil and sym.typ.kind == tkFunc and sym.typ.inner.len > 0:
resolvedRet = sym.typ.inner[^1]
for tp in added: for tp in added:
sema.typeTable.del(tp) sema.typeTable.del(tp)
return resolvedRet return resolvedRet
return retType
return makeUnknown()
# Check for method call: obj.method(args) # Check for method call: obj.method(args)
if expr.exprCallCallee.kind == ekField: if expr.exprCallCallee.kind == ekField:
+20 -6
View File
@@ -48,7 +48,7 @@
| A.2 | String: IsEmpty, ReplaceAll | Чести операции; само first-replace досега | ✅ (тази сесия) | | A.2 | String: IsEmpty, ReplaceAll | Чести операции; само first-replace досега | ✅ (тази сесия) |
| A.3 | Os_Exit + Test_AssertEqString / richer asserts | Тестове и CLI без raw `bux_exit` | ✅ (тази сесия) | | A.3 | Os_Exit + Test_AssertEqString / richer asserts | Тестове и CLI без raw `bux_exit` | ✅ (тази сесия) |
| A.4 | Map_Remove / Set polish | Completeness на колекциите | ✅ (тази сесия) | | A.4 | Map_Remove / Set polish | Completeness на колекциите | ✅ (тази сесия) |
| A.5 | Iter: map/filter/fold върху closures | Higher-order без boilerplate | ✅ Iter_Map/Filter/FoldInt | | A.5 | Iter: map/filter/fold върху closures | Higher-order без boilerplate | ✅ generic `Iter_Map`/`Filter`/`Fold` + Int aliases |
| A.6 | Result helpers: Expect, UnwrapErr, Or | По-малко match boilerplate | ✅ (тази сесия) | | A.6 | Result helpers: Expect, UnwrapErr, Or | По-малко match boilerplate | ✅ (тази сесия) |
### B — Compiler Correctness (P0) ### B — Compiler Correctness (P0)
@@ -246,10 +246,24 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
--- ---
## Сесия 14 (generic Iter map/filter/fold)
1. **`Iter_Map<T,U>` / `Filter<T>` / `Fold<T,Acc>` / `Any` / `All` / `ForEach`** — fat `func` params + monomorphization
2. **Int aliases** keep working: `Iter_MapInt``Iter_Map<int,int>`, …
3. **Bootstrap fixes:**
- call return type for local fat-func (`f: func(T)->U`) after mono (was always `int` → String map truncated pointers)
- generic call `Foo<T>(…)` now type-checks args (closures get capture analysis)
4. **Selfhost fixes:**
- `Lcx_SubstituteType` recurses into `tekFunc` (was leaving `BuxFn_U_T`)
- fat typedef emit covers cstr shapes + `#ifndef` guards
5. Example: `examples/iter_generic.bux` (int↔String map, filter, fold, closures)
6. Verified: bootstrap + **buxc2** + selfhost-loop IDENTICAL ✓
---
## Следващи стъпки ## Следващи стъпки
1. **Generic Iter map** (не само int), ако monomorphization с `func` params е стабилна 1. Struct/tuple patterns (`Point { x, y }`, `(a, b)`) + nested bindings
2. Struct/tuple patterns (`Point { x, y }`, `(a, b)`) + nested bindings 2. Match arm multi-stmt bodies (beyond single expr)
3. Match arm multi-stmt bodies (beyond single expr) 3. LSP: wire hover types from real sema (replace lightweight index where possible)
4. LSP: wire hover types from real sema (replace lightweight index where possible) 4. Generic type inference for `Iter_Map` without explicit `<T,U>`
+9 -8
View File
@@ -143,18 +143,19 @@ struct Iter<T> {
| `Iter_AllEq<T>` | `func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool` | True if all remaining equal value | | `Iter_AllEq<T>` | `func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool` | True if all remaining equal value |
| `Iter_Collect<T>` | `func Iter_Collect<T>(it: *Iter<T>) -> Array<T>` | Collect remaining into a new Array | | `Iter_Collect<T>` | `func Iter_Collect<T>(it: *Iter<T>) -> Array<T>` | Collect remaining into a new Array |
### Higher-order (int-specialized) ### Higher-order (generic + int aliases)
Take fat function pointers / closures (`func(int) -> int`, `func(int) -> bool`, …). Take fat function pointers / closures. Prefer the generic forms; `*Int` aliases remain for compatibility.
| Function | Signature | Description | | Function | Signature | Description |
|----------|-----------|-------------| |----------|-----------|-------------|
| `Iter_MapInt` | `func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int>` | Map each element | | `Iter_Map<T,U>` | `func Iter_Map<T,U>(it: *Iter<T>, f: func(T) -> U) -> Array<U>` | Map `T → U` |
| `Iter_FilterInt` | `func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int>` | Keep matching elements | | `Iter_Filter<T>` | `func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T>` | Keep matching |
| `Iter_FoldInt` | `func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int` | Left fold | | `Iter_Fold<T,Acc>` | `func Iter_Fold<T,Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc` | Left fold |
| `Iter_ForEachInt` | `func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int)` | Side-effect per element | | `Iter_ForEach<T>` | `func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int)` | Side-effect per element |
| `Iter_AnyInt` | `func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool` | Any matches pred | | `Iter_Any<T>` | `func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool` | Any matches pred |
| `Iter_AllInt` | `func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool` | All match pred | | `Iter_All<T>` | `func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool` | All match pred |
| `Iter_MapInt` … | wrappers → `Iter_Map<int,int>` etc. | Back-compat |
| `Iter_SumInt` | `func Iter_SumInt(it: *Iter<int>) -> int` | Sum remaining ints | | `Iter_SumInt` | `func Iter_SumInt(it: *Iter<int>) -> int` | Sum remaining ints |
### Example ### Example
+113
View File
@@ -0,0 +1,113 @@
// Generic Iter_Map / Filter / Fold / Any / All (not just int)
import Std::Io::{PrintLine, PrintInt};
import Std::Array::{
Array, Array_New, Array_Push, Array_Get, Array_Len, Array_Free
};
import Std::String::{String_FromInt, String_Len, String_Eq};
import Std::Iter::{
Array_Iter, Iter,
Iter_Map, Iter_Filter, Iter_Fold, Iter_Any, Iter_All,
Iter_MapInt, Iter_SumInt
};
import Std::Test::{
Test_AssertEqInt, Test_AssertTrue, Test_AssertFalse, Test_AssertEqString, Test_Pass
};
func Double(x: int) -> int {
return x * 2;
}
func IsEven(x: int) -> bool {
return (x % 2) == 0;
}
func IntToString(x: int) -> String {
return String_FromInt(x);
}
func StringLenAsInt(s: String) -> int {
return String_Len(s) as int;
}
func AddLens(acc: int, s: String) -> int {
return acc + (String_Len(s) as int);
}
func IsNonEmpty(s: String) -> bool {
return String_Len(s) > 0;
}
func Main() -> int {
var nums: Array<int> = Array_New<int>(8);
Array_Push<int>(&nums, 1);
Array_Push<int>(&nums, 2);
Array_Push<int>(&nums, 3);
Array_Push<int>(&nums, 4);
Array_Push<int>(&nums, 5);
// Generic Map int → int
let itA: Iter<int> = Array_Iter<int>(&nums);
var doubled: Array<int> = Iter_Map<int, int>(&itA, Double);
Test_AssertEqInt(Array_Get<int>(&doubled, 0), 2);
Test_AssertEqInt(Array_Get<int>(&doubled, 4), 10);
// Generic Map int → String
let itB: Iter<int> = Array_Iter<int>(&nums);
var asStr: Array<String> = Iter_Map<int, String>(&itB, IntToString);
Test_AssertEqInt(Array_Len<String>(&asStr) as int, 5);
Test_AssertEqString(Array_Get<String>(&asStr, 0), "1");
Test_AssertEqString(Array_Get<String>(&asStr, 4), "5");
// Map String → int (lengths)
let itC: Iter<String> = Array_Iter<String>(&asStr);
var lens: Array<int> = Iter_Map<String, int>(&itC, StringLenAsInt);
Test_AssertEqInt(Array_Get<int>(&lens, 0), 1);
Test_AssertEqInt(Array_Get<int>(&lens, 4), 1);
// Filter generic
let itD: Iter<int> = Array_Iter<int>(&nums);
var evens: Array<int> = Iter_Filter<int>(&itD, IsEven);
Test_AssertEqInt(Array_Len<int>(&evens) as int, 2);
Test_AssertEqInt(Array_Get<int>(&evens, 0), 2);
Test_AssertEqInt(Array_Get<int>(&evens, 1), 4);
// Fold String lengths
let itE: Iter<String> = Array_Iter<String>(&asStr);
let totalChars: int = Iter_Fold<String, int>(&itE, 0, AddLens);
Test_AssertEqInt(totalChars, 5); // "1"+"2"+"3"+"4"+"5"
// Any / All on String
let itF: Iter<String> = Array_Iter<String>(&asStr);
Test_AssertTrue(Iter_Any<String>(&itF, IsNonEmpty));
let itG: Iter<String> = Array_Iter<String>(&asStr);
Test_AssertTrue(Iter_All<String>(&itG, IsNonEmpty));
// Closures with generic Map
let scale: int = 100;
let itH: Iter<int> = Array_Iter<int>(&nums);
var scaled: Array<int> = Iter_Map<int, int>(&itH, |x: int| -> int {
return x * scale;
});
Test_AssertEqInt(Array_Get<int>(&scaled, 0), 100);
Test_AssertEqInt(Array_Get<int>(&scaled, 2), 300);
// Int aliases still work
let itI: Iter<int> = Array_Iter<int>(&nums);
var d2: Array<int> = Iter_MapInt(&itI, Double);
Test_AssertEqInt(Array_Get<int>(&d2, 1), 4);
let itJ: Iter<int> = Array_Iter<int>(&nums);
Test_AssertEqInt(Iter_SumInt(&itJ), 15);
PrintInt(totalChars);
PrintLine("");
Test_Pass("iter_generic");
Array_Free<int>(&nums);
Array_Free<int>(&doubled);
Array_Free<String>(&asStr);
Array_Free<int>(&lens);
Array_Free<int>(&evens);
Array_Free<int>(&scaled);
Array_Free<int>(&d2);
return 0;
}
+46 -18
View File
@@ -108,48 +108,48 @@ func Iter_Collect<T>(it: *Iter<T>) -> Array<T> {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Higher-order helpers (int-specialized; take fat func pointers / closures) // Higher-order helpers (generic; fat func pointers / closures)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Map each remaining int through f, collect into a new Array */ /* Map each remaining element through f: T → U, collect into Array<U> */
func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int> { func Iter_Map<T, U>(it: *Iter<T>, f: func(T) -> U) -> Array<U> {
let remaining: uint = it.len - it.pos; let remaining: uint = it.len - it.pos;
var cap: uint = remaining; var cap: uint = remaining;
if cap == 0 { if cap == 0 {
cap = 1; cap = 1;
} }
var out: Array<int> = Array_New<int>(cap); var out: Array<U> = Array_New<U>(cap);
var i: uint = it.pos; var i: uint = it.pos;
while i < it.len { while i < it.len {
let mapped: int = f(it.data[i]); let mapped: U = f(it.data[i]);
Array_Push<int>(&out, mapped); Array_Push<U>(&out, mapped);
i = i + 1; i = i + 1;
} }
return out; return out;
} }
/* Keep remaining ints for which pred returns true */ /* Keep remaining elements for which pred returns true */
func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int> { func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T> {
let remaining: uint = it.len - it.pos; let remaining: uint = it.len - it.pos;
var cap: uint = remaining; var cap: uint = remaining;
if cap == 0 { if cap == 0 {
cap = 1; cap = 1;
} }
var out: Array<int> = Array_New<int>(cap); var out: Array<T> = Array_New<T>(cap);
var i: uint = it.pos; var i: uint = it.pos;
while i < it.len { while i < it.len {
let v: int = it.data[i]; let v: T = it.data[i];
if pred(v) { if pred(v) {
Array_Push<int>(&out, v); Array_Push<T>(&out, v);
} }
i = i + 1; i = i + 1;
} }
return out; return out;
} }
/* Left-fold remaining ints: f(f(...f(init, x0), x1), ...) */ /* Left-fold: f(f(...f(init, x0), x1), ...) */
func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int { func Iter_Fold<T, Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc {
var acc: int = init; var acc: Acc = init;
var i: uint = it.pos; var i: uint = it.pos;
while i < it.len { while i < it.len {
acc = f(acc, it.data[i]); acc = f(acc, it.data[i]);
@@ -158,8 +158,8 @@ func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
return acc; return acc;
} }
/* Call f for each remaining int (side effects; f's return is ignored) */ /* Call f for each remaining element (return value of f is ignored) */
func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) { func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int) {
var i: uint = it.pos; var i: uint = it.pos;
while i < it.len { while i < it.len {
let _ignored: int = f(it.data[i]); let _ignored: int = f(it.data[i]);
@@ -168,7 +168,7 @@ func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
} }
/* True if any remaining element satisfies pred */ /* True if any remaining element satisfies pred */
func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool { func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
var i: uint = it.pos; var i: uint = it.pos;
while i < it.len { while i < it.len {
if pred(it.data[i]) { if pred(it.data[i]) {
@@ -180,7 +180,7 @@ func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
} }
/* True if all remaining elements satisfy pred (true if empty) */ /* True if all remaining elements satisfy pred (true if empty) */
func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool { func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
var i: uint = it.pos; var i: uint = it.pos;
while i < it.len { while i < it.len {
if !pred(it.data[i]) { if !pred(it.data[i]) {
@@ -202,4 +202,32 @@ func Iter_SumInt(it: *Iter<int>) -> int {
return total; return total;
} }
// ---------------------------------------------------------------------------
// Int-specialized aliases (backward compatible with earlier examples)
// ---------------------------------------------------------------------------
func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int> {
return Iter_Map<int, int>(it, f);
}
func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int> {
return Iter_Filter<int>(it, pred);
}
func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
return Iter_Fold<int, int>(it, init, f);
}
func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
Iter_ForEach<int>(it, f);
}
func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
return Iter_Any<int>(it, pred);
}
func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
return Iter_All<int>(it, pred);
}
} }
+19 -33
View File
@@ -774,32 +774,17 @@ func CBE_FatPartToC(part: String) -> String {
// Emit typedefs for common BuxFn_* shapes (fat function pointers) // Emit typedefs for common BuxFn_* shapes (fat function pointers)
func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) { func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) {
StringBuilder_Append(&cbe.sb, "/* Fat function pointer types (code + env) */\n"); StringBuilder_Append(&cbe.sb, "/* Fat function pointer types (code + env) */\n");
// (int)->int // Always emit core shapes
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int {\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int");
StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0);\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int_int");
StringBuilder_Append(&cbe.sb, " void* env;\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_bool_int");
StringBuilder_Append(&cbe.sb, "} BuxFn_int_int;\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_void_void");
// (int,int)->int CBE_EmitOneFatTypedef(cbe, "BuxFn_int_void");
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int_int {\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_cstr_int");
StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0, int a1);\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_int_cstr");
StringBuilder_Append(&cbe.sb, " void* env;\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_bool_cstr");
StringBuilder_Append(&cbe.sb, "} BuxFn_int_int_int;\n"); CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int_cstr");
// (int)->bool // Scan module for any other BuxFn_* names (deduped via #ifndef in EmitOne)
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_bool_int {\n");
StringBuilder_Append(&cbe.sb, " bool (*code)(void* env, int a0);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_bool_int;\n");
// ()->void
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_void_void {\n");
StringBuilder_Append(&cbe.sb, " void (*code)(void* env);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_void_void;\n");
// ()->int
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_void {\n");
StringBuilder_Append(&cbe.sb, " int (*code)(void* env);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_int_void;\n");
// Scan module for any other BuxFn_* names
var i: int = 0; var i: int = 0;
while i < mod.funcCount { while i < mod.funcCount {
CBE_MaybeEmitExtraFat(cbe, mod.funcs[i].retTypeName); CBE_MaybeEmitExtraFat(cbe, mod.funcs[i].retTypeName);
@@ -826,12 +811,6 @@ func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) {
func CBE_MaybeEmitExtraFat(cbe: *CEmitter, name: String) { func CBE_MaybeEmitExtraFat(cbe: *CEmitter, name: String) {
if String_Eq(name, "") { return; } if String_Eq(name, "") { return; }
if !String_StartsWith(name, "BuxFn_") { return; } if !String_StartsWith(name, "BuxFn_") { return; }
// Skip ones we already emit as built-ins
if String_Eq(name, "BuxFn_int_int") { return; }
if String_Eq(name, "BuxFn_int_int_int") { return; }
if String_Eq(name, "BuxFn_bool_int") { return; }
if String_Eq(name, "BuxFn_void_void") { return; }
if String_Eq(name, "BuxFn_int_void") { return; }
CBE_EmitOneFatTypedef(cbe, name); CBE_EmitOneFatTypedef(cbe, name);
} }
@@ -859,6 +838,13 @@ func CBE_EmitOneFatTypedef(cbe: *CEmitter, fatName: String) {
let retPart: String = String_SplitPart(rest, "_", 0); let retPart: String = String_SplitPart(rest, "_", 0);
let retC: String = CBE_FatPartToC(retPart); let retC: String = CBE_FatPartToC(retPart);
// Guard against redefinition if the same name is emitted twice
StringBuilder_Append(&cbe.sb, "#ifndef ");
StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, "_DEFINED\n#define ");
StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, "_DEFINED\n");
StringBuilder_Append(&cbe.sb, "typedef struct "); StringBuilder_Append(&cbe.sb, "typedef struct ");
StringBuilder_Append(&cbe.sb, fatName); StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, " {\n "); StringBuilder_Append(&cbe.sb, " {\n ");
@@ -876,7 +862,7 @@ func CBE_EmitOneFatTypedef(cbe: *CEmitter, fatName: String) {
} }
StringBuilder_Append(&cbe.sb, ");\n void* env;\n} "); StringBuilder_Append(&cbe.sb, ");\n void* env;\n} ");
StringBuilder_Append(&cbe.sb, fatName); StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, ";\n"); StringBuilder_Append(&cbe.sb, ";\n#endif\n");
} }
func CBE_EmitMakerDecl(cbe: *CEmitter, f: *HirFunc) { func CBE_EmitMakerDecl(cbe: *CEmitter, f: *HirFunc) {
+29
View File
@@ -152,6 +152,35 @@ func Lcx_SubstituteType(ctx: *LowerCtx, te: *TypeExpr) -> *TypeExpr {
return r; return r;
} }
// Fat function type: func(T)->U — substitute params and return
if te.kind == tekFunc {
let r: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
r.kind = tekFunc;
r.line = te.line;
r.column = te.column;
r.funcParamCount = te.funcParamCount;
r.funcRet = Lcx_SubstituteType(ctx, te.funcRet);
var head: *TypeExprList = null as *TypeExprList;
var tail: *TypeExprList = null as *TypeExprList;
var cur: *TypeExprList = te.funcParams;
while cur != null as *TypeExprList {
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
node.te = Lcx_SubstituteType(ctx, cur.te);
node.next = null as *TypeExprList;
if head == null as *TypeExprList {
head = node;
tail = node;
} else {
tail.next = node;
tail = node;
}
cur = cur.next;
}
r.funcParams = head;
r.typeName = Lcx_BuildFuncTypeName(r);
return r;
}
return te; return te;
} }