cairo_lang_semantic/items/
structure.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::ids::{
4    LanguageElementId, LookupItemId, MemberId, MemberLongId, ModuleItemId, StructId,
5};
6use cairo_lang_diagnostics::{Diagnostics, Maybe, ToMaybe};
7use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
8use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
9use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode};
10use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
11use cairo_lang_utils::{Intern, LookupIntern};
12use smol_str::SmolStr;
13
14use super::attribute::SemanticQueryAttrs;
15use super::feature_kind::extract_item_feature_config;
16use super::generics::{GenericParamsData, semantic_generic_params};
17use super::visibility::Visibility;
18use crate::db::SemanticGroup;
19use crate::diagnostic::SemanticDiagnosticKind::*;
20use crate::diagnostic::{SemanticDiagnostics, SemanticDiagnosticsBuilder};
21use crate::expr::inference::InferenceId;
22use crate::expr::inference::canonic::ResultNoErrEx;
23use crate::resolve::{Resolver, ResolverData};
24use crate::substitution::{GenericSubstitution, SemanticRewriter};
25use crate::types::{ConcreteStructId, add_type_based_diagnostics, resolve_type};
26use crate::{GenericParam, SemanticDiagnostic, semantic};
27
28#[cfg(test)]
29#[path = "structure_test.rs"]
30mod test;
31
32// TODO(spapini): Check for bad recursive types - those that will fail in Sierra generation.
33
34// Declaration.
35#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
36#[debug_db(dyn SemanticGroup + 'static)]
37pub struct StructDeclarationData {
38    diagnostics: Diagnostics<SemanticDiagnostic>,
39    generic_params: Vec<semantic::GenericParam>,
40    attributes: Vec<Attribute>,
41    resolver_data: Arc<ResolverData>,
42}
43
44/// Query implementation of [crate::db::SemanticGroup::priv_struct_declaration_data].
45pub fn priv_struct_declaration_data(
46    db: &dyn SemanticGroup,
47    struct_id: StructId,
48) -> Maybe<StructDeclarationData> {
49    let mut diagnostics = SemanticDiagnostics::default();
50    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
51    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
52    // the item instead of all the module data.
53    // TODO(spapini): Add generic args when they are supported on structs.
54    let struct_ast = db.module_struct_by_id(struct_id)?.to_maybe()?;
55    let syntax_db = db.upcast();
56
57    // Generic params.
58    let generic_params_data = db.struct_generic_params_data(struct_id)?;
59    let generic_params = generic_params_data.generic_params;
60    let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
61        ModuleItemId::Struct(struct_id),
62    ));
63    let mut resolver = Resolver::with_data(
64        db,
65        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
66    );
67    diagnostics.extend(generic_params_data.diagnostics);
68
69    let attributes = struct_ast.attributes(syntax_db).structurize(syntax_db);
70
71    // Check fully resolved.
72    let inference = &mut resolver.inference();
73    inference.finalize(&mut diagnostics, struct_ast.stable_ptr().untyped());
74
75    let generic_params = inference.rewrite(generic_params).no_err();
76    let resolver_data = Arc::new(resolver.data);
77    Ok(StructDeclarationData {
78        diagnostics: diagnostics.build(),
79        generic_params,
80        attributes,
81        resolver_data,
82    })
83}
84
85/// Query implementation of [crate::db::SemanticGroup::struct_declaration_diagnostics].
86pub fn struct_declaration_diagnostics(
87    db: &dyn SemanticGroup,
88    struct_id: StructId,
89) -> Diagnostics<SemanticDiagnostic> {
90    db.priv_struct_declaration_data(struct_id).map(|data| data.diagnostics).unwrap_or_default()
91}
92
93/// Query implementation of [crate::db::SemanticGroup::struct_generic_params].
94pub fn struct_generic_params(
95    db: &dyn SemanticGroup,
96    struct_id: StructId,
97) -> Maybe<Vec<GenericParam>> {
98    db.struct_generic_params_data(struct_id).map(|data| data.generic_params)
99}
100
101/// Query implementation of [crate::db::SemanticGroup::struct_generic_params_data].
102pub fn struct_generic_params_data(
103    db: &dyn SemanticGroup,
104    struct_id: StructId,
105) -> Maybe<GenericParamsData> {
106    let module_file_id = struct_id.module_file_id(db.upcast());
107    let mut diagnostics = SemanticDiagnostics::default();
108    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
109    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
110    // the item instead of all the module data.
111    // TODO(spapini): Add generic args when they are supported on structs.
112    let struct_ast = db.module_struct_by_id(struct_id)?.to_maybe()?;
113    // Generic params.
114    let inference_id =
115        InferenceId::LookupItemGenerics(LookupItemId::ModuleItem(ModuleItemId::Struct(struct_id)));
116    let mut resolver = Resolver::new(db, module_file_id, inference_id);
117    resolver.set_feature_config(&struct_id, &struct_ast, &mut diagnostics);
118    let generic_params = semantic_generic_params(
119        db,
120        &mut diagnostics,
121        &mut resolver,
122        module_file_id,
123        &struct_ast.generic_params(db.upcast()),
124    );
125    let inference = &mut resolver.inference();
126    inference.finalize(&mut diagnostics, struct_ast.stable_ptr().untyped());
127
128    let generic_params = inference.rewrite(generic_params).no_err();
129    let resolver_data = Arc::new(resolver.data);
130    Ok(GenericParamsData { generic_params, diagnostics: diagnostics.build(), resolver_data })
131}
132
133/// Query implementation of [crate::db::SemanticGroup::struct_attributes].
134pub fn struct_attributes(db: &dyn SemanticGroup, struct_id: StructId) -> Maybe<Vec<Attribute>> {
135    Ok(db.priv_struct_declaration_data(struct_id)?.attributes)
136}
137
138/// Query implementation of [crate::db::SemanticGroup::struct_declaration_resolver_data].
139pub fn struct_declaration_resolver_data(
140    db: &dyn SemanticGroup,
141    struct_id: StructId,
142) -> Maybe<Arc<ResolverData>> {
143    Ok(db.priv_struct_declaration_data(struct_id)?.resolver_data)
144}
145
146// Definition.
147#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
148#[debug_db(dyn SemanticGroup + 'static)]
149pub struct StructDefinitionData {
150    diagnostics: Diagnostics<SemanticDiagnostic>,
151    members: Arc<OrderedHashMap<SmolStr, Member>>,
152    resolver_data: Arc<ResolverData>,
153}
154#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject)]
155#[debug_db(dyn SemanticGroup + 'static)]
156pub struct Member {
157    pub id: MemberId,
158    pub ty: semantic::TypeId,
159    #[dont_rewrite]
160    pub visibility: Visibility,
161}
162
163/// Query implementation of [crate::db::SemanticGroup::priv_struct_definition_data].
164pub fn priv_struct_definition_data(
165    db: &dyn SemanticGroup,
166    struct_id: StructId,
167) -> Maybe<StructDefinitionData> {
168    let defs_db = db.upcast();
169
170    let module_file_id = struct_id.module_file_id(defs_db);
171    let crate_id = module_file_id.0.owning_crate(defs_db);
172    let mut diagnostics = SemanticDiagnostics::default();
173    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
174    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
175    // the item instead of all the module data.
176    // TODO(spapini): Add generic args when they are supported on structs.
177    let struct_ast = db.module_struct_by_id(struct_id)?.to_maybe()?;
178    let syntax_db = db.upcast();
179
180    // Generic params.
181    let generic_params_data = db.struct_generic_params_data(struct_id)?;
182    let inference_id = InferenceId::LookupItemDefinition(LookupItemId::ModuleItem(
183        ModuleItemId::Struct(struct_id),
184    ));
185    let mut resolver = Resolver::with_data(
186        db,
187        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
188    );
189    diagnostics.extend(generic_params_data.diagnostics);
190
191    // Members.
192    let mut members = OrderedHashMap::default();
193    for member in struct_ast.members(syntax_db).elements(syntax_db) {
194        let feature_restore = resolver
195            .data
196            .feature_config
197            .override_with(extract_item_feature_config(db, crate_id, &member, &mut diagnostics));
198        let id = MemberLongId(module_file_id, member.stable_ptr()).intern(db);
199        let ty = resolve_type(
200            db,
201            &mut diagnostics,
202            &mut resolver,
203            &member.type_clause(syntax_db).ty(syntax_db),
204        );
205        let visibility =
206            Visibility::from_ast(syntax_db, &mut diagnostics, &member.visibility(syntax_db));
207        let member_name = member.name(syntax_db).text(syntax_db);
208        if let Some(_other_member) =
209            members.insert(member_name.clone(), Member { id, ty, visibility })
210        {
211            diagnostics.report(&member, StructMemberRedefinition { struct_id, member_name });
212        }
213        resolver.data.feature_config.restore(feature_restore);
214    }
215
216    // Check fully resolved.
217    let inference = &mut resolver.inference();
218    inference.finalize(&mut diagnostics, struct_ast.stable_ptr().untyped());
219
220    for (_, member) in members.iter_mut() {
221        member.ty = inference.rewrite(member.ty).no_err();
222    }
223
224    let resolver_data = Arc::new(resolver.data);
225    Ok(StructDefinitionData {
226        diagnostics: diagnostics.build(),
227        members: members.into(),
228        resolver_data,
229    })
230}
231
232/// Query implementation of [crate::db::SemanticGroup::struct_definition_diagnostics].
233pub fn struct_definition_diagnostics(
234    db: &dyn SemanticGroup,
235    struct_id: StructId,
236) -> Diagnostics<SemanticDiagnostic> {
237    let Ok(data) = db.priv_struct_definition_data(struct_id) else {
238        return Default::default();
239    };
240
241    let crate_id = data.resolver_data.module_file_id.0.owning_crate(db.upcast());
242
243    // If the struct is a phantom type, no need to check if its members are fully valid types, as
244    // they won't be used.
245    if db
246        .declared_phantom_type_attributes(crate_id)
247        .iter()
248        .any(|attr| struct_id.has_attr(db, attr).unwrap_or_default())
249    {
250        return data.diagnostics;
251    }
252    let mut diagnostics = SemanticDiagnostics::from(data.diagnostics);
253    for (_, member) in data.members.iter() {
254        let stable_ptr = member.id.stable_ptr(db.upcast());
255        add_type_based_diagnostics(db, &mut diagnostics, member.ty, stable_ptr);
256        if member.ty.is_phantom(db) {
257            diagnostics.report(stable_ptr, NonPhantomTypeContainingPhantomType);
258        }
259    }
260    diagnostics.build()
261}
262
263/// Query implementation of [crate::db::SemanticGroup::struct_members].
264pub fn struct_members(
265    db: &dyn SemanticGroup,
266    struct_id: StructId,
267) -> Maybe<Arc<OrderedHashMap<SmolStr, Member>>> {
268    Ok(db.priv_struct_definition_data(struct_id)?.members)
269}
270
271/// Query implementation of [crate::db::SemanticGroup::struct_definition_resolver_data].
272pub fn struct_definition_resolver_data(
273    db: &dyn SemanticGroup,
274    struct_id: StructId,
275) -> Maybe<Arc<ResolverData>> {
276    Ok(db.priv_struct_definition_data(struct_id)?.resolver_data)
277}
278
279/// Query implementation of [crate::db::SemanticGroup::concrete_struct_members].
280pub fn concrete_struct_members(
281    db: &dyn SemanticGroup,
282    concrete_struct_id: ConcreteStructId,
283) -> Maybe<Arc<OrderedHashMap<SmolStr, semantic::Member>>> {
284    // TODO(spapini): Uphold the invariant that constructed ConcreteEnumId instances
285    //   always have the correct number of generic arguments.
286    let generic_params = db.struct_generic_params(concrete_struct_id.struct_id(db))?;
287    let generic_args = concrete_struct_id.lookup_intern(db).generic_args;
288    let substitution = GenericSubstitution::new(&generic_params, &generic_args);
289
290    let generic_members = db.struct_members(concrete_struct_id.struct_id(db))?;
291    Ok(Arc::new(
292        generic_members
293            .iter()
294            .map(|(name, member)| {
295                let ty = substitution.substitute(db, member.ty)?;
296                Ok((name.clone(), semantic::Member { ty, ..member.clone() }))
297            })
298            .collect::<Maybe<_>>()?,
299    ))
300}