From 96c202b046e28d215c64d3a558566141d8efd8b9 Mon Sep 17 00:00:00 2001 From: Lin Clark Date: Mon, 25 Jan 2021 11:30:39 -0500 Subject: [PATCH 01/19] Add 01-14 meeting notes --- meetings/2021/WASI-01-14.md | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/meetings/2021/WASI-01-14.md b/meetings/2021/WASI-01-14.md index 8012015..9ee3e0b 100644 --- a/meetings/2021/WASI-01-14.md +++ b/meetings/2021/WASI-01-14.md @@ -25,10 +25,51 @@ Installation is required, see the calendar invite. 1. Please help add your name to the meeting notes. 1. Please help take notes. 1. Thanks! +1. Announcement + 1. Pat will be changing branch name from `master` to `main` 1. Review of action items from previous meeting(s). - 1. Dan report on repository organization progress + 1. Lin update on repository organization progress 1. Proposals and discussions 1. Discussion: Testing guidelines for proposals 1. _Sumbit a PR to add your agenda item here_ ## Meeting Notes +### Attendees +Lin Clark +Josh Dolitsky +Dan Gohman +Pat Hickey +Sam Clegg +Casper Beyer +Mark McCaskey +Johnnie Birch +Andrew Brown +Yong He +Peter Engelbert +Peter Huene +Martin Duke +Till Schneidereit +Bogus Wolf + +For people taking notes, just type names; we’ll replace them with abbreviations before posting them publicly. + +**Pat Hickey:** Github is now rolling out features to allow for renaming git branches from “master” to “main”, including blob url redirection. It’s mostly automatic, but it does require people to do some changes locally to update. We’ll put the steps in the WASI github issue tracking this. + +Main topic: Testing. + +**Lin Clark:** + +The context here is WASI modularization -- breaking up the core WASI module into separate modules. + +Pat and Dan are working on a new witx mechanism for expressing dependencies between witx files, allowing modules to depend on each other. + +The main WASI repo will be the source of truth for stage-5 (standardized) proposals. + +Tests should include both the source, in high-level languages, and compiled wasm binaries. + +Things we still need to build: +A test runner +Tests. We’ll start with some tests from Wasmtime, and everyone can add tests. + + + From 89562f904b23cd0df07583b8947ca42c65b2f8a8 Mon Sep 17 00:00:00 2001 From: Lin Clark Date: Thu, 28 Jan 2021 18:15:58 -0500 Subject: [PATCH 02/19] Add 01-28 meeting notes --- meetings/2021/WASI-01-28.md | 177 +++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 3 deletions(-) diff --git a/meetings/2021/WASI-01-28.md b/meetings/2021/WASI-01-28.md index 757ab4d..cab4dbd 100644 --- a/meetings/2021/WASI-01-28.md +++ b/meetings/2021/WASI-01-28.md @@ -25,11 +25,182 @@ Installation is required, see the calendar invite. 1. Please help add your name to the meeting notes. 1. Please help take notes. 1. Thanks! -1. Review of action items from previous meeting(s). - 1. Dan report on repository organization progress +1. Announcements + 1. Lin Clark on WASI modularization milestone 1. Proposals and discussions 1. Presentation: Update from Luke Wagner on handles and resources in interface types - 1. Discussion: Transitioning WASI APIs to use IT handles 1. _Sumbit a PR to add your agenda item here_ ## Meeting Notes +### Attendees +- Martin Duke, F5 +- Sam Clegg +- Dan Gohman +- Luke Wagner +- Mark McCaskey +- Lin Clark +- Dave Protasowski +- Alex Crichton +- Andrew Brown +- Mingqiu Sun +- Till Schneidereit +- Pat Hickey +- Josh Dolitsky +- Matt Butcher +- Ralph Squillace + +### Announcements +**Lin Clark:** We now have a [milestone for WASI modularization](https://github.com/WebAssembly/WASI/milestone/1). Will be using milestones more in the future. + +### Proposals and discussions +#### Presentation: Update from Luke Wagner on handles and resources in interface types [(Slides)](https://docs.google.com/presentation/d/1ikwS2Ps-KLXFofuS5VAs6Bn14q4LBEaxMjPfLj61UZE) + +**Luke Wagner:** Handles and resources. Have been working on this for a while with a number of y’all +Give background, motivating problems, and proposed solution +Not at all final, still working on a PR. Your feedback is most welcome + +IT gives WASI a bunch of value types +In addition to core basic types, a bunch of abbreviations +Don’t want to have to add core types +This lets us take a WASI function and use a bunch of high level types + +Plan of record for a while has been abstract types for file descriptors +Type imports builds on reference types +Reference types are unforgeable, better than ints + +How does this get virtualized? +Virtualization is a core value of WASI. You can implement with another WASI module +For this, we use module linking proposal + +What does it look like to write that virtualized WASI? (walk through) +Packed the file index into the reference, that’s efficient + +Type imports and exports are transparent by default +This is forgeable. That’s why that was take one + +Type imports has a private type definition +I would take abstract type and make it a private type definition +Private.get and private.new only works inside module that defined the type definition +These private types have to be boxed. +Actually no worse than the native implementation to have one level of indirection + +Two remaining problem areas: resource lifetimes and dynamic casting + +Resource lifetimes +How do resources get released? +Could reference count. +Problematic for a variety of reasons. +Not virtualizable. +Because of downsides, WASI resources have explicit destructor + +Virt WASI then looks like…. +What happens on double close or use-after-close? +Spectrum of options, none of them good +How do I efficiently support cross-instance resource sharing? +Not great options either +How can a WASI impl robustly clean up after clients are destroyed? +Could leak. +Want FDs to be closed when the instance goes away, just like OS process +A native WASI impl could do this, but not a virtualized one +How do instances even get different lifetimes? This is a next step, to add ability to dynamically create and destroy instances +Also problems around dynamic casting +E.g. when store n distinct DOM node types in a single table +Not supporting would be a kind of regression + +Dynamic casting breaks otherwise-static invariants +If there’s a run function and I pass a RO, is it possible for X to somehow do a write? +It would be nice to have that answer based only on the static type +Fix is to treat different imports as different things for purpose of dynamic cast +To fix this, you need a wrapping at some membrane/boundary +OK for core wasm, because that’s just a compiler target +But not ok for ecosystem + +When we’re virtualizingWASI we want: … +Is all of this just for virtualization? +Resources are not a uniquely WASI concern +We should treat WASI as an unprivileged set of library interfaces + +Proposal +Add a new interface type called handle +Handle is a value that can be copied +Refers to a resource +A resource is some entity with a lifetime. It can not be copied +So resource types are like abstract type, but only private option +Additionally resource type definitions can have an optional destructor +Resource creation and use is symmetric to private.new/get +Handles are semantically ref-counted and call the destructor when they get to 0 +That’s what gives virt WASI same privileges as native +Also possible to explicitly destroy handles, and references trap + +**Sam Clegg:** would need to be at WASI core? +**Luke Wagner:** So far everything in Interface Types + +**Pat Hickey:** Attenuation +**Luke Wagner:** I’ve gone back and forth. Have an idea in open questions + +**Luke Wagner:** High level summary of IT +They’re lazy copy operations +Need a way to box all interface types +Generally valuable because it solves interesting performance use cases +For handle values, creates a root that creates the stack +That makes it so handles can outlive stack frame + +Once I have a reference, it can be cast +Will always trap if cast to differently privileged + +IT gives us the boundary/membrane where to wrap + +Proposal summary +Additions +New types +New definition +New instructions +Extend rtt.canon + +Problems: +Robust and efficient handling of double-free/use-after-free +…. + +Open Questions +Add abstract subtyping? +Should we have specialized versions of handle (e.g. a strong handle, unique handle) + +Next steps +Need to give lifetimes to instances +Can instances just be resources? +Could implement WASI command pattern in the toolchain rather than host magic + +**Sam Clegg:** What happens if you destroy a resource and another module still has a copy + +**Luke Wagner:** It will have a handle. That handle is then in a null state. When you try to use the handle, it would trap probably at resource.get + +**Sam Clegg:** So the trap would happen in the callee + +**Luke Wagner:** That’s where we might want to have a strong handle where no one could pass you an invalid handle. It’s an open question + +**Ralph Squillace:** For what it’s worth, I’ve been spinning around Pat’s question and going back to DCE-RPC. Believe that you should make or signal a strong decision about shared resources. + +**Luke Wagner:** That’s one of the reasons I like unique, because then when I give you a handle I’m explicitly saying I dont’ still have it. I guess that’s an argument for both unique and strong + +**Ralph Squillace:** Having the ability to make those choices is what comes to mind. If I choose a convention, I can signal it to others. Previous technologies tried to let people do it without signaling capabilities + +**Luke Wagner:** Would a summary be that you’re in favor of those two types of handles + +**Ralph Squillace:** I would say those two handles express two slices of the problem space. I wouldn’t say that I’ve thought through other possible types. + +**Luke Wagner:** Your’e right, I was trying to survey the literature and there are like 20 types + +**Ralph Squillace:** Even if you don’t have strong type, at least you can trap. Maybe that’s the first step. + +**Luke Wagner:** Hierarchy, undefined is worst, trap is better, statically preventing is best + +**Sam Clegg:** These handles are at IT types and at core go to reference. So references can be copied + +**Luke Wagner:** Once you exclusively use IT, there’s no back door + +**Sam Clegg:** Any incremental steps? + +**Luke Wagner:** In some sense, witx already has handle + +**Lin Clark** proposes that we make that the main topic for next meeting + From 9d9d4444abff2dc6dae879905f4268548fb590e4 Mon Sep 17 00:00:00 2001 From: Lin Clark Date: Thu, 4 Feb 2021 10:27:33 -0500 Subject: [PATCH 03/19] Add agendas for 02-11 and 02-25 (#389) --- meetings/2021/WASI-02-11.md | 33 +++++++++++++++++++++++++++++++++ meetings/2021/WASI-02-25.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 meetings/2021/WASI-02-11.md create mode 100644 meetings/2021/WASI-02-25.md diff --git a/meetings/2021/WASI-02-11.md b/meetings/2021/WASI-02-11.md new file mode 100644 index 0000000..f56b6c5 --- /dev/null +++ b/meetings/2021/WASI-02-11.md @@ -0,0 +1,33 @@ +![WASI logo](/WASI.png) + +## Agenda for the February 11 video call of WASI Subgroup + +- **Where**: zoom.us +- **When**: February 11, 17:00-18:00 UTC +- **Location**: *link on calendar invite* +- **Contact**: + - Name: Lin Clark + - Email: lclark@fastly.com + +### Registration + +None required if you've attended before. Email Lin Clark to sign up if it's your first time. + +The meeting is open to CG members only. You can [join the CG here](https://www.w3.org/community/webassembly/). + +## Logistics + +The meeting will be on a zoom.us video conference. +Installation is required, see the calendar invite. + +## Agenda items + +1. Opening, welcome and roll call + 1. Please help add your name to the meeting notes. + 1. Please help take notes. + 1. Thanks! +1. Announcements + 1. _Sumbit a PR to add your announcement here_ +1. Proposals and discussions + 1. Discussion: What are the next steps in adapting WASI to use Interface Types handles and resources? + 1. _Sumbit a PR to add your agenda item here_ diff --git a/meetings/2021/WASI-02-25.md b/meetings/2021/WASI-02-25.md new file mode 100644 index 0000000..dd4892e --- /dev/null +++ b/meetings/2021/WASI-02-25.md @@ -0,0 +1,33 @@ +![WASI logo](/WASI.png) + +## Agenda for the February 25 video call of WASI Subgroup + +- **Where**: zoom.us +- **When**: February 25, 17:00-18:00 UTC +- **Location**: *link on calendar invite* +- **Contact**: + - Name: Lin Clark + - Email: lclark@fastly.com + +### Registration + +None required if you've attended before. Email Lin Clark to sign up if it's your first time. + +The meeting is open to CG members only. You can [join the CG here](https://www.w3.org/community/webassembly/). + +## Logistics + +The meeting will be on a zoom.us video conference. +Installation is required, see the calendar invite. + +## Agenda items + +1. Opening, welcome and roll call + 1. Please help add your name to the meeting notes. + 1. Please help take notes. + 1. Thanks! +1. Announcements + 1. _Sumbit a PR to add your announcement here_ +1. Proposals and discussions + 1. Discussion: Better error handling in WASI + 1. _Sumbit a PR to add your agenda item here_ From a5d8557799c302b92f5ed0e201b288d3924165a6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 07:15:44 -0800 Subject: [PATCH 04/19] 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 93416c8..3f17fda 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 349952a..723d43c 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 34eab3c..9307d97 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 74ff083..b6f672c 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 b56e8c5..1a22344 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 b773727..5e10495 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 2e30836..fcf6a2e 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 8475620..7865bd8 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 02ef9ec..0eb2933 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 168eb77..2373f1e 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 3e2f6ee..6012d38 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 5ff6682..964a5ba 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 6181d35..c779cfa 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 4cf6c53..11778fa 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 2a52fc6..f541e9f 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 c4c5589..2114388 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 5dda0188d62f0a4e14118de98cd478fb0dab09cb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 07:24:15 -0800 Subject: [PATCH 05/19] 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 3f17fda..3bd147b 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 723d43c..d4cc627 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 9307d97..b56aa1f 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 b6f672c..37a49f0 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 1a22344..1cb642a 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 5e10495..b0ccece 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 fcf6a2e..65f354c 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 7865bd8..7335353 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 0eb2933..59c246d 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 2373f1e..65969b8 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 6012d38..d29f192 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 c612e60..d320e15 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 964a5ba..aedee80 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 11778fa..88d7d5d 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 f541e9f..2d03797 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 2114388..adfcb75 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 6bf11d8..2bff699 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 47fb347..a8e3560 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 fe8315d..fe169d3 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 9f88191..f42aac2 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 cede80128ba55154257617a3c50c34c475d79448 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 07:41:30 -0800 Subject: [PATCH 06/19] 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 65f354c..602db82 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 7335353..d226ad6 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 59c246d..fbf9208 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 65969b8..f40200e 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 d29f192..b918e0d 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 d320e15..72774c1 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 88d7d5d..412aaf7 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 a58d9b7a666f792a3e648879bef7cbae020fe1e4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 09:44:08 -0800 Subject: [PATCH 07/19] 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 3bd147b..3d317da 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 d4cc627..c456a99 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 602db82..242fcce 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 d226ad6..c11529f 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 fbf9208..454cac0 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 f40200e..1ca3a87 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 b918e0d..cc4fd0a 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 aedee80..ad91004 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 412aaf7..44ccb54 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 adfcb75..b51596f 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 90f2ba94f284a96b614ba51106b01e5a81157378 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 10:43:05 -0800 Subject: [PATCH 08/19] 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 3d317da..3c9b135 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 c456a99..f2cde82 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 b56aa1f..5c95433 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 37a49f0..4bf485b 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 1cb642a..8272501 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 b0ccece..a2da265 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 242fcce..2e5980f 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 c11529f..1ffe718 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 454cac0..fea7202 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 1ca3a87..4c32f91 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 cc4fd0a..ea23fd3 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 ad91004..d5ce8c3 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 44ccb54..d61c9b0 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 2d03797..5b88c0c 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 b51596f..279cf59 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 2bff699..723a360 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 732b5c0..30fb4df 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 2bcfe644dfa9cddc9a6a7be959d1f68e353b1d6e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 11:23:00 -0800 Subject: [PATCH 09/19] 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 3c9b135..c579e00 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 f2cde82..e10c4c3 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 5c95433..0e2f5bd 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 4bf485b..f24000e 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 8272501..e13dcc7 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 a2da265..13eb175 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 2e5980f..556dadd 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 1ffe718..b2cacf8 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 fea7202..c9fce08 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 4c32f91..22ea160 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 ea23fd3..2154eec 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 d5ce8c3..fad7061 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 d61c9b0..659f940 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 5b88c0c..d3f7bf8 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 4fb13b8..52ef3da 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 279cf59..88f4a44 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 723a360..a295a39 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 1e4830303159921aee2d6d291bd20012d758ef7d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 11:38:10 -0800 Subject: [PATCH 10/19] 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 556dadd..00c9fac 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 b2cacf8..3a5e412 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 c9fce08..a776907 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 22ea160..6519d07 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 2154eec..1e4b032 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 fad7061..6015cb0 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 659f940..16b92d4 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 88f4a44..8d698c7 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 b07d794306fee18648f711115838877de3c901e3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 14:12:49 -0800 Subject: [PATCH 11/19] 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 c579e00..7ea6969 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 e10c4c3..503aa0c 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 0e2f5bd..e77002b 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 f24000e..eb6f700 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 e13dcc7..1bbbe59 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 13eb175..70e7265 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 00c9fac..5ba5255 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 3a5e412..35fea80 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 a776907..eb4d3e7 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 6519d07..d3d5b75 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 1e4b032..6b77d8a 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 72774c1..9c0f530 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 6015cb0..ee8bf08 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 16b92d4..60df3d6 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 d3f7bf8..6c36e71 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 8d698c7..7abeef7 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 a295a39..6cfb2ea 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 30fb4df..6cfa43e 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 3cf4e196f8cb4db8651817be1b874c69efccee09 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Feb 2021 14:24:38 -0800 Subject: [PATCH 12/19] 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 7ea6969..54568c7 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 485fafd..803605f 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 ab56407..6357fe6 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 821d3e7..8192c9b 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 971a6fc..5b3e17e 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 5ba5255..a92c789 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 35fea80..8269903 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 eb4d3e7..5e077e5 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 6b77d8a..2bcd104 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 ee8bf08..325eb1d 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 60df3d6..ad551ae 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 6c36e71..99f73f5 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 52ef3da..993fc8c 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 7abeef7..88ae101 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 a8e3560..a6c7733 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 6cfa43e..31ade26 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 cbce3698a09f0127786ae758f48e3b3abe781381 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 9 Feb 2021 07:33:48 -0800 Subject: [PATCH 13/19] 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 54568c7..2cd2d13 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 a92c789..7d34b5f 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 5e077e5..6048f73 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", From 09bb9022b3acabc797ce53e13165187de26d232f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 8 Feb 2021 13:52:34 -0800 Subject: [PATCH 14/19] Update the Proposals.md links to match the proposals repositories. This updates the Proposals.md page, fixing a few broken links, and updating the names of the proposals to match the actual proposal repositories. --- docs/Proposals.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/Proposals.md b/docs/Proposals.md index 8bff83e..a7a831d 100644 --- a/docs/Proposals.md +++ b/docs/Proposals.md @@ -28,10 +28,11 @@ Proposals follow [this process document](https://github.com/WebAssembly/WASI/blo | ------------------------------------------------------------------------------ | -------------------------------------- | | [I/O][wasi-io] | Dan Gohman | | [Filesystem][wasi-filesystem] | Dan Gohman | -| [Command-Line][wasi-command-line] | Dan Gohman | +| ["Classic" Command-Line][wasi-classic-command] | Dan Gohman | | [Clocks][wasi-clocks] | Dan Gohman | | [Random][wasi-random] | Dan Gohman | -| [Misc][wasi-misc] | Dan Gohman | +| [Handle Index][handle-index] | Dan Gohman | +| [Poll][poll] | Dan Gohman | | [Machine Learning (wasi-nn)][wasi-nn] | Andrew Brown and Mingqiu Sun | ### Phase 1 - Feature Proposal (CG) @@ -51,7 +52,7 @@ Proposals follow [this process document](https://github.com/WebAssembly/WASI/blo Please see [Contributing to WebAssembly](https://github.com/WebAssembly/WASI/blob/master/Contributing.md) for the most up-to-date information on contributing proposals to standard. [wasi-clocks]: https://github.com/WebAssembly/wasi-clocks -[wasi-command-line]: https://github.com/WebAssembly/wasi-classic-command +[wasi-classic-command]: https://github.com/WebAssembly/wasi-classic-command [wasi-crypto]: https://github.com/WebAssembly/wasi-crypto [wasi-filesystem]: https://github.com/WebAssembly/wasi-filesystem [wasi-io]: https://github.com/WebAssembly/wasi-io @@ -59,3 +60,5 @@ Please see [Contributing to WebAssembly](https://github.com/WebAssembly/WASI/blo [wasi-nn]: https://github.com/WebAssembly/wasi-nn [wasi-proxy-wasm]: https://github.com/proxy-wasm/spec [wasi-random]: https://github.com/WebAssembly/wasi-random +[wasi-handle-index]: https://github.com/WebAssembly/wasi-handle-index +[wasi-poll]: https://github.com/WebAssembly/wasi-poll From 9d79e5840e864336a63678245509e257f4450afb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 11 Feb 2021 11:40:18 -0600 Subject: [PATCH 15/19] Add a new style of test suite for the `witx` tooling similar to `*.wast` (#392) * Add a wast-like testsuite for the witx tool As the functionality of `witx` grows this will hopefully make adding tests for new functionality as well as new kinds of tests easier. The goal is to make it very easy to drop a test file with various directives to exercise the functionality of `witx` and its internals. For now the testsuite is quite simple, simply asserting whether documents are either valid or invalid. My hope, though, is that this can be expanded over time with more styles of assertions directives. * Migrate union test to `union.witxt` * Add documentation/roundtrip testing to all documents * Translate multi-module test suite to `*.witxt` * Move wasi tests into `witxt` test suites * Convert representation tests to `*.witxt` * Rebased onto origin/main --- phases/ephemeral/docs.md | 4 +- tools/witx/Cargo.toml | 5 + tools/witx/src/lib.rs | 5 +- tools/witx/src/parser.rs | 5 +- tools/witx/src/representation.rs | 81 +--- tools/witx/src/toplevel.rs | 2 +- tools/witx/src/validate.rs | 34 +- tools/witx/tests/anonymous.rs | 43 -- tools/witx/tests/multimodule.rs | 58 --- tools/witx/tests/union.rs | 174 -------- tools/witx/tests/{wasi.rs => wasi-docs.rs} | 64 --- tools/witx/tests/witxt.rs | 409 ++++++++++++++++++ tools/witx/tests/witxt/anonymous.witxt | 56 +++ tools/witx/tests/witxt/multimodule.witxt | 22 + .../{ => witxt}/multimodule/redefine_a.witx | 0 .../tests/{ => witxt}/multimodule/type_a.witx | 0 .../tests/{ => witxt}/multimodule/type_b.witx | 0 .../tests/{ => witxt}/multimodule/type_c.witx | 0 tools/witx/tests/witxt/representation.witxt | 60 +++ tools/witx/tests/witxt/simple.witxt | 12 + tools/witx/tests/witxt/union.witxt | 97 +++++ tools/witx/tests/witxt/wasi.witxt | 26 ++ 22 files changed, 726 insertions(+), 431 deletions(-) delete mode 100644 tools/witx/tests/anonymous.rs delete mode 100644 tools/witx/tests/multimodule.rs delete mode 100644 tools/witx/tests/union.rs rename tools/witx/tests/{wasi.rs => wasi-docs.rs} (58%) create mode 100644 tools/witx/tests/witxt.rs create mode 100644 tools/witx/tests/witxt/anonymous.witxt create mode 100644 tools/witx/tests/witxt/multimodule.witxt rename tools/witx/tests/{ => witxt}/multimodule/redefine_a.witx (100%) rename tools/witx/tests/{ => witxt}/multimodule/type_a.witx (100%) rename tools/witx/tests/{ => witxt}/multimodule/type_b.witx (100%) rename tools/witx/tests/{ => witxt}/multimodule/type_c.witx (100%) create mode 100644 tools/witx/tests/witxt/representation.witxt create mode 100644 tools/witx/tests/witxt/simple.witxt create mode 100644 tools/witx/tests/witxt/union.witxt create mode 100644 tools/witx/tests/witxt/wasi.witxt diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 2cd2d13..a3c0d0a 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -871,12 +871,12 @@ Alignment: 8 - align: 8 - tag_size: 1 ### Variant cases +- `clock` + - `fd_read`: [`event_fd_readwrite`](#event_fd_readwrite) - `fd_write`: [`event_fd_readwrite`](#event_fd_readwrite) -- `clock` - ## `event`: Record An event that occurred. diff --git a/tools/witx/Cargo.toml b/tools/witx/Cargo.toml index 34c074d..bf9ce16 100644 --- a/tools/witx/Cargo.toml +++ b/tools/witx/Cargo.toml @@ -23,3 +23,8 @@ wast = { version = "22.0.0", default-features = false } diff = "0.1.11" pretty_env_logger = "0.4" structopt = "0.3" +rayon = "1.0" + +[[test]] +name = "witxt" +harness = false diff --git a/tools/witx/src/lib.rs b/tools/witx/src/lib.rs index 9c0f530..2c876aa 100644 --- a/tools/witx/src/lib.rs +++ b/tools/witx/src/lib.rs @@ -9,7 +9,7 @@ mod io; /// Calculate memory layout of types mod layout; /// Witx syntax parsing from SExprs -mod parser; +pub mod parser; /// Paths to witx documents for various proposal phases pub mod phases; /// Calculate required polyfill between interfaces @@ -28,10 +28,9 @@ pub use coretypes::{AtomType, CoreFuncType, CoreParamSignifies, CoreParamType, T pub use docs::Documentation; pub use io::{Filesystem, MockFs, WitxIo}; pub use layout::{Layout, RecordMemberLayout, SizeAlign}; -pub use parser::DeclSyntax; pub use render::SExpr; pub use representation::{RepEquality, Representable}; -pub use validate::ValidationError; +pub use validate::{DocValidation, ValidationError}; use std::path::{Path, PathBuf}; use thiserror::Error; diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index 325eb1d..19d1a03 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -477,7 +477,6 @@ impl<'a> Parse<'a> for UnionSyntax<'a> { None }; let mut fields = Vec::new(); - fields.push(parser.parse()?); while !parser.is_empty() { fields.push(parser.parse()?); } @@ -505,7 +504,9 @@ impl<'a> Parse<'a> for VariantSyntax<'a> { }; let mut cases = Vec::new(); while !parser.is_empty() { - cases.push(parser.parens(|p| p.parse())?); + let comments = parser.parse()?; + let item = parser.parens(|p| p.parse())?; + cases.push(Documented { comments, item }); } Ok(VariantSyntax { tag, cases }) } diff --git a/tools/witx/src/representation.rs b/tools/witx/src/representation.rs index 99f73f5..589b64e 100644 --- a/tools/witx/src/representation.rs +++ b/tools/witx/src/representation.rs @@ -81,12 +81,14 @@ impl Representable for Variant { let other_by_name = by .cases .iter() - .map(|c| (&c.name, c)) + .enumerate() + .map(|(i, c)| (&c.name, (c, i))) .collect::>(); // For each variant in self, must have variant of same name in by: - for v in self.cases.iter() { + for (i, v) in self.cases.iter().enumerate() { let other_ty = match other_by_name.get(&v.name) { - Some(other) => &other.tref, + Some((_, j)) if i != *j => return RepEquality::NotEq, + Some((other, _)) => &other.tref, None => return RepEquality::NotEq, }; match (&v.tref, other_ty) { @@ -157,76 +159,3 @@ impl Representable for Type { } } } - -#[cfg(test)] -mod test { - use super::*; - use crate::io::MockFs; - use crate::toplevel::parse_witx_with; - use crate::Id; - use std::rc::Rc; - - fn def_type(typename: &str, syntax: &str) -> Rc { - use std::path::Path; - let doc = parse_witx_with(&[Path::new("-")], &MockFs::new(&[("-", syntax)])) - .expect("parse witx doc"); - let t = doc.typename(&Id::new(typename)).expect("defined type"); - // Identity should always be true: - assert_eq!(t.representable(&t), RepEquality::Eq, "identity"); - t - } - - #[test] - fn different_typenames() { - 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 enum_() { - 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 (@witx tag u16) $b $c))"); - assert_eq!(smaller_size.representable(&base), RepEquality::Superset); - assert_eq!( - smaller_size.representable(&extra_variant), - RepEquality::Superset - ); - } - - #[test] - fn union() { - let base = def_type( - "a", - "(typename $tag (enum (@witx tag u8) $b $c)) - (typename $a (union (@witx tag $tag) u32 f32))", - ); - let extra_variant = def_type( - "a", - "(typename $tag (enum (@witx tag u8) $b $c $d)) - (typename $a (union (@witx tag $tag) u32 f32 f64))", - ); - - assert_eq!(base.representable(&extra_variant), RepEquality::Superset); - assert_eq!(extra_variant.representable(&base), RepEquality::NotEq); - - let other_ordering = def_type( - "a", - "(typename $tag (enum (@witx tag u8) $b $c)) - (typename $a (variant (@witx tag $tag) (case $c f32) (case $b u32)))", - ); - assert_eq!(base.representable(&other_ordering), RepEquality::Eq); - assert_eq!(other_ordering.representable(&base), RepEquality::Eq); - assert_eq!( - other_ordering.representable(&extra_variant), - RepEquality::Superset - ); - } -} diff --git a/tools/witx/src/toplevel.rs b/tools/witx/src/toplevel.rs index 993fc8c..83e940c 100644 --- a/tools/witx/src/toplevel.rs +++ b/tools/witx/src/toplevel.rs @@ -32,7 +32,7 @@ fn _parse_witx_with(paths: &[&Path], io: &dyn WitxIo) -> Result, + entries: HashMap, constant_scopes: HashMap, } @@ -155,6 +155,10 @@ impl DocValidation { path, } } + + pub fn into_document(self, defs: Vec) -> Document { + Document::new(defs, self.entries) + } } impl DocValidationScope<'_> { @@ -439,14 +443,16 @@ impl DocValidationScope<'_> { } } - let mut names = names.map(|names| names.into_iter().collect::>()); + let mut name_set = names + .as_ref() + .map(|names| names.iter().collect::>()); - let cases = syntax + let mut cases = syntax .cases .iter() .map(|case| { let name = Id::new(case.item.name.name()); - if let Some(names) = &mut names { + if let Some(names) = &mut name_set { if !names.remove(&name) { return Err(ValidationError::InvalidUnionField { name: name.as_str().to_string(), @@ -467,6 +473,18 @@ impl DocValidationScope<'_> { }) }) .collect::, _>>()?; + + // If we have an explicit tag with an enum then that's instructing us to + // reorder cases based on the order of the enum itself, so do that here. + if let Some(names) = names { + let name_pos = names + .iter() + .enumerate() + .map(|(i, name)| (name, i)) + .collect::>(); + cases.sort_by_key(|c| name_pos[&&c.name]); + } + Ok(Variant { tag_repr, cases }) } diff --git a/tools/witx/tests/anonymous.rs b/tools/witx/tests/anonymous.rs deleted file mode 100644 index 6cfb2ea..0000000 --- a/tools/witx/tests/anonymous.rs +++ /dev/null @@ -1,43 +0,0 @@ -use witx; - -fn is_anonymous_record_err(r: Result) -> bool { - match r { - Err(witx::WitxError::Validation(witx::ValidationError::AnonymousRecord { .. })) => true, - _ => false, - } -} - -#[test] -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 u8)))"); - assert!(is_anonymous_record_err(pointer_to_union)); - - 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 $b)))"); - assert!(is_anonymous_record_err(pointer_to_flags)); - - let pointer_to_handle = witx::parse("(typename $a (@witx pointer (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()); - - let pointer_to_pointer = witx::parse("(typename $a (@witx pointer (@witx const_pointer u8)))"); - assert!(pointer_to_pointer.is_ok()); - - 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 u8))))"); - assert!(is_anonymous_record_err(union_in_record)); - - 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 deleted file mode 100644 index a6c7733..0000000 --- a/tools/witx/tests/multimodule.rs +++ /dev/null @@ -1,58 +0,0 @@ -use witx::{load, BuiltinType, Id, Type, TypeRef}; - -#[test] -fn validate_multimodule() { - // B uses A, and C uses A. - let doc = load(&[ - "tests/multimodule/type_b.witx", - "tests/multimodule/type_c.witx", - ]) - .unwrap_or_else(|e| panic!("failed to validate: {}", e)); - - //println!("{}", doc); - - // 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 { - lang_ptr_size: false - }) - ); - - // `b` is a struct with a single member of type `a` - let type_b = doc.typename(&Id::new("b")).expect("type b exists"); - match &*type_b.type_() { - Type::Record(record) => { - assert_eq!(record.members.len(), 1); - assert_eq!( - record.members.get(0).unwrap().tref, - TypeRef::Name(type_a.clone()) - ); - } - _ => panic!("b is a struct"), - } - - // `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::Record(record) => { - assert_eq!(record.members.len(), 2); - assert_eq!( - record.members.get(0).unwrap().tref, - TypeRef::Name(type_a.clone()) - ); - assert_eq!(record.members.get(1).unwrap().tref, TypeRef::Name(type_a)); - } - _ => panic!("c is a struct"), - } -} - -#[test] -fn multimodule_reject_redefinition() { - assert!(load(&[ - "tests/multimodule/type_a.witx", - "tests/multimodule/redefine_a.witx", - ]) - .is_err()) -} diff --git a/tools/witx/tests/union.rs b/tools/witx/tests/union.rs deleted file mode 100644 index 31ade26..0000000 --- a/tools/witx/tests/union.rs +++ /dev/null @@ -1,174 +0,0 @@ -// Every worker needs a union! Time to organize -use witx::{Id, Representable}; - -#[test] -fn one_variant_union() { - let d = witx::parse( - "(typename $tag (enum $c)) - (typename $u (union (@witx tag $tag) u8))", - ); - assert!(d.is_ok()); -} - -#[test] -fn two_variant_union() { - let d1 = witx::parse( - "(typename $tag (enum $a $b)) - (typename $u (union (@witx tag $tag) u8 u16))", - ); - assert!(d1.is_ok(), "d1 is ok"); - - // Fields can come in whatever order: - let d2 = witx::parse( - "(typename $tag (enum $a $b)) - (typename $u (variant (@witx tag $tag) (case $b u16) (case $a u8)))", - ); - assert!(d2.is_ok(), "d2 is ok"); - - // These two unions should be represented the same: - let u1 = d1.unwrap().typename(&Id::new("u")).unwrap().type_(); - let u2 = d2.unwrap().typename(&Id::new("u")).unwrap().type_(); - - assert_eq!( - u1.representable(&u2), - witx::RepEquality::Eq, - "u1 can represent u2" - ); - assert_eq!( - u2.representable(&u1), - witx::RepEquality::Eq, - "u2 can represent u1" - ); - - // Tag order doesnt matter for validation - let d3 = witx::parse( - "(typename $tag (enum $b $a)) - (typename $u (union (@witx tag $tag) u16 u8))", - ); - assert!(d3.is_ok(), "d2 is ok"); -} - -#[test] -fn empty_variant_unions() { - let d1 = witx::parse( - "(typename $tag (enum $a $b)) - (typename $u (variant (@witx tag $tag) (case $a) (case $b u16)))", - ); - assert!(d1.is_ok(), "d1 is ok"); - - let d2 = witx::parse( - "(typename $tag (enum $a $b)) - (typename $u (variant (@witx tag $tag) (case $a) (case $b)))", - ); - assert!(d2.is_ok(), "d2 is ok"); -} - -#[test] -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 - (variant (@witx tag $tag) - (case $a u8) - (case $b u16) - (case $c u32) - (case $d u64) - (case $e s8) - (case $f s16) - (case $g s32) - (case $h s64) - (case $i f32) - (case $j f64) - (case $k (@witx usize)) - (case $l (@witx char8)) - (case $m) - ) - )", - ); - assert!(d1.is_ok(), "d1 is ok"); -} - -#[test] -fn no_tag_union() { - let d = witx::parse("(typename $u (union $tag (field $a u8) (field $b u16)))"); - assert!(d.is_err()); -} - -#[test] -fn wrong_kind_tag_union() { - let d = witx::parse( - "(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 or builtin"); - assert_eq!(got, "list"); -} - -#[test] -fn bad_field_unions() { - let d = witx::parse( - "(typename $tag (enum $c)) - (typename $u (variant (@witx tag $tag) (case $b u8)))", - ); - match validation_err(d) { - witx::ValidationError::InvalidUnionField { name, reason, .. } => { - assert_eq!(name, "b", "bad field name union 1"); - assert_eq!( - reason, "does not correspond to variant in tag `tag`", - "reason union 1" - ); - } - other => panic!("bad error: {}", other), - } - - let d = witx::parse( - "(typename $tag (enum $c)) - (typename $u (variant (@witx tag $tag) (case $c f32) (case $b u8)))", - ); - match validation_err(d) { - witx::ValidationError::UnionSizeMismatch { .. } => {} - other => panic!("bad error: {}", other), - } - - let d = witx::parse( - "(typename $tag (enum $c $d)) - (typename $u (variant (@witx tag $tag) (case $c f32)))", - ); - match validation_err(d) { - witx::ValidationError::UnionSizeMismatch { .. } => {} - other => panic!("bad error: {}", other), - } -} - -fn wrong_kind_name_err( - r: Result, -) -> Option<(&'static str, &'static str)> { - match r { - Err(witx::WitxError::Validation(witx::ValidationError::WrongKindName { - expected, - got, - .. - })) => Some((expected, got)), - Err(e) => { - eprintln!("expected WrongKindName ValidationError, got: {:?}", e); - None - } - Ok(_) => { - eprintln!("expected WrongKindName ValidationError: Ok(witx::Document)"); - None - } - } -} - -fn validation_err(r: Result) -> witx::ValidationError { - match r { - Err(witx::WitxError::Validation(e)) => e, - Err(e) => { - panic!("expected ValidationError, got: {:?}", e) - } - Ok(_) => { - panic!("expected ValidationError, got: Ok(witx::Document)") - } - } -} diff --git a/tools/witx/tests/wasi.rs b/tools/witx/tests/wasi-docs.rs similarity index 58% rename from tools/witx/tests/wasi.rs rename to tools/witx/tests/wasi-docs.rs index 27300a2..6702edf 100644 --- a/tools/witx/tests/wasi.rs +++ b/tools/witx/tests/wasi-docs.rs @@ -2,24 +2,6 @@ use std::fs; use std::path::Path; use witx::{self, Documentation}; -#[test] -fn validate_wasi_snapshot() { - witx::load(&witx::phases::snapshot().unwrap()) - .unwrap_or_else(|e| panic!("failed to parse: {}", e)); -} - -#[test] -fn validate_wasi_ephemeral() { - witx::load(&witx::phases::ephemeral().unwrap()) - .unwrap_or_else(|e| panic!("failed to parse: {}", e)); -} - -#[test] -fn validate_wasi_old_snapshot_0() { - witx::load(&witx::phases::old::snapshot_0().unwrap()) - .unwrap_or_else(|e| panic!("failed to parse: {}", e)); -} - #[test] fn validate_docs() { for phase in &[ @@ -32,52 +14,6 @@ fn validate_docs() { } } -#[test] -fn render_roundtrip() { - let doc = witx::load(&witx::phases::snapshot().unwrap()) - .unwrap_or_else(|e| panic!("failed to parse: {}", e)); - - let back_to_sexprs = format!("{}", doc); - println!("{}", back_to_sexprs); - - let doc2 = witx::parse(&back_to_sexprs) - .map_err(|e| e.report_with(&witx::MockFs::new(&[("-", &back_to_sexprs)]))) - .unwrap(); - - // I'd just assert_eq, but when it fails the debug print is thousands of lines long and impossible - // to figure out where they are unequal. - if doc != doc2 { - for type_ in doc.typenames() { - let type2 = doc2.typename(&type_.name).expect("doc2 missing datatype"); - assert_eq!(type_, type2); - } - for mod_ in doc.modules() { - let mod2 = doc2.module(&mod_.name).expect("doc2 missing module"); - for import in mod_.imports() { - let import2 = mod2.import(&import.name).expect("mod2 missing import"); - assert_eq!(import, import2); - } - for func in mod_.funcs() { - let func2 = mod2.func(&func.name).expect("mod2 missing func"); - assert_eq!(func, func2); - } - } - } - // This should be equivalent to the above, but just in case some code changes where it isnt: - assert_eq!(doc, doc2); -} - -#[test] -fn document_wasi_snapshot() { - use witx::Documentation; - println!( - "{}", - witx::load(&witx::phases::snapshot().unwrap()) - .unwrap_or_else(|e| panic!("failed to parse: {}", e)) - .to_md() - ); -} - fn dos2unix(s: &str) -> String { let mut t = String::new(); t.reserve(s.len()); diff --git a/tools/witx/tests/witxt.rs b/tools/witx/tests/witxt.rs new file mode 100644 index 0000000..1056947 --- /dev/null +++ b/tools/witx/tests/witxt.rs @@ -0,0 +1,409 @@ +//! You can run this test suite with: +//! +//! cargo test --test witxt +//! +//! An argument can be passed as well to filter, based on filename, which test +//! to run +//! +//! cargo test --test witxt foo.witxt + +use anyhow::{anyhow, bail, Context, Result}; +use rayon::prelude::*; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::str; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use wast::parser::{self, Parse, ParseBuffer, Parser}; +use witx::{Documentation, Representable}; + +fn main() { + let tests = find_tests(); + let filter = std::env::args().nth(1); + + let tests = tests + .par_iter() + .filter_map(|test| { + if let Some(filter) = &filter { + if let Some(s) = test.to_str() { + if !s.contains(filter) { + return None; + } + } + } + let contents = std::fs::read(test).unwrap(); + Some((test, contents)) + }) + .collect::>(); + + println!("running {} test files\n", tests.len()); + + let ntests = AtomicUsize::new(0); + let errors = tests + .par_iter() + .filter_map(|(test, contents)| { + WitxtRunner { + ntests: &ntests, + documents: HashMap::new(), + } + .run(test, contents) + .err() + }) + .collect::>(); + + if !errors.is_empty() { + for msg in errors.iter() { + eprintln!("{:?}", msg); + } + + panic!("{} tests failed", errors.len()) + } + + println!( + "test result: ok. {} directives passed\n", + ntests.load(SeqCst) + ); +} + +/// Recursively finds all tests in a whitelisted set of directories which we +/// then load up and test in parallel. +fn find_tests() -> Vec { + let mut tests = Vec::new(); + find_tests("tests/witxt".as_ref(), &mut tests); + tests.sort(); + return tests; + + fn find_tests(path: &Path, tests: &mut Vec) { + for f in path.read_dir().unwrap() { + let f = f.unwrap(); + if f.file_type().unwrap().is_dir() { + find_tests(&f.path(), tests); + continue; + } + + match f.path().extension().and_then(|s| s.to_str()) { + Some("witxt") => {} + _ => continue, + } + tests.push(f.path()); + } + } +} + +struct WitxtRunner<'a> { + ntests: &'a AtomicUsize, + documents: HashMap, +} + +impl WitxtRunner<'_> { + fn run(&mut self, test: &Path, contents: &[u8]) -> Result<()> { + let contents = str::from_utf8(contents)?; + macro_rules! adjust { + ($e:expr) => {{ + let mut e = wast::Error::from($e); + e.set_path(test); + e.set_text(contents); + e + }}; + } + let buf = ParseBuffer::new(contents).map_err(|e| adjust!(e))?; + let witxt = parser::parse::(&buf).map_err(|e| adjust!(e))?; + + let errors = witxt + .directives + .into_iter() + .filter_map(|directive| { + let (line, col) = directive.span().linecol_in(contents); + self.test_directive(contents, test, directive) + .with_context(|| { + format!( + "failed directive on {}:{}:{}", + test.display(), + line + 1, + col + 1 + ) + }) + .err() + }) + .collect::>(); + if errors.is_empty() { + return Ok(()); + } + let mut s = format!("{} test failures in {}:", errors.len(), test.display()); + for mut error in errors { + if let Some(err) = error.downcast_mut::() { + err.set_path(test); + err.set_text(contents); + } + s.push_str("\n\n\t--------------------------------\n\n\t"); + s.push_str(&format!("{:?}", error).replace("\n", "\n\t")); + } + bail!("{}", s) + } + + fn test_directive( + &mut self, + contents: &str, + test: &Path, + directive: WitxtDirective, + ) -> Result<()> { + self.bump_ntests(); + match directive { + WitxtDirective::Witx(witx) => { + let doc = witx.document(contents, test)?; + self.assert_roundtrip(&doc) + .context("failed to round-trip the document")?; + self.assert_md(&doc)?; + if let Some(name) = witx.id { + self.documents.insert(name.name().to_string(), doc); + } + } + WitxtDirective::AssertInvalid { witx, message, .. } => { + let err = match witx.document(contents, test) { + Ok(_) => bail!("witx was valid when it shouldn't be"), + Err(e) => format!("{:?}", anyhow::Error::from(e)), + }; + if !err.contains(message) { + bail!("expected error {:?}\nfound error {}", message, err); + } + } + WitxtDirective::AssertRepresentable { repr, t1, t2, .. } => { + let (t1m, t1t) = t1; + let (t2m, t2t) = t2; + let t1d = self + .documents + .get(t1m.name()) + .ok_or_else(|| anyhow!("no document named {:?}", t1m.name()))?; + let t2d = self + .documents + .get(t2m.name()) + .ok_or_else(|| anyhow!("no document named {:?}", t2m.name()))?; + let t1 = t1d + .typename(&witx::Id::new(t1t)) + .ok_or_else(|| anyhow!("no type named {:?}", t1t))?; + let t2 = t2d + .typename(&witx::Id::new(t2t)) + .ok_or_else(|| anyhow!("no type named {:?}", t2t))?; + match (repr, t1.type_().representable(&t2.type_())) { + (RepEquality::Eq, witx::RepEquality::Eq) + | (RepEquality::Superset, witx::RepEquality::Superset) + | (RepEquality::NotEq, witx::RepEquality::NotEq) => {} + (a, b) => { + bail!("expected {:?} representation, got {:?}", a, b); + } + } + } + } + Ok(()) + } + + fn assert_roundtrip(&self, doc: &witx::Document) -> Result<()> { + self.bump_ntests(); + let back_to_sexprs = format!("{}", doc); + let doc2 = witx::parse(&back_to_sexprs)?; + if *doc == doc2 { + return Ok(()); + } + + // Try to get a more specific error message that isn't thousands of + // lines long of debug representations. + for type_ in doc.typenames() { + let type2 = match doc2.typename(&type_.name) { + Some(t) => t, + None => bail!("doc2 missing datatype"), + }; + if type_ != type2 { + bail!("types are not equal\n{:?}\n !=\n{:?}", type_, type2); + } + } + for mod_ in doc.modules() { + let mod2 = match doc2.module(&mod_.name) { + Some(m) => m, + None => bail!("doc2 missing module"), + }; + for import in mod_.imports() { + let import2 = match mod2.import(&import.name) { + Some(i) => i, + None => bail!("mod2 missing import"), + }; + assert_eq!(import, import2); + } + for func in mod_.funcs() { + let func2 = match mod2.func(&func.name) { + Some(f) => f, + None => bail!("mod2 missing func"), + }; + assert_eq!(func, func2); + } + } + bail!("{:?} != {:?}", doc, doc2) + } + + fn assert_md(&self, doc: &witx::Document) -> Result<()> { + self.bump_ntests(); + doc.to_md(); + Ok(()) + } + + fn bump_ntests(&self) { + self.ntests.fetch_add(1, SeqCst); + } +} + +mod kw { + wast::custom_keyword!(assert_invalid); + wast::custom_keyword!(assert_representable); + wast::custom_keyword!(witx); + wast::custom_keyword!(eq); + wast::custom_keyword!(noteq); + wast::custom_keyword!(load); + wast::custom_keyword!(superset); +} + +struct Witxt<'a> { + directives: Vec>, +} + +impl<'a> Parse<'a> for Witxt<'a> { + fn parse(parser: Parser<'a>) -> parser::Result { + let mut directives = Vec::new(); + while !parser.is_empty() { + directives.push(parser.parens(|p| p.parse())?); + } + Ok(Witxt { directives }) + } +} + +enum WitxtDirective<'a> { + Witx(Witx<'a>), + AssertInvalid { + span: wast::Span, + witx: Witx<'a>, + message: &'a str, + }, + AssertRepresentable { + span: wast::Span, + repr: RepEquality, + t1: (wast::Id<'a>, &'a str), + t2: (wast::Id<'a>, &'a str), + }, +} + +impl WitxtDirective<'_> { + fn span(&self) -> wast::Span { + match self { + WitxtDirective::Witx(w) => w.span, + WitxtDirective::AssertInvalid { span, .. } + | WitxtDirective::AssertRepresentable { span, .. } => *span, + } + } +} + +impl<'a> Parse<'a> for WitxtDirective<'a> { + fn parse(parser: Parser<'a>) -> parser::Result { + let mut l = parser.lookahead1(); + if l.peek::() { + Ok(WitxtDirective::Witx(parser.parse()?)) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(WitxtDirective::AssertInvalid { + span, + witx: parser.parens(|p| p.parse())?, + message: parser.parse()?, + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(WitxtDirective::AssertRepresentable { + span, + repr: parser.parse()?, + t1: (parser.parse()?, parser.parse()?), + t2: (parser.parse()?, parser.parse()?), + }) + } else { + Err(l.error()) + } + } +} + +struct Witx<'a> { + span: wast::Span, + id: Option>, + def: WitxDef<'a>, +} + +enum WitxDef<'a> { + Fs(Vec<&'a str>), + Inline(Vec>>), +} + +impl Witx<'_> { + fn document(&self, contents: &str, file: &Path) -> Result { + match &self.def { + WitxDef::Inline(decls) => { + let mut validator = witx::DocValidation::new(); + let mut definitions = Vec::new(); + for decl in decls { + validator + .scope(contents, file) + .validate_decl(&decl.item, &decl.comments, &mut definitions) + .map_err(witx::WitxError::Validation)?; + } + Ok(validator.into_document(definitions)) + } + WitxDef::Fs(paths) => { + let parent = file.parent().unwrap(); + let paths = paths.iter().map(|p| parent.join(p)).collect::>(); + Ok(witx::load(&paths)?) + } + } + } +} + +impl<'a> Parse<'a> for Witx<'a> { + fn parse(parser: Parser<'a>) -> parser::Result { + let span = parser.parse::()?.0; + let id = parser.parse()?; + + let def = if parser.peek2::() { + parser.parens(|p| { + p.parse::()?; + let mut paths = Vec::new(); + while !p.is_empty() { + paths.push(p.parse()?); + } + Ok(WitxDef::Fs(paths)) + })? + } else { + let mut decls = Vec::new(); + while !parser.is_empty() { + decls.push(parser.parens(|p| p.parse())?); + } + WitxDef::Inline(decls) + }; + Ok(Witx { id, span, def }) + } +} + +#[derive(Debug)] +enum RepEquality { + Eq, + NotEq, + Superset, +} + +impl<'a> Parse<'a> for RepEquality { + fn parse(parser: Parser<'a>) -> parser::Result { + let mut l = parser.lookahead1(); + if l.peek::() { + parser.parse::()?; + Ok(RepEquality::Eq) + } else if l.peek::() { + parser.parse::()?; + Ok(RepEquality::NotEq) + } else if l.peek::() { + parser.parse::()?; + Ok(RepEquality::Superset) + } else { + Err(l.error()) + } + } +} diff --git a/tools/witx/tests/witxt/anonymous.witxt b/tools/witx/tests/witxt/anonymous.witxt new file mode 100644 index 0000000..0ed3bc0 --- /dev/null +++ b/tools/witx/tests/witxt/anonymous.witxt @@ -0,0 +1,56 @@ +;; Ensure that anonymous structured types are not allowed in type positions at +;; this time, everything has to be named to assist in binding in languages. + +(assert_invalid + (witx + (typename $a (@witx pointer (record (field $b u8)))) + ) + "Anonymous structured types") + +(assert_invalid + (witx + (typename $a (@witx pointer (union))) + ) + "Anonymous structured types") + +(assert_invalid + (witx + (typename $a (@witx pointer (enum $b))) + ) + "Anonymous structured types") + +(assert_invalid + (witx + (typename $a (@witx pointer (flags $b))) + ) + "Anonymous structured types") + +(assert_invalid + (witx + (typename $a (@witx pointer (handle))) + ) + "Anonymous structured types") + +(assert_invalid + (witx + (typename $a (record (field $b (record (field $c u8))))) + ) + "Anonymous structured types") + +(assert_invalid + (witx + (typename $tag (enum $c)) + (typename $a (record (field $b (union)))) + ) + "Anonymous structured types") + + +;; pointers don't count for anonymous indirections +(witx + (typename $a (@witx pointer u8))) + +(witx + (typename $a (@witx pointer (@witx const_pointer u8)))) + +(witx + (typename $a (record (field $b (@witx pointer u8))))) diff --git a/tools/witx/tests/witxt/multimodule.witxt b/tools/witx/tests/witxt/multimodule.witxt new file mode 100644 index 0000000..d8b6559 --- /dev/null +++ b/tools/witx/tests/witxt/multimodule.witxt @@ -0,0 +1,22 @@ +;; B uses A, and C uses A. +(witx $multi + (load "multimodule/type_b.witx" "multimodule/type_c.witx") +) + +(witx $reference + (typename $a u32) + (typename $b (record (field $member_a $a))) + (typename $c (record (field $first_a $a) (field $second_a $a))) +) + +(assert_representable eq $reference "a" $multi "a") +(assert_representable eq $reference "b" $multi "b") +(assert_representable eq $reference "c" $multi "c") + +(assert_invalid + (witx + (load + "multimodule/type_a.witx" + "multimodule/redefine_a.witx") + ) + "Redefinition of name `a`") diff --git a/tools/witx/tests/multimodule/redefine_a.witx b/tools/witx/tests/witxt/multimodule/redefine_a.witx similarity index 100% rename from tools/witx/tests/multimodule/redefine_a.witx rename to tools/witx/tests/witxt/multimodule/redefine_a.witx diff --git a/tools/witx/tests/multimodule/type_a.witx b/tools/witx/tests/witxt/multimodule/type_a.witx similarity index 100% rename from tools/witx/tests/multimodule/type_a.witx rename to tools/witx/tests/witxt/multimodule/type_a.witx diff --git a/tools/witx/tests/multimodule/type_b.witx b/tools/witx/tests/witxt/multimodule/type_b.witx similarity index 100% rename from tools/witx/tests/multimodule/type_b.witx rename to tools/witx/tests/witxt/multimodule/type_b.witx diff --git a/tools/witx/tests/multimodule/type_c.witx b/tools/witx/tests/witxt/multimodule/type_c.witx similarity index 100% rename from tools/witx/tests/multimodule/type_c.witx rename to tools/witx/tests/witxt/multimodule/type_c.witx diff --git a/tools/witx/tests/witxt/representation.witxt b/tools/witx/tests/witxt/representation.witxt new file mode 100644 index 0000000..37d20a7 --- /dev/null +++ b/tools/witx/tests/witxt/representation.witxt @@ -0,0 +1,60 @@ +;; type names don't matter +(witx $a + (typename $a (flags (@witx bitflags u8) $b $c))) +(witx $b + (typename $b (flags (@witx bitflags u8) $b $c))) + +(assert_representable eq $a "a" $b "b") +(assert_representable eq $b "b" $a "a") + +(; TODO: perhaps add assertions eventually for document-level representability? +;; flags +(witx $a + (typename $a (flags (@witx bitflags u8) $b $c))) +(witx $b + (typename $b (flags (@witx bitflags u8) $b $c $d))) + +(assert_representable noteq $b "b" $a "a") +(assert_representable superset $a "a" $b "b") + +(witx $c + (typename $c (flags (@witx bitflags u8) $b $e))) +(assert_representable noteq $a "a" $c "c") +(assert_representable noteq $c "c" $a "a") + +(witx $d + (typename $d (flags (@witx bitflags u16) $b $c))) +(assert_representable noteq $a "a" $d "d") +(assert_representable superset $d "d" $a "a") +(assert_representable superset $d "d" $b "b") +;) + +;; enums +(witx $a + (typename $a (enum $b $c))) +(witx $b + (typename $b (enum $b $c $d))) +(assert_representable superset $a "a" $b "b") +(assert_representable noteq $b "b" $a "a") + +(witx $c + (typename $c (enum (@witx tag u16) $b $c))) +(assert_representable superset $c "c" $a "a") +(assert_representable superset $c "c" $b "b") + +;; unions +(witx $a + (typename $tag (enum $b $c)) + (typename $a (union (@witx tag $tag) u32 f32))) +(witx $b + (typename $tag (enum $b $c $d)) + (typename $b (union (@witx tag $tag) u32 f32 f64))) +(assert_representable superset $a "a" $b "b") +(assert_representable noteq $b "b" $a "a") + +(witx $c + (typename $tag (enum $b $c)) + (typename $c (variant (@witx tag $tag) (case $c f32) (case $b u32)))) +(assert_representable eq $a "a" $c "c") +(assert_representable eq $c "c" $a "a") +(assert_representable superset $c "c" $b "b") diff --git a/tools/witx/tests/witxt/simple.witxt b/tools/witx/tests/witxt/simple.witxt new file mode 100644 index 0000000..af24030 --- /dev/null +++ b/tools/witx/tests/witxt/simple.witxt @@ -0,0 +1,12 @@ +(witx) + +(witx + (typename $x u32) +) + +(assert_invalid + (witx + (typename $x u32) + (typename $x u32) + ) + "Redefinition of name `x`") diff --git a/tools/witx/tests/witxt/union.witxt b/tools/witx/tests/witxt/union.witxt new file mode 100644 index 0000000..92be3dc --- /dev/null +++ b/tools/witx/tests/witxt/union.witxt @@ -0,0 +1,97 @@ + +(witx + (typename $u (union u8)) +) +(witx + (typename $tag (enum (@witx tag u8) $c)) + (typename $u (union (@witx tag $tag) u8)) +) + +(witx + (typename $tag (enum $a $b)) + (typename $u (variant (@witx tag $tag) (case $a) (case $b u16))) +) + +(witx + (typename $tag (enum $a $b)) + (typename $u (variant (@witx tag $tag) (case $a) (case $b))) +) + + +(witx + (typename $u + (union + u8 + u16 + u32 + u64 + s8 + s16 + s32 + s64 + f32 + f64 + (@witx usize) + (@witx char8) + ) + ) +) + +(assert_invalid + (witx (typename $u (union (@witx tag $tag) u8 u16))) + "Unknown name `tag`" +) + +(assert_invalid + (witx + (typename $tag string) + (typename $u (union (@witx tag $tag) u8 u16)) + ) + "Wrong kind of name `tag`: expected enum or builtin, got list" +) + +(assert_invalid + (witx + (typename $tag (enum $c)) + (typename $u (variant (@witx tag $tag) (case $b u8))) + ) + "Invalid union field `b`: does not correspond to variant in tag `tag`" +) + +(assert_invalid + (witx + (typename $tag (enum $c)) + (typename $u (union (@witx tag $tag) f32 u8)) + ) + "Union expected 1 variants, found 2" +) + +(assert_invalid + (witx + (typename $tag (enum $c $d)) + (typename $u (union (@witx tag $tag) f32)) + ) + "Union expected 2 variants, found 1" +) + +(witx $d1 + (typename $tag (enum $a $b)) + (typename $u (union (@witx tag $tag) u8 u16)) +) + +(witx $d2 + (typename $tag (enum $a $b)) + (typename $u (variant (@witx tag $tag) (case $b u16) (case $a u8))) +) + +;; These two unions should be represented the same: +(assert_representable eq $d1 "u" $d2 "u") +(assert_representable eq $d2 "u" $d1 "u") + +;; Tag order doesnt matter for validation, but does for rep equality +(witx $d3 + (typename $tag (enum $b $a)) + (typename $u (union (@witx tag $tag) u16 u8)) +) + +(assert_representable noteq $d3 "u" $d1 "u") diff --git a/tools/witx/tests/witxt/wasi.witxt b/tools/witx/tests/witxt/wasi.witxt new file mode 100644 index 0000000..f654a7e --- /dev/null +++ b/tools/witx/tests/witxt/wasi.witxt @@ -0,0 +1,26 @@ +;; Ensure that all current witx definitions in this repository load, parse, +;; roundtrip, and are documentable. + +(witx + (load "../../../../phases/old/snapshot_0/witx/wasi_unstable.witx")) +(witx + (load "../../../../phases/snapshot/witx/wasi_snapshot_preview1.witx")) + +(witx + (load + "../../../../phases/ephemeral/witx/wasi_ephemeral_args.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_clock.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_environ.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_fd.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_path.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_poll.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_proc.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_random.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_sched.witx" + "../../../../phases/ephemeral/witx/wasi_ephemeral_sock.witx" + ) +) +;; should be singularly-loadable as well +(witx + (load + "../../../../phases/ephemeral/witx/wasi_ephemeral_fd.witx")) From bc2eafdb3271908130a44bd922f1ece0d32f1e02 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 11 Feb 2021 09:45:51 -0800 Subject: [PATCH 16/19] Fix proposal links --- docs/Proposals.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Proposals.md b/docs/Proposals.md index a7a831d..8544672 100644 --- a/docs/Proposals.md +++ b/docs/Proposals.md @@ -31,8 +31,8 @@ Proposals follow [this process document](https://github.com/WebAssembly/WASI/blo | ["Classic" Command-Line][wasi-classic-command] | Dan Gohman | | [Clocks][wasi-clocks] | Dan Gohman | | [Random][wasi-random] | Dan Gohman | -| [Handle Index][handle-index] | Dan Gohman | -| [Poll][poll] | Dan Gohman | +| [Handle Index][wasi-handle-index] | Dan Gohman | +| [Poll][wasi-poll] | Dan Gohman | | [Machine Learning (wasi-nn)][wasi-nn] | Andrew Brown and Mingqiu Sun | ### Phase 1 - Feature Proposal (CG) From 3ca67fcee0cc4bcab72087ee4a602f40db5326af Mon Sep 17 00:00:00 2001 From: Lin Clark Date: Tue, 16 Feb 2021 12:54:08 -0500 Subject: [PATCH 17/19] Add 02-11 meeting notes --- meetings/2021/WASI-02-11.md | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/meetings/2021/WASI-02-11.md b/meetings/2021/WASI-02-11.md index f56b6c5..c18c4b8 100644 --- a/meetings/2021/WASI-02-11.md +++ b/meetings/2021/WASI-02-11.md @@ -31,3 +31,88 @@ Installation is required, see the calendar invite. 1. Proposals and discussions 1. Discussion: What are the next steps in adapting WASI to use Interface Types handles and resources? 1. _Sumbit a PR to add your agenda item here_ + +## Notes +### Attendees +Pat Hickey +Andrew Brown +Sergey Rubanov +Lin Clark +Mark McCaskey +Till Schneidereit +Dan Gohman +Josh Dolitsky +Mingqiu Sun +Ralph Squillace +Nicholas +Radu Matei +Matt Fisher +Johnnie Birch +Luke Wagner + +**Lin Clark:** Are there any announcements? +(none) + +**Lin Clark:** Lets talk about how we’re going to move from this version of WASI to a new version that uses handles + +**Dan Gohman:** Witx has been designed to anticipate interface types. It has String and Handle types. These are pretty close to the ones in interface types - PR 391 takes things to the next level and evolves the rest of the witx types towards the interface types. Going forward we need to get rid of the witx pointer and get buffers to work the way they will in interface types, but we’re still waiting to hear from interface types the final word on how those will work. See Interface Types issue 68. Our job on the witx side is to evolve into the Interface Types buffer types. Without waiting we can add support for strings and return types and lower them into the existing C ABI that WASI programs use. + +**Dan Gohman:** Another piece is WASI modularization. It has been in progress for a while but several pieces are coming together. Issue 378. Describes a more granular include mechanism than witx uses right now - import a symbol from a module, rather than import a file. + +**Dan Gohman:** The next two big pieces, which we can address in detail in the future: How do you pass handles into programs? Currently there is a table with preopens in it, but interface types is going to give us new mechanisms that are better than the preopen concept. + +**Dan Gohman:** The last piece is defining new abstractions. wasi PR 240 describes how to merge the send and recv on sockets with read and write on files. We can generalize part of files and sockets into a concept of I/O streams. In interface types you’ll have the vocabulary to ask for e.g. a stream you can write to, without knowing or caring that its backed by a socket or file or pipe etc. What is the noun that you use in function signatures to describe an I/O Stream? Beyond I/O Streams there’s non-streaming operations on files which don’t work on sockets or pipes - pread/pwrite - there’s a place for an I/O object with non-stream personality as well, an array of bytes or a we can call it a dynamic array because it can be resized. There will be other concepts as well - key-value stores, windowed IO with buffers... At the moment the nascent WASI I/O proposal will evolve to develop these ideas. There will still be files, and then a function that gives you a stream from a file. Files are going to be required to interop with existing systems, but we can push people towards interfaces that don’t tie them to the complications of those existing systems, particularly file semantics are very difficult to compose but streams are. This will help us enable shared-nothing linking of modules - we don’t want modules to have to share a filesystem to talk to each other. Questions? + +**Ralph Squillace:** What kinds of objections to this direction do you anticipate? + +**Dan Gohman:** This is going off the path of POSIX. Existing applications and programming languages will be more work to port to I/O streams than having us port libc and standard libraries to WASI. We have ideas for making the standard io operations work on top of I/O streams - you can treat an i/o stream as a libc FILE if you only ever read and write from it, other operations give an error. + +**Ralph Squillace:** The philosophical discussion is about how you weigh the objectives of different applications moving to WASI - some will have to adapt specific POSIX-isms to WASI, where does the work of porting existing code break down - does the code stay in WASM and not use WASI, have its own custom runtime - or does it move to WASI in the coherent way that resolves the semantic differences. I don’t personally see myself trying to port an existing huge monolithic application to WASI, whereas I do see myself working on libraries to make them work in WASI. + +**Lin Clark:** How much is WASI filesystem still going to allow porting POSIX applications? + +**Dan Gohman:** It will allow it. Its a question of priorities rather than absolutes. WASI today runs plenty of interesting POSIX code that doesnt do things like fork(2), WASI filesystem is going to still be there for these applications to use. But we don’t want that to be part of the default for WASI, we want to encourage applications to use simpler concepts that don’t imply a whole filesystem. I want to build tooling that bridges the gap between the shared-nothing-linking space and making big applications work. + +**Till Schneidereit:** We can look at this in terms of layering - there is a core of WASI that may not apply to every use case but you can virtualize many of the other needs on top of it. You can add a filesystem ion top of the core, in many cases, and let applications that need it use it. + +**Dan Gohman:** In terms of migration from where WASI is today to this ideal system - the WASI I/O abstractions can be worked on now, we have witx where we need it. We can flesh out exactly how streams and dynamic arrays work today. + +**Dan Gohman:** Another aspect is portability. I’ve been working on support for WASI on Windows vs Unix the last few months - there are so many differences between those platforms, the semantics of filesystems is radically different and there’s no portability in many cases (its too expensive to bridge them). The WASI I/O abstractions can be designed to hide all of the non-portable parts of filesystems. We can get away from having to specify “what does windows do here” and instead specify the things an application actually needs. + +**Ralph Squillace:** Layering will require thought. + +**Pat Hickey:** The customers we have writing applications right now aren’t concerned with the differences between the filesystem guarantees on linux vs windows, they care about http requests and responses. If we have I/O abstractions that unify the way http bodies work with the way certain aspects of the filesystem work, that is better for the applications we care about. + +**Till Schneidereit:** There are lots of use cases that work with streaming, which has traditionally been a file or a socket, but with abstractions that are hard to generalize. My interpretation of what you were just saying is that if we had a way to have a cleaner system that didn’t have that incidental platform dependence, it would be much easier to build code that works across these very different environments. We have all these applications that are popping up that are very different. Ideally we have a layer that doesn’t care about host differences, and then the layer that cares about that. What we do is just one specific example of that. Don’t want to optimize for old use cases, but all of the new novel ones that are coming about. + +**Radu Matei:** Is the assumption that wasi-filesystem will be developed in parallel? + +**Dan Gohman:** Yes, WASI-filesystem will continue, and wasi-io will be a peer. + +**Ralph Squillace:** Drawing older threads in, what about BSD sockets PR. + +**Dan Gohman:** The contributor who created that hasn’t been active, but I expect that will fit into the system in the same way as wasi-fs. You could get a stream. Networking is a complex topic and I dont’ want to simplify, but the first step will be to take sockets and say the socket gives you a stream that you can read, a stream you can write, and bi-directional r/w. + +Async is a subtlety that we’re leaving out. We can say that async is an orthogonal concern. We expect the Wasm platform will give us better answers over time. But the fundamental concepts of I/O are independent of whether you block that I/O. + +**Lin Clark:** Any other thoughts/comments + +**Luke Wagner:** I have a hypothesis I want to test. Why are people going to want to use wasm outside of a browser? Some people think it’s great because of ISA. We don’t think that’s a great motivator. We think running guest code in latency sensitive and low trust scenarios is the motivator. Things like routers ____ would be host APIs. The libraries would be wasm. These problematic cases don’t have a high value reason to be ported to Wasm. + +**Ralph Squillace:** I understand where you’re coming from. I don’t see WASI as a monolithic process sandbox. I do think that the idea about the sweet spot is right. I’m not a big believer in the thought that the world’s code is going to all be running on WASI. But I think it’s worth consideration how much of the world’s knowledge can be run as WASI. Shouldn’t wall off for all time, but I think the way that Dan laid out the direction, it lets us start with the sweet spot and either layer the others, or add support to WASI later on. I would hate to frame the boundary ruling out knowledge transfer. + +**Luke Wagner:** I agree with your points, so the layering and virtualization are also motivated by that. + +**Dan Gohman:** I can add one thing. Thinking of lib-c, and lots of languages have lib-c like std lib: under the covers, something that people are using as a file could be represented by streams. We can make that work under the covers. We could talk about how that will exactly work. But this is the framework. We can make porting easier by allowing you to use streams by allowing you to use files, but discourage from using file stat and depending on i node. + +**Andrew Brown:** It feels to me that filesystem should be in phase 3 or 4. How do these changes affect moving those to later stages + +**Dan Gohman:** That’s a great question. Wasi-fs is waiting to figure out it’s place. This conversation moves that forward. Understanding that wasi-fs won’t attempt to be abstracting over all hosts, wasi-io does that, and accepting that’s ok means that wasi-fs can probably move to phase 3 at this point. + +**Till Schneidereit:** I think the better way to put it is that it’s not an either/or, we don’t need to make it completely host specific, but let’s find a balance that’s actually viable. + +**Radu Matei:** do we have a document? + +**Dan Gohman:** Will be writing that up. Wanted to introduce here first. Will write up wasi-io ideas soon + +**Pat Hickey:** One blocker to wasi-fs advancing. The filesystem has cloud ABI system of rights. Need to work out what to do with that. From ef8c1a53feb2dfb763d4ea5c7d9e0a0126b45579 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 18 Feb 2021 11:39:17 -0600 Subject: [PATCH 18/19] Take another step towards interface types (#395) * Take another step towards interface types This commit is another large refactoring on the tail of #391 which is intended to help march one step closer to using pure interface types for specifying WASI and using `*.witx` files. Contained here is a large amount of refactoring of the `*.witx` files, the internals of the `witx` crate, and how code generators are supposed to work. At the `*.witx` level, some notable changes have been made: * All functions returning results now return `(expected ok? (error $errno))`. This helps signify that the intention of all functions is that they return a packaged value which is either a successful result or the erroneous error code on failure. ABI-wise nothing has changed, but this is intended to model more closely what the interface-types API of this would be. * The `flags` type in `*.witx` now desugars as a struct-of-bools rather than simply an `int`. This doesn't have any effect on the ABI today, but it does change the AST and type-level representation of these types. All existing `*.witx` files in this repository have been updated for all phases with these new forms. To reiterate, though, no details of the ABI have changed (or at least not intentionally). Everything should still be ABI-compatible with before. Under the hood for code generators this is a very large breaking change. The intention of this commit is to start moving code generators relying on `witx` into the direction that we'll end up with interface types. Namely the `coretypes` module is entirely replaced with a new `abi` module with the intention of handling lots more high-level details of translation than the previous module did. The `InterfaceFunc` type now sports two new methods: `call_wasm` and `call_interface`. These two methods represent the two halves of lifting/lowering operations performed by code generators. The `call_wasm` function takes interface types values (or whatever they are represented as in the source language) and converts them to wasm types to call a wasm import. The wasm import's return values are then "deserialized" back into the language's interface types values too. The `call_interface` function is the opposite, it assumes that you're coming from wasm values and calling, e.g., a host function with interface values. The `abi` module is refactored with an `Instruction` `enum` which lists out what are the current set of "adapter instructions". These adapter instructions are intended to somewhat model the eventual idea of adapter functions and their instructions, but for now they're pretty specific to WASI as-is today. All instructions currently model what WASI currently does, sometimes in very specific manners. It's expected that the `Instruction` type will be in flux as we tweak the ABI of WASI over time, but the current set of instructions will likely only be expanded because of maintaining compatibility with the current snapshot. The expected usage of `Instruction` is that code generators will implement how to generate code for each `Instruction`. This should be a very low-level operation (generating code per instruction) and should be in theory quite easy to implement. Not all code generators need to implement all instructions depending on their use case. Additionally WASI as-is today doesn't always exercise all types of instructions. The next steps after a PR like this include starting to define a new ABI for WASI which supports all types in all positions rather than the current ABI which supports only a limited subset of types in some positions. The intention is that this new ABI may add a new instruction or two but will generally not be a large breaking change for all existing code generators. Some new additions are expected but other than that existing code generators updated to use this PR will require little effort to support new ABIs. * Add more documentation and some more tests for shorthand syntax * Bump to 0.9.0 and bump wast dependency * Support variants as arguments Same as records, they use pointers * Fix a typo --- phases/ephemeral/docs.md | 969 +++++++++++++----- phases/ephemeral/witx/typenames.witx | 22 +- .../ephemeral/witx/wasi_ephemeral_args.witx | 10 +- .../ephemeral/witx/wasi_ephemeral_clock.witx | 6 +- .../witx/wasi_ephemeral_environ.witx | 10 +- phases/ephemeral/witx/wasi_ephemeral_fd.witx | 54 +- .../ephemeral/witx/wasi_ephemeral_path.witx | 25 +- .../ephemeral/witx/wasi_ephemeral_poll.witx | 3 +- .../ephemeral/witx/wasi_ephemeral_random.witx | 2 +- .../ephemeral/witx/wasi_ephemeral_sched.witx | 2 +- .../ephemeral/witx/wasi_ephemeral_sock.witx | 12 +- phases/old/snapshot_0/docs.md | 929 +++++++++++++---- phases/old/snapshot_0/witx/typenames.witx | 20 +- phases/old/snapshot_0/witx/wasi_unstable.witx | 127 +-- phases/snapshot/docs.md | 927 +++++++++++++---- phases/snapshot/witx/typenames.witx | 20 +- .../snapshot/witx/wasi_snapshot_preview1.witx | 125 +-- tools/witx/Cargo.toml | 4 +- tools/witx/src/abi.rs | 925 +++++++++++++++++ tools/witx/src/ast.rs | 178 +++- tools/witx/src/coretypes.rs | 173 ---- tools/witx/src/docs/ast.rs | 93 +- tools/witx/src/docs/md.rs | 51 +- tools/witx/src/layout.rs | 26 +- tools/witx/src/lib.rs | 6 +- tools/witx/src/parser.rs | 77 +- tools/witx/src/render.rs | 61 +- tools/witx/src/toplevel.rs | 6 +- tools/witx/src/validate.rs | 172 +++- tools/witx/tests/witxt.rs | 249 ++++- tools/witx/tests/witxt/abi.witxt | 490 +++++++++ tools/witx/tests/witxt/representation.witxt | 4 +- tools/witx/tests/witxt/shorthand.witxt | 59 ++ 33 files changed, 4528 insertions(+), 1309 deletions(-) create mode 100644 tools/witx/src/abi.rs delete mode 100644 tools/witx/src/coretypes.rs create mode 100644 tools/witx/tests/witxt/abi.witxt create mode 100644 tools/witx/tests/witxt/shorthand.witxt diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index a3c0d0a..7fa4c11 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -22,7 +22,7 @@ Size: 8 Alignment: 8 -## `clockid`: Variant +## `clockid`: `Variant` Identifiers for clocks. Size: 4 @@ -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`: Variant +## `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 @@ -282,82 +282,120 @@ Cross-device link. - `notcapable` Extension: Capabilities insufficient. -## `rights`: `u64` +## `rights`: `Record` File descriptor rights, determining which actions may be performed. Size: 8 Alignment: 8 -### Constants -- `fd_datasync` +### Record members +- `fd_datasync`: `bool` The right to invoke `fd_datasync`. If `path_open` is set, includes the right to invoke `path_open` with [`fdflags::dsync`](#fdflags.dsync). -- `fd_read` +Bit: 0 + +- `fd_read`: `bool` The right to invoke `fd_read` and `sock_recv`. If [`rights::fd_seek`](#rights.fd_seek) is set, includes the right to invoke `fd_pread`. -- `fd_seek` +Bit: 1 + +- `fd_seek`: `bool` The right to invoke `fd_seek`. This flag implies [`rights::fd_tell`](#rights.fd_tell). -- `fd_fdstat_set_flags` +Bit: 2 + +- `fd_fdstat_set_flags`: `bool` The right to invoke `fd_fdstat_set_flags`. -- `fd_sync` +Bit: 3 + +- `fd_sync`: `bool` The right to invoke `fd_sync`. If `path_open` is set, includes the right to invoke `path_open` with [`fdflags::rsync`](#fdflags.rsync) and [`fdflags::dsync`](#fdflags.dsync). -- `fd_tell` +Bit: 4 + +- `fd_tell`: `bool` The right to invoke `fd_seek` in such a way that the file offset remains unaltered (i.e., [`whence::cur`](#whence.cur) with offset zero), or to invoke `fd_tell`. -- `fd_write` +Bit: 5 + +- `fd_write`: `bool` The right to invoke `fd_write` and `sock_send`. If [`rights::fd_seek`](#rights.fd_seek) is set, includes the right to invoke `fd_pwrite`. -- `fd_advise` +Bit: 6 + +- `fd_advise`: `bool` The right to invoke `fd_advise`. -- `fd_allocate` +Bit: 7 + +- `fd_allocate`: `bool` The right to invoke `fd_allocate`. -- `path_create_directory` +Bit: 8 + +- `path_create_directory`: `bool` The right to invoke `path_create_directory`. -- `path_create_file` +Bit: 9 + +- `path_create_file`: `bool` If `path_open` is set, the right to invoke `path_open` with [`oflags::create`](#oflags.create). -- `path_link_source` +Bit: 10 + +- `path_link_source`: `bool` The right to invoke `path_link` with the file descriptor as the source directory. -- `path_link_target` +Bit: 11 + +- `path_link_target`: `bool` The right to invoke `path_link` with the file descriptor as the target directory. -- `path_open` +Bit: 12 + +- `path_open`: `bool` The right to invoke `path_open`. -- `fd_readdir` +Bit: 13 + +- `fd_readdir`: `bool` The right to invoke `fd_readdir`. -- `path_readlink` +Bit: 14 + +- `path_readlink`: `bool` The right to invoke `path_readlink`. -- `path_rename_source` +Bit: 15 + +- `path_rename_source`: `bool` The right to invoke `path_rename` with the file descriptor as the source directory. -- `path_rename_target` +Bit: 16 + +- `path_rename_target`: `bool` The right to invoke `path_rename` with the file descriptor as the target directory. -- `path_filestat_get` +Bit: 17 + +- `path_filestat_get`: `bool` The right to invoke `path_filestat_get`. -- `path_filestat_set_size` +Bit: 18 + +- `path_filestat_set_size`: `bool` The right to change a file's size. If `path_open` is set, includes the right to invoke `path_open` with [`oflags::trunc`](#oflags.trunc). Note: there is no function named `path_filestat_set_size`. This follows POSIX design, @@ -366,41 +404,65 @@ While such function would be desirable from the API design perspective, there ar no use cases for it since no code written for POSIX systems would use it. Moreover, implementing it would require multiple syscalls, leading to inferior performance. -- `path_filestat_set_times` +Bit: 19 + +- `path_filestat_set_times`: `bool` The right to invoke `path_filestat_set_times`. -- `path_permissions_set` +Bit: 20 + +- `path_permissions_set`: `bool` The right to invoke `path_permissions_set`. -- `fd_filestat_get` +Bit: 21 + +- `fd_filestat_get`: `bool` The right to invoke `fd_filestat_get`. -- `fd_filestat_set_size` +Bit: 22 + +- `fd_filestat_set_size`: `bool` The right to invoke `fd_filestat_set_size`. -- `fd_filestat_set_times` +Bit: 23 + +- `fd_filestat_set_times`: `bool` The right to invoke `fd_filestat_set_times`. -- `fd_permissions_set` +Bit: 24 + +- `fd_permissions_set`: `bool` The right to invoke `fd_permissions_set`. -- `path_symlink` +Bit: 25 + +- `path_symlink`: `bool` The right to invoke `path_symlink`. -- `path_remove_directory` +Bit: 26 + +- `path_remove_directory`: `bool` The right to invoke `path_remove_directory`. -- `path_unlink_file` +Bit: 27 + +- `path_unlink_file`: `bool` The right to invoke `path_unlink_file`. -- `poll_fd_readwrite` +Bit: 28 + +- `poll_fd_readwrite`: `bool` If [`rights::fd_read`](#rights.fd_read) is set, includes the right to invoke `poll_oneoff` to subscribe to [`eventtype::fd_read`](#eventtype.fd_read). If [`rights::fd_write`](#rights.fd_write) is set, includes the right to invoke `poll_oneoff` to subscribe to [`eventtype::fd_write`](#eventtype.fd_write). -- `sock_shutdown` +Bit: 29 + +- `sock_shutdown`: `bool` The right to invoke `sock_shutdown`. -## `fd` +Bit: 30 + +## `fd`: `Handle` A file descriptor handle. Size: 4 @@ -408,7 +470,7 @@ Size: 4 Alignment: 4 ### Supertypes -## `iovec`: Record +## `iovec`: `Record` A region of memory for scatter/gather reads. Size: 8 @@ -426,7 +488,7 @@ The length of the buffer to be filled. Offset: 4 -## `ciovec`: Record +## `ciovec`: `Record` A region of memory for scatter/gather writes. Size: 8 @@ -463,7 +525,7 @@ Size: 8 Alignment: 8 -## `whence`: Variant +## `whence`: `Variant` The position relative to which to set the offset of the file descriptor. Size: 1 @@ -504,7 +566,7 @@ Size: 8 Alignment: 8 -## `filetype`: Variant +## `filetype`: `Variant` The type of a file descriptor or file. Size: 1 @@ -539,7 +601,7 @@ The file refers to a symbolic link inode. - `fifo` The file descriptor or file refers to a FIFO. -## `dirent`: Record +## `dirent`: `Record` A directory entry. Size: 24 @@ -567,7 +629,7 @@ The length of the name of the directory entry. Offset: 20 -## `advice`: Variant +## `advice`: `Variant` File or memory access pattern advisory information. Size: 1 @@ -593,32 +655,42 @@ 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`: `u16` +## `fdflags`: `Record` File descriptor flags. Size: 2 Alignment: 2 -### Constants -- `append` +### Record members +- `append`: `bool` Append mode: Data written to the file is always appended to the file's end. -- `dsync` +Bit: 0 + +- `dsync`: `bool` Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. -- `nonblock` +Bit: 1 + +- `nonblock`: `bool` Non-blocking mode. -- `rsync` +Bit: 2 + +- `rsync`: `bool` Synchronized read I/O operations. -- `sync` +Bit: 3 + +- `sync`: `bool` 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`: Record +Bit: 4 + +## `fdstat`: `Record` File descriptor attributes. Size: 24 @@ -655,57 +727,75 @@ Size: 8 Alignment: 8 -## `fstflags`: `u16` +## `fstflags`: `Record` Which file time attributes to adjust. Size: 2 Alignment: 2 -### Constants -- `atim` +### Record members +- `atim`: `bool` Adjust the last data access timestamp to the value stored in [`filestat::atim`](#filestat.atim). -- `atim_now` +Bit: 0 + +- `atim_now`: `bool` Adjust the last data access timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -- `mtim` +Bit: 1 + +- `mtim`: `bool` Adjust the last data modification timestamp to the value stored in [`filestat::mtim`](#filestat.mtim). -- `mtim_now` +Bit: 2 + +- `mtim_now`: `bool` Adjust the last data modification timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -## `lookupflags`: `u32` +Bit: 3 + +## `lookupflags`: `Record` Flags determining the method of how paths are resolved. Size: 4 Alignment: 4 -### Constants -- `symlink_follow` +### Record members +- `symlink_follow`: `bool` As long as the resolved path corresponds to a symbolic link, it is expanded. -## `oflags`: `u16` +Bit: 0 + +## `oflags`: `Record` Open flags used by `path_open`. Size: 2 Alignment: 2 -### Constants -- `create` +### Record members +- `create`: `bool` Create file if it does not exist. -- `directory` +Bit: 0 + +- `directory`: `bool` Fail if not a directory. -- `excl` +Bit: 1 + +- `excl`: `bool` Fail if file already exists. -- `trunc` +Bit: 2 + +- `trunc`: `bool` Truncate file to size 0. +Bit: 3 + ## `linkcount`: `u64` Number of hard links to an inode. @@ -713,7 +803,7 @@ Size: 8 Alignment: 8 -## `permissions`: `u8` +## `permissions`: `Record` 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. @@ -722,8 +812,8 @@ Size: 1 Alignment: 1 -### Constants -- `read` +### Record members +- `read`: `bool` For files, permission to read the file. For directories, permission to do [`readdir`](#readdir) and access files within the directory. @@ -731,23 +821,31 @@ within the directory. Note: This is similar to the read bit being set on files, and the read *and* execute bits being set on directories, in POSIX. -- `write` +Bit: 0 + +- `write`: `bool` For files, permission to mutate the file. For directories, permission to create, remove, and rename items within the directory. -- `execute` +Bit: 1 + +- `execute`: `bool` For files, permission to "execute" the file, using whatever concept of "executing" the host filesystem has. This flag is not valid for directories. -- `private` +Bit: 2 + +- `private`: `bool` For filesystems which have a concept of multiple "users", this flag 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`: Record +Bit: 3 + +## `filestat`: `Record` File attributes. Size: 64 @@ -808,7 +906,7 @@ Size: 8 Alignment: 8 -## `eventtype`: Variant +## `eventtype`: `Variant` Type of a subscription to an event or its occurrence. Size: 1 @@ -828,7 +926,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`: `u16` +## `eventrwflags`: `Record` The state of the file descriptor subscribed to with [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -836,11 +934,13 @@ Size: 2 Alignment: 2 -### Constants -- `fd_readwrite_hangup` +### Record members +- `fd_readwrite_hangup`: `bool` The peer of this socket has closed or disconnected. -## `event_fd_readwrite`: Record +Bit: 0 + +## `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). @@ -859,7 +959,7 @@ The state of the file descriptor. Offset: 8 -## `event_u`: Variant +## `event_u`: `Variant` The contents of an [`event`](#event). Size: 24 @@ -877,7 +977,7 @@ Alignment: 8 - `fd_write`: [`event_fd_readwrite`](#event_fd_readwrite) -## `event`: Record +## `event`: `Record` An event that occurred. Size: 40 @@ -900,7 +1000,7 @@ The type of the event that occurred, and the contents of the event Offset: 16 -## `subclockflags`: `u16` +## `subclockflags`: `Record` Flags determining how to interpret the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -908,15 +1008,17 @@ Size: 2 Alignment: 2 -### Constants -- `subscription_clock_abstime` +### Record members +- `subscription_clock_abstime`: `bool` If set, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) as an absolute timestamp of clock [`subscription_clock::id`](#subscription_clock.id). If clear, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) relative to the current time value of clock [`subscription_clock::id`](#subscription_clock.id). -## `subscription_clock`: Record +Bit: 0 + +## `subscription_clock`: `Record` The contents of a [`subscription`](#subscription) when type is [`eventtype::clock`](#eventtype.clock). Size: 32 @@ -945,7 +1047,7 @@ Flags specifying whether the timeout is absolute or relative Offset: 24 -## `subscription_fd_readwrite`: Record +## `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). @@ -959,7 +1061,7 @@ The file descriptor on which to wait for it to become ready for reading or writi Offset: 0 -## `subscription_u`: Variant +## `subscription_u`: `Variant` The contents of a [`subscription`](#subscription). Size: 40 @@ -977,7 +1079,7 @@ Alignment: 8 - `fd_write`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) -## `subscription`: Record +## `subscription`: `Record` Subscription to an event. Size: 48 @@ -1003,31 +1105,37 @@ Size: 4 Alignment: 4 -## `riflags`: `u16` +## `riflags`: `Record` Flags provided to `sock_recv`. Size: 2 Alignment: 2 -### Constants -- `recv_peek` +### Record members +- `recv_peek`: `bool` Returns the message without removing it from the socket's receive queue. -- `recv_waitall` +Bit: 0 + +- `recv_waitall`: `bool` On byte-stream sockets, block until the full amount of data can be returned. -## `roflags`: `u16` +Bit: 1 + +## `roflags`: `Record` Flags returned by `sock_recv`. Size: 2 Alignment: 2 -### Constants -- `recv_data_truncated` +### Record members +- `recv_data_truncated`: `bool` Returned by `sock_recv`: Message data has been truncated. +Bit: 0 + ## `siflags`: `u16` Flags provided to `sock_send`. As there are currently no flags defined, it must be set to zero. @@ -1036,21 +1144,25 @@ Size: 2 Alignment: 2 -## `sdflags`: `u8` +## `sdflags`: `Record` Which channels on a socket to shut down. Size: 1 Alignment: 1 -### Constants -- `rd` +### Record members +- `rd`: `bool` Disables further receive operations. -- `wr` +Bit: 0 + +- `wr`: `bool` Disables further send operations. -## `preopentype`: Variant +Bit: 1 + +## `preopentype`: `Variant` Identifiers for preopened capabilities. Size: 1 @@ -1061,7 +1173,7 @@ Alignment: 1 - `dir` A pre-opened directory. -## `prestat_dir`: Record +## `prestat_dir`: `Record` The contents of a [`prestat`](#prestat) when its type is [`preopentype::dir`](#preopentype.dir). Size: 4 @@ -1074,7 +1186,7 @@ The length of the directory name for use with `fd_prestat_dir_name`. Offset: 0 -## `prestat`: Variant +## `prestat`: `Variant` Information about a pre-opened capability. Size: 8 @@ -1097,7 +1209,7 @@ When type is [`preopentype::dir`](#preopentype.dir): --- -#### `get(argv: Pointer>, argv_buf: Pointer) -> errno` +#### `get(argv: Pointer>, argv_buf: Pointer) -> Result<(), errno>` Read command-line argument data. The size of the array should match that returned by [`sizes_get`](#sizes_get) @@ -1107,23 +1219,46 @@ The size of the array should match that returned by [`sizes_get`](#sizes_get) - `argv_buf`: `Pointer` ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `sizes_get() -> (errno, size, size)` +#### `sizes_get() -> Result<(size, size), errno>` Return command-line argument data sizes. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, size), errno>` +Returns the number of arguments and the size of the argument string +data, or an error. + +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, size)` -- `argc`: [`size`](#size) -The number of arguments. +####### Record members +- `0`: [`size`](#size) -- `argv_buf_size`: [`size`](#size) -The size of the argument string data. +Offset: 0 + +- `1`: [`size`](#size) + +Offset: 4 + +- `err`: [`errno`](#errno) ## wasi_ephemeral_clock ### Imports @@ -1132,7 +1267,7 @@ The size of the argument string data. --- -#### `res_get(id: clockid) -> (errno, timestamp)` +#### `res_get(id: clockid) -> Result` Return the resolution of a clock. Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, return [`errno::inval`](#errno.inval). @@ -1143,15 +1278,22 @@ Note: This is similar to `clock_getres` in POSIX. The clock for which to return the resolution. ##### Results -- `error`: [`errno`](#errno) - -- `resolution`: [`timestamp`](#timestamp) +- `error`: `Result` The resolution of the clock. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`timestamp`](#timestamp) + +- `err`: [`errno`](#errno) + --- -#### `time_get(id: clockid, precision: timestamp) -> (errno, timestamp)` +#### `time_get(id: clockid, precision: timestamp) -> Result` Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX. @@ -1163,11 +1305,18 @@ The clock for which to return the time. The maximum lag (exclusive) that the returned time value may have, compared to its actual value. ##### Results -- `error`: [`errno`](#errno) - -- `time`: [`timestamp`](#timestamp) +- `error`: `Result` The time value of the clock. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`timestamp`](#timestamp) + +- `err`: [`errno`](#errno) + ## wasi_ephemeral_environ ### Imports #### Memory @@ -1175,7 +1324,7 @@ The time value of the clock. --- -#### `get(environ: Pointer>, environ_buf: Pointer) -> errno` +#### `get(environ: Pointer>, environ_buf: Pointer) -> Result<(), errno>` Read environment variable data. The sizes of the buffers should match that returned by [`sizes_get`](#sizes_get). @@ -1185,23 +1334,46 @@ The sizes of the buffers should match that returned by [`sizes_get`](#sizes_get) - `environ_buf`: `Pointer` ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `sizes_get() -> (errno, size, size)` +#### `sizes_get() -> Result<(size, size), errno>` Return environment variable data sizes. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, size), errno>` +Returns the number of environment variable arguments and the size of the +environment variable data. -- `environc`: [`size`](#size) -The number of environment variable arguments. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, size)` + +####### Record members +- `0`: [`size`](#size) + +Offset: 0 -- `environ_buf_size`: [`size`](#size) -The size of the environment variable data. +- `1`: [`size`](#size) + +Offset: 4 + +- `err`: [`errno`](#errno) ## wasi_ephemeral_fd ### Imports @@ -1210,7 +1382,7 @@ The size of the environment variable data. --- -#### `advise(fd: fd, offset: filesize, len: filesize, advice: advice) -> errno` +#### `advise(fd: fd, offset: filesize, len: filesize, advice: advice) -> Result<(), errno>` Provide file advisory information on a file descriptor. Note: This is similar to `posix_fadvise` in POSIX. @@ -1227,12 +1399,21 @@ The length of the region to which the advisory applies. The advice. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `allocate(fd: fd, offset: filesize, len: filesize) -> errno` +#### `allocate(fd: fd, offset: filesize, len: filesize) -> Result<(), errno>` Force the allocation of space in a file. Note: This is similar to `posix_fallocate` in POSIX. @@ -1246,12 +1427,21 @@ The offset at which to start the allocation. The length of the area that is allocated. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `close(fd: fd) -> errno` +#### `close(fd: fd) -> Result<(), errno>` Close a file descriptor. Note: This is similar to [`close`](#close) in POSIX. @@ -1259,12 +1449,21 @@ Note: This is similar to [`close`](#close) in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `datasync(fd: fd) -> errno` +#### `datasync(fd: fd) -> Result<(), errno>` Synchronize the data of a file to disk. Note: This is similar to `fdatasync` in POSIX. @@ -1272,12 +1471,21 @@ Note: This is similar to `fdatasync` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fdstat_get(fd: fd) -> (errno, fdstat)` +#### `fdstat_get(fd: fd) -> Result` Get the attributes of a file descriptor. Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. @@ -1285,15 +1493,22 @@ Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as ad - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `stat`: [`fdstat`](#fdstat) +- `error`: `Result` The buffer where the file descriptor's attributes are stored. +###### Variant Layout +- size: 32 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`fdstat`](#fdstat) + +- `err`: [`errno`](#errno) + --- -#### `fdstat_set_flags(fd: fd, flags: fdflags) -> errno` +#### `fdstat_set_flags(fd: fd, flags: fdflags) -> Result<(), errno>` Adjust the flags associated with a file descriptor. Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. @@ -1304,12 +1519,21 @@ Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. The desired values of the file descriptor flags. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fdstat_set_rights(fd: fd, fs_rights_base: rights, fs_rights_inheriting: rights) -> errno` +#### `fdstat_set_rights(fd: fd, fs_rights_base: rights, fs_rights_inheriting: rights) -> Result<(), errno>` Adjust the rights associated with a file descriptor. This can only be used to remove rights, and returns [`errno::notcapable`](#errno.notcapable) if called in a way that would attempt to add rights @@ -1322,27 +1546,43 @@ The desired rights of the file descriptor. - `fs_rights_inheriting`: [`rights`](#rights) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `filestat_get(fd: fd) -> (errno, filestat)` +#### `filestat_get(fd: fd) -> Result` Return the attributes of an open file. ##### Params - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`filestat`](#filestat) +- `error`: `Result` The buffer where the file's attributes are stored. +###### Variant Layout +- size: 72 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filestat`](#filestat) + +- `err`: [`errno`](#errno) + --- -#### `filestat_set_size(fd: fd, size: filesize) -> errno` +#### `filestat_set_size(fd: fd, size: filesize) -> Result<(), errno>` Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. Note: This is similar to `ftruncate` in POSIX. @@ -1353,12 +1593,21 @@ Note: This is similar to `ftruncate` in POSIX. The desired file size. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `filestat_set_times(fd: fd, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> errno` +#### `filestat_set_times(fd: fd, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> Result<(), errno>` Adjust the timestamps of an open file or directory. Note: This is similar to `futimens` in POSIX. @@ -1375,12 +1624,21 @@ The desired values of the data modification timestamp. A bitmask indicating which timestamps to adjust. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `permissions_set(fd: fd, permissions: permissions) -> errno` +#### `permissions_set(fd: fd, permissions: permissions) -> Result<(), errno>` Set the permissions of a file or directory. This sets the permissions associated with a file or directory in @@ -1401,12 +1659,21 @@ umask to determine which of the user/group/other flags to modify. The permissions associated with the file. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `pread(fd: fd, iovs: iovec_array, offset: filesize) -> (errno, size)` +#### `pread(fd: fd, iovs: iovec_array, offset: filesize) -> Result` Read from a file descriptor, without using and updating the file descriptor's offset. Note: This is similar to `preadv` in Linux (and other Unix-es). @@ -1420,30 +1687,44 @@ List of scatter/gather vectors in which to store data. The offset within the file at which to read. ##### Results -- `error`: [`errno`](#errno) - -- `nread`: [`size`](#size) +- `error`: `Result` The number of bytes read. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `prestat_get(fd: fd) -> (errno, prestat)` +#### `prestat_get(fd: fd) -> Result` Return a description of the given preopened file descriptor. ##### Params - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`prestat`](#prestat) +- `error`: `Result` The buffer where the description is stored. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`prestat`](#prestat) + +- `err`: [`errno`](#errno) + --- -#### `prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> errno` +#### `prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> Result<(), errno>` Return a description of the given preopened file descriptor. ##### Params @@ -1455,12 +1736,21 @@ A buffer into which to write the preopened directory name. - `path_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `pwrite(fd: fd, iovs: ciovec_array, offset: filesize) -> (errno, size)` +#### `pwrite(fd: fd, iovs: ciovec_array, offset: filesize) -> Result` Write to a file descriptor, without using and updating the file descriptor's offset. Note: This is similar to `pwritev` in Linux (and other Unix-es). @@ -1478,15 +1768,22 @@ List of scatter/gather vectors from which to retrieve data. The offset within the file at which to write. ##### Results -- `error`: [`errno`](#errno) - -- `nwritten`: [`size`](#size) +- `error`: `Result` The number of bytes written. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `read(fd: fd, iovs: iovec_array) -> (errno, size)` +#### `read(fd: fd, iovs: iovec_array) -> Result` Read from a file descriptor. Note: This is similar to `readv` in POSIX. @@ -1497,15 +1794,22 @@ Note: This is similar to `readv` in POSIX. List of scatter/gather vectors to which to store data. ##### Results -- `error`: [`errno`](#errno) - -- `nread`: [`size`](#size) +- `error`: `Result` The number of bytes read. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `readdir(fd: fd, buf: Pointer, buf_len: size, cookie: dircookie) -> (errno, size)` +#### `readdir(fd: fd, buf: Pointer, buf_len: size, cookie: dircookie) -> Result` Read directory entries from a directory. When successful, the contents of the output buffer consist of a sequence of directory entries. Each directory entry consists of a [`dirent`](#dirent) object, @@ -1528,15 +1832,22 @@ The buffer where directory entries are stored The location within the directory to start reading ##### Results -- `error`: [`errno`](#errno) - -- `bufused`: [`size`](#size) +- `error`: `Result` The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `renumber(fd: fd, to: fd) -> errno` +#### `renumber(fd: fd, to: fd) -> Result<(), errno>` Atomically replace a file descriptor by renumbering another file descriptor. Due to the strong focus on thread safety, this environment does not provide a mechanism to duplicate or renumber a file descriptor to an arbitrary @@ -1553,12 +1864,21 @@ would disappear if `dup2()` were to be removed entirely. The file descriptor to overwrite. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `seek(fd: fd, offset: filedelta, whence: whence) -> (errno, filesize)` +#### `seek(fd: fd, offset: filedelta, whence: whence) -> Result` Move the offset of a file descriptor. Note: This is similar to `lseek` in POSIX. @@ -1572,15 +1892,22 @@ The number of bytes to move. The base from which the offset is relative. ##### Results -- `error`: [`errno`](#errno) - -- `newoffset`: [`filesize`](#filesize) +- `error`: `Result` The new offset of the file descriptor, relative to the start of the file. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filesize`](#filesize) + +- `err`: [`errno`](#errno) + --- -#### `sync(fd: fd) -> errno` +#### `sync(fd: fd) -> Result<(), errno>` Synchronize the data and metadata of a file to disk. Note: This is similar to `fsync` in POSIX. @@ -1588,12 +1915,21 @@ Note: This is similar to `fsync` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `tell(fd: fd) -> (errno, filesize)` +#### `tell(fd: fd) -> Result` Return the current offset of a file descriptor. Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. @@ -1601,15 +1937,22 @@ Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `offset`: [`filesize`](#filesize) +- `error`: `Result` The current offset of the file descriptor, relative to the start of the file. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filesize`](#filesize) + +- `err`: [`errno`](#errno) + --- -#### `write(fd: fd, iovs: ciovec_array) -> (errno, size)` +#### `write(fd: fd, iovs: ciovec_array) -> Result` Write to a file descriptor. Note: This is similar to `writev` in POSIX. @@ -1624,11 +1967,18 @@ interleaved while [`write`](#write) is executed. List of scatter/gather vectors from which to retrieve data. ##### Results -- `error`: [`errno`](#errno) - -- `nwritten`: [`size`](#size) +- `error`: `Result` The number of bytes written. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + ## wasi_ephemeral_path ### Imports #### Memory @@ -1636,7 +1986,7 @@ The number of bytes written. --- -#### `create_directory(fd: fd, path: string) -> errno` +#### `create_directory(fd: fd, path: string) -> Result<(), errno>` Create a directory. Note: This is similar to `mkdirat` in POSIX. @@ -1647,12 +1997,21 @@ Note: This is similar to `mkdirat` in POSIX. The path at which to create the directory. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `filestat_get(fd: fd, flags: lookupflags, path: string) -> (errno, filestat)` +#### `filestat_get(fd: fd, flags: lookupflags, path: string) -> Result` Return the attributes of a file or directory. Note: This is similar to `stat` in POSIX. @@ -1666,15 +2025,22 @@ Flags determining the method of how the path is resolved. The path of the file or directory to inspect. ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`filestat`](#filestat) +- `error`: `Result` The buffer where the file's attributes are stored. +###### Variant Layout +- size: 72 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filestat`](#filestat) + +- `err`: [`errno`](#errno) + --- -#### `filestat_set_times(fd: fd, flags: lookupflags, path: string, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> errno` +#### `filestat_set_times(fd: fd, flags: lookupflags, path: string, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> Result<(), errno>` Adjust the timestamps of a file or directory. Note: This is similar to `utimensat` in POSIX. @@ -1697,12 +2063,21 @@ The desired values of the data modification timestamp. A bitmask indicating which timestamps to adjust. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `permissions_set(fd: fd, flags: lookupflags, path: string, permissions: permissions) -> errno` +#### `permissions_set(fd: fd, flags: lookupflags, path: string, permissions: permissions) -> Result<(), errno>` Set the permissions of a file or directory. This sets the permissions associated with a file or directory in @@ -1729,12 +2104,21 @@ The path to a file to query. The permissions to associate with the file. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `link(old_fd: fd, old_flags: lookupflags, old_path: string, new_fd: fd, new_path: string) -> errno` +#### `link(old_fd: fd, old_flags: lookupflags, old_path: string, new_fd: fd, new_path: string) -> Result<(), errno>` Create a hard link. Note: This is similar to `linkat` in POSIX. @@ -1754,12 +2138,21 @@ The working directory at which the resolution of the new path starts. The destination path at which to create the hard link. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `open(fd: fd, dirflags: lookupflags, path: string, oflags: oflags, fs_rights_base: rights, fs_rights_inheriting: rights, fdflags: fdflags, permissions: permissions) -> (errno, fd)` +#### `open(fd: fd, dirflags: lookupflags, path: string, oflags: oflags, fs_rights_base: rights, fs_rights_inheriting: rights, fdflags: fdflags, permissions: permissions) -> Result` Open a file or directory. The returned file descriptor is not guaranteed to be the lowest-numbered file descriptor not currently open; it is randomized to prevent @@ -1798,15 +2191,22 @@ file descriptors derived from it. If a file is created, the filesystem permissions to associate with it. ##### Results -- `error`: [`errno`](#errno) - -- `opened_fd`: [`fd`](#fd) +- `error`: `Result` The file descriptor of the file that has been opened. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`fd`](#fd) + +- `err`: [`errno`](#errno) + --- -#### `readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> (errno, size)` +#### `readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> Result` Read the contents of a symbolic link. Note: This is similar to `readlinkat` in POSIX. @@ -1822,15 +2222,22 @@ The buffer to which to write the contents of the symbolic link. - `buf_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) - -- `bufused`: [`size`](#size) +- `error`: `Result` The number of bytes placed in the buffer. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `remove_directory(fd: fd, path: string) -> errno` +#### `remove_directory(fd: fd, path: string) -> Result<(), errno>` Remove a directory. Return [`errno::notempty`](#errno.notempty) if the directory is not empty. Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. @@ -1842,12 +2249,21 @@ Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. The path to a directory to remove. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `rename(fd: fd, old_path: string, new_fd: fd, new_path: string) -> errno` +#### `rename(fd: fd, old_path: string, new_fd: fd, new_path: string) -> Result<(), errno>` Rename a file or directory. Note: This is similar to `renameat` in POSIX. @@ -1864,12 +2280,21 @@ The working directory at which the resolution of the new path starts. The destination path to which to rename the file or directory. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `symlink(old_path: string, fd: fd, new_path: string) -> errno` +#### `symlink(old_path: string, fd: fd, new_path: string) -> Result<(), errno>` Create a symbolic link. Note: This is similar to `symlinkat` in POSIX. @@ -1883,12 +2308,21 @@ The contents of the symbolic link. The destination path at which to create the symbolic link. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `unlink_file(fd: fd, path: string) -> errno` +#### `unlink_file(fd: fd, path: string) -> Result<(), errno>` Unlink a file. Return [`errno::isdir`](#errno.isdir) if the path refers to a directory. Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. @@ -1900,7 +2334,16 @@ Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. The path to a file to unlink. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) ## wasi_ephemeral_poll ### Imports @@ -1909,7 +2352,7 @@ The path to a file to unlink. --- -#### `oneoff(in: ConstPointer, out: Pointer, nsubscriptions: size) -> (errno, size)` +#### `oneoff(in: ConstPointer, out: Pointer, nsubscriptions: size) -> Result` Concurrently poll for the occurrence of a set of events. If `nsubscriptions` is 0, returns [`errno::inval`](#errno.inval). @@ -1925,11 +2368,18 @@ The events that have occurred. Both the number of subscriptions and events. ##### Results -- `error`: [`errno`](#errno) - -- `nevents`: [`size`](#size) +- `error`: `Result` The number of events stored. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + ## wasi_ephemeral_proc ### Imports ### Functions @@ -1953,7 +2403,7 @@ The exit code returned by the process. --- -#### `get(buf: Pointer, buf_len: size) -> errno` +#### `get(buf: Pointer, buf_len: size) -> Result<(), errno>` Write high-quality random data into a buffer. This function blocks when the implementation is unable to immediately provide sufficient high-quality random data. @@ -1968,7 +2418,16 @@ The buffer to fill with random data. - `buf_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) ## wasi_ephemeral_sched ### Imports @@ -1976,13 +2435,22 @@ The buffer to fill with random data. --- -#### `yield() -> errno` +#### `yield() -> Result<(), errno>` Temporarily yield execution of the calling thread. Note: This is similar to [`yield`](#yield) in POSIX. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) ## wasi_ephemeral_sock ### Imports @@ -1991,7 +2459,7 @@ Note: This is similar to [`yield`](#yield) in POSIX. --- -#### `recv(fd: fd, ri_data: iovec_array, ri_flags: riflags) -> (errno, size, roflags)` +#### `recv(fd: fd, ri_data: iovec_array, ri_flags: riflags) -> Result<(size, roflags), errno>` Receive a message from a socket. Note: This is similar to [`recv`](#recv) in POSIX, though it also supports reading the data into multiple buffers in the manner of `readv`. @@ -2006,18 +2474,31 @@ List of scatter/gather vectors to which to store data. Message flags. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, roflags), errno>` +Number of bytes stored in ri_data and message flags. -- `ro_datalen`: [`size`](#size) -Number of bytes stored in ri_data. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, roflags)` -- `ro_flags`: [`roflags`](#roflags) -Message flags. +####### Record members +- `0`: [`size`](#size) + +Offset: 0 + +- `1`: [`roflags`](#roflags) + +Offset: 4 + +- `err`: [`errno`](#errno) --- -#### `send(fd: fd, si_data: ciovec_array, si_flags: siflags) -> (errno, size)` +#### `send(fd: fd, si_data: ciovec_array, si_flags: siflags) -> Result` Send a message on a socket. Note: This is similar to [`send`](#send) in POSIX, though it also supports writing the data from multiple buffers in the manner of `writev`. @@ -2032,15 +2513,22 @@ List of scatter/gather vectors to which to retrieve data Message flags. ##### Results -- `error`: [`errno`](#errno) - -- `so_datalen`: [`size`](#size) +- `error`: `Result` Number of bytes transmitted. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `shutdown(fd: fd, how: sdflags) -> errno` +#### `shutdown(fd: fd, how: sdflags) -> Result<(), errno>` Shut down socket send and receive channels. Note: This is similar to [`shutdown`](#shutdown) in POSIX. @@ -2051,5 +2539,14 @@ Note: This is similar to [`shutdown`](#shutdown) in POSIX. Which channels on the socket to shut down. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) diff --git a/phases/ephemeral/witx/typenames.witx b/phases/ephemeral/witx/typenames.witx index 503aa0c..1f9abc5 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 (@witx bitflags u64) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u32) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u8) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u8) + (flags (@witx repr u8) ;;; Disables further receive operations. $rd ;;; Disables further send operations. diff --git a/phases/ephemeral/witx/wasi_ephemeral_args.witx b/phases/ephemeral/witx/wasi_ephemeral_args.witx index 803605f..b8d77b5 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_args.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_args.witx @@ -14,15 +14,13 @@ (@interface func (export "get") (param $argv (@witx pointer (@witx pointer (@witx char8)))) (param $argv_buf (@witx pointer (@witx char8))) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return command-line argument data sizes. (@interface func (export "sizes_get") - (result $error $errno) - ;;; The number of arguments. - (result $argc $size) - ;;; The size of the argument string data. - (result $argv_buf_size $size) + ;;; Returns the number of arguments and the size of the argument string + ;;; data, or an error. + (result $error (expected (tuple $size $size) (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_clock.witx b/phases/ephemeral/witx/wasi_ephemeral_clock.witx index 3b00e0a..a980529 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_clock.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_clock.witx @@ -18,9 +18,8 @@ (@interface func (export "res_get") ;;; The clock for which to return the resolution. (param $id $clockid) - (result $error $errno) ;;; The resolution of the clock. - (result $resolution $timestamp) + (result $error (expected $timestamp (error $errno))) ) ;;; Return the time value of a clock. @@ -30,8 +29,7 @@ (param $id $clockid) ;;; The maximum lag (exclusive) that the returned time value may have, compared to its actual value. (param $precision $timestamp) - (result $error $errno) ;;; The time value of the clock. - (result $time $timestamp) + (result $error (expected $timestamp (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_environ.witx b/phases/ephemeral/witx/wasi_ephemeral_environ.witx index 6357fe6..0cad3c1 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_environ.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_environ.witx @@ -14,15 +14,13 @@ (@interface func (export "get") (param $environ (@witx pointer (@witx pointer (@witx char8)))) (param $environ_buf (@witx pointer (@witx char8))) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return environment variable data sizes. (@interface func (export "sizes_get") - (result $error $errno) - ;;; The number of environment variable arguments. - (result $environc $size) - ;;; The size of the environment variable data. - (result $environ_buf_size $size) + ;;; Returns the number of environment variable arguments and the size of the + ;;; environment variable data. + (result $error (expected (tuple $size $size) (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_fd.witx b/phases/ephemeral/witx/wasi_ephemeral_fd.witx index 8192c9b..979e9a3 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_fd.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_fd.witx @@ -21,7 +21,7 @@ (param $len $filesize) ;;; The advice. (param $advice $advice) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Force the allocation of space in a file. @@ -32,30 +32,29 @@ (param $offset $filesize) ;;; The length of the area that is allocated. (param $len $filesize) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Close a file descriptor. ;;; Note: This is similar to `close` in POSIX. (@interface func (export "close") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Synchronize the data of a file to disk. ;;; Note: This is similar to `fdatasync` in POSIX. (@interface func (export "datasync") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Get the attributes of a file descriptor. ;;; Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. (@interface func (export "fdstat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the file descriptor's attributes are stored. - (result $stat $fdstat) + (result $error (expected $fdstat (error $errno))) ) ;;; Adjust the flags associated with a file descriptor. @@ -64,7 +63,7 @@ (param $fd $fd) ;;; The desired values of the file descriptor flags. (param $flags $fdflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Adjust the rights associated with a file descriptor. @@ -74,15 +73,14 @@ ;;; The desired rights of the file descriptor. (param $fs_rights_base $rights) (param $fs_rights_inheriting $rights) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the attributes of an open file. (@interface func (export "filestat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the file's attributes are stored. - (result $buf $filestat) + (result $error (expected $filestat (error $errno))) ) ;;; Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. @@ -91,7 +89,7 @@ (param $fd $fd) ;;; The desired file size. (param $size $filesize) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Adjust the timestamps of an open file or directory. @@ -104,7 +102,7 @@ (param $mtim $timestamp) ;;; A bitmask indicating which timestamps to adjust. (param $fst_flags $fstflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Set the permissions of a file or directory. @@ -123,7 +121,7 @@ (param $fd $fd) ;;; The permissions associated with the file. (param $permissions $permissions) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Read from a file descriptor, without using and updating the file descriptor's offset. @@ -134,17 +132,15 @@ (param $iovs $iovec_array) ;;; The offset within the file at which to read. (param $offset $filesize) - (result $error $errno) ;;; The number of bytes read. - (result $nread $size) + (result $error (expected $size (error $errno))) ) ;;; Return a description of the given preopened file descriptor. (@interface func (export "prestat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the description is stored. - (result $buf $prestat) + (result $error (expected $prestat (error $errno))) ) ;;; Return a description of the given preopened file descriptor. @@ -153,7 +149,7 @@ ;;; A buffer into which to write the preopened directory name. (param $path (@witx pointer (@witx char8))) (param $path_len $size) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Write to a file descriptor, without using and updating the file descriptor's offset. @@ -168,9 +164,8 @@ (param $iovs $ciovec_array) ;;; The offset within the file at which to write. (param $offset $filesize) - (result $error $errno) ;;; The number of bytes written. - (result $nwritten $size) + (result $error (expected $size (error $errno))) ) ;;; Read from a file descriptor. @@ -179,9 +174,8 @@ (param $fd $fd) ;;; List of scatter/gather vectors to which to store data. (param $iovs $iovec_array) - (result $error $errno) ;;; The number of bytes read. - (result $nread $size) + (result $error (expected $size (error $errno))) ) ;;; Read directory entries from a directory. @@ -201,9 +195,8 @@ (param $buf_len $size) ;;; The location within the directory to start reading (param $cookie $dircookie) - (result $error $errno) ;;; The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. - (result $bufused $size) + (result $error (expected $size (error $errno))) ) ;;; Atomically replace a file descriptor by renumbering another file descriptor. @@ -220,7 +213,7 @@ (param $fd $fd) ;;; The file descriptor to overwrite. (param $to $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Move the offset of a file descriptor. @@ -231,25 +224,23 @@ (param $offset $filedelta) ;;; The base from which the offset is relative. (param $whence $whence) - (result $error $errno) ;;; The new offset of the file descriptor, relative to the start of the file. - (result $newoffset $filesize) + (result $error (expected $filesize (error $errno))) ) ;;; Synchronize the data and metadata of a file to disk. ;;; Note: This is similar to `fsync` in POSIX. (@interface func (export "sync") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the current offset of a file descriptor. ;;; Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. (@interface func (export "tell") (param $fd $fd) - (result $error $errno) ;;; The current offset of the file descriptor, relative to the start of the file. - (result $offset $filesize) + (result $error (expected $filesize (error $errno))) ) ;;; Write to a file descriptor. @@ -262,8 +253,7 @@ (param $fd $fd) ;;; List of scatter/gather vectors from which to retrieve data. (param $iovs $ciovec_array) - (result $error $errno) ;;; The number of bytes written. - (result $nwritten $size) + (result $error (expected $size (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_path.witx b/phases/ephemeral/witx/wasi_ephemeral_path.witx index 5b3e17e..2901dec 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_path.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_path.witx @@ -17,7 +17,7 @@ (param $fd $fd) ;;; The path at which to create the directory. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the attributes of a file or directory. @@ -28,9 +28,8 @@ (param $flags $lookupflags) ;;; The path of the file or directory to inspect. (param $path string) - (result $error $errno) ;;; The buffer where the file's attributes are stored. - (result $buf $filestat) + (result $error (expected $filestat (error $errno))) ) ;;; Adjust the timestamps of a file or directory. @@ -47,7 +46,7 @@ (param $mtim $timestamp) ;;; A bitmask indicating which timestamps to adjust. (param $fst_flags $fstflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Set the permissions of a file or directory. @@ -70,7 +69,7 @@ (param $path string) ;;; The permissions to associate with the file. (param $permissions $permissions) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Create a hard link. @@ -85,7 +84,7 @@ (param $new_fd $fd) ;;; The destination path at which to create the hard link. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Open a file or directory. @@ -119,9 +118,8 @@ (param $fdflags $fdflags) ;;; If a file is created, the filesystem permissions to associate with it. (param $permissions $permissions) - (result $error $errno) ;;; The file descriptor of the file that has been opened. - (result $opened_fd $fd) + (result $error (expected $fd (error $errno))) ) ;;; Read the contents of a symbolic link. @@ -133,9 +131,8 @@ ;;; The buffer to which to write the contents of the symbolic link. (param $buf (@witx pointer (@witx char8))) (param $buf_len $size) - (result $error $errno) ;;; The number of bytes placed in the buffer. - (result $bufused $size) + (result $error (expected $size (error $errno))) ) ;;; Remove a directory. @@ -145,7 +142,7 @@ (param $fd $fd) ;;; The path to a directory to remove. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Rename a file or directory. @@ -158,7 +155,7 @@ (param $new_fd $fd) ;;; The destination path to which to rename the file or directory. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Create a symbolic link. @@ -169,7 +166,7 @@ (param $fd $fd) ;;; The destination path at which to create the symbolic link. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Unlink a file. @@ -179,6 +176,6 @@ (param $fd $fd) ;;; The path to a file to unlink. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_poll.witx b/phases/ephemeral/witx/wasi_ephemeral_poll.witx index a30b96a..08da5f0 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_poll.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_poll.witx @@ -21,8 +21,7 @@ (param $out (@witx pointer $event)) ;;; Both the number of subscriptions and events. (param $nsubscriptions $size) - (result $error $errno) ;;; The number of events stored. - (result $nevents $size) + (result $error (expected $size (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_random.witx b/phases/ephemeral/witx/wasi_ephemeral_random.witx index 55c6df0..4dc8e1a 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_random.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_random.witx @@ -21,6 +21,6 @@ ;;; The buffer to fill with random data. (param $buf (@witx pointer u8)) (param $buf_len $size) - (result $error $errno) + (result $error (expected (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_sched.witx b/phases/ephemeral/witx/wasi_ephemeral_sched.witx index 799a75f..3b70856 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_sched.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_sched.witx @@ -11,6 +11,6 @@ ;;; Temporarily yield execution of the calling thread. ;;; Note: This is similar to `yield` in POSIX. (@interface func (export "yield") - (result $error $errno) + (result $error (expected (error $errno))) ) ) diff --git a/phases/ephemeral/witx/wasi_ephemeral_sock.witx b/phases/ephemeral/witx/wasi_ephemeral_sock.witx index becf80f..a05b02e 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_sock.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_sock.witx @@ -20,11 +20,8 @@ (param $ri_data $iovec_array) ;;; Message flags. (param $ri_flags $riflags) - (result $error $errno) - ;;; Number of bytes stored in ri_data. - (result $ro_datalen $size) - ;;; Message flags. - (result $ro_flags $roflags) + ;;; Number of bytes stored in ri_data and message flags. + (result $error (expected (tuple $size $roflags) (error $errno))) ) ;;; Send a message on a socket. @@ -36,9 +33,8 @@ (param $si_data $ciovec_array) ;;; Message flags. (param $si_flags $siflags) - (result $error $errno) ;;; Number of bytes transmitted. - (result $so_datalen $size) + (result $error (expected $size (error $errno))) ) ;;; Shut down socket send and receive channels. @@ -47,6 +43,6 @@ (param $fd $fd) ;;; Which channels on the socket to shut down. (param $how $sdflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ) diff --git a/phases/old/snapshot_0/docs.md b/phases/old/snapshot_0/docs.md index e77002b..707c836 100644 --- a/phases/old/snapshot_0/docs.md +++ b/phases/old/snapshot_0/docs.md @@ -19,7 +19,7 @@ Size: 8 Alignment: 8 -## `clockid`: Variant +## `clockid`: `Variant` Identifiers for clocks. Size: 4 @@ -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`: Variant +## `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 @@ -285,114 +285,172 @@ Cross-device link. - `notcapable` Extension: Capabilities insufficient. -## `rights`: `u64` +## `rights`: `Record` File descriptor rights, determining which actions may be performed. Size: 8 Alignment: 8 -### Constants -- `fd_datasync` +### Record members +- `fd_datasync`: `bool` The right to invoke [`fd_datasync`](#fd_datasync). If [`rights::path_open`](#rights.path_open) is set, includes the right to invoke [`path_open`](#path_open) with [`fdflags::dsync`](#fdflags.dsync). -- `fd_read` +Bit: 0 + +- `fd_read`: `bool` The right to invoke [`fd_read`](#fd_read) and [`sock_recv`](#sock_recv). If [`rights::fd_seek`](#rights.fd_seek) is set, includes the right to invoke [`fd_pread`](#fd_pread). -- `fd_seek` +Bit: 1 + +- `fd_seek`: `bool` The right to invoke [`fd_seek`](#fd_seek). This flag implies [`rights::fd_tell`](#rights.fd_tell). -- `fd_fdstat_set_flags` +Bit: 2 + +- `fd_fdstat_set_flags`: `bool` The right to invoke [`fd_fdstat_set_flags`](#fd_fdstat_set_flags). -- `fd_sync` +Bit: 3 + +- `fd_sync`: `bool` The right to invoke [`fd_sync`](#fd_sync). If [`rights::path_open`](#rights.path_open) is set, includes the right to invoke [`path_open`](#path_open) with [`fdflags::rsync`](#fdflags.rsync) and [`fdflags::dsync`](#fdflags.dsync). -- `fd_tell` +Bit: 4 + +- `fd_tell`: `bool` The right to invoke [`fd_seek`](#fd_seek) in such a way that the file offset remains unaltered (i.e., [`whence::cur`](#whence.cur) with offset zero), or to invoke [`fd_tell`](#fd_tell). -- `fd_write` +Bit: 5 + +- `fd_write`: `bool` The right to invoke [`fd_write`](#fd_write) and [`sock_send`](#sock_send). If [`rights::fd_seek`](#rights.fd_seek) is set, includes the right to invoke [`fd_pwrite`](#fd_pwrite). -- `fd_advise` +Bit: 6 + +- `fd_advise`: `bool` The right to invoke [`fd_advise`](#fd_advise). -- `fd_allocate` +Bit: 7 + +- `fd_allocate`: `bool` The right to invoke [`fd_allocate`](#fd_allocate). -- `path_create_directory` +Bit: 8 + +- `path_create_directory`: `bool` The right to invoke [`path_create_directory`](#path_create_directory). -- `path_create_file` +Bit: 9 + +- `path_create_file`: `bool` If [`rights::path_open`](#rights.path_open) is set, the right to invoke [`path_open`](#path_open) with [`oflags::creat`](#oflags.creat). -- `path_link_source` +Bit: 10 + +- `path_link_source`: `bool` The right to invoke [`path_link`](#path_link) with the file descriptor as the source directory. -- `path_link_target` +Bit: 11 + +- `path_link_target`: `bool` The right to invoke [`path_link`](#path_link) with the file descriptor as the target directory. -- `path_open` +Bit: 12 + +- `path_open`: `bool` The right to invoke [`path_open`](#path_open). -- `fd_readdir` +Bit: 13 + +- `fd_readdir`: `bool` The right to invoke [`fd_readdir`](#fd_readdir). -- `path_readlink` +Bit: 14 + +- `path_readlink`: `bool` The right to invoke [`path_readlink`](#path_readlink). -- `path_rename_source` +Bit: 15 + +- `path_rename_source`: `bool` The right to invoke [`path_rename`](#path_rename) with the file descriptor as the source directory. -- `path_rename_target` +Bit: 16 + +- `path_rename_target`: `bool` The right to invoke [`path_rename`](#path_rename) with the file descriptor as the target directory. -- `path_filestat_get` +Bit: 17 + +- `path_filestat_get`: `bool` The right to invoke [`path_filestat_get`](#path_filestat_get). -- `path_filestat_set_size` +Bit: 18 + +- `path_filestat_set_size`: `bool` The right to change a file's size (there is no `path_filestat_set_size`). If [`rights::path_open`](#rights.path_open) is set, includes the right to invoke [`path_open`](#path_open) with [`oflags::trunc`](#oflags.trunc). -- `path_filestat_set_times` +Bit: 19 + +- `path_filestat_set_times`: `bool` The right to invoke [`path_filestat_set_times`](#path_filestat_set_times). -- `fd_filestat_get` +Bit: 20 + +- `fd_filestat_get`: `bool` The right to invoke [`fd_filestat_get`](#fd_filestat_get). -- `fd_filestat_set_size` +Bit: 21 + +- `fd_filestat_set_size`: `bool` The right to invoke [`fd_filestat_set_size`](#fd_filestat_set_size). -- `fd_filestat_set_times` +Bit: 22 + +- `fd_filestat_set_times`: `bool` The right to invoke [`fd_filestat_set_times`](#fd_filestat_set_times). -- `path_symlink` +Bit: 23 + +- `path_symlink`: `bool` The right to invoke [`path_symlink`](#path_symlink). -- `path_remove_directory` +Bit: 24 + +- `path_remove_directory`: `bool` The right to invoke [`path_remove_directory`](#path_remove_directory). -- `path_unlink_file` +Bit: 25 + +- `path_unlink_file`: `bool` The right to invoke [`path_unlink_file`](#path_unlink_file). -- `poll_fd_readwrite` +Bit: 26 + +- `poll_fd_readwrite`: `bool` If [`rights::fd_read`](#rights.fd_read) is set, includes the right to invoke [`poll_oneoff`](#poll_oneoff) to subscribe to [`eventtype::fd_read`](#eventtype.fd_read). If [`rights::fd_write`](#rights.fd_write) is set, includes the right to invoke [`poll_oneoff`](#poll_oneoff) to subscribe to [`eventtype::fd_write`](#eventtype.fd_write). -- `sock_shutdown` +Bit: 27 + +- `sock_shutdown`: `bool` The right to invoke [`sock_shutdown`](#sock_shutdown). -## `fd` +Bit: 28 + +## `fd`: `Handle` A file descriptor handle. Size: 4 @@ -400,7 +458,7 @@ Size: 4 Alignment: 4 ### Supertypes -## `iovec`: Record +## `iovec`: `Record` A region of memory for scatter/gather reads. Size: 8 @@ -418,7 +476,7 @@ The length of the buffer to be filled. Offset: 4 -## `ciovec`: Record +## `ciovec`: `Record` A region of memory for scatter/gather writes. Size: 8 @@ -455,7 +513,7 @@ Size: 8 Alignment: 8 -## `whence`: Variant +## `whence`: `Variant` The position relative to which to set the offset of the file descriptor. Size: 1 @@ -493,7 +551,7 @@ Size: 8 Alignment: 8 -## `filetype`: Variant +## `filetype`: `Variant` The type of a file descriptor or file. Size: 1 @@ -525,7 +583,7 @@ The file descriptor or file refers to a byte-stream socket. - `symbolic_link` The file refers to a symbolic link inode. -## `dirent`: Record +## `dirent`: `Record` A directory entry. Size: 24 @@ -553,7 +611,7 @@ The type of the file referred to by this directory entry. Offset: 20 -## `advice`: Variant +## `advice`: `Variant` File or memory access pattern advisory information. Size: 1 @@ -579,32 +637,42 @@ 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`: `u16` +## `fdflags`: `Record` File descriptor flags. Size: 2 Alignment: 2 -### Constants -- `append` +### Record members +- `append`: `bool` Append mode: Data written to the file is always appended to the file's end. -- `dsync` +Bit: 0 + +- `dsync`: `bool` Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. -- `nonblock` +Bit: 1 + +- `nonblock`: `bool` Non-blocking mode. -- `rsync` +Bit: 2 + +- `rsync`: `bool` Synchronized read I/O operations. -- `sync` +Bit: 3 + +- `sync`: `bool` 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`: Record +Bit: 4 + +## `fdstat`: `Record` File descriptor attributes. Size: 24 @@ -641,57 +709,75 @@ Size: 8 Alignment: 8 -## `fstflags`: `u16` +## `fstflags`: `Record` Which file time attributes to adjust. Size: 2 Alignment: 2 -### Constants -- `atim` +### Record members +- `atim`: `bool` Adjust the last data access timestamp to the value stored in [`filestat::atim`](#filestat.atim). -- `atim_now` +Bit: 0 + +- `atim_now`: `bool` Adjust the last data access timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -- `mtim` +Bit: 1 + +- `mtim`: `bool` Adjust the last data modification timestamp to the value stored in [`filestat::mtim`](#filestat.mtim). -- `mtim_now` +Bit: 2 + +- `mtim_now`: `bool` Adjust the last data modification timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -## `lookupflags`: `u32` +Bit: 3 + +## `lookupflags`: `Record` Flags determining the method of how paths are resolved. Size: 4 Alignment: 4 -### Constants -- `symlink_follow` +### Record members +- `symlink_follow`: `bool` As long as the resolved path corresponds to a symbolic link, it is expanded. -## `oflags`: `u16` +Bit: 0 + +## `oflags`: `Record` Open flags used by [`path_open`](#path_open). Size: 2 Alignment: 2 -### Constants -- `creat` +### Record members +- `creat`: `bool` Create file if it does not exist. -- `directory` +Bit: 0 + +- `directory`: `bool` Fail if not a directory. -- `excl` +Bit: 1 + +- `excl`: `bool` Fail if file already exists. -- `trunc` +Bit: 2 + +- `trunc`: `bool` Truncate file to size 0. +Bit: 3 + ## `linkcount`: `u32` Number of hard links to an inode. @@ -699,7 +785,7 @@ Size: 4 Alignment: 4 -## `filestat`: Record +## `filestat`: `Record` File attributes. Size: 56 @@ -755,7 +841,7 @@ Size: 8 Alignment: 8 -## `eventtype`: Variant +## `eventtype`: `Variant` Type of a subscription to an event or its occurrence. Size: 1 @@ -775,7 +861,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`: `u16` +## `eventrwflags`: `Record` The state of the file descriptor subscribed to with [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -783,11 +869,13 @@ Size: 2 Alignment: 2 -### Constants -- `fd_readwrite_hangup` +### Record members +- `fd_readwrite_hangup`: `bool` The peer of this socket has closed or disconnected. -## `event_fd_readwrite`: Record +Bit: 0 + +## `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 @@ -806,7 +894,7 @@ The state of the file descriptor. Offset: 8 -## `event`: Record +## `event`: `Record` An event that occurred. Size: 32 @@ -835,7 +923,7 @@ The contents of the event, if it is an [`eventtype::fd_read`](#eventtype.fd_read Offset: 16 -## `subclockflags`: `u16` +## `subclockflags`: `Record` Flags determining how to interpret the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -843,15 +931,17 @@ Size: 2 Alignment: 2 -### Constants -- `subscription_clock_abstime` +### Record members +- `subscription_clock_abstime`: `bool` If set, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) as an absolute timestamp of clock [`subscription_clock::id`](#subscription_clock.id). If clear, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) relative to the current time value of clock [`subscription_clock::id`](#subscription_clock.id). -## `subscription_clock`: Record +Bit: 0 + +## `subscription_clock`: `Record` The contents of a [`subscription`](#subscription) when type is [`eventtype::clock`](#eventtype.clock). Size: 40 @@ -885,7 +975,7 @@ Flags specifying whether the timeout is absolute or relative Offset: 32 -## `subscription_fd_readwrite`: Record +## `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). @@ -899,7 +989,7 @@ The file descriptor on which to wait for it to become ready for reading or writi Offset: 0 -## `subscription_u`: Variant +## `subscription_u`: `Variant` The contents of a [`subscription`](#subscription). Size: 48 @@ -917,7 +1007,7 @@ Alignment: 8 - `fd_write`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) -## `subscription`: Record +## `subscription`: `Record` Subscription to an event. Size: 56 @@ -943,7 +1033,7 @@ Size: 4 Alignment: 4 -## `signal`: Variant +## `signal`: `Variant` Signal condition. Size: 1 @@ -1075,31 +1165,37 @@ Action: Terminates the process. Bad system call. Action: Terminates the process. -## `riflags`: `u16` +## `riflags`: `Record` Flags provided to [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Constants -- `recv_peek` +### Record members +- `recv_peek`: `bool` Returns the message without removing it from the socket's receive queue. -- `recv_waitall` +Bit: 0 + +- `recv_waitall`: `bool` On byte-stream sockets, block until the full amount of data can be returned. -## `roflags`: `u16` +Bit: 1 + +## `roflags`: `Record` Flags returned by [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Constants -- `recv_data_truncated` +### Record members +- `recv_data_truncated`: `bool` Returned by [`sock_recv`](#sock_recv): Message data has been truncated. +Bit: 0 + ## `siflags`: `u16` Flags provided to [`sock_send`](#sock_send). As there are currently no flags defined, it must be set to zero. @@ -1108,21 +1204,25 @@ Size: 2 Alignment: 2 -## `sdflags`: `u8` +## `sdflags`: `Record` Which channels on a socket to shut down. Size: 1 Alignment: 1 -### Constants -- `rd` +### Record members +- `rd`: `bool` Disables further receive operations. -- `wr` +Bit: 0 + +- `wr`: `bool` Disables further send operations. -## `preopentype`: Variant +Bit: 1 + +## `preopentype`: `Variant` Identifiers for preopened capabilities. Size: 1 @@ -1133,7 +1233,7 @@ Alignment: 1 - `dir` A pre-opened directory. -## `prestat_dir`: Record +## `prestat_dir`: `Record` The contents of a $prestat when type is [`preopentype::dir`](#preopentype.dir). Size: 4 @@ -1146,7 +1246,7 @@ The length of the directory name for use with [`fd_prestat_dir_name`](#fd_presta Offset: 0 -## `prestat`: Variant +## `prestat`: `Variant` Information about a pre-opened capability. Size: 8 @@ -1168,7 +1268,7 @@ Alignment: 4 --- -#### `args_get(argv: Pointer>, argv_buf: Pointer) -> errno` +#### `args_get(argv: Pointer>, argv_buf: Pointer) -> Result<(), errno>` Read command-line argument data. The size of the array should match that returned by [`args_sizes_get`](#args_sizes_get) @@ -1178,28 +1278,51 @@ The size of the array should match that returned by [`args_sizes_get`](#args_siz - `argv_buf`: `Pointer` ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `args_sizes_get() -> (errno, size, size)` +#### `args_sizes_get() -> Result<(size, size), errno>` Return command-line argument data sizes. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, size), errno>` +Returns the number of arguments and the size of the argument string +data, or an error. + +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, size)` + +####### Record members +- `0`: [`size`](#size) -- `argc`: [`size`](#size) -The number of arguments. +Offset: 0 + +- `1`: [`size`](#size) + +Offset: 4 -- `argv_buf_size`: [`size`](#size) -The size of the argument string data. +- `err`: [`errno`](#errno) --- -#### `environ_get(environ: Pointer>, environ_buf: Pointer) -> errno` +#### `environ_get(environ: Pointer>, environ_buf: Pointer) -> Result<(), errno>` Read environment variable data. The sizes of the buffers should match that returned by [`environ_sizes_get`](#environ_sizes_get). @@ -1209,28 +1332,51 @@ The sizes of the buffers should match that returned by [`environ_sizes_get`](#en - `environ_buf`: `Pointer` ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `environ_sizes_get() -> (errno, size, size)` +#### `environ_sizes_get() -> Result<(size, size), errno>` Return environment variable data sizes. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, size), errno>` +Returns the number of environment variable arguments and the size of the +environment variable data. + +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, size)` + +####### Record members +- `0`: [`size`](#size) + +Offset: 0 + +- `1`: [`size`](#size) -- `environc`: [`size`](#size) -The number of environment variable arguments. +Offset: 4 -- `environ_buf_size`: [`size`](#size) -The size of the environment variable data. +- `err`: [`errno`](#errno) --- -#### `clock_res_get(id: clockid) -> (errno, timestamp)` +#### `clock_res_get(id: clockid) -> Result` Return the resolution of a clock. Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, return [`errno::inval`](#errno.inval). @@ -1241,15 +1387,22 @@ Note: This is similar to `clock_getres` in POSIX. The clock for which to return the resolution. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result` +The resolution of the clock, or an error if one happened. + +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`timestamp`](#timestamp) -- `resolution`: [`timestamp`](#timestamp) -The resolution of the clock. +- `err`: [`errno`](#errno) --- -#### `clock_time_get(id: clockid, precision: timestamp) -> (errno, timestamp)` +#### `clock_time_get(id: clockid, precision: timestamp) -> Result` Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX. @@ -1261,15 +1414,22 @@ The clock for which to return the time. The maximum lag (exclusive) that the returned time value may have, compared to its actual value. ##### Results -- `error`: [`errno`](#errno) - -- `time`: [`timestamp`](#timestamp) +- `error`: `Result` The time value of the clock. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`timestamp`](#timestamp) + +- `err`: [`errno`](#errno) + --- -#### `fd_advise(fd: fd, offset: filesize, len: filesize, advice: advice) -> errno` +#### `fd_advise(fd: fd, offset: filesize, len: filesize, advice: advice) -> Result<(), errno>` Provide file advisory information on a file descriptor. Note: This is similar to `posix_fadvise` in POSIX. @@ -1286,12 +1446,21 @@ The length of the region to which the advisory applies. The advice. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_allocate(fd: fd, offset: filesize, len: filesize) -> errno` +#### `fd_allocate(fd: fd, offset: filesize, len: filesize) -> Result<(), errno>` Force the allocation of space in a file. Note: This is similar to `posix_fallocate` in POSIX. @@ -1305,12 +1474,21 @@ The offset at which to start the allocation. The length of the area that is allocated. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_close(fd: fd) -> errno` +#### `fd_close(fd: fd) -> Result<(), errno>` Close a file descriptor. Note: This is similar to `close` in POSIX. @@ -1318,12 +1496,21 @@ Note: This is similar to `close` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_datasync(fd: fd) -> errno` +#### `fd_datasync(fd: fd) -> Result<(), errno>` Synchronize the data of a file to disk. Note: This is similar to `fdatasync` in POSIX. @@ -1331,12 +1518,21 @@ Note: This is similar to `fdatasync` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_fdstat_get(fd: fd) -> (errno, fdstat)` +#### `fd_fdstat_get(fd: fd) -> Result` Get the attributes of a file descriptor. Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. @@ -1344,15 +1540,22 @@ Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as ad - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `stat`: [`fdstat`](#fdstat) +- `error`: `Result` The buffer where the file descriptor's attributes are stored. +###### Variant Layout +- size: 32 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`fdstat`](#fdstat) + +- `err`: [`errno`](#errno) + --- -#### `fd_fdstat_set_flags(fd: fd, flags: fdflags) -> errno` +#### `fd_fdstat_set_flags(fd: fd, flags: fdflags) -> Result<(), errno>` Adjust the flags associated with a file descriptor. Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. @@ -1363,12 +1566,21 @@ Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. The desired values of the file descriptor flags. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_fdstat_set_rights(fd: fd, fs_rights_base: rights, fs_rights_inheriting: rights) -> errno` +#### `fd_fdstat_set_rights(fd: fd, fs_rights_base: rights, fs_rights_inheriting: rights) -> Result<(), errno>` Adjust the rights associated with a file descriptor. This can only be used to remove rights, and returns [`errno::notcapable`](#errno.notcapable) if called in a way that would attempt to add rights @@ -1381,27 +1593,43 @@ The desired rights of the file descriptor. - `fs_rights_inheriting`: [`rights`](#rights) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_filestat_get(fd: fd) -> (errno, filestat)` +#### `fd_filestat_get(fd: fd) -> Result` Return the attributes of an open file. ##### Params - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`filestat`](#filestat) +- `error`: `Result` The buffer where the file's attributes are stored. +###### Variant Layout +- size: 64 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filestat`](#filestat) + +- `err`: [`errno`](#errno) + --- -#### `fd_filestat_set_size(fd: fd, size: filesize) -> errno` +#### `fd_filestat_set_size(fd: fd, size: filesize) -> Result<(), errno>` Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. Note: This is similar to `ftruncate` in POSIX. @@ -1412,12 +1640,21 @@ Note: This is similar to `ftruncate` in POSIX. The desired file size. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_filestat_set_times(fd: fd, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> errno` +#### `fd_filestat_set_times(fd: fd, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> Result<(), errno>` Adjust the timestamps of an open file or directory. Note: This is similar to `futimens` in POSIX. @@ -1434,12 +1671,21 @@ The desired values of the data modification timestamp. A bitmask indicating which timestamps to adjust. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_pread(fd: fd, iovs: iovec_array, offset: filesize) -> (errno, size)` +#### `fd_pread(fd: fd, iovs: iovec_array, offset: filesize) -> Result` Read from a file descriptor, without using and updating the file descriptor's offset. Note: This is similar to `preadv` in POSIX. @@ -1453,30 +1699,44 @@ List of scatter/gather vectors in which to store data. The offset within the file at which to read. ##### Results -- `error`: [`errno`](#errno) - -- `nread`: [`size`](#size) +- `error`: `Result` The number of bytes read. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_prestat_get(fd: fd) -> (errno, prestat)` +#### `fd_prestat_get(fd: fd) -> Result` Return a description of the given preopened file descriptor. ##### Params - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`prestat`](#prestat) +- `error`: `Result` The buffer where the description is stored. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`prestat`](#prestat) + +- `err`: [`errno`](#errno) + --- -#### `fd_prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> errno` +#### `fd_prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> Result<(), errno>` Return a description of the given preopened file descriptor. ##### Params @@ -1488,12 +1748,21 @@ A buffer into which to write the preopened directory name. - `path_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_pwrite(fd: fd, iovs: ciovec_array, offset: filesize) -> (errno, size)` +#### `fd_pwrite(fd: fd, iovs: ciovec_array, offset: filesize) -> Result` Write to a file descriptor, without using and updating the file descriptor's offset. Note: This is similar to `pwritev` in POSIX. @@ -1507,15 +1776,22 @@ List of scatter/gather vectors from which to retrieve data. The offset within the file at which to write. ##### Results -- `error`: [`errno`](#errno) - -- `nwritten`: [`size`](#size) +- `error`: `Result` The number of bytes written. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_read(fd: fd, iovs: iovec_array) -> (errno, size)` +#### `fd_read(fd: fd, iovs: iovec_array) -> Result` Read from a file descriptor. Note: This is similar to `readv` in POSIX. @@ -1526,15 +1802,22 @@ Note: This is similar to `readv` in POSIX. List of scatter/gather vectors to which to store data. ##### Results -- `error`: [`errno`](#errno) - -- `nread`: [`size`](#size) +- `error`: `Result` The number of bytes read. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_readdir(fd: fd, buf: Pointer, buf_len: size, cookie: dircookie) -> (errno, size)` +#### `fd_readdir(fd: fd, buf: Pointer, buf_len: size, cookie: dircookie) -> Result` Read directory entries from a directory. When successful, the contents of the output buffer consist of a sequence of directory entries. Each directory entry consists of a [`dirent`](#dirent) object, @@ -1557,15 +1840,22 @@ The buffer where directory entries are stored The location within the directory to start reading ##### Results -- `error`: [`errno`](#errno) - -- `bufused`: [`size`](#size) +- `error`: `Result` The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_renumber(fd: fd, to: fd) -> errno` +#### `fd_renumber(fd: fd, to: fd) -> Result<(), errno>` Atomically replace a file descriptor by renumbering another file descriptor. Due to the strong focus on thread safety, this environment does not provide a mechanism to duplicate or renumber a file descriptor to an arbitrary @@ -1582,12 +1872,21 @@ would disappear if `dup2()` were to be removed entirely. The file descriptor to overwrite. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_seek(fd: fd, offset: filedelta, whence: whence) -> (errno, filesize)` +#### `fd_seek(fd: fd, offset: filedelta, whence: whence) -> Result` Move the offset of a file descriptor. Note: This is similar to `lseek` in POSIX. @@ -1601,15 +1900,22 @@ The number of bytes to move. The base from which the offset is relative. ##### Results -- `error`: [`errno`](#errno) - -- `newoffset`: [`filesize`](#filesize) +- `error`: `Result` The new offset of the file descriptor, relative to the start of the file. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filesize`](#filesize) + +- `err`: [`errno`](#errno) + --- -#### `fd_sync(fd: fd) -> errno` +#### `fd_sync(fd: fd) -> Result<(), errno>` Synchronize the data and metadata of a file to disk. Note: This is similar to `fsync` in POSIX. @@ -1617,12 +1923,21 @@ Note: This is similar to `fsync` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_tell(fd: fd) -> (errno, filesize)` +#### `fd_tell(fd: fd) -> Result` Return the current offset of a file descriptor. Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. @@ -1630,15 +1945,22 @@ Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `offset`: [`filesize`](#filesize) +- `error`: `Result` The current offset of the file descriptor, relative to the start of the file. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filesize`](#filesize) + +- `err`: [`errno`](#errno) + --- -#### `fd_write(fd: fd, iovs: ciovec_array) -> (errno, size)` +#### `fd_write(fd: fd, iovs: ciovec_array) -> Result` Write to a file descriptor. Note: This is similar to `writev` in POSIX. @@ -1649,15 +1971,21 @@ Note: This is similar to `writev` in POSIX. List of scatter/gather vectors from which to retrieve data. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result` -- `nwritten`: [`size`](#size) -The number of bytes written. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) --- -#### `path_create_directory(fd: fd, path: string) -> errno` +#### `path_create_directory(fd: fd, path: string) -> Result<(), errno>` Create a directory. Note: This is similar to `mkdirat` in POSIX. @@ -1668,12 +1996,21 @@ Note: This is similar to `mkdirat` in POSIX. The path at which to create the directory. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_filestat_get(fd: fd, flags: lookupflags, path: string) -> (errno, filestat)` +#### `path_filestat_get(fd: fd, flags: lookupflags, path: string) -> Result` Return the attributes of a file or directory. Note: This is similar to `stat` in POSIX. @@ -1687,15 +2024,22 @@ Flags determining the method of how the path is resolved. The path of the file or directory to inspect. ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`filestat`](#filestat) +- `error`: `Result` The buffer where the file's attributes are stored. +###### Variant Layout +- size: 64 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filestat`](#filestat) + +- `err`: [`errno`](#errno) + --- -#### `path_filestat_set_times(fd: fd, flags: lookupflags, path: string, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> errno` +#### `path_filestat_set_times(fd: fd, flags: lookupflags, path: string, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> Result<(), errno>` Adjust the timestamps of a file or directory. Note: This is similar to `utimensat` in POSIX. @@ -1718,12 +2062,21 @@ The desired values of the data modification timestamp. A bitmask indicating which timestamps to adjust. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_link(old_fd: fd, old_flags: lookupflags, old_path: string, new_fd: fd, new_path: string) -> errno` +#### `path_link(old_fd: fd, old_flags: lookupflags, old_path: string, new_fd: fd, new_path: string) -> Result<(), errno>` Create a hard link. Note: This is similar to `linkat` in POSIX. @@ -1743,12 +2096,21 @@ The working directory at which the resolution of the new path starts. The destination path at which to create the hard link. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_open(fd: fd, dirflags: lookupflags, path: string, oflags: oflags, fs_rights_base: rights, fs_rights_inheriting: rights, fdflags: fdflags) -> (errno, fd)` +#### `path_open(fd: fd, dirflags: lookupflags, path: string, oflags: oflags, fs_rights_base: rights, fs_rights_inheriting: rights, fdflags: fdflags) -> Result` Open a file or directory. The returned file descriptor is not guaranteed to be the lowest-numbered file descriptor not currently open; it is randomized to prevent @@ -1765,7 +2127,7 @@ Flags determining the method of how the path is resolved. - `path`: `string` The relative path of the file or directory to open, relative to the -[`fd`](#fd) directory. +[`path_open::fd`](#path_open.fd) directory. - `oflags`: [`oflags`](#oflags) The method by which to open the file. @@ -1784,15 +2146,22 @@ file descriptors derived from it. - `fdflags`: [`fdflags`](#fdflags) ##### Results -- `error`: [`errno`](#errno) - -- `opened_fd`: [`fd`](#fd) +- `error`: `Result` The file descriptor of the file that has been opened. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`fd`](#fd) + +- `err`: [`errno`](#errno) + --- -#### `path_readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> (errno, size)` +#### `path_readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> Result` Read the contents of a symbolic link. Note: This is similar to `readlinkat` in POSIX. @@ -1808,15 +2177,22 @@ The buffer to which to write the contents of the symbolic link. - `buf_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) - -- `bufused`: [`size`](#size) +- `error`: `Result` The number of bytes placed in the buffer. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `path_remove_directory(fd: fd, path: string) -> errno` +#### `path_remove_directory(fd: fd, path: string) -> Result<(), errno>` Remove a directory. Return [`errno::notempty`](#errno.notempty) if the directory is not empty. Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. @@ -1828,12 +2204,21 @@ Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. The path to a directory to remove. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_rename(fd: fd, old_path: string, new_fd: fd, new_path: string) -> errno` +#### `path_rename(fd: fd, old_path: string, new_fd: fd, new_path: string) -> Result<(), errno>` Rename a file or directory. Note: This is similar to `renameat` in POSIX. @@ -1850,12 +2235,21 @@ The working directory at which the resolution of the new path starts. The destination path to which to rename the file or directory. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_symlink(old_path: string, fd: fd, new_path: string) -> errno` +#### `path_symlink(old_path: string, fd: fd, new_path: string) -> Result<(), errno>` Create a symbolic link. Note: This is similar to `symlinkat` in POSIX. @@ -1869,12 +2263,21 @@ The contents of the symbolic link. The destination path at which to create the symbolic link. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_unlink_file(fd: fd, path: string) -> errno` +#### `path_unlink_file(fd: fd, path: string) -> Result<(), errno>` Unlink a file. Return [`errno::isdir`](#errno.isdir) if the path refers to a directory. Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. @@ -1886,12 +2289,21 @@ Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. The path to a file to unlink. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `poll_oneoff(in: ConstPointer, out: Pointer, nsubscriptions: size) -> (errno, size)` +#### `poll_oneoff(in: ConstPointer, out: Pointer, nsubscriptions: size) -> Result` Concurrently poll for the occurrence of a set of events. ##### Params @@ -1905,11 +2317,18 @@ The events that have occurred. Both the number of subscriptions and events. ##### Results -- `error`: [`errno`](#errno) - -- `nevents`: [`size`](#size) +- `error`: `Result` The number of events stored. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- @@ -1926,7 +2345,7 @@ The exit code returned by the process. --- -#### `proc_raise(sig: signal) -> errno` +#### `proc_raise(sig: signal) -> Result<(), errno>` Send a signal to the process of the calling thread. Note: This is similar to `raise` in POSIX. @@ -1935,23 +2354,41 @@ Note: This is similar to `raise` in POSIX. The signal condition to trigger. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `sched_yield() -> errno` +#### `sched_yield() -> Result<(), errno>` Temporarily yield execution of the calling thread. Note: This is similar to [`sched_yield`](#sched_yield) in POSIX. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `random_get(buf: Pointer, buf_len: size) -> errno` +#### `random_get(buf: Pointer, buf_len: size) -> Result<(), errno>` Write high-quality random data into a buffer. This function blocks when the implementation is unable to immediately provide sufficient high-quality random data. @@ -1966,12 +2403,21 @@ The buffer to fill with random data. - `buf_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `sock_recv(fd: fd, ri_data: iovec_array, ri_flags: riflags) -> (errno, size, roflags)` +#### `sock_recv(fd: fd, ri_data: iovec_array, ri_flags: riflags) -> Result<(size, roflags), errno>` Receive a message from a socket. Note: This is similar to `recv` in POSIX, though it also supports reading the data into multiple buffers in the manner of `readv`. @@ -1986,18 +2432,31 @@ List of scatter/gather vectors to which to store data. Message flags. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, roflags), errno>` +Number of bytes stored in ri_data and message flags. -- `ro_datalen`: [`size`](#size) -Number of bytes stored in ri_data. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, roflags)` -- `ro_flags`: [`roflags`](#roflags) -Message flags. +####### Record members +- `0`: [`size`](#size) + +Offset: 0 + +- `1`: [`roflags`](#roflags) + +Offset: 4 + +- `err`: [`errno`](#errno) --- -#### `sock_send(fd: fd, si_data: ciovec_array, si_flags: siflags) -> (errno, size)` +#### `sock_send(fd: fd, si_data: ciovec_array, si_flags: siflags) -> Result` Send a message on a socket. Note: This is similar to `send` in POSIX, though it also supports writing the data from multiple buffers in the manner of `writev`. @@ -2012,15 +2471,22 @@ List of scatter/gather vectors to which to retrieve data Message flags. ##### Results -- `error`: [`errno`](#errno) - -- `so_datalen`: [`size`](#size) +- `error`: `Result` Number of bytes transmitted. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `sock_shutdown(fd: fd, how: sdflags) -> errno` +#### `sock_shutdown(fd: fd, how: sdflags) -> Result<(), errno>` Shut down socket send and receive channels. Note: This is similar to `shutdown` in POSIX. @@ -2031,5 +2497,14 @@ Note: This is similar to `shutdown` in POSIX. Which channels on the socket to shut down. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) diff --git a/phases/old/snapshot_0/witx/typenames.witx b/phases/old/snapshot_0/witx/typenames.witx index eb6f700..f4ba788 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 (@witx bitflags u64) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u32) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u8) + (flags (@witx repr u8) ;;; Disables further receive operations. $rd ;;; Disables further send operations. diff --git a/phases/old/snapshot_0/witx/wasi_unstable.witx b/phases/old/snapshot_0/witx/wasi_unstable.witx index 0d16f1b..5481882 100644 --- a/phases/old/snapshot_0/witx/wasi_unstable.witx +++ b/phases/old/snapshot_0/witx/wasi_unstable.witx @@ -20,15 +20,13 @@ (@interface func (export "args_get") (param $argv (@witx pointer (@witx pointer u8))) (param $argv_buf (@witx pointer u8)) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return command-line argument data sizes. (@interface func (export "args_sizes_get") - (result $error $errno) - ;;; The number of arguments. - (result $argc $size) - ;;; The size of the argument string data. - (result $argv_buf_size $size) + ;;; Returns the number of arguments and the size of the argument string + ;;; data, or an error. + (result $error (expected (tuple $size $size) (error $errno))) ) ;;; Read environment variable data. @@ -36,15 +34,13 @@ (@interface func (export "environ_get") (param $environ (@witx pointer (@witx pointer u8))) (param $environ_buf (@witx pointer u8)) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return environment variable data sizes. (@interface func (export "environ_sizes_get") - (result $error $errno) - ;;; The number of environment variable arguments. - (result $environc $size) - ;;; The size of the environment variable data. - (result $environ_buf_size $size) + ;;; Returns the number of environment variable arguments and the size of the + ;;; environment variable data. + (result $error (expected (tuple $size $size) (error $errno))) ) ;;; Return the resolution of a clock. @@ -54,9 +50,8 @@ (@interface func (export "clock_res_get") ;;; The clock for which to return the resolution. (param $id $clockid) - (result $error $errno) - ;;; The resolution of the clock. - (result $resolution $timestamp) + ;;; The resolution of the clock, or an error if one happened. + (result $error (expected $timestamp (error $errno))) ) ;;; Return the time value of a clock. ;;; Note: This is similar to `clock_gettime` in POSIX. @@ -65,9 +60,8 @@ (param $id $clockid) ;;; The maximum lag (exclusive) that the returned time value may have, compared to its actual value. (param $precision $timestamp) - (result $error $errno) ;;; The time value of the clock. - (result $time $timestamp) + (result $error (expected $timestamp (error $errno))) ) ;;; Provide file advisory information on a file descriptor. @@ -80,7 +74,7 @@ (param $len $filesize) ;;; The advice. (param $advice $advice) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Force the allocation of space in a file. @@ -91,30 +85,29 @@ (param $offset $filesize) ;;; The length of the area that is allocated. (param $len $filesize) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Close a file descriptor. ;;; Note: This is similar to `close` in POSIX. (@interface func (export "fd_close") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Synchronize the data of a file to disk. ;;; Note: This is similar to `fdatasync` in POSIX. (@interface func (export "fd_datasync") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Get the attributes of a file descriptor. ;;; Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. (@interface func (export "fd_fdstat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the file descriptor's attributes are stored. - (result $stat $fdstat) + (result $error (expected $fdstat (error $errno))) ) ;;; Adjust the flags associated with a file descriptor. @@ -123,7 +116,7 @@ (param $fd $fd) ;;; The desired values of the file descriptor flags. (param $flags $fdflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Adjust the rights associated with a file descriptor. @@ -133,15 +126,14 @@ ;;; The desired rights of the file descriptor. (param $fs_rights_base $rights) (param $fs_rights_inheriting $rights) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the attributes of an open file. (@interface func (export "fd_filestat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the file's attributes are stored. - (result $buf $filestat) + (result $error (expected $filestat (error $errno))) ) ;;; Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. @@ -150,7 +142,7 @@ (param $fd $fd) ;;; The desired file size. (param $size $filesize) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Adjust the timestamps of an open file or directory. @@ -163,7 +155,7 @@ (param $mtim $timestamp) ;;; A bitmask indicating which timestamps to adjust. (param $fst_flags $fstflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Read from a file descriptor, without using and updating the file descriptor's offset. @@ -174,17 +166,15 @@ (param $iovs $iovec_array) ;;; The offset within the file at which to read. (param $offset $filesize) - (result $error $errno) ;;; The number of bytes read. - (result $nread $size) + (result $error (expected $size (error $errno))) ) ;;; Return a description of the given preopened file descriptor. (@interface func (export "fd_prestat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the description is stored. - (result $buf $prestat) + (result $error (expected $prestat (error $errno))) ) ;;; Return a description of the given preopened file descriptor. @@ -193,7 +183,7 @@ ;;; A buffer into which to write the preopened directory name. (param $path (@witx pointer u8)) (param $path_len $size) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Write to a file descriptor, without using and updating the file descriptor's offset. @@ -204,9 +194,8 @@ (param $iovs $ciovec_array) ;;; The offset within the file at which to write. (param $offset $filesize) - (result $error $errno) ;;; The number of bytes written. - (result $nwritten $size) + (result $error (expected $size (error $errno))) ) ;;; Read from a file descriptor. @@ -215,9 +204,8 @@ (param $fd $fd) ;;; List of scatter/gather vectors to which to store data. (param $iovs $iovec_array) - (result $error $errno) ;;; The number of bytes read. - (result $nread $size) + (result $error (expected $size (error $errno))) ) ;;; Read directory entries from a directory. @@ -237,9 +225,8 @@ (param $buf_len $size) ;;; The location within the directory to start reading (param $cookie $dircookie) - (result $error $errno) ;;; The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. - (result $bufused $size) + (result $error (expected $size (error $errno))) ) ;;; Atomically replace a file descriptor by renumbering another file descriptor. @@ -256,7 +243,7 @@ (param $fd $fd) ;;; The file descriptor to overwrite. (param $to $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Move the offset of a file descriptor. @@ -267,25 +254,23 @@ (param $offset $filedelta) ;;; The base from which the offset is relative. (param $whence $whence) - (result $error $errno) ;;; The new offset of the file descriptor, relative to the start of the file. - (result $newoffset $filesize) + (result $error (expected $filesize (error $errno))) ) ;;; Synchronize the data and metadata of a file to disk. ;;; Note: This is similar to `fsync` in POSIX. (@interface func (export "fd_sync") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the current offset of a file descriptor. ;;; Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. (@interface func (export "fd_tell") (param $fd $fd) - (result $error $errno) ;;; The current offset of the file descriptor, relative to the start of the file. - (result $offset $filesize) + (result $error (expected $filesize (error $errno))) ) ;;; Write to a file descriptor. @@ -294,9 +279,7 @@ (param $fd $fd) ;;; List of scatter/gather vectors from which to retrieve data. (param $iovs $ciovec_array) - (result $error $errno) - ;;; The number of bytes written. - (result $nwritten $size) + (result $error (expected $size (error $errno))) ) ;;; Create a directory. @@ -305,7 +288,7 @@ (param $fd $fd) ;;; The path at which to create the directory. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the attributes of a file or directory. @@ -316,9 +299,8 @@ (param $flags $lookupflags) ;;; The path of the file or directory to inspect. (param $path string) - (result $error $errno) ;;; The buffer where the file's attributes are stored. - (result $buf $filestat) + (result $error (expected $filestat (error $errno))) ) ;;; Adjust the timestamps of a file or directory. @@ -335,7 +317,7 @@ (param $mtim $timestamp) ;;; A bitmask indicating which timestamps to adjust. (param $fst_flags $fstflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Create a hard link. @@ -350,7 +332,7 @@ (param $new_fd $fd) ;;; The destination path at which to create the hard link. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Open a file or directory. @@ -367,7 +349,7 @@ ;;; Flags determining the method of how the path is resolved. (param $dirflags $lookupflags) ;;; The relative path of the file or directory to open, relative to the - ;;; `fd` directory. + ;;; `path_open::fd` directory. (param $path string) ;;; The method by which to open the file. (param $oflags $oflags) @@ -382,9 +364,8 @@ (param $fs_rights_base $rights) (param $fs_rights_inheriting $rights) (param $fdflags $fdflags) - (result $error $errno) ;;; The file descriptor of the file that has been opened. - (result $opened_fd $fd) + (result $error (expected $fd (error $errno))) ) ;;; Read the contents of a symbolic link. @@ -396,9 +377,8 @@ ;;; The buffer to which to write the contents of the symbolic link. (param $buf (@witx pointer u8)) (param $buf_len $size) - (result $error $errno) ;;; The number of bytes placed in the buffer. - (result $bufused $size) + (result $error (expected $size (error $errno))) ) ;;; Remove a directory. @@ -408,7 +388,7 @@ (param $fd $fd) ;;; The path to a directory to remove. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Rename a file or directory. @@ -421,7 +401,7 @@ (param $new_fd $fd) ;;; The destination path to which to rename the file or directory. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Create a symbolic link. @@ -432,7 +412,7 @@ (param $fd $fd) ;;; The destination path at which to create the symbolic link. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) @@ -443,7 +423,7 @@ (param $fd $fd) ;;; The path to a file to unlink. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Concurrently poll for the occurrence of a set of events. @@ -454,9 +434,8 @@ (param $out (@witx pointer $event)) ;;; Both the number of subscriptions and events. (param $nsubscriptions $size) - (result $error $errno) ;;; The number of events stored. - (result $nevents $size) + (result $error (expected $size (error $errno))) ) ;;; Terminate the process normally. An exit code of 0 indicates successful @@ -473,13 +452,13 @@ (@interface func (export "proc_raise") ;;; The signal condition to trigger. (param $sig $signal) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Temporarily yield execution of the calling thread. ;;; Note: This is similar to `sched_yield` in POSIX. (@interface func (export "sched_yield") - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Write high-quality random data into a buffer. @@ -492,7 +471,7 @@ ;;; The buffer to fill with random data. (param $buf (@witx pointer u8)) (param $buf_len $size) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Receive a message from a socket. @@ -504,11 +483,8 @@ (param $ri_data $iovec_array) ;;; Message flags. (param $ri_flags $riflags) - (result $error $errno) - ;;; Number of bytes stored in ri_data. - (result $ro_datalen $size) - ;;; Message flags. - (result $ro_flags $roflags) + ;;; Number of bytes stored in ri_data and message flags. + (result $error (expected (tuple $size $roflags) (error $errno))) ) ;;; Send a message on a socket. @@ -520,9 +496,8 @@ (param $si_data $ciovec_array) ;;; Message flags. (param $si_flags $siflags) - (result $error $errno) ;;; Number of bytes transmitted. - (result $so_datalen $size) + (result $error (expected $size (error $errno))) ) ;;; Shut down socket send and receive channels. @@ -531,6 +506,6 @@ (param $fd $fd) ;;; Which channels on the socket to shut down. (param $how $sdflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ) diff --git a/phases/snapshot/docs.md b/phases/snapshot/docs.md index 1bbbe59..c55a661 100644 --- a/phases/snapshot/docs.md +++ b/phases/snapshot/docs.md @@ -19,7 +19,7 @@ Size: 8 Alignment: 8 -## `clockid`: Variant +## `clockid`: `Variant` Identifiers for clocks. Size: 4 @@ -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`: Variant +## `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 @@ -285,114 +285,172 @@ Cross-device link. - `notcapable` Extension: Capabilities insufficient. -## `rights`: `u64` +## `rights`: `Record` File descriptor rights, determining which actions may be performed. Size: 8 Alignment: 8 -### Constants -- `fd_datasync` +### Record members +- `fd_datasync`: `bool` The right to invoke [`fd_datasync`](#fd_datasync). If [`path_open`](#path_open) is set, includes the right to invoke [`path_open`](#path_open) with [`fdflags::dsync`](#fdflags.dsync). -- `fd_read` +Bit: 0 + +- `fd_read`: `bool` The right to invoke [`fd_read`](#fd_read) and [`sock_recv`](#sock_recv). If [`rights::fd_seek`](#rights.fd_seek) is set, includes the right to invoke [`fd_pread`](#fd_pread). -- `fd_seek` +Bit: 1 + +- `fd_seek`: `bool` The right to invoke [`fd_seek`](#fd_seek). This flag implies [`rights::fd_tell`](#rights.fd_tell). -- `fd_fdstat_set_flags` +Bit: 2 + +- `fd_fdstat_set_flags`: `bool` The right to invoke [`fd_fdstat_set_flags`](#fd_fdstat_set_flags). -- `fd_sync` +Bit: 3 + +- `fd_sync`: `bool` The right to invoke [`fd_sync`](#fd_sync). If [`path_open`](#path_open) is set, includes the right to invoke [`path_open`](#path_open) with [`fdflags::rsync`](#fdflags.rsync) and [`fdflags::dsync`](#fdflags.dsync). -- `fd_tell` +Bit: 4 + +- `fd_tell`: `bool` The right to invoke [`fd_seek`](#fd_seek) in such a way that the file offset remains unaltered (i.e., [`whence::cur`](#whence.cur) with offset zero), or to invoke [`fd_tell`](#fd_tell). -- `fd_write` +Bit: 5 + +- `fd_write`: `bool` The right to invoke [`fd_write`](#fd_write) and [`sock_send`](#sock_send). If [`rights::fd_seek`](#rights.fd_seek) is set, includes the right to invoke [`fd_pwrite`](#fd_pwrite). -- `fd_advise` +Bit: 6 + +- `fd_advise`: `bool` The right to invoke [`fd_advise`](#fd_advise). -- `fd_allocate` +Bit: 7 + +- `fd_allocate`: `bool` The right to invoke [`fd_allocate`](#fd_allocate). -- `path_create_directory` +Bit: 8 + +- `path_create_directory`: `bool` The right to invoke [`path_create_directory`](#path_create_directory). -- `path_create_file` +Bit: 9 + +- `path_create_file`: `bool` If [`path_open`](#path_open) is set, the right to invoke [`path_open`](#path_open) with [`oflags::creat`](#oflags.creat). -- `path_link_source` +Bit: 10 + +- `path_link_source`: `bool` The right to invoke [`path_link`](#path_link) with the file descriptor as the source directory. -- `path_link_target` +Bit: 11 + +- `path_link_target`: `bool` The right to invoke [`path_link`](#path_link) with the file descriptor as the target directory. -- `path_open` +Bit: 12 + +- `path_open`: `bool` The right to invoke [`path_open`](#path_open). -- `fd_readdir` +Bit: 13 + +- `fd_readdir`: `bool` The right to invoke [`fd_readdir`](#fd_readdir). -- `path_readlink` +Bit: 14 + +- `path_readlink`: `bool` The right to invoke [`path_readlink`](#path_readlink). -- `path_rename_source` +Bit: 15 + +- `path_rename_source`: `bool` The right to invoke [`path_rename`](#path_rename) with the file descriptor as the source directory. -- `path_rename_target` +Bit: 16 + +- `path_rename_target`: `bool` The right to invoke [`path_rename`](#path_rename) with the file descriptor as the target directory. -- `path_filestat_get` +Bit: 17 + +- `path_filestat_get`: `bool` The right to invoke [`path_filestat_get`](#path_filestat_get). -- `path_filestat_set_size` +Bit: 18 + +- `path_filestat_set_size`: `bool` The right to change a file's size (there is no `path_filestat_set_size`). If [`path_open`](#path_open) is set, includes the right to invoke [`path_open`](#path_open) with [`oflags::trunc`](#oflags.trunc). -- `path_filestat_set_times` +Bit: 19 + +- `path_filestat_set_times`: `bool` The right to invoke [`path_filestat_set_times`](#path_filestat_set_times). -- `fd_filestat_get` +Bit: 20 + +- `fd_filestat_get`: `bool` The right to invoke [`fd_filestat_get`](#fd_filestat_get). -- `fd_filestat_set_size` +Bit: 21 + +- `fd_filestat_set_size`: `bool` The right to invoke [`fd_filestat_set_size`](#fd_filestat_set_size). -- `fd_filestat_set_times` +Bit: 22 + +- `fd_filestat_set_times`: `bool` The right to invoke [`fd_filestat_set_times`](#fd_filestat_set_times). -- `path_symlink` +Bit: 23 + +- `path_symlink`: `bool` The right to invoke [`path_symlink`](#path_symlink). -- `path_remove_directory` +Bit: 24 + +- `path_remove_directory`: `bool` The right to invoke [`path_remove_directory`](#path_remove_directory). -- `path_unlink_file` +Bit: 25 + +- `path_unlink_file`: `bool` The right to invoke [`path_unlink_file`](#path_unlink_file). -- `poll_fd_readwrite` +Bit: 26 + +- `poll_fd_readwrite`: `bool` If [`rights::fd_read`](#rights.fd_read) is set, includes the right to invoke [`poll_oneoff`](#poll_oneoff) to subscribe to [`eventtype::fd_read`](#eventtype.fd_read). If [`rights::fd_write`](#rights.fd_write) is set, includes the right to invoke [`poll_oneoff`](#poll_oneoff) to subscribe to [`eventtype::fd_write`](#eventtype.fd_write). -- `sock_shutdown` +Bit: 27 + +- `sock_shutdown`: `bool` The right to invoke [`sock_shutdown`](#sock_shutdown). -## `fd` +Bit: 28 + +## `fd`: `Handle` A file descriptor handle. Size: 4 @@ -400,7 +458,7 @@ Size: 4 Alignment: 4 ### Supertypes -## `iovec`: Record +## `iovec`: `Record` A region of memory for scatter/gather reads. Size: 8 @@ -418,7 +476,7 @@ The length of the buffer to be filled. Offset: 4 -## `ciovec`: Record +## `ciovec`: `Record` A region of memory for scatter/gather writes. Size: 8 @@ -455,7 +513,7 @@ Size: 8 Alignment: 8 -## `whence`: Variant +## `whence`: `Variant` The position relative to which to set the offset of the file descriptor. Size: 1 @@ -495,7 +553,7 @@ Size: 8 Alignment: 8 -## `filetype`: Variant +## `filetype`: `Variant` The type of a file descriptor or file. Size: 1 @@ -527,7 +585,7 @@ The file descriptor or file refers to a byte-stream socket. - `symbolic_link` The file refers to a symbolic link inode. -## `dirent`: Record +## `dirent`: `Record` A directory entry. Size: 24 @@ -555,7 +613,7 @@ The type of the file referred to by this directory entry. Offset: 20 -## `advice`: Variant +## `advice`: `Variant` File or memory access pattern advisory information. Size: 1 @@ -581,32 +639,42 @@ 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`: `u16` +## `fdflags`: `Record` File descriptor flags. Size: 2 Alignment: 2 -### Constants -- `append` +### Record members +- `append`: `bool` Append mode: Data written to the file is always appended to the file's end. -- `dsync` +Bit: 0 + +- `dsync`: `bool` Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. -- `nonblock` +Bit: 1 + +- `nonblock`: `bool` Non-blocking mode. -- `rsync` +Bit: 2 + +- `rsync`: `bool` Synchronized read I/O operations. -- `sync` +Bit: 3 + +- `sync`: `bool` 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`: Record +Bit: 4 + +## `fdstat`: `Record` File descriptor attributes. Size: 24 @@ -643,57 +711,75 @@ Size: 8 Alignment: 8 -## `fstflags`: `u16` +## `fstflags`: `Record` Which file time attributes to adjust. Size: 2 Alignment: 2 -### Constants -- `atim` +### Record members +- `atim`: `bool` Adjust the last data access timestamp to the value stored in [`filestat::atim`](#filestat.atim). -- `atim_now` +Bit: 0 + +- `atim_now`: `bool` Adjust the last data access timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -- `mtim` +Bit: 1 + +- `mtim`: `bool` Adjust the last data modification timestamp to the value stored in [`filestat::mtim`](#filestat.mtim). -- `mtim_now` +Bit: 2 + +- `mtim_now`: `bool` Adjust the last data modification timestamp to the time of clock [`clockid::realtime`](#clockid.realtime). -## `lookupflags`: `u32` +Bit: 3 + +## `lookupflags`: `Record` Flags determining the method of how paths are resolved. Size: 4 Alignment: 4 -### Constants -- `symlink_follow` +### Record members +- `symlink_follow`: `bool` As long as the resolved path corresponds to a symbolic link, it is expanded. -## `oflags`: `u16` +Bit: 0 + +## `oflags`: `Record` Open flags used by [`path_open`](#path_open). Size: 2 Alignment: 2 -### Constants -- `creat` +### Record members +- `creat`: `bool` Create file if it does not exist. -- `directory` +Bit: 0 + +- `directory`: `bool` Fail if not a directory. -- `excl` +Bit: 1 + +- `excl`: `bool` Fail if file already exists. -- `trunc` +Bit: 2 + +- `trunc`: `bool` Truncate file to size 0. +Bit: 3 + ## `linkcount`: `u64` Number of hard links to an inode. @@ -701,7 +787,7 @@ Size: 8 Alignment: 8 -## `filestat`: Record +## `filestat`: `Record` File attributes. Size: 64 @@ -757,7 +843,7 @@ Size: 8 Alignment: 8 -## `eventtype`: Variant +## `eventtype`: `Variant` Type of a subscription to an event or its occurrence. Size: 1 @@ -777,7 +863,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`: `u16` +## `eventrwflags`: `Record` The state of the file descriptor subscribed to with [`eventtype::fd_read`](#eventtype.fd_read) or [`eventtype::fd_write`](#eventtype.fd_write). @@ -785,11 +871,13 @@ Size: 2 Alignment: 2 -### Constants -- `fd_readwrite_hangup` +### Record members +- `fd_readwrite_hangup`: `bool` The peer of this socket has closed or disconnected. -## `event_fd_readwrite`: Record +Bit: 0 + +## `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). @@ -808,7 +896,7 @@ The state of the file descriptor. Offset: 8 -## `event`: Record +## `event`: `Record` An event that occurred. Size: 32 @@ -837,7 +925,7 @@ The contents of the event, if it is an [`eventtype::fd_read`](#eventtype.fd_read Offset: 16 -## `subclockflags`: `u16` +## `subclockflags`: `Record` Flags determining how to interpret the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout). @@ -845,15 +933,17 @@ Size: 2 Alignment: 2 -### Constants -- `subscription_clock_abstime` +### Record members +- `subscription_clock_abstime`: `bool` If set, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) as an absolute timestamp of clock [`subscription_clock::id`](#subscription_clock.id). If clear, treat the timestamp provided in [`subscription_clock::timeout`](#subscription_clock.timeout) relative to the current time value of clock [`subscription_clock::id`](#subscription_clock.id). -## `subscription_clock`: Record +Bit: 0 + +## `subscription_clock`: `Record` The contents of a [`subscription`](#subscription) when type is [`eventtype::clock`](#eventtype.clock). Size: 32 @@ -882,7 +972,7 @@ Flags specifying whether the timeout is absolute or relative Offset: 24 -## `subscription_fd_readwrite`: Record +## `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). @@ -896,7 +986,7 @@ The file descriptor on which to wait for it to become ready for reading or writi Offset: 0 -## `subscription_u`: Variant +## `subscription_u`: `Variant` The contents of a [`subscription`](#subscription). Size: 40 @@ -914,7 +1004,7 @@ Alignment: 8 - `fd_write`: [`subscription_fd_readwrite`](#subscription_fd_readwrite) -## `subscription`: Record +## `subscription`: `Record` Subscription to an event. Size: 48 @@ -940,7 +1030,7 @@ Size: 4 Alignment: 4 -## `signal`: Variant +## `signal`: `Variant` Signal condition. Size: 1 @@ -1072,31 +1162,37 @@ Action: Terminates the process. Bad system call. Action: Terminates the process. -## `riflags`: `u16` +## `riflags`: `Record` Flags provided to [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Constants -- `recv_peek` +### Record members +- `recv_peek`: `bool` Returns the message without removing it from the socket's receive queue. -- `recv_waitall` +Bit: 0 + +- `recv_waitall`: `bool` On byte-stream sockets, block until the full amount of data can be returned. -## `roflags`: `u16` +Bit: 1 + +## `roflags`: `Record` Flags returned by [`sock_recv`](#sock_recv). Size: 2 Alignment: 2 -### Constants -- `recv_data_truncated` +### Record members +- `recv_data_truncated`: `bool` Returned by [`sock_recv`](#sock_recv): Message data has been truncated. +Bit: 0 + ## `siflags`: `u16` Flags provided to [`sock_send`](#sock_send). As there are currently no flags defined, it must be set to zero. @@ -1105,21 +1201,25 @@ Size: 2 Alignment: 2 -## `sdflags`: `u8` +## `sdflags`: `Record` Which channels on a socket to shut down. Size: 1 Alignment: 1 -### Constants -- `rd` +### Record members +- `rd`: `bool` Disables further receive operations. -- `wr` +Bit: 0 + +- `wr`: `bool` Disables further send operations. -## `preopentype`: Variant +Bit: 1 + +## `preopentype`: `Variant` Identifiers for preopened capabilities. Size: 1 @@ -1130,7 +1230,7 @@ Alignment: 1 - `dir` A pre-opened directory. -## `prestat_dir`: Record +## `prestat_dir`: `Record` The contents of a $prestat when type is [`preopentype::dir`](#preopentype.dir). Size: 4 @@ -1143,7 +1243,7 @@ The length of the directory name for use with [`fd_prestat_dir_name`](#fd_presta Offset: 0 -## `prestat`: Variant +## `prestat`: `Variant` Information about a pre-opened capability. Size: 8 @@ -1165,7 +1265,7 @@ Alignment: 4 --- -#### `args_get(argv: Pointer>, argv_buf: Pointer) -> errno` +#### `args_get(argv: Pointer>, argv_buf: Pointer) -> Result<(), errno>` Read command-line argument data. The size of the array should match that returned by [`args_sizes_get`](#args_sizes_get) @@ -1175,28 +1275,51 @@ The size of the array should match that returned by [`args_sizes_get`](#args_siz - `argv_buf`: `Pointer` ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `args_sizes_get() -> (errno, size, size)` +#### `args_sizes_get() -> Result<(size, size), errno>` Return command-line argument data sizes. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, size), errno>` +Returns the number of arguments and the size of the argument string +data, or an error. + +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, size)` + +####### Record members +- `0`: [`size`](#size) -- `argc`: [`size`](#size) -The number of arguments. +Offset: 0 + +- `1`: [`size`](#size) + +Offset: 4 -- `argv_buf_size`: [`size`](#size) -The size of the argument string data. +- `err`: [`errno`](#errno) --- -#### `environ_get(environ: Pointer>, environ_buf: Pointer) -> errno` +#### `environ_get(environ: Pointer>, environ_buf: Pointer) -> Result<(), errno>` Read environment variable data. The sizes of the buffers should match that returned by [`environ_sizes_get`](#environ_sizes_get). @@ -1206,28 +1329,51 @@ The sizes of the buffers should match that returned by [`environ_sizes_get`](#en - `environ_buf`: `Pointer` ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `environ_sizes_get() -> (errno, size, size)` +#### `environ_sizes_get() -> Result<(size, size), errno>` Return environment variable data sizes. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, size), errno>` +Returns the number of environment variable arguments and the size of the +environment variable data. + +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, size)` + +####### Record members +- `0`: [`size`](#size) + +Offset: 0 + +- `1`: [`size`](#size) -- `environc`: [`size`](#size) -The number of environment variable arguments. +Offset: 4 -- `environ_buf_size`: [`size`](#size) -The size of the environment variable data. +- `err`: [`errno`](#errno) --- -#### `clock_res_get(id: clockid) -> (errno, timestamp)` +#### `clock_res_get(id: clockid) -> Result` Return the resolution of a clock. Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, return [`errno::inval`](#errno.inval). @@ -1238,15 +1384,22 @@ Note: This is similar to `clock_getres` in POSIX. The clock for which to return the resolution. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result` +The resolution of the clock, or an error if one happened. + +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`timestamp`](#timestamp) -- `resolution`: [`timestamp`](#timestamp) -The resolution of the clock. +- `err`: [`errno`](#errno) --- -#### `clock_time_get(id: clockid, precision: timestamp) -> (errno, timestamp)` +#### `clock_time_get(id: clockid, precision: timestamp) -> Result` Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX. @@ -1258,15 +1411,22 @@ The clock for which to return the time. The maximum lag (exclusive) that the returned time value may have, compared to its actual value. ##### Results -- `error`: [`errno`](#errno) - -- `time`: [`timestamp`](#timestamp) +- `error`: `Result` The time value of the clock. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`timestamp`](#timestamp) + +- `err`: [`errno`](#errno) + --- -#### `fd_advise(fd: fd, offset: filesize, len: filesize, advice: advice) -> errno` +#### `fd_advise(fd: fd, offset: filesize, len: filesize, advice: advice) -> Result<(), errno>` Provide file advisory information on a file descriptor. Note: This is similar to `posix_fadvise` in POSIX. @@ -1283,12 +1443,21 @@ The length of the region to which the advisory applies. The advice. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_allocate(fd: fd, offset: filesize, len: filesize) -> errno` +#### `fd_allocate(fd: fd, offset: filesize, len: filesize) -> Result<(), errno>` Force the allocation of space in a file. Note: This is similar to `posix_fallocate` in POSIX. @@ -1302,12 +1471,21 @@ The offset at which to start the allocation. The length of the area that is allocated. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_close(fd: fd) -> errno` +#### `fd_close(fd: fd) -> Result<(), errno>` Close a file descriptor. Note: This is similar to `close` in POSIX. @@ -1315,12 +1493,21 @@ Note: This is similar to `close` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_datasync(fd: fd) -> errno` +#### `fd_datasync(fd: fd) -> Result<(), errno>` Synchronize the data of a file to disk. Note: This is similar to `fdatasync` in POSIX. @@ -1328,12 +1515,21 @@ Note: This is similar to `fdatasync` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_fdstat_get(fd: fd) -> (errno, fdstat)` +#### `fd_fdstat_get(fd: fd) -> Result` Get the attributes of a file descriptor. Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. @@ -1341,15 +1537,22 @@ Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as ad - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `stat`: [`fdstat`](#fdstat) +- `error`: `Result` The buffer where the file descriptor's attributes are stored. +###### Variant Layout +- size: 32 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`fdstat`](#fdstat) + +- `err`: [`errno`](#errno) + --- -#### `fd_fdstat_set_flags(fd: fd, flags: fdflags) -> errno` +#### `fd_fdstat_set_flags(fd: fd, flags: fdflags) -> Result<(), errno>` Adjust the flags associated with a file descriptor. Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. @@ -1360,12 +1563,21 @@ Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. The desired values of the file descriptor flags. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_fdstat_set_rights(fd: fd, fs_rights_base: rights, fs_rights_inheriting: rights) -> errno` +#### `fd_fdstat_set_rights(fd: fd, fs_rights_base: rights, fs_rights_inheriting: rights) -> Result<(), errno>` Adjust the rights associated with a file descriptor. This can only be used to remove rights, and returns [`errno::notcapable`](#errno.notcapable) if called in a way that would attempt to add rights @@ -1378,27 +1590,43 @@ The desired rights of the file descriptor. - `fs_rights_inheriting`: [`rights`](#rights) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_filestat_get(fd: fd) -> (errno, filestat)` +#### `fd_filestat_get(fd: fd) -> Result` Return the attributes of an open file. ##### Params - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`filestat`](#filestat) +- `error`: `Result` The buffer where the file's attributes are stored. +###### Variant Layout +- size: 72 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filestat`](#filestat) + +- `err`: [`errno`](#errno) + --- -#### `fd_filestat_set_size(fd: fd, size: filesize) -> errno` +#### `fd_filestat_set_size(fd: fd, size: filesize) -> Result<(), errno>` Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. Note: This is similar to `ftruncate` in POSIX. @@ -1409,12 +1637,21 @@ Note: This is similar to `ftruncate` in POSIX. The desired file size. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_filestat_set_times(fd: fd, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> errno` +#### `fd_filestat_set_times(fd: fd, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> Result<(), errno>` Adjust the timestamps of an open file or directory. Note: This is similar to `futimens` in POSIX. @@ -1431,12 +1668,21 @@ The desired values of the data modification timestamp. A bitmask indicating which timestamps to adjust. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_pread(fd: fd, iovs: iovec_array, offset: filesize) -> (errno, size)` +#### `fd_pread(fd: fd, iovs: iovec_array, offset: filesize) -> Result` Read from a file descriptor, without using and updating the file descriptor's offset. Note: This is similar to `preadv` in POSIX. @@ -1450,30 +1696,44 @@ List of scatter/gather vectors in which to store data. The offset within the file at which to read. ##### Results -- `error`: [`errno`](#errno) - -- `nread`: [`size`](#size) +- `error`: `Result` The number of bytes read. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_prestat_get(fd: fd) -> (errno, prestat)` +#### `fd_prestat_get(fd: fd) -> Result` Return a description of the given preopened file descriptor. ##### Params - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`prestat`](#prestat) +- `error`: `Result` The buffer where the description is stored. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`prestat`](#prestat) + +- `err`: [`errno`](#errno) + --- -#### `fd_prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> errno` +#### `fd_prestat_dir_name(fd: fd, path: Pointer, path_len: size) -> Result<(), errno>` Return a description of the given preopened file descriptor. ##### Params @@ -1485,12 +1745,21 @@ A buffer into which to write the preopened directory name. - `path_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_pwrite(fd: fd, iovs: ciovec_array, offset: filesize) -> (errno, size)` +#### `fd_pwrite(fd: fd, iovs: ciovec_array, offset: filesize) -> Result` Write to a file descriptor, without using and updating the file descriptor's offset. Note: This is similar to `pwritev` in POSIX. @@ -1504,15 +1773,22 @@ List of scatter/gather vectors from which to retrieve data. The offset within the file at which to write. ##### Results -- `error`: [`errno`](#errno) - -- `nwritten`: [`size`](#size) +- `error`: `Result` The number of bytes written. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_read(fd: fd, iovs: iovec_array) -> (errno, size)` +#### `fd_read(fd: fd, iovs: iovec_array) -> Result` Read from a file descriptor. Note: This is similar to `readv` in POSIX. @@ -1523,15 +1799,22 @@ Note: This is similar to `readv` in POSIX. List of scatter/gather vectors to which to store data. ##### Results -- `error`: [`errno`](#errno) - -- `nread`: [`size`](#size) +- `error`: `Result` The number of bytes read. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_readdir(fd: fd, buf: Pointer, buf_len: size, cookie: dircookie) -> (errno, size)` +#### `fd_readdir(fd: fd, buf: Pointer, buf_len: size, cookie: dircookie) -> Result` Read directory entries from a directory. When successful, the contents of the output buffer consist of a sequence of directory entries. Each directory entry consists of a [`dirent`](#dirent) object, @@ -1554,15 +1837,22 @@ The buffer where directory entries are stored The location within the directory to start reading ##### Results -- `error`: [`errno`](#errno) - -- `bufused`: [`size`](#size) +- `error`: `Result` The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `fd_renumber(fd: fd, to: fd) -> errno` +#### `fd_renumber(fd: fd, to: fd) -> Result<(), errno>` Atomically replace a file descriptor by renumbering another file descriptor. Due to the strong focus on thread safety, this environment does not provide a mechanism to duplicate or renumber a file descriptor to an arbitrary @@ -1579,12 +1869,21 @@ would disappear if `dup2()` were to be removed entirely. The file descriptor to overwrite. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_seek(fd: fd, offset: filedelta, whence: whence) -> (errno, filesize)` +#### `fd_seek(fd: fd, offset: filedelta, whence: whence) -> Result` Move the offset of a file descriptor. Note: This is similar to `lseek` in POSIX. @@ -1598,15 +1897,22 @@ The number of bytes to move. The base from which the offset is relative. ##### Results -- `error`: [`errno`](#errno) - -- `newoffset`: [`filesize`](#filesize) +- `error`: `Result` The new offset of the file descriptor, relative to the start of the file. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filesize`](#filesize) + +- `err`: [`errno`](#errno) + --- -#### `fd_sync(fd: fd) -> errno` +#### `fd_sync(fd: fd) -> Result<(), errno>` Synchronize the data and metadata of a file to disk. Note: This is similar to `fsync` in POSIX. @@ -1614,12 +1920,21 @@ Note: This is similar to `fsync` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `fd_tell(fd: fd) -> (errno, filesize)` +#### `fd_tell(fd: fd) -> Result` Return the current offset of a file descriptor. Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. @@ -1627,15 +1942,22 @@ Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. - `fd`: [`fd`](#fd) ##### Results -- `error`: [`errno`](#errno) - -- `offset`: [`filesize`](#filesize) +- `error`: `Result` The current offset of the file descriptor, relative to the start of the file. +###### Variant Layout +- size: 16 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filesize`](#filesize) + +- `err`: [`errno`](#errno) + --- -#### `fd_write(fd: fd, iovs: ciovec_array) -> (errno, size)` +#### `fd_write(fd: fd, iovs: ciovec_array) -> Result` Write to a file descriptor. Note: This is similar to `writev` in POSIX. @@ -1646,15 +1968,21 @@ Note: This is similar to `writev` in POSIX. List of scatter/gather vectors from which to retrieve data. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result` -- `nwritten`: [`size`](#size) -The number of bytes written. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) --- -#### `path_create_directory(fd: fd, path: string) -> errno` +#### `path_create_directory(fd: fd, path: string) -> Result<(), errno>` Create a directory. Note: This is similar to `mkdirat` in POSIX. @@ -1665,12 +1993,21 @@ Note: This is similar to `mkdirat` in POSIX. The path at which to create the directory. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_filestat_get(fd: fd, flags: lookupflags, path: string) -> (errno, filestat)` +#### `path_filestat_get(fd: fd, flags: lookupflags, path: string) -> Result` Return the attributes of a file or directory. Note: This is similar to `stat` in POSIX. @@ -1684,15 +2021,22 @@ Flags determining the method of how the path is resolved. The path of the file or directory to inspect. ##### Results -- `error`: [`errno`](#errno) - -- `buf`: [`filestat`](#filestat) +- `error`: `Result` The buffer where the file's attributes are stored. +###### Variant Layout +- size: 72 +- align: 8 +- tag_size: 4 +###### Variant cases +- `ok`: [`filestat`](#filestat) + +- `err`: [`errno`](#errno) + --- -#### `path_filestat_set_times(fd: fd, flags: lookupflags, path: string, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> errno` +#### `path_filestat_set_times(fd: fd, flags: lookupflags, path: string, atim: timestamp, mtim: timestamp, fst_flags: fstflags) -> Result<(), errno>` Adjust the timestamps of a file or directory. Note: This is similar to `utimensat` in POSIX. @@ -1715,12 +2059,21 @@ The desired values of the data modification timestamp. A bitmask indicating which timestamps to adjust. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_link(old_fd: fd, old_flags: lookupflags, old_path: string, new_fd: fd, new_path: string) -> errno` +#### `path_link(old_fd: fd, old_flags: lookupflags, old_path: string, new_fd: fd, new_path: string) -> Result<(), errno>` Create a hard link. Note: This is similar to `linkat` in POSIX. @@ -1740,12 +2093,21 @@ The working directory at which the resolution of the new path starts. The destination path at which to create the hard link. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_open(fd: fd, dirflags: lookupflags, path: string, oflags: oflags, fs_rights_base: rights, fs_rights_inheriting: rights, fdflags: fdflags) -> (errno, fd)` +#### `path_open(fd: fd, dirflags: lookupflags, path: string, oflags: oflags, fs_rights_base: rights, fs_rights_inheriting: rights, fdflags: fdflags) -> Result` Open a file or directory. The returned file descriptor is not guaranteed to be the lowest-numbered file descriptor not currently open; it is randomized to prevent @@ -1781,15 +2143,22 @@ file descriptors derived from it. - `fdflags`: [`fdflags`](#fdflags) ##### Results -- `error`: [`errno`](#errno) - -- `opened_fd`: [`fd`](#fd) +- `error`: `Result` The file descriptor of the file that has been opened. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`fd`](#fd) + +- `err`: [`errno`](#errno) + --- -#### `path_readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> (errno, size)` +#### `path_readlink(fd: fd, path: string, buf: Pointer, buf_len: size) -> Result` Read the contents of a symbolic link. Note: This is similar to `readlinkat` in POSIX. @@ -1805,15 +2174,22 @@ The buffer to which to write the contents of the symbolic link. - `buf_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) - -- `bufused`: [`size`](#size) +- `error`: `Result` The number of bytes placed in the buffer. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `path_remove_directory(fd: fd, path: string) -> errno` +#### `path_remove_directory(fd: fd, path: string) -> Result<(), errno>` Remove a directory. Return [`errno::notempty`](#errno.notempty) if the directory is not empty. Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. @@ -1825,12 +2201,21 @@ Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. The path to a directory to remove. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_rename(fd: fd, old_path: string, new_fd: fd, new_path: string) -> errno` +#### `path_rename(fd: fd, old_path: string, new_fd: fd, new_path: string) -> Result<(), errno>` Rename a file or directory. Note: This is similar to `renameat` in POSIX. @@ -1847,12 +2232,21 @@ The working directory at which the resolution of the new path starts. The destination path to which to rename the file or directory. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_symlink(old_path: string, fd: fd, new_path: string) -> errno` +#### `path_symlink(old_path: string, fd: fd, new_path: string) -> Result<(), errno>` Create a symbolic link. Note: This is similar to `symlinkat` in POSIX. @@ -1866,12 +2260,21 @@ The contents of the symbolic link. The destination path at which to create the symbolic link. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `path_unlink_file(fd: fd, path: string) -> errno` +#### `path_unlink_file(fd: fd, path: string) -> Result<(), errno>` Unlink a file. Return [`errno::isdir`](#errno.isdir) if the path refers to a directory. Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. @@ -1883,12 +2286,21 @@ Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. The path to a file to unlink. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `poll_oneoff(in: ConstPointer, out: Pointer, nsubscriptions: size) -> (errno, size)` +#### `poll_oneoff(in: ConstPointer, out: Pointer, nsubscriptions: size) -> Result` Concurrently poll for the occurrence of a set of events. ##### Params @@ -1902,11 +2314,18 @@ The events that have occurred. Both the number of subscriptions and events. ##### Results -- `error`: [`errno`](#errno) - -- `nevents`: [`size`](#size) +- `error`: `Result` The number of events stored. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- @@ -1923,7 +2342,7 @@ The exit code returned by the process. --- -#### `proc_raise(sig: signal) -> errno` +#### `proc_raise(sig: signal) -> Result<(), errno>` Send a signal to the process of the calling thread. Note: This is similar to `raise` in POSIX. @@ -1932,23 +2351,41 @@ Note: This is similar to `raise` in POSIX. The signal condition to trigger. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `sched_yield() -> errno` +#### `sched_yield() -> Result<(), errno>` Temporarily yield execution of the calling thread. Note: This is similar to [`sched_yield`](#sched_yield) in POSIX. ##### Params ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `random_get(buf: Pointer, buf_len: size) -> errno` +#### `random_get(buf: Pointer, buf_len: size) -> Result<(), errno>` Write high-quality random data into a buffer. This function blocks when the implementation is unable to immediately provide sufficient high-quality random data. @@ -1963,12 +2400,21 @@ The buffer to fill with random data. - `buf_len`: [`size`](#size) ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) --- -#### `sock_recv(fd: fd, ri_data: iovec_array, ri_flags: riflags) -> (errno, size, roflags)` +#### `sock_recv(fd: fd, ri_data: iovec_array, ri_flags: riflags) -> Result<(size, roflags), errno>` Receive a message from a socket. Note: This is similar to `recv` in POSIX, though it also supports reading the data into multiple buffers in the manner of `readv`. @@ -1983,18 +2429,31 @@ List of scatter/gather vectors to which to store data. Message flags. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(size, roflags), errno>` +Number of bytes stored in ri_data and message flags. -- `ro_datalen`: [`size`](#size) -Number of bytes stored in ri_data. +###### Variant Layout +- size: 12 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: `(size, roflags)` -- `ro_flags`: [`roflags`](#roflags) -Message flags. +####### Record members +- `0`: [`size`](#size) + +Offset: 0 + +- `1`: [`roflags`](#roflags) + +Offset: 4 + +- `err`: [`errno`](#errno) --- -#### `sock_send(fd: fd, si_data: ciovec_array, si_flags: siflags) -> (errno, size)` +#### `sock_send(fd: fd, si_data: ciovec_array, si_flags: siflags) -> Result` Send a message on a socket. Note: This is similar to `send` in POSIX, though it also supports writing the data from multiple buffers in the manner of `writev`. @@ -2009,15 +2468,22 @@ List of scatter/gather vectors to which to retrieve data Message flags. ##### Results -- `error`: [`errno`](#errno) - -- `so_datalen`: [`size`](#size) +- `error`: `Result` Number of bytes transmitted. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`size`](#size) + +- `err`: [`errno`](#errno) + --- -#### `sock_shutdown(fd: fd, how: sdflags) -> errno` +#### `sock_shutdown(fd: fd, how: sdflags) -> Result<(), errno>` Shut down socket send and receive channels. Note: This is similar to `shutdown` in POSIX. @@ -2028,5 +2494,14 @@ Note: This is similar to `shutdown` in POSIX. Which channels on the socket to shut down. ##### Results -- `error`: [`errno`](#errno) +- `error`: `Result<(), errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`errno`](#errno) diff --git a/phases/snapshot/witx/typenames.witx b/phases/snapshot/witx/typenames.witx index 70e7265..311b422 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 (@witx bitflags u64) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u32) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 (@witx bitflags u16) + (flags (@witx repr 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 @@ -693,7 +693,7 @@ ;;; Flags provided to `sock_recv`. (typename $riflags - (flags (@witx bitflags u16) + (flags (@witx repr 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. @@ -703,7 +703,7 @@ ;;; Flags returned by `sock_recv`. (typename $roflags - (flags (@witx bitflags u16) + (flags (@witx repr u16) ;;; Returned by `sock_recv`: Message data has been truncated. $recv_data_truncated ) @@ -715,7 +715,7 @@ ;;; Which channels on a socket to shut down. (typename $sdflags - (flags (@witx bitflags u8) + (flags (@witx repr u8) ;;; Disables further receive operations. $rd ;;; Disables further send operations. diff --git a/phases/snapshot/witx/wasi_snapshot_preview1.witx b/phases/snapshot/witx/wasi_snapshot_preview1.witx index eacfab2..df3c670 100644 --- a/phases/snapshot/witx/wasi_snapshot_preview1.witx +++ b/phases/snapshot/witx/wasi_snapshot_preview1.witx @@ -17,15 +17,13 @@ (@interface func (export "args_get") (param $argv (@witx pointer (@witx pointer u8))) (param $argv_buf (@witx pointer u8)) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return command-line argument data sizes. (@interface func (export "args_sizes_get") - (result $error $errno) - ;;; The number of arguments. - (result $argc $size) - ;;; The size of the argument string data. - (result $argv_buf_size $size) + ;;; Returns the number of arguments and the size of the argument string + ;;; data, or an error. + (result $error (expected (tuple $size $size) (error $errno))) ) ;;; Read environment variable data. @@ -33,15 +31,13 @@ (@interface func (export "environ_get") (param $environ (@witx pointer (@witx pointer u8))) (param $environ_buf (@witx pointer u8)) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return environment variable data sizes. (@interface func (export "environ_sizes_get") - (result $error $errno) - ;;; The number of environment variable arguments. - (result $environc $size) - ;;; The size of the environment variable data. - (result $environ_buf_size $size) + ;;; Returns the number of environment variable arguments and the size of the + ;;; environment variable data. + (result $error (expected (tuple $size $size) (error $errno))) ) ;;; Return the resolution of a clock. @@ -51,9 +47,8 @@ (@interface func (export "clock_res_get") ;;; The clock for which to return the resolution. (param $id $clockid) - (result $error $errno) - ;;; The resolution of the clock. - (result $resolution $timestamp) + ;;; The resolution of the clock, or an error if one happened. + (result $error (expected $timestamp (error $errno))) ) ;;; Return the time value of a clock. ;;; Note: This is similar to `clock_gettime` in POSIX. @@ -62,9 +57,8 @@ (param $id $clockid) ;;; The maximum lag (exclusive) that the returned time value may have, compared to its actual value. (param $precision $timestamp) - (result $error $errno) ;;; The time value of the clock. - (result $time $timestamp) + (result $error (expected $timestamp (error $errno))) ) ;;; Provide file advisory information on a file descriptor. @@ -77,7 +71,7 @@ (param $len $filesize) ;;; The advice. (param $advice $advice) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Force the allocation of space in a file. @@ -88,30 +82,29 @@ (param $offset $filesize) ;;; The length of the area that is allocated. (param $len $filesize) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Close a file descriptor. ;;; Note: This is similar to `close` in POSIX. (@interface func (export "fd_close") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Synchronize the data of a file to disk. ;;; Note: This is similar to `fdatasync` in POSIX. (@interface func (export "fd_datasync") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Get the attributes of a file descriptor. ;;; Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. (@interface func (export "fd_fdstat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the file descriptor's attributes are stored. - (result $stat $fdstat) + (result $error (expected $fdstat (error $errno))) ) ;;; Adjust the flags associated with a file descriptor. @@ -120,7 +113,7 @@ (param $fd $fd) ;;; The desired values of the file descriptor flags. (param $flags $fdflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Adjust the rights associated with a file descriptor. @@ -130,15 +123,14 @@ ;;; The desired rights of the file descriptor. (param $fs_rights_base $rights) (param $fs_rights_inheriting $rights) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the attributes of an open file. (@interface func (export "fd_filestat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the file's attributes are stored. - (result $buf $filestat) + (result $error (expected $filestat (error $errno))) ) ;;; Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. @@ -147,7 +139,7 @@ (param $fd $fd) ;;; The desired file size. (param $size $filesize) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Adjust the timestamps of an open file or directory. @@ -160,7 +152,7 @@ (param $mtim $timestamp) ;;; A bitmask indicating which timestamps to adjust. (param $fst_flags $fstflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Read from a file descriptor, without using and updating the file descriptor's offset. @@ -171,17 +163,15 @@ (param $iovs $iovec_array) ;;; The offset within the file at which to read. (param $offset $filesize) - (result $error $errno) ;;; The number of bytes read. - (result $nread $size) + (result $error (expected $size (error $errno))) ) ;;; Return a description of the given preopened file descriptor. (@interface func (export "fd_prestat_get") (param $fd $fd) - (result $error $errno) ;;; The buffer where the description is stored. - (result $buf $prestat) + (result $error (expected $prestat (error $errno))) ) ;;; Return a description of the given preopened file descriptor. @@ -190,7 +180,7 @@ ;;; A buffer into which to write the preopened directory name. (param $path (@witx pointer u8)) (param $path_len $size) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Write to a file descriptor, without using and updating the file descriptor's offset. @@ -201,9 +191,8 @@ (param $iovs $ciovec_array) ;;; The offset within the file at which to write. (param $offset $filesize) - (result $error $errno) ;;; The number of bytes written. - (result $nwritten $size) + (result $error (expected $size (error $errno))) ) ;;; Read from a file descriptor. @@ -212,9 +201,8 @@ (param $fd $fd) ;;; List of scatter/gather vectors to which to store data. (param $iovs $iovec_array) - (result $error $errno) ;;; The number of bytes read. - (result $nread $size) + (result $error (expected $size (error $errno))) ) ;;; Read directory entries from a directory. @@ -234,9 +222,8 @@ (param $buf_len $size) ;;; The location within the directory to start reading (param $cookie $dircookie) - (result $error $errno) ;;; The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. - (result $bufused $size) + (result $error (expected $size (error $errno))) ) ;;; Atomically replace a file descriptor by renumbering another file descriptor. @@ -253,7 +240,7 @@ (param $fd $fd) ;;; The file descriptor to overwrite. (param $to $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Move the offset of a file descriptor. @@ -264,25 +251,23 @@ (param $offset $filedelta) ;;; The base from which the offset is relative. (param $whence $whence) - (result $error $errno) ;;; The new offset of the file descriptor, relative to the start of the file. - (result $newoffset $filesize) + (result $error (expected $filesize (error $errno))) ) ;;; Synchronize the data and metadata of a file to disk. ;;; Note: This is similar to `fsync` in POSIX. (@interface func (export "fd_sync") (param $fd $fd) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the current offset of a file descriptor. ;;; Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. (@interface func (export "fd_tell") (param $fd $fd) - (result $error $errno) ;;; The current offset of the file descriptor, relative to the start of the file. - (result $offset $filesize) + (result $error (expected $filesize (error $errno))) ) ;;; Write to a file descriptor. @@ -291,9 +276,7 @@ (param $fd $fd) ;;; List of scatter/gather vectors from which to retrieve data. (param $iovs $ciovec_array) - (result $error $errno) - ;;; The number of bytes written. - (result $nwritten $size) + (result $error (expected $size (error $errno))) ) ;;; Create a directory. @@ -302,7 +285,7 @@ (param $fd $fd) ;;; The path at which to create the directory. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Return the attributes of a file or directory. @@ -313,9 +296,8 @@ (param $flags $lookupflags) ;;; The path of the file or directory to inspect. (param $path string) - (result $error $errno) ;;; The buffer where the file's attributes are stored. - (result $buf $filestat) + (result $error (expected $filestat (error $errno))) ) ;;; Adjust the timestamps of a file or directory. @@ -332,7 +314,7 @@ (param $mtim $timestamp) ;;; A bitmask indicating which timestamps to adjust. (param $fst_flags $fstflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Create a hard link. @@ -347,7 +329,7 @@ (param $new_fd $fd) ;;; The destination path at which to create the hard link. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Open a file or directory. @@ -379,9 +361,8 @@ (param $fs_rights_base $rights) (param $fs_rights_inheriting $rights) (param $fdflags $fdflags) - (result $error $errno) ;;; The file descriptor of the file that has been opened. - (result $opened_fd $fd) + (result $error (expected $fd (error $errno))) ) ;;; Read the contents of a symbolic link. @@ -393,9 +374,8 @@ ;;; The buffer to which to write the contents of the symbolic link. (param $buf (@witx pointer u8)) (param $buf_len $size) - (result $error $errno) ;;; The number of bytes placed in the buffer. - (result $bufused $size) + (result $error (expected $size (error $errno))) ) ;;; Remove a directory. @@ -405,7 +385,7 @@ (param $fd $fd) ;;; The path to a directory to remove. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Rename a file or directory. @@ -418,7 +398,7 @@ (param $new_fd $fd) ;;; The destination path to which to rename the file or directory. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Create a symbolic link. @@ -429,7 +409,7 @@ (param $fd $fd) ;;; The destination path at which to create the symbolic link. (param $new_path string) - (result $error $errno) + (result $error (expected (error $errno))) ) @@ -440,7 +420,7 @@ (param $fd $fd) ;;; The path to a file to unlink. (param $path string) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Concurrently poll for the occurrence of a set of events. @@ -451,9 +431,8 @@ (param $out (@witx pointer $event)) ;;; Both the number of subscriptions and events. (param $nsubscriptions $size) - (result $error $errno) ;;; The number of events stored. - (result $nevents $size) + (result $error (expected $size (error $errno))) ) ;;; Terminate the process normally. An exit code of 0 indicates successful @@ -470,13 +449,13 @@ (@interface func (export "proc_raise") ;;; The signal condition to trigger. (param $sig $signal) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Temporarily yield execution of the calling thread. ;;; Note: This is similar to `sched_yield` in POSIX. (@interface func (export "sched_yield") - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Write high-quality random data into a buffer. @@ -489,7 +468,7 @@ ;;; The buffer to fill with random data. (param $buf (@witx pointer u8)) (param $buf_len $size) - (result $error $errno) + (result $error (expected (error $errno))) ) ;;; Receive a message from a socket. @@ -501,11 +480,8 @@ (param $ri_data $iovec_array) ;;; Message flags. (param $ri_flags $riflags) - (result $error $errno) - ;;; Number of bytes stored in ri_data. - (result $ro_datalen $size) - ;;; Message flags. - (result $ro_flags $roflags) + ;;; Number of bytes stored in ri_data and message flags. + (result $error (expected (tuple $size $roflags) (error $errno))) ) ;;; Send a message on a socket. @@ -517,9 +493,8 @@ (param $si_data $ciovec_array) ;;; Message flags. (param $si_flags $siflags) - (result $error $errno) ;;; Number of bytes transmitted. - (result $so_datalen $size) + (result $error (expected $size (error $errno))) ) ;;; Shut down socket send and receive channels. @@ -528,6 +503,6 @@ (param $fd $fd) ;;; Which channels on the socket to shut down. (param $how $sdflags) - (result $error $errno) + (result $error (expected (error $errno))) ) ) diff --git a/tools/witx/Cargo.toml b/tools/witx/Cargo.toml index bf9ce16..bd4bfc0 100644 --- a/tools/witx/Cargo.toml +++ b/tools/witx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "witx" -version = "0.8.8" +version = "0.9.0" description = "Parse and validate witx file format" homepage = "https://github.com/WebAssembly/WASI" repository = "https://github.com/WebAssembly/WASI" @@ -17,7 +17,7 @@ crate-type=["rlib"] anyhow = "1" log = "0.4" thiserror = "1.0" -wast = { version = "22.0.0", default-features = false } +wast = { version = "33.0.0", default-features = false } [dev-dependencies] diff = "0.1.11" diff --git a/tools/witx/src/abi.rs b/tools/witx/src/abi.rs new file mode 100644 index 0000000..0e1ca9c --- /dev/null +++ b/tools/witx/src/abi.rs @@ -0,0 +1,925 @@ +//! Definition of the ABI of witx functions +//! +//! This module is intended to assist with code generators which are binding or +//! implementing APIs defined by `*.witx` files. THis module contains all +//! details necessary to implement the actual ABI of these functions so wasm +//! modules and hosts can communicate with one another. +//! +//! Each interface types function (a function defined in `*.witx`) currently has +//! a well-known wasm signature associated with it. There's then also a standard +//! way to convert from interface-types values (whose representation is defined +//! per-language) into this wasm API. This module is intended to assist with +//! this definition. +//! +//! Contained within are two primary functions, [`InterfaceFunc::call_wasm`] and +//! [`InterfaceFunc::call_interface`]. These functions implement the two ways to +//! interact with an interface types function, namely calling the raw wasm +//! version and calling the high-level version with interface types. These two +//! functions are fed a structure that implements [`Bindgen`]. An instance of +//! [`Bindgen`] receives instructions which are low-level implementation details +//! of how to convert to and from wasm types and interface types. Code +//! generators will need to implement the various instructions to support APIs. + +use crate::{ + BuiltinType, Id, IntRepr, InterfaceFunc, InterfaceFuncParam, NamedType, Type, TypeRef, +}; + +/// Enumerates wasm types used by interface types when lowering/lifting. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum WasmType { + I32, + I64, + F32, + F64, + // NOTE: we don't lower interface types to any other Wasm type, + // e.g. externref, so we don't need to define them here. +} + +impl From for WasmType { + fn from(i: IntRepr) -> WasmType { + match i { + IntRepr::U8 | IntRepr::U16 | IntRepr::U32 => WasmType::I32, + IntRepr::U64 => WasmType::I64, + } + } +} + +/// Possible ABIs for interface functions to have. +/// +/// Note that this is a stopgap until we have more of interface types. Interface +/// types functions do not have ABIs, they have APIs. For the meantime, however, +/// we mandate ABIs to ensure we can all talk to each other. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Abi { + /// Only stable ABI currently, and is the historical WASI ABI since it was + /// first created. + /// + /// Note that this ABI is limited notably in its return values where it can + /// only return 0 results or one `Result` lookalike. + Preview1, +} + +// Helper macro for defining instructions without having to have tons of +// exhaustive `match` statements to update +macro_rules! def_instruction { + ( + $( #[$enum_attr:meta] )* + pub enum Instruction<'a> { + $( + $( #[$attr:meta] )* + $variant:ident $( { + $($field:ident : $field_ty:ty $(,)* )* + } )? + : + [$num_popped:expr] => [$num_pushed:expr], + )* + } + ) => { + $( #[$enum_attr] )* + pub enum Instruction<'a> { + $( + $( #[$attr] )* + $variant $( { + $( + $field : $field_ty, + )* + } )? , + )* + } + + impl Instruction<'_> { + /// How many operands does this instruction pop from the stack? + #[allow(unused_variables)] + pub fn operands_len(&self) -> usize { + match self { + $( + Self::$variant $( { + $( + $field, + )* + } )? => $num_popped, + )* + } + } + + /// How many results does this instruction push onto the stack? + #[allow(unused_variables)] + pub fn results_len(&self) -> usize { + match self { + $( + Self::$variant $( { + $( + $field, + )* + } )? => $num_pushed, + )* + } + } + } + }; +} + +def_instruction! { + #[derive(Debug)] + pub enum Instruction<'a> { + /// Acquires the specified parameter and places it on the stack. + /// Depending on the context this may refer to wasm parameters or + /// interface types parameters. + GetArg { nth: usize } : [0] => [1], + /// Takes the value off the top of the stack and writes it into linear + /// memory. Pushes the address in linear memory as an `i32`. + AddrOf : [1] => [1], + /// Converts an interface type `char` value to a 32-bit integer + /// representing the unicode scalar value. + I32FromChar : [1] => [1], + /// Converts an interface type `u64` value to a wasm `i64`. + I64FromU64 : [1] => [1], + /// Converts an interface type `s64` value to a wasm `i64`. + I64FromS64 : [1] => [1], + /// Converts an interface type `u32` value to a wasm `i32`. + I32FromU32 : [1] => [1], + /// Converts an interface type `s32` value to a wasm `i32`. + I32FromS32 : [1] => [1], + /// Converts a language-specific `usize` value to a wasm `i32`. + I32FromUsize : [1] => [1], + /// Converts an interface type `u16` value to a wasm `i32`. + I32FromU16 : [1] => [1], + /// Converts an interface type `s16` value to a wasm `i32`. + I32FromS16 : [1] => [1], + /// Converts an interface type `u8` value to a wasm `i32`. + I32FromU8 : [1] => [1], + /// Converts an interface type `s8` value to a wasm `i32`. + I32FromS8 : [1] => [1], + /// Converts a language-specific C `char` value to a wasm `i32`. + I32FromChar8 : [1] => [1], + /// Converts a language-specific pointer value to a wasm `i32`. + I32FromPointer : [1] => [1], + /// Converts a language-specific pointer value to a wasm `i32`. + I32FromConstPointer : [1] => [1], + /// Converts a language-specific handle value to a wasm `i32`. + I32FromHandle { ty: &'a NamedType } : [1] => [1], + /// Converts a language-specific record-of-bools to the packed + /// representation as an `i32`. + I32FromBitflags { ty: &'a NamedType } : [1] => [1], + /// Converts a language-specific record-of-bools to the packed + /// representation as an `i64`. + I64FromBitflags { ty: &'a NamedType } : [1] => [1], + /// Converts an interface type list into its pointer/length, pushing + /// them both on the stack. + ListPointerLength : [1] => [2], + /// Pops two `i32` values from the stack and creates a list from them of + /// the specified type. The first operand is the pointer in linear + /// memory to the start of the list and the second operand is the + /// length. + ListFromPointerLength { ty: &'a TypeRef } : [2] => [1], + /// Conversion an interface type `f32` value to a wasm `f32`. + /// + /// This may be a noop for some implementations, but it's here in case the + /// native language representation of `f32` is different than the wasm + /// representation of `f32`. + F32FromIf32 : [1] => [1], + /// Conversion an interface type `f64` value to a wasm `f64`. + /// + /// This may be a noop for some implementations, but it's here in case the + /// native language representation of `f64` is different than the wasm + /// representation of `f64`. + F64FromIf64 : [1] => [1], + + /// Represents a call to a raw WebAssembly API. The module/name are + /// provided inline as well as the types if necessary. + CallWasm { + module: &'a str, + name: &'a str, + params: &'a [WasmType], + results: &'a [WasmType], + } : [params.len()] => [results.len()], + + /// Same as `CallWasm`, except the dual where an interface is being + /// called rather than a raw wasm function. + CallInterface { + module: &'a str, + func: &'a InterfaceFunc, + } : [func.params.len()] => [func.results.len()], + + /// Converts a native wasm `i32` to an interface type `s8`. + /// + /// This will truncate the upper bits of the `i32`. + S8FromI32 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `u8`. + /// + /// This will truncate the upper bits of the `i32`. + U8FromI32 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `s16`. + /// + /// This will truncate the upper bits of the `i32`. + S16FromI32 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `u16`. + /// + /// This will truncate the upper bits of the `i32`. + U16FromI32 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `s32`. + S32FromI32 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `u32`. + U32FromI32 : [1] => [1], + /// Converts a native wasm `i64` to an interface type `s64`. + S64FromI64 : [1] => [1], + /// Converts a native wasm `i64` to an interface type `u64`. + U64FromI64 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `char`. + /// + /// It's safe to assume that the `i32` is indeed a valid unicode code point. + CharFromI32 : [1] => [1], + /// Converts a native wasm `i32` to a language-specific C `char`. + /// + /// This will truncate the upper bits of the `i32`. + Char8FromI32 : [1] => [1], + /// Converts a native wasm `i32` to a language-specific `usize`. + UsizeFromI32 : [1] => [1], + /// Converts a native wasm `f32` to an interface type `f32`. + If32FromF32 : [1] => [1], + /// Converts a native wasm `f64` to an interface type `f64`. + If64FromF64 : [1] => [1], + /// Converts a native wasm `i32` to an interface type `handle`. + HandleFromI32 { ty: &'a NamedType } : [1] => [1], + /// Converts a native wasm `i32` to a language-specific pointer. + PointerFromI32 { ty: &'a TypeRef }: [1] => [1], + /// Converts a native wasm `i32` to a language-specific pointer. + ConstPointerFromI32 { ty: &'a TypeRef } : [1] => [1], + /// Converts a native wasm `i32` to a language-specific record-of-bools. + BitflagsFromI32 { ty: &'a NamedType } : [1] => [1], + /// Converts a native wasm `i64` to a language-specific record-of-bools. + BitflagsFromI64 { ty: &'a NamedType } : [1] => [1], + /// Acquires the return pointer `n` and pushes an `i32` on the stack. + /// + /// Implementations of [`Bindgen`] may have [`Bindgen::allocate_space`] + /// called to reserve space in memory for the result of a computation to + /// get written. This instruction acquires a pointer to the space + /// reserved in `allocate_space`. + ReturnPointerGet { n: usize } : [0] => [1], + /// Loads the interface types value from an `i32` pointer popped from + /// the stack. + Load { ty: &'a NamedType } : [1] => [1], + /// Stores an interface types value into linear memory. The first + /// operand is the value to store and the second operand is the pointer + /// in linear memory to store it at. + Store { ty: &'a NamedType } : [2] => [0], + /// Pops a native wasm `i32` from the stack, as well as two blocks + /// internally from the code generator. + /// + /// If the value is 0 then the first "ok" block value should be used. + /// If the value is anything else then the second "err" block value + /// should be used, and the value is used as the error enum. + /// + /// Note that this is a special instruction matching the current ABI of + /// WASI and intentionally differs from the type-level grammar of + /// interface types results. + ResultLift : [1] => [1], + /// Pops a native interface value from the stack as well as two blocks + /// internally from the code generator. + /// + /// A `match` is performed on the value popped and the corresponding + /// block for ok/err is used depending on value. This pushes a single + /// `i32` onto the stack representing the error code for this result. + /// + /// Note that like `ResultLift` this is specialized to the current WASI + /// ABI. + ResultLower { + ok: Option<&'a TypeRef>, + err: Option<&'a TypeRef>, + } : [1] => [1], + /// Converts a native wasm `i32` to an interface type `enum` value. + /// + /// It's guaranteed that the interface type integer value is within + /// range for this enum's type. Additionally `ty` is guaranteed to be + /// enum-like as a `Variant` where all `case` arms have no associated + /// type with them. The purpose of this instruction is to convert a + /// native wasm integer into the enum type for the interface. + EnumLift { ty: &'a NamedType } : [1] => [1], + /// Converts an interface types enum value into a wasm `i32`. + EnumLower { ty: &'a NamedType } : [1] => [1], + /// Creates a tuple from the top `n` elements on the stack, pushing the + /// tuple onto the stack. + TupleLift { amt: usize } : [*amt] => [1], + /// Splits a tuple at the top of the stack into its `n` components, + /// pushing them all onto the stack. + TupleLower { amt: usize } : [1] => [*amt], + /// This is a special instruction specifically for the original ABI of + /// WASI. The raw return `i32` of a function is re-pushed onto the + /// stack for reuse. + ReuseReturn : [0] => [1], + /// Returns `amt` values on the stack. This is always the last + /// instruction. + Return { amt: usize } : [*amt] => [0], + /// This is a special instruction used at the entry of blocks used as + /// part of `ResultLower`, representing that the payload of that variant + /// being matched on should be pushed onto the stack. + VariantPayload : [0] => [1], + } +} + +impl Abi { + /// Validates the parameters/results are representable in this ABI. + /// + /// Returns an error string if they're not representable or returns `Ok` if + /// they're indeed representable. + pub fn validate( + &self, + _params: &[InterfaceFuncParam], + results: &[InterfaceFuncParam], + ) -> Result<(), String> { + assert_eq!(*self, Abi::Preview1); + match results.len() { + 0 => {} + 1 => match &**results[0].tref.type_() { + Type::Handle(_) | Type::Builtin(_) | Type::ConstPointer(_) | Type::Pointer(_) => {} + Type::Variant(v) => { + let (ok, err) = match v.as_expected() { + Some(pair) => pair, + None => return Err("invalid return type".to_string()), + }; + if let Some(ty) = ok { + match &**ty.type_() { + Type::Record(r) if r.is_tuple() => { + for member in r.members.iter() { + if !member.tref.named() { + return Err( + "only named types are allowed in results".to_string() + ); + } + } + } + _ => { + if !ty.named() { + return Err( + "only named types are allowed in results".to_string() + ); + } + } + } + } + if let Some(ty) = err { + if !ty.named() { + return Err("only named types are allowed in results".to_string()); + } + if let Type::Variant(v) = &**ty.type_() { + if v.is_enum() { + return Ok(()); + } + } + } + } + Type::Record(r) if r.bitflags_repr().is_some() => {} + Type::Record(_) | Type::List(_) => return Err("invalid return type".to_string()), + }, + _ => return Err("more than one result".to_string()), + } + Ok(()) + } +} + +/// Trait for language implementors to use to generate glue code between native +/// WebAssembly signatures and interface types signatures. +/// +/// This is used as an implementation detail in interpreting the ABI between +/// interface types and wasm types. Eventually this will be driven by interface +/// types adapters themselves, but for now the ABI of a function dictates what +/// instructions are fed in. +/// +/// Types implementing `Bindgen` are incrementally fed `Instruction` values to +/// generate code for. Instructions operate like a stack machine where each +/// instruction has a list of inputs and a list of outputs (provided by the +/// `emit` function). +pub trait Bindgen { + /// The intermediate type for fragments of code for this type. + /// + /// For most languages `String` is a suitable intermediate type. + type Operand; + + /// Emit code to implement the given instruction. + /// + /// Each operand is given in `operands` and can be popped off if ownership + /// is required. It's guaranteed that `operands` has the appropriate length + /// for the `inst` given, as specified with [`Instruction`]. + /// + /// Each result variable should be pushed onto `results`. This function must + /// push the appropriate number of results or binding generation will panic. + fn emit( + &mut self, + inst: &Instruction<'_>, + operands: &mut Vec, + results: &mut Vec, + ); + + /// Allocates temporary space in linear memory indexed by `slot` with enough + /// space to store `ty`. + /// + /// This is called when calling some wasm functions where a return pointer + /// is needed. + fn allocate_space(&mut self, slot: usize, ty: &NamedType); + + /// Enters a new block of code to generate code for. + /// + /// This is currently exclusively used for constructing variants. When a + /// variant is constructed a block here will be pushed for each case of a + /// variant, generating the code necessary to translate a variant case. + /// + /// Blocks are completed with `finish_block` below. It's expected that `emit` + /// will always push code (if necessary) into the "current block", which is + /// updated by calling this method and `finish_block` below. + fn push_block(&mut self); + + /// Indicates to the code generator that a block is completed, and the + /// `operand` specified was the resulting value of the block. + /// + /// This method will be used to compute the value of each arm of lifting a + /// variant. The `operand` will be `None` if the variant case didn't + /// actually have any type associated with it. Otherwise it will be `Some` + /// as the last value remaining on the stack representing the value + /// associated with a variant's `case`. + /// + /// It's expected that this will resume code generation in the previous + /// block before `push_block` was called. This must also save the results + /// of the current block internally for instructions like `ResultLift` to + /// use later. + fn finish_block(&mut self, operand: Option); +} + +impl InterfaceFunc { + /// Get the WebAssembly type signature for this interface function + /// + /// The first entry returned is the list of parameters and the second entry + /// is the list of results for the wasm function signature. + pub fn wasm_signature(&self) -> (Vec, Vec) { + assert_eq!(self.abi, Abi::Preview1); + let mut params = Vec::new(); + let mut results = Vec::new(); + for param in self.params.iter() { + match &**param.tref.type_() { + Type::Builtin(BuiltinType::S8) + | Type::Builtin(BuiltinType::U8 { .. }) + | Type::Builtin(BuiltinType::S16) + | Type::Builtin(BuiltinType::U16) + | Type::Builtin(BuiltinType::S32) + | Type::Builtin(BuiltinType::U32 { .. }) + | Type::Builtin(BuiltinType::Char) + | Type::Pointer(_) + | Type::ConstPointer(_) + | Type::Handle(_) + | Type::Variant(_) => params.push(WasmType::I32), + + Type::Record(r) => match r.bitflags_repr() { + Some(repr) => params.push(WasmType::from(repr)), + None => params.push(WasmType::I32), + }, + + Type::Builtin(BuiltinType::S64) | Type::Builtin(BuiltinType::U64) => { + params.push(WasmType::I64) + } + + Type::Builtin(BuiltinType::F32) => params.push(WasmType::F32), + Type::Builtin(BuiltinType::F64) => params.push(WasmType::F64), + + Type::List(_) => { + params.push(WasmType::I32); + params.push(WasmType::I32); + } + } + } + + for param in self.results.iter() { + match &**param.tref.type_() { + Type::Builtin(BuiltinType::S8) + | Type::Builtin(BuiltinType::U8 { .. }) + | Type::Builtin(BuiltinType::S16) + | Type::Builtin(BuiltinType::U16) + | Type::Builtin(BuiltinType::S32) + | Type::Builtin(BuiltinType::U32 { .. }) + | Type::Builtin(BuiltinType::Char) + | Type::Pointer(_) + | Type::ConstPointer(_) + | Type::Handle(_) => results.push(WasmType::I32), + + Type::Builtin(BuiltinType::S64) | Type::Builtin(BuiltinType::U64) => { + results.push(WasmType::I64) + } + + Type::Builtin(BuiltinType::F32) => results.push(WasmType::F32), + Type::Builtin(BuiltinType::F64) => results.push(WasmType::F64), + + Type::Record(r) => match r.bitflags_repr() { + Some(repr) => results.push(WasmType::from(repr)), + None => unreachable!(), + }, + Type::List(_) => unreachable!(), + + Type::Variant(v) => { + results.push(match v.tag_repr { + IntRepr::U64 => WasmType::I64, + IntRepr::U32 | IntRepr::U16 | IntRepr::U8 => WasmType::I32, + }); + if v.is_enum() { + continue; + } + // return pointer + if let Some(ty) = &v.cases[0].tref { + match &**ty.type_() { + Type::Record(r) if r.is_tuple() => { + for _ in 0..r.members.len() { + params.push(WasmType::I32); + } + } + _ => params.push(WasmType::I32), + } + } + } + } + } + (params, results) + } + + /// Generates an abstract sequence of instructions which represents this + /// function being adapted as an imported function. + /// + /// The instructions here, when executed, will emulate a language with + /// interface types calling the concrete wasm implementation. The parameters + /// for the returned instruction sequence are the language's own + /// interface-types parameters. One instruction in the instruction stream + /// will be a `Call` which represents calling the actual raw wasm function + /// signature. + /// + /// This function is useful, for example, if you're building a language + /// generator for WASI bindings. This will document how to translate + /// language-specific values into the wasm types to call a WASI function, + /// and it will also automatically convert the results of the WASI function + /// back to a language-specific value. + pub fn call_wasm(&self, module: &Id, bindgen: &mut impl Bindgen) { + assert_eq!(self.abi, Abi::Preview1); + Generator { + bindgen, + operands: vec![], + results: vec![], + stack: vec![], + } + .call_wasm(module, self); + } + + /// This is the dual of [`InterfaceFunc::call_wasm`], except that instead of + /// calling a wasm signature it generates code to come from a wasm signature + /// and call an interface types signature. + pub fn call_interface(&self, module: &Id, bindgen: &mut impl Bindgen) { + assert_eq!(self.abi, Abi::Preview1); + Generator { + bindgen, + operands: vec![], + results: vec![], + stack: vec![], + } + .call_interface(module, self); + } +} + +struct Generator<'a, B: Bindgen> { + bindgen: &'a mut B, + operands: Vec, + results: Vec, + stack: Vec, +} + +impl Generator<'_, B> { + fn call_wasm(&mut self, module: &Id, func: &InterfaceFunc) { + // Translate all parameters which are interface values by lowering them + // to their wasm types. + for (nth, param) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(¶m.tref, None); + } + + // If necessary for our ABI, insert return pointers for any returned + // values through a result. + assert!(func.results.len() < 2); + if let Some(result) = func.results.get(0) { + self.prep_return_pointer(&result.tref.type_()); + } + + let (params, results) = func.wasm_signature(); + self.emit(&Instruction::CallWasm { + module: module.as_str(), + name: func.name.as_str(), + params: ¶ms, + results: &results, + }); + + // Lift the return value if one is present. + if let Some(result) = func.results.get(0) { + self.lift(&result.tref, true); + } + + self.emit(&Instruction::Return { + amt: func.results.len(), + }); + } + + fn call_interface(&mut self, module: &Id, func: &InterfaceFunc) { + // Lift all wasm parameters into interface types first. + // + // Note that consuming arguments is somewhat janky right now by manually + // giving lists a second argument for their length. In the future we'll + // probably want to refactor the `lift` function to internally know how + // to consume arguments. + let mut nth = 0; + for param in func.params.iter() { + self.emit(&Instruction::GetArg { nth }); + nth += 1; + if let Type::List(_) = &**param.tref.type_() { + self.emit(&Instruction::GetArg { nth }); + nth += 1; + } + self.lift(¶m.tref, false); + } + + self.emit(&Instruction::CallInterface { + module: module.as_str(), + func, + }); + + // Like above the current ABI only has at most one result, so lower it + // here if necessary. + if let Some(result) = func.results.get(0) { + self.lower(&result.tref, Some(&mut nth)); + } + + let (_params, results) = func.wasm_signature(); + self.emit(&Instruction::Return { amt: results.len() }); + } + + fn emit(&mut self, inst: &Instruction<'_>) { + self.operands.clear(); + self.results.clear(); + + let operands_len = inst.operands_len(); + assert!( + self.stack.len() >= operands_len, + "not enough operands on stack for {:?}", + inst + ); + self.operands + .extend(self.stack.drain((self.stack.len() - operands_len)..)); + self.results.reserve(inst.results_len()); + + self.bindgen + .emit(inst, &mut self.operands, &mut self.results); + + assert_eq!( + self.results.len(), + inst.results_len(), + "{:?} expected {} results, got {}", + inst, + inst.results_len(), + self.results.len() + ); + self.stack.extend(self.results.drain(..)); + } + + fn lower(&mut self, ty: &TypeRef, retptr: Option<&mut usize>) { + use Instruction::*; + match &**ty.type_() { + Type::Builtin(BuiltinType::S8) => self.emit(&I32FromS8), + Type::Builtin(BuiltinType::U8 { lang_c_char: true }) => self.emit(&I32FromChar8), + Type::Builtin(BuiltinType::U8 { lang_c_char: false }) => self.emit(&I32FromU8), + Type::Builtin(BuiltinType::S16) => self.emit(&I32FromS16), + Type::Builtin(BuiltinType::U16) => self.emit(&I32FromU16), + Type::Builtin(BuiltinType::S32) => self.emit(&I32FromS32), + Type::Builtin(BuiltinType::U32 { + lang_ptr_size: true, + }) => self.emit(&I32FromUsize), + Type::Builtin(BuiltinType::U32 { + lang_ptr_size: false, + }) => self.emit(&I32FromU32), + Type::Builtin(BuiltinType::S64) => self.emit(&I64FromS64), + Type::Builtin(BuiltinType::U64) => self.emit(&I64FromU64), + Type::Builtin(BuiltinType::Char) => self.emit(&I32FromChar), + Type::Pointer(_) => self.emit(&I32FromPointer), + Type::ConstPointer(_) => self.emit(&I32FromConstPointer), + Type::Handle(_) => self.emit(&I32FromHandle { + ty: match ty { + TypeRef::Name(ty) => ty, + _ => unreachable!(), + }, + }), + Type::Record(r) => { + let ty = match ty { + TypeRef::Name(ty) => ty, + _ => unreachable!(), + }; + match r.bitflags_repr() { + Some(IntRepr::U64) => self.emit(&I64FromBitflags { ty }), + Some(_) => self.emit(&I32FromBitflags { ty }), + None => self.emit(&AddrOf), + } + } + Type::Variant(v) => { + // Enum-like variants are simply lowered to their discriminant. + if v.is_enum() { + return self.emit(&EnumLower { + ty: match ty { + TypeRef::Name(n) => n, + _ => unreachable!(), + }, + }); + } + + // If this variant is in the return position then it's special, + // otherwise it's an argument and we just pass the address. + let retptr = match retptr { + Some(ptr) => ptr, + None => return self.emit(&AddrOf), + }; + + // For the return position we emit some blocks to lower the + // ok/err payloads which means that in the ok branch we're + // storing to out-params and in the err branch we're simply + // lowering the error enum. + // + // Note that this is all very specific to the current WASI ABI. + let (ok, err) = v.as_expected().unwrap(); + self.bindgen.push_block(); + if let Some(ok) = ok { + self.emit(&VariantPayload); + let store = |me: &mut Self, ty: &TypeRef, n| { + me.emit(&GetArg { nth: *retptr + n }); + match ty { + TypeRef::Name(ty) => me.emit(&Store { ty }), + _ => unreachable!(), + } + }; + match &**ok.type_() { + Type::Record(r) if r.is_tuple() => { + self.emit(&TupleLower { + amt: r.members.len(), + }); + // Note that `rev()` is used here due to the order + // that tuples are pushed onto the stack and how we + // consume the last item first from the stack. + for (i, member) in r.members.iter().enumerate().rev() { + store(self, &member.tref, i); + } + } + _ => store(self, ok, 0), + } + }; + self.bindgen.finish_block(None); + + self.bindgen.push_block(); + let err_expr = if let Some(ty) = err { + self.emit(&VariantPayload); + self.lower(ty, None); + Some(self.stack.pop().unwrap()) + } else { + None + }; + self.bindgen.finish_block(err_expr); + + self.emit(&ResultLower { ok, err }); + } + Type::Builtin(BuiltinType::F32) => self.emit(&F32FromIf32), + Type::Builtin(BuiltinType::F64) => self.emit(&F64FromIf64), + Type::List(_) => self.emit(&ListPointerLength), + } + } + + fn prep_return_pointer(&mut self, ty: &Type) { + // Return pointers are only needed for `Result`... + let variant = match ty { + Type::Variant(v) => v, + _ => return, + }; + // ... and only if `T` is actually present in `Result` + let ok = match &variant.cases[0].tref { + Some(t) => t, + None => return, + }; + + // Tuples have each individual item in a separate return pointer while + // all other types go through a singular return pointer. + let mut n = 0; + let mut prep = |ty: &TypeRef| { + match ty { + TypeRef::Name(ty) => self.bindgen.allocate_space(n, ty), + _ => unreachable!(), + } + self.emit(&Instruction::ReturnPointerGet { n }); + n += 1; + }; + match &**ok.type_() { + Type::Record(r) if r.is_tuple() => { + for member in r.members.iter() { + prep(&member.tref); + } + } + _ => prep(ok), + } + } + + // Note that in general everything in this function is the opposite of the + // `lower` function above. This is intentional and should be kept this way! + fn lift(&mut self, ty: &TypeRef, is_return: bool) { + use Instruction::*; + match &**ty.type_() { + Type::Builtin(BuiltinType::S8) => self.emit(&S8FromI32), + Type::Builtin(BuiltinType::U8 { lang_c_char: true }) => self.emit(&Char8FromI32), + Type::Builtin(BuiltinType::U8 { lang_c_char: false }) => self.emit(&U8FromI32), + Type::Builtin(BuiltinType::S16) => self.emit(&S16FromI32), + Type::Builtin(BuiltinType::U16) => self.emit(&U16FromI32), + Type::Builtin(BuiltinType::S32) => self.emit(&S32FromI32), + Type::Builtin(BuiltinType::U32 { + lang_ptr_size: true, + }) => self.emit(&UsizeFromI32), + Type::Builtin(BuiltinType::U32 { + lang_ptr_size: false, + }) => self.emit(&U32FromI32), + Type::Builtin(BuiltinType::S64) => self.emit(&S64FromI64), + Type::Builtin(BuiltinType::U64) => self.emit(&U64FromI64), + Type::Builtin(BuiltinType::Char) => self.emit(&CharFromI32), + Type::Builtin(BuiltinType::F32) => self.emit(&If32FromF32), + Type::Builtin(BuiltinType::F64) => self.emit(&If64FromF64), + Type::Pointer(ty) => self.emit(&PointerFromI32 { ty }), + Type::ConstPointer(ty) => self.emit(&ConstPointerFromI32 { ty }), + Type::Handle(_) => self.emit(&HandleFromI32 { + ty: match ty { + TypeRef::Name(ty) => ty, + _ => unreachable!(), + }, + }), + Type::Variant(v) => { + if v.is_enum() { + return self.emit(&EnumLift { + ty: match ty { + TypeRef::Name(n) => n, + _ => unreachable!(), + }, + }); + } else if !is_return { + return self.emit(&Load { + ty: match ty { + TypeRef::Name(n) => n, + _ => unreachable!(), + }, + }); + } + + let (ok, err) = v.as_expected().unwrap(); + self.bindgen.push_block(); + let ok_expr = if let Some(ok) = ok { + let mut n = 0; + let mut load = |ty: &TypeRef| { + self.emit(&ReturnPointerGet { n }); + n += 1; + match ty { + TypeRef::Name(ty) => self.emit(&Load { ty }), + _ => unreachable!(), + } + }; + match &**ok.type_() { + Type::Record(r) if r.is_tuple() => { + for member in r.members.iter() { + load(&member.tref); + } + self.emit(&TupleLift { + amt: r.members.len(), + }); + } + _ => load(ok), + } + Some(self.stack.pop().unwrap()) + } else { + None + }; + self.bindgen.finish_block(ok_expr); + + self.bindgen.push_block(); + let err_expr = if let Some(ty) = err { + self.emit(&ReuseReturn); + self.lift(ty, false); + Some(self.stack.pop().unwrap()) + } else { + None + }; + self.bindgen.finish_block(err_expr); + + self.emit(&ResultLift); + } + Type::Record(r) => { + let ty = match ty { + TypeRef::Name(ty) => ty, + _ => unreachable!(), + }; + match r.bitflags_repr() { + Some(IntRepr::U64) => self.emit(&BitflagsFromI64 { ty }), + Some(_) => self.emit(&BitflagsFromI32 { ty }), + None => self.emit(&Load { ty }), + } + } + Type::List(ty) => self.emit(&ListFromPointerLength { ty }), + } + } +} diff --git a/tools/witx/src/ast.rs b/tools/witx/src/ast.rs index 7d34b5f..ee985d3 100644 --- a/tools/witx/src/ast.rs +++ b/tools/witx/src/ast.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +use crate::Abi; use std::collections::{HashMap, HashSet}; use std::rc::{Rc, Weak}; @@ -63,13 +63,27 @@ impl Document { _ => None, }) } - /// All of the (unique) types used as the first result value of a function. + /// All of the (unique) types used as "err" variant of results returned from + /// functions. pub fn error_types<'a>(&'a self) -> impl Iterator + 'a { let errors: HashSet = self .modules() .flat_map(|m| { m.funcs() - .filter_map(|f| f.results.get(0).as_ref().map(|r| r.tref.clone())) + .filter_map(|f| { + if f.results.len() == 1 { + Some(f.results[0].tref.type_().clone()) + } else { + None + } + }) + .filter_map(|t| match &*t { + Type::Variant(v) => { + let (_ok, err) = v.as_expected()?; + Some(err?.clone()) + } + _ => None, + }) .collect::>() }) .collect(); @@ -162,10 +176,17 @@ pub enum TypeRef { } impl TypeRef { - pub fn type_(&self) -> Rc { + pub fn type_(&self) -> &Rc { match self { TypeRef::Name(named) => named.type_(), - TypeRef::Value(ref v) => v.clone(), + TypeRef::Value(v) => v, + } + } + + pub fn named(&self) -> bool { + match self { + TypeRef::Name(_) => true, + TypeRef::Value(_) => false, } } } @@ -178,23 +199,42 @@ pub struct NamedType { } impl NamedType { - pub fn type_(&self) -> Rc { + pub fn type_(&self) -> &Rc { self.tref.type_() } } +/// Structure of all possible interface types. +/// +/// Note that this is intended to match the interface types proposal itself. +/// Currently this is relatively close to that with just a few `*.witx` +/// extensions for now. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { + /// A structure with named field. Record(RecordDatatype), + /// An enumeration where a value is one of a number of variants. Variant(Variant), + /// A "handle" which is an un-forgeable reference. Today this is an `i32` + /// where a module can't forge and use integers it was not already given + /// access to. Handle(HandleDatatype), + /// A list of a type, stored in linear memory. + /// + /// Note that lists of `char` are specialized to indicate strings. List(TypeRef), + /// A `witx`-specific type representing a raw mutable pointer into linear + /// memory Pointer(TypeRef), + /// A `witx`-specific type representing a raw const pointer into linear + /// memory ConstPointer(TypeRef), + /// A builtin base-case type. Builtin(BuiltinType), } impl Type { + /// Returns a human-readable string to describe this type. pub fn kind(&self) -> &'static str { use Type::*; match self { @@ -215,6 +255,7 @@ pub enum BuiltinType { /// /// Same as the Rust language's `char` type. Char, + /// An 8-bit unsigned integer. U8 { /// Indicates whether this type is intended to represent the `char` /// type in the C language. The C `char` type is often unsigned, but @@ -226,7 +267,9 @@ pub enum BuiltinType { /// pointer` to hint that it's pointing to unicode string data as well. lang_c_char: bool, }, + /// A 16-bit unsigned integer. U16, + /// A 32-bit unsigned integer. U32 { /// Indicates that this 32-bit value should actually be considered a /// pointer-like value in language bindings. At the interface types @@ -238,12 +281,19 @@ pub enum BuiltinType { /// argument or return-value is pointer-like. lang_ptr_size: bool, }, + /// A 64-bit unsigned integer. U64, + /// An 8-bit signed integer S8, + /// A 16-bit signed integer S16, + /// A 32-bit signed integer S32, + /// A 64-bit signed integer S64, + /// A 32-bit floating point value. F32, + /// A 64-bit floating point value. F64, } @@ -268,11 +318,37 @@ impl IntRepr { } } +/// A struct-like value with named fields. +/// +/// Records map to `struct`s in most languages where this is a type with a +/// number of named fields that all have their own particular type. Field order +/// dictates layout in memory. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RecordDatatype { + /// A hint as to what this record might be. + /// + /// Note that in the future this will only be a hint, not a control of the + /// actual representation itself. At this time though the record layout of + /// bitflags is different from other types. + pub kind: RecordKind, + + /// A list of named fields for this record. pub members: Vec, } +/// Different kinds of records used for hinting various language-specific types. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum RecordKind { + /// A tuple where the name of all fields are consecutive integers starting + /// at "0". + Tuple, + /// A record where all fields are `bool`s. Currently represented as an + /// integer with bits set or not set. + Bitflags(IntRepr), + /// All other structures. + Other, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RecordMember { pub name: Id, @@ -280,16 +356,98 @@ pub struct RecordMember { pub docs: String, } +impl RecordDatatype { + pub fn is_tuple(&self) -> bool { + match self.kind { + RecordKind::Tuple => true, + _ => false, + } + } + + pub fn bitflags_repr(&self) -> Option { + match self.kind { + RecordKind::Bitflags(i) => Some(i), + _ => None, + } + } +} + +/// A type which represents how values can be one of a set of possible cases. +/// +/// This type maps to an `enum` in languages like Rust, but doesn't have an +/// equivalent in languages like JS or C. The closest analog in C is a tagged +/// union, but a `Variant` is always consistent whereas a tagged union in C +/// could be mis-tagged or such. +/// +/// Variants are used to represent one of a possible set of types. For example +/// an enum-like variant, a result that is either success or failure, or even a +/// simple `bool`. Variants are primarily used heavily with various kinds of +/// shorthands in the `*.witx` format to represent idioms in languages. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Variant { + /// The bit representation of the width of this variant's tag when the + /// variant is stored in memory. pub tag_repr: IntRepr, + /// The possible cases that values of this variant type can take. pub cases: Vec, } +impl Variant { + /// If this variant looks like an `expected` shorthand, return the ok/err + /// types associated with this result. + /// + /// Only matches variants fo the form: + /// + /// ```text + /// (variant + /// (case "ok" ok?) + /// (case "err" err?)) + /// ``` + pub fn as_expected(&self) -> Option<(Option<&TypeRef>, Option<&TypeRef>)> { + if self.cases.len() != 2 { + return None; + } + if self.cases[0].name != "ok" { + return None; + } + if self.cases[1].name != "err" { + return None; + } + Some((self.cases[0].tref.as_ref(), self.cases[1].tref.as_ref())) + } + + /// Returns whether this variant type is "bool-like" meaning that it matches + /// this type: + /// + /// ```text + /// (variant + /// (case "false") + /// (case "true")) + /// ``` + pub fn is_bool(&self) -> bool { + self.cases.len() == 2 + && self.cases[0].name == "false" + && self.cases[1].name == "true" + && self.cases[0].tref.is_none() + && self.cases[1].tref.is_none() + } + + /// Returns whether this variant type is "enum-like" meaning that all of its + /// cases have no payload associated with them. + pub fn is_enum(&self) -> bool { + self.cases.iter().all(|c| c.tref.is_none()) + } +} + +/// One of a number of possible types that a `Variant` can take. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Case { + /// The name of this case and how to identify it. pub name: Id, + /// An optional payload type for this case and data that can be associated + /// with it. pub tref: Option, + /// Documentation for this case. pub docs: String, } @@ -409,6 +567,7 @@ pub enum ModuleImportVariant { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct InterfaceFunc { + pub abi: Abi, pub name: Id, pub params: Vec, pub results: Vec, @@ -420,16 +579,9 @@ pub struct InterfaceFunc { pub struct InterfaceFuncParam { pub name: Id, pub tref: TypeRef, - pub position: InterfaceFuncParamPosition, pub docs: String, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum InterfaceFuncParamPosition { - Param(usize), - Result(usize), -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Constant { pub ty: Id, diff --git a/tools/witx/src/coretypes.rs b/tools/witx/src/coretypes.rs deleted file mode 100644 index 8269903..0000000 --- a/tools/witx/src/coretypes.rs +++ /dev/null @@ -1,173 +0,0 @@ -use crate::{BuiltinType, IntRepr, InterfaceFunc, InterfaceFuncParam, Type}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -/// Enumerates the types permitted for function arguments in the WebAssembly spec -pub enum AtomType { - I32, - I64, - F32, - F64, -} - -impl From for AtomType { - fn from(i: IntRepr) -> AtomType { - match i { - IntRepr::U8 | IntRepr::U16 | IntRepr::U32 => AtomType::I32, - IntRepr::U64 => AtomType::I64, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -/// Enumerates the strategies which may be used to pass a datatype as an argument -pub enum TypePassedBy { - /// Pass by value specifies the AtomType used to represent that value - Value(AtomType), - /// Pass by a pointer into linear memory - Pointer, - /// Pass by a pointer and length pair, into linear memory - PointerLengthPair, -} - -impl Type { - /// Determine the simplest strategy by which a type may be passed. Value always preferred over - /// Pointer. - pub fn passed_by(&self) -> TypePassedBy { - match self { - Type::Builtin(b) => match b { - BuiltinType::U8 { .. } - | BuiltinType::U16 - | BuiltinType::U32 { .. } - | BuiltinType::S8 - | BuiltinType::S16 - | BuiltinType::S32 - | BuiltinType::Char => TypePassedBy::Value(AtomType::I32), - BuiltinType::U64 | BuiltinType::S64 => TypePassedBy::Value(AtomType::I64), - BuiltinType::F32 => TypePassedBy::Value(AtomType::F32), - BuiltinType::F64 => TypePassedBy::Value(AtomType::F64), - }, - Type::List { .. } => TypePassedBy::PointerLengthPair, - Type::Pointer { .. } | Type::ConstPointer { .. } => TypePassedBy::Value(AtomType::I32), - Type::Record { .. } => TypePassedBy::Pointer, - Type::Variant(v) => { - if v.cases.iter().all(|c| c.tref.is_none()) { - TypePassedBy::Value(v.tag_repr.into()) - } else { - TypePassedBy::Pointer - } - } - Type::Handle { .. } => TypePassedBy::Value(AtomType::I32), - } - } -} - -/// A parameter in the WebAssembly type of a function. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CoreParamType { - /// The interface function parameter to which this - pub param: InterfaceFuncParam, - /// The relationship of the WebAssembly parameter to the function interface parameter - pub signifies: CoreParamSignifies, -} - -impl CoreParamType { - /// Representation of the WebAssembly parameter. This is the type that will appear - /// in the function's WebAssembly type signature. - pub fn repr(&self) -> AtomType { - self.signifies.repr() - } -} - -/// Enumerates the sort of relationship an WebAssembly parameter to an interface function -/// parameter. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CoreParamSignifies { - /// Core type represents the value using an AtomType - Value(AtomType), - /// Core type represents a pointer into linear memory - PointerTo, - /// Core type represents a length of a region of linear memory - LengthOf, -} - -impl CoreParamSignifies { - /// Representation of the WebAssembly parameter. - pub fn repr(&self) -> AtomType { - match self { - CoreParamSignifies::Value(a) => *a, - CoreParamSignifies::PointerTo | CoreParamSignifies::LengthOf => AtomType::I32, - } - } -} - -impl InterfaceFuncParam { - /// Gives the WebAssembly type that corresponds to passing this interface func parameter by value. - /// Not all types can be passed by value: those which cannot return None - pub fn pass_by_value(&self) -> Option { - match self.tref.type_().passed_by() { - TypePassedBy::Value(atom) => Some(CoreParamType { - signifies: CoreParamSignifies::Value(atom), - param: self.clone(), - }), - TypePassedBy::Pointer | TypePassedBy::PointerLengthPair => None, - } - } - - /// Gives the WebAssembly types that correspond to passing this interface func parameter - /// by reference. Some types are passed by reference using a single pointer, others - /// require both a pointer and length. - pub fn pass_by_reference(&self) -> Vec { - match self.tref.type_().passed_by() { - TypePassedBy::Value(_) | TypePassedBy::Pointer => vec![CoreParamType { - signifies: CoreParamSignifies::PointerTo, - param: self.clone(), - }], - TypePassedBy::PointerLengthPair => vec![ - CoreParamType { - signifies: CoreParamSignifies::PointerTo, - param: self.clone(), - }, - CoreParamType { - signifies: CoreParamSignifies::LengthOf, - param: self.clone(), - }, - ], - } - } -} - -/// Describes the WebAssembly signature of a function -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CoreFuncType { - pub args: Vec, - pub ret: Option, -} - -impl InterfaceFunc { - /// Get the WebAssembly type signature for this interface function - pub fn core_type(&self) -> CoreFuncType { - let mut results = self.results.iter(); - // The ret value is the first result (if there is one), passed - // by value. - let ret = results.next().map(|param| { - param - .pass_by_value() - .expect("validation ensures first result can be passed by value") - }); - let args = self - .params - .iter() - .flat_map(|param| { - // interface function parameters are passed by value if possible, - // and fall back on passing by reference. - param - .pass_by_value() - .map(|ptype| vec![ptype]) - .unwrap_or_else(|| param.pass_by_reference()) - }) - // Then, the remaining results are passed by reference. - .chain(results.flat_map(|param| param.pass_by_reference())) - .collect(); - CoreFuncType { args, ret } - } -} diff --git a/tools/witx/src/docs/ast.rs b/tools/witx/src/docs/ast.rs index 6048f73..a295a67 100644 --- a/tools/witx/src/docs/ast.rs +++ b/tools/witx/src/docs/ast.rs @@ -1,5 +1,5 @@ use super::{ - md::{MdFunc, MdHeading, MdNamedType, MdNodeRef, MdSection, MdType, ToMarkdown}, + md::{MdFunc, MdHeading, MdNamedType, MdNodeRef, MdSection, ToMarkdown}, Documentation, }; use crate::{ @@ -68,11 +68,13 @@ impl ToMarkdown for Document { impl ToMarkdown for TypeRef { fn generate(&self, node: MdNodeRef) { match self { - TypeRef::Value(v) => v.generate(node.clone()), + TypeRef::Value(v) => { + v.generate(node.clone()); + node.content_ref_mut::().ty = Some(format!("`{}`", self.type_name())); + } TypeRef::Name(n) => { - node.content_ref_mut::().r#type = Some(MdType::Alias { - r#type: n.name.as_str().to_owned(), - }) + node.content_ref_mut::().ty = + Some(format!("[`{0}`](#{0})", n.name.as_str().to_owned())); } } } @@ -90,26 +92,10 @@ impl ToMarkdown for Type { Self::Record(a) => a.generate(node.clone()), Self::Variant(a) => a.generate(node.clone()), Self::Handle(a) => a.generate(node.clone()), - Self::List(a) => { - node.content_ref_mut::().r#type = Some(MdType::List { - r#type: a.type_name().to_owned(), - }) - } - Self::Pointer(a) => { - node.content_ref_mut::().r#type = Some(MdType::Pointer { - r#type: a.type_name().to_owned(), - }) - } - Self::ConstPointer(a) => { - node.content_ref_mut::().r#type = Some(MdType::ConstPointer { - r#type: a.type_name().to_owned(), - }) - } - Self::Builtin(a) => { - node.content_ref_mut::().r#type = Some(MdType::Builtin { - repr: a.type_name().to_owned(), - }) - } + Self::List(_) => {} + Self::Pointer(_) => {} + Self::ConstPointer(_) => {} + Self::Builtin(_) => {} } } } @@ -128,21 +114,27 @@ impl ToMarkdown for RecordDatatype { } else { name.to_owned() }; + let (div, offset_desc) = if self.bitflags_repr().is_some() { + (4, "Bit") + } else { + (1, "Offset") + }; let n = node.new_child(MdNamedType::new( MdHeading::new_bullet(), id.as_str(), name, - format!("{}\nOffset: {}\n", &member.docs, &offset).as_str(), + format!("{}\n{}: {}\n", &member.docs, offset_desc, offset / div).as_str(), )); member.tref.generate(n.clone()); } - - node.content_ref_mut::().r#type = Some(MdType::Record); } } impl ToMarkdown for Variant { fn generate(&self, node: MdNodeRef) { + if self.is_bool() { + return; + } 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")); @@ -184,8 +176,6 @@ impl ToMarkdown for Variant { ty.generate(n.clone()); } } - - node.content_ref_mut::().r#type = Some(MdType::Variant); } } @@ -194,7 +184,6 @@ impl ToMarkdown for HandleDatatype { // TODO this needs more work let heading = heading_from_node(&node, 1); node.new_child(MdSection::new(heading, "Supertypes")); - node.content_ref_mut::().r#type = Some(MdType::Handle); } } @@ -312,16 +301,50 @@ impl TypeRef { pub fn type_name(&self) -> String { match self { TypeRef::Name(n) => n.name.as_str().to_string(), - TypeRef::Value(ref v) => match &**v { - Type::List(a) => match &*a.type_() { + TypeRef::Value(v) => match &**v { + Type::List(a) => match &**a.type_() { Type::Builtin(BuiltinType::Char) => "string".to_string(), _ => format!("List<{}>", a.type_name()), }, Type::Pointer(p) => format!("Pointer<{}>", p.type_name()), Type::ConstPointer(p) => format!("ConstPointer<{}>", p.type_name()), Type::Builtin(b) => b.type_name().to_string(), - Type::Record { .. } | Type::Variant { .. } | Type::Handle { .. } => { - unimplemented!("type_name of anonymous compound datatypes") + Type::Record(RecordDatatype { + kind: RecordKind::Tuple, + members, + }) => { + let mut ret = "(".to_string(); + for (i, member) in members.iter().enumerate() { + if i > 0 { + ret.push_str(", "); + } + ret.push_str(&member.tref.type_name()); + } + ret.push_str(")"); + ret + } + Type::Record { .. } => { + format!("Record") + } + Type::Handle { .. } => { + format!("Handle") + } + Type::Variant(v) => { + if let Some((ok, err)) = v.as_expected() { + let ok = match ok { + Some(ty) => ty.type_name(), + None => "()".to_string(), + }; + let err = match err { + Some(ty) => ty.type_name(), + None => "()".to_string(), + }; + format!("Result<{}, {}>", ok, err) + } else if v.is_bool() { + format!("bool") + } else { + format!("Variant") + } } }, } diff --git a/tools/witx/src/docs/md.rs b/tools/witx/src/docs/md.rs index d3d5b75..569b318 100644 --- a/tools/witx/src/docs/md.rs +++ b/tools/witx/src/docs/md.rs @@ -322,7 +322,7 @@ pub(super) struct MdNamedType { pub id: String, pub name: String, pub docs: String, - pub r#type: Option, + pub ty: Option, } impl MdNamedType { @@ -332,54 +332,11 @@ impl MdNamedType { id: id.as_ref().to_owned(), name: name.as_ref().to_owned(), docs: docs.as_ref().to_owned(), - r#type: None, + ty: None, } } } -/// Helper struct encapsulating the `TypeRef` value. -// TODO `MdType` should probably store `TypeRef` and recursively -// unwind itself into final `String` representation rather than -// being outright flattened. -#[derive(Debug)] -pub(super) enum MdType { - Record, - Variant, - List { r#type: String }, - Pointer { r#type: String }, - ConstPointer { r#type: String }, - Builtin { repr: String }, - Handle, - Alias { r#type: String }, -} - -impl fmt::Display for MdType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Record => f.write_fmt(format_args!(": Record"))?, - Self::Variant => f.write_fmt(format_args!(": Variant"))?, - Self::List { r#type } => { - if r#type == "char" { - f.write_str(": `string`")? - } else { - f.write_fmt(format_args!(": `List<{}>`", r#type))? - } - } - Self::Pointer { r#type } => f.write_fmt(format_args!(": `Pointer<{}>`", r#type))?, - Self::ConstPointer { r#type } => { - f.write_fmt(format_args!(": `ConstPointer<{}>`", r#type))? - } - Self::Builtin { repr } => f.write_fmt(format_args!(": `{}`", repr))?, - Self::Handle => {} - Self::Alias { r#type } => { - f.write_fmt(format_args!(": [`{tt}`](#{tt})", tt = r#type))? - } - }; - - Ok(()) - } -} - impl MdElement for MdNamedType { fn id(&self) -> Option<&str> { Some(&self.id) @@ -411,8 +368,8 @@ impl fmt::Display for MdNamedType { name = self.name, ))?; - if let Some(tt) = &self.r#type { - f.write_fmt(format_args!("{}", tt))?; + if let Some(tt) = &self.ty { + f.write_fmt(format_args!(": {}", tt))?; } writeln!(f, "\n{}", self.docs) diff --git a/tools/witx/src/layout.rs b/tools/witx/src/layout.rs index 2bcd104..6812f01 100644 --- a/tools/witx/src/layout.rs +++ b/tools/witx/src/layout.rs @@ -64,7 +64,10 @@ impl Layout for NamedType { impl Type { fn layout(&self, cache: &mut HashMap) -> SizeAlign { match &self { - Type::Record(s) => s.layout(cache), + Type::Record(s) => match s.bitflags_repr() { + Some(repr) => repr.mem_size_align(), + None => s.layout(cache), + }, Type::Variant(s) => s.mem_size_align(), Type::Handle(h) => h.mem_size_align(), Type::List { .. } => SizeAlign { size: 8, align: 4 }, // Pointer and Length @@ -122,8 +125,13 @@ impl RecordDatatype { impl Layout for RecordDatatype { fn mem_size_align(&self) -> SizeAlign { - let mut cache = HashMap::new(); - self.layout(&mut cache) + match self.bitflags_repr() { + Some(repr) => repr.mem_size_align(), + None => { + let mut cache = HashMap::new(); + self.layout(&mut cache) + } + } } } @@ -143,6 +151,18 @@ impl Layout for Variant { } } +impl Variant { + pub fn payload_offset(&self) -> usize { + let mut offset = self.tag_repr.mem_size_align().size; + for case in self.cases.iter() { + if let Some(payload) = &case.tref { + offset = offset.max(align_to(offset, payload.mem_size_align().align)); + } + } + offset + } +} + /// 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 2c876aa..f1cc7e2 100644 --- a/tools/witx/src/lib.rs +++ b/tools/witx/src/lib.rs @@ -1,7 +1,7 @@ +/// Map witx types to core (wasm standard) types +mod abi; /// Types describing a validated witx document mod ast; -/// Map witx types to core (wasm standard) types -mod coretypes; /// Render documentation mod docs; /// Interface for filesystem or mock IO @@ -23,8 +23,8 @@ mod toplevel; /// Validate declarations into ast mod validate; +pub use abi::*; 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}; diff --git a/tools/witx/src/parser.rs b/tools/witx/src/parser.rs index 19d1a03..33cf745 100644 --- a/tools/witx/src/parser.rs +++ b/tools/witx/src/parser.rs @@ -1,5 +1,4 @@ use crate::BuiltinType; -use wast::lexer::Comment; use wast::parser::{Parse, Parser, Peek, Result}; ///! Parser turns s-expressions into unvalidated syntax constructs. @@ -18,7 +17,6 @@ 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!(case); wast::custom_keyword!(char8); wast::custom_keyword!(char); @@ -27,6 +25,8 @@ mod kw { wast::custom_keyword!(f64); wast::custom_keyword!(field); wast::custom_keyword!(empty); + wast::custom_keyword!(error); + wast::custom_keyword!(expected); wast::custom_keyword!(flags); wast::custom_keyword!(handle); wast::custom_keyword!(list); @@ -37,12 +37,14 @@ mod kw { wast::custom_keyword!(r#enum = "enum"); wast::custom_keyword!(r#union = "union"); wast::custom_keyword!(r#use = "use"); + wast::custom_keyword!(repr); wast::custom_keyword!(s16); wast::custom_keyword!(s32); wast::custom_keyword!(s64); wast::custom_keyword!(s8); wast::custom_keyword!(string); wast::custom_keyword!(tag); + wast::custom_keyword!(tuple); wast::custom_keyword!(typename); wast::custom_keyword!(u16); wast::custom_keyword!(u32); @@ -50,6 +52,7 @@ mod kw { wast::custom_keyword!(u8); wast::custom_keyword!(usize); wast::custom_keyword!(variant); + wast::custom_keyword!(bool_ = "bool"); } mod annotation { @@ -136,9 +139,10 @@ impl<'a> Parse<'a> for CommentSyntax<'a> { None => break, }; cursor = c; - comments.push(match comment { - Comment::Block(s) => &s[2..s.len() - 2], - Comment::Line(s) => &s[2..], + comments.push(if comment.starts_with(";;") { + &comment[2..] + } else { + &comment[2..comment.len() - 2] }); } Ok((comments, cursor)) @@ -277,6 +281,8 @@ impl<'a> Parse<'a> for TypenameSyntax<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum TypedefSyntax<'a> { Enum(EnumSyntax<'a>), + Tuple(TupleSyntax<'a>), + Expected(ExpectedSyntax<'a>), Flags(FlagsSyntax<'a>), Record(RecordSyntax<'a>), Union(UnionSyntax<'a>), @@ -288,6 +294,7 @@ pub enum TypedefSyntax<'a> { Builtin(BuiltinType), Ident(wast::Id<'a>), String, + Bool, } impl<'a> Parse<'a> for TypedefSyntax<'a> { @@ -300,11 +307,18 @@ impl<'a> Parse<'a> for TypedefSyntax<'a> { } else if l.peek::() { parser.parse::()?; Ok(TypedefSyntax::String) + } else if l.peek::() { + parser.parse::()?; + Ok(TypedefSyntax::Bool) } else if l.peek::() { parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::() { Ok(TypedefSyntax::Enum(parser.parse()?)) + } else if l.peek::() { + Ok(TypedefSyntax::Tuple(parser.parse()?)) + } else if l.peek::() { + Ok(TypedefSyntax::Expected(parser.parse()?)) } else if l.peek::() { Ok(TypedefSyntax::Flags(parser.parse()?)) } else if l.peek::() { @@ -377,6 +391,48 @@ impl<'a> Parse<'a> for EnumSyntax<'a> { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TupleSyntax<'a> { + pub types: Vec>, +} + +impl<'a> Parse<'a> for TupleSyntax<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + let mut types = Vec::new(); + while !parser.is_empty() { + types.push(parser.parse()?); + } + Ok(TupleSyntax { types }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExpectedSyntax<'a> { + pub ok: Option>>, + pub err: Option>>, +} + +impl<'a> Parse<'a> for ExpectedSyntax<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + let ok = if !parser.is_empty() && !parser.peek2::() { + Some(Box::new(parser.parse()?)) + } else { + None + }; + let err = parser.parens(|p| { + p.parse::()?; + Ok(if p.is_empty() { + None + } else { + Some(Box::new(p.parse()?)) + }) + })?; + Ok(ExpectedSyntax { ok, err }) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConstSyntax<'a> { pub ty: wast::Id<'a>, @@ -397,17 +453,17 @@ impl<'a> Parse<'a> for ConstSyntax<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub struct FlagsSyntax<'a> { - pub bitflags_repr: Option, + pub repr: Option, pub flags: Vec>>, } impl<'a> Parse<'a> for FlagsSyntax<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; - let bitflags_repr = if parser.peek2::() { + let repr = if parser.peek2::() { Some(parser.parens(|p| { p.parse::()?; - p.parse::()?; + p.parse::()?; p.parse() })?) } else { @@ -417,10 +473,7 @@ impl<'a> Parse<'a> for FlagsSyntax<'a> { while !parser.is_empty() { flags.push(parser.parse()?); } - Ok(FlagsSyntax { - bitflags_repr, - flags, - }) + Ok(FlagsSyntax { repr, flags }) } } diff --git a/tools/witx/src/render.rs b/tools/witx/src/render.rs index ad551ae..a740e28 100644 --- a/tools/witx/src/render.rs +++ b/tools/witx/src/render.rs @@ -143,29 +143,56 @@ impl Type { impl RecordDatatype { pub fn to_sexpr(&self) -> SExpr { - let header = vec![SExpr::word("record")]; - let members = self - .members - .iter() - .map(|m| { - SExpr::docs( - &m.docs, - SExpr::Vec(vec![ - SExpr::word("field"), - m.name.to_sexpr(), - m.tref.to_sexpr(), - ]), - ) - }) - .collect::>(); - SExpr::Vec([header, members].concat()) + match self.kind { + RecordKind::Tuple => { + let mut tuple = vec![SExpr::word("tuple")]; + for m in self.members.iter() { + tuple.push(SExpr::docs(&m.docs, m.tref.to_sexpr())); + } + SExpr::Vec(tuple) + } + RecordKind::Bitflags(repr) => { + let mut flags = vec![SExpr::word("flags")]; + flags.push(SExpr::Vec(vec![ + SExpr::word("@witx"), + SExpr::word("repr"), + repr.to_sexpr(), + ])); + flags.extend( + self.members + .iter() + .map(|m| SExpr::docs(&m.docs, m.name.to_sexpr())), + ); + SExpr::Vec(flags) + } + RecordKind::Other => { + let header = vec![SExpr::word("record")]; + let members = self + .members + .iter() + .map(|m| { + SExpr::docs( + &m.docs, + SExpr::Vec(vec![ + SExpr::word("field"), + m.name.to_sexpr(), + m.tref.to_sexpr(), + ]), + ) + }) + .collect::>(); + SExpr::Vec([header, members].concat()) + } + } } } impl Variant { pub fn to_sexpr(&self) -> SExpr { let mut list = Vec::new(); - if self.cases.iter().all(|c| c.tref.is_none()) { + if self.is_bool() { + return SExpr::word("bool"); + } else if self.is_enum() { list.push(SExpr::word("enum")); list.push(SExpr::Vec(vec![ SExpr::word("@witx"), diff --git a/tools/witx/src/toplevel.rs b/tools/witx/src/toplevel.rs index 83e940c..257e6ed 100644 --- a/tools/witx/src/toplevel.rs +++ b/tools/witx/src/toplevel.rs @@ -107,11 +107,11 @@ mod test { .expect("parse"); let b_float = doc.typename(&Id::new("b_float")).unwrap(); - assert_eq!(*b_float.type_(), Type::Builtin(BuiltinType::F64)); + assert_eq!(**b_float.type_(), Type::Builtin(BuiltinType::F64)); let c_int = doc.typename(&Id::new("c_int")).unwrap(); assert_eq!( - *c_int.type_(), + **c_int.type_(), Type::Builtin(BuiltinType::U32 { lang_ptr_size: false }) @@ -133,7 +133,7 @@ mod test { let d_char = doc.typename(&Id::new("d_char")).unwrap(); assert_eq!( - *d_char.type_(), + **d_char.type_(), Type::Builtin(BuiltinType::U8 { lang_c_char: false }) ); } diff --git a/tools/witx/src/validate.rs b/tools/witx/src/validate.rs index ade0228..cf4d0ad 100644 --- a/tools/witx/src/validate.rs +++ b/tools/witx/src/validate.rs @@ -1,14 +1,14 @@ use crate::{ io::{Filesystem, WitxIo}, parser::{ - CommentSyntax, DeclSyntax, Documented, EnumSyntax, FlagsSyntax, HandleSyntax, - ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TypedefSyntax, UnionSyntax, - VariantSyntax, + CommentSyntax, DeclSyntax, Documented, EnumSyntax, ExpectedSyntax, FlagsSyntax, + HandleSyntax, ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TupleSyntax, TypedefSyntax, + UnionSyntax, VariantSyntax, }, - BuiltinType, Case, Constant, Definition, Document, Entry, HandleDatatype, Id, IntRepr, - InterfaceFunc, InterfaceFuncParam, InterfaceFuncParamPosition, Location, Module, - ModuleDefinition, ModuleEntry, ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, - RecordMember, Type, TypePassedBy, TypeRef, Variant, + Abi, BuiltinType, Case, Constant, Definition, Document, Entry, HandleDatatype, Id, IntRepr, + InterfaceFunc, InterfaceFuncParam, Location, Module, ModuleDefinition, ModuleEntry, + ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordKind, RecordMember, Type, + TypeRef, Variant, }; use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -39,8 +39,8 @@ pub enum ValidationError { repr: BuiltinType, location: Location, }, - #[error("First result type must be pass-by-value")] - InvalidFirstResultType { location: Location }, + #[error("ABI error: {reason}")] + Abi { reason: String, location: Location }, #[error("Anonymous structured types (struct, union, enum, flags, handle) are not permitted")] AnonymousRecord { location: Location }, #[error("Union expected {expected} variants, found {found}")] @@ -67,7 +67,7 @@ impl ValidationError { | WrongKindName { location, .. } | Recursive { location, .. } | InvalidRepr { location, .. } - | InvalidFirstResultType { location, .. } + | Abi { location, .. } | AnonymousRecord { location, .. } | UnionSizeMismatch { location, .. } | InvalidUnionField { location, .. } @@ -131,6 +131,7 @@ pub struct DocValidation { scope: IdentValidation, entries: HashMap, constant_scopes: HashMap, + bool_ty: TypeRef, } pub struct DocValidationScope<'a> { @@ -145,6 +146,21 @@ impl DocValidation { scope: IdentValidation::new(), entries: HashMap::new(), constant_scopes: HashMap::new(), + bool_ty: TypeRef::Value(Rc::new(Type::Variant(Variant { + tag_repr: IntRepr::U32, + cases: vec![ + Case { + name: Id::new("false"), + tref: None, + docs: String::new(), + }, + Case { + name: Id::new("true"), + tref: None, + docs: String::new(), + }, + ], + }))), } } @@ -203,24 +219,6 @@ impl DocValidationScope<'_> { .entries .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) => { @@ -304,7 +302,11 @@ impl DocValidationScope<'_> { } other => Ok(TypeRef::Value(Rc::new(match other { TypedefSyntax::Enum(syntax) => Type::Variant(self.validate_enum(&syntax, span)?), - TypedefSyntax::Flags(syntax) => self.validate_flags(&syntax, span)?, + TypedefSyntax::Tuple(syntax) => Type::Record(self.validate_tuple(&syntax, span)?), + TypedefSyntax::Expected(syntax) => { + Type::Variant(self.validate_expected(&syntax, span)?) + } + TypedefSyntax::Flags(syntax) => Type::Record(self.validate_flags(&syntax, span)?), TypedefSyntax::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?), TypedefSyntax::Union(syntax) => Type::Variant(self.validate_union(&syntax, span)?), TypedefSyntax::Variant(syntax) => { @@ -324,6 +326,7 @@ impl DocValidationScope<'_> { TypedefSyntax::String => { Type::List(TypeRef::Value(Rc::new(Type::Builtin(BuiltinType::Char)))) } + TypedefSyntax::Bool => return Ok(self.doc.bool_ty.clone()), TypedefSyntax::Ident { .. } => unreachable!(), }))), } @@ -356,17 +359,83 @@ impl DocValidationScope<'_> { Ok(Variant { tag_repr, cases }) } + fn validate_tuple( + &self, + syntax: &TupleSyntax, + span: wast::Span, + ) -> Result { + let members = syntax + .types + .iter() + .enumerate() + .map(|(i, ty)| { + Ok(RecordMember { + name: Id::new(i.to_string()), + tref: self.validate_datatype(ty, false, span)?, + docs: String::new(), + }) + }) + .collect::, _>>()?; + + Ok(RecordDatatype { + kind: RecordKind::Tuple, + members, + }) + } + + fn validate_expected( + &self, + syntax: &ExpectedSyntax, + span: wast::Span, + ) -> Result { + let ok_ty = match &syntax.ok { + Some(ok) => Some(self.validate_datatype(ok, false, span)?), + None => None, + }; + let err_ty = match &syntax.err { + Some(err) => Some(self.validate_datatype(err, false, span)?), + None => None, + }; + Ok(Variant { + tag_repr: IntRepr::U32, + cases: vec![ + Case { + name: Id::new("ok"), + tref: ok_ty, + docs: String::new(), + }, + Case { + name: Id::new("err"), + tref: err_ty, + docs: String::new(), + }, + ], + }) + } + fn validate_flags( &self, syntax: &FlagsSyntax, span: wast::Span, - ) -> 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!(); - } + ) -> Result { + let repr = match syntax.repr { + Some(ty) => self.validate_int_repr(&ty, span)?, + None => IntRepr::U32, + }; + let mut flags_scope = IdentValidation::new(); + let mut members = Vec::new(); + for flag in syntax.flags.iter() { + let name = flags_scope.introduce(flag.item.name(), self.location(flag.item.span()))?; + let docs = flag.comments.docs(); + members.push(RecordMember { + name, + docs, + tref: self.doc.bool_ty.clone(), + }); + } + Ok(RecordDatatype { + kind: RecordKind::Bitflags(repr), + members, }) } @@ -388,7 +457,10 @@ impl DocValidationScope<'_> { }) .collect::, _>>()?; - Ok(RecordDatatype { members }) + Ok(RecordDatatype { + kind: RecordKind::Other, + members, + }) } fn validate_union( @@ -497,7 +569,7 @@ impl DocValidationScope<'_> { Some(tag) => self.validate_datatype(tag, false, span)?, None => return Ok((IntRepr::U32, None)), }; - match &*ty.type_() { + match &**ty.type_() { Type::Variant(e) => { let mut names = Vec::new(); for c in e.cases.iter() { @@ -594,8 +666,7 @@ impl<'a> ModuleValidation<'a> { let params = syntax .params .iter() - .enumerate() - .map(|(ix, f)| { + .map(|f| { Ok(InterfaceFuncParam { name: argnames.introduce( f.item.name.name(), @@ -606,7 +677,6 @@ impl<'a> ModuleValidation<'a> { false, f.item.name.span(), )?, - position: InterfaceFuncParamPosition::Param(ix), docs: f.comments.docs(), }) }) @@ -614,33 +684,29 @@ impl<'a> ModuleValidation<'a> { let results = syntax .results .iter() - .enumerate() - .map(|(ix, f)| { + .map(|f| { let tref = self.doc .validate_datatype(&f.item.type_, false, f.item.name.span())?; - if ix == 0 { - match tref.type_().passed_by() { - TypePassedBy::Value(_) => {} - _ => Err(ValidationError::InvalidFirstResultType { - location: self.doc.location(f.item.name.span()), - })?, - } - } Ok(InterfaceFuncParam { name: argnames.introduce( f.item.name.name(), self.doc.location(f.item.name.span()), )?, tref, - position: InterfaceFuncParamPosition::Result(ix), docs: f.comments.docs(), }) }) .collect::, _>>()?; let noreturn = syntax.noreturn; - + let abi = Abi::Preview1; + abi.validate(¶ms, &results) + .map_err(|reason| ValidationError::Abi { + reason, + location: self.doc.location(syntax.export_loc), + })?; let rc_func = Rc::new(InterfaceFunc { + abi, name: name.clone(), params, results, diff --git a/tools/witx/tests/witxt.rs b/tools/witx/tests/witxt.rs index 1056947..7c002fe 100644 --- a/tools/witx/tests/witxt.rs +++ b/tools/witx/tests/witxt.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use std::str; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use wast::parser::{self, Parse, ParseBuffer, Parser}; -use witx::{Documentation, Representable}; +use witx::{Documentation, Instruction, Representable, WasmType}; fn main() { let tests = find_tests(); @@ -192,6 +192,36 @@ impl WitxtRunner<'_> { } } } + WitxtDirective::AssertAbi { + witx, + wasm, + interface, + wasm_signature: (wasm_params, wasm_results), + .. + } => { + let doc = witx.document(contents, test)?; + let module = doc.modules().next().ok_or_else(|| anyhow!("no modules"))?; + let func = module.funcs().next().ok_or_else(|| anyhow!("no funcs"))?; + + let (params, results) = func.wasm_signature(); + if params != wasm_params { + bail!("expected params {:?}, found {:?}", wasm_params, params); + } + if results != wasm_results { + bail!("expected results {:?}, found {:?}", wasm_results, results); + } + + let mut check = AbiBindgen { + abi: wasm.instrs.iter(), + err: None, + contents, + }; + func.call_wasm(&module.name, &mut check); + check.check()?; + check.abi = interface.instrs.iter(); + func.call_interface(&module.name, &mut check); + check.check()?; + } } Ok(()) } @@ -249,14 +279,147 @@ impl WitxtRunner<'_> { } } +struct AbiBindgen<'a> { + abi: std::slice::Iter<'a, (wast::Span, &'a str)>, + err: Option, + contents: &'a str, +} + +impl AbiBindgen<'_> { + fn check(&mut self) -> Result<()> { + match self.err.take() { + None => Ok(()), + Some(e) => Err(e), + } + } + + fn assert(&mut self, name: &str) { + if self.err.is_some() { + return; + } + match self.abi.next() { + Some((_, s)) if *s == name => {} + Some((span, s)) => { + let (line, col) = span.linecol_in(self.contents); + self.err = Some(anyhow!( + "line {}:{} - expected `{}` found `{}`", + line + 1, + col + 1, + name, + s, + )); + } + None => { + self.err = Some(anyhow!( + "extra instruction `{}` found when none was expected", + name + )); + } + } + } +} + +impl witx::Bindgen for AbiBindgen<'_> { + type Operand = (); + fn emit( + &mut self, + inst: &Instruction<'_>, + _operands: &mut Vec, + results: &mut Vec, + ) { + use witx::Instruction::*; + match inst { + GetArg { nth } => self.assert(&format!("get-arg{}", nth)), + AddrOf => self.assert("addr-of"), + I32FromChar => self.assert("i32.from_char"), + I64FromU64 => self.assert("i64.from_u64"), + I64FromS64 => self.assert("i64.from_s64"), + I32FromU32 => self.assert("i32.from_u32"), + I32FromS32 => self.assert("i32.from_s32"), + I32FromUsize => self.assert("i32.from_usize"), + I32FromU16 => self.assert("i32.from_u16"), + I32FromS16 => self.assert("i32.from_s16"), + I32FromU8 => self.assert("i32.from_u8"), + I32FromS8 => self.assert("i32.from_s8"), + I32FromChar8 => self.assert("i32.from_char8"), + I32FromPointer => self.assert("i32.from_pointer"), + I32FromConstPointer => self.assert("i32.from_const_pointer"), + I32FromHandle { .. } => self.assert("i32.from_handle"), + ListPointerLength => self.assert("list.pointer_length"), + ListFromPointerLength { .. } => self.assert("list.from_pointer_length"), + F32FromIf32 => self.assert("f32.from_if32"), + F64FromIf64 => self.assert("f64.from_if64"), + CallWasm { .. } => self.assert("call.wasm"), + CallInterface { .. } => self.assert("call.interface"), + S8FromI32 => self.assert("s8.from_i32"), + U8FromI32 => self.assert("u8.from_i32"), + S16FromI32 => self.assert("s16.from_i32"), + U16FromI32 => self.assert("u16.from_i32"), + S32FromI32 => self.assert("s32.from_i32"), + U32FromI32 => self.assert("u32.from_i32"), + S64FromI64 => self.assert("s64.from_i64"), + U64FromI64 => self.assert("u64.from_i64"), + CharFromI32 => self.assert("char.from_i32"), + Char8FromI32 => self.assert("char8.from_i32"), + UsizeFromI32 => self.assert("usize.from_i32"), + If32FromF32 => self.assert("if32.from_f32"), + If64FromF64 => self.assert("if64.from_f64"), + HandleFromI32 { .. } => self.assert("handle.from_i32"), + PointerFromI32 { .. } => self.assert("pointer.from_i32"), + ConstPointerFromI32 { .. } => self.assert("const_pointer.from_i32"), + ReturnPointerGet { n } => self.assert(&format!("return_pointer.get{}", n)), + ResultLift => self.assert("result.lift"), + ResultLower { .. } => self.assert("result.lower"), + EnumLift { .. } => self.assert("enum.lift"), + EnumLower { .. } => self.assert("enum.lower"), + TupleLift { .. } => self.assert("tuple.lift"), + TupleLower { .. } => self.assert("tuple.lower"), + ReuseReturn => self.assert("reuse_return"), + Load { .. } => self.assert("load"), + Store { .. } => self.assert("store"), + Return { .. } => self.assert("return"), + VariantPayload => self.assert("variant-payload"), + I32FromBitflags { .. } => self.assert("i32.from_bitflags"), + BitflagsFromI32 { .. } => self.assert("bitflags.from_i32"), + I64FromBitflags { .. } => self.assert("i64.from_bitflags"), + BitflagsFromI64 { .. } => self.assert("bitflags.from_i64"), + } + for _ in 0..inst.results_len() { + results.push(()); + } + } + + fn allocate_space(&mut self, _: usize, _: &witx::NamedType) { + self.assert("allocate-space"); + } + + fn push_block(&mut self) { + self.assert("block.push"); + } + + fn finish_block(&mut self, _operand: Option) { + self.assert("block.finish"); + } +} + mod kw { wast::custom_keyword!(assert_invalid); wast::custom_keyword!(assert_representable); + wast::custom_keyword!(assert_abi); wast::custom_keyword!(witx); wast::custom_keyword!(eq); wast::custom_keyword!(noteq); wast::custom_keyword!(load); wast::custom_keyword!(superset); + wast::custom_keyword!(call_wasm); + wast::custom_keyword!(call_interface); + wast::custom_keyword!(param); + wast::custom_keyword!(result); + wast::custom_keyword!(wasm); + wast::custom_keyword!(i32); + wast::custom_keyword!(i64); + wast::custom_keyword!(f32); + wast::custom_keyword!(f64); } struct Witxt<'a> { @@ -286,6 +449,13 @@ enum WitxtDirective<'a> { t1: (wast::Id<'a>, &'a str), t2: (wast::Id<'a>, &'a str), }, + AssertAbi { + span: wast::Span, + witx: Witx<'a>, + wasm_signature: (Vec, Vec), + wasm: Abi<'a>, + interface: Abi<'a>, + }, } impl WitxtDirective<'_> { @@ -293,6 +463,7 @@ impl WitxtDirective<'_> { match self { WitxtDirective::Witx(w) => w.span, WitxtDirective::AssertInvalid { span, .. } + | WitxtDirective::AssertAbi { span, .. } | WitxtDirective::AssertRepresentable { span, .. } => *span, } } @@ -318,12 +489,69 @@ impl<'a> Parse<'a> for WitxtDirective<'a> { t1: (parser.parse()?, parser.parse()?), t2: (parser.parse()?, parser.parse()?), }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(WitxtDirective::AssertAbi { + span, + witx: parser.parens(|p| p.parse())?, + wasm_signature: parser.parens(|p| { + p.parse::()?; + let mut params = Vec::new(); + let mut results = Vec::new(); + if p.peek2::() { + p.parens(|p| { + p.parse::()?; + while !p.is_empty() { + params.push(parse_wasmtype(p)?); + } + Ok(()) + })?; + } + if p.peek2::() { + p.parens(|p| { + p.parse::()?; + while !p.is_empty() { + results.push(parse_wasmtype(p)?); + } + Ok(()) + })?; + } + Ok((params, results)) + })?, + wasm: parser.parens(|p| { + p.parse::()?; + p.parse() + })?, + interface: parser.parens(|p| { + p.parse::()?; + p.parse() + })?, + }) } else { Err(l.error()) } } } +fn parse_wasmtype(p: Parser<'_>) -> parser::Result { + let mut l = p.lookahead1(); + if l.peek::() { + p.parse::()?; + Ok(WasmType::I32) + } else if l.peek::() { + p.parse::()?; + Ok(WasmType::I64) + } else if l.peek::() { + p.parse::()?; + Ok(WasmType::F32) + } else if l.peek::() { + p.parse::()?; + Ok(WasmType::F64) + } else { + Err(l.error()) + } +} + struct Witx<'a> { span: wast::Span, id: Option>, @@ -407,3 +635,22 @@ impl<'a> Parse<'a> for RepEquality { } } } + +struct Abi<'a> { + instrs: Vec<(wast::Span, &'a str)>, +} + +impl<'a> Parse<'a> for Abi<'a> { + fn parse(parser: Parser<'a>) -> parser::Result { + let mut instrs = Vec::new(); + while !parser.is_empty() { + instrs.push(parser.step(|cursor| { + let (kw, next) = cursor + .keyword() + .ok_or_else(|| cursor.error("expected keyword"))?; + Ok(((cursor.cur_span(), kw), next)) + })?); + } + Ok(Abi { instrs }) + } +} diff --git a/tools/witx/tests/witxt/abi.witxt b/tools/witx/tests/witxt/abi.witxt new file mode 100644 index 0000000..ccdad7b --- /dev/null +++ b/tools/witx/tests/witxt/abi.witxt @@ -0,0 +1,490 @@ +(assert_abi + (witx (module $x (@interface func (export "f")))) + (wasm) + (call_wasm call.wasm return) + (call_interface call.interface return) +) + +;; scalar arguments +(assert_abi + (witx (module $x (@interface func (export "f") (param $p u8)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_u8 call.wasm return) + (call_interface get-arg0 u8.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p s8)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_s8 call.wasm return) + (call_interface get-arg0 s8.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p u16)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_u16 call.wasm return) + (call_interface get-arg0 u16.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p s16)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_s16 call.wasm return) + (call_interface get-arg0 s16.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p u32)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_u32 call.wasm return) + (call_interface get-arg0 u32.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p s32)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_s32 call.wasm return) + (call_interface get-arg0 s32.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p u64)))) + (wasm (param i64)) + (call_wasm get-arg0 i64.from_u64 call.wasm return) + (call_interface get-arg0 u64.from_i64 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p s64)))) + (wasm (param i64)) + (call_wasm get-arg0 i64.from_s64 call.wasm return) + (call_interface get-arg0 s64.from_i64 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p f32)))) + (wasm (param f32)) + (call_wasm get-arg0 f32.from_if32 call.wasm return) + (call_interface get-arg0 if32.from_f32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p f64)))) + (wasm (param f64)) + (call_wasm get-arg0 f64.from_if64 call.wasm return) + (call_interface get-arg0 if64.from_f64 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p (@witx usize))))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_usize call.wasm return) + (call_interface get-arg0 usize.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p (@witx char8))))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_char8 call.wasm return) + (call_interface get-arg0 char8.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p char)))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_char call.wasm return) + (call_interface get-arg0 char.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p (@witx pointer u8))))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_pointer call.wasm return) + (call_interface get-arg0 pointer.from_i32 call.interface return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (param $p (@witx const_pointer u8))))) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_const_pointer call.wasm return) + (call_interface get-arg0 const_pointer.from_i32 call.interface return) +) + +;; flags parameter +(assert_abi + (witx + (typename $a (flags $x $y)) + (module $x (@interface func (export "f") (param $p $a))) + ) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_bitflags call.wasm return) + (call_interface get-arg0 bitflags.from_i32 call.interface return) +) +(assert_abi + (witx + (typename $a (flags (@witx repr u64) $x $y)) + (module $x (@interface func (export "f") (param $p $a))) + ) + (wasm (param i64)) + (call_wasm get-arg0 i64.from_bitflags call.wasm return) + (call_interface get-arg0 bitflags.from_i64 call.interface return) +) + +;; struct parameter +(assert_abi + (witx + (typename $a (record (field $x u8))) + (module $x (@interface func (export "f") (param $p $a))) + ) + (wasm (param i32)) + (call_wasm get-arg0 addr-of call.wasm return) + (call_interface get-arg0 load call.interface return) +) + +;; handle parameter +(assert_abi + (witx + (typename $a (handle)) + (module $x (@interface func (export "f") (param $p $a))) + ) + (wasm (param i32)) + (call_wasm get-arg0 i32.from_handle call.wasm return) + (call_interface get-arg0 handle.from_i32 call.interface return) +) + +;; list parameter +(assert_abi + (witx + (module $x (@interface func (export "f") (param $p (list u8)))) + ) + (wasm (param i32 i32)) + (call_wasm get-arg0 list.pointer_length call.wasm return) + (call_interface get-arg0 get-arg1 list.from_pointer_length call.interface return) +) + +;; variant parameter -- some not allowed at this time +(assert_abi + (witx + (typename $a (enum $b)) + (module $x (@interface func (export "f") (param $p $a))) + ) + (wasm (param i32)) + (call_wasm get-arg0 enum.lower call.wasm return) + (call_interface get-arg0 enum.lift call.interface return) +) +(assert_abi + (witx + (typename $a (union f32)) + (module $x (@interface func (export "f") (param $p $a))) + ) + (wasm (param i32)) + (call_wasm get-arg0 addr-of call.wasm return) + (call_interface get-arg0 load call.interface return) +) + +;; scalar returns +(assert_abi + (witx (module $x (@interface func (export "f") (result $p u8)))) + (wasm (result i32)) + (call_wasm call.wasm u8.from_i32 return) + (call_interface call.interface i32.from_u8 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p s8)))) + (wasm (result i32)) + (call_wasm call.wasm s8.from_i32 return) + (call_interface call.interface i32.from_s8 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p u16)))) + (wasm (result i32)) + (call_wasm call.wasm u16.from_i32 return) + (call_interface call.interface i32.from_u16 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p s16)))) + (wasm (result i32)) + (call_wasm call.wasm s16.from_i32 return) + (call_interface call.interface i32.from_s16 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p u32)))) + (wasm (result i32)) + (call_wasm call.wasm u32.from_i32 return) + (call_interface call.interface i32.from_u32 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p s32)))) + (wasm (result i32)) + (call_wasm call.wasm s32.from_i32 return) + (call_interface call.interface i32.from_s32 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p u64)))) + (wasm (result i64)) + (call_wasm call.wasm u64.from_i64 return) + (call_interface call.interface i64.from_u64 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p s64)))) + (wasm (result i64)) + (call_wasm call.wasm s64.from_i64 return) + (call_interface call.interface i64.from_s64 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p f32)))) + (wasm (result f32)) + (call_wasm call.wasm if32.from_f32 return) + (call_interface call.interface f32.from_if32 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p f64)))) + (wasm (result f64)) + (call_wasm call.wasm if64.from_f64 return) + (call_interface call.interface f64.from_if64 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p (@witx usize))))) + (wasm (result i32)) + (call_wasm call.wasm usize.from_i32 return) + (call_interface call.interface i32.from_usize return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p (@witx char8))))) + (wasm (result i32)) + (call_wasm call.wasm char8.from_i32 return) + (call_interface call.interface i32.from_char8 return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p char)))) + (wasm (result i32)) + (call_wasm call.wasm char.from_i32 return) + (call_interface call.interface i32.from_char return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p (@witx pointer u8))))) + (wasm (result i32)) + (call_wasm call.wasm pointer.from_i32 return) + (call_interface call.interface i32.from_pointer return) +) +(assert_abi + (witx (module $x (@interface func (export "f") (result $p (@witx const_pointer u8))))) + (wasm (result i32)) + (call_wasm call.wasm const_pointer.from_i32 return) + (call_interface call.interface i32.from_const_pointer return) +) + +;; flags return +(assert_abi + (witx + (typename $a (flags $x $y)) + (module $x (@interface func (export "f") (result $p $a))) + ) + (wasm (result i32)) + (call_wasm call.wasm bitflags.from_i32 return) + (call_interface call.interface i32.from_bitflags return) +) +(assert_abi + (witx + (typename $a (flags (@witx repr u64) $x $y)) + (module $x (@interface func (export "f") (result $p $a))) + ) + (wasm (result i64)) + (call_wasm call.wasm bitflags.from_i64 return) + (call_interface call.interface i64.from_bitflags return) +) + +;; handle return +(assert_abi + (witx + (typename $a (handle)) + (module $x (@interface func (export "f") (result $p $a))) + ) + (wasm (result i32)) + (call_wasm call.wasm handle.from_i32 return) + (call_interface call.interface i32.from_handle return) +) + +;; struct return -- not supported +(assert_invalid + (witx + (typename $a (record (field $x u8))) + (module $x (@interface func (export "f") (result $p $a))) + ) + "ABI error: invalid return type" +) + +;; list return -- not supported +(assert_invalid + (witx + (module $x (@interface func (export "f") (result $p (list u8)))) + ) + "ABI error: invalid return type" +) + +;; variant return -- only some allowed +(assert_invalid + (witx + (typename $a (enum $b)) + (module $x (@interface func (export "f") (result $p $a))) + ) + "ABI error: invalid return type" +) +(assert_invalid + (witx + (typename $a (union s32 f32)) + (module $x (@interface func (export "f") (result $p $a))) + ) + "ABI error: invalid return type" +) +(assert_invalid + (witx + (typename $a (expected (error f32))) + (module $x (@interface func (export "f") (result $p $a))) + ) + "ABI error: only named types are allowed in results" +) +(assert_invalid + (witx + (typename $errno (enum $success $bad)) + (typename $a (expected f32 (error $errno))) + (module $x (@interface func (export "f") (result $p $a))) + ) + "ABI error: only named types are allowed in results" +) + +;; Result<(), $errno> +(assert_abi + (witx + (typename $errno (enum $success $bad)) + (typename $a (expected (error $errno))) + (module $x (@interface func (export "f") (result $p $a))) + ) + (wasm (result i32)) + + (call_wasm + call.wasm + ;; ok block, nothing happens + block.push + block.finish + ;; err block, we lift the return value as the num + block.push + reuse_return + enum.lift + block.finish + ;; consumes 2 blocks and uses the return value of the call to discriminate + result.lift + return) + + (call_interface + call.interface + + ;; ok block, nothing happens + block.push + block.finish + + ;; err block, lift the enum + block.push + variant-payload + enum.lower + block.finish + + ;; consume the 2 blocks and lower based on the call + result.lower + return) +) + +;; Result<$ty, $errno> +(assert_abi + (witx + (typename $errno (enum $success $bad)) + (typename $size u32) + (typename $a (expected $size (error $errno))) + (module $x (@interface func (export "f") (result $p $a))) + ) + (wasm (param i32) (result i32)) + + (call_wasm + ;; make space for the return value and push its pointer + allocate-space + return_pointer.get0 + + call.wasm + + ;; ok block, load the return pointer and have it be the result for the `Ok` + block.push + return_pointer.get0 + load + block.finish + + block.push + reuse_return + enum.lift + block.finish + + result.lift + return) + + (call_interface + call.interface + + ;; store the successful result at the first return pointer (the first param) + block.push + variant-payload + get-arg0 + store + block.finish + + block.push + variant-payload + enum.lower + block.finish + + result.lower + return) +) + +;; Result<($a, $b), $errno> +(assert_abi + (witx + (typename $errno (enum $success $bad)) + (typename $size u32) + (typename $other (record (field $a $size))) + (module $x (@interface func (export "f") + (result $p (expected (tuple $size $other) (error $errno))))) + ) + (wasm (param i32 i32) (result i32)) + + (call_wasm + allocate-space + return_pointer.get0 + allocate-space + return_pointer.get1 + + call.wasm + + block.push + return_pointer.get0 + load + return_pointer.get1 + load + tuple.lift + block.finish + + block.push + reuse_return + enum.lift + block.finish + + result.lift + return) + + (call_interface + call.interface + + ;; note the reverse order since we're consuming the results of lowering the + ;; tuple + block.push + variant-payload + tuple.lower + get-arg1 + store + get-arg0 + store + block.finish + + block.push + variant-payload + enum.lower + block.finish + + result.lower + return) +) diff --git a/tools/witx/tests/witxt/representation.witxt b/tools/witx/tests/witxt/representation.witxt index 37d20a7..53f71f1 100644 --- a/tools/witx/tests/witxt/representation.witxt +++ b/tools/witx/tests/witxt/representation.witxt @@ -1,8 +1,8 @@ ;; type names don't matter (witx $a - (typename $a (flags (@witx bitflags u8) $b $c))) + (typename $a (flags (@witx repr u8) $b $c))) (witx $b - (typename $b (flags (@witx bitflags u8) $b $c))) + (typename $b (flags (@witx repr u8) $b $c))) (assert_representable eq $a "a" $b "b") (assert_representable eq $b "b" $a "a") diff --git a/tools/witx/tests/witxt/shorthand.witxt b/tools/witx/tests/witxt/shorthand.witxt new file mode 100644 index 0000000..960615f --- /dev/null +++ b/tools/witx/tests/witxt/shorthand.witxt @@ -0,0 +1,59 @@ +(witx $a + (typename $a bool)) +(witx $b + (typename $a (variant (case $false) (case $true)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (expected (error)))) +(witx $b + (typename $a (variant (case $ok) (case $err)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (expected (error u32)))) +(witx $b + (typename $a (variant (case $ok) (case $err u32)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (expected u32 (error)))) +(witx $b + (typename $a (variant (case $ok u32) (case $err)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (expected u32 (error u64)))) +(witx $b + (typename $a (variant (case $ok u32) (case $err u64)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (flags $a $b))) +(witx $b + (typename $a (record (field $a bool) (field $b bool)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (enum $a $b))) +(witx $b + (typename $a (variant (case $a) (case $b)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a string)) +(witx $b + (typename $a (list char))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (tuple u32 u64))) +(witx $b + (typename $a (record (field $0 u32) (field $1 u64)))) +(assert_representable eq $a "a" $b "a") + +(witx $a + (typename $a (union u32 u64))) +(witx $b + (typename $a (variant (case $0 u32) (case $1 u64)))) +(assert_representable eq $a "a" $b "a") From 2706bceef3bbac17043649932e1b8761eb69f3c3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Feb 2021 13:57:47 -0800 Subject: [PATCH 19/19] Update the wasi-nn witx files --- phases/ephemeral/docs.md | 99 ++++++++++++++------ phases/ephemeral/witx/wasi_ephemeral_nn.witx | 31 +++--- tools/witx/tests/wasi-docs.rs | 2 +- tools/witx/tests/witxt/wasi.witxt | 7 ++ 4 files changed, 91 insertions(+), 48 deletions(-) diff --git a/phases/ephemeral/docs.md b/phases/ephemeral/docs.md index 3ebf218..6f9a8c8 100644 --- a/phases/ephemeral/docs.md +++ b/phases/ephemeral/docs.md @@ -1209,7 +1209,7 @@ Size: 4 Alignment: 4 -## `nn_errno`: Enum(`u16`) +## `nn_errno`: `Variant` Error codes returned by functions in this API. This is prefixed to avoid conflicts with the `$errno` in `typenames.witx`. @@ -1217,7 +1217,7 @@ Size: 2 Alignment: 2 -### Variants +### Variant cases - `success` No error occurred. @@ -1230,7 +1230,7 @@ Caller module is missing a memory export. - `busy` Device or resource busy. -## `tensor_dimensions`: `Array` +## `tensor_dimensions`: `List` The dimensions of a tensor. The array length matches the tensor rank and each element in the array @@ -1240,14 +1240,14 @@ Size: 8 Alignment: 4 -## `tensor_type`: Enum(`u8`) +## `tensor_type`: `Variant` The type of the elements in a tensor. Size: 1 Alignment: 1 -### Variants +### Variant cases - `f16` - `f32` @@ -1256,7 +1256,7 @@ Alignment: 1 - `i32` -## `tensor_data`: `Array` +## `tensor_data`: `List` The tensor data Initially conceived as a sparse representation, each empty cell would be filled with zeroes and @@ -1269,14 +1269,14 @@ Size: 8 Alignment: 4 -## `tensor`: Struct +## `tensor`: `Record` A tensor. Size: 20 Alignment: 4 -### Struct members +### Record members - `dimensions`: [`tensor_dimensions`](#tensor_dimensions) Describe the size of the tensor (e.g. 2x2x2x2 -> [2, 2, 2, 2]). To represent a tensor containing a single value, use `[1]` for the tensor dimensions. @@ -1292,7 +1292,7 @@ Contains the tensor data. Offset: 12 -## `graph_builder`: `Array` +## `graph_builder`: `List` The graph initialization data. This consists of an array of buffers because implementing backends may encode their graph IR in parts (e.g. OpenVINO stores its IR and weights separately). @@ -1300,13 +1300,13 @@ Size: 8 Alignment: 4 -## `graph_builder_array`: `Array` +## `graph_builder_array`: `List` Size: 8 Alignment: 4 -## `graph` +## `graph`: `Handle` An execution graph for performing inference (i.e. a model). Size: 4 @@ -1314,7 +1314,7 @@ Size: 4 Alignment: 4 ### Supertypes -## `graph_encoding`: Enum(`u8`) +## `graph_encoding`: `Variant` Describes the encoding of the graph. This allows the API to be implemented by various backends that encode (i.e. serialize) their graph IR differently. @@ -1322,25 +1322,25 @@ Size: 1 Alignment: 1 -### Variants +### Variant cases - `openvino` TODO document buffer order -## `execution_target`: Enum(`u8`) +## `execution_target`: `Variant` Define where the graph should be executed. Size: 1 Alignment: 1 -### Variants +### Variant cases - `cpu` - `gpu` - `tpu` -## `graph_execution_context` +## `graph_execution_context`: `Handle` A $graph_execution_context allows for attaching inputs prior to calling [`compute`](#compute) on a graph and retrieving outputs after the computation has completed. TODO a handle may not be the right type but we want it to be opaque to users. @@ -2705,7 +2705,7 @@ Which channels on the socket to shut down. --- -#### `load(builder: graph_builder_array, encoding: graph_encoding, target: execution_target) -> (nn_errno, graph)` +#### `load(builder: graph_builder_array, encoding: graph_encoding, target: execution_target) -> Result` Load an opaque sequence of bytes to use for inference. This allows runtime implementations to support multiple graph encoding formats. For unsupported graph encodings, @@ -2722,14 +2722,21 @@ The encoding of the graph. Where to execute the graph. ##### Results -- `error`: [`nn_errno`](#nn_errno) +- `error`: `Result` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`graph`](#graph) -- `graph`: [`graph`](#graph) +- `err`: [`nn_errno`](#nn_errno) --- -#### `init_execution_context(graph: graph) -> (nn_errno, graph_execution_context)` +#### `init_execution_context(graph: graph) -> Result` TODO Functions like `describe_graph_inputs` and `describe_graph_outputs` (returning an array of `$tensor_description`s) might be useful for introspecting the graph but are not yet included here. Create an execution instance of a loaded graph. @@ -2739,14 +2746,21 @@ TODO this may need to accept flags that might affect the compilation or executio - `graph`: [`graph`](#graph) ##### Results -- `error`: [`nn_errno`](#nn_errno) +- `error`: `Result` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`graph_execution_context`](#graph_execution_context) -- `context`: [`graph_execution_context`](#graph_execution_context) +- `err`: [`nn_errno`](#nn_errno) --- -#### `set_input(context: graph_execution_context, index: u32, tensor: tensor) -> nn_errno` +#### `set_input(context: graph_execution_context, index: u32, tensor: tensor) -> Result<(), nn_errno>` Define the inputs to use for inference. This should return an $nn_errno (TODO define) if the input tensor does not match the expected dimensions and type. @@ -2761,12 +2775,21 @@ The index of the input to change. The tensor to set as the input. ##### Results -- `error`: [`nn_errno`](#nn_errno) +- `error`: `Result<(), nn_errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`nn_errno`](#nn_errno) --- -#### `get_output(context: graph_execution_context, index: u32, out_buffer: Pointer, out_buffer_max_size: buffer_size) -> (nn_errno, buffer_size)` +#### `get_output(context: graph_execution_context, index: u32, out_buffer: Pointer, out_buffer_max_size: buffer_size) -> Result` Extract the outputs after inference. This should return an $nn_errno (TODO define) if the inference has not yet run. @@ -2785,15 +2808,22 @@ tensor metadata (i.e. dimension, element type) but this should be added at some - `out_buffer_max_size`: [`buffer_size`](#buffer_size) ##### Results -- `error`: [`nn_errno`](#nn_errno) - -- `bytes_written`: [`buffer_size`](#buffer_size) +- `error`: `Result` The number of bytes of tensor data written to the `$out_buffer`. +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok`: [`buffer_size`](#buffer_size) + +- `err`: [`nn_errno`](#nn_errno) + --- -#### `compute(context: graph_execution_context) -> nn_errno` +#### `compute(context: graph_execution_context) -> Result<(), nn_errno>` Compute the inference on the given inputs (see [`set_input`](#set_input)). This should return an $nn_errno (TODO define) if the inputs are not all defined. @@ -2802,5 +2832,14 @@ This should return an $nn_errno (TODO define) if the inputs are not all defined. - `context`: [`graph_execution_context`](#graph_execution_context) ##### Results -- `error`: [`nn_errno`](#nn_errno) +- `error`: `Result<(), nn_errno>` + +###### Variant Layout +- size: 8 +- align: 4 +- tag_size: 4 +###### Variant cases +- `ok` + +- `err`: [`nn_errno`](#nn_errno) diff --git a/phases/ephemeral/witx/wasi_ephemeral_nn.witx b/phases/ephemeral/witx/wasi_ephemeral_nn.witx index a5cc3a1..24f7191 100644 --- a/phases/ephemeral/witx/wasi_ephemeral_nn.witx +++ b/phases/ephemeral/witx/wasi_ephemeral_nn.witx @@ -15,7 +15,7 @@ ;;; Error codes returned by functions in this API. This is prefixed to avoid conflicts with the `$errno` in ;;; `typenames.witx`. (typename $nn_errno - (enum u16 + (enum (@witx tag u16) ;;; No error occurred. $success ;;; Caller module passed an invalid argument. @@ -31,11 +31,11 @@ ;;; ;;; The array length matches the tensor rank and each element in the array ;;; describes the size of each dimension. -(typename $tensor_dimensions (array u32)) +(typename $tensor_dimensions (list u32)) ;;; The type of the elements in a tensor. (typename $tensor_type - (enum u8 + (enum (@witx tag u8) $f16 $f32 $u8 @@ -50,11 +50,11 @@ ;;; tensor with 4-byte f32 elements would have a data array of length 16). Naturally, this representation requires ;;; some knowledge of how to lay out data in memory--e.g. using row-major ordering--and could perhaps be improved ;;; by future witx features (TODO). -(typename $tensor_data (array u8)) +(typename $tensor_data (list u8)) ;;; A tensor. (typename $tensor - (struct + (record ;;; Describe the size of the tensor (e.g. 2x2x2x2 -> [2, 2, 2, 2]). To represent a tensor containing a single value, ;;; use `[1]` for the tensor dimensions. (field $dimensions $tensor_dimensions) @@ -69,8 +69,8 @@ ;;; The graph initialization data. This consists of an array of buffers because implementing backends may encode their ;;; graph IR in parts (e.g. OpenVINO stores its IR and weights separately). -(typename $graph_builder (array u8)) -(typename $graph_builder_array (array $graph_builder)) +(typename $graph_builder (list u8)) +(typename $graph_builder_array (list $graph_builder)) ;;; An execution graph for performing inference (i.e. a model). (typename $graph (handle)) @@ -78,7 +78,7 @@ ;;; Describes the encoding of the graph. This allows the API to be implemented by various backends that encode (i.e. ;;; serialize) their graph IR differently. (typename $graph_encoding - (enum u8 + (enum (@witx tag u8) ;;; TODO document buffer order $openvino ) @@ -86,7 +86,7 @@ ;;; Define where the graph should be executed. (typename $execution_target - (enum u8 + (enum (@witx tag u8) $cpu $gpu $tpu @@ -113,8 +113,7 @@ ;;; Where to execute the graph. (param $target $execution_target) - (result $error $nn_errno) - (result $graph $graph) + (result $error (expected $graph (error $nn_errno))) ) ;;; TODO Functions like `describe_graph_inputs` and `describe_graph_outputs` (returning @@ -124,8 +123,7 @@ ;;; TODO this may need to accept flags that might affect the compilation or execution of the graph. (@interface func (export "init_execution_context") (param $graph $graph) - (result $error $nn_errno) - (result $context $graph_execution_context) + (result $error (expected $graph_execution_context (error $nn_errno))) ) ;;; Define the inputs to use for inference. @@ -138,7 +136,7 @@ ;;; The tensor to set as the input. (param $tensor $tensor) - (result $error $nn_errno) + (result $error (expected (error $nn_errno))) ) ;;; Extract the outputs after inference. @@ -154,9 +152,8 @@ (param $out_buffer (@witx pointer u8)) (param $out_buffer_max_size $buffer_size) - (result $error $nn_errno) ;;; The number of bytes of tensor data written to the `$out_buffer`. - (result $bytes_written $buffer_size) + (result $error (expected $buffer_size (error $nn_errno))) ) ;;; Compute the inference on the given inputs (see `set_input`). @@ -164,6 +161,6 @@ ;;; This should return an $nn_errno (TODO define) if the inputs are not all defined. (@interface func (export "compute") (param $context $graph_execution_context) - (result $error $nn_errno) + (result $error (expected (error $nn_errno))) ) ) diff --git a/tools/witx/tests/wasi-docs.rs b/tools/witx/tests/wasi-docs.rs index 6702edf..a0aa149 100644 --- a/tools/witx/tests/wasi-docs.rs +++ b/tools/witx/tests/wasi-docs.rs @@ -9,7 +9,7 @@ fn validate_docs() { witx::phases::ephemeral().unwrap(), witx::phases::old::snapshot_0().unwrap(), ] { - let doc = witx::load(&phase).unwrap_or_else(|e| panic!("failed to parse: {}", e)); + let doc = witx::load(&phase).unwrap_or_else(|e| panic!("failed to parse: {:?}", e)); diff_against_filesystem(&doc.to_md(), &witx::phases::docs_path(&phase)); } } diff --git a/tools/witx/tests/witxt/wasi.witxt b/tools/witx/tests/witxt/wasi.witxt index f654a7e..7a6c144 100644 --- a/tools/witx/tests/witxt/wasi.witxt +++ b/tools/witx/tests/witxt/wasi.witxt @@ -24,3 +24,10 @@ (witx (load "../../../../phases/ephemeral/witx/wasi_ephemeral_fd.witx")) + +;; wasi-nn should work +(witx + (load + "../../../../phases/ephemeral/witx/wasi_ephemeral_nn.witx" + ) +)