diff --git a/Makefile b/Makefile index 86fba4c..cc5d586 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SRC := bootstrap/main.nim OUT := buxc 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 diff --git a/bootstrap/hir_lower.nim b/bootstrap/hir_lower.nim index 6fc426a..5398939 100644 --- a/bootstrap/hir_lower.nim +++ b/bootstrap/hir_lower.nim @@ -548,9 +548,15 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type = return makeUnknown() else: return ctx.resolveExprType(expr.exprUnaryOperand) 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: 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] if expr.exprCallCallee.kind == ekField: let recvType = ctx.resolveExprType(expr.exprCallCallee.exprFieldObj) diff --git a/bootstrap/sema.nim b/bootstrap/sema.nim index 76d607e..23e9a60 100644 --- a/bootstrap/sema.nim +++ b/bootstrap/sema.nim @@ -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") return makeUnknown() - # Check for generic function call: Max(10, 20) + # Check for generic function call: Max(10, 20) or Iter_Map(…) if expr.exprCallCallee.kind == ekGenericCall: let sym = scope.lookup(expr.exprCallCallee.exprGenericCallee) if sym == nil: sema.emitError(expr.loc, &"undeclared identifier '{expr.exprCallCallee.exprGenericCallee}'") return makeUnknown() - if sym.typ != nil and sym.typ.kind == tkFunc: - let retType = sym.typ.inner[^1] - 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] = @[] - for i, tp in typeParams: - if i < expr.exprCallCallee.exprGenericTypeArgs.len: - let concrete = sema.resolveType(expr.exprCallCallee.exprGenericTypeArgs[i]) - sema.typeTable[tp.name] = concrete - added.add(tp.name) - let resolvedRet = sema.resolveType(sym2.decl.declFuncReturnType) - for tp in added: - sema.typeTable.del(tp) - return resolvedRet - return retType - return makeUnknown() + # Still type-check args (closures need capture analysis, etc.) + # Bind type params while checking so `func(T)->U` params resolve. + let sym2 = sema.globalScope.lookup(expr.exprCallCallee.exprGenericCallee) + 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: + if i < expr.exprCallCallee.exprGenericTypeArgs.len: + let concrete = sema.resolveType(expr.exprCallCallee.exprGenericTypeArgs[i]) + sema.typeTable[tp.name] = concrete + added.add(tp.name) + 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: + sema.typeTable.del(tp) + return resolvedRet # Check for method call: obj.method(args) if expr.exprCallCallee.kind == ekField: diff --git a/docs/QUALITY_PLAN.md b/docs/QUALITY_PLAN.md index 3d0c90e..19b6271 100644 --- a/docs/QUALITY_PLAN.md +++ b/docs/QUALITY_PLAN.md @@ -48,7 +48,7 @@ | A.2 | String: IsEmpty, ReplaceAll | Чести операции; само first-replace досега | ✅ (тази сесия) | | A.3 | Os_Exit + Test_AssertEqString / richer asserts | Тестове и CLI без raw `bux_exit` | ✅ (тази сесия) | | 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 | ✅ (тази сесия) | ### 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` / `Filter` / `Fold` / `Any` / `All` / `ForEach`** — fat `func` params + monomorphization +2. **Int aliases** keep working: `Iter_MapInt` → `Iter_Map`, … +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(…)` 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 е стабилна -2. Struct/tuple patterns (`Point { x, y }`, `(a, b)`) + nested bindings -3. Match arm multi-stmt bodies (beyond single expr) -4. LSP: wire hover types from real sema (replace lightweight index where possible) - +1. Struct/tuple patterns (`Point { x, y }`, `(a, b)`) + nested bindings +2. Match arm multi-stmt bodies (beyond single expr) +3. LSP: wire hover types from real sema (replace lightweight index where possible) +4. Generic type inference for `Iter_Map` without explicit `` diff --git a/docs/Stdlib.md b/docs/Stdlib.md index 0544c4b..c17c071 100644 --- a/docs/Stdlib.md +++ b/docs/Stdlib.md @@ -143,18 +143,19 @@ struct Iter { | `Iter_AllEq` | `func Iter_AllEq(it: *Iter, value: T) -> bool` | True if all remaining equal value | | `Iter_Collect` | `func Iter_Collect(it: *Iter) -> Array` | 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 | |----------|-----------|-------------| -| `Iter_MapInt` | `func Iter_MapInt(it: *Iter, f: func(int) -> int) -> Array` | Map each element | -| `Iter_FilterInt` | `func Iter_FilterInt(it: *Iter, pred: func(int) -> bool) -> Array` | Keep matching elements | -| `Iter_FoldInt` | `func Iter_FoldInt(it: *Iter, init: int, f: func(int, int) -> int) -> int` | Left fold | -| `Iter_ForEachInt` | `func Iter_ForEachInt(it: *Iter, f: func(int) -> int)` | Side-effect per element | -| `Iter_AnyInt` | `func Iter_AnyInt(it: *Iter, pred: func(int) -> bool) -> bool` | Any matches pred | -| `Iter_AllInt` | `func Iter_AllInt(it: *Iter, pred: func(int) -> bool) -> bool` | All match pred | +| `Iter_Map` | `func Iter_Map(it: *Iter, f: func(T) -> U) -> Array` | Map `T → U` | +| `Iter_Filter` | `func Iter_Filter(it: *Iter, pred: func(T) -> bool) -> Array` | Keep matching | +| `Iter_Fold` | `func Iter_Fold(it: *Iter, init: Acc, f: func(Acc, T) -> Acc) -> Acc` | Left fold | +| `Iter_ForEach` | `func Iter_ForEach(it: *Iter, f: func(T) -> int)` | Side-effect per element | +| `Iter_Any` | `func Iter_Any(it: *Iter, pred: func(T) -> bool) -> bool` | Any matches pred | +| `Iter_All` | `func Iter_All(it: *Iter, pred: func(T) -> bool) -> bool` | All match pred | +| `Iter_MapInt` … | wrappers → `Iter_Map` etc. | Back-compat | | `Iter_SumInt` | `func Iter_SumInt(it: *Iter) -> int` | Sum remaining ints | ### Example diff --git a/examples/iter_generic.bux b/examples/iter_generic.bux new file mode 100644 index 0000000..afce58a --- /dev/null +++ b/examples/iter_generic.bux @@ -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 = Array_New(8); + Array_Push(&nums, 1); + Array_Push(&nums, 2); + Array_Push(&nums, 3); + Array_Push(&nums, 4); + Array_Push(&nums, 5); + + // Generic Map int → int + let itA: Iter = Array_Iter(&nums); + var doubled: Array = Iter_Map(&itA, Double); + Test_AssertEqInt(Array_Get(&doubled, 0), 2); + Test_AssertEqInt(Array_Get(&doubled, 4), 10); + + // Generic Map int → String + let itB: Iter = Array_Iter(&nums); + var asStr: Array = Iter_Map(&itB, IntToString); + Test_AssertEqInt(Array_Len(&asStr) as int, 5); + Test_AssertEqString(Array_Get(&asStr, 0), "1"); + Test_AssertEqString(Array_Get(&asStr, 4), "5"); + + // Map String → int (lengths) + let itC: Iter = Array_Iter(&asStr); + var lens: Array = Iter_Map(&itC, StringLenAsInt); + Test_AssertEqInt(Array_Get(&lens, 0), 1); + Test_AssertEqInt(Array_Get(&lens, 4), 1); + + // Filter generic + let itD: Iter = Array_Iter(&nums); + var evens: Array = Iter_Filter(&itD, IsEven); + Test_AssertEqInt(Array_Len(&evens) as int, 2); + Test_AssertEqInt(Array_Get(&evens, 0), 2); + Test_AssertEqInt(Array_Get(&evens, 1), 4); + + // Fold String lengths + let itE: Iter = Array_Iter(&asStr); + let totalChars: int = Iter_Fold(&itE, 0, AddLens); + Test_AssertEqInt(totalChars, 5); // "1"+"2"+"3"+"4"+"5" + + // Any / All on String + let itF: Iter = Array_Iter(&asStr); + Test_AssertTrue(Iter_Any(&itF, IsNonEmpty)); + let itG: Iter = Array_Iter(&asStr); + Test_AssertTrue(Iter_All(&itG, IsNonEmpty)); + + // Closures with generic Map + let scale: int = 100; + let itH: Iter = Array_Iter(&nums); + var scaled: Array = Iter_Map(&itH, |x: int| -> int { + return x * scale; + }); + Test_AssertEqInt(Array_Get(&scaled, 0), 100); + Test_AssertEqInt(Array_Get(&scaled, 2), 300); + + // Int aliases still work + let itI: Iter = Array_Iter(&nums); + var d2: Array = Iter_MapInt(&itI, Double); + Test_AssertEqInt(Array_Get(&d2, 1), 4); + let itJ: Iter = Array_Iter(&nums); + Test_AssertEqInt(Iter_SumInt(&itJ), 15); + + PrintInt(totalChars); + PrintLine(""); + Test_Pass("iter_generic"); + + Array_Free(&nums); + Array_Free(&doubled); + Array_Free(&asStr); + Array_Free(&lens); + Array_Free(&evens); + Array_Free(&scaled); + Array_Free(&d2); + return 0; +} diff --git a/lib/Iter.bux b/lib/Iter.bux index 0ac6f46..6e2b3fd 100644 --- a/lib/Iter.bux +++ b/lib/Iter.bux @@ -108,48 +108,48 @@ func Iter_Collect(it: *Iter) -> Array { } // --------------------------------------------------------------------------- -// 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 */ -func Iter_MapInt(it: *Iter, f: func(int) -> int) -> Array { +/* Map each remaining element through f: T → U, collect into Array */ +func Iter_Map(it: *Iter, f: func(T) -> U) -> Array { let remaining: uint = it.len - it.pos; var cap: uint = remaining; if cap == 0 { cap = 1; } - var out: Array = Array_New(cap); + var out: Array = Array_New(cap); var i: uint = it.pos; while i < it.len { - let mapped: int = f(it.data[i]); - Array_Push(&out, mapped); + let mapped: U = f(it.data[i]); + Array_Push(&out, mapped); i = i + 1; } return out; } -/* Keep remaining ints for which pred returns true */ -func Iter_FilterInt(it: *Iter, pred: func(int) -> bool) -> Array { +/* Keep remaining elements for which pred returns true */ +func Iter_Filter(it: *Iter, pred: func(T) -> bool) -> Array { let remaining: uint = it.len - it.pos; var cap: uint = remaining; if cap == 0 { cap = 1; } - var out: Array = Array_New(cap); + var out: Array = Array_New(cap); var i: uint = it.pos; while i < it.len { - let v: int = it.data[i]; + let v: T = it.data[i]; if pred(v) { - Array_Push(&out, v); + Array_Push(&out, v); } i = i + 1; } return out; } -/* Left-fold remaining ints: f(f(...f(init, x0), x1), ...) */ -func Iter_FoldInt(it: *Iter, init: int, f: func(int, int) -> int) -> int { - var acc: int = init; +/* Left-fold: f(f(...f(init, x0), x1), ...) */ +func Iter_Fold(it: *Iter, init: Acc, f: func(Acc, T) -> Acc) -> Acc { + var acc: Acc = init; var i: uint = it.pos; while i < it.len { acc = f(acc, it.data[i]); @@ -158,8 +158,8 @@ func Iter_FoldInt(it: *Iter, init: int, f: func(int, int) -> int) -> int { return acc; } -/* Call f for each remaining int (side effects; f's return is ignored) */ -func Iter_ForEachInt(it: *Iter, f: func(int) -> int) { +/* Call f for each remaining element (return value of f is ignored) */ +func Iter_ForEach(it: *Iter, f: func(T) -> int) { var i: uint = it.pos; while i < it.len { let _ignored: int = f(it.data[i]); @@ -168,7 +168,7 @@ func Iter_ForEachInt(it: *Iter, f: func(int) -> int) { } /* True if any remaining element satisfies pred */ -func Iter_AnyInt(it: *Iter, pred: func(int) -> bool) -> bool { +func Iter_Any(it: *Iter, pred: func(T) -> bool) -> bool { var i: uint = it.pos; while i < it.len { if pred(it.data[i]) { @@ -180,7 +180,7 @@ func Iter_AnyInt(it: *Iter, pred: func(int) -> bool) -> bool { } /* True if all remaining elements satisfy pred (true if empty) */ -func Iter_AllInt(it: *Iter, pred: func(int) -> bool) -> bool { +func Iter_All(it: *Iter, pred: func(T) -> bool) -> bool { var i: uint = it.pos; while i < it.len { if !pred(it.data[i]) { @@ -202,4 +202,32 @@ func Iter_SumInt(it: *Iter) -> int { return total; } +// --------------------------------------------------------------------------- +// Int-specialized aliases (backward compatible with earlier examples) +// --------------------------------------------------------------------------- + +func Iter_MapInt(it: *Iter, f: func(int) -> int) -> Array { + return Iter_Map(it, f); +} + +func Iter_FilterInt(it: *Iter, pred: func(int) -> bool) -> Array { + return Iter_Filter(it, pred); +} + +func Iter_FoldInt(it: *Iter, init: int, f: func(int, int) -> int) -> int { + return Iter_Fold(it, init, f); +} + +func Iter_ForEachInt(it: *Iter, f: func(int) -> int) { + Iter_ForEach(it, f); +} + +func Iter_AnyInt(it: *Iter, pred: func(int) -> bool) -> bool { + return Iter_Any(it, pred); +} + +func Iter_AllInt(it: *Iter, pred: func(int) -> bool) -> bool { + return Iter_All(it, pred); +} + } diff --git a/src/c_backend.bux b/src/c_backend.bux index 466f137..562b0d9 100644 --- a/src/c_backend.bux +++ b/src/c_backend.bux @@ -774,32 +774,17 @@ func CBE_FatPartToC(part: String) -> String { // Emit typedefs for common BuxFn_* shapes (fat function pointers) func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) { StringBuilder_Append(&cbe.sb, "/* Fat function pointer types (code + env) */\n"); - // (int)->int - StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int {\n"); - StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0);\n"); - StringBuilder_Append(&cbe.sb, " void* env;\n"); - StringBuilder_Append(&cbe.sb, "} BuxFn_int_int;\n"); - // (int,int)->int - StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int_int {\n"); - StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0, int a1);\n"); - StringBuilder_Append(&cbe.sb, " void* env;\n"); - StringBuilder_Append(&cbe.sb, "} BuxFn_int_int_int;\n"); - // (int)->bool - 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 + // Always emit core shapes + CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int_int"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_bool_int"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_void_void"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_int_void"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_cstr_int"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_int_cstr"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_bool_cstr"); + CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int_cstr"); + // Scan module for any other BuxFn_* names (deduped via #ifndef in EmitOne) var i: int = 0; while i < mod.funcCount { 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) { if String_Eq(name, "") { 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); } @@ -859,6 +838,13 @@ func CBE_EmitOneFatTypedef(cbe: *CEmitter, fatName: String) { let retPart: String = String_SplitPart(rest, "_", 0); 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, fatName); 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, fatName); - StringBuilder_Append(&cbe.sb, ";\n"); + StringBuilder_Append(&cbe.sb, ";\n#endif\n"); } func CBE_EmitMakerDecl(cbe: *CEmitter, f: *HirFunc) { diff --git a/src/hir_lower.bux b/src/hir_lower.bux index a10e24d..b0b0e35 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -152,6 +152,35 @@ func Lcx_SubstituteType(ctx: *LowerCtx, te: *TypeExpr) -> *TypeExpr { 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; }