Skip to main content

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 an impl 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::new(lookup_item_id.parent_module(db));
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    _id: salsa::Id,
106    impl_alias_id: ImplAliasId<'db>,
107    _in_cycle: bool,
108) -> Maybe<ImplAliasData<'db>> {
109    impl_alias_semantic_data(db, impl_alias_id, true).clone()
110}
111
112/// A helper function to compute the semantic data of an impl-alias item when a cycle is detected.
113pub fn impl_alias_semantic_data_cycle_helper<'db>(
114    db: &'db dyn Database,
115    impl_alias_ast: &ast::ItemImplAlias<'db>,
116    lookup_item_id: LookupItemId<'db>,
117    generic_params_data: GenericParamsData<'db>,
118) -> Maybe<ImplAliasData<'db>> {
119    let mut diagnostics = SemanticDiagnostics::new(lookup_item_id.parent_module(db));
120    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
121    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
122    // the item instead of all the module data.
123    // TODO(spapini): Add generic args when they are supported on structs.
124    let err = Err(diagnostics.report(impl_alias_ast.name(db).stable_ptr(db), ImplAliasCycle));
125    diagnostics.extend(generic_params_data.diagnostics);
126    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
127    let attributes = impl_alias_ast.attributes(db).structurize(db);
128    Ok(ImplAliasData {
129        diagnostics: diagnostics.build(),
130        resolved_impl: err,
131        attributes,
132        resolver_data: (*generic_params_data.resolver_data)
133            .clone_with_inference_id(db, inference_id)
134            .into(),
135    })
136}
137
138/// Returns the generic parameters data of an impl alias.
139#[salsa::tracked(returns(ref))]
140fn impl_alias_generic_params_data<'db>(
141    db: &'db dyn Database,
142    impl_alias_id: ImplAliasId<'db>,
143) -> Maybe<GenericParamsData<'db>> {
144    let module_id = impl_alias_id.parent_module(db);
145    let impl_alias_ast = db.module_impl_alias_by_id(impl_alias_id)?;
146    impl_alias_generic_params_data_helper(
147        db,
148        module_id,
149        &impl_alias_ast,
150        LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)),
151        None,
152    )
153}
154
155/// Computes data about the generic parameters of an impl-alias item.
156pub fn impl_alias_generic_params_data_helper<'db>(
157    db: &'db dyn Database,
158    module_id: ModuleId<'db>,
159    impl_alias_ast: &ast::ItemImplAlias<'db>,
160    lookup_item_id: LookupItemId<'db>,
161    parent_resolver_data: Option<Arc<ResolverData<'db>>>,
162) -> Maybe<GenericParamsData<'db>> {
163    let mut diagnostics = SemanticDiagnostics::new(module_id);
164    let inference_id = InferenceId::LookupItemGenerics(lookup_item_id);
165
166    let mut resolver = match parent_resolver_data {
167        Some(parent_resolver_data) => {
168            Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
169        }
170        None => Resolver::new(db, module_id, inference_id),
171    };
172    resolver.set_feature_config(&lookup_item_id, impl_alias_ast, &mut diagnostics);
173    let generic_params = semantic_generic_params(
174        db,
175        &mut diagnostics,
176        &mut resolver,
177        module_id,
178        &impl_alias_ast.generic_params(db),
179    );
180
181    let inference = &mut resolver.inference();
182    inference.finalize(&mut diagnostics, impl_alias_ast.stable_ptr(db).untyped());
183
184    let generic_params = inference.rewrite(generic_params).no_err();
185    let resolver_data = Arc::new(resolver.data);
186    Ok(GenericParamsData { diagnostics: diagnostics.build(), generic_params, resolver_data })
187}
188
189/// Implementation of [ImplAliasSemantic::impl_alias_impl_def].
190#[salsa::tracked(cycle_result=impl_alias_impl_def_cycle)]
191fn impl_alias_impl_def<'db>(
192    db: &'db dyn Database,
193    impl_alias_id: ImplAliasId<'db>,
194) -> Maybe<ImplDefId<'db>> {
195    let module_id = impl_alias_id.parent_module(db);
196    let mut diagnostics = SemanticDiagnostics::new(module_id);
197    let impl_alias_ast = db.module_impl_alias_by_id(impl_alias_id)?;
198    let inference_id = InferenceId::ImplAliasImplDef(impl_alias_id);
199
200    let mut resolver = Resolver::new(db, module_id, inference_id);
201    resolver.set_feature_config(&impl_alias_id, &impl_alias_ast, &mut diagnostics);
202
203    let impl_path_syntax = impl_alias_ast.impl_path(db);
204
205    match resolver.resolve_generic_path_with_args(
206        &mut diagnostics,
207        &impl_path_syntax,
208        NotFoundItemType::Impl,
209        ResolutionContext::Default,
210    ) {
211        Ok(ResolvedGenericItem::Impl(imp)) => Ok(imp),
212        Ok(ResolvedGenericItem::GenericImplAlias(impl_alias)) => db.impl_alias_impl_def(impl_alias),
213        // Skipping diagnostics since we will get these through when resolving in the
214        // `impl_alias_semantic_data` query.
215        _ => Err(skip_diagnostic()),
216    }
217}
218
219/// Cycle handling for [ImplAliasSemantic::impl_alias_impl_def].
220fn impl_alias_impl_def_cycle<'db>(
221    _db: &dyn Database,
222    _id: salsa::Id,
223    _impl_alias_id: ImplAliasId<'db>,
224) -> Maybe<ImplDefId<'db>> {
225    // Skipping diagnostics since we will get these through when resolving in the
226    // `impl_alias_semantic_data` query.
227    Err(skip_diagnostic())
228}
229
230/// Trait for impl-alias-related semantic queries.
231pub trait ImplAliasSemantic<'db>: Database {
232    /// Returns the impl definition pointed to by the impl alias, or an error if it points to
233    /// something else.
234    fn impl_alias_impl_def(&'db self, id: ImplAliasId<'db>) -> Maybe<ImplDefId<'db>> {
235        impl_alias_impl_def(self.as_dyn_database(), id)
236    }
237    /// Returns the semantic diagnostics of an impl alias.
238    fn impl_alias_semantic_diagnostics(
239        &'db self,
240        id: ImplAliasId<'db>,
241    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
242        impl_alias_semantic_data(self.as_dyn_database(), id, false)
243            .as_ref()
244            .map(|data| data.diagnostics.clone())
245            .unwrap_or_default()
246    }
247    /// Returns the resolved impl of an impl alias.
248    fn impl_alias_resolved_impl(&'db self, id: ImplAliasId<'db>) -> Maybe<ImplId<'db>> {
249        let db = self.as_dyn_database();
250        if let Some(data) = db.cached_crate_semantic_data(id.parent_module(db).owning_crate(db)) {
251            if let Some(resolved_impl) = data.impl_aliases_resolved_impls.get(&id) {
252                return Ok(*resolved_impl);
253            } else {
254                panic!(
255                    "impl alias not found in cached impl_aliases_resolved_impls {:?}",
256                    id.debug(db)
257                );
258            }
259        };
260        impl_alias_semantic_data(self.as_dyn_database(), id, false).maybe_as_ref()?.resolved_impl
261    }
262    /// Returns the generic parameters of an impl alias.
263    fn impl_alias_generic_params(&'db self, id: ImplAliasId<'db>) -> Maybe<Vec<GenericParam<'db>>> {
264        Ok(impl_alias_generic_params_data(self.as_dyn_database(), id)
265            .maybe_as_ref()?
266            .generic_params
267            .clone())
268    }
269    /// Returns the resolver data of an impl alias.
270    fn impl_alias_resolver_data(&'db self, id: ImplAliasId<'db>) -> Maybe<Arc<ResolverData<'db>>> {
271        Ok(impl_alias_semantic_data(self.as_dyn_database(), id, false)
272            .maybe_as_ref()?
273            .resolver_data
274            .clone())
275    }
276    /// Returns the attributes attached to the impl alias.
277    fn impl_alias_attributes(&'db self, id: ImplAliasId<'db>) -> Maybe<&'db [Attribute<'db>]> {
278        Ok(&impl_alias_semantic_data(self.as_dyn_database(), id, false).maybe_as_ref()?.attributes)
279    }
280}
281impl<'db, T: Database + ?Sized> ImplAliasSemantic<'db> for T {}