cairo_lang_semantic/items/
structure.rs1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5 LanguageElementId, LookupItemId, MemberId, MemberLongId, ModuleItemId, StructId,
6};
7use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef};
8use cairo_lang_filesystem::ids::SmolStrId;
9use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
10use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
11use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode};
12use cairo_lang_utils::Intern;
13use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
14use salsa::Database;
15
16use super::attribute::SemanticQueryAttrs;
17use super::generics::{GenericParamsData, semantic_generic_params};
18use super::visibility::Visibility;
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#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
33#[debug_db(dyn Database)]
34struct StructDeclarationData<'db> {
35 diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
36 attributes: Vec<Attribute<'db>>,
37 resolver_data: Arc<ResolverData<'db>>,
38}
39
40#[salsa::tracked(returns(ref))]
42fn struct_declaration_data<'db>(
43 db: &'db dyn Database,
44 struct_id: StructId<'db>,
45) -> Maybe<StructDeclarationData<'db>> {
46 let mut diagnostics = SemanticDiagnostics::default();
47 let struct_ast = db.module_struct_by_id(struct_id)?;
52
53 let generic_params_data = struct_generic_params_data(db, struct_id).maybe_as_ref()?;
55 let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
56 ModuleItemId::Struct(struct_id),
57 ));
58 let mut resolver = Resolver::with_data(
59 db,
60 (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
61 );
62 diagnostics.extend(generic_params_data.diagnostics.clone());
63
64 let attributes = struct_ast.attributes(db).structurize(db);
65
66 let inference = &mut resolver.inference();
68 inference.finalize(&mut diagnostics, struct_ast.stable_ptr(db).untyped());
69
70 let resolver_data = Arc::new(resolver.data);
71 Ok(StructDeclarationData { diagnostics: diagnostics.build(), attributes, resolver_data })
72}
73
74#[salsa::tracked(returns(ref))]
76fn struct_generic_params_data<'db>(
77 db: &'db dyn Database,
78 struct_id: StructId<'db>,
79) -> Maybe<GenericParamsData<'db>> {
80 let module_id = struct_id.module_id(db);
81 let mut diagnostics = SemanticDiagnostics::default();
82 let struct_ast = db.module_struct_by_id(struct_id)?;
87 let inference_id =
89 InferenceId::LookupItemGenerics(LookupItemId::ModuleItem(ModuleItemId::Struct(struct_id)));
90 let mut resolver = Resolver::new(db, module_id, inference_id);
91 resolver.set_feature_config(&struct_id, &struct_ast, &mut diagnostics);
92 let generic_params = semantic_generic_params(
93 db,
94 &mut diagnostics,
95 &mut resolver,
96 module_id,
97 &struct_ast.generic_params(db),
98 );
99 let inference = &mut resolver.inference();
100 inference.finalize(&mut diagnostics, struct_ast.stable_ptr(db).untyped());
101
102 let generic_params = inference.rewrite(generic_params).no_err();
103 let resolver_data = Arc::new(resolver.data);
104 Ok(GenericParamsData { generic_params, diagnostics: diagnostics.build(), resolver_data })
105}
106
107#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
108#[debug_db(dyn Database)]
109struct StructDefinitionData<'db> {
110 diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
111 members: OrderedHashMap<SmolStrId<'db>, Member<'db>>,
112 resolver_data: Arc<ResolverData<'db>>,
113}
114
115#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, salsa::Update)]
116#[debug_db(dyn Database)]
117pub struct Member<'db> {
118 pub id: MemberId<'db>,
119 pub ty: semantic::TypeId<'db>,
120 #[dont_rewrite]
121 pub visibility: Visibility,
122}
123
124#[salsa::tracked(returns(ref))]
126fn struct_definition_data<'db>(
127 db: &'db dyn Database,
128 struct_id: StructId<'db>,
129) -> Maybe<StructDefinitionData<'db>> {
130 let module_id = struct_id.module_id(db);
131 let crate_id = module_id.owning_crate(db);
132 let mut diagnostics = SemanticDiagnostics::default();
133 let struct_ast = db.module_struct_by_id(struct_id)?;
138
139 let generic_params_data = struct_generic_params_data(db, struct_id).maybe_as_ref()?;
141 let inference_id = InferenceId::LookupItemDefinition(LookupItemId::ModuleItem(
142 ModuleItemId::Struct(struct_id),
143 ));
144 let mut resolver = Resolver::with_data(
145 db,
146 (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
147 );
148 diagnostics.extend(generic_params_data.diagnostics.clone());
149
150 let mut members = OrderedHashMap::default();
152 for member in struct_ast.members(db).elements(db) {
153 let feature_restore =
154 resolver.extend_feature_config_from_item(db, crate_id, &mut diagnostics, &member);
155 let id = MemberLongId(module_id, member.stable_ptr(db)).intern(db);
156 let ty = resolve_type(db, &mut diagnostics, &mut resolver, &member.type_clause(db).ty(db));
157 let visibility = Visibility::from_ast(db, &mut diagnostics, &member.visibility(db));
158 let member_name = member.name(db).text(db);
159 if let Some(_other_member) = members.insert(member_name, Member { id, ty, visibility }) {
160 diagnostics
161 .report(member.stable_ptr(db), StructMemberRedefinition { struct_id, member_name });
162 }
163 resolver.restore_feature_config(feature_restore);
164 }
165
166 let inference = &mut resolver.inference();
168 inference.finalize(&mut diagnostics, struct_ast.stable_ptr(db).untyped());
169
170 for (_, member) in members.iter_mut() {
171 member.ty = inference.rewrite(member.ty).no_err();
172 }
173
174 let resolver_data = Arc::new(resolver.data);
175 Ok(StructDefinitionData { diagnostics: diagnostics.build(), members, resolver_data })
176}
177
178#[salsa::tracked]
180fn struct_definition_diagnostics<'db>(
181 db: &'db dyn Database,
182 struct_id: StructId<'db>,
183) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
184 let Ok(data) = struct_definition_data(db, struct_id) else {
185 return Default::default();
186 };
187
188 let crate_id = data.resolver_data.module_id.owning_crate(db);
189
190 if db
193 .declared_phantom_type_attributes(crate_id)
194 .iter()
195 .any(|attr| struct_id.has_attr(db, attr.long(db).as_str()).unwrap_or_default())
196 {
197 return data.diagnostics.clone();
198 }
199 let mut diagnostics = SemanticDiagnostics::from(data.diagnostics.clone());
200 for (_, member) in data.members.iter() {
201 let stable_ptr = member.id.stable_ptr(db);
202 add_type_based_diagnostics(db, &mut diagnostics, member.ty, stable_ptr);
203 if member.ty.is_phantom(db) {
204 diagnostics.report(stable_ptr, NonPhantomTypeContainingPhantomType);
205 }
206 }
207 diagnostics.build()
208}
209
210#[salsa::tracked(returns(ref))]
212fn concrete_struct_members<'db>(
213 db: &'db dyn Database,
214 concrete_struct_id: ConcreteStructId<'db>,
215) -> Maybe<OrderedHashMap<SmolStrId<'db>, semantic::Member<'db>>> {
216 let generic_params = db.struct_generic_params(concrete_struct_id.struct_id(db))?;
219 let generic_args = &concrete_struct_id.long(db).generic_args;
220 let substitution = GenericSubstitution::new(generic_params, generic_args);
221
222 let generic_members = db.struct_members(concrete_struct_id.struct_id(db))?;
223 generic_members
224 .iter()
225 .map(|(name, member)| {
226 let ty = substitution.substitute(db, member.ty)?;
227 Ok((*name, semantic::Member { ty, ..member.clone() }))
228 })
229 .collect()
230}
231
232pub trait StructSemantic<'db>: Database {
234 fn struct_declaration_diagnostics(
236 &'db self,
237 struct_id: StructId<'db>,
238 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
239 let db = self.as_dyn_database();
240 struct_declaration_data(db, struct_id)
241 .as_ref()
242 .map(|data| data.diagnostics.clone())
243 .unwrap_or_default()
244 }
245 fn struct_generic_params(
247 &'db self,
248 struct_id: StructId<'db>,
249 ) -> Maybe<&'db [GenericParam<'db>]> {
250 let db = self.as_dyn_database();
251 Ok(&struct_generic_params_data(db, struct_id).maybe_as_ref()?.generic_params)
252 }
253 fn struct_attributes(&'db self, struct_id: StructId<'db>) -> Maybe<&'db [Attribute<'db>]> {
255 let db = self.as_dyn_database();
256 Ok(&struct_declaration_data(db, struct_id).maybe_as_ref()?.attributes)
257 }
258 fn struct_declaration_resolver_data(
260 &'db self,
261 struct_id: StructId<'db>,
262 ) -> Maybe<Arc<ResolverData<'db>>> {
263 let db = self.as_dyn_database();
264 Ok(struct_declaration_data(db, struct_id).maybe_as_ref()?.resolver_data.clone())
265 }
266 fn struct_definition_diagnostics(
268 &'db self,
269 struct_id: StructId<'db>,
270 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
271 struct_definition_diagnostics(self.as_dyn_database(), struct_id)
272 }
273 fn struct_members(
275 &'db self,
276 struct_id: StructId<'db>,
277 ) -> Maybe<&'db OrderedHashMap<SmolStrId<'db>, semantic::Member<'db>>> {
278 let db = self.as_dyn_database();
279 Ok(&struct_definition_data(db, struct_id).maybe_as_ref()?.members)
280 }
281 fn struct_definition_resolver_data(
283 &'db self,
284 struct_id: StructId<'db>,
285 ) -> Maybe<Arc<ResolverData<'db>>> {
286 let db = self.as_dyn_database();
287 Ok(struct_definition_data(db, struct_id).maybe_as_ref()?.resolver_data.clone())
288 }
289 fn concrete_struct_members(
291 &'db self,
292 concrete_struct_id: ConcreteStructId<'db>,
293 ) -> Maybe<&'db OrderedHashMap<SmolStrId<'db>, semantic::Member<'db>>> {
294 concrete_struct_members(self.as_dyn_database(), concrete_struct_id).maybe_as_ref()
295 }
296}
297
298impl<'db, T: Database + ?Sized> StructSemantic<'db> for T {}