/src/wasm-tools/crates/wit-parser/src/resolve/mod.rs
Line | Count | Source |
1 | | use alloc::borrow::ToOwned; |
2 | | use alloc::collections::BTreeMap; |
3 | | use alloc::string::{String, ToString}; |
4 | | use alloc::vec::Vec; |
5 | | use alloc::{format, vec}; |
6 | | use core::cmp::Ordering; |
7 | | use core::mem; |
8 | | |
9 | | use crate::resolve::error::{ResolveError, ResolveErrorKind}; |
10 | | use crate::*; |
11 | | use anyhow::{Context, anyhow, bail}; |
12 | | #[cfg(not(feature = "std"))] |
13 | | use hashbrown::hash_map::Entry; |
14 | | use id_arena::{Arena, Id}; |
15 | | use semver::Version; |
16 | | #[cfg(feature = "serde")] |
17 | | use serde_derive::Serialize; |
18 | | #[cfg(feature = "std")] |
19 | | use std::collections::hash_map::Entry; |
20 | | |
21 | | use crate::ast::lex::Span; |
22 | | use crate::ast::{ParsedUsePath, parse_use_path}; |
23 | | #[cfg(feature = "serde")] |
24 | | use crate::serde_::{serialize_arena, serialize_id_map}; |
25 | | use crate::{ |
26 | | AstItem, Docs, Function, FunctionKind, Handle, IncludeName, Interface, InterfaceId, |
27 | | LiftLowerAbi, ManglingAndAbi, PackageName, SourceMap, Stability, Type, TypeDef, TypeDefKind, |
28 | | TypeId, TypeIdVisitor, TypeOwner, UnresolvedPackage, UnresolvedPackageGroup, World, WorldId, |
29 | | WorldItem, WorldKey, |
30 | | }; |
31 | | |
32 | | pub use clone::CloneMaps; |
33 | | |
34 | | mod clone; |
35 | | pub mod error; |
36 | | |
37 | | #[cfg(feature = "std")] |
38 | | mod fs; |
39 | | #[cfg(feature = "std")] |
40 | | pub use fs::PackageSourceMap; |
41 | | |
42 | | /// Representation of a fully resolved set of WIT packages. |
43 | | /// |
44 | | /// This structure contains a graph of WIT packages and all of their contents |
45 | | /// merged together into the contained arenas. All items are sorted |
46 | | /// topologically and everything here is fully resolved, so with a `Resolve` no |
47 | | /// name lookups are necessary and instead everything is index-based. |
48 | | /// |
49 | | /// Working with a WIT package requires inserting it into a `Resolve` to ensure |
50 | | /// that all of its dependencies are satisfied. This will give the full picture |
51 | | /// of that package's types and such. |
52 | | /// |
53 | | /// Each item in a `Resolve` has a parent link to trace it back to the original |
54 | | /// package as necessary. |
55 | | #[derive(Default, Clone, Debug)] |
56 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
57 | | pub struct Resolve { |
58 | | /// All known worlds within this `Resolve`. |
59 | | /// |
60 | | /// Each world points at a `PackageId` which is stored below. No ordering is |
61 | | /// guaranteed between this list of worlds. |
62 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
63 | | pub worlds: Arena<World>, |
64 | | |
65 | | /// All known interfaces within this `Resolve`. |
66 | | /// |
67 | | /// Each interface points at a `PackageId` which is stored below. No |
68 | | /// ordering is guaranteed between this list of interfaces. |
69 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
70 | | pub interfaces: Arena<Interface>, |
71 | | |
72 | | /// All known types within this `Resolve`. |
73 | | /// |
74 | | /// Types are topologically sorted such that any type referenced from one |
75 | | /// type is guaranteed to be defined previously. Otherwise though these are |
76 | | /// not sorted by interface for example. |
77 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
78 | | pub types: Arena<TypeDef>, |
79 | | |
80 | | /// All known packages within this `Resolve`. |
81 | | /// |
82 | | /// This list of packages is not sorted. Sorted packages can be queried |
83 | | /// through [`Resolve::topological_packages`]. |
84 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
85 | | pub packages: Arena<Package>, |
86 | | |
87 | | /// A map of package names to the ID of the package with that name. |
88 | | #[cfg_attr(feature = "serde", serde(skip))] |
89 | | pub package_names: IndexMap<PackageName, PackageId>, |
90 | | |
91 | | /// Activated features for this [`Resolve`]. |
92 | | /// |
93 | | /// This set of features is empty by default. This is consulted for |
94 | | /// `@unstable` annotations in loaded WIT documents. Any items with |
95 | | /// `@unstable` are filtered out unless their feature is present within this |
96 | | /// set. |
97 | | #[cfg_attr(feature = "serde", serde(skip))] |
98 | | pub features: IndexSet<String>, |
99 | | |
100 | | /// Activate all features for this [`Resolve`]. |
101 | | #[cfg_attr(feature = "serde", serde(skip))] |
102 | | pub all_features: bool, |
103 | | |
104 | | /// Source map for converting spans to file locations. |
105 | | #[cfg_attr(feature = "serde", serde(skip))] |
106 | | pub source_map: SourceMap, |
107 | | } |
108 | | |
109 | | /// A WIT package within a `Resolve`. |
110 | | /// |
111 | | /// A package is a collection of interfaces and worlds. Packages additionally |
112 | | /// have a unique identifier that affects generated components and uniquely |
113 | | /// identifiers this particular package. |
114 | | #[derive(Clone, Debug)] |
115 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
116 | | pub struct Package { |
117 | | /// A unique name corresponding to this package. |
118 | | pub name: PackageName, |
119 | | |
120 | | /// Documentation associated with this package. |
121 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
122 | | pub docs: Docs, |
123 | | |
124 | | /// All interfaces contained in this packaged, keyed by the interface's |
125 | | /// name. |
126 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] |
127 | | pub interfaces: IndexMap<String, InterfaceId>, |
128 | | |
129 | | /// All worlds contained in this package, keyed by the world's name. |
130 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] |
131 | | pub worlds: IndexMap<String, WorldId>, |
132 | | } |
133 | | |
134 | | pub type PackageId = Id<Package>; |
135 | | |
136 | | /// Source name mappings for resolved packages (no_std compatible). |
137 | | #[derive(Clone, Debug)] |
138 | | pub struct PackageSources { |
139 | | sources: Vec<Vec<String>>, |
140 | | package_id_to_source_map_idx: BTreeMap<PackageId, usize>, |
141 | | } |
142 | | |
143 | | impl PackageSources { |
144 | 0 | pub fn from_single_source(package_id: PackageId, source: &str) -> Self { |
145 | 0 | Self { |
146 | 0 | sources: vec![vec![source.to_owned()]], |
147 | 0 | package_id_to_source_map_idx: BTreeMap::from([(package_id, 0)]), |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | 11.0k | pub fn from_source_maps( |
152 | 11.0k | source_maps: Vec<SourceMap>, |
153 | 11.0k | package_id_to_source_map_idx: BTreeMap<PackageId, usize>, |
154 | 11.0k | ) -> PackageSources { |
155 | 11.8k | for (package_id, idx) in &package_id_to_source_map_idx { |
156 | 11.8k | if *idx >= source_maps.len() { |
157 | 0 | panic!( |
158 | | "Invalid source map index: {}, package id: {:?}, source maps size: {}", |
159 | | idx, |
160 | | package_id, |
161 | 0 | source_maps.len() |
162 | | ) |
163 | 11.8k | } |
164 | | } |
165 | | |
166 | | Self { |
167 | 11.0k | sources: source_maps |
168 | 11.0k | .into_iter() |
169 | 20.1k | .map(|source_map| source_map.source_names().map(|s| s.to_owned()).collect()) |
170 | 11.0k | .collect(), |
171 | 11.0k | package_id_to_source_map_idx, |
172 | | } |
173 | 11.0k | } |
174 | | |
175 | | /// All unique source names. |
176 | 0 | pub fn source_names(&self) -> impl Iterator<Item = &str> { |
177 | 0 | self.sources |
178 | 0 | .iter() |
179 | 0 | .flatten() |
180 | 0 | .map(|s| s.as_str()) |
181 | 0 | .collect::<IndexSet<&str>>() |
182 | 0 | .into_iter() |
183 | 0 | } |
184 | | |
185 | | /// Source names for a specific package. |
186 | 0 | pub fn package_source_names(&self, id: PackageId) -> Option<impl Iterator<Item = &str>> { |
187 | 0 | self.package_id_to_source_map_idx |
188 | 0 | .get(&id) |
189 | 0 | .map(|&idx| self.sources[idx].iter().map(|s| s.as_str())) |
190 | 0 | } |
191 | | } |
192 | | |
193 | | /// Visitor helper for performing topological sort on a group of packages. |
194 | 12.6k | fn visit<'a>( |
195 | 12.6k | pkg: &'a UnresolvedPackage, |
196 | 12.6k | pkg_details_map: &'a BTreeMap<PackageName, (UnresolvedPackage, usize)>, |
197 | 12.6k | order: &mut IndexSet<PackageName>, |
198 | 12.6k | visiting: &mut HashSet<&'a PackageName>, |
199 | 12.6k | source_map_offsets: &[u32], |
200 | 12.6k | ) -> ResolveResult<()> { |
201 | 12.6k | if order.contains(&pkg.name) { |
202 | 870 | return Ok(()); |
203 | 11.8k | } |
204 | | |
205 | 11.8k | let (_, source_map_index) = pkg_details_map |
206 | 11.8k | .get(&pkg.name) |
207 | 11.8k | .expect("No pkg_details found for package when doing topological sort"); |
208 | 11.8k | let offset = source_map_offsets[*source_map_index]; |
209 | 11.8k | for (i, (dep, _)) in pkg.foreign_deps.iter().enumerate() { |
210 | 3.05k | let mut span = pkg.foreign_dep_spans[i]; |
211 | 3.05k | span.adjust(offset); |
212 | 3.05k | if !visiting.insert(dep) { |
213 | 0 | return Err(ResolveError::from(ResolveErrorKind::PackageCycle { |
214 | 0 | package: dep.clone(), |
215 | 0 | span, |
216 | 0 | })); |
217 | 3.05k | } |
218 | 3.05k | if let Some((dep_pkg, _)) = pkg_details_map.get(dep) { |
219 | 870 | visit( |
220 | 870 | dep_pkg, |
221 | 870 | pkg_details_map, |
222 | 870 | order, |
223 | 870 | visiting, |
224 | 870 | source_map_offsets, |
225 | 0 | )?; |
226 | 2.18k | } |
227 | 3.05k | assert!(visiting.remove(dep)); |
228 | | } |
229 | 11.8k | assert!(order.insert(pkg.name.clone())); |
230 | 11.8k | Ok(()) |
231 | 12.6k | } |
232 | | |
233 | | impl Resolve { |
234 | | /// Creates a new [`Resolve`] with no packages/items inside of it. |
235 | 0 | pub fn new() -> Resolve { |
236 | 0 | Resolve::default() |
237 | 0 | } |
238 | | |
239 | | /// Merge `main` and `deps` into this [`Resolve`], topologically sorting |
240 | | /// them internally. Returns the [`PackageId`] of `main` and a |
241 | | /// [`PackageSources`] covering all groups. |
242 | 11.0k | fn sort_unresolved_packages( |
243 | 11.0k | &mut self, |
244 | 11.0k | main: UnresolvedPackageGroup, |
245 | 11.0k | deps: Vec<UnresolvedPackageGroup>, |
246 | 11.0k | ) -> ResolveResult<(PackageId, PackageSources)> { |
247 | 11.0k | let mut source_maps: Vec<SourceMap> = Vec::new(); |
248 | 11.0k | let mut all_packages: Vec<(UnresolvedPackage, usize)> = Vec::new(); |
249 | | |
250 | 11.0k | let mut collect = |group: UnresolvedPackageGroup| { |
251 | | let UnresolvedPackageGroup { |
252 | 11.0k | main, |
253 | 11.0k | nested, |
254 | 11.0k | source_map, |
255 | 11.0k | } = group; |
256 | 11.0k | let i = source_maps.len(); |
257 | 11.0k | source_maps.push(source_map); |
258 | 11.8k | for pkg in nested.into_iter().chain([main]) { |
259 | 11.8k | all_packages.push((pkg, i)); |
260 | 11.8k | } |
261 | 11.0k | }; |
262 | | |
263 | 11.0k | let main_name = main.main.name.clone(); |
264 | 11.0k | collect(main); |
265 | 11.0k | for dep in deps { |
266 | 0 | collect(dep); |
267 | 0 | } |
268 | | |
269 | | // Merge all source maps into resolve.source_map upfront so that every |
270 | | // span produced during toposort and duplicate detection is valid in |
271 | | // resolve.source_map and can be located by rewrite_error below. |
272 | | // Each group has exactly one source map, so a Vec of offsets suffices. |
273 | 11.0k | let source_map_offsets: Vec<u32> = source_maps |
274 | 11.0k | .iter() |
275 | 11.0k | .map(|sm| self.push_source_map(sm.clone())) |
276 | 11.0k | .collect(); |
277 | | |
278 | 11.0k | let mut pkg_details_map: BTreeMap<PackageName, (UnresolvedPackage, usize)> = |
279 | 11.0k | BTreeMap::new(); |
280 | 11.8k | for (pkg, source_map_index) in all_packages { |
281 | 11.8k | let name = pkg.name.clone(); |
282 | 11.8k | let my_span = pkg.package_name_span; |
283 | 11.8k | let offset = source_map_offsets[source_map_index]; |
284 | 0 | if let Some((prev_pkg, prev_source_map_index)) = |
285 | 11.8k | pkg_details_map.insert(name.clone(), (pkg, source_map_index)) |
286 | | { |
287 | 0 | let prev_offset = source_map_offsets[prev_source_map_index]; |
288 | 0 | let mut span1 = my_span; |
289 | 0 | span1.adjust(offset); |
290 | 0 | let mut span2 = prev_pkg.package_name_span; |
291 | 0 | span2.adjust(prev_offset); |
292 | 0 | return Err(ResolveError::from(ResolveErrorKind::DuplicatePackage { |
293 | 0 | name, |
294 | 0 | span1, |
295 | 0 | span2, |
296 | 0 | })); |
297 | 11.8k | } |
298 | | } |
299 | | |
300 | | // Perform a simple topological sort which will bail out on cycles |
301 | | // and otherwise determine the order that packages must be added to |
302 | | // this `Resolve`. |
303 | 11.0k | let mut order = IndexSet::default(); |
304 | | { |
305 | 11.0k | let mut visiting = HashSet::new(); |
306 | 11.8k | for (pkg, _) in pkg_details_map.values() { |
307 | 11.8k | visit( |
308 | 11.8k | pkg, |
309 | 11.8k | &pkg_details_map, |
310 | 11.8k | &mut order, |
311 | 11.8k | &mut visiting, |
312 | 11.8k | &source_map_offsets, |
313 | 0 | )?; |
314 | | } |
315 | | } |
316 | | |
317 | 11.0k | let mut package_id_to_source_map_idx = BTreeMap::new(); |
318 | 11.0k | let mut main_pkg_id = None; |
319 | 11.8k | for name in order { |
320 | 11.8k | let (pkg, source_map_index) = pkg_details_map.remove(&name).unwrap(); |
321 | 11.8k | let span_offset = source_map_offsets[source_map_index]; |
322 | 11.8k | let is_main = pkg.name == main_name; |
323 | 11.8k | let id = self.push(pkg, span_offset)?; |
324 | 11.8k | if is_main { |
325 | 11.0k | assert!(main_pkg_id.is_none()); |
326 | 11.0k | main_pkg_id = Some(id); |
327 | 766 | } |
328 | 11.8k | package_id_to_source_map_idx.insert(id, source_map_index); |
329 | | } |
330 | | |
331 | 11.0k | Ok(( |
332 | 11.0k | main_pkg_id.unwrap(), |
333 | 11.0k | PackageSources::from_source_maps(source_maps, package_id_to_source_map_idx), |
334 | 11.0k | )) |
335 | 11.0k | } |
336 | | |
337 | | /// Appends a source map to this [`Resolve`]'s internal source map. |
338 | | /// |
339 | | /// Returns the byte offset that should be passed to [`Resolve::push`] for |
340 | | /// packages parsed from this source map. This offset ensures that spans |
341 | | /// in the resolved package point to the correct location in the combined |
342 | | /// source map. |
343 | 11.0k | pub fn push_source_map(&mut self, source_map: SourceMap) -> u32 { |
344 | 11.0k | self.source_map.append(source_map) |
345 | 11.0k | } |
346 | | |
347 | | /// Appends a new [`UnresolvedPackage`] to this [`Resolve`], creating a |
348 | | /// fully resolved package with no dangling references. |
349 | | /// |
350 | | /// All the dependencies of `unresolved` must already have been loaded |
351 | | /// within this `Resolve` via previous calls to `push` or other methods such |
352 | | /// as [`Resolve::push_path`]. |
353 | | /// |
354 | | /// The `span_offset` should be the value returned by |
355 | | /// [`Resolve::push_source_map`] if the source map was appended to this |
356 | | /// resolve, or `0` if this is a standalone package. |
357 | | /// |
358 | | /// Any dependency resolution error or otherwise world-elaboration error |
359 | | /// will be returned here, if successful a package identifier is returned |
360 | | /// which corresponds to the package that was just inserted. |
361 | 11.8k | pub fn push( |
362 | 11.8k | &mut self, |
363 | 11.8k | mut unresolved: UnresolvedPackage, |
364 | 11.8k | span_offset: u32, |
365 | 11.8k | ) -> ResolveResult<PackageId> { |
366 | 11.8k | unresolved.adjust_spans(span_offset); |
367 | 11.8k | let ret = Remap::default().append(self, unresolved); |
368 | 11.8k | if ret.is_ok() { |
369 | 11.8k | #[cfg(debug_assertions)] |
370 | 11.8k | self.assert_valid(); |
371 | 11.8k | } |
372 | 11.8k | ret |
373 | 11.8k | } |
374 | | |
375 | | /// Appends new [`UnresolvedPackageGroup`] to this [`Resolve`], creating a |
376 | | /// fully resolved package with no dangling references. |
377 | | /// |
378 | | /// Any dependency resolution error or otherwise world-elaboration error |
379 | | /// will be returned here, if successful a package identifier is returned |
380 | | /// which corresponds to the package that was just inserted. |
381 | | /// |
382 | | /// On error, `self` may be partially modified and should not be reused. |
383 | 11.0k | pub fn push_group( |
384 | 11.0k | &mut self, |
385 | 11.0k | unresolved_group: UnresolvedPackageGroup, |
386 | 11.0k | ) -> ResolveResult<PackageId> { |
387 | 11.0k | let (pkg_id, _) = self.sort_unresolved_packages(unresolved_group, Vec::new())?; |
388 | 11.0k | Ok(pkg_id) |
389 | 11.0k | } |
390 | | |
391 | | /// Appends a main [`UnresolvedPackageGroup`] and its dependencies to this |
392 | | /// [`Resolve`] in a single call, topologically sorting them internally. |
393 | | /// |
394 | | /// This is useful when you have a package and its local dependencies |
395 | | /// available as in-memory [`UnresolvedPackageGroup`]s and want dependency |
396 | | /// ordering and cycle detection handled automatically. |
397 | | /// |
398 | | /// The returned [`PackageId`] corresponds to `main`. |
399 | | /// |
400 | | /// On error, `self` may be partially modified and should not be reused. |
401 | 0 | pub fn push_groups( |
402 | 0 | &mut self, |
403 | 0 | main: UnresolvedPackageGroup, |
404 | 0 | deps: Vec<UnresolvedPackageGroup>, |
405 | 0 | ) -> ResolveResult<PackageId> { |
406 | 0 | let (pkg_id, _) = self.sort_unresolved_packages(main, deps)?; |
407 | 0 | Ok(pkg_id) |
408 | 0 | } |
409 | | |
410 | | /// Convenience method for combining [`SourceMap`] and [`Resolve::push_group`]. |
411 | | /// |
412 | | /// The `path` provided is used for error messages but otherwise is not |
413 | | /// read. This method does not touch the filesystem. The `contents` provided |
414 | | /// are the contents of a WIT package. |
415 | 4.13k | pub fn push_source(&mut self, path: &str, contents: &str) -> anyhow::Result<PackageId> { |
416 | 4.13k | let mut map = SourceMap::default(); |
417 | 4.13k | map.push_str(path, contents); |
418 | 4.13k | let group = self.parse_source_map(map)?; |
419 | 4.13k | Ok(self.push_group(group)?) |
420 | 4.13k | } |
421 | | |
422 | | /// Parses `map` into an [`UnresolvedPackageGroup`]. |
423 | | /// |
424 | | /// On parse failure the local [`SourceMap`] is merged into |
425 | | /// [`Resolve::source_map`] (with the typed [`crate::ParseError`]'s spans |
426 | | /// adjusted) so the downcasted error remains renderable against |
427 | | /// `self.source_map`. |
428 | 4.13k | pub(crate) fn parse_source_map( |
429 | 4.13k | &mut self, |
430 | 4.13k | map: SourceMap, |
431 | 4.13k | ) -> anyhow::Result<UnresolvedPackageGroup> { |
432 | 4.13k | map.parse().map_err(|(map, mut e)| { |
433 | 0 | let offset = self.source_map.append(map); |
434 | 0 | e.adjust_spans(offset); |
435 | 0 | e.into() |
436 | 0 | }) |
437 | 4.13k | } |
438 | | |
439 | | /// Renders a span as a human-readable location string (e.g., "file.wit:10:5"). |
440 | 0 | pub fn render_location(&self, span: Span) -> String { |
441 | 0 | self.source_map.render_location(span) |
442 | 0 | } |
443 | | |
444 | | /// Renders an error returned by this [`Resolve`]'s `push_*` methods with |
445 | | /// source context (file:line:col + snippet). |
446 | | #[cfg(feature = "std")] |
447 | 0 | pub fn render_error(&self, err: &anyhow::Error) -> String { |
448 | 0 | crate::render_anyhow_error(err, &self.source_map) |
449 | 0 | } |
450 | | |
451 | 0 | pub fn all_bits_valid(&self, ty: &Type) -> bool { |
452 | 0 | match ty { |
453 | | Type::U8 |
454 | | | Type::S8 |
455 | | | Type::U16 |
456 | | | Type::S16 |
457 | | | Type::U32 |
458 | | | Type::S32 |
459 | | | Type::U64 |
460 | | | Type::S64 |
461 | | | Type::F32 |
462 | 0 | | Type::F64 => true, |
463 | | |
464 | 0 | Type::Bool | Type::Char | Type::String | Type::ErrorContext => false, |
465 | | |
466 | 0 | Type::Id(id) => match &self.types[*id].kind { |
467 | | TypeDefKind::List(_) |
468 | | | TypeDefKind::Map(_, _) |
469 | | | TypeDefKind::Variant(_) |
470 | | | TypeDefKind::Enum(_) |
471 | | | TypeDefKind::Option(_) |
472 | | | TypeDefKind::Result(_) |
473 | | | TypeDefKind::Future(_) |
474 | 0 | | TypeDefKind::Stream(_) => false, |
475 | 0 | TypeDefKind::Type(t) | TypeDefKind::FixedLengthList(t, ..) => { |
476 | 0 | self.all_bits_valid(t) |
477 | | } |
478 | | |
479 | 0 | TypeDefKind::Handle(h) => match h { |
480 | 0 | crate::Handle::Own(_) => true, |
481 | 0 | crate::Handle::Borrow(_) => true, |
482 | | }, |
483 | | |
484 | 0 | TypeDefKind::Resource => false, |
485 | 0 | TypeDefKind::Record(r) => r.fields.iter().all(|f| self.all_bits_valid(&f.ty)), |
486 | 0 | TypeDefKind::Tuple(t) => t.types.iter().all(|t| self.all_bits_valid(t)), |
487 | | |
488 | | // FIXME: this could perhaps be `true` for multiples-of-32 but |
489 | | // seems better to probably leave this as unconditionally |
490 | | // `false` for now, may want to reconsider later? |
491 | 0 | TypeDefKind::Flags(_) => false, |
492 | | |
493 | 0 | TypeDefKind::Unknown => unreachable!(), |
494 | | }, |
495 | | } |
496 | 0 | } |
497 | | |
498 | | /// Merges all the contents of a different `Resolve` into this one. The |
499 | | /// `Remap` structure returned provides a mapping from all old indices to |
500 | | /// new indices |
501 | | /// |
502 | | /// This operation can fail if `resolve` disagrees with `self` about the |
503 | | /// packages being inserted. Otherwise though this will additionally attempt |
504 | | /// to "union" packages found in `resolve` with those found in `self`. |
505 | | /// Unioning packages is keyed on the name/url of packages for those with |
506 | | /// URLs present. If found then it's assumed that both `Resolve` instances |
507 | | /// were originally created from the same contents and are two views |
508 | | /// of the same package. |
509 | 6.55k | pub fn merge(&mut self, resolve: Resolve) -> anyhow::Result<Remap> { |
510 | 6.55k | log::trace!( |
511 | | "merging {} packages into {} packages", |
512 | 0 | resolve.packages.len(), |
513 | 0 | self.packages.len() |
514 | | ); |
515 | | |
516 | 6.55k | let mut map = MergeMap::new(&resolve, &self); |
517 | 6.55k | map.build()?; |
518 | | let MergeMap { |
519 | 6.55k | package_map, |
520 | 6.55k | interface_map, |
521 | 6.55k | type_map, |
522 | 6.55k | world_map, |
523 | 6.55k | interfaces_to_add, |
524 | 6.55k | worlds_to_add, |
525 | | .. |
526 | 6.55k | } = map; |
527 | | |
528 | | // With a set of maps from ids in `resolve` to ids in `self` the next |
529 | | // operation is to start moving over items and building a `Remap` to |
530 | | // update ids. |
531 | | // |
532 | | // Each component field of `resolve` is moved into `self` so long as |
533 | | // its ID is not within one of the maps above. If it's present in a map |
534 | | // above then that means the item is already present in `self` so a new |
535 | | // one need not be added. If it's not present in a map that means it's |
536 | | // not present in `self` so it must be added to an arena. |
537 | | // |
538 | | // When adding an item to an arena one of the `remap.update_*` methods |
539 | | // is additionally called to update all identifiers from pointers within |
540 | | // `resolve` to becoming pointers within `self`. |
541 | | // |
542 | | // Altogether this should weave all the missing items in `self` from |
543 | | // `resolve` into one structure while updating all identifiers to |
544 | | // be local within `self`. |
545 | | |
546 | 6.55k | let mut remap = Remap::default(); |
547 | | let Resolve { |
548 | 6.55k | types, |
549 | 6.55k | worlds, |
550 | 6.55k | interfaces, |
551 | 6.55k | packages, |
552 | 6.55k | package_names, |
553 | | features: _, |
554 | 6.55k | source_map, |
555 | | .. |
556 | 6.55k | } = resolve; |
557 | | |
558 | 6.55k | let span_offset = self.source_map.append(source_map); |
559 | | |
560 | 6.55k | let mut moved_types = Vec::new(); |
561 | 39.4k | for (id, mut ty) in types { |
562 | 39.4k | let new_id = match type_map.get(&id).copied() { |
563 | 119 | Some(id) => { |
564 | 119 | update_stability(&ty.stability, &mut self.types[id].stability, ty.span)?; |
565 | 119 | id |
566 | | } |
567 | | None => { |
568 | 39.3k | log::debug!("moving type {:?}", ty.name); |
569 | 39.3k | moved_types.push(id); |
570 | 39.3k | remap.update_typedef(self, &mut ty, Default::default())?; |
571 | 39.3k | ty.adjust_spans(span_offset); |
572 | 39.3k | self.types.alloc(ty) |
573 | | } |
574 | | }; |
575 | 39.4k | assert_eq!(remap.types.len(), id.index()); |
576 | 39.4k | remap.types.push(Some(new_id)); |
577 | | } |
578 | | |
579 | 6.55k | let mut moved_interfaces = Vec::new(); |
580 | 6.55k | for (id, mut iface) in interfaces { |
581 | 3.72k | let new_id = match interface_map.get(&id).copied() { |
582 | 35 | Some(into_id) => { |
583 | 35 | update_stability( |
584 | 35 | &iface.stability, |
585 | 35 | &mut self.interfaces[into_id].stability, |
586 | 35 | iface.span, |
587 | 0 | )?; |
588 | | |
589 | | // Add any extra types from `from`'s interface that |
590 | | // don't exist in `into`'s interface. These types were |
591 | | // already moved as new types above (since they weren't |
592 | | // in `type_map`), but they still need to be registered |
593 | | // in the target interface's `types` map. |
594 | 119 | for (name, from_type_id) in iface.types.iter() { |
595 | 119 | if self.interfaces[into_id].types.contains_key(name) { |
596 | 119 | continue; |
597 | 0 | } |
598 | 0 | let new_type_id = remap.map_type(*from_type_id, Default::default())?; |
599 | 0 | self.interfaces[into_id] |
600 | 0 | .types |
601 | 0 | .insert(name.clone(), new_type_id); |
602 | | } |
603 | | |
604 | | // Add any extra functions from `from`'s interface that |
605 | | // don't exist in `into`'s interface. These need their |
606 | | // type references remapped and spans adjusted. |
607 | 35 | let extra_funcs: Vec<_> = iface |
608 | 35 | .functions |
609 | 35 | .into_iter() |
610 | 35 | .filter(|(name, _)| { |
611 | 19 | !self.interfaces[into_id] |
612 | 19 | .functions |
613 | 19 | .contains_key(name.as_str()) |
614 | 19 | }) |
615 | 35 | .collect(); |
616 | 35 | for (name, mut func) in extra_funcs { |
617 | 0 | remap.update_function(self, &mut func, Default::default())?; |
618 | 0 | func.adjust_spans(span_offset); |
619 | 0 | self.interfaces[into_id].functions.insert(name, func); |
620 | | } |
621 | | |
622 | 35 | into_id |
623 | | } |
624 | | None => { |
625 | 3.69k | log::debug!("moving interface {:?}", iface.name); |
626 | 3.69k | moved_interfaces.push(id); |
627 | 3.69k | remap.update_interface(self, &mut iface)?; |
628 | 3.69k | iface.adjust_spans(span_offset); |
629 | 3.69k | self.interfaces.alloc(iface) |
630 | | } |
631 | | }; |
632 | 3.72k | assert_eq!(remap.interfaces.len(), id.index()); |
633 | 3.72k | remap.interfaces.push(Some(new_id)); |
634 | | } |
635 | | |
636 | 6.55k | let mut moved_worlds = Vec::new(); |
637 | 9.79k | for (id, mut world) in worlds { |
638 | 9.79k | let new_id = match world_map.get(&id).copied() { |
639 | 0 | Some(world_id) => { |
640 | 0 | update_stability( |
641 | 0 | &world.stability, |
642 | 0 | &mut self.worlds[world_id].stability, |
643 | 0 | world.span, |
644 | 0 | )?; |
645 | 0 | for from_import in world.imports.iter() { |
646 | 0 | Resolve::update_world_imports_stability( |
647 | 0 | from_import, |
648 | 0 | &mut self.worlds[world_id].imports, |
649 | 0 | &interface_map, |
650 | 0 | )?; |
651 | | } |
652 | 0 | for from_export in world.exports.iter() { |
653 | 0 | Resolve::update_world_imports_stability( |
654 | 0 | from_export, |
655 | 0 | &mut self.worlds[world_id].exports, |
656 | 0 | &interface_map, |
657 | 0 | )?; |
658 | | } |
659 | 0 | world_id |
660 | | } |
661 | | None => { |
662 | 9.79k | log::debug!("moving world {}", world.name); |
663 | 9.79k | moved_worlds.push(id); |
664 | 9.79k | let mut update = |
665 | 19.5k | |map: &mut IndexMap<WorldKey, WorldItem>| -> anyhow::Result<_> { |
666 | 22.5k | for (mut name, mut item) in mem::take(map) { |
667 | 22.5k | remap.update_world_key(&mut name, Default::default())?; |
668 | 22.5k | match &mut item { |
669 | 2.67k | WorldItem::Function(f) => { |
670 | 2.67k | remap.update_function(self, f, Default::default())? |
671 | | } |
672 | 16.2k | WorldItem::Interface { id, .. } => { |
673 | 16.2k | *id = remap.map_interface(*id, Default::default())?; |
674 | | } |
675 | 3.58k | WorldItem::Type { id, .. } => { |
676 | 3.58k | *id = remap.map_type(*id, Default::default())? |
677 | | } |
678 | | } |
679 | 22.5k | map.insert(name, item); |
680 | | } |
681 | 19.5k | Ok(()) |
682 | 19.5k | }; |
683 | 9.79k | update(&mut world.imports)?; |
684 | 9.79k | update(&mut world.exports)?; |
685 | 9.79k | world.adjust_spans(span_offset); |
686 | 9.79k | self.worlds.alloc(world) |
687 | | } |
688 | | }; |
689 | 9.79k | assert_eq!(remap.worlds.len(), id.index()); |
690 | 9.79k | remap.worlds.push(Some(new_id)); |
691 | | } |
692 | | |
693 | 11.0k | for (id, mut pkg) in packages { |
694 | 11.0k | let new_id = match package_map.get(&id).copied() { |
695 | 35 | Some(id) => id, |
696 | | None => { |
697 | 11.0k | for (_, id) in pkg.interfaces.iter_mut() { |
698 | 2.51k | *id = remap.map_interface(*id, Default::default())?; |
699 | | } |
700 | 11.0k | for (_, id) in pkg.worlds.iter_mut() { |
701 | 9.79k | *id = remap.map_world(*id, Default::default())?; |
702 | | } |
703 | 11.0k | self.packages.alloc(pkg) |
704 | | } |
705 | | }; |
706 | 11.0k | assert_eq!(remap.packages.len(), id.index()); |
707 | 11.0k | remap.packages.push(new_id); |
708 | | } |
709 | | |
710 | 7.85k | for (name, id) in package_names { |
711 | 7.85k | let id = remap.packages[id.index()]; |
712 | 7.85k | if let Some(prev) = self.package_names.insert(name, id) { |
713 | 35 | assert_eq!(prev, id); |
714 | 7.81k | } |
715 | | } |
716 | | |
717 | | // Fixup all "parent" links now. |
718 | | // |
719 | | // Note that this is only done for items that are actually moved from |
720 | | // `resolve` into `self`, which is tracked by the various `moved_*` |
721 | | // lists built incrementally above. The ids in the `moved_*` lists |
722 | | // are ids within `resolve`, so they're translated through `remap` to |
723 | | // ids within `self`. |
724 | 9.79k | for id in moved_worlds { |
725 | 9.79k | let id = remap.map_world(id, Default::default())?; |
726 | 9.79k | if let Some(pkg) = self.worlds[id].package.as_mut() { |
727 | 9.79k | *pkg = remap.packages[pkg.index()]; |
728 | 9.79k | } |
729 | | } |
730 | 6.55k | for id in moved_interfaces { |
731 | 3.69k | let id = remap.map_interface(id, Default::default())?; |
732 | 3.69k | if let Some(pkg) = self.interfaces[id].package.as_mut() { |
733 | 3.69k | *pkg = remap.packages[pkg.index()]; |
734 | 3.69k | } |
735 | 3.69k | if let Some(clone_of) = self.interfaces[id].clone_of.as_mut() { |
736 | 392 | *clone_of = remap.map_interface(*clone_of, Default::default())?; |
737 | 3.29k | } |
738 | | } |
739 | 39.3k | for id in moved_types { |
740 | 39.3k | let id = remap.map_type(id, Default::default())?; |
741 | 39.3k | match &mut self.types[id].owner { |
742 | 10.7k | TypeOwner::Interface(id) => *id = remap.map_interface(*id, Default::default())?, |
743 | 3.58k | TypeOwner::World(id) => *id = remap.map_world(*id, Default::default())?, |
744 | 25.0k | TypeOwner::None => {} |
745 | | } |
746 | | } |
747 | | |
748 | | // And finally process items that were present in `resolve` but were |
749 | | // not present in `self`. This is only done for merged packages as |
750 | | // documents may be added to `self.documents` but wouldn't otherwise be |
751 | | // present in the `documents` field of the corresponding package. |
752 | 6.55k | for (name, pkg, iface) in interfaces_to_add { |
753 | 3 | let prev = self.packages[pkg] |
754 | 3 | .interfaces |
755 | 3 | .insert(name, remap.map_interface(iface, Default::default())?); |
756 | 3 | assert!(prev.is_none()); |
757 | | } |
758 | 6.55k | for (name, pkg, world) in worlds_to_add { |
759 | 0 | let prev = self.packages[pkg] |
760 | 0 | .worlds |
761 | 0 | .insert(name, remap.map_world(world, Default::default())?); |
762 | 0 | assert!(prev.is_none()); |
763 | | } |
764 | | |
765 | 6.55k | log::trace!("now have {} packages", self.packages.len()); |
766 | | |
767 | | // Re-elaborate all worlds after the merge. Merging may have added |
768 | | // extra types to existing interfaces that introduce new interface |
769 | | // dependencies not yet present in the world's imports. |
770 | 6.55k | let world_ids: Vec<_> = self.worlds.iter().map(|(id, _)| id).collect(); |
771 | 16.3k | for world_id in world_ids { |
772 | 16.3k | let world_span = self.worlds[world_id].span; |
773 | 16.3k | self.elaborate_world(world_id, world_span)?; |
774 | | } |
775 | | |
776 | | #[cfg(debug_assertions)] |
777 | | self.assert_valid(); |
778 | 6.55k | Ok(remap) |
779 | 6.55k | } |
780 | | |
781 | 0 | fn update_world_imports_stability( |
782 | 0 | from_item: (&WorldKey, &WorldItem), |
783 | 0 | into_items: &mut IndexMap<WorldKey, WorldItem>, |
784 | 0 | interface_map: &HashMap<Id<Interface>, Id<Interface>>, |
785 | 0 | ) -> anyhow::Result<()> { |
786 | 0 | match from_item.0 { |
787 | | WorldKey::Name(_) => { |
788 | | // No stability info to update here, only updating import/include stability |
789 | 0 | Ok(()) |
790 | | } |
791 | 0 | key @ WorldKey::Interface(_) => { |
792 | 0 | let new_key = MergeMap::map_name(key, interface_map); |
793 | 0 | if let Some(into) = into_items.get_mut(&new_key) { |
794 | 0 | match (from_item.1, into) { |
795 | | ( |
796 | | WorldItem::Interface { |
797 | 0 | id: aid, |
798 | 0 | stability: astability, |
799 | 0 | span: aspan, |
800 | | .. |
801 | | }, |
802 | | WorldItem::Interface { |
803 | 0 | id: bid, |
804 | 0 | stability: bstability, |
805 | | .. |
806 | | }, |
807 | | ) => { |
808 | 0 | let aid = interface_map.get(aid).copied().unwrap_or(*aid); |
809 | 0 | assert_eq!(aid, *bid); |
810 | 0 | update_stability(astability, bstability, *aspan)?; |
811 | 0 | Ok(()) |
812 | | } |
813 | 0 | _ => unreachable!(), |
814 | | } |
815 | | } else { |
816 | | // we've already matched all the imports/exports by the time we are calling this |
817 | | // so this is unreachable since we should always find the item |
818 | 0 | unreachable!() |
819 | | } |
820 | | } |
821 | | } |
822 | 0 | } |
823 | | |
824 | | /// Merges the world `from` into the world `into`. |
825 | | /// |
826 | | /// This will attempt to merge one world into another, unioning all of its |
827 | | /// imports and exports together. This is an operation performed by |
828 | | /// `wit-component`, for example where two different worlds from two |
829 | | /// different libraries were linked into the same core wasm file and are |
830 | | /// producing a singular world that will be the final component's |
831 | | /// interface. |
832 | | /// |
833 | | /// During the merge operation, some of the types and/or interfaces in |
834 | | /// `from` might need to be cloned so that backreferences point to `into` |
835 | | /// instead of `from`. Any such clones will be added to `clone_maps`. |
836 | | /// |
837 | | /// This operation can fail if the imports/exports overlap. |
838 | 6.46k | pub fn merge_worlds( |
839 | 6.46k | &mut self, |
840 | 6.46k | from: WorldId, |
841 | 6.46k | into: WorldId, |
842 | 6.46k | clone_maps: &mut CloneMaps, |
843 | 6.46k | ) -> anyhow::Result<()> { |
844 | 6.46k | let mut new_imports = Vec::new(); |
845 | 6.46k | let mut new_exports = Vec::new(); |
846 | | |
847 | 6.46k | let from_world = &self.worlds[from]; |
848 | 6.46k | let into_world = &self.worlds[into]; |
849 | | |
850 | 6.46k | log::trace!("merging {} into {}", from_world.name, into_world.name); |
851 | | |
852 | | // First walk over all the imports of `from` world and figure out what |
853 | | // to do with them. |
854 | | // |
855 | | // If the same item exists in `from` and `into` then merge it together |
856 | | // below with `merge_world_item` which basically asserts they're the |
857 | | // same. Otherwise queue up a new import since if `from` has more |
858 | | // imports than `into` then it's fine to add new imports. |
859 | 6.46k | for (name, from_import) in from_world.imports.iter() { |
860 | 5.41k | let name_str = self.name_world_key(name); |
861 | 5.41k | match into_world.imports.get(name) { |
862 | 0 | Some(into_import) => { |
863 | 0 | log::trace!("info/from shared import on `{name_str}`"); |
864 | 0 | self.merge_world_item(from_import, into_import) |
865 | 0 | .with_context(|| format!("failed to merge world import {name_str}"))?; |
866 | | } |
867 | | None => { |
868 | 5.41k | log::trace!("new import: `{name_str}`"); |
869 | 5.41k | new_imports.push((name.clone(), from_import.clone())); |
870 | | } |
871 | | } |
872 | | } |
873 | | |
874 | | // Build a set of interfaces which are required to be imported because |
875 | | // of `into`'s exports. This set is then used below during |
876 | | // `ensure_can_add_world_export`. |
877 | | // |
878 | | // This is the set of interfaces which exports depend on that are |
879 | | // themselves not exports. |
880 | 6.46k | let mut must_be_imported = HashMap::new(); |
881 | 6.46k | for (key, export) in into_world.exports.iter() { |
882 | 0 | for dep in self.world_item_direct_deps(export) { |
883 | 0 | if into_world.exports.contains_key(&WorldKey::Interface(dep)) { |
884 | 0 | continue; |
885 | 0 | } |
886 | 0 | self.foreach_interface_dep(dep, &mut |id| { |
887 | 0 | must_be_imported.insert(id, key.clone()); |
888 | 0 | }); |
889 | | } |
890 | | } |
891 | | |
892 | | // Next walk over exports of `from` and process these similarly to |
893 | | // imports. |
894 | 9.42k | for (name, from_export) in from_world.exports.iter() { |
895 | 9.42k | let name_str = self.name_world_key(name); |
896 | 9.42k | match into_world.exports.get(name) { |
897 | 0 | Some(into_export) => { |
898 | 0 | log::trace!("info/from shared export on `{name_str}`"); |
899 | 0 | self.merge_world_item(from_export, into_export) |
900 | 0 | .with_context(|| format!("failed to merge world export {name_str}"))?; |
901 | | } |
902 | | None => { |
903 | 9.42k | log::trace!("new export `{name_str}`"); |
904 | | // See comments in `ensure_can_add_world_export` for why |
905 | | // this is slightly different than imports. |
906 | 9.42k | self.ensure_can_add_world_export( |
907 | 9.42k | into_world, |
908 | 9.42k | name, |
909 | 9.42k | from_export, |
910 | 9.42k | &must_be_imported, |
911 | | ) |
912 | 9.42k | .with_context(|| { |
913 | 0 | format!("failed to add export `{}`", self.name_world_key(name)) |
914 | 0 | })?; |
915 | 9.42k | new_exports.push((name.clone(), from_export.clone())); |
916 | | } |
917 | | } |
918 | | } |
919 | | |
920 | | // For all the new imports and exports they may need to be "cloned" to |
921 | | // be able to belong to the new world. For example: |
922 | | // |
923 | | // * Anonymous interfaces have a `package` field which points to the |
924 | | // package of the containing world, but `from` and `into` may not be |
925 | | // in the same package. |
926 | | // |
927 | | // * Type imports have an `owner` field that point to `from`, but they |
928 | | // now need to point to `into` instead. |
929 | | // |
930 | | // Cloning is no trivial task, however, so cloning is delegated to a |
931 | | // submodule to perform a "deep" clone and copy items into new arena |
932 | | // entries as necessary. |
933 | 6.46k | let mut cloner = clone::Cloner::new( |
934 | 6.46k | self, |
935 | 6.46k | clone_maps, |
936 | 6.46k | TypeOwner::World(from), |
937 | 6.46k | TypeOwner::World(into), |
938 | | ); |
939 | 6.46k | cloner.register_world_type_overlap(from, into); |
940 | 14.8k | for (name, item) in new_imports.iter_mut().chain(&mut new_exports) { |
941 | 14.8k | cloner.world_item(name, item); |
942 | 14.8k | } |
943 | | |
944 | | // Insert any new imports and new exports found first. |
945 | 6.46k | let into_world = &mut self.worlds[into]; |
946 | 6.46k | for (name, import) in new_imports { |
947 | 5.41k | let prev = into_world.imports.insert(name, import); |
948 | 5.41k | assert!(prev.is_none()); |
949 | | } |
950 | 9.42k | for (name, export) in new_exports { |
951 | 9.42k | let prev = into_world.exports.insert(name, export); |
952 | 9.42k | assert!(prev.is_none()); |
953 | | } |
954 | | |
955 | | #[cfg(debug_assertions)] |
956 | | self.assert_valid(); |
957 | 6.46k | Ok(()) |
958 | 6.46k | } |
959 | | |
960 | 0 | fn merge_world_item(&self, from: &WorldItem, into: &WorldItem) -> anyhow::Result<()> { |
961 | 0 | let mut map = MergeMap::new(self, self); |
962 | 0 | match (from, into) { |
963 | 0 | (WorldItem::Interface { id: from, .. }, WorldItem::Interface { id: into, .. }) => { |
964 | | // If these imports are the same that can happen, for |
965 | | // example, when both worlds to `import foo:bar/baz;`. That |
966 | | // foreign interface will point to the same interface within |
967 | | // `Resolve`. |
968 | 0 | if from == into { |
969 | 0 | return Ok(()); |
970 | 0 | } |
971 | | |
972 | | // .. otherwise this MUST be a case of |
973 | | // `import foo: interface { ... }`. If `from != into` but |
974 | | // both `from` and `into` have the same name then the |
975 | | // `WorldKey::Interface` case is ruled out as otherwise |
976 | | // they'd have different names. |
977 | | // |
978 | | // In the case of an anonymous interface all we can do is |
979 | | // ensure that the interfaces both match, so use `MergeMap` |
980 | | // for that. |
981 | 0 | map.build_interface(*from, *into) |
982 | 0 | .context("failed to merge interfaces")?; |
983 | | } |
984 | | |
985 | | // Like `WorldKey::Name` interfaces for functions and types the |
986 | | // structure is asserted to be the same. |
987 | 0 | (WorldItem::Function(from), WorldItem::Function(into)) => { |
988 | 0 | map.build_function(from, into) |
989 | 0 | .context("failed to merge functions")?; |
990 | | } |
991 | 0 | (WorldItem::Type { id: from, .. }, WorldItem::Type { id: into, .. }) => { |
992 | 0 | map.build_type_id(*from, *into) |
993 | 0 | .context("failed to merge types")?; |
994 | | } |
995 | | |
996 | | // Kind-level mismatches are caught here. |
997 | | (WorldItem::Interface { .. }, _) |
998 | | | (WorldItem::Function { .. }, _) |
999 | | | (WorldItem::Type { .. }, _) => { |
1000 | 0 | bail!("different kinds of items"); |
1001 | | } |
1002 | | } |
1003 | 0 | assert!(map.interfaces_to_add.is_empty()); |
1004 | 0 | assert!(map.worlds_to_add.is_empty()); |
1005 | 0 | Ok(()) |
1006 | 0 | } |
1007 | | |
1008 | | /// This method ensures that the world export of `name` and `item` can be |
1009 | | /// added to the world `into` without changing the meaning of `into`. |
1010 | | /// |
1011 | | /// All dependencies of world exports must either be: |
1012 | | /// |
1013 | | /// * An export themselves |
1014 | | /// * An import with all transitive dependencies of the import also imported |
1015 | | /// |
1016 | | /// It's not possible to depend on an import which then also depends on an |
1017 | | /// export at some point, for example. This method ensures that if `name` |
1018 | | /// and `item` are added that this property is upheld. |
1019 | 9.42k | fn ensure_can_add_world_export( |
1020 | 9.42k | &self, |
1021 | 9.42k | into: &World, |
1022 | 9.42k | name: &WorldKey, |
1023 | 9.42k | item: &WorldItem, |
1024 | 9.42k | must_be_imported: &HashMap<InterfaceId, WorldKey>, |
1025 | 9.42k | ) -> anyhow::Result<()> { |
1026 | 9.42k | assert!(!into.exports.contains_key(name)); |
1027 | 9.42k | let name = self.name_world_key(name); |
1028 | | |
1029 | | // First make sure that all of this item's dependencies are either |
1030 | | // exported or the entire chain of imports rooted at that dependency are |
1031 | | // all imported. |
1032 | 9.42k | for dep in self.world_item_direct_deps(item) { |
1033 | 1.19k | if into.exports.contains_key(&WorldKey::Interface(dep)) { |
1034 | 0 | continue; |
1035 | 1.19k | } |
1036 | 1.19k | self.ensure_not_exported(into, dep) |
1037 | 1.19k | .with_context(|| format!("failed validating export of `{name}`"))?; |
1038 | | } |
1039 | | |
1040 | | // Second make sure that this item, if it's an interface, will not alter |
1041 | | // the meaning of the preexisting world by ensuring that it's not in the |
1042 | | // set of "must be imported" items. |
1043 | 9.42k | if let WorldItem::Interface { id, .. } = item { |
1044 | 8.96k | if let Some(export) = must_be_imported.get(id) { |
1045 | 0 | let export_name = self.name_world_key(export); |
1046 | 0 | bail!( |
1047 | | "export `{export_name}` depends on `{name}` \ |
1048 | | previously as an import which will change meaning \ |
1049 | | if `{name}` is added as an export" |
1050 | | ); |
1051 | 8.96k | } |
1052 | 460 | } |
1053 | | |
1054 | 9.42k | Ok(()) |
1055 | 9.42k | } |
1056 | | |
1057 | 1.56k | fn ensure_not_exported(&self, world: &World, id: InterfaceId) -> anyhow::Result<()> { |
1058 | 1.56k | let key = WorldKey::Interface(id); |
1059 | 1.56k | let name = self.name_world_key(&key); |
1060 | 1.56k | if world.exports.contains_key(&key) { |
1061 | 0 | bail!( |
1062 | | "world exports `{name}` but it's also transitively used by an \ |
1063 | | import \ |
1064 | | which means that this is not valid" |
1065 | | ) |
1066 | 1.56k | } |
1067 | 1.56k | for dep in self.interface_direct_deps(id) { |
1068 | 370 | self.ensure_not_exported(world, dep) |
1069 | 370 | .with_context(|| format!("failed validating transitive import dep `{name}`"))?; |
1070 | | } |
1071 | 1.56k | Ok(()) |
1072 | 1.56k | } |
1073 | | |
1074 | | /// Returns an iterator of all the direct interface dependencies of this |
1075 | | /// `item`. |
1076 | | /// |
1077 | | /// Note that this doesn't include transitive dependencies, that must be |
1078 | | /// followed manually. |
1079 | 9.42k | fn world_item_direct_deps(&self, item: &WorldItem) -> impl Iterator<Item = InterfaceId> + '_ { |
1080 | 9.42k | let mut interface = None; |
1081 | 9.42k | let mut ty = None; |
1082 | 9.42k | match item { |
1083 | 460 | WorldItem::Function(_) => {} |
1084 | 0 | WorldItem::Type { id, .. } => ty = Some(*id), |
1085 | 8.96k | WorldItem::Interface { id, .. } => interface = Some(*id), |
1086 | | } |
1087 | | |
1088 | 9.42k | interface |
1089 | 9.42k | .into_iter() |
1090 | 9.42k | .flat_map(move |id| self.interface_direct_deps(id)) |
1091 | 9.42k | .chain(ty.and_then(|t| self.type_interface_dep(t))) |
1092 | 9.42k | } |
1093 | | |
1094 | | /// Invokes `f` with `id` and all transitive interface dependencies of `id`. |
1095 | | /// |
1096 | | /// Note that `f` may be called with the same id multiple times. |
1097 | 1.17k | fn foreach_interface_dep(&self, id: InterfaceId, f: &mut dyn FnMut(InterfaceId)) { |
1098 | 1.17k | self._foreach_interface_dep(id, f, &mut HashSet::new()) |
1099 | 1.17k | } |
1100 | | |
1101 | | // Internal detail of `foreach_interface_dep` which uses a hash map to prune |
1102 | | // the visit tree to ensure that this doesn't visit an exponential number of |
1103 | | // interfaces. |
1104 | 1.51k | fn _foreach_interface_dep( |
1105 | 1.51k | &self, |
1106 | 1.51k | id: InterfaceId, |
1107 | 1.51k | f: &mut dyn FnMut(InterfaceId), |
1108 | 1.51k | visited: &mut HashSet<InterfaceId>, |
1109 | 1.51k | ) { |
1110 | 1.51k | if !visited.insert(id) { |
1111 | 256 | return; |
1112 | 1.26k | } |
1113 | 1.26k | f(id); |
1114 | 1.26k | for dep in self.interface_direct_deps(id) { |
1115 | 344 | self._foreach_interface_dep(dep, f, visited); |
1116 | 344 | } |
1117 | 1.51k | } |
1118 | | |
1119 | | /// Returns the ID of the specified `interface`. |
1120 | | /// |
1121 | | /// Returns `None` for unnamed interfaces. |
1122 | 69.3k | pub fn id_of(&self, interface: InterfaceId) -> Option<String> { |
1123 | 69.3k | let interface = &self.interfaces[interface]; |
1124 | 69.3k | Some(self.id_of_name(interface.package.unwrap(), interface.name.as_ref()?)) |
1125 | 69.3k | } |
1126 | | |
1127 | | /// Returns the "canonicalized interface name" of `interface`. |
1128 | | /// |
1129 | | /// Returns `None` for unnamed interfaces. See `BuildTargets.md` in the |
1130 | | /// upstream component model repository for more information about this. |
1131 | 50.5k | pub fn canonicalized_id_of(&self, interface: InterfaceId) -> Option<String> { |
1132 | 50.5k | let interface = &self.interfaces[interface]; |
1133 | 50.5k | Some(self.canonicalized_id_of_name(interface.package.unwrap(), interface.name.as_ref()?)) |
1134 | 50.5k | } |
1135 | | |
1136 | | /// Helper to rename a world and update the package's world map. |
1137 | | /// |
1138 | | /// Used by both [`Resolve::importize`] and [`Resolve::exportize`] to |
1139 | | /// rename the world to avoid confusion with the original world name. |
1140 | 3.23k | fn rename_world( |
1141 | 3.23k | &mut self, |
1142 | 3.23k | world_id: WorldId, |
1143 | 3.23k | out_world_name: Option<String>, |
1144 | 3.23k | default_suffix: &str, |
1145 | 3.23k | ) { |
1146 | 3.23k | let world = &mut self.worlds[world_id]; |
1147 | 3.23k | let pkg = &mut self.packages[world.package.unwrap()]; |
1148 | 3.23k | pkg.worlds.shift_remove(&world.name); |
1149 | 3.23k | if let Some(name) = out_world_name { |
1150 | 0 | world.name = name.clone(); |
1151 | 0 | pkg.worlds.insert(name, world_id); |
1152 | 3.23k | } else { |
1153 | 3.23k | world.name.push_str(default_suffix); |
1154 | 3.23k | pkg.worlds.insert(world.name.clone(), world_id); |
1155 | 3.23k | } |
1156 | 3.23k | } |
1157 | | |
1158 | | /// Convert a world to an "importized" version where the world is updated |
1159 | | /// in-place to reflect what it would look like to be imported. |
1160 | | /// |
1161 | | /// This is a transformation which is used as part of the process of |
1162 | | /// importing a component today. For example when a component depends on |
1163 | | /// another component this is useful for generating WIT which can be use to |
1164 | | /// represent the component being imported. The general idea is that this |
1165 | | /// function will update the `world_id` specified such it imports the |
1166 | | /// functionality that it previously exported. The world will be left with |
1167 | | /// no exports. |
1168 | | /// |
1169 | | /// This world is then suitable for merging into other worlds or generating |
1170 | | /// bindings in a context that is importing the original world. This |
1171 | | /// is intended to be used as part of language tooling when depending on |
1172 | | /// other components. |
1173 | 3.23k | pub fn importize( |
1174 | 3.23k | &mut self, |
1175 | 3.23k | world_id: WorldId, |
1176 | 3.23k | out_world_name: Option<String>, |
1177 | 3.23k | ) -> anyhow::Result<()> { |
1178 | 3.23k | self.rename_world(world_id, out_world_name, "-importized"); |
1179 | | |
1180 | | // Trim all non-type definitions from imports. Types can be used by |
1181 | | // exported functions, for example, so they're preserved. |
1182 | 3.23k | let world = &mut self.worlds[world_id]; |
1183 | 3.23k | world.imports.retain(|_, item| match item { |
1184 | 1.18k | WorldItem::Type { .. } => true, |
1185 | 1.52k | _ => false, |
1186 | 2.70k | }); |
1187 | | |
1188 | 4.54k | for (name, export) in mem::take(&mut world.exports) { |
1189 | 4.54k | match (name.clone(), world.imports.insert(name, export)) { |
1190 | | // no previous item? this insertion was ok |
1191 | 4.42k | (_, None) => {} |
1192 | | |
1193 | | // cannot overwrite an import with an export |
1194 | 121 | (WorldKey::Name(name), Some(_)) => { |
1195 | 121 | bail!("world export `{name}` conflicts with import of same name"); |
1196 | | } |
1197 | | |
1198 | | // Exports already don't overlap each other and the only imports |
1199 | | // preserved above were types so this shouldn't be reachable. |
1200 | 0 | (WorldKey::Interface(_), _) => unreachable!(), |
1201 | | } |
1202 | | } |
1203 | | |
1204 | | // Fill out any missing transitive interface imports by elaborating this |
1205 | | // world which does that for us. |
1206 | 3.11k | let world_span = world.span; |
1207 | 3.11k | self.elaborate_world(world_id, world_span)?; |
1208 | | |
1209 | | #[cfg(debug_assertions)] |
1210 | | self.assert_valid(); |
1211 | 3.11k | Ok(()) |
1212 | 3.23k | } |
1213 | | |
1214 | | /// Convert a world to an "exportized" version where the world is updated |
1215 | | /// in-place to reflect what it would look like to be exported. |
1216 | | /// |
1217 | | /// This is the inverse of [`Resolve::importize`]. The general idea is that |
1218 | | /// this function will update the `world_id` specified such that it exports |
1219 | | /// the functionality that it previously imported. The world will be left |
1220 | | /// with no imports (except for transitive interface dependencies which may |
1221 | | /// be needed by exported interfaces). |
1222 | | /// |
1223 | | /// An optional `filter` can be provided to control which imports are moved. |
1224 | | /// When `Some`, only imports for which the filter returns `true` are moved |
1225 | | /// to exports; remaining imports are left as-is. When `None`, all imports |
1226 | | /// are moved. |
1227 | | /// |
1228 | | /// This world is then suitable for merging into other worlds or generating |
1229 | | /// bindings in a context that is exporting the original world. This is |
1230 | | /// intended to be used as part of language tooling when implementing |
1231 | | /// components. |
1232 | 0 | pub fn exportize( |
1233 | 0 | &mut self, |
1234 | 0 | world_id: WorldId, |
1235 | 0 | out_world_name: Option<String>, |
1236 | 0 | filter: Option<&dyn Fn(&WorldKey, &WorldItem) -> bool>, |
1237 | 0 | ) -> anyhow::Result<()> { |
1238 | 0 | self.rename_world(world_id, out_world_name, "-exportized"); |
1239 | | |
1240 | 0 | let world = &mut self.worlds[world_id]; |
1241 | 0 | world.exports.clear(); |
1242 | | |
1243 | 0 | let old_imports = mem::take(&mut world.imports); |
1244 | 0 | for (name, import) in old_imports { |
1245 | 0 | let should_move = match &filter { |
1246 | 0 | Some(f) => f(&name, &import), |
1247 | 0 | None => true, |
1248 | | }; |
1249 | 0 | if should_move { |
1250 | 0 | world.exports.insert(name, import); |
1251 | 0 | } else { |
1252 | 0 | world.imports.insert(name, import); |
1253 | 0 | } |
1254 | | } |
1255 | | |
1256 | | // Fill out any missing transitive interface imports by elaborating this |
1257 | | // world which does that for us. |
1258 | 0 | let world_span = world.span; |
1259 | 0 | self.elaborate_world(world_id, world_span)?; |
1260 | | |
1261 | | #[cfg(debug_assertions)] |
1262 | | self.assert_valid(); |
1263 | 0 | Ok(()) |
1264 | 0 | } |
1265 | | |
1266 | | /// Returns the ID of the specified `name` within the `pkg`. |
1267 | 72.5k | pub fn id_of_name(&self, pkg: PackageId, name: &str) -> String { |
1268 | 72.5k | let package = &self.packages[pkg]; |
1269 | 72.5k | let mut base = String::new(); |
1270 | 72.5k | base.push_str(&package.name.namespace); |
1271 | 72.5k | base.push_str(":"); |
1272 | 72.5k | base.push_str(&package.name.name); |
1273 | 72.5k | base.push_str("/"); |
1274 | 72.5k | base.push_str(name); |
1275 | 72.5k | if let Some(version) = &package.name.version { |
1276 | 59.5k | base.push_str(&format!("@{version}")); |
1277 | 59.5k | } |
1278 | 72.5k | base |
1279 | 72.5k | } |
1280 | | |
1281 | | /// Returns the "canonicalized interface name" of the specified `name` |
1282 | | /// within the `pkg`. |
1283 | | /// |
1284 | | /// See `BuildTargets.md` in the upstream component model repository for |
1285 | | /// more information about this. |
1286 | 50.5k | pub fn canonicalized_id_of_name(&self, pkg: PackageId, name: &str) -> String { |
1287 | 50.5k | let package = &self.packages[pkg]; |
1288 | 50.5k | let mut base = String::new(); |
1289 | 50.5k | base.push_str(&package.name.namespace); |
1290 | 50.5k | base.push_str(":"); |
1291 | 50.5k | base.push_str(&package.name.name); |
1292 | 50.5k | base.push_str("/"); |
1293 | 50.5k | base.push_str(name); |
1294 | 50.5k | if let Some(version) = &package.name.version { |
1295 | 44.8k | base.push_str("@"); |
1296 | 44.8k | let (string, _) = PackageName::version_compat_track_string(version); |
1297 | 44.8k | base.push_str(&string); |
1298 | 44.8k | } |
1299 | 50.5k | base |
1300 | 50.5k | } |
1301 | | |
1302 | | /// Selects a world from among the packages in a `Resolve`. |
1303 | | /// |
1304 | | /// A `Resolve` may have many packages, each with many worlds. Many WIT |
1305 | | /// tools need a specific world to operate on. This function chooses a |
1306 | | /// world, failing if the choice is ambiguous. |
1307 | | /// |
1308 | | /// `main_packages` provides the package IDs returned by |
1309 | | /// [`push_path`](Resolve::push_path), [`push_dir`](Resolve::push_dir), |
1310 | | /// [`push_file`](Resolve::push_file), [`push_group`](Resolve::push_group), |
1311 | | /// and [`push_str`](Resolve::push_str), which are the "main packages", |
1312 | | /// as distinguished from any packages nested inside them. |
1313 | | /// |
1314 | | /// `world` is a world name such as from a `--world` command-line option or |
1315 | | /// a `world:` macro parameter. `world` can be: |
1316 | | /// |
1317 | | /// * A kebab-name of a world, for example `"the-world"`. It is resolved |
1318 | | /// within the "main package", if there is exactly one. |
1319 | | /// |
1320 | | /// * An ID-based form of a world, for example `"wasi:http/proxy"`. Note |
1321 | | /// that a version does not need to be specified in this string if |
1322 | | /// there's only one package of the same name and it has a version. In |
1323 | | /// this situation the version can be omitted. |
1324 | | /// |
1325 | | /// * `None`. If there's exactly one "main package" and it contains exactly |
1326 | | /// one world, that world is chosen. |
1327 | | /// |
1328 | | /// If successful, the chosen `WorldId` is returned. |
1329 | | /// |
1330 | | /// # Examples |
1331 | | /// |
1332 | | /// ``` |
1333 | | /// use anyhow::Result; |
1334 | | /// use wit_parser::Resolve; |
1335 | | /// |
1336 | | /// fn main() -> Result<()> { |
1337 | | /// let mut resolve = Resolve::default(); |
1338 | | /// |
1339 | | /// // If there's a single package and only one world, that world is |
1340 | | /// // the obvious choice. |
1341 | | /// let wit1 = resolve.push_str( |
1342 | | /// "./my-test.wit", |
1343 | | /// r#" |
1344 | | /// package example:wit1; |
1345 | | /// |
1346 | | /// world foo { |
1347 | | /// // ... |
1348 | | /// } |
1349 | | /// "#, |
1350 | | /// )?; |
1351 | | /// assert!(resolve.select_world(&[wit1], None).is_ok()); |
1352 | | /// |
1353 | | /// // If there are multiple packages, we need to be told which package |
1354 | | /// // to use, either by a "main package" or by a fully-qualified name. |
1355 | | /// let wit2 = resolve.push_str( |
1356 | | /// "./my-test.wit", |
1357 | | /// r#" |
1358 | | /// package example:wit2; |
1359 | | /// |
1360 | | /// world foo { /* ... */ } |
1361 | | /// "#, |
1362 | | /// )?; |
1363 | | /// assert!(resolve.select_world(&[wit1, wit2], None).is_err()); |
1364 | | /// assert!(resolve.select_world(&[wit1, wit2], Some("foo")).is_err()); |
1365 | | /// // Fix: use fully-qualified names. |
1366 | | /// assert!(resolve.select_world(&[wit1, wit2], Some("example:wit1/foo")).is_ok()); |
1367 | | /// assert!(resolve.select_world(&[wit1, wit2], Some("example:wit2/foo")).is_ok()); |
1368 | | /// |
1369 | | /// // If a package has multiple worlds, then we can't guess the world |
1370 | | /// // even if we know the package. |
1371 | | /// let wit3 = resolve.push_str( |
1372 | | /// "./my-test.wit", |
1373 | | /// r#" |
1374 | | /// package example:wit3; |
1375 | | /// |
1376 | | /// world foo { /* ... */ } |
1377 | | /// |
1378 | | /// world bar { /* ... */ } |
1379 | | /// "#, |
1380 | | /// )?; |
1381 | | /// assert!(resolve.select_world(&[wit3], None).is_err()); |
1382 | | /// // Fix: pick between "foo" and "bar" here. |
1383 | | /// assert!(resolve.select_world(&[wit3], Some("foo")).is_ok()); |
1384 | | /// |
1385 | | /// // When selecting with a version it's ok to drop the version when |
1386 | | /// // there's only a single copy of that package in `Resolve`. |
1387 | | /// let wit5_1 = resolve.push_str( |
1388 | | /// "./my-test.wit", |
1389 | | /// r#" |
1390 | | /// package example:wit5@1.0.0; |
1391 | | /// |
1392 | | /// world foo { /* ... */ } |
1393 | | /// "#, |
1394 | | /// )?; |
1395 | | /// assert!(resolve.select_world(&[wit5_1], Some("foo")).is_ok()); |
1396 | | /// assert!(resolve.select_world(&[wit5_1], Some("example:wit5/foo")).is_ok()); |
1397 | | /// |
1398 | | /// // However when a single package has multiple versions in a resolve |
1399 | | /// // it's required to specify the version to select which one. |
1400 | | /// let wit5_2 = resolve.push_str( |
1401 | | /// "./my-test.wit", |
1402 | | /// r#" |
1403 | | /// package example:wit5@2.0.0; |
1404 | | /// |
1405 | | /// world foo { /* ... */ } |
1406 | | /// "#, |
1407 | | /// )?; |
1408 | | /// assert!(resolve.select_world(&[wit5_1, wit5_2], Some("example:wit5/foo")).is_err()); |
1409 | | /// // Fix: Pass explicit versions. |
1410 | | /// assert!(resolve.select_world(&[wit5_1, wit5_2], Some("example:wit5/foo@1.0.0")).is_ok()); |
1411 | | /// assert!(resolve.select_world(&[wit5_1, wit5_2], Some("example:wit5/foo@2.0.0")).is_ok()); |
1412 | | /// |
1413 | | /// Ok(()) |
1414 | | /// } |
1415 | | /// ``` |
1416 | 0 | pub fn select_world( |
1417 | 0 | &self, |
1418 | 0 | main_packages: &[PackageId], |
1419 | 0 | world: Option<&str>, |
1420 | 0 | ) -> anyhow::Result<WorldId> { |
1421 | | // Determine if `world` is a kebab-name or an ID. |
1422 | 0 | let world_path = match world { |
1423 | 0 | Some(world) => Some( |
1424 | 0 | parse_use_path(world) |
1425 | 0 | .with_context(|| format!("failed to parse world specifier `{world}`"))?, |
1426 | | ), |
1427 | 0 | None => None, |
1428 | | }; |
1429 | | |
1430 | 0 | match world_path { |
1431 | | // We have a world path. If needed, pick a package to resolve it in. |
1432 | 0 | Some(world_path) => { |
1433 | 0 | let (pkg, world_name) = match (main_packages, world_path) { |
1434 | | // We have no main packages; fail. |
1435 | 0 | ([], _) => bail!("No main packages defined"), |
1436 | | |
1437 | | // We have exactly one main package. |
1438 | 0 | ([main_package], ParsedUsePath::Name(name)) => (*main_package, name), |
1439 | | |
1440 | | // We have more than one main package; fail. |
1441 | 0 | (_, ParsedUsePath::Name(_name)) => { |
1442 | 0 | bail!( |
1443 | | "There are multiple main packages; a world must be explicitly chosen:{}", |
1444 | 0 | self.worlds |
1445 | 0 | .iter() |
1446 | 0 | .map(|world| format!( |
1447 | | "\n {}", |
1448 | 0 | self.id_of_name(world.1.package.unwrap(), &world.1.name) |
1449 | | )) |
1450 | 0 | .collect::<String>() |
1451 | | ) |
1452 | | } |
1453 | | |
1454 | | // The world name is fully-qualified. |
1455 | 0 | (_, ParsedUsePath::Package(pkg, world_name)) => { |
1456 | 0 | let pkg = match self.package_names.get(&pkg) { |
1457 | 0 | Some(pkg) => *pkg, |
1458 | | None => { |
1459 | 0 | let mut candidates = |
1460 | 0 | self.package_names.iter().filter(|(name, _)| { |
1461 | 0 | pkg.version.is_none() |
1462 | 0 | && pkg.name == name.name |
1463 | 0 | && pkg.namespace == name.namespace |
1464 | 0 | && name.version.is_some() |
1465 | 0 | }); |
1466 | 0 | let candidate = candidates.next(); |
1467 | 0 | if let Some((c2, _)) = candidates.next() { |
1468 | 0 | let (c1, _) = candidate.unwrap(); |
1469 | 0 | bail!( |
1470 | | "package name `{pkg}` is available at both \ |
1471 | | versions {} and {} but which is not specified", |
1472 | 0 | c1.version.as_ref().unwrap(), |
1473 | 0 | c2.version.as_ref().unwrap(), |
1474 | | ); |
1475 | 0 | } |
1476 | 0 | match candidate { |
1477 | 0 | Some((_, id)) => *id, |
1478 | 0 | None => bail!("unknown package `{pkg}`"), |
1479 | | } |
1480 | | } |
1481 | | }; |
1482 | 0 | (pkg, world_name.to_string()) |
1483 | | } |
1484 | | }; |
1485 | | |
1486 | | // Now that we've picked the package, resolve the world name. |
1487 | 0 | let pkg = &self.packages[pkg]; |
1488 | 0 | pkg.worlds.get(&world_name).copied().ok_or_else(|| { |
1489 | 0 | anyhow!("World `{world_name}` not found in package `{}`", pkg.name) |
1490 | 0 | }) |
1491 | | } |
1492 | | |
1493 | | // With no specified `world`, try to find a single obvious world. |
1494 | 0 | None => match main_packages { |
1495 | 0 | [] => bail!("No main packages defined"), |
1496 | | |
1497 | | // Check for exactly one main package with exactly one world. |
1498 | 0 | [main_package] => { |
1499 | 0 | let pkg = &self.packages[*main_package]; |
1500 | 0 | match pkg.worlds.len() { |
1501 | 0 | 0 => bail!("The main package `{}` contains no worlds", pkg.name), |
1502 | 0 | 1 => Ok(pkg.worlds[0]), |
1503 | 0 | _ => bail!( |
1504 | | "There are multiple worlds in `{}`; one must be explicitly chosen:{}", |
1505 | | pkg.name, |
1506 | 0 | pkg.worlds |
1507 | 0 | .values() |
1508 | 0 | .map(|world| format!( |
1509 | | "\n {}", |
1510 | 0 | self.id_of_name(*main_package, &self.worlds[*world].name) |
1511 | | )) |
1512 | 0 | .collect::<String>() |
1513 | | ), |
1514 | | } |
1515 | | } |
1516 | | |
1517 | | // Multiple main packages and no world name; fail. |
1518 | | _ => { |
1519 | 0 | bail!( |
1520 | | "There are multiple main packages; a world must be explicitly chosen:{}", |
1521 | 0 | self.worlds |
1522 | 0 | .iter() |
1523 | 0 | .map(|world| format!( |
1524 | | "\n {}", |
1525 | 0 | self.id_of_name(world.1.package.unwrap(), &world.1.name) |
1526 | | )) |
1527 | 0 | .collect::<String>() |
1528 | | ) |
1529 | | } |
1530 | | }, |
1531 | | } |
1532 | 0 | } |
1533 | | |
1534 | | /// Assigns a human readable name to the `WorldKey` specified. |
1535 | 186k | pub fn name_world_key(&self, key: &WorldKey) -> String { |
1536 | 186k | match key { |
1537 | 175k | WorldKey::Name(s) => s.to_string(), |
1538 | 10.5k | WorldKey::Interface(i) => self.id_of(*i).expect("unexpected anonymous interface"), |
1539 | | } |
1540 | 186k | } |
1541 | | |
1542 | | /// Same as [`Resolve::name_world_key`] except that `WorldKey::Interfaces` |
1543 | | /// uses [`Resolve::canonicalized_id_of`]. |
1544 | 596 | pub fn name_canonicalized_world_key(&self, key: &WorldKey) -> String { |
1545 | 596 | match key { |
1546 | 556 | WorldKey::Name(s) => s.to_string(), |
1547 | 40 | WorldKey::Interface(i) => self |
1548 | 40 | .canonicalized_id_of(*i) |
1549 | 40 | .expect("unexpected anonymous interface"), |
1550 | | } |
1551 | 596 | } |
1552 | | |
1553 | | /// Returns the component model `implements` interface for the world import of |
1554 | | /// `key` and `item`. |
1555 | | /// |
1556 | | /// See the component model explainer and 🏷️ for more information on this feature. |
1557 | 40.0k | pub fn implements_interface(&self, key: &WorldKey, item: &WorldItem) -> Option<InterfaceId> { |
1558 | 40.0k | if let WorldKey::Name(_) = key { |
1559 | 37.1k | if let WorldItem::Interface { id, .. } = item { |
1560 | 30.3k | if self.interfaces[*id].name.is_some() { |
1561 | 27.6k | return Some(*id); |
1562 | 2.62k | } |
1563 | 6.85k | } |
1564 | 2.89k | } |
1565 | 12.3k | None |
1566 | 40.0k | } |
1567 | | |
1568 | | /// Returns the component model `version-suffix` value for the interface `id`. |
1569 | | /// |
1570 | | /// See the component model explainer and 🔗 for more information on this feature. |
1571 | 50.0k | pub fn version_suffix_of(&self, id: InterfaceId) -> Option<String> { |
1572 | 50.0k | let pkg = self.interfaces[id].package?; |
1573 | 50.0k | let version = self.packages[pkg].name.version.as_ref()?; |
1574 | 44.4k | let (_, suffix) = PackageName::version_compat_track(version); |
1575 | 44.4k | suffix |
1576 | 50.0k | } |
1577 | | |
1578 | | /// Returns the component model `external-id` value for the world import of |
1579 | | /// `key` and `item`. |
1580 | | /// |
1581 | | /// See the component model explainer and 🏷️ for more information on this feature. |
1582 | 50.4k | pub fn external_id_value(&self, key: &WorldKey, item: &WorldItem) -> Option<String> { |
1583 | 50.4k | let _ = key; |
1584 | 50.4k | match item { |
1585 | 43.9k | WorldItem::Interface { external_id, .. } => external_id.clone(), |
1586 | 6.53k | WorldItem::Function(f) => f.external_id.clone(), |
1587 | 0 | WorldItem::Type { id, .. } => self.types[*id].external_id.clone(), |
1588 | | } |
1589 | 50.4k | } |
1590 | | |
1591 | | /// Returns the interface that `id` uses a type from, if it uses a type from |
1592 | | /// a different interface than `id` is defined within. |
1593 | | /// |
1594 | | /// If `id` is not a use-of-a-type or it's using a type in the same |
1595 | | /// interface then `None` is returned. |
1596 | 527k | pub fn type_interface_dep(&self, id: TypeId) -> Option<InterfaceId> { |
1597 | 527k | let ty = &self.types[id]; |
1598 | 54.3k | let dep = match ty.kind { |
1599 | 45.0k | TypeDefKind::Type(Type::Id(id)) => id, |
1600 | 482k | _ => return None, |
1601 | | }; |
1602 | 45.0k | let other = &self.types[dep]; |
1603 | 45.0k | if ty.owner == other.owner { |
1604 | 133 | None |
1605 | | } else { |
1606 | 44.9k | match other.owner { |
1607 | 44.9k | TypeOwner::Interface(id) => Some(id), |
1608 | 0 | _ => unreachable!(), |
1609 | | } |
1610 | | } |
1611 | 527k | } |
1612 | | |
1613 | | /// Returns an iterator of all interfaces that the interface `id` depends |
1614 | | /// on. |
1615 | | /// |
1616 | | /// Interfaces may depend on others for type information to resolve type |
1617 | | /// imports. |
1618 | | /// |
1619 | | /// Note that the returned iterator may yield the same interface as a |
1620 | | /// dependency multiple times. Additionally only direct dependencies of `id` |
1621 | | /// are yielded, not transitive dependencies. |
1622 | 183k | pub fn interface_direct_deps(&self, id: InterfaceId) -> impl Iterator<Item = InterfaceId> + '_ { |
1623 | 183k | self.interfaces[id] |
1624 | 183k | .types |
1625 | 183k | .iter() |
1626 | 482k | .filter_map(move |(_name, ty)| self.type_interface_dep(*ty)) |
1627 | 183k | } |
1628 | | |
1629 | | /// Returns an iterator of all packages that the package `id` depends |
1630 | | /// on. |
1631 | | /// |
1632 | | /// Packages may depend on others for type information to resolve type |
1633 | | /// imports or interfaces to resolve worlds. |
1634 | | /// |
1635 | | /// Note that the returned iterator may yield the same package as a |
1636 | | /// dependency multiple times. Additionally only direct dependencies of `id` |
1637 | | /// are yielded, not transitive dependencies. |
1638 | 15.9k | pub fn package_direct_deps(&self, id: PackageId) -> impl Iterator<Item = PackageId> + '_ { |
1639 | 15.9k | let pkg = &self.packages[id]; |
1640 | | |
1641 | 15.9k | pkg.interfaces |
1642 | 15.9k | .iter() |
1643 | 20.8k | .flat_map(move |(_name, id)| self.interface_direct_deps(*id)) |
1644 | 16.2k | .chain(pkg.worlds.iter().flat_map(move |(_name, id)| { |
1645 | 16.2k | let world = &self.worlds[*id]; |
1646 | 16.2k | world |
1647 | 16.2k | .imports |
1648 | 16.2k | .iter() |
1649 | 16.2k | .chain(world.exports.iter()) |
1650 | 37.3k | .filter_map(move |(_name, item)| match item { |
1651 | 26.9k | WorldItem::Interface { id, .. } => Some(*id), |
1652 | 4.44k | WorldItem::Function(_) => None, |
1653 | 5.95k | WorldItem::Type { id, .. } => self.type_interface_dep(*id), |
1654 | 37.3k | }) |
1655 | 16.2k | })) |
1656 | 31.7k | .filter_map(move |iface_id| { |
1657 | 31.7k | let pkg = self.interfaces[iface_id].package?; |
1658 | 31.7k | if pkg == id { None } else { Some(pkg) } |
1659 | 31.7k | }) |
1660 | 15.9k | } |
1661 | | |
1662 | | /// Returns a topological ordering of packages contained in this `Resolve`. |
1663 | | /// |
1664 | | /// This returns a list of `PackageId` such that when visited in order it's |
1665 | | /// guaranteed that all dependencies will have been defined by prior items |
1666 | | /// in the list. |
1667 | 10.6k | pub fn topological_packages(&self) -> Vec<PackageId> { |
1668 | 10.6k | let mut pushed = vec![false; self.packages.len()]; |
1669 | 10.6k | let mut order = Vec::new(); |
1670 | 15.9k | for (id, _) in self.packages.iter() { |
1671 | 15.9k | self.build_topological_package_ordering(id, &mut pushed, &mut order); |
1672 | 15.9k | } |
1673 | 10.6k | order |
1674 | 10.6k | } |
1675 | | |
1676 | 35.8k | fn build_topological_package_ordering( |
1677 | 35.8k | &self, |
1678 | 35.8k | id: PackageId, |
1679 | 35.8k | pushed: &mut Vec<bool>, |
1680 | 35.8k | order: &mut Vec<PackageId>, |
1681 | 35.8k | ) { |
1682 | 35.8k | if pushed[id.index()] { |
1683 | 19.8k | return; |
1684 | 15.9k | } |
1685 | 19.8k | for dep in self.package_direct_deps(id) { |
1686 | 19.8k | self.build_topological_package_ordering(dep, pushed, order); |
1687 | 19.8k | } |
1688 | 15.9k | order.push(id); |
1689 | 15.9k | pushed[id.index()] = true; |
1690 | 35.8k | } |
1691 | | |
1692 | | #[doc(hidden)] |
1693 | 4.13k | pub fn assert_valid(&self) { |
1694 | 4.13k | let mut package_interfaces = Vec::new(); |
1695 | 4.13k | let mut package_worlds = Vec::new(); |
1696 | 4.89k | for (id, pkg) in self.packages.iter() { |
1697 | 4.89k | let mut interfaces = HashSet::new(); |
1698 | 18.2k | for (name, iface) in pkg.interfaces.iter() { |
1699 | 18.2k | assert!(interfaces.insert(*iface)); |
1700 | 18.2k | let iface = &self.interfaces[*iface]; |
1701 | 18.2k | assert_eq!(name, iface.name.as_ref().unwrap()); |
1702 | 18.2k | assert_eq!(iface.package.unwrap(), id); |
1703 | | } |
1704 | 4.89k | package_interfaces.push(pkg.interfaces.values().copied().collect::<HashSet<_>>()); |
1705 | 4.89k | let mut worlds = HashSet::new(); |
1706 | 6.46k | for (name, world) in pkg.worlds.iter() { |
1707 | 6.46k | assert!(worlds.insert(*world)); |
1708 | 6.46k | assert_eq!( |
1709 | 6.46k | pkg.worlds.get_key_value(name), |
1710 | 6.46k | Some((name, world)), |
1711 | | "`MutableKeys` impl may have been used to change a key's hash or equality" |
1712 | | ); |
1713 | 6.46k | let world = &self.worlds[*world]; |
1714 | 6.46k | assert_eq!(*name, world.name); |
1715 | 6.46k | assert_eq!(world.package.unwrap(), id); |
1716 | | } |
1717 | 4.89k | package_worlds.push(pkg.worlds.values().copied().collect::<HashSet<_>>()); |
1718 | | } |
1719 | | |
1720 | 4.13k | let mut interface_types = Vec::new(); |
1721 | 19.0k | for (id, iface) in self.interfaces.iter() { |
1722 | 19.0k | assert!(self.packages.get(iface.package.unwrap()).is_some()); |
1723 | 19.0k | if iface.name.is_some() { |
1724 | 18.2k | match iface.clone_of { |
1725 | 0 | Some(other) => { |
1726 | 0 | assert_eq!(iface.name, self.interfaces[other].name); |
1727 | | } |
1728 | | None => { |
1729 | 18.2k | assert!(package_interfaces[iface.package.unwrap().index()].contains(&id)); |
1730 | | } |
1731 | | } |
1732 | 784 | } |
1733 | | |
1734 | 19.0k | for (name, ty) in iface.types.iter() { |
1735 | 12.6k | let ty = &self.types[*ty]; |
1736 | 12.6k | assert_eq!(ty.name.as_ref(), Some(name)); |
1737 | 12.6k | assert_eq!(ty.owner, TypeOwner::Interface(id)); |
1738 | | } |
1739 | 19.0k | interface_types.push(iface.types.values().copied().collect::<HashSet<_>>()); |
1740 | 19.0k | for (name, f) in iface.functions.iter() { |
1741 | 6.08k | assert_eq!(*name, f.name); |
1742 | | } |
1743 | | } |
1744 | | |
1745 | 4.13k | let mut world_types = Vec::new(); |
1746 | 6.46k | for (id, world) in self.worlds.iter() { |
1747 | 6.46k | log::debug!("validating world {}", &world.name); |
1748 | 6.46k | if let Some(package) = world.package { |
1749 | 6.46k | assert!(self.packages.get(package).is_some()); |
1750 | 6.46k | assert!(package_worlds[package.index()].contains(&id)); |
1751 | 0 | } |
1752 | 6.46k | assert!(world.includes.is_empty()); |
1753 | | |
1754 | 6.46k | let mut types = HashSet::new(); |
1755 | 14.8k | for (name, item) in world.imports.iter().chain(world.exports.iter()) { |
1756 | 14.8k | log::debug!("validating world item: {}", self.name_world_key(name)); |
1757 | 14.8k | match item { |
1758 | 10.7k | WorldItem::Interface { id, .. } => { |
1759 | | // Anonymous interfaces must belong to the same package, |
1760 | | // but interfaces through `implements` can be in any |
1761 | | // package. |
1762 | 10.7k | if matches!(name, WorldKey::Name(_)) { |
1763 | 9.84k | let iface = &self.interfaces[*id]; |
1764 | 9.84k | if iface.name.is_none() { |
1765 | 784 | assert_eq!(iface.package, world.package); |
1766 | 9.06k | } |
1767 | 862 | } |
1768 | | } |
1769 | 1.76k | WorldItem::Function(f) => { |
1770 | 1.76k | assert!(!matches!(name, WorldKey::Interface(_))); |
1771 | 1.76k | assert_eq!(f.name, name.clone().unwrap_name()); |
1772 | | } |
1773 | 2.36k | WorldItem::Type { id: ty, .. } => { |
1774 | 2.36k | assert!(!matches!(name, WorldKey::Interface(_))); |
1775 | 2.36k | assert!(types.insert(*ty)); |
1776 | 2.36k | let ty = &self.types[*ty]; |
1777 | 2.36k | assert_eq!(ty.name, Some(name.clone().unwrap_name())); |
1778 | 2.36k | assert_eq!(ty.owner, TypeOwner::World(id)); |
1779 | | } |
1780 | | } |
1781 | | } |
1782 | 6.46k | self.assert_world_elaborated(world); |
1783 | 6.46k | world_types.push(types); |
1784 | | } |
1785 | | |
1786 | 55.7k | for (ty_id, ty) in self.types.iter() { |
1787 | 55.7k | match ty.owner { |
1788 | 12.6k | TypeOwner::Interface(id) => { |
1789 | 12.6k | assert!(self.interfaces.get(id).is_some()); |
1790 | 12.6k | assert!(interface_types[id.index()].contains(&ty_id)); |
1791 | | } |
1792 | 2.36k | TypeOwner::World(id) => { |
1793 | 2.36k | assert!(self.worlds.get(id).is_some()); |
1794 | 2.36k | assert!(world_types[id.index()].contains(&ty_id)); |
1795 | | } |
1796 | 40.7k | TypeOwner::None => {} |
1797 | | } |
1798 | | } |
1799 | | |
1800 | 4.13k | self.assert_topologically_sorted(); |
1801 | 4.13k | } |
1802 | | |
1803 | 4.13k | fn assert_topologically_sorted(&self) { |
1804 | 4.13k | let mut positions = IndexMap::default(); |
1805 | 4.89k | for id in self.topological_packages() { |
1806 | 4.89k | let pkg = &self.packages[id]; |
1807 | 4.89k | log::debug!("pkg {}", pkg.name); |
1808 | 4.89k | let prev = positions.insert(Some(id), IndexSet::default()); |
1809 | 4.89k | assert!(prev.is_none()); |
1810 | | } |
1811 | 4.13k | positions.insert(None, IndexSet::default()); |
1812 | | |
1813 | 19.0k | for (id, iface) in self.interfaces.iter() { |
1814 | 19.0k | log::debug!("iface {:?}", iface.name); |
1815 | 19.0k | let ok = positions.get_mut(&iface.package).unwrap().insert(id); |
1816 | 19.0k | assert!(ok); |
1817 | | } |
1818 | | |
1819 | 6.46k | for (_, world) in self.worlds.iter() { |
1820 | 6.46k | log::debug!("world {:?}", world.name); |
1821 | | |
1822 | 6.46k | let my_package = world.package; |
1823 | 6.46k | let my_package_pos = positions.get_index_of(&my_package).unwrap(); |
1824 | | |
1825 | 14.8k | for (_, item) in world.imports.iter().chain(&world.exports) { |
1826 | 14.8k | let id = match item { |
1827 | 10.7k | WorldItem::Interface { id, .. } => *id, |
1828 | 4.13k | _ => continue, |
1829 | | }; |
1830 | 10.7k | let other_package = self.interfaces[id].package; |
1831 | 10.7k | let other_package_pos = positions.get_index_of(&other_package).unwrap(); |
1832 | | |
1833 | 10.7k | assert!(other_package_pos <= my_package_pos); |
1834 | | } |
1835 | | } |
1836 | | |
1837 | 55.7k | for (_id, ty) in self.types.iter() { |
1838 | 55.7k | log::debug!("type {:?} {:?}", ty.name, ty.owner); |
1839 | 4.47k | let other_id = match ty.kind { |
1840 | 3.85k | TypeDefKind::Type(Type::Id(ty)) => ty, |
1841 | 51.9k | _ => continue, |
1842 | | }; |
1843 | 3.85k | let other = &self.types[other_id]; |
1844 | 3.85k | if ty.kind == other.kind { |
1845 | 0 | continue; |
1846 | 3.85k | } |
1847 | 3.85k | let my_interface = match ty.owner { |
1848 | 3.46k | TypeOwner::Interface(id) => id, |
1849 | 392 | _ => continue, |
1850 | | }; |
1851 | 3.46k | let other_interface = match other.owner { |
1852 | 3.46k | TypeOwner::Interface(id) => id, |
1853 | 0 | _ => continue, |
1854 | | }; |
1855 | | |
1856 | 3.46k | let my_package = self.interfaces[my_interface].package; |
1857 | 3.46k | let other_package = self.interfaces[other_interface].package; |
1858 | 3.46k | let my_package_pos = positions.get_index_of(&my_package).unwrap(); |
1859 | 3.46k | let other_package_pos = positions.get_index_of(&other_package).unwrap(); |
1860 | | |
1861 | 3.46k | assert!(other_package_pos <= my_package_pos); |
1862 | | } |
1863 | 4.13k | } |
1864 | | |
1865 | 6.46k | fn assert_world_elaborated(&self, world: &World) { |
1866 | 6.46k | for (key, item) in world.imports.iter() { |
1867 | 5.41k | log::debug!( |
1868 | | "asserting elaborated world import {}", |
1869 | 0 | self.name_world_key(key) |
1870 | | ); |
1871 | 5.41k | match item { |
1872 | 2.36k | WorldItem::Type { id, .. } => self.assert_world_imports_type_deps(world, key, *id), |
1873 | | |
1874 | | // All types referred to must be imported. |
1875 | 1.30k | WorldItem::Function(f) => self.assert_world_function_imports_types(world, key, f), |
1876 | | |
1877 | | // All direct dependencies of this interface must be imported. |
1878 | 1.74k | WorldItem::Interface { id, .. } => { |
1879 | 1.74k | for dep in self.interface_direct_deps(*id) { |
1880 | 530 | assert!( |
1881 | 530 | world.imports.contains_key(&WorldKey::Interface(dep)), |
1882 | | "world import of {} is missing transitive dep of {}", |
1883 | 0 | self.name_world_key(key), |
1884 | 0 | self.id_of(dep).unwrap(), |
1885 | | ); |
1886 | | } |
1887 | | } |
1888 | | } |
1889 | | } |
1890 | 9.42k | for (key, item) in world.exports.iter() { |
1891 | 9.42k | log::debug!( |
1892 | | "asserting elaborated world export {}", |
1893 | 0 | self.name_world_key(key) |
1894 | | ); |
1895 | 9.42k | match item { |
1896 | | // Types referred to by this function must be imported. |
1897 | 460 | WorldItem::Function(f) => self.assert_world_function_imports_types(world, key, f), |
1898 | | |
1899 | | // Dependencies of exported interfaces must also be exported, or |
1900 | | // if imported then that entire chain of imports must be |
1901 | | // imported and not exported. |
1902 | 8.96k | WorldItem::Interface { id, .. } => { |
1903 | 8.96k | for dep in self.interface_direct_deps(*id) { |
1904 | 1.19k | let dep_key = WorldKey::Interface(dep); |
1905 | 1.19k | if world.exports.contains_key(&dep_key) { |
1906 | 18 | continue; |
1907 | 1.17k | } |
1908 | 1.26k | self.foreach_interface_dep(dep, &mut |dep| { |
1909 | 1.26k | let dep_key = WorldKey::Interface(dep); |
1910 | 1.26k | assert!( |
1911 | 1.26k | world.imports.contains_key(&dep_key), |
1912 | | "world should import {} (required by {})", |
1913 | 0 | self.name_world_key(&dep_key), |
1914 | 0 | self.name_world_key(key), |
1915 | | ); |
1916 | 1.26k | assert!( |
1917 | 1.26k | !world.exports.contains_key(&dep_key), |
1918 | | "world should not export {} (required by {})", |
1919 | 0 | self.name_world_key(&dep_key), |
1920 | 0 | self.name_world_key(key), |
1921 | | ); |
1922 | 1.26k | }); |
1923 | | } |
1924 | | } |
1925 | | |
1926 | | // exported types not allowed at this time |
1927 | 0 | WorldItem::Type { .. } => unreachable!(), |
1928 | | } |
1929 | | } |
1930 | 6.46k | } |
1931 | | |
1932 | 3.70k | fn assert_world_imports_type_deps(&self, world: &World, key: &WorldKey, ty: TypeId) { |
1933 | | // If this is a `use` statement then the referred-to interface must be |
1934 | | // imported into this world. |
1935 | 3.70k | let ty = &self.types[ty]; |
1936 | 594 | if let TypeDefKind::Type(Type::Id(other)) = ty.kind { |
1937 | 392 | if let TypeOwner::Interface(id) = self.types[other].owner { |
1938 | 380 | let key = WorldKey::Interface(id); |
1939 | 380 | assert!(world.imports.contains_key(&key)); |
1940 | 380 | return; |
1941 | 12 | } |
1942 | 3.31k | } |
1943 | | |
1944 | | // ... otherwise any named type that this type refers to, one level |
1945 | | // deep, must be imported into this world under that name. |
1946 | | |
1947 | 3.32k | let mut visitor = MyVisit(self, Vec::new()); |
1948 | 3.32k | visitor.visit_type_def(self, ty); |
1949 | 13.4k | for ty in visitor.1 { |
1950 | 13.4k | let ty = &self.types[ty]; |
1951 | 13.4k | let Some(name) = ty.name.clone() else { |
1952 | 13.0k | continue; |
1953 | | }; |
1954 | 428 | let dep_key = WorldKey::Name(name); |
1955 | 428 | assert!( |
1956 | 428 | world.imports.contains_key(&dep_key), |
1957 | | "world import `{}` should also force an import of `{}`", |
1958 | 0 | self.name_world_key(key), |
1959 | 0 | self.name_world_key(&dep_key), |
1960 | | ); |
1961 | | } |
1962 | | |
1963 | | struct MyVisit<'a>(&'a Resolve, Vec<TypeId>); |
1964 | | |
1965 | | impl TypeIdVisitor for MyVisit<'_> { |
1966 | 13.4k | fn before_visit_type_id(&mut self, id: TypeId) -> bool { |
1967 | 13.4k | self.1.push(id); |
1968 | | // recurse into unnamed types to look at all named types |
1969 | 13.4k | self.0.types[id].name.is_none() |
1970 | 13.4k | } |
1971 | | } |
1972 | 3.70k | } |
1973 | | |
1974 | | /// This asserts that all types referred to by `func` are imported into |
1975 | | /// `world` under `WorldKey::Name`. Note that this is only applicable to |
1976 | | /// named type |
1977 | 1.76k | fn assert_world_function_imports_types(&self, world: &World, key: &WorldKey, func: &Function) { |
1978 | 6.17k | for ty in func |
1979 | 1.76k | .parameter_and_result_types() |
1980 | 1.76k | .chain(func.kind.resource().map(Type::Id)) |
1981 | | { |
1982 | 6.17k | let Type::Id(id) = ty else { |
1983 | 4.83k | continue; |
1984 | | }; |
1985 | 1.34k | self.assert_world_imports_type_deps(world, key, id); |
1986 | | } |
1987 | 1.76k | } |
1988 | | |
1989 | | /// Returns whether the `stability` annotation contained within `pkg_id` |
1990 | | /// should be included or not. |
1991 | | /// |
1992 | | /// The `span` provided here is an optional span pointing to the item that |
1993 | | /// is annotated with `stability`. |
1994 | | /// |
1995 | | /// Returns `Ok(true)` if the item is included, or `Ok(false)` if the item |
1996 | | /// is not. |
1997 | | /// |
1998 | | /// # Errors |
1999 | | /// |
2000 | | /// Returns an error if the `pkg_id` isn't annotated with sufficient version |
2001 | | /// information to have a `stability` annotation. For example if `pkg_id` |
2002 | | /// has no version listed then an error will be returned if `stability` |
2003 | | /// mentions a version. |
2004 | 267k | fn include_stability( |
2005 | 267k | &self, |
2006 | 267k | stability: &Stability, |
2007 | 267k | pkg_id: &PackageId, |
2008 | 267k | span: Span, |
2009 | 267k | ) -> ResolveResult<bool> { |
2010 | 267k | Ok(match stability { |
2011 | 261k | Stability::Unknown => true, |
2012 | | // NOTE: deprecations are intentionally omitted -- an existing |
2013 | | // `@since` takes precedence over `@deprecated` |
2014 | 4.59k | Stability::Stable { since, .. } => { |
2015 | 4.59k | let Some(p) = self.packages.get(*pkg_id) else { |
2016 | | // We can't check much without a package (possibly dealing |
2017 | | // with an item in an `UnresolvedPackage`), @since version & |
2018 | | // deprecations can't be checked because there's no package |
2019 | | // version to compare to. |
2020 | | // |
2021 | | // Feature requirements on stabilized features are ignored |
2022 | | // in resolved packages, so we do the same here. |
2023 | 0 | return Ok(true); |
2024 | | }; |
2025 | | |
2026 | | // Use of feature gating with version specifiers inside a |
2027 | | // package that is not versioned is not allowed |
2028 | 4.59k | let package_version = p.name.version.as_ref().ok_or_else(|| { |
2029 | 0 | ResolveError::new_semantic( |
2030 | 0 | span, |
2031 | 0 | format!( |
2032 | | "package [{}] contains a feature gate with a version \ |
2033 | | specifier, so it must have a version", |
2034 | | p.name |
2035 | | ), |
2036 | | ) |
2037 | 0 | })?; |
2038 | | |
2039 | | // If the version on the feature gate is: |
2040 | | // - released, then we can include it |
2041 | | // - unreleased, then we must check the feature (if present) |
2042 | 4.59k | if since > package_version { |
2043 | 0 | return Err(ResolveError::new_semantic( |
2044 | 0 | span, |
2045 | 0 | format!( |
2046 | 0 | "feature gate cannot reference unreleased version \ |
2047 | 0 | {since} of package [{}] (current version {package_version})", |
2048 | 0 | p.name |
2049 | 0 | ), |
2050 | 0 | )); |
2051 | 4.59k | } |
2052 | | |
2053 | 4.59k | true |
2054 | | } |
2055 | 1.62k | Stability::Unstable { feature, .. } => { |
2056 | 1.62k | self.features.contains(feature) || self.all_features |
2057 | | } |
2058 | | }) |
2059 | 267k | } |
2060 | | |
2061 | | /// Performs the "elaboration process" necessary for the `world_id` |
2062 | | /// specified to ensure that all of its transitive imports are listed. |
2063 | | /// |
2064 | | /// This function will take the unordered lists of the specified world's |
2065 | | /// imports and exports and "elaborate" them to ensure that they're |
2066 | | /// topographically sorted where all transitively required interfaces by |
2067 | | /// imports, or exports, are listed. This will additionally validate that |
2068 | | /// the exports are all valid and present, specifically with the restriction |
2069 | | /// noted on `elaborate_world_exports`. |
2070 | | /// |
2071 | | /// The world is mutated in-place in this `Resolve`. |
2072 | 45.9k | fn elaborate_world(&mut self, world_id: WorldId, span: Span) -> ResolveResult<()> { |
2073 | | // First process all imports. This is easier than exports since the only |
2074 | | // requirement here is that all interfaces need to be added with a |
2075 | | // topological order between them. |
2076 | 45.9k | let mut new_imports = IndexMap::default(); |
2077 | 45.9k | let world = &self.worlds[world_id]; |
2078 | | |
2079 | | // Sort the imports by "class" to ensure that this matches the order |
2080 | | // that items are printed and that items are in topological order. |
2081 | | // |
2082 | | // When printing worlds in WIT: |
2083 | | // |
2084 | | // * interfaces come first |
2085 | | // * types are next |
2086 | | // * type imports are first |
2087 | | // * type definitions are next |
2088 | | // * resource definitions have methods printed inline |
2089 | | // * freestanding functions are last |
2090 | | // |
2091 | | // This reflects the topological order between items where types |
2092 | | // can refer to imports and functions can refer to these types. Ordering |
2093 | | // within a single class (e.g. imports depending on each other, types |
2094 | | // referring to each other) is already preserved by other passes in this |
2095 | | // file and general AST resolution. That means that a stable sort here |
2096 | | // can be used to ensure that each class is in the right location |
2097 | | // relative to the others. |
2098 | | // |
2099 | | // Overall this ensures that round-trips of WIT through wasm should |
2100 | | // always produce the same result. |
2101 | 72.2k | let sort_key = |resolve: &Resolve, item: &WorldItem| match item { |
2102 | 27.8k | WorldItem::Interface { .. } => 0, |
2103 | 31.9k | WorldItem::Type { id, .. } => { |
2104 | 31.9k | let ty = &resolve.types[*id]; |
2105 | 5.93k | match ty.kind { |
2106 | 5.93k | TypeDefKind::Type(Type::Id(t)) if resolve.types[t].owner != ty.owner => 1, |
2107 | 26.1k | _ => 2, |
2108 | | } |
2109 | | } |
2110 | 12.4k | WorldItem::Function(f) => { |
2111 | 12.4k | if f.kind.resource().is_none() { |
2112 | 8.26k | 3 |
2113 | | } else { |
2114 | 4.19k | 4 |
2115 | | } |
2116 | | } |
2117 | 72.2k | }; |
2118 | | |
2119 | | // Sort world items when we start to elaborate the world to start with a |
2120 | | // topological view of items. |
2121 | 45.9k | let mut world_imports = world.imports.iter().collect::<Vec<_>>(); |
2122 | 45.9k | world_imports.sort_by_key(|(_name, import)| sort_key(self, import)); |
2123 | 45.9k | for (name, item) in world_imports { |
2124 | 34.9k | match item { |
2125 | | // Interfaces get their dependencies added first followed by the |
2126 | | // interface itself. |
2127 | | WorldItem::Interface { |
2128 | 13.0k | id, |
2129 | 13.0k | stability, |
2130 | 13.0k | docs, |
2131 | 13.0k | external_id, |
2132 | | .. |
2133 | 13.0k | } => { |
2134 | 13.0k | self.elaborate_world_import( |
2135 | 13.0k | &mut new_imports, |
2136 | 13.0k | name.clone(), |
2137 | 13.0k | *id, |
2138 | 13.0k | &stability, |
2139 | 13.0k | docs, |
2140 | 13.0k | external_id.as_deref(), |
2141 | 13.0k | ); |
2142 | 13.0k | } |
2143 | | |
2144 | | // Functions are added as-is since their dependence on types in |
2145 | | // the world should already be satisfied. |
2146 | | WorldItem::Function(_) => { |
2147 | 7.56k | let prev = new_imports.insert(name.clone(), item.clone()); |
2148 | 7.56k | assert!(prev.is_none()); |
2149 | | } |
2150 | | |
2151 | | // Types may depend on an interface, in which case a (possibly) |
2152 | | // recursive addition of that interface happens here. Afterwards |
2153 | | // the type itself can be added safely. |
2154 | 14.3k | WorldItem::Type { id, .. } => { |
2155 | 14.3k | if let Some(dep) = self.type_interface_dep(*id) { |
2156 | 2.16k | self.elaborate_world_import( |
2157 | 2.16k | &mut new_imports, |
2158 | 2.16k | WorldKey::Interface(dep), |
2159 | 2.16k | dep, |
2160 | 2.16k | &self.types[*id].stability, |
2161 | 2.16k | &Docs::default(), |
2162 | 2.16k | None, |
2163 | 2.16k | ); |
2164 | 12.1k | } |
2165 | 14.3k | let prev = new_imports.insert(name.clone(), item.clone()); |
2166 | 14.3k | assert!(prev.is_none()); |
2167 | | } |
2168 | | } |
2169 | | } |
2170 | | |
2171 | | // Exports are trickier than imports, notably to uphold the invariant |
2172 | | // required by `elaborate_world_exports`. To do this the exports are |
2173 | | // partitioned into interfaces/functions. All functions are added to |
2174 | | // the new exports list during this loop but interfaces are all deferred |
2175 | | // to be handled in the `elaborate_world_exports` function. |
2176 | 45.9k | let mut new_exports = IndexMap::default(); |
2177 | 45.9k | let mut export_interfaces = IndexMap::default(); |
2178 | 59.5k | for (name, item) in world.exports.iter() { |
2179 | 59.5k | match item { |
2180 | | WorldItem::Interface { .. } => { |
2181 | 56.7k | let prev = export_interfaces.insert(name.clone(), item.clone()); |
2182 | 56.7k | assert!(prev.is_none()); |
2183 | | } |
2184 | | WorldItem::Function(_) => { |
2185 | 2.85k | let prev = new_exports.insert(name.clone(), item.clone()); |
2186 | 2.85k | assert!(prev.is_none()); |
2187 | | } |
2188 | 0 | WorldItem::Type { .. } => unreachable!(), |
2189 | | } |
2190 | | } |
2191 | | |
2192 | 45.9k | self.elaborate_world_exports(&export_interfaces, &mut new_imports, &mut new_exports, span)?; |
2193 | | |
2194 | | // In addition to sorting at the start of elaboration also sort here at |
2195 | | // the end of elaboration to handle types being interspersed with |
2196 | | // interfaces as they're found. |
2197 | 45.9k | new_imports.sort_by_cached_key(|_name, import| sort_key(self, import)); |
2198 | | |
2199 | | // And with all that done the world is updated in-place with |
2200 | | // imports/exports. |
2201 | 45.9k | log::trace!("imports = {new_imports:?}"); |
2202 | 45.9k | log::trace!("exports = {new_exports:?}"); |
2203 | 45.9k | let world = &mut self.worlds[world_id]; |
2204 | 45.9k | world.imports = new_imports; |
2205 | 45.9k | world.exports = new_exports; |
2206 | | |
2207 | 45.9k | Ok(()) |
2208 | 45.9k | } |
2209 | | |
2210 | 18.9k | fn elaborate_world_import( |
2211 | 18.9k | &self, |
2212 | 18.9k | imports: &mut IndexMap<WorldKey, WorldItem>, |
2213 | 18.9k | key: WorldKey, |
2214 | 18.9k | id: InterfaceId, |
2215 | 18.9k | stability: &Stability, |
2216 | 18.9k | docs: &Docs, |
2217 | 18.9k | external_id: Option<&str>, |
2218 | 18.9k | ) { |
2219 | 18.9k | if imports.contains_key(&key) { |
2220 | 5.01k | return; |
2221 | 13.8k | } |
2222 | | // Synthesized dependency imports carry no statement-level docs of their |
2223 | | // own. |
2224 | 13.8k | for dep in self.interface_direct_deps(id) { |
2225 | 3.72k | self.elaborate_world_import( |
2226 | 3.72k | imports, |
2227 | 3.72k | WorldKey::Interface(dep), |
2228 | 3.72k | dep, |
2229 | 3.72k | stability, |
2230 | 3.72k | &Docs::default(), |
2231 | 3.72k | None, |
2232 | 3.72k | ); |
2233 | 3.72k | } |
2234 | 13.8k | let prev = imports.insert( |
2235 | 13.8k | key, |
2236 | | WorldItem::Interface { |
2237 | 13.8k | id, |
2238 | 13.8k | stability: stability.clone(), |
2239 | 13.8k | docs: docs.clone(), |
2240 | 13.8k | span: Default::default(), |
2241 | 13.8k | external_id: external_id.map(|s| s.to_string()), |
2242 | | }, |
2243 | | ); |
2244 | 13.8k | assert!(prev.is_none()); |
2245 | 18.9k | } |
2246 | | |
2247 | | /// This function adds all of the interfaces in `export_interfaces` to the |
2248 | | /// list of exports of the `world` specified. |
2249 | | /// |
2250 | | /// This method is more involved than adding imports because it is fallible. |
2251 | | /// Chiefly what can happen is that the dependencies of all exports must be |
2252 | | /// satisfied by other exports or imports, but not both. For example given a |
2253 | | /// situation such as: |
2254 | | /// |
2255 | | /// ```wit |
2256 | | /// interface a { |
2257 | | /// type t = u32 |
2258 | | /// } |
2259 | | /// interface b { |
2260 | | /// use a.{t} |
2261 | | /// } |
2262 | | /// interface c { |
2263 | | /// use a.{t} |
2264 | | /// use b.{t as t2} |
2265 | | /// } |
2266 | | /// ``` |
2267 | | /// |
2268 | | /// where `c` depends on `b` and `a` where `b` depends on `a`, then the |
2269 | | /// purpose of this method is to reject this world: |
2270 | | /// |
2271 | | /// ```wit |
2272 | | /// world foo { |
2273 | | /// export a |
2274 | | /// export c |
2275 | | /// } |
2276 | | /// ``` |
2277 | | /// |
2278 | | /// The reasoning here is unfortunately subtle and is additionally the |
2279 | | /// subject of WebAssembly/component-model#208. Effectively the `c` |
2280 | | /// interface depends on `b`, but it's not listed explicitly as an import, |
2281 | | /// so it's then implicitly added as an import. This then transitively |
2282 | | /// depends on `a` so it's also added as an import. At this point though `c` |
2283 | | /// also depends on `a`, and it's also exported, so naively it should depend |
2284 | | /// on the export and not implicitly add an import. This means though that |
2285 | | /// `c` has access to two copies of `a`, one imported and one exported. This |
2286 | | /// is not valid, especially in the face of resource types. |
2287 | | /// |
2288 | | /// Overall this method is tasked with rejecting the above world by walking |
2289 | | /// over all the exports and adding their dependencies. Each dependency is |
2290 | | /// recorded with whether it's required to be imported, and then if an |
2291 | | /// export is added for something that's required to be an error then the |
2292 | | /// operation fails. |
2293 | 45.9k | fn elaborate_world_exports( |
2294 | 45.9k | &self, |
2295 | 45.9k | export_interfaces: &IndexMap<WorldKey, WorldItem>, |
2296 | 45.9k | imports: &mut IndexMap<WorldKey, WorldItem>, |
2297 | 45.9k | exports: &mut IndexMap<WorldKey, WorldItem>, |
2298 | 45.9k | span: Span, |
2299 | 45.9k | ) -> ResolveResult<()> { |
2300 | 45.9k | let mut required_imports = HashSet::new(); |
2301 | 56.6k | for (key, item) in export_interfaces.iter() { |
2302 | 56.6k | let name = self.name_world_key(&key); |
2303 | 56.6k | let ok = add_world_export( |
2304 | 56.6k | self, |
2305 | 56.6k | imports, |
2306 | 56.6k | exports, |
2307 | 56.6k | &export_interfaces, |
2308 | 56.6k | &mut required_imports, |
2309 | 56.6k | key.clone(), |
2310 | 56.6k | item.clone(), |
2311 | | true, |
2312 | | ); |
2313 | 56.6k | if !ok { |
2314 | | // FIXME: this is not a great error message and basically no |
2315 | | // one will know what to do when it gets printed. Improving |
2316 | | // this error message, however, is a chunk of work that may |
2317 | | // not be best spent doing this at this time, so I'm writing |
2318 | | // this comment instead. |
2319 | | // |
2320 | | // More-or-less what should happen here is that a "path" |
2321 | | // from this interface to the conflicting interface should |
2322 | | // be printed. It should be explained why an import is being |
2323 | | // injected, why that's conflicting with an export, and |
2324 | | // ideally with a suggestion of "add this interface to the |
2325 | | // export list to fix this error". |
2326 | | // |
2327 | | // That's a lot of info that's not easy to get at without |
2328 | | // more refactoring, so it's left to a future date in the |
2329 | | // hopes that most folks won't actually run into this for |
2330 | | // the time being. |
2331 | 18 | return Err(ResolveError::from( |
2332 | 18 | ResolveErrorKind::InvalidTransitiveDependency { name, span }, |
2333 | 18 | )); |
2334 | 56.6k | } |
2335 | | } |
2336 | 45.9k | return Ok(()); |
2337 | | |
2338 | 64.1k | fn add_world_export( |
2339 | 64.1k | resolve: &Resolve, |
2340 | 64.1k | imports: &mut IndexMap<WorldKey, WorldItem>, |
2341 | 64.1k | exports: &mut IndexMap<WorldKey, WorldItem>, |
2342 | 64.1k | export_interfaces: &IndexMap<WorldKey, WorldItem>, |
2343 | 64.1k | required_imports: &mut HashSet<InterfaceId>, |
2344 | 64.1k | key: WorldKey, |
2345 | 64.1k | item: WorldItem, |
2346 | 64.1k | add_export: bool, |
2347 | 64.1k | ) -> bool { |
2348 | 64.1k | if exports.contains_key(&key) { |
2349 | 91 | if add_export { |
2350 | 91 | return true; |
2351 | | } else { |
2352 | 0 | return false; |
2353 | | } |
2354 | 64.0k | } |
2355 | 64.0k | let (id, stability, external_id) = match &item { |
2356 | | WorldItem::Interface { |
2357 | 64.0k | id, |
2358 | 64.0k | stability, |
2359 | 64.0k | external_id, |
2360 | | .. |
2361 | 64.0k | } => (*id, stability, external_id), |
2362 | 0 | _ => unreachable!(), |
2363 | | }; |
2364 | | // If this is an import and it's already in the `imports` set then |
2365 | | // we can skip it as we've already visited this interface. |
2366 | 64.0k | if !add_export && required_imports.contains(&id) { |
2367 | 6.06k | return true; |
2368 | 58.0k | } |
2369 | 58.0k | let ok = resolve.interface_direct_deps(id).all(|dep| { |
2370 | 7.48k | let item = WorldItem::Interface { |
2371 | 7.48k | id: dep, |
2372 | 7.48k | stability: stability.clone(), |
2373 | 7.48k | docs: Default::default(), |
2374 | 7.48k | span: Default::default(), |
2375 | 7.48k | external_id: external_id.clone(), |
2376 | 7.48k | }; |
2377 | 7.48k | let key = WorldKey::Interface(dep); |
2378 | 7.48k | let add_export = add_export && export_interfaces.contains_key(&key); |
2379 | 7.48k | add_world_export( |
2380 | 7.48k | resolve, |
2381 | 7.48k | imports, |
2382 | 7.48k | exports, |
2383 | 7.48k | export_interfaces, |
2384 | 7.48k | required_imports, |
2385 | 7.48k | key, |
2386 | 7.48k | item, |
2387 | 7.48k | add_export, |
2388 | | ) |
2389 | 7.48k | }); |
2390 | 58.0k | if !ok { |
2391 | 0 | return false; |
2392 | 58.0k | } |
2393 | 58.0k | if add_export { |
2394 | 56.6k | if required_imports.contains(&id) { |
2395 | 18 | return false; |
2396 | 56.6k | } |
2397 | 56.6k | let prev = exports.insert(key.clone(), item); |
2398 | 56.6k | assert!(prev.is_none()); |
2399 | | } else { |
2400 | 1.33k | required_imports.insert(id); |
2401 | 1.33k | if !imports.contains_key(&key) { |
2402 | 318 | imports.insert(key.clone(), item); |
2403 | 1.01k | } |
2404 | | } |
2405 | 57.9k | true |
2406 | 64.1k | } |
2407 | 45.9k | } |
2408 | | |
2409 | | /// Remove duplicate imports from a world if they import from the same |
2410 | | /// interface with semver-compatible versions. |
2411 | | /// |
2412 | | /// This will merge duplicate interfaces present at multiple versions in |
2413 | | /// both a world by selecting the larger version of the two interfaces. This |
2414 | | /// requires that the interfaces are indeed semver-compatible and it means |
2415 | | /// that some imports might be removed and replaced. Note that this is only |
2416 | | /// done within a single semver track, for example the world imports 0.2.0 |
2417 | | /// and 0.2.1 then the result afterwards will be that it imports |
2418 | | /// 0.2.1. If, however, 0.3.0 where imported then the final result would |
2419 | | /// import both 0.2.0 and 0.3.0. |
2420 | 3.73k | pub fn merge_world_imports_based_on_semver(&mut self, world_id: WorldId) -> anyhow::Result<()> { |
2421 | 3.73k | let world = &self.worlds[world_id]; |
2422 | | |
2423 | | // The first pass here is to build a map of "semver tracks" where they |
2424 | | // key is per-interface and the value is the maximal version found in |
2425 | | // that semver-compatible-track plus the interface which is the maximal |
2426 | | // version. |
2427 | | // |
2428 | | // At the same time a `to_remove` set is maintained to remember what |
2429 | | // interfaces are being removed from `from` and `into`. All of |
2430 | | // `to_remove` are placed with a known other version. |
2431 | 3.73k | let mut semver_tracks = HashMap::new(); |
2432 | 3.73k | let mut to_remove = HashSet::new(); |
2433 | 3.73k | for (key, _) in world.imports.iter() { |
2434 | 3.14k | let iface_id = match key { |
2435 | 447 | WorldKey::Interface(id) => *id, |
2436 | 2.70k | WorldKey::Name(_) => continue, |
2437 | | }; |
2438 | 447 | let (track, version) = match self.semver_track(iface_id) { |
2439 | 413 | Some(track) => track, |
2440 | 34 | None => continue, |
2441 | | }; |
2442 | 413 | log::debug!( |
2443 | | "{} is on track {}/{}", |
2444 | 0 | self.id_of(iface_id).unwrap(), |
2445 | | track.0, |
2446 | | track.1, |
2447 | | ); |
2448 | 413 | match semver_tracks.entry(track) { |
2449 | 413 | Entry::Vacant(e) => { |
2450 | 413 | e.insert((version, iface_id)); |
2451 | 413 | } |
2452 | 0 | Entry::Occupied(mut e) => match version.cmp(&e.get().0) { |
2453 | 0 | Ordering::Greater => { |
2454 | 0 | to_remove.insert(e.get().1); |
2455 | 0 | e.insert((version, iface_id)); |
2456 | 0 | } |
2457 | 0 | Ordering::Equal => {} |
2458 | 0 | Ordering::Less => { |
2459 | 0 | to_remove.insert(iface_id); |
2460 | 0 | } |
2461 | | }, |
2462 | | } |
2463 | | } |
2464 | | |
2465 | | // Build a map of "this interface is replaced with this interface" using |
2466 | | // the results of the loop above. |
2467 | 3.73k | let mut replacements = HashMap::new(); |
2468 | 3.73k | for id in to_remove { |
2469 | 0 | let (track, _) = self.semver_track(id).unwrap(); |
2470 | 0 | let (_, latest) = semver_tracks[&track]; |
2471 | 0 | let prev = replacements.insert(id, latest); |
2472 | 0 | assert!(prev.is_none()); |
2473 | | } |
2474 | | // Explicit drop needed for hashbrown compatibility - hashbrown's HashMap |
2475 | | // destructor may access stored references, extending the borrow. |
2476 | 3.73k | drop(semver_tracks); |
2477 | | |
2478 | | // Validate that `merge_world_item` succeeds for merging all removed |
2479 | | // interfaces with their replacement. This is a double-check that the |
2480 | | // semver version is actually correct and all items present in the old |
2481 | | // interface are in the new. |
2482 | 3.73k | for (to_replace, replace_with) in replacements.iter() { |
2483 | 0 | self.merge_world_item( |
2484 | 0 | &WorldItem::Interface { |
2485 | 0 | id: *to_replace, |
2486 | 0 | stability: Default::default(), |
2487 | 0 | docs: Default::default(), |
2488 | 0 | span: Default::default(), |
2489 | 0 | external_id: Default::default(), |
2490 | 0 | }, |
2491 | 0 | &WorldItem::Interface { |
2492 | 0 | id: *replace_with, |
2493 | 0 | stability: Default::default(), |
2494 | 0 | docs: Default::default(), |
2495 | 0 | span: Default::default(), |
2496 | 0 | external_id: Default::default(), |
2497 | 0 | }, |
2498 | | ) |
2499 | 0 | .with_context(|| { |
2500 | 0 | let old_name = self.id_of(*to_replace).unwrap(); |
2501 | 0 | let new_name = self.id_of(*replace_with).unwrap(); |
2502 | 0 | format!( |
2503 | | "failed to upgrade `{old_name}` to `{new_name}`, was \ |
2504 | | this semver-compatible update not semver compatible?" |
2505 | | ) |
2506 | 0 | })?; |
2507 | | } |
2508 | | |
2509 | 3.73k | for (to_replace, replace_with) in replacements.iter() { |
2510 | 0 | log::debug!( |
2511 | | "REPLACE {} => {}", |
2512 | 0 | self.id_of(*to_replace).unwrap(), |
2513 | 0 | self.id_of(*replace_with).unwrap(), |
2514 | | ); |
2515 | | } |
2516 | | |
2517 | | // Finally perform the actual transformation of the imports/exports. |
2518 | | // Here all imports are removed if they're replaced and otherwise all |
2519 | | // imports have their dependencies updated, possibly transitively, to |
2520 | | // point to the new interfaces in `replacements`. |
2521 | | // |
2522 | | // Afterwards exports are additionally updated, but only their |
2523 | | // dependencies on imports which were remapped. Exports themselves are |
2524 | | // not deduplicated and/or removed. |
2525 | 3.73k | for (key, item) in mem::take(&mut self.worlds[world_id].imports) { |
2526 | 3.14k | if let WorldItem::Interface { id, .. } = item { |
2527 | 990 | if replacements.contains_key(&id) { |
2528 | 0 | if let WorldKey::Interface(_) = key { |
2529 | 0 | continue; |
2530 | 0 | } |
2531 | | // Keep labeled imports with `implements` version unchanged |
2532 | 990 | } |
2533 | 2.15k | } |
2534 | | |
2535 | 3.14k | self.update_interface_deps_of_world_item(&item, &replacements); |
2536 | | |
2537 | 3.14k | let prev = self.worlds[world_id].imports.insert(key, item); |
2538 | 3.14k | assert!(prev.is_none()); |
2539 | | } |
2540 | 5.58k | for (key, item) in mem::take(&mut self.worlds[world_id].exports) { |
2541 | 5.58k | self.update_interface_deps_of_world_item(&item, &replacements); |
2542 | 5.58k | let prev = self.worlds[world_id].exports.insert(key, item); |
2543 | 5.58k | assert!(prev.is_none()); |
2544 | | } |
2545 | | |
2546 | | // Run through `elaborate_world` to reorder imports as appropriate and |
2547 | | // fill anything back in if it's actually required by exports. For now |
2548 | | // this doesn't tamper with exports at all. Also note that this is |
2549 | | // applied to all worlds in this `Resolve` because interfaces were |
2550 | | // modified directly. |
2551 | 3.73k | let ids = self.worlds.iter().map(|(id, _)| id).collect::<Vec<_>>(); |
2552 | 10.1k | for world_id in ids { |
2553 | 10.1k | let world_span = self.worlds[world_id].span; |
2554 | 10.1k | self.elaborate_world(world_id, world_span)?; |
2555 | | } |
2556 | | |
2557 | | #[cfg(debug_assertions)] |
2558 | | self.assert_valid(); |
2559 | | |
2560 | 3.73k | Ok(()) |
2561 | 3.73k | } |
2562 | | |
2563 | 8.73k | fn update_interface_deps_of_world_item( |
2564 | 8.73k | &mut self, |
2565 | 8.73k | item: &WorldItem, |
2566 | 8.73k | replacements: &HashMap<InterfaceId, InterfaceId>, |
2567 | 8.73k | ) { |
2568 | 8.73k | match *item { |
2569 | 1.42k | WorldItem::Type { id, .. } => self.update_interface_dep_of_type(id, &replacements), |
2570 | 6.31k | WorldItem::Interface { id, .. } => { |
2571 | 6.31k | let types = self.interfaces[id] |
2572 | 6.31k | .types |
2573 | 6.31k | .values() |
2574 | 6.31k | .copied() |
2575 | 6.31k | .collect::<Vec<_>>(); |
2576 | 23.4k | for ty in types { |
2577 | 23.4k | self.update_interface_dep_of_type(ty, &replacements); |
2578 | 23.4k | } |
2579 | | } |
2580 | 994 | WorldItem::Function(_) => {} |
2581 | | } |
2582 | 8.73k | } |
2583 | | |
2584 | | /// Returns the "semver track" of an interface plus the interface's version. |
2585 | | /// |
2586 | | /// This function returns `None` if the interface `id` has a package without |
2587 | | /// a version. If the version is present, however, the first element of the |
2588 | | /// tuple returned is a "semver track" for the specific interface. The |
2589 | | /// version listed in `PackageName` will be modified so all |
2590 | | /// semver-compatible versions are listed the same way. |
2591 | | /// |
2592 | | /// The second element in the returned tuple is this interface's package's |
2593 | | /// version. |
2594 | 447 | fn semver_track(&self, id: InterfaceId) -> Option<((PackageName, String), &Version)> { |
2595 | 447 | let iface = &self.interfaces[id]; |
2596 | 447 | let pkg = &self.packages[iface.package?]; |
2597 | 447 | let version = pkg.name.version.as_ref()?; |
2598 | 413 | let mut name = pkg.name.clone(); |
2599 | 413 | name.version = Some(PackageName::version_compat_track(version).0); |
2600 | 413 | Some(((name, iface.name.clone()?), version)) |
2601 | 447 | } |
2602 | | |
2603 | | /// If `ty` is a definition where it's a `use` from another interface, then |
2604 | | /// change what interface it's using from according to the pairs in the |
2605 | | /// `replacements` map. |
2606 | 24.8k | fn update_interface_dep_of_type( |
2607 | 24.8k | &mut self, |
2608 | 24.8k | ty: TypeId, |
2609 | 24.8k | replacements: &HashMap<InterfaceId, InterfaceId>, |
2610 | 24.8k | ) { |
2611 | 24.8k | let to_replace = match self.type_interface_dep(ty) { |
2612 | 1.25k | Some(id) => id, |
2613 | 23.5k | None => return, |
2614 | | }; |
2615 | 1.25k | let replace_with = match replacements.get(&to_replace) { |
2616 | 0 | Some(id) => id, |
2617 | 1.25k | None => return, |
2618 | | }; |
2619 | 0 | let dep = match self.types[ty].kind { |
2620 | 0 | TypeDefKind::Type(Type::Id(id)) => id, |
2621 | 0 | _ => return, |
2622 | | }; |
2623 | 0 | let name = self.types[dep].name.as_ref().unwrap(); |
2624 | | // Note the infallible name indexing happening here. This should be |
2625 | | // previously validated with `merge_world_item` to succeed. |
2626 | 0 | let replacement_id = self.interfaces[*replace_with].types[name]; |
2627 | 0 | self.types[ty].kind = TypeDefKind::Type(Type::Id(replacement_id)); |
2628 | 24.8k | } |
2629 | | |
2630 | | /// Returns the core wasm module/field names for the specified `import`. |
2631 | | /// |
2632 | | /// This function will return the core wasm module/field that can be used to |
2633 | | /// use `import` with the name `mangling` scheme specified as well. This can |
2634 | | /// be useful for bindings generators, for example, and these names are |
2635 | | /// recognized by `wit-component` and `wasm-tools component new`. |
2636 | 5.02k | pub fn wasm_import_name( |
2637 | 5.02k | &self, |
2638 | 5.02k | mangling: ManglingAndAbi, |
2639 | 5.02k | import: WasmImport<'_>, |
2640 | 5.02k | ) -> (String, String) { |
2641 | 5.02k | match mangling { |
2642 | 158 | ManglingAndAbi::Standard32 => match import { |
2643 | 91 | WasmImport::Func { interface, func } => { |
2644 | 91 | let module = match interface { |
2645 | 34 | Some(key) => format!("cm32p2|{}", self.name_canonicalized_world_key(key)), |
2646 | 57 | None => format!("cm32p2"), |
2647 | | }; |
2648 | 91 | (module, func.name.clone()) |
2649 | | } |
2650 | | WasmImport::ResourceIntrinsic { |
2651 | 67 | interface, |
2652 | 67 | resource, |
2653 | 67 | intrinsic, |
2654 | | } => { |
2655 | 67 | let name = self.types[resource].name.as_ref().unwrap(); |
2656 | 67 | let (prefix, name) = match intrinsic { |
2657 | 10 | ResourceIntrinsic::ImportedDrop => ("", format!("{name}_drop")), |
2658 | 19 | ResourceIntrinsic::ExportedDrop => ("_ex_", format!("{name}_drop")), |
2659 | 19 | ResourceIntrinsic::ExportedNew => ("_ex_", format!("{name}_new")), |
2660 | 19 | ResourceIntrinsic::ExportedRep => ("_ex_", format!("{name}_rep")), |
2661 | | }; |
2662 | 67 | let module = match interface { |
2663 | 63 | Some(key) => { |
2664 | 63 | format!("cm32p2|{prefix}{}", self.name_canonicalized_world_key(key)) |
2665 | | } |
2666 | | None => { |
2667 | 4 | assert_eq!(prefix, ""); |
2668 | 4 | format!("cm32p2") |
2669 | | } |
2670 | | }; |
2671 | 67 | (module, name) |
2672 | | } |
2673 | | WasmImport::FutureIntrinsic { .. } | WasmImport::StreamIntrinsic { .. } => { |
2674 | 0 | panic!( |
2675 | | "at the time of writing, standard32 name mangling only supports the \ |
2676 | | synchronous ABI and does not define future/stream intrinsic imports; \ |
2677 | | use legacy mangling for these imports" |
2678 | | ) |
2679 | | } |
2680 | | }, |
2681 | 4.86k | ManglingAndAbi::Legacy(abi) => match import { |
2682 | 1.33k | WasmImport::Func { interface, func } => { |
2683 | 1.33k | let module = match interface { |
2684 | 735 | Some(key) => self.name_world_key(key), |
2685 | 596 | None => format!("$root"), |
2686 | | }; |
2687 | 1.33k | (module, format!("{}{}", abi.import_prefix(), func.name)) |
2688 | | } |
2689 | | WasmImport::ResourceIntrinsic { |
2690 | 772 | interface, |
2691 | 772 | resource, |
2692 | 772 | intrinsic, |
2693 | | } => { |
2694 | 772 | let name = self.types[resource].name.as_ref().unwrap(); |
2695 | 772 | let (prefix, name) = match intrinsic { |
2696 | 154 | ResourceIntrinsic::ImportedDrop => ("", format!("[resource-drop]{name}")), |
2697 | | ResourceIntrinsic::ExportedDrop => { |
2698 | 206 | ("[export]", format!("[resource-drop]{name}")) |
2699 | | } |
2700 | | ResourceIntrinsic::ExportedNew => { |
2701 | 206 | ("[export]", format!("[resource-new]{name}")) |
2702 | | } |
2703 | | ResourceIntrinsic::ExportedRep => { |
2704 | 206 | ("[export]", format!("[resource-rep]{name}")) |
2705 | | } |
2706 | | }; |
2707 | 772 | let module = match interface { |
2708 | 707 | Some(key) => format!("{prefix}{}", self.name_world_key(key)), |
2709 | | None => { |
2710 | 65 | assert_eq!(prefix, ""); |
2711 | 65 | format!("$root") |
2712 | | } |
2713 | | }; |
2714 | 772 | (module, format!("{}{name}", abi.import_prefix())) |
2715 | | } |
2716 | | WasmImport::FutureIntrinsic { |
2717 | 1.74k | interface, |
2718 | 1.74k | func, |
2719 | 1.74k | ty, |
2720 | 1.74k | intrinsic, |
2721 | 1.74k | exported, |
2722 | 1.74k | async_, |
2723 | | } => { |
2724 | 1.74k | let module_prefix = if exported { "[export]" } else { "" }; |
2725 | 1.74k | let module = match interface { |
2726 | 1.44k | Some(key) => format!("{module_prefix}{}", self.name_world_key(key)), |
2727 | 300 | None => format!("{module_prefix}$root"), |
2728 | | }; |
2729 | 1.74k | let type_index = match ty { |
2730 | 1.74k | Some(ty) => func |
2731 | 1.74k | .find_futures_and_streams(self) |
2732 | 1.74k | .into_iter() |
2733 | 9.79k | .position(|candidate| candidate == ty) |
2734 | 1.74k | .unwrap_or_else(|| { |
2735 | 0 | panic!( |
2736 | | "future type {ty:?} not found in `find_futures_and_streams` for `{}`", |
2737 | | func.name |
2738 | | ) |
2739 | | }) |
2740 | 1.74k | .to_string(), |
2741 | 0 | None => "unit".to_string(), |
2742 | | }; |
2743 | 1.74k | let (async_prefix, name) = match intrinsic { |
2744 | | FutureIntrinsic::New => { |
2745 | 145 | assert!(!async_, "future.new cannot be async-lowered"); |
2746 | 145 | ("", "new") |
2747 | | } |
2748 | | FutureIntrinsic::Read => { |
2749 | 290 | (if async_ { "[async-lower]" } else { "" }, "read") |
2750 | | } |
2751 | | FutureIntrinsic::Write => { |
2752 | 290 | (if async_ { "[async-lower]" } else { "" }, "write") |
2753 | | } |
2754 | | FutureIntrinsic::Forward => { |
2755 | 145 | assert!(!async_, "future.forward cannot be async-lowered"); |
2756 | 145 | ("", "forward") |
2757 | | } |
2758 | | FutureIntrinsic::CancelRead => { |
2759 | 290 | (if async_ { "[async-lower]" } else { "" }, "cancel-read") |
2760 | | } |
2761 | | FutureIntrinsic::CancelWrite => { |
2762 | 290 | (if async_ { "[async-lower]" } else { "" }, "cancel-write") |
2763 | | } |
2764 | | FutureIntrinsic::DropReadable => { |
2765 | 145 | assert!(!async_, "future.drop-readable cannot be async-lowered"); |
2766 | 145 | ("", "drop-readable") |
2767 | | } |
2768 | | FutureIntrinsic::DropWritable => { |
2769 | 145 | assert!(!async_, "future.drop-writable cannot be async-lowered"); |
2770 | 145 | ("", "drop-writable") |
2771 | | } |
2772 | | }; |
2773 | 1.74k | ( |
2774 | 1.74k | module, |
2775 | 1.74k | format!("{async_prefix}[future-{name}-{type_index}]{}", func.name), |
2776 | 1.74k | ) |
2777 | | } |
2778 | | WasmImport::StreamIntrinsic { |
2779 | 1.02k | interface, |
2780 | 1.02k | func, |
2781 | 1.02k | ty, |
2782 | 1.02k | intrinsic, |
2783 | 1.02k | exported, |
2784 | 1.02k | async_, |
2785 | | } => { |
2786 | 1.02k | let module_prefix = if exported { "[export]" } else { "" }; |
2787 | 1.02k | let module = match interface { |
2788 | 408 | Some(key) => format!("{module_prefix}{}", self.name_world_key(key)), |
2789 | 612 | None => format!("{module_prefix}$root"), |
2790 | | }; |
2791 | 1.02k | let type_index = match ty { |
2792 | 1.02k | Some(ty) => func |
2793 | 1.02k | .find_futures_and_streams(self) |
2794 | 1.02k | .into_iter() |
2795 | 5.07k | .position(|candidate| candidate == ty) |
2796 | 1.02k | .unwrap_or_else(|| { |
2797 | 0 | panic!( |
2798 | | "stream type {ty:?} not found in `find_futures_and_streams` for `{}`", |
2799 | | func.name |
2800 | | ) |
2801 | | }) |
2802 | 1.02k | .to_string(), |
2803 | 0 | None => "unit".to_string(), |
2804 | | }; |
2805 | 1.02k | let (async_prefix, name) = match intrinsic { |
2806 | | StreamIntrinsic::New => { |
2807 | 85 | assert!(!async_, "stream.new cannot be async-lowered"); |
2808 | 85 | ("", "new") |
2809 | | } |
2810 | | StreamIntrinsic::Read => { |
2811 | 170 | (if async_ { "[async-lower]" } else { "" }, "read") |
2812 | | } |
2813 | | StreamIntrinsic::Write => { |
2814 | 170 | (if async_ { "[async-lower]" } else { "" }, "write") |
2815 | | } |
2816 | | StreamIntrinsic::Forward => { |
2817 | 85 | assert!(!async_, "stream.forward cannot be async-lowered"); |
2818 | 85 | ("", "forward") |
2819 | | } |
2820 | | StreamIntrinsic::CancelRead => { |
2821 | 170 | (if async_ { "[async-lower]" } else { "" }, "cancel-read") |
2822 | | } |
2823 | | StreamIntrinsic::CancelWrite => { |
2824 | 170 | (if async_ { "[async-lower]" } else { "" }, "cancel-write") |
2825 | | } |
2826 | | StreamIntrinsic::DropReadable => { |
2827 | 85 | assert!(!async_, "stream.drop-readable cannot be async-lowered"); |
2828 | 85 | ("", "drop-readable") |
2829 | | } |
2830 | | StreamIntrinsic::DropWritable => { |
2831 | 85 | assert!(!async_, "stream.drop-writable cannot be async-lowered"); |
2832 | 85 | ("", "drop-writable") |
2833 | | } |
2834 | | }; |
2835 | 1.02k | ( |
2836 | 1.02k | module, |
2837 | 1.02k | format!("{async_prefix}[stream-{name}-{type_index}]{}", func.name), |
2838 | 1.02k | ) |
2839 | | } |
2840 | | }, |
2841 | | } |
2842 | 5.02k | } |
2843 | | |
2844 | | /// Returns the core wasm export name for the specified `export`. |
2845 | | /// |
2846 | | /// This is the same as [`Resolve::wasm_import_name`], except for exports. |
2847 | 14.6k | pub fn wasm_export_name(&self, mangling: ManglingAndAbi, export: WasmExport<'_>) -> String { |
2848 | 14.6k | match mangling { |
2849 | 1.05k | ManglingAndAbi::Standard32 => match export { |
2850 | | WasmExport::Func { |
2851 | 528 | interface, |
2852 | 528 | func, |
2853 | 528 | kind, |
2854 | | } => { |
2855 | 528 | let mut name = String::from("cm32p2|"); |
2856 | 528 | if let Some(interface) = interface { |
2857 | 480 | let s = self.name_canonicalized_world_key(interface); |
2858 | 480 | name.push_str(&s); |
2859 | 480 | } |
2860 | 528 | name.push_str("|"); |
2861 | 528 | name.push_str(&func.name); |
2862 | 528 | match kind { |
2863 | 264 | WasmExportKind::Normal => {} |
2864 | 264 | WasmExportKind::PostReturn => name.push_str("_post"), |
2865 | 0 | WasmExportKind::Callback => todo!( |
2866 | | "not yet supported: \ |
2867 | | async callback functions using standard name mangling" |
2868 | | ), |
2869 | | } |
2870 | 528 | name |
2871 | | } |
2872 | | WasmExport::ResourceDtor { |
2873 | 19 | interface, |
2874 | 19 | resource, |
2875 | | } => { |
2876 | 19 | let name = self.types[resource].name.as_ref().unwrap(); |
2877 | 19 | let interface = self.name_canonicalized_world_key(interface); |
2878 | 19 | format!("cm32p2|{interface}|{name}_dtor") |
2879 | | } |
2880 | 168 | WasmExport::Memory => "cm32p2_memory".to_string(), |
2881 | 168 | WasmExport::Initialize => "cm32p2_initialize".to_string(), |
2882 | 168 | WasmExport::Realloc => "cm32p2_realloc".to_string(), |
2883 | | }, |
2884 | 13.6k | ManglingAndAbi::Legacy(abi) => match export { |
2885 | | WasmExport::Func { |
2886 | 4.23k | interface, |
2887 | 4.23k | func, |
2888 | 4.23k | kind, |
2889 | | } => { |
2890 | 4.23k | let mut name = abi.export_prefix().to_string(); |
2891 | 4.23k | match kind { |
2892 | 2.13k | WasmExportKind::Normal => {} |
2893 | 1.90k | WasmExportKind::PostReturn => name.push_str("cabi_post_"), |
2894 | | WasmExportKind::Callback => { |
2895 | 188 | assert!(matches!(abi, LiftLowerAbi::AsyncCallback)); |
2896 | 188 | name = format!("[callback]{name}") |
2897 | | } |
2898 | | } |
2899 | 4.23k | if let Some(interface) = interface { |
2900 | 3.83k | let s = self.name_world_key(interface); |
2901 | 3.83k | name.push_str(&s); |
2902 | 3.83k | name.push_str("#"); |
2903 | 3.83k | } |
2904 | 4.23k | name.push_str(&func.name); |
2905 | 4.23k | name |
2906 | | } |
2907 | | WasmExport::ResourceDtor { |
2908 | 206 | interface, |
2909 | 206 | resource, |
2910 | | } => { |
2911 | 206 | let name = self.types[resource].name.as_ref().unwrap(); |
2912 | 206 | let interface = self.name_world_key(interface); |
2913 | 206 | format!("{}{interface}#[dtor]{name}", abi.export_prefix()) |
2914 | | } |
2915 | 3.06k | WasmExport::Memory => "memory".to_string(), |
2916 | 3.06k | WasmExport::Initialize => "_initialize".to_string(), |
2917 | 3.06k | WasmExport::Realloc => "cabi_realloc".to_string(), |
2918 | | }, |
2919 | | } |
2920 | 14.6k | } |
2921 | | |
2922 | | /// This method will rewrite the `world` provided to ensure that, where |
2923 | | /// necessary, all types in interfaces referred to by the `world` have |
2924 | | /// nominal type ids for bindings generation. |
2925 | | /// |
2926 | | /// The need for this method primarily arises from bindings generators |
2927 | | /// generating types in a programming language. Bindings generators try to |
2928 | | /// generate a type-per-WIT-type but this becomes problematic in situations |
2929 | | /// such as when an `interface` is both imported and exported. For example: |
2930 | | /// |
2931 | | /// ```wit |
2932 | | /// interface x { |
2933 | | /// resource r; |
2934 | | /// } |
2935 | | /// |
2936 | | /// world foo { |
2937 | | /// import x; |
2938 | | /// export x; |
2939 | | /// } |
2940 | | /// ``` |
2941 | | /// |
2942 | | /// Here the `r` resource, before this method, exists once within this |
2943 | | /// [`Resolve`]. This is a problem for bindings generators because guest |
2944 | | /// languages typically want to represent this world with two types: one |
2945 | | /// for the import and one for the export. This matches component model |
2946 | | /// semantics where `r` is a different type between the import and the |
2947 | | /// export. |
2948 | | /// |
2949 | | /// The purpose of this method is to ensure that languages with nominal |
2950 | | /// types, where type identity is unique based on definition not structure, |
2951 | | /// will have an easier time generating bindings. This method will |
2952 | | /// duplicate the interface `x`, for example, and everything it contains. |
2953 | | /// This means that the `world foo` above will have a different |
2954 | | /// `InterfaceId` for the import and the export of `x`, despite them using |
2955 | | /// the same interface in WIT. This is intended to make bindings generators' |
2956 | | /// jobs much easier because now Id-uniqueness matches the semantic meaning |
2957 | | /// of the world as well. |
2958 | | /// |
2959 | | /// This function will rewrite imported/exported interfaces, as appropriate, |
2960 | | /// to all have unique ids if they would otherwise overlap with the imports. |
2961 | 6.46k | pub fn generate_nominal_type_ids(&mut self, world_id: WorldId) { |
2962 | 6.46k | let mut seen = HashSet::new(); |
2963 | 6.46k | let mut interface_keys_rewritten = HashSet::new(); |
2964 | 6.46k | let mut maps = CloneMaps::default(); |
2965 | | |
2966 | | // Pull out the imports/exports from `self` to have simultaneous |
2967 | | // borrows. |
2968 | 6.46k | let world = &mut self.worlds[world_id]; |
2969 | 6.46k | let mut imports = mem::take(&mut world.imports); |
2970 | 6.46k | let mut exports = mem::take(&mut world.exports); |
2971 | | |
2972 | | // Notably visit `imports` first as they always get priority in the |
2973 | | // order of having things imported from them. After `imports` are |
2974 | | // visited then process all `exports`. |
2975 | 6.46k | log::trace!("nominalizing world imports"); |
2976 | 6.46k | self.nominalize_world_items( |
2977 | 6.46k | &mut maps, |
2978 | 6.46k | world_id, |
2979 | 6.46k | &mut imports, |
2980 | 6.46k | &mut seen, |
2981 | 6.46k | &mut interface_keys_rewritten, |
2982 | | ); |
2983 | 6.46k | log::trace!("nominalizing world exports"); |
2984 | 6.46k | self.nominalize_world_items( |
2985 | 6.46k | &mut maps, |
2986 | 6.46k | world_id, |
2987 | 6.46k | &mut exports, |
2988 | 6.46k | &mut seen, |
2989 | 6.46k | &mut interface_keys_rewritten, |
2990 | | ); |
2991 | | |
2992 | | // Put that thing back where it came from, or so help me! |
2993 | 6.46k | let world = &mut self.worlds[world_id]; |
2994 | 6.46k | assert!(world.imports.is_empty()); |
2995 | 6.46k | assert!(world.exports.is_empty()); |
2996 | 6.46k | world.imports = imports; |
2997 | 6.46k | world.exports = exports; |
2998 | | |
2999 | | #[cfg(debug_assertions)] |
3000 | | self.assert_valid(); |
3001 | 6.46k | } |
3002 | | |
3003 | | /// Implementation of `generate_nominal_type_ids` which processes either a |
3004 | | /// world's imports or exports as specified in `items`. |
3005 | | /// |
3006 | | /// The parameters here are: |
3007 | | /// |
3008 | | /// * `maps` - the set of types that have previously been cloned by the |
3009 | | /// previous stage, if any. This is used to update any dependencies of an |
3010 | | /// interface that is cloned. |
3011 | | /// |
3012 | | /// * `world` - the world that's being nominalized. |
3013 | | /// |
3014 | | /// * `items` - either `imports` or `exports` for the `world`. This is |
3015 | | /// mutated in-place to have keys/items rewritten as-needed. |
3016 | | /// |
3017 | | /// * `seen` - the set of interfaces that have been seen previously when |
3018 | | /// iterating over this world. All interfaces processed are added to this |
3019 | | /// set. |
3020 | | /// |
3021 | | /// * `interface_keys_rewritten` - a distinct set from `seen` which only |
3022 | | /// tracks rewritten interfaces which have `WorldKey::Interface`. This is |
3023 | | /// the set of interfaces which dependencies can pull types from, which |
3024 | | /// needs to be tracked separately to know when to rewrite. |
3025 | 12.9k | fn nominalize_world_items( |
3026 | 12.9k | &mut self, |
3027 | 12.9k | maps: &mut CloneMaps, |
3028 | 12.9k | world: WorldId, |
3029 | 12.9k | items: &mut IndexMap<WorldKey, WorldItem>, |
3030 | 12.9k | seen: &mut HashSet<InterfaceId>, |
3031 | 12.9k | interface_keys_rewritten: &mut HashSet<InterfaceId>, |
3032 | 12.9k | ) { |
3033 | | // Overall the problem that this function is trying to solve is not an |
3034 | | // easy one. The input is an AST-like structure and the goal of this |
3035 | | // function is to effectively perform a name resolution pass. The end |
3036 | | // result is that if a thing points to another thing (e.g. type-use or |
3037 | | // interface id) then that represents the actual name resolution of what |
3038 | | // it points to. |
3039 | | // |
3040 | | // Currently, though, this isn't really a full-blown name resolution |
3041 | | // pass. This is a pretty simple "do things in the right order" and name |
3042 | | // resolution pops out. The conventions of WIT and how it translates to |
3043 | | // components is what falls out of this loop below. |
3044 | | // |
3045 | | // The first rule of WIT is that interfaces can only use types from |
3046 | | // other interface imports/exports. This notably excludes named imports. |
3047 | | // For example: |
3048 | | // |
3049 | | // interface a { |
3050 | | // type t = u32; |
3051 | | // } |
3052 | | // |
3053 | | // interface b { |
3054 | | // use a.{t}; |
3055 | | // } |
3056 | | // |
3057 | | // world w { |
3058 | | // import a; |
3059 | | // import b; // uses `import a` |
3060 | | // import c: b; // also uses `import a` |
3061 | | // import d: interface { |
3062 | | // use a.{t}; // uses `import a` |
3063 | | // } |
3064 | | // } |
3065 | | // |
3066 | | // The next rule is that exported interfaces will use types from |
3067 | | // imports, unless the interface is also exported. For example: |
3068 | | // |
3069 | | // interface a { |
3070 | | // type t = u32; |
3071 | | // } |
3072 | | // |
3073 | | // interface b { |
3074 | | // use a.{t}; |
3075 | | // } |
3076 | | // |
3077 | | // world w1 { |
3078 | | // import a; |
3079 | | // |
3080 | | // export b; // uses `import a` |
3081 | | // export c: b; // uses `import a` |
3082 | | // export d: interface { |
3083 | | // use a.{t}; // uses `import a` |
3084 | | // } |
3085 | | // } |
3086 | | // |
3087 | | // world w2 { |
3088 | | // export a; |
3089 | | // export b; // uses `export a` |
3090 | | // export c: b; // uses `export a` |
3091 | | // export d: interface { |
3092 | | // use a.{t}; // uses `export a` |
3093 | | // } |
3094 | | // } |
3095 | | // |
3096 | | // Finally, named imports, such as `import a: b` and `import a: |
3097 | | // interface { ... }` cannot be used by anything. They can only |
3098 | | // reference types in other interface imports/exports. |
3099 | | // |
3100 | | // Overall this is a pretty simplistic system. It's "good enough" for |
3101 | | // now but will almost certainly be expanded over time. The hope is that |
3102 | | // expanding this involves making this function more complicated but |
3103 | | // ideally nowhere else. |
3104 | | |
3105 | | // Given all that intro, the first thing we need to prioritize is that |
3106 | | // `WorldKey::Name`'d interfaces are visited after `WorldKey::Interface` |
3107 | | // interfaces. This is a bit of a weird result of how this function is |
3108 | | // implemented right now. This visit order is a bit of a hack and |
3109 | | // probably won't live beyond making WIT more powerful. |
3110 | | // |
3111 | | // Anyway, the reason for this has to do with the `CloneMaps` down |
3112 | | // below. Basically what we're doing here is cloning interfaces, but |
3113 | | // when doing so we need to be able to rewrite references to |
3114 | | // previously-cloned interfaces if need be. `CloneMaps` represents the |
3115 | | // aggregate results of all previous clones. Due to named interfaces |
3116 | | // never being importable-from it means that mutations to `CloneMaps` |
3117 | | // are discarded when named interfaces are cloned. The trick here |
3118 | | // happens where this unconditionally preserves all modifications to |
3119 | | // `CloneMaps` for `WorldKey::Interface` clones. Behavior then "falls |
3120 | | // out" where references to cloned interfaces are naturally rewritten. |
3121 | | // |
3122 | | // This all falls down, however, if an import is cloned and recorded. |
3123 | | // Interfaces can be both exported and imported, which would mean that |
3124 | | // the import and export are both cloned, and both need to be preserved |
3125 | | // in `CloneMaps`. Right now `CloneMaps` requires uniqueness when |
3126 | | // cloning (e.g. can't clone the same thing twice). |
3127 | | // |
3128 | | // Long story short: it's a hack that sort order here is the way it is. |
3129 | | // Sorry. Be prepared to delete this should WIT get more powerful. |
3130 | 12.9k | let mut order = items.iter().enumerate().collect::<Vec<_>>(); |
3131 | 21.6k | order.sort_by_key(|(_, (key, item))| match (key, item) { |
3132 | 15.6k | (WorldKey::Name(_), WorldItem::Interface { .. }) => 1, |
3133 | 6.00k | _ => 0, |
3134 | 21.6k | }); |
3135 | 12.9k | let mut to_rewrite = IndexMap::default(); |
3136 | 14.8k | for (i, (key, item)) in order { |
3137 | 14.8k | let id = match item { |
3138 | 10.7k | WorldItem::Interface { id, .. } => *id, |
3139 | | |
3140 | | // Functions/types aren't rewritten, they're all nominal at the |
3141 | | // world-level anyway. |
3142 | 4.13k | WorldItem::Function(_) | WorldItem::Type { .. } => continue, |
3143 | | }; |
3144 | | |
3145 | | // If this interface itself is being visited for the second time |
3146 | | // (e.g. imported & exported, imported twice, exported twice, etc), |
3147 | | // or if any dependency of this interface is rewritten, then the |
3148 | | // interface itself needs to be rewritten. Otherwise continue |
3149 | | // onwards. |
3150 | 10.7k | let duplicated = !seen.insert(id); |
3151 | 10.7k | let any_dep_rewritten = self |
3152 | 10.7k | .interface_direct_deps(id) |
3153 | 10.7k | .any(|dep| interface_keys_rewritten.contains(&dep)); |
3154 | 10.7k | if !(duplicated || any_dep_rewritten) { |
3155 | 3.27k | log::trace!("{} already nominal", self.name_world_key(key)); |
3156 | 3.27k | continue; |
3157 | 7.43k | } |
3158 | 7.43k | log::trace!("{} getting rewritten nominal", self.name_world_key(key)); |
3159 | | |
3160 | | // If this is `WorldKey::Interface` then register this in the |
3161 | | // `interface_keys_rewritten` map, and also plumb this through to |
3162 | | // the rewriting stage to know whether `maps` needs to be reset or |
3163 | | // not. |
3164 | 7.43k | let is_name = matches!(key, WorldKey::Name(_)); |
3165 | 7.43k | if !is_name { |
3166 | 62 | assert!(interface_keys_rewritten.insert(id)); |
3167 | 7.37k | } |
3168 | | |
3169 | 7.43k | to_rewrite |
3170 | 7.43k | .entry(id) |
3171 | 7.43k | .or_insert(Vec::new()) |
3172 | 7.43k | .push((i, is_name)); |
3173 | | } |
3174 | | |
3175 | | // Now that we know what to rewrite, rewrite everything. |
3176 | | // |
3177 | | // The trickiest part here is deciding what to do with `maps`. As |
3178 | | // interfaces are cloned they'll record all remappings of |
3179 | | // types/interfaces/etc within `maps`. We don't want to persist |
3180 | | // everything because if an interface is cloned twice then everything |
3181 | | // will get overwritten/corrupted within the map. In theory what we want |
3182 | | // is for `maps` to track, for any one interface, just the transitive |
3183 | | // set of dependencies for that interface and how they've been cloned. |
3184 | | // What's implemented here is an approximation of this that should work |
3185 | | // for now. |
3186 | | // |
3187 | | // Notably `to_rewrite` is an ordered list keyed by `InterfaceId`. This |
3188 | | // means that if we walk `to_rewrite` in order we're walking this in |
3189 | | // topological order. Second we then additionally sort the `list` for |
3190 | | // each `to_rewrite` entry to ensure that all `WorldKey::Name` items are |
3191 | | // visited first. In doing so we also discard all mutations to `maps` |
3192 | | // after visiting is done. In effect what this does is it discards all |
3193 | | // modifications due to `WorldKey::Name`, because nothing can depend on |
3194 | | // those interfaces, and then it preserves modifications for |
3195 | | // `WorldKey::Interface`, which other interfaces can indeed depend on. |
3196 | | // In the end this basically does a very careful walk over a very |
3197 | | // careful organization of `to_rewrite`. |
3198 | | // |
3199 | | // This'll need massive refactoring if WIT gets the ability to express |
3200 | | // arbitrary edges between interfaces. It's WIT-level restriction right |
3201 | | // now of sorts. There's no current way to model a `import a: i;` where |
3202 | | // `i`'s dependencies are pulled from `import b: dep`, for example. |
3203 | | // Right now if `i` depends on `dep` then that just always results in |
3204 | | // `import dep`. |
3205 | 12.9k | for (id, mut list) in to_rewrite { |
3206 | 11.8k | list.sort_by_key(|(_, is_name)| if *is_name { 0 } else { 1 }); |
3207 | 7.43k | for (i, is_name) in list { |
3208 | 7.43k | let prev_maps = if is_name { Some(maps.clone()) } else { None }; |
3209 | | |
3210 | 7.43k | let mut cloner = clone::Cloner::new( |
3211 | 7.43k | self, |
3212 | 7.43k | maps, |
3213 | 7.43k | TypeOwner::World(world), |
3214 | 7.43k | TypeOwner::World(world), |
3215 | | ); |
3216 | | |
3217 | 7.43k | let mut new_id = id; |
3218 | 7.43k | cloner.new_package = cloner.resolve.interfaces[id].package; |
3219 | 7.43k | cloner.interface(&mut new_id); |
3220 | 7.43k | let (key, prev) = items.get_index_mut(i).unwrap(); |
3221 | 7.43k | match prev { |
3222 | 7.43k | WorldItem::Interface { id, .. } => *id = new_id, |
3223 | 0 | _ => unreachable!(), |
3224 | | } |
3225 | | |
3226 | 7.43k | match key { |
3227 | | // If the key for this is an `Interface` then that means we |
3228 | | // need to update the key as well. Here that's replaced by-index |
3229 | | // in the `IndexMap` to preserve the same ordering as before, |
3230 | | // and this operation should always succeed since `new_id` is |
3231 | | // fresh, hence the `unwrap()`. |
3232 | 62 | WorldKey::Interface(_) => { |
3233 | 62 | items.replace_index(i, WorldKey::Interface(new_id)).unwrap(); |
3234 | 62 | } |
3235 | | |
3236 | | // Name-based keys don't need updating as they only contain a |
3237 | | // string, no ids. |
3238 | 7.37k | WorldKey::Name(_) => {} |
3239 | | } |
3240 | | |
3241 | 7.43k | if let Some(prev) = prev_maps { |
3242 | 7.37k | *maps = prev; |
3243 | 7.37k | } |
3244 | | } |
3245 | | } |
3246 | 12.9k | } |
3247 | | } |
3248 | | |
3249 | | /// Possible imports that can be passed to [`Resolve::wasm_import_name`]. |
3250 | | #[derive(Debug)] |
3251 | | pub enum WasmImport<'a> { |
3252 | | /// A WIT function is being imported. Optionally from an interface. |
3253 | | Func { |
3254 | | /// The name of the interface that the function is being imported from. |
3255 | | /// |
3256 | | /// If the function is imported directly from the world then this is |
3257 | | /// `None`. |
3258 | | interface: Option<&'a WorldKey>, |
3259 | | |
3260 | | /// The function being imported. |
3261 | | func: &'a Function, |
3262 | | }, |
3263 | | |
3264 | | /// A resource-related intrinsic is being imported. |
3265 | | ResourceIntrinsic { |
3266 | | /// The optional interface to import from, same as `WasmImport::Func`. |
3267 | | interface: Option<&'a WorldKey>, |
3268 | | |
3269 | | /// The resource that's being operated on. |
3270 | | resource: TypeId, |
3271 | | |
3272 | | /// The intrinsic that's being imported. |
3273 | | intrinsic: ResourceIntrinsic, |
3274 | | }, |
3275 | | |
3276 | | /// A future-related intrinsic is being imported. |
3277 | | FutureIntrinsic { |
3278 | | /// The optional interface to import from, same as `WasmImport::Func`. |
3279 | | interface: Option<&'a WorldKey>, |
3280 | | |
3281 | | /// The function whose signature this future type appears in. |
3282 | | func: &'a Function, |
3283 | | |
3284 | | /// The future type appearing in `func.find_futures_and_streams(resolve)`. |
3285 | | /// |
3286 | | /// Use `None` for the special `unit` payload case. |
3287 | | ty: Option<TypeId>, |
3288 | | |
3289 | | /// The intrinsic that's being imported. |
3290 | | intrinsic: FutureIntrinsic, |
3291 | | |
3292 | | /// Whether this import is for an exported WIT function. |
3293 | | /// |
3294 | | /// This controls whether the module name is prefixed with `[export]`. |
3295 | | exported: bool, |
3296 | | |
3297 | | /// Whether this intrinsic import is async-lowered. |
3298 | | /// |
3299 | | /// This is only valid for read/write/cancel intrinsics. |
3300 | | async_: bool, |
3301 | | }, |
3302 | | |
3303 | | /// A stream-related intrinsic is being imported. |
3304 | | StreamIntrinsic { |
3305 | | /// The optional interface to import from, same as `WasmImport::Func`. |
3306 | | interface: Option<&'a WorldKey>, |
3307 | | |
3308 | | /// The function whose signature this stream type appears in. |
3309 | | func: &'a Function, |
3310 | | |
3311 | | /// The stream type appearing in `func.find_futures_and_streams(resolve)`. |
3312 | | /// |
3313 | | /// Use `None` for the special `unit` payload case. |
3314 | | ty: Option<TypeId>, |
3315 | | |
3316 | | /// The intrinsic that's being imported. |
3317 | | intrinsic: StreamIntrinsic, |
3318 | | |
3319 | | /// Whether this import is for an exported WIT function. |
3320 | | /// |
3321 | | /// This controls whether the module name is prefixed with `[export]`. |
3322 | | exported: bool, |
3323 | | |
3324 | | /// Whether this intrinsic import is async-lowered. |
3325 | | /// |
3326 | | /// This is only valid for read/write/cancel intrinsics. |
3327 | | async_: bool, |
3328 | | }, |
3329 | | } |
3330 | | |
3331 | | /// Intrinsic definitions to go with [`WasmImport::ResourceIntrinsic`] which |
3332 | | /// also goes with [`Resolve::wasm_import_name`]. |
3333 | | #[derive(Debug)] |
3334 | | pub enum ResourceIntrinsic { |
3335 | | ImportedDrop, |
3336 | | ExportedDrop, |
3337 | | ExportedNew, |
3338 | | ExportedRep, |
3339 | | } |
3340 | | |
3341 | | /// Intrinsic definitions to go with [`WasmImport::FutureIntrinsic`] which |
3342 | | /// also goes with [`Resolve::wasm_import_name`]. |
3343 | | #[derive(Debug)] |
3344 | | pub enum FutureIntrinsic { |
3345 | | New, |
3346 | | Read, |
3347 | | Write, |
3348 | | Forward, |
3349 | | CancelRead, |
3350 | | CancelWrite, |
3351 | | DropReadable, |
3352 | | DropWritable, |
3353 | | } |
3354 | | |
3355 | | /// Intrinsic definitions to go with [`WasmImport::StreamIntrinsic`] which |
3356 | | /// also goes with [`Resolve::wasm_import_name`]. |
3357 | | #[derive(Debug)] |
3358 | | pub enum StreamIntrinsic { |
3359 | | New, |
3360 | | Read, |
3361 | | Write, |
3362 | | Forward, |
3363 | | CancelRead, |
3364 | | CancelWrite, |
3365 | | DropReadable, |
3366 | | DropWritable, |
3367 | | } |
3368 | | |
3369 | | /// Indicates whether a function export is a normal export, a post-return |
3370 | | /// function, or a callback function. |
3371 | | #[derive(Debug)] |
3372 | | pub enum WasmExportKind { |
3373 | | /// Normal function export. |
3374 | | Normal, |
3375 | | |
3376 | | /// Post-return function. |
3377 | | PostReturn, |
3378 | | |
3379 | | /// Async callback function. |
3380 | | Callback, |
3381 | | } |
3382 | | |
3383 | | /// Different kinds of exports that can be passed to |
3384 | | /// [`Resolve::wasm_export_name`] to export from core wasm modules. |
3385 | | #[derive(Debug)] |
3386 | | pub enum WasmExport<'a> { |
3387 | | /// A WIT function is being exported, optionally from an interface. |
3388 | | Func { |
3389 | | /// An optional interface which owns `func`. Use `None` for top-level |
3390 | | /// world function. |
3391 | | interface: Option<&'a WorldKey>, |
3392 | | |
3393 | | /// The function being exported. |
3394 | | func: &'a Function, |
3395 | | |
3396 | | /// Kind of function (normal, post-return, or callback) being exported. |
3397 | | kind: WasmExportKind, |
3398 | | }, |
3399 | | |
3400 | | /// A destructor for a resource exported from this module. |
3401 | | ResourceDtor { |
3402 | | /// The interface that owns the resource. |
3403 | | interface: &'a WorldKey, |
3404 | | /// The resource itself that the destructor is for. |
3405 | | resource: TypeId, |
3406 | | }, |
3407 | | |
3408 | | /// Linear memory, the one that the canonical ABI uses. |
3409 | | Memory, |
3410 | | |
3411 | | /// An initialization function (not the core wasm `start`). |
3412 | | Initialize, |
3413 | | |
3414 | | /// The general-purpose realloc hook. |
3415 | | Realloc, |
3416 | | } |
3417 | | |
3418 | | /// Structure returned by [`Resolve::merge`] which contains mappings from |
3419 | | /// old-ids to new-ids after the merge. |
3420 | | #[derive(Default)] |
3421 | | pub struct Remap { |
3422 | | pub types: Vec<Option<TypeId>>, |
3423 | | pub interfaces: Vec<Option<InterfaceId>>, |
3424 | | pub worlds: Vec<Option<WorldId>>, |
3425 | | pub packages: Vec<PackageId>, |
3426 | | |
3427 | | /// A cache of anonymous `own<T>` handles for resource types. |
3428 | | /// |
3429 | | /// The appending operation of `Remap` is the one responsible for |
3430 | | /// translating references to `T` where `T` is a resource into `own<T>` |
3431 | | /// instead. This map is used to deduplicate the `own<T>` types generated |
3432 | | /// to generate as few as possible. |
3433 | | /// |
3434 | | /// The key of this map is the resource id `T` in the new resolve, and |
3435 | | /// the value is the `own<T>` type pointing to `T`. |
3436 | | own_handles: HashMap<TypeId, TypeId>, |
3437 | | |
3438 | | type_has_borrow: Vec<Option<bool>>, |
3439 | | } |
3440 | | |
3441 | 389k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Span) -> ResolveResult<Id<T>> { |
3442 | 389k | match map.get(id.index()) { |
3443 | 389k | Some(Some(id)) => Ok(*id), |
3444 | | Some(None) => { |
3445 | 0 | let msg = format!( |
3446 | | "found a reference to a {desc} which is excluded \ |
3447 | | due to its feature not being activated" |
3448 | | ); |
3449 | 0 | Err(ResolveError::new_semantic(span, msg)) |
3450 | | } |
3451 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), |
3452 | | } |
3453 | 389k | } wit_parser::resolve::apply_map::<wit_parser::World> Line | Count | Source | 3441 | 35.6k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Span) -> ResolveResult<Id<T>> { | 3442 | 35.6k | match map.get(id.index()) { | 3443 | 35.6k | Some(Some(id)) => Ok(*id), | 3444 | | Some(None) => { | 3445 | 0 | let msg = format!( | 3446 | | "found a reference to a {desc} which is excluded \ | 3447 | | due to its feature not being activated" | 3448 | | ); | 3449 | 0 | Err(ResolveError::new_semantic(span, msg)) | 3450 | | } | 3451 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), | 3452 | | } | 3453 | 35.6k | } |
wit_parser::resolve::apply_map::<wit_parser::TypeDef> Line | Count | Source | 3441 | 245k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Span) -> ResolveResult<Id<T>> { | 3442 | 245k | match map.get(id.index()) { | 3443 | 245k | Some(Some(id)) => Ok(*id), | 3444 | | Some(None) => { | 3445 | 0 | let msg = format!( | 3446 | | "found a reference to a {desc} which is excluded \ | 3447 | | due to its feature not being activated" | 3448 | | ); | 3449 | 0 | Err(ResolveError::new_semantic(span, msg)) | 3450 | | } | 3451 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), | 3452 | | } | 3453 | 245k | } |
wit_parser::resolve::apply_map::<wit_parser::Interface> Line | Count | Source | 3441 | 108k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Span) -> ResolveResult<Id<T>> { | 3442 | 108k | match map.get(id.index()) { | 3443 | 108k | Some(Some(id)) => Ok(*id), | 3444 | | Some(None) => { | 3445 | 0 | let msg = format!( | 3446 | | "found a reference to a {desc} which is excluded \ | 3447 | | due to its feature not being activated" | 3448 | | ); | 3449 | 0 | Err(ResolveError::new_semantic(span, msg)) | 3450 | | } | 3451 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), | 3452 | | } | 3453 | 108k | } |
|
3454 | | |
3455 | 0 | fn rename(original_name: &str, include_name: &IncludeName) -> Option<String> { |
3456 | | // Strip all leading `[...]` annotations. |
3457 | 0 | let mut prefix_len = 0; |
3458 | 0 | while original_name[prefix_len..].starts_with('[') { |
3459 | 0 | let close = original_name[prefix_len..].find(']')?; |
3460 | 0 | prefix_len += close + 1; |
3461 | | } |
3462 | 0 | let (prefix, rest) = original_name.split_at(prefix_len); |
3463 | | |
3464 | 0 | match rest.split_once('.') { |
3465 | | // Rename just the resource portion. |
3466 | 0 | Some((resource, func)) if resource == include_name.name => { |
3467 | 0 | Some(format!("{prefix}{}.{func}", include_name.as_)) |
3468 | | } |
3469 | | // Rename the whole label. |
3470 | 0 | None if rest == include_name.name => Some(format!("{prefix}{}", include_name.as_)), |
3471 | 0 | _ => None, |
3472 | | } |
3473 | 0 | } |
3474 | | |
3475 | | impl Remap { |
3476 | 245k | pub fn map_type(&self, id: TypeId, span: Span) -> ResolveResult<TypeId> { |
3477 | 245k | apply_map(&self.types, id, "type", span) |
3478 | 245k | } |
3479 | | |
3480 | 108k | pub fn map_interface(&self, id: InterfaceId, span: Span) -> ResolveResult<InterfaceId> { |
3481 | 108k | apply_map(&self.interfaces, id, "interface", span) |
3482 | 108k | } |
3483 | | |
3484 | 35.6k | pub fn map_world(&self, id: WorldId, span: Span) -> ResolveResult<WorldId> { |
3485 | 35.6k | apply_map(&self.worlds, id, "world", span) |
3486 | 35.6k | } |
3487 | | |
3488 | 5.97k | pub fn map_world_for_type(&self, id: WorldId, span: Span) -> ResolveResult<WorldId> { |
3489 | 5.97k | self.map_world(id, span).map_err(|e| { |
3490 | 0 | ResolveError::new_semantic( |
3491 | 0 | span, |
3492 | 0 | format!("{e}; this type is not gated by a feature but its world is"), |
3493 | | ) |
3494 | 0 | }) |
3495 | 5.97k | } |
3496 | | |
3497 | 37.8k | pub fn map_interface_for_type( |
3498 | 37.8k | &self, |
3499 | 37.8k | id: InterfaceId, |
3500 | 37.8k | span: Span, |
3501 | 37.8k | ) -> ResolveResult<InterfaceId> { |
3502 | 37.8k | self.map_interface(id, span).map_err(|e| { |
3503 | 0 | ResolveError::new_semantic( |
3504 | 0 | span, |
3505 | 0 | format!("{e}; this type is not gated by a feature but its interface is"), |
3506 | | ) |
3507 | 0 | }) |
3508 | 37.8k | } |
3509 | | |
3510 | 11.8k | fn append( |
3511 | 11.8k | &mut self, |
3512 | 11.8k | resolve: &mut Resolve, |
3513 | 11.8k | unresolved: UnresolvedPackage, |
3514 | 11.8k | ) -> ResolveResult<PackageId> { |
3515 | 11.8k | let pkgid = resolve.packages.alloc(Package { |
3516 | 11.8k | name: unresolved.name.clone(), |
3517 | 11.8k | docs: unresolved.docs.clone(), |
3518 | 11.8k | interfaces: Default::default(), |
3519 | 11.8k | worlds: Default::default(), |
3520 | 11.8k | }); |
3521 | 11.8k | assert!( |
3522 | 11.8k | !resolve.package_names.contains_key(&unresolved.name), |
3523 | | "attempting to re-add package `{}` when it's already present in this `Resolve`", |
3524 | | unresolved.name, |
3525 | | ); |
3526 | 11.8k | resolve.package_names.insert(unresolved.name.clone(), pkgid); |
3527 | 11.8k | self.process_foreign_deps(resolve, pkgid, &unresolved)?; |
3528 | | |
3529 | 11.8k | let foreign_types = self.types.len(); |
3530 | 11.8k | let foreign_interfaces = self.interfaces.len(); |
3531 | 11.8k | let foreign_worlds = self.worlds.len(); |
3532 | | |
3533 | | // Copy over all types first, updating any intra-type references. Note |
3534 | | // that types are sorted topologically which means this iteration |
3535 | | // order should be sufficient. Also note though that the interface |
3536 | | // owner of a type isn't updated here due to interfaces not being known |
3537 | | // yet. |
3538 | 120k | for (id, mut ty) in unresolved.types.into_iter().skip(foreign_types) { |
3539 | 120k | let span = ty.span; |
3540 | 120k | if !resolve.include_stability(&ty.stability, &pkgid, span)? { |
3541 | 0 | self.types.push(None); |
3542 | 0 | continue; |
3543 | 120k | } |
3544 | | |
3545 | 120k | self.update_typedef(resolve, &mut ty, span)?; |
3546 | 120k | let new_id = resolve.types.alloc(ty); |
3547 | 120k | assert_eq!(self.types.len(), id.index()); |
3548 | | |
3549 | 120k | let new_id = match resolve.types[new_id] { |
3550 | | // If this is an `own<T>` handle then either replace it with a |
3551 | | // preexisting `own<T>` handle which may have been generated in |
3552 | | // `update_ty`. If that doesn't exist though then insert it into |
3553 | | // the `own_handles` cache. |
3554 | | TypeDef { |
3555 | | name: None, |
3556 | | owner: TypeOwner::None, |
3557 | 0 | kind: TypeDefKind::Handle(Handle::Own(id)), |
3558 | | docs: _, |
3559 | | stability: _, |
3560 | | span: _, |
3561 | | external_id: _, |
3562 | 0 | } => *self.own_handles.entry(id).or_insert(new_id), |
3563 | | |
3564 | | // Everything not-related to `own<T>` doesn't get its ID |
3565 | | // modified. |
3566 | 120k | _ => new_id, |
3567 | | }; |
3568 | 120k | self.types.push(Some(new_id)); |
3569 | | } |
3570 | | |
3571 | | // Next transfer all interfaces into `Resolve`, updating type ids |
3572 | | // referenced along the way. |
3573 | 44.1k | for (id, mut iface) in unresolved.interfaces.into_iter().skip(foreign_interfaces) { |
3574 | 44.1k | let span = iface.span; |
3575 | 44.1k | if !resolve.include_stability(&iface.stability, &pkgid, span)? { |
3576 | 0 | self.interfaces.push(None); |
3577 | 0 | continue; |
3578 | 44.1k | } |
3579 | 44.1k | assert!(iface.package.is_none()); |
3580 | 44.1k | iface.package = Some(pkgid); |
3581 | 44.1k | self.update_interface(resolve, &mut iface)?; |
3582 | 44.1k | let new_id = resolve.interfaces.alloc(iface); |
3583 | 44.1k | assert_eq!(self.interfaces.len(), id.index()); |
3584 | 44.1k | self.interfaces.push(Some(new_id)); |
3585 | | } |
3586 | | |
3587 | | // Now that interfaces are identified go back through the types and |
3588 | | // update their interface owners. |
3589 | 120k | for id in self.types.iter().skip(foreign_types) { |
3590 | 120k | let id = match id { |
3591 | 120k | Some(id) => *id, |
3592 | 0 | None => continue, |
3593 | | }; |
3594 | 120k | let span = resolve.types[id].span; |
3595 | 120k | match &mut resolve.types[id].owner { |
3596 | 37.8k | TypeOwner::Interface(iface_id) => { |
3597 | 37.8k | *iface_id = self.map_interface_for_type(*iface_id, span)?; |
3598 | | } |
3599 | 82.5k | TypeOwner::World(_) | TypeOwner::None => {} |
3600 | | } |
3601 | | } |
3602 | | |
3603 | | // Expand worlds. Note that this does NOT process `include` statements, |
3604 | | // that's handled below. Instead this just handles world item updates |
3605 | | // and resolves references to types/items within `Resolve`. |
3606 | | // |
3607 | | // This is done after types/interfaces are fully settled so the |
3608 | | // transitive relation between interfaces, through types, is understood |
3609 | | // here. |
3610 | 16.3k | for (id, mut world) in unresolved.worlds.into_iter().skip(foreign_worlds) { |
3611 | 16.3k | let world_span = world.span; |
3612 | 16.3k | if !resolve.include_stability(&world.stability, &pkgid, world_span)? { |
3613 | 0 | self.worlds.push(None); |
3614 | 0 | continue; |
3615 | 16.3k | } |
3616 | 16.3k | self.update_world(&mut world, resolve, &pkgid)?; |
3617 | | |
3618 | 16.3k | let new_id = resolve.worlds.alloc(world); |
3619 | 16.3k | assert_eq!(self.worlds.len(), id.index()); |
3620 | 16.3k | self.worlds.push(Some(new_id)); |
3621 | | } |
3622 | | |
3623 | | // As with interfaces, now update the ids of world-owned types. |
3624 | 120k | for id in self.types.iter().skip(foreign_types) { |
3625 | 120k | let id = match id { |
3626 | 120k | Some(id) => *id, |
3627 | 0 | None => continue, |
3628 | | }; |
3629 | 120k | let span = resolve.types[id].span; |
3630 | 120k | match &mut resolve.types[id].owner { |
3631 | 5.97k | TypeOwner::World(world_id) => { |
3632 | 5.97k | *world_id = self.map_world_for_type(*world_id, span)?; |
3633 | | } |
3634 | 114k | TypeOwner::Interface(_) | TypeOwner::None => {} |
3635 | | } |
3636 | | } |
3637 | | |
3638 | | // After the above, process `include` statements for worlds and |
3639 | | // additionally fully elaborate them. Processing of `include` is |
3640 | | // deferred until after the steps above so the fully resolved state of |
3641 | | // local types in this package are all available. This is required |
3642 | | // because `include` may copy types between worlds when the type is |
3643 | | // defined in the world itself. |
3644 | | // |
3645 | | // This step, after processing `include`, will also use |
3646 | | // `elaborate_world` to fully expand the world in terms of |
3647 | | // imports/exports and ensure that all necessary imports/exports are all |
3648 | | // listed. |
3649 | | // |
3650 | | // Note that `self.worlds` is already sorted in topological order so if |
3651 | | // one world refers to another via `include` then it's guaranteed that |
3652 | | // the one we're referring to is already expanded and ready to be |
3653 | | // included. |
3654 | 16.2k | for id in self.worlds.iter().skip(foreign_worlds) { |
3655 | 16.2k | let Some(id) = *id else { |
3656 | 0 | continue; |
3657 | | }; |
3658 | 16.2k | self.process_world_includes(id, resolve, &pkgid)?; |
3659 | | |
3660 | 16.2k | let world_span = resolve.worlds[id].span; |
3661 | 16.2k | resolve.elaborate_world(id, world_span)?; |
3662 | | } |
3663 | | |
3664 | | // Fixup "parent" ids now that everything has been identified |
3665 | 44.0k | for id in self.interfaces.iter().skip(foreign_interfaces) { |
3666 | 44.0k | let id = match id { |
3667 | 44.0k | Some(id) => *id, |
3668 | 0 | None => continue, |
3669 | | }; |
3670 | 44.0k | let iface = &mut resolve.interfaces[id]; |
3671 | 44.0k | iface.package = Some(pkgid); |
3672 | 44.0k | if let Some(name) = &iface.name { |
3673 | 42.2k | let prev = resolve.packages[pkgid].interfaces.insert(name.clone(), id); |
3674 | 42.2k | assert!(prev.is_none()); |
3675 | 1.78k | } |
3676 | | } |
3677 | 16.2k | for id in self.worlds.iter().skip(foreign_worlds) { |
3678 | 16.2k | let id = match id { |
3679 | 16.2k | Some(id) => *id, |
3680 | 0 | None => continue, |
3681 | | }; |
3682 | 16.2k | let world = &mut resolve.worlds[id]; |
3683 | 16.2k | world.package = Some(pkgid); |
3684 | 16.2k | let prev = resolve.packages[pkgid] |
3685 | 16.2k | .worlds |
3686 | 16.2k | .insert(world.name.clone(), id); |
3687 | 16.2k | assert!(prev.is_none()); |
3688 | | } |
3689 | 11.8k | Ok(pkgid) |
3690 | 11.8k | } |
3691 | | |
3692 | 11.8k | fn process_foreign_deps( |
3693 | 11.8k | &mut self, |
3694 | 11.8k | resolve: &mut Resolve, |
3695 | 11.8k | pkgid: PackageId, |
3696 | 11.8k | unresolved: &UnresolvedPackage, |
3697 | 11.8k | ) -> ResolveResult<()> { |
3698 | | // Invert the `foreign_deps` map to be keyed by world id to get |
3699 | | // used in the loops below. |
3700 | 11.8k | let mut world_to_package = HashMap::new(); |
3701 | 11.8k | let mut interface_to_package = HashMap::new(); |
3702 | 11.8k | for (i, (pkg_name, worlds_or_ifaces)) in unresolved.foreign_deps.iter().enumerate() { |
3703 | 3.53k | for (name, (item, stabilities)) in worlds_or_ifaces { |
3704 | 3.53k | match item { |
3705 | 3.53k | AstItem::Interface(unresolved_interface_id) => { |
3706 | 3.53k | let prev = interface_to_package.insert( |
3707 | 3.53k | *unresolved_interface_id, |
3708 | 3.53k | (pkg_name, name, unresolved.foreign_dep_spans[i], stabilities), |
3709 | | ); |
3710 | 3.53k | assert!(prev.is_none()); |
3711 | | } |
3712 | 0 | AstItem::World(unresolved_world_id) => { |
3713 | 0 | let prev = world_to_package.insert( |
3714 | 0 | *unresolved_world_id, |
3715 | 0 | (pkg_name, name, unresolved.foreign_dep_spans[i], stabilities), |
3716 | | ); |
3717 | 0 | assert!(prev.is_none()); |
3718 | | } |
3719 | | } |
3720 | | } |
3721 | | } |
3722 | | |
3723 | | // Connect all interfaces referred to in `interface_to_package`, which |
3724 | | // are at the front of `unresolved.interfaces`, to interfaces already |
3725 | | // contained within `resolve`. |
3726 | 11.8k | self.process_foreign_interfaces(unresolved, &interface_to_package, resolve, &pkgid)?; |
3727 | | |
3728 | | // Connect all worlds referred to in `world_to_package`, which |
3729 | | // are at the front of `unresolved.worlds`, to worlds already |
3730 | | // contained within `resolve`. |
3731 | 11.8k | self.process_foreign_worlds(unresolved, &world_to_package, resolve, &pkgid)?; |
3732 | | |
3733 | | // Finally, iterate over all foreign-defined types and determine |
3734 | | // what they map to. |
3735 | 11.8k | self.process_foreign_types(unresolved, pkgid, resolve)?; |
3736 | | |
3737 | 11.8k | for (id, span) in unresolved.required_resource_types.iter() { |
3738 | | // Note that errors are ignored here because an error represents a |
3739 | | // type that has been configured away. If a type is configured away |
3740 | | // then any future use of it will generate an error so there's no |
3741 | | // need to validate that it's a resource here. |
3742 | 17 | let Ok(mut id) = self.map_type(*id, *span) else { |
3743 | 0 | continue; |
3744 | | }; |
3745 | | loop { |
3746 | 19 | match resolve.types[id].kind { |
3747 | 2 | TypeDefKind::Type(Type::Id(i)) => id = i, |
3748 | 17 | TypeDefKind::Resource => break, |
3749 | | _ => { |
3750 | 0 | return Err(ResolveError::new_semantic( |
3751 | 0 | *span, |
3752 | 0 | "type used in a handle must be a resource", |
3753 | 0 | )); |
3754 | | } |
3755 | | } |
3756 | | } |
3757 | | } |
3758 | | |
3759 | | #[cfg(debug_assertions)] |
3760 | | resolve.assert_valid(); |
3761 | | |
3762 | 11.8k | Ok(()) |
3763 | 11.8k | } |
3764 | | |
3765 | 11.8k | fn process_foreign_interfaces( |
3766 | 11.8k | &mut self, |
3767 | 11.8k | unresolved: &UnresolvedPackage, |
3768 | 11.8k | interface_to_package: &HashMap<InterfaceId, (&PackageName, &String, Span, &Vec<Stability>)>, |
3769 | 11.8k | resolve: &mut Resolve, |
3770 | 11.8k | parent_pkg_id: &PackageId, |
3771 | 11.8k | ) -> ResolveResult<()> { |
3772 | 14.2k | for (unresolved_iface_id, unresolved_iface) in unresolved.interfaces.iter() { |
3773 | 3.53k | let (pkg_name, interface, span, stabilities) = |
3774 | 14.2k | match interface_to_package.get(&unresolved_iface_id) { |
3775 | 3.53k | Some(items) => *items, |
3776 | | // All foreign interfaces are defined first, so the first one |
3777 | | // which is defined in a non-foreign document means that all |
3778 | | // further interfaces will be non-foreign as well. |
3779 | 10.6k | None => break, |
3780 | | }; |
3781 | | |
3782 | 3.53k | let pkgid = resolve |
3783 | 3.53k | .package_names |
3784 | 3.53k | .get(pkg_name) |
3785 | 3.53k | .copied() |
3786 | 3.53k | .ok_or_else(|| { |
3787 | 0 | ResolveError::from(ResolveErrorKind::PackageNotFound { |
3788 | 0 | span, |
3789 | 0 | requested: pkg_name.clone(), |
3790 | 0 | known: resolve.package_names.keys().cloned().collect(), |
3791 | 0 | }) |
3792 | 0 | })?; |
3793 | | |
3794 | | // Functions can't be imported so this should be empty. |
3795 | 3.53k | assert!(unresolved_iface.functions.is_empty()); |
3796 | | |
3797 | 3.53k | let pkg = &resolve.packages[pkgid]; |
3798 | 3.53k | let iface_span = unresolved_iface.span; |
3799 | | |
3800 | 3.53k | let mut enabled = false; |
3801 | 3.53k | for stability in stabilities { |
3802 | 3.53k | if resolve.include_stability(stability, parent_pkg_id, iface_span)? { |
3803 | 3.53k | enabled = true; |
3804 | 3.53k | break; |
3805 | 0 | } |
3806 | | } |
3807 | | |
3808 | 3.53k | if !enabled { |
3809 | 0 | self.interfaces.push(None); |
3810 | 0 | continue; |
3811 | 3.53k | } |
3812 | | |
3813 | 3.53k | let iface_id = pkg.interfaces.get(interface).copied().ok_or_else(|| { |
3814 | 0 | ResolveError::from(ResolveErrorKind::InterfaceNotFound { |
3815 | 0 | span: iface_span, |
3816 | 0 | requested: interface.to_string(), |
3817 | 0 | package: pkg.name.clone(), |
3818 | 0 | }) |
3819 | 0 | })?; |
3820 | 3.53k | assert_eq!(self.interfaces.len(), unresolved_iface_id.index()); |
3821 | 3.53k | self.interfaces.push(Some(iface_id)); |
3822 | | } |
3823 | 44.1k | for (id, _) in unresolved.interfaces.iter().skip(self.interfaces.len()) { |
3824 | 44.1k | assert!( |
3825 | 44.1k | interface_to_package.get(&id).is_none(), |
3826 | | "found foreign interface after local interface" |
3827 | | ); |
3828 | | } |
3829 | 11.8k | Ok(()) |
3830 | 11.8k | } |
3831 | | |
3832 | 11.8k | fn process_foreign_worlds( |
3833 | 11.8k | &mut self, |
3834 | 11.8k | unresolved: &UnresolvedPackage, |
3835 | 11.8k | world_to_package: &HashMap<WorldId, (&PackageName, &String, Span, &Vec<Stability>)>, |
3836 | 11.8k | resolve: &mut Resolve, |
3837 | 11.8k | parent_pkg_id: &PackageId, |
3838 | 11.8k | ) -> ResolveResult<()> { |
3839 | 11.8k | for (unresolved_world_id, unresolved_world) in unresolved.worlds.iter() { |
3840 | 0 | let (pkg_name, world, span, stabilities) = |
3841 | 8.19k | match world_to_package.get(&unresolved_world_id) { |
3842 | 0 | Some(items) => *items, |
3843 | | // Same as above, all worlds are foreign until we find a |
3844 | | // non-foreign one. |
3845 | 8.19k | None => break, |
3846 | | }; |
3847 | | |
3848 | 0 | let pkgid = resolve |
3849 | 0 | .package_names |
3850 | 0 | .get(pkg_name) |
3851 | 0 | .copied() |
3852 | 0 | .ok_or_else(|| { |
3853 | 0 | ResolveError::from(ResolveErrorKind::PackageNotFound { |
3854 | 0 | span, |
3855 | 0 | requested: pkg_name.clone(), |
3856 | 0 | known: resolve.package_names.keys().cloned().collect(), |
3857 | 0 | }) |
3858 | 0 | })?; |
3859 | 0 | let pkg = &resolve.packages[pkgid]; |
3860 | 0 | let world_span = unresolved_world.span; |
3861 | | |
3862 | 0 | let mut enabled = false; |
3863 | 0 | for stability in stabilities { |
3864 | 0 | if resolve.include_stability(stability, parent_pkg_id, world_span)? { |
3865 | 0 | enabled = true; |
3866 | 0 | break; |
3867 | 0 | } |
3868 | | } |
3869 | | |
3870 | 0 | if !enabled { |
3871 | 0 | self.worlds.push(None); |
3872 | 0 | continue; |
3873 | 0 | } |
3874 | | |
3875 | 0 | let world_id = pkg.worlds.get(world).copied().ok_or_else(|| { |
3876 | 0 | ResolveError::from(ResolveErrorKind::WorldNotFound { |
3877 | 0 | span: world_span, |
3878 | 0 | requested: world.to_string(), |
3879 | 0 | package: pkg.name.clone(), |
3880 | 0 | }) |
3881 | 0 | })?; |
3882 | 0 | assert_eq!(self.worlds.len(), unresolved_world_id.index()); |
3883 | 0 | self.worlds.push(Some(world_id)); |
3884 | | } |
3885 | 16.3k | for (id, _) in unresolved.worlds.iter().skip(self.worlds.len()) { |
3886 | 16.3k | assert!( |
3887 | 16.3k | world_to_package.get(&id).is_none(), |
3888 | | "found foreign world after local world" |
3889 | | ); |
3890 | | } |
3891 | 11.8k | Ok(()) |
3892 | 11.8k | } |
3893 | | |
3894 | 11.8k | fn process_foreign_types( |
3895 | 11.8k | &mut self, |
3896 | 11.8k | unresolved: &UnresolvedPackage, |
3897 | 11.8k | pkgid: PackageId, |
3898 | 11.8k | resolve: &mut Resolve, |
3899 | 11.8k | ) -> ResolveResult<()> { |
3900 | 11.8k | for (unresolved_type_id, unresolved_ty) in unresolved.types.iter() { |
3901 | | // All "Unknown" types should appear first so once we're no longer |
3902 | | // in unknown territory it's package-defined types so break out of |
3903 | | // this loop. |
3904 | 10.8k | match unresolved_ty.kind { |
3905 | 2.49k | TypeDefKind::Unknown => {} |
3906 | 8.34k | _ => break, |
3907 | | } |
3908 | | |
3909 | 2.49k | let span = unresolved_ty.span; |
3910 | 2.49k | if !resolve.include_stability(&unresolved_ty.stability, &pkgid, span)? { |
3911 | 0 | self.types.push(None); |
3912 | 0 | continue; |
3913 | 2.49k | } |
3914 | | |
3915 | 2.49k | let unresolved_iface_id = match unresolved_ty.owner { |
3916 | 2.49k | TypeOwner::Interface(id) => id, |
3917 | 0 | _ => unreachable!(), |
3918 | | }; |
3919 | 2.49k | let iface_id = self.map_interface(unresolved_iface_id, Default::default())?; |
3920 | 2.49k | let name = unresolved_ty.name.as_ref().unwrap(); |
3921 | 2.49k | let span = unresolved.unknown_type_spans[unresolved_type_id.index()]; |
3922 | 2.49k | let type_id = *resolve.interfaces[iface_id] |
3923 | 2.49k | .types |
3924 | 2.49k | .get(name) |
3925 | 2.49k | .ok_or_else(|| { |
3926 | 0 | ResolveError::new_semantic( |
3927 | 0 | span, |
3928 | 0 | format!("type `{name}` not defined in interface"), |
3929 | | ) |
3930 | 0 | })?; |
3931 | 2.49k | assert_eq!(self.types.len(), unresolved_type_id.index()); |
3932 | 2.49k | self.types.push(Some(type_id)); |
3933 | | } |
3934 | 120k | for (_, ty) in unresolved.types.iter().skip(self.types.len()) { |
3935 | 120k | if let TypeDefKind::Unknown = ty.kind { |
3936 | 0 | panic!("unknown type after defined type"); |
3937 | 120k | } |
3938 | | } |
3939 | 11.8k | Ok(()) |
3940 | 11.8k | } |
3941 | | |
3942 | 159k | fn update_typedef( |
3943 | 159k | &mut self, |
3944 | 159k | resolve: &mut Resolve, |
3945 | 159k | ty: &mut TypeDef, |
3946 | 159k | span: Span, |
3947 | 159k | ) -> ResolveResult<()> { |
3948 | | // NB: note that `ty.owner` is not updated here since interfaces |
3949 | | // haven't been mapped yet and that's done in a separate step. |
3950 | | use crate::TypeDefKind::*; |
3951 | 5.33k | match &mut ty.kind { |
3952 | 1.39k | Handle(handle) => match handle { |
3953 | 1.22k | crate::Handle::Own(ty) | crate::Handle::Borrow(ty) => { |
3954 | 1.39k | self.update_type_id(ty, span)? |
3955 | | } |
3956 | | }, |
3957 | 2.74k | Resource => {} |
3958 | 3.87k | Record(r) => { |
3959 | 12.2k | for field in r.fields.iter_mut() { |
3960 | 12.2k | self.update_ty(resolve, &mut field.ty, field.span)? |
3961 | | } |
3962 | | } |
3963 | 40.0k | Tuple(t) => { |
3964 | 134k | for ty in t.types.iter_mut() { |
3965 | 134k | self.update_ty(resolve, ty, span)?; |
3966 | | } |
3967 | | } |
3968 | 5.41k | Variant(v) => { |
3969 | 19.4k | for case in v.cases.iter_mut() { |
3970 | 19.4k | if let Some(t) = &mut case.ty { |
3971 | 15.9k | self.update_ty(resolve, t, span)?; |
3972 | 3.44k | } |
3973 | | } |
3974 | | } |
3975 | 20.3k | Option(t) |
3976 | 8.85k | | List(t, ..) |
3977 | 3.85k | | FixedLengthList(t, ..) |
3978 | 4.76k | | Future(Some(t)) |
3979 | 44.2k | | Stream(Some(t)) => self.update_ty(resolve, t, span)?, |
3980 | 0 | Map(k, v) => { |
3981 | 0 | self.update_ty(resolve, k, span)?; |
3982 | 0 | self.update_ty(resolve, v, span)?; |
3983 | | } |
3984 | 16.8k | Result(r) => { |
3985 | 16.8k | if let Some(ty) = &mut r.ok { |
3986 | 13.6k | self.update_ty(resolve, ty, span)?; |
3987 | 3.21k | } |
3988 | 16.8k | if let Some(ty) = &mut r.err { |
3989 | 13.5k | self.update_ty(resolve, ty, span)?; |
3990 | 3.28k | } |
3991 | | } |
3992 | | |
3993 | | // Note that `update_ty` is specifically not used here as typedefs |
3994 | | // because for the `type a = b` form that doesn't force `a` to be a |
3995 | | // handle type if `b` is a resource type, instead `a` is |
3996 | | // simultaneously usable as a resource and a handle type |
3997 | 17.6k | Type(crate::Type::Id(id)) => self.update_type_id(id, span)?, |
3998 | 1.93k | Type(_) => {} |
3999 | | |
4000 | | // nothing to do for these as they're just names or empty |
4001 | 25.5k | Flags(_) | Enum(_) | Future(None) | Stream(None) => {} |
4002 | | |
4003 | 0 | Unknown => unreachable!(), |
4004 | | } |
4005 | | |
4006 | 159k | Ok(()) |
4007 | 159k | } |
4008 | | |
4009 | 326k | fn update_ty(&mut self, resolve: &mut Resolve, ty: &mut Type, span: Span) -> ResolveResult<()> { |
4010 | 326k | let id = match ty { |
4011 | 123k | Type::Id(id) => id, |
4012 | 202k | _ => return Ok(()), |
4013 | | }; |
4014 | 123k | self.update_type_id(id, span)?; |
4015 | | |
4016 | | // If `id` points to a `Resource` type then this means that what was |
4017 | | // just discovered was a reference to what will implicitly become an |
4018 | | // `own<T>` handle. This `own` handle is implicitly allocated here |
4019 | | // and handled during the merging process. |
4020 | 123k | let mut cur = *id; |
4021 | 123k | let points_to_resource = loop { |
4022 | 124k | match resolve.types[cur].kind { |
4023 | 655 | TypeDefKind::Type(Type::Id(id)) => cur = id, |
4024 | 1.26k | TypeDefKind::Resource => break true, |
4025 | 122k | _ => break false, |
4026 | | } |
4027 | | }; |
4028 | | |
4029 | 123k | if points_to_resource { |
4030 | 1.26k | *id = *self.own_handles.entry(*id).or_insert_with(|| { |
4031 | 658 | resolve.types.alloc(TypeDef { |
4032 | 658 | name: None, |
4033 | 658 | owner: TypeOwner::None, |
4034 | 658 | kind: TypeDefKind::Handle(Handle::Own(*id)), |
4035 | 658 | docs: Default::default(), |
4036 | 658 | stability: Default::default(), |
4037 | 658 | span: Default::default(), |
4038 | 658 | external_id: None, |
4039 | 658 | }) |
4040 | 658 | }); |
4041 | 122k | } |
4042 | 123k | Ok(()) |
4043 | 326k | } |
4044 | | |
4045 | 196k | fn update_type_id(&self, id: &mut TypeId, span: Span) -> ResolveResult<()> { |
4046 | 196k | *id = self.map_type(*id, span)?; |
4047 | 196k | Ok(()) |
4048 | 196k | } |
4049 | | |
4050 | 47.8k | fn update_interface( |
4051 | 47.8k | &mut self, |
4052 | 47.8k | resolve: &mut Resolve, |
4053 | 47.8k | iface: &mut Interface, |
4054 | 47.8k | ) -> ResolveResult<()> { |
4055 | 48.6k | iface.types.retain(|_, ty| self.types[ty.index()].is_some()); |
4056 | 47.8k | let iface_pkg_id = iface.package.as_ref().unwrap_or_else(|| { |
4057 | 0 | panic!( |
4058 | | "unexpectedly missing package on interface [{}]", |
4059 | 0 | iface |
4060 | 0 | .name |
4061 | 0 | .as_ref() |
4062 | 0 | .map(String::as_str) |
4063 | 0 | .unwrap_or("<unknown>"), |
4064 | | ) |
4065 | | }); |
4066 | | |
4067 | | // NB: note that `iface.doc` is not updated here since interfaces |
4068 | | // haven't been mapped yet and that's done in a separate step. |
4069 | 48.6k | for (_name, ty) in iface.types.iter_mut() { |
4070 | 48.6k | self.update_type_id(ty, iface.span)?; |
4071 | | } |
4072 | 47.8k | for (_, func) in iface.functions.iter_mut() { |
4073 | 18.5k | let span = func.span; |
4074 | 18.5k | if !resolve.include_stability(&func.stability, iface_pkg_id, span)? { |
4075 | 0 | continue; |
4076 | 18.5k | } |
4077 | 18.5k | self.update_function(resolve, func, span)? |
4078 | | } |
4079 | | |
4080 | | // Filter out all of the existing functions in interface which fail the |
4081 | | // `include_stability()` check, as they shouldn't be available. |
4082 | 47.8k | for (name, func) in mem::take(&mut iface.functions) { |
4083 | 18.5k | if resolve.include_stability(&func.stability, iface_pkg_id, func.span)? { |
4084 | 18.5k | iface.functions.insert(name, func); |
4085 | 18.5k | } |
4086 | | } |
4087 | | |
4088 | 47.8k | Ok(()) |
4089 | 47.8k | } |
4090 | | |
4091 | 26.0k | fn update_function( |
4092 | 26.0k | &mut self, |
4093 | 26.0k | resolve: &mut Resolve, |
4094 | 26.0k | func: &mut Function, |
4095 | 26.0k | span: Span, |
4096 | 26.0k | ) -> ResolveResult<()> { |
4097 | 26.0k | if let Some(id) = func.kind.resource_mut() { |
4098 | 4.89k | self.update_type_id(id, span)?; |
4099 | 21.1k | } |
4100 | 72.5k | for param in func.params.iter_mut() { |
4101 | 72.5k | self.update_ty(resolve, &mut param.ty, span)?; |
4102 | | } |
4103 | 26.0k | if let Some(ty) = &mut func.result { |
4104 | 19.3k | self.update_ty(resolve, ty, span)?; |
4105 | 6.71k | } |
4106 | | |
4107 | 26.0k | if let Some(ty) = &func.result { |
4108 | 19.3k | if self.type_has_borrow(resolve, ty) { |
4109 | 0 | return Err(ResolveError::new_semantic( |
4110 | 0 | span, |
4111 | 0 | format!( |
4112 | 0 | "function `{}` returns a type which contains a `borrow<T>` which is not supported", |
4113 | 0 | func.name, |
4114 | 0 | ), |
4115 | 0 | )); |
4116 | 19.3k | } |
4117 | 6.71k | } |
4118 | | |
4119 | 26.0k | Ok(()) |
4120 | 26.0k | } |
4121 | | |
4122 | 16.3k | fn update_world( |
4123 | 16.3k | &mut self, |
4124 | 16.3k | world: &mut World, |
4125 | 16.3k | resolve: &mut Resolve, |
4126 | 16.3k | pkg_id: &PackageId, |
4127 | 16.3k | ) -> ResolveResult<()> { |
4128 | | // Rewrite imports/exports with their updated versions. Note that this |
4129 | | // may involve updating the key of the imports/exports maps so this |
4130 | | // starts by emptying them out and then everything is re-inserted. |
4131 | 16.3k | let imports = mem::take(&mut world.imports).into_iter().map(|p| (p, true)); |
4132 | 16.3k | let exports = mem::take(&mut world.exports) |
4133 | 16.3k | .into_iter() |
4134 | 30.3k | .map(|p| (p, false)); |
4135 | 43.1k | for ((mut name, mut item), import) in imports.chain(exports) { |
4136 | 43.1k | let span = item.span(); |
4137 | | // Update the `id` eagerly here so `item.stability(..)` below |
4138 | | // works. |
4139 | 43.1k | if let WorldItem::Type { id, .. } = &mut item { |
4140 | 5.97k | *id = self.map_type(*id, span)?; |
4141 | 37.2k | } |
4142 | 43.1k | let stability = item.stability(resolve); |
4143 | 43.1k | if !resolve.include_stability(stability, pkg_id, span)? { |
4144 | 0 | continue; |
4145 | 43.1k | } |
4146 | 43.1k | self.update_world_key(&mut name, span)?; |
4147 | 43.1k | match &mut item { |
4148 | 32.4k | WorldItem::Interface { id, .. } => { |
4149 | 32.4k | *id = self.map_interface(*id, span)?; |
4150 | | } |
4151 | 4.76k | WorldItem::Function(f) => { |
4152 | 4.76k | self.update_function(resolve, f, span)?; |
4153 | | } |
4154 | 5.97k | WorldItem::Type { .. } => { |
4155 | 5.97k | // already mapped above |
4156 | 5.97k | } |
4157 | | } |
4158 | | |
4159 | 43.1k | let dst = if import { |
4160 | 12.8k | &mut world.imports |
4161 | | } else { |
4162 | 30.3k | &mut world.exports |
4163 | | }; |
4164 | 43.1k | let prev = dst.insert(name, item); |
4165 | 43.1k | assert!(prev.is_none()); |
4166 | | } |
4167 | | |
4168 | 16.3k | Ok(()) |
4169 | 16.3k | } |
4170 | | |
4171 | 16.2k | fn process_world_includes( |
4172 | 16.2k | &self, |
4173 | 16.2k | id: WorldId, |
4174 | 16.2k | resolve: &mut Resolve, |
4175 | 16.2k | pkg_id: &PackageId, |
4176 | 16.2k | ) -> ResolveResult<()> { |
4177 | 16.2k | let world = &mut resolve.worlds[id]; |
4178 | | // Resolve all `include` statements of the world which will add more |
4179 | | // entries to the imports/exports list for this world. |
4180 | 16.2k | let includes = mem::take(&mut world.includes); |
4181 | 16.2k | for include in includes { |
4182 | 0 | if !resolve.include_stability(&include.stability, pkg_id, include.span)? { |
4183 | 0 | continue; |
4184 | 0 | } |
4185 | 0 | self.resolve_include( |
4186 | 0 | id, |
4187 | 0 | include.id, |
4188 | 0 | &include.names, |
4189 | 0 | include.span, |
4190 | 0 | pkg_id, |
4191 | 0 | resolve, |
4192 | 0 | )?; |
4193 | | } |
4194 | | |
4195 | | // Validate that all names in the imports/exports are still strongly unique. |
4196 | 16.2k | Self::validate_world_strongly_unique_names(resolve, id)?; |
4197 | | |
4198 | | // Every setter must have a corresponding getter. This is checked here |
4199 | | // rather than at parse time since it's possible for a getter to come |
4200 | | // from an included WIT world. |
4201 | 16.2k | let world = &mut resolve.worlds[id]; |
4202 | 16.2k | place_setters_after_getters(&mut world.imports, world_item_func); |
4203 | 16.2k | place_setters_after_getters(&mut world.exports, world_item_func); |
4204 | 32.5k | for (items, item_type) in [(&world.imports, "import"), (&world.exports, "export")] { |
4205 | 42.8k | for item in items.values() { |
4206 | 42.8k | if let Some(f) = world_item_func(item) |
4207 | 4.75k | && f.kind.accessor() == Some(AccessorKind::Setter) |
4208 | | { |
4209 | 0 | let getter = f.name.replacen("[set]", "[get]", 1); |
4210 | 0 | if !items.contains_key(&WorldKey::Name(getter.clone())) { |
4211 | 0 | return Err(ResolveError::new_semantic( |
4212 | 0 | item.span(), |
4213 | 0 | format!( |
4214 | 0 | "{item_type} `{}` in world `{}` has no corresponding getter `{getter}`", |
4215 | 0 | f.name, world.name, |
4216 | 0 | ), |
4217 | 0 | )); |
4218 | 0 | } |
4219 | 42.8k | } |
4220 | | } |
4221 | | } |
4222 | | |
4223 | 16.2k | Ok(()) |
4224 | 16.2k | } |
4225 | | |
4226 | | /// Validates that a world's imports and exports are each strongly unique. |
4227 | 16.2k | fn validate_world_strongly_unique_names( |
4228 | 16.2k | resolve: &Resolve, |
4229 | 16.2k | world_id: WorldId, |
4230 | 16.2k | ) -> ResolveResult<()> { |
4231 | | use wasmparser::names::ComponentName; |
4232 | | |
4233 | 16.2k | let world = &resolve.worlds[world_id]; |
4234 | | |
4235 | 16.2k | let validate_names = |
4236 | 32.5k | |items: &IndexMap<WorldKey, WorldItem>, item_type: &str| -> ResolveResult<()> { |
4237 | 32.5k | let mut seen: HashMap<ComponentName, &str> = HashMap::new(); |
4238 | | |
4239 | 42.8k | for key in items.keys() { |
4240 | | // Only `WorldKey::Name` variants can conflict with each |
4241 | | // other here because interfaces are keyed by ID. |
4242 | 42.8k | let WorldKey::Name(name) = key else { |
4243 | 1.23k | continue; |
4244 | | }; |
4245 | 41.6k | let Ok(component_name) = crate::parse_component_name(name) else { |
4246 | 0 | continue; |
4247 | | }; |
4248 | | |
4249 | 41.6k | if let Some(existing_name) = seen.get(&component_name) { |
4250 | | // TODO: `WorldKey::Name` does not carry a `Span`, so we |
4251 | | // cannot point at the conflicting item. Add a span to |
4252 | | // `WorldKey::Name` to improve this error. |
4253 | 0 | return Err(ResolveError::new_semantic( |
4254 | 0 | Span::default(), |
4255 | 0 | format!( |
4256 | 0 | "{item_type} `{name}` in world `{}` conflicts with \ |
4257 | 0 | {item_type} `{existing_name}` \ |
4258 | 0 | (names must be strongly unique)", |
4259 | 0 | world.name, |
4260 | 0 | ), |
4261 | 0 | )); |
4262 | 41.6k | } |
4263 | | |
4264 | 41.6k | seen.insert(component_name, name); |
4265 | | } |
4266 | | |
4267 | 32.5k | Ok(()) |
4268 | 32.5k | }; |
4269 | | |
4270 | 16.2k | validate_names(&world.imports, "import")?; |
4271 | 16.2k | validate_names(&world.exports, "export")?; |
4272 | | |
4273 | 16.2k | Ok(()) |
4274 | 16.2k | } |
4275 | | |
4276 | 65.6k | fn update_world_key(&self, key: &mut WorldKey, span: Span) -> ResolveResult<()> { |
4277 | 65.6k | match key { |
4278 | 63.1k | WorldKey::Name(_) => {} |
4279 | 2.54k | WorldKey::Interface(id) => { |
4280 | 2.54k | *id = self.map_interface(*id, span)?; |
4281 | | } |
4282 | | } |
4283 | 65.6k | Ok(()) |
4284 | 65.6k | } |
4285 | | |
4286 | 0 | fn resolve_include( |
4287 | 0 | &self, |
4288 | 0 | id: WorldId, |
4289 | 0 | include_world_id_orig: WorldId, |
4290 | 0 | names: &[IncludeName], |
4291 | 0 | span: Span, |
4292 | 0 | pkg_id: &PackageId, |
4293 | 0 | resolve: &mut Resolve, |
4294 | 0 | ) -> ResolveResult<()> { |
4295 | 0 | let world = &resolve.worlds[id]; |
4296 | 0 | let include_world_id = self.map_world(include_world_id_orig, span)?; |
4297 | 0 | let include_world = resolve.worlds[include_world_id].clone(); |
4298 | 0 | let mut names_ = names.to_owned(); |
4299 | 0 | let is_external_include = world.package != include_world.package; |
4300 | | |
4301 | | // remove all imports and exports that match the names we're including |
4302 | 0 | for import in include_world.imports.iter() { |
4303 | 0 | self.remove_matching_name(import, &mut names_); |
4304 | 0 | } |
4305 | 0 | for export in include_world.exports.iter() { |
4306 | 0 | self.remove_matching_name(export, &mut names_); |
4307 | 0 | } |
4308 | 0 | if !names_.is_empty() { |
4309 | 0 | return Err(ResolveError::new_semantic( |
4310 | 0 | span, |
4311 | 0 | format!( |
4312 | 0 | "no import or export kebab-name `{}`. Note that an ID does not support renaming", |
4313 | 0 | names_[0].name |
4314 | 0 | ), |
4315 | 0 | )); |
4316 | 0 | } |
4317 | | |
4318 | 0 | let mut maps = Default::default(); |
4319 | 0 | let mut cloner = clone::Cloner::new( |
4320 | 0 | resolve, |
4321 | 0 | &mut maps, |
4322 | 0 | TypeOwner::World(if is_external_include { |
4323 | 0 | include_world_id |
4324 | | } else { |
4325 | 0 | include_world_id |
4326 | | // include_world_id_orig |
4327 | | }), |
4328 | 0 | TypeOwner::World(id), |
4329 | | ); |
4330 | 0 | cloner.new_package = Some(*pkg_id); |
4331 | | |
4332 | | // copy the imports and exports from the included world into the current world |
4333 | 0 | for import in include_world.imports.iter() { |
4334 | 0 | self.resolve_include_item( |
4335 | 0 | &mut cloner, |
4336 | 0 | names, |
4337 | 0 | |resolve| &mut resolve.worlds[id].imports, |
4338 | 0 | import, |
4339 | 0 | span, |
4340 | 0 | "import", |
4341 | 0 | is_external_include, |
4342 | 0 | )?; |
4343 | | } |
4344 | | |
4345 | 0 | for export in include_world.exports.iter() { |
4346 | 0 | self.resolve_include_item( |
4347 | 0 | &mut cloner, |
4348 | 0 | names, |
4349 | 0 | |resolve| &mut resolve.worlds[id].exports, |
4350 | 0 | export, |
4351 | 0 | span, |
4352 | 0 | "export", |
4353 | 0 | is_external_include, |
4354 | 0 | )?; |
4355 | | } |
4356 | 0 | Ok(()) |
4357 | 0 | } |
4358 | | |
4359 | 0 | fn resolve_include_item( |
4360 | 0 | &self, |
4361 | 0 | cloner: &mut clone::Cloner<'_>, |
4362 | 0 | names: &[IncludeName], |
4363 | 0 | get_items: impl Fn(&mut Resolve) -> &mut IndexMap<WorldKey, WorldItem>, |
4364 | 0 | item: (&WorldKey, &WorldItem), |
4365 | 0 | span: Span, |
4366 | 0 | item_type: &str, |
4367 | 0 | is_external_include: bool, |
4368 | 0 | ) -> ResolveResult<()> { |
4369 | 0 | match item.0 { |
4370 | 0 | WorldKey::Name(n) => { |
4371 | 0 | let n = names |
4372 | 0 | .into_iter() |
4373 | 0 | .find_map(|include_name| rename(n, include_name)) Unexecuted instantiation: <wit_parser::resolve::Remap>::resolve_include_item::<<wit_parser::resolve::Remap>::resolve_include::{closure#0}>::{closure#0}Unexecuted instantiation: <wit_parser::resolve::Remap>::resolve_include_item::<<wit_parser::resolve::Remap>::resolve_include::{closure#1}>::{closure#0} |
4374 | 0 | .unwrap_or(n.clone()); |
4375 | | |
4376 | | // When the `with` option to the `include` directive is |
4377 | | // specified and is used to rename a function that means that |
4378 | | // the function's own original name needs to be updated, so |
4379 | | // reflect the change not only in the world key but additionally |
4380 | | // in the function itself. |
4381 | 0 | let mut new_item = item.1.clone(); |
4382 | 0 | let key = WorldKey::Name(n.clone()); |
4383 | 0 | cloner.world_item(&key, &mut new_item); |
4384 | 0 | match &mut new_item { |
4385 | 0 | WorldItem::Function(f) => f.name = n.clone(), |
4386 | 0 | WorldItem::Type { id, .. } => cloner.resolve.types[*id].name = Some(n.clone()), |
4387 | 0 | WorldItem::Interface { .. } => {} |
4388 | | } |
4389 | | |
4390 | 0 | let prev = get_items(cloner.resolve).insert(key, new_item); |
4391 | 0 | if prev.is_some() { |
4392 | 0 | return Err(ResolveError::from(ResolveErrorKind::ItemShadowing { |
4393 | 0 | span, |
4394 | 0 | item_type: item_type.to_owned(), |
4395 | 0 | name: n, |
4396 | 0 | })); |
4397 | 0 | } |
4398 | | } |
4399 | 0 | key @ WorldKey::Interface(_) => { |
4400 | 0 | let prev = get_items(cloner.resolve) |
4401 | 0 | .entry(key.clone()) |
4402 | 0 | .or_insert(item.1.clone()); |
4403 | 0 | match (&item.1, prev) { |
4404 | | ( |
4405 | | WorldItem::Interface { |
4406 | 0 | id: aid, |
4407 | 0 | stability: astability, |
4408 | 0 | span: aspan, |
4409 | | .. |
4410 | | }, |
4411 | | WorldItem::Interface { |
4412 | 0 | id: bid, |
4413 | 0 | stability: bstability, |
4414 | | .. |
4415 | | }, |
4416 | | ) => { |
4417 | 0 | assert_eq!(*aid, *bid); |
4418 | 0 | merge_include_stability( |
4419 | 0 | astability, |
4420 | 0 | bstability, |
4421 | 0 | is_external_include, |
4422 | 0 | *aspan, |
4423 | 0 | )?; |
4424 | | } |
4425 | 0 | (WorldItem::Interface { .. }, _) => unreachable!(), |
4426 | 0 | (WorldItem::Function(_), _) => unreachable!(), |
4427 | 0 | (WorldItem::Type { .. }, _) => unreachable!(), |
4428 | | } |
4429 | | } |
4430 | | }; |
4431 | | |
4432 | 0 | Ok(()) |
4433 | 0 | } Unexecuted instantiation: <wit_parser::resolve::Remap>::resolve_include_item::<<wit_parser::resolve::Remap>::resolve_include::{closure#0}>Unexecuted instantiation: <wit_parser::resolve::Remap>::resolve_include_item::<<wit_parser::resolve::Remap>::resolve_include::{closure#1}> |
4434 | | |
4435 | 0 | fn remove_matching_name(&self, item: (&WorldKey, &WorldItem), names: &mut Vec<IncludeName>) { |
4436 | 0 | match item.0 { |
4437 | 0 | WorldKey::Name(n) => { |
4438 | 0 | names.retain(|name| rename(n, name).is_none()); |
4439 | | } |
4440 | 0 | _ => {} |
4441 | | } |
4442 | 0 | } |
4443 | | |
4444 | 56.1k | fn type_has_borrow(&mut self, resolve: &Resolve, ty: &Type) -> bool { |
4445 | 56.1k | let id = match ty { |
4446 | 23.3k | Type::Id(id) => *id, |
4447 | 32.8k | _ => return false, |
4448 | | }; |
4449 | | |
4450 | 23.3k | if let Some(Some(has_borrow)) = self.type_has_borrow.get(id.index()) { |
4451 | 2.78k | return *has_borrow; |
4452 | 20.5k | } |
4453 | | |
4454 | 20.5k | let result = self.typedef_has_borrow(resolve, &resolve.types[id]); |
4455 | 20.5k | if self.type_has_borrow.len() <= id.index() { |
4456 | 19.1k | self.type_has_borrow.resize(id.index() + 1, None); |
4457 | 19.1k | } |
4458 | 20.5k | self.type_has_borrow[id.index()] = Some(result); |
4459 | 20.5k | result |
4460 | 56.1k | } |
4461 | | |
4462 | 20.5k | fn typedef_has_borrow(&mut self, resolve: &Resolve, ty: &TypeDef) -> bool { |
4463 | 686 | match &ty.kind { |
4464 | 76 | TypeDefKind::Type(t) => self.type_has_borrow(resolve, t), |
4465 | 17 | TypeDefKind::Variant(v) => v |
4466 | 17 | .cases |
4467 | 17 | .iter() |
4468 | 81 | .filter_map(|case| case.ty.as_ref()) |
4469 | 73 | .any(|ty| self.type_has_borrow(resolve, ty)), |
4470 | 0 | TypeDefKind::Handle(Handle::Borrow(_)) => true, |
4471 | 686 | TypeDefKind::Handle(Handle::Own(_)) => false, |
4472 | 0 | TypeDefKind::Resource => false, |
4473 | 57 | TypeDefKind::Record(r) => r |
4474 | 57 | .fields |
4475 | 57 | .iter() |
4476 | 214 | .any(|case| self.type_has_borrow(resolve, &case.ty)), |
4477 | 17 | TypeDefKind::Flags(_) => false, |
4478 | 21.0k | TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.type_has_borrow(resolve, t)), |
4479 | 100 | TypeDefKind::Enum(_) => false, |
4480 | 1.81k | TypeDefKind::List(ty) |
4481 | 1.00k | | TypeDefKind::FixedLengthList(ty, ..) |
4482 | 787 | | TypeDefKind::Future(Some(ty)) |
4483 | 1.74k | | TypeDefKind::Stream(Some(ty)) |
4484 | 7.94k | | TypeDefKind::Option(ty) => self.type_has_borrow(resolve, ty), |
4485 | 0 | TypeDefKind::Map(k, v) => { |
4486 | 0 | self.type_has_borrow(resolve, k) || self.type_has_borrow(resolve, v) |
4487 | | } |
4488 | 4.52k | TypeDefKind::Result(r) => [&r.ok, &r.err] |
4489 | 4.52k | .iter() |
4490 | 9.04k | .filter_map(|t| t.as_ref()) |
4491 | 7.40k | .any(|t| self.type_has_borrow(resolve, t)), |
4492 | 105 | TypeDefKind::Future(None) | TypeDefKind::Stream(None) => false, |
4493 | 0 | TypeDefKind::Unknown => unreachable!(), |
4494 | | } |
4495 | 20.5k | } |
4496 | | } |
4497 | | |
4498 | | struct MergeMap<'a> { |
4499 | | /// A map of package ids in `from` to those in `into` for those that are |
4500 | | /// found to be equivalent. |
4501 | | package_map: HashMap<PackageId, PackageId>, |
4502 | | |
4503 | | /// A map of interface ids in `from` to those in `into` for those that are |
4504 | | /// found to be equivalent. |
4505 | | interface_map: HashMap<InterfaceId, InterfaceId>, |
4506 | | |
4507 | | /// A map of type ids in `from` to those in `into` for those that are |
4508 | | /// found to be equivalent. |
4509 | | type_map: HashMap<TypeId, TypeId>, |
4510 | | |
4511 | | /// A map of world ids in `from` to those in `into` for those that are |
4512 | | /// found to be equivalent. |
4513 | | world_map: HashMap<WorldId, WorldId>, |
4514 | | |
4515 | | /// A list of documents that need to be added to packages in `into`. |
4516 | | /// |
4517 | | /// The elements here are: |
4518 | | /// |
4519 | | /// * The name of the interface/world |
4520 | | /// * The ID within `into` of the package being added to |
4521 | | /// * The ID within `from` of the item being added. |
4522 | | interfaces_to_add: Vec<(String, PackageId, InterfaceId)>, |
4523 | | worlds_to_add: Vec<(String, PackageId, WorldId)>, |
4524 | | |
4525 | | /// Which `Resolve` is being merged from. |
4526 | | from: &'a Resolve, |
4527 | | |
4528 | | /// Which `Resolve` is being merged into. |
4529 | | into: &'a Resolve, |
4530 | | } |
4531 | | |
4532 | | impl<'a> MergeMap<'a> { |
4533 | 6.55k | fn new(from: &'a Resolve, into: &'a Resolve) -> MergeMap<'a> { |
4534 | 6.55k | MergeMap { |
4535 | 6.55k | package_map: Default::default(), |
4536 | 6.55k | interface_map: Default::default(), |
4537 | 6.55k | type_map: Default::default(), |
4538 | 6.55k | world_map: Default::default(), |
4539 | 6.55k | interfaces_to_add: Default::default(), |
4540 | 6.55k | worlds_to_add: Default::default(), |
4541 | 6.55k | from, |
4542 | 6.55k | into, |
4543 | 6.55k | } |
4544 | 6.55k | } |
4545 | | |
4546 | 6.55k | fn build(&mut self) -> anyhow::Result<()> { |
4547 | 11.0k | for from_id in self.from.topological_packages() { |
4548 | 11.0k | let from = &self.from.packages[from_id]; |
4549 | 11.0k | let into_id = match self.into.package_names.get(&from.name) { |
4550 | 35 | Some(id) => *id, |
4551 | | |
4552 | | // This package, according to its name and url, is not present |
4553 | | // in `self` so it needs to get added below. |
4554 | | None => { |
4555 | 11.0k | log::trace!("adding unique package {}", from.name); |
4556 | 11.0k | continue; |
4557 | | } |
4558 | | }; |
4559 | 35 | log::trace!("merging duplicate package {}", from.name); |
4560 | | |
4561 | 35 | self.build_package(from_id, into_id).with_context(|| { |
4562 | 0 | format!("failed to merge package `{}` into existing copy", from.name) |
4563 | 0 | })?; |
4564 | | } |
4565 | | |
4566 | 6.55k | Ok(()) |
4567 | 6.55k | } |
4568 | | |
4569 | 35 | fn build_package(&mut self, from_id: PackageId, into_id: PackageId) -> anyhow::Result<()> { |
4570 | 35 | let prev = self.package_map.insert(from_id, into_id); |
4571 | 35 | assert!(prev.is_none()); |
4572 | | |
4573 | 35 | let from = &self.from.packages[from_id]; |
4574 | 35 | let into = &self.into.packages[into_id]; |
4575 | | |
4576 | | // If an interface is present in `from_id` but not present in `into_id` |
4577 | | // then it can be copied over wholesale. That copy is scheduled to |
4578 | | // happen within the `self.interfaces_to_add` list. |
4579 | 38 | for (name, from_interface_id) in from.interfaces.iter() { |
4580 | 38 | let into_interface_id = match into.interfaces.get(name) { |
4581 | 35 | Some(id) => *id, |
4582 | | None => { |
4583 | 3 | log::trace!("adding unique interface {name}"); |
4584 | 3 | self.interfaces_to_add |
4585 | 3 | .push((name.clone(), into_id, *from_interface_id)); |
4586 | 3 | continue; |
4587 | | } |
4588 | | }; |
4589 | | |
4590 | 35 | log::trace!("merging duplicate interfaces {name}"); |
4591 | 35 | self.build_interface(*from_interface_id, into_interface_id) |
4592 | 35 | .with_context(|| format!("failed to merge interface `{name}`"))?; |
4593 | | } |
4594 | | |
4595 | 35 | for (name, from_world_id) in from.worlds.iter() { |
4596 | 0 | let into_world_id = match into.worlds.get(name) { |
4597 | 0 | Some(id) => *id, |
4598 | | None => { |
4599 | 0 | log::trace!("adding unique world {name}"); |
4600 | 0 | self.worlds_to_add |
4601 | 0 | .push((name.clone(), into_id, *from_world_id)); |
4602 | 0 | continue; |
4603 | | } |
4604 | | }; |
4605 | | |
4606 | 0 | log::trace!("merging duplicate worlds {name}"); |
4607 | 0 | self.build_world(*from_world_id, into_world_id) |
4608 | 0 | .with_context(|| format!("failed to merge world `{name}`"))?; |
4609 | | } |
4610 | | |
4611 | 35 | Ok(()) |
4612 | 35 | } |
4613 | | |
4614 | 35 | fn build_interface( |
4615 | 35 | &mut self, |
4616 | 35 | from_id: InterfaceId, |
4617 | 35 | into_id: InterfaceId, |
4618 | 35 | ) -> anyhow::Result<()> { |
4619 | 35 | let prev = self.interface_map.insert(from_id, into_id); |
4620 | 35 | assert!(prev.is_none()); |
4621 | | |
4622 | 35 | let from_interface = &self.from.interfaces[from_id]; |
4623 | 35 | let into_interface = &self.into.interfaces[into_id]; |
4624 | | |
4625 | | // When merging interfaces, types and functions that exist in both |
4626 | | // `from` and `into` must match structurally. Either side is allowed |
4627 | | // to have extra types or functions not present in the other, which |
4628 | | // enables commutative merging of partial views of the same |
4629 | | // interface. The only requirement is that the intersection of the |
4630 | | // two interfaces is compatible. |
4631 | | |
4632 | 119 | for (name, from_type_id) in from_interface.types.iter() { |
4633 | 119 | let into_type_id = match into_interface.types.get(name) { |
4634 | 119 | Some(id) => *id, |
4635 | | // Extra type in `from` not present in `into`; it will be |
4636 | | // moved as a new type and added to the interface later. |
4637 | 0 | None => continue, |
4638 | | }; |
4639 | 119 | let prev = self.type_map.insert(*from_type_id, into_type_id); |
4640 | 119 | assert!(prev.is_none()); |
4641 | | |
4642 | 119 | self.build_type_id(*from_type_id, into_type_id) |
4643 | 119 | .with_context(|| format!("mismatch in type `{name}`"))?; |
4644 | | } |
4645 | | |
4646 | 35 | for (name, from_func) in from_interface.functions.iter() { |
4647 | 19 | let into_func = match into_interface.functions.get(name) { |
4648 | 19 | Some(func) => func, |
4649 | | // Extra function in `from` not present in `into`; it will |
4650 | | // be added to the interface during the merge phase. |
4651 | 0 | None => continue, |
4652 | | }; |
4653 | 19 | self.build_function(from_func, into_func) |
4654 | 19 | .with_context(|| format!("mismatch in function `{name}`"))?; |
4655 | | } |
4656 | | |
4657 | 35 | Ok(()) |
4658 | 35 | } |
4659 | | |
4660 | 148 | fn build_type_id(&mut self, from_id: TypeId, into_id: TypeId) -> anyhow::Result<()> { |
4661 | | // FIXME: ideally the types should be "structurally |
4662 | | // equal" but that's not trivial to do in the face of |
4663 | | // resources. |
4664 | 148 | let _ = from_id; |
4665 | 148 | let _ = into_id; |
4666 | 148 | Ok(()) |
4667 | 148 | } |
4668 | | |
4669 | 80 | fn build_type(&mut self, from_ty: &Type, into_ty: &Type) -> anyhow::Result<()> { |
4670 | 80 | match (from_ty, into_ty) { |
4671 | 20 | (Type::Id(from), Type::Id(into)) => { |
4672 | 20 | self.build_type_id(*from, *into)?; |
4673 | | } |
4674 | 60 | (from, into) if from != into => bail!("different kinds of types"), |
4675 | 60 | _ => {} |
4676 | | } |
4677 | 80 | Ok(()) |
4678 | 80 | } |
4679 | | |
4680 | 19 | fn build_function(&mut self, from_func: &Function, into_func: &Function) -> anyhow::Result<()> { |
4681 | 19 | if from_func.name != into_func.name { |
4682 | 0 | bail!( |
4683 | | "different function names `{}` and `{}`", |
4684 | | from_func.name, |
4685 | | into_func.name |
4686 | | ); |
4687 | 19 | } |
4688 | 19 | match (&from_func.kind, &into_func.kind) { |
4689 | 1 | (FunctionKind::Freestanding, FunctionKind::Freestanding) => {} |
4690 | 9 | (FunctionKind::AsyncFreestanding, FunctionKind::AsyncFreestanding) => {} |
4691 | 0 | (FunctionKind::Getter, FunctionKind::Getter) => {} |
4692 | 0 | (FunctionKind::Setter, FunctionKind::Setter) => {} |
4693 | | |
4694 | 0 | (FunctionKind::Method(from), FunctionKind::Method(into)) |
4695 | 0 | | (FunctionKind::Static(from), FunctionKind::Static(into)) |
4696 | 7 | | (FunctionKind::AsyncMethod(from), FunctionKind::AsyncMethod(into)) |
4697 | 2 | | (FunctionKind::AsyncStatic(from), FunctionKind::AsyncStatic(into)) |
4698 | 0 | | (FunctionKind::MethodGetter(from), FunctionKind::MethodGetter(into)) |
4699 | 0 | | (FunctionKind::MethodSetter(from), FunctionKind::MethodSetter(into)) |
4700 | 0 | | (FunctionKind::StaticGetter(from), FunctionKind::StaticGetter(into)) |
4701 | 0 | | (FunctionKind::StaticSetter(from), FunctionKind::StaticSetter(into)) |
4702 | 0 | | (FunctionKind::Constructor(from), FunctionKind::Constructor(into)) => { |
4703 | 9 | self.build_type_id(*from, *into) |
4704 | 9 | .context("different function kind types")?; |
4705 | | } |
4706 | | |
4707 | | (FunctionKind::Method(_), _) |
4708 | | | (FunctionKind::Constructor(_), _) |
4709 | | | (FunctionKind::Static(_), _) |
4710 | | | (FunctionKind::Freestanding, _) |
4711 | | | (FunctionKind::AsyncFreestanding, _) |
4712 | | | (FunctionKind::AsyncMethod(_), _) |
4713 | | | (FunctionKind::AsyncStatic(_), _) |
4714 | | | (FunctionKind::Getter, _) |
4715 | | | (FunctionKind::Setter, _) |
4716 | | | (FunctionKind::MethodGetter(_), _) |
4717 | | | (FunctionKind::MethodSetter(_), _) |
4718 | | | (FunctionKind::StaticGetter(_), _) |
4719 | | | (FunctionKind::StaticSetter(_), _) => { |
4720 | 0 | bail!("different function kind types") |
4721 | | } |
4722 | | } |
4723 | | |
4724 | 19 | if from_func.params.len() != into_func.params.len() { |
4725 | 0 | bail!("different number of function parameters"); |
4726 | 19 | } |
4727 | 61 | for (from_param, into_param) in from_func.params.iter().zip(&into_func.params) { |
4728 | 61 | if from_param.name != into_param.name { |
4729 | 0 | bail!( |
4730 | | "different function parameter names: {} != {}", |
4731 | | from_param.name, |
4732 | | into_param.name |
4733 | | ); |
4734 | 61 | } |
4735 | 61 | self.build_type(&from_param.ty, &into_param.ty) |
4736 | 61 | .with_context(|| { |
4737 | 0 | format!( |
4738 | | "different function parameter types for `{}`", |
4739 | | from_param.name |
4740 | | ) |
4741 | 0 | })?; |
4742 | | } |
4743 | 19 | match (&from_func.result, &into_func.result) { |
4744 | 19 | (Some(from_ty), Some(into_ty)) => { |
4745 | 19 | self.build_type(from_ty, into_ty) |
4746 | 19 | .context("different function result types")?; |
4747 | | } |
4748 | 0 | (None, None) => {} |
4749 | 0 | (Some(_), None) | (None, Some(_)) => bail!("different number of function results"), |
4750 | | } |
4751 | 19 | Ok(()) |
4752 | 19 | } |
4753 | | |
4754 | 0 | fn build_world(&mut self, from_id: WorldId, into_id: WorldId) -> anyhow::Result<()> { |
4755 | 0 | let prev = self.world_map.insert(from_id, into_id); |
4756 | 0 | assert!(prev.is_none()); |
4757 | | |
4758 | 0 | let from_world = &self.from.worlds[from_id]; |
4759 | 0 | let into_world = &self.into.worlds[into_id]; |
4760 | | |
4761 | | // Same as interfaces worlds are expected to exactly match to avoid |
4762 | | // unexpectedly changing a particular component's view of imports and |
4763 | | // exports. |
4764 | | // |
4765 | | // FIXME: this should probably share functionality with |
4766 | | // `Resolve::merge_worlds` to support adding imports but not changing |
4767 | | // exports. |
4768 | | |
4769 | 0 | if from_world.imports.len() != into_world.imports.len() { |
4770 | 0 | bail!("world contains different number of imports than expected"); |
4771 | 0 | } |
4772 | 0 | if from_world.exports.len() != into_world.exports.len() { |
4773 | 0 | bail!("world contains different number of exports than expected"); |
4774 | 0 | } |
4775 | | |
4776 | 0 | for (from_name, from) in from_world.imports.iter() { |
4777 | 0 | let into_name = MergeMap::map_name(from_name, &self.interface_map); |
4778 | 0 | let name_str = self.from.name_world_key(from_name); |
4779 | 0 | let into = into_world |
4780 | 0 | .imports |
4781 | 0 | .get(&into_name) |
4782 | 0 | .ok_or_else(|| anyhow!("import `{name_str}` not found in target world"))?; |
4783 | 0 | self.match_world_item(from, into) |
4784 | 0 | .with_context(|| format!("import `{name_str}` didn't match target world"))?; |
4785 | | } |
4786 | | |
4787 | 0 | for (from_name, from) in from_world.exports.iter() { |
4788 | 0 | let into_name = MergeMap::map_name(from_name, &self.interface_map); |
4789 | 0 | let name_str = self.from.name_world_key(from_name); |
4790 | 0 | let into = into_world |
4791 | 0 | .exports |
4792 | 0 | .get(&into_name) |
4793 | 0 | .ok_or_else(|| anyhow!("export `{name_str}` not found in target world"))?; |
4794 | 0 | self.match_world_item(from, into) |
4795 | 0 | .with_context(|| format!("export `{name_str}` didn't match target world"))?; |
4796 | | } |
4797 | | |
4798 | 0 | Ok(()) |
4799 | 0 | } |
4800 | | |
4801 | 0 | fn map_name( |
4802 | 0 | from_name: &WorldKey, |
4803 | 0 | interface_map: &HashMap<InterfaceId, InterfaceId>, |
4804 | 0 | ) -> WorldKey { |
4805 | 0 | match from_name { |
4806 | 0 | WorldKey::Name(s) => WorldKey::Name(s.clone()), |
4807 | 0 | WorldKey::Interface(id) => { |
4808 | 0 | WorldKey::Interface(interface_map.get(id).copied().unwrap_or(*id)) |
4809 | | } |
4810 | | } |
4811 | 0 | } |
4812 | | |
4813 | 0 | fn match_world_item(&mut self, from: &WorldItem, into: &WorldItem) -> anyhow::Result<()> { |
4814 | 0 | match (from, into) { |
4815 | 0 | (WorldItem::Interface { id: from, .. }, WorldItem::Interface { id: into, .. }) => { |
4816 | | match ( |
4817 | 0 | &self.from.interfaces[*from].name, |
4818 | 0 | &self.into.interfaces[*into].name, |
4819 | | ) { |
4820 | | // If one interface is unnamed then they must both be |
4821 | | // unnamed and they must both have the same structure for |
4822 | | // now. |
4823 | 0 | (None, None) => self.build_interface(*from, *into)?, |
4824 | | |
4825 | | // Otherwise both interfaces must be named and they must |
4826 | | // have been previously found to be equivalent. Note that |
4827 | | // if either is unnamed it won't be present in |
4828 | | // `interface_map` so this'll return an error. |
4829 | | _ => { |
4830 | 0 | if self.interface_map.get(from) != Some(into) { |
4831 | 0 | bail!("interfaces are not the same"); |
4832 | 0 | } |
4833 | | } |
4834 | | } |
4835 | | } |
4836 | 0 | (WorldItem::Function(from), WorldItem::Function(into)) => { |
4837 | 0 | let _ = (from, into); |
4838 | 0 | // FIXME: should assert an check that `from` structurally |
4839 | 0 | // matches `into` |
4840 | 0 | } |
4841 | 0 | (WorldItem::Type { id: from, .. }, WorldItem::Type { id: into, .. }) => { |
4842 | | // FIXME: should assert an check that `from` structurally |
4843 | | // matches `into` |
4844 | 0 | let prev = self.type_map.insert(*from, *into); |
4845 | 0 | assert!(prev.is_none()); |
4846 | | } |
4847 | | |
4848 | | (WorldItem::Interface { .. }, _) |
4849 | | | (WorldItem::Function(_), _) |
4850 | | | (WorldItem::Type { .. }, _) => { |
4851 | 0 | bail!("world items do not have the same type") |
4852 | | } |
4853 | | } |
4854 | 0 | Ok(()) |
4855 | 0 | } |
4856 | | } |
4857 | | |
4858 | | /// Updates stability annotations when merging `from` into `into`. |
4859 | | /// |
4860 | | /// This is done to keep up-to-date stability information if possible. |
4861 | | /// Components for example don't carry stability information but WIT does so |
4862 | | /// this tries to move from "unknown" to stable/unstable if possible. |
4863 | 154 | fn update_stability(from: &Stability, into: &mut Stability, span: Span) -> ResolveResult<()> { |
4864 | | // If `from` is unknown or the two stability annotations are equal then |
4865 | | // there's nothing to do here. |
4866 | 154 | if from == into || from.is_unknown() { |
4867 | 154 | return Ok(()); |
4868 | 0 | } |
4869 | | // Otherwise if `into` is unknown then inherit the stability listed in |
4870 | | // `from`. |
4871 | 0 | if into.is_unknown() { |
4872 | 0 | *into = from.clone(); |
4873 | 0 | return Ok(()); |
4874 | 0 | } |
4875 | | |
4876 | | // Failing all that this means that the two attributes are different so |
4877 | | // generate an error. |
4878 | 0 | Err(ResolveError::from(ResolveErrorKind::StabilityMismatch { |
4879 | 0 | span, |
4880 | 0 | from: from.clone(), |
4881 | 0 | into: into.clone(), |
4882 | 0 | })) |
4883 | 154 | } |
4884 | | |
4885 | 0 | fn merge_include_stability( |
4886 | 0 | from: &Stability, |
4887 | 0 | into: &mut Stability, |
4888 | 0 | is_external_include: bool, |
4889 | 0 | span: Span, |
4890 | 0 | ) -> ResolveResult<()> { |
4891 | 0 | if is_external_include && from.is_stable() { |
4892 | 0 | log::trace!("dropped stability from external package"); |
4893 | 0 | *into = Stability::Unknown; |
4894 | 0 | return Ok(()); |
4895 | 0 | } |
4896 | | |
4897 | 0 | update_stability(from, into, span) |
4898 | 0 | } |
4899 | | |
4900 | | #[cfg(test)] |
4901 | | mod tests { |
4902 | | use crate::alloc::format; |
4903 | | use crate::alloc::string::{String, ToString}; |
4904 | | use crate::alloc::vec::Vec; |
4905 | | use crate::{Resolve, SourceMap, WorldItem, WorldKey}; |
4906 | | use anyhow::Result; |
4907 | | |
4908 | | #[test] |
4909 | | fn select_world() -> Result<()> { |
4910 | | let mut resolve = Resolve::default(); |
4911 | | resolve.push_str( |
4912 | | "test.wit", |
4913 | | r#" |
4914 | | package foo:bar@0.1.0; |
4915 | | |
4916 | | world foo {} |
4917 | | "#, |
4918 | | )?; |
4919 | | resolve.push_str( |
4920 | | "test.wit", |
4921 | | r#" |
4922 | | package foo:baz@0.1.0; |
4923 | | |
4924 | | world foo {} |
4925 | | "#, |
4926 | | )?; |
4927 | | resolve.push_str( |
4928 | | "test.wit", |
4929 | | r#" |
4930 | | package foo:baz@0.2.0; |
4931 | | |
4932 | | world foo {} |
4933 | | "#, |
4934 | | )?; |
4935 | | |
4936 | | let dummy = resolve.push_str( |
4937 | | "test.wit", |
4938 | | r#" |
4939 | | package foo:dummy; |
4940 | | |
4941 | | world foo {} |
4942 | | "#, |
4943 | | )?; |
4944 | | |
4945 | | assert!(resolve.select_world(&[dummy], None).is_ok()); |
4946 | | assert!(resolve.select_world(&[dummy], Some("xx")).is_err()); |
4947 | | assert!(resolve.select_world(&[dummy], Some("")).is_err()); |
4948 | | assert!(resolve.select_world(&[dummy], Some("foo:bar/foo")).is_ok()); |
4949 | | assert!( |
4950 | | resolve |
4951 | | .select_world(&[dummy], Some("foo:bar/foo@0.1.0")) |
4952 | | .is_ok() |
4953 | | ); |
4954 | | assert!(resolve.select_world(&[dummy], Some("foo:baz/foo")).is_err()); |
4955 | | assert!( |
4956 | | resolve |
4957 | | .select_world(&[dummy], Some("foo:baz/foo@0.1.0")) |
4958 | | .is_ok() |
4959 | | ); |
4960 | | assert!( |
4961 | | resolve |
4962 | | .select_world(&[dummy], Some("foo:baz/foo@0.2.0")) |
4963 | | .is_ok() |
4964 | | ); |
4965 | | Ok(()) |
4966 | | } |
4967 | | |
4968 | | #[test] |
4969 | | fn wasm_import_name_future_and_stream_intrinsics() -> Result<()> { |
4970 | | use crate::{FutureIntrinsic, LiftLowerAbi, ManglingAndAbi, StreamIntrinsic, WasmImport}; |
4971 | | |
4972 | | let mut resolve = Resolve::default(); |
4973 | | let pkg = resolve.push_str( |
4974 | | "test.wit", |
4975 | | r#" |
4976 | | package foo:bar; |
4977 | | |
4978 | | interface iface { |
4979 | | iface-func: func(x: future<u32>) -> stream<u32>; |
4980 | | } |
4981 | | |
4982 | | world w { |
4983 | | import import-func: func(x: future<future<u32>>, y: u32) -> stream<string>; |
4984 | | export export-func: func(x: future, y: stream); |
4985 | | import iface; |
4986 | | export iface; |
4987 | | } |
4988 | | "#, |
4989 | | )?; |
4990 | | let world = resolve.packages[pkg].worlds["w"]; |
4991 | | let world = &resolve.worlds[world]; |
4992 | | let mangling = ManglingAndAbi::Legacy(LiftLowerAbi::AsyncStackful); |
4993 | | |
4994 | | let WorldItem::Function(import_func) = |
4995 | | &world.imports[&WorldKey::Name("import-func".to_string())] |
4996 | | else { |
4997 | | panic!("expected `import-func` to be a top-level world import"); |
4998 | | }; |
4999 | | let WorldItem::Function(export_func) = |
5000 | | &world.exports[&WorldKey::Name("export-func".to_string())] |
5001 | | else { |
5002 | | panic!("expected `export-func` to be a top-level world export"); |
5003 | | }; |
5004 | | let import_types = import_func.find_futures_and_streams(&resolve); |
5005 | | assert_eq!(import_types.len(), 3); |
5006 | | |
5007 | | let (interface_key, interface_func) = world |
5008 | | .imports |
5009 | | .iter() |
5010 | | .find_map(|(key, item)| match item { |
5011 | | WorldItem::Interface { id, .. } => Some(( |
5012 | | key.clone(), |
5013 | | &resolve.interfaces[*id].functions["iface-func"], |
5014 | | )), |
5015 | | _ => None, |
5016 | | }) |
5017 | | .expect("expected interface import"); |
5018 | | let interface_types = interface_func.find_futures_and_streams(&resolve); |
5019 | | assert_eq!(interface_types.len(), 2); |
5020 | | |
5021 | | let (module, name) = resolve.wasm_import_name( |
5022 | | mangling, |
5023 | | WasmImport::FutureIntrinsic { |
5024 | | interface: None, |
5025 | | func: import_func, |
5026 | | ty: Some(import_types[0]), |
5027 | | intrinsic: FutureIntrinsic::New, |
5028 | | exported: false, |
5029 | | async_: false, |
5030 | | }, |
5031 | | ); |
5032 | | assert_eq!(module, "$root"); |
5033 | | assert_eq!(name, "[future-new-0]import-func"); |
5034 | | |
5035 | | let (module, name) = resolve.wasm_import_name( |
5036 | | mangling, |
5037 | | WasmImport::FutureIntrinsic { |
5038 | | interface: None, |
5039 | | func: import_func, |
5040 | | ty: Some(import_types[1]), |
5041 | | intrinsic: FutureIntrinsic::Read, |
5042 | | exported: false, |
5043 | | async_: true, |
5044 | | }, |
5045 | | ); |
5046 | | assert_eq!(module, "$root"); |
5047 | | assert_eq!(name, "[async-lower][future-read-1]import-func"); |
5048 | | |
5049 | | let (module, name) = resolve.wasm_import_name( |
5050 | | mangling, |
5051 | | WasmImport::FutureIntrinsic { |
5052 | | interface: None, |
5053 | | func: import_func, |
5054 | | ty: Some(import_types[1]), |
5055 | | intrinsic: FutureIntrinsic::Forward, |
5056 | | exported: false, |
5057 | | async_: false, |
5058 | | }, |
5059 | | ); |
5060 | | assert_eq!(module, "$root"); |
5061 | | assert_eq!(name, "[future-forward-1]import-func"); |
5062 | | |
5063 | | let (module, name) = resolve.wasm_import_name( |
5064 | | mangling, |
5065 | | WasmImport::StreamIntrinsic { |
5066 | | interface: None, |
5067 | | func: import_func, |
5068 | | ty: Some(import_types[2]), |
5069 | | intrinsic: StreamIntrinsic::Forward, |
5070 | | exported: false, |
5071 | | async_: false, |
5072 | | }, |
5073 | | ); |
5074 | | assert_eq!(module, "$root"); |
5075 | | assert_eq!(name, "[stream-forward-2]import-func"); |
5076 | | |
5077 | | let (module, name) = resolve.wasm_import_name( |
5078 | | mangling, |
5079 | | WasmImport::StreamIntrinsic { |
5080 | | interface: None, |
5081 | | func: import_func, |
5082 | | ty: Some(import_types[2]), |
5083 | | intrinsic: StreamIntrinsic::CancelRead, |
5084 | | exported: false, |
5085 | | async_: true, |
5086 | | }, |
5087 | | ); |
5088 | | assert_eq!(module, "$root"); |
5089 | | assert_eq!(name, "[async-lower][stream-cancel-read-2]import-func"); |
5090 | | |
5091 | | let (module, name) = resolve.wasm_import_name( |
5092 | | mangling, |
5093 | | WasmImport::FutureIntrinsic { |
5094 | | interface: None, |
5095 | | func: export_func, |
5096 | | ty: None, |
5097 | | intrinsic: FutureIntrinsic::DropReadable, |
5098 | | exported: true, |
5099 | | async_: false, |
5100 | | }, |
5101 | | ); |
5102 | | assert_eq!(module, "[export]$root"); |
5103 | | assert_eq!(name, "[future-drop-readable-unit]export-func"); |
5104 | | |
5105 | | let (module, name) = resolve.wasm_import_name( |
5106 | | mangling, |
5107 | | WasmImport::StreamIntrinsic { |
5108 | | interface: None, |
5109 | | func: export_func, |
5110 | | ty: None, |
5111 | | intrinsic: StreamIntrinsic::Write, |
5112 | | exported: true, |
5113 | | async_: true, |
5114 | | }, |
5115 | | ); |
5116 | | assert_eq!(module, "[export]$root"); |
5117 | | assert_eq!(name, "[async-lower][stream-write-unit]export-func"); |
5118 | | |
5119 | | let (module, name) = resolve.wasm_import_name( |
5120 | | mangling, |
5121 | | WasmImport::StreamIntrinsic { |
5122 | | interface: Some(&interface_key), |
5123 | | func: interface_func, |
5124 | | ty: Some(interface_types[1]), |
5125 | | intrinsic: StreamIntrinsic::Read, |
5126 | | exported: true, |
5127 | | async_: false, |
5128 | | }, |
5129 | | ); |
5130 | | assert_eq!( |
5131 | | module, |
5132 | | format!("[export]{}", resolve.name_world_key(&interface_key)) |
5133 | | ); |
5134 | | assert_eq!(name, "[stream-read-1]iface-func"); |
5135 | | |
5136 | | Ok(()) |
5137 | | } |
5138 | | |
5139 | | /// When there are multiple packages and there's no main package, don't |
5140 | | /// pick a world just based on it being the only one that matches. |
5141 | | #[test] |
5142 | | fn select_world_multiple_packages() -> Result<()> { |
5143 | | use wit_parser::Resolve; |
5144 | | |
5145 | | let mut resolve = Resolve::default(); |
5146 | | |
5147 | | // Just one world in one package; we always succeed. |
5148 | | let stuff = resolve.push_str( |
5149 | | "./my-test.wit", |
5150 | | r#" |
5151 | | package test:stuff; |
5152 | | |
5153 | | world foo { |
5154 | | // ... |
5155 | | } |
5156 | | "#, |
5157 | | )?; |
5158 | | assert!(resolve.select_world(&[stuff], None).is_ok()); |
5159 | | assert!(resolve.select_world(&[stuff], Some("foo")).is_ok()); |
5160 | | |
5161 | | // Multiple packages, but still just one total world. Lookups |
5162 | | // without a main package now fail. |
5163 | | let empty = resolve.push_str( |
5164 | | "./my-test.wit", |
5165 | | r#" |
5166 | | package test:empty; |
5167 | | "#, |
5168 | | )?; |
5169 | | assert!(resolve.select_world(&[stuff, empty], None).is_err()); |
5170 | | assert!(resolve.select_world(&[stuff, empty], Some("foo")).is_err()); |
5171 | | assert!(resolve.select_world(&[empty], None).is_err()); |
5172 | | assert!(resolve.select_world(&[empty], Some("foo")).is_err()); |
5173 | | |
5174 | | Ok(()) |
5175 | | } |
5176 | | |
5177 | | /// Test selecting a world with multiple versions of a package name. |
5178 | | #[test] |
5179 | | fn select_world_versions() -> Result<()> { |
5180 | | use wit_parser::Resolve; |
5181 | | |
5182 | | let mut resolve = Resolve::default(); |
5183 | | |
5184 | | let _id = resolve.push_str( |
5185 | | "./my-test.wit", |
5186 | | r#" |
5187 | | package example:distraction; |
5188 | | "#, |
5189 | | )?; |
5190 | | |
5191 | | // When selecting with a version it's ok to drop the version when |
5192 | | // there's only a single copy of that package in `Resolve`. |
5193 | | let versions_1 = resolve.push_str( |
5194 | | "./my-test.wit", |
5195 | | r#" |
5196 | | package example:versions@1.0.0; |
5197 | | |
5198 | | world foo { /* ... */ } |
5199 | | "#, |
5200 | | )?; |
5201 | | assert!(resolve.select_world(&[versions_1], Some("foo")).is_ok()); |
5202 | | assert!( |
5203 | | resolve |
5204 | | .select_world(&[versions_1], Some("foo@1.0.0")) |
5205 | | .is_err() |
5206 | | ); |
5207 | | assert!( |
5208 | | resolve |
5209 | | .select_world(&[versions_1], Some("example:versions/foo")) |
5210 | | .is_ok() |
5211 | | ); |
5212 | | assert!( |
5213 | | resolve |
5214 | | .select_world(&[versions_1], Some("example:versions/foo@1.0.0")) |
5215 | | .is_ok() |
5216 | | ); |
5217 | | |
5218 | | // However when a single package has multiple versions in a resolve |
5219 | | // it's required to specify the version to select which one. |
5220 | | let versions_2 = resolve.push_str( |
5221 | | "./my-test.wit", |
5222 | | r#" |
5223 | | package example:versions@2.0.0; |
5224 | | |
5225 | | world foo { /* ... */ } |
5226 | | "#, |
5227 | | )?; |
5228 | | assert!( |
5229 | | resolve |
5230 | | .select_world(&[versions_1, versions_2], Some("foo")) |
5231 | | .is_err() |
5232 | | ); |
5233 | | assert!( |
5234 | | resolve |
5235 | | .select_world(&[versions_1, versions_2], Some("foo@1.0.0")) |
5236 | | .is_err() |
5237 | | ); |
5238 | | assert!( |
5239 | | resolve |
5240 | | .select_world(&[versions_1, versions_2], Some("foo@2.0.0")) |
5241 | | .is_err() |
5242 | | ); |
5243 | | assert!( |
5244 | | resolve |
5245 | | .select_world(&[versions_1, versions_2], Some("example:versions/foo")) |
5246 | | .is_err() |
5247 | | ); |
5248 | | assert!( |
5249 | | resolve |
5250 | | .select_world( |
5251 | | &[versions_1, versions_2], |
5252 | | Some("example:versions/foo@1.0.0") |
5253 | | ) |
5254 | | .is_ok() |
5255 | | ); |
5256 | | assert!( |
5257 | | resolve |
5258 | | .select_world( |
5259 | | &[versions_1, versions_2], |
5260 | | Some("example:versions/foo@2.0.0") |
5261 | | ) |
5262 | | .is_ok() |
5263 | | ); |
5264 | | |
5265 | | Ok(()) |
5266 | | } |
5267 | | |
5268 | | /// Test overriding a main package using name qualification |
5269 | | #[test] |
5270 | | fn select_world_override_qualification() -> Result<()> { |
5271 | | use wit_parser::Resolve; |
5272 | | |
5273 | | let mut resolve = Resolve::default(); |
5274 | | |
5275 | | let other = resolve.push_str( |
5276 | | "./my-test.wit", |
5277 | | r#" |
5278 | | package example:other; |
5279 | | |
5280 | | world foo { } |
5281 | | "#, |
5282 | | )?; |
5283 | | |
5284 | | // A fully-qualified name overrides a main package. |
5285 | | let fq = resolve.push_str( |
5286 | | "./my-test.wit", |
5287 | | r#" |
5288 | | package example:fq; |
5289 | | |
5290 | | world bar { } |
5291 | | "#, |
5292 | | )?; |
5293 | | assert!(resolve.select_world(&[other, fq], Some("foo")).is_err()); |
5294 | | assert!(resolve.select_world(&[other, fq], Some("bar")).is_err()); |
5295 | | assert!( |
5296 | | resolve |
5297 | | .select_world(&[other, fq], Some("example:other/foo")) |
5298 | | .is_ok() |
5299 | | ); |
5300 | | assert!( |
5301 | | resolve |
5302 | | .select_world(&[other, fq], Some("example:fq/bar")) |
5303 | | .is_ok() |
5304 | | ); |
5305 | | assert!( |
5306 | | resolve |
5307 | | .select_world(&[other, fq], Some("example:other/bar")) |
5308 | | .is_err() |
5309 | | ); |
5310 | | assert!( |
5311 | | resolve |
5312 | | .select_world(&[other, fq], Some("example:fq/foo")) |
5313 | | .is_err() |
5314 | | ); |
5315 | | |
5316 | | Ok(()) |
5317 | | } |
5318 | | |
5319 | | /// Test selecting with fully-qualified world names. |
5320 | | #[test] |
5321 | | fn select_world_fully_qualified() -> Result<()> { |
5322 | | use wit_parser::Resolve; |
5323 | | |
5324 | | let mut resolve = Resolve::default(); |
5325 | | |
5326 | | let distraction = resolve.push_str( |
5327 | | "./my-test.wit", |
5328 | | r#" |
5329 | | package example:distraction; |
5330 | | "#, |
5331 | | )?; |
5332 | | |
5333 | | // If a package has multiple worlds, then we can't guess the world |
5334 | | // even if we know the package. |
5335 | | let multiworld = resolve.push_str( |
5336 | | "./my-test.wit", |
5337 | | r#" |
5338 | | package example:multiworld; |
5339 | | |
5340 | | world foo { /* ... */ } |
5341 | | |
5342 | | world bar { /* ... */ } |
5343 | | "#, |
5344 | | )?; |
5345 | | assert!( |
5346 | | resolve |
5347 | | .select_world(&[distraction, multiworld], None) |
5348 | | .is_err() |
5349 | | ); |
5350 | | assert!( |
5351 | | resolve |
5352 | | .select_world(&[distraction, multiworld], Some("foo")) |
5353 | | .is_err() |
5354 | | ); |
5355 | | assert!( |
5356 | | resolve |
5357 | | .select_world(&[distraction, multiworld], Some("example:multiworld/foo")) |
5358 | | .is_ok() |
5359 | | ); |
5360 | | assert!( |
5361 | | resolve |
5362 | | .select_world(&[distraction, multiworld], Some("bar")) |
5363 | | .is_err() |
5364 | | ); |
5365 | | assert!( |
5366 | | resolve |
5367 | | .select_world(&[distraction, multiworld], Some("example:multiworld/bar")) |
5368 | | .is_ok() |
5369 | | ); |
5370 | | |
5371 | | Ok(()) |
5372 | | } |
5373 | | |
5374 | | /// Test `select_world` with single and multiple packages. |
5375 | | #[test] |
5376 | | fn select_world_packages() -> Result<()> { |
5377 | | use wit_parser::Resolve; |
5378 | | |
5379 | | let mut resolve = Resolve::default(); |
5380 | | |
5381 | | // If there's a single package and only one world, that world is |
5382 | | // the obvious choice. |
5383 | | let wit1 = resolve.push_str( |
5384 | | "./my-test.wit", |
5385 | | r#" |
5386 | | package example:wit1; |
5387 | | |
5388 | | world foo { |
5389 | | // ... |
5390 | | } |
5391 | | "#, |
5392 | | )?; |
5393 | | assert!(resolve.select_world(&[wit1], None).is_ok()); |
5394 | | assert!(resolve.select_world(&[wit1], Some("foo")).is_ok()); |
5395 | | assert!( |
5396 | | resolve |
5397 | | .select_world(&[wit1], Some("example:wit1/foo")) |
5398 | | .is_ok() |
5399 | | ); |
5400 | | assert!(resolve.select_world(&[wit1], Some("bar")).is_err()); |
5401 | | assert!( |
5402 | | resolve |
5403 | | .select_world(&[wit1], Some("example:wit2/foo")) |
5404 | | .is_err() |
5405 | | ); |
5406 | | |
5407 | | // If there are multiple packages, we need to be told which package |
5408 | | // to use. |
5409 | | let wit2 = resolve.push_str( |
5410 | | "./my-test.wit", |
5411 | | r#" |
5412 | | package example:wit2; |
5413 | | |
5414 | | world foo { /* ... */ } |
5415 | | "#, |
5416 | | )?; |
5417 | | assert!(resolve.select_world(&[wit1, wit2], None).is_err()); |
5418 | | assert!(resolve.select_world(&[wit1, wit2], Some("foo")).is_err()); |
5419 | | assert!( |
5420 | | resolve |
5421 | | .select_world(&[wit1, wit2], Some("example:wit1/foo")) |
5422 | | .is_ok() |
5423 | | ); |
5424 | | assert!(resolve.select_world(&[wit2], None).is_ok()); |
5425 | | assert!(resolve.select_world(&[wit2], Some("foo")).is_ok()); |
5426 | | assert!( |
5427 | | resolve |
5428 | | .select_world(&[wit2], Some("example:wit1/foo")) |
5429 | | .is_ok() |
5430 | | ); |
5431 | | assert!(resolve.select_world(&[wit1, wit2], Some("bar")).is_err()); |
5432 | | assert!( |
5433 | | resolve |
5434 | | .select_world(&[wit1, wit2], Some("example:wit2/foo")) |
5435 | | .is_ok() |
5436 | | ); |
5437 | | assert!(resolve.select_world(&[wit2], Some("bar")).is_err()); |
5438 | | assert!( |
5439 | | resolve |
5440 | | .select_world(&[wit2], Some("example:wit2/foo")) |
5441 | | .is_ok() |
5442 | | ); |
5443 | | |
5444 | | Ok(()) |
5445 | | } |
5446 | | |
5447 | | #[test] |
5448 | | fn span_preservation() -> Result<()> { |
5449 | | let mut resolve = Resolve::default(); |
5450 | | let pkg = resolve.push_str( |
5451 | | "test.wit", |
5452 | | r#" |
5453 | | package foo:bar; |
5454 | | |
5455 | | interface my-iface { |
5456 | | type my-type = u32; |
5457 | | my-func: func(); |
5458 | | } |
5459 | | |
5460 | | world my-world { |
5461 | | export my-export: func(); |
5462 | | } |
5463 | | "#, |
5464 | | )?; |
5465 | | |
5466 | | let iface_id = resolve.packages[pkg].interfaces["my-iface"]; |
5467 | | assert!(resolve.interfaces[iface_id].span.is_known()); |
5468 | | |
5469 | | let type_id = resolve.interfaces[iface_id].types["my-type"]; |
5470 | | assert!(resolve.types[type_id].span.is_known()); |
5471 | | |
5472 | | assert!( |
5473 | | resolve.interfaces[iface_id].functions["my-func"] |
5474 | | .span |
5475 | | .is_known() |
5476 | | ); |
5477 | | |
5478 | | let world_id = resolve.packages[pkg].worlds["my-world"]; |
5479 | | assert!(resolve.worlds[world_id].span.is_known()); |
5480 | | |
5481 | | let WorldItem::Function(f) = |
5482 | | &resolve.worlds[world_id].exports[&WorldKey::Name("my-export".to_string())] |
5483 | | else { |
5484 | | panic!("expected function"); |
5485 | | }; |
5486 | | assert!(f.span.is_known()); |
5487 | | |
5488 | | Ok(()) |
5489 | | } |
5490 | | |
5491 | | #[test] |
5492 | | fn span_preservation_through_merge() -> Result<()> { |
5493 | | let mut resolve1 = Resolve::default(); |
5494 | | resolve1.push_str( |
5495 | | "test1.wit", |
5496 | | r#" |
5497 | | package foo:bar; |
5498 | | |
5499 | | interface iface1 { |
5500 | | type type1 = u32; |
5501 | | func1: func(); |
5502 | | } |
5503 | | "#, |
5504 | | )?; |
5505 | | |
5506 | | let mut resolve2 = Resolve::default(); |
5507 | | let pkg2 = resolve2.push_str( |
5508 | | "test2.wit", |
5509 | | r#" |
5510 | | package foo:baz; |
5511 | | |
5512 | | interface iface2 { |
5513 | | type type2 = string; |
5514 | | func2: func(); |
5515 | | } |
5516 | | "#, |
5517 | | )?; |
5518 | | |
5519 | | let iface2_old_id = resolve2.packages[pkg2].interfaces["iface2"]; |
5520 | | let remap = resolve1.merge(resolve2)?; |
5521 | | let iface2_id = remap.interfaces[iface2_old_id.index()].unwrap(); |
5522 | | |
5523 | | assert!(resolve1.interfaces[iface2_id].span.is_known()); |
5524 | | |
5525 | | let type2_id = resolve1.interfaces[iface2_id].types["type2"]; |
5526 | | assert!(resolve1.types[type2_id].span.is_known()); |
5527 | | |
5528 | | assert!( |
5529 | | resolve1.interfaces[iface2_id].functions["func2"] |
5530 | | .span |
5531 | | .is_known() |
5532 | | ); |
5533 | | |
5534 | | Ok(()) |
5535 | | } |
5536 | | |
5537 | | #[test] |
5538 | | fn span_preservation_through_include() -> Result<()> { |
5539 | | let mut resolve = Resolve::default(); |
5540 | | let pkg = resolve.push_str( |
5541 | | "test.wit", |
5542 | | r#" |
5543 | | package foo:bar; |
5544 | | |
5545 | | world base { |
5546 | | export my-func: func(); |
5547 | | } |
5548 | | |
5549 | | world extended { |
5550 | | include base; |
5551 | | } |
5552 | | "#, |
5553 | | )?; |
5554 | | |
5555 | | let base_id = resolve.packages[pkg].worlds["base"]; |
5556 | | let extended_id = resolve.packages[pkg].worlds["extended"]; |
5557 | | |
5558 | | let WorldItem::Function(base_func) = |
5559 | | &resolve.worlds[base_id].exports[&WorldKey::Name("my-func".to_string())] |
5560 | | else { |
5561 | | panic!("expected function"); |
5562 | | }; |
5563 | | assert!(base_func.span.is_known()); |
5564 | | |
5565 | | let WorldItem::Function(extended_func) = |
5566 | | &resolve.worlds[extended_id].exports[&WorldKey::Name("my-func".to_string())] |
5567 | | else { |
5568 | | panic!("expected function"); |
5569 | | }; |
5570 | | assert!(extended_func.span.is_known()); |
5571 | | |
5572 | | Ok(()) |
5573 | | } |
5574 | | |
5575 | | #[test] |
5576 | | fn span_preservation_through_include_with_rename() -> Result<()> { |
5577 | | let mut resolve = Resolve::default(); |
5578 | | let pkg = resolve.push_str( |
5579 | | "test.wit", |
5580 | | r#" |
5581 | | package foo:bar; |
5582 | | |
5583 | | world base { |
5584 | | export original-name: func(); |
5585 | | } |
5586 | | |
5587 | | world extended { |
5588 | | include base with { original-name as renamed-func } |
5589 | | } |
5590 | | "#, |
5591 | | )?; |
5592 | | |
5593 | | let extended_id = resolve.packages[pkg].worlds["extended"]; |
5594 | | |
5595 | | let WorldItem::Function(f) = |
5596 | | &resolve.worlds[extended_id].exports[&WorldKey::Name("renamed-func".to_string())] |
5597 | | else { |
5598 | | panic!("expected function"); |
5599 | | }; |
5600 | | assert!(f.span.is_known()); |
5601 | | |
5602 | | assert!( |
5603 | | !resolve.worlds[extended_id] |
5604 | | .exports |
5605 | | .contains_key(&WorldKey::Name("original-name".to_string())) |
5606 | | ); |
5607 | | |
5608 | | Ok(()) |
5609 | | } |
5610 | | |
5611 | | /// Test that spans work when included world is defined after the including world |
5612 | | #[test] |
5613 | | fn span_preservation_through_include_reverse_order() -> Result<()> { |
5614 | | let mut resolve = Resolve::default(); |
5615 | | let pkg = resolve.push_str( |
5616 | | "test.wit", |
5617 | | r#" |
5618 | | package foo:bar; |
5619 | | |
5620 | | world extended { |
5621 | | include base; |
5622 | | } |
5623 | | |
5624 | | world base { |
5625 | | export my-func: func(); |
5626 | | } |
5627 | | "#, |
5628 | | )?; |
5629 | | |
5630 | | let base_id = resolve.packages[pkg].worlds["base"]; |
5631 | | let extended_id = resolve.packages[pkg].worlds["extended"]; |
5632 | | |
5633 | | let WorldItem::Function(base_func) = |
5634 | | &resolve.worlds[base_id].exports[&WorldKey::Name("my-func".to_string())] |
5635 | | else { |
5636 | | panic!("expected function"); |
5637 | | }; |
5638 | | assert!(base_func.span.is_known()); |
5639 | | |
5640 | | let WorldItem::Function(extended_func) = |
5641 | | &resolve.worlds[extended_id].exports[&WorldKey::Name("my-func".to_string())] |
5642 | | else { |
5643 | | panic!("expected function"); |
5644 | | }; |
5645 | | assert!(extended_func.span.is_known()); |
5646 | | |
5647 | | Ok(()) |
5648 | | } |
5649 | | |
5650 | | #[test] |
5651 | | fn span_line_numbers() -> Result<()> { |
5652 | | let mut resolve = Resolve::default(); |
5653 | | let pkg = resolve.push_source( |
5654 | | "test.wit", |
5655 | | "package foo:bar; |
5656 | | |
5657 | | interface my-iface { |
5658 | | type my-type = u32; |
5659 | | my-func: func(); |
5660 | | } |
5661 | | |
5662 | | world my-world { |
5663 | | export my-export: func(); |
5664 | | } |
5665 | | ", |
5666 | | )?; |
5667 | | |
5668 | | let iface_id = resolve.packages[pkg].interfaces["my-iface"]; |
5669 | | let iface_span = resolve.interfaces[iface_id].span; |
5670 | | let iface_loc = resolve.render_location(iface_span); |
5671 | | assert!( |
5672 | | iface_loc.contains(":3:"), |
5673 | | "interface location was {iface_loc}" |
5674 | | ); |
5675 | | |
5676 | | let type_id = resolve.interfaces[iface_id].types["my-type"]; |
5677 | | let type_span = resolve.types[type_id].span; |
5678 | | let type_loc = resolve.render_location(type_span); |
5679 | | assert!(type_loc.contains(":4:"), "type location was {type_loc}"); |
5680 | | |
5681 | | let func_span = resolve.interfaces[iface_id].functions["my-func"].span; |
5682 | | let func_loc = resolve.render_location(func_span); |
5683 | | assert!(func_loc.contains(":5:"), "function location was {func_loc}"); |
5684 | | |
5685 | | let world_id = resolve.packages[pkg].worlds["my-world"]; |
5686 | | let world_span = resolve.worlds[world_id].span; |
5687 | | let world_loc = resolve.render_location(world_span); |
5688 | | assert!(world_loc.contains(":8:"), "world location was {world_loc}"); |
5689 | | |
5690 | | let WorldItem::Function(export_func) = |
5691 | | &resolve.worlds[world_id].exports[&WorldKey::Name("my-export".to_string())] |
5692 | | else { |
5693 | | panic!("expected function"); |
5694 | | }; |
5695 | | let export_loc = resolve.render_location(export_func.span); |
5696 | | assert!( |
5697 | | export_loc.contains(":9:"), |
5698 | | "export location was {export_loc}" |
5699 | | ); |
5700 | | |
5701 | | Ok(()) |
5702 | | } |
5703 | | |
5704 | | #[test] |
5705 | | fn span_line_numbers_through_merge() -> Result<()> { |
5706 | | let mut resolve1 = Resolve::default(); |
5707 | | resolve1.push_source( |
5708 | | "first.wit", |
5709 | | "package foo:first; |
5710 | | |
5711 | | interface iface1 { |
5712 | | func1: func(); |
5713 | | } |
5714 | | ", |
5715 | | )?; |
5716 | | |
5717 | | let mut resolve2 = Resolve::default(); |
5718 | | let pkg2 = resolve2.push_source( |
5719 | | "second.wit", |
5720 | | "package foo:second; |
5721 | | |
5722 | | interface iface2 { |
5723 | | func2: func(); |
5724 | | } |
5725 | | ", |
5726 | | )?; |
5727 | | |
5728 | | let iface2_old_id = resolve2.packages[pkg2].interfaces["iface2"]; |
5729 | | let remap = resolve1.merge(resolve2)?; |
5730 | | let iface2_id = remap.interfaces[iface2_old_id.index()].unwrap(); |
5731 | | |
5732 | | let iface2_span = resolve1.interfaces[iface2_id].span; |
5733 | | let iface2_loc = resolve1.render_location(iface2_span); |
5734 | | assert!( |
5735 | | iface2_loc.contains("second.wit"), |
5736 | | "should reference second.wit, got {iface2_loc}" |
5737 | | ); |
5738 | | assert!( |
5739 | | iface2_loc.contains(":3:"), |
5740 | | "interface should be on line 3, got {iface2_loc}" |
5741 | | ); |
5742 | | |
5743 | | let func2_span = resolve1.interfaces[iface2_id].functions["func2"].span; |
5744 | | let func2_loc = resolve1.render_location(func2_span); |
5745 | | assert!( |
5746 | | func2_loc.contains("second.wit"), |
5747 | | "should reference second.wit, got {func2_loc}" |
5748 | | ); |
5749 | | assert!( |
5750 | | func2_loc.contains(":4:"), |
5751 | | "function should be on line 4, got {func2_loc}" |
5752 | | ); |
5753 | | |
5754 | | Ok(()) |
5755 | | } |
5756 | | |
5757 | | #[test] |
5758 | | fn span_line_numbers_multiple_sources() -> Result<()> { |
5759 | | let mut resolve = Resolve::default(); |
5760 | | |
5761 | | let pkg1 = resolve.push_source( |
5762 | | "first.wit", |
5763 | | "package test:first; |
5764 | | |
5765 | | interface first-iface { |
5766 | | first-func: func(); |
5767 | | } |
5768 | | ", |
5769 | | )?; |
5770 | | |
5771 | | let pkg2 = resolve.push_source( |
5772 | | "second.wit", |
5773 | | "package test:second; |
5774 | | |
5775 | | interface second-iface { |
5776 | | second-func: func(); |
5777 | | } |
5778 | | ", |
5779 | | )?; |
5780 | | |
5781 | | let iface1_id = resolve.packages[pkg1].interfaces["first-iface"]; |
5782 | | let iface1_span = resolve.interfaces[iface1_id].span; |
5783 | | let iface1_loc = resolve.render_location(iface1_span); |
5784 | | assert!( |
5785 | | iface1_loc.contains("first.wit"), |
5786 | | "should reference first.wit, got {iface1_loc}" |
5787 | | ); |
5788 | | assert!( |
5789 | | iface1_loc.contains(":3:"), |
5790 | | "interface should be on line 3, got {iface1_loc}" |
5791 | | ); |
5792 | | |
5793 | | let func1_span = resolve.interfaces[iface1_id].functions["first-func"].span; |
5794 | | let func1_loc = resolve.render_location(func1_span); |
5795 | | assert!( |
5796 | | func1_loc.contains("first.wit"), |
5797 | | "should reference first.wit, got {func1_loc}" |
5798 | | ); |
5799 | | assert!( |
5800 | | func1_loc.contains(":4:"), |
5801 | | "function should be on line 4, got {func1_loc}" |
5802 | | ); |
5803 | | |
5804 | | let iface2_id = resolve.packages[pkg2].interfaces["second-iface"]; |
5805 | | let iface2_span = resolve.interfaces[iface2_id].span; |
5806 | | let iface2_loc = resolve.render_location(iface2_span); |
5807 | | assert!( |
5808 | | iface2_loc.contains("second.wit"), |
5809 | | "should reference second.wit, got {iface2_loc}" |
5810 | | ); |
5811 | | assert!( |
5812 | | iface2_loc.contains(":3:"), |
5813 | | "interface should be on line 3, got {iface2_loc}" |
5814 | | ); |
5815 | | |
5816 | | let func2_span = resolve.interfaces[iface2_id].functions["second-func"].span; |
5817 | | let func2_loc = resolve.render_location(func2_span); |
5818 | | assert!( |
5819 | | func2_loc.contains("second.wit"), |
5820 | | "should reference second.wit, got {func2_loc}" |
5821 | | ); |
5822 | | assert!( |
5823 | | func2_loc.contains(":4:"), |
5824 | | "function should be on line 4, got {func2_loc}" |
5825 | | ); |
5826 | | |
5827 | | Ok(()) |
5828 | | } |
5829 | | |
5830 | | #[test] |
5831 | | fn span_preservation_for_fields_and_cases() -> Result<()> { |
5832 | | use crate::TypeDefKind; |
5833 | | |
5834 | | let mut resolve = Resolve::default(); |
5835 | | let pkg = resolve.push_str( |
5836 | | "test.wit", |
5837 | | r#" |
5838 | | package foo:bar; |
5839 | | |
5840 | | interface my-iface { |
5841 | | record my-record { |
5842 | | field1: u32, |
5843 | | field2: string, |
5844 | | } |
5845 | | |
5846 | | flags my-flags { |
5847 | | flag1, |
5848 | | flag2, |
5849 | | } |
5850 | | |
5851 | | variant my-variant { |
5852 | | case1, |
5853 | | case2(u32), |
5854 | | } |
5855 | | |
5856 | | enum my-enum { |
5857 | | val1, |
5858 | | val2, |
5859 | | } |
5860 | | } |
5861 | | "#, |
5862 | | )?; |
5863 | | |
5864 | | let iface_id = resolve.packages[pkg].interfaces["my-iface"]; |
5865 | | |
5866 | | // Check record fields have spans |
5867 | | let record_id = resolve.interfaces[iface_id].types["my-record"]; |
5868 | | let TypeDefKind::Record(record) = &resolve.types[record_id].kind else { |
5869 | | panic!("expected record"); |
5870 | | }; |
5871 | | assert!(record.fields[0].span.is_known(), "field1 should have span"); |
5872 | | assert!(record.fields[1].span.is_known(), "field2 should have span"); |
5873 | | |
5874 | | // Check flags have spans |
5875 | | let flags_id = resolve.interfaces[iface_id].types["my-flags"]; |
5876 | | let TypeDefKind::Flags(flags) = &resolve.types[flags_id].kind else { |
5877 | | panic!("expected flags"); |
5878 | | }; |
5879 | | assert!(flags.flags[0].span.is_known(), "flag1 should have span"); |
5880 | | assert!(flags.flags[1].span.is_known(), "flag2 should have span"); |
5881 | | |
5882 | | // Check variant cases have spans |
5883 | | let variant_id = resolve.interfaces[iface_id].types["my-variant"]; |
5884 | | let TypeDefKind::Variant(variant) = &resolve.types[variant_id].kind else { |
5885 | | panic!("expected variant"); |
5886 | | }; |
5887 | | assert!(variant.cases[0].span.is_known(), "case1 should have span"); |
5888 | | assert!(variant.cases[1].span.is_known(), "case2 should have span"); |
5889 | | |
5890 | | // Check enum cases have spans |
5891 | | let enum_id = resolve.interfaces[iface_id].types["my-enum"]; |
5892 | | let TypeDefKind::Enum(e) = &resolve.types[enum_id].kind else { |
5893 | | panic!("expected enum"); |
5894 | | }; |
5895 | | assert!(e.cases[0].span.is_known(), "val1 should have span"); |
5896 | | assert!(e.cases[1].span.is_known(), "val2 should have span"); |
5897 | | |
5898 | | Ok(()) |
5899 | | } |
5900 | | |
5901 | | #[test] |
5902 | | fn span_preservation_for_fields_through_merge() -> Result<()> { |
5903 | | use crate::TypeDefKind; |
5904 | | |
5905 | | let mut resolve1 = Resolve::default(); |
5906 | | resolve1.push_str( |
5907 | | "test1.wit", |
5908 | | r#" |
5909 | | package foo:bar; |
5910 | | |
5911 | | interface iface1 { |
5912 | | record rec1 { |
5913 | | f1: u32, |
5914 | | } |
5915 | | } |
5916 | | "#, |
5917 | | )?; |
5918 | | |
5919 | | let mut resolve2 = Resolve::default(); |
5920 | | let pkg2 = resolve2.push_str( |
5921 | | "test2.wit", |
5922 | | r#" |
5923 | | package foo:baz; |
5924 | | |
5925 | | interface iface2 { |
5926 | | record rec2 { |
5927 | | f2: string, |
5928 | | } |
5929 | | |
5930 | | variant var2 { |
5931 | | c2, |
5932 | | } |
5933 | | } |
5934 | | "#, |
5935 | | )?; |
5936 | | |
5937 | | let iface2_old_id = resolve2.packages[pkg2].interfaces["iface2"]; |
5938 | | let rec2_old_id = resolve2.interfaces[iface2_old_id].types["rec2"]; |
5939 | | let var2_old_id = resolve2.interfaces[iface2_old_id].types["var2"]; |
5940 | | |
5941 | | let remap = resolve1.merge(resolve2)?; |
5942 | | |
5943 | | let rec2_id = remap.types[rec2_old_id.index()].unwrap(); |
5944 | | let TypeDefKind::Record(record) = &resolve1.types[rec2_id].kind else { |
5945 | | panic!("expected record"); |
5946 | | }; |
5947 | | assert!( |
5948 | | record.fields[0].span.is_known(), |
5949 | | "field should have span after merge" |
5950 | | ); |
5951 | | |
5952 | | let var2_id = remap.types[var2_old_id.index()].unwrap(); |
5953 | | let TypeDefKind::Variant(variant) = &resolve1.types[var2_id].kind else { |
5954 | | panic!("expected variant"); |
5955 | | }; |
5956 | | assert!( |
5957 | | variant.cases[0].span.is_known(), |
5958 | | "case should have span after merge" |
5959 | | ); |
5960 | | |
5961 | | Ok(()) |
5962 | | } |
5963 | | |
5964 | | #[test] |
5965 | | fn param_spans_point_to_names() -> Result<()> { |
5966 | | let source = "\ |
5967 | | package foo:bar; |
5968 | | |
5969 | | interface iface { |
5970 | | my-func: func(a: u32, b: string); |
5971 | | } |
5972 | | "; |
5973 | | let mut resolve = Resolve::default(); |
5974 | | let pkg = resolve.push_str("test.wit", source)?; |
5975 | | |
5976 | | let iface_id = resolve.packages[pkg].interfaces["iface"]; |
5977 | | let func = &resolve.interfaces[iface_id].functions["my-func"]; |
5978 | | assert_eq!(func.params.len(), 2); |
5979 | | for param in &func.params { |
5980 | | let start = param.span.start() as usize; |
5981 | | let end = param.span.end() as usize; |
5982 | | let snippet = &source[start..end]; |
5983 | | assert_eq!( |
5984 | | snippet, param.name, |
5985 | | "param `{}` span points to {:?}", |
5986 | | param.name, snippet |
5987 | | ); |
5988 | | } |
5989 | | |
5990 | | Ok(()) |
5991 | | } |
5992 | | |
5993 | | #[test] |
5994 | | fn push_groups_resolves_dep_before_main() -> Result<()> { |
5995 | | // push_groups must topologically sort main + deps internally and succeed |
5996 | | // even when the dep is listed after main in the caller's mental model. |
5997 | | let dep = { |
5998 | | let mut map = SourceMap::default(); |
5999 | | map.push_str( |
6000 | | "file:///dep.wit", |
6001 | | "package foo:dep;\ninterface i { type t = u32; }", |
6002 | | ); |
6003 | | map.parse().map_err(|(_, e)| e)? |
6004 | | }; |
6005 | | let main = { |
6006 | | let mut map = SourceMap::default(); |
6007 | | map.push_str( |
6008 | | "file:///main.wit", |
6009 | | "package foo:main;\ninterface j { use foo:dep/i.{t}; type u = t; }", |
6010 | | ); |
6011 | | map.parse().map_err(|(_, e)| e)? |
6012 | | }; |
6013 | | let mut resolve = Resolve::default(); |
6014 | | resolve.push_groups(main, Vec::from([dep]))?; |
6015 | | assert_eq!(resolve.packages.len(), 2); |
6016 | | Ok(()) |
6017 | | } |
6018 | | |
6019 | | #[test] |
6020 | | fn push_groups_cycle_error_contains_location() { |
6021 | | // A cross-group cycle must produce an error message with a file URI and |
6022 | | // line/col. This validates that source maps are merged into resolve.source_map |
6023 | | // *before* toposort runs, so the span in the cycle error is resolvable. |
6024 | | let a = { |
6025 | | let mut map = SourceMap::default(); |
6026 | | map.push_str( |
6027 | | "file:///a.wit", |
6028 | | "package foo:a;\ninterface i { use foo:b/j.{}; }", |
6029 | | ); |
6030 | | map.parse().unwrap() |
6031 | | }; |
6032 | | let b = { |
6033 | | let mut map = SourceMap::default(); |
6034 | | map.push_str( |
6035 | | "file:///b.wit", |
6036 | | "package foo:b;\ninterface j { use foo:a/i.{}; }", |
6037 | | ); |
6038 | | map.parse().unwrap() |
6039 | | }; |
6040 | | let mut resolve = Resolve::default(); |
6041 | | let err = resolve.push_groups(a, Vec::from([b])).unwrap_err(); |
6042 | | let msg = err.render(&resolve.source_map); |
6043 | | assert!( |
6044 | | msg.contains("file:///"), |
6045 | | "cycle error should contain a file URI, got: {msg}" |
6046 | | ); |
6047 | | } |
6048 | | |
6049 | | #[test] |
6050 | | fn param_spans_preserved_through_merge() -> Result<()> { |
6051 | | let mut resolve1 = Resolve::default(); |
6052 | | resolve1.push_str( |
6053 | | "test1.wit", |
6054 | | r#" |
6055 | | package foo:bar; |
6056 | | |
6057 | | interface iface1 { |
6058 | | f1: func(x: u32); |
6059 | | } |
6060 | | "#, |
6061 | | )?; |
6062 | | |
6063 | | let mut resolve2 = Resolve::default(); |
6064 | | let pkg2 = resolve2.push_str( |
6065 | | "test2.wit", |
6066 | | r#" |
6067 | | package foo:baz; |
6068 | | |
6069 | | interface iface2 { |
6070 | | f2: func(y: string, z: bool); |
6071 | | } |
6072 | | "#, |
6073 | | )?; |
6074 | | |
6075 | | let iface2_old_id = resolve2.packages[pkg2].interfaces["iface2"]; |
6076 | | |
6077 | | let remap = resolve1.merge(resolve2)?; |
6078 | | |
6079 | | let iface2_id = remap.interfaces[iface2_old_id.index()].unwrap(); |
6080 | | let func = &resolve1.interfaces[iface2_id].functions["f2"]; |
6081 | | for param in &func.params { |
6082 | | assert!( |
6083 | | param.span.is_known(), |
6084 | | "param `{}` should have span after merge", |
6085 | | param.name |
6086 | | ); |
6087 | | } |
6088 | | |
6089 | | Ok(()) |
6090 | | } |
6091 | | |
6092 | | /// Demonstrates the round-trip property: starting from a world with only |
6093 | | /// exports, `importize` turns them into imports, then `exportize` turns |
6094 | | /// them back. The resulting world has the same set of exports (by key) |
6095 | | /// as the original. |
6096 | | #[test] |
6097 | | fn exportize_importize_roundtrip() -> Result<()> { |
6098 | | let mut resolve = Resolve::default(); |
6099 | | let pkg = resolve.push_str( |
6100 | | "test.wit", |
6101 | | r#" |
6102 | | package foo:bar; |
6103 | | |
6104 | | interface types { |
6105 | | type my-type = u32; |
6106 | | } |
6107 | | |
6108 | | interface api { |
6109 | | use types.{my-type}; |
6110 | | do-something: func(a: my-type) -> my-type; |
6111 | | } |
6112 | | |
6113 | | world w { |
6114 | | export api; |
6115 | | } |
6116 | | "#, |
6117 | | )?; |
6118 | | let world_id = resolve.packages[pkg].worlds["w"]; |
6119 | | |
6120 | | // Snapshot original export keys. |
6121 | | let original_export_keys: Vec<String> = resolve.worlds[world_id] |
6122 | | .exports |
6123 | | .keys() |
6124 | | .map(|k| resolve.name_world_key(k)) |
6125 | | .collect(); |
6126 | | assert!(!original_export_keys.is_empty()); |
6127 | | assert!(resolve.worlds[world_id].imports.iter().all(|(_, item)| { |
6128 | | // Before importize the only imports should be elaborated |
6129 | | // interface deps (all interface items). |
6130 | | matches!(item, WorldItem::Interface { .. }) |
6131 | | })); |
6132 | | |
6133 | | // importize: exports -> imports, no exports remain. |
6134 | | resolve.importize(world_id, Some("w-temp".to_string()))?; |
6135 | | assert!( |
6136 | | resolve.worlds[world_id].exports.is_empty(), |
6137 | | "importize should leave no exports" |
6138 | | ); |
6139 | | // The original exports should now appear as imports. |
6140 | | for key in &original_export_keys { |
6141 | | assert!( |
6142 | | resolve.worlds[world_id] |
6143 | | .imports |
6144 | | .keys() |
6145 | | .any(|k| resolve.name_world_key(k) == *key), |
6146 | | "expected `{key}` to be an import after importize" |
6147 | | ); |
6148 | | } |
6149 | | |
6150 | | // exportize: imports -> exports, round-tripping back. |
6151 | | resolve.exportize(world_id, Some("w-final".to_string()), None)?; |
6152 | | assert!( |
6153 | | !resolve.worlds[world_id].exports.is_empty(), |
6154 | | "exportize should produce exports" |
6155 | | ); |
6156 | | // The original export keys should be present as exports again. |
6157 | | let final_export_keys: Vec<String> = resolve.worlds[world_id] |
6158 | | .exports |
6159 | | .keys() |
6160 | | .map(|k| resolve.name_world_key(k)) |
6161 | | .collect(); |
6162 | | for key in &original_export_keys { |
6163 | | assert!( |
6164 | | final_export_keys.contains(key), |
6165 | | "expected `{key}` to be an export after round-trip, got exports: {final_export_keys:?}" |
6166 | | ); |
6167 | | } |
6168 | | |
6169 | | Ok(()) |
6170 | | } |
6171 | | |
6172 | | #[test] |
6173 | | fn include_with_renames_resource_accessors() -> Result<()> { |
6174 | | let mut resolve = Resolve::default(); |
6175 | | let pkg = resolve.push_str( |
6176 | | "test.wit", |
6177 | | r#" |
6178 | | package foo:foo; |
6179 | | |
6180 | | world base { |
6181 | | resource r { |
6182 | | p: get() -> u32; |
6183 | | p: set(v: u32); |
6184 | | s: static get() -> u32; |
6185 | | s: static set(v: u32); |
6186 | | m: func(); |
6187 | | } |
6188 | | } |
6189 | | |
6190 | | world derived { |
6191 | | include base with { r as r2 } |
6192 | | } |
6193 | | "#, |
6194 | | )?; |
6195 | | |
6196 | | let world_id = resolve.packages[pkg].worlds["derived"]; |
6197 | | let mut imports: Vec<String> = resolve.worlds[world_id] |
6198 | | .imports |
6199 | | .keys() |
6200 | | .map(|k| resolve.name_world_key(k)) |
6201 | | .collect(); |
6202 | | imports.sort(); |
6203 | | |
6204 | | let mut expected: Vec<String> = [ |
6205 | | "r2", |
6206 | | "[method][get]r2.p", |
6207 | | "[method][set]r2.p", |
6208 | | "[static][get]r2.s", |
6209 | | "[static][set]r2.s", |
6210 | | "[method]r2.m", |
6211 | | ] |
6212 | | .iter() |
6213 | | .map(|s| s.to_string()) |
6214 | | .collect(); |
6215 | | expected.sort(); |
6216 | | |
6217 | | assert_eq!(imports, expected); |
6218 | | Ok(()) |
6219 | | } |
6220 | | } |