diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index d5c198456fb..4d9bbb39e87 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -5497,13 +5497,12 @@ end (* Var *) (* FIX ME: calling into the prelude will not work if we ever need to compile a program that requires top-level cps conversion; use new prims instead *) -module Prelude = struct +module Internals = struct let call_prelude_function env ae var = - match Var.get_val env ae var with - | (SR.Const(_, Const.Fun mk_fi), code) -> - code ^^ - compile_unboxed_zero ^^ (* A dummy closure *) - G.i (Call (nr (mk_fi ()))) + match VarEnv.lookup_var ae var with + | Some (VarEnv.Const(_, Const.Fun mk_fi)) -> + compile_unboxed_zero ^^ (* A dummy closure *) + G.i (Call (nr (mk_fi ()))) | _ -> assert false let add_cycles env ae = call_prelude_function env ae "@add_cycles" @@ -5560,8 +5559,8 @@ module FuncDec = struct Func.of_body outer_env [] [] (fun env -> G.with_region at ( message_start env sort ^^ (* cycles *) - Prelude.reset_cycles env outer_ae ^^ - Prelude.reset_refund env outer_ae ^^ + Internals.reset_cycles env outer_ae ^^ + Internals.reset_refund env outer_ae ^^ (* reply early for a oneway *) (if control = Type.Returns then @@ -6818,7 +6817,7 @@ and compile_exp (env : E.t) ae exp = let (set_meth_pair, get_meth_pair) = new_local env "meth_pair" in let (set_arg, get_arg) = new_local env "arg" in let _, _, _, ts, _ = Type.as_func e1.note.Note.typ in - let add_cycles = Prelude.add_cycles env ae in + let add_cycles = Internals.add_cycles env ae in code1 ^^ StackRep.adjust env fun_sr SR.Vanilla ^^ set_meth_pair ^^ compile_exp_as env ae SR.Vanilla e2 ^^ set_arg ^^ @@ -7360,7 +7359,7 @@ and compile_exp (env : E.t) ae exp = let (set_arg, get_arg) = new_local env "arg" in let (set_k, get_k) = new_local env "k" in let (set_r, get_r) = new_local env "r" in - let add_cycles = Prelude.add_cycles env ae in + let add_cycles = Internals.add_cycles env ae in compile_exp_as env ae SR.Vanilla f ^^ set_meth_pair ^^ compile_exp_as env ae SR.Vanilla e ^^ set_arg ^^ compile_exp_as env ae SR.Vanilla k ^^ set_k ^^ @@ -7510,7 +7509,7 @@ and compile_exp (env : E.t) ae exp = let (set_r, get_r) = new_local env "r" in let mk_body env1 ae1 = compile_exp_as env1 ae1 SR.unit exp_f in let captured = Freevars.captured exp_f in - let add_cycles = Prelude.add_cycles env ae in + let add_cycles = Internals.add_cycles env ae in FuncDec.async_body env ae ts captured mk_body exp.at ^^ set_closure_idx ^^ diff --git a/src/ir_def/arrange_ir.ml b/src/ir_def/arrange_ir.ml index f3e090556c1..0e692b7312b 100644 --- a/src/ir_def/arrange_ir.ml +++ b/src/ir_def/arrange_ir.ml @@ -67,6 +67,7 @@ and prim = function | AwaitPrim -> Atom "AwaitPrim" | AssertPrim -> Atom "AssertPrim" | ThrowPrim -> Atom "ThrowPrim" + | TypRep t -> "TypRep" $$ [typ t] | ShowPrim t -> "ShowPrim" $$ [typ t] | SerializePrim t -> "SerializePrim" $$ List.map typ t | DeserializePrim t -> "DeserializePrim" $$ List.map typ t diff --git a/src/ir_def/check_ir.ml b/src/ir_def/check_ir.ml index 7e6c2d80964..46e089ae4c0 100644 --- a/src/ir_def/check_ir.ml +++ b/src/ir_def/check_ir.ml @@ -497,6 +497,9 @@ let rec check_exp env (exp:Ir.exp) : unit = | AssertPrim, [exp1] -> typ exp1 <: T.bool; T.unit <: t + | TypRep ot, [] -> + check (T.shared ot) "gen_typrep is not defined for operand type"; + Construct.typRepT <: t | ShowPrim ot, [exp1] -> check env.flavor.has_show "show expression in non-show flavor"; check (Show.can_show ot) "show is not defined for operand type"; diff --git a/src/ir_def/construct.ml b/src/ir_def/construct.ml index d5222a1738c..1293d08434b 100644 --- a/src/ir_def/construct.ml +++ b/src/ir_def/construct.ml @@ -185,6 +185,22 @@ let optE e = at = no_region; } +let arrayE t es = + let effs = List.map eff es in + let eff = List.fold_left max_eff T.Triv effs in + { it = PrimE (ArrayPrim (Const, t), es); + note = Note.{ def with typ = T.Array t; eff }; + at = no_region; + } + +let mutArrayE t es = + let effs = List.map eff es in + let eff = List.fold_left max_eff T.Triv effs in + { it = PrimE (ArrayPrim (Var, t), es); + note = Note.{ def with typ = T.Array (T.Mut t); eff }; + at = no_region; + } + let tagE i e = { it = PrimE (TagPrim i, [e]); note = Note.{ def with typ = T.Variant [{T.lab = i; typ = typ e; depr = None}]; eff = eff e }; @@ -216,6 +232,18 @@ let blockE decs exp = note = Note.{ def with typ; eff } } +let natE n = + { it = LitE (NatLit n); + at = no_region; + note = Note.{ def with typ = T.nat } + } + +let nat32E n = + { it = LitE (Nat32Lit n); + at = no_region; + note = Note.{ def with typ = T.(Prim Nat32) } + } + let textE s = { it = LitE (TextLit s); at = no_region; @@ -301,6 +329,16 @@ let dotE exp name typ = } } +let idxE exp1 exp2 = + { it = PrimE (IdxPrim, [exp1; exp2]); + at = no_region; + note = Note.{ def with + typ = T.as_array (typ exp1); + eff = max_eff (eff exp1) (eff exp2) + } + } + + let switch_optE exp1 exp2 pat exp3 typ1 = { it = SwitchE @@ -353,6 +391,7 @@ let tupE exps = note = Note.{ def with typ = T.Tup (List.map typ exps); eff }; } +(* TODO: Is it ok to share this value? *) let unitE = tupE [] let breakE l exp = @@ -505,16 +544,6 @@ let funcD ((id, typ) as f) x exp = let nary_funcD ((id, typ) as f) xs exp = letD f (nary_funcE id typ xs exp) -(* Continuation types *) - -let answerT = T.unit - -let contT typ = T.Func (T.Local, T.Returns, [], T.as_seq typ, []) - -let err_contT = T.Func (T.Local, T.Returns, [], [T.catch], []) - -let cpsT typ = T.Func (T.Local, T.Returns, [], [contT typ; err_contT], []) - (* Sequence expressions *) let seqE es = @@ -626,3 +655,52 @@ let unreachableE = (* Do we want a dedicated UnreachableE in the AST? *) loopE unitE +(* Internal types *) + +(* +These type definitions mirror the type definitions in prelude/internals.mo. +It would be good to get rid of that duplication somehow, and use those +in `prelude/internals.mo` directly. +*) + +let answerT = T.unit + +let contT typ = T.Func (T.Local, T.Returns, [], T.as_seq typ, []) + +let err_contT = T.Func (T.Local, T.Returns, [], [T.catch], []) + +let cpsT typ = T.Func (T.Local, T.Returns, [], [contT typ; err_contT], []) + +let (typRepT, typRepFieldT) = + let open T in + let typRepC = Con.fresh "TypRep" (Abs ([], Pre)) in + let typRepT = Con (typRepC, []) in + let fieldC = Con.fresh "Field" (Abs ([], Pre)) in + let fieldT = Con (fieldC, []) in + let fieldsT = Array fieldT in + let objSortT = Variant (List.sort compare_field ( + List.map (fun lab -> { lab; typ = T.unit; depr = None }) [ + "object_"; "actor_"; "module_"; "memory" + ] + )) in + let typRepRhs = Variant (List.sort compare_field ( + List.map (fun lab -> { lab; typ = T.unit; depr = None }) [ + "null_"; "bool"; "nat"; "int"; + "nat8"; "nat16"; "nat32"; "nat64"; + "int8"; "int16"; "int32"; "int64"; + "word8"; "word16"; "word32"; "word64"; + "float"; "char"; "text"; "blob"; "error"; "principal"; + "func_"; "any"; "non" + ] @ List.map (fun (lab,typ) -> { lab; typ; depr = None }) [ + ("ref", Array (Mut typRepT)); + ("obj", Tup [objSortT; Array fieldT]); + ("variant", fieldsT); + ("array", typRepT); + ("opt", typRepT); + ("tup", Array typRepT); + ] + )) in + let fieldRhs = Tup [text; Prim Nat32; typRepT] in + set_kind typRepC (Def ([], typRepRhs)); + set_kind fieldC (Def ([], fieldRhs)); + typRepT, fieldT diff --git a/src/ir_def/construct.mli b/src/ir_def/construct.mli index 2931cfe310c..0a9eeb208fd 100644 --- a/src/ir_def/construct.mli +++ b/src/ir_def/construct.mli @@ -58,8 +58,12 @@ val ic_rejectE : exp -> exp val ic_callE : exp -> exp -> exp -> exp -> exp val projE : exp -> int -> exp val optE : exp -> exp +val arrayE : typ -> exp list -> exp +val mutArrayE : typ -> exp list -> exp val tagE : id -> exp -> exp val blockE : dec list -> exp -> exp +val natE : Mo_values.Numerics.Nat.t -> exp +val nat32E : Mo_values.Numerics.Nat32.t -> exp val textE : string -> exp val blobE : string -> exp val letE : var -> exp -> exp -> exp @@ -76,6 +80,7 @@ val callE : exp -> typ list -> exp -> exp val ifE : exp -> exp -> exp -> typ -> exp val dotE : exp -> Type.lab -> typ -> exp +val idxE : exp -> exp -> exp val switch_optE : exp -> exp -> pat -> exp -> typ -> exp val switch_variantE : exp -> (id * pat * exp) list -> typ -> exp val tupE : exp list -> exp @@ -113,13 +118,6 @@ val nary_funcD : var -> var list -> exp -> dec val let_no_shadow : var -> exp -> dec list -> dec list -(* Continuations *) - -val answerT : typ -val contT : typ -> typ -val err_contT : typ -val cpsT : typ -> typ - (* Sequence expressions *) val seqE : exp list -> exp @@ -130,3 +128,20 @@ val (-->) : var -> exp -> exp val (-->*) : var list -> exp -> exp (* n-ary local *) val forall : typ_bind list -> exp -> exp (* generalization *) val (-*-) : exp -> exp -> exp (* application *) + +(* Internal types *) + +(* +These type definitions mirror the type definitions in prelude/internals.mo. +It would be good to get rid of that duplication somehow, and use those +in `prelude/internals.mo` directly. +*) + + +val answerT : typ +val contT : typ -> typ +val err_contT : typ +val cpsT : typ -> typ +val typRepT : typ +val typRepFieldT : typ + diff --git a/src/ir_def/ir.ml b/src/ir_def/ir.ml index 0b718e8f838..50e11028ae0 100644 --- a/src/ir_def/ir.ml +++ b/src/ir_def/ir.ml @@ -119,6 +119,7 @@ and prim = | AwaitPrim (* await *) | AssertPrim (* assertion *) | ThrowPrim (* throw *) + | TypRep of Type.typ (* Generates the type representation *) | ShowPrim of Type.typ (* debug_show *) | SerializePrim of Type.typ list (* Candid serialization prim *) | DeserializePrim of Type.typ list (* Candid deserialization prim *) diff --git a/src/ir_passes/typrep.ml b/src/ir_passes/typrep.ml new file mode 100644 index 00000000000..ea705fdf393 --- /dev/null +++ b/src/ir_passes/typrep.ml @@ -0,0 +1,269 @@ +(* Translates away calls to the `TypRep` prim *) + +(* NB: This code re-caluculates the typ_hash many times. Could be optimized. *) + +open Ir_def +open Mo_types + +open Source +open Ir +module T = Type +open Construct +open Typ_hash + +(* Environment *) + +(* We go through the file and collect all type arguments to `TypRep`. + We store them in `params`, indexed by their `type_id` +*) + +module M = Map.Make(String) +type env = + { params : T.typ M.t ref + } + +let empty_env () : env = { + params = ref M.empty; + } + +let add_type env t : unit = + env.params := M.add (typ_hash t) t !(env.params) + +(* Definition names *) + +let name_for t = "@typ_rep<" ^ typ_hash t ^ ">" +let var_for t : Construct.var = var (name_for t) T.(Array (Mut typRepT)) +let exp_for t : exp = tagE "ref" (varE (var_for t)) +let lexp_for t : lexp = + { it = IdxLE (varE (var_for t), natE Mo_values.Numerics.Int.zero) + ; at = no_region + ; note = T.(Mut typRepT) + } + +(* Synthesizing a single TypRep value *) + +let tag_for_prim : T.prim -> string = function + | T.Null -> "null_" + | T.Bool -> "bool" + | T.Nat -> "nat" + | T.Nat8 -> "nat8" + | T.Nat16 -> "nat16" + | T.Nat32 -> "nat32" + | T.Nat64 -> "nat64" + | T.Int -> "int" + | T.Int8 -> "int8" + | T.Int16 -> "int16" + | T.Int32 -> "int32" + | T.Int64 -> "int64" + | T.Word8 -> "word8" + | T.Word16 -> "word16" + | T.Word32 -> "word32" + | T.Word64 -> "word64" + | T.Float -> "float" + | T.Char -> "char" + | T.Text -> "text" + | T.Blob -> "blob" + | T.Error -> "error" + | T.Principal -> "principal" + +let tag_for_sort : T.obj_sort -> string = function + | T.Object -> "object_" + | T.Actor -> "actor" + | T.Module -> "module_" + | T.Memory -> "memory" + +let fields_for : T.field list -> exp = fun tfs -> + arrayE typRepFieldT (List.filter_map (fun tf -> + if T.is_typ tf.T.typ then None else Some (tupE [ + textE tf.T.lab; + nat32E (Mo_values.Numerics.Nat32.wrapping_of_big_int + (Big_int.big_int_of_int32 (Hash.hash tf.T.lab)) + ); + exp_for tf.T.typ + ]) + ) tfs) + +(* The type rep value for the given type *) + +let rhs_for : T.typ -> Ir.exp = fun t -> + match T.normalize t with + | T.(Prim p) -> + tagE (tag_for_prim p) unitE + | T.Obj (s, fs) -> + tagE "obj" (tupE [ + tagE (tag_for_sort s) unitE; + fields_for fs; + ]) + | T.Variant fs -> + tagE "variant" (fields_for fs) + | T.Array (T.Mut t) -> + (* mutable and immutable arrays have the same representation *) + tagE "array" (exp_for t) + | T.Array t -> + tagE "array" (exp_for t) + | T.Opt t -> + tagE "opt" (exp_for t) + | T.Tup ts -> + tagE "tup" (arrayE typRepT (List.map exp_for ts)) + | T.Func _ -> + tagE "func_" unitE + | T.Any -> + tagE "any" unitE + | T.Non -> + tagE "non" unitE + | T.Var _ | T.Con _ | T.Async _ | T.Mut _ | T.Typ _ | T.Pre -> assert false + +(* The subterms of this type *) + +let subterms_of : T.typ -> T.typ list = fun t -> + let open T in + match normalize t with + | Obj (_,tfs) -> List.filter_map (fun tf -> + if is_typ tf.typ then None else Some tf.typ + ) tfs + | Variant tfs -> List.map (fun tf -> tf.typ) tfs + | Array t -> [t] + | Opt t -> [t] + | Tup ts -> ts + | Func _ -> [] (* todo *) + | _ -> [] + +(* Synthesizing the types recursively. Hopefully well-founded. *) + +let decls : T.typ M.t -> Ir.dec list = fun roots -> + let roots = List.map snd (M.bindings roots) in + + (* Enumerate all subterms (Depth-first traversal) *) + let seen = ref M.empty in + let rec go = function + | [] -> () + | t::todo when M.mem (typ_hash t) !seen -> + go todo + | t::todo -> + seen := M.add (typ_hash t) t !seen; + go (subterms_of t @ todo) + in go roots; + + (* Now generate declarations: First declare a ref + for every type, then assign to it. + + Possible optimization: save a lot of extra allocations if we only use #ref + when needed, e.g. to break loops. + *) + List.map (fun (_h, t) -> + letD (var_for t) (mutArrayE typRepT [tagE "any" unitE]) + ) (M.bindings !seen) @ + List.map (fun (_h, t) -> + expD { + it = AssignE (lexp_for t, rhs_for t); + at = no_region; + note = Note.{def with typ = T.unit} + } + ) (M.bindings !seen) + + +(* The AST traversal *) + +(* Does two things: + - collects all uses of `TypRep` in the `env` + - for each actor, resets the environment, recurses, + and adds the generated declarations (this keeps closed actors closed) +*) + +let rec t_exps env decs = List.map (t_exp env) decs + +and t_exp env (e : Ir.exp) = + { e with it = t_exp' env e.it } + +and t_exp' env = function + | LitE l -> LitE l + | VarE id -> VarE id + | PrimE (TypRep ot, []) -> + let t' = T.normalize ot in + add_type env t'; + (exp_for t').it + | PrimE (p, es) -> PrimE (p, t_exps env es) + | AssignE (lexp1, exp2) -> + AssignE (t_lexp env lexp1, t_exp env exp2) + | FuncE (s, c, id, typbinds, pat, typT, exp) -> + FuncE (s, c, id, typbinds, pat, typT, t_exp env exp) + | BlockE block -> BlockE (t_block env block) + | IfE (exp1, exp2, exp3) -> + IfE (t_exp env exp1, t_exp env exp2, t_exp env exp3) + | SwitchE (exp1, cases) -> + let cases' = + List.map + (fun {it = {pat;exp}; at; note} -> + {it = {pat = pat; exp = t_exp env exp}; at; note}) + cases + in + SwitchE (t_exp env exp1, cases') + | TryE (exp1, cases) -> + let cases' = + List.map + (fun {it = {pat;exp}; at; note} -> + {it = {pat = pat; exp = t_exp env exp}; at; note}) + cases + in + TryE (t_exp env exp1, cases') + | LoopE exp1 -> + LoopE (t_exp env exp1) + | LabelE (id, typ, exp1) -> + LabelE (id, typ, t_exp env exp1) + | AsyncE (tb, e, typ) -> AsyncE (tb, t_exp env e, typ) + | DeclareE (id, typ, exp1) -> + DeclareE (id, typ, t_exp env exp1) + | DefineE (id, mut ,exp1) -> + DefineE (id, mut, t_exp env exp1) + | NewObjE (sort, ids, t) -> + NewObjE (sort, ids, t) + | SelfCallE (ts, e1, e2, e3) -> + SelfCallE (ts, t_exp env e1, t_exp env e2, t_exp env e3) + | ActorE (ds, fields, {pre; post}, typ) -> + (* Until Actor expressions become their own units, + we repeat what we do in `comp_unit` below *) + let env1 = empty_env () in + let ds' = t_decs env1 ds in + let pre' = t_exp env1 pre in + let post' = t_exp env1 post in + let ds = decls !(env1.params) in + ActorE (ds @ ds', fields, {pre = pre'; post = post'}, typ) + +and t_lexp env (e : Ir.lexp) = { e with it = t_lexp' env e.it } +and t_lexp' env = function + | VarLE id -> VarLE id + | IdxLE (exp1, exp2) -> + IdxLE (t_exp env exp1, t_exp env exp2) + | DotLE (exp1, n) -> + DotLE (t_exp env exp1, n) + +and t_dec env dec = { dec with it = t_dec' env dec.it } + +and t_dec' env dec' = + match dec' with + | LetD (pat,exp) -> LetD (pat,t_exp env exp) + | VarD (id, typ, exp) -> VarD (id, typ, t_exp env exp) + +and t_decs env decs = List.map (t_dec env) decs + +and t_block env (ds, exp) = (t_decs env ds, t_exp env exp) + +and t_comp_unit = function + | LibU _ -> raise (Invalid_argument "cannot compile library") + | ProgU ds -> + let env = empty_env () in + let ds' = t_decs env ds in + let ds = decls !(env.params) in + ProgU (ds @ ds') + | ActorU (as_opt, ds, fields, {pre; post}, typ) -> + let env = empty_env () in + let ds' = t_decs env ds in + let pre' = t_exp env pre in + let post' = t_exp env post in + let ds = decls !(env.params) in + ActorU (as_opt, ds @ ds', fields, {pre = pre'; post = post'}, typ) + +(* Entry point for the program transformation *) + +let transform (cu, flavor) = + (t_comp_unit cu, flavor) diff --git a/src/ir_passes/typrep.mli b/src/ir_passes/typrep.mli new file mode 100644 index 00000000000..9fa8d1f9abb --- /dev/null +++ b/src/ir_passes/typrep.mli @@ -0,0 +1,3 @@ +open Ir_def + +val transform : Ir.prog -> Ir.prog diff --git a/src/lowering/desugar.ml b/src/lowering/desugar.ml index 4d38b57eb3f..c057ceae16e 100644 --- a/src/lowering/desugar.ml +++ b/src/lowering/desugar.ml @@ -138,6 +138,14 @@ and exp' at note = function I.PrimE (I.CastPrim (T.seq ts1, T.seq ts2), [exp e]) | _ -> assert false end + | S.CallE ({it=S.AnnotE ({it=S.PrimE "gen_typrep";_}, _);note;_}, _, {it = S.LitE l; _}) -> + begin + if !l != NullLit then assert false; + match note.S.note_typ with + | T.Func (T.Local, T.Returns, [], [T.Opt t], _) -> + I.PrimE (I.TypRep t, []) + | _ -> assert false + end | S.CallE ({it=S.AnnotE ({it=S.PrimE "serialize";_}, _);note;_}, _, e) -> begin match note.S.note_typ with | T.Func (T.Local, T.Returns, [], ts1, ts2) -> diff --git a/src/pipeline/pipeline.ml b/src/pipeline/pipeline.ml index c710c18ac26..fc9be98a034 100644 --- a/src/pipeline/pipeline.ml +++ b/src/pipeline/pipeline.ml @@ -560,6 +560,9 @@ let tailcall_optimization = let show_translation = transform_if "Translate show" Show.transform +let typ_rep_translation = + transform_if "Translate type rep generation" Typrep.transform + let eq_translation = transform_if "Translate polymorphic equality" Eq.transform @@ -571,6 +574,7 @@ let analyze analysis_name analysis prog name = let ir_passes mode prog_ir name = (* translations that extend the progam and must be done before await/cps conversion *) + let prog_ir = typ_rep_translation true prog_ir name in let prog_ir = show_translation true prog_ir name in let prog_ir = eq_translation true prog_ir name in (* cps conversion and local transformations *) diff --git a/src/prelude/internals.mo b/src/prelude/internals.mo index 9356a7d74f6..46af2a91098 100644 --- a/src/prelude/internals.mo +++ b/src/prelude/internals.mo @@ -418,3 +418,65 @@ func @create_actor_helper(wasm_module_ : Blob, arg_ : Blob) : async Principal = return canister_id_; }; + +/* +Type representation + +The following type is used by the runtime to describe, well, motoko types. This +is used in the implementation of generic code (equality, serialization, +showing). + +Only `shared` values need to be supported here so far. Therefore we take short-cuts: + * no parametric fuctions + * no mutable object fields + +Values of this type are produced in an IR-to-IR pass; this way we get some +type-checking. + +In contrast to normal Motoko values, these are coninductive, e.g. can be +cyclic! +*/ + +// cf. Mo_types.typ +type @TypRep = { + #ref : [var @TypRep]; + #null_; + #bool; + #nat; + #nat8; + #nat16; + #nat32; + #nat64; + #int; + #int8; + #int16; + #int32; + #int64; + #word8; + #word16; + #word32; + #word64; + #float; + #char; + #text; + #blob; + #error; + #principal; + #obj : ({#object_; #actor_; #module_; #memory}, [@Field]); + #variant : [@Field]; + #array : @TypRep; + #opt : @TypRep; + #tup : [@TypRep]; + #func_; // TODO: function type arguments + #any; + #non; +}; + +// tuple, not record, as it is more compact. +// Field names thus just documentation. +type @Field = ( + field_name : Text, + motoko_hash : Nat32, + field_type : @TypRep, +); + diff --git a/test/run/ok/typrep-show.run-low.ok b/test/run/ok/typrep-show.run-low.ok new file mode 100644 index 00000000000..0d50684c2b6 --- /dev/null +++ b/test/run/ok/typrep-show.run-low.ok @@ -0,0 +1,7 @@ +#ref([var #bool]) +#ref([var #tup([])]) +#ref([var #tup([#ref([var #array(#ref([var #bool]))]), #ref([var #opt(#ref([var #int]))])])]) +#ref([var #obj(#object_, [("first_name", 650_209_274, #ref([var #text])), ("last_name", 898_649_108, #ref([var #text])), ("points", 1_999_508_003, #ref([var #int]))])]) +#ref([var #variant([("gotboredday", 987_113_604, #ref([var #tup([])])), ("monday", 1_197_631_248, #ref([var #tup([])])), ("tuesday", 323_181_965, #ref([var #tup([])]))])]) +#ref([var #variant([("gotboredday", 987_113_604, #ref([var #tup([])])), ("monday", 1_197_631_248, #ref([var #tup([])])), ("tuesday", 323_181_965, #ref([var #tup([])]))])]) +#ref([var #func_]) diff --git a/test/run/ok/typrep-show.wasm-run.ok b/test/run/ok/typrep-show.wasm-run.ok new file mode 100644 index 00000000000..0d50684c2b6 --- /dev/null +++ b/test/run/ok/typrep-show.wasm-run.ok @@ -0,0 +1,7 @@ +#ref([var #bool]) +#ref([var #tup([])]) +#ref([var #tup([#ref([var #array(#ref([var #bool]))]), #ref([var #opt(#ref([var #int]))])])]) +#ref([var #obj(#object_, [("first_name", 650_209_274, #ref([var #text])), ("last_name", 898_649_108, #ref([var #text])), ("points", 1_999_508_003, #ref([var #int]))])]) +#ref([var #variant([("gotboredday", 987_113_604, #ref([var #tup([])])), ("monday", 1_197_631_248, #ref([var #tup([])])), ("tuesday", 323_181_965, #ref([var #tup([])]))])]) +#ref([var #variant([("gotboredday", 987_113_604, #ref([var #tup([])])), ("monday", 1_197_631_248, #ref([var #tup([])])), ("tuesday", 323_181_965, #ref([var #tup([])]))])]) +#ref([var #func_]) diff --git a/test/run/typrep-show.mo b/test/run/typrep-show.mo new file mode 100644 index 00000000000..3e88c509630 --- /dev/null +++ b/test/run/typrep-show.mo @@ -0,0 +1,51 @@ + +// These tests use `debug_show` on the generated typrep to exercise the type +// rep generation code. Only works for non-cyclic types of course. + + +// Need to unlock prim to access `@TypRep` and the `gen_typrep` prim +//MOC-ENV MOC_UNLOCK_PRIM=yesplease + +import Prim "mo:prim"; + +do { + type T = Bool; + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +do { + type T = (); + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +do { + type T = ([Bool],?Int); + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +do { + type T = {first_name: Text; last_name: Text; points : Int}; + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +do { + type T = {#monday; #tuesday; #gotboredday}; + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +do { + type T = {#monday; #tuesday; #gotboredday}; + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +do { + type T = shared Text -> async Text; + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; + +// This is infinite, cannot print that: +/* +do { + type T = ?T + Prim.debugPrint (debug_show ((prim "gen_typrep" : ?T -> @TypRep) null)); +}; +*/ + + +//SKIP run +//SKIP run-ir +// NB: Run-low should work!