diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 93416c8eb..2cd2d1361 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -22,14 +22,14 @@ Size: 8 Alignment: 8 -## `clockid`: Enum(`u32`) +## `clockid`: Variant Identifiers for clocks. Size: 4 Alignment: 4 -### Variants +### Variant cases - `realtime` The clock measuring real time. Time value zero corresponds with 1970-01-01T00:00:00Z. @@ -40,7 +40,7 @@ real time, whose value cannot be adjusted and which cannot have negative clock jumps. The epoch of this clock is undefined. The absolute time value of this clock therefore has no meaning. -## `errno`: Enum(`u16`) +## `errno`: Variant Error codes returned by functions. Not all of these error codes are returned by the functions provided by this API; some are used in higher-level library layers, and others are provided @@ -50,7 +50,7 @@ Size: 2 Alignment: 2 -### Variants +### Variant cases - `success` No error occurred. System call completed successfully. @@ -282,14 +282,14 @@ Cross-device link. - `notcapable` Extension: Capabilities insufficient. -## `rights`: Flags(`u64`) +## `rights`: `u64` File descriptor rights, determining which actions may be performed. Size: 8 Alignment: 8 -### Flags +### Constants - `fd_datasync` The right to invoke `fd_datasync`. If `path_open` is set, includes the right to invoke @@ -408,14 +408,14 @@ Size: 4 Alignment: 4 ### Supertypes -## `iovec`: Struct +## `iovec`: Record A region of memory for scatter/gather reads. Size: 8 Alignment: 4 -### Struct members +### Record members - `buf`: `Pointer` The address of the buffer to be filled. @@ -426,14 +426,14 @@ The length of the buffer to be filled. Offset: 4 -## `ciovec`: Struct +## `ciovec`: Record A region of memory for scatter/gather writes. Size: 8 Alignment: 4 -### Struct members +### Record members - `buf`: `ConstPointer` The address of the buffer to be written. @@ -444,13 +444,13 @@ The length of the buffer to be written. Offset: 4 -## `iovec_array`: `Array` +## `iovec_array`: `List` Size: 8 Alignment: 4 -## `ciovec_array`: `Array` +## `ciovec_array`: `List` Size: 8 @@ -463,14 +463,14 @@ Size: 8 Alignment: 8 -## `whence`: Enum(`u8`) +## `whence`: Variant The position relative to which to set the offset of the file descriptor. Size: 1 Alignment: 1 -### Variants +### Variant cases - `set` Seek relative to start-of-file. @@ -480,16 +480,15 @@ Seek relative to current position. - `end` Seek relative to end-of-file. -## `dircookie`: Int(`u64`) +## `dircookie`: `u64` A reference to the offset of a directory entry. Size: 8 Alignment: 8 -### Consts +### Constants - `start` -In an `fd_readdir` call, this value signifies the start of the directory. ## `dirnamlen`: `u32` The type for the [`dirent::d_namlen`](#dirent.d_namlen) field of [`dirent`](#dirent). @@ -505,14 +504,14 @@ Size: 8 Alignment: 8 -## `filetype`: Enum(`u8`) +## `filetype`: Variant The type of a file descriptor or file. Size: 1 Alignment: 1 -### Variants +### Variant cases - `unknown` The type of the file descriptor or file is unknown or is different from any of the other types specified. @@ -540,14 +539,14 @@ The file refers to a symbolic link inode. - `fifo` The file descriptor or file refers to a FIFO. -## `dirent`: Struct +## `dirent`: Record A directory entry. Size: 24 Alignment: 8 -### Struct members +### Record members - `d_next`: [`dircookie`](#dircookie) The offset of the next directory entry stored in this directory. @@ -568,14 +567,14 @@ The length of the name of the directory entry. Offset: 20 -## `advice`: Enum(`u8`) +## `advice`: Variant File or memory access pattern advisory information. Size: 1 Alignment: 1 -### Variants +### Variant cases - `normal` The application has no advice to give on its behavior with respect to the specified data. @@ -594,14 +593,14 @@ The application expects that it will not access the specified data in the near f - `noreuse` The application expects to access the specified data once and then not reuse it thereafter. -## `fdflags`: Flags(`u16`) +## `fdflags`: `u16` File descriptor flags. Size: 2 Alignment: 2 -### Flags +### Constants - `append` Append mode: Data written to the file is always appended to the file's end. @@ -619,14 +618,14 @@ Write according to synchronized I/O file integrity completion. In addition to synchronizing the data stored in the file, the implementation may also synchronously update the file's metadata. -## `fdstat`: Struct +## `fdstat`: Record File descriptor attributes. Size: 24 Alignment: 8 -### Struct members +### Record members - `fs_filetype`: [`filetype`](#filetype) File type. @@ -656,14 +655,14 @@ Size: 8 Alignment: 8 -## `fstflags`: Flags(`u16`) +## `fstflags`: `u16` Which file time attributes to adjust. Size: 2 Alignment: 2 -### Flags +### Constants - `atim` Adjust the last data access timestamp to the value stored in [`filestat::atim`](#filestat.atim). @@ -676,25 +675,25 @@ Adjust the last data modification timestamp to the value stored in [`filestat::m - `mtim_now` Adjust the last data modification timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -## `lookupflags`: Flags(`u32`) +## `lookupflags`: `u32` Flags determining the method of how paths are resolved. Size: 4 Alignment: 4 -### Flags +### Constants - `symlink_follow` As long as the resolved path corresponds to a symbolic link, it is expanded. -## `oflags`: Flags(`u16`) +## `oflags`: `u16` Open flags used by `path_open`. Size: 2 Alignment: 2 -### Flags +### Constants - `create` Create file if it does not exist. @@ -714,7 +713,7 @@ Size: 8 Alignment: 8 -## `permissions`: Flags(`u8`) +## `permissions`: `u8` File permissions. This represents the permissions associated with a file in a filesystem, and don't fully reflect all the conditions which determine whether a given WASI program can access the file. @@ -723,7 +722,7 @@ Size: 1 Alignment: 1 -### Flags +### Constants - `read` For files, permission to read the file. For directories, permission to do [`readdir`](#readdir) and access files @@ -748,14 +747,14 @@ indicates that the file is only accessible by the effective "user" that the WASI store uses to access the filesystem, and inaccessible to other "users". -## `filestat`: Struct +## `filestat`: Record File attributes. Size: 64 Alignment: 8 -### Struct members +### Record members - `dev`: [`device`](#device) Device ID of device containing the file. @@ -809,14 +808,14 @@ Size: 8 Alignment: 8 -## `eventtype`: Enum(`u8`) +## `eventtype`: Variant Type of a subscription to an event or its occurrence. Size: 1 Alignment: 1 -### Variants +### Variant cases - `clock` The time value of clock [`subscription_clock::id`](#subscription_clock.id) has reached timestamp [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -829,7 +828,7 @@ available for reading. This event always triggers for regular files. File descriptor [`subscription_fd_readwrite::fd`](#subscription_fd_readwrite.fd) has capacity available for writing. This event always triggers for regular files. -## `eventrwflags`: Flags(`u16`) +## `eventrwflags`: `u16` The state of the file descriptor subscribed to with [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -837,11 +836,11 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `fd_readwrite_hangup` The peer of this socket has closed or disconnected. -## `event_fd_readwrite`: Struct +## `event_fd_readwrite`: Record The contents of an [`event`](#event) when type is [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -849,7 +848,7 @@ Size: 16 Alignment: 8 -### Struct members +### Record members - `nbytes`: [`filesize`](#filesize) The number of bytes available for reading or writing. @@ -860,34 +859,32 @@ The state of the file descriptor. Offset: 8 -## `event_u`: Union +## `event_u`: Variant The contents of an [`event`](#event). Size: 24 Alignment: 8 -### Union Layout +### Variant Layout +- size: 24 +- align: 8 - tag_size: 1 -- tag_align: 1 -- contents_offset: 8 -- contents_size: 16 -- contents_align: 8 -### Union variants +### Variant cases - `fd_read`: [`event_fd_readwrite`](#event_fd_readwrite) - `fd_write`: [`event_fd_readwrite`](#event_fd_readwrite) - `clock` -## `event`: Struct +## `event`: Record An event that occurred. Size: 40 Alignment: 8 -### Struct members +### Record members - `userdata`: [`userdata`](#userdata) User-provided value that got attached to [`subscription::userdata`](#subscription.userdata). @@ -903,7 +900,7 @@ The type of the event that occurred, and the contents of the event Offset: 16 -## `subclockflags`: Flags(`u16`) +## `subclockflags`: `u16` Flags determining how to interpret the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -911,7 +908,7 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `subscription_clock_abstime` If set, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) as an absolute timestamp of clock @@ -919,14 +916,14 @@ If set, treat the timestamp provided in provided in [`subscription_clock::timeout`](#subscription_clock.timeout) relative to the current time value of clock [`subscription_clock::id`](#subscription_clock.id). -## `subscription_clock`: Struct +## `subscription_clock`: Record The contents of a [`subscription`](#subscription) when type is [`eventtype::clock`](#eventtype.clock). Size: 32 Alignment: 8 -### Struct members +### Record members - `id`: [`clockid`](#clockid) The clock against which to compare the timestamp. @@ -948,7 +945,7 @@ Flags specifying whether the timeout is absolute or relative Offset: 24 -## `subscription_fd_readwrite`: Struct +## `subscription_fd_readwrite`: Record The contents of a [`subscription`](#subscription) when type is type is [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -956,40 +953,38 @@ Size: 4 Alignment: 4 -### Struct members +### Record members - `fd`: [`fd`](#fd) The file descriptor on which to wait for it to become ready for reading or writing. Offset: 0 -## `subscription_u`: Union +## `subscription_u`: Variant The contents of a [`subscription`](#subscription). Size: 40 Alignment: 8 -### Union Layout +### Variant Layout +- size: 40 +- align: 8 - tag_size: 1 -- tag_align: 1 -- contents_offset: 8 -- contents_size: 32 -- contents_align: 8 -### Union variants +### Variant cases - `clock`: [`subscription_clock`](#subscription_clock) - `fd_read`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) - `fd_write`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) -## `subscription`: Struct +## `subscription`: Record Subscription to an event. Size: 48 Alignment: 8 -### Struct members +### Record members - `userdata`: [`userdata`](#userdata) User-provided value that is attached to the subscription in the implementation and returned through [`event::userdata`](#event.userdata). @@ -1008,28 +1003,28 @@ Size: 4 Alignment: 4 -## `riflags`: Flags(`u16`) +## `riflags`: `u16` Flags provided to `sock_recv`. Size: 2 Alignment: 2 -### Flags +### Constants - `recv_peek` Returns the message without removing it from the socket's receive queue. - `recv_waitall` On byte-stream sockets, block until the full amount of data can be returned. -## `roflags`: Flags(`u16`) +## `roflags`: `u16` Flags returned by `sock_recv`. Size: 2 Alignment: 2 -### Flags +### Constants - `recv_data_truncated` Returned by `sock_recv`: Message data has been truncated. @@ -1041,58 +1036,56 @@ Size: 2 Alignment: 2 -## `sdflags`: Flags(`u8`) +## `sdflags`: `u8` Which channels on a socket to shut down. Size: 1 Alignment: 1 -### Flags +### Constants - `rd` Disables further receive operations. - `wr` Disables further send operations. -## `preopentype`: Enum(`u8`) +## `preopentype`: Variant Identifiers for preopened capabilities. Size: 1 Alignment: 1 -### Variants +### Variant cases - `dir` A pre-opened directory. -## `prestat_dir`: Struct +## `prestat_dir`: Record The contents of a [`prestat`](#prestat) when its type is [`preopentype::dir`](#preopentype.dir). Size: 4 Alignment: 4 -### Struct members +### Record members - `pr_name_len`: [`size`](#size) The length of the directory name for use with `fd_prestat_dir_name`. Offset: 0 -## `prestat`: Union +## `prestat`: Variant Information about a pre-opened capability. Size: 8 Alignment: 4 -### Union Layout +### Variant Layout +- size: 8 +- align: 4 - tag_size: 1 -- tag_align: 1 -- contents_offset: 4 -- contents_size: 4 -- contents_align: 4 -### Union variants +### Variant cases - `dir`: [`prestat_dir`](#prestat_dir) When type is [`preopentype::dir`](#preopentype.dir): @@ -1104,14 +1097,14 @@ When type is [`preopentype::dir`](#preopentype.dir): --- -#### `get(argv: Pointer>, argv_buf: Pointer) -> errno` +#### `get(argv: Pointer>, argv_buf: Pointer) -> errno` Read command-line argument data. The size of the array should match that returned by [`sizes_get`](#sizes_get) ##### Params -- `argv`: `Pointer>` +- `argv`: `Pointer>` -- `argv_buf`: `Pointer` +- `argv_buf`: `Pointer` ##### Results - `error`: [`errno`](#errno) @@ -1182,14 +1175,14 @@ The time value of the clock. --- -#### `get(environ: Pointer>, environ_buf: Pointer) -> errno` +#### `get(environ: Pointer>, environ_buf: Pointer) -> errno` Read environment variable data. The sizes of the buffers should match that returned by [`sizes_get`](#sizes_get). ##### Params -- `environ`: `Pointer>` +- `environ`: `Pointer>` -- `environ_buf`: `Pointer` +- `environ_buf`: `Pointer` ##### Results - `error`: [`errno`](#errno) @@ -1450,13 +1443,13 @@ The buffer where the description is stored. --- -#### `prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> errno` +#### `prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> errno` Return a description of the given preopened file descriptor. ##### Params - `fd`: [`fd`](#fd) -- `path`: `Pointer` +- `path`: `Pointer` A buffer into which to write the preopened directory name. - `path_len`: [`size`](#size) @@ -1813,7 +1806,7 @@ The file descriptor of the file that has been opened. --- -#### `readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> (errno, size)` +#### `readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> (errno, size)` Read the contents of a symbolic link. Note: This is similar to `readlinkat` in POSIX. @@ -1823,7 +1816,7 @@ Note: This is similar to `readlinkat` in POSIX. - `path`: `string` The path of the symbolic link from which to read. -- `buf`: `Pointer` +- `buf`: `Pointer` The buffer to which to write the contents of the symbolic link. - `buf_len`: [`size`](#size) diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index 349952a15..503aa0cf6 100644 --- a/phases/ephemeral/witx/typenames.witx +++ b/phases/ephemeral/witx/typenames.witx @@ -18,7 +18,7 @@ ;;; Identifiers for clocks. (typename $clockid - (enum u32 + (enum (@witx tag u32) ;;; The clock measuring real time. Time value zero corresponds with ;;; 1970-01-01T00:00:00Z. $realtime @@ -35,7 +35,7 @@ ;;; API; some are used in higher-level library layers, and others are provided ;;; merely for alignment with POSIX. (typename $errno - (enum u16 + (enum (@witx tag u16) ;;; No error occurred. System call completed successfully. $success ;;; Argument list too long. @@ -195,7 +195,7 @@ ;;; File descriptor rights, determining which actions may be performed. (typename $rights - (flags u64 + (flags (@witx bitflags u64) ;;; The right to invoke `fd_datasync`. ;; ;;; If `path_open` is set, includes the right to invoke @@ -286,7 +286,7 @@ ;;; A region of memory for scatter/gather reads. (typename $iovec - (struct + (record ;;; The address of the buffer to be filled. (field $buf (@witx pointer u8)) ;;; The length of the buffer to be filled. @@ -296,7 +296,7 @@ ;;; A region of memory for scatter/gather writes. (typename $ciovec - (struct + (record ;;; The address of the buffer to be written. (field $buf (@witx const_pointer u8)) ;;; The length of the buffer to be written. @@ -304,15 +304,15 @@ ) ) -(typename $iovec_array (array $iovec)) -(typename $ciovec_array (array $ciovec)) +(typename $iovec_array (list $iovec)) +(typename $ciovec_array (list $ciovec)) ;;; Relative offset within a file. (typename $filedelta s64) ;;; The position relative to which to set the offset of the file descriptor. (typename $whence - (enum u8 + (enum (@witx tag u8) ;;; Seek relative to start-of-file. $set ;;; Seek relative to current position. @@ -323,12 +323,10 @@ ) ;;; A reference to the offset of a directory entry. -(typename $dircookie - (int u64 - ;;; In an `fd_readdir` call, this value signifies the start of the directory. - (const $start 0) - ) -) +(typename $dircookie u64) + +;;; In an `fd_readdir` call, this value signifies the start of the directory. +(@witx const $dircookie $start 0) ;;; The type for the `dirent::d_namlen` field of `dirent`. (typename $dirnamlen u32) @@ -338,7 +336,7 @@ ;;; The type of a file descriptor or file. (typename $filetype - (enum u8 + (enum (@witx tag u8) ;;; The type of the file descriptor or file is unknown or is different from any of the other types specified. $unknown ;;; The file descriptor or file refers to a block device inode. @@ -362,7 +360,7 @@ ;;; A directory entry. (typename $dirent - (struct + (record ;;; The offset of the next directory entry stored in this directory. (field $d_next $dircookie) ;;; The serial number of the file referred to by this directory entry. @@ -376,7 +374,7 @@ ;;; File or memory access pattern advisory information. (typename $advice - (enum u8 + (enum (@witx tag u8) ;;; The application has no advice to give on its behavior with respect to the specified data. $normal ;;; The application expects to access the specified data sequentially from lower offsets to higher offsets. @@ -394,7 +392,7 @@ ;;; File descriptor flags. (typename $fdflags - (flags u16 + (flags (@witx bitflags u16) ;;; Append mode: Data written to the file is always appended to the file's end. $append ;;; Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. @@ -412,7 +410,7 @@ ;;; File descriptor attributes. (typename $fdstat - (struct + (record ;;; File type. (field $fs_filetype $filetype) ;;; File descriptor flags. @@ -431,7 +429,7 @@ ;;; Which file time attributes to adjust. (typename $fstflags - (flags u16 + (flags (@witx bitflags u16) ;;; Adjust the last data access timestamp to the value stored in `filestat::atim`. $atim ;;; Adjust the last data access timestamp to the time of clock `clockid::realtime`. @@ -445,7 +443,7 @@ ;;; Flags determining the method of how paths are resolved. (typename $lookupflags - (flags u32 + (flags (@witx bitflags u32) ;;; As long as the resolved path corresponds to a symbolic link, it is expanded. $symlink_follow ) @@ -453,7 +451,7 @@ ;;; Open flags used by `path_open`. (typename $oflags - (flags u16 + (flags (@witx bitflags u16) ;;; Create file if it does not exist. $create ;;; Fail if not a directory. @@ -472,7 +470,7 @@ ;;; file in a filesystem, and don't fully reflect all the conditions ;;; which determine whether a given WASI program can access the file. (typename $permissions - (flags u8 + (flags (@witx bitflags u8) ;;; For files, permission to read the file. ;;; For directories, permission to do `readdir` and access files ;;; within the directory. @@ -501,7 +499,7 @@ ;;; File attributes. (typename $filestat - (struct + (record ;;; Device ID of device containing the file. (field $dev $device) ;;; File serial number. @@ -529,7 +527,7 @@ ;;; Type of a subscription to an event or its occurrence. (typename $eventtype - (enum u8 + (enum (@witx tag u8) ;;; The time value of clock `subscription_clock::id` has ;;; reached timestamp `subscription_clock::timeout`. $clock @@ -545,7 +543,7 @@ ;;; The state of the file descriptor subscribed to with ;;; `eventtype::fd_read` or `eventtype::fd_write`. (typename $eventrwflags - (flags u16 + (flags (@witx bitflags u16) ;;; The peer of this socket has closed or disconnected. $fd_readwrite_hangup ) @@ -554,7 +552,7 @@ ;;; The contents of an `event` when type is `eventtype::fd_read` or ;;; `eventtype::fd_write`. (typename $event_fd_readwrite - (struct + (record ;;; The number of bytes available for reading or writing. (field $nbytes $filesize) ;;; The state of the file descriptor. @@ -564,16 +562,16 @@ ;;; The contents of an `event`. (typename $event_u - (union $eventtype - (field $fd_read $event_fd_readwrite) - (field $fd_write $event_fd_readwrite) - (empty $clock) + (variant (@witx tag $eventtype) + (case $fd_read $event_fd_readwrite) + (case $fd_write $event_fd_readwrite) + (case $clock) ) ) ;;; An event that occurred. (typename $event - (struct + (record ;;; User-provided value that got attached to `subscription::userdata`. (field $userdata $userdata) ;;; If non-zero, an error that occurred while processing the subscription request. @@ -586,7 +584,7 @@ ;;; Flags determining how to interpret the timestamp provided in ;;; `subscription_clock::timeout`. (typename $subclockflags - (flags u16 + (flags (@witx bitflags u16) ;;; If set, treat the timestamp provided in ;;; `subscription_clock::timeout` as an absolute timestamp of clock ;;; `subscription_clock::id`. If clear, treat the timestamp @@ -598,7 +596,7 @@ ;;; The contents of a `subscription` when type is `eventtype::clock`. (typename $subscription_clock - (struct + (record ;;; The clock against which to compare the timestamp. (field $id $clockid) ;;; The absolute or relative timestamp. @@ -614,7 +612,7 @@ ;;; The contents of a `subscription` when type is type is ;;; `eventtype::fd_read` or `eventtype::fd_write`. (typename $subscription_fd_readwrite - (struct + (record ;;; The file descriptor on which to wait for it to become ready for reading or writing. (field $fd $fd) ) @@ -622,16 +620,16 @@ ;;; The contents of a `subscription`. (typename $subscription_u - (union $eventtype - (field $clock $subscription_clock) - (field $fd_read $subscription_fd_readwrite) - (field $fd_write $subscription_fd_readwrite) + (union (@witx tag $eventtype) + $subscription_clock + $subscription_fd_readwrite + $subscription_fd_readwrite ) ) ;;; Subscription to an event. (typename $subscription - (struct + (record ;;; User-provided value that is attached to the subscription in the ;;; implementation and returned through `event::userdata`. (field $userdata $userdata) @@ -645,7 +643,7 @@ ;;; Flags provided to `sock_recv`. (typename $riflags - (flags u16 + (flags (@witx bitflags u16) ;;; Returns the message without removing it from the socket's receive queue. $recv_peek ;;; On byte-stream sockets, block until the full amount of data can be returned. @@ -655,7 +653,7 @@ ;;; Flags returned by `sock_recv`. (typename $roflags - (flags u16 + (flags (@witx bitflags u16) ;;; Returned by `sock_recv`: Message data has been truncated. $recv_data_truncated ) @@ -667,7 +665,7 @@ ;;; Which channels on a socket to shut down. (typename $sdflags - (flags u8 + (flags (@witx bitflags u8) ;;; Disables further receive operations. $rd ;;; Disables further send operations. @@ -677,7 +675,7 @@ ;;; Identifiers for preopened capabilities. (typename $preopentype - (enum u8 + (enum (@witx tag u8) ;;; A pre-opened directory. $dir ) @@ -685,7 +683,7 @@ ;;; The contents of a `prestat` when its type is `preopentype::dir`. (typename $prestat_dir - (struct + (record ;;; The length of the directory name for use with `fd_prestat_dir_name`. (field $pr_name_len $size) ) @@ -693,8 +691,8 @@ ;;; Information about a pre-opened capability. (typename $prestat - (union $preopentype + (union (@witx tag $preopentype) ;;; When type is `preopentype::dir`: - (field $dir $prestat_dir) + $prestat_dir ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_args.witx b/phases/ephemeral/witx/wasi_ephemeral_args.witx index 485fafd27..803605fee 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_args.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_args.witx @@ -12,8 +12,8 @@ ;;; Read command-line argument data. ;;; The size of the array should match that returned by `sizes_get` (@interface func (export "get") - (param $argv (@witx pointer (@witx pointer char8))) - (param $argv_buf (@witx pointer char8)) + (param $argv (@witx pointer (@witx pointer (@witx char8)))) + (param $argv_buf (@witx pointer (@witx char8))) (result $error $errno) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_environ.witx b/phases/ephemeral/witx/wasi_ephemeral_environ.witx index ab564076d..6357fe602 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_environ.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_environ.witx @@ -12,8 +12,8 @@ ;;; Read environment variable data. ;;; The sizes of the buffers should match that returned by `sizes_get`. (@interface func (export "get") - (param $environ (@witx pointer (@witx pointer char8))) - (param $environ_buf (@witx pointer char8)) + (param $environ (@witx pointer (@witx pointer (@witx char8)))) + (param $environ_buf (@witx pointer (@witx char8))) (result $error $errno) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_fd.witx b/phases/ephemeral/witx/wasi_ephemeral_fd.witx index 821d3e707..8192c9b7f 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_fd.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_fd.witx @@ -151,7 +151,7 @@ (@interface func (export "prestat_dir_name") (param $fd $fd) ;;; A buffer into which to write the preopened directory name. - (param $path (@witx pointer char8)) + (param $path (@witx pointer (@witx char8))) (param $path_len $size) (result $error $errno) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_path.witx b/phases/ephemeral/witx/wasi_ephemeral_path.witx index 971a6fc9f..5b3e17e88 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_path.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_path.witx @@ -131,7 +131,7 @@ ;;; The path of the symbolic link from which to read. (param $path string) ;;; The buffer to which to write the contents of the symbolic link. - (param $buf (@witx pointer char8)) + (param $buf (@witx pointer (@witx char8))) (param $buf_len $size) (result $error $errno) ;;; The number of bytes placed in the buffer. diff --git a/phases/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index 34eab3c03..e77002bc6 100644 --- a/phases/old/snapshot_0/docs.md +++ b/phases/old/snapshot_0/docs.md @@ -19,14 +19,14 @@ Size: 8 Alignment: 8 -## `clockid`: Enum(`u32`) +## `clockid`: Variant Identifiers for clocks. Size: 4 Alignment: 4 -### Variants +### Variant cases - `realtime` The clock measuring real time. Time value zero corresponds with 1970-01-01T00:00:00Z. @@ -43,7 +43,7 @@ The CPU-time clock associated with the current process. - `thread_cputime_id` The CPU-time clock associated with the current thread. -## `errno`: Enum(`u16`) +## `errno`: Variant Error codes returned by functions. Not all of these error codes are returned by the functions provided by this API; some are used in higher-level library layers, and others are provided @@ -53,7 +53,7 @@ Size: 2 Alignment: 2 -### Variants +### Variant cases - `success` No error occurred. System call completed successfully. @@ -285,14 +285,14 @@ Cross-device link. - `notcapable` Extension: Capabilities insufficient. -## `rights`: Flags(`u64`) +## `rights`: `u64` File descriptor rights, determining which actions may be performed. Size: 8 Alignment: 8 -### Flags +### Constants - `fd_datasync` The right to invoke [`fd_datasync`](#fd_datasync). If [`rights::path_open`](#rights.path_open) is set, includes the right to invoke @@ -400,14 +400,14 @@ Size: 4 Alignment: 4 ### Supertypes -## `iovec`: Struct +## `iovec`: Record A region of memory for scatter/gather reads. Size: 8 Alignment: 4 -### Struct members +### Record members - `buf`: `Pointer` The address of the buffer to be filled. @@ -418,14 +418,14 @@ The length of the buffer to be filled. Offset: 4 -## `ciovec`: Struct +## `ciovec`: Record A region of memory for scatter/gather writes. Size: 8 Alignment: 4 -### Struct members +### Record members - `buf`: `ConstPointer` The address of the buffer to be written. @@ -436,13 +436,13 @@ The length of the buffer to be written. Offset: 4 -## `iovec_array`: `Array` +## `iovec_array`: `List` Size: 8 Alignment: 4 -## `ciovec_array`: `Array` +## `ciovec_array`: `List` Size: 8 @@ -455,14 +455,14 @@ Size: 8 Alignment: 8 -## `whence`: Enum(`u8`) +## `whence`: Variant The position relative to which to set the offset of the file descriptor. Size: 1 Alignment: 1 -### Variants +### Variant cases - `cur` Seek relative to current position. @@ -493,14 +493,14 @@ Size: 8 Alignment: 8 -## `filetype`: Enum(`u8`) +## `filetype`: Variant The type of a file descriptor or file. Size: 1 Alignment: 1 -### Variants +### Variant cases - `unknown` The type of the file descriptor or file is unknown or is different from any of the other types specified. @@ -525,14 +525,14 @@ The file descriptor or file refers to a byte-stream socket. - `symbolic_link` The file refers to a symbolic link inode. -## `dirent`: Struct +## `dirent`: Record A directory entry. Size: 24 Alignment: 8 -### Struct members +### Record members - `d_next`: [`dircookie`](#dircookie) The offset of the next directory entry stored in this directory. @@ -553,14 +553,14 @@ The type of the file referred to by this directory entry. Offset: 20 -## `advice`: Enum(`u8`) +## `advice`: Variant File or memory access pattern advisory information. Size: 1 Alignment: 1 -### Variants +### Variant cases - `normal` The application has no advice to give on its behavior with respect to the specified data. @@ -579,14 +579,14 @@ The application expects that it will not access the specified data in the near f - `noreuse` The application expects to access the specified data once and then not reuse it thereafter. -## `fdflags`: Flags(`u16`) +## `fdflags`: `u16` File descriptor flags. Size: 2 Alignment: 2 -### Flags +### Constants - `append` Append mode: Data written to the file is always appended to the file's end. @@ -604,14 +604,14 @@ Write according to synchronized I/O file integrity completion. In addition to synchronizing the data stored in the file, the implementation may also synchronously update the file's metadata. -## `fdstat`: Struct +## `fdstat`: Record File descriptor attributes. Size: 24 Alignment: 8 -### Struct members +### Record members - `fs_filetype`: [`filetype`](#filetype) File type. @@ -641,14 +641,14 @@ Size: 8 Alignment: 8 -## `fstflags`: Flags(`u16`) +## `fstflags`: `u16` Which file time attributes to adjust. Size: 2 Alignment: 2 -### Flags +### Constants - `atim` Adjust the last data access timestamp to the value stored in [`filestat::atim`](#filestat.atim). @@ -661,25 +661,25 @@ Adjust the last data modification timestamp to the value stored in [`filestat::m - `mtim_now` Adjust the last data modification timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -## `lookupflags`: Flags(`u32`) +## `lookupflags`: `u32` Flags determining the method of how paths are resolved. Size: 4 Alignment: 4 -### Flags +### Constants - `symlink_follow` As long as the resolved path corresponds to a symbolic link, it is expanded. -## `oflags`: Flags(`u16`) +## `oflags`: `u16` Open flags used by [`path_open`](#path_open). Size: 2 Alignment: 2 -### Flags +### Constants - `creat` Create file if it does not exist. @@ -699,14 +699,14 @@ Size: 4 Alignment: 4 -## `filestat`: Struct +## `filestat`: Record File attributes. Size: 56 Alignment: 8 -### Struct members +### Record members - `dev`: [`device`](#device) Device ID of device containing the file. @@ -755,14 +755,14 @@ Size: 8 Alignment: 8 -## `eventtype`: Enum(`u8`) +## `eventtype`: Variant Type of a subscription to an event or its occurrence. Size: 1 Alignment: 1 -### Variants +### Variant cases - `clock` The time value of clock [`subscription_clock::id`](#subscription_clock.id) has reached timestamp [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -775,7 +775,7 @@ available for reading. This event always triggers for regular files. File descriptor [`subscription_fd_readwrite::file_descriptor`](#subscription_fd_readwrite.file_descriptor) has capacity available for writing. This event always triggers for regular files. -## `eventrwflags`: Flags(`u16`) +## `eventrwflags`: `u16` The state of the file descriptor subscribed to with [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -783,11 +783,11 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `fd_readwrite_hangup` The peer of this socket has closed or disconnected. -## `event_fd_readwrite`: Struct +## `event_fd_readwrite`: Record The contents of an [`event`](#event) for the [`eventtype::fd_read`](#eventtype.fd_read) and [`eventtype::fd_write`](#eventtype.fd_write) variants @@ -795,7 +795,7 @@ Size: 16 Alignment: 8 -### Struct members +### Record members - `nbytes`: [`filesize`](#filesize) The number of bytes available for reading or writing. @@ -806,14 +806,14 @@ The state of the file descriptor. Offset: 8 -## `event`: Struct +## `event`: Record An event that occurred. Size: 32 Alignment: 8 -### Struct members +### Record members - `userdata`: [`userdata`](#userdata) User-provided value that got attached to [`subscription::userdata`](#subscription.userdata). @@ -835,7 +835,7 @@ The contents of the event, if it is an [`eventtype::fd_read`](#eventtype.fd_read Offset: 16 -## `subclockflags`: Flags(`u16`) +## `subclockflags`: `u16` Flags determining how to interpret the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -843,7 +843,7 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `subscription_clock_abstime` If set, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) as an absolute timestamp of clock @@ -851,14 +851,14 @@ If set, treat the timestamp provided in provided in [`subscription_clock::timeout`](#subscription_clock.timeout) relative to the current time value of clock [`subscription_clock::id`](#subscription_clock.id). -## `subscription_clock`: Struct +## `subscription_clock`: Record The contents of a [`subscription`](#subscription) when type is [`eventtype::clock`](#eventtype.clock). Size: 40 Alignment: 8 -### Struct members +### Record members - `identifier`: [`userdata`](#userdata) The user-defined unique identifier of the clock. @@ -885,7 +885,7 @@ Flags specifying whether the timeout is absolute or relative Offset: 32 -## `subscription_fd_readwrite`: Struct +## `subscription_fd_readwrite`: Record The contents of a [`subscription`](#subscription) when the variant is [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -893,40 +893,38 @@ Size: 4 Alignment: 4 -### Struct members +### Record members - `file_descriptor`: [`fd`](#fd) The file descriptor on which to wait for it to become ready for reading or writing. Offset: 0 -## `subscription_u`: Union +## `subscription_u`: Variant The contents of a [`subscription`](#subscription). Size: 48 Alignment: 8 -### Union Layout +### Variant Layout +- size: 48 +- align: 8 - tag_size: 1 -- tag_align: 1 -- contents_offset: 8 -- contents_size: 40 -- contents_align: 8 -### Union variants +### Variant cases - `clock`: [`subscription_clock`](#subscription_clock) - `fd_read`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) - `fd_write`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) -## `subscription`: Struct +## `subscription`: Record Subscription to an event. Size: 56 Alignment: 8 -### Struct members +### Record members - `userdata`: [`userdata`](#userdata) User-provided value that is attached to the subscription in the implementation and returned through [`event::userdata`](#event.userdata). @@ -945,14 +943,14 @@ Size: 4 Alignment: 4 -## `signal`: Enum(`u8`) +## `signal`: Variant Signal condition. Size: 1 Alignment: 1 -### Variants +### Variant cases - `none` No signal. Note that POSIX has special semantics for `kill(pid, 0)`, so this value is reserved. @@ -1077,28 +1075,28 @@ Action: Terminates the process. Bad system call. Action: Terminates the process. -## `riflags`: Flags(`u16`) +## `riflags`: `u16` Flags provided to [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Flags +### Constants - `recv_peek` Returns the message without removing it from the socket's receive queue. - `recv_waitall` On byte-stream sockets, block until the full amount of data can be returned. -## `roflags`: Flags(`u16`) +## `roflags`: `u16` Flags returned by [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Flags +### Constants - `recv_data_truncated` Returned by [`sock_recv`](#sock_recv): Message data has been truncated. @@ -1110,58 +1108,56 @@ Size: 2 Alignment: 2 -## `sdflags`: Flags(`u8`) +## `sdflags`: `u8` Which channels on a socket to shut down. Size: 1 Alignment: 1 -### Flags +### Constants - `rd` Disables further receive operations. - `wr` Disables further send operations. -## `preopentype`: Enum(`u8`) +## `preopentype`: Variant Identifiers for preopened capabilities. Size: 1 Alignment: 1 -### Variants +### Variant cases - `dir` A pre-opened directory. -## `prestat_dir`: Struct +## `prestat_dir`: Record The contents of a $prestat when type is [`preopentype::dir`](#preopentype.dir). Size: 4 Alignment: 4 -### Struct members +### Record members - `pr_name_len`: [`size`](#size) The length of the directory name for use with [`fd_prestat_dir_name`](#fd_prestat_dir_name). Offset: 0 -## `prestat`: Union +## `prestat`: Variant Information about a pre-opened capability. Size: 8 Alignment: 4 -### Union Layout +### Variant Layout +- size: 8 +- align: 4 - tag_size: 1 -- tag_align: 1 -- contents_offset: 4 -- contents_size: 4 -- contents_align: 4 -### Union variants +### Variant cases - `dir`: [`prestat_dir`](#prestat_dir) # Modules diff --git a/phases/old/snapshot_0/witx/typenames.witx b/phases/old/snapshot_0/witx/typenames.witx index 74ff0835a..eb6f70020 100644 --- a/phases/old/snapshot_0/witx/typenames.witx +++ b/phases/old/snapshot_0/witx/typenames.witx @@ -15,7 +15,7 @@ ;;; Identifiers for clocks. (typename $clockid - (enum u32 + (enum (@witx tag u32) ;;; The clock measuring real time. Time value zero corresponds with ;;; 1970-01-01T00:00:00Z. $realtime @@ -36,7 +36,7 @@ ;;; API; some are used in higher-level library layers, and others are provided ;;; merely for alignment with POSIX. (typename $errno - (enum u16 + (enum (@witx tag u16) ;;; No error occurred. System call completed successfully. $success ;;; Argument list too long. @@ -196,7 +196,7 @@ ;;; File descriptor rights, determining which actions may be performed. (typename $rights - (flags u64 + (flags (@witx bitflags u64) ;;; The right to invoke `fd_datasync`. ;; ;;; If `rights::path_open` is set, includes the right to invoke @@ -278,7 +278,7 @@ ;;; A region of memory for scatter/gather reads. (typename $iovec - (struct + (record ;;; The address of the buffer to be filled. (field $buf (@witx pointer u8)) ;;; The length of the buffer to be filled. @@ -288,7 +288,7 @@ ;;; A region of memory for scatter/gather writes. (typename $ciovec - (struct + (record ;;; The address of the buffer to be written. (field $buf (@witx const_pointer u8)) ;;; The length of the buffer to be written. @@ -296,15 +296,15 @@ ) ) -(typename $iovec_array (array $iovec)) -(typename $ciovec_array (array $ciovec)) +(typename $iovec_array (list $iovec)) +(typename $ciovec_array (list $ciovec)) ;;; Relative offset within a file. (typename $filedelta s64) ;;; The position relative to which to set the offset of the file descriptor. (typename $whence - (enum u8 + (enum (@witx tag u8) ;;; Seek relative to current position. $cur ;;; Seek relative to end-of-file. @@ -325,7 +325,7 @@ ;;; The type of a file descriptor or file. (typename $filetype - (enum u8 + (enum (@witx tag u8) ;;; The type of the file descriptor or file is unknown or is different from any of the other types specified. $unknown ;;; The file descriptor or file refers to a block device inode. @@ -347,7 +347,7 @@ ;;; A directory entry. (typename $dirent - (struct + (record ;;; The offset of the next directory entry stored in this directory. (field $d_next $dircookie) ;;; The serial number of the file referred to by this directory entry. @@ -361,7 +361,7 @@ ;;; File or memory access pattern advisory information. (typename $advice - (enum u8 + (enum (@witx tag u8) ;;; The application has no advice to give on its behavior with respect to the specified data. $normal ;;; The application expects to access the specified data sequentially from lower offsets to higher offsets. @@ -379,7 +379,7 @@ ;;; File descriptor flags. (typename $fdflags - (flags u16 + (flags (@witx bitflags u16) ;;; Append mode: Data written to the file is always appended to the file's end. $append ;;; Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. @@ -397,7 +397,7 @@ ;;; File descriptor attributes. (typename $fdstat - (struct + (record ;;; File type. (field $fs_filetype $filetype) ;;; File descriptor flags. @@ -416,7 +416,7 @@ ;;; Which file time attributes to adjust. (typename $fstflags - (flags u16 + (flags (@witx bitflags u16) ;;; Adjust the last data access timestamp to the value stored in `filestat::atim`. $atim ;;; Adjust the last data access timestamp to the time of clock `clockid::realtime`. @@ -430,7 +430,7 @@ ;;; Flags determining the method of how paths are resolved. (typename $lookupflags - (flags u32 + (flags (@witx bitflags u32) ;;; As long as the resolved path corresponds to a symbolic link, it is expanded. $symlink_follow ) @@ -438,7 +438,7 @@ ;;; Open flags used by `path_open`. (typename $oflags - (flags u16 + (flags (@witx bitflags u16) ;;; Create file if it does not exist. $creat ;;; Fail if not a directory. @@ -455,7 +455,7 @@ ;;; File attributes. (typename $filestat - (struct + (record ;;; Device ID of device containing the file. (field $dev $device) ;;; File serial number. @@ -481,7 +481,7 @@ ;;; Type of a subscription to an event or its occurrence. (typename $eventtype - (enum u8 + (enum (@witx tag u8) ;;; The time value of clock `subscription_clock::id` has ;;; reached timestamp `subscription_clock::timeout`. $clock @@ -497,7 +497,7 @@ ;;; The state of the file descriptor subscribed to with ;;; `eventtype::fd_read` or `eventtype::fd_write`. (typename $eventrwflags - (flags u16 + (flags (@witx bitflags u16) ;;; The peer of this socket has closed or disconnected. $fd_readwrite_hangup ) @@ -506,7 +506,7 @@ ;;; The contents of an `event` for the `eventtype::fd_read` and ;;; `eventtype::fd_write` variants (typename $event_fd_readwrite - (struct + (record ;;; The number of bytes available for reading or writing. (field $nbytes $filesize) ;;; The state of the file descriptor. @@ -516,7 +516,7 @@ ;;; An event that occurred. (typename $event - (struct + (record ;;; User-provided value that got attached to `subscription::userdata`. (field $userdata $userdata) ;;; If non-zero, an error that occurred while processing the subscription request. @@ -532,7 +532,7 @@ ;;; Flags determining how to interpret the timestamp provided in ;;; `subscription_clock::timeout`. (typename $subclockflags - (flags u16 + (flags (@witx bitflags u16) ;;; If set, treat the timestamp provided in ;;; `subscription_clock::timeout` as an absolute timestamp of clock ;;; `subscription_clock::id`. If clear, treat the timestamp @@ -544,7 +544,7 @@ ;;; The contents of a `subscription` when type is `eventtype::clock`. (typename $subscription_clock - (struct + (record ;;; The user-defined unique identifier of the clock. (field $identifier $userdata) ;;; The clock against which to compare the timestamp. @@ -562,7 +562,7 @@ ;;; The contents of a `subscription` when the variant is ;;; `eventtype::fd_read` or `eventtype::fd_write`. (typename $subscription_fd_readwrite - (struct + (record ;;; The file descriptor on which to wait for it to become ready for reading or writing. (field $file_descriptor $fd) ) @@ -570,16 +570,16 @@ ;;; The contents of a `subscription`. (typename $subscription_u - (union $eventtype - (field $clock $subscription_clock) - (field $fd_read $subscription_fd_readwrite) - (field $fd_write $subscription_fd_readwrite) + (union (@witx tag $eventtype) + $subscription_clock + $subscription_fd_readwrite + $subscription_fd_readwrite ) ) ;;; Subscription to an event. (typename $subscription - (struct + (record ;;; User-provided value that is attached to the subscription in the ;;; implementation and returned through `event::userdata`. (field $userdata $userdata) @@ -593,7 +593,7 @@ ;;; Signal condition. (typename $signal - (enum u8 + (enum (@witx tag u8) ;;; No signal. Note that POSIX has special semantics for `kill(pid, 0)`, ;;; so this value is reserved. $none @@ -692,7 +692,7 @@ ;;; Flags provided to `sock_recv`. (typename $riflags - (flags u16 + (flags (@witx bitflags u16) ;;; Returns the message without removing it from the socket's receive queue. $recv_peek ;;; On byte-stream sockets, block until the full amount of data can be returned. @@ -702,7 +702,7 @@ ;;; Flags returned by `sock_recv`. (typename $roflags - (flags u16 + (flags (@witx bitflags u16) ;;; Returned by `sock_recv`: Message data has been truncated. $recv_data_truncated ) @@ -714,7 +714,7 @@ ;;; Which channels on a socket to shut down. (typename $sdflags - (flags u8 + (flags (@witx bitflags u8) ;;; Disables further receive operations. $rd ;;; Disables further send operations. @@ -724,7 +724,7 @@ ;;; Identifiers for preopened capabilities. (typename $preopentype - (enum u8 + (enum (@witx tag u8) ;;; A pre-opened directory. $dir ) @@ -732,7 +732,7 @@ ;;; The contents of a $prestat when type is `preopentype::dir`. (typename $prestat_dir - (struct + (record ;;; The length of the directory name for use with `fd_prestat_dir_name`. (field $pr_name_len $size) ) @@ -740,7 +740,7 @@ ;;; Information about a pre-opened capability. (typename $prestat - (union $preopentype - (field $dir $prestat_dir) + (union (@witx tag $preopentype) + $prestat_dir ) ) diff --git a/phases/snapshot/docs.md b/phases/snapshot/docs.md index b56e8c533..1bbbe59f2 100644 --- a/phases/snapshot/docs.md +++ b/phases/snapshot/docs.md @@ -19,14 +19,14 @@ Size: 8 Alignment: 8 -## `clockid`: Enum(`u32`) +## `clockid`: Variant Identifiers for clocks. Size: 4 Alignment: 4 -### Variants +### Variant cases - `realtime` The clock measuring real time. Time value zero corresponds with 1970-01-01T00:00:00Z. @@ -43,7 +43,7 @@ The CPU-time clock associated with the current process. - `thread_cputime_id` The CPU-time clock associated with the current thread. -## `errno`: Enum(`u16`) +## `errno`: Variant Error codes returned by functions. Not all of these error codes are returned by the functions provided by this API; some are used in higher-level library layers, and others are provided @@ -53,7 +53,7 @@ Size: 2 Alignment: 2 -### Variants +### Variant cases - `success` No error occurred. System call completed successfully. @@ -285,14 +285,14 @@ Cross-device link. - `notcapable` Extension: Capabilities insufficient. -## `rights`: Flags(`u64`) +## `rights`: `u64` File descriptor rights, determining which actions may be performed. Size: 8 Alignment: 8 -### Flags +### Constants - `fd_datasync` The right to invoke [`fd_datasync`](#fd_datasync). If [`path_open`](#path_open) is set, includes the right to invoke @@ -400,14 +400,14 @@ Size: 4 Alignment: 4 ### Supertypes -## `iovec`: Struct +## `iovec`: Record A region of memory for scatter/gather reads. Size: 8 Alignment: 4 -### Struct members +### Record members - `buf`: `Pointer` The address of the buffer to be filled. @@ -418,14 +418,14 @@ The length of the buffer to be filled. Offset: 4 -## `ciovec`: Struct +## `ciovec`: Record A region of memory for scatter/gather writes. Size: 8 Alignment: 4 -### Struct members +### Record members - `buf`: `ConstPointer` The address of the buffer to be written. @@ -436,13 +436,13 @@ The length of the buffer to be written. Offset: 4 -## `iovec_array`: `Array` +## `iovec_array`: `List` Size: 8 Alignment: 4 -## `ciovec_array`: `Array` +## `ciovec_array`: `List` Size: 8 @@ -455,14 +455,14 @@ Size: 8 Alignment: 8 -## `whence`: Enum(`u8`) +## `whence`: Variant The position relative to which to set the offset of the file descriptor. Size: 1 Alignment: 1 -### Variants +### Variant cases - `set` Seek relative to start-of-file. @@ -495,14 +495,14 @@ Size: 8 Alignment: 8 -## `filetype`: Enum(`u8`) +## `filetype`: Variant The type of a file descriptor or file. Size: 1 Alignment: 1 -### Variants +### Variant cases - `unknown` The type of the file descriptor or file is unknown or is different from any of the other types specified. @@ -527,14 +527,14 @@ The file descriptor or file refers to a byte-stream socket. - `symbolic_link` The file refers to a symbolic link inode. -## `dirent`: Struct +## `dirent`: Record A directory entry. Size: 24 Alignment: 8 -### Struct members +### Record members - `d_next`: [`dircookie`](#dircookie) The offset of the next directory entry stored in this directory. @@ -555,14 +555,14 @@ The type of the file referred to by this directory entry. Offset: 20 -## `advice`: Enum(`u8`) +## `advice`: Variant File or memory access pattern advisory information. Size: 1 Alignment: 1 -### Variants +### Variant cases - `normal` The application has no advice to give on its behavior with respect to the specified data. @@ -581,14 +581,14 @@ The application expects that it will not access the specified data in the near f - `noreuse` The application expects to access the specified data once and then not reuse it thereafter. -## `fdflags`: Flags(`u16`) +## `fdflags`: `u16` File descriptor flags. Size: 2 Alignment: 2 -### Flags +### Constants - `append` Append mode: Data written to the file is always appended to the file's end. @@ -606,14 +606,14 @@ Write according to synchronized I/O file integrity completion. In addition to synchronizing the data stored in the file, the implementation may also synchronously update the file's metadata. -## `fdstat`: Struct +## `fdstat`: Record File descriptor attributes. Size: 24 Alignment: 8 -### Struct members +### Record members - `fs_filetype`: [`filetype`](#filetype) File type. @@ -643,14 +643,14 @@ Size: 8 Alignment: 8 -## `fstflags`: Flags(`u16`) +## `fstflags`: `u16` Which file time attributes to adjust. Size: 2 Alignment: 2 -### Flags +### Constants - `atim` Adjust the last data access timestamp to the value stored in [`filestat::atim`](#filestat.atim). @@ -663,25 +663,25 @@ Adjust the last data modification timestamp to the value stored in [`filestat::m - `mtim_now` Adjust the last data modification timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -## `lookupflags`: Flags(`u32`) +## `lookupflags`: `u32` Flags determining the method of how paths are resolved. Size: 4 Alignment: 4 -### Flags +### Constants - `symlink_follow` As long as the resolved path corresponds to a symbolic link, it is expanded. -## `oflags`: Flags(`u16`) +## `oflags`: `u16` Open flags used by [`path_open`](#path_open). Size: 2 Alignment: 2 -### Flags +### Constants - `creat` Create file if it does not exist. @@ -701,14 +701,14 @@ Size: 8 Alignment: 8 -## `filestat`: Struct +## `filestat`: Record File attributes. Size: 64 Alignment: 8 -### Struct members +### Record members - `dev`: [`device`](#device) Device ID of device containing the file. @@ -757,14 +757,14 @@ Size: 8 Alignment: 8 -## `eventtype`: Enum(`u8`) +## `eventtype`: Variant Type of a subscription to an event or its occurrence. Size: 1 Alignment: 1 -### Variants +### Variant cases - `clock` The time value of clock [`subscription_clock::id`](#subscription_clock.id) has reached timestamp [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -777,7 +777,7 @@ available for reading. This event always triggers for regular files. File descriptor [`subscription_fd_readwrite::file_descriptor`](#subscription_fd_readwrite.file_descriptor) has capacity available for writing. This event always triggers for regular files. -## `eventrwflags`: Flags(`u16`) +## `eventrwflags`: `u16` The state of the file descriptor subscribed to with [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -785,11 +785,11 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `fd_readwrite_hangup` The peer of this socket has closed or disconnected. -## `event_fd_readwrite`: Struct +## `event_fd_readwrite`: Record The contents of an [`event`](#event) when type is [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -797,7 +797,7 @@ Size: 16 Alignment: 8 -### Struct members +### Record members - `nbytes`: [`filesize`](#filesize) The number of bytes available for reading or writing. @@ -808,14 +808,14 @@ The state of the file descriptor. Offset: 8 -## `event`: Struct +## `event`: Record An event that occurred. Size: 32 Alignment: 8 -### Struct members +### Record members - `userdata`: [`userdata`](#userdata) User-provided value that got attached to [`subscription::userdata`](#subscription.userdata). @@ -837,7 +837,7 @@ The contents of the event, if it is an [`eventtype::fd_read`](#eventtype.fd_read Offset: 16 -## `subclockflags`: Flags(`u16`) +## `subclockflags`: `u16` Flags determining how to interpret the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -845,7 +845,7 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `subscription_clock_abstime` If set, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) as an absolute timestamp of clock @@ -853,14 +853,14 @@ If set, treat the timestamp provided in provided in [`subscription_clock::timeout`](#subscription_clock.timeout) relative to the current time value of clock [`subscription_clock::id`](#subscription_clock.id). -## `subscription_clock`: Struct +## `subscription_clock`: Record The contents of a [`subscription`](#subscription) when type is [`eventtype::clock`](#eventtype.clock). Size: 32 Alignment: 8 -### Struct members +### Record members - `id`: [`clockid`](#clockid) The clock against which to compare the timestamp. @@ -882,7 +882,7 @@ Flags specifying whether the timeout is absolute or relative Offset: 24 -## `subscription_fd_readwrite`: Struct +## `subscription_fd_readwrite`: Record The contents of a [`subscription`](#subscription) when type is type is [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -890,40 +890,38 @@ Size: 4 Alignment: 4 -### Struct members +### Record members - `file_descriptor`: [`fd`](#fd) The file descriptor on which to wait for it to become ready for reading or writing. Offset: 0 -## `subscription_u`: Union +## `subscription_u`: Variant The contents of a [`subscription`](#subscription). Size: 40 Alignment: 8 -### Union Layout +### Variant Layout +- size: 40 +- align: 8 - tag_size: 1 -- tag_align: 1 -- contents_offset: 8 -- contents_size: 32 -- contents_align: 8 -### Union variants +### Variant cases - `clock`: [`subscription_clock`](#subscription_clock) - `fd_read`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) - `fd_write`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) -## `subscription`: Struct +## `subscription`: Record Subscription to an event. Size: 48 Alignment: 8 -### Struct members +### Record members - `userdata`: [`userdata`](#userdata) User-provided value that is attached to the subscription in the implementation and returned through [`event::userdata`](#event.userdata). @@ -942,14 +940,14 @@ Size: 4 Alignment: 4 -## `signal`: Enum(`u8`) +## `signal`: Variant Signal condition. Size: 1 Alignment: 1 -### Variants +### Variant cases - `none` No signal. Note that POSIX has special semantics for `kill(pid, 0)`, so this value is reserved. @@ -1074,28 +1072,28 @@ Action: Terminates the process. Bad system call. Action: Terminates the process. -## `riflags`: Flags(`u16`) +## `riflags`: `u16` Flags provided to [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Flags +### Constants - `recv_peek` Returns the message without removing it from the socket's receive queue. - `recv_waitall` On byte-stream sockets, block until the full amount of data can be returned. -## `roflags`: Flags(`u16`) +## `roflags`: `u16` Flags returned by [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Flags +### Constants - `recv_data_truncated` Returned by [`sock_recv`](#sock_recv): Message data has been truncated. @@ -1107,58 +1105,56 @@ Size: 2 Alignment: 2 -## `sdflags`: Flags(`u8`) +## `sdflags`: `u8` Which channels on a socket to shut down. Size: 1 Alignment: 1 -### Flags +### Constants - `rd` Disables further receive operations. - `wr` Disables further send operations. -## `preopentype`: Enum(`u8`) +## `preopentype`: Variant Identifiers for preopened capabilities. Size: 1 Alignment: 1 -### Variants +### Variant cases - `dir` A pre-opened directory. -## `prestat_dir`: Struct +## `prestat_dir`: Record The contents of a $prestat when type is [`preopentype::dir`](#preopentype.dir). Size: 4 Alignment: 4 -### Struct members +### Record members - `pr_name_len`: [`size`](#size) The length of the directory name for use with [`fd_prestat_dir_name`](#fd_prestat_dir_name). Offset: 0 -## `prestat`: Union +## `prestat`: Variant Information about a pre-opened capability. Size: 8 Alignment: 4 -### Union Layout +### Variant Layout +- size: 8 +- align: 4 - tag_size: 1 -- tag_align: 1 -- contents_offset: 4 -- contents_size: 4 -- contents_align: 4 -### Union variants +### Variant cases - `dir`: [`prestat_dir`](#prestat_dir) # Modules diff --git a/phases/snapshot/witx/typenames.witx b/phases/snapshot/witx/typenames.witx index b77372731..70e726553 100644 --- a/phases/snapshot/witx/typenames.witx +++ b/phases/snapshot/witx/typenames.witx @@ -15,7 +15,7 @@ ;;; Identifiers for clocks. (typename $clockid - (enum u32 + (enum (@witx tag u32) ;;; The clock measuring real time. Time value zero corresponds with ;;; 1970-01-01T00:00:00Z. $realtime @@ -36,7 +36,7 @@ ;;; API; some are used in higher-level library layers, and others are provided ;;; merely for alignment with POSIX. (typename $errno - (enum u16 + (enum (@witx tag u16) ;;; No error occurred. System call completed successfully. $success ;;; Argument list too long. @@ -196,7 +196,7 @@ ;;; File descriptor rights, determining which actions may be performed. (typename $rights - (flags u64 + (flags (@witx bitflags u64) ;;; The right to invoke `fd_datasync`. ;; ;;; If `path_open` is set, includes the right to invoke @@ -278,7 +278,7 @@ ;;; A region of memory for scatter/gather reads. (typename $iovec - (struct + (record ;;; The address of the buffer to be filled. (field $buf (@witx pointer u8)) ;;; The length of the buffer to be filled. @@ -288,7 +288,7 @@ ;;; A region of memory for scatter/gather writes. (typename $ciovec - (struct + (record ;;; The address of the buffer to be written. (field $buf (@witx const_pointer u8)) ;;; The length of the buffer to be written. @@ -296,15 +296,15 @@ ) ) -(typename $iovec_array (array $iovec)) -(typename $ciovec_array (array $ciovec)) +(typename $iovec_array (list $iovec)) +(typename $ciovec_array (list $ciovec)) ;;; Relative offset within a file. (typename $filedelta s64) ;;; The position relative to which to set the offset of the file descriptor. (typename $whence - (enum u8 + (enum (@witx tag u8) ;;; Seek relative to start-of-file. $set ;;; Seek relative to current position. @@ -327,7 +327,7 @@ ;;; The type of a file descriptor or file. (typename $filetype - (enum u8 + (enum (@witx tag u8) ;;; The type of the file descriptor or file is unknown or is different from any of the other types specified. $unknown ;;; The file descriptor or file refers to a block device inode. @@ -349,7 +349,7 @@ ;;; A directory entry. (typename $dirent - (struct + (record ;;; The offset of the next directory entry stored in this directory. (field $d_next $dircookie) ;;; The serial number of the file referred to by this directory entry. @@ -363,7 +363,7 @@ ;;; File or memory access pattern advisory information. (typename $advice - (enum u8 + (enum (@witx tag u8) ;;; The application has no advice to give on its behavior with respect to the specified data. $normal ;;; The application expects to access the specified data sequentially from lower offsets to higher offsets. @@ -381,7 +381,7 @@ ;;; File descriptor flags. (typename $fdflags - (flags u16 + (flags (@witx bitflags u16) ;;; Append mode: Data written to the file is always appended to the file's end. $append ;;; Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. @@ -399,7 +399,7 @@ ;;; File descriptor attributes. (typename $fdstat - (struct + (record ;;; File type. (field $fs_filetype $filetype) ;;; File descriptor flags. @@ -418,7 +418,7 @@ ;;; Which file time attributes to adjust. (typename $fstflags - (flags u16 + (flags (@witx bitflags u16) ;;; Adjust the last data access timestamp to the value stored in `filestat::atim`. $atim ;;; Adjust the last data access timestamp to the time of clock `clockid::realtime`. @@ -432,7 +432,7 @@ ;;; Flags determining the method of how paths are resolved. (typename $lookupflags - (flags u32 + (flags (@witx bitflags u32) ;;; As long as the resolved path corresponds to a symbolic link, it is expanded. $symlink_follow ) @@ -440,7 +440,7 @@ ;;; Open flags used by `path_open`. (typename $oflags - (flags u16 + (flags (@witx bitflags u16) ;;; Create file if it does not exist. $creat ;;; Fail if not a directory. @@ -457,7 +457,7 @@ ;;; File attributes. (typename $filestat - (struct + (record ;;; Device ID of device containing the file. (field $dev $device) ;;; File serial number. @@ -483,7 +483,7 @@ ;;; Type of a subscription to an event or its occurrence. (typename $eventtype - (enum u8 + (enum (@witx tag u8) ;;; The time value of clock `subscription_clock::id` has ;;; reached timestamp `subscription_clock::timeout`. $clock @@ -499,7 +499,7 @@ ;;; The state of the file descriptor subscribed to with ;;; `eventtype::fd_read` or `eventtype::fd_write`. (typename $eventrwflags - (flags u16 + (flags (@witx bitflags u16) ;;; The peer of this socket has closed or disconnected. $fd_readwrite_hangup ) @@ -508,7 +508,7 @@ ;;; The contents of an `event` when type is `eventtype::fd_read` or ;;; `eventtype::fd_write`. (typename $event_fd_readwrite - (struct + (record ;;; The number of bytes available for reading or writing. (field $nbytes $filesize) ;;; The state of the file descriptor. @@ -518,7 +518,7 @@ ;;; An event that occurred. (typename $event - (struct + (record ;;; User-provided value that got attached to `subscription::userdata`. (field $userdata $userdata) ;;; If non-zero, an error that occurred while processing the subscription request. @@ -534,7 +534,7 @@ ;;; Flags determining how to interpret the timestamp provided in ;;; `subscription_clock::timeout`. (typename $subclockflags - (flags u16 + (flags (@witx bitflags u16) ;;; If set, treat the timestamp provided in ;;; `subscription_clock::timeout` as an absolute timestamp of clock ;;; `subscription_clock::id`. If clear, treat the timestamp @@ -546,7 +546,7 @@ ;;; The contents of a `subscription` when type is `eventtype::clock`. (typename $subscription_clock - (struct + (record ;;; The clock against which to compare the timestamp. (field $id $clockid) ;;; The absolute or relative timestamp. @@ -562,7 +562,7 @@ ;;; The contents of a `subscription` when type is type is ;;; `eventtype::fd_read` or `eventtype::fd_write`. (typename $subscription_fd_readwrite - (struct + (record ;;; The file descriptor on which to wait for it to become ready for reading or writing. (field $file_descriptor $fd) ) @@ -570,16 +570,17 @@ ;;; The contents of a `subscription`. (typename $subscription_u - (union $eventtype - (field $clock $subscription_clock) - (field $fd_read $subscription_fd_readwrite) - (field $fd_write $subscription_fd_readwrite) + (union + (@witx tag $eventtype) + $subscription_clock + $subscription_fd_readwrite + $subscription_fd_readwrite ) ) ;;; Subscription to an event. (typename $subscription - (struct + (record ;;; User-provided value that is attached to the subscription in the ;;; implementation and returned through `event::userdata`. (field $userdata $userdata) @@ -593,7 +594,7 @@ ;;; Signal condition. (typename $signal - (enum u8 + (enum (@witx tag u8) ;;; No signal. Note that POSIX has special semantics for `kill(pid, 0)`, ;;; so this value is reserved. $none @@ -692,7 +693,7 @@ ;;; Flags provided to `sock_recv`. (typename $riflags - (flags u16 + (flags (@witx bitflags u16) ;;; Returns the message without removing it from the socket's receive queue. $recv_peek ;;; On byte-stream sockets, block until the full amount of data can be returned. @@ -702,7 +703,7 @@ ;;; Flags returned by `sock_recv`. (typename $roflags - (flags u16 + (flags (@witx bitflags u16) ;;; Returned by `sock_recv`: Message data has been truncated. $recv_data_truncated ) @@ -714,7 +715,7 @@ ;;; Which channels on a socket to shut down. (typename $sdflags - (flags u8 + (flags (@witx bitflags u8) ;;; Disables further receive operations. $rd ;;; Disables further send operations. @@ -724,7 +725,7 @@ ;;; Identifiers for preopened capabilities. (typename $preopentype - (enum u8 + (enum (@witx tag u8) ;;; A pre-opened directory. $dir ) @@ -732,7 +733,7 @@ ;;; The contents of a $prestat when type is `preopentype::dir`. (typename $prestat_dir - (struct + (record ;;; The length of the directory name for use with `fd_prestat_dir_name`. (field $pr_name_len $size) ) @@ -740,8 +741,8 @@ ;;; Information about a pre-opened capability. (typename $prestat - (union $preopentype - (field $dir $prestat_dir) + (union (@witx tag $preopentype) + $prestat_dir ) ) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 2e30836b1..7d34b5f7f 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -87,6 +87,13 @@ impl Document { _ => None, }) } + + pub fn constants<'a>(&'a self) -> impl Iterator + 'a { + self.definitions.iter().filter_map(|d| match d { + Definition::Constant(c) => Some(c), + _ => None, + }) + } } impl PartialEq for Document { @@ -108,6 +115,7 @@ impl std::hash::Hash for Document { pub enum Definition { Typename(Rc), Module(Rc), + Constant(Constant), } #[derive(Debug, Clone)] @@ -177,13 +185,10 @@ impl NamedType { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { - Enum(EnumDatatype), - Int(IntDatatype), - Flags(FlagsDatatype), - Struct(StructDatatype), - Union(UnionDatatype), + Record(RecordDatatype), + Variant(Variant), Handle(HandleDatatype), - Array(TypeRef), + List(TypeRef), Pointer(TypeRef), ConstPointer(TypeRef), Builtin(BuiltinType), @@ -193,13 +198,10 @@ impl Type { pub fn kind(&self) -> &'static str { use Type::*; match self { - Enum(_) => "enum", - Int(_) => "int", - Flags(_) => "flags", - Struct(_) => "struct", - Union(_) => "union", + Record(_) => "record", + Variant(_) => "variant", Handle(_) => "handle", - Array(_) => "array", + List(_) => "list", Pointer(_) => "pointer", ConstPointer(_) => "constpointer", Builtin(_) => "builtin", @@ -209,12 +211,33 @@ impl Type { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BuiltinType { - String, - Char8, - USize, - U8, + /// This is a 32-bit unicode scalar value, not a code point. + /// + /// Same as the Rust language's `char` type. + Char, + U8 { + /// Indicates whether this type is intended to represent the `char` + /// type in the C language. The C `char` type is often unsigned, but + /// it's language-specific. At an interface-types level this is an + /// unsigned byte but binding generators may wish to bind this as the + /// language-specific representation for a C character instead. + /// + /// This is also currently used exclusively in conjunction with `@witx + /// pointer` to hint that it's pointing to unicode string data as well. + lang_c_char: bool, + }, U16, - U32, + U32 { + /// Indicates that this 32-bit value should actually be considered a + /// pointer-like value in language bindings. At the interface types + /// layer this is always a 32-bit unsigned value, but binding + /// generators may wish to instead bind this as the equivalent of C's + /// `size_t` for convenience with other APIs. + /// + /// This allows witx authors to communicate the intent that the + /// argument or return-value is pointer-like. + lang_ptr_size: bool, + }, U64, S8, S16, @@ -232,63 +255,39 @@ pub enum IntRepr { U64, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct EnumDatatype { - pub repr: IntRepr, - pub variants: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct EnumVariant { - pub name: Id, - pub docs: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct IntDatatype { - pub repr: IntRepr, - pub consts: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct IntConst { - pub name: Id, - pub value: u64, - pub docs: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FlagsDatatype { - pub repr: IntRepr, - pub flags: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FlagsMember { - pub name: Id, - pub docs: String, +impl IntRepr { + pub fn to_builtin(&self) -> BuiltinType { + match self { + IntRepr::U8 => BuiltinType::U8 { lang_c_char: false }, + IntRepr::U16 => BuiltinType::U16, + IntRepr::U32 => BuiltinType::U32 { + lang_ptr_size: false, + }, + IntRepr::U64 => BuiltinType::U64, + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StructDatatype { - pub members: Vec, +pub struct RecordDatatype { + pub members: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StructMember { +pub struct RecordMember { pub name: Id, pub tref: TypeRef, pub docs: String, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UnionDatatype { - pub tag: Rc, - pub variants: Vec, +pub struct Variant { + pub tag_repr: IntRepr, + pub cases: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UnionVariant { +pub struct Case { pub name: Id, pub tref: Option, pub docs: String, @@ -430,3 +429,11 @@ pub enum InterfaceFuncParamPosition { Param(usize), Result(usize), } + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Constant { + pub ty: Id, + pub name: Id, + pub value: u64, + pub docs: String, +} diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 84756209d..826990369 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -35,25 +35,27 @@ impl Type { pub fn passed_by(&self) -> TypePassedBy { match self { Type::Builtin(b) => match b { - BuiltinType::String => TypePassedBy::PointerLengthPair, - BuiltinType::U8 + BuiltinType::U8 { .. } | BuiltinType::U16 - | BuiltinType::U32 + | BuiltinType::U32 { .. } | BuiltinType::S8 | BuiltinType::S16 | BuiltinType::S32 - | BuiltinType::Char8 - | BuiltinType::USize => TypePassedBy::Value(AtomType::I32), + | BuiltinType::Char => TypePassedBy::Value(AtomType::I32), BuiltinType::U64 | BuiltinType::S64 => TypePassedBy::Value(AtomType::I64), BuiltinType::F32 => TypePassedBy::Value(AtomType::F32), BuiltinType::F64 => TypePassedBy::Value(AtomType::F64), }, - Type::Array { .. } => TypePassedBy::PointerLengthPair, + Type::List { .. } => TypePassedBy::PointerLengthPair, Type::Pointer { .. } | Type::ConstPointer { .. } => TypePassedBy::Value(AtomType::I32), - Type::Enum(e) => TypePassedBy::Value(e.repr.into()), - Type::Int(i) => TypePassedBy::Value(i.repr.into()), - Type::Flags(f) => TypePassedBy::Value(f.repr.into()), - Type::Struct { .. } | Type::Union { .. } => TypePassedBy::Pointer, + Type::Record { .. } => TypePassedBy::Pointer, + Type::Variant(v) => { + if v.cases.iter().all(|c| c.tref.is_none()) { + TypePassedBy::Value(v.tag_repr.into()) + } else { + TypePassedBy::Pointer + } + } Type::Handle { .. } => TypePassedBy::Value(AtomType::I32), } } diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index 02ef9ec22..6048f73a1 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -3,15 +3,12 @@ use super::{ Documentation, }; use crate::{ - ast::{ - BuiltinType, Document, EnumDatatype, FlagsDatatype, HandleDatatype, IntDatatype, IntRepr, - InterfaceFunc, InterfaceFuncParam, Module, ModuleImport, ModuleImportVariant, NamedType, - StructDatatype, Type, TypeRef, UnionDatatype, - }, + ast::*, layout::Layout, polyfill::{FuncPolyfill, ModulePolyfill, ParamPolyfill, Polyfill, TypePolyfill}, RepEquality, }; +use std::collections::HashMap; fn heading_from_node(node: &MdNodeRef, levels_down: usize) -> MdHeading { MdHeading::new_header(node.borrow().ancestors().len() + levels_down) @@ -21,6 +18,12 @@ impl ToMarkdown for Document { fn generate(&self, node: MdNodeRef) { let heading = heading_from_node(&node, 1); let types = node.new_child(MdSection::new(heading, "Types")); + + let mut constants_by_name = HashMap::new(); + for c in self.constants() { + constants_by_name.entry(&c.ty).or_insert(Vec::new()).push(c); + } + for d in self.typenames() { let name = d.name.as_str(); let child = types.new_child(MdNamedType::new( @@ -35,6 +38,18 @@ impl ToMarkdown for Document { ) .as_str(), )); + if let Some(constants) = constants_by_name.remove(&d.name) { + let heading = heading_from_node(&child, 1); + child.new_child(MdSection::new(heading, "Constants")); + for constant in constants { + child.new_child(MdNamedType::new( + MdHeading::new_bullet(), + format!("{}.{}", name, constant.name.as_str()).as_str(), + constant.name.as_str(), + &constant.docs, + )); + } + } d.generate(child.clone()); } @@ -45,6 +60,8 @@ impl ToMarkdown for Document { let child = modules.new_child(content); d.generate(child.clone()); } + + assert!(constants_by_name.is_empty()); } } @@ -70,14 +87,11 @@ impl ToMarkdown for NamedType { impl ToMarkdown for Type { fn generate(&self, node: MdNodeRef) { match self { - Self::Enum(a) => a.generate(node.clone()), - Self::Int(a) => a.generate(node.clone()), - Self::Flags(a) => a.generate(node.clone()), - Self::Struct(a) => a.generate(node.clone()), - Self::Union(a) => a.generate(node.clone()), + Self::Record(a) => a.generate(node.clone()), + Self::Variant(a) => a.generate(node.clone()), Self::Handle(a) => a.generate(node.clone()), - Self::Array(a) => { - node.content_ref_mut::().r#type = Some(MdType::Array { + Self::List(a) => { + node.content_ref_mut::().r#type = Some(MdType::List { r#type: a.type_name().to_owned(), }) } @@ -100,141 +114,61 @@ impl ToMarkdown for Type { } } -impl ToMarkdown for EnumDatatype { +impl ToMarkdown for RecordDatatype { fn generate(&self, node: MdNodeRef) { let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Variants")); + node.new_child(MdSection::new(heading, "Record members")); - for variant in &self.variants { - let name = variant.name.as_str(); + for member_layout in &self.member_layout() { + let member = member_layout.member; + let offset = member_layout.offset; + let name = member.name.as_str(); let id = if let Some(id) = node.any_ref().id() { format!("{}.{}", id, name) } else { name.to_owned() }; - node.new_child(MdNamedType::new( + let n = node.new_child(MdNamedType::new( MdHeading::new_bullet(), id.as_str(), name, - &variant.docs, + format!("{}\nOffset: {}\n", &member.docs, &offset).as_str(), )); + member.tref.generate(n.clone()); } - node.content_ref_mut::().r#type = Some(MdType::Enum { - repr: self.repr.type_name().to_owned(), - }); - } -} - -impl ToMarkdown for IntDatatype { - fn generate(&self, node: MdNodeRef) { - let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Consts")); - - for r#const in &self.consts { - let name = r#const.name.as_str(); - let id = if let Some(id) = node.any_ref().id() { - format!("{}.{}", id, name) - } else { - name.to_owned() - }; - let tt = MdNamedType::new(MdHeading::new_bullet(), id.as_str(), name, &r#const.docs); - // TODO handle r#const.value - node.new_child(tt); - } - - node.content_ref_mut::().r#type = Some(MdType::Int { - repr: self.repr.type_name().to_owned(), - }); + node.content_ref_mut::().r#type = Some(MdType::Record); } } -impl ToMarkdown for FlagsDatatype { +impl ToMarkdown for Variant { fn generate(&self, node: MdNodeRef) { - let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Flags")); + if self.cases.iter().any(|c| c.tref.is_some()) { + let heading = heading_from_node(&node, 1); + node.new_child(MdSection::new(heading, "Variant Layout")); - for flag in &self.flags { - let name = flag.name.as_str(); - let id = if let Some(id) = node.any_ref().id() { - format!("{}.{}", id, name) - } else { - name.to_owned() - }; - node.new_child(MdNamedType::new( + let whole = self.mem_size_align(); + node.new_child(MdSection::new( MdHeading::new_bullet(), - id.as_str(), - name, - &flag.docs, + format!("size: {}", whole.size), + )); + node.new_child(MdSection::new( + MdHeading::new_bullet(), + format!("align: {}", whole.align), )); - } - - node.content_ref_mut::().r#type = Some(MdType::Flags { - repr: self.repr.type_name().to_owned(), - }); - } -} - -impl ToMarkdown for StructDatatype { - fn generate(&self, node: MdNodeRef) { - let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Struct members")); - for member_layout in &self.member_layout() { - let member = member_layout.member; - let offset = member_layout.offset; - let name = member.name.as_str(); - let id = if let Some(id) = node.any_ref().id() { - format!("{}.{}", id, name) - } else { - name.to_owned() - }; - let n = node.new_child(MdNamedType::new( + let tag = self.tag_repr.mem_size_align(); + node.new_child(MdSection::new( MdHeading::new_bullet(), - id.as_str(), - name, - format!("{}\nOffset: {}\n", &member.docs, &offset).as_str(), + format!("tag_size: {}", tag.size), )); - member.tref.generate(n.clone()); } - node.content_ref_mut::().r#type = Some(MdType::Struct); - } -} + let heading = heading_from_node(&node, 1); + node.new_child(MdSection::new(heading, "Variant cases")); -impl ToMarkdown for UnionDatatype { - fn generate(&self, node: MdNodeRef) { - // Sizes & Alignments - let sizes_heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(sizes_heading, "Union Layout")); - let union_layout = &self.union_layout(); - node.new_child(MdSection::new( - MdHeading::new_bullet(), - format!("tag_size: {}", union_layout.tag_size).as_str(), - )); - node.new_child(MdSection::new( - MdHeading::new_bullet(), - format!("tag_align: {}", union_layout.tag_align).as_str(), - )); - node.new_child(MdSection::new( - MdHeading::new_bullet(), - format!("contents_offset: {}", union_layout.contents_offset).as_str(), - )); - node.new_child(MdSection::new( - MdHeading::new_bullet(), - format!("contents_size: {}", union_layout.contents_size).as_str(), - )); - node.new_child(MdSection::new( - MdHeading::new_bullet(), - format!("contents_align: {}", union_layout.contents_align).as_str(), - )); - - // Variants - let variants_heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(variants_heading, "Union variants")); - - for variant in &self.variants { - let name = variant.name.as_str(); + for case in self.cases.iter() { + let name = case.name.as_str(); let id = if let Some(id) = node.any_ref().id() { format!("{}.{}", id, name) } else { @@ -244,16 +178,14 @@ impl ToMarkdown for UnionDatatype { MdHeading::new_bullet(), id.as_str(), name, - &variant.docs, + &case.docs, )); - if let Some(ref tref) = variant.tref { - tref.generate(n.clone()); - } else { - n.content_ref_mut::().r#type = None; + if let Some(ty) = &case.tref { + ty.generate(n.clone()); } } - node.content_ref_mut::().r#type = Some(MdType::Union); + node.content_ref_mut::().r#type = Some(MdType::Variant); } } @@ -356,12 +288,15 @@ impl ToMarkdown for InterfaceFuncParam { impl BuiltinType { pub fn type_name(&self) -> &'static str { match self { - BuiltinType::String => "string", - BuiltinType::Char8 => "char8", - BuiltinType::USize => "usize", - BuiltinType::U8 => "u8", + BuiltinType::Char => "char", + BuiltinType::U8 { .. } => "u8", BuiltinType::U16 => "u16", - BuiltinType::U32 => "u32", + BuiltinType::U32 { + lang_ptr_size: false, + } => "u32", + BuiltinType::U32 { + lang_ptr_size: true, + } => "usize", BuiltinType::U64 => "u64", BuiltinType::S8 => "s8", BuiltinType::S16 => "s16", @@ -378,16 +313,14 @@ impl TypeRef { match self { TypeRef::Name(n) => n.name.as_str().to_string(), TypeRef::Value(ref v) => match &**v { - Type::Array(a) => format!("Array<{}>", a.type_name()), + Type::List(a) => match &*a.type_() { + Type::Builtin(BuiltinType::Char) => "string".to_string(), + _ => format!("List<{}>", a.type_name()), + }, Type::Pointer(p) => format!("Pointer<{}>", p.type_name()), Type::ConstPointer(p) => format!("ConstPointer<{}>", p.type_name()), Type::Builtin(b) => b.type_name().to_string(), - Type::Enum { .. } - | Type::Int { .. } - | Type::Flags { .. } - | Type::Struct { .. } - | Type::Union { .. } - | Type::Handle { .. } => { + Type::Record { .. } | Type::Variant { .. } | Type::Handle { .. } => { unimplemented!("type_name of anonymous compound datatypes") } }, @@ -395,17 +328,6 @@ impl TypeRef { } } -impl IntRepr { - fn type_name(&self) -> &'static str { - match self { - IntRepr::U8 => "u8", - IntRepr::U16 => "u16", - IntRepr::U32 => "u32", - IntRepr::U64 => "u64", - } - } -} - // TODO // Generate Markdown tree for the polyfill impl Documentation for Polyfill { diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 168eb77f4..d3d5b756d 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -163,7 +163,7 @@ impl fmt::Display for MdNodeRef { } } -/// Struct representing the Markdown tree's root. +/// Record representing the Markdown tree's root. /// /// Doesn't render to anything. #[derive(Debug, Default)] @@ -237,7 +237,7 @@ impl fmt::Display for MdHeading { } } -/// Struct representing a Markdown section without any `docs`, consisting +/// Record representing a Markdown section without any `docs`, consisting /// of only a `header` (e.g., "###"), maybe some referencable `id` (i.e., /// a Markdown link), and some `title`. /// @@ -298,7 +298,7 @@ impl fmt::Display for MdSection { } } -/// Struct representing a Markdown section representing any `NamedType` element +/// Record representing a Markdown section representing any `NamedType` element /// of the AST. /// Consists of: /// * `header`, e.g., "###", or "-" for Enum variants, etc., @@ -343,12 +343,9 @@ impl MdNamedType { // being outright flattened. #[derive(Debug)] pub(super) enum MdType { - Enum { repr: String }, - Int { repr: String }, - Flags { repr: String }, - Struct, - Union, - Array { r#type: String }, + Record, + Variant, + List { r#type: String }, Pointer { r#type: String }, ConstPointer { r#type: String }, Builtin { repr: String }, @@ -359,12 +356,15 @@ pub(super) enum MdType { impl fmt::Display for MdType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::Enum { repr } => f.write_fmt(format_args!(": Enum(`{}`)", repr))?, - Self::Int { repr } => f.write_fmt(format_args!(": Int(`{}`)", repr))?, - Self::Flags { repr } => f.write_fmt(format_args!(": Flags(`{}`)", repr))?, - Self::Struct => f.write_fmt(format_args!(": Struct"))?, - Self::Union => f.write_fmt(format_args!(": Union"))?, - Self::Array { r#type } => f.write_fmt(format_args!(": `Array<{}>`", r#type))?, + Self::Record => f.write_fmt(format_args!(": Record"))?, + Self::Variant => f.write_fmt(format_args!(": Variant"))?, + Self::List { r#type } => { + if r#type == "char" { + f.write_str(": `string`")? + } else { + f.write_fmt(format_args!(": `List<{}>`", r#type))? + } + } Self::Pointer { r#type } => f.write_fmt(format_args!(": `Pointer<{}>`", r#type))?, Self::ConstPointer { r#type } => { f.write_fmt(format_args!(": `ConstPointer<{}>`", r#type))? @@ -419,7 +419,7 @@ impl fmt::Display for MdNamedType { } } -/// Struct representing a Markdown section representing any `InterfaceFunc` element +/// Record representing a Markdown section representing any `InterfaceFunc` element /// of the AST. /// Consists of: /// * `header`, e.g., "###", diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index 3e2f6ee09..2bcd1042a 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -7,6 +7,17 @@ pub struct SizeAlign { pub align: usize, } +impl SizeAlign { + fn zero() -> SizeAlign { + SizeAlign { size: 0, align: 0 } + } + fn append_field(&mut self, other: &SizeAlign) { + self.align = self.align.max(other.align); + self.size = align_to(self.size, other.align); + self.size += other.size; + } +} + pub trait Layout { fn mem_size_align(&self) -> SizeAlign; fn mem_size(&self) -> usize { @@ -53,14 +64,11 @@ impl Layout for NamedType { impl Type { fn layout(&self, cache: &mut HashMap) -> SizeAlign { match &self { - Type::Enum(e) => e.repr.mem_size_align(), - Type::Int(i) => i.repr.mem_size_align(), - Type::Flags(f) => f.repr.mem_size_align(), - Type::Struct(s) => s.layout(cache), - Type::Union(u) => u.layout(cache), + Type::Record(s) => s.layout(cache), + Type::Variant(s) => s.mem_size_align(), Type::Handle(h) => h.mem_size_align(), - Type::Array { .. } => BuiltinType::String.mem_size_align(), - Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(), + Type::List { .. } => SizeAlign { size: 8, align: 4 }, // Pointer and Length + Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::S32.mem_size_align(), Type::Builtin(b) => b.mem_size_align(), } } @@ -75,58 +83,66 @@ impl Layout for Type { impl Layout for IntRepr { fn mem_size_align(&self) -> SizeAlign { - match self { - IntRepr::U8 => BuiltinType::U8.mem_size_align(), - IntRepr::U16 => BuiltinType::U16.mem_size_align(), - IntRepr::U32 => BuiltinType::U32.mem_size_align(), - IntRepr::U64 => BuiltinType::U64.mem_size_align(), - } + self.to_builtin().mem_size_align() } } -pub struct StructMemberLayout<'a> { - pub member: &'a StructMember, +pub struct RecordMemberLayout<'a> { + pub member: &'a RecordMember, pub offset: usize, } -impl StructDatatype { - pub fn member_layout(&self) -> Vec { - self.member_layout_(&mut HashMap::new()) +impl RecordDatatype { + pub fn member_layout(&self) -> Vec { + self.member_layout_(&mut HashMap::new()).1 } - fn member_layout_(&self, cache: &mut HashMap) -> Vec { + fn member_layout_( + &self, + cache: &mut HashMap, + ) -> (SizeAlign, Vec) { let mut members = Vec::new(); - let mut offset = 0; + let mut sa = SizeAlign::zero(); for m in self.members.iter() { - let sa = m.tref.layout(cache); - offset = align_to(offset, sa.align); - members.push(StructMemberLayout { member: m, offset }); - offset += sa.size; + let member = m.tref.layout(cache); + sa.append_field(&member); + members.push(RecordMemberLayout { + member: m, + offset: sa.size - member.size, + }); } - members + sa.size = align_to(sa.size, sa.align); + (sa, members) } fn layout(&self, cache: &mut HashMap) -> SizeAlign { - let members = self.member_layout_(cache); - let align = members - .iter() - .map(|m| m.member.tref.layout(cache).align) - .max() - .expect("nonzero struct members"); - let last = members.last().expect("nonzero struct members"); - let size = last.offset + last.member.tref.layout(cache).size; - let size = align_to(size, align); - SizeAlign { size, align } + self.member_layout_(cache).0 } } -impl Layout for StructDatatype { +impl Layout for RecordDatatype { fn mem_size_align(&self) -> SizeAlign { let mut cache = HashMap::new(); self.layout(&mut cache) } } +impl Layout for Variant { + fn mem_size_align(&self) -> SizeAlign { + let mut max = SizeAlign { size: 0, align: 0 }; + for case in self.cases.iter() { + let mut size = self.tag_repr.mem_size_align(); + if let Some(payload) = &case.tref { + size.append_field(&payload.mem_size_align()); + } + size.size = align_to(size.size, size.align); + max.size = max.size.max(size.size); + max.align = max.align.max(size.align); + } + max + } +} + /// If the next free byte in the struct is `offs`, and the next /// element has alignment `alignment`, determine the offset at /// which to place that element. @@ -161,76 +177,18 @@ mod test { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct UnionLayout { - pub tag_size: usize, - pub tag_align: usize, - pub contents_offset: usize, - pub contents_size: usize, - pub contents_align: usize, -} - -impl Layout for UnionLayout { - fn mem_size_align(&self) -> SizeAlign { - let align = std::cmp::max(self.tag_align, self.contents_align); - let size = align_to(self.contents_offset + self.contents_size, align); - SizeAlign { size, align } - } -} - -impl UnionDatatype { - pub fn union_layout(&self) -> UnionLayout { - let mut cache = HashMap::new(); - self.union_layout_(&mut cache) - } - fn union_layout_(&self, cache: &mut HashMap) -> UnionLayout { - let tag = self.tag.layout(cache); - - let variant_sas = self - .variants - .iter() - .filter_map(|v| v.tref.as_ref().map(|t| t.layout(cache))) - .collect::>(); - - let contents_size = variant_sas.iter().map(|sa| sa.size).max().unwrap_or(0); - let contents_align = variant_sas.iter().map(|sa| sa.align).max().unwrap_or(1); - - UnionLayout { - tag_size: tag.size, - tag_align: tag.align, - contents_offset: align_to(tag.size, contents_align), - contents_size, - contents_align, - } - } - - fn layout(&self, cache: &mut HashMap) -> SizeAlign { - self.union_layout_(cache).mem_size_align() - } -} - -impl Layout for UnionDatatype { - fn mem_size_align(&self) -> SizeAlign { - let mut cache = HashMap::new(); - self.layout(&mut cache) - } -} - impl Layout for HandleDatatype { fn mem_size_align(&self) -> SizeAlign { - BuiltinType::U32.mem_size_align() + BuiltinType::S32.mem_size_align() } } impl Layout for BuiltinType { fn mem_size_align(&self) -> SizeAlign { match self { - BuiltinType::String => SizeAlign { size: 8, align: 4 }, // Pointer and Length - BuiltinType::U8 | BuiltinType::S8 | BuiltinType::Char8 => { - SizeAlign { size: 1, align: 1 } - } + BuiltinType::U8 { .. } | BuiltinType::S8 => SizeAlign { size: 1, align: 1 }, BuiltinType::U16 | BuiltinType::S16 => SizeAlign { size: 2, align: 2 }, - BuiltinType::USize | BuiltinType::U32 | BuiltinType::S32 | BuiltinType::F32 => { + BuiltinType::Char | BuiltinType::U32 { .. } | BuiltinType::S32 | BuiltinType::F32 => { SizeAlign { size: 4, align: 4 } } BuiltinType::U64 | BuiltinType::S64 | BuiltinType::F64 => { diff --git a/tools/witx/src/lib.rs b/tools/witx/src/lib.rs index c612e601d..9c0f53060 100644 --- a/tools/witx/src/lib.rs +++ b/tools/witx/src/lib.rs @@ -23,17 +23,11 @@ mod toplevel; /// Validate declarations into ast mod validate; -pub use ast::{ - BuiltinType, Definition, Document, Entry, EnumDatatype, EnumVariant, FlagsDatatype, - FlagsMember, HandleDatatype, Id, IntConst, IntDatatype, IntRepr, InterfaceFunc, - InterfaceFuncParam, InterfaceFuncParamPosition, Module, ModuleDefinition, ModuleEntry, - ModuleImport, ModuleImportVariant, NamedType, StructDatatype, StructMember, Type, TypeRef, - UnionDatatype, UnionVariant, -}; +pub use ast::*; pub use coretypes::{AtomType, CoreFuncType, CoreParamSignifies, CoreParamType, TypePassedBy}; pub use docs::Documentation; pub use io::{Filesystem, MockFs, WitxIo}; -pub use layout::{Layout, SizeAlign, StructMemberLayout, UnionLayout}; +pub use layout::{Layout, RecordMemberLayout, SizeAlign}; pub use parser::DeclSyntax; pub use render::SExpr; pub use representation::{RepEquality, Representable}; diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index 5ff66828e..325eb1d35 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -18,8 +18,10 @@ use wast::parser::{Parse, Parser, Peek, Result}; mod kw { pub use wast::kw::{export, func, import, memory, module, param, result}; - wast::custom_keyword!(array); + wast::custom_keyword!(bitflags); + wast::custom_keyword!(case); wast::custom_keyword!(char8); + wast::custom_keyword!(char); wast::custom_keyword!(const_pointer); wast::custom_keyword!(f32); wast::custom_keyword!(f64); @@ -27,12 +29,12 @@ mod kw { wast::custom_keyword!(empty); wast::custom_keyword!(flags); wast::custom_keyword!(handle); - wast::custom_keyword!(int); + wast::custom_keyword!(list); wast::custom_keyword!(noreturn); wast::custom_keyword!(pointer); + wast::custom_keyword!(record); wast::custom_keyword!(r#const = "const"); wast::custom_keyword!(r#enum = "enum"); - wast::custom_keyword!(r#struct = "struct"); wast::custom_keyword!(r#union = "union"); wast::custom_keyword!(r#use = "use"); wast::custom_keyword!(s16); @@ -40,12 +42,14 @@ mod kw { wast::custom_keyword!(s64); wast::custom_keyword!(s8); wast::custom_keyword!(string); + wast::custom_keyword!(tag); wast::custom_keyword!(typename); wast::custom_keyword!(u16); wast::custom_keyword!(u32); wast::custom_keyword!(u64); wast::custom_keyword!(u8); wast::custom_keyword!(usize); + wast::custom_keyword!(variant); } mod annotation { @@ -56,21 +60,20 @@ mod annotation { impl Parse<'_> for BuiltinType { fn parse(parser: Parser<'_>) -> Result { let mut l = parser.lookahead1(); - if l.peek::() { - parser.parse::()?; - Ok(BuiltinType::String) - } else if l.peek::() { - parser.parse::()?; - Ok(BuiltinType::Char8) + if l.peek::() { + parser.parse::()?; + Ok(BuiltinType::Char) } else if l.peek::() { parser.parse::()?; - Ok(BuiltinType::U8) + Ok(BuiltinType::U8 { lang_c_char: false }) } else if l.peek::() { parser.parse::()?; Ok(BuiltinType::U16) } else if l.peek::() { parser.parse::()?; - Ok(BuiltinType::U32) + Ok(BuiltinType::U32 { + lang_ptr_size: false, + }) } else if l.peek::() { parser.parse::()?; Ok(BuiltinType::U64) @@ -100,8 +103,7 @@ impl Parse<'_> for BuiltinType { impl wast::parser::Peek for BuiltinType { fn peek(cursor: wast::parser::Cursor<'_>) -> bool { - ::peek(cursor) - || ::peek(cursor) + ::peek(cursor) || ::peek(cursor) || ::peek(cursor) || ::peek(cursor) @@ -113,10 +115,12 @@ impl wast::parser::Peek for BuiltinType { || ::peek(cursor) || ::peek(cursor) } + fn display() -> &'static str { "builtin type" } } + #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct CommentSyntax<'a> { pub comments: Vec<&'a str>, @@ -237,6 +241,7 @@ impl<'a> Parse<'a> for TopLevelSyntax<'a> { pub enum DeclSyntax<'a> { Typename(TypenameSyntax<'a>), Module(ModuleSyntax<'a>), + Const(Documented<'a, ConstSyntax<'a>>), } impl<'a> Parse<'a> for DeclSyntax<'a> { @@ -246,6 +251,8 @@ impl<'a> Parse<'a> for DeclSyntax<'a> { Ok(DeclSyntax::Module(parser.parse()?)) } else if l.peek::() { Ok(DeclSyntax::Typename(parser.parse()?)) + } else if l.peek::() { + Ok(DeclSyntax::Const(parser.parse()?)) } else { Err(l.error()) } @@ -270,16 +277,17 @@ impl<'a> Parse<'a> for TypenameSyntax<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum TypedefSyntax<'a> { Enum(EnumSyntax<'a>), - Int(IntSyntax<'a>), Flags(FlagsSyntax<'a>), - Struct(StructSyntax<'a>), + Record(RecordSyntax<'a>), Union(UnionSyntax<'a>), + Variant(VariantSyntax<'a>), Handle(HandleSyntax), - Array(Box>), + List(Box>), Pointer(Box>), ConstPointer(Box>), Builtin(BuiltinType), Ident(wast::Id<'a>), + String, } impl<'a> Parse<'a> for TypedefSyntax<'a> { @@ -289,24 +297,27 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { Ok(TypedefSyntax::Ident(parser.parse()?)) } else if l.peek::() { Ok(TypedefSyntax::Builtin(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(TypedefSyntax::String) } else if l.peek::() { parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::() { Ok(TypedefSyntax::Enum(parser.parse()?)) - } else if l.peek::() { - Ok(TypedefSyntax::Int(parser.parse()?)) } else if l.peek::() { Ok(TypedefSyntax::Flags(parser.parse()?)) - } else if l.peek::() { - Ok(TypedefSyntax::Struct(parser.parse()?)) + } else if l.peek::() { + Ok(TypedefSyntax::Record(parser.parse()?)) } else if l.peek::() { Ok(TypedefSyntax::Union(parser.parse()?)) + } else if l.peek::() { + Ok(TypedefSyntax::Variant(parser.parse()?)) } else if l.peek::() { Ok(TypedefSyntax::Handle(parser.parse()?)) - } else if l.peek::() { - parser.parse::()?; - Ok(TypedefSyntax::Array(Box::new(parser.parse()?))) + } else if l.peek::() { + parser.parse::()?; + Ok(TypedefSyntax::List(Box::new(parser.parse()?))) } else if l.peek::() { parser.parse::()?; let mut l = parser.lookahead1(); @@ -318,7 +329,14 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { Ok(TypedefSyntax::Pointer(Box::new(parser.parse()?))) } else if l.peek::() { parser.parse::()?; - Ok(TypedefSyntax::Builtin(BuiltinType::USize)) + Ok(TypedefSyntax::Builtin(BuiltinType::U32 { + lang_ptr_size: true, + })) + } else if l.peek::() { + parser.parse::()?; + Ok(TypedefSyntax::Builtin(BuiltinType::U8 { + lang_c_char: true, + })) } else { Err(l.error()) } @@ -334,14 +352,22 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumSyntax<'a> { - pub repr: BuiltinType, + pub repr: Option, pub members: Vec>>, } impl<'a> Parse<'a> for EnumSyntax<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; - let repr = parser.parse()?; + let repr = if parser.peek2::() { + Some(parser.parens(|p| { + p.parse::()?; + p.parse::()?; + p.parse() + })?) + } else { + None + }; let mut members = Vec::new(); members.push(parser.parse()?); while !parser.is_empty() { @@ -351,74 +377,67 @@ impl<'a> Parse<'a> for EnumSyntax<'a> { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IntSyntax<'a> { - pub repr: BuiltinType, - pub consts: Vec>>, -} - -impl<'a> Parse<'a> for IntSyntax<'a> { - fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; - let repr = parser.parse()?; - let mut consts = Vec::new(); - consts.push(parser.parse()?); - while !parser.is_empty() { - consts.push(parser.parse()?); - } - Ok(IntSyntax { repr, consts }) - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConstSyntax<'a> { + pub ty: wast::Id<'a>, pub name: wast::Id<'a>, pub value: u64, } impl<'a> Parse<'a> for ConstSyntax<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parens(|p| { - p.parse::()?; - let name = p.parse()?; - let value = p.parse()?; - Ok(ConstSyntax { name, value }) - }) + parser.parse::()?; + parser.parse::()?; + let ty = parser.parse()?; + let name = parser.parse()?; + let value = parser.parse()?; + Ok(ConstSyntax { ty, name, value }) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct FlagsSyntax<'a> { - pub repr: BuiltinType, + pub bitflags_repr: Option, pub flags: Vec>>, } impl<'a> Parse<'a> for FlagsSyntax<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; - let repr = parser.parse()?; + let bitflags_repr = if parser.peek2::() { + Some(parser.parens(|p| { + p.parse::()?; + p.parse::()?; + p.parse() + })?) + } else { + None + }; let mut flags = Vec::new(); while !parser.is_empty() { flags.push(parser.parse()?); } - Ok(FlagsSyntax { repr, flags }) + Ok(FlagsSyntax { + bitflags_repr, + flags, + }) } } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct StructSyntax<'a> { +pub struct RecordSyntax<'a> { pub fields: Vec>>, } -impl<'a> Parse<'a> for StructSyntax<'a> { +impl<'a> Parse<'a> for RecordSyntax<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + parser.parse::()?; let mut fields = Vec::new(); fields.push(parser.parse()?); while !parser.is_empty() { fields.push(parser.parse()?); } - Ok(StructSyntax { fields }) + Ok(RecordSyntax { fields }) } } @@ -439,42 +458,24 @@ impl<'a> Parse<'a> for FieldSyntax<'a> { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum VariantSyntax<'a> { - Field(FieldSyntax<'a>), - Empty(wast::Id<'a>), -} - -impl<'a> Parse<'a> for VariantSyntax<'a> { - fn parse(parser: Parser<'a>) -> Result { - parser.parens(|p| { - let mut l = p.lookahead1(); - if l.peek::() { - parser.parse::()?; - let name = p.parse()?; - let type_ = p.parse()?; - Ok(VariantSyntax::Field(FieldSyntax { name, type_ })) - } else if l.peek::() { - parser.parse::()?; - let name = p.parse()?; - Ok(VariantSyntax::Empty(name)) - } else { - Err(l.error()) - } - }) - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct UnionSyntax<'a> { - pub tag: wast::Id<'a>, - pub fields: Vec>>, + pub tag: Option>>, + pub fields: Vec>>, } impl<'a> Parse<'a> for UnionSyntax<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; - let tag = parser.parse()?; + let tag = if parser.peek2::() { + Some(parser.parens(|p| { + p.parse::()?; + p.parse::()?; + p.parse().map(Box::new) + })?) + } else { + None + }; let mut fields = Vec::new(); fields.push(parser.parse()?); while !parser.is_empty() { @@ -484,6 +485,52 @@ impl<'a> Parse<'a> for UnionSyntax<'a> { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VariantSyntax<'a> { + pub tag: Option>>, + pub cases: Vec>>, +} + +impl<'a> Parse<'a> for VariantSyntax<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + let tag = if parser.peek2::() { + Some(parser.parens(|p| { + p.parse::()?; + p.parse::()?; + p.parse().map(Box::new) + })?) + } else { + None + }; + let mut cases = Vec::new(); + while !parser.is_empty() { + cases.push(parser.parens(|p| p.parse())?); + } + Ok(VariantSyntax { tag, cases }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CaseSyntax<'a> { + pub name: wast::Id<'a>, + pub ty: Option>, +} + +impl<'a> Parse<'a> for CaseSyntax<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + Ok(CaseSyntax { + name: parser.parse()?, + ty: if parser.is_empty() { + None + } else { + Some(parser.parse()?) + }, + }) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct HandleSyntax {} diff --git a/tools/witx/src/polyfill.rs b/tools/witx/src/polyfill.rs index 6181d357c..c779cfa4e 100644 --- a/tools/witx/src/polyfill.rs +++ b/tools/witx/src/polyfill.rs @@ -178,7 +178,7 @@ impl ParamPolyfill { fn common_denominator(a: TypeRef, b: TypeRef) -> (TypeRef, TypeRef) { match (&a, &b) { (TypeRef::Value(va), TypeRef::Value(vb)) => match (&**va, &**vb) { - (Type::Array(a), Type::Array(b)) => (a.clone(), b.clone()), + (Type::List(a), Type::List(b)) => (a.clone(), b.clone()), (Type::Pointer(a), Type::Pointer(b)) => (a.clone(), b.clone()), (Type::ConstPointer(a), Type::ConstPointer(b)) => (a.clone(), b.clone()), _ => (a, b), diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 4cf6c531f..ad551ae9b 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -77,12 +77,18 @@ impl Id { impl BuiltinType { pub fn to_sexpr(&self) -> SExpr { match self { - BuiltinType::String => SExpr::word("string"), - BuiltinType::Char8 => SExpr::word("char8"), - BuiltinType::USize => SExpr::Vec(vec![SExpr::annot("witx"), SExpr::word("usize")]), - BuiltinType::U8 => SExpr::word("u8"), + BuiltinType::Char => SExpr::word("char"), + BuiltinType::U8 { lang_c_char: true } => { + SExpr::Vec(vec![SExpr::annot("witx"), SExpr::word("char8")]) + } + BuiltinType::U8 { lang_c_char: false } => SExpr::word("u8"), BuiltinType::U16 => SExpr::word("u16"), - BuiltinType::U32 => SExpr::word("u32"), + BuiltinType::U32 { + lang_ptr_size: false, + } => SExpr::word("u32"), + BuiltinType::U32 { + lang_ptr_size: true, + } => SExpr::Vec(vec![SExpr::annot("witx"), SExpr::word("usize")]), BuiltinType::U64 => SExpr::word("u64"), BuiltinType::S8 => SExpr::word("s8"), BuiltinType::S16 => SExpr::word("s16"), @@ -116,13 +122,10 @@ impl TypeRef { impl Type { pub fn to_sexpr(&self) -> SExpr { match self { - Type::Enum(a) => a.to_sexpr(), - Type::Int(a) => a.to_sexpr(), - Type::Flags(a) => a.to_sexpr(), - Type::Struct(a) => a.to_sexpr(), - Type::Union(a) => a.to_sexpr(), + Type::Record(a) => a.to_sexpr(), + Type::Variant(a) => a.to_sexpr(), Type::Handle(a) => a.to_sexpr(), - Type::Array(a) => SExpr::Vec(vec![SExpr::word("array"), a.to_sexpr()]), + Type::List(a) => SExpr::Vec(vec![SExpr::word("list"), a.to_sexpr()]), Type::Pointer(p) => SExpr::Vec(vec![ SExpr::annot("witx"), SExpr::word("pointer"), @@ -138,54 +141,9 @@ impl Type { } } -impl EnumDatatype { +impl RecordDatatype { pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("enum"), self.repr.to_sexpr()]; - let variants = self - .variants - .iter() - .map(|v| SExpr::docs(&v.docs, v.name.to_sexpr())) - .collect::>(); - SExpr::Vec([header, variants].concat()) - } -} - -impl IntDatatype { - fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("int"), self.repr.to_sexpr()]; - let consts = self - .consts - .iter() - .map(|v| { - SExpr::docs( - &v.docs, - SExpr::Vec(vec![ - SExpr::word("const"), - v.name.to_sexpr(), - SExpr::word(&format!("{}", v.value)), - ]), - ) - }) - .collect::>(); - SExpr::Vec([header, consts].concat()) - } -} - -impl FlagsDatatype { - pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("flags"), self.repr.to_sexpr()]; - let flags = self - .flags - .iter() - .map(|f| SExpr::docs(&f.docs, f.name.to_sexpr())) - .collect::>(); - SExpr::Vec([header, flags].concat()) - } -} - -impl StructDatatype { - pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("struct")]; + let header = vec![SExpr::word("record")]; let members = self .members .iter() @@ -204,31 +162,35 @@ impl StructDatatype { } } -impl UnionDatatype { +impl Variant { pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("union"), self.tag.name.to_sexpr()]; - let variants = self - .variants - .iter() - .map(|v| { - if let Some(ref tref) = v.tref { - SExpr::docs( - &v.docs, - SExpr::Vec(vec![ - SExpr::word("field"), - v.name.to_sexpr(), - tref.to_sexpr(), - ]), - ) - } else { - SExpr::docs( - &v.docs, - SExpr::Vec(vec![SExpr::word("empty"), v.name.to_sexpr()]), - ) + let mut list = Vec::new(); + if self.cases.iter().all(|c| c.tref.is_none()) { + list.push(SExpr::word("enum")); + list.push(SExpr::Vec(vec![ + SExpr::word("@witx"), + SExpr::word("tag"), + self.tag_repr.to_sexpr(), + ])); + for case in self.cases.iter() { + list.push(SExpr::docs(&case.docs, case.name.to_sexpr())); + } + } else { + list.push(SExpr::word("variant")); + list.push(SExpr::Vec(vec![ + SExpr::word("@witx"), + SExpr::word("tag"), + self.tag_repr.to_sexpr(), + ])); + for case in self.cases.iter() { + let mut case_expr = vec![SExpr::word("case"), case.name.to_sexpr()]; + if let Some(ty) = &case.tref { + case_expr.push(ty.to_sexpr()); } - }) - .collect::>(); - SExpr::Vec([header, variants].concat()) + list.push(SExpr::docs(&case.docs, SExpr::Vec(case_expr))); + } + } + SExpr::Vec(list) } } @@ -237,6 +199,7 @@ impl HandleDatatype { SExpr::Vec(vec![SExpr::word("handle")]) } } + impl IntRepr { pub fn to_sexpr(&self) -> SExpr { match self { diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index 2a52fc69b..99f73f5b6 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -1,7 +1,5 @@ -use crate::{ - BuiltinType, EnumDatatype, FlagsDatatype, IntRepr, NamedType, StructDatatype, Type, TypeRef, - UnionDatatype, -}; +use crate::{BuiltinType, IntRepr, NamedType, RecordDatatype, Type, TypeRef, Variant}; +use std::collections::HashMap; // A lattice. Eq + Eq = Eq, SuperSet + any = NotEq, NotEq + any = NotEq. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -32,15 +30,17 @@ impl Representable for BuiltinType { return RepEquality::Eq; } match self { - BuiltinType::U8 => match by { - BuiltinType::U64 | BuiltinType::U32 | BuiltinType::U16 => RepEquality::Superset, + BuiltinType::U8 { .. } => match by { + BuiltinType::U64 | BuiltinType::U32 { .. } | BuiltinType::U16 => { + RepEquality::Superset + } _ => RepEquality::NotEq, }, BuiltinType::U16 => match by { - BuiltinType::U64 | BuiltinType::U32 => RepEquality::Superset, + BuiltinType::U64 | BuiltinType::U32 { .. } => RepEquality::Superset, _ => RepEquality::NotEq, }, - BuiltinType::U32 => match by { + BuiltinType::U32 { .. } => match by { BuiltinType::U64 => RepEquality::Superset, _ => RepEquality::NotEq, }, @@ -69,57 +69,50 @@ impl Representable for IntRepr { } } -impl Representable for EnumDatatype { - fn representable(&self, by: &Self) -> RepEquality { - // Integer representation must be compatible - if self.repr.representable(&by.repr) == RepEquality::NotEq { - return RepEquality::NotEq; - } - // For each variant in self, must have variant of same name and position in by: - for (ix, v) in self.variants.iter().enumerate() { - if let Some(by_v) = by.variants.get(ix) { - if by_v.name != v.name { - return RepEquality::NotEq; - } - } else { - return RepEquality::NotEq; - } - } - if by.variants.len() > self.variants.len() { - RepEquality::Superset - } else { - self.repr.representable(&by.repr) - } - } -} - -impl Representable for FlagsDatatype { +impl Representable for Variant { fn representable(&self, by: &Self) -> RepEquality { + let mut superset = false; // Integer representation must be compatible - if self.repr.representable(&by.repr) == RepEquality::NotEq { - return RepEquality::NotEq; - } - // For each flag in self, must have flag of same name and position in by: - for (ix, f) in self.flags.iter().enumerate() { - if let Some(by_f) = by.flags.get(ix) { - if by_f.name != f.name { - return RepEquality::NotEq; - } - } else { - return RepEquality::NotEq; + match self.tag_repr.representable(&by.tag_repr) { + RepEquality::NotEq => return RepEquality::NotEq, + RepEquality::Eq => {} + RepEquality::Superset => superset = true, + } + let other_by_name = by + .cases + .iter() + .map(|c| (&c.name, c)) + .collect::>(); + // For each variant in self, must have variant of same name in by: + for v in self.cases.iter() { + let other_ty = match other_by_name.get(&v.name) { + Some(other) => &other.tref, + None => return RepEquality::NotEq, + }; + match (&v.tref, other_ty) { + (Some(me), Some(other)) => match me.representable(other) { + RepEquality::NotEq => return RepEquality::NotEq, + RepEquality::Eq => {} + RepEquality::Superset => superset = true, + }, + // We added fields, that's not ok + (Some(_), None) => return RepEquality::NotEq, + // Fields were deleted, that's ok + (None, Some(_)) => superset = true, + (None, None) => {} } } - if by.flags.len() > self.flags.len() { + if superset || self.cases.len() < by.cases.len() { RepEquality::Superset } else { - self.repr.representable(&by.repr) + RepEquality::Eq } } } -impl Representable for StructDatatype { +impl Representable for RecordDatatype { fn representable(&self, by: &Self) -> RepEquality { - // Structs must have exact structural equality - same members, must + // Records must have exact structural equality - same members, must // be Eq, in the same order. // We would require require a more expressive RepEquality enum to describe which members // might be supersets. @@ -138,56 +131,6 @@ impl Representable for StructDatatype { } } -impl Representable for UnionDatatype { - fn representable(&self, by: &Self) -> RepEquality { - // Unions must have equal variants, by name (independent of order). If `by` has extra - // variants, its a superset. - // We would require require a more expressive RepEquality enum to describe which variants - // might be supersets. - if self.variants.len() > by.variants.len() { - return RepEquality::NotEq; - } - for v in self.variants.iter() { - if let Some(byv) = by.variants.iter().find(|byv| byv.name == v.name) { - if v.tref.is_none() && byv.tref.is_none() { - // Both empty is OK - } else if v.tref.is_some() && byv.tref.is_some() { - if v.tref - .as_ref() - .unwrap() - .type_() - .representable(&*byv.tref.as_ref().unwrap().type_()) - != RepEquality::Eq - { - // Fields must be Eq - return RepEquality::NotEq; - } - } else { - // Either one empty means not representable - return RepEquality::NotEq; - } - } else { - return RepEquality::NotEq; - } - } - if by.variants.len() > self.variants.len() { - // By is a superset of self only if the tags are as well: - if self.tag.type_().representable(&*by.tag.type_()) == RepEquality::Superset { - RepEquality::Superset - } else { - RepEquality::NotEq - } - } else { - // By and self have matching variants, so they are equal if tags are: - if self.tag.type_().representable(&*by.tag.type_()) == RepEquality::Eq { - RepEquality::Eq - } else { - RepEquality::NotEq - } - } - } -} - impl Representable for TypeRef { fn representable(&self, by: &Self) -> RepEquality { self.type_().representable(&*by.type_()) @@ -203,12 +146,10 @@ impl Representable for NamedType { impl Representable for Type { fn representable(&self, by: &Self) -> RepEquality { match (&self, &by) { - (Type::Enum(s), Type::Enum(b)) => s.representable(b), - (Type::Flags(s), Type::Flags(b)) => s.representable(b), - (Type::Struct(s), Type::Struct(b)) => s.representable(b), - (Type::Union(s), Type::Union(b)) => s.representable(b), + (Type::Variant(s), Type::Variant(b)) => s.representable(b), + (Type::Record(s), Type::Record(b)) => s.representable(b), (Type::Handle(_), Type::Handle(_)) => RepEquality::Eq, // Handles are nominal, not structural - (Type::Array(s), Type::Array(b)) => s.representable(b), + (Type::List(s), Type::List(b)) => s.representable(b), (Type::Pointer(s), Type::Pointer(b)) => s.representable(b), (Type::ConstPointer(s), Type::ConstPointer(b)) => s.representable(b), (Type::Builtin(s), Type::Builtin(b)) => s.representable(b), @@ -237,43 +178,22 @@ mod test { #[test] fn different_typenames() { - let a = def_type("a", "(typename $a (flags u32 $b $c))"); - let d = def_type("d", "(typename $d (flags u32 $b $c))"); + let a = def_type("a", "(typename $a (flags (@witx bitflags u32) $b $c))"); + let d = def_type("d", "(typename $d (flags (@witx bitflags u32) $b $c))"); assert_eq!(a.representable(&d), RepEquality::Eq); assert_eq!(d.representable(&a), RepEquality::Eq); } - #[test] - fn flags() { - let base = def_type("a", "(typename $a (flags u32 $b $c))"); - let extra_flag = def_type("a", "(typename $a (flags u32 $b $c $d))"); - - assert_eq!(base.representable(&extra_flag), RepEquality::Superset); - assert_eq!(extra_flag.representable(&base), RepEquality::NotEq); - - let different_flagnames = def_type("d", "(typename $d (flags u32 $b $e))"); - assert_eq!(base.representable(&different_flagnames), RepEquality::NotEq); - assert_eq!(different_flagnames.representable(&base), RepEquality::NotEq); - - let smaller_size = def_type("a", "(typename $a (flags u16 $b $c))"); - assert_eq!(smaller_size.representable(&base), RepEquality::Superset); - assert_eq!( - smaller_size.representable(&extra_flag), - RepEquality::Superset - ); - assert_eq!(base.representable(&smaller_size), RepEquality::NotEq); - } - #[test] fn enum_() { - let base = def_type("a", "(typename $a (enum u32 $b $c))"); - let extra_variant = def_type("a", "(typename $a (enum u32 $b $c $d))"); + let base = def_type("a", "(typename $a (enum $b $c))"); + let extra_variant = def_type("a", "(typename $a (enum $b $c $d))"); assert_eq!(base.representable(&extra_variant), RepEquality::Superset); assert_eq!(extra_variant.representable(&base), RepEquality::NotEq); - let smaller_size = def_type("a", "(typename $a (enum u16 $b $c))"); + let smaller_size = def_type("a", "(typename $a (enum (@witx tag u16) $b $c))"); assert_eq!(smaller_size.representable(&base), RepEquality::Superset); assert_eq!( smaller_size.representable(&extra_variant), @@ -285,13 +205,13 @@ mod test { fn union() { let base = def_type( "a", - "(typename $tag (enum u8 $b $c)) - (typename $a (union $tag (field $b u32) (field $c f32)))", + "(typename $tag (enum (@witx tag u8) $b $c)) + (typename $a (union (@witx tag $tag) u32 f32))", ); let extra_variant = def_type( "a", - "(typename $tag (enum u8 $b $c $d)) - (typename $a (union $tag (field $b u32) (field $c f32) (field $d f64)))", + "(typename $tag (enum (@witx tag u8) $b $c $d)) + (typename $a (union (@witx tag $tag) u32 f32 f64))", ); assert_eq!(base.representable(&extra_variant), RepEquality::Superset); @@ -299,8 +219,8 @@ mod test { let other_ordering = def_type( "a", - "(typename $tag (enum u8 $b $c)) - (typename $a (union $tag (field $c f32) (field $b u32)))", + "(typename $tag (enum (@witx tag u8) $b $c)) + (typename $a (variant (@witx tag $tag) (case $c f32) (case $b u32)))", ); assert_eq!(base.representable(&other_ordering), RepEquality::Eq); assert_eq!(other_ordering.representable(&base), RepEquality::Eq); diff --git a/tools/witx/src/toplevel.rs b/tools/witx/src/toplevel.rs index 4fb13b8d7..993fc8cc4 100644 --- a/tools/witx/src/toplevel.rs +++ b/tools/witx/src/toplevel.rs @@ -60,11 +60,10 @@ fn parse_file( for t in doc.items { match t.item { TopLevelSyntax::Decl(d) => { - let def = validator + validator .scope(&input, &path) - .validate_decl(&d, &t.comments) + .validate_decl(&d, &t.comments, definitions) .map_err(WitxError::Validation)?; - definitions.push(def); } TopLevelSyntax::Use(u) => { parse_file(u.as_ref(), io, root, validator, definitions, parsed)?; @@ -111,7 +110,12 @@ mod test { assert_eq!(*b_float.type_(), Type::Builtin(BuiltinType::F64)); let c_int = doc.typename(&Id::new("c_int")).unwrap(); - assert_eq!(*c_int.type_(), Type::Builtin(BuiltinType::U32)); + assert_eq!( + *c_int.type_(), + Type::Builtin(BuiltinType::U32 { + lang_ptr_size: false + }) + ); } #[test] @@ -128,7 +132,10 @@ mod test { .expect("parse"); let d_char = doc.typename(&Id::new("d_char")).unwrap(); - assert_eq!(*d_char.type_(), Type::Builtin(BuiltinType::U8)); + assert_eq!( + *d_char.type_(), + Type::Builtin(BuiltinType::U8 { lang_c_char: false }) + ); } #[test] diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index c4c5589e4..88ae10147 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -2,16 +2,15 @@ use crate::{ io::{Filesystem, WitxIo}, parser::{ CommentSyntax, DeclSyntax, Documented, EnumSyntax, FlagsSyntax, HandleSyntax, - ImportTypeSyntax, IntSyntax, ModuleDeclSyntax, StructSyntax, TypedefSyntax, UnionSyntax, + ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TypedefSyntax, UnionSyntax, VariantSyntax, }, - BuiltinType, Definition, Entry, EnumDatatype, EnumVariant, FlagsDatatype, FlagsMember, - HandleDatatype, Id, IntConst, IntDatatype, IntRepr, InterfaceFunc, InterfaceFuncParam, - InterfaceFuncParamPosition, Location, Module, ModuleDefinition, ModuleEntry, ModuleImport, - ModuleImportVariant, NamedType, StructDatatype, StructMember, Type, TypePassedBy, TypeRef, - UnionDatatype, UnionVariant, + BuiltinType, Case, Constant, Definition, Entry, HandleDatatype, Id, IntRepr, InterfaceFunc, + InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, ModuleDefinition, + ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, + TypePassedBy, TypeRef, Variant, }; -use std::collections::{hash_map, HashMap}; +use std::collections::{HashMap, HashSet}; use std::path::Path; use std::rc::Rc; use thiserror::Error; @@ -43,7 +42,15 @@ pub enum ValidationError { #[error("First result type must be pass-by-value")] InvalidFirstResultType { location: Location }, #[error("Anonymous structured types (struct, union, enum, flags, handle) are not permitted")] - AnonymousStructure { location: Location }, + AnonymousRecord { location: Location }, + #[error("Union expected {expected} variants, found {found}")] + UnionSizeMismatch { + expected: usize, + found: usize, + location: Location, + }, + #[error("Invalid union tag: {reason}")] + InvalidUnionTag { reason: String, location: Location }, #[error("Invalid union field `{name}`: {reason}")] InvalidUnionField { name: String, @@ -61,8 +68,10 @@ impl ValidationError { | Recursive { location, .. } | InvalidRepr { location, .. } | InvalidFirstResultType { location, .. } - | AnonymousStructure { location, .. } - | InvalidUnionField { location, .. } => { + | AnonymousRecord { location, .. } + | UnionSizeMismatch { location, .. } + | InvalidUnionField { location, .. } + | InvalidUnionTag { location, .. } => { format!("{}\n{}", location.highlight_source_with(witxio), &self) } NameAlreadyExists { @@ -121,6 +130,7 @@ impl IdentValidation { pub struct DocValidation { scope: IdentValidation, pub entries: HashMap, + constant_scopes: HashMap, } pub struct DocValidationScope<'a> { @@ -134,6 +144,7 @@ impl DocValidation { Self { scope: IdentValidation::new(), entries: HashMap::new(), + constant_scopes: HashMap::new(), } } @@ -171,7 +182,8 @@ impl DocValidationScope<'_> { &mut self, decl: &DeclSyntax, comments: &CommentSyntax, - ) -> Result { + definitions: &mut Vec, + ) -> Result<(), ValidationError> { match decl { DeclSyntax::Typename(decl) => { let name = self.introduce(&decl.ident)?; @@ -185,13 +197,32 @@ impl DocValidationScope<'_> { }); self.doc .entries - .insert(name, Entry::Typename(Rc::downgrade(&rc_datatype))); - Ok(Definition::Typename(rc_datatype)) + .insert(name.clone(), Entry::Typename(Rc::downgrade(&rc_datatype))); + definitions.push(Definition::Typename(rc_datatype)); + + if let TypedefSyntax::Flags(syntax) = &decl.def { + if syntax.bitflags_repr.is_some() { + let mut flags_scope = IdentValidation::new(); + let ty = name; + for (i, flag) in syntax.flags.iter().enumerate() { + let name = flags_scope + .introduce(flag.item.name(), self.location(flag.item.span()))?; + let docs = flag.comments.docs(); + definitions.push(Definition::Constant(Constant { + ty: ty.clone(), + name, + value: 1 << i, + docs, + })); + } + } + } } + DeclSyntax::Module(syntax) => { let name = self.introduce(&syntax.name)?; let mut module_validator = ModuleValidation::new(self); - let definitions = syntax + let decls = syntax .decls .iter() .map(|d| module_validator.validate_decl(&d)) @@ -199,16 +230,36 @@ impl DocValidationScope<'_> { let rc_module = Rc::new(Module::new( name.clone(), - definitions, + decls, module_validator.entries, comments.docs(), )); self.doc .entries .insert(name, Entry::Module(Rc::downgrade(&rc_module))); - Ok(Definition::Module(rc_module)) + definitions.push(Definition::Module(rc_module)); + } + + DeclSyntax::Const(syntax) => { + let ty = Id::new(syntax.item.ty.name()); + let loc = self.location(syntax.item.name.span()); + let scope = self + .doc + .constant_scopes + .entry(ty.clone()) + .or_insert_with(IdentValidation::new); + let name = scope.introduce(syntax.item.name.name(), loc)?; + // TODO: validate `ty` is a integer datatype that `syntax.value` + // fits within. + definitions.push(Definition::Constant(Constant { + ty, + name, + value: syntax.item.value, + docs: syntax.comments.docs(), + })); } } + Ok(()) } fn validate_datatype( @@ -237,26 +288,27 @@ impl DocValidationScope<'_> { } } TypedefSyntax::Enum { .. } - | TypedefSyntax::Int { .. } | TypedefSyntax::Flags { .. } - | TypedefSyntax::Struct { .. } + | TypedefSyntax::Record { .. } | TypedefSyntax::Union { .. } | TypedefSyntax::Handle { .. } if !named => { - Err(ValidationError::AnonymousStructure { + Err(ValidationError::AnonymousRecord { location: self.location(span), }) } other => Ok(TypeRef::Value(Rc::new(match other { - TypedefSyntax::Enum(syntax) => Type::Enum(self.validate_enum(&syntax, span)?), - TypedefSyntax::Int(syntax) => Type::Int(self.validate_int(&syntax, span)?), - TypedefSyntax::Flags(syntax) => Type::Flags(self.validate_flags(&syntax, span)?), - TypedefSyntax::Struct(syntax) => Type::Struct(self.validate_struct(&syntax, span)?), - TypedefSyntax::Union(syntax) => Type::Union(self.validate_union(&syntax, span)?), + TypedefSyntax::Enum(syntax) => Type::Variant(self.validate_enum(&syntax, span)?), + TypedefSyntax::Flags(syntax) => self.validate_flags(&syntax, span)?, + TypedefSyntax::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?), + TypedefSyntax::Union(syntax) => Type::Variant(self.validate_union(&syntax, span)?), + TypedefSyntax::Variant(syntax) => { + Type::Variant(self.validate_variant(&syntax, span)?) + } TypedefSyntax::Handle(syntax) => Type::Handle(self.validate_handle(syntax, span)?), - TypedefSyntax::Array(syntax) => { - Type::Array(self.validate_datatype(syntax, false, span)?) + TypedefSyntax::List(syntax) => { + Type::List(self.validate_datatype(syntax, false, span)?) } TypedefSyntax::Pointer(syntax) => { Type::Pointer(self.validate_datatype(syntax, false, span)?) @@ -265,6 +317,9 @@ impl DocValidationScope<'_> { Type::ConstPointer(self.validate_datatype(syntax, false, span)?) } TypedefSyntax::Builtin(builtin) => Type::Builtin(*builtin), + TypedefSyntax::String => { + Type::List(TypeRef::Value(Rc::new(Type::Builtin(BuiltinType::Char)))) + } TypedefSyntax::Ident { .. } => unreachable!(), }))), } @@ -274,69 +329,48 @@ impl DocValidationScope<'_> { &self, syntax: &EnumSyntax, span: wast::Span, - ) -> Result { + ) -> Result { let mut enum_scope = IdentValidation::new(); - let repr = self.validate_int_repr(&syntax.repr, span)?; - let variants = syntax + let tag_repr = match &syntax.repr { + Some(repr) => self.validate_int_repr(repr, span)?, + None => IntRepr::U32, + }; + let cases = syntax .members .iter() .map(|i| { let name = enum_scope.introduce(i.item.name(), self.location(i.item.span()))?; let docs = i.comments.docs(); - Ok(EnumVariant { name, docs }) - }) - .collect::, _>>()?; - - Ok(EnumDatatype { repr, variants }) - } - - fn validate_int( - &self, - syntax: &IntSyntax, - span: wast::Span, - ) -> Result { - let mut int_scope = IdentValidation::new(); - let repr = self.validate_int_repr(&syntax.repr, span)?; - let consts = syntax - .consts - .iter() - .map(|i| { - let name = - int_scope.introduce(i.item.name.name(), self.location(i.item.name.span()))?; - let value = i.item.value; - let docs = i.comments.docs(); - Ok(IntConst { name, value, docs }) + Ok(Case { + name, + tref: None, + docs, + }) }) - .collect::, _>>()?; + .collect::, _>>()?; - Ok(IntDatatype { repr, consts }) + Ok(Variant { tag_repr, cases }) } fn validate_flags( &self, syntax: &FlagsSyntax, span: wast::Span, - ) -> Result { - let mut flags_scope = IdentValidation::new(); - let repr = self.validate_int_repr(&syntax.repr, span)?; - let flags = syntax - .flags - .iter() - .map(|i| { - let name = flags_scope.introduce(i.item.name(), self.location(i.item.span()))?; - let docs = i.comments.docs(); - Ok(FlagsMember { name, docs }) - }) - .collect::, _>>()?; - - Ok(FlagsDatatype { repr, flags }) + ) -> Result { + Ok(match &syntax.bitflags_repr { + Some(repr) => Type::Builtin(self.validate_int_repr(repr, span)?.to_builtin()), + None => { + // TODO: auto-translate to a struct-of-bool-fields + unimplemented!(); + } + }) } - fn validate_struct( + fn validate_record( &self, - syntax: &StructSyntax, + syntax: &RecordSyntax, _span: wast::Span, - ) -> Result { + ) -> Result { let mut member_scope = IdentValidation::new(); let members = syntax .fields @@ -346,110 +380,132 @@ impl DocValidationScope<'_> { .introduce(f.item.name.name(), self.location(f.item.name.span()))?; let tref = self.validate_datatype(&f.item.type_, false, f.item.name.span())?; let docs = f.comments.docs(); - Ok(StructMember { name, tref, docs }) + Ok(RecordMember { name, tref, docs }) }) - .collect::, _>>()?; + .collect::, _>>()?; - Ok(StructDatatype { members }) + Ok(RecordDatatype { members }) } fn validate_union( &self, syntax: &UnionSyntax, span: wast::Span, - ) -> Result { - let mut variant_scope = IdentValidation::new(); - let tag_id = self.get(&syntax.tag)?; - let (tag, mut variant_name_uses) = match self.doc.entries.get(&tag_id) { - Some(Entry::Typename(weak_ref)) => { - let named_dt = weak_ref.upgrade().expect("weak backref to defined type"); - match &*named_dt.type_() { - Type::Enum(e) => { - let uses = e - .variants - .iter() - .map(|v| (v.name.clone(), false)) - .collect::>(); - Ok((named_dt, uses)) - } - other => Err(ValidationError::WrongKindName { - name: syntax.tag.name().to_string(), - location: self.location(syntax.tag.span()), - expected: "enum", - got: other.kind(), - }), - } + ) -> Result { + let (tag_repr, names) = self.union_tag_repr(&syntax.tag, span)?; + + if let Some(names) = &names { + if names.len() != syntax.fields.len() { + return Err(ValidationError::UnionSizeMismatch { + expected: names.len(), + found: syntax.fields.len(), + location: self.location(span), + }); } - other => Err(ValidationError::WrongKindName { - name: syntax.tag.name().to_string(), - location: self.location(syntax.tag.span()), - expected: "enum", - got: match other { - Some(e) => e.kind(), - None => "unknown", - }, - }), - }?; + } - let variants = syntax + let cases = syntax .fields .iter() - .map(|v| { - let variant_name = match v.item { - VariantSyntax::Field(ref f) => &f.name, - VariantSyntax::Empty(ref name) => name, - }; - let name = variant_scope - .introduce(variant_name.name(), self.location(variant_name.span()))?; - let tref = match &v.item { - VariantSyntax::Field(f) => { - Some(self.validate_datatype(&f.type_, false, variant_name.span())?) - } - VariantSyntax::Empty { .. } => None, - }; - let docs = v.comments.docs(); - match variant_name_uses.entry(name.clone()) { - hash_map::Entry::Occupied(mut e) => { - if *e.get() { - Err(ValidationError::InvalidUnionField { - name: variant_name.name().to_string(), - reason: "variant already defined".to_owned(), - location: self.location(variant_name.span()), - })?; - } else { - e.insert(true); - } + .enumerate() + .map(|(i, case)| { + Ok(Case { + name: match &names { + Some(names) => names[i].clone(), + None => Id::new(i.to_string()), + }, + tref: Some(self.validate_datatype(&case.item, false, span)?), + docs: case.comments.docs(), + }) + }) + .collect::, _>>()?; + Ok(Variant { tag_repr, cases }) + } + + fn validate_variant( + &self, + syntax: &VariantSyntax, + span: wast::Span, + ) -> Result { + let (tag_repr, names) = self.union_tag_repr(&syntax.tag, span)?; + + if let Some(names) = &names { + if names.len() != syntax.cases.len() { + return Err(ValidationError::UnionSizeMismatch { + expected: names.len(), + found: syntax.cases.len(), + location: self.location(span), + }); + } + } + + let mut names = names.map(|names| names.into_iter().collect::>()); + + let cases = syntax + .cases + .iter() + .map(|case| { + let name = Id::new(case.item.name.name()); + if let Some(names) = &mut names { + if !names.remove(&name) { + return Err(ValidationError::InvalidUnionField { + name: name.as_str().to_string(), + location: self.location(case.item.name.span()), + reason: format!("does not correspond to variant in tag `tag`"), + }); } - hash_map::Entry::Vacant { .. } => Err(ValidationError::InvalidUnionField { - name: variant_name.name().to_string(), - reason: format!( - "does not correspond to variant in tag `{}`", - tag.name.as_str() - ), - location: self.location(variant_name.span()), - })?, } - Ok(UnionVariant { name, tref, docs }) + Ok(Case { + name: Id::new(case.item.name.name()), + tref: match &case.item.ty { + Some(ty) => { + Some(self.validate_datatype(ty, false, case.item.name.span())?) + } + None => None, + }, + docs: case.comments.docs(), + }) }) - .collect::, _>>()?; + .collect::, _>>()?; + Ok(Variant { tag_repr, cases }) + } - let unused_variants = variant_name_uses - .iter() - .filter(|(_k, used)| **used == false) - .map(|(k, _)| k.clone()) - .collect::>(); - if !unused_variants.is_empty() { - Err(ValidationError::InvalidUnionField { - name: unused_variants - .iter() - .map(|i| i.as_str()) - .collect::>() - .join(", "), - reason: format!("missing variants from tag `{}`", tag.name.as_str()), - location: self.location(span), - })?; + fn union_tag_repr( + &self, + tag: &Option>>, + span: wast::Span, + ) -> Result<(IntRepr, Option>), ValidationError> { + let ty = match tag { + Some(tag) => self.validate_datatype(tag, false, span)?, + None => return Ok((IntRepr::U32, None)), + }; + match &*ty.type_() { + Type::Variant(e) => { + let mut names = Vec::new(); + for c in e.cases.iter() { + if c.tref.is_some() { + return Err(ValidationError::InvalidUnionTag { + location: self.location(span), + reason: format!("all variant cases should have empty payloads"), + }); + } + names.push(c.name.clone()); + } + return Ok((e.tag_repr, Some(names))); + } + Type::Builtin(BuiltinType::U8 { .. }) => return Ok((IntRepr::U8, None)), + Type::Builtin(BuiltinType::U16) => return Ok((IntRepr::U16, None)), + Type::Builtin(BuiltinType::U32 { .. }) => return Ok((IntRepr::U32, None)), + Type::Builtin(BuiltinType::U64) => return Ok((IntRepr::U64, None)), + _ => {} } - Ok(UnionDatatype { tag, variants }) + + Err(ValidationError::WrongKindName { + name: "tag".to_string(), + location: self.location(span), + expected: "enum or builtin", + got: ty.type_().kind(), + }) } fn validate_handle( @@ -466,9 +522,9 @@ impl DocValidationScope<'_> { span: wast::Span, ) -> Result { match type_ { - BuiltinType::U8 => Ok(IntRepr::U8), + BuiltinType::U8 { .. } => Ok(IntRepr::U8), BuiltinType::U16 => Ok(IntRepr::U16), - BuiltinType::U32 => Ok(IntRepr::U32), + BuiltinType::U32 { .. } => Ok(IntRepr::U32), BuiltinType::U64 => Ok(IntRepr::U64), _ => Err(ValidationError::InvalidRepr { repr: type_.clone(), diff --git a/tools/witx/tests/anonymous.rs b/tools/witx/tests/anonymous.rs index 6bf11d883..6cfb2ea7e 100644 --- a/tools/witx/tests/anonymous.rs +++ b/tools/witx/tests/anonymous.rs @@ -1,30 +1,29 @@ use witx; -fn is_anonymous_struct_err(r: Result) -> bool { +fn is_anonymous_record_err(r: Result) -> bool { match r { - Err(witx::WitxError::Validation(witx::ValidationError::AnonymousStructure { .. })) => true, + Err(witx::WitxError::Validation(witx::ValidationError::AnonymousRecord { .. })) => true, _ => false, } } #[test] fn anonymous_types() { - let pointer_to_struct = witx::parse("(typename $a (@witx pointer (struct (field $b u8))))"); - assert!(is_anonymous_struct_err(pointer_to_struct)); + let pointer_to_record = witx::parse("(typename $a (@witx pointer (record (field $b u8))))"); + assert!(is_anonymous_record_err(pointer_to_record)); - let pointer_to_union = witx::parse( - "(typename $tag (enum u8 $b)) (typename $a (@witx pointer (union $tag (field $b u8))))", - ); - assert!(is_anonymous_struct_err(pointer_to_union)); + let pointer_to_union = + witx::parse("(typename $tag (enum $b)) (typename $a (@witx pointer (union u8)))"); + assert!(is_anonymous_record_err(pointer_to_union)); - let pointer_to_enum = witx::parse("(typename $a (@witx pointer (enum u32 $b)))"); - assert!(is_anonymous_struct_err(pointer_to_enum)); + let pointer_to_enum = witx::parse("(typename $a (@witx pointer (enum $b)))"); + assert!(is_anonymous_record_err(pointer_to_enum)); - let pointer_to_flags = witx::parse("(typename $a (@witx pointer (flags u32 $b)))"); - assert!(is_anonymous_struct_err(pointer_to_flags)); + let pointer_to_flags = witx::parse("(typename $a (@witx pointer (flags $b)))"); + assert!(is_anonymous_record_err(pointer_to_flags)); let pointer_to_handle = witx::parse("(typename $a (@witx pointer (handle)))"); - assert!(is_anonymous_struct_err(pointer_to_handle)); + assert!(is_anonymous_record_err(pointer_to_handle)); let pointer_to_builtin = witx::parse("(typename $a (@witx pointer u8))"); assert!(pointer_to_builtin.is_ok()); @@ -32,14 +31,13 @@ fn anonymous_types() { let pointer_to_pointer = witx::parse("(typename $a (@witx pointer (@witx const_pointer u8)))"); assert!(pointer_to_pointer.is_ok()); - let struct_in_struct = witx::parse("(typename $a (struct (field $b (struct (field $c u8)))))"); - assert!(is_anonymous_struct_err(struct_in_struct)); + let record_in_record = witx::parse("(typename $a (record (field $b (record (field $c u8)))))"); + assert!(is_anonymous_record_err(record_in_record)); - let union_in_struct = witx::parse( - "(typename $tag (enum u8 $c)) (typename $a (struct (field $b (union $tag (field $c u8)))))", - ); - assert!(is_anonymous_struct_err(union_in_struct)); + let union_in_record = + witx::parse("(typename $tag (enum $c)) (typename $a (record (field $b (union u8))))"); + assert!(is_anonymous_record_err(union_in_record)); - let pointer_in_struct = witx::parse("(typename $a (struct (field $b (@witx pointer u8))))"); - assert!(pointer_in_struct.is_ok()) + let pointer_in_record = witx::parse("(typename $a (record (field $b (@witx pointer u8))))"); + assert!(pointer_in_record.is_ok()) } diff --git a/tools/witx/tests/multimodule.rs b/tools/witx/tests/multimodule.rs index 47fb34755..a6c773363 100644 --- a/tools/witx/tests/multimodule.rs +++ b/tools/witx/tests/multimodule.rs @@ -13,15 +13,20 @@ fn validate_multimodule() { // Check that the `a` both modules use is what we expect: let type_a = doc.typename(&Id::new("a")).expect("type a exists"); - assert_eq!(*type_a.type_(), Type::Builtin(BuiltinType::U32)); + assert_eq!( + *type_a.type_(), + Type::Builtin(BuiltinType::U32 { + lang_ptr_size: false + }) + ); // `b` is a struct with a single member of type `a` let type_b = doc.typename(&Id::new("b")).expect("type b exists"); match &*type_b.type_() { - Type::Struct(struct_) => { - assert_eq!(struct_.members.len(), 1); + Type::Record(record) => { + assert_eq!(record.members.len(), 1); assert_eq!( - struct_.members.get(0).unwrap().tref, + record.members.get(0).unwrap().tref, TypeRef::Name(type_a.clone()) ); } @@ -31,13 +36,13 @@ fn validate_multimodule() { // `c` is a struct with a two members of type `a` let type_c = doc.typename(&Id::new("c")).expect("type c exists"); match &*type_c.type_() { - Type::Struct(struct_) => { - assert_eq!(struct_.members.len(), 2); + Type::Record(record) => { + assert_eq!(record.members.len(), 2); assert_eq!( - struct_.members.get(0).unwrap().tref, + record.members.get(0).unwrap().tref, TypeRef::Name(type_a.clone()) ); - assert_eq!(struct_.members.get(1).unwrap().tref, TypeRef::Name(type_a)); + assert_eq!(record.members.get(1).unwrap().tref, TypeRef::Name(type_a)); } _ => panic!("c is a struct"), } diff --git a/tools/witx/tests/multimodule/type_b.witx b/tools/witx/tests/multimodule/type_b.witx index fe8315da9..fe169d3ec 100644 --- a/tools/witx/tests/multimodule/type_b.witx +++ b/tools/witx/tests/multimodule/type_b.witx @@ -1,2 +1,2 @@ (use "type_a.witx") -(typename $b (struct (field $member_a $a))) +(typename $b (record (field $member_a $a))) diff --git a/tools/witx/tests/multimodule/type_c.witx b/tools/witx/tests/multimodule/type_c.witx index 9f8819162..f42aac2d0 100644 --- a/tools/witx/tests/multimodule/type_c.witx +++ b/tools/witx/tests/multimodule/type_c.witx @@ -1,2 +1,2 @@ (use "type_a.witx") -(typename $c (struct (field $first_a $a) (field $second_a $a))) +(typename $c (record (field $first_a $a) (field $second_a $a))) diff --git a/tools/witx/tests/union.rs b/tools/witx/tests/union.rs index 732b5c007..31ade263a 100644 --- a/tools/witx/tests/union.rs +++ b/tools/witx/tests/union.rs @@ -4,8 +4,8 @@ use witx::{Id, Representable}; #[test] fn one_variant_union() { let d = witx::parse( - "(typename $tag (enum u8 $c)) - (typename $u (union $tag (field $c u8)))", + "(typename $tag (enum $c)) + (typename $u (union (@witx tag $tag) u8))", ); assert!(d.is_ok()); } @@ -13,15 +13,15 @@ fn one_variant_union() { #[test] fn two_variant_union() { let d1 = witx::parse( - "(typename $tag (enum u8 $a $b)) - (typename $u (union $tag (field $a u8) (field $b u16)))", + "(typename $tag (enum $a $b)) + (typename $u (union (@witx tag $tag) u8 u16))", ); assert!(d1.is_ok(), "d1 is ok"); // Fields can come in whatever order: let d2 = witx::parse( - "(typename $tag (enum u8 $a $b)) - (typename $u (union $tag (field $b u16) (field $a u8)))", + "(typename $tag (enum $a $b)) + (typename $u (variant (@witx tag $tag) (case $b u16) (case $a u8)))", ); assert!(d2.is_ok(), "d2 is ok"); @@ -40,31 +40,25 @@ fn two_variant_union() { "u2 can represent u1" ); - // Tag order doesnt matter for validation, but does for rep equality + // Tag order doesnt matter for validation let d3 = witx::parse( - "(typename $tag (enum u8 $b $a)) - (typename $u (union $tag (field $b u16) (field $a u8)))", + "(typename $tag (enum $b $a)) + (typename $u (union (@witx tag $tag) u16 u8))", ); assert!(d3.is_ok(), "d2 is ok"); - let u3 = d3.unwrap().typename(&Id::new("u")).unwrap().type_(); - assert_eq!( - u3.representable(&u1), - witx::RepEquality::NotEq, - "u3 cannot represent u1" - ); } #[test] fn empty_variant_unions() { let d1 = witx::parse( - "(typename $tag (enum u8 $a $b)) - (typename $u (union $tag (empty $a) (field $b u16)))", + "(typename $tag (enum $a $b)) + (typename $u (variant (@witx tag $tag) (case $a) (case $b u16)))", ); assert!(d1.is_ok(), "d1 is ok"); let d2 = witx::parse( - "(typename $tag (enum u8 $a $b)) - (typename $u (union $tag (empty $a) (empty $b)))", + "(typename $tag (enum $a $b)) + (typename $u (variant (@witx tag $tag) (case $a) (case $b)))", ); assert!(d2.is_ok(), "d2 is ok"); } @@ -72,22 +66,22 @@ fn empty_variant_unions() { #[test] fn many_variant_unions() { let d1 = witx::parse( - "(typename $tag (enum u32 $a $b $c $d $e $f $g $h $i $j $k $l $m)) + "(typename $tag (enum $a $b $c $d $e $f $g $h $i $j $k $l $m)) (typename $u - (union $tag - (field $a u8) - (field $b u16) - (field $c u32) - (field $d u64) - (field $e s8) - (field $f s16) - (field $g s32) - (field $h s64) - (field $i f32) - (field $j f64) - (field $k (@witx usize)) - (field $l char8) - (empty $m) + (variant (@witx tag $tag) + (case $a u8) + (case $b u16) + (case $c u32) + (case $d u64) + (case $e s8) + (case $f s16) + (case $g s32) + (case $h s64) + (case $i f32) + (case $j f64) + (case $k (@witx usize)) + (case $l (@witx char8)) + (case $m) ) )", ); @@ -103,45 +97,48 @@ fn no_tag_union() { #[test] fn wrong_kind_tag_union() { let d = witx::parse( - "(typename $tag u32) - (typename $u (union $tag (field $a u8) (field $b u16)))", + "(typename $tag string) + (typename $u (union (@witx tag $tag) u8 u16))", ); let (expected, got) = wrong_kind_name_err(d).expect("wrong kind of tag"); - assert_eq!(expected, "enum"); - assert_eq!(got, "builtin"); + assert_eq!(expected, "enum or builtin"); + assert_eq!(got, "list"); } #[test] fn bad_field_unions() { let d = witx::parse( - "(typename $tag (enum u8 $c)) - (typename $u (union $tag (field $b u8)))", - ); - let (name, reason) = union_field_err(d).expect("bad field union 1"); - assert_eq!(name, "b", "bad field name union 1"); - assert_eq!( - reason, "does not correspond to variant in tag `tag`", - "reason union 1" - ); + "(typename $tag (enum $c)) + (typename $u (variant (@witx tag $tag) (case $b u8)))", + ); + match validation_err(d) { + witx::ValidationError::InvalidUnionField { name, reason, .. } => { + assert_eq!(name, "b", "bad field name union 1"); + assert_eq!( + reason, "does not correspond to variant in tag `tag`", + "reason union 1" + ); + } + other => panic!("bad error: {}", other), + } let d = witx::parse( - "(typename $tag (enum u8 $c)) - (typename $u (union $tag (field $c f32) (field $b u8)))", - ); - let (name, reason) = union_field_err(d).expect("bad field union 2"); - assert_eq!(name, "b", "bad field name union 2"); - assert_eq!( - reason, "does not correspond to variant in tag `tag`", - "reason union 2" + "(typename $tag (enum $c)) + (typename $u (variant (@witx tag $tag) (case $c f32) (case $b u8)))", ); + match validation_err(d) { + witx::ValidationError::UnionSizeMismatch { .. } => {} + other => panic!("bad error: {}", other), + } let d = witx::parse( - "(typename $tag (enum u8 $c $d)) - (typename $u (union $tag (field $c f32)))", + "(typename $tag (enum $c $d)) + (typename $u (variant (@witx tag $tag) (case $c f32)))", ); - let (name, reason) = union_field_err(d).expect("bad field union 3"); - assert_eq!(name, "d", "bad field name union 3"); - assert_eq!(reason, "missing variants from tag `tag`", "reason union 3"); + match validation_err(d) { + witx::ValidationError::UnionSizeMismatch { .. } => {} + other => panic!("bad error: {}", other), + } } fn wrong_kind_name_err( @@ -164,20 +161,14 @@ fn wrong_kind_name_err( } } -fn union_field_err(r: Result) -> Option<(String, String)> { +fn validation_err(r: Result) -> witx::ValidationError { match r { - Err(witx::WitxError::Validation(witx::ValidationError::InvalidUnionField { - name, - reason, - .. - })) => Some((name, reason)), + Err(witx::WitxError::Validation(e)) => e, Err(e) => { - eprintln!("expected InvalidUnionField ValidationError, got: {:?}", e); - None + panic!("expected ValidationError, got: {:?}", e) } Ok(_) => { - eprintln!("expected InvalidUnionField ValidationError, got: Ok(witx::Document)"); - None + panic!("expected ValidationError, got: Ok(witx::Document)") } } }