From 2ed4a638f46c771672f248ffaff844e35b4d41bf Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 11 May 2026 10:07:10 -0700 Subject: [PATCH] Make cont.new a constant expression Document this in Explainer.md, update the interpreter, and add tests. Remove a runtime type check during global allocation that incorrectly fails for globals holding continuations because continuations do not contain runtime type information. Closes #145. --- interpreter/runtime/global.ml | 1 - interpreter/valid/valid.ml | 2 +- proposals/stack-switching/Explainer.md | 6 +++- test/core/stack-switching/cont.new.wast | 39 ++++++++++++------------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/interpreter/runtime/global.ml b/interpreter/runtime/global.ml index cf69d2ac..8775976f 100644 --- a/interpreter/runtime/global.ml +++ b/interpreter/runtime/global.ml @@ -9,7 +9,6 @@ exception NotMutable let alloc (GlobalT (_mut, t) as ty) v = assert Free.((val_type t).types = Set.empty); - if not (Match.match_val_type [] (type_of_value v) t) then raise Type; {ty; content = v} let type_of glob = diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index aa6621ce..0cca2f22 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -1147,7 +1147,7 @@ let is_const (c : context) (e : instr) = | Const _ | VecConst _ | Binary (Value.I32 I32Op.(Add | Sub | Mul)) | Binary (Value.I64 I64Op.(Add | Sub | Mul)) - | RefNull _ | RefFunc _ + | RefNull _ | RefFunc _ | ContNew _ | RefI31 | StructNew _ | ArrayNew _ | ArrayNewFixed _ -> true | GlobalGet x -> let GlobalT (mut, _t) = global c x in mut = Cons | _ -> false diff --git a/proposals/stack-switching/Explainer.md b/proposals/stack-switching/Explainer.md index f370591c..d2196dbf 100644 --- a/proposals/stack-switching/Explainer.md +++ b/proposals/stack-switching/Explainer.md @@ -550,7 +550,8 @@ function. ``` It takes a reference to a function of type `[t1*] -> [t2*]` whose body -may perform non-local control flow. +may perform non-local control flow. `cont.new` is a constant +expression, so it can be used in places like global initializers. ### Invoking continuations @@ -1035,6 +1036,9 @@ where `rt` is the respective target type of the cast instruction, and the `casta - `rt castable` - iff not (rt <: (ref null cont)) + +Finally, `cont.new` is added to the set of constant expressions. + ### Execution The same control tag may be used simultaneously by `throw`, `suspend`, diff --git a/test/core/stack-switching/cont.new.wast b/test/core/stack-switching/cont.new.wast index 4e17fec5..a843577d 100644 --- a/test/core/stack-switching/cont.new.wast +++ b/test/core/stack-switching/cont.new.wast @@ -322,24 +322,21 @@ "type mismatch" ) -;; TODO: Make cont.new constant -;; https://github.com/WebAssembly/stack-switching/issues/145 - -;; ;; Constant expression in global definition. -;; (module -;; (type $f (func)) -;; (type $k (cont $f)) -;; (global $k (export "k") (ref $k) (cont.new $k (ref.func $f))) -;; (func $f (type $f)) -;; ) -;; (assert_return (get "k") (ref.cont)) - -;; ;; Constant expression in element segment definition. -;; (module -;; (type $f (func)) -;; (type $k (cont $f)) -;; (table $t (ref null $k) (elem (cont.new $k (ref.func $f)))) -;; (func $f (type $f)) -;; (func (export "get") (result (ref null $k)) (table.get $t (i32.const 0))) -;; ) -;; (assert_return (invoke "get") (ref.cont)) +;; Constant expression in global definition. +(module + (type $f (func)) + (type $k (cont $f)) + (global $k (export "k") (ref $k) (cont.new $k (ref.func $f))) + (func $f (type $f)) +) +(assert_return (get "k") (ref.cont)) + +;; Constant expression in element segment definition. +(module + (type $f (func)) + (type $k (cont $f)) + (table $t (ref null $k) (elem (cont.new $k (ref.func $f)))) + (func $f (type $f)) + (func (export "get") (result (ref null $k)) (table.get $t (i32.const 0))) +) +(assert_return (invoke "get") (ref.cont))