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