@@ -27,6 +27,14 @@ type context = {
2727 globals : global list ; (* * global variables *)
2828 locals : (string * int ) list ; (* * local variable name to index map *)
2929 next_local : int ; (* * next available local index *)
30+ local_types : (int * value_type ) list ;
31+ (* * Float wall: local index -> wasm value type, for indices whose value is
32+ f64 (Float params and Float-bound `let`s). Indices absent here default to
33+ i32. Drives the typed [f_locals] emission so a float bound to a local is
34+ declared f64 rather than failing wasm validation. *)
35+ fn_ret_types : (string * value_type ) list ;
36+ (* * Float wall: function name -> wasm result value type, so a call site
37+ `let x = f(...)` can infer whether x is f64. *)
3038 loop_depth : int ; (* * current loop nesting depth *)
3139 func_indices : (string * int ) list ;
3240 (* * Top-level name environment shared by functions and constants.
@@ -95,6 +103,8 @@ let create_context () : context = {
95103 globals = [] ;
96104 locals = [] ;
97105 next_local = 0 ;
106+ local_types = [] ;
107+ fn_ret_types = [] ;
98108 loop_depth = 0 ;
99109 func_indices = [] ;
100110 lambda_funcs = [] ;
@@ -418,6 +428,41 @@ let gen_literal (ctx : context) (lit : literal) : (context * instr) result =
418428 Ok (ctx', I32Const (Int32. of_int offset))
419429 end
420430
431+ (* * Float wall: structurally infer the wasm value type an expression yields,
432+ AFTER elaboration (Float ops are [ExprFloatBinary]). Used to decide whether
433+ a `let`-bound local must be declared f64. Conservative — anything not seen
434+ to be f64 defaults to i32, matching the pre-float-wall world. *)
435+ let rec expr_val_type (ctx : context ) (e : expr ) : value_type =
436+ match e with
437+ | ExprLit (LitFloat _ ) -> F64
438+ | ExprFloatBinary (_ , (OpAdd | OpSub | OpMul | OpDiv ), _ ) -> F64
439+ | ExprFloatBinary (_ , _ , _ ) -> I32 (* comparisons yield Bool/i32 *)
440+ | ExprUnary (OpNeg, e1 ) -> expr_val_type ctx e1
441+ | ExprSpan (e1 , _ ) -> expr_val_type ctx e1
442+ | ExprVar id ->
443+ (match lookup_local ctx id.name with
444+ | Ok idx ->
445+ (match List. assoc_opt idx ctx.local_types with Some t -> t | None -> I32 )
446+ | Error _ -> I32 )
447+ | ExprApp (ExprVar id , _ ) ->
448+ (match List. assoc_opt id.name ctx.fn_ret_types with Some t -> t | None -> I32 )
449+ | ExprIf r -> expr_val_type ctx r.ei_then
450+ | ExprBlock blk ->
451+ (match blk.blk_expr with Some e1 -> expr_val_type ctx e1 | None -> I32 )
452+ | ExprLet lb ->
453+ (match lb.el_body with Some e1 -> expr_val_type ctx e1 | None -> I32 )
454+ | _ -> I32
455+
456+ (* * Float wall: run-length-encode local value types (in index order) into the
457+ wasm [local] group list (default i32). *)
458+ let rle_locals (vts : value_type list ) : local list =
459+ List. fold_right (fun vt acc ->
460+ match acc with
461+ | { l_count; l_type } :: rest when l_type = vt ->
462+ { l_count = l_count + 1 ; l_type = vt } :: rest
463+ | _ -> { l_count = 1 ; l_type = vt } :: acc)
464+ vts []
465+
421466(* * Generate code for binary operation *)
422467let gen_binop (op : binary_op ) : instr =
423468 match op with
@@ -731,6 +776,25 @@ let rec gen_expr (ctx : context) (expr : expr) : (context * instr list) result =
731776 in
732777 Ok (ctxB, code)
733778
779+ | ExprFloatBinary (left , op , right ) ->
780+ (* Float wall: lower a Float binop to the f64 instruction family. Arith
781+ (+,-,*,/) yields an f64 value; the six comparisons yield an i32 bool.
782+ Operands are f64 (float literals -> F64Const, Float params -> f64 param
783+ slots, nested ExprFloatBinary arith -> f64), so this validates. *)
784+ let * (ctx1, left_code) = gen_expr ctx left in
785+ let * (ctx2, right_code) = gen_expr ctx1 right in
786+ let * f64_op = match op with
787+ | OpAdd -> Ok F64Add | OpSub -> Ok F64Sub
788+ | OpMul -> Ok F64Mul | OpDiv -> Ok F64Div
789+ | OpLt -> Ok F64Lt | OpLe -> Ok F64Le
790+ | OpGt -> Ok F64Gt | OpGe -> Ok F64Ge
791+ | OpEq -> Ok F64Eq | OpNe -> Ok F64Ne
792+ | _ -> Error (UnsupportedFeature
793+ " Float wall: only + - * / and the six comparisons lower to f64; \
794+ Float OpMod / bitwise are unsupported" )
795+ in
796+ Ok (ctx2, left_code @ right_code @ [f64_op])
797+
734798 | ExprBinary (left , op , right ) ->
735799 let * (ctx', left_code) = gen_expr ctx left in
736800 let * (ctx'', right_code) = gen_expr ctx' right in
@@ -805,6 +869,15 @@ let rec gen_expr (ctx : context) (expr : expr) : (context * instr list) result =
805869 | ExprLet lb ->
806870 let * (ctx', rhs_code) = gen_expr ctx lb.el_value in
807871 let * (ctx'', pat_code) = gen_pattern_bind ctx' lb.el_pat in
872+ (* Float wall: record a float-bound local's f64 type (see StmtLet). *)
873+ let ctx'' = match lb.el_pat with
874+ | PatVar id ->
875+ (match lookup_local ctx'' id.name with
876+ | Ok idx when expr_val_type ctx'' lb.el_value = F64 ->
877+ { ctx'' with local_types = (idx, F64 ) :: ctx''.local_types }
878+ | _ -> ctx'')
879+ | _ -> ctx''
880+ in
808881 begin match lb.el_body with
809882 | Some body ->
810883 let * (ctx_final, body_code) = gen_expr ctx'' body in
@@ -2362,6 +2435,12 @@ and gen_stmt (ctx : context) (stmt : stmt) : (context * instr list) result =
23622435 | None -> layout_from_rhs ()
23632436 in
23642437 let (ctx'', idx) = alloc_local ctx' id.name in
2438+ (* Float wall: a float-bound local must be declared f64 (else a LocalSet
2439+ of an f64 into an i32 slot fails wasm validation). *)
2440+ let ctx'' = match expr_val_type ctx'' sl.sl_value with
2441+ | F64 -> { ctx'' with local_types = (idx, F64 ) :: ctx''.local_types }
2442+ | _ -> ctx''
2443+ in
23652444 let ctx_with_layout = match layout_opt with
23662445 | Some layout ->
23672446 { ctx'' with field_layouts = (id.name, layout) :: ctx''.field_layouts }
@@ -2871,14 +2950,24 @@ let () =
28712950
28722951let gen_function (ctx : context ) (fd : fn_decl ) : (context * func) result =
28732952 (* Create fresh context for function scope, but preserve lambda_funcs and next_lambda_id *)
2874- let fn_ctx = { ctx with locals = [] ; next_local = 0 ; loop_depth = 0 } in
2953+ (* Float wall: reset [local_types] too — local indices restart at 0 per
2954+ function, so a previous function's f64-local entries must not leak (else a
2955+ later function's i32 local at the same index is mis-declared f64). Keep
2956+ [fn_ret_types]: it is module-level (cross-function call inference). *)
2957+ let fn_ctx = { ctx with locals = [] ; next_local = 0 ; loop_depth = 0 ;
2958+ local_types = [] } in
28752959
28762960 (* Parameters become locals 0..n-1. When a parameter's declared type
28772961 names a known struct, register its field layout under the parameter
28782962 name so body-side `.field_N` reads resolve to the correct offset
28792963 rather than defaulting to 0. *)
28802964 let (ctx_with_params, _) = List. fold_left (fun (c , _ ) param ->
28812965 let (c', idx) = alloc_local c param.p_name.name in
2966+ (* Float wall: record the param's wasm value type (Float -> F64) so body
2967+ val-type inference and the f_locals emission see f64 params. *)
2968+ let c' = { c' with local_types =
2969+ (idx, (match type_to_wasm param.p_ty with Ok t -> t | Error _ -> I32 ))
2970+ :: c'.local_types } in
28822971 let c'' =
28832972 match struct_name_of_ty param.p_ty with
28842973 | Some sname ->
@@ -2929,11 +3018,13 @@ let gen_function (ctx : context) (fd : fn_decl) : (context * func) result =
29293018
29303019 (* Compute additional locals (beyond parameters) *)
29313020 let local_count = ctx_final.next_local - param_count in
2932- let locals = if local_count > 0 then
2933- [{ l_count = local_count; l_type = I32 }]
2934- else
2935- []
3021+ (* Float wall: declare each non-param local with its tracked value type
3022+ (default i32), so a float-bound local is f64; RLE into local groups. *)
3023+ let local_vts = List. init (max 0 local_count) (fun i ->
3024+ let idx = param_count + i in
3025+ match List. assoc_opt idx ctx_final.local_types with Some t -> t | None -> I32 )
29363026 in
3027+ let locals = rle_locals local_vts in
29373028
29383029 (* Create function (type index will be set by gen_decl) *)
29393030 let func = {
@@ -2955,13 +3046,14 @@ let intern_func_type (types : func_type list) (ft : func_type) : int * func_type
29553046 in
29563047 find_idx 0 types
29573048
2958- (* * Build a WASM-side [func_type] for a top-level function declaration, mirroring
2959- the convention used by [gen_decl TopFn]: every param is i32, the result is
2960- [i32]. Used both for local fn types and for imported fn types so that calls
2961- through either path agree on signature. *)
3049+ (* * Build a WASM-side [func_type] for a top-level function declaration. Params
3050+ and result follow the AffineScript type via [type_to_wasm] (Float wall:
3051+ `Float` -> F64, everything else -> i32). Used by [gen_decl TopFn] AND by
3052+ call/import sites so every path agrees on the signature. *)
29623053let func_type_of_fn_decl (fd : fn_decl ) : func_type =
2963- let params = List. map (fun _ -> I32 ) fd.fd_params in
2964- let results = [I32 ] in
3054+ let vt ty = match type_to_wasm ty with Ok t -> t | Error _ -> I32 in
3055+ let params = List. map (fun p -> vt p.p_ty) fd.fd_params in
3056+ let results = match fd.fd_ret_ty with Some ty -> [vt ty] | None -> [I32 ] in
29653057 { ft_params = params; ft_results = results }
29663058
29673059let gen_decl (ctx : context ) (decl : top_level ) : context result =
@@ -2992,9 +3084,9 @@ let gen_decl (ctx : context) (decl : top_level) : context result =
29923084
29933085 | TopFn fd ->
29943086 (* Create function type *)
2995- let param_types = List. map ( fun _ -> I32 ) fd.fd_params in
2996- let result_type = [ I32 ] in
2997- let func_type = { ft_params = param_types; ft_results = result_type } in
3087+ (* Float wall: params/results follow the AS type (Float -> F64) via the
3088+ shared builder, so the definition signature matches call/import sites. *)
3089+ let func_type = func_type_of_fn_decl fd in
29983090
29993091 (* Add type to types list *)
30003092 let type_idx = List. length ctx.types in
@@ -3022,6 +3114,10 @@ let gen_decl (ctx : context) (decl : top_level) : context result =
30223114 func_indices = ctx_with_type.func_indices @ [(fd.fd_name.name, func_idx)];
30233115 ownership_annots = ctx_with_type.ownership_annots @ [(func_idx, param_kinds, ret_kind)];
30243116 fn_ret_structs = fn_ret_structs';
3117+ (* Float wall: record this fn's result value type for call-site inference. *)
3118+ fn_ret_types = (fd.fd_name.name,
3119+ (match func_type.ft_results with t :: _ -> t | [] -> I32 ))
3120+ :: ctx_with_type.fn_ret_types;
30253121 } in
30263122
30273123 (* Generate function with correct type index *)
0 commit comments