Skip to main content

sway_core/decl_engine/
ref.rs

1//! Represents the use of / syntactic reference to a declaration.
2//!
3//! ### Is a [DeclRef] effectively the same as a [DeclId]?
4//!
5//! A [DeclRef] is a smart wrapper around a [DeclId] and canonically represents
6//! the use / syntactic reference to a declaration. This does not include the
7//! syntactic locations for where declarations are declared though. For example,
8//! function declaration `fn my_function() { .. }` would just create a [DeclId],
9//! while function application `my_function()` would create a [DeclRef].
10//!
11//! [DeclRef] contains a [DeclId] field `id`, as well as some additional helpful
12//! information. These additional fields include an [Ident] for the declaration
13//! `name` and a [Span] for the declaration `decl_span`. Note, `name` and
14//! `decl_span` can also be found by using `id` to get the declaration itself
15//! from the [DeclEngine]. But the [DeclRef] type allows Sway compiler writers
16//! to reduce unnecessary lookups into the [DeclEngine] when only the `name` or
17//! `decl_span` is desired.
18//!
19//! It is recommend to use [DeclId] for cases like function declaration
20//! `fn my_function() { .. }`, and to use [DeclRef] for cases like function
21//! application `my_function()`.
22
23use crate::{
24    decl_engine::*,
25    engine_threading::*,
26    language::ty::{
27        self, TyAbiDecl, TyConstantDecl, TyDeclParsedType, TyEnumDecl, TyFunctionDecl,
28        TyImplSelfOrTrait, TyStorageDecl, TyStructDecl, TyTraitDecl, TyTraitFn, TyTraitType,
29    },
30    semantic_analysis::TypeCheckContext,
31    type_system::*,
32    HasChanges,
33};
34use serde::{Deserialize, Serialize};
35use std::hash::{Hash, Hasher};
36use sway_error::handler::{ErrorEmitted, Handler};
37use sway_types::{Ident, Named, Span, Spanned};
38
39pub type DeclRefFunction = DeclRef<DeclId<TyFunctionDecl>>;
40pub type DeclRefTrait = DeclRef<DeclId<TyTraitDecl>>;
41pub type DeclRefTraitFn = DeclRef<DeclId<TyTraitFn>>;
42pub type DeclRefTraitType = DeclRef<DeclId<TyTraitType>>;
43pub type DeclRefImplTrait = DeclRef<DeclId<TyImplSelfOrTrait>>;
44pub type DeclRefStruct = DeclRef<DeclId<TyStructDecl>>;
45pub type DeclRefStorage = DeclRef<DeclId<TyStorageDecl>>;
46pub type DeclRefAbi = DeclRef<DeclId<TyAbiDecl>>;
47pub type DeclRefConstant = DeclRef<DeclId<TyConstantDecl>>;
48pub type DeclRefEnum = DeclRef<DeclId<TyEnumDecl>>;
49
50pub type DeclRefMixedFunctional = DeclRef<AssociatedItemDeclId>;
51pub type DeclRefMixedInterface = DeclRef<InterfaceDeclId>;
52
53/// Represents the use of / syntactic reference to a declaration. A
54/// smart-wrapper around a [DeclId], containing additional information about a
55/// declaration.
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct DeclRef<I> {
58    /// The name of the declaration.
59    // NOTE: In the case of storage, the name is "storage".
60    name: Ident,
61
62    /// The index into the [DeclEngine].
63    id: I,
64
65    /// The [Span] of the entire declaration.
66    decl_span: Span,
67}
68
69impl<I> DeclRef<I> {
70    pub(crate) fn new(name: Ident, id: I, decl_span: Span) -> Self {
71        DeclRef {
72            name,
73            id,
74            decl_span,
75        }
76    }
77
78    pub fn name(&self) -> &Ident {
79        &self.name
80    }
81
82    pub fn id(&self) -> &I {
83        &self.id
84    }
85
86    pub fn decl_span(&self) -> &Span {
87        &self.decl_span
88    }
89}
90
91impl<T> DeclRef<DeclId<T>> {
92    pub(crate) fn replace_id(&mut self, index: DeclId<T>) {
93        self.id.replace_id(index);
94    }
95}
96
97impl<T> DeclRef<DeclId<T>>
98where
99    DeclEngine: DeclEngineIndex<T> + DeclEngineInsert<T> + DeclEngineGetParsedDeclId<T>,
100    T: Named + Spanned + IsConcrete + SubstTypes + Clone + TyDeclParsedType,
101{
102    pub(crate) fn subst_types_and_insert_new(&self, ctx: &SubstTypesContext) -> Option<Self> {
103        let decl_engine = ctx.engines.de();
104        if ctx
105            .type_subst_map
106            .is_some_and(|tsm| tsm.source_ids_contains_concrete_type(ctx.engines))
107            || !decl_engine
108                .get(&self.id)
109                .is_concrete(ctx.handler, ctx.engines)
110        {
111            let mut decl = (*decl_engine.get(&self.id)).clone();
112            if decl.subst(ctx).has_changes() {
113                Some(decl_engine.insert(decl, decl_engine.get_parsed_decl_id(&self.id).as_ref()))
114            } else {
115                None
116            }
117        } else {
118            None
119        }
120    }
121}
122
123impl<T> DeclRef<DeclId<T>>
124where
125    AssociatedItemDeclId: From<DeclId<T>>,
126{
127    pub(crate) fn with_parent(
128        self,
129        decl_engine: &DeclEngine,
130        parent: AssociatedItemDeclId,
131    ) -> Self {
132        let id: DeclId<T> = self.id;
133        decl_engine.register_parent(id.into(), parent);
134        self
135    }
136}
137
138impl<T> DeclRef<DeclId<T>>
139where
140    AssociatedItemDeclId: From<DeclId<T>>,
141    DeclEngine: DeclEngineIndex<T> + DeclEngineInsert<T> + DeclEngineGetParsedDeclId<T>,
142    T: Named + Spanned + IsConcrete + SubstTypes + Clone + TyDeclParsedType,
143{
144    pub(crate) fn subst_types_and_insert_new_with_parent(
145        &self,
146        ctx: &SubstTypesContext,
147    ) -> Option<Self> {
148        let decl_engine = ctx.engines.de();
149        let mut decl = (*decl_engine.get(&self.id)).clone();
150        if decl.subst(ctx).has_changes() {
151            Some(
152                decl_engine
153                    .insert(decl, decl_engine.get_parsed_decl_id(&self.id).as_ref())
154                    .with_parent(decl_engine, self.id.into()),
155            )
156        } else {
157            None
158        }
159    }
160}
161
162impl<T> EqWithEngines for DeclRef<DeclId<T>>
163where
164    DeclEngine: DeclEngineIndex<T>,
165    T: Named + Spanned + PartialEqWithEngines + EqWithEngines,
166{
167}
168
169impl<T> PartialEqWithEngines for DeclRef<DeclId<T>>
170where
171    DeclEngine: DeclEngineIndex<T>,
172    T: Named + Spanned + PartialEqWithEngines,
173{
174    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
175        let decl_engine = ctx.engines().de();
176        let DeclRef {
177            name: ln,
178            id: lid,
179            // these fields are not used in comparison because they aren't
180            // relevant/a reliable source of obj v. obj distinction
181            decl_span: _,
182            // temporarily omitted
183        } = self;
184        let DeclRef {
185            name: rn,
186            id: rid,
187            // these fields are not used in comparison because they aren't
188            // relevant/a reliable source of obj v. obj distinction
189            decl_span: _,
190            // temporarily omitted
191        } = other;
192        ln == rn && decl_engine.get(lid).eq(&decl_engine.get(rid), ctx)
193    }
194}
195
196impl<T> HashWithEngines for DeclRef<DeclId<T>>
197where
198    DeclEngine: DeclEngineIndex<T>,
199    T: Named + Spanned + HashWithEngines,
200{
201    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
202        let decl_engine = engines.de();
203        let DeclRef {
204            name,
205            id,
206            // these fields are not hashed because they aren't relevant/a
207            // reliable source of obj v. obj distinction
208            decl_span: _,
209        } = self;
210        name.hash(state);
211        decl_engine.get(id).hash(state, engines);
212    }
213}
214
215impl EqWithEngines for DeclRefMixedInterface {}
216impl PartialEqWithEngines for DeclRefMixedInterface {
217    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
218        let decl_engine = ctx.engines().de();
219        match (&self.id, &other.id) {
220            (InterfaceDeclId::Abi(self_id), InterfaceDeclId::Abi(other_id)) => {
221                let left = decl_engine.get(self_id);
222                let right = decl_engine.get(other_id);
223                self.name == other.name && left.eq(&right, ctx)
224            }
225            (InterfaceDeclId::Trait(self_id), InterfaceDeclId::Trait(other_id)) => {
226                let left = decl_engine.get(self_id);
227                let right = decl_engine.get(other_id);
228                self.name == other.name && left.eq(&right, ctx)
229            }
230            _ => false,
231        }
232    }
233}
234
235impl HashWithEngines for DeclRefMixedInterface {
236    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
237        match self.id {
238            InterfaceDeclId::Abi(id) => {
239                state.write_u8(0);
240                let decl_engine = engines.de();
241                let decl = decl_engine.get(&id);
242                decl.hash(state, engines);
243            }
244            InterfaceDeclId::Trait(id) => {
245                state.write_u8(1);
246                let decl_engine = engines.de();
247                let decl = decl_engine.get(&id);
248                decl.hash(state, engines);
249            }
250        }
251    }
252}
253
254impl<I> Spanned for DeclRef<I> {
255    fn span(&self) -> Span {
256        self.decl_span.clone()
257    }
258}
259
260impl<T> SubstTypes for DeclRef<DeclId<T>>
261where
262    DeclEngine: DeclEngineIndex<T>,
263    T: Named + Spanned + SubstTypes + Clone,
264{
265    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
266        let decl_engine = ctx.engines.de();
267        let mut decl = (*decl_engine.get(&self.id)).clone();
268        if decl.subst(ctx).has_changes() {
269            decl_engine.replace(self.id, decl);
270            HasChanges::Yes
271        } else {
272            HasChanges::No
273        }
274    }
275}
276
277impl ReplaceDecls for DeclRefFunction {
278    fn replace_decls_inner(
279        &mut self,
280        decl_mapping: &DeclMapping,
281        handler: &Handler,
282        ctx: &mut TypeCheckContext,
283    ) -> Result<HasChanges, ErrorEmitted> {
284        let engines = ctx.engines();
285        let decl_engine = engines.de();
286
287        let func = decl_engine.get(self);
288
289        if let Some(new_decl_ref) = decl_mapping.find_match(
290            handler,
291            ctx.engines(),
292            self.id.into(),
293            func.implementing_for,
294            ctx.self_type(),
295        )? {
296            return Ok(
297                if let AssociatedItemDeclId::Function(new_decl_ref) = new_decl_ref {
298                    self.id = new_decl_ref;
299                    HasChanges::Yes
300                } else {
301                    HasChanges::No
302                },
303            );
304        }
305        let all_parents = decl_engine.find_all_parents(engines, &self.id);
306        for parent in all_parents.iter() {
307            if let Some(new_decl_ref) = decl_mapping.find_match(
308                handler,
309                ctx.engines(),
310                parent.clone(),
311                func.implementing_for,
312                ctx.self_type(),
313            )? {
314                return Ok(
315                    if let AssociatedItemDeclId::Function(new_decl_ref) = new_decl_ref {
316                        self.id = new_decl_ref;
317                        HasChanges::Yes
318                    } else {
319                        HasChanges::No
320                    },
321                );
322            }
323        }
324        Ok(HasChanges::No)
325    }
326}
327
328impl ReplaceFunctionImplementingType for DeclRefFunction {
329    fn replace_implementing_type(&mut self, engines: &Engines, implementing_type: ty::TyDecl) {
330        let decl_engine = engines.de();
331        let mut decl = (*decl_engine.get(&self.id)).clone();
332        decl.set_implementing_type(implementing_type);
333        decl_engine.replace(self.id, decl);
334    }
335}