Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions interpreter/exec/eval.mli
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ exception Crash of Source.region * string

val init : Ast.module_ -> extern list -> module_inst (* raises Link, Trap *)
val invoke : func_inst -> value list -> value list (* raises Trap *)

type ctxt
type handle_table

type cont = int32 * ctxt (* TODO: represent type properly *)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these types aren't used externally, I believe we can simplify even further:

Suggested change
type ctxt
type handle_table
type cont = int32 * ctxt (* TODO: represent type properly *)
type cont

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

type ref_ += ContRef of cont option ref
5 changes: 3 additions & 2 deletions interpreter/script/run.ml
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,11 @@ let assert_ref_pat r p =
| RefTypePat Types.EqHT, (I31.I31Ref _ | Aggr.StructRef _ | Aggr.ArrayRef _)
| RefTypePat Types.I31HT, I31.I31Ref _
| RefTypePat Types.StructHT, Aggr.StructRef _
| RefTypePat Types.ArrayHT, Aggr.ArrayRef _ -> true
| RefTypePat Types.ArrayHT, Aggr.ArrayRef _
| RefTypePat Types.FuncHT, Instance.FuncRef _
| RefTypePat Types.ContHT, Eval.ContRef _
| RefTypePat Types.ExnHT, Exn.ExnRef _
| RefTypePat Types.ExternHT, _ -> true
| RefTypePat Types.ExternHT, _

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just my opinion:
Using this kind of pattern (sic) has a code smell to it. Especially since the rhs of the equations are so simple, I would prefer having explicit true/false. It would have the additional merit of being easier to refactor correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using or-patterns to share r.h.s.'s is common practice and the reason why this syntax exists. What was odd before were the spurious true's — not sure why they were there, so I agree with removing them.

| NullPat, Value.NullRef _ -> true
| _ -> false

Expand Down
1 change: 1 addition & 0 deletions interpreter/text/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ rule token = parse
| "ref.func" -> REF_FUNC
| "ref.struct" -> REF_STRUCT
| "ref.array" -> REF_ARRAY
| "ref.cont" -> REF_CONT
| "ref.exn" -> REF_EXN
| "ref.extern" -> REF_EXTERN
| "ref.host" -> REF_HOST
Expand Down
3 changes: 2 additions & 1 deletion interpreter/text/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ let parse_annots (m : module_) : Custom.section list =
%token<string> OFFSET_EQ_NAT ALIGN_EQ_NAT
%token<string Source.phrase -> Ast.instr' * Value.num> CONST
%token<Ast.instr'> UNARY BINARY TEST COMPARE CONVERT
%token REF_NULL REF_FUNC REF_I31 REF_STRUCT REF_ARRAY REF_EXN REF_EXTERN REF_HOST
%token REF_NULL REF_FUNC REF_I31 REF_STRUCT REF_ARRAY REF_CONT REF_EXN REF_EXTERN REF_HOST
%token REF_EQ REF_IS_NULL REF_AS_NON_NULL REF_TEST REF_CAST
%token<Ast.instr'> I31_GET
%token<Ast.idx -> Ast.instr'> STRUCT_NEW ARRAY_NEW ARRAY_GET
Expand Down Expand Up @@ -1626,6 +1626,7 @@ result :
| LPAR REF_STRUCT RPAR { RefResult (RefTypePat StructHT) @@ $sloc }
| LPAR REF_ARRAY RPAR { RefResult (RefTypePat ArrayHT) @@ $sloc }
| LPAR REF_FUNC RPAR { RefResult (RefTypePat FuncHT) @@ $sloc }
| LPAR REF_CONT RPAR { RefResult (RefTypePat ContHT) @@ $sloc }
| LPAR REF_EXN RPAR { RefResult (RefTypePat ExnHT) @@ $sloc }
| LPAR REF_EXTERN RPAR { RefResult (RefTypePat ExternHT) @@ $sloc }
| LPAR REF_NULL RPAR { RefResult NullPat @@ $sloc }
Expand Down
345 changes: 345 additions & 0 deletions test/core/stack-switching/cont.new.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,345 @@
;; No type immediate.
(assert_malformed
(module quote
"(module"
"(func (drop (cont.new)))"
")"
)
"unexpected token"
)

