From 89f29db5d13dfe6452663fbe48f62076b4143ce1 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 18 Jun 2020 13:06:31 +0200 Subject: [PATCH] [interpreter] Fix symbolic locals with typeuse --- document/core/exec/runtime.rst | 2 ++ document/core/util/macros.def | 2 +- interpreter/text/parser.mly | 63 ++++++++++++++++++++-------------- test/core/func.wast | 40 +++++++++++++++++++++ 4 files changed, 81 insertions(+), 26 deletions(-) diff --git a/document/core/exec/runtime.rst b/document/core/exec/runtime.rst index a736feb37d..fc0771d911 100644 --- a/document/core/exec/runtime.rst +++ b/document/core/exec/runtime.rst @@ -408,6 +408,8 @@ and a reference to the function's own :ref:`module instance ` The values of the locals are mutated by respective :ref:`variable instructions `. +.. _exec-expand: + Conventions ........... diff --git a/document/core/util/macros.def b/document/core/util/macros.def index d7523ed127..44a8743f5f 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -873,7 +873,7 @@ .. Stack, meta functions -.. |expand| mathdef:: \xref{exec/runtime}{syntax-frame}{\F{expand}} +.. |expand| mathdef:: \xref{exec/runtime}{exec-expand}{\F{expand}} .. Administrative Instructions, terminals diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 031608431e..7b9b3e7443 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -72,12 +72,19 @@ let empty_types () = {space = empty (); list = []} type context = { types : types; tables : space; memories : space; - funcs : space; locals : space; globals : space; labels : int32 VarMap.t } + funcs : space; locals : space; globals : space; + labels : int32 VarMap.t; deferred_locals : (unit -> unit) list ref + } let empty_context () = { types = empty_types (); tables = empty (); memories = empty (); funcs = empty (); locals = empty (); globals = empty (); - labels = VarMap.empty } + labels = VarMap.empty; deferred_locals = ref [] + } + +let force_locals (c : context) = + List.fold_right Stdlib.(@@) !(c.deferred_locals) (); + c.deferred_locals := [] let enter_func (c : context) = {c with labels = VarMap.empty; locals = empty ()} @@ -88,7 +95,7 @@ let lookup category space x = let type_ (c : context) x = lookup "type" c.types.space x let func (c : context) x = lookup "function" c.funcs x -let local (c : context) x = lookup "local" c.locals x +let local (c : context) x = force_locals c; lookup "local" c.locals x let global (c : context) x = lookup "global" c.globals x let table (c : context) x = lookup "table" c.tables x let memory (c : context) x = lookup "memory" c.memories x @@ -101,53 +108,57 @@ let func_type (c : context) x = with Failure _ -> error x.at ("unknown type " ^ Int32.to_string x.it) +let anon category space n = + let i = space.count in + space.count <- Int32.add i n; + if I32.lt_u space.count n then + error no_region ("too many " ^ category ^ " bindings"); + i + let bind category space x = + let i = anon category space 1l in if VarMap.mem x.it space.map then error x.at ("duplicate " ^ category ^ " " ^ x.it); - let i = space.count in - space.map <- VarMap.add x.it space.count space.map; - space.count <- Int32.add space.count 1l; - if space.count = 0l then - error x.at ("too many " ^ category ^ " bindings"); + space.map <- VarMap.add x.it i space.map; i let bind_type (c : context) x ty = c.types.list <- c.types.list @ [ty]; bind "type" c.types.space x let bind_func (c : context) x = bind "function" c.funcs x -let bind_local (c : context) x = bind "local" c.locals x +let bind_local (c : context) x = force_locals c; bind "local" c.locals x let bind_global (c : context) x = bind "global" c.globals x let bind_table (c : context) x = bind "table" c.tables x let bind_memory (c : context) x = bind "memory" c.memories x let bind_label (c : context) x = {c with labels = VarMap.add x.it 0l (VarMap.map (Int32.add 1l) c.labels)} -let anon category space n = - let i = space.count in - space.count <- Int32.add space.count n; - if I32.lt_u space.count n then - error no_region ("too many " ^ category ^ " bindings"); - i - let anon_type (c : context) ty = c.types.list <- c.types.list @ [ty]; anon "type" c.types.space 1l let anon_func (c : context) = anon "function" c.funcs 1l -let anon_locals (c : context) ts = - ignore (anon "local" c.locals (Lib.List32.length ts)) +let anon_locals (c : context) lazy_ts = + let f () = + ignore (anon "local" c.locals (Lib.List32.length (Lazy.force lazy_ts))) + in c.deferred_locals := f :: !(c.deferred_locals) let anon_global (c : context) = anon "global" c.globals 1l let anon_table (c : context) = anon "table" c.tables 1l let anon_memory (c : context) = anon "memory" c.memories 1l let anon_label (c : context) = {c with labels = VarMap.map (Int32.add 1l) c.labels} + let inline_type (c : context) ft at = match Lib.List.index_where (fun ty -> ty.it = ft) c.types.list with | Some i -> Int32.of_int i @@ at | None -> anon_type c (ft @@ at) @@ at let inline_type_explicit (c : context) x ft at = - if ft <> FuncType ([], []) && ft <> func_type c x then + if ft = FuncType ([], []) then + (* Laziness ensures that type lookup is only triggered when + symbolic identifiers are used, and not for desugared functions *) + anon_locals c (lazy (let FuncType (ts, _) = func_type c x in ts)) + else if ft <> func_type c x then error at "inline function type does not match explicit type"; x @@ -536,12 +547,14 @@ func : func_fields : | type_use func_fields_body { fun c x at -> - let y = inline_type_explicit c ($1 c type_) (fst $2) at in - [{(snd $2 (enter_func c)) with ftype = y} @@ at], [], [] } + let c' = enter_func c in + let y = inline_type_explicit c' ($1 c' type_) (fst $2) at in + [{(snd $2 c') with ftype = y} @@ at], [], [] } | func_fields_body /* Sugar */ { fun c x at -> - let y = inline_type c (fst $1) at in - [{(snd $1 (enter_func c)) with ftype = y} @@ at], [], [] } + let c' = enter_func c in + let y = inline_type c' (fst $1) at in + [{(snd $1 c') with ftype = y} @@ at], [], [] } | inline_import type_use func_fields_import /* Sugar */ { fun c x at -> let y = inline_type_explicit c ($2 c type_) $3 at in @@ -575,7 +588,7 @@ func_fields_body : | LPAR PARAM value_type_list RPAR func_fields_body { let FuncType (ins, out) = fst $5 in FuncType ($3 @ ins, out), - fun c -> ignore (anon_locals c $3); snd $5 c } + fun c -> anon_locals c (lazy $3); snd $5 c } | LPAR PARAM bind_var VALUE_TYPE RPAR func_fields_body /* Sugar */ { let FuncType (ins, out) = fst $6 in FuncType ($4 :: ins, out), @@ -592,7 +605,7 @@ func_body : { fun c -> let c' = anon_label c in {ftype = -1l @@ at(); locals = []; body = $1 c'} } | LPAR LOCAL value_type_list RPAR func_body - { fun c -> ignore (anon_locals c $3); let f = $5 c in + { fun c -> anon_locals c (lazy $3); let f = $5 c in {f with locals = $3 @ f.locals} } | LPAR LOCAL bind_var VALUE_TYPE RPAR func_body /* Sugar */ { fun c -> ignore (bind_local c $3); let f = $6 c in diff --git a/test/core/func.wast b/test/core/func.wast index af81d982c7..7189257c99 100644 --- a/test/core/func.wast +++ b/test/core/func.wast @@ -444,6 +444,46 @@ "unknown type" ) +(assert_malformed + (module quote + "(func $f (result f64) (f64.const 0))" ;; adds implicit type definition + "(func $g (param i32))" ;; reuses explicit type definition + "(func $h (result f64) (f64.const 1))" ;; reuses implicit type definition + "(type $t (func (param i32)))" + + "(func (type 2) (param i32))" ;; does not exist + ) + "unknown type" +) + +(module + (type $proc (func (result i32))) + (type $sig (func (param i32) (result i32))) + + (func (export "f") (type $sig) + (local $var i32) + (local.get $var) + ) + + (func $g (type $sig) + (local $var i32) + (local.get $var) + ) + (func (export "g") (type $sig) + (call $g (local.get 0)) + ) + + (func (export "p") (type $proc) + (local $var i32) + (local.set 0 (i32.const 42)) + (local.get $var) + ) +) + +(assert_return (invoke "f" (i32.const 42)) (i32.const 0)) +(assert_return (invoke "g" (i32.const 42)) (i32.const 0)) +(assert_return (invoke "p") (i32.const 42)) + (module (type $sig (func))