@@ -1189,52 +1189,66 @@ let rec transformExpr (com: IBeamCompiler) (ctx: Context) (expr: Expr) : Beam.Er
11891189 Beam.ErlExpr.Literal( Beam.ErlLiteral.AtomLit( Beam.Atom " ok" ))
11901190
11911191 | Import( importInfo, typ, _ range) ->
1192- // Standalone import (function reference from another module, not a direct call).
1193- // Generate a lambda wrapper: fun(A1, ..., AN) -> module:function(A1, ..., AN) end
11941192 let moduleName = resolveImportModuleName com importInfo.Path
11951193
1196- let funcName = sanitizeErlangName importInfo.Selector
1197-
1198- // Count arity from the function type (follows all nested LambdaType levels)
1199- let rec functionArity ( t : Fable.AST.Fable.Type ) =
1200- match t with
1201- | Fable.AST.Fable.Type.LambdaType(_, returnType) -> 1 + functionArity returnType
1202- | Fable.AST.Fable.Type.DelegateType( argTypes, _) -> argTypes.Length
1203- | _ -> 0
1204-
1205- // Determine the actual Erlang arity of the imported function.
1206- // For MemberImport, use NonCurriedArgTypes which gives us the actual parameter count.
1207- // This is critical because functionArity(typ) follows the full curried type chain
1208- // (including return type nesting), which over-counts when the return type is itself
1209- // a function type (e.g., HttpHandler -> HttpHandler -> HttpHandler where HttpHandler
1210- // is a function type — functionArity returns 4 but actual Erlang arity is 2).
1211- let arity =
1212- match importInfo.Kind with
1213- | Fable.AST.Fable.MemberImport( Fable.AST.Fable.MemberRef(_, info)) ->
1214- match info.NonCurriedArgTypes with
1215- | Some argTypes -> argTypes.Length
1216- | None -> 0 // Value binding (no explicit params) → 0-arity in Erlang
1217- | _ -> functionArity typ
1218-
1219- if arity = 0 then
1220- // Not a function or value binding — call with no args to get the value
1221- Beam.ErlExpr.Call( moduleName, funcName, [])
1222- else
1223- // Generate lambda wrapper for the function reference
1224- let counter = com.IncrementCounter()
1225-
1226- let argNames = List.init arity ( fun i -> $" Import_arg_%d {i}_%d {counter}" )
1227- let argPats = argNames |> List.map Beam.PVar
1228- let argExprs = argNames |> List.map Beam.ErlExpr.Variable
1194+ if importInfo.Selector = " *" then
1195+ // ImportAll: represents a whole module (e.g., [<ImportAll("maps")>] let maps: IMaps = nativeOnly).
1196+ // When used as a value (assigned to variable), generate a tagged tuple
1197+ // {fable_import_all, module_atom} that fable_utils:iface_get recognizes
1198+ // and dispatches dynamically via erlang:make_fun/3.
1199+ let modAtom =
1200+ Beam.ErlExpr.Literal( Beam.ErlLiteral.AtomLit( Beam.Atom( moduleName |> Option.defaultValue " unknown" )))
12291201
1230- Beam.ErlExpr.Fun
1202+ Beam.ErlExpr.Tuple
12311203 [
1232- {
1233- Patterns = argPats
1234- Guard = []
1235- Body = [ Beam.ErlExpr.Call( moduleName, funcName, argExprs) ]
1236- }
1204+ Beam.ErlExpr.Literal( Beam.ErlLiteral.AtomLit( Beam.Atom " fable_import_all" ))
1205+ modAtom
12371206 ]
1207+ else
1208+ // Standalone import (function reference from another module, not a direct call).
1209+ // Generate a lambda wrapper: fun(A1, ..., AN) -> module:function(A1, ..., AN) end
1210+ let funcName = sanitizeErlangName importInfo.Selector
1211+
1212+ // Count arity from the function type (follows all nested LambdaType levels)
1213+ let rec functionArity ( t : Fable.AST.Fable.Type ) =
1214+ match t with
1215+ | Fable.AST.Fable.Type.LambdaType(_, returnType) -> 1 + functionArity returnType
1216+ | Fable.AST.Fable.Type.DelegateType( argTypes, _) -> argTypes.Length
1217+ | _ -> 0
1218+
1219+ // Determine the actual Erlang arity of the imported function.
1220+ // For MemberImport, use NonCurriedArgTypes which gives us the actual parameter count.
1221+ // This is critical because functionArity(typ) follows the full curried type chain
1222+ // (including return type nesting), which over-counts when the return type is itself
1223+ // a function type (e.g., HttpHandler -> HttpHandler -> HttpHandler where HttpHandler
1224+ // is a function type — functionArity returns 4 but actual Erlang arity is 2).
1225+ let arity =
1226+ match importInfo.Kind with
1227+ | Fable.AST.Fable.MemberImport( Fable.AST.Fable.MemberRef(_, info)) ->
1228+ match info.NonCurriedArgTypes with
1229+ | Some argTypes -> argTypes.Length
1230+ | None -> 0 // Value binding (no explicit params) → 0-arity in Erlang
1231+ | _ -> functionArity typ
1232+
1233+ if arity = 0 then
1234+ // Not a function or value binding — call with no args to get the value
1235+ Beam.ErlExpr.Call( moduleName, funcName, [])
1236+ else
1237+ // Generate lambda wrapper for the function reference
1238+ let counter = com.IncrementCounter()
1239+
1240+ let argNames = List.init arity ( fun i -> $" Import_arg_%d {i}_%d {counter}" )
1241+ let argPats = argNames |> List.map Beam.PVar
1242+ let argExprs = argNames |> List.map Beam.ErlExpr.Variable
1243+
1244+ Beam.ErlExpr.Fun
1245+ [
1246+ {
1247+ Patterns = argPats
1248+ Guard = []
1249+ Body = [ Beam.ErlExpr.Call( moduleName, funcName, argExprs) ]
1250+ }
1251+ ]
12381252
12391253and transformValue ( com : IBeamCompiler ) ( ctx : Context ) ( value : ValueKind ) : Beam.ErlExpr =
12401254 match value with
@@ -2313,6 +2327,18 @@ and transformCall (com: IBeamCompiler) (ctx: Context) (callee: Expr) (info: Call
23132327 | " toSeq" -> Beam.ErlExpr.Call( Some " maps" , " to_list" , cleanArgs)
23142328 | _ -> Beam.ErlExpr.Call( Some " maps" , sanitizeErlangName selector, cleanArgs)
23152329 |> wrapWithHoisted hoisted
2330+ | " *" ->
2331+ // ImportAll: selector is "*" meaning the callee is a whole-module import.
2332+ // Use MemberRef to get the actual function name being called.
2333+ match info.MemberRef with
2334+ | Some( Fable.AST.Fable.MemberRef(_, memberInfo)) ->
2335+ let funcName = sanitizeErlangName memberInfo.CompiledName
2336+ let args = info.Args |> List.map ( transformExpr com ctx)
2337+ let hoisted , cleanArgs = hoistBlocksFromArgs args
2338+
2339+ Beam.ErlExpr.Call( importModuleName, funcName, cleanArgs)
2340+ |> wrapWithHoisted hoisted
2341+ | _ -> failwith " ImportAll call without MemberRef — cannot resolve function name"
23162342 | selector ->
23172343 let args = info.Args |> List.map ( transformExpr com ctx)
23182344 let hoisted , cleanArgs = hoistBlocksFromArgs args
@@ -2436,12 +2462,14 @@ and transformCall (com: IBeamCompiler) (ctx: Context) (callee: Expr) (info: Call
24362462 let allHoisted = calleeHoisted @ argsHoisted
24372463
24382464 if isInterfaceExpr then
2439- // Interface method dispatch: (fable_utils:iface_get(method_name, Obj))(Args)
2440- // Works for both object expressions (maps) and class instances (refs)
2465+ // Interface method dispatch: (fable_utils:iface_get(method_name, arity, Obj))(Args)
2466+ // Works for object expressions (maps), class instances (refs), and ImportAll modules.
2467+ // Passing arity resolves ambiguity when a module exports the same name at multiple arities.
24412468 let methodAtom = atomLit ( sanitizeErlangName fieldInfo.Name)
2469+ let arityLit = Beam.ErlExpr.Literal( Beam.ErlLiteral.Integer( int64 cleanArgs.Length))
24422470
24432471 let lookup =
2444- Beam.ErlExpr.Call( Some " fable_utils" , " iface_get" , [ methodAtom; cleanCallee ])
2472+ Beam.ErlExpr.Call( Some " fable_utils" , " iface_get" , [ methodAtom; arityLit ; cleanCallee ])
24452473
24462474 Beam.ErlExpr.Apply( lookup, cleanArgs) |> wrapWithHoisted allHoisted
24472475 else
0 commit comments