sway_core/language/
call_path.rs

1use crate::{
2    engine_threading::{
3        DebugWithEngines, DisplayWithEngines, EqWithEngines, HashWithEngines, OrdWithEngines,
4        OrdWithEnginesContext, PartialEqWithEngines, PartialEqWithEnginesContext,
5    },
6    parsed::QualifiedPathType,
7    Engines, Ident, Namespace, TypeArgument,
8};
9use serde::{Deserialize, Serialize};
10use std::{
11    cmp::Ordering,
12    fmt,
13    hash::{Hash, Hasher},
14    sync::Arc,
15};
16use sway_error::{
17    error::CompileError,
18    handler::{ErrorEmitted, Handler},
19};
20use sway_types::{span::Span, Spanned};
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
23pub struct CallPathTree {
24    pub qualified_call_path: QualifiedCallPath,
25    pub children: Vec<CallPathTree>,
26}
27
28impl HashWithEngines for CallPathTree {
29    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
30        let CallPathTree {
31            qualified_call_path,
32            children,
33        } = self;
34        qualified_call_path.hash(state, engines);
35        children.hash(state, engines);
36    }
37}
38
39impl EqWithEngines for CallPathTree {}
40impl PartialEqWithEngines for CallPathTree {
41    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
42        let CallPathTree {
43            qualified_call_path,
44            children,
45        } = self;
46        qualified_call_path.eq(&other.qualified_call_path, ctx) && children.eq(&other.children, ctx)
47    }
48}
49
50impl<T: PartialEqWithEngines> EqWithEngines for Vec<T> {}
51impl<T: PartialEqWithEngines> PartialEqWithEngines for Vec<T> {
52    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
53        if self.len() != other.len() {
54            return false;
55        }
56        self.iter().zip(other.iter()).all(|(a, b)| a.eq(b, ctx))
57    }
58}
59
60impl OrdWithEngines for CallPathTree {
61    fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> Ordering {
62        let CallPathTree {
63            qualified_call_path: l_call_path,
64            children: l_children,
65        } = self;
66        let CallPathTree {
67            qualified_call_path: r_call_path,
68            children: r_children,
69        } = other;
70        l_call_path
71            .cmp(r_call_path, ctx)
72            .then_with(|| l_children.cmp(r_children, ctx))
73    }
74}
75
76#[derive(Clone, Debug, Serialize, Deserialize)]
77
78pub struct QualifiedCallPath {
79    pub call_path: CallPath,
80    pub qualified_path_root: Option<Box<QualifiedPathType>>,
81}
82
83impl std::convert::From<Ident> for QualifiedCallPath {
84    fn from(other: Ident) -> Self {
85        QualifiedCallPath {
86            call_path: CallPath {
87                prefixes: vec![],
88                suffix: other,
89                callpath_type: CallPathType::Ambiguous,
90            },
91            qualified_path_root: None,
92        }
93    }
94}
95
96impl std::convert::From<CallPath> for QualifiedCallPath {
97    fn from(other: CallPath) -> Self {
98        QualifiedCallPath {
99            call_path: other,
100            qualified_path_root: None,
101        }
102    }
103}
104
105impl QualifiedCallPath {
106    pub fn to_call_path(self, handler: &Handler) -> Result<CallPath, ErrorEmitted> {
107        if let Some(qualified_path_root) = self.qualified_path_root {
108            Err(handler.emit_err(CompileError::Internal(
109                "Unexpected qualified path.",
110                qualified_path_root.as_trait_span,
111            )))
112        } else {
113            Ok(self.call_path)
114        }
115    }
116}
117
118impl HashWithEngines for QualifiedCallPath {
119    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
120        let QualifiedCallPath {
121            call_path,
122            qualified_path_root,
123        } = self;
124        call_path.hash(state);
125        qualified_path_root.hash(state, engines);
126    }
127}
128
129impl EqWithEngines for QualifiedCallPath {}
130impl PartialEqWithEngines for QualifiedCallPath {
131    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
132        let QualifiedCallPath {
133            call_path,
134            qualified_path_root,
135        } = self;
136        PartialEqWithEngines::eq(call_path, &other.call_path, ctx)
137            && qualified_path_root.eq(&other.qualified_path_root, ctx)
138    }
139}
140
141impl OrdWithEngines for QualifiedCallPath {
142    fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> Ordering {
143        let QualifiedCallPath {
144            call_path: l_call_path,
145            qualified_path_root: l_qualified_path_root,
146        } = self;
147        let QualifiedCallPath {
148            call_path: r_call_path,
149            qualified_path_root: r_qualified_path_root,
150        } = other;
151        l_call_path
152            .cmp(r_call_path)
153            .then_with(|| l_qualified_path_root.cmp(r_qualified_path_root, ctx))
154    }
155}
156
157impl DisplayWithEngines for QualifiedCallPath {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
159        if let Some(qualified_path_root) = &self.qualified_path_root {
160            write!(
161                f,
162                "{}::{}",
163                engines.help_out(qualified_path_root),
164                &self.call_path
165            )
166        } else {
167            write!(f, "{}", &self.call_path)
168        }
169    }
170}
171
172impl DebugWithEngines for QualifiedCallPath {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
174        write!(f, "{}", engines.help_out(self))
175    }
176}
177
178#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
179pub enum CallPathType {
180    /// An unresolved path on the form `::X::Y::Z`. The path must be resolved relative to the
181    /// current package root module.
182    /// The path can be converted to a full path by prepending the package name, so if the path
183    /// `::X::Y::Z` occurs in package `A`, then the corresponding full path will be `A::X::Y::Z`.
184    RelativeToPackageRoot,
185    /// An unresolved path on the form `X::Y::Z`. The path must either be resolved relative to the
186    /// current module, in which case `X` is either a submodule or a name bound in the current
187    /// module, or as a full path, in which case `X` is the name of an external package.
188    /// If the path is resolved relative to the current module, and the current module has a module
189    /// path `A::B::C`, then the corresponding full path is `A::B::C::X::Y::Z`.
190    /// If the path is resolved as a full path, then the full path is `X::Y::Z`.
191    Ambiguous,
192    /// A full path on the form `X::Y::Z`. The first identifier `X` is the name of either the
193    /// current package or an external package.
194    /// After that comes a (possibly empty) series of names of submodules. Then comes the name of an
195    /// item (a type, a trait, a function, or something else declared in that module). Additionally,
196    /// there may be additional names such as the name of an enum variant or associated types.
197    Full,
198}
199
200/// In the expression `a::b::c()`, `a` and `b` are the prefixes and `c` is the suffix.
201/// `c` can be any type `T`, but in practice `c` is either an `Ident` or a `TypeInfo`.
202#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
203pub struct CallPath<T = Ident> {
204    pub prefixes: Vec<Ident>,
205    pub suffix: T,
206    pub callpath_type: CallPathType,
207}
208
209impl EqWithEngines for CallPath {}
210impl PartialEqWithEngines for CallPath {
211    fn eq(&self, other: &Self, _ctx: &PartialEqWithEnginesContext) -> bool {
212        self.prefixes == other.prefixes
213            && self.suffix == other.suffix
214            && self.callpath_type == other.callpath_type
215    }
216}
217
218impl<T: EqWithEngines> EqWithEngines for CallPath<T> {}
219impl<T: PartialEqWithEngines> PartialEqWithEngines for CallPath<T> {
220    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
221        self.prefixes == other.prefixes
222            && self.suffix.eq(&other.suffix, ctx)
223            && self.callpath_type == other.callpath_type
224    }
225}
226
227impl<T: OrdWithEngines> OrdWithEngines for CallPath<T> {
228    fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> Ordering {
229        self.prefixes
230            .cmp(&other.prefixes)
231            .then_with(|| self.suffix.cmp(&other.suffix, ctx))
232            .then_with(|| self.callpath_type.cmp(&other.callpath_type))
233    }
234}
235
236#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
237pub struct ResolvedCallPath<T, U = Ident> {
238    pub decl: T,
239    pub unresolved_call_path: CallPath<U>,
240}
241
242impl std::convert::From<Ident> for CallPath {
243    fn from(other: Ident) -> Self {
244        CallPath {
245            prefixes: vec![],
246            suffix: other,
247            callpath_type: CallPathType::Ambiguous,
248        }
249    }
250}
251
252impl<T> fmt::Display for CallPath<T>
253where
254    T: fmt::Display,
255{
256    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
257        for prefix in self.prefixes.iter() {
258            write!(f, "{}::", prefix.as_str())?;
259        }
260        write!(f, "{}", &self.suffix)
261    }
262}
263
264impl<T: DisplayWithEngines> DisplayWithEngines for CallPath<T> {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
266        for prefix in self.prefixes.iter() {
267            write!(f, "{}::", prefix.as_str())?;
268        }
269        write!(f, "{}", engines.help_out(&self.suffix))
270    }
271}
272
273impl<T: DisplayWithEngines> DebugWithEngines for CallPath<T> {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
275        for prefix in self.prefixes.iter() {
276            write!(f, "{}::", prefix.as_str())?;
277        }
278        write!(f, "{}", engines.help_out(&self.suffix))
279    }
280}
281
282impl<T: Spanned> Spanned for CallPath<T> {
283    fn span(&self) -> Span {
284        if self.prefixes.is_empty() {
285            self.suffix.span()
286        } else {
287            let suffix_span = self.suffix.span();
288            let mut prefixes_spans = self
289                .prefixes
290                .iter()
291                .map(|x| x.span())
292                // Depending on how the call path is constructed, we
293                // might have a situation that the parts do not belong
294                // to the same source and do not have the same source id.
295                // In that case, we will take only the suffix' span, as
296                // the span for the whole call path. Otherwise, we join
297                // the spans of all the parts.
298                .filter(|x| {
299                    Arc::ptr_eq(x.src(), suffix_span.src())
300                        && x.source_id() == suffix_span.source_id()
301                })
302                .peekable();
303            if prefixes_spans.peek().is_some() {
304                Span::join(Span::join_all(prefixes_spans), &suffix_span)
305            } else {
306                suffix_span
307            }
308        }
309    }
310}
311
312/// This controls the type of display type for call path display string conversions.
313pub enum CallPathDisplayType {
314    /// Prints the regular call path as exists internally.
315    Regular,
316    /// Strips the current root package if it exists as prefix.
317    StripPackagePrefix,
318}
319
320impl CallPath {
321    pub fn fullpath(path: &[&str]) -> Self {
322        assert!(!path.is_empty());
323
324        CallPath {
325            prefixes: path
326                .iter()
327                .take(path.len() - 1)
328                .map(|&x| Ident::new_no_span(x.into()))
329                .collect(),
330            suffix: path.last().map(|&x| Ident::new_no_span(x.into())).unwrap(),
331            callpath_type: CallPathType::Full,
332        }
333    }
334
335    /// Shifts the last prefix into the suffix, and removes the old suffix.
336    /// Does nothing if prefixes are empty, or if the path is a full path and there is only a single prefix (which must be the package name, which is obligatory for full paths)
337    pub fn rshift(&self) -> CallPath {
338        if self.prefixes.is_empty()
339            || (matches!(self.callpath_type, CallPathType::Full) && self.prefixes.len() == 1)
340        {
341            self.clone()
342        } else {
343            CallPath {
344                prefixes: self.prefixes[0..self.prefixes.len() - 1].to_vec(),
345                suffix: self.prefixes.last().unwrap().clone(),
346                callpath_type: self.callpath_type,
347            }
348        }
349    }
350
351    /// Removes the first prefix. Does nothing if prefixes are empty.
352    pub fn lshift(&self) -> CallPath {
353        if self.prefixes.is_empty() {
354            self.clone()
355        } else {
356            let new_callpath_type = match self.callpath_type {
357                CallPathType::RelativeToPackageRoot | CallPathType::Ambiguous => {
358                    CallPathType::Ambiguous
359                }
360                CallPathType::Full => CallPathType::RelativeToPackageRoot,
361            };
362            CallPath {
363                prefixes: self.prefixes[1..self.prefixes.len()].to_vec(),
364                suffix: self.suffix.clone(),
365                callpath_type: new_callpath_type,
366            }
367        }
368    }
369
370    pub fn as_vec_string(&self) -> Vec<String> {
371        self.prefixes
372            .iter()
373            .map(|p| p.to_string())
374            .chain(std::iter::once(self.suffix.to_string()))
375            .collect::<Vec<_>>()
376    }
377
378    /// Create a full [CallPath] from a given [Ident] and the [Namespace] in which the [Ident] is
379    /// declared.
380    ///
381    /// This function is intended to be used while typechecking the identifier declaration, i.e.,
382    /// before the identifier is added to the environment.
383    pub fn ident_to_fullpath(suffix: Ident, namespace: &Namespace) -> CallPath {
384        let mut res: Self = suffix.clone().into();
385        for mod_path in namespace.current_mod_path() {
386            res.prefixes.push(mod_path.clone())
387        }
388        res.callpath_type = CallPathType::Full;
389        res
390    }
391
392    /// Convert a given [CallPath] into a call path suitable for a `use` statement.
393    ///
394    /// For example, given a path `pkga::SOME_CONST` where `pkga` is an _internal_ library of a package named
395    /// `my_project`, the corresponding call path is `pkga::SOME_CONST`.
396    ///
397    /// Paths to _external_ libraries such `std::lib1::lib2::my_obj` are left unchanged.
398    pub fn to_import_path(&self, engines: &Engines, namespace: &Namespace) -> CallPath {
399        let converted = self.to_fullpath(engines, namespace);
400
401        if let Some(first) = converted.prefixes.first() {
402            if namespace.current_package_name() == first {
403                return converted.lshift();
404            }
405        }
406        converted
407    }
408
409    pub fn to_display_path(
410        &self,
411        display_type: CallPathDisplayType,
412        namespace: &Namespace,
413    ) -> CallPath {
414        let mut display_path = self.clone();
415
416        match display_type {
417            CallPathDisplayType::Regular => {}
418            CallPathDisplayType::StripPackagePrefix => {
419                if let Some(first) = self.prefixes.first() {
420                    if namespace.root_ref().current_package_root_module().name() == first {
421                        display_path = display_path.lshift();
422                    }
423                }
424            }
425        };
426
427        display_path
428    }
429
430    /// Create a string form of the given [CallPath] and zero or more [TypeArgument]s.
431    /// The returned string is convenient for displaying full names, including generic arguments, in help messages.
432    /// E.g.:
433    /// - `some::module::SomeType`
434    /// - `some::module::SomeGenericType<T, u64>`
435    ///
436    /// Note that the trailing arguments are never separated by `::` from the suffix.
437    pub(crate) fn to_string_with_args(&self, engines: &Engines, args: &[TypeArgument]) -> String {
438        let args = args
439            .iter()
440            .map(|type_arg| engines.help_out(type_arg).to_string())
441            .collect::<Vec<_>>()
442            .join(", ");
443
444        format!(
445            "{}{}",
446            // TODO: Replace with a context aware string representation of the path
447            //       once https://github.com/FuelLabs/sway/issues/6873 is fixed.
448            &self,
449            if args.is_empty() {
450                String::new()
451            } else {
452                format!("<{args}>")
453            }
454        )
455    }
456}
457
458impl<T: Clone> CallPath<T> {
459    /// Convert a given [CallPath] to a symbol to a full [CallPath] to a program point in which the
460    /// symbol can be resolved (assuming the given [CallPath] is a legal Sway path).
461    ///
462    /// The resulting [CallPath] is not guaranteed to be located in the package where the symbol is
463    /// declared. To obtain the path to the declaration, use [to_canonical_path].
464    ///
465    /// The [CallPath] is converted within the current module of the supplied namespace.
466    ///
467    /// For example, given a path `pkga::SOME_CONST` where `pkga` is an _internal_ module of a
468    /// package named `my_project`, the corresponding call path is
469    /// `my_project::pkga::SOME_CONST`. This does not imply that `SOME_CONST` is declared in the
470    /// `my_project::pkga`, but only that the name `SOME_CONST` is bound in `my_project::pkga`.
471    ///
472    /// Paths to _external_ libraries such `std::lib1::lib2::my_obj` are considered full already
473    /// and are left unchanged since `std` is a root of the package `std`.
474    pub fn to_fullpath(&self, engines: &Engines, namespace: &Namespace) -> CallPath<T> {
475        self.to_fullpath_from_mod_path(engines, namespace, namespace.current_mod_path())
476    }
477
478    /// Convert a given [CallPath] to a symbol to a full [CallPath] to a program point in which the
479    /// symbol can be resolved (assuming the given [CallPath] is a legal Sway path).
480    ///
481    /// The resulting [CallPath] is not guaranteed to be located in the package where the symbol is
482    /// declared. To obtain the path to the declaration, use [to_canonical_path].
483    ///
484    /// The [CallPath] is converted within the module given by `mod_path`, which must be a legal
485    /// path to a module.
486    ///
487    /// For example, given a path `pkga::SOME_CONST` where `pkga` is an _internal_ module of a
488    /// package named `my_project`, the corresponding call path is
489    /// `my_project::pkga::SOME_CONST`. This does not imply that `SOME_CONST` is declared in the
490    /// `my_project::pkga`, but only that the name `SOME_CONST` is bound in `my_project::pkga`.
491    ///
492    /// Paths to _external_ libraries such `std::lib1::lib2::my_obj` are considered full already
493    /// and are left unchanged since `std` is a root of the package `std`.
494    pub fn to_fullpath_from_mod_path(
495        &self,
496        engines: &Engines,
497        namespace: &Namespace,
498        mod_path: &Vec<Ident>,
499    ) -> CallPath<T> {
500        let mod_path_module = namespace.module_from_absolute_path(mod_path);
501
502        match self.callpath_type {
503            CallPathType::Full => self.clone(),
504            CallPathType::RelativeToPackageRoot => {
505                let mut prefixes = vec![mod_path[0].clone()];
506                for ident in self.prefixes.iter() {
507                    prefixes.push(ident.clone());
508                }
509                Self {
510                    prefixes,
511                    suffix: self.suffix.clone(),
512                    callpath_type: CallPathType::Full,
513                }
514            }
515            CallPathType::Ambiguous => {
516                if self.prefixes.is_empty() {
517                    // 		    // Given a path to a symbol that has no prefixes, discover the path to the symbol as a
518                    // 		    // combination of the package name in which the symbol is defined and the path to the
519                    // 		    // current submodule.
520                    CallPath {
521                        prefixes: mod_path.clone(),
522                        suffix: self.suffix.clone(),
523                        callpath_type: CallPathType::Full,
524                    }
525                } else if mod_path_module.is_some()
526                    && (mod_path_module.unwrap().has_submodule(&self.prefixes[0])
527                        || namespace.module_has_binding(engines, mod_path, &self.prefixes[0]))
528                {
529                    // The first identifier in the prefix is a submodule of the current
530                    // module.
531                    //
532                    // The path is a qualified path relative to the current module
533                    //
534                    // Complete the path by prepending the package name and the path to the current module.
535                    CallPath {
536                        prefixes: mod_path.iter().chain(&self.prefixes).cloned().collect(),
537                        suffix: self.suffix.clone(),
538                        callpath_type: CallPathType::Full,
539                    }
540                } else if namespace.package_exists(&self.prefixes[0])
541                    && namespace.module_is_external(&self.prefixes)
542                {
543                    // The first identifier refers to an external package. The path is already fully qualified.
544                    CallPath {
545                        prefixes: self.prefixes.clone(),
546                        suffix: self.suffix.clone(),
547                        callpath_type: CallPathType::Full,
548                    }
549                } else {
550                    // The first identifier in the prefix is neither a submodule of the current module nor the name of an external package.
551                    // This is probably an illegal path, so let it fail by assuming it is bound in the current module.
552                    CallPath {
553                        prefixes: mod_path.iter().chain(&self.prefixes).cloned().collect(),
554                        suffix: self.suffix.clone(),
555                        callpath_type: CallPathType::Full,
556                    }
557                }
558            }
559        }
560    }
561}
562
563impl CallPath {
564    /// Convert a given [CallPath] to a symbol to a full [CallPath] to where the symbol is declared
565    /// (assuming the given [CallPath] is a legal Sway path).
566    ///
567    /// The [CallPath] is converted within the current module of the supplied namespace.
568    ///
569    /// For example, given a path `pkga::SOME_CONST` where `pkga` is an _internal_ module of a
570    /// package named `my_project`, and `SOME_CONST` is bound in the module `my_project::pkga`, then
571    /// the corresponding call path is the full callpath to the declaration that `SOME_CONST` is
572    /// bound to. This does not imply that `SOME_CONST` is declared in the `my_project::pkga`, since
573    /// the binding may be the result of an import.
574    ///
575    /// Paths to _external_ libraries such `std::lib1::lib2::my_obj` are considered full already
576    /// and are left unchanged since `std` is a root of the package `std`.
577    pub fn to_canonical_path(&self, engines: &Engines, namespace: &Namespace) -> CallPath {
578        // Generate a full path to a module where the suffix can be resolved
579        let full_path = self.to_fullpath(engines, namespace);
580
581        match namespace.module_from_absolute_path(&full_path.prefixes) {
582            Some(module) => {
583                // Resolve the path suffix in the found module
584                match module.resolve_symbol(&Handler::default(), engines, &full_path.suffix) {
585                    Ok((decl, decl_path)) => {
586                        let name = decl.expect_typed().get_name(engines);
587                        let suffix = if name.as_str() != full_path.suffix.as_str() {
588                            name
589                        } else {
590                            full_path.suffix
591                        };
592                        // Replace the resolvable path with the declaration's path
593                        CallPath {
594                            prefixes: decl_path,
595                            suffix,
596                            callpath_type: full_path.callpath_type,
597                        }
598                    }
599                    Err(_) => {
600                        // The symbol does not resolve. The symbol isn't bound, so the best bet is
601                        // the full path.
602                        full_path
603                    }
604                }
605            }
606            None => {
607                // The resolvable module doesn't exist. The symbol probably doesn't exist, so
608                // the best bet is the full path.
609                full_path
610            }
611        }
612    }
613}