123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450openAstopenInfer(* A member-completion candidate for [recv.<here>] or [ns::<here>]: a struct
field, a value method, or a namespace free function, its [member_kind]
driving the editor's icon and [member_detail] a rendered type/signature —
the field's declared type or the method/function's signature. *)typemember_kind=Field|Method|Functiontypemember_candidate={member_name:string;member_kind:member_kind;member_detail:string;}(* What a member access [recv.<here>]'s completion candidates are derived from.
The typer records this lightweight descriptor (a kind and the receiver's
type), and {!member_candidates} turns it into the candidate list on demand —
so the list, which for a v128 or memory receiver is large, is built only for
the access under the cursor, not at every access in the file. *)typemember_receiver=|R_numericofinferred_type(** a value receiver: its integer / float / v128 methods *)|R_structoffieldtypeAst.annotated_array(** the struct's fields *)|R_arrayoffieldtype(** by element type: [length]/[fill]/[copy]/[init] *)|R_memoryof[`I32|`I64](** by address type *)|R_tableof[`I32|`I64]*reftype(** by address and element type *)|R_contofmember_candidatelist(** a continuation-typed receiver: the resume family and [switch],
prebuilt (their signatures need the type context) *)(* What a value method's result type is relative to its receiver: [Same] as the
receiver, or the equal-width opposite numeric family ([i32]<->[f32],
[i64]<->[f64]), as [from_bits] / [to_bits] reinterpret. *)typemethod_result=Same|Reinterprettypevalue_method={vm_name:string;vm_binary:bool;(** takes a second operand of the receiver's type *)vm_result:method_result;}letmeth?(binary=false)?(result=Same)vm_name={vm_name;vm_binary=binary;vm_result=result}(* The value methods offered by member completion for an integer / float
receiver. A curated registry: the method dispatch (see
[type_unary_intrinsic_call] / [type_binary_intrinsic_call]) is match-based
and cannot be enumerated, so the test in test/method-consistency type-checks
each of these — arity and result type included — to keep the registry in step
with what the typer actually accepts. Vector ([v128]) and memory / table
methods (a different dispatch path) are not covered yet. *)letinteger_methods=[meth"clz";meth"ctz";meth"popcnt";meth"extend8_s";meth"extend16_s";meth~result:Reinterpret"from_bits";meth~binary:true"rotl";meth~binary:true"rotr";]letfloat_methods=[meth"abs";meth"ceil";meth"floor";meth"trunc";meth"nearest";meth"sqrt";meth~result:Reinterpret"to_bits";meth~binary:true"min";meth~binary:true"max";meth~binary:true"copysign";]letnumtype_name:Ast.valtype->string=function|I32->"i32"|I64->"i64"|F32->"f32"|F64->"f64"|V128->"v128"|Ref_->"ref"(* The member-completion candidates for [methods] on a numeric receiver
rendered as [recv_name] (a concrete [i32] or a flexible-literal family like
[int]), with a real signature ([fn() -> i32], [fn(f32) -> f32]).
[reinterp_name] is the result type of a bit-reinterpreting method
([from_bits]/[to_bits]) — the opposite family, which for a flexible receiver
is rendered by family name too. *)letmethod_candidates~recv_name~reinterp_namemethods=List.map(funm->letparams=ifm.vm_binarythenrecv_nameelse""inletresult=matchm.vm_resultwith|Same->recv_name|Reinterpret->reinterp_namein{member_name=m.vm_name;member_kind=Method;member_detail=Printf.sprintf"fn(%s) -> %s"paramsresult;})methods(* A struct field's declared type, rendered for the member-completion detail
(e.g. [i32], [mut i32], [&point]) as it reads in a type definition. [Output]
here is [Infer.Output] (open Infer), whose printers return trimmed strings. *)letrender_fieldtype(f:Ast.fieldtype)=Output.fieldtype_stringf(* A reference type rendered as it reads in source (e.g. [&func], [&?extern]),
for a table's element type in the member-completion detail. *)letrender_reftype(rt:Ast.reftype)=Output.valtype_string(Ast.Refrt)(* The member candidates for a struct's [fields] (each name and declared type),
for member completion. *)letstruct_candidatesfields=Array.to_listfields|>List.map(funf->letnm,typ=f.Annot.descin{member_name=nm.Annot.desc;member_kind=Field;member_detail=render_fieldtypetyp;})(* Expected operand/result type of a SIMD intrinsic, as a fresh type cell. *)letsimd_valtype:Simd.ty->inferred_valtype=function|TV128->{typ=V128;internal=V128;anon_comptype=None}|TI32->i32_valtype|TI64->i64_valtype|TF32->f32_valtype|TF64->f64_valtypeletsimd_ty_namet=numtype_name(simd_valtypet).typ(* The member-completion candidate for a SIMD method [name] (e.g. [add_i32x4]),
its signature read straight from the registry the typer dispatches through
([Simd.classify]): the leading constant lane immediates, then the
non-receiver stack operands, then the result. *)letsimd_method_candidatename=letdetail=matchSimd.classifynamewith|Some{operands=_receiver::rest;result;imm;_}->letimm_params=matchimmwith|Simd.No_imm->[]|Lane_->["lane index"]|Shuffle->["16 lane indices"]inletparams=imm_params@List.mapsimd_ty_namerestinletresult=matchresultwithSomet->simd_ty_namet|None->"()"inPrintf.sprintf"fn(%s) -> %s"(String.concat", "params)result|_->""in{member_name=name;member_kind=Method;member_detail=detail}(* The value methods offered by member completion for a [v128] receiver — the
vector ops [v.add_i32x4(w)], enumerated from the SIMD registry (so, unlike
the scalar registries above, no drift is possible: the same table classifies
the call). *)letsimd_v128_methods()=List.mapsimd_method_candidate(Simd.method_namesSimd.TV128)(* The value-method candidates member completion offers for a numeric receiver
of inferred type [t], or [None] if it has none. Beyond the concrete numeric
valtypes ([i32] … [f64], [v128]), a receiver can still be a flexible literal
type: an [int] takes its integer methods only, a [number] or [large number]
both families (either narrowing is still open), a [float] its float methods
only. A packed [i8]/[i16] read must be cast before any method, so gets none.
The [from_bits]/[to_bits] reinterpretation flips the family, rendered by
family name for a flexible receiver since the width is uncommitted. *)letnumeric_receiver_candidates(t:inferred_type):member_candidatelistoption=letints~recv_name~reinterp=method_candidates~recv_name~reinterp_name:reinterpinteger_methodsinletfloats~recv_name~reinterp=method_candidates~recv_name~reinterp_name:reinterpfloat_methodsinmatchtwith|Valtype{typ=I32;_}->Some(ints~recv_name:"i32"~reinterp:"f32")|Valtype{typ=I64;_}->Some(ints~recv_name:"i64"~reinterp:"f64")|Valtype{typ=F32;_}->Some(floats~recv_name:"f32"~reinterp:"i32")|Valtype{typ=F64;_}->Some(floats~recv_name:"f64"~reinterp:"i64")|Valtype{typ=V128;_}->Some(simd_v128_methods())|Int->Some(ints~recv_name:"int"~reinterp:"float")|Number->Some(ints~recv_name:"number"~reinterp:"float"@floats~recv_name:"number"~reinterp:"int")|LargeInt->Some(ints~recv_name:"large number"~reinterp:"float"@floats~recv_name:"large number"~reinterp:"int")|Float->Some(floats~recv_name:"float"~reinterp:"int")|_->None(* Whether a value receiver of type [t] has value methods, as an [R_numeric]
descriptor — the cheap classification the recorder uses to decide whether to
record, without building the (possibly large) candidate list. Its domain must
match [numeric_receiver_candidates] returning [Some]. *)letnumeric_receiver_kind(t:inferred_type):member_receiveroption=matchtwith|Valtype{typ=I32|I64|F32|F64|V128;_}|Int|Number|LargeInt|Float->Some(R_numerict)|_->Noneletaddress_type_name:[`I32|`I64]->string=function|`I32->"i32"|`I64->"i64"(* [fn(<params>) -> <result>], with an empty result rendered [()] and several
as a tuple. *)letrender_signatureparamsresult=letresult=matchresultwith|[]->"()"|[r]->r|rs->"("^String.concat", "rs^")"inPrintf.sprintf"fn(%s) -> %s"(String.concat", "params)result(* The methods member completion offers on a continuation-typed receiver — the
resume family and [switch] — with [params]/[results] the rendered parameter
and result types of the continuation's function type and [switch_results]
the rendered results of a [switch] (the last parameter's own continuation
parameters, when it has one). Unlike the other receivers, the candidate
list is built at record time (the signatures need the type context) and
carried by {!R_cont}; the editor's signature help rebuilds it from the
declarations. *)letcont_method_candidates~params~results~switch_results=letmmember_namemember_detail={member_name;member_kind=Method;member_detail}inletleading=List.filteri(funi_->i<List.lengthparams-1)paramsin[m"resume"(render_signatureparamsresults);m"resume_throw"(render_signature["tag(payload)"]results);m"resume_throw_ref"(render_signature["&?exn"]results);m"switch"(render_signature(leading@["tag: tag"])switch_results);](* The atomic memory accesses ([mem.atomic_load32(addr)],
[mem.atomic_rmw_add8(addr, v)], …), enumerated from the
{!Wax_wasm.Atomics.families} the typer dispatches on; the address takes
[addr_name]. The name carries the access width only: a narrow load returns
the raw-bits [i8]/[i16] (resolved by a surrounding [as iN_u] cast) and a
narrow store/RMW value picks the i32/i64 family by its type (rendered
[int]); the 64-bit accesses are necessarily [i64]. *)letatomic_method_candidates~addr_name=letvalue:Wax_wasm.Atomics.width->string=function|`W8|`W16|`W32->"int"|`W64->"i64"inletload_result:Wax_wasm.Atomics.width->string=function|`W8->"i8"|`W16->"i16"|`W32->"i32"|`W64->"i64"inList.map(funf->letoperands,results=match(f:Wax_wasm.Atomics.family)with|Loadw->([],[load_resultw])|Storew->([valuew],[])|Rmw(Wax_wasm.Ast.AtomicCmpxchg,w)->([valuew;valuew],[valuew])|Rmw(_,w)->([valuew],[valuew])|Wait`I32->(["i32";"i64"],["i32"])|Wait`I64->(["i64";"i64"],["i32"])|Notify->(["i32"],["i32"])in{member_name=Wax_wasm.Atomics.method_namef;member_kind=Method;member_detail=render_signature((addr_name::operands)@["offset?: int"])results;})Wax_wasm.Atomics.families(* The SIMD memory accesses ([mem.loadv128(addr)],
[mem.load8_lane(addr, v, lane)], …), enumerated from
{!Wax_wasm.Simd.mem_method_names}; the first operand is the address. *)letsimd_mem_method_candidates~addr_name=List.map(funname->letmi:Simd.mem_intrinsic=Option.get(Simd.mem_methodname)inletrest=matchmi.m_operandswith|_addr::r->List.mapsimd_ty_namer|[]->[]inletparams=(addr_name::rest)@(ifmi.m_lanethen["lane: int"]else[])@["offset?: int";"align?: int"]in{member_name=name;member_kind=Method;member_detail=render_signatureparams(matchmi.m_resultwithSomet->[simd_ty_namet]|None->[]);})Simd.mem_method_names(* The value methods member completion offers on a memory receiver
[mem.load8(addr)], with [addr_name] the memory's address type: the scalar
loads/stores (with their optional labelled [offset]/[align] immediates),
the size/grow/fill/copy/init management ops, and the atomic and SIMD memory
accesses. *)letmemory_method_candidates~addr_name=letmmember_namemember_detail={member_name;member_kind=Method;member_detail}inletloadnamer=mname(Printf.sprintf"fn(%s, offset?: int, align?: int) -> %s"addr_namer)inletstorenamev=mname(Printf.sprintf"fn(%s, %s, offset?: int, align?: int) -> ()"addr_namev)in[load"load8""i32";load"load16""i32";load"load32""i32";load"load64""i64";load"loadf32""f32";load"loadf64""f64";store"store8""i32";store"store16""i32";store"store32""i32";store"store64""i64";store"storef32""f32";store"storef64""f64";m"size"(Printf.sprintf"fn() -> %s"addr_name);m"grow"(Printf.sprintf"fn(%s) -> %s"addr_nameaddr_name);m"fill"(Printf.sprintf"fn(%s, i32, %s) -> ()"addr_nameaddr_name);m"copy"(Printf.sprintf"fn(%s, %s, %s) -> ()"addr_nameaddr_nameaddr_name);m"init"(Printf.sprintf"fn(data, %s, i32, i32) -> ()"addr_name);]@atomic_method_candidates~addr_name@simd_mem_method_candidates~addr_name(* The value methods member completion offers on a table receiver [tab.size()],
with [addr_name] the table's address type and [elem_name] its element type:
the size/grow/fill/copy/init management ops. Element access is [tab[i]], not
a method. *)lettable_method_candidates~addr_name~elem_name=letmmember_namemember_detail={member_name;member_kind=Method;member_detail}in[m"size"(Printf.sprintf"fn() -> %s"addr_name);m"grow"(Printf.sprintf"fn(%s, %s) -> %s"elem_nameaddr_nameaddr_name);m"fill"(Printf.sprintf"fn(%s, %s, %s) -> ()"addr_nameelem_nameaddr_name);m"copy"(Printf.sprintf"fn(%s, %s, %s) -> ()"addr_nameaddr_nameaddr_name);m"init"(Printf.sprintf"fn(elem, %s, i32, i32) -> ()"addr_name);](* The methods member completion offers on an array receiver [a.length()] with
element [elem]: [length], and the [fill]/[copy]/[init] bulk operations (the
last from a data / element segment). Indices and counts are [i32]; [fill]'s
value and [copy]'s source array are the element type. *)letarray_method_candidateselem=letmmember_namemember_detail={member_name;member_kind=Method;member_detail}inletvalue=render_fieldtype{elemwithAst.mut=false}inletarr="&["^render_fieldtypeelem^"]"in[m"length""fn() -> i32";m"fill"(Printf.sprintf"fn(i32, %s, i32) -> ()"value);m"copy"(Printf.sprintf"fn(i32, %s, i32, i32) -> ()"arr);m"init"(Printf.sprintf"fn(seg, i32, i32, i32) -> ()");](* The member-completion candidates a recorded {!member_receiver} stands for,
derived on demand (the editor forces only the one under the cursor). *)letmember_candidates:member_receiver->member_candidatelist=function|R_numerict->Option.value~default:[](numeric_receiver_candidatest)|R_structfields->struct_candidatesfields|R_arrayelem->array_method_candidateselem|R_memoryat->memory_method_candidates~addr_name:(address_type_nameat)|R_table(at,rt)->table_method_candidates~addr_name:(address_type_nameat)~elem_name:(render_reftypert)|R_contl->l(* Free-function members offered after [v128::] — [bitselect] and the per-shape
const constructors — with signatures from the SIMD registry. *)letsimd_free_members()=List.map(funname->letfull=Simd.free_fullnameinletdetail=matchSimd.const_shape_of_namefullwith|Someshape->Printf.sprintf"fn(%d lanes) -> v128"(Simd.const_arityshape)|None->(matchSimd.classifyfullwith|Some{operands;result;_}->Printf.sprintf"fn(%s) -> %s"(String.concat", "(List.mapsimd_ty_nameoperands))(matchresultwithSomet->simd_ty_namet|None->"()")|None->"")in{member_name=name;member_kind=Function;member_detail=detail})Simd.free_member_names(* The free functions offered by completion after an intrinsic namespace path
[ns::]: [v128::] holds the SIMD const constructors and [bitselect], [i64::]
the wide-arithmetic ops, [atomic::] the memory fence. Mirrors the dispatch in
[type_path_intrinsic_call] / [type_wide_arith_call] (test/method-consistency
type-checks each offered call). Empty for an unknown namespace. *)letnamespace_membersns:member_candidatelist=letfnmember_namemember_detail={member_name;member_kind=Function;member_detail}inletwide="fn(i64, i64, i64, i64) -> (i64, i64)"inletmul="fn(i64, i64) -> (i64, i64)"inmatchnswith|"v128"->simd_free_members()|"i64"->[fn"add128"wide;fn"sub128"wide;fn"mul_wide_s"mul;fn"mul_wide_u"mul;]|"atomic"->[fn"fence""fn() -> ()"]|_->[](* The intrinsic namespace names ([v128], [i64], [atomic]), for completion of
the [ns] before [::]. Exactly the namespaces {!namespace_members} answers. *)letintrinsic_namespaces=["v128";"i64";"atomic"]