From 3bcf82b62efb7b2fec003ba58bd032b2458e062b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 07:15:44 -0800 Subject: [PATCH 01/10] Rename `Array` to `List` --- phases/ephemeral/docs.md | 4 ++-- phases/ephemeral/witx/typenames.witx | 4 ++-- phases/old/snapshot_0/docs.md | 4 ++-- phases/old/snapshot_0/witx/typenames.witx | 4 ++-- phases/snapshot/docs.md | 4 ++-- phases/snapshot/witx/typenames.witx | 4 ++-- tools/witx/src/ast.rs | 4 ++-- tools/witx/src/coretypes.rs | 2 +- tools/witx/src/docs/ast.rs | 6 +++--- tools/witx/src/docs/md.rs | 4 ++-- tools/witx/src/layout.rs | 2 +- tools/witx/src/parser.rs | 10 +++++----- tools/witx/src/polyfill.rs | 2 +- tools/witx/src/render.rs | 2 +- tools/witx/src/representation.rs | 2 +- tools/witx/src/validate.rs | 4 ++-- 16 files changed, 31 insertions(+), 31 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 93416c8eb..3f17fdacf 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -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 diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index 349952a15..723d43cb7 100644 --- a/phases/ephemeral/witx/typenames.witx +++ b/phases/ephemeral/witx/typenames.witx @@ -304,8 +304,8 @@ ) ) -(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) diff --git a/phases/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index 34eab3c03..9307d9754 100644 --- a/phases/old/snapshot_0/docs.md +++ b/phases/old/snapshot_0/docs.md @@ -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 diff --git a/phases/old/snapshot_0/witx/typenames.witx b/phases/old/snapshot_0/witx/typenames.witx index 74ff0835a..b6f672c3a 100644 --- a/phases/old/snapshot_0/witx/typenames.witx +++ b/phases/old/snapshot_0/witx/typenames.witx @@ -296,8 +296,8 @@ ) ) -(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) diff --git a/phases/snapshot/docs.md b/phases/snapshot/docs.md index b56e8c533..1a223448f 100644 --- a/phases/snapshot/docs.md +++ b/phases/snapshot/docs.md @@ -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 diff --git a/phases/snapshot/witx/typenames.witx b/phases/snapshot/witx/typenames.witx index b77372731..5e1049553 100644 --- a/phases/snapshot/witx/typenames.witx +++ b/phases/snapshot/witx/typenames.witx @@ -296,8 +296,8 @@ ) ) -(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) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 2e30836b1..fcf6a2e94 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -183,7 +183,7 @@ pub enum Type { Struct(StructDatatype), Union(UnionDatatype), Handle(HandleDatatype), - Array(TypeRef), + List(TypeRef), Pointer(TypeRef), ConstPointer(TypeRef), Builtin(BuiltinType), @@ -199,7 +199,7 @@ impl Type { Struct(_) => "struct", Union(_) => "union", Handle(_) => "handle", - Array(_) => "array", + List(_) => "list", Pointer(_) => "pointer", ConstPointer(_) => "constpointer", Builtin(_) => "builtin", diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 84756209d..7865bd8fb 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -48,7 +48,7 @@ impl Type { 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()), diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index 02ef9ec22..0eb2933fe 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -76,8 +76,8 @@ impl ToMarkdown for Type { Self::Struct(a) => a.generate(node.clone()), Self::Union(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(), }) } @@ -378,7 +378,7 @@ 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) => 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(), diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 168eb77f4..2373f1ed5 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -348,7 +348,7 @@ pub(super) enum MdType { Flags { repr: String }, Struct, Union, - Array { r#type: String }, + List { r#type: String }, Pointer { r#type: String }, ConstPointer { r#type: String }, Builtin { repr: String }, @@ -364,7 +364,7 @@ impl fmt::Display for MdType { 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::List { r#type } => 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))? diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index 3e2f6ee09..6012d38d8 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -59,7 +59,7 @@ impl Type { Type::Struct(s) => s.layout(cache), Type::Union(u) => u.layout(cache), Type::Handle(h) => h.mem_size_align(), - Type::Array { .. } => BuiltinType::String.mem_size_align(), + Type::List { .. } => BuiltinType::String.mem_size_align(), Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(), Type::Builtin(b) => b.mem_size_align(), } diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index 5ff66828e..964a5ba48 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -18,7 +18,6 @@ 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!(char8); wast::custom_keyword!(const_pointer); wast::custom_keyword!(f32); @@ -28,6 +27,7 @@ mod kw { 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!(r#const = "const"); @@ -275,7 +275,7 @@ pub enum TypedefSyntax<'a> { Struct(StructSyntax<'a>), Union(UnionSyntax<'a>), Handle(HandleSyntax), - Array(Box>), + List(Box>), Pointer(Box>), ConstPointer(Box>), Builtin(BuiltinType), @@ -304,9 +304,9 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { Ok(TypedefSyntax::Union(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(); 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..11778fa15 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -122,7 +122,7 @@ impl Type { Type::Struct(a) => a.to_sexpr(), Type::Union(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"), diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index 2a52fc69b..f541e9fc1 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -208,7 +208,7 @@ impl Representable for Type { (Type::Struct(s), Type::Struct(b)) => s.representable(b), (Type::Union(s), Type::Union(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), diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index c4c5589e4..211438819 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -255,8 +255,8 @@ impl DocValidationScope<'_> { TypedefSyntax::Struct(syntax) => Type::Struct(self.validate_struct(&syntax, span)?), TypedefSyntax::Union(syntax) => Type::Union(self.validate_union(&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)?) From 1fc1dd269a7b358d5453baaa5fe0be3369f0e04b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 07:24:15 -0800 Subject: [PATCH 02/10] Rename structs to records --- phases/ephemeral/docs.md | 44 +++++++++++------------ phases/ephemeral/witx/typenames.witx | 22 ++++++------ phases/old/snapshot_0/docs.md | 44 +++++++++++------------ phases/old/snapshot_0/witx/typenames.witx | 22 ++++++------ phases/snapshot/docs.md | 44 +++++++++++------------ phases/snapshot/witx/typenames.witx | 22 ++++++------ tools/witx/src/ast.rs | 10 +++--- tools/witx/src/coretypes.rs | 2 +- tools/witx/src/docs/ast.rs | 12 +++---- tools/witx/src/docs/md.rs | 12 +++---- tools/witx/src/layout.rs | 16 ++++----- tools/witx/src/lib.rs | 4 +-- tools/witx/src/parser.rs | 16 ++++----- tools/witx/src/render.rs | 6 ++-- tools/witx/src/representation.rs | 8 ++--- tools/witx/src/validate.rs | 26 +++++++------- tools/witx/tests/anonymous.rs | 30 ++++++++-------- tools/witx/tests/multimodule.rs | 14 ++++---- tools/witx/tests/multimodule/type_b.witx | 2 +- tools/witx/tests/multimodule/type_c.witx | 2 +- 20 files changed, 179 insertions(+), 179 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 3f17fdacf..3bd147b43 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -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. @@ -540,14 +540,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. @@ -619,14 +619,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. @@ -748,14 +748,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. @@ -841,7 +841,7 @@ Alignment: 2 - `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 +849,7 @@ Size: 16 Alignment: 8 -### Struct members +### Record members - `nbytes`: [`filesize`](#filesize) The number of bytes available for reading or writing. @@ -880,14 +880,14 @@ Alignment: 8 - `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). @@ -919,14 +919,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 +948,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,7 +956,7 @@ 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. @@ -982,14 +982,14 @@ Alignment: 8 - `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). @@ -1066,14 +1066,14 @@ Alignment: 1 - `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`. diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index 723d43cb7..d4cc6274c 100644 --- a/phases/ephemeral/witx/typenames.witx +++ b/phases/ephemeral/witx/typenames.witx @@ -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. @@ -362,7 +362,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. @@ -412,7 +412,7 @@ ;;; File descriptor attributes. (typename $fdstat - (struct + (record ;;; File type. (field $fs_filetype $filetype) ;;; File descriptor flags. @@ -501,7 +501,7 @@ ;;; File attributes. (typename $filestat - (struct + (record ;;; Device ID of device containing the file. (field $dev $device) ;;; File serial number. @@ -554,7 +554,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. @@ -573,7 +573,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. @@ -598,7 +598,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 +614,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) ) @@ -631,7 +631,7 @@ ;;; 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) @@ -685,7 +685,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) ) diff --git a/phases/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index 9307d9754..b56aa1f5e 100644 --- a/phases/old/snapshot_0/docs.md +++ b/phases/old/snapshot_0/docs.md @@ -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. @@ -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. @@ -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. @@ -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. @@ -787,7 +787,7 @@ Alignment: 2 - `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). @@ -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,7 +893,7 @@ 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. @@ -919,14 +919,14 @@ Alignment: 8 - `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). @@ -1135,14 +1135,14 @@ Alignment: 1 - `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). diff --git a/phases/old/snapshot_0/witx/typenames.witx b/phases/old/snapshot_0/witx/typenames.witx index b6f672c3a..37a49f083 100644 --- a/phases/old/snapshot_0/witx/typenames.witx +++ b/phases/old/snapshot_0/witx/typenames.witx @@ -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. @@ -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. @@ -397,7 +397,7 @@ ;;; File descriptor attributes. (typename $fdstat - (struct + (record ;;; File type. (field $fs_filetype $filetype) ;;; File descriptor flags. @@ -455,7 +455,7 @@ ;;; File attributes. (typename $filestat - (struct + (record ;;; Device ID of device containing the file. (field $dev $device) ;;; File serial number. @@ -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. @@ -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) ) @@ -579,7 +579,7 @@ ;;; 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) @@ -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) ) diff --git a/phases/snapshot/docs.md b/phases/snapshot/docs.md index 1a223448f..1cb642abe 100644 --- a/phases/snapshot/docs.md +++ b/phases/snapshot/docs.md @@ -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. @@ -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. @@ -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. @@ -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. @@ -789,7 +789,7 @@ Alignment: 2 - `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). @@ -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,7 +890,7 @@ 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. @@ -916,14 +916,14 @@ Alignment: 8 - `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). @@ -1132,14 +1132,14 @@ Alignment: 1 - `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). diff --git a/phases/snapshot/witx/typenames.witx b/phases/snapshot/witx/typenames.witx index 5e1049553..b0ccece64 100644 --- a/phases/snapshot/witx/typenames.witx +++ b/phases/snapshot/witx/typenames.witx @@ -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. @@ -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. @@ -399,7 +399,7 @@ ;;; File descriptor attributes. (typename $fdstat - (struct + (record ;;; File type. (field $fs_filetype $filetype) ;;; File descriptor flags. @@ -457,7 +457,7 @@ ;;; File attributes. (typename $filestat - (struct + (record ;;; Device ID of device containing the file. (field $dev $device) ;;; File serial number. @@ -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. @@ -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) ) @@ -579,7 +579,7 @@ ;;; 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) @@ -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) ) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index fcf6a2e94..65f354c7e 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -180,7 +180,7 @@ pub enum Type { Enum(EnumDatatype), Int(IntDatatype), Flags(FlagsDatatype), - Struct(StructDatatype), + Record(RecordDatatype), Union(UnionDatatype), Handle(HandleDatatype), List(TypeRef), @@ -196,7 +196,7 @@ impl Type { Enum(_) => "enum", Int(_) => "int", Flags(_) => "flags", - Struct(_) => "struct", + Record(_) => "record", Union(_) => "union", Handle(_) => "handle", List(_) => "list", @@ -270,12 +270,12 @@ pub struct FlagsMember { } #[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, diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 7865bd8fb..733535318 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -53,7 +53,7 @@ impl Type { 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 { .. } | Type::Union { .. } => 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 0eb2933fe..59c246d23 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -6,7 +6,7 @@ use crate::{ ast::{ BuiltinType, Document, EnumDatatype, FlagsDatatype, HandleDatatype, IntDatatype, IntRepr, InterfaceFunc, InterfaceFuncParam, Module, ModuleImport, ModuleImportVariant, NamedType, - StructDatatype, Type, TypeRef, UnionDatatype, + RecordDatatype, Type, TypeRef, UnionDatatype, }, layout::Layout, polyfill::{FuncPolyfill, ModulePolyfill, ParamPolyfill, Polyfill, TypePolyfill}, @@ -73,7 +73,7 @@ impl ToMarkdown for Type { 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::Record(a) => a.generate(node.clone()), Self::Union(a) => a.generate(node.clone()), Self::Handle(a) => a.generate(node.clone()), Self::List(a) => { @@ -175,10 +175,10 @@ impl ToMarkdown for FlagsDatatype { } } -impl ToMarkdown for StructDatatype { +impl ToMarkdown for RecordDatatype { fn generate(&self, node: MdNodeRef) { let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Struct members")); + node.new_child(MdSection::new(heading, "Record members")); for member_layout in &self.member_layout() { let member = member_layout.member; @@ -198,7 +198,7 @@ impl ToMarkdown for StructDatatype { member.tref.generate(n.clone()); } - node.content_ref_mut::().r#type = Some(MdType::Struct); + node.content_ref_mut::().r#type = Some(MdType::Record); } } @@ -385,7 +385,7 @@ impl TypeRef { Type::Enum { .. } | Type::Int { .. } | Type::Flags { .. } - | Type::Struct { .. } + | Type::Record { .. } | Type::Union { .. } | Type::Handle { .. } => { unimplemented!("type_name of anonymous compound datatypes") diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 2373f1ed5..65969b8d2 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., @@ -346,7 +346,7 @@ pub(super) enum MdType { Enum { repr: String }, Int { repr: String }, Flags { repr: String }, - Struct, + Record, Union, List { r#type: String }, Pointer { r#type: String }, @@ -362,7 +362,7 @@ impl fmt::Display for MdType { 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::Record => f.write_fmt(format_args!(": Record"))?, Self::Union => f.write_fmt(format_args!(": Union"))?, Self::List { r#type } => f.write_fmt(format_args!(": `List<{}>`", r#type))?, Self::Pointer { r#type } => f.write_fmt(format_args!(": `Pointer<{}>`", 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 6012d38d8..d29f192b8 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -56,7 +56,7 @@ impl Type { 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::Record(s) => s.layout(cache), Type::Union(u) => u.layout(cache), Type::Handle(h) => h.mem_size_align(), Type::List { .. } => BuiltinType::String.mem_size_align(), @@ -84,23 +84,23 @@ impl Layout for IntRepr { } } -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 { +impl RecordDatatype { + pub fn member_layout(&self) -> Vec { self.member_layout_(&mut HashMap::new()) } - fn member_layout_(&self, cache: &mut HashMap) -> Vec { + fn member_layout_(&self, cache: &mut HashMap) -> Vec { let mut members = Vec::new(); let mut offset = 0; for m in self.members.iter() { let sa = m.tref.layout(cache); offset = align_to(offset, sa.align); - members.push(StructMemberLayout { member: m, offset }); + members.push(RecordMemberLayout { member: m, offset }); offset += sa.size; } members @@ -120,7 +120,7 @@ impl StructDatatype { } } -impl Layout for StructDatatype { +impl Layout for RecordDatatype { fn mem_size_align(&self) -> SizeAlign { let mut cache = HashMap::new(); self.layout(&mut cache) diff --git a/tools/witx/src/lib.rs b/tools/witx/src/lib.rs index c612e601d..d320e15d6 100644 --- a/tools/witx/src/lib.rs +++ b/tools/witx/src/lib.rs @@ -27,13 +27,13 @@ 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, + ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, TypeRef, UnionDatatype, UnionVariant, }; 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, UnionLayout}; 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 964a5ba48..aedee808f 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -30,9 +30,9 @@ mod kw { 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); @@ -272,7 +272,7 @@ pub enum TypedefSyntax<'a> { Enum(EnumSyntax<'a>), Int(IntSyntax<'a>), Flags(FlagsSyntax<'a>), - Struct(StructSyntax<'a>), + Record(RecordSyntax<'a>), Union(UnionSyntax<'a>), Handle(HandleSyntax), List(Box>), @@ -298,8 +298,8 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { 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::() { @@ -406,19 +406,19 @@ impl<'a> Parse<'a> for FlagsSyntax<'a> { } #[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 }) } } diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 11778fa15..88d7d5dce 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -119,7 +119,7 @@ impl Type { 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::Record(a) => a.to_sexpr(), Type::Union(a) => a.to_sexpr(), Type::Handle(a) => a.to_sexpr(), Type::List(a) => SExpr::Vec(vec![SExpr::word("list"), a.to_sexpr()]), @@ -183,9 +183,9 @@ impl FlagsDatatype { } } -impl StructDatatype { +impl RecordDatatype { pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("struct")]; + let header = vec![SExpr::word("record")]; let members = self .members .iter() diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index f541e9fc1..2d03797be 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -1,5 +1,5 @@ use crate::{ - BuiltinType, EnumDatatype, FlagsDatatype, IntRepr, NamedType, StructDatatype, Type, TypeRef, + BuiltinType, EnumDatatype, FlagsDatatype, IntRepr, NamedType, RecordDatatype, Type, TypeRef, UnionDatatype, }; @@ -117,9 +117,9 @@ impl Representable for FlagsDatatype { } } -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. @@ -205,7 +205,7 @@ impl Representable for Type { 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::Record(s), Type::Record(b)) => s.representable(b), (Type::Union(s), Type::Union(b)) => s.representable(b), (Type::Handle(_), Type::Handle(_)) => RepEquality::Eq, // Handles are nominal, not structural (Type::List(s), Type::List(b)) => s.representable(b), diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index 211438819..adfcb757e 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -2,13 +2,13 @@ use crate::{ io::{Filesystem, WitxIo}, parser::{ CommentSyntax, DeclSyntax, Documented, EnumSyntax, FlagsSyntax, HandleSyntax, - ImportTypeSyntax, IntSyntax, ModuleDeclSyntax, StructSyntax, TypedefSyntax, UnionSyntax, + ImportTypeSyntax, IntSyntax, 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, + ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, TypePassedBy, TypeRef, UnionDatatype, UnionVariant, }; use std::collections::{hash_map, HashMap}; @@ -43,7 +43,7 @@ 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("Invalid union field `{name}`: {reason}")] InvalidUnionField { name: String, @@ -61,7 +61,7 @@ impl ValidationError { | Recursive { location, .. } | InvalidRepr { location, .. } | InvalidFirstResultType { location, .. } - | AnonymousStructure { location, .. } + | AnonymousRecord { location, .. } | InvalidUnionField { location, .. } => { format!("{}\n{}", location.highlight_source_with(witxio), &self) } @@ -239,12 +239,12 @@ 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), }) } @@ -252,7 +252,7 @@ impl DocValidationScope<'_> { 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::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?), TypedefSyntax::Union(syntax) => Type::Union(self.validate_union(&syntax, span)?), TypedefSyntax::Handle(syntax) => Type::Handle(self.validate_handle(syntax, span)?), TypedefSyntax::List(syntax) => { @@ -332,11 +332,11 @@ impl DocValidationScope<'_> { Ok(FlagsDatatype { repr, flags }) } - 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,11 +346,11 @@ 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( diff --git a/tools/witx/tests/anonymous.rs b/tools/witx/tests/anonymous.rs index 6bf11d883..2bff69960 100644 --- a/tools/witx/tests/anonymous.rs +++ b/tools/witx/tests/anonymous.rs @@ -1,30 +1,30 @@ 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)); + 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)); + 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)); + 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 +32,14 @@ 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)))))", + let union_in_record = witx::parse( + "(typename $tag (enum u8 $c)) (typename $a (record (field $b (union $tag (field $c u8)))))", ); - assert!(is_anonymous_struct_err(union_in_struct)); + 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..a8e35609f 100644 --- a/tools/witx/tests/multimodule.rs +++ b/tools/witx/tests/multimodule.rs @@ -18,10 +18,10 @@ fn validate_multimodule() { // `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 +31,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))) From e6451fbccfbe7c2acc9a177d73db83fc3da3f530 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 07:41:30 -0800 Subject: [PATCH 03/10] Add initial support for `variant` type --- tools/witx/src/ast.rs | 14 +++++++++ tools/witx/src/coretypes.rs | 4 ++- tools/witx/src/docs/ast.rs | 35 ++++++++++++++++++---- tools/witx/src/docs/md.rs | 2 ++ tools/witx/src/layout.rs | 60 ++++++++++++++++++++++++++----------- tools/witx/src/lib.rs | 8 +---- tools/witx/src/render.rs | 19 ++++++++++++ 7 files changed, 111 insertions(+), 31 deletions(-) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 65f354c7e..602db8219 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -181,6 +181,7 @@ pub enum Type { Int(IntDatatype), Flags(FlagsDatatype), Record(RecordDatatype), + Variant(Variant), Union(UnionDatatype), Handle(HandleDatatype), List(TypeRef), @@ -197,6 +198,7 @@ impl Type { Int(_) => "int", Flags(_) => "flags", Record(_) => "record", + Variant(_) => "variant", Union(_) => "union", Handle(_) => "handle", List(_) => "list", @@ -281,6 +283,18 @@ pub struct RecordMember { pub docs: String, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Variant { + pub cases: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Case { + pub name: Id, + pub tref: Option, + pub docs: String, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct UnionDatatype { pub tag: Rc, diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 733535318..d226ad691 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -53,7 +53,9 @@ impl Type { 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::Record { .. } | Type::Union { .. } => TypePassedBy::Pointer, + Type::Record { .. } | Type::Variant { .. } | Type::Union { .. } => { + 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 59c246d23..fbf92080b 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -3,11 +3,7 @@ use super::{ Documentation, }; use crate::{ - ast::{ - BuiltinType, Document, EnumDatatype, FlagsDatatype, HandleDatatype, IntDatatype, IntRepr, - InterfaceFunc, InterfaceFuncParam, Module, ModuleImport, ModuleImportVariant, NamedType, - RecordDatatype, Type, TypeRef, UnionDatatype, - }, + ast::*, layout::Layout, polyfill::{FuncPolyfill, ModulePolyfill, ParamPolyfill, Polyfill, TypePolyfill}, RepEquality, @@ -74,6 +70,7 @@ impl ToMarkdown for Type { Self::Int(a) => a.generate(node.clone()), Self::Flags(a) => a.generate(node.clone()), Self::Record(a) => a.generate(node.clone()), + Self::Variant(a) => a.generate(node.clone()), Self::Union(a) => a.generate(node.clone()), Self::Handle(a) => a.generate(node.clone()), Self::List(a) => { @@ -202,6 +199,33 @@ impl ToMarkdown for RecordDatatype { } } +impl ToMarkdown for Variant { + fn generate(&self, node: MdNodeRef) { + let heading = heading_from_node(&node, 1); + node.new_child(MdSection::new(heading, "Variant cases")); + + 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 { + name.to_owned() + }; + let n = node.new_child(MdNamedType::new( + MdHeading::new_bullet(), + id.as_str(), + name, + format!("{}\n", &case.docs).as_str(), + )); + if let Some(ty) = &case.tref { + ty.generate(n.clone()); + } + } + + node.content_ref_mut::().r#type = Some(MdType::Variant); + } +} + impl ToMarkdown for UnionDatatype { fn generate(&self, node: MdNodeRef) { // Sizes & Alignments @@ -386,6 +410,7 @@ impl TypeRef { | Type::Int { .. } | Type::Flags { .. } | Type::Record { .. } + | Type::Variant { .. } | Type::Union { .. } | Type::Handle { .. } => { unimplemented!("type_name of anonymous compound datatypes") diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 65969b8d2..f40200e8a 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -347,6 +347,7 @@ pub(super) enum MdType { Int { repr: String }, Flags { repr: String }, Record, + Variant, Union, List { r#type: String }, Pointer { r#type: String }, @@ -363,6 +364,7 @@ impl fmt::Display for MdType { Self::Int { repr } => f.write_fmt(format_args!(": Int(`{}`)", repr))?, Self::Flags { repr } => f.write_fmt(format_args!(": Flags(`{}`)", repr))?, Self::Record => f.write_fmt(format_args!(": Record"))?, + Self::Variant => f.write_fmt(format_args!(": Variant"))?, Self::Union => f.write_fmt(format_args!(": Union"))?, Self::List { r#type } => f.write_fmt(format_args!(": `List<{}>`", r#type))?, Self::Pointer { r#type } => f.write_fmt(format_args!(": `Pointer<{}>`", r#type))?, diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index d29f192b8..b918e0d6b 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 { @@ -57,6 +68,7 @@ impl Type { Type::Int(i) => i.repr.mem_size_align(), Type::Flags(f) => f.repr.mem_size_align(), Type::Record(s) => s.layout(cache), + Type::Variant(s) => s.mem_size_align(), Type::Union(u) => u.layout(cache), Type::Handle(h) => h.mem_size_align(), Type::List { .. } => BuiltinType::String.mem_size_align(), @@ -91,32 +103,29 @@ pub struct RecordMemberLayout<'a> { impl RecordDatatype { pub fn member_layout(&self) -> Vec { - self.member_layout_(&mut HashMap::new()) + 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(RecordMemberLayout { 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 } } @@ -127,6 +136,21 @@ impl Layout for RecordDatatype { } } +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 = BuiltinType::S32.mem_size_align(); + if let Some(payload) = &case.tref { + size.append_field(&payload.mem_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. diff --git a/tools/witx/src/lib.rs b/tools/witx/src/lib.rs index d320e15d6..72774c16c 100644 --- a/tools/witx/src/lib.rs +++ b/tools/witx/src/lib.rs @@ -23,13 +23,7 @@ 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, RecordDatatype, RecordMember, 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}; diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 88d7d5dce..412aaf73b 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -120,6 +120,7 @@ impl Type { Type::Int(a) => a.to_sexpr(), Type::Flags(a) => a.to_sexpr(), Type::Record(a) => a.to_sexpr(), + Type::Variant(a) => a.to_sexpr(), Type::Union(a) => a.to_sexpr(), Type::Handle(a) => a.to_sexpr(), Type::List(a) => SExpr::Vec(vec![SExpr::word("list"), a.to_sexpr()]), @@ -204,6 +205,24 @@ impl RecordDatatype { } } +impl Variant { + pub fn to_sexpr(&self) -> SExpr { + let header = vec![SExpr::word("variant")]; + let cases = self + .cases + .iter() + .map(|m| { + let mut list = vec![SExpr::word("case"), m.name.to_sexpr()]; + if let Some(ty) = &m.tref { + list.push(ty.to_sexpr()); + } + SExpr::docs(&m.docs, SExpr::Vec(list)) + }) + .collect::>(); + SExpr::Vec([header, cases].concat()) + } +} + impl UnionDatatype { pub fn to_sexpr(&self) -> SExpr { let header = vec![SExpr::word("union"), self.tag.name.to_sexpr()]; From ce0c8c19d4e533755a0b4dab63281bac25f1a517 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 09:44:08 -0800 Subject: [PATCH 04/10] Remove `IntDatatype` Move constants to a separate list in `Document` --- phases/ephemeral/docs.md | 6 +--- phases/ephemeral/witx/typenames.witx | 10 +++--- tools/witx/src/ast.rs | 31 +++++++++-------- tools/witx/src/coretypes.rs | 1 - tools/witx/src/docs/ast.rs | 27 ++------------- tools/witx/src/docs/md.rs | 2 -- tools/witx/src/layout.rs | 1 - tools/witx/src/parser.rs | 39 ++++++--------------- tools/witx/src/render.rs | 22 ------------ tools/witx/src/validate.rs | 51 +++++++++++++--------------- 10 files changed, 57 insertions(+), 133 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 3bd147b43..3d317dae7 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -480,17 +480,13 @@ 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 -- `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). diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index d4cc6274c..c456a99a7 100644 --- a/phases/ephemeral/witx/typenames.witx +++ b/phases/ephemeral/witx/typenames.witx @@ -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) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 602db8219..242fcce1c 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)] @@ -178,7 +186,6 @@ impl NamedType { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { Enum(EnumDatatype), - Int(IntDatatype), Flags(FlagsDatatype), Record(RecordDatatype), Variant(Variant), @@ -195,7 +202,6 @@ impl Type { use Type::*; match self { Enum(_) => "enum", - Int(_) => "int", Flags(_) => "flags", Record(_) => "record", Variant(_) => "variant", @@ -246,19 +252,6 @@ pub struct EnumVariant { 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, @@ -444,3 +437,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 d226ad691..c11529f9e 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -51,7 +51,6 @@ impl Type { 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::Record { .. } | Type::Variant { .. } | Type::Union { .. } => { TypePassedBy::Pointer diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index fbf92080b..454cac010 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -41,6 +41,8 @@ impl ToMarkdown for Document { let child = modules.new_child(content); d.generate(child.clone()); } + + // TODO: document constants } } @@ -67,7 +69,6 @@ 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::Record(a) => a.generate(node.clone()), Self::Variant(a) => a.generate(node.clone()), @@ -123,29 +124,6 @@ impl ToMarkdown for EnumDatatype { } } -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(), - }); - } -} - impl ToMarkdown for FlagsDatatype { fn generate(&self, node: MdNodeRef) { let heading = heading_from_node(&node, 1); @@ -407,7 +385,6 @@ impl TypeRef { Type::ConstPointer(p) => format!("ConstPointer<{}>", p.type_name()), Type::Builtin(b) => b.type_name().to_string(), Type::Enum { .. } - | Type::Int { .. } | Type::Flags { .. } | Type::Record { .. } | Type::Variant { .. } diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index f40200e8a..1ca3a8794 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -344,7 +344,6 @@ impl MdNamedType { #[derive(Debug)] pub(super) enum MdType { Enum { repr: String }, - Int { repr: String }, Flags { repr: String }, Record, Variant, @@ -361,7 +360,6 @@ 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::Record => f.write_fmt(format_args!(": Record"))?, Self::Variant => f.write_fmt(format_args!(": Variant"))?, diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index b918e0d6b..cc4fd0add 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -65,7 +65,6 @@ 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::Record(s) => s.layout(cache), Type::Variant(s) => s.mem_size_align(), diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index aedee808f..ad9100481 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -26,7 +26,6 @@ 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); @@ -237,6 +236,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 +246,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,7 +272,6 @@ 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>), Record(RecordSyntax<'a>), Union(UnionSyntax<'a>), @@ -294,8 +295,6 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { 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::() { @@ -351,39 +350,21 @@ 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 }) } } diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 412aaf73b..44ccb54df 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -117,7 +117,6 @@ 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::Record(a) => a.to_sexpr(), Type::Variant(a) => a.to_sexpr(), @@ -151,27 +150,6 @@ impl EnumDatatype { } } -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()]; diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index adfcb757e..b51596fe3 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -2,11 +2,11 @@ use crate::{ io::{Filesystem, WitxIo}, parser::{ CommentSyntax, DeclSyntax, Documented, EnumSyntax, FlagsSyntax, HandleSyntax, - ImportTypeSyntax, IntSyntax, ModuleDeclSyntax, RecordSyntax, TypedefSyntax, UnionSyntax, + ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TypedefSyntax, UnionSyntax, VariantSyntax, }, - BuiltinType, Definition, Entry, EnumDatatype, EnumVariant, FlagsDatatype, FlagsMember, - HandleDatatype, Id, IntConst, IntDatatype, IntRepr, InterfaceFunc, InterfaceFuncParam, + BuiltinType, Constant, Definition, Entry, EnumDatatype, EnumVariant, FlagsDatatype, + FlagsMember, HandleDatatype, Id, IntRepr, InterfaceFunc, InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, ModuleDefinition, ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, TypePassedBy, TypeRef, UnionDatatype, UnionVariant, @@ -121,6 +121,7 @@ impl IdentValidation { pub struct DocValidation { scope: IdentValidation, pub entries: HashMap, + constant_scopes: HashMap, } pub struct DocValidationScope<'a> { @@ -134,6 +135,7 @@ impl DocValidation { Self { scope: IdentValidation::new(), entries: HashMap::new(), + constant_scopes: HashMap::new(), } } @@ -208,6 +210,25 @@ impl DocValidationScope<'_> { .insert(name, Entry::Module(Rc::downgrade(&rc_module))); Ok(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. + Ok(Definition::Constant(Constant { + ty, + name, + value: syntax.item.value, + docs: syntax.comments.docs(), + })) + } } } @@ -237,7 +258,6 @@ impl DocValidationScope<'_> { } } TypedefSyntax::Enum { .. } - | TypedefSyntax::Int { .. } | TypedefSyntax::Flags { .. } | TypedefSyntax::Record { .. } | TypedefSyntax::Union { .. } @@ -250,7 +270,6 @@ impl DocValidationScope<'_> { } 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::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?), TypedefSyntax::Union(syntax) => Type::Union(self.validate_union(&syntax, span)?), @@ -290,28 +309,6 @@ impl DocValidationScope<'_> { 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 }) - }) - .collect::, _>>()?; - - Ok(IntDatatype { repr, consts }) - } - fn validate_flags( &self, syntax: &FlagsSyntax, From 01360e1176e54ad9f87c98e32c1f2d252e790915 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 10:43:05 -0800 Subject: [PATCH 05/10] Implement enums in terms of `Variant` --- phases/ephemeral/docs.md | 28 +++++------ phases/ephemeral/witx/typenames.witx | 14 +++--- phases/old/snapshot_0/docs.md | 32 ++++++------- phases/old/snapshot_0/witx/typenames.witx | 16 +++---- phases/snapshot/docs.md | 32 ++++++------- phases/snapshot/witx/typenames.witx | 16 +++---- tools/witx/src/ast.rs | 15 +----- tools/witx/src/coretypes.rs | 10 ++-- tools/witx/src/docs/ast.rs | 32 +------------ tools/witx/src/docs/md.rs | 2 - tools/witx/src/layout.rs | 4 +- tools/witx/src/parser.rs | 13 +++++- tools/witx/src/render.rs | 46 +++++++++--------- tools/witx/src/representation.rs | 30 ++++++------ tools/witx/src/validate.rs | 57 +++++++++++++++-------- tools/witx/tests/anonymous.rs | 6 +-- tools/witx/tests/union.rs | 20 ++++---- 17 files changed, 179 insertions(+), 194 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 3d317dae7..3c9b13531 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. @@ -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. @@ -501,14 +501,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. @@ -564,14 +564,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. @@ -805,14 +805,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). @@ -1051,14 +1051,14 @@ 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. diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index c456a99a7..f2cde8272 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. @@ -312,7 +312,7 @@ ;;; 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. @@ -336,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. @@ -374,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. @@ -527,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 @@ -675,7 +675,7 @@ ;;; Identifiers for preopened capabilities. (typename $preopentype - (enum u8 + (enum (@witx tag u8) ;;; A pre-opened directory. $dir ) diff --git a/phases/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index b56aa1f5e..5c9543331 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. @@ -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. @@ -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. @@ -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). @@ -945,14 +945,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. @@ -1124,14 +1124,14 @@ 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. diff --git a/phases/old/snapshot_0/witx/typenames.witx b/phases/old/snapshot_0/witx/typenames.witx index 37a49f083..4bf485ba8 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. @@ -304,7 +304,7 @@ ;;; 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. @@ -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. @@ -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 @@ -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 @@ -724,7 +724,7 @@ ;;; Identifiers for preopened capabilities. (typename $preopentype - (enum u8 + (enum (@witx tag u8) ;;; A pre-opened directory. $dir ) diff --git a/phases/snapshot/docs.md b/phases/snapshot/docs.md index 1cb642abe..827250111 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. @@ -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. @@ -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. @@ -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). @@ -942,14 +942,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. @@ -1121,14 +1121,14 @@ 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. diff --git a/phases/snapshot/witx/typenames.witx b/phases/snapshot/witx/typenames.witx index b0ccece64..a2da265c2 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. @@ -304,7 +304,7 @@ ;;; 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. @@ -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. @@ -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 @@ -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 @@ -724,7 +724,7 @@ ;;; Identifiers for preopened capabilities. (typename $preopentype - (enum u8 + (enum (@witx tag u8) ;;; A pre-opened directory. $dir ) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 242fcce1c..2e5980ff1 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -185,7 +185,6 @@ impl NamedType { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { - Enum(EnumDatatype), Flags(FlagsDatatype), Record(RecordDatatype), Variant(Variant), @@ -201,7 +200,6 @@ impl Type { pub fn kind(&self) -> &'static str { use Type::*; match self { - Enum(_) => "enum", Flags(_) => "flags", Record(_) => "record", Variant(_) => "variant", @@ -240,18 +238,6 @@ 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 FlagsDatatype { pub repr: IntRepr, @@ -278,6 +264,7 @@ pub struct RecordMember { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Variant { + pub tag_repr: IntRepr, pub cases: Vec, } diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index c11529f9e..1ffe718f4 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -50,10 +50,14 @@ impl Type { }, Type::List { .. } => TypePassedBy::PointerLengthPair, Type::Pointer { .. } | Type::ConstPointer { .. } => TypePassedBy::Value(AtomType::I32), - Type::Enum(e) => TypePassedBy::Value(e.repr.into()), Type::Flags(f) => TypePassedBy::Value(f.repr.into()), - Type::Record { .. } | Type::Variant { .. } | Type::Union { .. } => { - TypePassedBy::Pointer + Type::Record { .. } | Type::Union { .. } => 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 454cac010..fea7202cf 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -68,7 +68,6 @@ impl ToMarkdown for NamedType { impl ToMarkdown for Type { fn generate(&self, node: MdNodeRef) { match self { - Self::Enum(a) => a.generate(node.clone()), Self::Flags(a) => a.generate(node.clone()), Self::Record(a) => a.generate(node.clone()), Self::Variant(a) => a.generate(node.clone()), @@ -98,32 +97,6 @@ impl ToMarkdown for Type { } } -impl ToMarkdown for EnumDatatype { - fn generate(&self, node: MdNodeRef) { - let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Variants")); - - for variant in &self.variants { - let name = variant.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( - MdHeading::new_bullet(), - id.as_str(), - name, - &variant.docs, - )); - } - - node.content_ref_mut::().r#type = Some(MdType::Enum { - repr: self.repr.type_name().to_owned(), - }); - } -} - impl ToMarkdown for FlagsDatatype { fn generate(&self, node: MdNodeRef) { let heading = heading_from_node(&node, 1); @@ -193,7 +166,7 @@ impl ToMarkdown for Variant { MdHeading::new_bullet(), id.as_str(), name, - format!("{}\n", &case.docs).as_str(), + &case.docs, )); if let Some(ty) = &case.tref { ty.generate(n.clone()); @@ -384,8 +357,7 @@ impl TypeRef { 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::Flags { .. } + Type::Flags { .. } | Type::Record { .. } | Type::Variant { .. } | Type::Union { .. } diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 1ca3a8794..4c32f9179 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -343,7 +343,6 @@ impl MdNamedType { // being outright flattened. #[derive(Debug)] pub(super) enum MdType { - Enum { repr: String }, Flags { repr: String }, Record, Variant, @@ -359,7 +358,6 @@ 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::Flags { repr } => f.write_fmt(format_args!(": Flags(`{}`)", repr))?, Self::Record => f.write_fmt(format_args!(": Record"))?, Self::Variant => f.write_fmt(format_args!(": Variant"))?, diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index cc4fd0add..ea23fd31a 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -64,7 +64,6 @@ impl Layout for NamedType { impl Type { fn layout(&self, cache: &mut HashMap) -> SizeAlign { match &self { - Type::Enum(e) => e.repr.mem_size_align(), Type::Flags(f) => f.repr.mem_size_align(), Type::Record(s) => s.layout(cache), Type::Variant(s) => s.mem_size_align(), @@ -139,10 +138,11 @@ 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 = BuiltinType::S32.mem_size_align(); + 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); } diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index ad9100481..d5ce8c3dd 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -39,6 +39,7 @@ 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); @@ -333,14 +334,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() { diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 44ccb54df..d61c9b04e 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -116,7 +116,6 @@ impl TypeRef { impl Type { pub fn to_sexpr(&self) -> SExpr { match self { - Type::Enum(a) => a.to_sexpr(), Type::Flags(a) => a.to_sexpr(), Type::Record(a) => a.to_sexpr(), Type::Variant(a) => a.to_sexpr(), @@ -138,18 +137,6 @@ impl Type { } } -impl EnumDatatype { - 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 FlagsDatatype { pub fn to_sexpr(&self) -> SExpr { let header = vec![SExpr::word("flags"), self.repr.to_sexpr()]; @@ -185,19 +172,28 @@ impl RecordDatatype { impl Variant { pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("variant")]; - let cases = self - .cases - .iter() - .map(|m| { - let mut list = vec![SExpr::word("case"), m.name.to_sexpr()]; - if let Some(ty) = &m.tref { - list.push(ty.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")); + 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()); } - SExpr::docs(&m.docs, SExpr::Vec(list)) - }) - .collect::>(); - SExpr::Vec([header, cases].concat()) + list.push(SExpr::docs(&case.docs, SExpr::Vec(case_expr))); + } + } + SExpr::Vec(list) } } diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index 2d03797be..5b88c0c02 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -1,6 +1,6 @@ use crate::{ - BuiltinType, EnumDatatype, FlagsDatatype, IntRepr, NamedType, RecordDatatype, Type, TypeRef, - UnionDatatype, + BuiltinType, FlagsDatatype, IntRepr, NamedType, RecordDatatype, Type, TypeRef, UnionDatatype, + Variant, }; // A lattice. Eq + Eq = Eq, SuperSet + any = NotEq, NotEq + any = NotEq. @@ -69,15 +69,15 @@ impl Representable for IntRepr { } } -impl Representable for EnumDatatype { +impl Representable for Variant { fn representable(&self, by: &Self) -> RepEquality { // Integer representation must be compatible - if self.repr.representable(&by.repr) == RepEquality::NotEq { + if self.tag_repr.representable(&by.tag_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) { + for (ix, v) in self.cases.iter().enumerate() { + if let Some(by_v) = by.cases.get(ix) { if by_v.name != v.name { return RepEquality::NotEq; } @@ -85,10 +85,10 @@ impl Representable for EnumDatatype { return RepEquality::NotEq; } } - if by.variants.len() > self.variants.len() { + if by.cases.len() > self.cases.len() { RepEquality::Superset } else { - self.repr.representable(&by.repr) + self.tag_repr.representable(&by.tag_repr) } } } @@ -203,7 +203,7 @@ 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::Variant(s), Type::Variant(b)) => s.representable(b), (Type::Flags(s), Type::Flags(b)) => s.representable(b), (Type::Record(s), Type::Record(b)) => s.representable(b), (Type::Union(s), Type::Union(b)) => s.representable(b), @@ -267,13 +267,13 @@ mod test { #[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,12 +285,12 @@ mod test { fn union() { let base = def_type( "a", - "(typename $tag (enum u8 $b $c)) + "(typename $tag (enum (@witx tag u8) $b $c)) (typename $a (union $tag (field $b u32) (field $c f32)))", ); let extra_variant = def_type( "a", - "(typename $tag (enum u8 $b $c $d)) + "(typename $tag (enum (@witx tag u8) $b $c $d)) (typename $a (union $tag (field $b u32) (field $c f32) (field $d f64)))", ); @@ -299,7 +299,7 @@ mod test { let other_ordering = def_type( "a", - "(typename $tag (enum u8 $b $c)) + "(typename $tag (enum (@witx tag u8) $b $c)) (typename $a (union $tag (field $c f32) (field $b u32)))", ); assert_eq!(base.representable(&other_ordering), RepEquality::Eq); diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index b51596fe3..279cf594c 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -5,11 +5,10 @@ use crate::{ ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TypedefSyntax, UnionSyntax, VariantSyntax, }, - BuiltinType, Constant, Definition, Entry, EnumDatatype, EnumVariant, FlagsDatatype, - FlagsMember, HandleDatatype, Id, IntRepr, InterfaceFunc, InterfaceFuncParam, - InterfaceFuncParamPosition, Location, Module, ModuleDefinition, ModuleEntry, ModuleImport, - ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, TypePassedBy, TypeRef, - UnionDatatype, UnionVariant, + BuiltinType, Case, Constant, Definition, Entry, FlagsDatatype, FlagsMember, HandleDatatype, Id, + IntRepr, InterfaceFunc, InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, + ModuleDefinition, ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, + RecordMember, Type, TypePassedBy, TypeRef, UnionDatatype, UnionVariant, Variant, }; use std::collections::{hash_map, HashMap}; use std::path::Path; @@ -50,6 +49,12 @@ pub enum ValidationError { reason: String, location: Location, }, + #[error("Invalid union tag `{name}`: {reason}")] + InvalidUnionTag { + name: String, + reason: String, + location: Location, + }, } impl ValidationError { @@ -62,7 +67,8 @@ impl ValidationError { | InvalidRepr { location, .. } | InvalidFirstResultType { location, .. } | AnonymousRecord { location, .. } - | InvalidUnionField { location, .. } => { + | InvalidUnionField { location, .. } + | InvalidUnionTag { location, .. } => { format!("{}\n{}", location.highlight_source_with(witxio), &self) } NameAlreadyExists { @@ -269,7 +275,7 @@ impl DocValidationScope<'_> { }) } other => Ok(TypeRef::Value(Rc::new(match other { - TypedefSyntax::Enum(syntax) => Type::Enum(self.validate_enum(&syntax, span)?), + TypedefSyntax::Enum(syntax) => Type::Variant(self.validate_enum(&syntax, span)?), TypedefSyntax::Flags(syntax) => Type::Flags(self.validate_flags(&syntax, span)?), TypedefSyntax::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?), TypedefSyntax::Union(syntax) => Type::Union(self.validate_union(&syntax, span)?), @@ -293,20 +299,27 @@ 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 }) + Ok(Case { + name, + tref: None, + docs, + }) }) - .collect::, _>>()?; + .collect::, _>>()?; - Ok(EnumDatatype { repr, variants }) + Ok(Variant { tag_repr, cases }) } fn validate_flags( @@ -361,12 +374,18 @@ impl DocValidationScope<'_> { 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::>(); + Type::Variant(e) => { + let mut uses = HashMap::new(); + for c in e.cases.iter() { + if c.tref.is_some() { + return Err(ValidationError::InvalidUnionTag { + name: syntax.tag.name().to_string(), + location: self.location(syntax.tag.span()), + reason: format!("all variant cases should have empty payloads"), + }); + } + uses.insert(c.name.clone(), false); + } Ok((named_dt, uses)) } other => Err(ValidationError::WrongKindName { diff --git a/tools/witx/tests/anonymous.rs b/tools/witx/tests/anonymous.rs index 2bff69960..723a36030 100644 --- a/tools/witx/tests/anonymous.rs +++ b/tools/witx/tests/anonymous.rs @@ -13,11 +13,11 @@ fn anonymous_types() { 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))))", + "(typename $tag (enum $b)) (typename $a (@witx pointer (union $tag (field $b u8))))", ); assert!(is_anonymous_record_err(pointer_to_union)); - let pointer_to_enum = witx::parse("(typename $a (@witx pointer (enum u32 $b)))"); + 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)))"); @@ -36,7 +36,7 @@ fn anonymous_types() { assert!(is_anonymous_record_err(record_in_record)); let union_in_record = witx::parse( - "(typename $tag (enum u8 $c)) (typename $a (record (field $b (union $tag (field $c u8)))))", + "(typename $tag (enum $c)) (typename $a (record (field $b (union $tag (field $c u8)))))", ); assert!(is_anonymous_record_err(union_in_record)); diff --git a/tools/witx/tests/union.rs b/tools/witx/tests/union.rs index 732b5c007..30fb4dfdf 100644 --- a/tools/witx/tests/union.rs +++ b/tools/witx/tests/union.rs @@ -4,7 +4,7 @@ use witx::{Id, Representable}; #[test] fn one_variant_union() { let d = witx::parse( - "(typename $tag (enum u8 $c)) + "(typename $tag (enum $c)) (typename $u (union $tag (field $c u8)))", ); assert!(d.is_ok()); @@ -13,14 +13,14 @@ fn one_variant_union() { #[test] fn two_variant_union() { let d1 = witx::parse( - "(typename $tag (enum u8 $a $b)) + "(typename $tag (enum $a $b)) (typename $u (union $tag (field $a u8) (field $b 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 $tag (enum $a $b)) (typename $u (union $tag (field $b u16) (field $a u8)))", ); assert!(d2.is_ok(), "d2 is ok"); @@ -42,7 +42,7 @@ fn two_variant_union() { // Tag order doesnt matter for validation, but does for rep equality let d3 = witx::parse( - "(typename $tag (enum u8 $b $a)) + "(typename $tag (enum $b $a)) (typename $u (union $tag (field $b u16) (field $a u8)))", ); assert!(d3.is_ok(), "d2 is ok"); @@ -57,13 +57,13 @@ fn two_variant_union() { #[test] fn empty_variant_unions() { let d1 = witx::parse( - "(typename $tag (enum u8 $a $b)) + "(typename $tag (enum $a $b)) (typename $u (union $tag (empty $a) (field $b u16)))", ); assert!(d1.is_ok(), "d1 is ok"); let d2 = witx::parse( - "(typename $tag (enum u8 $a $b)) + "(typename $tag (enum $a $b)) (typename $u (union $tag (empty $a) (empty $b)))", ); assert!(d2.is_ok(), "d2 is ok"); @@ -72,7 +72,7 @@ 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) @@ -114,7 +114,7 @@ fn wrong_kind_tag_union() { #[test] fn bad_field_unions() { let d = witx::parse( - "(typename $tag (enum u8 $c)) + "(typename $tag (enum $c)) (typename $u (union $tag (field $b u8)))", ); let (name, reason) = union_field_err(d).expect("bad field union 1"); @@ -125,7 +125,7 @@ fn bad_field_unions() { ); let d = witx::parse( - "(typename $tag (enum u8 $c)) + "(typename $tag (enum $c)) (typename $u (union $tag (field $c f32) (field $b u8)))", ); let (name, reason) = union_field_err(d).expect("bad field union 2"); @@ -136,7 +136,7 @@ fn bad_field_unions() { ); let d = witx::parse( - "(typename $tag (enum u8 $c $d)) + "(typename $tag (enum $c $d)) (typename $u (union $tag (field $c f32)))", ); let (name, reason) = union_field_err(d).expect("bad field union 3"); From 616dac95a81721a3bd526153a3468e0778a2fc50 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 11:23:00 -0800 Subject: [PATCH 06/10] Define `Flags` in terms of integers Remove the `Type::Flags` variant in favor of a witx-specific way to define a bitflags with a number of constants. --- phases/ephemeral/docs.md | 47 +++++++-------- phases/ephemeral/witx/typenames.witx | 22 ++++---- phases/old/snapshot_0/docs.md | 40 ++++++------- phases/old/snapshot_0/witx/typenames.witx | 20 +++---- phases/snapshot/docs.md | 40 ++++++------- phases/snapshot/witx/typenames.witx | 20 +++---- tools/witx/src/ast.rs | 21 +++---- tools/witx/src/coretypes.rs | 1 - tools/witx/src/docs/ast.rs | 62 +++++++------------- tools/witx/src/docs/md.rs | 2 - tools/witx/src/layout.rs | 8 +-- tools/witx/src/parser.rs | 18 +++++- tools/witx/src/render.rs | 13 ----- tools/witx/src/representation.rs | 53 +---------------- tools/witx/src/toplevel.rs | 5 +- tools/witx/src/validate.rs | 69 ++++++++++++++--------- tools/witx/tests/anonymous.rs | 2 +- 17 files changed, 190 insertions(+), 253 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 3c9b13531..c579e00a3 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -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 @@ -487,6 +487,9 @@ Size: 8 Alignment: 8 +### Constants +- `start` + ## `dirnamlen`: `u32` The type for the [`dirent::d_namlen`](#dirent.d_namlen) field of [`dirent`](#dirent). @@ -590,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. @@ -652,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). @@ -672,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. @@ -710,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. @@ -719,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 @@ -825,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). @@ -833,7 +836,7 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `fd_readwrite_hangup` The peer of this socket has closed or disconnected. @@ -899,7 +902,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). @@ -907,7 +910,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 @@ -1004,28 +1007,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. @@ -1037,14 +1040,14 @@ 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. diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index f2cde8272..e10c4c30d 100644 --- a/phases/ephemeral/witx/typenames.witx +++ b/phases/ephemeral/witx/typenames.witx @@ -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 @@ -392,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. @@ -429,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`. @@ -443,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 ) @@ -451,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. @@ -470,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. @@ -543,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 ) @@ -584,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 @@ -643,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. @@ -653,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 ) @@ -665,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. diff --git a/phases/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index 5c9543331..0e2f5bde0 100644 --- a/phases/old/snapshot_0/docs.md +++ b/phases/old/snapshot_0/docs.md @@ -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 @@ -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. @@ -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. @@ -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,7 +783,7 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `fd_readwrite_hangup` The peer of this socket has closed or disconnected. @@ -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 @@ -1077,28 +1077,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,14 +1110,14 @@ 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. diff --git a/phases/old/snapshot_0/witx/typenames.witx b/phases/old/snapshot_0/witx/typenames.witx index 4bf485ba8..f24000e00 100644 --- a/phases/old/snapshot_0/witx/typenames.witx +++ b/phases/old/snapshot_0/witx/typenames.witx @@ -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 @@ -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. @@ -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. @@ -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 ) @@ -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 @@ -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. diff --git a/phases/snapshot/docs.md b/phases/snapshot/docs.md index 827250111..e13dcc79d 100644 --- a/phases/snapshot/docs.md +++ b/phases/snapshot/docs.md @@ -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 @@ -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. @@ -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. @@ -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,7 +785,7 @@ Size: 2 Alignment: 2 -### Flags +### Constants - `fd_readwrite_hangup` The peer of this socket has closed or disconnected. @@ -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 @@ -1074,28 +1074,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,14 +1107,14 @@ 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. diff --git a/phases/snapshot/witx/typenames.witx b/phases/snapshot/witx/typenames.witx index a2da265c2..13eb1759f 100644 --- a/phases/snapshot/witx/typenames.witx +++ b/phases/snapshot/witx/typenames.witx @@ -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 @@ -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. @@ -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. @@ -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 ) @@ -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 @@ -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. diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 2e5980ff1..556dadd6a 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -185,7 +185,6 @@ impl NamedType { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { - Flags(FlagsDatatype), Record(RecordDatatype), Variant(Variant), Union(UnionDatatype), @@ -200,7 +199,6 @@ impl Type { pub fn kind(&self) -> &'static str { use Type::*; match self { - Flags(_) => "flags", Record(_) => "record", Variant(_) => "variant", Union(_) => "union", @@ -238,16 +236,15 @@ pub enum IntRepr { U64, } -#[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, + IntRepr::U16 => BuiltinType::U16, + IntRepr::U32 => BuiltinType::U32, + IntRepr::U64 => BuiltinType::U64, + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 1ffe718f4..b2cacf8e8 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -50,7 +50,6 @@ impl Type { }, Type::List { .. } => TypePassedBy::PointerLengthPair, Type::Pointer { .. } | Type::ConstPointer { .. } => TypePassedBy::Value(AtomType::I32), - Type::Flags(f) => TypePassedBy::Value(f.repr.into()), Type::Record { .. } | Type::Union { .. } => TypePassedBy::Pointer, Type::Variant(v) => { if v.cases.iter().all(|c| c.tref.is_none()) { diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index fea7202cf..c9fce08ce 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -8,6 +8,7 @@ use crate::{ 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) @@ -17,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( @@ -31,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()); } @@ -42,7 +61,7 @@ impl ToMarkdown for Document { d.generate(child.clone()); } - // TODO: document constants + assert!(constants_by_name.is_empty()); } } @@ -68,7 +87,6 @@ impl ToMarkdown for NamedType { impl ToMarkdown for Type { fn generate(&self, node: MdNodeRef) { match self { - Self::Flags(a) => a.generate(node.clone()), Self::Record(a) => a.generate(node.clone()), Self::Variant(a) => a.generate(node.clone()), Self::Union(a) => a.generate(node.clone()), @@ -97,32 +115,6 @@ impl ToMarkdown for Type { } } -impl ToMarkdown for FlagsDatatype { - fn generate(&self, node: MdNodeRef) { - let heading = heading_from_node(&node, 1); - node.new_child(MdSection::new(heading, "Flags")); - - 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( - MdHeading::new_bullet(), - id.as_str(), - name, - &flag.docs, - )); - } - - node.content_ref_mut::().r#type = Some(MdType::Flags { - repr: self.repr.type_name().to_owned(), - }); - } -} - impl ToMarkdown for RecordDatatype { fn generate(&self, node: MdNodeRef) { let heading = heading_from_node(&node, 1); @@ -357,8 +349,7 @@ impl TypeRef { 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::Flags { .. } - | Type::Record { .. } + Type::Record { .. } | Type::Variant { .. } | Type::Union { .. } | Type::Handle { .. } => { @@ -369,17 +360,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 4c32f9179..22ea16074 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -343,7 +343,6 @@ impl MdNamedType { // being outright flattened. #[derive(Debug)] pub(super) enum MdType { - Flags { repr: String }, Record, Variant, Union, @@ -358,7 +357,6 @@ pub(super) enum MdType { impl fmt::Display for MdType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::Flags { repr } => f.write_fmt(format_args!(": Flags(`{}`)", repr))?, Self::Record => f.write_fmt(format_args!(": Record"))?, Self::Variant => f.write_fmt(format_args!(": Variant"))?, Self::Union => f.write_fmt(format_args!(": Union"))?, diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index ea23fd31a..2154eecfe 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -64,7 +64,6 @@ impl Layout for NamedType { impl Type { fn layout(&self, cache: &mut HashMap) -> SizeAlign { match &self { - Type::Flags(f) => f.repr.mem_size_align(), Type::Record(s) => s.layout(cache), Type::Variant(s) => s.mem_size_align(), Type::Union(u) => u.layout(cache), @@ -85,12 +84,7 @@ 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() } } diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index d5ce8c3dd..fad70619b 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -18,6 +18,7 @@ use wast::parser::{Parse, Parser, Peek, Result}; mod kw { pub use wast::kw::{export, func, import, memory, module, param, result}; + wast::custom_keyword!(bitflags); wast::custom_keyword!(char8); wast::custom_keyword!(const_pointer); wast::custom_keyword!(f32); @@ -379,19 +380,30 @@ impl<'a> Parse<'a> for ConstSyntax<'a> { #[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, + }) } } diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index d61c9b04e..659f940c8 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -116,7 +116,6 @@ impl TypeRef { impl Type { pub fn to_sexpr(&self) -> SExpr { match self { - Type::Flags(a) => a.to_sexpr(), Type::Record(a) => a.to_sexpr(), Type::Variant(a) => a.to_sexpr(), Type::Union(a) => a.to_sexpr(), @@ -137,18 +136,6 @@ impl Type { } } -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 RecordDatatype { pub fn to_sexpr(&self) -> SExpr { let header = vec![SExpr::word("record")]; diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index 5b88c0c02..d3f7bf817 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -1,6 +1,5 @@ use crate::{ - BuiltinType, FlagsDatatype, IntRepr, NamedType, RecordDatatype, Type, TypeRef, UnionDatatype, - Variant, + BuiltinType, IntRepr, NamedType, RecordDatatype, Type, TypeRef, UnionDatatype, Variant, }; // A lattice. Eq + Eq = Eq, SuperSet + any = NotEq, NotEq + any = NotEq. @@ -93,30 +92,6 @@ impl Representable for Variant { } } -impl Representable for FlagsDatatype { - fn representable(&self, by: &Self) -> RepEquality { - // 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; - } - } - if by.flags.len() > self.flags.len() { - RepEquality::Superset - } else { - self.repr.representable(&by.repr) - } - } -} - impl Representable for RecordDatatype { fn representable(&self, by: &Self) -> RepEquality { // Records must have exact structural equality - same members, must @@ -204,7 +179,6 @@ impl Representable for Type { fn representable(&self, by: &Self) -> RepEquality { match (&self, &by) { (Type::Variant(s), Type::Variant(b)) => s.representable(b), - (Type::Flags(s), Type::Flags(b)) => s.representable(b), (Type::Record(s), Type::Record(b)) => s.representable(b), (Type::Union(s), Type::Union(b)) => s.representable(b), (Type::Handle(_), Type::Handle(_)) => RepEquality::Eq, // Handles are nominal, not structural @@ -237,34 +211,13 @@ 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 $b $c))"); diff --git a/tools/witx/src/toplevel.rs b/tools/witx/src/toplevel.rs index 4fb13b8d7..52ef3daa3 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)?; diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index 279cf594c..88f4a44fb 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -5,10 +5,10 @@ use crate::{ ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TypedefSyntax, UnionSyntax, VariantSyntax, }, - BuiltinType, Case, Constant, Definition, Entry, FlagsDatatype, FlagsMember, HandleDatatype, Id, - IntRepr, InterfaceFunc, InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, - ModuleDefinition, ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, - RecordMember, Type, TypePassedBy, TypeRef, UnionDatatype, UnionVariant, Variant, + BuiltinType, Case, Constant, Definition, Entry, HandleDatatype, Id, IntRepr, InterfaceFunc, + InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, ModuleDefinition, + ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, + TypePassedBy, TypeRef, UnionDatatype, UnionVariant, Variant, }; use std::collections::{hash_map, HashMap}; use std::path::Path; @@ -179,7 +179,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)?; @@ -193,13 +194,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)) @@ -207,14 +227,14 @@ 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) => { @@ -228,14 +248,15 @@ impl DocValidationScope<'_> { let name = scope.introduce(syntax.item.name.name(), loc)?; // TODO: validate `ty` is a integer datatype that `syntax.value` // fits within. - Ok(Definition::Constant(Constant { + definitions.push(Definition::Constant(Constant { ty, name, value: syntax.item.value, docs: syntax.comments.docs(), - })) + })); } } + Ok(()) } fn validate_datatype( @@ -276,7 +297,7 @@ impl DocValidationScope<'_> { } other => Ok(TypeRef::Value(Rc::new(match other { TypedefSyntax::Enum(syntax) => Type::Variant(self.validate_enum(&syntax, span)?), - TypedefSyntax::Flags(syntax) => Type::Flags(self.validate_flags(&syntax, span)?), + TypedefSyntax::Flags(syntax) => self.validate_flags(&syntax, span)?, TypedefSyntax::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?), TypedefSyntax::Union(syntax) => Type::Union(self.validate_union(&syntax, span)?), TypedefSyntax::Handle(syntax) => Type::Handle(self.validate_handle(syntax, span)?), @@ -326,20 +347,14 @@ impl DocValidationScope<'_> { &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_record( diff --git a/tools/witx/tests/anonymous.rs b/tools/witx/tests/anonymous.rs index 723a36030..a295a3915 100644 --- a/tools/witx/tests/anonymous.rs +++ b/tools/witx/tests/anonymous.rs @@ -20,7 +20,7 @@ fn anonymous_types() { 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)))"); + 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)))"); From de0b5ff13baca22376e50b88747200bb3c5e3594 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 11:38:10 -0800 Subject: [PATCH 07/10] Represent strings as lists of `char` internally --- tools/witx/src/ast.rs | 2 +- tools/witx/src/coretypes.rs | 2 +- tools/witx/src/docs/ast.rs | 7 +++++-- tools/witx/src/docs/md.rs | 8 +++++++- tools/witx/src/layout.rs | 11 ++++++----- tools/witx/src/parser.rs | 19 +++++++++++++------ tools/witx/src/render.rs | 2 +- tools/witx/src/validate.rs | 3 +++ 8 files changed, 37 insertions(+), 17 deletions(-) diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 556dadd6a..00c9fac83 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -213,8 +213,8 @@ impl Type { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BuiltinType { - String, Char8, + Char, USize, U8, U16, diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index b2cacf8e8..3a5e41284 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -35,7 +35,6 @@ impl Type { pub fn passed_by(&self) -> TypePassedBy { match self { Type::Builtin(b) => match b { - BuiltinType::String => TypePassedBy::PointerLengthPair, BuiltinType::U8 | BuiltinType::U16 | BuiltinType::U32 @@ -43,6 +42,7 @@ impl Type { | BuiltinType::S16 | BuiltinType::S32 | BuiltinType::Char8 + | BuiltinType::Char | BuiltinType::USize => TypePassedBy::Value(AtomType::I32), BuiltinType::U64 | BuiltinType::S64 => TypePassedBy::Value(AtomType::I64), BuiltinType::F32 => TypePassedBy::Value(AtomType::F32), diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index c9fce08ce..a776907a3 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -323,8 +323,8 @@ impl ToMarkdown for InterfaceFuncParam { impl BuiltinType { pub fn type_name(&self) -> &'static str { match self { - BuiltinType::String => "string", BuiltinType::Char8 => "char8", + BuiltinType::Char => "char", BuiltinType::USize => "usize", BuiltinType::U8 => "u8", BuiltinType::U16 => "u16", @@ -345,7 +345,10 @@ impl TypeRef { match self { TypeRef::Name(n) => n.name.as_str().to_string(), TypeRef::Value(ref v) => match &**v { - Type::List(a) => format!("List<{}>", 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(), diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 22ea16074..6519d0745 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -360,7 +360,13 @@ impl fmt::Display for MdType { Self::Record => f.write_fmt(format_args!(": Record"))?, Self::Variant => f.write_fmt(format_args!(": Variant"))?, Self::Union => f.write_fmt(format_args!(": Union"))?, - Self::List { r#type } => f.write_fmt(format_args!(": `List<{}>`", r#type))?, + 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))? diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index 2154eecfe..1e4b03211 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -68,7 +68,7 @@ impl Type { Type::Variant(s) => s.mem_size_align(), Type::Union(u) => u.layout(cache), Type::Handle(h) => h.mem_size_align(), - Type::List { .. } => BuiltinType::String.mem_size_align(), + Type::List { .. } => SizeAlign { size: 8, align: 4 }, // Pointer and Length Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(), Type::Builtin(b) => b.mem_size_align(), } @@ -242,14 +242,15 @@ impl Layout for HandleDatatype { 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::U16 | BuiltinType::S16 => SizeAlign { size: 2, align: 2 }, - BuiltinType::USize | BuiltinType::U32 | BuiltinType::S32 | BuiltinType::F32 => { - SizeAlign { size: 4, align: 4 } - } + BuiltinType::Char + | BuiltinType::USize + | BuiltinType::U32 + | BuiltinType::S32 + | BuiltinType::F32 => SizeAlign { size: 4, align: 4 }, BuiltinType::U64 | BuiltinType::S64 | BuiltinType::F64 => { SizeAlign { size: 8, align: 8 } } diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index fad70619b..6015cb052 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -20,6 +20,7 @@ mod kw { wast::custom_keyword!(bitflags); wast::custom_keyword!(char8); + wast::custom_keyword!(char); wast::custom_keyword!(const_pointer); wast::custom_keyword!(f32); wast::custom_keyword!(f64); @@ -57,12 +58,12 @@ 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::() { + if l.peek::() { parser.parse::()?; Ok(BuiltinType::Char8) + } else if l.peek::() { + parser.parse::()?; + Ok(BuiltinType::Char) } else if l.peek::() { parser.parse::()?; Ok(BuiltinType::U8) @@ -101,8 +102,8 @@ 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) || ::peek(cursor) @@ -114,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>, @@ -283,6 +286,7 @@ pub enum TypedefSyntax<'a> { ConstPointer(Box>), Builtin(BuiltinType), Ident(wast::Id<'a>), + String, } impl<'a> Parse<'a> for TypedefSyntax<'a> { @@ -292,6 +296,9 @@ 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(); diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 659f940c8..16b92d48a 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -77,8 +77,8 @@ impl Id { impl BuiltinType { pub fn to_sexpr(&self) -> SExpr { match self { - BuiltinType::String => SExpr::word("string"), BuiltinType::Char8 => SExpr::word("char8"), + BuiltinType::Char => SExpr::word("char"), BuiltinType::USize => SExpr::Vec(vec![SExpr::annot("witx"), SExpr::word("usize")]), BuiltinType::U8 => SExpr::word("u8"), BuiltinType::U16 => SExpr::word("u16"), diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index 88f4a44fb..8d698c77a 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -311,6 +311,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!(), }))), } From cf290811d1f8569781a58a5f37adb8df6ddf2167 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 14:12:49 -0800 Subject: [PATCH 08/10] Implement Union in terms of Variant --- phases/ephemeral/docs.md | 36 ++-- phases/ephemeral/witx/typenames.witx | 20 +- phases/old/snapshot_0/docs.md | 24 +-- phases/old/snapshot_0/witx/typenames.witx | 12 +- phases/snapshot/docs.md | 24 +-- phases/snapshot/witx/typenames.witx | 13 +- tools/witx/src/ast.rs | 15 -- tools/witx/src/coretypes.rs | 2 +- tools/witx/src/docs/ast.rs | 82 +++----- tools/witx/src/docs/md.rs | 2 - tools/witx/src/layout.rs | 56 ------ tools/witx/src/lib.rs | 2 +- tools/witx/src/parser.rs | 91 ++++++--- tools/witx/src/render.rs | 35 +--- tools/witx/src/representation.rs | 103 ++++------ tools/witx/src/validate.rs | 220 ++++++++++++---------- tools/witx/tests/anonymous.rs | 10 +- tools/witx/tests/union.rs | 111 +++++------ 18 files changed, 360 insertions(+), 498 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index c579e00a3..7ea6969f8 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -859,20 +859,18 @@ 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) @@ -961,20 +959,18 @@ The file descriptor on which to wait for it to become ready for reading or writi 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) @@ -1078,20 +1074,18 @@ 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): diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index e10c4c30d..503aa0cf6 100644 --- a/phases/ephemeral/witx/typenames.witx +++ b/phases/ephemeral/witx/typenames.witx @@ -562,10 +562,10 @@ ;;; 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) ) ) @@ -620,10 +620,10 @@ ;;; 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 ) ) @@ -691,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/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index 0e2f5bde0..e77002bc6 100644 --- a/phases/old/snapshot_0/docs.md +++ b/phases/old/snapshot_0/docs.md @@ -899,20 +899,18 @@ The file descriptor on which to wait for it to become ready for reading or writi 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) @@ -1148,20 +1146,18 @@ The length of the directory name for use with [`fd_prestat_dir_name`](#fd_presta 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 f24000e00..eb6f70020 100644 --- a/phases/old/snapshot_0/witx/typenames.witx +++ b/phases/old/snapshot_0/witx/typenames.witx @@ -570,10 +570,10 @@ ;;; 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 ) ) @@ -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 e13dcc79d..1bbbe59f2 100644 --- a/phases/snapshot/docs.md +++ b/phases/snapshot/docs.md @@ -896,20 +896,18 @@ The file descriptor on which to wait for it to become ready for reading or writi 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) @@ -1145,20 +1143,18 @@ The length of the directory name for use with [`fd_prestat_dir_name`](#fd_presta 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 13eb1759f..70e726553 100644 --- a/phases/snapshot/witx/typenames.witx +++ b/phases/snapshot/witx/typenames.witx @@ -570,10 +570,11 @@ ;;; 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 ) ) @@ -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 00c9fac83..5ba525520 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -187,7 +187,6 @@ impl NamedType { pub enum Type { Record(RecordDatatype), Variant(Variant), - Union(UnionDatatype), Handle(HandleDatatype), List(TypeRef), Pointer(TypeRef), @@ -201,7 +200,6 @@ impl Type { match self { Record(_) => "record", Variant(_) => "variant", - Union(_) => "union", Handle(_) => "handle", List(_) => "list", Pointer(_) => "pointer", @@ -272,19 +270,6 @@ pub struct Case { pub docs: String, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UnionDatatype { - pub tag: Rc, - pub variants: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UnionVariant { - pub name: Id, - pub tref: Option, - pub docs: String, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct HandleDatatype {} diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 3a5e41284..35fea801e 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -50,7 +50,7 @@ impl Type { }, Type::List { .. } => TypePassedBy::PointerLengthPair, Type::Pointer { .. } | Type::ConstPointer { .. } => TypePassedBy::Value(AtomType::I32), - Type::Record { .. } | 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()) diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index a776907a3..eb4d3e7cb 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -89,7 +89,6 @@ impl ToMarkdown for Type { match self { Self::Record(a) => a.generate(node.clone()), Self::Variant(a) => a.generate(node.clone()), - Self::Union(a) => a.generate(node.clone()), Self::Handle(a) => a.generate(node.clone()), Self::List(a) => { node.content_ref_mut::().r#type = Some(MdType::List { @@ -144,6 +143,27 @@ impl ToMarkdown for RecordDatatype { impl ToMarkdown for Variant { fn generate(&self, node: MdNodeRef) { + 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")); + + let whole = self.mem_size_align(); + node.new_child(MdSection::new( + MdHeading::new_bullet(), + format!("size: {}", whole.size), + )); + node.new_child(MdSection::new( + MdHeading::new_bullet(), + format!("align: {}", whole.align), + )); + + let tag = self.tag_repr.mem_size_align(); + node.new_child(MdSection::new( + MdHeading::new_bullet(), + format!("tag_size: {}", tag.size), + )); + } + let heading = heading_from_node(&node, 1); node.new_child(MdSection::new(heading, "Variant cases")); @@ -169,61 +189,6 @@ impl ToMarkdown for Variant { } } -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(); - let id = if let Some(id) = node.any_ref().id() { - format!("{}.{}", id, name) - } else { - name.to_owned() - }; - let n = node.new_child(MdNamedType::new( - MdHeading::new_bullet(), - id.as_str(), - name, - &variant.docs, - )); - if let Some(ref tref) = variant.tref { - tref.generate(n.clone()); - } else { - n.content_ref_mut::().r#type = None; - } - } - - node.content_ref_mut::().r#type = Some(MdType::Union); - } -} - impl ToMarkdown for HandleDatatype { fn generate(&self, node: MdNodeRef) { // TODO this needs more work @@ -352,10 +317,7 @@ impl TypeRef { 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::Record { .. } - | Type::Variant { .. } - | Type::Union { .. } - | Type::Handle { .. } => { + Type::Record { .. } | Type::Variant { .. } | Type::Handle { .. } => { unimplemented!("type_name of anonymous compound datatypes") } }, diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index 6519d0745..d3d5b756d 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -345,7 +345,6 @@ impl MdNamedType { pub(super) enum MdType { Record, Variant, - Union, List { r#type: String }, Pointer { r#type: String }, ConstPointer { r#type: String }, @@ -359,7 +358,6 @@ impl fmt::Display for MdType { match self { Self::Record => f.write_fmt(format_args!(": Record"))?, Self::Variant => f.write_fmt(format_args!(": Variant"))?, - Self::Union => f.write_fmt(format_args!(": Union"))?, Self::List { r#type } => { if r#type == "char" { f.write_str(": `string`")? diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index 1e4b03211..6b77d8a32 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -66,7 +66,6 @@ impl Type { match &self { Type::Record(s) => s.layout(cache), Type::Variant(s) => s.mem_size_align(), - Type::Union(u) => u.layout(cache), Type::Handle(h) => h.mem_size_align(), Type::List { .. } => SizeAlign { size: 8, align: 4 }, // Pointer and Length Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(), @@ -178,61 +177,6 @@ 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() diff --git a/tools/witx/src/lib.rs b/tools/witx/src/lib.rs index 72774c16c..9c0f53060 100644 --- a/tools/witx/src/lib.rs +++ b/tools/witx/src/lib.rs @@ -27,7 +27,7 @@ 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, RecordMemberLayout, SizeAlign, 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 6015cb052..ee8bf081e 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -19,6 +19,7 @@ mod kw { pub use wast::kw::{export, func, import, memory, module, param, result}; wast::custom_keyword!(bitflags); + wast::custom_keyword!(case); wast::custom_keyword!(char8); wast::custom_keyword!(char); wast::custom_keyword!(const_pointer); @@ -48,6 +49,7 @@ mod kw { wast::custom_keyword!(u64); wast::custom_keyword!(u8); wast::custom_keyword!(usize); + wast::custom_keyword!(variant); } mod annotation { @@ -280,6 +282,7 @@ pub enum TypedefSyntax<'a> { Flags(FlagsSyntax<'a>), Record(RecordSyntax<'a>), Union(UnionSyntax<'a>), + Variant(VariantSyntax<'a>), Handle(HandleSyntax), List(Box>), Pointer(Box>), @@ -310,6 +313,8 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { 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::() { @@ -448,42 +453,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() { @@ -493,6 +480,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/render.rs b/tools/witx/src/render.rs index 16b92d48a..60df3d6a2 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -118,7 +118,6 @@ impl Type { match self { Type::Record(a) => a.to_sexpr(), Type::Variant(a) => a.to_sexpr(), - Type::Union(a) => a.to_sexpr(), Type::Handle(a) => a.to_sexpr(), Type::List(a) => SExpr::Vec(vec![SExpr::word("list"), a.to_sexpr()]), Type::Pointer(p) => SExpr::Vec(vec![ @@ -172,6 +171,11 @@ impl Variant { } } 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 { @@ -184,39 +188,12 @@ impl Variant { } } -impl UnionDatatype { - 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()]), - ) - } - }) - .collect::>(); - SExpr::Vec([header, variants].concat()) - } -} - impl HandleDatatype { pub fn to_sexpr(&self) -> SExpr { 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 d3f7bf817..6c36e71e7 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -1,6 +1,5 @@ -use crate::{ - BuiltinType, IntRepr, NamedType, RecordDatatype, Type, TypeRef, UnionDatatype, Variant, -}; +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)] @@ -70,24 +69,41 @@ impl Representable for IntRepr { impl Representable for Variant { fn representable(&self, by: &Self) -> RepEquality { + let mut superset = false; // Integer representation must be compatible - if self.tag_repr.representable(&by.tag_repr) == RepEquality::NotEq { - return RepEquality::NotEq; + match self.tag_repr.representable(&by.tag_repr) { + RepEquality::NotEq => return RepEquality::NotEq, + RepEquality::Eq => {} + RepEquality::Superset => superset = true, } - // For each variant in self, must have variant of same name and position in by: - for (ix, v) in self.cases.iter().enumerate() { - if let Some(by_v) = by.cases.get(ix) { - if by_v.name != v.name { - return RepEquality::NotEq; - } - } else { - return RepEquality::NotEq; + 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.cases.len() > self.cases.len() { + if superset || self.cases.len() < by.cases.len() { RepEquality::Superset } else { - self.tag_repr.representable(&by.tag_repr) + RepEquality::Eq } } } @@ -113,56 +129,6 @@ impl Representable for RecordDatatype { } } -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_()) @@ -180,7 +146,6 @@ impl Representable for Type { match (&self, &by) { (Type::Variant(s), Type::Variant(b)) => s.representable(b), (Type::Record(s), Type::Record(b)) => s.representable(b), - (Type::Union(s), Type::Union(b)) => s.representable(b), (Type::Handle(_), Type::Handle(_)) => RepEquality::Eq, // Handles are nominal, not structural (Type::List(s), Type::List(b)) => s.representable(b), (Type::Pointer(s), Type::Pointer(b)) => s.representable(b), @@ -239,12 +204,12 @@ mod test { let base = def_type( "a", "(typename $tag (enum (@witx tag u8) $b $c)) - (typename $a (union $tag (field $b u32) (field $c f32)))", + (typename $a (union (@witx tag $tag) u32 f32))", ); let extra_variant = def_type( "a", "(typename $tag (enum (@witx tag u8) $b $c $d)) - (typename $a (union $tag (field $b u32) (field $c f32) (field $d f64)))", + (typename $a (union (@witx tag $tag) u32 f32 f64))", ); assert_eq!(base.representable(&extra_variant), RepEquality::Superset); @@ -253,7 +218,7 @@ mod test { let other_ordering = def_type( "a", "(typename $tag (enum (@witx tag u8) $b $c)) - (typename $a (union $tag (field $c f32) (field $b u32)))", + (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/validate.rs b/tools/witx/src/validate.rs index 8d698c77a..7abeef733 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -8,9 +8,9 @@ use crate::{ BuiltinType, Case, Constant, Definition, Entry, HandleDatatype, Id, IntRepr, InterfaceFunc, InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, ModuleDefinition, ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordMember, Type, - TypePassedBy, TypeRef, UnionDatatype, UnionVariant, Variant, + 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,14 +43,16 @@ pub enum ValidationError { InvalidFirstResultType { location: Location }, #[error("Anonymous structured types (struct, union, enum, flags, handle) are not permitted")] AnonymousRecord { location: Location }, - #[error("Invalid union field `{name}`: {reason}")] - InvalidUnionField { - name: String, - reason: String, + #[error("Union expected {expected} variants, found {found}")] + UnionSizeMismatch { + expected: usize, + found: usize, location: Location, }, - #[error("Invalid union tag `{name}`: {reason}")] - InvalidUnionTag { + #[error("Invalid union tag: {reason}")] + InvalidUnionTag { reason: String, location: Location }, + #[error("Invalid union field `{name}`: {reason}")] + InvalidUnionField { name: String, reason: String, location: Location, @@ -67,6 +69,7 @@ impl ValidationError { | InvalidRepr { location, .. } | InvalidFirstResultType { location, .. } | AnonymousRecord { location, .. } + | UnionSizeMismatch { location, .. } | InvalidUnionField { location, .. } | InvalidUnionTag { location, .. } => { format!("{}\n{}", location.highlight_source_with(witxio), &self) @@ -299,7 +302,10 @@ impl DocValidationScope<'_> { 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::Union(self.validate_union(&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::List(syntax) => { Type::List(self.validate_datatype(syntax, false, span)?) @@ -385,105 +391,121 @@ impl DocValidationScope<'_> { &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::Variant(e) => { - let mut uses = HashMap::new(); - for c in e.cases.iter() { - if c.tref.is_some() { - return Err(ValidationError::InvalidUnionTag { - name: syntax.tag.name().to_string(), - location: self.location(syntax.tag.span()), - reason: format!("all variant cases should have empty payloads"), - }); - } - uses.insert(c.name.clone(), false); - } - 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( diff --git a/tools/witx/tests/anonymous.rs b/tools/witx/tests/anonymous.rs index a295a3915..6cfb2ea7e 100644 --- a/tools/witx/tests/anonymous.rs +++ b/tools/witx/tests/anonymous.rs @@ -12,9 +12,8 @@ fn anonymous_types() { 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 $b)) (typename $a (@witx pointer (union $tag (field $b u8))))", - ); + 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 $b)))"); @@ -35,9 +34,8 @@ fn anonymous_types() { 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_record = witx::parse( - "(typename $tag (enum $c)) (typename $a (record (field $b (union $tag (field $c u8)))))", - ); + 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_record = witx::parse("(typename $a (record (field $b (@witx pointer u8))))"); diff --git a/tools/witx/tests/union.rs b/tools/witx/tests/union.rs index 30fb4dfdf..6cfa43e39 100644 --- a/tools/witx/tests/union.rs +++ b/tools/witx/tests/union.rs @@ -5,7 +5,7 @@ use witx::{Id, Representable}; fn one_variant_union() { let d = witx::parse( "(typename $tag (enum $c)) - (typename $u (union $tag (field $c u8)))", + (typename $u (union (@witx tag $tag) u8))", ); assert!(d.is_ok()); } @@ -14,14 +14,14 @@ fn one_variant_union() { fn two_variant_union() { let d1 = witx::parse( "(typename $tag (enum $a $b)) - (typename $u (union $tag (field $a u8) (field $b u16)))", + (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 $a $b)) - (typename $u (union $tag (field $b u16) (field $a u8)))", + (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 $b $a)) - (typename $u (union $tag (field $b u16) (field $a u8)))", + (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 $a $b)) - (typename $u (union $tag (empty $a) (field $b u16)))", + (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 $a $b)) - (typename $u (union $tag (empty $a) (empty $b)))", + (typename $u (variant (@witx tag $tag) (case $a) (case $b)))", ); assert!(d2.is_ok(), "d2 is ok"); } @@ -74,20 +68,20 @@ fn many_variant_unions() { let d1 = witx::parse( "(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 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 $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 $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 $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 $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 $c $d)) - (typename $u (union $tag (field $c f32)))", + (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)") } } } From a958d6aad1c786465a329370e856afbb7d34aac0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 14:24:38 -0800 Subject: [PATCH 09/10] Embed type-hint declarations in AST payloads Instead of having a separate variant for `Char8` and `Usize` instead store boolean flags on `U8` and `U32` whether they're intended for these language-specific purposes. This additionally changes to require `@witx` when parsing `char8` to ensure usage is flagged as witx-specific. --- phases/ephemeral/docs.md | 22 +++++++------- .../ephemeral/witx/wasi_ephemeral_args.witx | 4 +-- .../witx/wasi_ephemeral_environ.witx | 4 +-- phases/ephemeral/witx/wasi_ephemeral_fd.witx | 2 +- .../ephemeral/witx/wasi_ephemeral_path.witx | 2 +- tools/witx/src/ast.rs | 29 +++++++++++++++---- tools/witx/src/coretypes.rs | 8 ++--- tools/witx/src/docs/ast.rs | 6 ++-- tools/witx/src/layout.rs | 16 ++++------ tools/witx/src/parser.rs | 23 +++++++++------ tools/witx/src/render.rs | 14 ++++++--- tools/witx/src/representation.rs | 10 ++++--- tools/witx/src/toplevel.rs | 12 ++++++-- tools/witx/src/validate.rs | 8 ++--- tools/witx/tests/multimodule.rs | 7 ++++- tools/witx/tests/union.rs | 2 +- 16 files changed, 102 insertions(+), 67 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 7ea6969f8..54568c75d 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -1,5 +1,5 @@ # Types -## `size`: `usize` +## `size`: `u32` An array size. Note: This is similar to `size_t` in POSIX. @@ -1097,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) @@ -1175,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) @@ -1443,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) @@ -1806,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. @@ -1816,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/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/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 5ba525520..a92c7895f 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -211,12 +211,27 @@ impl Type { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BuiltinType { - Char8, Char, - USize, - U8, + 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. + 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, @@ -237,9 +252,11 @@ pub enum IntRepr { impl IntRepr { pub fn to_builtin(&self) -> BuiltinType { match self { - IntRepr::U8 => BuiltinType::U8, + IntRepr::U8 => BuiltinType::U8 { lang_c_char: false }, IntRepr::U16 => BuiltinType::U16, - IntRepr::U32 => BuiltinType::U32, + IntRepr::U32 => BuiltinType::U32 { + lang_ptr_size: false, + }, IntRepr::U64 => BuiltinType::U64, } } diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs index 35fea801e..826990369 100644 --- a/tools/witx/src/coretypes.rs +++ b/tools/witx/src/coretypes.rs @@ -35,15 +35,13 @@ impl Type { pub fn passed_by(&self) -> TypePassedBy { match self { Type::Builtin(b) => match b { - BuiltinType::U8 + BuiltinType::U8 { .. } | BuiltinType::U16 - | BuiltinType::U32 + | BuiltinType::U32 { .. } | BuiltinType::S8 | BuiltinType::S16 | BuiltinType::S32 - | BuiltinType::Char8 - | BuiltinType::Char - | 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), diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index eb4d3e7cb..5e077e588 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -288,12 +288,10 @@ impl ToMarkdown for InterfaceFuncParam { impl BuiltinType { pub fn type_name(&self) -> &'static str { match self { - BuiltinType::Char8 => "char8", BuiltinType::Char => "char", - BuiltinType::USize => "usize", - BuiltinType::U8 => "u8", + BuiltinType::U8 { .. } => "u8", BuiltinType::U16 => "u16", - BuiltinType::U32 => "u32", + BuiltinType::U32 { .. } => "u32", BuiltinType::U64 => "u64", BuiltinType::S8 => "s8", BuiltinType::S16 => "s16", diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index 6b77d8a32..2bcd1042a 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -68,7 +68,7 @@ impl Type { Type::Variant(s) => s.mem_size_align(), Type::Handle(h) => h.mem_size_align(), Type::List { .. } => SizeAlign { size: 8, align: 4 }, // Pointer and Length - Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(), + Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::S32.mem_size_align(), Type::Builtin(b) => b.mem_size_align(), } } @@ -179,22 +179,18 @@ mod test { 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::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::Char - | BuiltinType::USize - | BuiltinType::U32 - | BuiltinType::S32 - | BuiltinType::F32 => SizeAlign { size: 4, align: 4 }, + BuiltinType::Char | BuiltinType::U32 { .. } | BuiltinType::S32 | BuiltinType::F32 => { + SizeAlign { size: 4, align: 4 } + } BuiltinType::U64 | BuiltinType::S64 | BuiltinType::F64 => { SizeAlign { size: 8, align: 8 } } diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index ee8bf081e..325eb1d35 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -60,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::Char8) - } else if l.peek::() { + 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) @@ -104,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) @@ -331,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()) } diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index 60df3d6a2..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::Char8 => SExpr::word("char8"), BuiltinType::Char => SExpr::word("char"), - BuiltinType::USize => SExpr::Vec(vec![SExpr::annot("witx"), SExpr::word("usize")]), - BuiltinType::U8 => SExpr::word("u8"), + 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"), diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index 6c36e71e7..99f73f5b6 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -30,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, }, diff --git a/tools/witx/src/toplevel.rs b/tools/witx/src/toplevel.rs index 52ef3daa3..993fc8cc4 100644 --- a/tools/witx/src/toplevel.rs +++ b/tools/witx/src/toplevel.rs @@ -110,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] @@ -127,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 7abeef733..88ae10147 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -493,9 +493,9 @@ impl DocValidationScope<'_> { } return Ok((e.tag_repr, Some(names))); } - Type::Builtin(BuiltinType::U8) => return Ok((IntRepr::U8, None)), + 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::U32 { .. }) => return Ok((IntRepr::U32, None)), Type::Builtin(BuiltinType::U64) => return Ok((IntRepr::U64, None)), _ => {} } @@ -522,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/multimodule.rs b/tools/witx/tests/multimodule.rs index a8e35609f..a6c773363 100644 --- a/tools/witx/tests/multimodule.rs +++ b/tools/witx/tests/multimodule.rs @@ -13,7 +13,12 @@ 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"); diff --git a/tools/witx/tests/union.rs b/tools/witx/tests/union.rs index 6cfa43e39..31ade263a 100644 --- a/tools/witx/tests/union.rs +++ b/tools/witx/tests/union.rs @@ -80,7 +80,7 @@ fn many_variant_unions() { (case $i f32) (case $j f64) (case $k (@witx usize)) - (case $l char8) + (case $l (@witx char8)) (case $m) ) )", From 2cdd6be2f6f4fb7f9237489d026bccac244da31b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 9 Feb 2021 07:33:48 -0800 Subject: [PATCH 10/10] Address some review feedback --- phases/ephemeral/docs.md | 2 +- tools/witx/src/ast.rs | 6 ++++++ tools/witx/src/docs/ast.rs | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 54568c75d..2cd2d1361 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -1,5 +1,5 @@ # Types -## `size`: `u32` +## `size`: `usize` An array size. Note: This is similar to `size_t` in POSIX. diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index a92c7895f..7d34b5f7f 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -211,6 +211,9 @@ impl Type { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BuiltinType { + /// 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` @@ -218,6 +221,9 @@ pub enum BuiltinType { /// 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, diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index 5e077e588..6048f73a1 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -291,7 +291,12 @@ impl BuiltinType { 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",