diff --git a/src/astgen.cpp b/src/astgen.cpp index acee9f3..3787c54 100644 --- a/src/astgen.cpp +++ b/src/astgen.cpp @@ -2210,14 +2210,39 @@ static JirRef astgenAsCast(AstGenCtx &gctx, const AstNode &n) { return emit(gctx, inst); } // Pointer ↔ integer cast — only u64 is wide enough to round-trip - // a pointer on every supported target, so restrict to that width. - // `myPtr as u64` or `addr as *mut[] u8`. Stays out of the int↔int - // path below because that one issues SExt/Trunc instead. + // a pointer on every supported target, so restrict to that width + // for the ptr→int direction. `myPtr as u64` stays the canonical form. if (isPtr(src) && dst.kind == TypeKind::Int && dst.a == 64) { return emitCast(JirTag::PtrToInt); } - if (src.kind == TypeKind::Int && src.a == 64 && isPtr(dst)) { - return emitCast(JirTag::IntToPtr); + // Int → thin pointer (PtrSingle / PtrMany). Accepts any integer + // width — matches Rust's *addr-ptr-cast* (compiler/rustc_hir_typeck/ + // src/cast.rs:1101 check_addr_ptr_cast), which permits the cast as + // long as the target pointer is sized/thin. Narrower-than-pointer + // sources are zero/sign-extended to u64 first so LLVM's IntToPtr + // gets a pointer-width operand. The slice type ([]T, which carries + // a length field) is intentionally excluded by `isPtr` — round- + // tripping that needs an explicit (ptr, len) pair. + if (src.kind == TypeKind::Int && isPtr(dst)) { + JirRef widened = val; + if (src.a < 64) { + JirInst ext{}; + ext.tag = (src.b != 0) ? JirTag::SExt : JirTag::ZExt; + ext.a = val; + ext.ty = gctx.ctx.getTypePool().internInt(64, false); + widened = emit(gctx, ext); + } else if (src.a > 64) { + JirInst tr{}; + tr.tag = JirTag::Trunc; + tr.a = val; + tr.ty = gctx.ctx.getTypePool().internInt(64, false); + widened = emit(gctx, tr); + } + JirInst i2p{}; + i2p.tag = JirTag::IntToPtr; + i2p.a = widened; + i2p.ty = dstTy; + return emit(gctx, i2p); } if (src.kind == TypeKind::Int && dst.kind == TypeKind::Int) { uint32_t sw = src.a; diff --git a/src/codegen.cpp b/src/codegen.cpp index 7547a3c..6814477 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -484,9 +484,7 @@ JamCodegenContext::getFunctionAST(const std::string &name) const { auto nsIt = moduleNamespaces_.find(defMod); if (nsIt != moduleNamespaces_.end()) { auto fIt = nsIt->second.functions.find(name); - if (fIt != nsIt->second.functions.end()) { - return fIt->second; - } + if (fIt != nsIt->second.functions.end()) { return fIt->second; } } } } diff --git a/src/codegen.h b/src/codegen.h index b041882..1e4f908 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -504,11 +504,12 @@ class JamCodegenContext { // concrete generic args, creates a fresh LLVM struct type with a // synthesized name, and returns a Named TypeIdx pointing at it. // Memoizes by instantiated name. - TypeIdx instantiateStructExpr( - const AstNode &exprNode, const std::string &calleeName, - const std::vector &args, - const std::unordered_map &subst, - const std::string &definingModulePath) const; + TypeIdx + instantiateStructExpr(const AstNode &exprNode, + const std::string &calleeName, + const std::vector &args, + const std::unordered_map &subst, + const std::string &definingModulePath) const; // Mirror of instantiateStructExpr for `enum { ... }` expressions. // Substitutes each variant's payload types with the concrete diff --git a/src/main.cpp b/src/main.cpp index 84a795a..da01c2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -338,9 +338,13 @@ static int compileAndRun(const std::string &filename, JamCodegenContext::ModuleNamespace ns; ns.path = path; for (auto &func : importedModule->Functions) { + // All functions (pub + private) go into the per-module ns + // so a body in this module can resolve a sibling helper + // regardless of pub status. Only pub ones leak into the + // global flat map. + ns.functions[func->Name] = func.get(); if (func->isPub) { codegenCtx.registerFunctionAST(func->Name, func.get()); - ns.functions[func->Name] = func.get(); // Eagerly declare LLVM prototypes for pub-extern fns // (libc allocator, write, putchar, …). A generic // instantiation in Pass B may emit a Call to one of @@ -352,9 +356,9 @@ static int compileAndRun(const std::string &filename, // those to Pass B. if (func->isExtern && !func->isGeneric()) { JirFunction jfn = astgenMetadata(*func, codegenCtx); - jfn.name = mangledFunctionName( - *func, codegenCtx.getTypePool(), - codegenCtx.getStringPool()); + jfn.name = + mangledFunctionName(*func, codegenCtx.getTypePool(), + codegenCtx.getStringPool()); jirDeclarePrototype(jfn, codegenCtx); } } @@ -393,12 +397,15 @@ static int compileAndRun(const std::string &filename, // fallback, against the generic's defining-module namespace. for (const auto &[path, importedModule] : resolver.getLoadedModules()) { for (auto &func : importedModule->Functions) { - if (func->isPub && !func->isGeneric()) { - JirFunction jfn = astgenMetadata(*func, codegenCtx); - jfn.name = mangledFunctionName(*func, codegenCtx.getTypePool(), - codegenCtx.getStringPool()); - jirDeclarePrototype(jfn, codegenCtx); - } + if (func->isGeneric()) continue; + // Pub fns need prototypes so callers can call them. Private + // helpers also need them so the pub bodies above that + // reference them (via defining-module-scope lookup) resolve + // at LLVM codegen time. + JirFunction jfn = astgenMetadata(*func, codegenCtx); + jfn.name = mangledFunctionName(*func, codegenCtx.getTypePool(), + codegenCtx.getStringPool()); + jirDeclarePrototype(jfn, codegenCtx); } } // Register every flat `handle.X` mapping for a given (handle name, @@ -792,17 +799,26 @@ static int compileAndRun(const std::string &filename, } } for (const auto &[path, importedModule] : resolver.getLoadedModules()) { + // Push the defining module so the bodies of `pub` functions + // imported from it can resolve bare-name calls (e.g. a private + // helper called from a pub fn) against that module's namespace, + // not the caller's. Same mechanism the generic-instantiation + // path uses — see JamCodegenContext::pushBodyModule. + codegenCtx.pushBodyModule(path); for (auto &func : importedModule->Functions) { - if (func->isPub && !func->isGeneric()) { - try { - JirFunction jfn = astgenFunction(*func, codegenCtx); - jfn.name = - mangledFunctionName(*func, codegenCtx.getTypePool(), - codegenCtx.getStringPool()); - jirFunctions.push_back(std::move(jfn)); - } catch (const AstGenAnalysisFail &) { - // diagnostic already pushed - } + if (func->isGeneric()) continue; + if (func->isExtern) continue; + // Emit bodies for ALL non-extern fns in the imported + // module (pub + private). A pub fn that calls a private + // helper needs the helper's body emitted in the same + // compilation unit so LLVM has a definition to link. + try { + JirFunction jfn = astgenFunction(*func, codegenCtx); + jfn.name = mangledFunctionName(*func, codegenCtx.getTypePool(), + codegenCtx.getStringPool()); + jirFunctions.push_back(std::move(jfn)); + } catch (const AstGenAnalysisFail &) { + // diagnostic already pushed } } for (auto &s : importedModule->Structs) { @@ -818,6 +834,7 @@ static int compileAndRun(const std::string &filename, } } } + codegenCtx.popBodyModule(); } // Astgen accumulated every per-decl diagnostic onto the