From e01a5502b846cd6691772041d5a4c3c9ae598297 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 29 Apr 2022 07:59:20 -0700 Subject: [PATCH] Split out the `Tuple` type from the `Record` type This makes the `tuple` type a dedicated type in the type system which splits the lifting/lowerings for code generators. This ends up simplifying most code generators since they already had an if/else handling for most `Record` types for tuples and not-tuples. --- crates/gen-c/src/lib.rs | 121 ++++++++---- crates/gen-core/src/lib.rs | 12 ++ crates/gen-js/src/lib.rs | 138 +++++++------- crates/gen-markdown/src/lib.rs | 37 +++- crates/gen-rust-wasm/src/lib.rs | 18 ++ crates/gen-rust/src/lib.rs | 200 ++++++++++---------- crates/gen-spidermonkey/src/lib.rs | 17 +- crates/gen-wasmtime-py/src/lib.rs | 122 ++++++------ crates/gen-wasmtime/src/lib.rs | 19 +- crates/parser/src/abi.rs | 136 +++++++++---- crates/parser/src/ast.rs | 20 +- crates/parser/src/ast/resolve.rs | 33 +++- crates/parser/src/lib.rs | 44 ++--- crates/parser/src/sizealign.rs | 16 +- crates/parser/tests/all.rs | 6 + crates/parser/tests/ui/functions.wit.result | 14 +- crates/parser/tests/ui/import-me.wit.result | 19 +- crates/parser/tests/ui/imports.wit.result | 19 +- crates/parser/tests/ui/imports2.wit.result | 19 +- crates/parser/tests/ui/types.wit.result | 36 ++-- crates/parser/tests/ui/values.wit.result | 18 +- crates/test-helpers/src/lib.rs | 5 +- crates/wasmlink/src/adapter/call.rs | 88 ++++++--- crates/wasmlink/src/module.rs | 1 + crates/wit-component/src/decoding.rs | 21 +- crates/wit-component/src/encoding.rs | 99 ++++++---- crates/wit-component/src/printing.rs | 49 +++-- 27 files changed, 764 insertions(+), 563 deletions(-) diff --git a/crates/gen-c/src/lib.rs b/crates/gen-c/src/lib.rs index 02898c0e2..c7e1e025f 100644 --- a/crates/gen-c/src/lib.rs +++ b/crates/gen-c/src/lib.rs @@ -72,11 +72,7 @@ struct CSig { enum Scalar { Void, OptionBool(Type), - ExpectedEnum { - err: TypeId, - ok: Option, - max_err: usize, - }, + ExpectedEnum { err: TypeId, max_err: usize }, Type(Type), } @@ -171,7 +167,7 @@ impl C { TypeDefKind::Type(t) => self.is_arg_by_pointer(iface, t), TypeDefKind::Variant(v) => !v.is_enum(), TypeDefKind::Flags(_) => false, - TypeDefKind::Record(_) | TypeDefKind::List(_) => true, + TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, }, Type::String => true, _ => false, @@ -247,6 +243,7 @@ impl C { match &ty.kind { TypeDefKind::Type(t) => self.print_ty(iface, t), TypeDefKind::Flags(_) + | TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) | TypeDefKind::Variant(_) => { @@ -285,14 +282,13 @@ impl C { } match &ty.kind { TypeDefKind::Type(t) => self.print_ty_name(iface, t), - TypeDefKind::Flags(_) => unimplemented!(), - TypeDefKind::Record(r) => { - assert!(r.is_tuple()); + TypeDefKind::Record(_) | TypeDefKind::Flags(_) => unimplemented!(), + TypeDefKind::Tuple(t) => { self.src.h("tuple"); - self.src.h(&r.fields.len().to_string()); - for field in r.fields.iter() { + self.src.h(&t.types.len().to_string()); + for ty in t.types.iter() { self.src.h("_"); - self.print_ty_name(iface, &field.ty); + self.print_ty_name(iface, ty); } } TypeDefKind::Variant(v) => { @@ -328,16 +324,15 @@ impl C { self.src.h("typedef "); let kind = &iface.types[ty].kind; match kind { - TypeDefKind::Type(_) | TypeDefKind::Flags(_) => { + TypeDefKind::Type(_) | TypeDefKind::Flags(_) | TypeDefKind::Record(_) => { unreachable!() } - TypeDefKind::Record(r) => { - assert!(r.is_tuple()); + TypeDefKind::Tuple(t) => { self.src.h("struct {\n"); - for (i, f) in r.fields.iter().enumerate() { - self.print_ty(iface, &f.ty); + for (i, t) in t.types.iter().enumerate() { + self.print_ty(iface, t); self.src.h(" "); - self.src.h(&format!("f{};\n", i)); + self.src.h(&format!("f{i};\n")); } self.src.h("}"); } @@ -399,6 +394,7 @@ impl C { match &iface.types[id].kind { TypeDefKind::Type(t) => self.is_empty_type(iface, t), TypeDefKind::Record(r) => r.fields.is_empty(), + TypeDefKind::Tuple(t) => t.types.is_empty(), _ => false, } } @@ -466,15 +462,20 @@ impl C { self.free( iface, &field.ty, - &format!( - "&ptr->{}{}", - if r.is_tuple() { "f" } else { "" }, - field.name.to_snake_case() - ), + &format!("&ptr->{}", field.name.to_snake_case()), ); } } + TypeDefKind::Tuple(t) => { + for (i, ty) in t.types.iter().enumerate() { + if !self.owns_anything(iface, ty) { + continue; + } + self.free(iface, ty, &format!("&ptr->f{i}")); + } + } + TypeDefKind::List(t) => { if self.owns_anything(iface, t) { self.src.c("for (size_t i = 0; i < ptr->len; i++) {\n"); @@ -524,6 +525,7 @@ impl C { match &iface.types[id].kind { TypeDefKind::Type(t) => self.owns_anything(iface, t), TypeDefKind::Record(r) => r.fields.iter().any(|t| self.owns_anything(iface, &t.ty)), + TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.owns_anything(iface, t)), TypeDefKind::Flags(_) => false, TypeDefKind::List(_) => true, TypeDefKind::Variant(v) => v @@ -584,8 +586,13 @@ impl Return { self.scalar = Some(Scalar::Type(*orig_ty)); } - // record returns may become many return pointers with tuples - TypeDefKind::Record(_) => self.splat_tuples(iface, ty, orig_ty), + // Tuples get splatted to multiple return pointers + TypeDefKind::Tuple(_) => self.splat_tuples(iface, ty, orig_ty), + + // Record returns are always through a pointer + TypeDefKind::Record(_) => { + self.retptrs.push(*orig_ty); + } // other records/lists/buffers always go to return pointers TypeDefKind::List(_) => self.retptrs.push(*orig_ty), @@ -613,7 +620,6 @@ impl Return { if let TypeDefKind::Variant(e) = &iface.types[*err].kind { if e.is_enum() { self.scalar = Some(Scalar::ExpectedEnum { - ok: ok.cloned(), err: *err, max_err: e.cases.len(), }); @@ -642,9 +648,9 @@ impl Return { } }; match &iface.types[id].kind { - TypeDefKind::Record(r) if r.is_tuple() => { + TypeDefKind::Tuple(t) => { self.splat_tuple = true; - self.retptrs.extend(r.fields.iter().map(|f| f.ty)); + self.retptrs.extend(t.types.iter()); } _ => self.retptrs.push(*orig_ty), } @@ -673,9 +679,6 @@ impl Generator for C { for field in record.fields.iter() { self.print_ty(iface, &field.ty); self.src.h(" "); - if record.is_tuple() { - self.src.h("f"); - } self.src.h(&field.name.to_snake_case()); self.src.h(";\n"); } @@ -688,6 +691,31 @@ impl Generator for C { .insert(id, mem::replace(&mut self.src.header, prev)); } + fn type_tuple( + &mut self, + iface: &Interface, + id: TypeId, + name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + let prev = mem::take(&mut self.src.header); + self.docs(docs); + self.names.insert(&name.to_snake_case()).unwrap(); + self.src.h("typedef struct {\n"); + for (i, ty) in tuple.types.iter().enumerate() { + self.print_ty(iface, ty); + self.src.h(&format!(" f{i};\n")); + } + self.src.h("} "); + self.print_namespace(iface); + self.src.h(&name.to_snake_case()); + self.src.h("_t;\n"); + + self.types + .insert(id, mem::replace(&mut self.src.header, prev)); + } + fn type_flags( &mut self, iface: &Interface, @@ -1386,16 +1414,9 @@ impl Bindgen for FunctionBindgen<'_> { } Instruction::RecordLower { record, .. } => { - if record.is_tuple() { - let op = &operands[0]; - for i in 0..record.fields.len() { - results.push(format!("({}).f{}", op, i)); - } - } else { - let op = &operands[0]; - for f in record.fields.iter() { - results.push(format!("({}).{}", op, f.name.to_snake_case())); - } + let op = &operands[0]; + for f in record.fields.iter() { + results.push(format!("({}).{}", op, f.name.to_snake_case())); } } Instruction::RecordLift { ty, .. } => { @@ -1408,6 +1429,22 @@ impl Bindgen for FunctionBindgen<'_> { results.push(result); } + Instruction::TupleLower { tuple, .. } => { + let op = &operands[0]; + for i in 0..tuple.types.len() { + results.push(format!("({}).f{}", op, i)); + } + } + Instruction::TupleLift { ty, .. } => { + let name = self.gen.type_string(iface, &Type::Id(*ty)); + let mut result = format!("({}) {{\n", name); + for op in operands { + result.push_str(&format!("{},\n", op)); + } + result.push_str("}"); + results.push(result); + } + // TODO: checked Instruction::FlagsLower { flags, ty, .. } => match flags_repr(flags) { Int::U8 | Int::U16 | Int::U32 => { @@ -1685,7 +1722,7 @@ impl Bindgen for FunctionBindgen<'_> { )); results.push(option_ret); } - Some(Scalar::ExpectedEnum { err, max_err, .. }) => { + Some(Scalar::ExpectedEnum { err, max_err }) => { let ret = self.locals.tmp("ret"); let mut ok_names = Vec::new(); for ty in self.sig.ret.retptrs.iter() { diff --git a/crates/gen-core/src/lib.rs b/crates/gen-core/src/lib.rs index 337cfd168..23adaf791 100644 --- a/crates/gen-core/src/lib.rs +++ b/crates/gen-core/src/lib.rs @@ -56,6 +56,7 @@ pub trait Generator { docs: &Docs, ); fn type_flags(&mut self, iface: &Interface, id: TypeId, name: &str, flags: &Flags, docs: &Docs); + fn type_tuple(&mut self, iface: &Interface, id: TypeId, name: &str, flags: &Tuple, docs: &Docs); fn type_variant( &mut self, iface: &Interface, @@ -90,6 +91,7 @@ pub trait Generator { match &ty.kind { TypeDefKind::Record(record) => self.type_record(iface, id, name, record, &ty.docs), TypeDefKind::Flags(flags) => self.type_flags(iface, id, name, flags, &ty.docs), + TypeDefKind::Tuple(tuple) => self.type_tuple(iface, id, name, tuple, &ty.docs), TypeDefKind::Variant(variant) => { self.type_variant(iface, id, name, variant, &ty.docs) } @@ -190,6 +192,11 @@ impl Types { info |= self.type_info(iface, &field.ty); } } + TypeDefKind::Tuple(t) => { + for ty in t.types.iter() { + info |= self.type_info(iface, ty); + } + } TypeDefKind::Flags(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { @@ -228,6 +235,11 @@ impl Types { self.set_param_result_ty(iface, &field.ty, param, result) } } + TypeDefKind::Tuple(t) => { + for ty in t.types.iter() { + self.set_param_result_ty(iface, ty, param, result) + } + } TypeDefKind::Flags(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { diff --git a/crates/gen-js/src/lib.rs b/crates/gen-js/src/lib.rs index d5e8d92bf..c3783803c 100644 --- a/crates/gen-js/src/lib.rs +++ b/crates/gen-js/src/lib.rs @@ -169,7 +169,7 @@ impl Js { } match &ty.kind { TypeDefKind::Type(t) => self.print_ty(iface, t), - TypeDefKind::Record(r) if r.is_tuple() => self.print_tuple(iface, r), + TypeDefKind::Tuple(t) => self.print_tuple(iface, t), TypeDefKind::Record(_) => panic!("anonymous record"), TypeDefKind::Flags(_) => panic!("anonymous flags"), TypeDefKind::Variant(v) => { @@ -214,13 +214,13 @@ impl Js { } } - fn print_tuple(&mut self, iface: &Interface, record: &Record) { + fn print_tuple(&mut self, iface: &Interface, tuple: &Tuple) { self.src.ts("["); - for (i, field) in record.fields.iter().enumerate() { + for (i, ty) in tuple.types.iter().enumerate() { if i > 0 { self.src.ts(", "); } - self.print_ty(iface, &field.ty); + self.print_ty(iface, ty); } self.src.ts("]"); } @@ -332,26 +332,34 @@ impl Generator for Js { docs: &Docs, ) { self.docs(docs); - if record.is_tuple() { - self.src - .ts(&format!("export type {} = ", name.to_camel_case())); - self.print_tuple(iface, record); - self.src.ts(";\n"); - } else { + self.src + .ts(&format!("export interface {} {{\n", name.to_camel_case())); + for field in record.fields.iter() { + self.docs(&field.docs); + let (option_str, ty) = self + .get_nullable_option(iface, &field.ty) + .map_or(("", &field.ty), |ty| ("?", ty)); self.src - .ts(&format!("export interface {} {{\n", name.to_camel_case())); - for field in record.fields.iter() { - self.docs(&field.docs); - let (option_str, ty) = self - .get_nullable_option(iface, &field.ty) - .map_or(("", &field.ty), |ty| ("?", ty)); - self.src - .ts(&format!("{}{}: ", field.name.to_mixed_case(), option_str)); - self.print_ty(iface, ty); - self.src.ts(",\n"); - } - self.src.ts("}\n"); + .ts(&format!("{}{}: ", field.name.to_mixed_case(), option_str)); + self.print_ty(iface, ty); + self.src.ts(",\n"); } + self.src.ts("}\n"); + } + + fn type_tuple( + &mut self, + iface: &Interface, + _id: TypeId, + name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + self.docs(docs); + self.src + .ts(&format!("export type {} = ", name.to_camel_case())); + self.print_tuple(iface, tuple); + self.src.ts(";\n"); } fn type_flags( @@ -1358,56 +1366,56 @@ impl Bindgen for FunctionBindgen<'_> { } Instruction::RecordLower { record, .. } => { - if record.is_tuple() { - // Tuples are represented as an array, sowe can use - // destructuring assignment to lower the tuple into its - // components. - let tmp = self.tmp(); - let mut expr = "const [".to_string(); - for i in 0..record.fields.len() { - if i > 0 { - expr.push_str(", "); - } - let name = format!("tuple{}_{}", tmp, i); - expr.push_str(&name); - results.push(name); - } - self.src.js(&format!("{}] = {};\n", expr, operands[0])); - } else { - // Otherwise we use destructuring field access to get each - // field individually. - let tmp = self.tmp(); - let mut expr = "const {".to_string(); - for (i, field) in record.fields.iter().enumerate() { - if i > 0 { - expr.push_str(", "); - } - let name = format!("v{}_{}", tmp, i); - expr.push_str(&field.name.to_mixed_case()); - expr.push_str(": "); - expr.push_str(&name); - results.push(name); + // use destructuring field access to get each + // field individually. + let tmp = self.tmp(); + let mut expr = "const {".to_string(); + for (i, field) in record.fields.iter().enumerate() { + if i > 0 { + expr.push_str(", "); } - self.src.js(&format!("{} }} = {};\n", expr, operands[0])); + let name = format!("v{}_{}", tmp, i); + expr.push_str(&field.name.to_mixed_case()); + expr.push_str(": "); + expr.push_str(&name); + results.push(name); } + self.src.js(&format!("{} }} = {};\n", expr, operands[0])); } Instruction::RecordLift { record, .. } => { - if record.is_tuple() { - // Tuples are represented as an array, so we just shove all - // the operands into an array. - results.push(format!("[{}]", operands.join(", "))); - } else { - // Otherwise records are represented as plain objects, so we - // make a new object and set all the fields with an object - // literal. - let mut result = "{\n".to_string(); - for (field, op) in record.fields.iter().zip(operands) { - result.push_str(&format!("{}: {},\n", field.name.to_mixed_case(), op)); + // records are represented as plain objects, so we + // make a new object and set all the fields with an object + // literal. + let mut result = "{\n".to_string(); + for (field, op) in record.fields.iter().zip(operands) { + result.push_str(&format!("{}: {},\n", field.name.to_mixed_case(), op)); + } + result.push_str("}"); + results.push(result); + } + + Instruction::TupleLower { tuple, .. } => { + // Tuples are represented as an array, sowe can use + // destructuring assignment to lower the tuple into its + // components. + let tmp = self.tmp(); + let mut expr = "const [".to_string(); + for i in 0..tuple.types.len() { + if i > 0 { + expr.push_str(", "); } - result.push_str("}"); - results.push(result); + let name = format!("tuple{}_{}", tmp, i); + expr.push_str(&name); + results.push(name); } + self.src.js(&format!("{}] = {};\n", expr, operands[0])); + } + + Instruction::TupleLift { .. } => { + // Tuples are represented as an array, so we just shove all + // the operands into an array. + results.push(format!("[{}]", operands.join(", "))); } Instruction::FlagsLower { flags, .. } => { diff --git a/crates/gen-markdown/src/lib.rs b/crates/gen-markdown/src/lib.rs index b71da58ed..f2aefbb5c 100644 --- a/crates/gen-markdown/src/lib.rs +++ b/crates/gen-markdown/src/lib.rs @@ -68,18 +68,17 @@ impl Markdown { } match &ty.kind { TypeDefKind::Type(t) => self.print_ty(iface, t, false), - TypeDefKind::Record(r) => { - assert!(r.is_tuple()); + TypeDefKind::Tuple(t) => { self.src.push_str("("); - for (i, f) in r.fields.iter().enumerate() { + for (i, t) in t.types.iter().enumerate() { if i > 0 { self.src.push_str(", "); } - self.print_ty(iface, &f.ty, false); + self.print_ty(iface, t, false); } self.src.push_str(")"); } - TypeDefKind::Flags(_) => unreachable!(), + TypeDefKind::Record(_) | TypeDefKind::Flags(_) => unreachable!(), TypeDefKind::Variant(v) => { if let Some(t) = v.as_option() { self.src.push_str("option<"); @@ -183,6 +182,34 @@ impl Generator for Markdown { } } + fn type_tuple( + &mut self, + iface: &Interface, + id: TypeId, + name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + self.print_type_header(name); + self.src.push_str("tuple\n\n"); + self.print_type_info(id, docs); + self.src.push_str("\n### Tuple Fields\n\n"); + for (i, ty) in tuple.types.iter().enumerate() { + self.src.push_str(&format!( + "- [`{name}`](#{r}.{f}): ", + r = name.to_snake_case(), + f = i, + name = i, + )); + self.hrefs.insert( + format!("{}::{}", name, i), + format!("#{}.{}", name.to_snake_case(), i), + ); + self.print_ty(iface, ty, false); + self.src.push_str("\n"); + } + } + fn type_flags( &mut self, _iface: &Interface, diff --git a/crates/gen-rust-wasm/src/lib.rs b/crates/gen-rust-wasm/src/lib.rs index 407cda398..85db18bf9 100644 --- a/crates/gen-rust-wasm/src/lib.rs +++ b/crates/gen-rust-wasm/src/lib.rs @@ -172,6 +172,17 @@ impl Generator for RustWasm { self.print_typedef_record(iface, id, record, docs); } + fn type_tuple( + &mut self, + iface: &Interface, + id: TypeId, + _name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + self.print_typedef_tuple(iface, id, tuple, docs); + } + fn type_flags( &mut self, _iface: &Interface, @@ -944,6 +955,13 @@ impl Bindgen for FunctionBindgen<'_> { self.record_lift(iface, *ty, record, operands, results); } + Instruction::TupleLower { tuple, .. } => { + self.tuple_lower(tuple, &operands[0], results); + } + Instruction::TupleLift { .. } => { + self.tuple_lift(operands, results); + } + Instruction::VariantPayloadName => results.push("e".to_string()), Instruction::VariantLower { diff --git a/crates/gen-rust/src/lib.rs b/crates/gen-rust/src/lib.rs index ac10272e0..02da500a0 100644 --- a/crates/gen-rust/src/lib.rs +++ b/crates/gen-rust/src/lib.rs @@ -225,11 +225,15 @@ pub trait RustGenerator { fn needs_generics(iface: &Interface, ty: &TypeDefKind) -> bool { match ty { - TypeDefKind::Variant(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, + TypeDefKind::Variant(_) + | TypeDefKind::Record(_) + | TypeDefKind::List(_) + | TypeDefKind::Flags(_) + | TypeDefKind::Tuple(_) => true, TypeDefKind::Type(Type::Id(t)) => needs_generics(iface, &iface.types[*t].kind), TypeDefKind::Type(Type::String) => true, TypeDefKind::Type(Type::Handle(_)) => true, - _ => false, + TypeDefKind::Type(_) => false, } } } @@ -266,10 +270,10 @@ pub trait RustGenerator { // Tuple-like records are mapped directly to Rust tuples of // types. Note the trailing comma after each member to // appropriately handle 1-tuples. - TypeDefKind::Record(r) if r.is_tuple() => { + TypeDefKind::Tuple(t) => { self.push_str("("); - for field in r.fields.iter() { - self.print_ty(iface, &field.ty, mode); + for ty in t.types.iter() { + self.print_ty(iface, ty, mode); self.push_str(","); } self.push_str(")"); @@ -382,56 +386,59 @@ pub trait RustGenerator { for (name, mode) in self.modes_of(iface, id) { let lt = self.lifetime_for(&info, mode); self.rustdoc(docs); - if record.is_tuple() { - self.push_str(&format!("pub type {}", name)); - self.print_generics(&info, lt, true); - self.push_str(" = ("); - for field in record.fields.iter() { - self.print_ty(iface, &field.ty, mode); - self.push_str(","); - } - self.push_str(");\n"); - } else { - if !info.owns_data() { - self.push_str("#[repr(C)]\n"); - self.push_str("#[derive(Copy, Clone)]\n"); - } else if !info.has_handle { - self.push_str("#[derive(Clone)]\n"); - } - self.push_str(&format!("pub struct {}", name)); - self.print_generics(&info, lt, true); - self.push_str(" {\n"); - for field in record.fields.iter() { - self.rustdoc(&field.docs); - self.push_str("pub "); - self.push_str(&to_rust_ident(&field.name)); - self.push_str(": "); - self.print_ty(iface, &field.ty, mode); - self.push_str(",\n"); - } - self.push_str("}\n"); + if !info.owns_data() { + self.push_str("#[repr(C)]\n"); + self.push_str("#[derive(Copy, Clone)]\n"); + } else if !info.has_handle { + self.push_str("#[derive(Clone)]\n"); + } + self.push_str(&format!("pub struct {}", name)); + self.print_generics(&info, lt, true); + self.push_str(" {\n"); + for field in record.fields.iter() { + self.rustdoc(&field.docs); + self.push_str("pub "); + self.push_str(&to_rust_ident(&field.name)); + self.push_str(": "); + self.print_ty(iface, &field.ty, mode); + self.push_str(",\n"); + } + self.push_str("}\n"); - self.push_str("impl"); - self.print_generics(&info, lt, true); - self.push_str(" std::fmt::Debug for "); - self.push_str(&name); - self.print_generics(&info, lt, false); - self.push_str(" {\n"); - self.push_str( - "fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", - ); - self.push_str(&format!("f.debug_struct(\"{}\")", name)); - for field in record.fields.iter() { - self.push_str(&format!( - ".field(\"{}\", &self.{})", - field.name, - to_rust_ident(&field.name) - )); - } - self.push_str(".finish()"); - self.push_str("}\n"); - self.push_str("}\n"); + self.push_str("impl"); + self.print_generics(&info, lt, true); + self.push_str(" std::fmt::Debug for "); + self.push_str(&name); + self.print_generics(&info, lt, false); + self.push_str(" {\n"); + self.push_str("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n"); + self.push_str(&format!("f.debug_struct(\"{}\")", name)); + for field in record.fields.iter() { + self.push_str(&format!( + ".field(\"{}\", &self.{})", + field.name, + to_rust_ident(&field.name) + )); } + self.push_str(".finish()"); + self.push_str("}\n"); + self.push_str("}\n"); + } + } + + fn print_typedef_tuple(&mut self, iface: &Interface, id: TypeId, tuple: &Tuple, docs: &Docs) { + let info = self.info(id); + for (name, mode) in self.modes_of(iface, id) { + let lt = self.lifetime_for(&info, mode); + self.rustdoc(docs); + self.push_str(&format!("pub type {}", name)); + self.print_generics(&info, lt, true); + self.push_str(" = ("); + for ty in tuple.types.iter() { + self.print_ty(iface, ty, mode); + self.push_str(","); + } + self.push_str(");\n"); } } @@ -719,35 +726,22 @@ pub trait RustFunctionGenerator { results: &mut Vec, ) { let tmp = self.tmp(); - if record.is_tuple() { - self.push_str("let ("); - for i in 0..record.fields.len() { - let arg = format!("t{}_{}", tmp, i); - self.push_str(&arg); - self.push_str(", "); - results.push(arg); - } - self.push_str(") = "); - self.push_str(operand); - self.push_str(";\n"); - } else { - self.push_str("let "); - let name = self.typename_lower(iface, id); + self.push_str("let "); + let name = self.typename_lower(iface, id); + self.push_str(&name); + self.push_str("{ "); + for field in record.fields.iter() { + let name = to_rust_ident(&field.name); + let arg = format!("{}{}", name, tmp); self.push_str(&name); - self.push_str("{ "); - for field in record.fields.iter() { - let name = to_rust_ident(&field.name); - let arg = format!("{}{}", name, tmp); - self.push_str(&name); - self.push_str(":"); - self.push_str(&arg); - self.push_str(", "); - results.push(arg); - } - self.push_str("} = "); - self.push_str(operand); - self.push_str(";\n"); + self.push_str(":"); + self.push_str(&arg); + self.push_str(", "); + results.push(arg); } + self.push_str("} = "); + self.push_str(operand); + self.push_str(";\n"); } fn record_lift( @@ -758,23 +752,37 @@ pub trait RustFunctionGenerator { operands: &[String], results: &mut Vec, ) { - if ty.is_tuple() { - if operands.len() == 1 { - results.push(format!("({},)", operands[0])); - } else { - results.push(format!("({})", operands.join(", "))); - } + let mut result = self.typename_lift(iface, id); + result.push_str("{"); + for (field, val) in ty.fields.iter().zip(operands) { + result.push_str(&to_rust_ident(&field.name)); + result.push_str(":"); + result.push_str(&val); + result.push_str(", "); + } + result.push_str("}"); + results.push(result); + } + + fn tuple_lower(&mut self, tuple: &Tuple, operand: &str, results: &mut Vec) { + let tmp = self.tmp(); + self.push_str("let ("); + for i in 0..tuple.types.len() { + let arg = format!("t{}_{}", tmp, i); + self.push_str(&arg); + self.push_str(", "); + results.push(arg); + } + self.push_str(") = "); + self.push_str(operand); + self.push_str(";\n"); + } + + fn tuple_lift(&mut self, operands: &[String], results: &mut Vec) { + if operands.len() == 1 { + results.push(format!("({},)", operands[0])); } else { - let mut result = self.typename_lift(iface, id); - result.push_str("{"); - for (field, val) in ty.fields.iter().zip(operands) { - result.push_str(&to_rust_ident(&field.name)); - result.push_str(":"); - result.push_str(&val); - result.push_str(", "); - } - result.push_str("}"); - results.push(result); + results.push(format!("({})", operands.join(", "))); } } diff --git a/crates/gen-spidermonkey/src/lib.rs b/crates/gen-spidermonkey/src/lib.rs index edf35554f..50ae4ad87 100644 --- a/crates/gen-spidermonkey/src/lib.rs +++ b/crates/gen-spidermonkey/src/lib.rs @@ -16,7 +16,8 @@ use wasm_encoder::Instruction; use wit_bindgen_gen_core::{ wit_parser::{ abi::{self, AbiVariant, WasmSignature, WasmType}, - Docs, Flags, Function, Interface, Record, ResourceId, SizeAlign, Type, TypeId, Variant, + Docs, Flags, Function, Interface, Record, ResourceId, SizeAlign, Tuple, Type, TypeId, + Variant, }, Direction, Files, Generator, }; @@ -951,6 +952,18 @@ impl Generator for SpiderMonkeyWasm<'_> { todo!() } + fn type_tuple( + &mut self, + iface: &Interface, + id: TypeId, + name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + let _ = (iface, id, name, tuple, docs); + todo!() + } + fn type_flags( &mut self, iface: &Interface, @@ -1889,6 +1902,8 @@ impl abi::Bindgen for Bindgen<'_, '_> { name: _, ty: _, } => todo!(), + abi::Instruction::TupleLower { tuple: _, ty: _ } => todo!(), + abi::Instruction::TupleLift { tuple: _, ty: _ } => todo!(), abi::Instruction::FlagsLower { flags: _, name: _, diff --git a/crates/gen-wasmtime-py/src/lib.rs b/crates/gen-wasmtime-py/src/lib.rs index 76fc70619..a44937e01 100644 --- a/crates/gen-wasmtime-py/src/lib.rs +++ b/crates/gen-wasmtime-py/src/lib.rs @@ -410,9 +410,7 @@ impl WasmtimePy { } match &ty.kind { TypeDefKind::Type(t) => self.print_ty(iface, t), - TypeDefKind::Record(r) if r.is_tuple() => { - self.print_tuple(iface, r.fields.iter().map(|f| &f.ty)) - } + TypeDefKind::Tuple(t) => self.print_tuple(iface, t), TypeDefKind::Record(_) | TypeDefKind::Flags(_) => unreachable!(), TypeDefKind::Variant(v) => { if let Some(t) = v.as_option() { @@ -443,14 +441,13 @@ impl WasmtimePy { } } - fn print_tuple<'a>(&mut self, iface: &Interface, types: impl IntoIterator) { - let types = types.into_iter().collect::>(); - if types.is_empty() { + fn print_tuple(&mut self, iface: &Interface, tuple: &Tuple) { + if tuple.types.is_empty() { return self.src.push_str("None"); } self.pyimport("typing", "Tuple"); self.src.push_str("Tuple["); - for (i, t) in types.iter().enumerate() { + for (i, t) in tuple.types.iter().enumerate() { if i > 0 { self.src.push_str(", "); } @@ -581,26 +578,35 @@ impl Generator for WasmtimePy { docs: &Docs, ) { self.docs(docs); - if record.is_tuple() { - self.src.push_str(&format!("{} = ", name.to_camel_case())); - self.print_tuple(iface, record.fields.iter().map(|f| &f.ty)); - } else { - self.pyimport("dataclasses", "dataclass"); + self.pyimport("dataclasses", "dataclass"); + self.src + .push_str(&format!("@dataclass\nclass {}:\n", name.to_camel_case())); + self.indent(); + for field in record.fields.iter() { + self.docs(&field.docs); self.src - .push_str(&format!("@dataclass\nclass {}:\n", name.to_camel_case())); - self.indent(); - for field in record.fields.iter() { - self.docs(&field.docs); - self.src - .push_str(&format!("{}: ", field.name.to_snake_case())); - self.print_ty(iface, &field.ty); - self.src.push_str("\n"); - } - if record.fields.is_empty() { - self.src.push_str("pass\n"); - } - self.deindent(); + .push_str(&format!("{}: ", field.name.to_snake_case())); + self.print_ty(iface, &field.ty); + self.src.push_str("\n"); } + if record.fields.is_empty() { + self.src.push_str("pass\n"); + } + self.deindent(); + self.src.push_str("\n"); + } + + fn type_tuple( + &mut self, + iface: &Interface, + _id: TypeId, + name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + self.docs(docs); + self.src.push_str(&format!("{} = ", name.to_camel_case())); + self.print_tuple(iface, tuple); self.src.push_str("\n"); } @@ -1494,43 +1500,43 @@ impl Bindgen for FunctionBindgen<'_> { if record.fields.is_empty() { return; } - if record.is_tuple() { - self.src.push_str("("); - for _ in 0..record.fields.len() { - let name = self.locals.tmp("tuplei"); - self.src.push_str(&name); - self.src.push_str(","); - results.push(name); - } - self.src.push_str(") = "); - self.src.push_str(&operands[0]); - self.src.push_str("\n"); - } else { - let tmp = self.locals.tmp("record"); - self.src.push_str(&format!("{} = {}\n", tmp, operands[0])); - for field in record.fields.iter() { - let name = self.locals.tmp("field"); - self.src.push_str(&format!( - "{} = {}.{}\n", - name, - tmp, - field.name.to_snake_case(), - )); - results.push(name); - } + let tmp = self.locals.tmp("record"); + self.src.push_str(&format!("{} = {}\n", tmp, operands[0])); + for field in record.fields.iter() { + let name = self.locals.tmp("field"); + self.src.push_str(&format!( + "{} = {}.{}\n", + name, + tmp, + field.name.to_snake_case(), + )); + results.push(name); } } - Instruction::RecordLift { record, name, .. } => { - if record.is_tuple() { - if operands.is_empty() { - results.push("None".to_string()); - } else { - results.push(format!("({},)", operands.join(", "))); - } + Instruction::RecordLift { name, .. } => { + results.push(format!("{}({})", name.to_camel_case(), operands.join(", "))); + } + Instruction::TupleLower { tuple, .. } => { + if tuple.types.is_empty() { + return; + } + self.src.push_str("("); + for _ in 0..tuple.types.len() { + let name = self.locals.tmp("tuplei"); + self.src.push_str(&name); + self.src.push_str(","); + results.push(name); + } + self.src.push_str(") = "); + self.src.push_str(&operands[0]); + self.src.push_str("\n"); + } + Instruction::TupleLift { .. } => { + if operands.is_empty() { + results.push("None".to_string()); } else { - let name = name.unwrap(); - results.push(format!("{}({})", name.to_camel_case(), operands.join(", "))); + results.push(format!("({},)", operands.join(", "))); } } Instruction::FlagsLift { name, .. } => { diff --git a/crates/gen-wasmtime/src/lib.rs b/crates/gen-wasmtime/src/lib.rs index 0edc28932..63af19557 100644 --- a/crates/gen-wasmtime/src/lib.rs +++ b/crates/gen-wasmtime/src/lib.rs @@ -335,7 +335,6 @@ impl Generator for Wasmtime { // is usable. if self.modes_of(iface, id).len() > 0 && record.fields.iter().all(|f| iface.all_bits_valid(&f.ty)) - && !record.is_tuple() { self.src.push_str("impl wit_bindgen_wasmtime::Endian for "); self.src.push_str(&name.to_camel_case()); @@ -375,6 +374,17 @@ impl Generator for Wasmtime { } } + fn type_tuple( + &mut self, + iface: &Interface, + id: TypeId, + _name: &str, + tuple: &Tuple, + docs: &Docs, + ) { + self.print_typedef_tuple(iface, id, tuple, docs); + } + fn type_flags( &mut self, _iface: &Interface, @@ -1540,6 +1550,13 @@ impl Bindgen for FunctionBindgen<'_> { self.record_lift(iface, *ty, record, operands, results); } + Instruction::TupleLower { tuple, .. } => { + self.tuple_lower(tuple, &operands[0], results); + } + Instruction::TupleLift { .. } => { + self.tuple_lift(operands, results); + } + Instruction::FlagsLower { flags, .. } => { let tmp = self.tmp(); self.push_str(&format!("let flags{} = {};\n", tmp, operands[0])); diff --git a/crates/parser/src/abi.rs b/crates/parser/src/abi.rs index 998af4b7f..388286306 100644 --- a/crates/parser/src/abi.rs +++ b/crates/parser/src/abi.rs @@ -1,7 +1,7 @@ use crate::sizealign::align_to; use crate::{ - Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Type, TypeDefKind, TypeId, - Variant, + Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Tuple, Type, TypeDefKind, + TypeId, Variant, }; use std::mem; @@ -499,7 +499,7 @@ def_instruction! { /// its fields, and then pushes the fields onto the stack. RecordLower { record: &'a Record, - name: Option<&'a str>, + name: &'a str, ty: TypeId, } : [1] => [record.fields.len()], @@ -507,10 +507,24 @@ def_instruction! { /// into a record. RecordLift { record: &'a Record, - name: Option<&'a str>, + name: &'a str, ty: TypeId, } : [record.fields.len()] => [1], + /// Pops a tuple value off the stack, decomposes the tuple to all of + /// its fields, and then pushes the fields onto the stack. + TupleLower { + tuple: &'a Tuple, + ty: TypeId, + } : [1] => [tuple.types.len()], + + /// Pops all fields for a tuple off the stack and then composes them + /// into a tuple. + TupleLift { + tuple: &'a Tuple, + ty: TypeId, + } : [tuple.types.len()] => [1], + /// Converts a language-specific record-of-bools to a list of `i32`. FlagsLower { flags: &'a Flags, @@ -910,6 +924,12 @@ impl Interface { } } + TypeDefKind::Tuple(t) => { + for ty in t.types.iter() { + self.push_wasm(variant, ty, result); + } + } + TypeDefKind::Flags(r) => { for _ in 0..r.repr().count() { result.push(WasmType::I32); @@ -1405,7 +1425,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&RecordLower { record, ty: id, - name: self.iface.types[id].name.as_deref(), + name: self.iface.types[id].name.as_deref().unwrap(), }); let values = self .stack @@ -1416,6 +1436,17 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lower(&field.ty); } } + TypeDefKind::Tuple(tuple) => { + self.emit(&TupleLower { tuple, ty: id }); + let values = self + .stack + .drain(self.stack.len() - tuple.types.len()..) + .collect::>(); + for (ty, value) in tuple.types.iter().zip(values) { + self.stack.push(value); + self.lower(ty); + } + } TypeDefKind::Flags(flags) => { self.emit(&FlagsLower { @@ -1569,9 +1600,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&RecordLift { record, ty: id, - name: self.iface.types[id].name.as_deref(), + name: self.iface.types[id].name.as_deref().unwrap(), }); } + TypeDefKind::Tuple(tuple) => { + let mut temp = Vec::new(); + self.iface.push_wasm(self.variant, ty, &mut temp); + let mut args = self + .stack + .drain(self.stack.len() - temp.len()..) + .collect::>(); + for ty in tuple.types.iter() { + temp.truncate(0); + self.iface.push_wasm(self.variant, ty, &mut temp); + self.stack.extend(args.drain(..temp.len())); + self.lift(ty); + } + self.emit(&TupleLift { tuple, ty: id }); + } TypeDefKind::Flags(flags) => { self.emit(&FlagsLift { flags, @@ -1663,27 +1709,17 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&RecordLower { record, ty: id, - name: self.iface.types[id].name.as_deref(), + name: self.iface.types[id].name.as_deref().unwrap(), }); - let fields = self - .stack - .drain(self.stack.len() - record.fields.len()..) - .collect::>(); - for ((field_offset, op), field) in self - .bindgen - .sizes() - .field_offsets(record) - .into_iter() - .zip(fields) - .zip(&record.fields) - { - self.stack.push(op); - self.write_to_memory( - &field.ty, - addr.clone(), - offset + (field_offset as i32), - ); - } + self.write_fields_to_memory( + &record.fields.iter().map(|f| f.ty).collect::>(), + addr, + offset, + ); + } + TypeDefKind::Tuple(tuple) => { + self.emit(&TupleLower { tuple, ty: id }); + self.write_fields_to_memory(&tuple.types, addr, offset); } TypeDefKind::Flags(f) => { @@ -1749,6 +1785,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::I32Store { offset }); } + fn write_fields_to_memory(&mut self, tys: &[Type], addr: B::Operand, offset: i32) { + let fields = self + .stack + .drain(self.stack.len() - tys.len()..) + .collect::>(); + for ((field_offset, op), ty) in self + .bindgen + .sizes() + .field_offsets(tys.iter()) + .into_iter() + .zip(fields) + .zip(tys) + { + self.stack.push(op); + self.write_to_memory(ty, addr.clone(), offset + (field_offset as i32)); + } + } + fn lower_and_emit(&mut self, ty: &Type, addr: B::Operand, instr: &Instruction) { self.lower(ty); self.stack.push(addr); @@ -1782,25 +1836,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { // as we go along, then aggregate all the fields into the // record. TypeDefKind::Record(record) => { - for (field_offset, field) in self - .bindgen - .sizes() - .field_offsets(record) - .into_iter() - .zip(&record.fields) - { - self.read_from_memory( - &field.ty, - addr.clone(), - offset + (field_offset as i32), - ); - } + self.read_fields_from_memory( + &record.fields.iter().map(|f| f.ty).collect::>(), + addr, + offset, + ); self.emit(&RecordLift { record, ty: id, - name: self.iface.types[id].name.as_deref(), + name: self.iface.types[id].name.as_deref().unwrap(), }); } + TypeDefKind::Tuple(tuple) => { + self.read_fields_from_memory(&tuple.types, addr, offset); + self.emit(&TupleLift { tuple, ty: id }); + } TypeDefKind::Flags(f) => { match f.repr() { @@ -1860,6 +1910,12 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } + fn read_fields_from_memory(&mut self, tys: &[Type], addr: B::Operand, offset: i32) { + for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys).into_iter().zip(tys) { + self.read_from_memory(ty, addr.clone(), offset + (field_offset as i32)); + } + } + fn emit_and_lift(&mut self, ty: &Type, addr: B::Operand, instr: &Instruction) { self.stack.push(addr); self.emit(instr); diff --git a/crates/parser/src/ast.rs b/crates/parser/src/ast.rs index 23d24cfba..85ecaccac 100644 --- a/crates/parser/src/ast.rs +++ b/crates/parser/src/ast.rs @@ -93,10 +93,10 @@ enum Type<'a> { Record(Record<'a>), Flags(Flags<'a>), Variant(Variant<'a>), + Tuple(Vec>), } struct Record<'a> { - tuple_hint: bool, fields: Vec>, } @@ -261,7 +261,6 @@ impl<'a> TypeDef<'a> { tokens.expect(Token::Record)?; let name = parse_id(tokens)?; let ty = Type::Record(Record { - tuple_hint: false, fields: parse_list( tokens, Token::LeftBrace, @@ -461,24 +460,13 @@ impl<'a> Type<'a> { // tuple Some((_span, Token::Tuple)) => { - let mut i = 0; - let fields = parse_list( + let types = parse_list( tokens, Token::LessThan, Token::GreaterThan, - |docs, tokens| { - i += 1; - Ok(Field { - docs, - name: (i - 1).to_string().into(), - ty: Type::parse(tokens)?, - }) - }, + |_docs, tokens| Type::parse(tokens), )?; - Ok(Type::Record(Record { - fields, - tuple_hint: true, - })) + Ok(Type::Tuple(types)) } Some((_span, Token::Unit)) => Ok(Type::Unit), diff --git a/crates/parser/src/ast/resolve.rs b/crates/parser/src/ast/resolve.rs index 964ee6fc1..1a021b57f 100644 --- a/crates/parser/src/ast/resolve.rs +++ b/crates/parser/src/ast/resolve.rs @@ -22,6 +22,7 @@ enum Key { Variant(Vec<(String, Option)>), Record(Vec<(String, Type)>), Flags(Vec), + Tuple(Vec), List(Type), } @@ -203,9 +204,15 @@ impl Resolver { ty: self.copy_type(dep_name, dep, field.ty), }) .collect(), - kind: r.kind, }), TypeDefKind::Flags(f) => TypeDefKind::Flags(f.clone()), + TypeDefKind::Tuple(t) => TypeDefKind::Tuple(Tuple { + types: t + .types + .iter() + .map(|ty| self.copy_type(dep_name, dep, *ty)) + .collect(), + }), TypeDefKind::Variant(v) => TypeDefKind::Variant(Variant { cases: v .cases @@ -366,14 +373,7 @@ impl Resolver { }) }) .collect::>>()?; - TypeDefKind::Record(Record { - kind: if record.tuple_hint { - RecordKind::Tuple - } else { - RecordKind::infer(&fields) - }, - fields, - }) + TypeDefKind::Record(Record { fields }) } super::Type::Flags(flags) => { let flags = flags @@ -386,6 +386,13 @@ impl Resolver { .collect::>(); TypeDefKind::Flags(Flags { flags }) } + super::Type::Tuple(types) => { + let types = types + .iter() + .map(|ty| self.resolve_type(ty)) + .collect::>>()?; + TypeDefKind::Tuple(Tuple { types }) + } super::Type::Variant(variant) => { if variant.cases.is_empty() { return Err(Error { @@ -473,6 +480,7 @@ impl Resolver { TypeDefKind::Flags(r) => { Key::Flags(r.flags.iter().map(|f| f.name.clone()).collect::>()) } + TypeDefKind::Tuple(t) => Key::Tuple(t.types.clone()), TypeDefKind::List(ty) => Key::List(*ty), }; let types = &mut self.types; @@ -631,6 +639,13 @@ impl Resolver { } } } + TypeDefKind::Tuple(t) => { + for ty in t.types.iter() { + if let Type::Id(id) = *ty { + self.validate_type_not_recursive(span, id, visiting, valid)?; + } + } + } TypeDefKind::Flags(_) | TypeDefKind::List(_) | TypeDefKind::Type(_) => {} } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index c37e7ab04..0c79951af 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -47,6 +47,7 @@ pub struct TypeDef { pub enum TypeDefKind { Record(Record), Flags(Flags), + Tuple(Tuple), Variant(Variant), List(Type), Type(Type), @@ -83,13 +84,6 @@ pub enum Int { #[derive(Debug)] pub struct Record { pub fields: Vec, - pub kind: RecordKind, -} - -#[derive(Copy, Clone, Debug)] -pub enum RecordKind { - Other, - Tuple, } #[derive(Debug)] @@ -99,31 +93,6 @@ pub struct Field { pub ty: Type, } -impl Record { - pub fn is_tuple(&self) -> bool { - matches!(self.kind, RecordKind::Tuple) - } -} - -impl RecordKind { - fn infer(fields: &[Field]) -> RecordKind { - if fields.is_empty() { - return RecordKind::Other; - } - - // fields with consecutive integer names get represented as tuples. - if fields - .iter() - .enumerate() - .all(|(i, m)| m.name.as_str().parse().ok() == Some(i)) - { - return RecordKind::Tuple; - } - - return RecordKind::Other; - } -} - #[derive(Debug, Clone)] pub struct Flags { pub flags: Vec, @@ -162,6 +131,11 @@ impl FlagsRepr { } } +#[derive(Debug, Clone)] +pub struct Tuple { + pub types: Vec, +} + #[derive(Debug)] pub struct Variant { pub cases: Vec, @@ -413,6 +387,11 @@ impl Interface { self.topo_visit_ty(&f.ty, list, visited); } } + TypeDefKind::Tuple(t) => { + for t in t.types.iter() { + self.topo_visit_ty(t, list, visited); + } + } TypeDefKind::Variant(v) => { for v in v.cases.iter() { if let Some(ty) = &v.ty { @@ -450,6 +429,7 @@ impl Interface { TypeDefKind::List(_) | TypeDefKind::Variant(_) => false, TypeDefKind::Type(t) => self.all_bits_valid(t), TypeDefKind::Record(r) => r.fields.iter().all(|f| self.all_bits_valid(&f.ty)), + TypeDefKind::Tuple(t) => t.types.iter().all(|t| self.all_bits_valid(t)), // FIXME: this could perhaps be `true` for multiples-of-32 but // seems better to probably leave this as unconditionally diff --git a/crates/parser/src/sizealign.rs b/crates/parser/src/sizealign.rs index 5037f8bf4..9edb95066 100644 --- a/crates/parser/src/sizealign.rs +++ b/crates/parser/src/sizealign.rs @@ -1,4 +1,4 @@ -use crate::{FlagsRepr, Int, Interface, Record, Type, TypeDef, TypeDefKind, Variant}; +use crate::{FlagsRepr, Int, Interface, Type, TypeDef, TypeDefKind, Variant}; #[derive(Default)] pub struct SizeAlign { @@ -19,6 +19,7 @@ impl SizeAlign { TypeDefKind::Type(t) => (self.size(t), self.align(t)), TypeDefKind::List(_) => (8, 4), TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)), + TypeDefKind::Tuple(t) => self.record(t.types.iter()), TypeDefKind::Flags(f) => match f.repr() { FlagsRepr::U8 => (1, 1), FlagsRepr::U16 => (2, 2), @@ -64,14 +65,13 @@ impl SizeAlign { } } - pub fn field_offsets(&self, record: &Record) -> Vec { + pub fn field_offsets<'a>(&self, types: impl IntoIterator) -> Vec { let mut cur = 0; - record - .fields - .iter() - .map(|field| { - let ret = align_to(cur, self.align(&field.ty)); - cur = ret + self.size(&field.ty); + types + .into_iter() + .map(|ty| { + let ret = align_to(cur, self.align(ty)); + cur = ret + self.size(ty); ret }) .collect() diff --git a/crates/parser/tests/all.rs b/crates/parser/tests/all.rs index 1ac96449d..b3f64bcd1 100644 --- a/crates/parser/tests/all.rs +++ b/crates/parser/tests/all.rs @@ -197,6 +197,9 @@ fn to_json(i: &Interface) -> String { Variant { cases: Vec<(String, Option)>, }, + Tuple { + types: Vec, + }, List(String), } @@ -271,6 +274,9 @@ fn to_json(i: &Interface) -> String { .map(|f| (f.name.clone(), translate_type(&f.ty))) .collect(), }, + TypeDefKind::Tuple(t) => Type::Tuple { + types: t.types.iter().map(|ty| translate_type(ty)).collect(), + }, TypeDefKind::Flags(r) => Type::Flags { flags: r.flags.iter().map(|f| f.name.clone()).collect(), }, diff --git a/crates/parser/tests/ui/functions.wit.result b/crates/parser/tests/ui/functions.wit.result index 3c9f11d92..f48ba3d83 100644 --- a/crates/parser/tests/ui/functions.wit.result +++ b/crates/parser/tests/ui/functions.wit.result @@ -2,16 +2,10 @@ "types": [ { "idx": 0, - "record": { - "fields": [ - [ - "0", - "u32" - ], - [ - "1", - "u32" - ] + "tuple": { + "types": [ + "u32", + "u32" ] } }, diff --git a/crates/parser/tests/ui/import-me.wit.result b/crates/parser/tests/ui/import-me.wit.result index 35ab127fb..94a04f5eb 100644 --- a/crates/parser/tests/ui/import-me.wit.result +++ b/crates/parser/tests/ui/import-me.wit.result @@ -22,20 +22,11 @@ { "idx": 3, "name": "some-record", - "record": { - "fields": [ - [ - "0", - "u32" - ], - [ - "1", - "u64" - ], - [ - "2", - "float32" - ] + "tuple": { + "types": [ + "u32", + "u64", + "float32" ] } } diff --git a/crates/parser/tests/ui/imports.wit.result b/crates/parser/tests/ui/imports.wit.result index 101e9c240..dbe0a3452 100644 --- a/crates/parser/tests/ui/imports.wit.result +++ b/crates/parser/tests/ui/imports.wit.result @@ -29,20 +29,11 @@ { "idx": 3, "name": "some-record", - "record": { - "fields": [ - [ - "0", - "u32" - ], - [ - "1", - "u64" - ], - [ - "2", - "float32" - ] + "tuple": { + "types": [ + "u32", + "u64", + "float32" ] }, "foreign_module": "import-me" diff --git a/crates/parser/tests/ui/imports2.wit.result b/crates/parser/tests/ui/imports2.wit.result index d0bb44f0f..aa3f1fd0c 100644 --- a/crates/parser/tests/ui/imports2.wit.result +++ b/crates/parser/tests/ui/imports2.wit.result @@ -21,20 +21,11 @@ { "idx": 2, "name": "some-record", - "record": { - "fields": [ - [ - "0", - "u32" - ], - [ - "1", - "u64" - ], - [ - "2", - "float32" - ] + "tuple": { + "types": [ + "u32", + "u64", + "float32" ] }, "foreign_module": "import-me" diff --git a/crates/parser/tests/ui/types.wit.result b/crates/parser/tests/ui/types.wit.result index a36f72571..a3d56ef78 100644 --- a/crates/parser/tests/ui/types.wit.result +++ b/crates/parser/tests/ui/types.wit.result @@ -244,47 +244,35 @@ { "idx": 27, "name": "t26", - "record": { - "fields": [] + "tuple": { + "types": [] } }, { "idx": 28, "name": "t27", - "record": { - "fields": [ - [ - "0", - "u32" - ] + "tuple": { + "types": [ + "u32" ] } }, { "idx": 29, "name": "t28", - "record": { - "fields": [ - [ - "0", - "u32" - ] + "tuple": { + "types": [ + "u32" ] } }, { "idx": 30, "name": "t29", - "record": { - "fields": [ - [ - "0", - "u32" - ], - [ - "1", - "u64" - ] + "tuple": { + "types": [ + "u32", + "u64" ] } }, diff --git a/crates/parser/tests/ui/values.wit.result b/crates/parser/tests/ui/values.wit.result index aaca1e9e6..d04150c13 100644 --- a/crates/parser/tests/ui/values.wit.result +++ b/crates/parser/tests/ui/values.wit.result @@ -9,22 +9,16 @@ }, { "idx": 1, - "record": { - "fields": [] + "tuple": { + "types": [] } }, { "idx": 2, - "record": { - "fields": [ - [ - "0", - "u32" - ], - [ - "1", - "type-0" - ] + "tuple": { + "types": [ + "u32", + "type-0" ] } } diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 1cf23b5ad..864c89364 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -180,8 +180,9 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { quote::quote! { Vec<#t> } } TypeDefKind::Flags(_) => panic!("unknown flags"), - TypeDefKind::Record(r) => { - let fields = r.fields.iter().map(|f| quote_ty(param, iface, &f.ty)); + TypeDefKind::Record(_) => panic!("unknown record"), + TypeDefKind::Tuple(t) => { + let fields = t.types.iter().map(|ty| quote_ty(param, iface, ty)); quote::quote! { (#(#fields,)*) } } TypeDefKind::Variant(v) => { diff --git a/crates/wasmlink/src/adapter/call.rs b/crates/wasmlink/src/adapter/call.rs index f5364266f..d775ef3e6 100644 --- a/crates/wasmlink/src/adapter/call.rs +++ b/crates/wasmlink/src/adapter/call.rs @@ -2,8 +2,7 @@ use crate::module::Interface; use std::collections::HashMap; use wasm_encoder::{BlockType, Instruction, MemArg, ValType}; use wit_parser::{ - abi::WasmSignature, Function, Int, Interface as WitInterface, RecordKind, SizeAlign, Type, - TypeDefKind, + abi::WasmSignature, Function, Int, Interface as WitInterface, SizeAlign, Type, TypeDefKind, }; // The parent's memory is imported, so it is always index 0 for the adapter logic @@ -456,21 +455,33 @@ impl<'a> CallAdapter<'a> { params.next().unwrap(); } } - TypeDefKind::Record(r) => match r.kind { - RecordKind::Tuple | RecordKind::Other => { - for f in &r.fields { - Self::push_operands( - interface, - sizes, - &f.ty, - params, - mode, - locals_count, - operands, - ); - } + TypeDefKind::Record(r) => { + for f in &r.fields { + Self::push_operands( + interface, + sizes, + &f.ty, + params, + mode, + locals_count, + operands, + ); } - }, + } + TypeDefKind::Tuple(t) => { + for ty in &t.types { + Self::push_operands( + interface, + sizes, + ty, + params, + mode, + locals_count, + operands, + ); + } + } + TypeDefKind::Variant(v) if v.is_enum() => { params.next().unwrap(); } @@ -608,23 +619,38 @@ impl<'a> CallAdapter<'a> { }); } TypeDefKind::Flags(_) => {} - TypeDefKind::Record(r) => match r.kind { - RecordKind::Tuple | RecordKind::Other => { - let offsets = sizes.field_offsets(r); + TypeDefKind::Record(r) => { + let offsets = sizes.field_offsets(r.fields.iter().map(|f| &f.ty)); - for (f, o) in r.fields.iter().zip(offsets) { - Self::push_element_operands( - interface, - sizes, - &f.ty, - offset + o as u32, - mode, - locals_count, - operands, - ); - } + for (f, o) in r.fields.iter().zip(offsets) { + Self::push_element_operands( + interface, + sizes, + &f.ty, + offset + o as u32, + mode, + locals_count, + operands, + ); } - }, + } + + TypeDefKind::Tuple(t) => { + let offsets = sizes.field_offsets(&t.types); + + for (ty, o) in t.types.iter().zip(offsets) { + Self::push_element_operands( + interface, + sizes, + ty, + offset + o as u32, + mode, + locals_count, + operands, + ); + } + } + TypeDefKind::Variant(v) if v.is_enum() => {} TypeDefKind::Variant(v) => { let payload_offset = sizes.payload_offset(v) as u32; diff --git a/crates/wasmlink/src/module.rs b/crates/wasmlink/src/module.rs index 255439a6d..9ea4d7ced 100644 --- a/crates/wasmlink/src/module.rs +++ b/crates/wasmlink/src/module.rs @@ -50,6 +50,7 @@ fn has_list(interface: &WitInterface, ty: &WitType) -> bool { TypeDefKind::Type(t) => has_list(interface, t), TypeDefKind::Flags(_) => false, TypeDefKind::Record(r) => r.fields.iter().any(|f| has_list(interface, &f.ty)), + TypeDefKind::Tuple(t) => t.types.iter().any(|ty| has_list(interface, ty)), TypeDefKind::Variant(v) => v.cases.iter().any(|c| { c.ty.as_ref() .map(|t| has_list(interface, t)) diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs index 08eabbf36..1e3b85fd7 100644 --- a/crates/wit-component/src/decoding.rs +++ b/crates/wit-component/src/decoding.rs @@ -5,8 +5,8 @@ use wasmparser::{ Validator, WasmFeatures, }; use wit_parser::{ - validate_id, Case, Docs, Field, Flag, Flags, Function, FunctionKind, Interface, Record, - RecordKind, Type, TypeDef, TypeDefKind, TypeId, Variant, + validate_id, Case, Docs, Field, Flag, Flags, Function, FunctionKind, Interface, Record, Tuple, + Type, TypeDef, TypeDefKind, TypeId, Variant, }; /// Represents information about a decoded WebAssembly component. @@ -365,7 +365,6 @@ impl<'a> InterfaceDecoder<'a> { }) }) .collect::>()?, - kind: RecordKind::Other, }; Ok(Type::Id(self.alloc_type( @@ -420,22 +419,14 @@ impl<'a> InterfaceDecoder<'a> { name: Option, tys: &[types::InterfaceTypeRef], ) -> Result { - let record = Record { - fields: tys + let tuple = Tuple { + types: tys .iter() - .enumerate() - .map(|(i, ty)| { - Ok(Field { - docs: Docs::default(), - name: i.to_string(), - ty: self.decode_type(ty)?, - }) - }) + .map(|ty| self.decode_type(ty)) .collect::>()?, - kind: RecordKind::Tuple, }; - Ok(Type::Id(self.alloc_type(name, TypeDefKind::Record(record)))) + Ok(Type::Id(self.alloc_type(name, TypeDefKind::Tuple(tuple)))) } fn decode_flags( diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 01f301d10..fe5babaa4 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -13,8 +13,7 @@ use wasm_encoder::*; use wasmparser::{Validator, WasmFeatures}; use wit_parser::{ abi::{AbiVariant, WasmSignature, WasmType}, - Flags, Function, FunctionKind, Interface, Record, RecordKind, Type, TypeDef, TypeDefKind, - Variant, + Flags, Function, FunctionKind, Interface, Record, Tuple, Type, TypeDef, TypeDefKind, Variant, }; const INDIRECT_TABLE_NAME: &str = "$imports"; @@ -91,6 +90,22 @@ impl PartialEq for TypeDefKey<'_> { }) }) } + (TypeDefKind::Tuple(t1), TypeDefKind::Tuple(t2)) => { + if t1.types.len() != t2.types.len() { + return false; + } + + t1.types.iter().zip(t2.types.iter()).all(|(t1, t2)| { + TypeKey { + interface: self.interface, + ty: *t1, + } + .eq(&TypeKey { + interface: other.interface, + ty: *t2, + }) + }) + } (TypeDefKind::Flags(f1), TypeDefKind::Flags(f2)) => { if f1.flags.len() != f2.flags.len() { return false; @@ -152,14 +167,24 @@ impl Hash for TypeDefKey<'_> { .hash(state); } } - TypeDefKind::Flags(r) => { + TypeDefKind::Tuple(t) => { state.write_u8(1); + for ty in &t.types { + TypeKey { + interface: self.interface, + ty: *ty, + } + .hash(state); + } + } + TypeDefKind::Flags(r) => { + state.write_u8(2); for f in &r.flags { f.name.hash(state); } } TypeDefKind::Variant(v) => { - state.write_u8(2); + state.write_u8(3); for c in &v.cases { c.name.hash(state); c.ty.map(|ty| TypeKey { @@ -170,7 +195,7 @@ impl Hash for TypeDefKey<'_> { } } TypeDefKind::List(ty) => { - state.write_u8(3); + state.write_u8(4); TypeKey { interface: self.interface, ty: *ty, @@ -178,7 +203,7 @@ impl Hash for TypeDefKey<'_> { .hash(state); } TypeDefKind::Type(ty) => { - state.write_u8(4); + state.write_u8(5); TypeKey { interface: self.interface, ty: *ty, @@ -362,6 +387,7 @@ impl<'a> TypeEncoder<'a> { } else { let mut encoded = match &ty.kind { TypeDefKind::Record(r) => self.encode_record(interface, instance, r)?, + TypeDefKind::Tuple(t) => self.encode_tuple(interface, instance, t)?, TypeDefKind::Flags(r) => self.encode_flags(r)?, TypeDefKind::Variant(v) => self.encode_variant(interface, instance, v)?, TypeDefKind::List(ty) => { @@ -410,36 +436,38 @@ impl<'a> TypeEncoder<'a> { instance: &mut Option>, record: &Record, ) -> Result { - Ok(match record.kind { - RecordKind::Other => { - let fields = record - .fields - .iter() - .map(|f| { - Ok(( - f.name.as_str(), - self.encode_type(interface, instance, &f.ty)?, - )) - }) - .collect::>>()?; + let fields = record + .fields + .iter() + .map(|f| { + Ok(( + f.name.as_str(), + self.encode_type(interface, instance, &f.ty)?, + )) + }) + .collect::>>()?; - let index = self.types.len(); - let encoder = self.types.interface_type(); - encoder.record(fields); - InterfaceTypeRef::Type(index) - } - RecordKind::Tuple => { - let tys = record - .fields - .iter() - .map(|f| self.encode_type(interface, instance, &f.ty)) - .collect::>>()?; - let index = self.types.len(); - let encoder = self.types.interface_type(); - encoder.tuple(tys); - InterfaceTypeRef::Type(index) - } - }) + let index = self.types.len(); + let encoder = self.types.interface_type(); + encoder.record(fields); + Ok(InterfaceTypeRef::Type(index)) + } + + fn encode_tuple( + &mut self, + interface: &'a Interface, + instance: &mut Option>, + tuple: &Tuple, + ) -> Result { + let tys = tuple + .types + .iter() + .map(|ty| self.encode_type(interface, instance, ty)) + .collect::>>()?; + let index = self.types.len(); + let encoder = self.types.interface_type(); + encoder.tuple(tys); + Ok(InterfaceTypeRef::Type(index)) } fn encode_flags(&mut self, flags: &Flags) -> Result { @@ -584,6 +612,7 @@ impl RequiredOptions { TypeDefKind::Record(r) => { Self::for_types(interface, r.fields.iter().map(|f| &f.ty)) } + TypeDefKind::Tuple(t) => Self::for_types(interface, t.types.iter()), TypeDefKind::Flags(_) => Self::None, TypeDefKind::Variant(v) => { Self::for_types(interface, v.cases.iter().filter_map(|c| c.ty.as_ref())) diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 39cacf1d9..9124782d5 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Result}; use std::collections::HashSet; use std::fmt::Write; -use wit_parser::{Flags, Interface, Record, Type, TypeDefKind, TypeId, Variant}; +use wit_parser::{Flags, Interface, Record, Tuple, Type, TypeDefKind, TypeId, Variant}; /// A utility for printing WebAssembly interface definitions to a string. #[derive(Default)] @@ -69,8 +69,11 @@ impl InterfacePrinter { } match &ty.kind { - TypeDefKind::Record(r) => { - self.print_record_type(interface, r)?; + TypeDefKind::Tuple(t) => { + self.print_tuple_type(interface, t)?; + } + TypeDefKind::Record(_) => { + bail!("interface has an unnamed record type"); } TypeDefKind::Flags(_) => { bail!("interface has unnamed flags type") @@ -93,17 +96,13 @@ impl InterfacePrinter { Ok(()) } - fn print_record_type(&mut self, interface: &Interface, record: &Record) -> Result<()> { - if !record.is_tuple() { - bail!("interface has an unnamed record type"); - } - + fn print_tuple_type(&mut self, interface: &Interface, tuple: &Tuple) -> Result<()> { self.output.push_str("tuple<"); - for (i, field) in record.fields.iter().enumerate() { + for (i, ty) in tuple.types.iter().enumerate() { if i > 0 { self.output.push_str(", "); } - self.print_type_name(interface, &field.ty)?; + self.print_type_name(interface, ty)?; } self.output.push('>'); @@ -163,6 +162,9 @@ impl InterfacePrinter { TypeDefKind::Record(r) => { self.declare_record(interface, ty.name.as_deref(), r)? } + TypeDefKind::Tuple(t) => { + self.declare_tuple(interface, ty.name.as_deref(), t)? + } TypeDefKind::Flags(f) => self.declare_flags(ty.name.as_deref(), f)?, TypeDefKind::Variant(v) => { self.declare_variant(interface, ty.name.as_deref(), v)? @@ -196,15 +198,6 @@ impl InterfacePrinter { self.declare_type(interface, &field.ty)?; } - if record.is_tuple() { - if let Some(name) = name { - write!(&mut self.output, "type {} = ", name)?; - self.print_record_type(interface, record)?; - self.output.push_str("\n\n"); - } - return Ok(()); - } - match name { Some(name) => { writeln!(&mut self.output, "record {} {{", name)?; @@ -221,6 +214,24 @@ impl InterfacePrinter { } } + fn declare_tuple( + &mut self, + interface: &Interface, + name: Option<&str>, + tuple: &Tuple, + ) -> Result<()> { + for ty in tuple.types.iter() { + self.declare_type(interface, ty)?; + } + + if let Some(name) = name { + write!(&mut self.output, "type {} = ", name)?; + self.print_tuple_type(interface, tuple)?; + self.output.push_str("\n\n"); + } + return Ok(()); + } + fn declare_flags(&mut self, name: Option<&str>, flags: &Flags) -> Result<()> { match name { Some(name) => {