/src/wasm-tools/crates/wit-parser/src/resolve.rs
Line | Count | Source |
1 | | use std::cmp::Ordering; |
2 | | use std::collections::hash_map; |
3 | | use std::collections::{BTreeMap, HashMap, HashSet}; |
4 | | use std::fmt; |
5 | | use std::mem; |
6 | | use std::path::{Path, PathBuf}; |
7 | | |
8 | | use anyhow::{Context, Result, anyhow, bail}; |
9 | | use id_arena::{Arena, Id}; |
10 | | use indexmap::{IndexMap, IndexSet}; |
11 | | use semver::Version; |
12 | | #[cfg(feature = "serde")] |
13 | | use serde_derive::Serialize; |
14 | | |
15 | | use crate::ast::lex::Span; |
16 | | use crate::ast::{ParsedUsePath, parse_use_path}; |
17 | | #[cfg(feature = "serde")] |
18 | | use crate::serde_::{serialize_arena, serialize_id_map}; |
19 | | use crate::{ |
20 | | AstItem, Docs, Error, Function, FunctionKind, Handle, IncludeName, Interface, InterfaceId, |
21 | | InterfaceSpan, LiftLowerAbi, ManglingAndAbi, PackageName, PackageNotFoundError, SourceMap, |
22 | | Stability, Type, TypeDef, TypeDefKind, TypeId, TypeIdVisitor, TypeOwner, UnresolvedPackage, |
23 | | UnresolvedPackageGroup, World, WorldId, WorldItem, WorldKey, WorldSpan, |
24 | | }; |
25 | | |
26 | | pub use clone::CloneMaps; |
27 | | |
28 | | mod clone; |
29 | | |
30 | | /// Representation of a fully resolved set of WIT packages. |
31 | | /// |
32 | | /// This structure contains a graph of WIT packages and all of their contents |
33 | | /// merged together into the contained arenas. All items are sorted |
34 | | /// topologically and everything here is fully resolved, so with a `Resolve` no |
35 | | /// name lookups are necessary and instead everything is index-based. |
36 | | /// |
37 | | /// Working with a WIT package requires inserting it into a `Resolve` to ensure |
38 | | /// that all of its dependencies are satisfied. This will give the full picture |
39 | | /// of that package's types and such. |
40 | | /// |
41 | | /// Each item in a `Resolve` has a parent link to trace it back to the original |
42 | | /// package as necessary. |
43 | | #[derive(Default, Clone, Debug)] |
44 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
45 | | pub struct Resolve { |
46 | | /// All known worlds within this `Resolve`. |
47 | | /// |
48 | | /// Each world points at a `PackageId` which is stored below. No ordering is |
49 | | /// guaranteed between this list of worlds. |
50 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
51 | | pub worlds: Arena<World>, |
52 | | |
53 | | /// All known interfaces within this `Resolve`. |
54 | | /// |
55 | | /// Each interface points at a `PackageId` which is stored below. No |
56 | | /// ordering is guaranteed between this list of interfaces. |
57 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
58 | | pub interfaces: Arena<Interface>, |
59 | | |
60 | | /// All known types within this `Resolve`. |
61 | | /// |
62 | | /// Types are topologically sorted such that any type referenced from one |
63 | | /// type is guaranteed to be defined previously. Otherwise though these are |
64 | | /// not sorted by interface for example. |
65 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
66 | | pub types: Arena<TypeDef>, |
67 | | |
68 | | /// All known packages within this `Resolve`. |
69 | | /// |
70 | | /// This list of packages is not sorted. Sorted packages can be queried |
71 | | /// through [`Resolve::topological_packages`]. |
72 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arena"))] |
73 | | pub packages: Arena<Package>, |
74 | | |
75 | | /// A map of package names to the ID of the package with that name. |
76 | | #[cfg_attr(feature = "serde", serde(skip))] |
77 | | pub package_names: IndexMap<PackageName, PackageId>, |
78 | | |
79 | | /// Activated features for this [`Resolve`]. |
80 | | /// |
81 | | /// This set of features is empty by default. This is consulted for |
82 | | /// `@unstable` annotations in loaded WIT documents. Any items with |
83 | | /// `@unstable` are filtered out unless their feature is present within this |
84 | | /// set. |
85 | | #[cfg_attr(feature = "serde", serde(skip))] |
86 | | pub features: IndexSet<String>, |
87 | | |
88 | | /// Activate all features for this [`Resolve`]. |
89 | | #[cfg_attr(feature = "serde", serde(skip))] |
90 | | pub all_features: bool, |
91 | | } |
92 | | |
93 | | /// A WIT package within a `Resolve`. |
94 | | /// |
95 | | /// A package is a collection of interfaces and worlds. Packages additionally |
96 | | /// have a unique identifier that affects generated components and uniquely |
97 | | /// identifiers this particular package. |
98 | | #[derive(Clone, Debug)] |
99 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
100 | | pub struct Package { |
101 | | /// A unique name corresponding to this package. |
102 | | pub name: PackageName, |
103 | | |
104 | | /// Documentation associated with this package. |
105 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
106 | | pub docs: Docs, |
107 | | |
108 | | /// All interfaces contained in this packaged, keyed by the interface's |
109 | | /// name. |
110 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] |
111 | | pub interfaces: IndexMap<String, InterfaceId>, |
112 | | |
113 | | /// All worlds contained in this package, keyed by the world's name. |
114 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] |
115 | | pub worlds: IndexMap<String, WorldId>, |
116 | | } |
117 | | |
118 | | pub type PackageId = Id<Package>; |
119 | | |
120 | | /// All the sources used during resolving a directory or path. |
121 | | #[derive(Clone, Debug)] |
122 | | pub struct PackageSourceMap { |
123 | | sources: Vec<Vec<PathBuf>>, |
124 | | package_id_to_source_map_idx: BTreeMap<PackageId, usize>, |
125 | | } |
126 | | |
127 | | impl PackageSourceMap { |
128 | 0 | fn from_single_source(package_id: PackageId, source: &Path) -> Self { |
129 | 0 | Self { |
130 | 0 | sources: vec![vec![source.to_path_buf()]], |
131 | 0 | package_id_to_source_map_idx: BTreeMap::from([(package_id, 0)]), |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | 12.6k | fn from_source_maps( |
136 | 12.6k | source_maps: Vec<SourceMap>, |
137 | 12.6k | package_id_to_source_map_idx: BTreeMap<PackageId, usize>, |
138 | 12.6k | ) -> PackageSourceMap { |
139 | 25.7k | for (package_id, idx) in &package_id_to_source_map_idx { |
140 | 13.1k | if *idx >= source_maps.len() { |
141 | 0 | panic!( |
142 | 0 | "Invalid source map index: {}, package id: {:?}, source maps size: {}", |
143 | | idx, |
144 | | package_id, |
145 | 0 | source_maps.len() |
146 | | ) |
147 | 13.1k | } |
148 | | } |
149 | | |
150 | | Self { |
151 | 12.6k | sources: source_maps |
152 | 12.6k | .into_iter() |
153 | 12.6k | .map(|source_map| { |
154 | 12.6k | source_map |
155 | 12.6k | .source_files() |
156 | 21.7k | .map(|path| path.to_path_buf()) |
157 | 12.6k | .collect() |
158 | 12.6k | }) |
159 | 12.6k | .collect(), |
160 | 12.6k | package_id_to_source_map_idx, |
161 | | } |
162 | 12.6k | } |
163 | | |
164 | | /// All unique source paths. |
165 | 0 | pub fn paths(&self) -> impl Iterator<Item = &Path> { |
166 | | // Usually any two source map should not have duplicated source paths, |
167 | | // but it can happen, e.g. with using [`Resolve::push_str`] directly. |
168 | | // To be sure we use a set for deduplication here. |
169 | 0 | self.sources |
170 | 0 | .iter() |
171 | 0 | .flatten() |
172 | 0 | .map(|path_buf| path_buf.as_ref()) |
173 | 0 | .collect::<IndexSet<&Path>>() |
174 | 0 | .into_iter() |
175 | 0 | } |
176 | | |
177 | | /// Source paths for package |
178 | 0 | pub fn package_paths(&self, id: PackageId) -> Option<impl Iterator<Item = &Path>> { |
179 | 0 | self.package_id_to_source_map_idx |
180 | 0 | .get(&id) |
181 | 0 | .map(|&idx| self.sources[idx].iter().map(|path_buf| path_buf.as_ref())) |
182 | 0 | } |
183 | | } |
184 | | |
185 | | enum ParsedFile { |
186 | | #[cfg(feature = "decoding")] |
187 | | Package(PackageId), |
188 | | Unresolved(UnresolvedPackageGroup), |
189 | | } |
190 | | |
191 | | /// Visitor helper for performing topological sort on a group of packages. |
192 | 13.6k | fn visit<'a>( |
193 | 13.6k | pkg: &'a UnresolvedPackage, |
194 | 13.6k | pkg_details_map: &'a BTreeMap<PackageName, (UnresolvedPackage, usize)>, |
195 | 13.6k | order: &mut IndexSet<PackageName>, |
196 | 13.6k | visiting: &mut HashSet<&'a PackageName>, |
197 | 13.6k | source_maps: &[SourceMap], |
198 | 13.6k | ) -> Result<()> { |
199 | 13.6k | if order.contains(&pkg.name) { |
200 | 508 | return Ok(()); |
201 | 13.1k | } |
202 | | |
203 | 13.1k | match pkg_details_map.get(&pkg.name) { |
204 | 13.1k | Some(pkg_details) => { |
205 | 13.1k | let (_, source_maps_index) = pkg_details; |
206 | 13.1k | source_maps[*source_maps_index].rewrite_error(|| { |
207 | 13.1k | for (i, (dep, _)) in pkg.foreign_deps.iter().enumerate() { |
208 | 1.87k | let span = pkg.foreign_dep_spans[i]; |
209 | 1.87k | if !visiting.insert(dep) { |
210 | 0 | bail!(Error::new(span, "package depends on itself")); |
211 | 1.87k | } |
212 | 1.87k | if let Some(dep) = pkg_details_map.get(dep) { |
213 | 508 | let (dep_pkg, _) = dep; |
214 | 508 | visit(dep_pkg, pkg_details_map, order, visiting, source_maps)?; |
215 | 1.36k | } |
216 | 1.87k | assert!(visiting.remove(dep)); |
217 | | } |
218 | 13.1k | assert!(order.insert(pkg.name.clone())); |
219 | 13.1k | Ok(()) |
220 | 13.1k | }) |
221 | | } |
222 | 0 | None => panic!("No pkg_details found for package when doing topological sort"), |
223 | | } |
224 | 13.6k | } |
225 | | |
226 | | impl Resolve { |
227 | | /// Creates a new [`Resolve`] with no packages/items inside of it. |
228 | 0 | pub fn new() -> Resolve { |
229 | 0 | Resolve::default() |
230 | 0 | } |
231 | | |
232 | | /// Parse WIT packages from the input `path`. |
233 | | /// |
234 | | /// The input `path` can be one of: |
235 | | /// |
236 | | /// * A directory containing a WIT package with an optional `deps` directory |
237 | | /// for any dependent WIT packages it references. |
238 | | /// * A single standalone WIT file. |
239 | | /// * A wasm-encoded WIT package as a single file in the wasm binary format. |
240 | | /// * A wasm-encoded WIT package as a single file in the wasm text format. |
241 | | /// |
242 | | /// In all of these cases packages are allowed to depend on previously |
243 | | /// inserted packages into this `Resolve`. Resolution for packages is based |
244 | | /// on the name of each package and reference. |
245 | | /// |
246 | | /// This method returns a `PackageId` and additionally a `PackageSourceMap`. |
247 | | /// The `PackageId` represent the main package that was parsed. For example if a single WIT |
248 | | /// file was specified this will be the main package found in the file. For a directory this |
249 | | /// will be all the main package in the directory itself. The `PackageId` value is useful |
250 | | /// to pass to [`Resolve::select_world`] to take a user-specified world in a |
251 | | /// conventional fashion and select which to use for bindings generation. |
252 | | /// |
253 | | /// The returned [`PackageSourceMap`] contains all the sources used during this operation. |
254 | | /// This can be useful for systems that want to rebuild or regenerate bindings based on files modified, |
255 | | /// or for ones which like to identify the used files for a package. |
256 | | /// |
257 | | /// More information can also be found at [`Resolve::push_dir`] and |
258 | | /// [`Resolve::push_file`]. |
259 | | pub fn push_path(&mut self, path: impl AsRef<Path>) -> Result<(PackageId, PackageSourceMap)> { |
260 | | self._push_path(path.as_ref()) |
261 | | } |
262 | | |
263 | 0 | fn _push_path(&mut self, path: &Path) -> Result<(PackageId, PackageSourceMap)> { |
264 | 0 | if path.is_dir() { |
265 | 0 | self.push_dir(path).with_context(|| { |
266 | 0 | format!( |
267 | 0 | "failed to resolve directory while parsing WIT for path [{}]", |
268 | 0 | path.display() |
269 | | ) |
270 | 0 | }) |
271 | | } else { |
272 | 0 | let id = self.push_file(path)?; |
273 | 0 | Ok((id, PackageSourceMap::from_single_source(id, path))) |
274 | | } |
275 | 0 | } |
276 | | |
277 | 12.6k | fn sort_unresolved_packages( |
278 | 12.6k | &mut self, |
279 | 12.6k | main: UnresolvedPackageGroup, |
280 | 12.6k | deps: Vec<UnresolvedPackageGroup>, |
281 | 12.6k | ) -> Result<(PackageId, PackageSourceMap)> { |
282 | 12.6k | let mut pkg_details_map = BTreeMap::new(); |
283 | 12.6k | let mut source_maps = Vec::new(); |
284 | | |
285 | 12.6k | let mut insert = |group: UnresolvedPackageGroup| { |
286 | | let UnresolvedPackageGroup { |
287 | 12.6k | main, |
288 | 12.6k | nested, |
289 | 12.6k | source_map, |
290 | 12.6k | } = group; |
291 | 12.6k | let i = source_maps.len(); |
292 | 12.6k | source_maps.push(source_map); |
293 | | |
294 | 13.1k | for pkg in nested.into_iter().chain([main]) { |
295 | 13.1k | let name = pkg.name.clone(); |
296 | 13.1k | let my_span = pkg.package_name_span; |
297 | 13.1k | let (prev_pkg, prev_i) = match pkg_details_map.insert(name.clone(), (pkg, i)) { |
298 | 0 | Some(pair) => pair, |
299 | 13.1k | None => continue, |
300 | | }; |
301 | 0 | let loc1 = source_maps[i].render_location(my_span); |
302 | 0 | let loc2 = source_maps[prev_i].render_location(prev_pkg.package_name_span); |
303 | 0 | bail!( |
304 | 0 | "\ |
305 | 0 | package {name} is defined in two different locations:\n\ |
306 | 0 | * {loc1}\n\ |
307 | 0 | * {loc2}\n\ |
308 | 0 | " |
309 | | ) |
310 | | } |
311 | 12.6k | Ok(()) |
312 | 12.6k | }; |
313 | | |
314 | 12.6k | let main_name = main.main.name.clone(); |
315 | 12.6k | insert(main)?; |
316 | 12.6k | for dep in deps { |
317 | 0 | insert(dep)?; |
318 | | } |
319 | | |
320 | | // Perform a simple topological sort which will bail out on cycles |
321 | | // and otherwise determine the order that packages must be added to |
322 | | // this `Resolve`. |
323 | 12.6k | let mut order = IndexSet::new(); |
324 | 12.6k | let mut visiting = HashSet::new(); |
325 | 13.1k | for pkg_details in pkg_details_map.values() { |
326 | 13.1k | let (pkg, _) = pkg_details; |
327 | 13.1k | visit( |
328 | 13.1k | pkg, |
329 | 13.1k | &pkg_details_map, |
330 | 13.1k | &mut order, |
331 | 13.1k | &mut visiting, |
332 | 13.1k | &source_maps, |
333 | 0 | )?; |
334 | | } |
335 | | |
336 | | // Ensure that the final output is topologically sorted. Use a set to ensure that we render |
337 | | // the buffers for each `SourceMap` only once, even though multiple packages may reference |
338 | | // the same `SourceMap`. |
339 | 12.6k | let mut package_id_to_source_map_idx = BTreeMap::new(); |
340 | 12.6k | let mut main_pkg_id = None; |
341 | 25.7k | for name in order { |
342 | 13.1k | let (pkg, source_map_index) = pkg_details_map.remove(&name).unwrap(); |
343 | 13.1k | let source_map = &source_maps[source_map_index]; |
344 | 13.1k | let is_main = pkg.name == main_name; |
345 | 13.1k | let id = self.push(pkg, source_map)?; |
346 | 13.1k | if is_main { |
347 | 12.6k | assert!(main_pkg_id.is_none()); |
348 | 12.6k | main_pkg_id = Some(id); |
349 | 490 | } |
350 | 13.1k | package_id_to_source_map_idx.insert(id, source_map_index); |
351 | | } |
352 | | |
353 | 12.6k | Ok(( |
354 | 12.6k | main_pkg_id.unwrap(), |
355 | 12.6k | PackageSourceMap::from_source_maps(source_maps, package_id_to_source_map_idx), |
356 | 12.6k | )) |
357 | 12.6k | } |
358 | | |
359 | | /// Parses the filesystem directory at `path` as a WIT package and returns |
360 | | /// a fully resolved [`PackageId`] list as a result. |
361 | | /// |
362 | | /// The directory itself is parsed with [`UnresolvedPackageGroup::parse_dir`] |
363 | | /// and then all packages found are inserted into this `Resolve`. The `path` |
364 | | /// specified may have a `deps` subdirectory which is probed automatically |
365 | | /// for any other WIT dependencies. |
366 | | /// |
367 | | /// The `deps` folder may contain: |
368 | | /// |
369 | | /// * `$path/deps/my-package/*.wit` - a directory that may contain multiple |
370 | | /// WIT files. This is parsed with [`UnresolvedPackageGroup::parse_dir`] |
371 | | /// and then inserted into this [`Resolve`]. Note that cannot recursively |
372 | | /// contain a `deps` directory. |
373 | | /// * `$path/deps/my-package.wit` - a single-file WIT package. This is |
374 | | /// parsed with [`Resolve::push_file`] and then added to `self` for |
375 | | /// name resolution. |
376 | | /// * `$path/deps/my-package.{wasm,wat}` - a wasm-encoded WIT package either |
377 | | /// in the text for binary format. |
378 | | /// |
379 | | /// In all cases entries in the `deps` folder are added to `self` first |
380 | | /// before adding files found in `path` itself. All WIT packages found are |
381 | | /// candidates for name-based resolution that other packages may use. |
382 | | /// |
383 | | /// This function returns a tuple of two values. The first value is a |
384 | | /// [`PackageId`], which represents the main WIT package found within |
385 | | /// `path`. This argument is useful for passing to [`Resolve::select_world`] |
386 | | /// for choosing something to bindgen with. |
387 | | /// |
388 | | /// The second value returned is a [`PackageSourceMap`], which contains all the sources |
389 | | /// that were parsed during resolving. This can be useful for: |
390 | | /// * build systems that want to rebuild bindings whenever one of the files changed |
391 | | /// * or other tools, which want to identify the sources for the resolved packages |
392 | 0 | pub fn push_dir(&mut self, path: impl AsRef<Path>) -> Result<(PackageId, PackageSourceMap)> { |
393 | 0 | self._push_dir(path.as_ref()) |
394 | 0 | } |
395 | | |
396 | 0 | fn _push_dir(&mut self, path: &Path) -> Result<(PackageId, PackageSourceMap)> { |
397 | 0 | let top_pkg = UnresolvedPackageGroup::parse_dir(path) |
398 | 0 | .with_context(|| format!("failed to parse package: {}", path.display()))?; |
399 | 0 | let deps = path.join("deps"); |
400 | 0 | let deps = self |
401 | 0 | .parse_deps_dir(&deps) |
402 | 0 | .with_context(|| format!("failed to parse dependency directory: {}", deps.display()))?; |
403 | | |
404 | 0 | self.sort_unresolved_packages(top_pkg, deps) |
405 | 0 | } |
406 | | |
407 | 0 | fn parse_deps_dir(&mut self, path: &Path) -> Result<Vec<UnresolvedPackageGroup>> { |
408 | 0 | let mut ret = Vec::new(); |
409 | 0 | if !path.exists() { |
410 | 0 | return Ok(ret); |
411 | 0 | } |
412 | 0 | let mut entries = path |
413 | 0 | .read_dir() |
414 | 0 | .and_then(|i| i.collect::<std::io::Result<Vec<_>>>()) |
415 | 0 | .context("failed to read directory")?; |
416 | 0 | entries.sort_by_key(|e| e.file_name()); |
417 | 0 | for dep in entries { |
418 | 0 | let path = dep.path(); |
419 | 0 | let pkg = if dep.file_type()?.is_dir() || path.metadata()?.is_dir() { |
420 | | // If this entry is a directory or a symlink point to a |
421 | | // directory then always parse it as an `UnresolvedPackage` |
422 | | // since it's intentional to not support recursive `deps` |
423 | | // directories. |
424 | 0 | UnresolvedPackageGroup::parse_dir(&path) |
425 | 0 | .with_context(|| format!("failed to parse package: {}", path.display()))? |
426 | | } else { |
427 | | // If this entry is a file then we may want to ignore it but |
428 | | // this may also be a standalone WIT file or a `*.wasm` or |
429 | | // `*.wat` encoded package. |
430 | 0 | let filename = dep.file_name(); |
431 | 0 | match Path::new(&filename).extension().and_then(|s| s.to_str()) { |
432 | 0 | Some("wit") | Some("wat") | Some("wasm") => match self._push_file(&path)? { |
433 | | #[cfg(feature = "decoding")] |
434 | 0 | ParsedFile::Package(_) => continue, |
435 | 0 | ParsedFile::Unresolved(pkg) => pkg, |
436 | | }, |
437 | | |
438 | | // Other files in deps dir are ignored for now to avoid |
439 | | // accidentally including things like `.DS_Store` files in |
440 | | // the call below to `parse_dir`. |
441 | 0 | _ => continue, |
442 | | } |
443 | | }; |
444 | 0 | ret.push(pkg); |
445 | | } |
446 | 0 | Ok(ret) |
447 | 0 | } |
448 | | |
449 | | /// Parses the contents of `path` from the filesystem and pushes the result |
450 | | /// into this `Resolve`. |
451 | | /// |
452 | | /// The `path` referenced here can be one of: |
453 | | /// |
454 | | /// * A WIT file. Note that in this case this single WIT file will be the |
455 | | /// entire package and any dependencies it has must already be in `self`. |
456 | | /// * A WIT package encoded as WebAssembly, either in text or binary form. |
457 | | /// In this the package and all of its dependencies are automatically |
458 | | /// inserted into `self`. |
459 | | /// |
460 | | /// In both situations the `PackageId`s of the resulting resolved packages |
461 | | /// are returned from this method. The return value is mostly useful in |
462 | | /// conjunction with [`Resolve::select_world`]. |
463 | 0 | pub fn push_file(&mut self, path: impl AsRef<Path>) -> Result<PackageId> { |
464 | 0 | match self._push_file(path.as_ref())? { |
465 | | #[cfg(feature = "decoding")] |
466 | 0 | ParsedFile::Package(id) => Ok(id), |
467 | 0 | ParsedFile::Unresolved(pkg) => self.push_group(pkg), |
468 | | } |
469 | 0 | } |
470 | | |
471 | 0 | fn _push_file(&mut self, path: &Path) -> Result<ParsedFile> { |
472 | 0 | let contents = std::fs::read(path) |
473 | 0 | .with_context(|| format!("failed to read path for WIT [{}]", path.display()))?; |
474 | | |
475 | | // If decoding is enabled at compile time then try to see if this is a |
476 | | // wasm file. |
477 | | #[cfg(feature = "decoding")] |
478 | | { |
479 | | use crate::decoding::{DecodedWasm, decode}; |
480 | | |
481 | | #[cfg(feature = "wat")] |
482 | | let is_wasm = wat::Detect::from_bytes(&contents).is_wasm(); |
483 | | #[cfg(not(feature = "wat"))] |
484 | 0 | let is_wasm = wasmparser::Parser::is_component(&contents); |
485 | | |
486 | 0 | if is_wasm { |
487 | | #[cfg(feature = "wat")] |
488 | | let contents = wat::parse_bytes(&contents).map_err(|mut e| { |
489 | | e.set_path(path); |
490 | | e |
491 | | })?; |
492 | | |
493 | 0 | match decode(&contents)? { |
494 | | DecodedWasm::Component(..) => { |
495 | 0 | bail!("found an actual component instead of an encoded WIT package in wasm") |
496 | | } |
497 | 0 | DecodedWasm::WitPackage(resolve, pkg) => { |
498 | 0 | let remap = self.merge(resolve)?; |
499 | 0 | return Ok(ParsedFile::Package(remap.packages[pkg.index()])); |
500 | | } |
501 | | } |
502 | 0 | } |
503 | | } |
504 | | |
505 | | // If this wasn't a wasm file then assume it's a WIT file. |
506 | 0 | let text = match std::str::from_utf8(&contents) { |
507 | 0 | Ok(s) => s, |
508 | 0 | Err(_) => bail!("input file is not valid utf-8 [{}]", path.display()), |
509 | | }; |
510 | 0 | let pkgs = UnresolvedPackageGroup::parse(path, text)?; |
511 | 0 | Ok(ParsedFile::Unresolved(pkgs)) |
512 | 0 | } |
513 | | |
514 | | /// Appends a new [`UnresolvedPackage`] to this [`Resolve`], creating a |
515 | | /// fully resolved package with no dangling references. |
516 | | /// |
517 | | /// All the dependencies of `unresolved` must already have been loaded |
518 | | /// within this `Resolve` via previous calls to `push` or other methods such |
519 | | /// as [`Resolve::push_path`]. |
520 | | /// |
521 | | /// Any dependency resolution error or otherwise world-elaboration error |
522 | | /// will be returned here, if successful a package identifier is returned |
523 | | /// which corresponds to the package that was just inserted. |
524 | 13.1k | pub fn push( |
525 | 13.1k | &mut self, |
526 | 13.1k | unresolved: UnresolvedPackage, |
527 | 13.1k | source_map: &SourceMap, |
528 | 13.1k | ) -> Result<PackageId> { |
529 | 13.1k | let ret = source_map.rewrite_error(|| Remap::default().append(self, unresolved)); |
530 | 13.1k | if ret.is_ok() { |
531 | 13.1k | #[cfg(debug_assertions)] |
532 | 13.1k | self.assert_valid(); |
533 | 13.1k | } |
534 | 13.1k | ret |
535 | 13.1k | } |
536 | | |
537 | | /// Appends new [`UnresolvedPackageGroup`] to this [`Resolve`], creating a |
538 | | /// fully resolved package with no dangling references. |
539 | | /// |
540 | | /// Any dependency resolution error or otherwise world-elaboration error |
541 | | /// will be returned here, if successful a package identifier is returned |
542 | | /// which corresponds to the package that was just inserted. |
543 | | /// |
544 | | /// The returned [`PackageId`]s are listed in topologically sorted order. |
545 | 12.6k | pub fn push_group(&mut self, unresolved_group: UnresolvedPackageGroup) -> Result<PackageId> { |
546 | 12.6k | let (pkg_id, _) = self.sort_unresolved_packages(unresolved_group, Vec::new())?; |
547 | 12.6k | Ok(pkg_id) |
548 | 12.6k | } |
549 | | |
550 | | /// Convenience method for combining [`UnresolvedPackageGroup::parse`] and |
551 | | /// [`Resolve::push_group`]. |
552 | | /// |
553 | | /// The `path` provided is used for error messages but otherwise is not |
554 | | /// read. This method does not touch the filesystem. The `contents` provided |
555 | | /// are the contents of a WIT package. |
556 | 5.46k | pub fn push_str(&mut self, path: impl AsRef<Path>, contents: &str) -> Result<PackageId> { |
557 | 5.46k | self.push_group(UnresolvedPackageGroup::parse(path.as_ref(), contents)?) |
558 | 5.46k | } |
559 | | |
560 | 0 | pub fn all_bits_valid(&self, ty: &Type) -> bool { |
561 | 0 | match ty { |
562 | | Type::U8 |
563 | | | Type::S8 |
564 | | | Type::U16 |
565 | | | Type::S16 |
566 | | | Type::U32 |
567 | | | Type::S32 |
568 | | | Type::U64 |
569 | | | Type::S64 |
570 | | | Type::F32 |
571 | 0 | | Type::F64 => true, |
572 | | |
573 | 0 | Type::Bool | Type::Char | Type::String | Type::ErrorContext => false, |
574 | | |
575 | 0 | Type::Id(id) => match &self.types[*id].kind { |
576 | | TypeDefKind::List(_) |
577 | | | TypeDefKind::Variant(_) |
578 | | | TypeDefKind::Enum(_) |
579 | | | TypeDefKind::Option(_) |
580 | | | TypeDefKind::Result(_) |
581 | | | TypeDefKind::Future(_) |
582 | 0 | | TypeDefKind::Stream(_) => false, |
583 | 0 | TypeDefKind::Type(t) | TypeDefKind::FixedSizeList(t, ..) => self.all_bits_valid(t), |
584 | | |
585 | 0 | TypeDefKind::Handle(h) => match h { |
586 | 0 | crate::Handle::Own(_) => true, |
587 | 0 | crate::Handle::Borrow(_) => true, |
588 | | }, |
589 | | |
590 | 0 | TypeDefKind::Resource => false, |
591 | 0 | TypeDefKind::Record(r) => r.fields.iter().all(|f| self.all_bits_valid(&f.ty)), |
592 | 0 | TypeDefKind::Tuple(t) => t.types.iter().all(|t| self.all_bits_valid(t)), |
593 | | |
594 | | // FIXME: this could perhaps be `true` for multiples-of-32 but |
595 | | // seems better to probably leave this as unconditionally |
596 | | // `false` for now, may want to reconsider later? |
597 | 0 | TypeDefKind::Flags(_) => false, |
598 | | |
599 | 0 | TypeDefKind::Unknown => unreachable!(), |
600 | | }, |
601 | | } |
602 | 0 | } |
603 | | |
604 | | /// Merges all the contents of a different `Resolve` into this one. The |
605 | | /// `Remap` structure returned provides a mapping from all old indices to |
606 | | /// new indices |
607 | | /// |
608 | | /// This operation can fail if `resolve` disagrees with `self` about the |
609 | | /// packages being inserted. Otherwise though this will additionally attempt |
610 | | /// to "union" packages found in `resolve` with those found in `self`. |
611 | | /// Unioning packages is keyed on the name/url of packages for those with |
612 | | /// URLs present. If found then it's assumed that both `Resolve` instances |
613 | | /// were originally created from the same contents and are two views |
614 | | /// of the same package. |
615 | 12.3k | pub fn merge(&mut self, resolve: Resolve) -> Result<Remap> { |
616 | 12.3k | log::trace!( |
617 | 0 | "merging {} packages into {} packages", |
618 | 0 | resolve.packages.len(), |
619 | 0 | self.packages.len() |
620 | | ); |
621 | | |
622 | 12.3k | let mut map = MergeMap::new(&resolve, &self); |
623 | 12.3k | map.build()?; |
624 | | let MergeMap { |
625 | 12.3k | package_map, |
626 | 12.3k | interface_map, |
627 | 12.3k | type_map, |
628 | 12.3k | world_map, |
629 | 12.3k | interfaces_to_add, |
630 | 12.3k | worlds_to_add, |
631 | | .. |
632 | 12.3k | } = map; |
633 | | |
634 | | // With a set of maps from ids in `resolve` to ids in `self` the next |
635 | | // operation is to start moving over items and building a `Remap` to |
636 | | // update ids. |
637 | | // |
638 | | // Each component field of `resolve` is moved into `self` so long as |
639 | | // its ID is not within one of the maps above. If it's present in a map |
640 | | // above then that means the item is already present in `self` so a new |
641 | | // one need not be added. If it's not present in a map that means it's |
642 | | // not present in `self` so it must be added to an arena. |
643 | | // |
644 | | // When adding an item to an arena one of the `remap.update_*` methods |
645 | | // is additionally called to update all identifiers from pointers within |
646 | | // `resolve` to becoming pointers within `self`. |
647 | | // |
648 | | // Altogether this should weave all the missing items in `self` from |
649 | | // `resolve` into one structure while updating all identifiers to |
650 | | // be local within `self`. |
651 | | |
652 | 12.3k | let mut remap = Remap::default(); |
653 | | let Resolve { |
654 | 12.3k | types, |
655 | 12.3k | worlds, |
656 | 12.3k | interfaces, |
657 | 12.3k | packages, |
658 | 12.3k | package_names, |
659 | | features: _, |
660 | | .. |
661 | 12.3k | } = resolve; |
662 | | |
663 | 12.3k | let mut moved_types = Vec::new(); |
664 | 69.4k | for (id, mut ty) in types { |
665 | 57.1k | let new_id = match type_map.get(&id).copied() { |
666 | 10 | Some(id) => { |
667 | 10 | update_stability(&ty.stability, &mut self.types[id].stability)?; |
668 | 10 | id |
669 | | } |
670 | | None => { |
671 | 57.0k | log::debug!("moving type {:?}", ty.name); |
672 | 57.0k | moved_types.push(id); |
673 | 57.0k | remap.update_typedef(self, &mut ty, None)?; |
674 | 57.0k | self.types.alloc(ty) |
675 | | } |
676 | | }; |
677 | 57.1k | assert_eq!(remap.types.len(), id.index()); |
678 | 57.1k | remap.types.push(Some(new_id)); |
679 | | } |
680 | | |
681 | 12.3k | let mut moved_interfaces = Vec::new(); |
682 | 16.3k | for (id, mut iface) in interfaces { |
683 | 4.04k | let new_id = match interface_map.get(&id).copied() { |
684 | 6 | Some(id) => { |
685 | 6 | update_stability(&iface.stability, &mut self.interfaces[id].stability)?; |
686 | 6 | id |
687 | | } |
688 | | None => { |
689 | 4.03k | log::debug!("moving interface {:?}", iface.name); |
690 | 4.03k | moved_interfaces.push(id); |
691 | 4.03k | remap.update_interface(self, &mut iface, None)?; |
692 | 4.03k | self.interfaces.alloc(iface) |
693 | | } |
694 | | }; |
695 | 4.04k | assert_eq!(remap.interfaces.len(), id.index()); |
696 | 4.04k | remap.interfaces.push(Some(new_id)); |
697 | | } |
698 | | |
699 | 12.3k | let mut moved_worlds = Vec::new(); |
700 | 28.8k | for (id, mut world) in worlds { |
701 | 16.4k | let new_id = match world_map.get(&id).copied() { |
702 | 0 | Some(world_id) => { |
703 | 0 | update_stability(&world.stability, &mut self.worlds[world_id].stability)?; |
704 | 0 | for from_import in world.imports.iter() { |
705 | 0 | Resolve::update_world_imports_stability( |
706 | 0 | from_import, |
707 | 0 | &mut self.worlds[world_id].imports, |
708 | 0 | &interface_map, |
709 | 0 | )?; |
710 | | } |
711 | 0 | for from_export in world.exports.iter() { |
712 | 0 | Resolve::update_world_imports_stability( |
713 | 0 | from_export, |
714 | 0 | &mut self.worlds[world_id].exports, |
715 | 0 | &interface_map, |
716 | 0 | )?; |
717 | | } |
718 | 0 | world_id |
719 | | } |
720 | | None => { |
721 | 16.4k | log::debug!("moving world {}", world.name); |
722 | 16.4k | moved_worlds.push(id); |
723 | 32.9k | let mut update = |map: &mut IndexMap<WorldKey, WorldItem>| -> Result<_> { |
724 | 32.9k | for (mut name, mut item) in mem::take(map) { |
725 | 24.6k | remap.update_world_key(&mut name, None)?; |
726 | 24.6k | match &mut item { |
727 | 5.00k | WorldItem::Function(f) => remap.update_function(self, f, None)?, |
728 | 4.54k | WorldItem::Interface { id, .. } => { |
729 | 4.54k | *id = remap.map_interface(*id, None)? |
730 | | } |
731 | 15.1k | WorldItem::Type(i) => *i = remap.map_type(*i, None)?, |
732 | | } |
733 | 24.6k | map.insert(name, item); |
734 | | } |
735 | 32.9k | Ok(()) |
736 | 32.9k | }; |
737 | 16.4k | update(&mut world.imports)?; |
738 | 16.4k | update(&mut world.exports)?; |
739 | 16.4k | self.worlds.alloc(world) |
740 | | } |
741 | | }; |
742 | 16.4k | assert_eq!(remap.worlds.len(), id.index()); |
743 | 16.4k | remap.worlds.push(Some(new_id)); |
744 | | } |
745 | | |
746 | 29.1k | for (id, mut pkg) in packages { |
747 | 16.8k | let new_id = match package_map.get(&id).copied() { |
748 | 105 | Some(id) => id, |
749 | | None => { |
750 | 16.7k | for (_, id) in pkg.interfaces.iter_mut() { |
751 | 1.15k | *id = remap.map_interface(*id, None)?; |
752 | | } |
753 | 16.7k | for (_, id) in pkg.worlds.iter_mut() { |
754 | 16.3k | *id = remap.map_world(*id, None)?; |
755 | | } |
756 | 16.7k | self.packages.alloc(pkg) |
757 | | } |
758 | | }; |
759 | 16.8k | assert_eq!(remap.packages.len(), id.index()); |
760 | 16.8k | remap.packages.push(new_id); |
761 | | } |
762 | | |
763 | 25.0k | for (name, id) in package_names { |
764 | 12.6k | let id = remap.packages[id.index()]; |
765 | 12.6k | if let Some(prev) = self.package_names.insert(name, id) { |
766 | 105 | assert_eq!(prev, id); |
767 | 12.5k | } |
768 | | } |
769 | | |
770 | | // Fixup all "parent" links now. |
771 | | // |
772 | | // Note that this is only done for items that are actually moved from |
773 | | // `resolve` into `self`, which is tracked by the various `moved_*` |
774 | | // lists built incrementally above. The ids in the `moved_*` lists |
775 | | // are ids within `resolve`, so they're translated through `remap` to |
776 | | // ids within `self`. |
777 | 28.8k | for id in moved_worlds { |
778 | 16.4k | let id = remap.map_world(id, None)?; |
779 | 16.4k | if let Some(pkg) = self.worlds[id].package.as_mut() { |
780 | 16.4k | *pkg = remap.packages[pkg.index()]; |
781 | 16.4k | } |
782 | | } |
783 | 16.3k | for id in moved_interfaces { |
784 | 4.03k | let id = remap.map_interface(id, None)?; |
785 | 4.03k | if let Some(pkg) = self.interfaces[id].package.as_mut() { |
786 | 4.03k | *pkg = remap.packages[pkg.index()]; |
787 | 4.03k | } |
788 | | } |
789 | 69.4k | for id in moved_types { |
790 | 57.0k | let id = remap.map_type(id, None)?; |
791 | 57.0k | match &mut self.types[id].owner { |
792 | 6.49k | TypeOwner::Interface(id) => *id = remap.map_interface(*id, None)?, |
793 | 15.1k | TypeOwner::World(id) => *id = remap.map_world(*id, None)?, |
794 | 35.4k | TypeOwner::None => {} |
795 | | } |
796 | | } |
797 | | |
798 | | // And finally process items that were present in `resolve` but were |
799 | | // not present in `self`. This is only done for merged packages as |
800 | | // documents may be added to `self.documents` but wouldn't otherwise be |
801 | | // present in the `documents` field of the corresponding package. |
802 | 12.3k | for (name, pkg, iface) in interfaces_to_add { |
803 | 8 | let prev = self.packages[pkg] |
804 | 8 | .interfaces |
805 | 8 | .insert(name, remap.map_interface(iface, None)?); |
806 | 8 | assert!(prev.is_none()); |
807 | | } |
808 | 12.4k | for (name, pkg, world) in worlds_to_add { |
809 | 103 | let prev = self.packages[pkg] |
810 | 103 | .worlds |
811 | 103 | .insert(name, remap.map_world(world, None)?); |
812 | 103 | assert!(prev.is_none()); |
813 | | } |
814 | | |
815 | 12.3k | log::trace!("now have {} packages", self.packages.len()); |
816 | | |
817 | | #[cfg(debug_assertions)] |
818 | | self.assert_valid(); |
819 | 12.3k | Ok(remap) |
820 | 12.3k | } |
821 | | |
822 | 0 | fn update_world_imports_stability( |
823 | 0 | from_item: (&WorldKey, &WorldItem), |
824 | 0 | into_items: &mut IndexMap<WorldKey, WorldItem>, |
825 | 0 | interface_map: &HashMap<Id<Interface>, Id<Interface>>, |
826 | 0 | ) -> Result<()> { |
827 | 0 | match from_item.0 { |
828 | | WorldKey::Name(_) => { |
829 | | // No stability info to update here, only updating import/include stability |
830 | 0 | Ok(()) |
831 | | } |
832 | 0 | key @ WorldKey::Interface(_) => { |
833 | 0 | let new_key = MergeMap::map_name(key, interface_map); |
834 | 0 | if let Some(into) = into_items.get_mut(&new_key) { |
835 | 0 | match (from_item.1, into) { |
836 | | ( |
837 | | WorldItem::Interface { |
838 | 0 | id: aid, |
839 | 0 | stability: astability, |
840 | | }, |
841 | | WorldItem::Interface { |
842 | 0 | id: bid, |
843 | 0 | stability: bstability, |
844 | | }, |
845 | | ) => { |
846 | 0 | let aid = interface_map.get(aid).copied().unwrap_or(*aid); |
847 | 0 | assert_eq!(aid, *bid); |
848 | 0 | update_stability(astability, bstability)?; |
849 | 0 | Ok(()) |
850 | | } |
851 | 0 | _ => unreachable!(), |
852 | | } |
853 | | } else { |
854 | | // we've already matched all the imports/exports by the time we are calling this |
855 | | // so this is unreachable since we should always find the item |
856 | 0 | unreachable!() |
857 | | } |
858 | | } |
859 | | } |
860 | 0 | } |
861 | | |
862 | | /// Merges the world `from` into the world `into`. |
863 | | /// |
864 | | /// This will attempt to merge one world into another, unioning all of its |
865 | | /// imports and exports together. This is an operation performed by |
866 | | /// `wit-component`, for example where two different worlds from two |
867 | | /// different libraries were linked into the same core wasm file and are |
868 | | /// producing a singular world that will be the final component's |
869 | | /// interface. |
870 | | /// |
871 | | /// During the merge operation, some of the types and/or interfaces in |
872 | | /// `from` might need to be cloned so that backreferences point to `into` |
873 | | /// instead of `from`. Any such clones will be added to `clone_maps`. |
874 | | /// |
875 | | /// This operation can fail if the imports/exports overlap. |
876 | 12.3k | pub fn merge_worlds( |
877 | 12.3k | &mut self, |
878 | 12.3k | from: WorldId, |
879 | 12.3k | into: WorldId, |
880 | 12.3k | clone_maps: &mut CloneMaps, |
881 | 12.3k | ) -> Result<()> { |
882 | 12.3k | let mut new_imports = Vec::new(); |
883 | 12.3k | let mut new_exports = Vec::new(); |
884 | | |
885 | 12.3k | let from_world = &self.worlds[from]; |
886 | 12.3k | let into_world = &self.worlds[into]; |
887 | | |
888 | 12.3k | log::trace!("merging {} into {}", from_world.name, into_world.name); |
889 | | |
890 | | // First walk over all the imports of `from` world and figure out what |
891 | | // to do with them. |
892 | | // |
893 | | // If the same item exists in `from` and `into` then merge it together |
894 | | // below with `merge_world_item` which basically asserts they're the |
895 | | // same. Otherwise queue up a new import since if `from` has more |
896 | | // imports than `into` then it's fine to add new imports. |
897 | 16.1k | for (name, from_import) in from_world.imports.iter() { |
898 | 16.1k | let name_str = self.name_world_key(name); |
899 | 16.1k | match into_world.imports.get(name) { |
900 | 62 | Some(into_import) => { |
901 | 62 | log::trace!("info/from shared import on `{name_str}`"); |
902 | 62 | self.merge_world_item(from_import, into_import) |
903 | 62 | .with_context(|| format!("failed to merge world import {name_str}"))?; |
904 | | } |
905 | | None => { |
906 | 16.0k | log::trace!("new import: `{name_str}`"); |
907 | 16.0k | new_imports.push((name.clone(), from_import.clone())); |
908 | | } |
909 | | } |
910 | | } |
911 | | |
912 | | // Build a set of interfaces which are required to be imported because |
913 | | // of `into`'s exports. This set is then used below during |
914 | | // `ensure_can_add_world_export`. |
915 | | // |
916 | | // This is the set of interfaces which exports depend on that are |
917 | | // themselves not exports. |
918 | 12.3k | let mut must_be_imported = HashMap::new(); |
919 | 12.3k | for (key, export) in into_world.exports.iter() { |
920 | 28 | for dep in self.world_item_direct_deps(export) { |
921 | 2 | if into_world.exports.contains_key(&WorldKey::Interface(dep)) { |
922 | 1 | continue; |
923 | 1 | } |
924 | 1 | self.foreach_interface_dep(dep, &mut |id| { |
925 | 1 | must_be_imported.insert(id, key.clone()); |
926 | 1 | }); |
927 | | } |
928 | | } |
929 | | |
930 | | // Next walk over exports of `from` and process these similarly to |
931 | | // imports. |
932 | 12.3k | for (name, from_export) in from_world.exports.iter() { |
933 | 2.28k | let name_str = self.name_world_key(name); |
934 | 2.28k | match into_world.exports.get(name) { |
935 | 5 | Some(into_export) => { |
936 | 5 | log::trace!("info/from shared export on `{name_str}`"); |
937 | 5 | self.merge_world_item(from_export, into_export) |
938 | 5 | .with_context(|| format!("failed to merge world export {name_str}"))?; |
939 | | } |
940 | | None => { |
941 | 2.27k | log::trace!("new export `{name_str}`"); |
942 | | // See comments in `ensure_can_add_world_export` for why |
943 | | // this is slightly different than imports. |
944 | 2.27k | self.ensure_can_add_world_export( |
945 | 2.27k | into_world, |
946 | 2.27k | name, |
947 | 2.27k | from_export, |
948 | 2.27k | &must_be_imported, |
949 | | ) |
950 | 2.27k | .with_context(|| { |
951 | 0 | format!("failed to add export `{}`", self.name_world_key(name)) |
952 | 0 | })?; |
953 | 2.27k | new_exports.push((name.clone(), from_export.clone())); |
954 | | } |
955 | | } |
956 | | } |
957 | | |
958 | | // For all the new imports and exports they may need to be "cloned" to |
959 | | // be able to belong to the new world. For example: |
960 | | // |
961 | | // * Anonymous interfaces have a `package` field which points to the |
962 | | // package of the containing world, but `from` and `into` may not be |
963 | | // in the same package. |
964 | | // |
965 | | // * Type imports have an `owner` field that point to `from`, but they |
966 | | // now need to point to `into` instead. |
967 | | // |
968 | | // Cloning is no trivial task, however, so cloning is delegated to a |
969 | | // submodule to perform a "deep" clone and copy items into new arena |
970 | | // entries as necessary. |
971 | 12.3k | let mut cloner = clone::Cloner::new(self, TypeOwner::World(from), TypeOwner::World(into)); |
972 | 12.3k | cloner.register_world_type_overlap(from, into); |
973 | 18.3k | for (name, item) in new_imports.iter_mut().chain(&mut new_exports) { |
974 | 18.3k | cloner.world_item(name, item, clone_maps); |
975 | 18.3k | } |
976 | | |
977 | 12.3k | clone_maps.types.extend(cloner.types); |
978 | | |
979 | | // Insert any new imports and new exports found first. |
980 | 12.3k | let into_world = &mut self.worlds[into]; |
981 | 28.3k | for (name, import) in new_imports { |
982 | 16.0k | let prev = into_world.imports.insert(name, import); |
983 | 16.0k | assert!(prev.is_none()); |
984 | | } |
985 | 14.5k | for (name, export) in new_exports { |
986 | 2.27k | let prev = into_world.exports.insert(name, export); |
987 | 2.27k | assert!(prev.is_none()); |
988 | | } |
989 | | |
990 | | #[cfg(debug_assertions)] |
991 | | self.assert_valid(); |
992 | 12.3k | Ok(()) |
993 | 12.3k | } |
994 | | |
995 | 67 | fn merge_world_item(&self, from: &WorldItem, into: &WorldItem) -> Result<()> { |
996 | 67 | let mut map = MergeMap::new(self, self); |
997 | 67 | match (from, into) { |
998 | 8 | (WorldItem::Interface { id: from, .. }, WorldItem::Interface { id: into, .. }) => { |
999 | | // If these imports are the same that can happen, for |
1000 | | // example, when both worlds to `import foo:bar/baz;`. That |
1001 | | // foreign interface will point to the same interface within |
1002 | | // `Resolve`. |
1003 | 8 | if from == into { |
1004 | 6 | return Ok(()); |
1005 | 2 | } |
1006 | | |
1007 | | // .. otherwise this MUST be a case of |
1008 | | // `import foo: interface { ... }`. If `from != into` but |
1009 | | // both `from` and `into` have the same name then the |
1010 | | // `WorldKey::Interface` case is ruled out as otherwise |
1011 | | // they'd have different names. |
1012 | | // |
1013 | | // In the case of an anonymous interface all we can do is |
1014 | | // ensure that the interfaces both match, so use `MergeMap` |
1015 | | // for that. |
1016 | 2 | map.build_interface(*from, *into) |
1017 | 2 | .context("failed to merge interfaces")?; |
1018 | | } |
1019 | | |
1020 | | // Like `WorldKey::Name` interfaces for functions and types the |
1021 | | // structure is asserted to be the same. |
1022 | 2 | (WorldItem::Function(from), WorldItem::Function(into)) => { |
1023 | 2 | map.build_function(from, into) |
1024 | 2 | .context("failed to merge functions")?; |
1025 | | } |
1026 | 49 | (WorldItem::Type(from), WorldItem::Type(into)) => { |
1027 | 49 | map.build_type_id(*from, *into) |
1028 | 49 | .context("failed to merge types")?; |
1029 | | } |
1030 | | |
1031 | | // Kind-level mismatches are caught here. |
1032 | | (WorldItem::Interface { .. }, _) |
1033 | | | (WorldItem::Function { .. }, _) |
1034 | | | (WorldItem::Type { .. }, _) => { |
1035 | 8 | bail!("different kinds of items"); |
1036 | | } |
1037 | | } |
1038 | 52 | assert!(map.interfaces_to_add.is_empty()); |
1039 | 52 | assert!(map.worlds_to_add.is_empty()); |
1040 | 52 | Ok(()) |
1041 | 67 | } |
1042 | | |
1043 | | /// This method ensures that the world export of `name` and `item` can be |
1044 | | /// added to the world `into` without changing the meaning of `into`. |
1045 | | /// |
1046 | | /// All dependencies of world exports must either be: |
1047 | | /// |
1048 | | /// * An export themselves |
1049 | | /// * An import with all transitive dependencies of the import also imported |
1050 | | /// |
1051 | | /// It's not possible to depend on an import which then also depends on an |
1052 | | /// export at some point, for example. This method ensures that if `name` |
1053 | | /// and `item` are added that this property is upheld. |
1054 | 2.27k | fn ensure_can_add_world_export( |
1055 | 2.27k | &self, |
1056 | 2.27k | into: &World, |
1057 | 2.27k | name: &WorldKey, |
1058 | 2.27k | item: &WorldItem, |
1059 | 2.27k | must_be_imported: &HashMap<InterfaceId, WorldKey>, |
1060 | 2.27k | ) -> Result<()> { |
1061 | 2.27k | assert!(!into.exports.contains_key(name)); |
1062 | 2.27k | let name = self.name_world_key(name); |
1063 | | |
1064 | | // First make sure that all of this item's dependencies are either |
1065 | | // exported or the entire chain of imports rooted at that dependency are |
1066 | | // all imported. |
1067 | 2.27k | for dep in self.world_item_direct_deps(item) { |
1068 | 210 | if into.exports.contains_key(&WorldKey::Interface(dep)) { |
1069 | 0 | continue; |
1070 | 210 | } |
1071 | 210 | self.ensure_not_exported(into, dep) |
1072 | 210 | .with_context(|| format!("failed validating export of `{name}`"))?; |
1073 | | } |
1074 | | |
1075 | | // Second make sure that this item, if it's an interface, will not alter |
1076 | | // the meaning of the preexisting world by ensuring that it's not in the |
1077 | | // set of "must be imported" items. |
1078 | 2.27k | if let WorldItem::Interface { id, .. } = item { |
1079 | 1.27k | if let Some(export) = must_be_imported.get(&id) { |
1080 | 0 | let export_name = self.name_world_key(export); |
1081 | 0 | bail!( |
1082 | 0 | "export `{export_name}` depends on `{name}` \ |
1083 | 0 | previously as an import which will change meaning \ |
1084 | 0 | if `{name}` is added as an export" |
1085 | | ); |
1086 | 1.27k | } |
1087 | 1.00k | } |
1088 | | |
1089 | 2.27k | Ok(()) |
1090 | 2.27k | } |
1091 | | |
1092 | 285 | fn ensure_not_exported(&self, world: &World, id: InterfaceId) -> Result<()> { |
1093 | 285 | let key = WorldKey::Interface(id); |
1094 | 285 | let name = self.name_world_key(&key); |
1095 | 285 | if world.exports.contains_key(&key) { |
1096 | 0 | bail!( |
1097 | 0 | "world exports `{name}` but it's also transitively used by an \ |
1098 | 0 | import \ |
1099 | 0 | which means that this is not valid" |
1100 | | ) |
1101 | 285 | } |
1102 | 285 | for dep in self.interface_direct_deps(id) { |
1103 | 75 | self.ensure_not_exported(world, dep) |
1104 | 75 | .with_context(|| format!("failed validating transitive import dep `{name}`"))?; |
1105 | | } |
1106 | 285 | Ok(()) |
1107 | 285 | } |
1108 | | |
1109 | | /// Returns an iterator of all the direct interface dependencies of this |
1110 | | /// `item`. |
1111 | | /// |
1112 | | /// Note that this doesn't include transitive dependencies, that must be |
1113 | | /// followed manually. |
1114 | 2.30k | fn world_item_direct_deps(&self, item: &WorldItem) -> impl Iterator<Item = InterfaceId> + '_ { |
1115 | 2.30k | let mut interface = None; |
1116 | 2.30k | let mut ty = None; |
1117 | 2.30k | match item { |
1118 | 1.01k | WorldItem::Function(_) => {} |
1119 | 0 | WorldItem::Type(id) => ty = Some(*id), |
1120 | 1.28k | WorldItem::Interface { id, .. } => interface = Some(*id), |
1121 | | } |
1122 | | |
1123 | 2.30k | interface |
1124 | 2.30k | .into_iter() |
1125 | 2.30k | .flat_map(move |id| self.interface_direct_deps(id)) |
1126 | 2.30k | .chain(ty.and_then(|t| self.type_interface_dep(t))) |
1127 | 2.30k | } |
1128 | | |
1129 | | /// Invokes `f` with `id` and all transitive interface dependencies of `id`. |
1130 | | /// |
1131 | | /// Note that `f` may be called with the same id multiple times. |
1132 | 133 | fn foreach_interface_dep(&self, id: InterfaceId, f: &mut dyn FnMut(InterfaceId)) { |
1133 | 133 | self._foreach_interface_dep(id, f, &mut HashSet::new()) |
1134 | 133 | } |
1135 | | |
1136 | | // Internal detail of `foreach_interface_dep` which uses a hash map to prune |
1137 | | // the visit tree to ensure that this doesn't visit an exponential number of |
1138 | | // interfaces. |
1139 | 183 | fn _foreach_interface_dep( |
1140 | 183 | &self, |
1141 | 183 | id: InterfaceId, |
1142 | 183 | f: &mut dyn FnMut(InterfaceId), |
1143 | 183 | visited: &mut HashSet<InterfaceId>, |
1144 | 183 | ) { |
1145 | 183 | if !visited.insert(id) { |
1146 | 26 | return; |
1147 | 157 | } |
1148 | 157 | f(id); |
1149 | 157 | for dep in self.interface_direct_deps(id) { |
1150 | 50 | self._foreach_interface_dep(dep, f, visited); |
1151 | 50 | } |
1152 | 183 | } |
1153 | | |
1154 | | /// Returns the ID of the specified `interface`. |
1155 | | /// |
1156 | | /// Returns `None` for unnamed interfaces. |
1157 | 68.5k | pub fn id_of(&self, interface: InterfaceId) -> Option<String> { |
1158 | 68.5k | let interface = &self.interfaces[interface]; |
1159 | 68.5k | Some(self.id_of_name(interface.package.unwrap(), interface.name.as_ref()?)) |
1160 | 68.5k | } |
1161 | | |
1162 | | /// Returns the "canonicalized interface name" of `interface`. |
1163 | | /// |
1164 | | /// Returns `None` for unnamed interfaces. See `BuildTargets.md` in the |
1165 | | /// upstream component model repository for more information about this. |
1166 | 187 | pub fn canonicalized_id_of(&self, interface: InterfaceId) -> Option<String> { |
1167 | 187 | let interface = &self.interfaces[interface]; |
1168 | 187 | Some(self.canonicalized_id_of_name(interface.package.unwrap(), interface.name.as_ref()?)) |
1169 | 187 | } |
1170 | | |
1171 | | /// Convert a world to an "importized" version where the world is updated |
1172 | | /// in-place to reflect what it would look like to be imported. |
1173 | | /// |
1174 | | /// This is a transformation which is used as part of the process of |
1175 | | /// importing a component today. For example when a component depends on |
1176 | | /// another component this is useful for generating WIT which can be use to |
1177 | | /// represent the component being imported. The general idea is that this |
1178 | | /// function will update the `world_id` specified such it imports the |
1179 | | /// functionality that it previously exported. The world will be left with |
1180 | | /// no exports. |
1181 | | /// |
1182 | | /// This world is then suitable for merging into other worlds or generating |
1183 | | /// bindings in a context that is importing the original world. This |
1184 | | /// is intended to be used as part of language tooling when depending on |
1185 | | /// other components. |
1186 | 4.07k | pub fn importize(&mut self, world_id: WorldId, out_world_name: Option<String>) -> Result<()> { |
1187 | | // Rename the world to avoid having it get confused with the original |
1188 | | // name of the world. Add `-importized` to it for now. Precisely how |
1189 | | // this new world is created may want to be updated over time if this |
1190 | | // becomes problematic. |
1191 | 4.07k | let world = &mut self.worlds[world_id]; |
1192 | 4.07k | let pkg = &mut self.packages[world.package.unwrap()]; |
1193 | 4.07k | pkg.worlds.shift_remove(&world.name); |
1194 | 4.07k | if let Some(name) = out_world_name { |
1195 | 0 | world.name = name.clone(); |
1196 | 0 | pkg.worlds.insert(name, world_id); |
1197 | 4.07k | } else { |
1198 | 4.07k | world.name.push_str("-importized"); |
1199 | 4.07k | pkg.worlds.insert(world.name.clone(), world_id); |
1200 | 4.07k | } |
1201 | | |
1202 | | // Trim all non-type definitions from imports. Types can be used by |
1203 | | // exported functions, for example, so they're preserved. |
1204 | 5.32k | world.imports.retain(|_, item| match item { |
1205 | 3.72k | WorldItem::Type(_) => true, |
1206 | 1.59k | _ => false, |
1207 | 5.32k | }); |
1208 | | |
1209 | 4.07k | for (name, export) in mem::take(&mut world.exports) { |
1210 | 735 | match (name.clone(), world.imports.insert(name, export)) { |
1211 | | // no previous item? this insertion was ok |
1212 | 603 | (_, None) => {} |
1213 | | |
1214 | | // cannot overwrite an import with an export |
1215 | 132 | (WorldKey::Name(name), Some(_)) => { |
1216 | 132 | bail!("world export `{name}` conflicts with import of same name"); |
1217 | | } |
1218 | | |
1219 | | // Exports already don't overlap each other and the only imports |
1220 | | // preserved above were types so this shouldn't be reachable. |
1221 | 0 | (WorldKey::Interface(_), _) => unreachable!(), |
1222 | | } |
1223 | | } |
1224 | | |
1225 | | // Fill out any missing transitive interface imports by elaborating this |
1226 | | // world which does that for us. |
1227 | 3.94k | self.elaborate_world(world_id)?; |
1228 | | |
1229 | | #[cfg(debug_assertions)] |
1230 | | self.assert_valid(); |
1231 | 3.94k | Ok(()) |
1232 | 4.07k | } |
1233 | | |
1234 | | /// Returns the ID of the specified `name` within the `pkg`. |
1235 | 72.6k | pub fn id_of_name(&self, pkg: PackageId, name: &str) -> String { |
1236 | 72.6k | let package = &self.packages[pkg]; |
1237 | 72.6k | let mut base = String::new(); |
1238 | 72.6k | base.push_str(&package.name.namespace); |
1239 | 72.6k | base.push_str(":"); |
1240 | 72.6k | base.push_str(&package.name.name); |
1241 | 72.6k | base.push_str("/"); |
1242 | 72.6k | base.push_str(name); |
1243 | 72.6k | if let Some(version) = &package.name.version { |
1244 | 51.8k | base.push_str(&format!("@{version}")); |
1245 | 51.8k | } |
1246 | 72.6k | base |
1247 | 72.6k | } |
1248 | | |
1249 | | /// Returns the "canonicalized interface name" of the specified `name` |
1250 | | /// within the `pkg`. |
1251 | | /// |
1252 | | /// See `BuildTargets.md` in the upstream component model repository for |
1253 | | /// more information about this. |
1254 | 187 | pub fn canonicalized_id_of_name(&self, pkg: PackageId, name: &str) -> String { |
1255 | 187 | let package = &self.packages[pkg]; |
1256 | 187 | let mut base = String::new(); |
1257 | 187 | base.push_str(&package.name.namespace); |
1258 | 187 | base.push_str(":"); |
1259 | 187 | base.push_str(&package.name.name); |
1260 | 187 | base.push_str("/"); |
1261 | 187 | base.push_str(name); |
1262 | 187 | if let Some(version) = &package.name.version { |
1263 | 151 | base.push_str("@"); |
1264 | 151 | let string = PackageName::version_compat_track_string(version); |
1265 | 151 | base.push_str(&string); |
1266 | 151 | } |
1267 | 187 | base |
1268 | 187 | } |
1269 | | |
1270 | | /// Selects a world from among the packages in a `Resolve`. |
1271 | | /// |
1272 | | /// A `Resolve` may have many packages, each with many worlds. Many WIT |
1273 | | /// tools need a specific world to operate on. This function choses a |
1274 | | /// world, failing if the choice is ambiguous. |
1275 | | /// |
1276 | | /// `main_packages` provides the package IDs returned by |
1277 | | /// [`push_path`](Resolve::push_path), [`push_dir`](Resolve::push_dir), |
1278 | | /// [`push_file`](Resolve::push_file), [`push_group`](Resolve::push_group), |
1279 | | /// and [`push_str`](Resolve::push_str), which are the "main packages", |
1280 | | /// as distinguished from any packages nested inside them. |
1281 | | /// |
1282 | | /// `world` is a world name such as from a `--world` command-line option or |
1283 | | /// a `world:` macro parameter. `world` can be: |
1284 | | /// |
1285 | | /// * A kebab-name of a world, for example `"the-world"`. It is resolved |
1286 | | /// within the "main package", if there is exactly one. |
1287 | | /// |
1288 | | /// * An ID-based form of a world, for example `"wasi:http/proxy"`. Note |
1289 | | /// that a version does not need to be specified in this string if |
1290 | | /// there's only one package of the same name and it has a version. In |
1291 | | /// this situation the version can be omitted. |
1292 | | /// |
1293 | | /// * `None`. If there's exactly one "main package" and it contains exactly |
1294 | | /// one world, that world is chosen. |
1295 | | /// |
1296 | | /// If successful, the chosen `WorldId` is returned. |
1297 | | /// |
1298 | | /// # Examples |
1299 | | /// |
1300 | | /// ``` |
1301 | | /// use anyhow::Result; |
1302 | | /// use wit_parser::Resolve; |
1303 | | /// |
1304 | | /// fn main() -> Result<()> { |
1305 | | /// let mut resolve = Resolve::default(); |
1306 | | /// |
1307 | | /// // If there's a single package and only one world, that world is |
1308 | | /// // the obvious choice. |
1309 | | /// let wit1 = resolve.push_str( |
1310 | | /// "./my-test.wit", |
1311 | | /// r#" |
1312 | | /// package example:wit1; |
1313 | | /// |
1314 | | /// world foo { |
1315 | | /// // ... |
1316 | | /// } |
1317 | | /// "#, |
1318 | | /// )?; |
1319 | | /// assert!(resolve.select_world(&[wit1], None).is_ok()); |
1320 | | /// |
1321 | | /// // If there are multiple packages, we need to be told which package |
1322 | | /// // to use, either by a "main package" or by a fully-qualified name. |
1323 | | /// let wit2 = resolve.push_str( |
1324 | | /// "./my-test.wit", |
1325 | | /// r#" |
1326 | | /// package example:wit2; |
1327 | | /// |
1328 | | /// world foo { /* ... */ } |
1329 | | /// "#, |
1330 | | /// )?; |
1331 | | /// assert!(resolve.select_world(&[wit1, wit2], None).is_err()); |
1332 | | /// assert!(resolve.select_world(&[wit1, wit2], Some("foo")).is_err()); |
1333 | | /// // Fix: use fully-qualified names. |
1334 | | /// assert!(resolve.select_world(&[wit1, wit2], Some("example:wit1/foo")).is_ok()); |
1335 | | /// assert!(resolve.select_world(&[wit1, wit2], Some("example:wit2/foo")).is_ok()); |
1336 | | /// |
1337 | | /// // If a package has multiple worlds, then we can't guess the world |
1338 | | /// // even if we know the package. |
1339 | | /// let wit3 = resolve.push_str( |
1340 | | /// "./my-test.wit", |
1341 | | /// r#" |
1342 | | /// package example:wit3; |
1343 | | /// |
1344 | | /// world foo { /* ... */ } |
1345 | | /// |
1346 | | /// world bar { /* ... */ } |
1347 | | /// "#, |
1348 | | /// )?; |
1349 | | /// assert!(resolve.select_world(&[wit3], None).is_err()); |
1350 | | /// // Fix: pick between "foo" and "bar" here. |
1351 | | /// assert!(resolve.select_world(&[wit3], Some("foo")).is_ok()); |
1352 | | /// |
1353 | | /// // When selecting with a version it's ok to drop the version when |
1354 | | /// // there's only a single copy of that package in `Resolve`. |
1355 | | /// let wit5_1 = resolve.push_str( |
1356 | | /// "./my-test.wit", |
1357 | | /// r#" |
1358 | | /// package example:wit5@1.0.0; |
1359 | | /// |
1360 | | /// world foo { /* ... */ } |
1361 | | /// "#, |
1362 | | /// )?; |
1363 | | /// assert!(resolve.select_world(&[wit5_1], Some("foo")).is_ok()); |
1364 | | /// assert!(resolve.select_world(&[wit5_1], Some("example:wit5/foo")).is_ok()); |
1365 | | /// |
1366 | | /// // However when a single package has multiple versions in a resolve |
1367 | | /// // it's required to specify the version to select which one. |
1368 | | /// let wit5_2 = resolve.push_str( |
1369 | | /// "./my-test.wit", |
1370 | | /// r#" |
1371 | | /// package example:wit5@2.0.0; |
1372 | | /// |
1373 | | /// world foo { /* ... */ } |
1374 | | /// "#, |
1375 | | /// )?; |
1376 | | /// assert!(resolve.select_world(&[wit5_1, wit5_2], Some("example:wit5/foo")).is_err()); |
1377 | | /// // Fix: Pass explicit versions. |
1378 | | /// assert!(resolve.select_world(&[wit5_1, wit5_2], Some("example:wit5/foo@1.0.0")).is_ok()); |
1379 | | /// assert!(resolve.select_world(&[wit5_1, wit5_2], Some("example:wit5/foo@2.0.0")).is_ok()); |
1380 | | /// |
1381 | | /// Ok(()) |
1382 | | /// } |
1383 | | /// ``` |
1384 | 0 | pub fn select_world( |
1385 | 0 | &self, |
1386 | 0 | main_packages: &[PackageId], |
1387 | 0 | world: Option<&str>, |
1388 | 0 | ) -> Result<WorldId> { |
1389 | | // Determine if `world` is a kebab-name or an ID. |
1390 | 0 | let world_path = match world { |
1391 | 0 | Some(world) => Some( |
1392 | 0 | parse_use_path(world) |
1393 | 0 | .with_context(|| format!("failed to parse world specifier `{world}`"))?, |
1394 | | ), |
1395 | 0 | None => None, |
1396 | | }; |
1397 | | |
1398 | 0 | match world_path { |
1399 | | // We have a world path. If needed, pick a package to resolve it in. |
1400 | 0 | Some(world_path) => { |
1401 | 0 | let (pkg, world_name) = match (main_packages, world_path) { |
1402 | | // We have no main packages; fail. |
1403 | 0 | ([], _) => bail!("No main packages defined"), |
1404 | | |
1405 | | // We have exactly one main package. |
1406 | 0 | ([main_package], ParsedUsePath::Name(name)) => (*main_package, name), |
1407 | | |
1408 | | // We have more than one main package; fail. |
1409 | 0 | (_, ParsedUsePath::Name(_name)) => { |
1410 | 0 | bail!( |
1411 | 0 | "There are multiple main packages; a world must be explicitly chosen:{}", |
1412 | 0 | self.worlds |
1413 | 0 | .iter() |
1414 | 0 | .map(|world| format!( |
1415 | 0 | "\n {}", |
1416 | 0 | self.id_of_name(world.1.package.unwrap(), &world.1.name) |
1417 | | )) |
1418 | 0 | .collect::<String>() |
1419 | | ) |
1420 | | } |
1421 | | |
1422 | | // The world name is fully-qualified. |
1423 | 0 | (_, ParsedUsePath::Package(pkg, world_name)) => { |
1424 | 0 | let pkg = match self.package_names.get(&pkg) { |
1425 | 0 | Some(pkg) => *pkg, |
1426 | | None => { |
1427 | 0 | let mut candidates = |
1428 | 0 | self.package_names.iter().filter(|(name, _)| { |
1429 | 0 | pkg.version.is_none() |
1430 | 0 | && pkg.name == name.name |
1431 | 0 | && pkg.namespace == name.namespace |
1432 | 0 | && name.version.is_some() |
1433 | 0 | }); |
1434 | 0 | let candidate = candidates.next(); |
1435 | 0 | if let Some((c2, _)) = candidates.next() { |
1436 | 0 | let (c1, _) = candidate.unwrap(); |
1437 | 0 | bail!( |
1438 | 0 | "package name `{pkg}` is available at both \ |
1439 | 0 | versions {} and {} but which is not specified", |
1440 | 0 | c1.version.as_ref().unwrap(), |
1441 | 0 | c2.version.as_ref().unwrap(), |
1442 | | ); |
1443 | 0 | } |
1444 | 0 | match candidate { |
1445 | 0 | Some((_, id)) => *id, |
1446 | 0 | None => bail!("unknown package `{pkg}`"), |
1447 | | } |
1448 | | } |
1449 | | }; |
1450 | 0 | (pkg, world_name.to_string()) |
1451 | | } |
1452 | | }; |
1453 | | |
1454 | | // Now that we've picked the package, resolve the world name. |
1455 | 0 | let pkg = &self.packages[pkg]; |
1456 | 0 | pkg.worlds.get(&world_name).copied().ok_or_else(|| { |
1457 | 0 | anyhow!("World `{world_name}` not found in package `{}`", pkg.name) |
1458 | 0 | }) |
1459 | | } |
1460 | | |
1461 | | // With no specified `world`, try to find a single obvious world. |
1462 | 0 | None => match main_packages { |
1463 | 0 | [] => bail!("No main packages defined"), |
1464 | | |
1465 | | // Check for exactly one main package with exactly one world. |
1466 | 0 | [main_package] => { |
1467 | 0 | let pkg = &self.packages[*main_package]; |
1468 | 0 | match pkg.worlds.len() { |
1469 | 0 | 0 => bail!("The main package `{}` contains no worlds", pkg.name), |
1470 | 0 | 1 => Ok(pkg.worlds[0]), |
1471 | 0 | _ => bail!( |
1472 | 0 | "There are multiple worlds in `{}`; one must be explicitly chosen:{}", |
1473 | | pkg.name, |
1474 | 0 | pkg.worlds |
1475 | 0 | .values() |
1476 | 0 | .map(|world| format!( |
1477 | 0 | "\n {}", |
1478 | 0 | self.id_of_name(*main_package, &self.worlds[*world].name) |
1479 | | )) |
1480 | 0 | .collect::<String>() |
1481 | | ), |
1482 | | } |
1483 | | } |
1484 | | |
1485 | | // Multiple main packages and no world name; fail. |
1486 | | _ => { |
1487 | 0 | bail!( |
1488 | 0 | "There are multiple main packages; a world must be explicitly chosen:{}", |
1489 | 0 | self.worlds |
1490 | 0 | .iter() |
1491 | 0 | .map(|world| format!( |
1492 | 0 | "\n {}", |
1493 | 0 | self.id_of_name(world.1.package.unwrap(), &world.1.name) |
1494 | | )) |
1495 | 0 | .collect::<String>() |
1496 | | ) |
1497 | | } |
1498 | | }, |
1499 | | } |
1500 | 0 | } |
1501 | | |
1502 | | /// Assigns a human readable name to the `WorldKey` specified. |
1503 | 72.9k | pub fn name_world_key(&self, key: &WorldKey) -> String { |
1504 | 72.9k | match key { |
1505 | 65.0k | WorldKey::Name(s) => s.to_string(), |
1506 | 7.92k | WorldKey::Interface(i) => self.id_of(*i).expect("unexpected anonymous interface"), |
1507 | | } |
1508 | 72.9k | } |
1509 | | |
1510 | | /// Same as [`Resolve::name_world_key`] except that `WorldKey::Interfaces` |
1511 | | /// uses [`Resolve::canonicalized_id_of`]. |
1512 | 318 | pub fn name_canonicalized_world_key(&self, key: &WorldKey) -> String { |
1513 | 318 | match key { |
1514 | 205 | WorldKey::Name(s) => s.to_string(), |
1515 | 113 | WorldKey::Interface(i) => self |
1516 | 113 | .canonicalized_id_of(*i) |
1517 | 113 | .expect("unexpected anonymous interface"), |
1518 | | } |
1519 | 318 | } |
1520 | | |
1521 | | /// Returns the interface that `id` uses a type from, if it uses a type from |
1522 | | /// a different interface than `id` is defined within. |
1523 | | /// |
1524 | | /// If `id` is not a use-of-a-type or it's using a type in the same |
1525 | | /// interface then `None` is returned. |
1526 | 139k | pub fn type_interface_dep(&self, id: TypeId) -> Option<InterfaceId> { |
1527 | 139k | let ty = &self.types[id]; |
1528 | 33.8k | let dep = match ty.kind { |
1529 | 28.9k | TypeDefKind::Type(Type::Id(id)) => id, |
1530 | 110k | _ => return None, |
1531 | | }; |
1532 | 28.9k | let other = &self.types[dep]; |
1533 | 28.9k | if ty.owner == other.owner { |
1534 | 135 | None |
1535 | | } else { |
1536 | 28.8k | match other.owner { |
1537 | 28.8k | TypeOwner::Interface(id) => Some(id), |
1538 | 0 | _ => unreachable!(), |
1539 | | } |
1540 | | } |
1541 | 139k | } |
1542 | | |
1543 | | /// Returns an iterator of all interfaces that the interface `id` depends |
1544 | | /// on. |
1545 | | /// |
1546 | | /// Interfaces may depend on others for type information to resolve type |
1547 | | /// imports. |
1548 | | /// |
1549 | | /// Note that the returned iterator may yield the same interface as a |
1550 | | /// dependency multiple times. Additionally only direct dependencies of `id` |
1551 | | /// are yielded, not transitive dependencies. |
1552 | 102k | pub fn interface_direct_deps(&self, id: InterfaceId) -> impl Iterator<Item = InterfaceId> + '_ { |
1553 | 102k | self.interfaces[id] |
1554 | 102k | .types |
1555 | 102k | .iter() |
1556 | 102k | .filter_map(move |(_name, ty)| self.type_interface_dep(*ty)) |
1557 | 102k | } |
1558 | | |
1559 | | /// Returns an iterator of all packages that the package `id` depends |
1560 | | /// on. |
1561 | | /// |
1562 | | /// Packages may depend on others for type information to resolve type |
1563 | | /// imports or interfaces to resolve worlds. |
1564 | | /// |
1565 | | /// Note that the returned iterator may yield the same package as a |
1566 | | /// dependency multiple times. Additionally only direct dependencies of `id` |
1567 | | /// are yielded, not transitive dependencies. |
1568 | 22.8k | pub fn package_direct_deps(&self, id: PackageId) -> impl Iterator<Item = PackageId> + '_ { |
1569 | 22.8k | let pkg = &self.packages[id]; |
1570 | | |
1571 | 22.8k | pkg.interfaces |
1572 | 22.8k | .iter() |
1573 | 25.8k | .flat_map(move |(_name, id)| self.interface_direct_deps(*id)) |
1574 | 24.6k | .chain(pkg.worlds.iter().flat_map(move |(_name, id)| { |
1575 | 24.6k | let world = &self.worlds[*id]; |
1576 | 24.6k | world |
1577 | 24.6k | .imports |
1578 | 24.6k | .iter() |
1579 | 24.6k | .chain(world.exports.iter()) |
1580 | 36.8k | .filter_map(move |(_name, item)| match item { |
1581 | 6.78k | WorldItem::Interface { id, .. } => Some(*id), |
1582 | 7.47k | WorldItem::Function(_) => None, |
1583 | 22.5k | WorldItem::Type(t) => self.type_interface_dep(*t), |
1584 | 36.8k | }) |
1585 | 24.6k | })) |
1586 | 22.8k | .filter_map(move |iface_id| { |
1587 | 12.7k | let pkg = self.interfaces[iface_id].package?; |
1588 | 12.7k | if pkg == id { None } else { Some(pkg) } |
1589 | 12.7k | }) |
1590 | 22.8k | } |
1591 | | |
1592 | | /// Returns a topological ordering of packages contained in this `Resolve`. |
1593 | | /// |
1594 | | /// This returns a list of `PackageId` such that when visited in order it's |
1595 | | /// guaranteed that all dependencies will have been defined by prior items |
1596 | | /// in the list. |
1597 | 17.7k | pub fn topological_packages(&self) -> Vec<PackageId> { |
1598 | 17.7k | let mut pushed = vec![false; self.packages.len()]; |
1599 | 17.7k | let mut order = Vec::new(); |
1600 | 22.8k | for (id, _) in self.packages.iter() { |
1601 | 22.8k | self.build_topological_package_ordering(id, &mut pushed, &mut order); |
1602 | 22.8k | } |
1603 | 17.7k | order |
1604 | 17.7k | } |
1605 | | |
1606 | 26.6k | fn build_topological_package_ordering( |
1607 | 26.6k | &self, |
1608 | 26.6k | id: PackageId, |
1609 | 26.6k | pushed: &mut Vec<bool>, |
1610 | 26.6k | order: &mut Vec<PackageId>, |
1611 | 26.6k | ) { |
1612 | 26.6k | if pushed[id.index()] { |
1613 | 3.79k | return; |
1614 | 22.8k | } |
1615 | 22.8k | for dep in self.package_direct_deps(id) { |
1616 | 3.79k | self.build_topological_package_ordering(dep, pushed, order); |
1617 | 3.79k | } |
1618 | 22.8k | order.push(id); |
1619 | 22.8k | pushed[id.index()] = true; |
1620 | 26.6k | } |
1621 | | |
1622 | | #[doc(hidden)] |
1623 | 5.46k | pub fn assert_valid(&self) { |
1624 | 5.46k | let mut package_interfaces = Vec::new(); |
1625 | 5.46k | let mut package_worlds = Vec::new(); |
1626 | 5.95k | for (id, pkg) in self.packages.iter() { |
1627 | 5.95k | let mut interfaces = HashSet::new(); |
1628 | 24.7k | for (name, iface) in pkg.interfaces.iter() { |
1629 | 24.7k | assert!(interfaces.insert(*iface)); |
1630 | 24.7k | let iface = &self.interfaces[*iface]; |
1631 | 24.7k | assert_eq!(name, iface.name.as_ref().unwrap()); |
1632 | 24.7k | assert_eq!(iface.package.unwrap(), id); |
1633 | | } |
1634 | 5.95k | package_interfaces.push(pkg.interfaces.values().copied().collect::<HashSet<_>>()); |
1635 | 5.95k | let mut worlds = HashSet::new(); |
1636 | 8.14k | for (name, world) in pkg.worlds.iter() { |
1637 | 8.14k | assert!(worlds.insert(*world)); |
1638 | 8.14k | assert_eq!( |
1639 | 8.14k | pkg.worlds.get_key_value(name), |
1640 | 8.14k | Some((name, world)), |
1641 | 0 | "`MutableKeys` impl may have been used to change a key's hash or equality" |
1642 | | ); |
1643 | 8.14k | let world = &self.worlds[*world]; |
1644 | 8.14k | assert_eq!(*name, world.name); |
1645 | 8.14k | assert_eq!(world.package.unwrap(), id); |
1646 | | } |
1647 | 5.95k | package_worlds.push(pkg.worlds.values().copied().collect::<HashSet<_>>()); |
1648 | | } |
1649 | | |
1650 | 5.46k | let mut interface_types = Vec::new(); |
1651 | 26.1k | for (id, iface) in self.interfaces.iter() { |
1652 | 26.1k | assert!(self.packages.get(iface.package.unwrap()).is_some()); |
1653 | 26.1k | if iface.name.is_some() { |
1654 | 24.7k | assert!(package_interfaces[iface.package.unwrap().index()].contains(&id)); |
1655 | 1.41k | } |
1656 | | |
1657 | 26.1k | for (name, ty) in iface.types.iter() { |
1658 | 11.1k | let ty = &self.types[*ty]; |
1659 | 11.1k | assert_eq!(ty.name.as_ref(), Some(name)); |
1660 | 11.1k | assert_eq!(ty.owner, TypeOwner::Interface(id)); |
1661 | | } |
1662 | 26.1k | interface_types.push(iface.types.values().copied().collect::<HashSet<_>>()); |
1663 | 26.1k | for (name, f) in iface.functions.iter() { |
1664 | 9.43k | assert_eq!(*name, f.name); |
1665 | | } |
1666 | | } |
1667 | | |
1668 | 5.46k | let mut world_types = Vec::new(); |
1669 | 8.14k | for (id, world) in self.worlds.iter() { |
1670 | 8.14k | log::debug!("validating world {}", &world.name); |
1671 | 8.14k | if let Some(package) = world.package { |
1672 | 8.14k | assert!(self.packages.get(package).is_some()); |
1673 | 8.14k | assert!(package_worlds[package.index()].contains(&id)); |
1674 | 0 | } |
1675 | 8.14k | assert!(world.includes.is_empty()); |
1676 | | |
1677 | 8.14k | let mut types = HashSet::new(); |
1678 | 12.1k | for (name, item) in world.imports.iter().chain(world.exports.iter()) { |
1679 | 12.1k | log::debug!("validating world item: {}", self.name_world_key(name)); |
1680 | 12.1k | match item { |
1681 | 2.23k | WorldItem::Interface { id, .. } => { |
1682 | | // anonymous interfaces must belong to the same package |
1683 | | // as the world's package. |
1684 | 2.23k | if matches!(name, WorldKey::Name(_)) { |
1685 | 1.41k | assert_eq!(self.interfaces[*id].package, world.package); |
1686 | 816 | } |
1687 | | } |
1688 | 2.47k | WorldItem::Function(f) => { |
1689 | 2.47k | assert!(!matches!(name, WorldKey::Interface(_))); |
1690 | 2.47k | assert_eq!(f.name, name.clone().unwrap_name()); |
1691 | | } |
1692 | 7.45k | WorldItem::Type(ty) => { |
1693 | 7.45k | assert!(!matches!(name, WorldKey::Interface(_))); |
1694 | 7.45k | assert!(types.insert(*ty)); |
1695 | 7.45k | let ty = &self.types[*ty]; |
1696 | 7.45k | assert_eq!(ty.name, Some(name.clone().unwrap_name())); |
1697 | 7.45k | assert_eq!(ty.owner, TypeOwner::World(id)); |
1698 | | } |
1699 | | } |
1700 | | } |
1701 | 8.14k | self.assert_world_elaborated(world); |
1702 | 8.14k | world_types.push(types); |
1703 | | } |
1704 | | |
1705 | 73.0k | for (ty_id, ty) in self.types.iter() { |
1706 | 73.0k | match ty.owner { |
1707 | 11.1k | TypeOwner::Interface(id) => { |
1708 | 11.1k | assert!(self.interfaces.get(id).is_some()); |
1709 | 11.1k | assert!(interface_types[id.index()].contains(&ty_id)); |
1710 | | } |
1711 | 7.45k | TypeOwner::World(id) => { |
1712 | 7.45k | assert!(self.worlds.get(id).is_some()); |
1713 | 7.45k | assert!(world_types[id.index()].contains(&ty_id)); |
1714 | | } |
1715 | 54.4k | TypeOwner::None => {} |
1716 | | } |
1717 | | } |
1718 | | |
1719 | 5.46k | self.assert_topologically_sorted(); |
1720 | 5.46k | } |
1721 | | |
1722 | 5.46k | fn assert_topologically_sorted(&self) { |
1723 | 5.46k | let mut positions = IndexMap::new(); |
1724 | 5.95k | for id in self.topological_packages() { |
1725 | 5.95k | let pkg = &self.packages[id]; |
1726 | 5.95k | log::debug!("pkg {}", pkg.name); |
1727 | 5.95k | let prev = positions.insert(Some(id), IndexSet::new()); |
1728 | 5.95k | assert!(prev.is_none()); |
1729 | | } |
1730 | 5.46k | positions.insert(None, IndexSet::new()); |
1731 | | |
1732 | 26.1k | for (id, iface) in self.interfaces.iter() { |
1733 | 26.1k | log::debug!("iface {:?}", iface.name); |
1734 | 26.1k | let ok = positions.get_mut(&iface.package).unwrap().insert(id); |
1735 | 26.1k | assert!(ok); |
1736 | | } |
1737 | | |
1738 | 8.14k | for (_, world) in self.worlds.iter() { |
1739 | 8.14k | log::debug!("world {:?}", world.name); |
1740 | | |
1741 | 8.14k | let my_package = world.package; |
1742 | 8.14k | let my_package_pos = positions.get_index_of(&my_package).unwrap(); |
1743 | | |
1744 | 12.1k | for (_, item) in world.imports.iter().chain(&world.exports) { |
1745 | 12.1k | let id = match item { |
1746 | 2.23k | WorldItem::Interface { id, .. } => *id, |
1747 | 9.92k | _ => continue, |
1748 | | }; |
1749 | 2.23k | let other_package = self.interfaces[id].package; |
1750 | 2.23k | let other_package_pos = positions.get_index_of(&other_package).unwrap(); |
1751 | | |
1752 | 2.23k | assert!(other_package_pos <= my_package_pos); |
1753 | | } |
1754 | | } |
1755 | | |
1756 | 73.0k | for (_id, ty) in self.types.iter() { |
1757 | 73.0k | log::debug!("type {:?} {:?}", ty.name, ty.owner); |
1758 | 4.61k | let other_id = match ty.kind { |
1759 | 4.05k | TypeDefKind::Type(Type::Id(ty)) => ty, |
1760 | 68.9k | _ => continue, |
1761 | | }; |
1762 | 4.05k | let other = &self.types[other_id]; |
1763 | 4.05k | if ty.kind == other.kind { |
1764 | 0 | continue; |
1765 | 4.05k | } |
1766 | 4.05k | let my_interface = match ty.owner { |
1767 | 3.09k | TypeOwner::Interface(id) => id, |
1768 | 964 | _ => continue, |
1769 | | }; |
1770 | 3.09k | let other_interface = match other.owner { |
1771 | 3.09k | TypeOwner::Interface(id) => id, |
1772 | 0 | _ => continue, |
1773 | | }; |
1774 | | |
1775 | 3.09k | let my_package = self.interfaces[my_interface].package; |
1776 | 3.09k | let other_package = self.interfaces[other_interface].package; |
1777 | 3.09k | let my_package_pos = positions.get_index_of(&my_package).unwrap(); |
1778 | 3.09k | let other_package_pos = positions.get_index_of(&other_package).unwrap(); |
1779 | | |
1780 | 3.09k | if my_package_pos == other_package_pos { |
1781 | 1.76k | let interfaces = &positions[&my_package]; |
1782 | 1.76k | let my_interface_pos = interfaces.get_index_of(&my_interface).unwrap(); |
1783 | 1.76k | let other_interface_pos = interfaces.get_index_of(&other_interface).unwrap(); |
1784 | 1.76k | assert!(other_interface_pos <= my_interface_pos); |
1785 | | } else { |
1786 | 1.32k | assert!(other_package_pos < my_package_pos); |
1787 | | } |
1788 | | } |
1789 | 5.46k | } |
1790 | | |
1791 | 8.14k | fn assert_world_elaborated(&self, world: &World) { |
1792 | 10.6k | for (key, item) in world.imports.iter() { |
1793 | 10.6k | log::debug!( |
1794 | 0 | "asserting elaborated world import {}", |
1795 | 0 | self.name_world_key(key) |
1796 | | ); |
1797 | 10.6k | match item { |
1798 | 7.45k | WorldItem::Type(t) => self.assert_world_imports_type_deps(world, key, *t), |
1799 | | |
1800 | | // All types referred to must be imported. |
1801 | 1.80k | WorldItem::Function(f) => self.assert_world_function_imports_types(world, key, f), |
1802 | | |
1803 | | // All direct dependencies of this interface must be imported. |
1804 | 1.39k | WorldItem::Interface { id, .. } => { |
1805 | 1.39k | for dep in self.interface_direct_deps(*id) { |
1806 | 322 | assert!( |
1807 | 322 | world.imports.contains_key(&WorldKey::Interface(dep)), |
1808 | 0 | "world import of {} is missing transitive dep of {}", |
1809 | 0 | self.name_world_key(key), |
1810 | 0 | self.id_of(dep).unwrap(), |
1811 | | ); |
1812 | | } |
1813 | | } |
1814 | | } |
1815 | | } |
1816 | 8.14k | for (key, item) in world.exports.iter() { |
1817 | 1.50k | log::debug!( |
1818 | 0 | "asserting elaborated world export {}", |
1819 | 0 | self.name_world_key(key) |
1820 | | ); |
1821 | 1.50k | match item { |
1822 | | // Types referred to by this function must be imported. |
1823 | 666 | WorldItem::Function(f) => self.assert_world_function_imports_types(world, key, f), |
1824 | | |
1825 | | // Dependencies of exported interfaces must also be exported, or |
1826 | | // if imported then that entire chain of imports must be |
1827 | | // imported and not exported. |
1828 | 840 | WorldItem::Interface { id, .. } => { |
1829 | 840 | for dep in self.interface_direct_deps(*id) { |
1830 | 140 | let dep_key = WorldKey::Interface(dep); |
1831 | 140 | if world.exports.contains_key(&dep_key) { |
1832 | 8 | continue; |
1833 | 132 | } |
1834 | 156 | self.foreach_interface_dep(dep, &mut |dep| { |
1835 | 156 | let dep_key = WorldKey::Interface(dep); |
1836 | 156 | assert!( |
1837 | 156 | world.imports.contains_key(&dep_key), |
1838 | 0 | "world should import {} (required by {})", |
1839 | 0 | self.name_world_key(&dep_key), |
1840 | 0 | self.name_world_key(key), |
1841 | | ); |
1842 | 156 | assert!( |
1843 | 156 | !world.exports.contains_key(&dep_key), |
1844 | 0 | "world should not export {} (required by {})", |
1845 | 0 | self.name_world_key(&dep_key), |
1846 | 0 | self.name_world_key(key), |
1847 | | ); |
1848 | 156 | }); |
1849 | | } |
1850 | | } |
1851 | | |
1852 | | // exported types not allowed at this time |
1853 | 0 | WorldItem::Type(_) => unreachable!(), |
1854 | | } |
1855 | | } |
1856 | 8.14k | } |
1857 | | |
1858 | 9.70k | fn assert_world_imports_type_deps(&self, world: &World, key: &WorldKey, ty: TypeId) { |
1859 | | // If this is a `use` statement then the referred-to interface must be |
1860 | | // imported into this world. |
1861 | 9.70k | let ty = &self.types[ty]; |
1862 | 1.14k | if let TypeDefKind::Type(Type::Id(other)) = ty.kind { |
1863 | 974 | if let TypeOwner::Interface(id) = self.types[other].owner { |
1864 | 970 | let key = WorldKey::Interface(id); |
1865 | 970 | assert!(world.imports.contains_key(&key)); |
1866 | 970 | return; |
1867 | 4 | } |
1868 | 8.72k | } |
1869 | | |
1870 | | // ... otherwise any named type that this type refers to, one level |
1871 | | // deep, must be imported into this world under that name. |
1872 | | |
1873 | 8.73k | let mut visitor = MyVisit(self, Vec::new()); |
1874 | 8.73k | visitor.visit_type_def(self, ty); |
1875 | 18.9k | for ty in visitor.1 { |
1876 | 10.2k | let ty = &self.types[ty]; |
1877 | 10.2k | let Some(name) = ty.name.clone() else { |
1878 | 9.28k | continue; |
1879 | | }; |
1880 | 936 | let dep_key = WorldKey::Name(name); |
1881 | 936 | assert!( |
1882 | 936 | world.imports.contains_key(&dep_key), |
1883 | 0 | "world import `{}` should also force an import of `{}`", |
1884 | 0 | self.name_world_key(key), |
1885 | 0 | self.name_world_key(&dep_key), |
1886 | | ); |
1887 | | } |
1888 | | |
1889 | | struct MyVisit<'a>(&'a Resolve, Vec<TypeId>); |
1890 | | |
1891 | | impl TypeIdVisitor for MyVisit<'_> { |
1892 | 10.2k | fn before_visit_type_id(&mut self, id: TypeId) -> bool { |
1893 | 10.2k | self.1.push(id); |
1894 | | // recurse into unnamed types to look at all named types |
1895 | 10.2k | self.0.types[id].name.is_none() |
1896 | 10.2k | } |
1897 | | } |
1898 | 9.70k | } |
1899 | | |
1900 | | /// This asserts that all types referred to by `func` are imported into |
1901 | | /// `world` under `WorldKey::Name`. Note that this is only applicable to |
1902 | | /// named type |
1903 | 2.47k | fn assert_world_function_imports_types(&self, world: &World, key: &WorldKey, func: &Function) { |
1904 | 7.41k | for ty in func |
1905 | 2.47k | .parameter_and_result_types() |
1906 | 2.47k | .chain(func.kind.resource().map(Type::Id)) |
1907 | | { |
1908 | 7.41k | let Type::Id(id) = ty else { |
1909 | 5.16k | continue; |
1910 | | }; |
1911 | 2.25k | self.assert_world_imports_type_deps(world, key, id); |
1912 | | } |
1913 | 2.47k | } |
1914 | | |
1915 | | /// Returns whether the `stability` annotation contained within `pkg_id` |
1916 | | /// should be included or not. |
1917 | | /// |
1918 | | /// The `span` provided here is an optional span pointing to the item that |
1919 | | /// is annotated with `stability`. |
1920 | | /// |
1921 | | /// Returns `Ok(true)` if the item is included, or `Ok(false)` if the item |
1922 | | /// is not. |
1923 | | /// |
1924 | | /// # Errors |
1925 | | /// |
1926 | | /// Returns an error if the `pkg_id` isn't annotated with sufficient version |
1927 | | /// information to have a `stability` annotation. For example if `pkg_id` |
1928 | | /// has no version listed then an error will be returned if `stability` |
1929 | | /// mentions a version. |
1930 | 293k | fn include_stability( |
1931 | 293k | &self, |
1932 | 293k | stability: &Stability, |
1933 | 293k | pkg_id: &PackageId, |
1934 | 293k | span: Option<Span>, |
1935 | 293k | ) -> Result<bool> { |
1936 | 293k | let err = |msg: String| match span { |
1937 | 0 | Some(span) => Error::new(span, msg).into(), |
1938 | 0 | None => anyhow::Error::msg(msg), |
1939 | 0 | }; |
1940 | 293k | Ok(match stability { |
1941 | 286k | Stability::Unknown => true, |
1942 | | // NOTE: deprecations are intentionally omitted -- an existing |
1943 | | // `@since` takes precedence over `@deprecated` |
1944 | 5.65k | Stability::Stable { since, .. } => { |
1945 | 5.65k | let Some(p) = self.packages.get(*pkg_id) else { |
1946 | | // We can't check much without a package (possibly dealing |
1947 | | // with an item in an `UnresolvedPackage`), @since version & |
1948 | | // deprecations can't be checked because there's no package |
1949 | | // version to compare to. |
1950 | | // |
1951 | | // Feature requirements on stabilized features are ignored |
1952 | | // in resolved packages, so we do the same here. |
1953 | 0 | return Ok(true); |
1954 | | }; |
1955 | | |
1956 | | // Use of feature gating with version specifiers inside a |
1957 | | // package that is not versioned is not allowed |
1958 | 5.65k | let package_version = p.name.version.as_ref().ok_or_else(|| { |
1959 | 0 | err(format!( |
1960 | 0 | "package [{}] contains a feature gate with a version \ |
1961 | 0 | specifier, so it must have a version", |
1962 | 0 | p.name |
1963 | 0 | )) |
1964 | 0 | })?; |
1965 | | |
1966 | | // If the version on the feature gate is: |
1967 | | // - released, then we can include it |
1968 | | // - unreleased, then we must check the feature (if present) |
1969 | 5.65k | if since > package_version { |
1970 | 0 | return Err(err(format!( |
1971 | 0 | "feature gate cannot reference unreleased version \ |
1972 | 0 | {since} of package [{}] (current version {package_version})", |
1973 | 0 | p.name |
1974 | 0 | ))); |
1975 | 5.65k | } |
1976 | | |
1977 | 5.65k | true |
1978 | | } |
1979 | 2.02k | Stability::Unstable { feature, .. } => { |
1980 | 2.02k | self.features.contains(feature) || self.all_features |
1981 | | } |
1982 | | }) |
1983 | 293k | } |
1984 | | |
1985 | | /// Convenience wrapper around `include_stability` specialized for types |
1986 | | /// with a more targeted error message. |
1987 | 143k | fn include_type(&self, ty: &TypeDef, pkgid: PackageId, span: Span) -> Result<bool> { |
1988 | 143k | self.include_stability(&ty.stability, &pkgid, Some(span)) |
1989 | 143k | .with_context(|| { |
1990 | 0 | format!( |
1991 | 0 | "failed to process feature gate for type [{}] in package [{}]", |
1992 | 0 | ty.name.as_ref().map(String::as_str).unwrap_or("<unknown>"), |
1993 | 0 | self.packages[pkgid].name, |
1994 | | ) |
1995 | 0 | }) |
1996 | 143k | } |
1997 | | |
1998 | | /// Performs the "elaboration process" necessary for the `world_id` |
1999 | | /// specified to ensure that all of its transitive imports are listed. |
2000 | | /// |
2001 | | /// This function will take the unordered lists of the specified world's |
2002 | | /// imports and exports and "elaborate" them to ensure that they're |
2003 | | /// topographically sorted where all transitively required interfaces by |
2004 | | /// imports, or exports, are listed. This will additionally validate that |
2005 | | /// the exports are all valid and present, specifically with the restriction |
2006 | | /// noted on `elaborate_world_exports`. |
2007 | | /// |
2008 | | /// The world is mutated in-place in this `Resolve`. |
2009 | 34.8k | fn elaborate_world(&mut self, world_id: WorldId) -> Result<()> { |
2010 | | // First process all imports. This is easier than exports since the only |
2011 | | // requirement here is that all interfaces need to be added with a |
2012 | | // topological order between them. |
2013 | 34.8k | let mut new_imports = IndexMap::new(); |
2014 | 34.8k | let world = &self.worlds[world_id]; |
2015 | | |
2016 | | // Sort the imports by "class" to ensure that this matches the order |
2017 | | // that items are printed and that items are in topological order. |
2018 | | // |
2019 | | // When printing worlds in WIT: |
2020 | | // |
2021 | | // * interfaces come first |
2022 | | // * types are next |
2023 | | // * type imports are first |
2024 | | // * type definitions are next |
2025 | | // * resource definitions have methods printed inline |
2026 | | // * freestanding functions are last |
2027 | | // |
2028 | | // This reflects the topological order between items where types |
2029 | | // can refer to imports and functions can refer to these types. Ordering |
2030 | | // within a single class (e.g. imports depending on each other, types |
2031 | | // referring to each other) is already preserved by other passes in this |
2032 | | // file and general AST resolution. That means that a stable sort here |
2033 | | // can be used to ensure that each class is in the right location |
2034 | | // relative to the others. |
2035 | | // |
2036 | | // Overall this ensures that round-trips of WIT through wasm should |
2037 | | // always produce the same result. |
2038 | 112k | let sort_key = |resolve: &Resolve, item: &WorldItem| match item { |
2039 | 12.0k | WorldItem::Interface { .. } => 0, |
2040 | 87.6k | WorldItem::Type(ty) => { |
2041 | 87.6k | let ty = &resolve.types[*ty]; |
2042 | 11.6k | match ty.kind { |
2043 | 11.6k | TypeDefKind::Type(Type::Id(t)) if resolve.types[t].owner != ty.owner => 1, |
2044 | 76.0k | _ => 2, |
2045 | | } |
2046 | | } |
2047 | 12.8k | WorldItem::Function(f) => { |
2048 | 12.8k | if f.kind.resource().is_none() { |
2049 | 6.18k | 3 |
2050 | | } else { |
2051 | 6.70k | 4 |
2052 | | } |
2053 | | } |
2054 | 112k | }; |
2055 | | |
2056 | | // Sort world items when we start to elaborate the world to start with a |
2057 | | // topological view of items. |
2058 | 34.8k | let mut world_imports = world.imports.iter().collect::<Vec<_>>(); |
2059 | 70.0k | world_imports.sort_by_key(|(_name, import)| sort_key(self, import)); |
2060 | 82.5k | for (name, item) in world_imports { |
2061 | 47.6k | match item { |
2062 | | // Interfaces get their dependencies added first followed by the |
2063 | | // interface itself. |
2064 | 5.15k | WorldItem::Interface { id, stability } => { |
2065 | 5.15k | self.elaborate_world_import(&mut new_imports, name.clone(), *id, &stability); |
2066 | 5.15k | } |
2067 | | |
2068 | | // Functions are added as-is since their dependence on types in |
2069 | | // the world should already be satisfied. |
2070 | | WorldItem::Function(_) => { |
2071 | 6.88k | let prev = new_imports.insert(name.clone(), item.clone()); |
2072 | 6.88k | assert!(prev.is_none()); |
2073 | | } |
2074 | | |
2075 | | // Types may depend on an interface, in which case a (possibly) |
2076 | | // recursive addition of that interface happens here. Afterwards |
2077 | | // the type itself can be added safely. |
2078 | 35.5k | WorldItem::Type(id) => { |
2079 | 35.5k | if let Some(dep) = self.type_interface_dep(*id) { |
2080 | 4.01k | self.elaborate_world_import( |
2081 | 4.01k | &mut new_imports, |
2082 | 4.01k | WorldKey::Interface(dep), |
2083 | 4.01k | dep, |
2084 | 4.01k | &self.types[*id].stability, |
2085 | 4.01k | ); |
2086 | 31.5k | } |
2087 | 35.5k | let prev = new_imports.insert(name.clone(), item.clone()); |
2088 | 35.5k | assert!(prev.is_none()); |
2089 | | } |
2090 | | } |
2091 | | } |
2092 | | |
2093 | | // Exports are trickier than imports, notably to uphold the invariant |
2094 | | // required by `elaborate_world_exports`. To do this the exports are |
2095 | | // partitioned into interfaces/functions. All functions are added to |
2096 | | // the new exports list during this loop but interfaces are all deferred |
2097 | | // to be handled in the `elaborate_world_exports` function. |
2098 | 34.8k | let mut new_exports = IndexMap::new(); |
2099 | 34.8k | let mut export_interfaces = IndexMap::new(); |
2100 | 34.8k | for (name, item) in world.exports.iter() { |
2101 | 5.33k | match item { |
2102 | 2.96k | WorldItem::Interface { id, stability } => { |
2103 | 2.96k | let prev = export_interfaces.insert(*id, (name.clone(), stability)); |
2104 | 2.96k | assert!(prev.is_none()); |
2105 | | } |
2106 | | WorldItem::Function(_) => { |
2107 | 2.36k | let prev = new_exports.insert(name.clone(), item.clone()); |
2108 | 2.36k | assert!(prev.is_none()); |
2109 | | } |
2110 | 0 | WorldItem::Type(_) => unreachable!(), |
2111 | | } |
2112 | | } |
2113 | | |
2114 | 34.8k | self.elaborate_world_exports(&export_interfaces, &mut new_imports, &mut new_exports)?; |
2115 | | |
2116 | | // In addition to sorting at the start of elaboration also sort here at |
2117 | | // the end of elaboration to handle types being interspersed with |
2118 | | // interfaces as they're found. |
2119 | 42.5k | new_imports.sort_by_cached_key(|_name, import| sort_key(self, import)); |
2120 | | |
2121 | | // And with all that done the world is updated in-place with |
2122 | | // imports/exports. |
2123 | 34.8k | log::trace!("imports = {new_imports:?}"); |
2124 | 34.8k | log::trace!("exports = {new_exports:?}"); |
2125 | 34.8k | let world = &mut self.worlds[world_id]; |
2126 | 34.8k | world.imports = new_imports; |
2127 | 34.8k | world.exports = new_exports; |
2128 | | |
2129 | 34.8k | Ok(()) |
2130 | 34.8k | } |
2131 | | |
2132 | 10.6k | fn elaborate_world_import( |
2133 | 10.6k | &self, |
2134 | 10.6k | imports: &mut IndexMap<WorldKey, WorldItem>, |
2135 | 10.6k | key: WorldKey, |
2136 | 10.6k | id: InterfaceId, |
2137 | 10.6k | stability: &Stability, |
2138 | 10.6k | ) { |
2139 | 10.6k | if imports.contains_key(&key) { |
2140 | 4.59k | return; |
2141 | 6.02k | } |
2142 | 6.02k | for dep in self.interface_direct_deps(id) { |
2143 | 1.44k | self.elaborate_world_import(imports, WorldKey::Interface(dep), dep, stability); |
2144 | 1.44k | } |
2145 | 6.02k | let prev = imports.insert( |
2146 | 6.02k | key, |
2147 | 6.02k | WorldItem::Interface { |
2148 | 6.02k | id, |
2149 | 6.02k | stability: stability.clone(), |
2150 | 6.02k | }, |
2151 | | ); |
2152 | 6.02k | assert!(prev.is_none()); |
2153 | 10.6k | } |
2154 | | |
2155 | | /// This function adds all of the interfaces in `export_interfaces` to the |
2156 | | /// list of exports of the `world` specified. |
2157 | | /// |
2158 | | /// This method is more involved than adding imports because it is fallible. |
2159 | | /// Chiefly what can happen is that the dependencies of all exports must be |
2160 | | /// satisfied by other exports or imports, but not both. For example given a |
2161 | | /// situation such as: |
2162 | | /// |
2163 | | /// ```wit |
2164 | | /// interface a { |
2165 | | /// type t = u32 |
2166 | | /// } |
2167 | | /// interface b { |
2168 | | /// use a.{t} |
2169 | | /// } |
2170 | | /// interface c { |
2171 | | /// use a.{t} |
2172 | | /// use b.{t as t2} |
2173 | | /// } |
2174 | | /// ``` |
2175 | | /// |
2176 | | /// where `c` depends on `b` and `a` where `b` depends on `a`, then the |
2177 | | /// purpose of this method is to reject this world: |
2178 | | /// |
2179 | | /// ```wit |
2180 | | /// world foo { |
2181 | | /// export a |
2182 | | /// export c |
2183 | | /// } |
2184 | | /// ``` |
2185 | | /// |
2186 | | /// The reasoning here is unfortunately subtle and is additionally the |
2187 | | /// subject of WebAssembly/component-model#208. Effectively the `c` |
2188 | | /// interface depends on `b`, but it's not listed explicitly as an import, |
2189 | | /// so it's then implicitly added as an import. This then transitively |
2190 | | /// depends on `a` so it's also added as an import. At this point though `c` |
2191 | | /// also depends on `a`, and it's also exported, so naively it should depend |
2192 | | /// on the export and not implicitly add an import. This means though that |
2193 | | /// `c` has access to two copies of `a`, one imported and one exported. This |
2194 | | /// is not valid, especially in the face of resource types. |
2195 | | /// |
2196 | | /// Overall this method is tasked with rejecting the above world by walking |
2197 | | /// over all the exports and adding their dependencies. Each dependency is |
2198 | | /// recorded with whether it's required to be imported, and then if an |
2199 | | /// export is added for something that's required to be an error then the |
2200 | | /// operation fails. |
2201 | 34.8k | fn elaborate_world_exports( |
2202 | 34.8k | &self, |
2203 | 34.8k | export_interfaces: &IndexMap<InterfaceId, (WorldKey, &Stability)>, |
2204 | 34.8k | imports: &mut IndexMap<WorldKey, WorldItem>, |
2205 | 34.8k | exports: &mut IndexMap<WorldKey, WorldItem>, |
2206 | 34.8k | ) -> Result<()> { |
2207 | 34.8k | let mut required_imports = HashSet::new(); |
2208 | 34.8k | for (id, (key, stability)) in export_interfaces.iter() { |
2209 | 2.96k | let name = self.name_world_key(&key); |
2210 | 2.96k | let ok = add_world_export( |
2211 | 2.96k | self, |
2212 | 2.96k | imports, |
2213 | 2.96k | exports, |
2214 | 2.96k | export_interfaces, |
2215 | 2.96k | &mut required_imports, |
2216 | 2.96k | *id, |
2217 | 2.96k | key, |
2218 | | true, |
2219 | 2.96k | stability, |
2220 | | ); |
2221 | 2.96k | if !ok { |
2222 | 0 | bail!( |
2223 | | // FIXME: this is not a great error message and basically no |
2224 | | // one will know what to do when it gets printed. Improving |
2225 | | // this error message, however, is a chunk of work that may |
2226 | | // not be best spent doing this at this time, so I'm writing |
2227 | | // this comment instead. |
2228 | | // |
2229 | | // More-or-less what should happen here is that a "path" |
2230 | | // from this interface to the conflicting interface should |
2231 | | // be printed. It should be explained why an import is being |
2232 | | // injected, why that's conflicting with an export, and |
2233 | | // ideally with a suggestion of "add this interface to the |
2234 | | // export list to fix this error". |
2235 | | // |
2236 | | // That's a lot of info that's not easy to get at without |
2237 | | // more refactoring, so it's left to a future date in the |
2238 | | // hopes that most folks won't actually run into this for |
2239 | | // the time being. |
2240 | 0 | InvalidTransitiveDependency(name), |
2241 | | ); |
2242 | 2.96k | } |
2243 | | } |
2244 | 34.8k | return Ok(()); |
2245 | | |
2246 | 3.56k | fn add_world_export( |
2247 | 3.56k | resolve: &Resolve, |
2248 | 3.56k | imports: &mut IndexMap<WorldKey, WorldItem>, |
2249 | 3.56k | exports: &mut IndexMap<WorldKey, WorldItem>, |
2250 | 3.56k | export_interfaces: &IndexMap<InterfaceId, (WorldKey, &Stability)>, |
2251 | 3.56k | required_imports: &mut HashSet<InterfaceId>, |
2252 | 3.56k | id: InterfaceId, |
2253 | 3.56k | key: &WorldKey, |
2254 | 3.56k | add_export: bool, |
2255 | 3.56k | stability: &Stability, |
2256 | 3.56k | ) -> bool { |
2257 | 3.56k | if exports.contains_key(key) { |
2258 | 26 | if add_export { |
2259 | 26 | return true; |
2260 | | } else { |
2261 | 0 | return false; |
2262 | | } |
2263 | 3.53k | } |
2264 | | // If this is an import and it's already in the `required_imports` |
2265 | | // set then we can skip it as we've already visited this interface. |
2266 | 3.53k | if !add_export && required_imports.contains(&id) { |
2267 | 228 | return true; |
2268 | 3.30k | } |
2269 | 3.30k | let ok = resolve.interface_direct_deps(id).all(|dep| { |
2270 | 596 | let key = WorldKey::Interface(dep); |
2271 | 596 | let add_export = add_export && export_interfaces.contains_key(&dep); |
2272 | 596 | add_world_export( |
2273 | 596 | resolve, |
2274 | 596 | imports, |
2275 | 596 | exports, |
2276 | 596 | export_interfaces, |
2277 | 596 | required_imports, |
2278 | 596 | dep, |
2279 | 596 | &key, |
2280 | 596 | add_export, |
2281 | 596 | stability, |
2282 | | ) |
2283 | 596 | }); |
2284 | 3.30k | if !ok { |
2285 | 0 | return false; |
2286 | 3.30k | } |
2287 | 3.30k | let item = WorldItem::Interface { |
2288 | 3.30k | id, |
2289 | 3.30k | stability: stability.clone(), |
2290 | 3.30k | }; |
2291 | 3.30k | if add_export { |
2292 | 2.96k | if required_imports.contains(&id) { |
2293 | 0 | return false; |
2294 | 2.96k | } |
2295 | 2.96k | exports.insert(key.clone(), item); |
2296 | 342 | } else { |
2297 | 342 | required_imports.insert(id); |
2298 | 342 | imports.insert(key.clone(), item); |
2299 | 342 | } |
2300 | 3.30k | true |
2301 | 3.56k | } |
2302 | 34.8k | } |
2303 | | |
2304 | | /// Remove duplicate imports from a world if they import from the same |
2305 | | /// interface with semver-compatible versions. |
2306 | | /// |
2307 | | /// This will merge duplicate interfaces present at multiple versions in |
2308 | | /// both a world by selecting the larger version of the two interfaces. This |
2309 | | /// requires that the interfaces are indeed semver-compatible and it means |
2310 | | /// that some imports might be removed and replaced. Note that this is only |
2311 | | /// done within a single semver track, for example the world imports 0.2.0 |
2312 | | /// and 0.2.1 then the result afterwards will be that it imports |
2313 | | /// 0.2.1. If, however, 0.3.0 where imported then the final result would |
2314 | | /// import both 0.2.0 and 0.3.0. |
2315 | 4.70k | pub fn merge_world_imports_based_on_semver(&mut self, world_id: WorldId) -> Result<()> { |
2316 | 4.70k | let world = &self.worlds[world_id]; |
2317 | | |
2318 | | // The first pass here is to build a map of "semver tracks" where they |
2319 | | // key is per-interface and the value is the maximal version found in |
2320 | | // that semver-compatible-track plus the interface which is the maximal |
2321 | | // version. |
2322 | | // |
2323 | | // At the same time a `to_remove` set is maintained to remember what |
2324 | | // interfaces are being removed from `from` and `into`. All of |
2325 | | // `to_remove` are placed with a known other version. |
2326 | 4.70k | let mut semver_tracks = HashMap::new(); |
2327 | 4.70k | let mut to_remove = HashSet::new(); |
2328 | 6.24k | for (key, _) in world.imports.iter() { |
2329 | 6.24k | let iface_id = match key { |
2330 | 422 | WorldKey::Interface(id) => *id, |
2331 | 5.82k | WorldKey::Name(_) => continue, |
2332 | | }; |
2333 | 422 | let (track, version) = match self.semver_track(iface_id) { |
2334 | 332 | Some(track) => track, |
2335 | 90 | None => continue, |
2336 | | }; |
2337 | 332 | log::debug!( |
2338 | 0 | "{} is on track {}/{}", |
2339 | 0 | self.id_of(iface_id).unwrap(), |
2340 | | track.0, |
2341 | | track.1, |
2342 | | ); |
2343 | 332 | match semver_tracks.entry(track.clone()) { |
2344 | 332 | hash_map::Entry::Vacant(e) => { |
2345 | 332 | e.insert((version, iface_id)); |
2346 | 332 | } |
2347 | 0 | hash_map::Entry::Occupied(mut e) => match version.cmp(&e.get().0) { |
2348 | 0 | Ordering::Greater => { |
2349 | 0 | to_remove.insert(e.get().1); |
2350 | 0 | e.insert((version, iface_id)); |
2351 | 0 | } |
2352 | 0 | Ordering::Equal => {} |
2353 | 0 | Ordering::Less => { |
2354 | 0 | to_remove.insert(iface_id); |
2355 | 0 | } |
2356 | | }, |
2357 | | } |
2358 | | } |
2359 | | |
2360 | | // Build a map of "this interface is replaced with this interface" using |
2361 | | // the results of the loop above. |
2362 | 4.70k | let mut replacements = HashMap::new(); |
2363 | 4.70k | for id in to_remove { |
2364 | 0 | let (track, _) = self.semver_track(id).unwrap(); |
2365 | 0 | let (_, latest) = semver_tracks[&track]; |
2366 | 0 | let prev = replacements.insert(id, latest); |
2367 | 0 | assert!(prev.is_none()); |
2368 | | } |
2369 | | |
2370 | | // Validate that `merge_world_item` succeeds for merging all removed |
2371 | | // interfaces with their replacement. This is a double-check that the |
2372 | | // semver version is actually correct and all items present in the old |
2373 | | // interface are in the new. |
2374 | 4.70k | for (to_replace, replace_with) in replacements.iter() { |
2375 | 0 | self.merge_world_item( |
2376 | 0 | &WorldItem::Interface { |
2377 | 0 | id: *to_replace, |
2378 | 0 | stability: Default::default(), |
2379 | 0 | }, |
2380 | 0 | &WorldItem::Interface { |
2381 | 0 | id: *replace_with, |
2382 | 0 | stability: Default::default(), |
2383 | 0 | }, |
2384 | | ) |
2385 | 0 | .with_context(|| { |
2386 | 0 | let old_name = self.id_of(*to_replace).unwrap(); |
2387 | 0 | let new_name = self.id_of(*replace_with).unwrap(); |
2388 | 0 | format!( |
2389 | 0 | "failed to upgrade `{old_name}` to `{new_name}`, was \ |
2390 | 0 | this semver-compatible update not semver compatible?" |
2391 | | ) |
2392 | 0 | })?; |
2393 | | } |
2394 | | |
2395 | 4.70k | for (to_replace, replace_with) in replacements.iter() { |
2396 | 0 | log::debug!( |
2397 | 0 | "REPLACE {} => {}", |
2398 | 0 | self.id_of(*to_replace).unwrap(), |
2399 | 0 | self.id_of(*replace_with).unwrap(), |
2400 | | ); |
2401 | | } |
2402 | | |
2403 | | // Finally perform the actual transformation of the imports/exports. |
2404 | | // Here all imports are removed if they're replaced and otherwise all |
2405 | | // imports have their dependencies updated, possibly transitively, to |
2406 | | // point to the new interfaces in `replacements`. |
2407 | | // |
2408 | | // Afterwards exports are additionally updated, but only their |
2409 | | // dependencies on imports which were remapped. Exports themselves are |
2410 | | // not deduplicated and/or removed. |
2411 | 6.24k | for (key, item) in mem::take(&mut self.worlds[world_id].imports) { |
2412 | 6.24k | if let WorldItem::Interface { id, .. } = item { |
2413 | 819 | if replacements.contains_key(&id) { |
2414 | 0 | continue; |
2415 | 819 | } |
2416 | 5.42k | } |
2417 | | |
2418 | 6.24k | self.update_interface_deps_of_world_item(&item, &replacements); |
2419 | | |
2420 | 6.24k | let prev = self.worlds[world_id].imports.insert(key, item); |
2421 | 6.24k | assert!(prev.is_none()); |
2422 | | } |
2423 | 4.70k | for (key, item) in mem::take(&mut self.worlds[world_id].exports) { |
2424 | 873 | self.update_interface_deps_of_world_item(&item, &replacements); |
2425 | 873 | let prev = self.worlds[world_id].exports.insert(key, item); |
2426 | 873 | assert!(prev.is_none()); |
2427 | | } |
2428 | | |
2429 | | // Run through `elaborate_world` to reorder imports as appropriate and |
2430 | | // fill anything back in if it's actually required by exports. For now |
2431 | | // this doesn't tamper with exports at all. Also note that this is |
2432 | | // applied to all worlds in this `Resolve` because interfaces were |
2433 | | // modified directly. |
2434 | 4.70k | let ids = self.worlds.iter().map(|(id, _)| id).collect::<Vec<_>>(); |
2435 | 18.1k | for world_id in ids { |
2436 | 13.4k | self.elaborate_world(world_id).with_context(|| { |
2437 | 0 | let name = &self.worlds[world_id].name; |
2438 | 0 | format!( |
2439 | 0 | "failed to elaborate world `{name}` after deduplicating imports \ |
2440 | 0 | based on semver" |
2441 | | ) |
2442 | 0 | })?; |
2443 | | } |
2444 | | |
2445 | | #[cfg(debug_assertions)] |
2446 | | self.assert_valid(); |
2447 | | |
2448 | 4.70k | Ok(()) |
2449 | 4.70k | } |
2450 | | |
2451 | 7.12k | fn update_interface_deps_of_world_item( |
2452 | 7.12k | &mut self, |
2453 | 7.12k | item: &WorldItem, |
2454 | 7.12k | replacements: &HashMap<InterfaceId, InterfaceId>, |
2455 | 7.12k | ) { |
2456 | 7.12k | match *item { |
2457 | 4.42k | WorldItem::Type(t) => self.update_interface_dep_of_type(t, &replacements), |
2458 | 1.30k | WorldItem::Interface { id, .. } => { |
2459 | 1.30k | let types = self.interfaces[id] |
2460 | 1.30k | .types |
2461 | 1.30k | .values() |
2462 | 1.30k | .copied() |
2463 | 1.30k | .collect::<Vec<_>>(); |
2464 | 3.50k | for ty in types { |
2465 | 2.19k | self.update_interface_dep_of_type(ty, &replacements); |
2466 | 2.19k | } |
2467 | | } |
2468 | 1.38k | WorldItem::Function(_) => {} |
2469 | | } |
2470 | 7.12k | } |
2471 | | |
2472 | | /// Returns the "semver track" of an interface plus the interface's version. |
2473 | | /// |
2474 | | /// This function returns `None` if the interface `id` has a package without |
2475 | | /// a version. If the version is present, however, the first element of the |
2476 | | /// tuple returned is a "semver track" for the specific interface. The |
2477 | | /// version listed in `PackageName` will be modified so all |
2478 | | /// semver-compatible versions are listed the same way. |
2479 | | /// |
2480 | | /// The second element in the returned tuple is this interface's package's |
2481 | | /// version. |
2482 | 422 | fn semver_track(&self, id: InterfaceId) -> Option<((PackageName, String), &Version)> { |
2483 | 422 | let iface = &self.interfaces[id]; |
2484 | 422 | let pkg = &self.packages[iface.package?]; |
2485 | 422 | let version = pkg.name.version.as_ref()?; |
2486 | 332 | let mut name = pkg.name.clone(); |
2487 | 332 | name.version = Some(PackageName::version_compat_track(version)); |
2488 | 332 | Some(((name, iface.name.clone()?), version)) |
2489 | 422 | } |
2490 | | |
2491 | | /// If `ty` is a definition where it's a `use` from another interface, then |
2492 | | /// change what interface it's using from according to the pairs in the |
2493 | | /// `replacements` map. |
2494 | 6.62k | fn update_interface_dep_of_type( |
2495 | 6.62k | &mut self, |
2496 | 6.62k | ty: TypeId, |
2497 | 6.62k | replacements: &HashMap<InterfaceId, InterfaceId>, |
2498 | 6.62k | ) { |
2499 | 6.62k | let to_replace = match self.type_interface_dep(ty) { |
2500 | 858 | Some(id) => id, |
2501 | 5.76k | None => return, |
2502 | | }; |
2503 | 858 | let replace_with = match replacements.get(&to_replace) { |
2504 | 0 | Some(id) => id, |
2505 | 858 | None => return, |
2506 | | }; |
2507 | 0 | let dep = match self.types[ty].kind { |
2508 | 0 | TypeDefKind::Type(Type::Id(id)) => id, |
2509 | 0 | _ => return, |
2510 | | }; |
2511 | 0 | let name = self.types[dep].name.as_ref().unwrap(); |
2512 | | // Note the infallible name indexing happening here. This should be |
2513 | | // previously validated with `merge_world_item` to succeed. |
2514 | 0 | let replacement_id = self.interfaces[*replace_with].types[name]; |
2515 | 0 | self.types[ty].kind = TypeDefKind::Type(Type::Id(replacement_id)); |
2516 | 6.62k | } |
2517 | | |
2518 | | /// Returns the core wasm module/field names for the specified `import`. |
2519 | | /// |
2520 | | /// This function will return the core wasm module/field that can be used to |
2521 | | /// use `import` with the name `mangling` scheme specified as well. This can |
2522 | | /// be useful for bindings generators, for example, and these names are |
2523 | | /// recognized by `wit-component` and `wasm-tools component new`. |
2524 | 2.41k | pub fn wasm_import_name( |
2525 | 2.41k | &self, |
2526 | 2.41k | mangling: ManglingAndAbi, |
2527 | 2.41k | import: WasmImport<'_>, |
2528 | 2.41k | ) -> (String, String) { |
2529 | 2.41k | match mangling { |
2530 | 188 | ManglingAndAbi::Standard32 => match import { |
2531 | 125 | WasmImport::Func { interface, func } => { |
2532 | 125 | let module = match interface { |
2533 | 71 | Some(key) => format!("cm32p2|{}", self.name_canonicalized_world_key(key)), |
2534 | 54 | None => format!("cm32p2"), |
2535 | | }; |
2536 | 125 | (module, func.name.clone()) |
2537 | | } |
2538 | | WasmImport::ResourceIntrinsic { |
2539 | 63 | interface, |
2540 | 63 | resource, |
2541 | 63 | intrinsic, |
2542 | | } => { |
2543 | 63 | let name = self.types[resource].name.as_ref().unwrap(); |
2544 | 63 | let (prefix, name) = match intrinsic { |
2545 | 27 | ResourceIntrinsic::ImportedDrop => ("", format!("{name}_drop")), |
2546 | 12 | ResourceIntrinsic::ExportedDrop => ("_ex_", format!("{name}_drop")), |
2547 | 12 | ResourceIntrinsic::ExportedNew => ("_ex_", format!("{name}_new")), |
2548 | 12 | ResourceIntrinsic::ExportedRep => ("_ex_", format!("{name}_rep")), |
2549 | | }; |
2550 | 63 | let module = match interface { |
2551 | 47 | Some(key) => { |
2552 | 47 | format!("cm32p2|{prefix}{}", self.name_canonicalized_world_key(key)) |
2553 | | } |
2554 | | None => { |
2555 | 16 | assert_eq!(prefix, ""); |
2556 | 16 | format!("cm32p2") |
2557 | | } |
2558 | | }; |
2559 | 63 | (module, name) |
2560 | | } |
2561 | | }, |
2562 | 2.22k | ManglingAndAbi::Legacy(abi) => match import { |
2563 | 1.75k | WasmImport::Func { interface, func } => { |
2564 | 1.75k | let module = match interface { |
2565 | 907 | Some(key) => self.name_world_key(key), |
2566 | 849 | None => format!("$root"), |
2567 | | }; |
2568 | 1.75k | (module, format!("{}{}", abi.import_prefix(), func.name)) |
2569 | | } |
2570 | | WasmImport::ResourceIntrinsic { |
2571 | 470 | interface, |
2572 | 470 | resource, |
2573 | 470 | intrinsic, |
2574 | | } => { |
2575 | 470 | let name = self.types[resource].name.as_ref().unwrap(); |
2576 | 470 | let (prefix, name) = match intrinsic { |
2577 | 254 | ResourceIntrinsic::ImportedDrop => ("", format!("[resource-drop]{name}")), |
2578 | | ResourceIntrinsic::ExportedDrop => { |
2579 | 72 | ("[export]", format!("[resource-drop]{name}")) |
2580 | | } |
2581 | | ResourceIntrinsic::ExportedNew => { |
2582 | 72 | ("[export]", format!("[resource-new]{name}")) |
2583 | | } |
2584 | | ResourceIntrinsic::ExportedRep => { |
2585 | 72 | ("[export]", format!("[resource-rep]{name}")) |
2586 | | } |
2587 | | }; |
2588 | 470 | let module = match interface { |
2589 | 285 | Some(key) => format!("{prefix}{}", self.name_world_key(key)), |
2590 | | None => { |
2591 | 185 | assert_eq!(prefix, ""); |
2592 | 185 | format!("$root") |
2593 | | } |
2594 | | }; |
2595 | 470 | (module, format!("{}{name}", abi.import_prefix())) |
2596 | | } |
2597 | | }, |
2598 | | } |
2599 | 2.41k | } |
2600 | | |
2601 | | /// Returns the core wasm export name for the specified `export`. |
2602 | | /// |
2603 | | /// This is the same as [`Resolve::wasm_import_name`], except for exports. |
2604 | 14.5k | pub fn wasm_export_name(&self, mangling: ManglingAndAbi, export: WasmExport<'_>) -> String { |
2605 | 14.5k | match mangling { |
2606 | 888 | ManglingAndAbi::Standard32 => match export { |
2607 | | WasmExport::Func { |
2608 | 246 | interface, |
2609 | 246 | func, |
2610 | 246 | kind, |
2611 | | } => { |
2612 | 246 | let mut name = String::from("cm32p2|"); |
2613 | 246 | if let Some(interface) = interface { |
2614 | 188 | let s = self.name_canonicalized_world_key(interface); |
2615 | 188 | name.push_str(&s); |
2616 | 188 | } |
2617 | 246 | name.push_str("|"); |
2618 | 246 | name.push_str(&func.name); |
2619 | 246 | match kind { |
2620 | 123 | WasmExportKind::Normal => {} |
2621 | 123 | WasmExportKind::PostReturn => name.push_str("_post"), |
2622 | 0 | WasmExportKind::Callback => todo!( |
2623 | | "not yet supported: \ |
2624 | | async callback functions using standard name mangling" |
2625 | | ), |
2626 | | } |
2627 | 246 | name |
2628 | | } |
2629 | | WasmExport::ResourceDtor { |
2630 | 12 | interface, |
2631 | 12 | resource, |
2632 | | } => { |
2633 | 12 | let name = self.types[resource].name.as_ref().unwrap(); |
2634 | 12 | let interface = self.name_canonicalized_world_key(interface); |
2635 | 12 | format!("cm32p2|{interface}|{name}_dtor") |
2636 | | } |
2637 | 210 | WasmExport::Memory => "cm32p2_memory".to_string(), |
2638 | 210 | WasmExport::Initialize => "cm32p2_initialize".to_string(), |
2639 | 210 | WasmExport::Realloc => "cm32p2_realloc".to_string(), |
2640 | | }, |
2641 | 13.6k | ManglingAndAbi::Legacy(abi) => match export { |
2642 | | WasmExport::Func { |
2643 | 1.98k | interface, |
2644 | 1.98k | func, |
2645 | 1.98k | kind, |
2646 | | } => { |
2647 | 1.98k | let mut name = abi.export_prefix().to_string(); |
2648 | 1.98k | match kind { |
2649 | 1.02k | WasmExportKind::Normal => {} |
2650 | 813 | WasmExportKind::PostReturn => name.push_str("cabi_post_"), |
2651 | | WasmExportKind::Callback => { |
2652 | 150 | assert!(matches!(abi, LiftLowerAbi::AsyncCallback)); |
2653 | 150 | name = format!("[callback]{name}") |
2654 | | } |
2655 | | } |
2656 | 1.98k | if let Some(interface) = interface { |
2657 | 1.39k | let s = self.name_world_key(interface); |
2658 | 1.39k | name.push_str(&s); |
2659 | 1.39k | name.push_str("#"); |
2660 | 1.39k | } |
2661 | 1.98k | name.push_str(&func.name); |
2662 | 1.98k | name |
2663 | | } |
2664 | | WasmExport::ResourceDtor { |
2665 | 72 | interface, |
2666 | 72 | resource, |
2667 | | } => { |
2668 | 72 | let name = self.types[resource].name.as_ref().unwrap(); |
2669 | 72 | let interface = self.name_world_key(interface); |
2670 | 72 | format!("{}{interface}#[dtor]{name}", abi.export_prefix()) |
2671 | | } |
2672 | 3.86k | WasmExport::Memory => "memory".to_string(), |
2673 | 3.86k | WasmExport::Initialize => "_initialize".to_string(), |
2674 | 3.86k | WasmExport::Realloc => "cabi_realloc".to_string(), |
2675 | | }, |
2676 | | } |
2677 | 14.5k | } |
2678 | | } |
2679 | | |
2680 | | /// Possible imports that can be passed to [`Resolve::wasm_import_name`]. |
2681 | | #[derive(Debug)] |
2682 | | pub enum WasmImport<'a> { |
2683 | | /// A WIT function is being imported. Optionally from an interface. |
2684 | | Func { |
2685 | | /// The name of the interface that the function is being imported from. |
2686 | | /// |
2687 | | /// If the function is imported directly from the world then this is |
2688 | | /// `Noen`. |
2689 | | interface: Option<&'a WorldKey>, |
2690 | | |
2691 | | /// The function being imported. |
2692 | | func: &'a Function, |
2693 | | }, |
2694 | | |
2695 | | /// A resource-related intrinsic is being imported. |
2696 | | ResourceIntrinsic { |
2697 | | /// The optional interface to import from, same as `WasmImport::Func`. |
2698 | | interface: Option<&'a WorldKey>, |
2699 | | |
2700 | | /// The resource that's being operated on. |
2701 | | resource: TypeId, |
2702 | | |
2703 | | /// The intrinsic that's being imported. |
2704 | | intrinsic: ResourceIntrinsic, |
2705 | | }, |
2706 | | } |
2707 | | |
2708 | | /// Intrinsic definitions to go with [`WasmImport::ResourceIntrinsic`] which |
2709 | | /// also goes with [`Resolve::wasm_import_name`]. |
2710 | | #[derive(Debug)] |
2711 | | pub enum ResourceIntrinsic { |
2712 | | ImportedDrop, |
2713 | | ExportedDrop, |
2714 | | ExportedNew, |
2715 | | ExportedRep, |
2716 | | } |
2717 | | |
2718 | | /// Indicates whether a function export is a normal export, a post-return |
2719 | | /// function, or a callback function. |
2720 | | #[derive(Debug)] |
2721 | | pub enum WasmExportKind { |
2722 | | /// Normal function export. |
2723 | | Normal, |
2724 | | |
2725 | | /// Post-return function. |
2726 | | PostReturn, |
2727 | | |
2728 | | /// Async callback function. |
2729 | | Callback, |
2730 | | } |
2731 | | |
2732 | | /// Different kinds of exports that can be passed to |
2733 | | /// [`Resolve::wasm_export_name`] to export from core wasm modules. |
2734 | | #[derive(Debug)] |
2735 | | pub enum WasmExport<'a> { |
2736 | | /// A WIT function is being exported, optionally from an interface. |
2737 | | Func { |
2738 | | /// An optional interface which owns `func`. Use `None` for top-level |
2739 | | /// world function. |
2740 | | interface: Option<&'a WorldKey>, |
2741 | | |
2742 | | /// The function being exported. |
2743 | | func: &'a Function, |
2744 | | |
2745 | | /// Kind of function (normal, post-return, or callback) being exported. |
2746 | | kind: WasmExportKind, |
2747 | | }, |
2748 | | |
2749 | | /// A destructor for a resource exported from this module. |
2750 | | ResourceDtor { |
2751 | | /// The interface that owns the resource. |
2752 | | interface: &'a WorldKey, |
2753 | | /// The resource itself that the destructor is for. |
2754 | | resource: TypeId, |
2755 | | }, |
2756 | | |
2757 | | /// Linear memory, the one that the canonical ABI uses. |
2758 | | Memory, |
2759 | | |
2760 | | /// An initialization function (not the core wasm `start`). |
2761 | | Initialize, |
2762 | | |
2763 | | /// The general-purpose realloc hook. |
2764 | | Realloc, |
2765 | | } |
2766 | | |
2767 | | /// Structure returned by [`Resolve::merge`] which contains mappings from |
2768 | | /// old-ids to new-ids after the merge. |
2769 | | #[derive(Default)] |
2770 | | pub struct Remap { |
2771 | | pub types: Vec<Option<TypeId>>, |
2772 | | pub interfaces: Vec<Option<InterfaceId>>, |
2773 | | pub worlds: Vec<Option<WorldId>>, |
2774 | | pub packages: Vec<PackageId>, |
2775 | | |
2776 | | /// A cache of anonymous `own<T>` handles for resource types. |
2777 | | /// |
2778 | | /// The appending operation of `Remap` is the one responsible for |
2779 | | /// translating references to `T` where `T` is a resource into `own<T>` |
2780 | | /// instead. This map is used to deduplicate the `own<T>` types generated |
2781 | | /// to generate as few as possible. |
2782 | | /// |
2783 | | /// The key of this map is the resource id `T` in the new resolve, and |
2784 | | /// the value is the `own<T>` type pointing to `T`. |
2785 | | own_handles: HashMap<TypeId, TypeId>, |
2786 | | |
2787 | | type_has_borrow: Vec<Option<bool>>, |
2788 | | } |
2789 | | |
2790 | 435k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Option<Span>) -> Result<Id<T>> { |
2791 | 435k | match map.get(id.index()) { |
2792 | 435k | Some(Some(id)) => Ok(*id), |
2793 | | Some(None) => { |
2794 | 0 | let msg = format!( |
2795 | 0 | "found a reference to a {desc} which is excluded \ |
2796 | 0 | due to its feature not being activated" |
2797 | | ); |
2798 | 0 | match span { |
2799 | 0 | Some(span) => Err(Error::new(span, msg).into()), |
2800 | 0 | None => bail!("{msg}"), |
2801 | | } |
2802 | | } |
2803 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), |
2804 | | } |
2805 | 435k | } wit_parser::resolve::apply_map::<wit_parser::World> Line | Count | Source | 2790 | 80.0k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Option<Span>) -> Result<Id<T>> { | 2791 | 80.0k | match map.get(id.index()) { | 2792 | 80.0k | Some(Some(id)) => Ok(*id), | 2793 | | Some(None) => { | 2794 | 0 | let msg = format!( | 2795 | 0 | "found a reference to a {desc} which is excluded \ | 2796 | 0 | due to its feature not being activated" | 2797 | | ); | 2798 | 0 | match span { | 2799 | 0 | Some(span) => Err(Error::new(span, msg).into()), | 2800 | 0 | None => bail!("{msg}"), | 2801 | | } | 2802 | | } | 2803 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), | 2804 | | } | 2805 | 80.0k | } |
wit_parser::resolve::apply_map::<wit_parser::TypeDef> Line | Count | Source | 2790 | 298k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Option<Span>) -> Result<Id<T>> { | 2791 | 298k | match map.get(id.index()) { | 2792 | 298k | Some(Some(id)) => Ok(*id), | 2793 | | Some(None) => { | 2794 | 0 | let msg = format!( | 2795 | 0 | "found a reference to a {desc} which is excluded \ | 2796 | 0 | due to its feature not being activated" | 2797 | | ); | 2798 | 0 | match span { | 2799 | 0 | Some(span) => Err(Error::new(span, msg).into()), | 2800 | 0 | None => bail!("{msg}"), | 2801 | | } | 2802 | | } | 2803 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), | 2804 | | } | 2805 | 298k | } |
wit_parser::resolve::apply_map::<wit_parser::Interface> Line | Count | Source | 2790 | 56.7k | fn apply_map<T>(map: &[Option<Id<T>>], id: Id<T>, desc: &str, span: Option<Span>) -> Result<Id<T>> { | 2791 | 56.7k | match map.get(id.index()) { | 2792 | 56.7k | Some(Some(id)) => Ok(*id), | 2793 | | Some(None) => { | 2794 | 0 | let msg = format!( | 2795 | 0 | "found a reference to a {desc} which is excluded \ | 2796 | 0 | due to its feature not being activated" | 2797 | | ); | 2798 | 0 | match span { | 2799 | 0 | Some(span) => Err(Error::new(span, msg).into()), | 2800 | 0 | None => bail!("{msg}"), | 2801 | | } | 2802 | | } | 2803 | 0 | None => panic!("request to remap a {desc} that has not yet been registered"), | 2804 | | } | 2805 | 56.7k | } |
|
2806 | | |
2807 | 0 | fn rename(original_name: &str, include_name: &IncludeName) -> Option<String> { |
2808 | 0 | if original_name == include_name.name { |
2809 | 0 | return Some(include_name.as_.to_string()); |
2810 | 0 | } |
2811 | 0 | let (kind, rest) = original_name.split_once(']')?; |
2812 | 0 | match rest.split_once('.') { |
2813 | 0 | Some((name, rest)) if name == include_name.name => { |
2814 | 0 | Some(format!("{kind}]{}.{rest}", include_name.as_)) |
2815 | | } |
2816 | 0 | _ if rest == include_name.name => Some(format!("{kind}]{}", include_name.as_)), |
2817 | 0 | _ => None, |
2818 | | } |
2819 | 0 | } |
2820 | | |
2821 | | impl Remap { |
2822 | 298k | pub fn map_type(&self, id: TypeId, span: Option<Span>) -> Result<TypeId> { |
2823 | 298k | apply_map(&self.types, id, "type", span) |
2824 | 298k | } |
2825 | | |
2826 | 56.7k | pub fn map_interface(&self, id: InterfaceId, span: Option<Span>) -> Result<InterfaceId> { |
2827 | 56.7k | apply_map(&self.interfaces, id, "interface", span) |
2828 | 56.7k | } |
2829 | | |
2830 | 80.0k | pub fn map_world(&self, id: WorldId, span: Option<Span>) -> Result<WorldId> { |
2831 | 80.0k | apply_map(&self.worlds, id, "world", span) |
2832 | 80.0k | } |
2833 | | |
2834 | 13.1k | fn append( |
2835 | 13.1k | &mut self, |
2836 | 13.1k | resolve: &mut Resolve, |
2837 | 13.1k | unresolved: UnresolvedPackage, |
2838 | 13.1k | ) -> Result<PackageId> { |
2839 | 13.1k | let pkgid = resolve.packages.alloc(Package { |
2840 | 13.1k | name: unresolved.name.clone(), |
2841 | 13.1k | docs: unresolved.docs.clone(), |
2842 | 13.1k | interfaces: Default::default(), |
2843 | 13.1k | worlds: Default::default(), |
2844 | 13.1k | }); |
2845 | 13.1k | let prev = resolve.package_names.insert(unresolved.name.clone(), pkgid); |
2846 | 13.1k | if let Some(prev) = prev { |
2847 | 0 | resolve.package_names.insert(unresolved.name.clone(), prev); |
2848 | 0 | bail!( |
2849 | 0 | "attempting to re-add package `{}` when it's already present in this `Resolve`", |
2850 | | unresolved.name, |
2851 | | ); |
2852 | 13.1k | } |
2853 | | |
2854 | 13.1k | self.process_foreign_deps(resolve, pkgid, &unresolved)?; |
2855 | | |
2856 | 13.1k | let foreign_types = self.types.len(); |
2857 | 13.1k | let foreign_interfaces = self.interfaces.len(); |
2858 | 13.1k | let foreign_worlds = self.worlds.len(); |
2859 | | |
2860 | | // Copy over all types first, updating any intra-type references. Note |
2861 | | // that types are sorted topologically which means this iteration |
2862 | | // order should be sufficient. Also note though that the interface |
2863 | | // owner of a type isn't updated here due to interfaces not being known |
2864 | | // yet. |
2865 | 13.1k | assert_eq!(unresolved.types.len(), unresolved.type_spans.len()); |
2866 | 141k | for ((id, mut ty), span) in unresolved |
2867 | 13.1k | .types |
2868 | 13.1k | .into_iter() |
2869 | 13.1k | .zip(&unresolved.type_spans) |
2870 | 13.1k | .skip(foreign_types) |
2871 | | { |
2872 | 141k | if !resolve.include_type(&ty, pkgid, *span)? { |
2873 | 0 | self.types.push(None); |
2874 | 0 | continue; |
2875 | 141k | } |
2876 | | |
2877 | 141k | self.update_typedef(resolve, &mut ty, Some(*span))?; |
2878 | 141k | let new_id = resolve.types.alloc(ty); |
2879 | 141k | assert_eq!(self.types.len(), id.index()); |
2880 | | |
2881 | 141k | let new_id = match resolve.types[new_id] { |
2882 | | // If this is an `own<T>` handle then either replace it with a |
2883 | | // preexisting `own<T>` handle which may have been generated in |
2884 | | // `update_ty`. If that doesn't exist though then insert it into |
2885 | | // the `own_handles` cache. |
2886 | | TypeDef { |
2887 | | name: None, |
2888 | | owner: TypeOwner::None, |
2889 | 0 | kind: TypeDefKind::Handle(Handle::Own(id)), |
2890 | | docs: _, |
2891 | | stability: _, |
2892 | 0 | } => *self.own_handles.entry(id).or_insert(new_id), |
2893 | | |
2894 | | // Everything not-related to `own<T>` doesn't get its ID |
2895 | | // modified. |
2896 | 141k | _ => new_id, |
2897 | | }; |
2898 | 141k | self.types.push(Some(new_id)); |
2899 | | } |
2900 | | |
2901 | | // Next transfer all interfaces into `Resolve`, updating type ids |
2902 | | // referenced along the way. |
2903 | 13.1k | assert_eq!( |
2904 | 13.1k | unresolved.interfaces.len(), |
2905 | 13.1k | unresolved.interface_spans.len() |
2906 | | ); |
2907 | 54.6k | for ((id, mut iface), span) in unresolved |
2908 | 13.1k | .interfaces |
2909 | 13.1k | .into_iter() |
2910 | 13.1k | .zip(&unresolved.interface_spans) |
2911 | 13.1k | .skip(foreign_interfaces) |
2912 | | { |
2913 | 54.6k | if !resolve |
2914 | 54.6k | .include_stability(&iface.stability, &pkgid, Some(span.span)) |
2915 | 54.6k | .with_context(|| { |
2916 | 0 | format!( |
2917 | 0 | "failed to process feature gate for interface [{}] in package [{}]", |
2918 | 0 | iface |
2919 | 0 | .name |
2920 | 0 | .as_ref() |
2921 | 0 | .map(String::as_str) |
2922 | 0 | .unwrap_or("<unknown>"), |
2923 | 0 | resolve.packages[pkgid].name, |
2924 | | ) |
2925 | 0 | })? |
2926 | | { |
2927 | 0 | self.interfaces.push(None); |
2928 | 0 | continue; |
2929 | 54.6k | } |
2930 | 54.6k | assert!(iface.package.is_none()); |
2931 | 54.6k | iface.package = Some(pkgid); |
2932 | 54.6k | self.update_interface(resolve, &mut iface, Some(span))?; |
2933 | 54.6k | let new_id = resolve.interfaces.alloc(iface); |
2934 | 54.6k | assert_eq!(self.interfaces.len(), id.index()); |
2935 | 54.6k | self.interfaces.push(Some(new_id)); |
2936 | | } |
2937 | | |
2938 | | // Now that interfaces are identified go back through the types and |
2939 | | // update their interface owners. |
2940 | 141k | for (i, id) in self.types.iter().enumerate().skip(foreign_types) { |
2941 | 141k | let id = match id { |
2942 | 141k | Some(id) => *id, |
2943 | 0 | None => continue, |
2944 | | }; |
2945 | 141k | match &mut resolve.types[id].owner { |
2946 | 31.5k | TypeOwner::Interface(id) => { |
2947 | 31.5k | let span = unresolved.type_spans[i]; |
2948 | 31.5k | *id = self.map_interface(*id, Some(span)) |
2949 | 31.5k | .with_context(|| { |
2950 | 0 | "this type is not gated by a feature but its interface is gated by a feature" |
2951 | 0 | })?; |
2952 | | } |
2953 | 109k | TypeOwner::World(_) | TypeOwner::None => {} |
2954 | | } |
2955 | | } |
2956 | | |
2957 | | // Expand worlds. Note that this does NOT process `include` statements, |
2958 | | // that's handled below. Instead this just handles world item updates |
2959 | | // and resolves references to types/items within `Resolve`. |
2960 | | // |
2961 | | // This is done after types/interfaces are fully settled so the |
2962 | | // transitive relation between interfaces, through types, is understood |
2963 | | // here. |
2964 | 13.1k | assert_eq!(unresolved.worlds.len(), unresolved.world_spans.len()); |
2965 | 17.4k | for ((id, mut world), span) in unresolved |
2966 | 13.1k | .worlds |
2967 | 13.1k | .into_iter() |
2968 | 13.1k | .zip(&unresolved.world_spans) |
2969 | 13.1k | .skip(foreign_worlds) |
2970 | | { |
2971 | 17.4k | if !resolve |
2972 | 17.4k | .include_stability(&world.stability, &pkgid, Some(span.span)) |
2973 | 17.4k | .with_context(|| { |
2974 | 0 | format!( |
2975 | 0 | "failed to process feature gate for world [{}] in package [{}]", |
2976 | 0 | world.name, resolve.packages[pkgid].name, |
2977 | | ) |
2978 | 0 | })? |
2979 | | { |
2980 | 0 | self.worlds.push(None); |
2981 | 0 | continue; |
2982 | 17.4k | } |
2983 | 17.4k | self.update_world(&mut world, resolve, &pkgid, &span)?; |
2984 | | |
2985 | 17.4k | let new_id = resolve.worlds.alloc(world); |
2986 | 17.4k | assert_eq!(self.worlds.len(), id.index()); |
2987 | 17.4k | self.worlds.push(Some(new_id)); |
2988 | | } |
2989 | | |
2990 | | // As with interfaces, now update the ids of world-owned types. |
2991 | 141k | for (i, id) in self.types.iter().enumerate().skip(foreign_types) { |
2992 | 141k | let id = match id { |
2993 | 141k | Some(id) => *id, |
2994 | 0 | None => continue, |
2995 | | }; |
2996 | 141k | match &mut resolve.types[id].owner { |
2997 | 19.6k | TypeOwner::World(id) => { |
2998 | 19.6k | let span = unresolved.type_spans[i]; |
2999 | 19.6k | *id = self.map_world(*id, Some(span)) |
3000 | 19.6k | .with_context(|| { |
3001 | 0 | "this type is not gated by a feature but its interface is gated by a feature" |
3002 | 0 | })?; |
3003 | | } |
3004 | 121k | TypeOwner::Interface(_) | TypeOwner::None => {} |
3005 | | } |
3006 | | } |
3007 | | |
3008 | | // After the above, process `include` statements for worlds and |
3009 | | // additionally fully elaborate them. Processing of `include` is |
3010 | | // deferred until after the steps above so the fully resolved state of |
3011 | | // local types in this package are all available. This is required |
3012 | | // because `include` may copy types between worlds when the type is |
3013 | | // defined in the world itself. |
3014 | | // |
3015 | | // This step, after processing `include`, will also use |
3016 | | // `elaborate_world` to fully expand the world in terms of |
3017 | | // imports/exports and ensure that all necessary imports/exports are all |
3018 | | // listed. |
3019 | | // |
3020 | | // Note that `self.worlds` is already sorted in topological order so if |
3021 | | // one world refers to another via `include` then it's guaranteed that |
3022 | | // the one we're referring to is already expanded and ready to be |
3023 | | // included. |
3024 | 13.1k | assert_eq!(self.worlds.len(), unresolved.world_spans.len()); |
3025 | 17.4k | for (id, span) in self |
3026 | 13.1k | .worlds |
3027 | 13.1k | .iter() |
3028 | 13.1k | .zip(unresolved.world_spans.iter()) |
3029 | 13.1k | .skip(foreign_worlds) |
3030 | | { |
3031 | 17.4k | let Some(id) = *id else { |
3032 | 0 | continue; |
3033 | | }; |
3034 | 17.4k | self.process_world_includes(id, resolve, &pkgid, &span)?; |
3035 | | |
3036 | 17.4k | resolve.elaborate_world(id).with_context(|| { |
3037 | 0 | Error::new( |
3038 | 0 | span.span, |
3039 | 0 | format!( |
3040 | 0 | "failed to elaborate world imports/exports of `{}`", |
3041 | 0 | resolve.worlds[id].name |
3042 | | ), |
3043 | | ) |
3044 | 0 | })?; |
3045 | | } |
3046 | | |
3047 | | // Fixup "parent" ids now that everything has been identified |
3048 | 54.6k | for id in self.interfaces.iter().skip(foreign_interfaces) { |
3049 | 54.6k | let id = match id { |
3050 | 54.6k | Some(id) => *id, |
3051 | 0 | None => continue, |
3052 | | }; |
3053 | 54.6k | let iface = &mut resolve.interfaces[id]; |
3054 | 54.6k | iface.package = Some(pkgid); |
3055 | 54.6k | if let Some(name) = &iface.name { |
3056 | 51.7k | let prev = resolve.packages[pkgid].interfaces.insert(name.clone(), id); |
3057 | 51.7k | assert!(prev.is_none()); |
3058 | 2.94k | } |
3059 | | } |
3060 | 17.4k | for id in self.worlds.iter().skip(foreign_worlds) { |
3061 | 17.4k | let id = match id { |
3062 | 17.4k | Some(id) => *id, |
3063 | 0 | None => continue, |
3064 | | }; |
3065 | 17.4k | let world = &mut resolve.worlds[id]; |
3066 | 17.4k | world.package = Some(pkgid); |
3067 | 17.4k | let prev = resolve.packages[pkgid] |
3068 | 17.4k | .worlds |
3069 | 17.4k | .insert(world.name.clone(), id); |
3070 | 17.4k | assert!(prev.is_none()); |
3071 | | } |
3072 | 13.1k | Ok(pkgid) |
3073 | 13.1k | } |
3074 | | |
3075 | 13.1k | fn process_foreign_deps( |
3076 | 13.1k | &mut self, |
3077 | 13.1k | resolve: &mut Resolve, |
3078 | 13.1k | pkgid: PackageId, |
3079 | 13.1k | unresolved: &UnresolvedPackage, |
3080 | 13.1k | ) -> Result<()> { |
3081 | | // Invert the `foreign_deps` map to be keyed by world id to get |
3082 | | // used in the loops below. |
3083 | 13.1k | let mut world_to_package = HashMap::new(); |
3084 | 13.1k | let mut interface_to_package = HashMap::new(); |
3085 | 13.1k | for (i, (pkg_name, worlds_or_ifaces)) in unresolved.foreign_deps.iter().enumerate() { |
3086 | 4.04k | for (name, item) in worlds_or_ifaces { |
3087 | 2.17k | match item { |
3088 | 2.17k | AstItem::Interface(unresolved_interface_id) => { |
3089 | 2.17k | let prev = interface_to_package.insert( |
3090 | 2.17k | *unresolved_interface_id, |
3091 | 2.17k | (pkg_name, name, unresolved.foreign_dep_spans[i]), |
3092 | | ); |
3093 | 2.17k | assert!(prev.is_none()); |
3094 | | } |
3095 | 0 | AstItem::World(unresolved_world_id) => { |
3096 | 0 | let prev = world_to_package.insert( |
3097 | 0 | *unresolved_world_id, |
3098 | 0 | (pkg_name, name, unresolved.foreign_dep_spans[i]), |
3099 | | ); |
3100 | 0 | assert!(prev.is_none()); |
3101 | | } |
3102 | | } |
3103 | | } |
3104 | | } |
3105 | | |
3106 | | // Connect all interfaces referred to in `interface_to_package`, which |
3107 | | // are at the front of `unresolved.interfaces`, to interfaces already |
3108 | | // contained within `resolve`. |
3109 | 13.1k | self.process_foreign_interfaces(unresolved, &interface_to_package, resolve)?; |
3110 | | |
3111 | | // Connect all worlds referred to in `world_to_package`, which |
3112 | | // are at the front of `unresolved.worlds`, to worlds already |
3113 | | // contained within `resolve`. |
3114 | 13.1k | self.process_foreign_worlds(unresolved, &world_to_package, resolve)?; |
3115 | | |
3116 | | // Finally, iterate over all foreign-defined types and determine |
3117 | | // what they map to. |
3118 | 13.1k | self.process_foreign_types(unresolved, pkgid, resolve)?; |
3119 | | |
3120 | 13.1k | for (id, span) in unresolved.required_resource_types.iter() { |
3121 | | // Note that errors are ignored here because an error represents a |
3122 | | // type that has been configured away. If a type is configured away |
3123 | | // then any future use of it will generate an error so there's no |
3124 | | // need to validate that it's a resource here. |
3125 | 16 | let Ok(mut id) = self.map_type(*id, Some(*span)) else { |
3126 | 0 | continue; |
3127 | | }; |
3128 | | loop { |
3129 | 20 | match resolve.types[id].kind { |
3130 | 4 | TypeDefKind::Type(Type::Id(i)) => id = i, |
3131 | 16 | TypeDefKind::Resource => break, |
3132 | 0 | _ => bail!(Error::new( |
3133 | 0 | *span, |
3134 | 0 | format!("type used in a handle must be a resource"), |
3135 | 0 | )), |
3136 | | } |
3137 | | } |
3138 | | } |
3139 | | |
3140 | | #[cfg(debug_assertions)] |
3141 | | resolve.assert_valid(); |
3142 | | |
3143 | 13.1k | Ok(()) |
3144 | 13.1k | } |
3145 | | |
3146 | 13.1k | fn process_foreign_interfaces( |
3147 | 13.1k | &mut self, |
3148 | 13.1k | unresolved: &UnresolvedPackage, |
3149 | 13.1k | interface_to_package: &HashMap<InterfaceId, (&PackageName, &String, Span)>, |
3150 | 13.1k | resolve: &mut Resolve, |
3151 | 13.1k | ) -> Result<(), anyhow::Error> { |
3152 | 13.8k | for (unresolved_iface_id, unresolved_iface) in unresolved.interfaces.iter() { |
3153 | 13.8k | let (pkg_name, interface, span) = match interface_to_package.get(&unresolved_iface_id) { |
3154 | 2.17k | Some(items) => *items, |
3155 | | // All foreign interfaces are defined first, so the first one |
3156 | | // which is defined in a non-foreign document means that all |
3157 | | // further interfaces will be non-foreign as well. |
3158 | 11.7k | None => break, |
3159 | | }; |
3160 | 2.17k | let pkgid = resolve |
3161 | 2.17k | .package_names |
3162 | 2.17k | .get(pkg_name) |
3163 | 2.17k | .copied() |
3164 | 2.17k | .ok_or_else(|| { |
3165 | 0 | PackageNotFoundError::new( |
3166 | 0 | span, |
3167 | 0 | pkg_name.clone(), |
3168 | 0 | resolve.package_names.keys().cloned().collect(), |
3169 | | ) |
3170 | 0 | })?; |
3171 | | |
3172 | | // Functions can't be imported so this should be empty. |
3173 | 2.17k | assert!(unresolved_iface.functions.is_empty()); |
3174 | | |
3175 | 2.17k | let pkg = &resolve.packages[pkgid]; |
3176 | 2.17k | let span = &unresolved.interface_spans[unresolved_iface_id.index()]; |
3177 | 2.17k | let iface_id = pkg |
3178 | 2.17k | .interfaces |
3179 | 2.17k | .get(interface) |
3180 | 2.17k | .copied() |
3181 | 2.17k | .ok_or_else(|| Error::new(span.span, "interface not found in package"))?; |
3182 | 2.17k | assert_eq!(self.interfaces.len(), unresolved_iface_id.index()); |
3183 | 2.17k | self.interfaces.push(Some(iface_id)); |
3184 | | } |
3185 | 54.6k | for (id, _) in unresolved.interfaces.iter().skip(self.interfaces.len()) { |
3186 | 54.6k | assert!( |
3187 | 54.6k | interface_to_package.get(&id).is_none(), |
3188 | 0 | "found foreign interface after local interface" |
3189 | | ); |
3190 | | } |
3191 | 13.1k | Ok(()) |
3192 | 13.1k | } |
3193 | | |
3194 | 13.1k | fn process_foreign_worlds( |
3195 | 13.1k | &mut self, |
3196 | 13.1k | unresolved: &UnresolvedPackage, |
3197 | 13.1k | world_to_package: &HashMap<WorldId, (&PackageName, &String, Span)>, |
3198 | 13.1k | resolve: &mut Resolve, |
3199 | 13.1k | ) -> Result<(), anyhow::Error> { |
3200 | 13.1k | for (unresolved_world_id, _) in unresolved.worlds.iter() { |
3201 | 9.71k | let (pkg_name, world, span) = match world_to_package.get(&unresolved_world_id) { |
3202 | 0 | Some(items) => *items, |
3203 | | // Same as above, all worlds are foreign until we find a |
3204 | | // non-foreign one. |
3205 | 9.71k | None => break, |
3206 | | }; |
3207 | | |
3208 | 0 | let pkgid = resolve |
3209 | 0 | .package_names |
3210 | 0 | .get(pkg_name) |
3211 | 0 | .copied() |
3212 | 0 | .ok_or_else(|| Error::new(span, "package not found"))?; |
3213 | 0 | let pkg = &resolve.packages[pkgid]; |
3214 | 0 | let span = &unresolved.world_spans[unresolved_world_id.index()]; |
3215 | 0 | let world_id = pkg |
3216 | 0 | .worlds |
3217 | 0 | .get(world) |
3218 | 0 | .copied() |
3219 | 0 | .ok_or_else(|| Error::new(span.span, "world not found in package"))?; |
3220 | 0 | assert_eq!(self.worlds.len(), unresolved_world_id.index()); |
3221 | 0 | self.worlds.push(Some(world_id)); |
3222 | | } |
3223 | 17.4k | for (id, _) in unresolved.worlds.iter().skip(self.worlds.len()) { |
3224 | 17.4k | assert!( |
3225 | 17.4k | world_to_package.get(&id).is_none(), |
3226 | 0 | "found foreign world after local world" |
3227 | | ); |
3228 | | } |
3229 | 13.1k | Ok(()) |
3230 | 13.1k | } |
3231 | | |
3232 | 13.1k | fn process_foreign_types( |
3233 | 13.1k | &mut self, |
3234 | 13.1k | unresolved: &UnresolvedPackage, |
3235 | 13.1k | pkgid: PackageId, |
3236 | 13.1k | resolve: &mut Resolve, |
3237 | 13.1k | ) -> Result<(), anyhow::Error> { |
3238 | 10.5k | for ((unresolved_type_id, unresolved_ty), span) in |
3239 | 13.1k | unresolved.types.iter().zip(&unresolved.type_spans) |
3240 | | { |
3241 | | // All "Unknown" types should appear first so once we're no longer |
3242 | | // in unknown territory it's package-defined types so break out of |
3243 | | // this loop. |
3244 | 10.5k | match unresolved_ty.kind { |
3245 | 2.05k | TypeDefKind::Unknown => {} |
3246 | 8.49k | _ => break, |
3247 | | } |
3248 | | |
3249 | 2.05k | if !resolve.include_type(unresolved_ty, pkgid, *span)? { |
3250 | 0 | self.types.push(None); |
3251 | 0 | continue; |
3252 | 2.05k | } |
3253 | | |
3254 | 2.05k | let unresolved_iface_id = match unresolved_ty.owner { |
3255 | 2.05k | TypeOwner::Interface(id) => id, |
3256 | 0 | _ => unreachable!(), |
3257 | | }; |
3258 | 2.05k | let iface_id = self.map_interface(unresolved_iface_id, None)?; |
3259 | 2.05k | let name = unresolved_ty.name.as_ref().unwrap(); |
3260 | 2.05k | let span = unresolved.unknown_type_spans[unresolved_type_id.index()]; |
3261 | 2.05k | let type_id = *resolve.interfaces[iface_id] |
3262 | 2.05k | .types |
3263 | 2.05k | .get(name) |
3264 | 2.05k | .ok_or_else(|| { |
3265 | 0 | Error::new(span, format!("type `{name}` not defined in interface")) |
3266 | 0 | })?; |
3267 | 2.05k | assert_eq!(self.types.len(), unresolved_type_id.index()); |
3268 | 2.05k | self.types.push(Some(type_id)); |
3269 | | } |
3270 | 141k | for (_, ty) in unresolved.types.iter().skip(self.types.len()) { |
3271 | 141k | if let TypeDefKind::Unknown = ty.kind { |
3272 | 0 | panic!("unknown type after defined type"); |
3273 | 141k | } |
3274 | | } |
3275 | 13.1k | Ok(()) |
3276 | 13.1k | } |
3277 | | |
3278 | 198k | fn update_typedef( |
3279 | 198k | &mut self, |
3280 | 198k | resolve: &mut Resolve, |
3281 | 198k | ty: &mut TypeDef, |
3282 | 198k | span: Option<Span>, |
3283 | 198k | ) -> Result<()> { |
3284 | | // NB: note that `ty.owner` is not updated here since interfaces |
3285 | | // haven't been mapped yet and that's done in a separate step. |
3286 | | use crate::TypeDefKind::*; |
3287 | 6.19k | match &mut ty.kind { |
3288 | 2.22k | Handle(handle) => match handle { |
3289 | 1.74k | crate::Handle::Own(ty) | crate::Handle::Borrow(ty) => { |
3290 | 2.22k | self.update_type_id(ty, span)? |
3291 | | } |
3292 | | }, |
3293 | 3.88k | Resource => {} |
3294 | 5.83k | Record(r) => { |
3295 | 16.0k | for field in r.fields.iter_mut() { |
3296 | 16.0k | self.update_ty(resolve, &mut field.ty, span) |
3297 | 16.0k | .with_context(|| format!("failed to update field `{}`", field.name))?; |
3298 | | } |
3299 | | } |
3300 | 28.6k | Tuple(t) => { |
3301 | 107k | for ty in t.types.iter_mut() { |
3302 | 107k | self.update_ty(resolve, ty, span)?; |
3303 | | } |
3304 | | } |
3305 | 7.79k | Variant(v) => { |
3306 | 26.9k | for case in v.cases.iter_mut() { |
3307 | 26.9k | if let Some(t) = &mut case.ty { |
3308 | 22.7k | self.update_ty(resolve, t, span)?; |
3309 | 4.22k | } |
3310 | | } |
3311 | | } |
3312 | 30.5k | Option(t) | List(t, ..) | FixedSizeList(t, ..) | Future(Some(t)) | Stream(Some(t)) => { |
3313 | 56.1k | self.update_ty(resolve, t, span)? |
3314 | | } |
3315 | 39.1k | Result(r) => { |
3316 | 39.1k | if let Some(ty) = &mut r.ok { |
3317 | 35.2k | self.update_ty(resolve, ty, span)?; |
3318 | 3.89k | } |
3319 | 39.1k | if let Some(ty) = &mut r.err { |
3320 | 32.9k | self.update_ty(resolve, ty, span)?; |
3321 | 6.25k | } |
3322 | | } |
3323 | | |
3324 | | // Note that `update_ty` is specifically not used here as typedefs |
3325 | | // because for the `type a = b` form that doesn't force `a` to be a |
3326 | | // handle type if `b` is a resource type, instead `a` is |
3327 | | // simultaneously usable as a resource and a handle type |
3328 | 16.6k | Type(crate::Type::Id(id)) => self.update_type_id(id, span)?, |
3329 | 1.90k | Type(_) => {} |
3330 | | |
3331 | | // nothing to do for these as they're just names or empty |
3332 | 36.0k | Flags(_) | Enum(_) | Future(None) | Stream(None) => {} |
3333 | | |
3334 | 0 | Unknown => unreachable!(), |
3335 | | } |
3336 | | |
3337 | 198k | Ok(()) |
3338 | 198k | } |
3339 | | |
3340 | 376k | fn update_ty( |
3341 | 376k | &mut self, |
3342 | 376k | resolve: &mut Resolve, |
3343 | 376k | ty: &mut Type, |
3344 | 376k | span: Option<Span>, |
3345 | 376k | ) -> Result<()> { |
3346 | 376k | let id = match ty { |
3347 | 142k | Type::Id(id) => id, |
3348 | 233k | _ => return Ok(()), |
3349 | | }; |
3350 | 142k | self.update_type_id(id, span)?; |
3351 | | |
3352 | | // If `id` points to a `Resource` type then this means that what was |
3353 | | // just discovered was a reference to what will implicitly become an |
3354 | | // `own<T>` handle. This `own` handle is implicitly allocated here |
3355 | | // and handled during the merging process. |
3356 | 142k | let mut cur = *id; |
3357 | 142k | let points_to_resource = loop { |
3358 | 143k | match resolve.types[cur].kind { |
3359 | 381 | TypeDefKind::Type(Type::Id(id)) => cur = id, |
3360 | 1.35k | TypeDefKind::Resource => break true, |
3361 | 141k | _ => break false, |
3362 | | } |
3363 | | }; |
3364 | | |
3365 | 142k | if points_to_resource { |
3366 | 1.35k | *id = *self.own_handles.entry(*id).or_insert_with(|| { |
3367 | 766 | resolve.types.alloc(TypeDef { |
3368 | 766 | name: None, |
3369 | 766 | owner: TypeOwner::None, |
3370 | 766 | kind: TypeDefKind::Handle(Handle::Own(*id)), |
3371 | 766 | docs: Default::default(), |
3372 | 766 | stability: Default::default(), |
3373 | 766 | }) |
3374 | 766 | }); |
3375 | 141k | } |
3376 | 142k | Ok(()) |
3377 | 376k | } |
3378 | | |
3379 | 206k | fn update_type_id(&self, id: &mut TypeId, span: Option<Span>) -> Result<()> { |
3380 | 206k | *id = self.map_type(*id, span)?; |
3381 | 206k | Ok(()) |
3382 | 206k | } |
3383 | | |
3384 | 58.7k | fn update_interface( |
3385 | 58.7k | &mut self, |
3386 | 58.7k | resolve: &mut Resolve, |
3387 | 58.7k | iface: &mut Interface, |
3388 | 58.7k | spans: Option<&InterfaceSpan>, |
3389 | 58.7k | ) -> Result<()> { |
3390 | 58.7k | iface.types.retain(|_, ty| self.types[ty.index()].is_some()); |
3391 | 58.7k | let iface_pkg_id = iface.package.as_ref().unwrap_or_else(|| { |
3392 | 0 | panic!( |
3393 | 0 | "unexpectedly missing package on interface [{}]", |
3394 | 0 | iface |
3395 | 0 | .name |
3396 | 0 | .as_ref() |
3397 | 0 | .map(String::as_str) |
3398 | 0 | .unwrap_or("<unknown>"), |
3399 | | ) |
3400 | | }); |
3401 | | |
3402 | | // NB: note that `iface.doc` is not updated here since interfaces |
3403 | | // haven't been mapped yet and that's done in a separate step. |
3404 | 58.7k | for (_name, ty) in iface.types.iter_mut() { |
3405 | 38.0k | self.update_type_id(ty, spans.map(|s| s.span))?; |
3406 | | } |
3407 | 58.7k | if let Some(spans) = spans { |
3408 | 54.6k | assert_eq!(iface.functions.len(), spans.funcs.len()); |
3409 | 4.03k | } |
3410 | 58.7k | for (i, (func_name, func)) in iface.functions.iter_mut().enumerate() { |
3411 | 24.8k | let span = spans.map(|s| s.funcs[i]); |
3412 | 24.8k | if !resolve |
3413 | 24.8k | .include_stability(&func.stability, iface_pkg_id, span) |
3414 | 24.8k | .with_context(|| { |
3415 | 0 | format!( |
3416 | 0 | "failed to process feature gate for function [{func_name}] in package [{}]", |
3417 | 0 | resolve.packages[*iface_pkg_id].name, |
3418 | | ) |
3419 | 0 | })? |
3420 | | { |
3421 | 0 | continue; |
3422 | 24.8k | } |
3423 | 24.8k | self.update_function(resolve, func, span) |
3424 | 24.8k | .with_context(|| format!("failed to update function `{}`", func.name))?; |
3425 | | } |
3426 | | |
3427 | | // Filter out all of the existing functions in interface which fail the |
3428 | | // `include_stability()` check, as they shouldn't be available. |
3429 | 58.7k | for (name, func) in mem::take(&mut iface.functions) { |
3430 | 24.8k | if resolve.include_stability(&func.stability, iface_pkg_id, None)? { |
3431 | 24.8k | iface.functions.insert(name, func); |
3432 | 24.8k | } |
3433 | | } |
3434 | | |
3435 | 58.7k | Ok(()) |
3436 | 58.7k | } |
3437 | | |
3438 | 34.8k | fn update_function( |
3439 | 34.8k | &mut self, |
3440 | 34.8k | resolve: &mut Resolve, |
3441 | 34.8k | func: &mut Function, |
3442 | 34.8k | span: Option<Span>, |
3443 | 34.8k | ) -> Result<()> { |
3444 | 34.8k | if let Some(id) = func.kind.resource_mut() { |
3445 | 7.06k | self.update_type_id(id, span)?; |
3446 | 27.8k | } |
3447 | 80.9k | for (_, ty) in func.params.iter_mut() { |
3448 | 80.9k | self.update_ty(resolve, ty, span)?; |
3449 | | } |
3450 | 34.8k | if let Some(ty) = &mut func.result { |
3451 | 25.0k | self.update_ty(resolve, ty, span)?; |
3452 | 9.81k | } |
3453 | | |
3454 | 34.8k | if let Some(ty) = &func.result { |
3455 | 25.0k | if self.type_has_borrow(resolve, ty) { |
3456 | 0 | match span { |
3457 | 0 | Some(span) => { |
3458 | 0 | bail!(Error::new( |
3459 | 0 | span, |
3460 | 0 | format!( |
3461 | 0 | "function returns a type which contains \ |
3462 | 0 | a `borrow<T>` which is not supported" |
3463 | 0 | ) |
3464 | 0 | )) |
3465 | | } |
3466 | 0 | None => unreachable!(), |
3467 | | } |
3468 | 25.0k | } |
3469 | 9.81k | } |
3470 | | |
3471 | 34.8k | Ok(()) |
3472 | 34.8k | } |
3473 | | |
3474 | 17.4k | fn update_world( |
3475 | 17.4k | &mut self, |
3476 | 17.4k | world: &mut World, |
3477 | 17.4k | resolve: &mut Resolve, |
3478 | 17.4k | pkg_id: &PackageId, |
3479 | 17.4k | spans: &WorldSpan, |
3480 | 17.4k | ) -> Result<()> { |
3481 | 17.4k | assert_eq!(world.imports.len(), spans.imports.len()); |
3482 | 17.4k | assert_eq!(world.exports.len(), spans.exports.len()); |
3483 | | |
3484 | | // Rewrite imports/exports with their updated versions. Note that this |
3485 | | // may involve updating the key of the imports/exports maps so this |
3486 | | // starts by emptying them out and then everything is re-inserted. |
3487 | 17.4k | let imports = mem::take(&mut world.imports).into_iter(); |
3488 | 25.8k | let imports = imports.zip(&spans.imports).map(|p| (p, true)); |
3489 | 17.4k | let exports = mem::take(&mut world.exports).into_iter(); |
3490 | 17.4k | let exports = exports.zip(&spans.exports).map(|p| (p, false)); |
3491 | 28.6k | for (((mut name, mut item), span), import) in imports.chain(exports) { |
3492 | | // Update the `id` eagerly here so `item.stability(..)` below |
3493 | | // works. |
3494 | 28.6k | if let WorldItem::Type(id) = &mut item { |
3495 | 19.6k | *id = self.map_type(*id, Some(*span))?; |
3496 | 9.05k | } |
3497 | 28.6k | let stability = item.stability(resolve); |
3498 | 28.6k | if !resolve |
3499 | 28.6k | .include_stability(stability, pkg_id, Some(*span)) |
3500 | 28.6k | .with_context(|| format!("failed to process world item in `{}`", world.name))? |
3501 | | { |
3502 | 0 | continue; |
3503 | 28.6k | } |
3504 | 28.6k | self.update_world_key(&mut name, Some(*span))?; |
3505 | 28.6k | match &mut item { |
3506 | 4.06k | WorldItem::Interface { id, .. } => { |
3507 | 4.06k | *id = self.map_interface(*id, Some(*span))?; |
3508 | | } |
3509 | 4.98k | WorldItem::Function(f) => { |
3510 | 4.98k | self.update_function(resolve, f, Some(*span))?; |
3511 | | } |
3512 | 19.6k | WorldItem::Type(_) => { |
3513 | 19.6k | // already mapped above |
3514 | 19.6k | } |
3515 | | } |
3516 | | |
3517 | 28.6k | let dst = if import { |
3518 | 25.8k | &mut world.imports |
3519 | | } else { |
3520 | 2.83k | &mut world.exports |
3521 | | }; |
3522 | 28.6k | let prev = dst.insert(name, item); |
3523 | 28.6k | assert!(prev.is_none()); |
3524 | | } |
3525 | | |
3526 | 17.4k | Ok(()) |
3527 | 17.4k | } |
3528 | | |
3529 | 17.4k | fn process_world_includes( |
3530 | 17.4k | &self, |
3531 | 17.4k | id: WorldId, |
3532 | 17.4k | resolve: &mut Resolve, |
3533 | 17.4k | pkg_id: &PackageId, |
3534 | 17.4k | spans: &WorldSpan, |
3535 | 17.4k | ) -> Result<()> { |
3536 | 17.4k | let world = &mut resolve.worlds[id]; |
3537 | | // Resolve all `include` statements of the world which will add more |
3538 | | // entries to the imports/exports list for this world. |
3539 | 17.4k | assert_eq!(world.includes.len(), spans.includes.len()); |
3540 | 17.4k | let includes = mem::take(&mut world.includes); |
3541 | 17.4k | let include_names = mem::take(&mut world.include_names); |
3542 | 17.4k | for (((stability, include_world), span), names) in includes |
3543 | 17.4k | .into_iter() |
3544 | 17.4k | .zip(&spans.includes) |
3545 | 17.4k | .zip(&include_names) |
3546 | | { |
3547 | 0 | if !resolve |
3548 | 0 | .include_stability(&stability, pkg_id, Some(*span)) |
3549 | 0 | .with_context(|| { |
3550 | 0 | format!( |
3551 | 0 | "failed to process feature gate for included world [{}] in package [{}]", |
3552 | 0 | resolve.worlds[include_world].name.as_str(), |
3553 | 0 | resolve.packages[*pkg_id].name |
3554 | | ) |
3555 | 0 | })? |
3556 | | { |
3557 | 0 | continue; |
3558 | 0 | } |
3559 | 0 | self.resolve_include(id, include_world, names, *span, pkg_id, resolve)?; |
3560 | | } |
3561 | | |
3562 | | // Validate that there are no case-insensitive duplicate names in imports/exports |
3563 | 17.4k | Self::validate_world_case_insensitive_names(resolve, id)?; |
3564 | | |
3565 | 17.4k | Ok(()) |
3566 | 17.4k | } |
3567 | | |
3568 | | /// Validates that a world's imports and exports don't have case-insensitive |
3569 | | /// duplicate names. Per the WIT specification, kebab-case identifiers are |
3570 | | /// case-insensitive within the same scope. |
3571 | 17.4k | fn validate_world_case_insensitive_names(resolve: &Resolve, world_id: WorldId) -> Result<()> { |
3572 | 17.4k | let world = &resolve.worlds[world_id]; |
3573 | | |
3574 | | // Helper closure to check for case-insensitive duplicates in a map |
3575 | 17.4k | let validate_names = |items: &IndexMap<WorldKey, WorldItem>, |
3576 | | item_type: &str| |
3577 | 34.9k | -> Result<()> { |
3578 | 34.9k | let mut seen_lowercase: HashMap<String, String> = HashMap::new(); |
3579 | | |
3580 | 34.9k | for key in items.keys() { |
3581 | | // Only WorldKey::Name variants can have case-insensitive conflicts |
3582 | 28.6k | if let WorldKey::Name(name) = key { |
3583 | 27.5k | let lowercase_name = name.to_lowercase(); |
3584 | | |
3585 | 27.5k | if let Some(existing_name) = seen_lowercase.get(&lowercase_name) { |
3586 | | // Only error on case-insensitive duplicates (e.g., "foo" vs "FOO"). |
3587 | | // Exact duplicates would have been caught earlier. |
3588 | 0 | if existing_name != name { |
3589 | 0 | bail!( |
3590 | 0 | "{item_type} `{name}` conflicts with {item_type} `{existing_name}` \ |
3591 | 0 | (kebab-case identifiers are case-insensitive)" |
3592 | | ); |
3593 | 0 | } |
3594 | 27.5k | } |
3595 | | |
3596 | 27.5k | seen_lowercase.insert(lowercase_name, name.clone()); |
3597 | 1.12k | } |
3598 | | } |
3599 | | |
3600 | 34.9k | Ok(()) |
3601 | 34.9k | }; |
3602 | | |
3603 | 17.4k | validate_names(&world.imports, "import") |
3604 | 17.4k | .with_context(|| format!("failed to validate imports in world `{}`", world.name))?; |
3605 | 17.4k | validate_names(&world.exports, "export") |
3606 | 17.4k | .with_context(|| format!("failed to validate exports in world `{}`", world.name))?; |
3607 | | |
3608 | 17.4k | Ok(()) |
3609 | 17.4k | } |
3610 | | |
3611 | 53.3k | fn update_world_key(&self, key: &mut WorldKey, span: Option<Span>) -> Result<()> { |
3612 | 53.3k | match key { |
3613 | 50.5k | WorldKey::Name(_) => {} |
3614 | 2.80k | WorldKey::Interface(id) => { |
3615 | 2.80k | *id = self.map_interface(*id, span)?; |
3616 | | } |
3617 | | } |
3618 | 53.3k | Ok(()) |
3619 | 53.3k | } |
3620 | | |
3621 | 0 | fn resolve_include( |
3622 | 0 | &self, |
3623 | 0 | id: WorldId, |
3624 | 0 | include_world_id_orig: WorldId, |
3625 | 0 | names: &[IncludeName], |
3626 | 0 | span: Span, |
3627 | 0 | pkg_id: &PackageId, |
3628 | 0 | resolve: &mut Resolve, |
3629 | 0 | ) -> Result<()> { |
3630 | 0 | let world = &resolve.worlds[id]; |
3631 | 0 | let include_world_id = self.map_world(include_world_id_orig, Some(span))?; |
3632 | 0 | let include_world = resolve.worlds[include_world_id].clone(); |
3633 | 0 | let mut names_ = names.to_owned(); |
3634 | 0 | let is_external_include = world.package != include_world.package; |
3635 | | |
3636 | | // remove all imports and exports that match the names we're including |
3637 | 0 | for import in include_world.imports.iter() { |
3638 | 0 | self.remove_matching_name(import, &mut names_); |
3639 | 0 | } |
3640 | 0 | for export in include_world.exports.iter() { |
3641 | 0 | self.remove_matching_name(export, &mut names_); |
3642 | 0 | } |
3643 | 0 | if !names_.is_empty() { |
3644 | 0 | bail!(Error::new( |
3645 | 0 | span, |
3646 | 0 | format!( |
3647 | 0 | "no import or export kebab-name `{}`. Note that an ID does not support renaming", |
3648 | 0 | names_[0].name |
3649 | 0 | ), |
3650 | 0 | )); |
3651 | 0 | } |
3652 | | |
3653 | 0 | let mut cloner = clone::Cloner::new( |
3654 | 0 | resolve, |
3655 | 0 | TypeOwner::World(if is_external_include { |
3656 | 0 | include_world_id |
3657 | | } else { |
3658 | 0 | include_world_id |
3659 | | // include_world_id_orig |
3660 | | }), |
3661 | 0 | TypeOwner::World(id), |
3662 | | ); |
3663 | 0 | cloner.new_package = Some(*pkg_id); |
3664 | | |
3665 | | // copy the imports and exports from the included world into the current world |
3666 | 0 | for import in include_world.imports.iter() { |
3667 | 0 | self.resolve_include_item( |
3668 | 0 | &mut cloner, |
3669 | 0 | names, |
3670 | 0 | |resolve| &mut resolve.worlds[id].imports, |
3671 | 0 | import, |
3672 | 0 | span, |
3673 | 0 | "import", |
3674 | 0 | is_external_include, |
3675 | 0 | )?; |
3676 | | } |
3677 | | |
3678 | 0 | for export in include_world.exports.iter() { |
3679 | 0 | self.resolve_include_item( |
3680 | 0 | &mut cloner, |
3681 | 0 | names, |
3682 | 0 | |resolve| &mut resolve.worlds[id].exports, |
3683 | 0 | export, |
3684 | 0 | span, |
3685 | 0 | "export", |
3686 | 0 | is_external_include, |
3687 | 0 | )?; |
3688 | | } |
3689 | 0 | Ok(()) |
3690 | 0 | } |
3691 | | |
3692 | 0 | fn resolve_include_item( |
3693 | 0 | &self, |
3694 | 0 | cloner: &mut clone::Cloner<'_>, |
3695 | 0 | names: &[IncludeName], |
3696 | 0 | get_items: impl Fn(&mut Resolve) -> &mut IndexMap<WorldKey, WorldItem>, |
3697 | 0 | item: (&WorldKey, &WorldItem), |
3698 | 0 | span: Span, |
3699 | 0 | item_type: &str, |
3700 | 0 | is_external_include: bool, |
3701 | 0 | ) -> Result<()> { |
3702 | 0 | match item.0 { |
3703 | 0 | WorldKey::Name(n) => { |
3704 | 0 | let n = names |
3705 | 0 | .into_iter() |
3706 | 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} |
3707 | 0 | .unwrap_or(n.clone()); |
3708 | | |
3709 | | // When the `with` option to the `include` directive is |
3710 | | // specified and is used to rename a function that means that |
3711 | | // the function's own original name needs to be updated, so |
3712 | | // reflect the change not only in the world key but additionally |
3713 | | // in the function itself. |
3714 | 0 | let mut new_item = item.1.clone(); |
3715 | 0 | let key = WorldKey::Name(n.clone()); |
3716 | 0 | cloner.world_item(&key, &mut new_item, &mut CloneMaps::default()); |
3717 | 0 | match &mut new_item { |
3718 | 0 | WorldItem::Function(f) => f.name = n.clone(), |
3719 | 0 | WorldItem::Type(id) => cloner.resolve.types[*id].name = Some(n.clone()), |
3720 | 0 | WorldItem::Interface { .. } => {} |
3721 | | } |
3722 | | |
3723 | 0 | let prev = get_items(cloner.resolve).insert(key, new_item); |
3724 | 0 | if prev.is_some() { |
3725 | 0 | bail!(Error::new( |
3726 | 0 | span, |
3727 | 0 | format!("{item_type} of `{n}` shadows previously {item_type}ed items"), |
3728 | 0 | )) |
3729 | 0 | } |
3730 | | } |
3731 | 0 | key @ WorldKey::Interface(_) => { |
3732 | 0 | let prev = get_items(cloner.resolve) |
3733 | 0 | .entry(key.clone()) |
3734 | 0 | .or_insert(item.1.clone()); |
3735 | 0 | match (&item.1, prev) { |
3736 | | ( |
3737 | | WorldItem::Interface { |
3738 | 0 | id: aid, |
3739 | 0 | stability: astability, |
3740 | | }, |
3741 | | WorldItem::Interface { |
3742 | 0 | id: bid, |
3743 | 0 | stability: bstability, |
3744 | | }, |
3745 | | ) => { |
3746 | 0 | assert_eq!(*aid, *bid); |
3747 | 0 | merge_include_stability(astability, bstability, is_external_include)?; |
3748 | | } |
3749 | 0 | (WorldItem::Interface { .. }, _) => unreachable!(), |
3750 | 0 | (WorldItem::Function(_), _) => unreachable!(), |
3751 | 0 | (WorldItem::Type(_), _) => unreachable!(), |
3752 | | } |
3753 | | } |
3754 | | }; |
3755 | | |
3756 | 0 | Ok(()) |
3757 | 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}> |
3758 | | |
3759 | 0 | fn remove_matching_name(&self, item: (&WorldKey, &WorldItem), names: &mut Vec<IncludeName>) { |
3760 | 0 | match item.0 { |
3761 | 0 | WorldKey::Name(n) => { |
3762 | 0 | names.retain(|name| rename(n, name).is_none()); |
3763 | | } |
3764 | 0 | _ => {} |
3765 | | } |
3766 | 0 | } |
3767 | | |
3768 | 72.2k | fn type_has_borrow(&mut self, resolve: &Resolve, ty: &Type) -> bool { |
3769 | 72.2k | let id = match ty { |
3770 | 31.2k | Type::Id(id) => *id, |
3771 | 40.9k | _ => return false, |
3772 | | }; |
3773 | | |
3774 | 31.2k | if let Some(Some(has_borrow)) = self.type_has_borrow.get(id.index()) { |
3775 | 2.18k | return *has_borrow; |
3776 | 29.0k | } |
3777 | | |
3778 | 29.0k | let result = self.typedef_has_borrow(resolve, &resolve.types[id]); |
3779 | 29.0k | if self.type_has_borrow.len() <= id.index() { |
3780 | 26.8k | self.type_has_borrow.resize(id.index() + 1, None); |
3781 | 26.8k | } |
3782 | 29.0k | self.type_has_borrow[id.index()] = Some(result); |
3783 | 29.0k | result |
3784 | 72.2k | } |
3785 | | |
3786 | 29.0k | fn typedef_has_borrow(&mut self, resolve: &Resolve, ty: &TypeDef) -> bool { |
3787 | 1.01k | match &ty.kind { |
3788 | 55 | TypeDefKind::Type(t) => self.type_has_borrow(resolve, t), |
3789 | 38 | TypeDefKind::Variant(v) => v |
3790 | 38 | .cases |
3791 | 38 | .iter() |
3792 | 144 | .filter_map(|case| case.ty.as_ref()) |
3793 | 87 | .any(|ty| self.type_has_borrow(resolve, ty)), |
3794 | 0 | TypeDefKind::Handle(Handle::Borrow(_)) => true, |
3795 | 1.01k | TypeDefKind::Handle(Handle::Own(_)) => false, |
3796 | 0 | TypeDefKind::Resource => false, |
3797 | 45 | TypeDefKind::Record(r) => r |
3798 | 45 | .fields |
3799 | 45 | .iter() |
3800 | 143 | .any(|case| self.type_has_borrow(resolve, &case.ty)), |
3801 | 9 | TypeDefKind::Flags(_) => false, |
3802 | 17.6k | TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.type_has_borrow(resolve, t)), |
3803 | 84 | TypeDefKind::Enum(_) => false, |
3804 | 1.60k | TypeDefKind::List(ty) |
3805 | 3.20k | | TypeDefKind::FixedSizeList(ty, ..) |
3806 | 705 | | TypeDefKind::Future(Some(ty)) |
3807 | 1.43k | | TypeDefKind::Stream(Some(ty)) |
3808 | 13.4k | | TypeDefKind::Option(ty) => self.type_has_borrow(resolve, ty), |
3809 | 8.74k | TypeDefKind::Result(r) => [&r.ok, &r.err] |
3810 | 8.74k | .iter() |
3811 | 17.4k | .filter_map(|t| t.as_ref()) |
3812 | 15.7k | .any(|t| self.type_has_borrow(resolve, t)), |
3813 | 71 | TypeDefKind::Future(None) | TypeDefKind::Stream(None) => false, |
3814 | 0 | TypeDefKind::Unknown => unreachable!(), |
3815 | | } |
3816 | 29.0k | } |
3817 | | } |
3818 | | |
3819 | | struct MergeMap<'a> { |
3820 | | /// A map of package ids in `from` to those in `into` for those that are |
3821 | | /// found to be equivalent. |
3822 | | package_map: HashMap<PackageId, PackageId>, |
3823 | | |
3824 | | /// A map of interface ids in `from` to those in `into` for those that are |
3825 | | /// found to be equivalent. |
3826 | | interface_map: HashMap<InterfaceId, InterfaceId>, |
3827 | | |
3828 | | /// A map of type ids in `from` to those in `into` for those that are |
3829 | | /// found to be equivalent. |
3830 | | type_map: HashMap<TypeId, TypeId>, |
3831 | | |
3832 | | /// A map of world ids in `from` to those in `into` for those that are |
3833 | | /// found to be equivalent. |
3834 | | world_map: HashMap<WorldId, WorldId>, |
3835 | | |
3836 | | /// A list of documents that need to be added to packages in `into`. |
3837 | | /// |
3838 | | /// The elements here are: |
3839 | | /// |
3840 | | /// * The name of the interface/world |
3841 | | /// * The ID within `into` of the package being added to |
3842 | | /// * The ID within `from` of the item being added. |
3843 | | interfaces_to_add: Vec<(String, PackageId, InterfaceId)>, |
3844 | | worlds_to_add: Vec<(String, PackageId, WorldId)>, |
3845 | | |
3846 | | /// Which `Resolve` is being merged from. |
3847 | | from: &'a Resolve, |
3848 | | |
3849 | | /// Which `Resolve` is being merged into. |
3850 | | into: &'a Resolve, |
3851 | | } |
3852 | | |
3853 | | impl<'a> MergeMap<'a> { |
3854 | 12.3k | fn new(from: &'a Resolve, into: &'a Resolve) -> MergeMap<'a> { |
3855 | 12.3k | MergeMap { |
3856 | 12.3k | package_map: Default::default(), |
3857 | 12.3k | interface_map: Default::default(), |
3858 | 12.3k | type_map: Default::default(), |
3859 | 12.3k | world_map: Default::default(), |
3860 | 12.3k | interfaces_to_add: Default::default(), |
3861 | 12.3k | worlds_to_add: Default::default(), |
3862 | 12.3k | from, |
3863 | 12.3k | into, |
3864 | 12.3k | } |
3865 | 12.3k | } |
3866 | | |
3867 | 12.3k | fn build(&mut self) -> Result<()> { |
3868 | 16.8k | for from_id in self.from.topological_packages() { |
3869 | 16.8k | let from = &self.from.packages[from_id]; |
3870 | 16.8k | let into_id = match self.into.package_names.get(&from.name) { |
3871 | 105 | Some(id) => *id, |
3872 | | |
3873 | | // This package, according to its name and url, is not present |
3874 | | // in `self` so it needs to get added below. |
3875 | | None => { |
3876 | 16.7k | log::trace!("adding unique package {}", from.name); |
3877 | 16.7k | continue; |
3878 | | } |
3879 | | }; |
3880 | 105 | log::trace!("merging duplicate package {}", from.name); |
3881 | | |
3882 | 105 | self.build_package(from_id, into_id).with_context(|| { |
3883 | 0 | format!("failed to merge package `{}` into existing copy", from.name) |
3884 | 0 | })?; |
3885 | | } |
3886 | | |
3887 | 12.3k | Ok(()) |
3888 | 12.3k | } |
3889 | | |
3890 | 105 | fn build_package(&mut self, from_id: PackageId, into_id: PackageId) -> Result<()> { |
3891 | 105 | let prev = self.package_map.insert(from_id, into_id); |
3892 | 105 | assert!(prev.is_none()); |
3893 | | |
3894 | 105 | let from = &self.from.packages[from_id]; |
3895 | 105 | let into = &self.into.packages[into_id]; |
3896 | | |
3897 | | // If an interface is present in `from_id` but not present in `into_id` |
3898 | | // then it can be copied over wholesale. That copy is scheduled to |
3899 | | // happen within the `self.interfaces_to_add` list. |
3900 | 105 | for (name, from_interface_id) in from.interfaces.iter() { |
3901 | 14 | let into_interface_id = match into.interfaces.get(name) { |
3902 | 6 | Some(id) => *id, |
3903 | | None => { |
3904 | 8 | log::trace!("adding unique interface {name}"); |
3905 | 8 | self.interfaces_to_add |
3906 | 8 | .push((name.clone(), into_id, *from_interface_id)); |
3907 | 8 | continue; |
3908 | | } |
3909 | | }; |
3910 | | |
3911 | 6 | log::trace!("merging duplicate interfaces {name}"); |
3912 | 6 | self.build_interface(*from_interface_id, into_interface_id) |
3913 | 6 | .with_context(|| format!("failed to merge interface `{name}`"))?; |
3914 | | } |
3915 | | |
3916 | 105 | for (name, from_world_id) in from.worlds.iter() { |
3917 | 103 | let into_world_id = match into.worlds.get(name) { |
3918 | 0 | Some(id) => *id, |
3919 | | None => { |
3920 | 103 | log::trace!("adding unique world {name}"); |
3921 | 103 | self.worlds_to_add |
3922 | 103 | .push((name.clone(), into_id, *from_world_id)); |
3923 | 103 | continue; |
3924 | | } |
3925 | | }; |
3926 | | |
3927 | 0 | log::trace!("merging duplicate worlds {name}"); |
3928 | 0 | self.build_world(*from_world_id, into_world_id) |
3929 | 0 | .with_context(|| format!("failed to merge world `{name}`"))?; |
3930 | | } |
3931 | | |
3932 | 105 | Ok(()) |
3933 | 105 | } |
3934 | | |
3935 | 8 | fn build_interface(&mut self, from_id: InterfaceId, into_id: InterfaceId) -> Result<()> { |
3936 | 8 | let prev = self.interface_map.insert(from_id, into_id); |
3937 | 8 | assert!(prev.is_none()); |
3938 | | |
3939 | 8 | let from_interface = &self.from.interfaces[from_id]; |
3940 | 8 | let into_interface = &self.into.interfaces[into_id]; |
3941 | | |
3942 | | // Unlike documents/interfaces above if an interface in `from` |
3943 | | // differs from the interface in `into` then that's considered an |
3944 | | // error. Changing interfaces can reflect changes in imports/exports |
3945 | | // which may not be expected so it's currently required that all |
3946 | | // interfaces, when merged, exactly match. |
3947 | | // |
3948 | | // One case to consider here, for example, is that if a world in |
3949 | | // `into` exports the interface `into_id` then if `from_id` were to |
3950 | | // add more items into `into` then it would unexpectedly require more |
3951 | | // items to be exported which may not work. In an import context this |
3952 | | // might work since it's "just more items available for import", but |
3953 | | // for now a conservative route of "interfaces must match" is taken. |
3954 | | |
3955 | 11 | for (name, from_type_id) in from_interface.types.iter() { |
3956 | 11 | let into_type_id = *into_interface |
3957 | 11 | .types |
3958 | 11 | .get(name) |
3959 | 11 | .ok_or_else(|| anyhow!("expected type `{name}` to be present"))?; |
3960 | 10 | let prev = self.type_map.insert(*from_type_id, into_type_id); |
3961 | 10 | assert!(prev.is_none()); |
3962 | | |
3963 | 10 | self.build_type_id(*from_type_id, into_type_id) |
3964 | 10 | .with_context(|| format!("mismatch in type `{name}`"))?; |
3965 | | } |
3966 | | |
3967 | 15 | for (name, from_func) in from_interface.functions.iter() { |
3968 | 15 | let into_func = match into_interface.functions.get(name) { |
3969 | 15 | Some(func) => func, |
3970 | 0 | None => bail!("expected function `{name}` to be present"), |
3971 | | }; |
3972 | 15 | self.build_function(from_func, into_func) |
3973 | 15 | .with_context(|| format!("mismatch in function `{name}`"))?; |
3974 | | } |
3975 | | |
3976 | 7 | Ok(()) |
3977 | 8 | } |
3978 | | |
3979 | 87 | fn build_type_id(&mut self, from_id: TypeId, into_id: TypeId) -> Result<()> { |
3980 | | // FIXME: ideally the types should be "structurally |
3981 | | // equal" but that's not trivial to do in the face of |
3982 | | // resources. |
3983 | 87 | let _ = from_id; |
3984 | 87 | let _ = into_id; |
3985 | 87 | Ok(()) |
3986 | 87 | } |
3987 | | |
3988 | 40 | fn build_type(&mut self, from_ty: &Type, into_ty: &Type) -> Result<()> { |
3989 | 40 | match (from_ty, into_ty) { |
3990 | 16 | (Type::Id(from), Type::Id(into)) => { |
3991 | 16 | self.build_type_id(*from, *into)?; |
3992 | | } |
3993 | 24 | (from, into) if from != into => bail!("different kinds of types"), |
3994 | 24 | _ => {} |
3995 | | } |
3996 | 40 | Ok(()) |
3997 | 40 | } |
3998 | | |
3999 | 17 | fn build_function(&mut self, from_func: &Function, into_func: &Function) -> Result<()> { |
4000 | 17 | if from_func.name != into_func.name { |
4001 | 0 | bail!( |
4002 | 0 | "different function names `{}` and `{}`", |
4003 | | from_func.name, |
4004 | | into_func.name |
4005 | | ); |
4006 | 17 | } |
4007 | 17 | match (&from_func.kind, &into_func.kind) { |
4008 | 4 | (FunctionKind::Freestanding, FunctionKind::Freestanding) => {} |
4009 | 1 | (FunctionKind::AsyncFreestanding, FunctionKind::AsyncFreestanding) => {} |
4010 | | |
4011 | 0 | (FunctionKind::Method(from), FunctionKind::Method(into)) |
4012 | 0 | | (FunctionKind::Static(from), FunctionKind::Static(into)) |
4013 | 7 | | (FunctionKind::AsyncMethod(from), FunctionKind::AsyncMethod(into)) |
4014 | 3 | | (FunctionKind::AsyncStatic(from), FunctionKind::AsyncStatic(into)) |
4015 | 2 | | (FunctionKind::Constructor(from), FunctionKind::Constructor(into)) => { |
4016 | 12 | self.build_type_id(*from, *into) |
4017 | 12 | .context("different function kind types")?; |
4018 | | } |
4019 | | |
4020 | | (FunctionKind::Method(_), _) |
4021 | | | (FunctionKind::Constructor(_), _) |
4022 | | | (FunctionKind::Static(_), _) |
4023 | | | (FunctionKind::Freestanding, _) |
4024 | | | (FunctionKind::AsyncFreestanding, _) |
4025 | | | (FunctionKind::AsyncMethod(_), _) |
4026 | | | (FunctionKind::AsyncStatic(_), _) => { |
4027 | 0 | bail!("different function kind types") |
4028 | | } |
4029 | | } |
4030 | | |
4031 | 17 | if from_func.params.len() != into_func.params.len() { |
4032 | 0 | bail!("different number of function parameters"); |
4033 | 17 | } |
4034 | 27 | for ((from_name, from_ty), (into_name, into_ty)) in |
4035 | 17 | from_func.params.iter().zip(&into_func.params) |
4036 | | { |
4037 | 27 | if from_name != into_name { |
4038 | 0 | bail!("different function parameter names: {from_name} != {into_name}"); |
4039 | 27 | } |
4040 | 27 | self.build_type(from_ty, into_ty) |
4041 | 27 | .with_context(|| format!("different function parameter types for `{from_name}`"))?; |
4042 | | } |
4043 | 17 | match (&from_func.result, &into_func.result) { |
4044 | 13 | (Some(from_ty), Some(into_ty)) => { |
4045 | 13 | self.build_type(from_ty, into_ty) |
4046 | 13 | .context("different function result types")?; |
4047 | | } |
4048 | 4 | (None, None) => {} |
4049 | 0 | (Some(_), None) | (None, Some(_)) => bail!("different number of function results"), |
4050 | | } |
4051 | 17 | Ok(()) |
4052 | 17 | } |
4053 | | |
4054 | 0 | fn build_world(&mut self, from_id: WorldId, into_id: WorldId) -> Result<()> { |
4055 | 0 | let prev = self.world_map.insert(from_id, into_id); |
4056 | 0 | assert!(prev.is_none()); |
4057 | | |
4058 | 0 | let from_world = &self.from.worlds[from_id]; |
4059 | 0 | let into_world = &self.into.worlds[into_id]; |
4060 | | |
4061 | | // Same as interfaces worlds are expected to exactly match to avoid |
4062 | | // unexpectedly changing a particular component's view of imports and |
4063 | | // exports. |
4064 | | // |
4065 | | // FIXME: this should probably share functionality with |
4066 | | // `Resolve::merge_worlds` to support adding imports but not changing |
4067 | | // exports. |
4068 | | |
4069 | 0 | if from_world.imports.len() != into_world.imports.len() { |
4070 | 0 | bail!("world contains different number of imports than expected"); |
4071 | 0 | } |
4072 | 0 | if from_world.exports.len() != into_world.exports.len() { |
4073 | 0 | bail!("world contains different number of exports than expected"); |
4074 | 0 | } |
4075 | | |
4076 | 0 | for (from_name, from) in from_world.imports.iter() { |
4077 | 0 | let into_name = MergeMap::map_name(from_name, &self.interface_map); |
4078 | 0 | let name_str = self.from.name_world_key(from_name); |
4079 | 0 | let into = into_world |
4080 | 0 | .imports |
4081 | 0 | .get(&into_name) |
4082 | 0 | .ok_or_else(|| anyhow!("import `{name_str}` not found in target world"))?; |
4083 | 0 | self.match_world_item(from, into) |
4084 | 0 | .with_context(|| format!("import `{name_str}` didn't match target world"))?; |
4085 | | } |
4086 | | |
4087 | 0 | for (from_name, from) in from_world.exports.iter() { |
4088 | 0 | let into_name = MergeMap::map_name(from_name, &self.interface_map); |
4089 | 0 | let name_str = self.from.name_world_key(from_name); |
4090 | 0 | let into = into_world |
4091 | 0 | .exports |
4092 | 0 | .get(&into_name) |
4093 | 0 | .ok_or_else(|| anyhow!("export `{name_str}` not found in target world"))?; |
4094 | 0 | self.match_world_item(from, into) |
4095 | 0 | .with_context(|| format!("export `{name_str}` didn't match target world"))?; |
4096 | | } |
4097 | | |
4098 | 0 | Ok(()) |
4099 | 0 | } |
4100 | | |
4101 | 0 | fn map_name( |
4102 | 0 | from_name: &WorldKey, |
4103 | 0 | interface_map: &HashMap<InterfaceId, InterfaceId>, |
4104 | 0 | ) -> WorldKey { |
4105 | 0 | match from_name { |
4106 | 0 | WorldKey::Name(s) => WorldKey::Name(s.clone()), |
4107 | 0 | WorldKey::Interface(id) => { |
4108 | 0 | WorldKey::Interface(interface_map.get(id).copied().unwrap_or(*id)) |
4109 | | } |
4110 | | } |
4111 | 0 | } |
4112 | | |
4113 | 0 | fn match_world_item(&mut self, from: &WorldItem, into: &WorldItem) -> Result<()> { |
4114 | 0 | match (from, into) { |
4115 | 0 | (WorldItem::Interface { id: from, .. }, WorldItem::Interface { id: into, .. }) => { |
4116 | | match ( |
4117 | 0 | &self.from.interfaces[*from].name, |
4118 | 0 | &self.into.interfaces[*into].name, |
4119 | | ) { |
4120 | | // If one interface is unnamed then they must both be |
4121 | | // unnamed and they must both have the same structure for |
4122 | | // now. |
4123 | 0 | (None, None) => self.build_interface(*from, *into)?, |
4124 | | |
4125 | | // Otherwise both interfaces must be named and they must |
4126 | | // have been previously found to be equivalent. Note that |
4127 | | // if either is unnamed it won't be present in |
4128 | | // `interface_map` so this'll return an error. |
4129 | | _ => { |
4130 | 0 | if self.interface_map.get(&from) != Some(&into) { |
4131 | 0 | bail!("interfaces are not the same"); |
4132 | 0 | } |
4133 | | } |
4134 | | } |
4135 | | } |
4136 | 0 | (WorldItem::Function(from), WorldItem::Function(into)) => { |
4137 | 0 | let _ = (from, into); |
4138 | 0 | // FIXME: should assert an check that `from` structurally |
4139 | 0 | // matches `into` |
4140 | 0 | } |
4141 | 0 | (WorldItem::Type(from), WorldItem::Type(into)) => { |
4142 | | // FIXME: should assert an check that `from` structurally |
4143 | | // matches `into` |
4144 | 0 | let prev = self.type_map.insert(*from, *into); |
4145 | 0 | assert!(prev.is_none()); |
4146 | | } |
4147 | | |
4148 | | (WorldItem::Interface { .. }, _) |
4149 | | | (WorldItem::Function(_), _) |
4150 | | | (WorldItem::Type(_), _) => { |
4151 | 0 | bail!("world items do not have the same type") |
4152 | | } |
4153 | | } |
4154 | 0 | Ok(()) |
4155 | 0 | } |
4156 | | } |
4157 | | |
4158 | | /// Updates stability annotations when merging `from` into `into`. |
4159 | | /// |
4160 | | /// This is done to keep up-to-date stability information if possible. |
4161 | | /// Components for example don't carry stability information but WIT does so |
4162 | | /// this tries to move from "unknown" to stable/unstable if possible. |
4163 | 16 | fn update_stability(from: &Stability, into: &mut Stability) -> Result<()> { |
4164 | | // If `from` is unknown or the two stability annotations are equal then |
4165 | | // there's nothing to do here. |
4166 | 16 | if from == into || from.is_unknown() { |
4167 | 16 | return Ok(()); |
4168 | 0 | } |
4169 | | // Otherwise if `into` is unknown then inherit the stability listed in |
4170 | | // `from`. |
4171 | 0 | if into.is_unknown() { |
4172 | 0 | *into = from.clone(); |
4173 | 0 | return Ok(()); |
4174 | 0 | } |
4175 | | |
4176 | | // Failing all that this means that the two attributes are different so |
4177 | | // generate an error. |
4178 | 0 | bail!("mismatch in stability from '{:?}' to '{:?}'", from, into) |
4179 | 16 | } |
4180 | | |
4181 | 0 | fn merge_include_stability( |
4182 | 0 | from: &Stability, |
4183 | 0 | into: &mut Stability, |
4184 | 0 | is_external_include: bool, |
4185 | 0 | ) -> Result<()> { |
4186 | 0 | if is_external_include && from.is_stable() { |
4187 | 0 | log::trace!("dropped stability from external package"); |
4188 | 0 | *into = Stability::Unknown; |
4189 | 0 | return Ok(()); |
4190 | 0 | } |
4191 | | |
4192 | 0 | return update_stability(from, into); |
4193 | 0 | } |
4194 | | |
4195 | | /// An error that can be returned during "world elaboration" during various |
4196 | | /// [`Resolve`] operations. |
4197 | | /// |
4198 | | /// Methods on [`Resolve`] which mutate its internals, such as |
4199 | | /// [`Resolve::push_dir`] or [`Resolve::importize`] can fail if `world` imports |
4200 | | /// in WIT packages are invalid. This error indicates one of these situations |
4201 | | /// where an invalid dependency graph between imports and exports are detected. |
4202 | | /// |
4203 | | /// Note that at this time this error is subtle and not easy to understand, and |
4204 | | /// work needs to be done to explain this better and additionally provide a |
4205 | | /// better error message. For now though this type enables callers to test for |
4206 | | /// the exact kind of error emitted. |
4207 | | #[derive(Debug, Clone)] |
4208 | | pub struct InvalidTransitiveDependency(String); |
4209 | | |
4210 | | impl fmt::Display for InvalidTransitiveDependency { |
4211 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
4212 | 0 | write!( |
4213 | 0 | f, |
4214 | 0 | "interface `{}` transitively depends on an interface in \ |
4215 | 0 | incompatible ways", |
4216 | | self.0 |
4217 | | ) |
4218 | 0 | } |
4219 | | } |
4220 | | |
4221 | | impl std::error::Error for InvalidTransitiveDependency {} |
4222 | | |
4223 | | #[cfg(test)] |
4224 | | mod tests { |
4225 | | use crate::Resolve; |
4226 | | use anyhow::Result; |
4227 | | |
4228 | | #[test] |
4229 | | fn select_world() -> Result<()> { |
4230 | | let mut resolve = Resolve::default(); |
4231 | | resolve.push_str( |
4232 | | "test.wit", |
4233 | | r#" |
4234 | | package foo:bar@0.1.0; |
4235 | | |
4236 | | world foo {} |
4237 | | "#, |
4238 | | )?; |
4239 | | resolve.push_str( |
4240 | | "test.wit", |
4241 | | r#" |
4242 | | package foo:baz@0.1.0; |
4243 | | |
4244 | | world foo {} |
4245 | | "#, |
4246 | | )?; |
4247 | | resolve.push_str( |
4248 | | "test.wit", |
4249 | | r#" |
4250 | | package foo:baz@0.2.0; |
4251 | | |
4252 | | world foo {} |
4253 | | "#, |
4254 | | )?; |
4255 | | |
4256 | | let dummy = resolve.push_str( |
4257 | | "test.wit", |
4258 | | r#" |
4259 | | package foo:dummy; |
4260 | | |
4261 | | world foo {} |
4262 | | "#, |
4263 | | )?; |
4264 | | |
4265 | | assert!(resolve.select_world(&[dummy], None).is_ok()); |
4266 | | assert!(resolve.select_world(&[dummy], Some("xx")).is_err()); |
4267 | | assert!(resolve.select_world(&[dummy], Some("")).is_err()); |
4268 | | assert!(resolve.select_world(&[dummy], Some("foo:bar/foo")).is_ok()); |
4269 | | assert!( |
4270 | | resolve |
4271 | | .select_world(&[dummy], Some("foo:bar/foo@0.1.0")) |
4272 | | .is_ok() |
4273 | | ); |
4274 | | assert!(resolve.select_world(&[dummy], Some("foo:baz/foo")).is_err()); |
4275 | | assert!( |
4276 | | resolve |
4277 | | .select_world(&[dummy], Some("foo:baz/foo@0.1.0")) |
4278 | | .is_ok() |
4279 | | ); |
4280 | | assert!( |
4281 | | resolve |
4282 | | .select_world(&[dummy], Some("foo:baz/foo@0.2.0")) |
4283 | | .is_ok() |
4284 | | ); |
4285 | | Ok(()) |
4286 | | } |
4287 | | |
4288 | | /// When there are multiple packages and there's no main package, don't |
4289 | | /// pick a world just based on it being the only one that matches. |
4290 | | #[test] |
4291 | | fn select_world_multiple_packages() -> Result<()> { |
4292 | | use wit_parser::Resolve; |
4293 | | |
4294 | | let mut resolve = Resolve::default(); |
4295 | | |
4296 | | // Just one world in one package; we always succeed. |
4297 | | let stuff = resolve.push_str( |
4298 | | "./my-test.wit", |
4299 | | r#" |
4300 | | package test:stuff; |
4301 | | |
4302 | | world foo { |
4303 | | // ... |
4304 | | } |
4305 | | "#, |
4306 | | )?; |
4307 | | assert!(resolve.select_world(&[stuff], None).is_ok()); |
4308 | | assert!(resolve.select_world(&[stuff], Some("foo")).is_ok()); |
4309 | | |
4310 | | // Multiple packages, but still just one total world. Lookups |
4311 | | // without a main package now fail. |
4312 | | let empty = resolve.push_str( |
4313 | | "./my-test.wit", |
4314 | | r#" |
4315 | | package test:empty; |
4316 | | "#, |
4317 | | )?; |
4318 | | assert!(resolve.select_world(&[stuff, empty], None).is_err()); |
4319 | | assert!(resolve.select_world(&[stuff, empty], Some("foo")).is_err()); |
4320 | | assert!(resolve.select_world(&[empty], None).is_err()); |
4321 | | assert!(resolve.select_world(&[empty], Some("foo")).is_err()); |
4322 | | |
4323 | | Ok(()) |
4324 | | } |
4325 | | |
4326 | | /// Test selecting a world with multiple versions of a package name. |
4327 | | #[test] |
4328 | | fn select_world_versions() -> Result<()> { |
4329 | | use wit_parser::Resolve; |
4330 | | |
4331 | | let mut resolve = Resolve::default(); |
4332 | | |
4333 | | let _id = resolve.push_str( |
4334 | | "./my-test.wit", |
4335 | | r#" |
4336 | | package example:distraction; |
4337 | | "#, |
4338 | | )?; |
4339 | | |
4340 | | // When selecting with a version it's ok to drop the version when |
4341 | | // there's only a single copy of that package in `Resolve`. |
4342 | | let versions_1 = resolve.push_str( |
4343 | | "./my-test.wit", |
4344 | | r#" |
4345 | | package example:versions@1.0.0; |
4346 | | |
4347 | | world foo { /* ... */ } |
4348 | | "#, |
4349 | | )?; |
4350 | | assert!(resolve.select_world(&[versions_1], Some("foo")).is_ok()); |
4351 | | assert!( |
4352 | | resolve |
4353 | | .select_world(&[versions_1], Some("foo@1.0.0")) |
4354 | | .is_err() |
4355 | | ); |
4356 | | assert!( |
4357 | | resolve |
4358 | | .select_world(&[versions_1], Some("example:versions/foo")) |
4359 | | .is_ok() |
4360 | | ); |
4361 | | assert!( |
4362 | | resolve |
4363 | | .select_world(&[versions_1], Some("example:versions/foo@1.0.0")) |
4364 | | .is_ok() |
4365 | | ); |
4366 | | |
4367 | | // However when a single package has multiple versions in a resolve |
4368 | | // it's required to specify the version to select which one. |
4369 | | let versions_2 = resolve.push_str( |
4370 | | "./my-test.wit", |
4371 | | r#" |
4372 | | package example:versions@2.0.0; |
4373 | | |
4374 | | world foo { /* ... */ } |
4375 | | "#, |
4376 | | )?; |
4377 | | assert!( |
4378 | | resolve |
4379 | | .select_world(&[versions_1, versions_2], Some("foo")) |
4380 | | .is_err() |
4381 | | ); |
4382 | | assert!( |
4383 | | resolve |
4384 | | .select_world(&[versions_1, versions_2], Some("foo@1.0.0")) |
4385 | | .is_err() |
4386 | | ); |
4387 | | assert!( |
4388 | | resolve |
4389 | | .select_world(&[versions_1, versions_2], Some("foo@2.0.0")) |
4390 | | .is_err() |
4391 | | ); |
4392 | | assert!( |
4393 | | resolve |
4394 | | .select_world(&[versions_1, versions_2], Some("example:versions/foo")) |
4395 | | .is_err() |
4396 | | ); |
4397 | | assert!( |
4398 | | resolve |
4399 | | .select_world( |
4400 | | &[versions_1, versions_2], |
4401 | | Some("example:versions/foo@1.0.0") |
4402 | | ) |
4403 | | .is_ok() |
4404 | | ); |
4405 | | assert!( |
4406 | | resolve |
4407 | | .select_world( |
4408 | | &[versions_1, versions_2], |
4409 | | Some("example:versions/foo@2.0.0") |
4410 | | ) |
4411 | | .is_ok() |
4412 | | ); |
4413 | | |
4414 | | Ok(()) |
4415 | | } |
4416 | | |
4417 | | /// Test overriding a main package using name qualification |
4418 | | #[test] |
4419 | | fn select_world_override_qualification() -> Result<()> { |
4420 | | use wit_parser::Resolve; |
4421 | | |
4422 | | let mut resolve = Resolve::default(); |
4423 | | |
4424 | | let other = resolve.push_str( |
4425 | | "./my-test.wit", |
4426 | | r#" |
4427 | | package example:other; |
4428 | | |
4429 | | world foo { } |
4430 | | "#, |
4431 | | )?; |
4432 | | |
4433 | | // A fully-qualified name overrides a main package. |
4434 | | let fq = resolve.push_str( |
4435 | | "./my-test.wit", |
4436 | | r#" |
4437 | | package example:fq; |
4438 | | |
4439 | | world bar { } |
4440 | | "#, |
4441 | | )?; |
4442 | | assert!(resolve.select_world(&[other, fq], Some("foo")).is_err()); |
4443 | | assert!(resolve.select_world(&[other, fq], Some("bar")).is_err()); |
4444 | | assert!( |
4445 | | resolve |
4446 | | .select_world(&[other, fq], Some("example:other/foo")) |
4447 | | .is_ok() |
4448 | | ); |
4449 | | assert!( |
4450 | | resolve |
4451 | | .select_world(&[other, fq], Some("example:fq/bar")) |
4452 | | .is_ok() |
4453 | | ); |
4454 | | assert!( |
4455 | | resolve |
4456 | | .select_world(&[other, fq], Some("example:other/bar")) |
4457 | | .is_err() |
4458 | | ); |
4459 | | assert!( |
4460 | | resolve |
4461 | | .select_world(&[other, fq], Some("example:fq/foo")) |
4462 | | .is_err() |
4463 | | ); |
4464 | | |
4465 | | Ok(()) |
4466 | | } |
4467 | | |
4468 | | /// Test selecting with fully-qualified world names. |
4469 | | #[test] |
4470 | | fn select_world_fully_qualified() -> Result<()> { |
4471 | | use wit_parser::Resolve; |
4472 | | |
4473 | | let mut resolve = Resolve::default(); |
4474 | | |
4475 | | let distraction = resolve.push_str( |
4476 | | "./my-test.wit", |
4477 | | r#" |
4478 | | package example:distraction; |
4479 | | "#, |
4480 | | )?; |
4481 | | |
4482 | | // If a package has multiple worlds, then we can't guess the world |
4483 | | // even if we know the package. |
4484 | | let multiworld = resolve.push_str( |
4485 | | "./my-test.wit", |
4486 | | r#" |
4487 | | package example:multiworld; |
4488 | | |
4489 | | world foo { /* ... */ } |
4490 | | |
4491 | | world bar { /* ... */ } |
4492 | | "#, |
4493 | | )?; |
4494 | | assert!( |
4495 | | resolve |
4496 | | .select_world(&[distraction, multiworld], None) |
4497 | | .is_err() |
4498 | | ); |
4499 | | assert!( |
4500 | | resolve |
4501 | | .select_world(&[distraction, multiworld], Some("foo")) |
4502 | | .is_err() |
4503 | | ); |
4504 | | assert!( |
4505 | | resolve |
4506 | | .select_world(&[distraction, multiworld], Some("example:multiworld/foo")) |
4507 | | .is_ok() |
4508 | | ); |
4509 | | assert!( |
4510 | | resolve |
4511 | | .select_world(&[distraction, multiworld], Some("bar")) |
4512 | | .is_err() |
4513 | | ); |
4514 | | assert!( |
4515 | | resolve |
4516 | | .select_world(&[distraction, multiworld], Some("example:multiworld/bar")) |
4517 | | .is_ok() |
4518 | | ); |
4519 | | |
4520 | | Ok(()) |
4521 | | } |
4522 | | |
4523 | | /// Test `select_world` with single and multiple packages. |
4524 | | #[test] |
4525 | | fn select_world_packages() -> Result<()> { |
4526 | | use wit_parser::Resolve; |
4527 | | |
4528 | | let mut resolve = Resolve::default(); |
4529 | | |
4530 | | // If there's a single package and only one world, that world is |
4531 | | // the obvious choice. |
4532 | | let wit1 = resolve.push_str( |
4533 | | "./my-test.wit", |
4534 | | r#" |
4535 | | package example:wit1; |
4536 | | |
4537 | | world foo { |
4538 | | // ... |
4539 | | } |
4540 | | "#, |
4541 | | )?; |
4542 | | assert!(resolve.select_world(&[wit1], None).is_ok()); |
4543 | | assert!(resolve.select_world(&[wit1], Some("foo")).is_ok()); |
4544 | | assert!( |
4545 | | resolve |
4546 | | .select_world(&[wit1], Some("example:wit1/foo")) |
4547 | | .is_ok() |
4548 | | ); |
4549 | | assert!(resolve.select_world(&[wit1], Some("bar")).is_err()); |
4550 | | assert!( |
4551 | | resolve |
4552 | | .select_world(&[wit1], Some("example:wit2/foo")) |
4553 | | .is_err() |
4554 | | ); |
4555 | | |
4556 | | // If there are multiple packages, we need to be told which package |
4557 | | // to use. |
4558 | | let wit2 = resolve.push_str( |
4559 | | "./my-test.wit", |
4560 | | r#" |
4561 | | package example:wit2; |
4562 | | |
4563 | | world foo { /* ... */ } |
4564 | | "#, |
4565 | | )?; |
4566 | | assert!(resolve.select_world(&[wit1, wit2], None).is_err()); |
4567 | | assert!(resolve.select_world(&[wit1, wit2], Some("foo")).is_err()); |
4568 | | assert!( |
4569 | | resolve |
4570 | | .select_world(&[wit1, wit2], Some("example:wit1/foo")) |
4571 | | .is_ok() |
4572 | | ); |
4573 | | assert!(resolve.select_world(&[wit2], None).is_ok()); |
4574 | | assert!(resolve.select_world(&[wit2], Some("foo")).is_ok()); |
4575 | | assert!( |
4576 | | resolve |
4577 | | .select_world(&[wit2], Some("example:wit1/foo")) |
4578 | | .is_ok() |
4579 | | ); |
4580 | | assert!(resolve.select_world(&[wit1, wit2], Some("bar")).is_err()); |
4581 | | assert!( |
4582 | | resolve |
4583 | | .select_world(&[wit1, wit2], Some("example:wit2/foo")) |
4584 | | .is_ok() |
4585 | | ); |
4586 | | assert!(resolve.select_world(&[wit2], Some("bar")).is_err()); |
4587 | | assert!( |
4588 | | resolve |
4589 | | .select_world(&[wit2], Some("example:wit2/foo")) |
4590 | | .is_ok() |
4591 | | ); |
4592 | | |
4593 | | Ok(()) |
4594 | | } |
4595 | | } |