(assert_malformed
(module binary
"\00asm\01\00\00\00"
"\01\04\01\60\00\00" ;; Type section: 1 type
"\03\02\01\00" ;; Function section: 1 function
"\0a\04\01" ;; Code section: 1 function
;; function 0
"\02\00" ;; Function size and local type count
"\e0" ;; cont.new (missing type immediate)
)
"unexpected end"
)

;; Valid binary module parsing, validating, and executing correctly
(module binary
"\00\61\73\6d\01\00\00\00" ;; Magic header and version
"\01\8b\80\80\80\00\03" ;; Type section: 3 types
"\60\00\00" ;; type 0: func [] -> []
"\5d\00" ;; type 1: cont 0
"\60\00\01\64\01" ;; type 2: func [] -> [ref 1]
"\03\83\80\80\80\00\02" ;; Function section: 2 functions
"\00\02" ;; func 0: type 0, func 1: type 2
"\07\88\80\80\80\00\01" ;; Export section: 1 export
"\04\74\65\73\74\00\01" ;; "test" -> func 1
"\09\85\80\80\80\00\01" ;; Element section: 1 segment
"\03\00\01\00" ;; declarative, elem kind func, 1 func, func index 0
"\0a\93\80\80\80\00\02" ;; Code section: 2 functions
;; function 0
"\82\80\80\80\00\00\0b" ;; size 2, 0 locals, end
;; function 1
"\86\80\80\80\00\00" ;; size 6, 0 locals
"\d2\00" ;; ref.func 0
"\e0\01" ;; cont.new 1
"\0b" ;; end
)
(assert_return (invoke "test") (ref.cont))

;; Out-of-bounds type immediate.
(assert_invalid
(module
(func (drop (cont.new 0 (unreachable))))
)
"non-continuation type"
)

;; Non-continuation type.
(assert_invalid
(module
(type $f (func))
(func (drop (cont.new $f (unreachable))))
)
"non-continuation type"
)

;; Defined function ref.func operand.
(module
(type $f (func))
(type $k (cont $f))
(elem declare func $f)
(func $f (type $f))
(func (export "test") (result (ref $k)) (cont.new $k (ref.func $f)))
)
(assert_return (invoke "test") (ref.cont))

;; Imported function ref.func operand.
(module definition
(type $f (func))
(type $k (cont $f))
(elem declare func $f)
(import "" "" (func $f (type $f)))
(func (result (ref $k)) (cont.new $k (ref.func $f)))
)

;; Defined global operand.
(module
(type $f (func))
(type $k (cont $f))
(global $g (ref null $f) (ref.null nofunc))
(func (export "test") (result (ref $k)) (cont.new $k (global.get $g)))
)
(assert_trap (invoke "test") "null function reference")

;; Defined global operand (non-null at runtime).
(module
(type $f (func))
(type $k (cont $f))
(elem declare func $f)
(func $f (type $f))
(global $g (ref null $f) (ref.func $f))
(func (export "test") (result (ref $k)) (cont.new $k (global.get $g)))
)
(assert_return (invoke "test") (ref.cont))

;; Imported global operand.
(module definition
(type $f (func))
(type $k (cont $f))
(import "" "" (global $g (ref null $f)))
(func (result (ref $k)) (cont.new $k (global.get $g)))
)

;; Param operand.
(module
(type $f (func))
(type $k (cont $f))
(func (export "test") (param (ref null $f)) (result (ref $k))
(cont.new $k (local.get 0))
)
)
(assert_trap (invoke "test" (ref.null nofunc)) "null function reference")

;; Stack-polymorphic (unreachable) input.
(module
(type $f (func))
(type $k (cont $f))
(func (export "test") (result (ref $k)) (cont.new $k (unreachable)))
)
(assert_trap (invoke "test") "unreachable")

;; Stack-polymorphic (unreachable) input due to branch.
(module
(type $f (func))
(type $k (cont $f))
(func (export "test")
(drop
(block $l (result (ref $k))
(cont.new $k (return))
)
)
)
)
(assert_return (invoke "test"))

;; Uninhabitable bottom input.
(module
(type $f (func))
(type $k (cont $f))
(func (param (ref nofunc)) (result (ref $k)) (cont.new $k (local.get 0)))
)

