Skip to main content

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