Skip to content

Commit 01bec9c

Browse files
authored
WIT: Implement @SInCE and @unstable annotations (#1508)
* Add a span to all types when parsing * Preserve error context when highlighting errors Previously the entire error message was replaced, losing any attached context. This commit updates error highlighting to only augment the single error found in the chain that's being highlighted (in the most common case). This required a small refactoring of the `Error` type and changes all existing users to a method-based constructor rather than explicit struct-based construction. * Add initial parsing of attributes Nothing uses the results of parsing yet, that's going to come in a future commit. * Record spans for all types in `UnresolvedPackage` Will be used for errors in a future commit. * Push stability attributes into top-level AST This commit pushes stability attributes through the resolution process to the next stage of AST. The top-level user-facing types in `wit-parser` now have `Stability` annotations were they can be added. This commit notably changes the `WorldItem::Interface` enum variant to contain a stability attribute in addition to the id listed. * Filter out `@unstable` items that aren't enabled This finishes support for `@unstable` and `@since` in `Resolve` by handling all items there and specifically filtering out any disabled items. * Add CLI support for WIT features * Implement printing WIT stability attributes * Round-trip stability through the wasm binary format This involved a number of refactorings and "tricks" to get this to work out. Namely when possible the old format of the custom section is still emitted to ensure older/newer tools can interoperate when possible. * Fix compile * Fix some compile warnings
1 parent 063f48f commit 01bec9c

File tree

74 files changed

+3862
-1118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3862
-1118
lines changed

crates/wit-component/src/dummy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub fn dummy_module(resolve: &Resolve, world: WorldId) -> Vec<u8> {
1616
push_tys(&mut wat, "result", &sig.results);
1717
wat.push_str("))\n");
1818
}
19-
WorldItem::Interface(import) => {
19+
WorldItem::Interface { id: import, .. } => {
2020
let name = resolve.name_world_key(name);
2121
for (_, func) in resolve.interfaces[*import].functions.iter() {
2222
let sig = resolve.wasm_signature(AbiVariant::GuestImport, func);
@@ -39,7 +39,7 @@ pub fn dummy_module(resolve: &Resolve, world: WorldId) -> Vec<u8> {
3939
// Import any resource-related functions for exports.
4040
for (name, export) in world.exports.iter() {
4141
let export = match export {
42-
WorldItem::Interface(export) => *export,
42+
WorldItem::Interface { id, .. } => *id,
4343
_ => continue,
4444
};
4545
let module = format!("[export]{}", resolve.name_world_key(name));
@@ -64,7 +64,7 @@ pub fn dummy_module(resolve: &Resolve, world: WorldId) -> Vec<u8> {
6464
WorldItem::Function(func) => {
6565
push_func(&mut wat, &func.name, resolve, func);
6666
}
67-
WorldItem::Interface(export) => {
67+
WorldItem::Interface { id: export, .. } => {
6868
let name = resolve.name_world_key(name);
6969
for (_, func) in resolve.interfaces[*export].functions.iter() {
7070
let name = func.core_export_name(Some(&name));

crates/wit-component/src/encoding.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ impl<'a> EncodingState<'a> {
515515
for (name, item) in resolve.worlds[world].imports.iter() {
516516
let func = match item {
517517
WorldItem::Function(f) => f,
518-
WorldItem::Interface(_) | WorldItem::Type(_) => continue,
518+
WorldItem::Interface { .. } | WorldItem::Type(_) => continue,
519519
};
520520
let name = resolve.name_world_key(name);
521521
if !info.lowerings.contains_key(&name) {
@@ -751,8 +751,8 @@ impl<'a> EncodingState<'a> {
751751
self.component
752752
.export(&export_string, ComponentExportKind::Func, idx, None);
753753
}
754-
WorldItem::Interface(export) => {
755-
self.encode_interface_export(&export_string, module, *export)?;
754+
WorldItem::Interface { id, .. } => {
755+
self.encode_interface_export(&export_string, module, *id)?;
756756
}
757757
WorldItem::Type(_) => unreachable!(),
758758
}
@@ -2001,7 +2001,7 @@ impl ComponentEncoder {
20012001
.with_context(|| {
20022002
format!("failed to merge WIT packages of adapter `{name}` into main packages")
20032003
})?
2004-
.worlds[metadata.world.index()];
2004+
.map_world(metadata.world, None)?;
20052005
self.metadata
20062006
.resolve
20072007
.merge_worlds(world, self.metadata.world)

crates/wit-component/src/encoding/wit/v1.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<Compone
3232
};
3333
encoder.run()?;
3434

35-
let package_docs = PackageDocs::extract(resolve, package);
35+
let package_metadata = PackageMetadata::extract(resolve, package);
3636
encoder.component.custom_section(&CustomSection {
37-
name: PackageDocs::SECTION_NAME.into(),
38-
data: package_docs.encode()?.into(),
37+
name: PackageMetadata::SECTION_NAME.into(),
38+
data: package_metadata.encode()?.into(),
3939
});
4040

4141
Ok(encoder.component)
@@ -351,9 +351,9 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
351351
let name = resolve.name_world_key(name);
352352
log::trace!("encoding import {name}");
353353
let ty = match import {
354-
WorldItem::Interface(i) => {
355-
component.interface = Some(*i);
356-
let idx = component.encode_instance(*i)?;
354+
WorldItem::Interface { id, .. } => {
355+
component.interface = Some(*id);
356+
let idx = component.encode_instance(*id)?;
357357
ComponentTypeRef::Instance(idx)
358358
}
359359
WorldItem::Function(f) => {
@@ -376,9 +376,9 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
376376
let name = resolve.name_world_key(name);
377377
log::trace!("encoding export {name}");
378378
let ty = match export {
379-
WorldItem::Interface(i) => {
380-
component.interface = Some(*i);
381-
let idx = component.encode_instance(*i)?;
379+
WorldItem::Interface { id, .. } => {
380+
component.interface = Some(*id);
381+
let idx = component.encode_instance(*id)?;
382382
ComponentTypeRef::Instance(idx)
383383
}
384384
WorldItem::Function(f) => {

crates/wit-component/src/encoding/wit/v2.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<Compone
3232
};
3333
encoder.run()?;
3434

35-
let package_docs = PackageDocs::extract(resolve, package);
35+
let package_metadata = PackageMetadata::extract(resolve, package);
3636
encoder.component.custom_section(&CustomSection {
37-
name: PackageDocs::SECTION_NAME.into(),
38-
data: package_docs.encode()?.into(),
37+
name: PackageMetadata::SECTION_NAME.into(),
38+
data: package_metadata.encode()?.into(),
3939
});
4040

4141
Ok(encoder.component)

crates/wit-component/src/encoding/world.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ impl<'a> ComponentWorld<'a> {
120120
.iter()
121121
.all(|name| match &resolve.worlds[world].exports[name] {
122122
WorldItem::Function(_) => false,
123-
WorldItem::Interface(id) => resolve.interfaces[*id].functions.is_empty(),
123+
WorldItem::Interface { id, .. } => {
124+
resolve.interfaces[*id].functions.is_empty()
125+
}
124126
WorldItem::Type(_) => true,
125127
})
126128
};
@@ -209,7 +211,7 @@ impl<'a> ComponentWorld<'a> {
209211
for name in required_exports {
210212
match &resolve.worlds[world].exports[name] {
211213
WorldItem::Function(func) => add_func(func, None),
212-
WorldItem::Interface(id) => {
214+
WorldItem::Interface { id, .. } => {
213215
let name = resolve.name_world_key(name);
214216
for (_, func) in resolve.interfaces[*id].functions.iter() {
215217
add_func(func, Some(&name));
@@ -277,11 +279,11 @@ impl<'a> ComponentWorld<'a> {
277279
let empty = IndexSet::new();
278280
let import_map_key = match item {
279281
WorldItem::Function(_) | WorldItem::Type(_) => None,
280-
WorldItem::Interface(_) => Some(name),
282+
WorldItem::Interface { .. } => Some(name),
281283
};
282284
let interface_id = match item {
283285
WorldItem::Function(_) | WorldItem::Type(_) => None,
284-
WorldItem::Interface(id) => Some(*id),
286+
WorldItem::Interface { id, .. } => Some(*id),
285287
};
286288
let required = required
287289
.get(import_map_key.as_deref().unwrap_or(BARE_FUNC_MODULE_NAME))
@@ -300,7 +302,7 @@ impl<'a> ComponentWorld<'a> {
300302
WorldItem::Type(ty) => {
301303
interface.add_type(required, resolve, *ty);
302304
}
303-
WorldItem::Interface(id) => {
305+
WorldItem::Interface { id, .. } => {
304306
for (_name, ty) in resolve.interfaces[*id].types.iter() {
305307
interface.add_type(required, resolve, *ty);
306308
}
@@ -344,7 +346,7 @@ impl<'a> ComponentWorld<'a> {
344346
for (name, item) in resolve.worlds[world].exports.iter() {
345347
log::trace!("add live world export `{}`", resolve.name_world_key(name));
346348
let id = match item {
347-
WorldItem::Interface(id) => id,
349+
WorldItem::Interface { id, .. } => id,
348350
WorldItem::Function(_) | WorldItem::Type(_) => {
349351
live.add_world_item(resolve, item);
350352
continue;
@@ -400,7 +402,7 @@ impl<'a> ComponentWorld<'a> {
400402
log::trace!("add live function import `{name}`");
401403
live.add_func(resolve, func);
402404
}
403-
WorldItem::Interface(id) => {
405+
WorldItem::Interface { id, .. } => {
404406
let required = match required.get(name.as_str()) {
405407
Some(set) => set,
406408
None => continue,
@@ -431,7 +433,7 @@ impl<'a> ComponentWorld<'a> {
431433
for (_name, item) in exports.iter() {
432434
let id = match item {
433435
WorldItem::Function(_) => continue,
434-
WorldItem::Interface(id) => *id,
436+
WorldItem::Interface { id, .. } => *id,
435437
WorldItem::Type(_) => unreachable!(),
436438
};
437439
let mut set = HashSet::new();

crates/wit-component/src/metadata.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl Default for Bindgen {
9292
includes: Default::default(),
9393
include_names: Default::default(),
9494
package: Some(package),
95+
stability: Default::default(),
9596
});
9697
resolve.packages[package]
9798
.worlds
@@ -306,7 +307,7 @@ impl Bindgen {
306307
.resolve
307308
.merge(resolve)
308309
.context("failed to merge WIT package sets together")?
309-
.worlds[world.index()];
310+
.map_world(world, None)?;
310311
self.resolve
311312
.merge_worlds(world, self.world)
312313
.context("failed to merge worlds from two documents")?;
@@ -361,8 +362,8 @@ impl ModuleMetadata {
361362
.insert((BARE_FUNC_MODULE_NAME.to_string(), name.clone()), encoding);
362363
assert!(prev.is_none());
363364
}
364-
WorldItem::Interface(i) => {
365-
for (func, _) in resolve.interfaces[*i].functions.iter() {
365+
WorldItem::Interface { id, .. } => {
366+
for (func, _) in resolve.interfaces[*id].functions.iter() {
366367
let prev = ret
367368
.import_encodings
368369
.insert((name.clone(), func.clone()), encoding);
@@ -381,8 +382,8 @@ impl ModuleMetadata {
381382
let prev = ret.export_encodings.insert(name.clone(), encoding);
382383
assert!(prev.is_none());
383384
}
384-
WorldItem::Interface(i) => {
385-
for (_, func) in resolve.interfaces[*i].functions.iter() {
385+
WorldItem::Interface { id, .. } => {
386+
for (_, func) in resolve.interfaces[*id].functions.iter() {
386387
let name = func.core_export_name(Some(&name)).into_owned();
387388
let prev = ret.export_encodings.insert(name, encoding);
388389
assert!(prev.is_none());

crates/wit-component/src/printing.rs

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ impl WitPrinter {
6565
self.output.push_str("\n\n");
6666
for (name, id) in pkg.interfaces.iter() {
6767
self.print_docs(&resolve.interfaces[*id].docs);
68+
self.print_stability(&resolve.interfaces[*id].stability);
6869
self.output.push_str("interface ");
6970
self.print_name(name);
7071
self.output.push_str(" {\n");
@@ -74,6 +75,7 @@ impl WitPrinter {
7475

7576
for (name, id) in pkg.worlds.iter() {
7677
self.print_docs(&resolve.worlds[*id].docs);
78+
self.print_stability(&resolve.worlds[*id].stability);
7779
self.output.push_str("world ");
7880
self.print_name(name);
7981
self.output.push_str(" {\n");
@@ -125,6 +127,7 @@ impl WitPrinter {
125127
for (name, func) in freestanding {
126128
self.new_item();
127129
self.print_docs(&func.docs);
130+
self.print_stability(&func.stability);
128131
self.print_name(name);
129132
self.output.push_str(": ");
130133
self.print_function(resolve, func)?;
@@ -147,7 +150,7 @@ impl WitPrinter {
147150
// Partition types defined in this interface into either those imported
148151
// from foreign interfaces or those defined locally.
149152
let mut types_to_declare = Vec::new();
150-
let mut types_to_import: Vec<(_, Vec<_>)> = Vec::new();
153+
let mut types_to_import: Vec<(_, &_, Vec<_>)> = Vec::new();
151154
for (name, ty_id) in types {
152155
let ty = &resolve.types[ty_id];
153156
if let TypeDefKind::Type(Type::Id(other)) = ty.kind {
@@ -159,13 +162,17 @@ impl WitPrinter {
159162
.name
160163
.as_ref()
161164
.ok_or_else(|| anyhow!("cannot import unnamed type"))?;
162-
if let Some((owner, list)) = types_to_import.last_mut() {
163-
if *owner == other_owner {
165+
if let Some((owner, stability, list)) = types_to_import.last_mut() {
166+
if *owner == other_owner && ty.stability == **stability {
164167
list.push((name, other_name));
165168
continue;
166169
}
167170
}
168-
types_to_import.push((other_owner, vec![(name, other_name)]));
171+
types_to_import.push((
172+
other_owner,
173+
&ty.stability,
174+
vec![(name, other_name)],
175+
));
169176
continue;
170177
}
171178
_ => {}
@@ -181,8 +188,9 @@ impl WitPrinter {
181188
TypeOwner::World(id) => resolve.worlds[id].package.unwrap(),
182189
TypeOwner::None => unreachable!(),
183190
};
184-
for (owner, tys) in types_to_import {
191+
for (owner, stability, tys) in types_to_import {
185192
self.any_items = true;
193+
self.print_stability(stability);
186194
write!(&mut self.output, "use ")?;
187195
let id = match owner {
188196
TypeOwner::Interface(id) => id,
@@ -212,6 +220,7 @@ impl WitPrinter {
212220
for id in types_to_declare {
213221
self.new_item();
214222
self.print_docs(&resolve.types[id].docs);
223+
self.print_stability(&resolve.types[id].stability);
215224
match resolve.types[id].kind {
216225
TypeDefKind::Resource => self.print_resource(
217226
resolve,
@@ -236,17 +245,16 @@ impl WitPrinter {
236245
}
237246
self.output.push_str(" {\n");
238247
for func in funcs {
248+
self.print_docs(&func.docs);
249+
self.print_stability(&func.stability);
250+
239251
match &func.kind {
240-
FunctionKind::Constructor(_) => {
241-
self.print_docs(&func.docs);
242-
}
252+
FunctionKind::Constructor(_) => {}
243253
FunctionKind::Method(_) => {
244-
self.print_docs(&func.docs);
245254
self.print_name(func.item_name());
246255
self.output.push_str(": ");
247256
}
248257
FunctionKind::Static(_) => {
249-
self.print_docs(&func.docs);
250258
self.print_name(func.item_name());
251259
self.output.push_str(": ");
252260
self.output.push_str("static ");
@@ -367,21 +375,22 @@ impl WitPrinter {
367375
// Print inline item docs
368376
if matches!(name, WorldKey::Name(_)) {
369377
self.print_docs(match item {
370-
WorldItem::Interface(id) => &resolve.interfaces[*id].docs,
378+
WorldItem::Interface { id, .. } => &resolve.interfaces[*id].docs,
371379
WorldItem::Function(f) => &f.docs,
372380
// Types are handled separately
373381
WorldItem::Type(_) => unreachable!(),
374382
});
375383
}
376384

385+
self.print_stability(item.stability(resolve));
377386
self.output.push_str(desc);
378387
self.output.push_str(" ");
379388
match name {
380389
WorldKey::Name(name) => {
381390
self.print_name(name);
382391
self.output.push_str(": ");
383392
match item {
384-
WorldItem::Interface(id) => {
393+
WorldItem::Interface { id, .. } => {
385394
assert!(resolve.interfaces[*id].name.is_none());
386395
writeln!(self.output, "interface {{")?;
387396
self.print_interface(resolve, *id)?;
@@ -398,7 +407,7 @@ impl WitPrinter {
398407
}
399408
WorldKey::Interface(id) => {
400409
match item {
401-
WorldItem::Interface(id2) => assert_eq!(id, id2),
410+
WorldItem::Interface { id: id2, .. } => assert_eq!(id, id2),
402411
_ => unreachable!(),
403412
}
404413
self.print_path_to_interface(resolve, *id, cur_pkg)?;
@@ -868,6 +877,26 @@ impl WitPrinter {
868877
}
869878
}
870879
}
880+
881+
fn print_stability(&mut self, stability: &Stability) {
882+
match stability {
883+
Stability::Unknown => {}
884+
Stability::Stable { since, feature } => {
885+
self.output.push_str("@since(version = ");
886+
self.output.push_str(&since.to_string());
887+
if let Some(feature) = feature {
888+
self.output.push_str(", feature = ");
889+
self.output.push_str(feature);
890+
}
891+
self.output.push_str(")\n");
892+
}
893+
Stability::Unstable { feature } => {
894+
self.output.push_str("@unstable(feature = ");
895+
self.output.push_str(feature);
896+
self.output.push_str(")\n");
897+
}
898+
}
899+
}
871900
}
872901

873902
fn resource_func(f: &Function) -> Option<TypeId> {

0 commit comments

Comments
 (0)