From f8229049add6a38f6a69f23ae5ca72671a9d8be9 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 09:44:12 +0300 Subject: [PATCH 01/22] api: initialize closures --- api/fn.hpp | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) diff --git a/api/fn.hpp b/api/fn.hpp index 5bdb0c641..42442d975 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -13,6 +13,7 @@ #include "types.hpp" #include "error.hpp" #include "panic.hpp" +#include "platform.hpp" #define __JULE_CO_SPAWN(ROUTINE) \ (std::thread{ROUTINE}) @@ -139,3 +140,248 @@ namespace jule } // namespace jule #endif // ifndef __JULE_FN_HPP + +namespace jule +{ + + // std::function wrapper of JuleC. + template + struct Fn2 + { + public: + struct CtxHandler + { + void (*dealloc)(jule::Ptr &alloc); + }; + + Ret (*f)(Args...); + jule::Ptr ctx; // Closure ctx. + CtxHandler *ctxHandler; + + Fn2(void) = default; + Fn2(const Fn2 &) = default; + Fn2(std::nullptr_t) : Fn2() {} + + Fn2(Ret (*f)(Args...)) noexcept + { + this->f = f; + } + + ~Fn2(void) noexcept + { + this->f = nullptr; + if (this->ctxHandler) + { + this->ctxHandler->dealloc(this->ctx); + this->ctxHandler = nullptr; + } + } + + template + Ret call( +#ifndef __JULE_ENABLE__PRODUCTION + const char *file, +#endif + Args... args) + { +#ifndef __JULE_DISABLE__SAFETY + if (this->f == nullptr) +#ifndef __JULE_ENABLE__PRODUCTION + jule::panic((std::string(__JULE_ERROR__INVALID_MEMORY) + "\nfile: ") + file); +#else + jule::panic(__JULE_ERROR__INVALID_MEMORY); +#endif // PRODUCTION +#endif // SAFETY + return this->f(args...); + } + + inline auto operator()(Args... args) + { +#ifndef __JULE_ENABLE__PRODUCTION + return this->call("/api/fn.hpp", args...); +#else + return this->call(args...); +#endif + } + + inline Fn2 &operator=(std::nullptr_t) noexcept + { + this->f = nullptr; + return *this; + } + + constexpr jule::Bool operator==(std::nullptr_t) const noexcept + { + return this->buffer == nullptr; + } + + constexpr jule::Bool operator!=(std::nullptr_t) const noexcept + { + return !this->operator==(nullptr); + } + + friend std::ostream &operator<<(std::ostream &stream, + const Fn2 &f) noexcept + { + if (f == nullptr) + return (stream << ""); + return (stream << (void *)f.f); + } + }; +} // namespace jule + +#ifdef OS_WINDOWS +#include +#else +#include +#include +#endif + +#ifdef OS_WINDOWS +static SRWLOCK __closure_mtx; +#define __JULE_CLOSURE_MTX_INIT() InitializeSRWLock(&jule::__closure_mtx) +#define __JULE_CLOSURE_MTX_LOCK() AcquireSRWLockExclusive(&jule::__closure_mtx) +#define __JULE_CLOSURE_MTX_UNLOCK() ReleaseSRWLockExclusive(&jule::__closure_mtx) +#else +#define __JULE_CLOSURE_MTX_INIT() pthread_mutex_init(&jule::__closure_mtx, 0) +#define __JULE_CLOSURE_MTX_LOCK() pthread_mutex_lock(&jule::__closure_mtx) +#define __JULE_CLOSURE_MTX_UNLOCK() pthread_mutex_unlock(&jule::__closure_mtx) +#endif + +#define __JULE_ASSUMED_PAGE_SIZE 0x4000 +#define __JULE_CLOSURE_SIZE (((sizeof(void *) << 1 > sizeof(jule::__closure_thunk) ? sizeof(void *) << 1 : sizeof(jule::__closure_thunk)) + sizeof(void *) - 1) & ~(sizeof(void *) - 1)) + +#define __JULE_CLOSURE_PAGE_PTR(closure) \ + ((void **)(closure - __JULE_ASSUMED_PAGE_SIZE)) + +namespace jule +{ + static int __page_size = __JULE_ASSUMED_PAGE_SIZE; + +#if defined(ARCH_AMD64) + static const char __closure_thunk[] = { + 0xF3, 0x44, 0x0F, 0x7E, 0x3D, 0xF7, 0xBF, 0xFF, 0xFF, // movq xmm15, QWORD PTR [rip - userdata] + 0xFF, 0x25, 0xF9, 0xBF, 0xFF, 0xFF // jmp QWORD PTR [rip - fn] + }; + static char __closure_get_ctx_bytes[] = { + 0xE0, 0x03, 0x11, 0xAA, // mov x0, x17 + 0xC0, 0x03, 0x5F, 0xD6 // ret + }; +#elif defined(ARCH_I386) + static char __closure_thunk[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, // call here + // here: + 0x59, // pop ecx + 0x66, 0x0F, 0x6E, 0xF9, // movd xmm7, ecx + 0xff, 0xA1, 0xff, 0xbf, 0xff, 0xff, // jmp DWORD PTR [ecx - 0x4001] # + }; + static char __closure_get_ctx_bytes[] = { + 0x66, 0x0F, 0x7E, 0xF8, // movd eax, xmm7 + 0x8B, 0x80, 0xFB, 0xBF, 0xFF, 0xFF, // mov eax, DWORD PTR [eax - 0x4005] + 0xc3 // ret + }; +#elif defined(ARCH_ARM64) + static char __closure_thunk[] = { + 0x11, 0x00, 0xFE, 0x58, // ldr x17, userdata + 0x30, 0x00, 0xFE, 0x58, // ldr x16, fn + 0x00, 0x02, 0x1F, 0xD6 // br x16 + }; + static char __closure_get_ctx_bytes[] = { + 0xE0, 0x03, 0x11, 0xAA, // mov x0, x17 + 0xC0, 0x03, 0x5F, 0xD6 // ret + }; +#endif + +#ifdef _WIN32 + static SRWLOCK __closure_mtx; +#else + static pthread_mutex_t __closure_mtx; +#endif + + static jule::U8 *__closure_ptr = 0; + static int __closure_cap = 0; + + static void *(*__closure_get_ctx)(void) = nullptr; + + static void __closure_alloc(void) + { +#ifdef _WIN32 + jule::U8 *p = (jule::U8 *)VirtualAlloc(NULL, jule::__page_size << 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (p == NULL) + jule::panic(__JULE_ERROR__MEMORY_ALLOCATION_FAILED + "\nruntime: heap allocation failed for closure"); +#else + jule::U8 *p = (jule::U8 *)mmap(0, jule::__page_size << 1, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + jule::panic(__JULE_ERROR__MEMORY_ALLOCATION_FAILED + "\nruntime: heap allocation failed for closure"); +#endif + jule::U8 *x = p + jule::__page_size; + int rem = jule::__page_size / __JULE_CLOSURE_SIZE; + jule::__closure_ptr = x; + jule::__closure_cap = rem; + while (rem > 0) + { + (void)memcpy(x, jule::__closure_thunk, sizeof(jule::__closure_thunk)); + rem--; + x += __JULE_CLOSURE_SIZE; + } +#ifdef _WIN32 + DWORD temp; + VirtualProtect(jule::__closure_ptr, jule::__page_size, PAGE_EXECUTE_READ, &temp); +#else + (void)mprotect(jule::__closure_ptr, jule::__page_size, PROT_READ | PROT_EXEC); +#endif + } + + template + jule::Fn2 __new_closure(void *fn, jule::Ptr ctx, typename jule::Fn2::CtxHandler *ctxHandler) + { + __JULE_CLOSURE_MTX_LOCK(); + if (!jule::__closure_cap) + jule::__closure_alloc(); + jule::__closure_cap--; + jule::U8 *closure = jule::__closure_ptr; + jule::__closure_ptr += __JULE_CLOSURE_SIZE; + void **ptr = __JULE_CLOSURE_PAGE_PTR(closure); + ptr[0] = ctx; + ptr[1] = fn; + __JULE_CLOSURE_MTX_UNLOCK(); + Ret (*static_closure)(Args...) = (Ret(*)(Args...))closure; + jule::Fn2 fn2(static_closure); + fn2.ctx = ctx; + fn2.ctxHandler = ctxHandler; + return fn2; + } + +#ifdef OS_WINDOWS + void __closure_init() + { + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + const uint32_t page_size = si.dwPageSize * (((jule::__page_size - 1) / si.dwPageSize) + 1); + jule::__page_size = page_size; + jule::__closure_alloc(); + DWORD temp; + VirtualProtect(jule::__closure_ptr, page_size, PAGE_READWRITE, &temp); + (void)memcpy(jule::__closure_ptr, jule::__closure_get_ctx_bytes, sizeof(jule::__closure_get_ctx_bytes)); + VirtualProtect(jule::__closure_ptr, page_size, PAGE_EXECUTE_READ, &temp); + jule::__closure_get_ctx = (void *(*)(void))jule::__closure_ptr; + jule::__closure_ptr += __JULE_CLOSURE_SIZE; + jule::__closure_cap--; + } +#else + void __closure_init() + { + const uint32_t page_size = sysconf(_SC_PAGESIZE) * (((__JULE_ASSUMED_PAGE_SIZE - 1) / page_size) + 1); + jule::__page_size = page_size; + jule::__closure_alloc(); + (void)mprotect(jule::__closure_ptr, page_size, PROT_READ | PROT_WRITE); + (void)memcpy(jule::__closure_ptr, jule::__closure_get_ctx_bytes, sizeof(jule::__closure_get_ctx_bytes)); + (void)mprotect(jule::__closure_ptr, page_size, PROT_READ | PROT_EXEC); + jule::__closure_get_ctx = (void *(*)(void))jule::__closure_ptr; + jule::__closure_ptr += __JULE_CLOSURE_SIZE; + jule::__closure_cap--; + } +#endif +} \ No newline at end of file From d9c83ac08d7ecee01961a53af48a06a97d050d89 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 10:43:09 +0300 Subject: [PATCH 02/22] compiler: initialize code generation for closures --- api/fn.hpp | 12 ++----- src/julec/obj/cxx/expr.jule | 22 ++++++++---- src/julec/obj/cxx/object.jule | 64 +++++++++++++++++++++++++++++++---- src/julec/obj/cxx/type.jule | 26 +++++++------- 4 files changed, 88 insertions(+), 36 deletions(-) diff --git a/api/fn.hpp b/api/fn.hpp index 42442d975..645a6ab3d 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -143,20 +143,14 @@ namespace jule namespace jule { - // std::function wrapper of JuleC. template struct Fn2 { public: - struct CtxHandler - { - void (*dealloc)(jule::Ptr &alloc); - }; - Ret (*f)(Args...); jule::Ptr ctx; // Closure ctx. - CtxHandler *ctxHandler; + void (*ctxhandler)(jule::Ptr &alloc) = nullptr; Fn2(void) = default; Fn2(const Fn2 &) = default; @@ -172,7 +166,7 @@ namespace jule this->f = nullptr; if (this->ctxHandler) { - this->ctxHandler->dealloc(this->ctx); + this->ctxHandler(this->ctx); this->ctxHandler = nullptr; } } @@ -335,7 +329,7 @@ namespace jule } template - jule::Fn2 __new_closure(void *fn, jule::Ptr ctx, typename jule::Fn2::CtxHandler *ctxHandler) + jule::Fn2 __new_closure(void *fn, jule::Ptr ctx, void (*ctxHandler)(jule::Ptr &)) { __JULE_CLOSURE_MTX_LOCK(); if (!jule::__closure_cap) diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index 7c37f8106..8d1e9a7c7 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -918,13 +918,21 @@ impl exprCoder { } fn anonFunc(mut &self, mut m: &AnonFnExprModel) { - self.oc.tc.func(self.oc.Buf, m.Func) - self.oc.write("([=]") - self.oc.paramsIns(m.Func.Params) - self.oc.write(" mutable -> ") - self.oc.tc.funcInsResult(self.oc.Buf, m.Func) - self.oc.write(" ") - self.oc.sc.funcScope(m.Func) + ident := self.oc.pushAnonFn(m.Func) + self.oc.write("jule::__new_closure<") + self.oc.tc.anonFunc(self.oc.Buf, m.Func) + self.oc.write(">((void*)") + self.oc.write(ident) + self.oc.write(", jule::Ptr<") + self.oc.write(ident) + self.oc.write(anonFnCtxSuffix) + self.oc.write(">::make(") + self.oc.write(ident) + self.oc.write(anonFnCtxSuffix) + self.oc.write("{") + self.oc.write("}).as<" + typeCoder.Uintptr + ">(), ") + self.oc.write(ident) + self.oc.write(anonFnCtxHandlerSuffix) self.oc.write(")") } diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index 74851d1fe..88f811b9a 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -44,6 +44,8 @@ use std::time::{Time} // Data offset of empty trait. const emptyTraitOffset = 0x0 +const anonFnCtxSuffix = "_ctx" // Anon fn identifier suffix for ctx struct identifier. +const anonFnCtxHandlerSuffix = anonFnCtxSuffix + "_handler" // Anon fn identifier suffix for ctx allocation handler. const anyTypeIdent = "__jule_any_type" const indentKind = '\t' @@ -68,8 +70,9 @@ struct ObjectCoder { // Internal buffer which is commonly used. Buf: StrBuilder - resultDecls: StrBuilder - anyObj: StrBuilder + resultDecls: StrBuilder // Struct wrappers for multi-ret function types. + anyObj: StrBuilder // Type handlers and others for the type. + anonObj: StrBuilder // Anonymous functions. ir: &IR info: SerializationInfo @@ -134,6 +137,34 @@ impl ObjectCoder { ret -1 } + fn pushAnonFn(mut &self, mut &f: &FnIns): (ident: str) { + // Handle identifier and generate ctx. + self.anonObj.WriteStr("struct ") + l := self.anonObj.Len() + self.anonObj.WriteStr("__jule_anon_") + self.anonObj.WriteStr(conv::FmtUint(u64(uintptr(f)), 0xF)) + ident = str(unsafe { self.anonObj.Buf()[l:] }) + self.anonObj.WriteStr(anonFnCtxSuffix + "{\n") + self.anonObj.WriteStr("};\n") + + // Ctx handler function. + self.anonObj.WriteStr("static void ") + self.anonObj.WriteStr(ident) + self.anonObj.WriteStr(anonFnCtxHandlerSuffix + "(jule::Ptr<" + typeCoder.Uintptr + "> &ptr) { ptr.__as<") + self.anonObj.WriteStr(ident) + self.anonObj.WriteStr(anonFnCtxSuffix + ">().dealloc(); }\n") + + // Anonymous function. + mut oldBuf := unsafe { self.Buf.Buf() } + unsafe { self.Buf.SetBuf(self.anonObj.Buf()) } + self.anonFuncIns(f, ident) + unsafe { + self.anonObj.SetBuf(self.Buf.Buf()) + self.Buf.SetBuf(oldBuf) + } + ret + } + fn pushAnyType(mut &self, mut t: &TypeKind): int { if t.Enum() != nil { t = t.Enum().Kind.Kind @@ -822,7 +853,7 @@ impl ObjectCoder { self.write(")") } - fn funcHead(mut &self, mut &f: &FnIns, ptr: bool) { + fn funcHead(mut &self, mut &f: &FnIns, ptr: bool, ident: str) { if !ptr && opt::Inline && !f.Decl.IsEntryPoint() { self.write("inline ") } @@ -833,13 +864,17 @@ impl ObjectCoder { self.write(")") } else { self.write(" ") - identCoder.funcIns(self.Buf, f) + if ident == "" { + identCoder.funcIns(self.Buf, f) + } else { + self.write(ident) + } } } fn funcDeclIns(mut &self, mut &f: &FnIns, ptr: bool) { self.indent() - self.funcHead(f, ptr) + self.funcHead(f, ptr, "") self.paramsDecls(f.Params) self.write(";\n") } @@ -1001,8 +1036,18 @@ impl ObjectCoder { self.varInitExpr(v, nil) } + fn anonFuncIns(mut &self, mut &f: &FnIns, ident: str) { + self.funcHead(f, false, ident) + self.paramsIns(f.Params) + self.write(" ") + self.sc.funcScope(f) + if f.Scope != nil { + self.write("\n\n") + } + } + fn funcIns(mut &self, mut &f: &FnIns) { - self.funcHead(f, false) + self.funcHead(f, false, "") self.paramsIns(f.Params) self.write(" ") self.sc.funcScope(f) @@ -1349,6 +1394,13 @@ impl ObjectCoder { head = append(head, unsafe { self.Buf.Buf() }[self.declPos:]...) unsafe { self.Buf.SetBuf(head) } } + if self.anonObj.Len() > 0 { + mut head := make([]byte, 0, self.Buf.Len() + self.anonObj.Len()) + head = append(head, unsafe { self.Buf.Buf() }[:self.declPos]...) + head = append(head, unsafe { self.anonObj.Buf() }...) + head = append(head, unsafe { self.Buf.Buf() }[self.declPos:]...) + unsafe { self.Buf.SetBuf(head) } + } } fn Serialize(mut &self) { diff --git a/src/julec/obj/cxx/type.jule b/src/julec/obj/cxx/type.jule index 3d12d0461..bbac740c3 100644 --- a/src/julec/obj/cxx/type.jule +++ b/src/julec/obj/cxx/type.jule @@ -57,7 +57,7 @@ impl typeCoder { const Slice = "jule::Slice" const Trait = "jule::Trait" const Array = "jule::Array" - const Fn = "jule::Fn" + const Fn = "jule::Fn2" const Bool = "jule::Bool" const Int = "jule::Int" const Uintptr = "jule::Uintptr" @@ -276,21 +276,19 @@ impl typeCoder { self.oc.pushResultIns(f) } self.funcInsResult(buf, f) - buf.WriteByte('(') - if len(f.Params) > 0 { - for (i, mut param) in f.Params { - if param.Decl.IsSelf() { - continue - } - self.paramIns(buf, param) - if len(f.Params)-i > 1 { - buf.WriteByte(',') - } + if len(f.Params) == 0 { + ret + } + buf.WriteByte(',') + for (i, mut param) in f.Params { + if param.Decl.IsSelf() { + continue + } + self.paramIns(buf, param) + if len(f.Params)-i > 1 { + buf.WriteByte(',') } - } else { - buf.WriteStr("void") } - buf.WriteByte(')') } // Generates C++ code of Fn TypeKind. From 95d775ca263ee49b2b07d9788524a59be74ad2ec Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 10:59:12 +0300 Subject: [PATCH 03/22] sema: initialize capture analysis for closures --- std/jule/sema/eval.jule | 15 ++++++++++++--- std/jule/sema/model.jule | 5 +++-- std/jule/sema/scope.jule | 33 ++++++++++++++++++++------------- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index aac462439..d60428eb6 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -592,13 +592,19 @@ impl Eval { ret nil } | &scopeChecker: - if !v.Reference || self.isUnsafe() { - break - } mut s := (&scopeChecker)(self.lookup) for s.owner == nil && s.parent != nil { s = s.parent } + if s.owner == nil || !s.owner.Anon { + ret + } + if s.captured != nil && s.findVar(v.Ident) == nil { + *s.captured = append(*s.captured, v) + } + if !v.Reference || self.isUnsafe() { + break + } if s.owner != nil && s.owner.Anon && v.Scope != s.owner.Scope { self.pushErr(errorToken, LogMsg.UsedRefInAnonFnFromParentScope, v.Ident) } @@ -2910,6 +2916,7 @@ impl Eval { if ins == nil { ret nil } + mut captured := make([]&Var, 0) match type self.lookup { | &scopeChecker: mut sc := (&scopeChecker)(self.lookup) @@ -2921,6 +2928,7 @@ impl Eval { scc.it = 0 scc.cse = 0 scc.owner = ins + scc.captured = unsafe { (&[]&Var)(&captured) }, self.s.checkFnInsSc(ins, scc) |: self.s.checkFnIns(ins) @@ -2929,6 +2937,7 @@ impl Eval { ret &Data{ Kind: &TypeKind{Kind: ins}, Model: &AnonFnExprModel{ + Captured: captured, Func: ins, Global: self.isGlobal(), }, diff --git a/std/jule/sema/model.jule b/std/jule/sema/model.jule index 789f3155d..7e6f3ae70 100644 --- a/std/jule/sema/model.jule +++ b/std/jule/sema/model.jule @@ -127,8 +127,9 @@ struct IndexingExprModel { // Anonymous function expression Model:. struct AnonFnExprModel { - Func: &FnIns - Global: bool + Captured: []&Var + Func: &FnIns + Global: bool } // Key-value expression pair Model:. diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index b39236057..dec80a028 100644 --- a/std/jule/sema/scope.jule +++ b/std/jule/sema/scope.jule @@ -444,6 +444,7 @@ struct scopeChecker { result: &FnIns // Result type for last statement. it: uintptr cse: uintptr + captured: &[]&Var // Anonymous function's captured variables will be stored here. labels: &[]&scopeLabel // All labels of all scopes. gotos: &[]&scopeGoto // All gotos of all scopes. i: int @@ -468,22 +469,10 @@ impl Lookup for scopeChecker { // - Parent scopes. // - Sema. fn FindVar(mut self, ident: str, binded: bool): &Var { - // Search reverse for correct shadowing. - const Reverse = true - mut v := self.table.findVar(ident, binded, Reverse) + mut v := self.findVar(ident, binded) if v != nil { ret v } - - mut parent := self.parent - for parent != nil { - v = parent.table.findVar(ident, binded, Reverse) - if v != nil { - ret v - } - parent = parent.parent - } - ret self.s.FindVar(ident, binded) } @@ -631,6 +620,24 @@ impl scopeChecker { ret root } + // Like [Lookup.FindVar] but designed for local variables only. + fn findVar(ident: str, binded: bool): &Var { + const Reverse = true // Search reverse for correct shadowing. + mut v := self.table.findVar(ident, binded, Reverse) + if v != nil { + ret v + } + mut parent := self.parent + for parent != nil { + v = parent.table.findVar(ident, binded, Reverse) + if v != nil { + ret v + } + parent = parent.parent + } + ret nil + } + // Returns label by identifier. // Returns nil if not exist any label in this identifier. // Just lookups current scope. From 173ec8383c8dbcaf8a3aba5659e9fb25ab62ae99 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:10:51 +0300 Subject: [PATCH 04/22] sema: caught captured variables for anonymous functions --- std/jule/sema/eval.jule | 40 +++++++++++++++++++++++++++++++--------- std/jule/sema/scope.jule | 2 +- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index d60428eb6..25db8bc8b 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -593,18 +593,16 @@ impl Eval { } | &scopeChecker: mut s := (&scopeChecker)(self.lookup) - for s.owner == nil && s.parent != nil { - s = s.parent - } - if s.owner == nil || !s.owner.Anon { - ret - } - if s.captured != nil && s.findVar(v.Ident) == nil { - *s.captured = append(*s.captured, v) + mut root := s.getRoot() + if root.captured != nil && isVarCaptured(root, s, v) { + *root.captured = append(*root.captured, v) } if !v.Reference || self.isUnsafe() { break } + for s.owner == nil && s.parent != nil { + s = s.parent + } if s.owner != nil && s.owner.Anon && v.Scope != s.owner.Scope { self.pushErr(errorToken, LogMsg.UsedRefInAnonFnFromParentScope, v.Ident) } @@ -2928,7 +2926,7 @@ impl Eval { scc.it = 0 scc.cse = 0 scc.owner = ins - scc.captured = unsafe { (&[]&Var)(&captured) }, + scc.captured = unsafe { (&[]&Var)(&captured) } self.s.checkFnInsSc(ins, scc) |: self.s.checkFnIns(ins) @@ -4586,4 +4584,28 @@ fn makeVariadic(mut &d: &Data, mut elem: &TypeKind) { BindIdent: elem.BindIdent, Kind: elem.Kind, } +} + +// Reports whether variable is captured from parent scope. +// Parameters: +// - r: root scope of anonymous function +// - s: current scope which is v accessed from +// - v: variable to check +fn isVarCaptured(r: &scopeChecker, mut s: &scopeChecker, v: &Var): bool { + if v.Scope == nil { // Global scope variable. + ret false + } + if s == r { // Root scope, check fast way. + ret r.scope != v.Scope + } + for s != r { + if s.scope == v.Scope { + ret false + } + if s.parent == nil { + break + } + s = s.parent + } + ret true } \ No newline at end of file diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index dec80a028..8ad0b20aa 100644 --- a/std/jule/sema/scope.jule +++ b/std/jule/sema/scope.jule @@ -621,7 +621,7 @@ impl scopeChecker { } // Like [Lookup.FindVar] but designed for local variables only. - fn findVar(ident: str, binded: bool): &Var { + fn findVar(mut self, ident: str, binded: bool): &Var { const Reverse = true // Search reverse for correct shadowing. mut v := self.table.findVar(ident, binded, Reverse) if v != nil { From a3cbd64c08b7fa0858acadee81df4068f53c7bb5 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:19:11 +0300 Subject: [PATCH 05/22] compiler: generate ctx for closure --- src/julec/obj/cxx/expr.jule | 11 ++++++++++- src/julec/obj/cxx/object.jule | 14 +++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index 8d1e9a7c7..c0d50b008 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -918,7 +918,7 @@ impl exprCoder { } fn anonFunc(mut &self, mut m: &AnonFnExprModel) { - ident := self.oc.pushAnonFn(m.Func) + ident := self.oc.pushAnonFn(m) self.oc.write("jule::__new_closure<") self.oc.tc.anonFunc(self.oc.Buf, m.Func) self.oc.write(">((void*)") @@ -930,6 +930,15 @@ impl exprCoder { self.oc.write(ident) self.oc.write(anonFnCtxSuffix) self.oc.write("{") + for (i, mut v) in m.Captured { + self.oc.write(".") + identCoder.var(self.oc.Buf, v) + self.oc.write("=") + identCoder.var(self.oc.Buf, v) + if len(m.Captured)-i > 1 { + self.oc.write(",") + } + } self.oc.write("}).as<" + typeCoder.Uintptr + ">(), ") self.oc.write(ident) self.oc.write(anonFnCtxHandlerSuffix) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index 88f811b9a..4eef89ded 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -36,6 +36,7 @@ use std::jule::sema::{ Sptr, TypeSymbol, Operators, + AnonFnExprModel, } use path for std::fs::path use strings for std::strings::{StrBuilder} @@ -137,14 +138,21 @@ impl ObjectCoder { ret -1 } - fn pushAnonFn(mut &self, mut &f: &FnIns): (ident: str) { + fn pushAnonFn(mut &self, mut &m: &AnonFnExprModel): (ident: str) { // Handle identifier and generate ctx. self.anonObj.WriteStr("struct ") l := self.anonObj.Len() self.anonObj.WriteStr("__jule_anon_") - self.anonObj.WriteStr(conv::FmtUint(u64(uintptr(f)), 0xF)) + self.anonObj.WriteStr(conv::FmtUint(u64(uintptr(m.Func)), 0xF)) ident = str(unsafe { self.anonObj.Buf()[l:] }) self.anonObj.WriteStr(anonFnCtxSuffix + "{\n") + for (_, mut v) in m.Captured { + self.anonObj.WriteByte(indentKind) // 1x indent + self.tc.kind(self.anonObj, v.Kind.Kind) + self.anonObj.WriteByte(' ') + identCoder.var(self.anonObj, v) + self.anonObj.WriteStr(";\n") + } self.anonObj.WriteStr("};\n") // Ctx handler function. @@ -157,7 +165,7 @@ impl ObjectCoder { // Anonymous function. mut oldBuf := unsafe { self.Buf.Buf() } unsafe { self.Buf.SetBuf(self.anonObj.Buf()) } - self.anonFuncIns(f, ident) + self.anonFuncIns(m.Func, ident) unsafe { self.anonObj.SetBuf(self.Buf.Buf()) self.Buf.SetBuf(oldBuf) From 62ea87cb09cce7d968c76ed80e5db7d54505462b Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:32:38 +0300 Subject: [PATCH 06/22] compiler: initialize special scope generation for closure --- src/julec/obj/cxx/expr.jule | 7 +++++++ src/julec/obj/cxx/object.jule | 15 +++++++++------ src/julec/obj/cxx/scope.jule | 23 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index c0d50b008..604a2bf88 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -103,6 +103,10 @@ enum compExprModel: type { struct exprCoder { oc: &ObjectCoder + + // It will executed before common variable handling algorithm. + // If it returns true, common algorithm will not be executed. + varPrefix: fn(mut v: &Var): bool } impl exprCoder { @@ -280,6 +284,9 @@ impl exprCoder { } fn var(mut &self, mut m: &Var) { + if self.varPrefix != nil && self.varPrefix(m) { + ret + } if m.Binded { d := findDirective(m.Directives, Directive.Namespace) if d != nil { diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index 4eef89ded..4ffe55257 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -165,7 +165,7 @@ impl ObjectCoder { // Anonymous function. mut oldBuf := unsafe { self.Buf.Buf() } unsafe { self.Buf.SetBuf(self.anonObj.Buf()) } - self.anonFuncIns(m.Func, ident) + self.anonFuncIns(m, ident) unsafe { self.anonObj.SetBuf(self.Buf.Buf()) self.Buf.SetBuf(oldBuf) @@ -861,6 +861,9 @@ impl ObjectCoder { self.write(")") } + // The ident parameter means this function is anon, mostly. + // But this parameter not only for anonymous functions. + // It also useable as custom identifiers for functions. fn funcHead(mut &self, mut &f: &FnIns, ptr: bool, ident: str) { if !ptr && opt::Inline && !f.Decl.IsEntryPoint() { self.write("inline ") @@ -1044,12 +1047,12 @@ impl ObjectCoder { self.varInitExpr(v, nil) } - fn anonFuncIns(mut &self, mut &f: &FnIns, ident: str) { - self.funcHead(f, false, ident) - self.paramsIns(f.Params) + fn anonFuncIns(mut &self, mut &m: &AnonFnExprModel, ident: str) { + self.funcHead(m.Func, false, ident) + self.paramsIns(m.Func.Params) self.write(" ") - self.sc.funcScope(f) - if f.Scope != nil { + self.sc.anonFuncScope(m) + if m.Func.Scope != nil { self.write("\n\n") } } diff --git a/src/julec/obj/cxx/scope.jule b/src/julec/obj/cxx/scope.jule index ec05d1d48..88a1ccc0e 100644 --- a/src/julec/obj/cxx/scope.jule +++ b/src/julec/obj/cxx/scope.jule @@ -42,6 +42,7 @@ use std::jule::sema::{ SlicingExprModel, IndexingExprModel, FnCallExprModel, + AnonFnExprModel, } use std::strings::{StrBuilder} @@ -956,6 +957,28 @@ impl scopeCoder { } } + fn anonFuncScope(mut &self, mut &m: &AnonFnExprModel, ident: str) { + if f.Scope == nil { + ret + } + oldPrefix := self.oc.ec.varPrefix + self.oc.write("{\n") + self.oc.addIndent() + + // Get ctx. + self.oc.write(idnet) + self.oc.write(anonFnCtxSuffix) + self.oc.write("__jule_closure_ctx = ") + self.oc.write(idnet) + self.oc.write(anonFnCtxSuffix) + self.oc.write("*)jule::__closure_get_ctx();") + + self.oc.doneIndent() + self.oc.indent() + self.oc.write("}") + self.oc.ec.varPrefix = oldPrefix + } + // Generates C++ code of function's scope. fn funcScope(mut &self, mut f: &FnIns) { if f.Scope == nil { From 5973809b212c7e137b0b20b626b7ddd9560d14c0 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:48:06 +0300 Subject: [PATCH 07/22] compiler: generate ctx-based scope for anonymous functions --- src/julec/obj/cxx/object.jule | 2 +- src/julec/obj/cxx/scope.jule | 75 +++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index 4ffe55257..ebb717713 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -1051,7 +1051,7 @@ impl ObjectCoder { self.funcHead(m.Func, false, ident) self.paramsIns(m.Func.Params) self.write(" ") - self.sc.anonFuncScope(m) + self.sc.anonFuncScope(m, ident) if m.Func.Scope != nil { self.write("\n\n") } diff --git a/src/julec/obj/cxx/scope.jule b/src/julec/obj/cxx/scope.jule index 88a1ccc0e..cadbef21f 100644 --- a/src/julec/obj/cxx/scope.jule +++ b/src/julec/obj/cxx/scope.jule @@ -957,35 +957,7 @@ impl scopeCoder { } } - fn anonFuncScope(mut &self, mut &m: &AnonFnExprModel, ident: str) { - if f.Scope == nil { - ret - } - oldPrefix := self.oc.ec.varPrefix - self.oc.write("{\n") - self.oc.addIndent() - - // Get ctx. - self.oc.write(idnet) - self.oc.write(anonFnCtxSuffix) - self.oc.write("__jule_closure_ctx = ") - self.oc.write(idnet) - self.oc.write(anonFnCtxSuffix) - self.oc.write("*)jule::__closure_get_ctx();") - - self.oc.doneIndent() - self.oc.indent() - self.oc.write("}") - self.oc.ec.varPrefix = oldPrefix - } - - // Generates C++ code of function's scope. - fn funcScope(mut &self, mut f: &FnIns) { - if f.Scope == nil { - ret - } - self.oc.write("{\n") - self.oc.addIndent() + fn commonFuncScope(mut &self, mut &f: &FnIns) { if !f.Decl.IsVoid() { mut tup := f.Result.Tup() if tup != nil { @@ -1023,6 +995,40 @@ impl scopeCoder { self.oc.indent() self.oc.write("return jule::VoidExceptional{};\n") } + } + + fn anonFuncScope(mut &self, mut m: &AnonFnExprModel, ident: str) { + if m.Func.Scope == nil { + ret + } + oldPrefix := self.oc.ec.varPrefix + self.oc.ec.varPrefix = fn(mut v: &Var): bool { ret captureVarHandling(self.oc, m, v) } + self.oc.write("{\n") + self.oc.addIndent() + + // Get ctx. + self.oc.indent() + self.oc.write(ident) + self.oc.write(anonFnCtxSuffix + " *__jule_closure_ctx = (") + self.oc.write(ident) + self.oc.write(anonFnCtxSuffix + "*)jule::__closure_get_ctx();\n") + + self.commonFuncScope(m.Func) + + self.oc.doneIndent() + self.oc.indent() + self.oc.write("}") + self.oc.ec.varPrefix = oldPrefix + } + + // Generates C++ code of function's scope. + fn funcScope(mut &self, mut f: &FnIns) { + if f.Scope == nil { + ret + } + self.oc.write("{\n") + self.oc.addIndent() + self.commonFuncScope(f) self.oc.doneIndent() self.oc.indent() self.oc.write("}") @@ -1045,4 +1051,15 @@ fn isIterCopyOptimizable(&expr: &Data, &v: &Var): bool { ret true } ret !v.Mutable && !expr.Mutable +} + +fn captureVarHandling(mut &oc: &ObjectCoder, mut &m: &AnonFnExprModel, mut &v: &Var): bool { + for _, cv in m.Captured { + if cv == v { + oc.write("__jule_closure_ctx->") + identCoder.var(oc.Buf, v) + ret true + } + } + ret false } \ No newline at end of file From bdf39cf1d6f6b0b8ce0e131fad7c2217d611f4f7 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:48:20 +0300 Subject: [PATCH 08/22] api: fix naming --- api/fn.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/fn.hpp b/api/fn.hpp index 645a6ab3d..c467a18c8 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -150,7 +150,7 @@ namespace jule public: Ret (*f)(Args...); jule::Ptr ctx; // Closure ctx. - void (*ctxhandler)(jule::Ptr &alloc) = nullptr; + void (*ctxHandler)(jule::Ptr &alloc) = nullptr; Fn2(void) = default; Fn2(const Fn2 &) = default; From c327bf0500a57253eda6aeaccd539e9c32184fec Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:49:50 +0300 Subject: [PATCH 09/22] compiler: initialize closures at entry point --- src/julec/obj/cxx/object.jule | 1 + 1 file changed, 1 insertion(+) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index ebb717713..5e830c7ae 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -1358,6 +1358,7 @@ impl ObjectCoder { fn end(mut &self) { self.write(`int main(int argc, char *argv[], char *envp[]) { + jule::__closure_init(); jule::setup_argv(argc, argv); jule::setup_envp(envp); From 642b9cbb348c5b025dfb1f98d66608b9d4200ba8 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 11:55:00 +0300 Subject: [PATCH 10/22] sema: fix captured analysis allows duplications --- std/jule/sema/eval.jule | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index 25db8bc8b..62d988d16 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -595,7 +595,13 @@ impl Eval { mut s := (&scopeChecker)(self.lookup) mut root := s.getRoot() if root.captured != nil && isVarCaptured(root, s, v) { + for _, cv in *root.captured { + if cv == v { + goto exist + } + } *root.captured = append(*root.captured, v) + exist: } if !v.Reference || self.isUnsafe() { break From a1791b6e94969e3670a4af6b3e425a1db8e2a597 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 14:00:19 +0300 Subject: [PATCH 11/22] compiler: minor fix for backend declaration handling --- src/julec/obj/cxx/object.jule | 78 +++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index 5e830c7ae..f447945a9 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -67,6 +67,11 @@ struct traitCast { t2: &Trait } +struct anonHash { + expr: &AnonFnExprModel + ident: str +} + struct ObjectCoder { // Internal buffer which is commonly used. Buf: StrBuilder @@ -78,6 +83,7 @@ struct ObjectCoder { ir: &IR info: SerializationInfo tmap: []&traitHash + anons: []&anonHash // Current indentation. indentBuffer: []byte @@ -163,13 +169,8 @@ impl ObjectCoder { self.anonObj.WriteStr(anonFnCtxSuffix + ">().dealloc(); }\n") // Anonymous function. - mut oldBuf := unsafe { self.Buf.Buf() } - unsafe { self.Buf.SetBuf(self.anonObj.Buf()) } - self.anonFuncIns(m, ident) - unsafe { - self.anonObj.SetBuf(self.Buf.Buf()) - self.Buf.SetBuf(oldBuf) - } + self.anons = append(self.anons, &anonHash{expr: m, ident: ident}) + self.anonFuncInsDecl(m, ident) ret } @@ -864,28 +865,28 @@ impl ObjectCoder { // The ident parameter means this function is anon, mostly. // But this parameter not only for anonymous functions. // It also useable as custom identifiers for functions. - fn funcHead(mut &self, mut &f: &FnIns, ptr: bool, ident: str) { + fn funcHead(mut &self, mut &buf: StrBuilder, mut &f: &FnIns, ptr: bool, ident: str) { if !ptr && opt::Inline && !f.Decl.IsEntryPoint() { - self.write("inline ") + buf.WriteStr("inline ") } - self.tc.funcInsResult(self.Buf, f) + self.tc.funcInsResult(buf, f) if ptr { - self.write("(*") - identCoder.funcIns(self.Buf, f) - self.write(")") + buf.WriteStr("(*") + identCoder.funcIns(buf, f) + buf.WriteByte(')') } else { - self.write(" ") + buf.WriteByte(' ') if ident == "" { - identCoder.funcIns(self.Buf, f) + identCoder.funcIns(buf, f) } else { - self.write(ident) + buf.WriteStr(ident) } } } fn funcDeclIns(mut &self, mut &f: &FnIns, ptr: bool) { self.indent() - self.funcHead(f, ptr, "") + self.funcHead(self.Buf, f, ptr, "") self.paramsDecls(f.Params) self.write(";\n") } @@ -977,25 +978,25 @@ impl ObjectCoder { }) } - fn paramIns(mut &self, mut &p: &ParamIns) { - self.tc.paramIns(self.Buf, p) - self.write(" ") - identCoder.param(self.Buf, p.Decl) + fn paramIns(mut &self, mut &buf: StrBuilder, mut &p: &ParamIns) { + self.tc.paramIns(buf, p) + buf.WriteByte(' ') + identCoder.param(buf, p.Decl) } - fn paramsIns(mut &self, mut ¶ms: []&ParamIns) { + fn paramsIns(mut &self, mut &buf: StrBuilder, mut ¶ms: []&ParamIns) { if len(params) == 0 { - self.write("(void)") + buf.WriteStr("(void)") ret } - self.write("(") + buf.WriteByte('(') for (i, mut p) in params { - self.paramIns(p) + self.paramIns(buf, p) if len(params)-i > 1 { - self.write(", ") + buf.WriteStr(", ") } } - self.write(")") + buf.WriteByte(')') } fn varInitExpr(mut &self, mut &v: &Var, init: fn()) { @@ -1047,9 +1048,15 @@ impl ObjectCoder { self.varInitExpr(v, nil) } + fn anonFuncInsDecl(mut &self, mut &m: &AnonFnExprModel, ident: str) { + self.funcHead(self.anonObj, m.Func, false, ident) + self.paramsIns(self.anonObj, m.Func.Params) + self.anonObj.WriteByte(';') + } + fn anonFuncIns(mut &self, mut &m: &AnonFnExprModel, ident: str) { - self.funcHead(m.Func, false, ident) - self.paramsIns(m.Func.Params) + self.funcHead(self.Buf, m.Func, false, ident) + self.paramsIns(self.Buf, m.Func.Params) self.write(" ") self.sc.anonFuncScope(m, ident) if m.Func.Scope != nil { @@ -1058,8 +1065,8 @@ impl ObjectCoder { } fn funcIns(mut &self, mut &f: &FnIns) { - self.funcHead(f, false, "") - self.paramsIns(f.Params) + self.funcHead(self.Buf, f, false, "") + self.paramsIns(self.Buf, f.Params) self.write(" ") self.sc.funcScope(f) if f.Scope != nil { @@ -1346,6 +1353,12 @@ impl ObjectCoder { }) } + fn anonHashes(mut &self) { + for (_, mut h) in self.anons { + self.anonFuncIns(h.expr, h.ident) + } + } + fn initCaller(mut &self) { self.write("void " + initCallerIdent + "(void) {\n") self.addIndent() @@ -1399,12 +1412,15 @@ impl ObjectCoder { self.initCaller() self.write("\n\n") + self.anonHashes() + if self.anyObj.Len() > 0 { mut head := make([]byte, 0, self.Buf.Len() + self.anyObj.Len()) head = append(head, unsafe { self.Buf.Buf() }[:self.declPos]...) head = append(head, unsafe { self.anyObj.Buf() }...) head = append(head, unsafe { self.Buf.Buf() }[self.declPos:]...) unsafe { self.Buf.SetBuf(head) } + self.declPos += self.anyObj.Len() } if self.anonObj.Len() > 0 { mut head := make([]byte, 0, self.Buf.Len() + self.anonObj.Len()) From b0ae61e881c406b0176bdb645192ec25cdad8259 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 14:07:12 +0300 Subject: [PATCH 12/22] compiler: minor fix for closure ctx code generation --- src/julec/obj/cxx/object.jule | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index f447945a9..e0d7ec069 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -156,6 +156,9 @@ impl ObjectCoder { self.anonObj.WriteByte(indentKind) // 1x indent self.tc.kind(self.anonObj, v.Kind.Kind) self.anonObj.WriteByte(' ') + if v.Reference { + self.anonObj.WriteByte('*') + } identCoder.var(self.anonObj, v) self.anonObj.WriteStr(";\n") } From 6cb17cba589ba6fd9446e69ec135d3ab537cbae4 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 14:24:27 +0300 Subject: [PATCH 13/22] sema: minor fix for closure capture analysis --- std/jule/sema/eval.jule | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index 62d988d16..1b7f22c8e 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -4601,13 +4601,14 @@ fn isVarCaptured(r: &scopeChecker, mut s: &scopeChecker, v: &Var): bool { if v.Scope == nil { // Global scope variable. ret false } - if s == r { // Root scope, check fast way. - ret r.scope != v.Scope - } - for s != r { + for { if s.scope == v.Scope { ret false } + // Break iteration if parent is not exist or scope already reached to root. + if s == r { + ret false + } if s.parent == nil { break } From 6b9e618467bf4194ab7d2d72851ae54aac15802c Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 14:24:39 +0300 Subject: [PATCH 14/22] api: minor naming fix --- api/fn.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/fn.hpp b/api/fn.hpp index c467a18c8..d84e31cc5 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -206,7 +206,7 @@ namespace jule constexpr jule::Bool operator==(std::nullptr_t) const noexcept { - return this->buffer == nullptr; + return this->f == nullptr; } constexpr jule::Bool operator!=(std::nullptr_t) const noexcept From ac5988842d7e416f2c37fc05109582cccc4e49b8 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 14:33:04 +0300 Subject: [PATCH 15/22] sema: minor fix for closure capture analysis --- std/jule/sema/eval.jule | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index 1b7f22c8e..9bfb36953 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -4606,10 +4606,7 @@ fn isVarCaptured(r: &scopeChecker, mut s: &scopeChecker, v: &Var): bool { ret false } // Break iteration if parent is not exist or scope already reached to root. - if s == r { - ret false - } - if s.parent == nil { + if s.parent == nil || s == r { break } s = s.parent From c9b576043dede4ddacf333003c68c975ca3ad982 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 17:06:27 +0300 Subject: [PATCH 16/22] sema, compiler: minor fix for capture analysis and handling of captured variables --- src/julec/obj/cxx/expr.jule | 10 ++++++---- src/julec/obj/cxx/scope.jule | 5 ++--- std/jule/sema/eval.jule | 11 ++--------- std/jule/sema/scope.jule | 24 ++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index 604a2bf88..8ecfe7e1b 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -106,7 +106,7 @@ struct exprCoder { // It will executed before common variable handling algorithm. // If it returns true, common algorithm will not be executed. - varPrefix: fn(mut v: &Var): bool + varPrefixes: []fn(mut v: &Var): bool } impl exprCoder { @@ -284,8 +284,10 @@ impl exprCoder { } fn var(mut &self, mut m: &Var) { - if self.varPrefix != nil && self.varPrefix(m) { - ret + for _, prefix in self.varPrefixes { + if prefix(m) { + ret + } } if m.Binded { d := findDirective(m.Directives, Directive.Namespace) @@ -941,7 +943,7 @@ impl exprCoder { self.oc.write(".") identCoder.var(self.oc.Buf, v) self.oc.write("=") - identCoder.var(self.oc.Buf, v) + self.var(v) if len(m.Captured)-i > 1 { self.oc.write(",") } diff --git a/src/julec/obj/cxx/scope.jule b/src/julec/obj/cxx/scope.jule index cadbef21f..aad12fd9a 100644 --- a/src/julec/obj/cxx/scope.jule +++ b/src/julec/obj/cxx/scope.jule @@ -1001,8 +1001,7 @@ impl scopeCoder { if m.Func.Scope == nil { ret } - oldPrefix := self.oc.ec.varPrefix - self.oc.ec.varPrefix = fn(mut v: &Var): bool { ret captureVarHandling(self.oc, m, v) } + self.oc.ec.varPrefixes = append(self.oc.ec.varPrefixes, fn(mut v: &Var): bool { ret captureVarHandling(self.oc, m, v) }) self.oc.write("{\n") self.oc.addIndent() @@ -1018,7 +1017,7 @@ impl scopeCoder { self.oc.doneIndent() self.oc.indent() self.oc.write("}") - self.oc.ec.varPrefix = oldPrefix + self.oc.ec.varPrefixes = self.oc.ec.varPrefixes[:len(self.oc.ec.varPrefixes)-1] } // Generates C++ code of function's scope. diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index 9bfb36953..7dcf964a6 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -595,13 +595,7 @@ impl Eval { mut s := (&scopeChecker)(self.lookup) mut root := s.getRoot() if root.captured != nil && isVarCaptured(root, s, v) { - for _, cv in *root.captured { - if cv == v { - goto exist - } - } - *root.captured = append(*root.captured, v) - exist: + root.pushCaptured(v) } if !v.Reference || self.isUnsafe() { break @@ -2927,11 +2921,10 @@ impl Eval { mut scc := sc.newChildChecker() scc.labels = new([]&scopeLabel, nil) scc.gotos = new([]&scopeGoto, nil) - scc.owner = nil + scc.owner = ins scc.childIndex = 0 scc.it = 0 scc.cse = 0 - scc.owner = ins scc.captured = unsafe { (&[]&Var)(&captured) } self.s.checkFnInsSc(ins, scc) |: diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index 8ad0b20aa..06e5471c0 100644 --- a/std/jule/sema/scope.jule +++ b/std/jule/sema/scope.jule @@ -600,6 +600,30 @@ impl scopeChecker { ret false } + // Push captured variable. + // It will append to all [captured] fields, + // because if child-scopes captures this variable, + // parent-scopes must be capture this variable too. + // Starts pushing from self to all parents. + fn pushCaptured(mut &self, mut &v: &Var) { + mut sc := self + for { + if sc.captured != nil { + for _, cv in *sc.captured { + if cv == v { + goto exist + } + } + *sc.captured = append(*sc.captured, v) + exist: + } + if sc.parent == nil { + break + } + sc = sc.parent + } + } + // Returns root scope. // Accepts anonymous functions as root. fn getRoot(mut &self): &scopeChecker { From 3ff2529163d95429c3e050955d2f375136c51c2a Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 17:06:43 +0300 Subject: [PATCH 17/22] compiler: minor formatting improvement for closure code generation --- src/julec/obj/cxx/object.jule | 1 + 1 file changed, 1 insertion(+) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index e0d7ec069..94c76daf0 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -174,6 +174,7 @@ impl ObjectCoder { // Anonymous function. self.anons = append(self.anons, &anonHash{expr: m, ident: ident}) self.anonFuncInsDecl(m, ident) + self.anonObj.WriteByte('\n') ret } From 217916206fa22d783d18e4d494f32fbc47f13608 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 18:12:25 +0300 Subject: [PATCH 18/22] compiler: fix anonymous function handling --- src/julec/obj/cxx/object.jule | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index 94c76daf0..e4b7ea3cd 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -1358,9 +1358,18 @@ impl ObjectCoder { } fn anonHashes(mut &self) { - for (_, mut h) in self.anons { + // Use recursive algorithm, because anonymous function may have + // anonymous functions, so [self.anons] may grow. + // Make sure all whether all anonymous functions are handled. + repeat: + mut anons := self.anons + self.anons = nil + for (_, mut h) in anons { self.anonFuncIns(h.expr, h.ident) } + if len(self.anons) > 0 { + goto repeat + } } fn initCaller(mut &self) { From aaa1426f86208d7cde12e08a2d4f8f3752cac4b9 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 18:24:41 +0300 Subject: [PATCH 19/22] api: fix memory handling of closure implementation --- api/fn.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/fn.hpp b/api/fn.hpp index d84e31cc5..68c09f175 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -286,7 +286,7 @@ namespace jule }; #endif -#ifdef _WIN32 +#ifdef OS_WINDOWS static SRWLOCK __closure_mtx; #else static pthread_mutex_t __closure_mtx; @@ -299,7 +299,7 @@ namespace jule static void __closure_alloc(void) { -#ifdef _WIN32 +#ifdef OS_WINDOWS jule::U8 *p = (jule::U8 *)VirtualAlloc(NULL, jule::__page_size << 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (p == NULL) jule::panic(__JULE_ERROR__MEMORY_ALLOCATION_FAILED @@ -320,7 +320,7 @@ namespace jule rem--; x += __JULE_CLOSURE_SIZE; } -#ifdef _WIN32 +#ifdef OS_WINDOWS DWORD temp; VirtualProtect(jule::__closure_ptr, jule::__page_size, PAGE_EXECUTE_READ, &temp); #else @@ -367,7 +367,8 @@ namespace jule #else void __closure_init() { - const uint32_t page_size = sysconf(_SC_PAGESIZE) * (((__JULE_ASSUMED_PAGE_SIZE - 1) / page_size) + 1); + uint32_t page_size = sysconf(_SC_PAGESIZE); + page_size *= (((__JULE_ASSUMED_PAGE_SIZE - 1) / page_size) + 1); jule::__page_size = page_size; jule::__closure_alloc(); (void)mprotect(jule::__closure_ptr, page_size, PROT_READ | PROT_WRITE); From 05e44b1d7650868c3fa972b7d6e19305aea65269 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 19:05:59 +0300 Subject: [PATCH 20/22] runtime: fix GC for closure ctx --- api/fn.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/fn.hpp b/api/fn.hpp index 68c09f175..40a035260 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -168,6 +168,8 @@ namespace jule { this->ctxHandler(this->ctx); this->ctxHandler = nullptr; + this->ctx.ref = nullptr; // Disable GC for allocation. + this->ctx = nullptr; // Assign as nullptr safely. } } @@ -343,8 +345,9 @@ namespace jule __JULE_CLOSURE_MTX_UNLOCK(); Ret (*static_closure)(Args...) = (Ret(*)(Args...))closure; jule::Fn2 fn2(static_closure); - fn2.ctx = ctx; + fn2.ctx = std::move(ctx); fn2.ctxHandler = ctxHandler; + ctx = nullptr; return fn2; } From f93b5aaddc26b981b51a22a3c422c1d3f259cd24 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 20:31:19 +0300 Subject: [PATCH 21/22] api: minor improvements for closure runtime --- api/fn.hpp | 98 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/api/fn.hpp b/api/fn.hpp index 40a035260..7ea5618f4 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -141,6 +141,29 @@ namespace jule #endif // ifndef __JULE_FN_HPP +#ifdef OS_WINDOWS +#include +#else +#include +#include +#endif + +#ifdef OS_WINDOWS +#define __JULE_CLOSURE_MTX_INIT() InitializeSRWLock(&jule::__closure_mtx) +#define __JULE_CLOSURE_MTX_LOCK() AcquireSRWLockExclusive(&jule::__closure_mtx) +#define __JULE_CLOSURE_MTX_UNLOCK() ReleaseSRWLockExclusive(&jule::__closure_mtx) +#else +#define __JULE_CLOSURE_MTX_INIT() pthread_mutex_init(&jule::__closure_mtx, 0) +#define __JULE_CLOSURE_MTX_LOCK() pthread_mutex_lock(&jule::__closure_mtx) +#define __JULE_CLOSURE_MTX_UNLOCK() pthread_mutex_unlock(&jule::__closure_mtx) +#endif + +#define __JULE_ASSUMED_PAGE_SIZE 0x4000 +#define __JULE_CLOSURE_SIZE (((sizeof(void *) << 1 > sizeof(jule::__closure_thunk) ? sizeof(void *) << 1 : sizeof(jule::__closure_thunk)) + sizeof(void *) - 1) & ~(sizeof(void *) - 1)) + +#define __JULE_CLOSURE_PAGE_PTR(closure) \ + ((void **)(closure - __JULE_ASSUMED_PAGE_SIZE)) + namespace jule { // std::function wrapper of JuleC. @@ -224,35 +247,8 @@ namespace jule return (stream << (void *)f.f); } }; -} // namespace jule - -#ifdef OS_WINDOWS -#include -#else -#include -#include -#endif - -#ifdef OS_WINDOWS -static SRWLOCK __closure_mtx; -#define __JULE_CLOSURE_MTX_INIT() InitializeSRWLock(&jule::__closure_mtx) -#define __JULE_CLOSURE_MTX_LOCK() AcquireSRWLockExclusive(&jule::__closure_mtx) -#define __JULE_CLOSURE_MTX_UNLOCK() ReleaseSRWLockExclusive(&jule::__closure_mtx) -#else -#define __JULE_CLOSURE_MTX_INIT() pthread_mutex_init(&jule::__closure_mtx, 0) -#define __JULE_CLOSURE_MTX_LOCK() pthread_mutex_lock(&jule::__closure_mtx) -#define __JULE_CLOSURE_MTX_UNLOCK() pthread_mutex_unlock(&jule::__closure_mtx) -#endif - -#define __JULE_ASSUMED_PAGE_SIZE 0x4000 -#define __JULE_CLOSURE_SIZE (((sizeof(void *) << 1 > sizeof(jule::__closure_thunk) ? sizeof(void *) << 1 : sizeof(jule::__closure_thunk)) + sizeof(void *) - 1) & ~(sizeof(void *) - 1)) -#define __JULE_CLOSURE_PAGE_PTR(closure) \ - ((void **)(closure - __JULE_ASSUMED_PAGE_SIZE)) - -namespace jule -{ - static int __page_size = __JULE_ASSUMED_PAGE_SIZE; + static jule::Uint __page_size = __JULE_ASSUMED_PAGE_SIZE; #if defined(ARCH_AMD64) static const char __closure_thunk[] = { @@ -288,22 +284,43 @@ namespace jule }; #endif + static jule::U8 *__closure_ptr = 0; + static jule::Int __closure_cap = 0; + + static void *(*__closure_get_ctx)(void) = nullptr; + #ifdef OS_WINDOWS static SRWLOCK __closure_mtx; + inline void __closure_mtx_init(void) noexcept { InitializeSRWLock(&jule::__closure_mtx) } + inline void __closure_mtx_lock(void) noexcept { AcquireSRWLockExclusive(&jule::__closure_mtx) } + inline void __closure_mtx_unlock(void) noexcept { ReleaseSRWLockExclusive(&jule::__closure_mtx) } #else static pthread_mutex_t __closure_mtx; -#endif - static jule::U8 *__closure_ptr = 0; - static int __closure_cap = 0; + inline void __closure_mtx_init(void) noexcept + { + if (pthread_mutex_init(&jule::__closure_mtx, 0) != 0) + jule::panic("closure mutex initialization failed"); + } - static void *(*__closure_get_ctx)(void) = nullptr; + inline void __closure_mtx_lock(void) noexcept + { + if (pthread_mutex_lock(&jule::__closure_mtx) != 0) + jule::panic("closure mutex locking failed"); + } + + inline void __closure_mtx_unlock(void) noexcept + { + if (pthread_mutex_unlock(&jule::__closure_mtx) != 0) + jule::panic("closure mutex unlocking failed"); + } +#endif - static void __closure_alloc(void) + static void __closure_alloc(void) noexcept { #ifdef OS_WINDOWS jule::U8 *p = (jule::U8 *)VirtualAlloc(NULL, jule::__page_size << 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (p == NULL) + if (!p) jule::panic(__JULE_ERROR__MEMORY_ALLOCATION_FAILED "\nruntime: heap allocation failed for closure"); #else @@ -313,7 +330,7 @@ namespace jule "\nruntime: heap allocation failed for closure"); #endif jule::U8 *x = p + jule::__page_size; - int rem = jule::__page_size / __JULE_CLOSURE_SIZE; + jule::Uint rem = jule::__page_size / __JULE_CLOSURE_SIZE; jule::__closure_ptr = x; jule::__closure_cap = rem; while (rem > 0) @@ -331,10 +348,10 @@ namespace jule } template - jule::Fn2 __new_closure(void *fn, jule::Ptr ctx, void (*ctxHandler)(jule::Ptr &)) + jule::Fn2 __new_closure(void *fn, jule::Ptr ctx, void (*ctxHandler)(jule::Ptr &)) noexcept { __JULE_CLOSURE_MTX_LOCK(); - if (!jule::__closure_cap) + if (jule::__closure_cap < 1) jule::__closure_alloc(); jule::__closure_cap--; jule::U8 *closure = jule::__closure_ptr; @@ -352,7 +369,7 @@ namespace jule } #ifdef OS_WINDOWS - void __closure_init() + void __closure_init(void) noexcept { SYSTEM_INFO si; GetNativeSystemInfo(&si); @@ -368,9 +385,10 @@ namespace jule jule::__closure_cap--; } #else - void __closure_init() + void __closure_init(void) noexcept { uint32_t page_size = sysconf(_SC_PAGESIZE); + // page_size must initialized with relevant expression before multiplication. page_size *= (((__JULE_ASSUMED_PAGE_SIZE - 1) / page_size) + 1); jule::__page_size = page_size; jule::__closure_alloc(); @@ -382,4 +400,4 @@ namespace jule jule::__closure_cap--; } #endif -} \ No newline at end of file +} // namespace jule \ No newline at end of file From 30605e88ede9ad7e372dbfb2453559a673d3aa24 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Fri, 30 Aug 2024 21:22:30 +0300 Subject: [PATCH 22/22] jule: completing implementation of new closures --- api/fn.hpp | 160 +++++------------------------------- src/julec/obj/cxx/type.jule | 2 +- 2 files changed, 21 insertions(+), 141 deletions(-) diff --git a/api/fn.hpp b/api/fn.hpp index 7ea5618f4..deff002e0 100644 --- a/api/fn.hpp +++ b/api/fn.hpp @@ -7,9 +7,15 @@ #include #include -#include #include +#ifdef OS_WINDOWS +#include +#else +#include +#include +#endif + #include "types.hpp" #include "error.hpp" #include "panic.hpp" @@ -20,134 +26,6 @@ #define __JULE_CO(EXPR) \ (__JULE_CO_SPAWN([=](void) mutable -> void { EXPR; }).detach()) -namespace jule -{ - - // std::function wrapper of JuleC. - template - struct Fn; - - template - jule::Uintptr addr_of_fn(std::function f) noexcept; - - template - struct Fn - { - public: - std::function buffer; - jule::Uintptr _addr; - - Fn(void) = default; - Fn(const Fn &fn) = default; - Fn(std::nullptr_t) : Fn() {} - - Fn(const std::function &function) noexcept - { - this->_addr = jule::addr_of_fn(function); - if (this->_addr == 0) - this->_addr = (jule::Uintptr)(&function); - this->buffer = function; - } - - Fn(const Function *function) noexcept - { - this->buffer = function; - this->_addr = jule::addr_of_fn(this->buffer); - if (this->_addr == 0) - this->_addr = (jule::Uintptr)(function); - } - - template - auto call( -#ifndef __JULE_ENABLE__PRODUCTION - const char *file, -#endif - Arguments... arguments) - { -#ifndef __JULE_DISABLE__SAFETY - if (this->buffer == nullptr) -#ifndef __JULE_ENABLE__PRODUCTION - jule::panic((std::string(__JULE_ERROR__INVALID_MEMORY) + "\nfile: ") + file); -#else - jule::panic(__JULE_ERROR__INVALID_MEMORY); -#endif // PRODUCTION -#endif // SAFETY - return this->buffer(arguments...); - } - - template - inline auto operator()(Arguments... arguments) - { -#ifndef __JULE_ENABLE__PRODUCTION - return this->call("/api/fn.hpp", arguments...); -#else - return this->call(arguments...); -#endif - } - - constexpr jule::Uintptr addr(void) const noexcept - { - return this->_addr; - } - - inline Fn &operator=(std::nullptr_t) noexcept - { - this->buffer = nullptr; - return *this; - } - - inline Fn &operator=(const std::function &function) - { - this->buffer = function; - return *this; - } - - inline Fn &operator=(const Function &function) - { - this->buffer = function; - return *this; - } - - constexpr jule::Bool operator==(std::nullptr_t) const noexcept - { - return this->buffer == nullptr; - } - - constexpr jule::Bool operator!=(std::nullptr_t) const noexcept - { - return !this->operator==(nullptr); - } - - friend std::ostream &operator<<(std::ostream &stream, - const Fn &src) noexcept - { - if (src == nullptr) - return (stream << ""); - return (stream << (void *)src._addr); - } - }; - - template - jule::Uintptr addr_of_fn(std::function f) noexcept - { - typedef T(FnType)(U...); - FnType **fn_ptr = f.template target(); - if (!fn_ptr) - return 0; - return (jule::Uintptr)(*fn_ptr); - } - -} // namespace jule - -#endif // ifndef __JULE_FN_HPP - -#ifdef OS_WINDOWS -#include -#else -#include -#include -#endif - #ifdef OS_WINDOWS #define __JULE_CLOSURE_MTX_INIT() InitializeSRWLock(&jule::__closure_mtx) #define __JULE_CLOSURE_MTX_LOCK() AcquireSRWLockExclusive(&jule::__closure_mtx) @@ -168,23 +46,23 @@ namespace jule { // std::function wrapper of JuleC. template - struct Fn2 + struct Fn { public: Ret (*f)(Args...); jule::Ptr ctx; // Closure ctx. void (*ctxHandler)(jule::Ptr &alloc) = nullptr; - Fn2(void) = default; - Fn2(const Fn2 &) = default; - Fn2(std::nullptr_t) : Fn2() {} + Fn(void) = default; + Fn(const Fn &) = default; + Fn(std::nullptr_t) : Fn() {} - Fn2(Ret (*f)(Args...)) noexcept + Fn(Ret (*f)(Args...)) noexcept { this->f = f; } - ~Fn2(void) noexcept + ~Fn(void) noexcept { this->f = nullptr; if (this->ctxHandler) @@ -223,7 +101,7 @@ namespace jule #endif } - inline Fn2 &operator=(std::nullptr_t) noexcept + inline Fn &operator=(std::nullptr_t) noexcept { this->f = nullptr; return *this; @@ -240,7 +118,7 @@ namespace jule } friend std::ostream &operator<<(std::ostream &stream, - const Fn2 &f) noexcept + const Fn &f) noexcept { if (f == nullptr) return (stream << ""); @@ -348,7 +226,7 @@ namespace jule } template - jule::Fn2 __new_closure(void *fn, jule::Ptr ctx, void (*ctxHandler)(jule::Ptr &)) noexcept + jule::Fn __new_closure(void *fn, jule::Ptr ctx, void (*ctxHandler)(jule::Ptr &)) noexcept { __JULE_CLOSURE_MTX_LOCK(); if (jule::__closure_cap < 1) @@ -361,7 +239,7 @@ namespace jule ptr[1] = fn; __JULE_CLOSURE_MTX_UNLOCK(); Ret (*static_closure)(Args...) = (Ret(*)(Args...))closure; - jule::Fn2 fn2(static_closure); + jule::Fn fn2(static_closure); fn2.ctx = std::move(ctx); fn2.ctxHandler = ctxHandler; ctx = nullptr; @@ -400,4 +278,6 @@ namespace jule jule::__closure_cap--; } #endif -} // namespace jule \ No newline at end of file +} // namespace jule + +#endif // ifndef __JULE_FN_HPP \ No newline at end of file diff --git a/src/julec/obj/cxx/type.jule b/src/julec/obj/cxx/type.jule index bbac740c3..6ff1c7b5e 100644 --- a/src/julec/obj/cxx/type.jule +++ b/src/julec/obj/cxx/type.jule @@ -57,7 +57,7 @@ impl typeCoder { const Slice = "jule::Slice" const Trait = "jule::Trait" const Array = "jule::Array" - const Fn = "jule::Fn2" + const Fn = "jule::Fn" const Bool = "jule::Bool" const Int = "jule::Int" const Uintptr = "jule::Uintptr"