diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 121aa5f..6350d57 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -188,6 +188,26 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): # build the reference from gv.Name (full name, matching the definition when SHA1-prefixed), # avoiding an undefined symbol for a bare @_mbuddy under per-module prefixed definitions return self.make_global_ref(pool, gv.Name, gv.Ty) + # A module function used as a value (`return serial.puts`, `cb = serial.puts`): the symbol + # is already declared in this module as `llvm.func @.puts`, so manufacturing an + # external global under the same name makes mlir-opt reject the module with "redefinition + # of symbol named '.puts'". Hand back the function symbol itself instead: a Value + # whose text is the reference `@` typed Ptr(Func), the same shape the same-module + # `&f` branch (ExprGenOps.translate_unaryop) produces -- the caller must not load it. + if gv_q is not None: + fn_ma: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, at.attr) + if fn_ma is not None: + fname_ma: str = mlirlite.function_get_name(fn_ma) + # exact match only: a bare suffix hit could be another module's same-name function + if fname_ma is not None and string.strcmp(fname_ma, gv_q) == 0: + fn_ty_ma: mlirlite.LLVMType* = self.Gen.CallGenBase._FuncTyFromFunction(pool, fn_ma) + if fn_ty_ma is not None: + fnlen_ma: sizet = string.strlen(fname_ma) + fbuf_ma: str = pool.alloc(fnlen_ma + 4) + if fbuf_ma is not None: + if self.Gen.ClassGenVTable._vtable_name_needs_quote(fname_ma) != 0: viperlib.snprintf(fbuf_ma, fnlen_ma + 4, "@\"%s\"", fname_ma) + else: viperlib.snprintf(fbuf_ma, fnlen_ma + 4, "@%s", fname_ma) + return mlirlite.SSAValue(pool, mlirlite.Ptr(pool, fn_ty_ma), fbuf_ma) # not found: create an external global declaration and add it to the module; otherwise only # an @_argc reference is emitted with no declaration (LLVM: "use of undefined value"). use_ty: mlirlite.LLVMType* = ty_hint @@ -469,10 +489,15 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if mod_gv_ref is not None and mod_gv_ref.Ty is not None: load_ty_mod: mlirlite.LLVMType* = mod_gv_ref.Ty.Pointee if load_ty_mod is not None: - # module-level global array (e.g. zdef.zdeflate_len_base: CArray[T, N]): - # decay to the base address per C semantics; loading [N x T] cannot be GEP base. + # A module function used as a value is already the address (Ptr(Func), + # the symbol reference itself): loading it would read the function body, + # so return the reference unchanged. match load_ty_mod: + case mlirlite.LLVMType.Func(_, _, _): + return mod_gv_ref case mlirlite.LLVMType.Array(_, _): + # module-level global array (e.g. zdef.zdeflate_len_base: CArray[T, N]): + # decay to the base address per C semantics; loading [N x T] cannot be GEP base. return mod_gv_ref case _: pass