Skip to main content

sway_core/language/ty/declaration/
storage.rs

1use crate::{
2    engine_threading::*,
3    ir_generation::storage::get_storage_key_string,
4    language::parsed::StorageDeclaration,
5    transform::{self},
6    ty::*,
7    type_system::*,
8    Namespace,
9};
10use serde::{Deserialize, Serialize};
11use std::hash::{Hash, Hasher};
12use sway_error::{
13    error::{CompileError, StructFieldUsageContext},
14    handler::{ErrorEmitted, Handler},
15};
16use sway_types::{Ident, Named, Span, Spanned};
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct TyStorageDecl {
20    pub fields: Vec<TyStorageField>,
21    pub span: Span,
22    pub attributes: transform::Attributes,
23    pub storage_keyword: Ident,
24}
25
26impl TyDeclParsedType for TyStorageDecl {
27    type ParsedType = StorageDeclaration;
28}
29
30impl Named for TyStorageDecl {
31    fn name(&self) -> &Ident {
32        &self.storage_keyword
33    }
34}
35
36impl EqWithEngines for TyStorageDecl {}
37impl PartialEqWithEngines for TyStorageDecl {
38    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
39        self.fields.eq(&other.fields, ctx) && self.attributes == other.attributes
40    }
41}
42
43impl HashWithEngines for TyStorageDecl {
44    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
45        let TyStorageDecl {
46            fields,
47            // these fields are not hashed because they aren't relevant/a
48            // reliable source of obj v. obj distinction
49            span: _,
50            attributes: _,
51            storage_keyword: _,
52        } = self;
53        fields.hash(state, engines);
54    }
55}
56
57impl Spanned for TyStorageDecl {
58    fn span(&self) -> Span {
59        self.span.clone()
60    }
61}
62
63impl TyStorageDecl {
64    /// Given a path that consists of `fields`, where the first field is one of the storage fields,
65    /// find the type information of all the elements in the path and return it as a [TyStorageAccess].
66    ///
67    /// The first element in the `fields` must be one of the storage fields.
68    /// The last element in the `fields` can, but must not be, a struct.
69    /// All the elements in between must be structs.
70    ///
71    /// An error is returned if the above constraints are violated or if the access to the struct fields
72    /// fails. E.g, if the struct field does not exists or is an inaccessible private field.
73    ///
74    /// - `storage_fields` - all the storage fields declared in the `storage` declaration.
75    /// - `field_names` - field names in the storage access expression.
76    /// - `namespace_names` - namespace names in the storage access expression.
77    #[allow(clippy::too_many_arguments)]
78    pub fn apply_storage_access(
79        &self,
80        handler: &Handler,
81        engines: &Engines,
82        namespace: &Namespace,
83        namespace_names: &[Ident],
84        field_names: &[Ident],
85        storage_fields: &[TyStorageField],
86        storage_keyword_span: Span,
87    ) -> Result<(TyStorageAccess, TypeId), ErrorEmitted> {
88        let type_engine = engines.te();
89        let decl_engine = engines.de();
90
91        // The resulting storage access descriptors, built on the go as we move through the `fields`.
92        let mut access_descriptors = vec![];
93        // The field we've analyzed before the current field we are on, and its type id.
94        let mut previous_field: &Ident;
95        let mut previous_field_type_id: TypeId;
96
97        let (first_field, remaining_fields) = field_names.split_first().expect(
98            "Having at least one element in a storage access is guaranteed by the grammar.",
99        );
100
101        let (initial_field_type, initial_field_key, initial_field_name) =
102            match storage_fields.iter().find(|sf| {
103                &sf.name == first_field
104                    && sf.namespace_names.len() == namespace_names.len()
105                    && sf
106                        .namespace_names
107                        .iter()
108                        .zip(namespace_names.iter())
109                        .all(|(n1, n2)| n1 == n2)
110            }) {
111                Some(TyStorageField {
112                    type_argument,
113                    key_expression,
114                    name,
115                    ..
116                }) => (type_argument.type_id, key_expression, name),
117                None => {
118                    return Err(handler.emit_err(CompileError::StorageFieldDoesNotExist {
119                        field_name: first_field.into(),
120                        available_fields: storage_fields
121                            .iter()
122                            .map(|sf| (sf.namespace_names.clone(), sf.name.clone()))
123                            .collect(),
124                        storage_decl_span: self.span(),
125                    }));
126                }
127            };
128
129        access_descriptors.push(TyStorageAccessDescriptor {
130            name: first_field.clone(),
131            type_id: initial_field_type,
132            span: first_field.span(),
133        });
134
135        previous_field = first_field;
136        previous_field_type_id = initial_field_type;
137
138        // Storage cannot contain references, so there is no need for checking
139        // if the declaration is a reference to a struct. References can still
140        // be erroneously declared in the storage, and the type behind a concrete
141        // field access might be a reference to struct, but we do not treat that
142        // as a special case but just another one "not a struct".
143        // The `FieldAccessOnNonStruct` error message will explain that in the case
144        // of storage access, fields can be accessed only on structs.
145        let get_struct_decl = |type_id: TypeId| match &*type_engine.get(type_id) {
146            TypeInfo::Struct(decl_ref) => Some(decl_engine.get_struct(decl_ref)),
147            _ => None,
148        };
149
150        let mut struct_field_names = vec![];
151
152        for field in remaining_fields {
153            match get_struct_decl(previous_field_type_id) {
154                Some(struct_decl) => {
155                    let (struct_can_be_changed, is_public_struct_access) =
156                        StructAccessInfo::get_info(engines, &struct_decl, namespace).into();
157
158                    match struct_decl.find_field(field) {
159                        Some(struct_field) => {
160                            if is_public_struct_access && struct_field.is_private() {
161                                return Err(handler.emit_err(CompileError::StructFieldIsPrivate {
162                                    field_name: field.into(),
163                                    struct_name: struct_decl.call_path.suffix.clone(),
164                                    field_decl_span: struct_field.name.span(),
165                                    struct_can_be_changed,
166                                    usage_context: StructFieldUsageContext::StorageAccess,
167                                }));
168                            }
169
170                            // Everything is fine. Push the storage access descriptor and move to the next field.
171
172                            let current_field_type_id = struct_field.type_argument.type_id;
173
174                            access_descriptors.push(TyStorageAccessDescriptor {
175                                name: field.clone(),
176                                type_id: current_field_type_id,
177                                span: field.span(),
178                            });
179
180                            struct_field_names.push(field.as_str().to_string());
181
182                            previous_field = field;
183                            previous_field_type_id = current_field_type_id;
184                        }
185                        None => {
186                            // Since storage cannot be passed to other modules, the access
187                            // is always in the module of the storage declaration.
188                            // If the struct cannot be instantiated in this module at all,
189                            // we will just show the error, without any additional help lines
190                            // showing available fields or anything.
191                            // Note that if the struct is empty it can always be instantiated.
192                            let struct_can_be_instantiated =
193                                !is_public_struct_access || !struct_decl.has_private_fields();
194
195                            let available_fields = if struct_can_be_instantiated {
196                                struct_decl.accessible_fields_names(is_public_struct_access)
197                            } else {
198                                vec![]
199                            };
200
201                            return Err(handler.emit_err(CompileError::StructFieldDoesNotExist {
202                                field_name: field.into(),
203                                available_fields,
204                                is_public_struct_access,
205                                struct_name: struct_decl.call_path.suffix.clone(),
206                                struct_decl_span: struct_decl.span(),
207                                struct_is_empty: struct_decl.is_empty(),
208                                usage_context: StructFieldUsageContext::StorageAccess,
209                            }));
210                        }
211                    }
212                }
213                None => {
214                    return Err(handler.emit_err(CompileError::FieldAccessOnNonStruct {
215                        actually: engines.help_out(previous_field_type_id).to_string(),
216                        storage_variable: Some(previous_field.to_string()),
217                        field_name: field.into(),
218                        span: previous_field.span(),
219                    }))
220                }
221            };
222        }
223
224        let return_type = access_descriptors[access_descriptors.len() - 1].type_id;
225
226        Ok((
227            TyStorageAccess {
228                fields: access_descriptors,
229                key_expression: initial_field_key.clone().map(Box::new),
230                storage_field_path: namespace_names
231                    .iter()
232                    .map(|n| n.as_str().to_string())
233                    .chain(vec![initial_field_name.as_str().to_string()])
234                    .collect(),
235                struct_field_names,
236                storage_keyword_span,
237            },
238            return_type,
239        ))
240    }
241}
242
243impl Spanned for TyStorageField {
244    fn span(&self) -> Span {
245        self.span.clone()
246    }
247}
248
249#[derive(Clone, Debug, Serialize, Deserialize)]
250pub struct TyStorageField {
251    pub name: Ident,
252    pub namespace_names: Vec<Ident>,
253    pub key_expression: Option<TyExpression>,
254    pub type_argument: GenericTypeArgument,
255    pub initializer: TyExpression,
256    pub(crate) span: Span,
257    pub attributes: transform::Attributes,
258}
259
260impl TyStorageField {
261    /// Returns the [TyStorageField]'s full name, consisting
262    /// of its name preceded by its namespace names, and starting
263    /// with the `storage` keyword.
264    /// E.g., "storage::<namespace_1>::<namespace_2>.<name>".
265    pub fn full_name(&self) -> String {
266        get_storage_key_string(&self.path())
267    }
268
269    /// Returns the [TyStorageField]'s path, consisting
270    /// of its name preceded by its namespace names.
271    /// E.g., ["<namespace_1>", "<namespace_2>", "<name>"].
272    pub fn path(&self) -> Vec<String> {
273        self.namespace_names
274            .iter()
275            .map(|ns_name| ns_name.as_str().to_string())
276            .chain(std::iter::once(self.name.as_str().to_string()))
277            .collect()
278    }
279}
280
281impl EqWithEngines for TyStorageField {}
282impl PartialEqWithEngines for TyStorageField {
283    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
284        self.name == other.name
285            && self.namespace_names.eq(&other.namespace_names)
286            && self.type_argument.eq(&other.type_argument, ctx)
287            && self.initializer.eq(&other.initializer, ctx)
288    }
289}
290
291impl HashWithEngines for TyStorageField {
292    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
293        let TyStorageField {
294            name,
295            namespace_names,
296            key_expression,
297            type_argument,
298            initializer,
299            // these fields are not hashed because they aren't relevant/a
300            // reliable source of obj v. obj distinction
301            span: _,
302            attributes: _,
303        } = self;
304        name.hash(state);
305        namespace_names.hash(state);
306        key_expression.hash(state, engines);
307        type_argument.hash(state, engines);
308        initializer.hash(state, engines);
309    }
310}