Skip to main content

sway_core/language/ty/declaration/
struct.rs

1use crate::{
2    ast_elements::type_argument::GenericTypeArgument,
3    decl_engine::MaterializeConstGenerics,
4    engine_threading::*,
5    error::module_can_be_changed,
6    has_changes,
7    language::{
8        parsed::StructDeclaration, ty::TyDeclParsedType, CallPath, CallPathType, Visibility,
9    },
10    transform,
11    type_system::*,
12    HasChanges, Namespace,
13};
14use ast_elements::type_parameter::ConstGenericExpr;
15use monomorphization::MonomorphizeHelper;
16use serde::{Deserialize, Serialize};
17use std::{
18    cmp::Ordering,
19    hash::{Hash, Hasher},
20};
21use sway_error::handler::{ErrorEmitted, Handler};
22use sway_types::{Ident, Named, Span, Spanned};
23
24#[derive(Clone, Debug, Serialize, Deserialize)]
25pub struct TyStructDecl {
26    pub call_path: CallPath,
27    pub fields: Vec<TyStructField>,
28    pub generic_parameters: Vec<TypeParameter>,
29    pub visibility: Visibility,
30    pub span: Span,
31    pub attributes: transform::Attributes,
32}
33
34impl TyDeclParsedType for TyStructDecl {
35    type ParsedType = StructDeclaration;
36}
37
38impl Named for TyStructDecl {
39    fn name(&self) -> &Ident {
40        &self.call_path.suffix
41    }
42}
43
44impl EqWithEngines for TyStructDecl {}
45impl PartialEqWithEngines for TyStructDecl {
46    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
47        self.call_path == other.call_path
48            && self.fields.eq(&other.fields, ctx)
49            && self.generic_parameters.eq(&other.generic_parameters, ctx)
50            && self.visibility == other.visibility
51    }
52}
53
54impl HashWithEngines for TyStructDecl {
55    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
56        let TyStructDecl {
57            call_path,
58            fields,
59            generic_parameters: type_parameters,
60            visibility,
61            // these fields are not hashed because they aren't relevant/a
62            // reliable source of obj v. obj distinction
63            span: _,
64            attributes: _,
65        } = self;
66        call_path.hash(state);
67        fields.hash(state, engines);
68        type_parameters.hash(state, engines);
69        visibility.hash(state);
70    }
71}
72
73impl SubstTypes for TyStructDecl {
74    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
75        has_changes! {
76            self.fields.subst(ctx);
77            self.generic_parameters.subst(ctx);
78        }
79    }
80}
81
82impl Spanned for TyStructDecl {
83    fn span(&self) -> Span {
84        self.span.clone()
85    }
86}
87
88impl MonomorphizeHelper for TyStructDecl {
89    fn type_parameters(&self) -> &[TypeParameter] {
90        &self.generic_parameters
91    }
92
93    fn name(&self) -> &Ident {
94        &self.call_path.suffix
95    }
96
97    fn has_self_type_param(&self) -> bool {
98        false
99    }
100}
101
102impl MaterializeConstGenerics for TyStructDecl {
103    fn materialize_const_generics(
104        &mut self,
105        engines: &Engines,
106        handler: &Handler,
107        name: &str,
108        value: &crate::language::ty::TyExpression,
109    ) -> Result<HasChanges, ErrorEmitted> {
110        let mut has_changes = HasChanges::No;
111        for p in self.generic_parameters.iter_mut() {
112            match p {
113                TypeParameter::Const(p) if p.name.as_str() == name => {
114                    p.expr = Some(ConstGenericExpr::from_ty_expression(handler, value)?);
115                    has_changes = HasChanges::Yes;
116                }
117                _ => {}
118            }
119        }
120
121        for field in self.fields.iter_mut() {
122            has_changes |= field
123                .type_argument
124                .type_id
125                .materialize_const_generics(engines, handler, name, value)?;
126        }
127
128        Ok(has_changes)
129    }
130}
131
132impl TyStructDecl {
133    /// Returns names of the [TyStructField]s of the struct `self` accessible in the given context.
134    /// If `is_public_struct_access` is true, only the names of the public fields are returned, otherwise
135    /// the names of all fields.
136    /// Suitable for error reporting.
137    pub(crate) fn accessible_fields_names(&self, is_public_struct_access: bool) -> Vec<Ident> {
138        TyStructField::accessible_fields_names(&self.fields, is_public_struct_access)
139    }
140
141    /// Returns [TyStructField] with the given `field_name`, or `None` if the field with the
142    /// name `field_name` does not exist.
143    pub(crate) fn find_field(&self, field_name: &Ident) -> Option<&TyStructField> {
144        self.fields.iter().find(|field| field.name == *field_name)
145    }
146
147    /// For the given `field_name` returns the zero-based index and the type of the field
148    /// within the struct memory layout, or `None` if the field with the
149    /// name `field_name` does not exist.
150    pub(crate) fn get_field_index_and_type(&self, field_name: &Ident) -> Option<(u64, TypeId)> {
151        // TODO-MEMLAY: Warning! This implementation assumes that fields are laid out in
152        //              memory in the order of their declaration.
153        //              This assumption can be changed in the future.
154        self.fields
155            .iter()
156            .enumerate()
157            .find(|(_, field)| field.name == *field_name)
158            .map(|(idx, field)| (idx as u64, field.type_argument.type_id))
159    }
160
161    /// Returns true if the struct `self` has at least one private field.
162    pub(crate) fn has_private_fields(&self) -> bool {
163        self.fields.iter().any(|field| field.is_private())
164    }
165
166    /// Returns true if the struct `self` has fields (it is not empty)
167    /// and all fields are private.
168    pub(crate) fn has_only_private_fields(&self) -> bool {
169        !self.is_empty() && self.fields.iter().all(|field| field.is_private())
170    }
171
172    /// Returns true if the struct `self` does not have any fields.
173    pub(crate) fn is_empty(&self) -> bool {
174        self.fields.is_empty()
175    }
176}
177
178/// Provides information about the struct access within a particular [Namespace].
179pub struct StructAccessInfo {
180    /// True if the programmer who can change the code in the [Namespace]
181    /// can also change the struct declaration.
182    struct_can_be_changed: bool,
183    /// True if the struct access is public, i.e., outside of the module in
184    /// which the struct is defined.
185    is_public_struct_access: bool,
186}
187
188impl StructAccessInfo {
189    pub fn get_info(engines: &Engines, struct_decl: &TyStructDecl, namespace: &Namespace) -> Self {
190        assert!(
191            matches!(struct_decl.call_path.callpath_type, CallPathType::Full),
192            "The call path of the struct declaration must always be fully resolved."
193        );
194
195        let struct_can_be_changed =
196            module_can_be_changed(engines, namespace, &struct_decl.call_path.prefixes);
197        let is_public_struct_access =
198            !namespace.module_is_submodule_of(&struct_decl.call_path.prefixes, true);
199
200        Self {
201            struct_can_be_changed,
202            is_public_struct_access,
203        }
204    }
205}
206
207impl From<StructAccessInfo> for (bool, bool) {
208    /// Deconstructs `struct_access_info` into (`struct_can_be_changed`, `is_public_struct_access`)
209    fn from(struct_access_info: StructAccessInfo) -> (bool, bool) {
210        let StructAccessInfo {
211            struct_can_be_changed,
212            is_public_struct_access,
213        } = struct_access_info;
214        (struct_can_be_changed, is_public_struct_access)
215    }
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct TyStructField {
220    pub visibility: Visibility,
221    pub name: Ident,
222    pub span: Span,
223    pub type_argument: GenericTypeArgument,
224    pub attributes: transform::Attributes,
225}
226
227impl TyStructField {
228    pub fn is_private(&self) -> bool {
229        matches!(self.visibility, Visibility::Private)
230    }
231
232    pub fn is_public(&self) -> bool {
233        matches!(self.visibility, Visibility::Public)
234    }
235
236    /// Returns [TyStructField]s from the `fields` that are accessible in the given context.
237    /// If `is_public_struct_access` is true, only public fields are returned, otherwise
238    /// all fields.
239    pub(crate) fn accessible_fields(
240        fields: &[TyStructField],
241        is_public_struct_access: bool,
242    ) -> impl Iterator<Item = &TyStructField> {
243        fields
244            .iter()
245            .filter(move |field| !is_public_struct_access || field.is_public())
246    }
247
248    /// Returns names of the [TyStructField]s from the `fields` that are accessible in the given context.
249    /// If `is_public_struct_access` is true, only the names of the public fields are returned, otherwise
250    /// the names of all fields.
251    /// Suitable for error reporting.
252    pub(crate) fn accessible_fields_names(
253        fields: &[TyStructField],
254        is_public_struct_access: bool,
255    ) -> Vec<Ident> {
256        Self::accessible_fields(fields, is_public_struct_access)
257            .map(|field| field.name.clone())
258            .collect()
259    }
260}
261
262impl Spanned for TyStructField {
263    fn span(&self) -> Span {
264        self.span.clone()
265    }
266}
267
268impl HashWithEngines for TyStructField {
269    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
270        let TyStructField {
271            visibility,
272            name,
273            type_argument,
274            // these fields are not hashed because they aren't relevant/a
275            // reliable source of obj v. obj distinction
276            span: _,
277            attributes: _,
278        } = self;
279        visibility.hash(state);
280        name.hash(state);
281        type_argument.hash(state, engines);
282    }
283}
284
285impl EqWithEngines for TyStructField {}
286impl PartialEqWithEngines for TyStructField {
287    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
288        self.name == other.name && self.type_argument.eq(&other.type_argument, ctx)
289    }
290}
291
292impl OrdWithEngines for TyStructField {
293    fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> Ordering {
294        let TyStructField {
295            name: ln,
296            type_argument: lta,
297            // these fields are not compared because they aren't relevant for ordering
298            span: _,
299            attributes: _,
300            visibility: _,
301        } = self;
302        let TyStructField {
303            name: rn,
304            type_argument: rta,
305            // these fields are not compared because they aren't relevant for ordering
306            span: _,
307            attributes: _,
308            visibility: _,
309        } = other;
310        ln.cmp(rn).then_with(|| lta.cmp(rta, ctx))
311    }
312}
313
314impl SubstTypes for TyStructField {
315    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
316        self.type_argument.subst_inner(ctx)
317    }
318}