cairo_lang_semantic/items/
impl_alias.rs

1use std::sync::Arc;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_defs::db::DefsGroup;
5use cairo_lang_defs::ids::{
6    ImplAliasId, ImplDefId, LanguageElementId, LookupItemId, ModuleId, ModuleItemId,
7};
8use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef, skip_diagnostic};
9use cairo_lang_proc_macros::DebugWithDb;
10use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
11use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
12use cairo_lang_utils::try_extract_matches;
13use salsa::Database;
14
15use super::generics::{GenericParamsData, semantic_generic_params};
16use super::imp::ImplId;
17use crate::db::SemanticGroup;
18use crate::diagnostic::SemanticDiagnosticKind::*;
19use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder};
20use crate::expr::inference::InferenceId;
21use crate::expr::inference::canonic::ResultNoErrEx;
22use crate::resolve::{
23    ResolutionContext, ResolvedConcreteItem, ResolvedGenericItem, Resolver, ResolverData,
24};
25use crate::substitution::SemanticRewriter;
26use crate::{GenericParam, SemanticDiagnostic};
27
28#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
29#[debug_db(dyn Database)]
30pub struct ImplAliasData<'db> {
31    pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
32    pub resolved_impl: Maybe<ImplId<'db>>,
33    attributes: Vec<Attribute<'db>>,
34    pub resolver_data: Arc<ResolverData<'db>>,
35}
36
37/// Returns data about a type alias.
38#[salsa::tracked(cycle_result=impl_alias_semantic_data_cycle, returns(ref))]
39fn impl_alias_semantic_data<'db>(
40    db: &'db dyn Database,
41    impl_alias_id: ImplAliasId<'db>,
42    in_cycle: bool,
43) -> Maybe<ImplAliasData<'db>> {
44    let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id));
45    let impl_alias_ast = db.module_impl_alias_by_id(impl_alias_id)?;
46
47    let generic_params_data =
48        impl_alias_generic_params_data(db, impl_alias_id).maybe_as_ref()?.clone();
49
50    if in_cycle {
51        impl_alias_semantic_data_cycle_helper(
52            db,
53            &impl_alias_ast,
54            lookup_item_id,
55            generic_params_data,
56        )
57    } else {
58        impl_alias_semantic_data_helper(db, &impl_alias_ast, lookup_item_id, generic_params_data)
59    }
60}
61
62/// A helper function to compute the semantic data of an impl-alias item.
63pub fn impl_alias_semantic_data_helper<'db>(
64    db: &'db dyn Database,
65    impl_alias_ast: &ast::ItemImplAlias<'db>,
66    lookup_item_id: LookupItemId<'db>,
67    generic_params_data: GenericParamsData<'db>,
68) -> Maybe<ImplAliasData<'db>> {
69    let mut diagnostics = SemanticDiagnostics::default();
70    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
71    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
72    // the item instead of all the module data.
73    // TODO(spapini): Add generic args when they are supported on structs.
74    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
75    let mut resolver = Resolver::with_data(
76        db,
77        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
78    );
79    diagnostics.extend(generic_params_data.diagnostics);
80
81    let item = resolver.resolve_concrete_path(
82        &mut diagnostics,
83        &impl_alias_ast.impl_path(db),
84        NotFoundItemType::Impl,
85    );
86    let resolved_impl = item.and_then(|item| {
87        try_extract_matches!(item, ResolvedConcreteItem::Impl).ok_or_else(|| {
88            diagnostics.report(impl_alias_ast.impl_path(db).stable_ptr(db), UnknownImpl)
89        })
90    });
91
92    // Check fully resolved.
93    let inference = &mut resolver.inference();
94    inference.finalize(&mut diagnostics, impl_alias_ast.stable_ptr(db).untyped());
95
96    let resolved_impl = inference.rewrite(resolved_impl).no_err();
97
98    let attributes = impl_alias_ast.attributes(db).structurize(db);
99    let resolver_data = Arc::new(resolver.data);
100    Ok(ImplAliasData { diagnostics: diagnostics.build(), resolved_impl, attributes, resolver_data })
101}
102
103fn impl_alias_semantic_data_cycle<'db>(
104    db: &'db dyn Database,
105    impl_alias_id: ImplAliasId<'db>,
106    _in_cycle: bool,
107) -> Maybe<ImplAliasData<'db>> {
108    impl_alias_semantic_data(db, impl_alias_id, true).clone()
109}
110
111/// A helper function to compute the semantic data of an impl-alias item when a cycle is detected.
112pub fn impl_alias_semantic_data_cycle_helper<'db>(
113    db: &'db dyn Database,
114    impl_alias_ast: &ast::ItemImplAlias<'db>,
115    lookup_item_id: LookupItemId<'db>,
116    generic_params_data: GenericParamsData<'db>,
117) -> Maybe<ImplAliasData<'db>> {
118    let mut diagnostics = SemanticDiagnostics::default();
119    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
120    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
121    // the item instead of all the module data.
122    // TODO(spapini): Add generic args when they are supported on structs.
123    let err = Err(diagnostics.report(impl_alias_ast.name(db).stable_ptr(db), ImplAliasCycle));
124    diagnostics.extend(generic_params_data.diagnostics);
125    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
126    let attributes = impl_alias_ast.attributes(db).structurize(db);
127    Ok(ImplAliasData {
128        diagnostics: diagnostics.build(),
129        resolved_impl: err,
130        attributes,
131        resolver_data: (*generic_params_data.resolver_data)
132            .clone_with_inference_id(db, inference_id)
133            .into(),
134    })
135}
136
137/// Returns the generic parameters data of a type alias.
138#[salsa::tracked(returns(ref))]
139fn impl_alias_generic_params_data<'db>(
140    db: &'db dyn Database,
141    impl_alias_id: ImplAliasId<'db>,
142) -> Maybe<GenericParamsData<'db>> {
143    let module_id = impl_alias_id.module_id(db);
144    let impl_alias_ast = db.module_impl_alias_by_id(impl_alias_id)?;
145    impl_alias_generic_params_data_helper(
146        db,
147        module_id,
148        &impl_alias_ast,
149        LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)),
150        None,
151    )
152}
153
154/// Computes data about the generic parameters of an impl-alias item.
155pub fn impl_alias_generic_params_data_helper<'db>(
156    db: &'db dyn Database,
157    module_id: ModuleId<'db>,
158    impl_alias_ast: &ast::ItemImplAlias<'db>,
159    lookup_item_id: LookupItemId<'db>,
160    parent_resolver_data: Option<Arc<ResolverData<'db>>>,
161) -> Maybe<GenericParamsData<'db>> {
162    let mut diagnostics = SemanticDiagnostics::default();
163    let inference_id = InferenceId::LookupItemGenerics(lookup_item_id);
164
165    let mut resolver = match parent_resolver_data {
166        Some(parent_resolver_data) => {
167            Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
168        }
169        None => Resolver::new(db, module_id, inference_id),
170    };
171    resolver.set_feature_config(&lookup_item_id, impl_alias_ast, &mut diagnostics);
172    let generic_params = semantic_generic_params(
173        db,
174        &mut diagnostics,
175        &mut resolver,
176        module_id,
177        &impl_alias_ast.generic_params(db),
178    );
179
180    let inference = &mut resolver.inference();
181    inference.finalize(&mut diagnostics, impl_alias_ast.stable_ptr(db).untyped());
182
183    let generic_params = inference.rewrite(generic_params).no_err();
184    let resolver_data = Arc::new(resolver.data);
185    Ok(GenericParamsData { diagnostics: diagnostics.build(), generic_params, resolver_data })
186}
187
188/// Implementation of [ImplAliasSemantic::impl_alias_impl_def].
189#[salsa::tracked(cycle_result=impl_alias_impl_def_cycle)]
190fn impl_alias_impl_def<'db>(
191    db: &'db dyn Database,
192    impl_alias_id: ImplAliasId<'db>,
193) -> Maybe<ImplDefId<'db>> {
194    let module_id = impl_alias_id.module_id(db);
195    let mut diagnostics = SemanticDiagnostics::default();
196    let impl_alias_ast = db.module_impl_alias_by_id(impl_alias_id)?;
197    let inference_id = InferenceId::ImplAliasImplDef(impl_alias_id);
198
199    let mut resolver = Resolver::new(db, module_id, inference_id);
200    resolver.set_feature_config(&impl_alias_id, &impl_alias_ast, &mut diagnostics);
201
202    let impl_path_syntax = impl_alias_ast.impl_path(db);
203
204    match resolver.resolve_generic_path_with_args(
205        &mut diagnostics,
206        &impl_path_syntax,
207        NotFoundItemType::Impl,
208        ResolutionContext::Default,
209    ) {
210        Ok(ResolvedGenericItem::Impl(imp)) => Ok(imp),
211        Ok(ResolvedGenericItem::GenericImplAlias(impl_alias)) => db.impl_alias_impl_def(impl_alias),
212        // Skipping diagnostics since we will get these through when resolving in the
213        // `impl_alias_semantic_data` query.
214        _ => Err(skip_diagnostic()),
215    }
216}
217
218/// Cycle handling for [ImplAliasSemantic::impl_alias_impl_def].
219fn impl_alias_impl_def_cycle<'db>(
220    _db: &dyn Database,
221    _impl_alias_id: ImplAliasId<'db>,
222) -> Maybe<ImplDefId<'db>> {
223    // Skipping diagnostics since we will get these through when resolving in the
224    // `impl_alias_semantic_data` query.
225    Err(skip_diagnostic())
226}
227
228/// Trait for impl-alias-related semantic queries.
229pub trait ImplAliasSemantic<'db>: Database {
230    /// Returns the impl definition pointed to by the impl alias, or an error if it points to
231    /// something else.
232    fn impl_alias_impl_def(&'db self, id: ImplAliasId<'db>) -> Maybe<ImplDefId<'db>> {
233        impl_alias_impl_def(self.as_dyn_database(), id)
234    }
235    /// Returns the semantic diagnostics of a type alias.
236    fn impl_alias_semantic_diagnostics(
237        &'db self,
238        id: ImplAliasId<'db>,
239    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
240        impl_alias_semantic_data(self.as_dyn_database(), id, false)
241            .as_ref()
242            .map(|data| data.diagnostics.clone())
243            .unwrap_or_default()
244    }
245    /// Returns the resolved type of a type alias.
246    fn impl_alias_resolved_impl(&'db self, id: ImplAliasId<'db>) -> Maybe<ImplId<'db>> {
247        let db = self.as_dyn_database();
248        if let Some(data) = db.cached_crate_semantic_data(id.module_id(db).owning_crate(db)) {
249            if let Some(resolved_impl) = data.impl_aliases_resolved_impls.get(&id) {
250                return Ok(*resolved_impl);
251            } else {
252                panic!(
253                    "impl alias not found in cached impl_aliases_resolved_impls {:?}",
254                    id.debug(db)
255                );
256            }
257        };
258        impl_alias_semantic_data(self.as_dyn_database(), id, false).maybe_as_ref()?.resolved_impl
259    }
260    /// Returns the generic parameters of a type alias.
261    fn impl_alias_generic_params(&'db self, id: ImplAliasId<'db>) -> Maybe<Vec<GenericParam<'db>>> {
262        Ok(impl_alias_generic_params_data(self.as_dyn_database(), id)
263            .maybe_as_ref()?
264            .generic_params
265            .clone())
266    }
267    /// Returns the resolution resolved_items of a type alias.
268    fn impl_alias_resolver_data(&'db self, id: ImplAliasId<'db>) -> Maybe<Arc<ResolverData<'db>>> {
269        Ok(impl_alias_semantic_data(self.as_dyn_database(), id, false)
270            .maybe_as_ref()?
271            .resolver_data
272            .clone())
273    }
274    /// Returns the attributes attached to the impl alias.
275    fn impl_alias_attributes(&'db self, id: ImplAliasId<'db>) -> Maybe<&'db [Attribute<'db>]> {
276        Ok(&impl_alias_semantic_data(self.as_dyn_database(), id, false).maybe_as_ref()?.attributes)
277    }
278}
279impl<'db, T: Database + ?Sized> ImplAliasSemantic<'db> for T {}