;; Null constant input.
(module
(type $f (func))
(type $k (cont $f))
(func (export "test") (result (ref $k)) (cont.new $k (ref.null $f)))
)
(assert_trap (invoke "test") "null function reference")

;; Bottom null constant input.
(module
(type $f (func))
(type $k (cont $f))
(func (export "test") (result (ref $k)) (cont.new $k (ref.null nofunc)))
)
(assert_trap (invoke "test") "null function reference")

;; Top null constant input.
(assert_invalid
(module
(type $f (func))
(type $k (cont $f))
(func (result (ref $k)) (cont.new $k (ref.null func)))
)
"type mismatch"
)

;; Any hierarchy null constant input.
(assert_invalid
(module
(type $f (func))
(type $k (cont $f))
(func (result (ref $k)) (cont.new $k (ref.null none)))
)
"type mismatch"
)

;; Cont hierarchy null constant input.
(assert_invalid
(module
(type $f (func))
(type $k (cont $f))
(func (result (ref $k)) (cont.new $k (ref.null nocont)))
)
"type mismatch"
)

;; Top reference input.
(assert_invalid
(module
(type $f (func))
(type $k (cont $f))
(func (param funcref) (result (ref $k)) (cont.new $k (local.get 0)))
)
"type mismatch"
)

;; Declared subtype input.
(module
(type $super (sub (func)))
(type $sub (sub $super (func)))
(type $k (cont $super))
(elem declare func $sub)
(func $sub (type $sub))
(func (export "test") (result (ref $k)) (cont.new $k (ref.func $sub)))
)
(assert_return (invoke "test") (ref.cont))

;; Declared supertype input.
(assert_invalid
(module
(type $super (sub (func)))
(type $sub (sub $super (func)))
(type $k (cont $sub))
(func (param (ref null $super)) (result (ref $k)) (cont.new $k (local.get 0)))
)
"type mismatch"
)

;; Unrelated input.
(assert_invalid
(module
(rec
(type $f (func))
(type $other (func))
)
(type $k (cont $f))
(func (param (ref null $other)) (result (ref $k)) (cont.new $k (local.get 0)))
)
"type mismatch"
)

;; Missing input.
(assert_invalid
(module
(type $f (func))
(type $k (cont $f))
(func (result (ref $k)) (cont.new $k))
)
"type mismatch"
)

;; Extra input.
(assert_invalid
(module
(type $f (func))
(type $k (cont $f))
(func (param (ref null $f)) (result (ref $k)) (cont.new $k (i32.const 0) (local.get 0)))
)
"type mismatch"
)

;; Extra input matching continuation params.
(assert_invalid
(module
(type $f (func (param i32)))
(type $k (cont $f))
(func (param (ref null $f)) (result (ref $k)) (cont.new $k (i32.const 0) (local.get 0)))
)
"type mismatch"
)

;; Contref output type.
(module
(type $f (func))
(type $k (cont $f))
(func (param (ref null $f)) (result contref) (cont.new $k (local.get 0)))
)

;; Nullable cont reference output type.
(module
(type $f (func))
(type $k (cont $f))
(func (param (ref null $f)) (result (ref null cont)) (cont.new $k (local.get 0)))
)

;; Non-nullable cont reference output type.
(module
(type $f (func))
(type $k (cont $f))
(func (param (ref null $f)) (result (ref cont)) (cont.new $k (local.get 0)))
)

;; Declared supertype output type.
(module
(type $f (func))
(type $super (sub (cont $f)))
(type $sub (sub $super (cont $f)))
(func (param (ref null $f)) (result (ref $super)) (cont.new $sub (local.get 0)))
)

;; Declared subtype output type.
(assert_invalid
(module
(type $f (func))
(type $super (sub (cont $f)))
(type $sub (sub $super (cont $f)))
(func (param (ref null $f)) (result (ref $sub)) (cont.new $super (local.get 0)))
)
"type mismatch"
)

;; Unrelated output.
(assert_invalid
(module
(type $f (func))
(rec
(type $k (cont $f))
(type $other (cont $f))
)
(func (param (ref null $f)) (result (ref $other)) (cont.new $k (local.get 0)))
)
"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))
Loading