cairo_lang_semantic/expr/
compute.rs

1//! This module is responsible of computing the semantic model of expressions and statements in
2//! the code, while type checking.
3//! It is invoked by queries for function bodies and other code blocks.
4
5use core::panic;
6use std::ops::Deref;
7use std::sync::Arc;
8
9use ast::PathSegment;
10use cairo_lang_debug::DebugWithDb;
11use cairo_lang_defs::db::{DefsGroup, get_all_path_leaves, validate_attributes_flat};
12use cairo_lang_defs::diagnostic_utils::StableLocation;
13use cairo_lang_defs::ids::{
14    FunctionTitleId, GenericKind, LanguageElementId, LocalVarLongId, LookupItemId, MemberId,
15    ModuleId, ModuleItemId, NamedLanguageElementId, StatementConstLongId, StatementItemId,
16    StatementUseLongId, TraitFunctionId, TraitId, VarId,
17};
18use cairo_lang_defs::plugin::{InlineMacroExprPlugin, MacroPluginMetadata};
19use cairo_lang_diagnostics::{DiagnosticNote, Maybe, skip_diagnostic};
20use cairo_lang_filesystem::cfg::CfgSet;
21use cairo_lang_filesystem::db::FilesGroup;
22use cairo_lang_filesystem::ids::{
23    CodeMapping, CodeOrigin, FileKind, FileLongId, SmolStrId, VirtualFile,
24};
25use cairo_lang_filesystem::span::TextOffset;
26use cairo_lang_parser::db::ParserGroup;
27use cairo_lang_proc_macros::DebugWithDb;
28use cairo_lang_syntax::attribute::consts::UNUSED_VARIABLES;
29use cairo_lang_syntax::node::ast::{
30    BinaryOperator, BlockOrIf, ClosureParamWrapper, ConditionListAnd, ExprPtr,
31    OptionReturnTypeClause, PatternListOr, PatternStructParam, TerminalIdentifier, UnaryOperator,
32};
33use cairo_lang_syntax::node::helpers::{GetIdentifier, PathSegmentEx, QueryAttrs};
34use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
35use cairo_lang_syntax::node::kind::SyntaxKind;
36use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
37use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
38use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
39use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
40use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
41use cairo_lang_utils::{
42    self as utils, Intern, OptionHelper, extract_matches, require, try_extract_matches,
43};
44use itertools::{Itertools, chain, zip_eq};
45use num_bigint::BigInt;
46use num_traits::ToPrimitive;
47use salsa::Database;
48
49use super::inference::canonic::ResultNoErrEx;
50use super::inference::conform::InferenceConform;
51use super::inference::infers::InferenceEmbeddings;
52use super::inference::{Inference, InferenceData, InferenceError};
53use super::objects::*;
54use super::pattern::{
55    Pattern, PatternEnumVariant, PatternFixedSizeArray, PatternLiteral, PatternMissing,
56    PatternOtherwise, PatternTuple, PatternVariable,
57};
58use crate::corelib::{
59    CorelibSemantic, core_binary_operator, core_bool_ty, core_unary_operator, false_literal_expr,
60    get_usize_ty, never_ty, true_literal_expr, try_get_core_ty_by_name, unit_expr, unit_ty,
61    unwrap_error_propagation_type, validate_literal,
62};
63use crate::diagnostic::SemanticDiagnosticKind::{self, *};
64use crate::diagnostic::{
65    ElementKind, MultiArmExprKind, NotFoundItemType, SemanticDiagnostics,
66    SemanticDiagnosticsBuilder, TraitInferenceErrors, UnsupportedOutsideOfFunctionFeatureName,
67};
68use crate::expr::inference::solver::SolutionSet;
69use crate::expr::inference::{ImplVarTraitItemMappings, InferenceId};
70use crate::items::constant::{
71    ConstValue, ConstantSemantic, resolve_const_expr_and_evaluate, validate_const_expr,
72};
73use crate::items::enm::{EnumSemantic, SemanticEnumEx};
74use crate::items::feature_kind::{FeatureConfig, FeatureConfigRestore};
75use crate::items::functions::{FunctionsSemantic, function_signature_params};
76use crate::items::generics::GenericParamSemantic;
77use crate::items::imp::{
78    DerefInfo, ImplLookupContextId, ImplSemantic, filter_candidate_traits, infer_impl_by_self,
79};
80use crate::items::macro_declaration::{
81    MacroDeclarationSemantic, MatcherContext, expand_macro_rule, is_macro_rule_match,
82};
83use crate::items::modifiers::compute_mutability;
84use crate::items::module::ModuleSemantic;
85use crate::items::structure::StructSemantic;
86use crate::items::trt::TraitSemantic;
87use crate::items::visibility;
88use crate::keyword::MACRO_CALL_SITE;
89use crate::lsp_helpers::LspHelpers;
90use crate::resolve::{
91    AsSegments, EnrichedMembers, EnrichedTypeMemberAccess, ResolutionContext, ResolvedConcreteItem,
92    ResolvedGenericItem, Resolver, ResolverMacroData,
93};
94use crate::semantic::{self, Binding, FunctionId, LocalVariable, TypeId, TypeLongId};
95use crate::substitution::SemanticRewriter;
96use crate::types::{
97    ClosureTypeLongId, ConcreteTypeId, add_type_based_diagnostics, are_coupons_enabled,
98    extract_fixed_size_array_size, peel_snapshots, peel_snapshots_ex, resolve_type_ex,
99    verify_fixed_size_array_size, wrap_in_snapshots,
100};
101use crate::usage::Usages;
102use crate::{
103    ConcreteEnumId, ConcreteVariant, GenericArgumentId, GenericParam, LocalItem, Member,
104    Mutability, Parameter, PatternStringLiteral, PatternStruct, Signature, StatementItemKind,
105};
106
107/// The information of a macro expansion.
108#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
109struct MacroExpansionInfo<'db> {
110    /// The code mappings for this expansion.
111    mappings: Arc<[CodeMapping]>,
112    /// The kind of macro the expansion is from.
113    kind: MacroKind,
114    /// The variables that should be exposed to the parent scope after the macro expansion.
115    /// This is used for unhygienic macros.
116    vars_to_expose: Vec<(SmolStrId<'db>, Binding<'db>)>,
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub struct ExpansionOffset(TextOffset);
121impl ExpansionOffset {
122    /// Creates a new origin offset.
123    pub fn new(offset: TextOffset) -> Self {
124        Self(offset)
125    }
126    /// Returns the origin of the position that was expanded at the given offset, if any.
127    pub fn mapped(self, mappings: &[CodeMapping]) -> Option<Self> {
128        let mapping = mappings
129            .iter()
130            .find(|mapping| mapping.span.start <= self.0 && self.0 <= mapping.span.end)?;
131        Some(Self::new(match mapping.origin {
132            CodeOrigin::Start(offset) => offset.add_width(self.0 - mapping.span.start),
133            CodeOrigin::Span(span) | CodeOrigin::CallSite(span) => span.start,
134        }))
135    }
136}
137
138/// Describes the origin and hygiene behavior of a macro expansion.
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum MacroKind {
141    /// A user-defined macro, expanded with standard hygiene.
142    UserDefined,
143    /// A plugin macro.
144    Plugin,
145    /// An unhygienic macro, whose variables are injected into the parent scope.
146    Unhygienic,
147}
148
149/// Expression with its id.
150#[derive(Debug, Clone)]
151pub struct ExprAndId<'db> {
152    pub expr: Expr<'db>,
153    pub id: ExprId,
154}
155impl<'db> Deref for ExprAndId<'db> {
156    type Target = Expr<'db>;
157
158    fn deref(&self) -> &Self::Target {
159        &self.expr
160    }
161}
162
163#[derive(Debug, Clone)]
164pub struct PatternAndId<'db> {
165    pub pattern: Pattern<'db>,
166    pub id: PatternId,
167}
168impl<'db> Deref for PatternAndId<'db> {
169    type Target = Pattern<'db>;
170
171    fn deref(&self) -> &Self::Target {
172        &self.pattern
173    }
174}
175
176/// Named argument in a function call.
177#[derive(Debug, Clone)]
178pub struct NamedArg<'db>(ExprAndId<'db>, Option<ast::TerminalIdentifier<'db>>, Mutability);
179
180pub enum ContextFunction<'db> {
181    Global,
182    Function(Maybe<FunctionId<'db>>),
183}
184
185/// Context inside loops or closures.
186#[derive(Debug, Clone)]
187struct InnerContext<'db> {
188    /// The return type in the current context.
189    return_type: TypeId<'db>,
190    /// The kind of inner context.
191    kind: InnerContextKind<'db>,
192}
193
194/// Kinds of inner context.
195#[derive(Debug, Clone)]
196enum InnerContextKind<'db> {
197    /// Context inside a `loop`
198    Loop { type_merger: FlowMergeTypeHelper<'db> },
199    /// Context inside a `while` loop
200    While,
201    /// Context inside a `for` loop
202    For,
203    /// Context inside a `closure`
204    Closure,
205}
206
207/// The result of expanding an inline macro.
208#[derive(Debug, Clone)]
209struct InlineMacroExpansion<'db> {
210    pub content: Arc<str>,
211    pub name: String,
212    pub info: MacroExpansionInfo<'db>,
213}
214
215/// Context for computing the semantic model of expression trees.
216pub struct ComputationContext<'ctx, 'mt> {
217    pub db: &'ctx dyn Database,
218    pub diagnostics: &'mt mut SemanticDiagnostics<'ctx>,
219    pub resolver: &'mt mut Resolver<'ctx>,
220    /// Tracks variable definitions and their mutability state.
221    pub variable_tracker: VariableTracker<'ctx>,
222    signature: Option<&'mt Signature<'ctx>>,
223    environment: Box<Environment<'ctx>>,
224    /// Arenas of semantic objects.
225    pub arenas: Arenas<'ctx>,
226    function_id: ContextFunction<'ctx>,
227    inner_ctx: Option<InnerContext<'ctx>>,
228    cfg_set: Arc<CfgSet>,
229    /// Whether to look for closures when calling variables.
230    /// TODO(TomerStarkware): Remove this once we disallow calling shadowed functions.
231    are_closures_in_context: bool,
232    /// Whether variables defined in the current macro scope should be injected into the parent
233    /// scope.
234    macro_defined_var_unhygienic: bool,
235}
236impl<'ctx, 'mt> ComputationContext<'ctx, 'mt> {
237    /// Creates a new computation context.
238    pub fn new(
239        db: &'ctx dyn Database,
240        diagnostics: &'mt mut SemanticDiagnostics<'ctx>,
241        resolver: &'mt mut Resolver<'ctx>,
242        signature: Option<&'mt Signature<'ctx>>,
243        environment: Environment<'ctx>,
244        function_id: ContextFunction<'ctx>,
245    ) -> Self {
246        let cfg_set =
247            Arc::new(resolver.settings.cfg_set.as_ref().unwrap_or_else(|| db.cfg_set()).clone());
248        let mut variable_tracker = VariableTracker::default();
249        variable_tracker.extend_from_environment(&environment);
250        Self {
251            db,
252            diagnostics,
253            resolver,
254            signature,
255            environment: Box::new(environment),
256            arenas: Default::default(),
257            function_id,
258            variable_tracker,
259            inner_ctx: None,
260            cfg_set,
261            are_closures_in_context: false,
262            macro_defined_var_unhygienic: false,
263        }
264    }
265
266    /// Creates a new computation context for a global scope.
267    pub fn new_global(
268        db: &'ctx dyn Database,
269        diagnostics: &'mt mut SemanticDiagnostics<'ctx>,
270        resolver: &'mt mut Resolver<'ctx>,
271    ) -> Self {
272        Self::new(db, diagnostics, resolver, None, Environment::empty(), ContextFunction::Global)
273    }
274
275    /// Inserts a variable into both environment and variable tracker.
276    /// Returns the old value if the variable was already defined.
277    pub fn insert_variable(
278        &mut self,
279        name: SmolStrId<'ctx>,
280        var_def: Binding<'ctx>,
281    ) -> Option<Binding<'ctx>> {
282        // Ignore re-definitions in the variable tracker, diagnostics will be issued by the caller
283        // using this method's return value.
284        let _ = self.variable_tracker.insert(var_def.clone());
285        self.environment.variables.insert(name, var_def)
286    }
287
288    /// Runs a function with a modified context, with a new environment for a subscope.
289    /// This environment holds no variables of its own, but points to the current environment as a
290    /// parent. Used for blocks of code that introduce a new scope like function bodies, if
291    /// blocks, loops, etc.
292    fn run_in_subscope<T, F>(&mut self, f: F) -> T
293    where
294        F: FnOnce(&mut Self) -> T,
295    {
296        self.run_in_subscope_ex(f, None)
297    }
298
299    /// Similar to run_in_subscope, but for macro expanded code. It creates a new environment
300    /// that points to the current environment as a parent, and also contains the macro expansion
301    /// data. When looking up variables, we will get out of the macro expansion environment if
302    /// and only if the text was originated from expanding a placeholder.
303    fn run_in_macro_subscope<T, F>(
304        &mut self,
305        operation: F,
306        macro_info: MacroExpansionInfo<'ctx>,
307    ) -> T
308    where
309        F: FnOnce(&mut Self) -> T,
310    {
311        let prev_macro_hygiene_kind = self.macro_defined_var_unhygienic;
312        let prev_default_module_allowed = self.resolver.default_module_allowed;
313        match macro_info.kind {
314            MacroKind::Unhygienic => {
315                self.macro_defined_var_unhygienic = true;
316            }
317            MacroKind::Plugin => {
318                self.resolver.set_default_module_allowed(true);
319            }
320            MacroKind::UserDefined => {}
321        }
322        let result = self.run_in_subscope_ex(operation, Some(macro_info));
323        self.macro_defined_var_unhygienic = prev_macro_hygiene_kind;
324        self.resolver.set_default_module_allowed(prev_default_module_allowed);
325        result
326    }
327    /// Runs a function with a modified context, with a new environment for a subscope.
328    /// Shouldn't be called directly, use [Self::run_in_subscope] or [Self::run_in_macro_subscope]
329    /// instead.
330    fn run_in_subscope_ex<T, F>(&mut self, f: F, macro_info: Option<MacroExpansionInfo<'ctx>>) -> T
331    where
332        F: FnOnce(&mut Self) -> T,
333    {
334        // Push an environment to the stack.
335        let parent = std::mem::replace(&mut self.environment, Environment::empty().into());
336        self.environment.parent = Some(parent);
337        self.environment.macro_info = macro_info;
338        let res = f(self);
339
340        // Pop the environment from the stack.
341        let parent = self.environment.parent.take().unwrap();
342        let mut closed = std::mem::replace(&mut self.environment, parent);
343        let parent = &mut self.environment;
344        if let Some(macro_info) = closed.macro_info {
345            for (name, binding) in macro_info.vars_to_expose {
346                // Exposed variables are considered moved in the closed scope, as they still may be
347                // used later.
348                if !closed.used_variables.insert(binding.id()) {
349                    // In case a variable was already marked as used in the closed environment, it
350                    // means it was actually used, and is marked as used in the environment it is
351                    // moved to.
352                    parent.used_variables.insert(binding.id());
353                }
354                if let Some(old_var) = parent.variables.insert(name, binding.clone()) {
355                    add_unused_binding_warning(
356                        self.diagnostics,
357                        self.db,
358                        &parent.used_variables,
359                        name,
360                        &old_var,
361                        &self.resolver.data.feature_config,
362                    );
363                }
364                if let Some(parent_macro_info) = parent.macro_info.as_mut() {
365                    parent_macro_info.vars_to_expose.push((name, binding));
366                }
367            }
368        }
369        for (name, binding) in closed.variables {
370            add_unused_binding_warning(
371                self.diagnostics,
372                self.db,
373                &closed.used_variables,
374                name,
375                &binding,
376                &self.resolver.data.feature_config,
377            );
378        }
379        // Adds warning for unused items if required.
380        for (ty_name, statement_ty) in closed.use_items {
381            if !closed.used_use_items.contains(&ty_name) && !ty_name.long(self.db).starts_with('_')
382            {
383                self.diagnostics.report(statement_ty.stable_ptr, UnusedUse);
384            }
385        }
386        res
387    }
388
389    /// Returns the return type in the current context if available.
390    fn get_return_type(&mut self) -> Option<TypeId<'ctx>> {
391        if let Some(inner_ctx) = &self.inner_ctx {
392            return Some(inner_ctx.return_type);
393        }
394
395        if let Some(signature) = self.signature {
396            return Some(signature.return_type);
397        }
398
399        None
400    }
401
402    fn reduce_ty(&mut self, ty: TypeId<'ctx>) -> TypeId<'ctx> {
403        self.resolver.inference().rewrite(ty).no_err()
404    }
405
406    /// Applies inference rewriter to all the expressions in the computation context, and adds
407    /// errors on types from the final expressions.
408    pub fn apply_inference_rewriter_to_exprs(&mut self) {
409        let mut analyzed_types = UnorderedHashSet::<_>::default();
410        for (_id, expr) in &mut self.arenas.exprs {
411            self.resolver.inference().internal_rewrite(expr).no_err();
412            // Adding an error only once per type.
413            if analyzed_types.insert(expr.ty()) {
414                add_type_based_diagnostics(self.db, self.diagnostics, expr.ty(), &*expr);
415            }
416        }
417    }
418
419    /// Applies inference rewriter to all the rewritable things in the computation context.
420    fn apply_inference_rewriter(&mut self) {
421        self.apply_inference_rewriter_to_exprs();
422        for (_id, pattern) in &mut self.arenas.patterns {
423            self.resolver.inference().internal_rewrite(pattern).no_err();
424        }
425        for (_id, stmt) in &mut self.arenas.statements {
426            self.resolver.inference().internal_rewrite(stmt).no_err();
427        }
428    }
429    /// Returns whether the current context is inside a loop.
430    fn is_inside_loop(&self) -> bool {
431        let Some(inner_ctx) = &self.inner_ctx else {
432            return false;
433        };
434
435        match inner_ctx.kind {
436            InnerContextKind::Closure => false,
437            InnerContextKind::Loop { .. } | InnerContextKind::While | InnerContextKind::For => true,
438        }
439    }
440
441    /// Validates the features of the given item, then pushes them into the context.
442    /// IMPORTANT: Don't forget to restore through `restore_features`!
443    fn add_features_from_statement<Item: QueryAttrs<'ctx> + TypedSyntaxNode<'ctx>>(
444        &mut self,
445        item: &Item,
446    ) -> FeatureConfigRestore<'ctx> {
447        validate_statement_attributes(self, item);
448        let crate_id = self.resolver.owning_crate_id;
449        self.resolver.extend_feature_config_from_item(self.db, crate_id, self.diagnostics, item)
450    }
451
452    /// Restores the feature config to its state before [Self::add_features_from_statement],
453    /// using the restoration state returned by that method.
454    fn restore_features(&mut self, feature_restore: FeatureConfigRestore<'ctx>) {
455        self.resolver.restore_feature_config(feature_restore);
456    }
457}
458
459/// Tracks variable definitions and their mutability state.
460#[derive(Debug, Default, PartialEq, Eq)]
461pub struct VariableTracker<'ctx> {
462    /// Definitions of semantic variables.
463    semantic_defs: UnorderedHashMap<semantic::VarId<'ctx>, Binding<'ctx>>,
464    /// Mutable variables that have been referenced (lost mutability).
465    referenced_mut_vars: UnorderedHashMap<semantic::VarId<'ctx>, SyntaxStablePtrId<'ctx>>,
466}
467
468impl<'ctx> VariableTracker<'ctx> {
469    /// Extends the variable tracker from the variables present in the environment.
470    pub fn extend_from_environment(&mut self, environment: &Environment<'ctx>) {
471        self.semantic_defs
472            .extend(environment.variables.values().cloned().map(|var| (var.id(), var)));
473    }
474    /// Inserts a semantic definition for a variable.
475    /// Returns the old value if the variable was already defined.
476    pub fn insert(&mut self, var: Binding<'ctx>) -> Option<Binding<'ctx>> {
477        self.semantic_defs.insert(var.id(), var)
478    }
479
480    /// Returns true if the variable is currently mutable.
481    /// A variable is mutable only if declared mutable AND not referenced.
482    pub fn is_mut(&self, var_id: &semantic::VarId<'ctx>) -> bool {
483        self.semantic_defs
484            .get(var_id)
485            .is_some_and(|def| def.is_mut() && !self.referenced_mut_vars.contains_key(var_id))
486    }
487
488    /// Returns true if the expression is a variable or a member access of a mutable variable.
489    pub fn is_mut_expr(&self, expr: &ExprAndId<'ctx>) -> bool {
490        expr.as_member_path().is_some_and(|var| self.is_mut(&var.base_var()))
491    }
492
493    /// Reports a mutability error if the variable cannot be modified.
494    pub fn report_var_mutability_error(
495        &self,
496        db: &'ctx dyn Database,
497        diagnostics: &mut SemanticDiagnostics<'ctx>,
498        var_id: &semantic::VarId<'ctx>,
499        error_ptr: impl Into<SyntaxStablePtrId<'ctx>>,
500        immutable_diagnostic: SemanticDiagnosticKind<'ctx>,
501    ) {
502        let Some(def) = self.semantic_defs.get(var_id) else {
503            return;
504        };
505
506        if !def.is_mut() {
507            diagnostics.report(error_ptr, immutable_diagnostic);
508            return;
509        }
510
511        if let Some(&referenced_at) = self.referenced_mut_vars.get(var_id) {
512            let note = DiagnosticNote::with_location(
513                "variable pointer taken here".into(),
514                StableLocation::new(referenced_at).span_in_file(db),
515            );
516            diagnostics.report(error_ptr, AssignmentToReprPtrVariable(vec![note]));
517        }
518    }
519
520    /// Marks an expression as pointed to if it refers to a mutable variable.
521    pub fn mark_referenced(
522        &mut self,
523        expr: &ExprAndId<'ctx>,
524        reference_location: SyntaxStablePtrId<'ctx>,
525    ) {
526        if let Some(base_var) = expr.as_member_path().map(|path| path.base_var())
527            && self.is_mut(&base_var)
528        {
529            // This insert happens only once, since `is_mut` returns false if already referenced.
530            let _ = self.referenced_mut_vars.insert(base_var, reference_location);
531        }
532    }
533}
534
535/// Adds warning for unused bindings if required.
536fn add_unused_binding_warning<'db>(
537    diagnostics: &mut SemanticDiagnostics<'db>,
538    db: &'db dyn Database,
539    used_bindings: &UnorderedHashSet<VarId<'db>>,
540    name: SmolStrId<'db>,
541    binding: &Binding<'db>,
542    ctx_feature_config: &FeatureConfig<'db>,
543) {
544    if !name.long(db).starts_with('_') && !used_bindings.contains(&binding.id()) {
545        match binding {
546            Binding::LocalItem(local_item) => match local_item.id {
547                StatementItemId::Constant(_) => {
548                    diagnostics.report(binding.stable_ptr(db), UnusedConstant);
549                }
550                StatementItemId::Use(_) => {
551                    diagnostics.report(binding.stable_ptr(db), UnusedUse);
552                }
553            },
554            Binding::LocalVar(local_var) => {
555                if !local_var.allow_unused
556                    && !ctx_feature_config
557                        .allowed_lints
558                        .contains(&SmolStrId::from(db, UNUSED_VARIABLES))
559                {
560                    diagnostics.report(binding.stable_ptr(db), UnusedVariable);
561                }
562            }
563            Binding::Param(_) => {
564                if !ctx_feature_config
565                    .allowed_lints
566                    .contains(&SmolStrId::from(db, UNUSED_VARIABLES))
567                {
568                    diagnostics.report(binding.stable_ptr(db), UnusedVariable);
569                }
570            }
571        }
572    }
573}
574
575// TODO(ilya): Change value to VarId.
576pub type EnvVariables<'db> = OrderedHashMap<SmolStrId<'db>, Binding<'db>>;
577
578type EnvItems<'db> = OrderedHashMap<SmolStrId<'db>, StatementGenericItemData<'db>>;
579
580/// Struct that holds the resolved generic type of a statement item.
581#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
582#[debug_db(dyn Database)]
583struct StatementGenericItemData<'db> {
584    resolved_generic_item: ResolvedGenericItem<'db>,
585    stable_ptr: SyntaxStablePtrId<'db>,
586}
587
588/// A state which contains all the variables defined at the current resolver until now, and a
589/// pointer to the parent environment.
590#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
591pub struct Environment<'db> {
592    parent: Option<Box<Environment<'db>>>,
593    variables: EnvVariables<'db>,
594    used_variables: UnorderedHashSet<semantic::VarId<'db>>,
595    use_items: EnvItems<'db>,
596    used_use_items: UnorderedHashSet<SmolStrId<'db>>,
597    /// Information for macro in case the current environment was create by expanding a macro.
598    macro_info: Option<MacroExpansionInfo<'db>>,
599}
600impl<'db> Environment<'db> {
601    /// Adds a parameter to the environment.
602    pub fn add_param(
603        &mut self,
604        db: &'db dyn Database,
605        diagnostics: &mut SemanticDiagnostics<'db>,
606        semantic_param: Parameter<'db>,
607        ast_param: &ast::Param<'db>,
608        function_title_id: Option<FunctionTitleId<'db>>,
609    ) -> Maybe<()> {
610        if let utils::ordered_hash_map::Entry::Vacant(entry) =
611            self.variables.entry(semantic_param.name)
612        {
613            entry.insert(Binding::Param(semantic_param));
614            Ok(())
615        } else {
616            Err(diagnostics.report(
617                ast_param.stable_ptr(db),
618                ParamNameRedefinition { function_title_id, param_name: semantic_param.name },
619            ))
620        }
621    }
622
623    pub fn empty() -> Self {
624        Self {
625            parent: None,
626            variables: Default::default(),
627            used_variables: Default::default(),
628            use_items: Default::default(),
629            used_use_items: Default::default(),
630            macro_info: None,
631        }
632    }
633}
634
635/// Returns the requested item from the environment if it exists. Returns None otherwise.
636pub fn get_statement_item_by_name<'db>(
637    env: &mut Environment<'db>,
638    item_name: SmolStrId<'db>,
639) -> Option<ResolvedGenericItem<'db>> {
640    let mut maybe_env = Some(&mut *env);
641    while let Some(curr_env) = maybe_env {
642        if let Some(var) = curr_env.use_items.get(&item_name) {
643            curr_env.used_use_items.insert(item_name);
644            return Some(var.resolved_generic_item.clone());
645        }
646        maybe_env = curr_env.parent.as_deref_mut();
647    }
648    None
649}
650
651/// Computes the semantic model of an expression.
652/// Note that this expr will always be "registered" in the arena, so it can be looked up in the
653/// language server.
654pub fn compute_expr_semantic<'db>(
655    ctx: &mut ComputationContext<'db, '_>,
656    syntax: &ast::Expr<'db>,
657) -> ExprAndId<'db> {
658    let expr = maybe_compute_expr_semantic(ctx, syntax);
659    let expr = wrap_maybe_with_missing(ctx, expr, syntax.stable_ptr(ctx.db));
660    let id = ctx.arenas.exprs.alloc(expr.clone());
661    ExprAndId { expr, id }
662}
663
664/// Converts `Maybe<Expr>` to a possibly [missing](ExprMissing) [Expr].
665fn wrap_maybe_with_missing<'db>(
666    ctx: &mut ComputationContext<'db, '_>,
667    expr: Maybe<Expr<'db>>,
668    stable_ptr: ast::ExprPtr<'db>,
669) -> Expr<'db> {
670    expr.unwrap_or_else(|diag_added| {
671        Expr::Missing(ExprMissing {
672            ty: TypeId::missing(ctx.db, diag_added),
673            stable_ptr,
674            diag_added,
675        })
676    })
677}
678
679/// Computes the semantic model of an expression, or returns a SemanticDiagnosticKind on error.
680pub fn maybe_compute_expr_semantic<'db>(
681    ctx: &mut ComputationContext<'db, '_>,
682    syntax: &ast::Expr<'db>,
683) -> Maybe<Expr<'db>> {
684    let db = ctx.db;
685
686    // TODO(spapini): When Expr holds the syntax pointer, add it here as well.
687    match syntax {
688        ast::Expr::Path(path) => resolve_expr_path(ctx, path),
689        ast::Expr::Literal(literal_syntax) => {
690            Ok(Expr::Literal(literal_to_semantic(ctx, literal_syntax)?))
691        }
692        ast::Expr::ShortString(literal_syntax) => {
693            Ok(Expr::Literal(short_string_to_semantic(ctx, literal_syntax)?))
694        }
695        ast::Expr::String(literal_syntax) => {
696            Ok(Expr::StringLiteral(string_literal_to_semantic(ctx, literal_syntax)?))
697        }
698        ast::Expr::False(syntax) => Ok(false_literal_expr(ctx, syntax.stable_ptr(db).into())),
699        ast::Expr::True(syntax) => Ok(true_literal_expr(ctx, syntax.stable_ptr(db).into())),
700        ast::Expr::Parenthesized(paren_syntax) => {
701            maybe_compute_expr_semantic(ctx, &paren_syntax.expr(db))
702        }
703        ast::Expr::Unary(syntax) => compute_expr_unary_semantic(ctx, syntax),
704        ast::Expr::Binary(binary_op_syntax) => compute_expr_binary_semantic(ctx, binary_op_syntax),
705        ast::Expr::Tuple(tuple_syntax) => compute_expr_tuple_semantic(ctx, tuple_syntax),
706        ast::Expr::FunctionCall(call_syntax) => {
707            compute_expr_function_call_semantic(ctx, call_syntax)
708        }
709        ast::Expr::StructCtorCall(ctor_syntax) => struct_ctor_expr(ctx, ctor_syntax),
710        ast::Expr::Block(block_syntax) => compute_expr_block_semantic(ctx, block_syntax),
711        ast::Expr::Match(expr_match) => compute_expr_match_semantic(ctx, expr_match),
712        ast::Expr::If(expr_if) => compute_expr_if_semantic(ctx, expr_if),
713        ast::Expr::Loop(expr_loop) => compute_expr_loop_semantic(ctx, expr_loop),
714        ast::Expr::While(expr_while) => compute_expr_while_semantic(ctx, expr_while),
715        ast::Expr::ErrorPropagate(expr) => compute_expr_error_propagate_semantic(ctx, expr),
716        ast::Expr::InlineMacro(expr) => compute_expr_inline_macro_semantic(ctx, expr),
717        ast::Expr::Missing(_) | ast::Expr::FieldInitShorthand(_) => {
718            Err(ctx.diagnostics.report(syntax.stable_ptr(db), SemanticDiagnosticKind::Unsupported))
719        }
720        ast::Expr::Indexed(expr) => compute_expr_indexed_semantic(ctx, expr),
721        ast::Expr::FixedSizeArray(expr) => compute_expr_fixed_size_array_semantic(ctx, expr),
722        ast::Expr::For(expr) => compute_expr_for_semantic(ctx, expr),
723        ast::Expr::Closure(expr) => compute_expr_closure_semantic(ctx, expr, None),
724    }
725}
726
727/// Expands an inline macro invocation and returns the generated code and related metadata.
728fn expand_inline_macro<'db>(
729    ctx: &mut ComputationContext<'db, '_>,
730    syntax: &ast::ExprInlineMacro<'db>,
731) -> Maybe<InlineMacroExpansion<'db>> {
732    let db = ctx.db;
733    let macro_name = syntax.path(db).identifier(ctx.db);
734    let crate_id = ctx.resolver.owning_crate_id;
735    // Skipping expanding an inline macro if it had a parser error.
736    if syntax.as_syntax_node().descendants(db).any(|node| {
737        matches!(
738            node.kind(db),
739            SyntaxKind::ExprMissing
740                | SyntaxKind::WrappedArgListMissing
741                | SyntaxKind::StatementMissing
742                | SyntaxKind::ModuleItemMissing
743                | SyntaxKind::TraitItemMissing
744                | SyntaxKind::ImplItemMissing
745                | SyntaxKind::TokenMissing
746                | SyntaxKind::TokenSkipped
747                | SyntaxKind::WrappedTokenTreeMissing
748        )
749    }) {
750        return Err(skip_diagnostic());
751    }
752    // We call the resolver with a new diagnostics, since the diagnostics should not be reported
753    // if the macro was found as a plugin.
754    let user_defined_macro = ctx.resolver.resolve_generic_path(
755        &mut Default::default(),
756        &syntax.path(db),
757        NotFoundItemType::Macro,
758        ResolutionContext::Statement(&mut ctx.environment),
759    );
760    if let Ok(ResolvedGenericItem::Macro(macro_declaration_id)) = user_defined_macro {
761        let macro_rules = ctx.db.macro_declaration_rules(macro_declaration_id)?;
762        let Some((rule, (captures, placeholder_to_rep_id))) = macro_rules.iter().find_map(|rule| {
763            is_macro_rule_match(ctx.db, rule, &syntax.arguments(db)).map(|res| (rule, res))
764        }) else {
765            return Err(ctx
766                .diagnostics
767                .report(syntax.stable_ptr(ctx.db), InlineMacroNoMatchingRule(macro_name)));
768        };
769        let mut matcher_ctx =
770            MatcherContext { captures, placeholder_to_rep_id, ..Default::default() };
771        let expanded_code = expand_macro_rule(ctx.db, rule, &mut matcher_ctx)?;
772
773        let macro_defsite_resolver_data =
774            ctx.db.macro_declaration_resolver_data(macro_declaration_id)?;
775        let inference_id = ctx.resolver.inference().inference_id;
776        let callsite_resolver = ctx.resolver.data.clone_with_inference_id(ctx.db, inference_id);
777        let parent_macro_call_data = ctx.resolver.macro_call_data.clone();
778        let info = MacroExpansionInfo {
779            mappings: expanded_code.code_mappings,
780            kind: MacroKind::UserDefined,
781            vars_to_expose: vec![],
782        };
783        ctx.resolver.macro_call_data = Some(Arc::new(ResolverMacroData {
784            defsite_module_id: macro_defsite_resolver_data.module_id,
785            callsite_module_id: callsite_resolver.module_id,
786            expansion_mappings: info.mappings.clone(),
787            parent_macro_call_data,
788        }));
789        Ok(InlineMacroExpansion {
790            content: expanded_code.text,
791            name: macro_name.to_string(db),
792            info,
793        })
794    } else if let Some(macro_plugin_id) =
795        ctx.db.crate_inline_macro_plugins(crate_id).get(&macro_name.to_string(db)).cloned()
796    {
797        let macro_plugin = macro_plugin_id.long(ctx.db);
798        let result = macro_plugin.generate_code(
799            db,
800            syntax,
801            &MacroPluginMetadata {
802                cfg_set: &ctx.cfg_set,
803                declared_derives: ctx.db.declared_derives(crate_id),
804                allowed_features: &ctx.resolver.data.feature_config.allowed_features,
805                edition: ctx.resolver.settings.edition,
806            },
807        );
808        let mut diag_added = None;
809        for diagnostic in result.diagnostics {
810            diag_added = match diagnostic.inner_span {
811                None => Some(
812                    ctx.diagnostics.report(diagnostic.stable_ptr, PluginDiagnostic(diagnostic)),
813                ),
814                Some((offset, width)) => Some(ctx.diagnostics.report_with_inner_span(
815                    diagnostic.stable_ptr,
816                    (offset, width),
817                    PluginDiagnostic(diagnostic),
818                )),
819            }
820        }
821        let Some(code) = result.code else {
822            return Err(diag_added.unwrap_or_else(|| {
823                ctx.diagnostics.report(syntax.stable_ptr(ctx.db), InlineMacroNotFound(macro_name))
824            }));
825        };
826        Ok(InlineMacroExpansion {
827            content: code.content.into(),
828            name: code.name.to_string(),
829            info: MacroExpansionInfo {
830                mappings: code.code_mappings.into(),
831                kind: if code.is_unhygienic { MacroKind::Unhygienic } else { MacroKind::Plugin },
832                vars_to_expose: vec![],
833            },
834        })
835    } else {
836        let macro_name = syntax.path(db).as_syntax_node().get_text_without_trivia(db);
837        Err(ctx.diagnostics.report(syntax.stable_ptr(db), InlineMacroNotFound(macro_name)))
838    }
839}
840
841/// Expands and computes the semantic model of an inline macro used in expression position.
842fn compute_expr_inline_macro_semantic<'db>(
843    ctx: &mut ComputationContext<'db, '_>,
844    syntax: &ast::ExprInlineMacro<'db>,
845) -> Maybe<Expr<'db>> {
846    let prev_macro_call_data = ctx.resolver.macro_call_data.clone();
847    let InlineMacroExpansion { content, name, info } = expand_inline_macro(ctx, syntax)?;
848    let new_file_long_id = FileLongId::Virtual(VirtualFile {
849        parent: Some(syntax.stable_ptr(ctx.db).untyped().span_in_file(ctx.db)),
850        name: SmolStrId::from(ctx.db, name),
851        content: SmolStrId::from(ctx.db, content),
852        code_mappings: info.mappings.clone(),
853        kind: FileKind::Expr,
854        original_item_removed: true,
855    });
856    ctx.db.accumulate_inline_macro_expansion(&new_file_long_id);
857    let new_file_id = new_file_long_id.intern(ctx.db);
858    let expr_syntax = ctx.db.file_expr_syntax(new_file_id)?;
859    let parser_diagnostics = ctx.db.file_syntax_diagnostics(new_file_id);
860    if let Err(diag_added) = parser_diagnostics.check_error_free() {
861        for diag in parser_diagnostics.get_diagnostics_without_duplicates(ctx.db) {
862            ctx.diagnostics.report(
863                syntax.stable_ptr(ctx.db),
864                SemanticDiagnosticKind::MacroGeneratedCodeParserDiagnostic(diag),
865            );
866        }
867        return Err(diag_added);
868    }
869    let expr = ctx.run_in_macro_subscope(|ctx| compute_expr_semantic(ctx, &expr_syntax), info);
870    ctx.resolver.macro_call_data = prev_macro_call_data;
871    Ok(expr.expr)
872}
873
874/// Computes the semantic model of a tail expression, handling inline macros recursively and
875/// ensuring the correct tail expression is extracted from the resulting statements.
876fn compute_tail_semantic<'db>(
877    ctx: &mut ComputationContext<'db, '_>,
878    tail: &ast::StatementExpr<'db>,
879    statements_ids: &mut Vec<StatementId>,
880) -> ExprAndId<'db> {
881    // Push the statement's attributes into the context, restored after the computation is resolved.
882    let feature_restore = ctx.add_features_from_statement(tail);
883
884    let db = ctx.db;
885    let expr = tail.expr(db);
886    let res = match &expr {
887        ast::Expr::InlineMacro(inline_macro_syntax) => {
888            match expand_macro_for_statement(ctx, inline_macro_syntax, true, statements_ids) {
889                Ok(Some(expr_and_id)) => expr_and_id,
890                Ok(None) => unreachable!("Tail expression should not be None"),
891                Err(diag_added) => {
892                    let expr = Expr::Missing(ExprMissing {
893                        ty: TypeId::missing(db, diag_added),
894                        stable_ptr: expr.stable_ptr(db),
895                        diag_added,
896                    });
897                    ExprAndId { id: ctx.arenas.exprs.alloc(expr.clone()), expr }
898                }
899            }
900        }
901        _ => compute_expr_semantic(ctx, &expr),
902    };
903
904    // Pop the statement's attributes from the context.
905    ctx.restore_features(feature_restore);
906    res
907}
908
909/// Expands an inline macro used in statement position, computes its semantic model, and extends
910/// `statements` with it.
911fn expand_macro_for_statement<'db>(
912    ctx: &mut ComputationContext<'db, '_>,
913    syntax: &ast::ExprInlineMacro<'db>,
914    is_tail: bool,
915    statements_ids: &mut Vec<StatementId>,
916) -> Maybe<Option<ExprAndId<'db>>> {
917    let prev_macro_call_data = ctx.resolver.macro_call_data.clone();
918    let InlineMacroExpansion { content, name, info } = expand_inline_macro(ctx, syntax)?;
919    let new_file_long_id = FileLongId::Virtual(VirtualFile {
920        parent: Some(syntax.stable_ptr(ctx.db).untyped().span_in_file(ctx.db)),
921        name: SmolStrId::from(ctx.db, name),
922        content: SmolStrId::from_arcstr(ctx.db, &content),
923        code_mappings: info.mappings.clone(),
924        kind: FileKind::StatementList,
925        original_item_removed: true,
926    });
927    ctx.db.accumulate_inline_macro_expansion(&new_file_long_id);
928    let new_file_id = new_file_long_id.intern(ctx.db);
929    let parser_diagnostics = ctx.db.file_syntax_diagnostics(new_file_id);
930    if let Err(diag_added) = parser_diagnostics.check_error_free() {
931        for diag in parser_diagnostics.get_diagnostics_without_duplicates(ctx.db) {
932            ctx.diagnostics.report(
933                syntax.stable_ptr(ctx.db),
934                SemanticDiagnosticKind::MacroGeneratedCodeParserDiagnostic(diag),
935            );
936        }
937        return Err(diag_added);
938    }
939    let statement_list = ctx.db.file_statement_list_syntax(new_file_id)?;
940    let (parsed_statements, tail) = statements_and_tail(ctx.db, statement_list);
941    let result = ctx.run_in_macro_subscope(
942        |ctx| {
943            compute_statements_semantic_and_extend(ctx, parsed_statements, statements_ids);
944            if is_tail {
945                if let Some(tail_expr) = tail {
946                    Ok(Some(compute_tail_semantic(ctx, &tail_expr, statements_ids)))
947                } else {
948                    Err(ctx.diagnostics.report_after(syntax.stable_ptr(ctx.db), MissingSemicolon))
949                }
950            } else {
951                if let Some(tail_expr) = tail {
952                    let expr = compute_expr_semantic(ctx, &tail_expr.expr(ctx.db));
953                    statements_ids.push(ctx.arenas.statements.alloc(semantic::Statement::Expr(
954                        semantic::StatementExpr {
955                            expr: expr.id,
956                            stable_ptr: tail_expr.stable_ptr(ctx.db).into(),
957                        },
958                    )));
959                }
960                Ok(None)
961            }
962        },
963        info,
964    );
965    ctx.resolver.macro_call_data = prev_macro_call_data;
966    result
967}
968
969fn compute_expr_unary_semantic<'db>(
970    ctx: &mut ComputationContext<'db, '_>,
971    syntax: &ast::ExprUnary<'db>,
972) -> Maybe<Expr<'db>> {
973    let db = ctx.db;
974    let unary_op = syntax.op(db);
975    let inner = syntax.expr(db);
976    match (&unary_op, &inner) {
977        // If this is not an actual function call, but actually a minus literal (e.g. -1).
978        (UnaryOperator::Minus(_), ast::Expr::Literal(literal)) => {
979            let (value, ty) = literal.numeric_value_and_suffix(db).unwrap_or_default();
980
981            Ok(Expr::Literal(new_literal_expr(ctx, ty, -value, syntax.stable_ptr(db).into())?))
982        }
983        (UnaryOperator::At(_), inner) => {
984            let expr = compute_expr_semantic(ctx, inner);
985
986            let ty = TypeLongId::Snapshot(expr.ty()).intern(ctx.db);
987            Ok(Expr::Snapshot(ExprSnapshot {
988                inner: expr.id,
989                ty,
990                stable_ptr: syntax.stable_ptr(db).into(),
991            }))
992        }
993        (UnaryOperator::Desnap(_), inner) => {
994            // If the desnapped expr is a snapshot we `desnap` it.
995            // If it is a known type we look for a deref impl and call it.
996            // Otherwise we conform the desnapped type to snapshot type, and return the inner var
997            // type.
998            let (desnapped_expr, desnapped_ty) = {
999                // The expr the desnap acts on. E.g. `x` in `*x`.
1000                let desnapped_expr = compute_expr_semantic(ctx, inner);
1001                let desnapped_expr_type = ctx.reduce_ty(desnapped_expr.ty());
1002
1003                let desnapped_ty = match desnapped_expr_type.long(ctx.db) {
1004                    TypeLongId::Var(_) | TypeLongId::ImplType(_) => {
1005                        let inference = &mut ctx.resolver.inference();
1006                        // The type of the full desnap expr. E.g. the type of `*x` for `*x`.
1007                        let desnap_expr_type =
1008                            inference.new_type_var(Some(inner.stable_ptr(db).untyped()));
1009                        let desnapped_expr_type_var =
1010                            TypeLongId::Snapshot(desnap_expr_type).intern(ctx.db);
1011                        if let Err(err_set) =
1012                            inference.conform_ty(desnapped_expr_type_var, desnapped_expr_type)
1013                        {
1014                            let diag_added = ctx.diagnostics.report(
1015                                syntax.stable_ptr(db),
1016                                WrongArgumentType {
1017                                    expected_ty: desnapped_expr_type_var,
1018                                    actual_ty: desnapped_expr_type,
1019                                },
1020                            );
1021                            inference.consume_reported_error(err_set, diag_added);
1022                            return Err(diag_added);
1023                        };
1024                        ctx.reduce_ty(desnap_expr_type)
1025                    }
1026                    TypeLongId::Snapshot(ty) => *ty,
1027                    _ => {
1028                        // If the desnapped type is not a snapshot, we look for a deref impl and
1029                        // call it.
1030                        let deref_chain = ctx.db.deref_chain(
1031                            desnapped_expr_type,
1032                            ctx.resolver.owning_crate_id,
1033                            ctx.variable_tracker.is_mut_expr(&desnapped_expr),
1034                        )?;
1035                        let Some(DerefInfo { function_id, self_mutability, target_ty: _ }) =
1036                            deref_chain.derefs.first()
1037                        else {
1038                            return Err(ctx.diagnostics.report(
1039                                unary_op.stable_ptr(db),
1040                                DerefNonRef { ty: desnapped_expr_type },
1041                            ));
1042                        };
1043                        return expr_function_call(
1044                            ctx,
1045                            *function_id,
1046                            vec![NamedArg(desnapped_expr, None, *self_mutability)],
1047                            syntax.stable_ptr(db),
1048                            syntax.stable_ptr(db).into(),
1049                        );
1050                    }
1051                };
1052                (desnapped_expr, desnapped_ty)
1053            };
1054
1055            Ok(Expr::Desnap(ExprDesnap {
1056                inner: desnapped_expr.id,
1057                ty: desnapped_ty,
1058                stable_ptr: syntax.stable_ptr(db).into(),
1059            }))
1060        }
1061        (UnaryOperator::Reference(_), inner) => {
1062            let stable_ptr = syntax.stable_ptr(db);
1063            if !crate::types::are_repr_ptrs_enabled(ctx.db, ctx.resolver.module_id) {
1064                return Err(ctx.diagnostics.report(stable_ptr, ReprPtrsDisabled));
1065            }
1066            let inner_expr = compute_expr_semantic(ctx, inner);
1067
1068            // Disable mutability from repr ptr variable.
1069            ctx.variable_tracker.mark_referenced(&inner_expr, stable_ptr.untyped());
1070
1071            // Snapshot inner expression.
1072            let inner_ty = inner_expr.ty();
1073            let snapshot_ty = TypeLongId::Snapshot(inner_ty).intern(ctx.db);
1074            let snapshot_expr = ExprSnapshot {
1075                inner: inner_expr.id,
1076                ty: snapshot_ty,
1077                stable_ptr: stable_ptr.into(),
1078            };
1079            let snapshot_expr_id = ctx.arenas.exprs.alloc(Expr::Snapshot(snapshot_expr.clone()));
1080
1081            // Get BoxTrait::<@T>::new
1082            let info = ctx.db.core_info();
1083            let generic_args = vec![GenericArgumentId::Type(snapshot_ty)];
1084            let concrete_trait_id =
1085                crate::ConcreteTraitLongId { trait_id: info.box_trt, generic_args }.intern(ctx.db);
1086            let concrete_trait_function =
1087                crate::items::trt::ConcreteTraitGenericFunctionLongId::new(
1088                    ctx.db,
1089                    concrete_trait_id,
1090                    info.box_new_fn,
1091                )
1092                .intern(ctx.db);
1093
1094            // Resolve which BoxTrait implementation applies to this type.
1095            let impl_lookup_context = ctx.resolver.impl_lookup_context();
1096            let mut inference = ctx.resolver.inference();
1097            let function = inference
1098                .infer_trait_function(
1099                    concrete_trait_function,
1100                    impl_lookup_context,
1101                    Some(stable_ptr.untyped()),
1102                )
1103                .map_err(|err_set| {
1104                    inference.report_on_pending_error(
1105                        err_set,
1106                        ctx.diagnostics,
1107                        stable_ptr.untyped(),
1108                    )
1109                })?;
1110
1111            // Call BoxTrait::new(@x).
1112            expr_function_call(
1113                ctx,
1114                function,
1115                vec![NamedArg(
1116                    ExprAndId { expr: Expr::Snapshot(snapshot_expr), id: snapshot_expr_id },
1117                    None,
1118                    Mutability::Immutable,
1119                )],
1120                stable_ptr,
1121                stable_ptr.into(),
1122            )
1123        }
1124        (_, inner) => {
1125            let expr = compute_expr_semantic(ctx, inner);
1126
1127            let mut inference = ctx.resolver.inference();
1128            let concrete_trait_function = match core_unary_operator(
1129                ctx.db,
1130                &mut inference,
1131                &unary_op,
1132                syntax.stable_ptr(db).untyped(),
1133            )? {
1134                Err(err_kind) => {
1135                    return Err(ctx.diagnostics.report(unary_op.stable_ptr(db), err_kind));
1136                }
1137                Ok(function) => function,
1138            };
1139
1140            let impl_lookup_context = ctx.resolver.impl_lookup_context();
1141            let mut inference = ctx.resolver.inference();
1142            let function = inference
1143                .infer_trait_function(
1144                    concrete_trait_function,
1145                    impl_lookup_context,
1146                    Some(syntax.stable_ptr(db).untyped()),
1147                )
1148                .map_err(|err_set| {
1149                    inference.report_on_pending_error(
1150                        err_set,
1151                        ctx.diagnostics,
1152                        syntax.stable_ptr(db).untyped(),
1153                    )
1154                })?;
1155
1156            expr_function_call(
1157                ctx,
1158                function,
1159                vec![NamedArg(expr, None, Mutability::Immutable)],
1160                syntax.stable_ptr(db),
1161                syntax.stable_ptr(db).into(),
1162            )
1163        }
1164    }
1165}
1166
1167fn compute_expr_binary_semantic<'db>(
1168    ctx: &mut ComputationContext<'db, '_>,
1169    syntax: &ast::ExprBinary<'db>,
1170) -> Maybe<Expr<'db>> {
1171    let db = ctx.db;
1172
1173    let stable_ptr = syntax.stable_ptr(db).into();
1174    let binary_op = syntax.op(db);
1175    let lhs_syntax = &syntax.lhs(db);
1176    let rhs_syntax = syntax.rhs(db);
1177
1178    match binary_op {
1179        ast::BinaryOperator::Dot(_) => {
1180            let lexpr = compute_expr_semantic(ctx, lhs_syntax);
1181            dot_expr(ctx, lexpr, rhs_syntax, stable_ptr)
1182        }
1183        ast::BinaryOperator::Eq(_) => {
1184            let lexpr = compute_expr_semantic(ctx, lhs_syntax);
1185            let rexpr = compute_expr_semantic(ctx, &rhs_syntax);
1186
1187            let member_path = match lexpr.expr {
1188                Expr::Var(expr) => ExprVarMemberPath::Var(expr),
1189                Expr::MemberAccess(ExprMemberAccess { member_path: Some(ref_arg), .. }) => ref_arg,
1190                _ => {
1191                    return Err(ctx
1192                        .diagnostics
1193                        .report(lhs_syntax.stable_ptr(db), InvalidLhsForAssignment));
1194                }
1195            };
1196
1197            let inference = &mut ctx.resolver.inference();
1198            inference.conform_ty_for_diag(
1199                rexpr.ty(),
1200                member_path.ty(),
1201                ctx.diagnostics,
1202                || rhs_syntax.stable_ptr(db).untyped(),
1203                |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
1204            )?;
1205            ctx.variable_tracker.report_var_mutability_error(
1206                db,
1207                ctx.diagnostics,
1208                &member_path.base_var(),
1209                syntax.stable_ptr(db),
1210                AssignmentToImmutableVar,
1211            );
1212            Ok(Expr::Assignment(ExprAssignment {
1213                ref_arg: member_path,
1214                rhs: rexpr.id,
1215                ty: unit_ty(db),
1216                stable_ptr,
1217            }))
1218        }
1219        ast::BinaryOperator::AndAnd(_) | ast::BinaryOperator::OrOr(_) => {
1220            let lexpr = compute_expr_semantic(ctx, lhs_syntax);
1221            let rexpr = compute_expr_semantic(ctx, &rhs_syntax);
1222
1223            let op = match binary_op {
1224                ast::BinaryOperator::AndAnd(_) => LogicalOperator::AndAnd,
1225                ast::BinaryOperator::OrOr(_) => LogicalOperator::OrOr,
1226                _ => unreachable!(),
1227            };
1228
1229            let inference = &mut ctx.resolver.inference();
1230            let bool_ty = core_bool_ty(db);
1231            let _ = inference.conform_ty_for_diag(
1232                lexpr.expr.ty(),
1233                bool_ty,
1234                ctx.diagnostics,
1235                || lhs_syntax.stable_ptr(db).untyped(),
1236                |actual_ty, expected_ty| WrongType { expected_ty, actual_ty },
1237            );
1238            let _ = inference.conform_ty_for_diag(
1239                rexpr.expr.ty(),
1240                bool_ty,
1241                ctx.diagnostics,
1242                || rhs_syntax.stable_ptr(db).untyped(),
1243                |actual_ty, expected_ty| WrongType { expected_ty, actual_ty },
1244            );
1245
1246            Ok(Expr::LogicalOperator(ExprLogicalOperator {
1247                lhs: lexpr.id,
1248                op,
1249                rhs: rexpr.id,
1250                ty: bool_ty,
1251                stable_ptr,
1252            }))
1253        }
1254        _ => call_core_binary_op(ctx, syntax, lhs_syntax, &rhs_syntax),
1255    }
1256}
1257
1258/// Get the function call expression of a binary operation that is defined in the corelib.
1259fn call_core_binary_op<'db>(
1260    ctx: &mut ComputationContext<'db, '_>,
1261    syntax: &ast::ExprBinary<'db>,
1262    lhs_syntax: &ast::Expr<'db>,
1263    rhs_syntax: &ast::Expr<'db>,
1264) -> Maybe<Expr<'db>> {
1265    let db = ctx.db;
1266    let stable_ptr = syntax.stable_ptr(db);
1267    let binary_op = syntax.op(db);
1268
1269    let (concrete_trait_function, snapshot) = match core_binary_operator(
1270        db,
1271        &mut ctx.resolver.inference(),
1272        &binary_op,
1273        stable_ptr.untyped(),
1274    )? {
1275        Err(err_kind) => {
1276            return Err(ctx.diagnostics.report(binary_op.stable_ptr(db), err_kind));
1277        }
1278        Ok(res) => res,
1279    };
1280
1281    let impl_lookup_context = ctx.resolver.impl_lookup_context();
1282    let inference = &mut ctx.resolver.inference();
1283    let function = inference
1284        .infer_trait_function(
1285            concrete_trait_function,
1286            impl_lookup_context,
1287            Some(stable_ptr.untyped()),
1288        )
1289        .map_err(|err_set| {
1290            inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr.untyped())
1291        })?;
1292
1293    let mut lexpr = compute_expr_semantic(ctx, lhs_syntax);
1294
1295    if let (Expr::Missing(_), BinaryOperator::LT(_)) = (&lexpr.expr, &binary_op) {
1296        return Err(ctx
1297            .diagnostics
1298            .report(binary_op.stable_ptr(db), SemanticDiagnosticKind::MaybeMissingColonColon));
1299    }
1300
1301    let mut rexpr = compute_expr_semantic(ctx, rhs_syntax);
1302
1303    ctx.reduce_ty(lexpr.ty()).check_not_missing(db)?;
1304    ctx.reduce_ty(rexpr.ty()).check_not_missing(db)?;
1305
1306    if snapshot {
1307        let ty = TypeLongId::Snapshot(lexpr.ty()).intern(ctx.db);
1308        let expr =
1309            Expr::Snapshot(ExprSnapshot { inner: lexpr.id, ty, stable_ptr: lexpr.stable_ptr() });
1310        lexpr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
1311        let ty = TypeLongId::Snapshot(rexpr.ty()).intern(ctx.db);
1312        let expr =
1313            Expr::Snapshot(ExprSnapshot { inner: rexpr.id, ty, stable_ptr: rexpr.stable_ptr() });
1314        rexpr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
1315    }
1316
1317    let sig = ctx.db.concrete_function_signature(function)?;
1318    let first_param = sig.params.first().unwrap();
1319
1320    expr_function_call(
1321        ctx,
1322        function,
1323        vec![
1324            NamedArg(lexpr, None, first_param.mutability),
1325            NamedArg(rexpr, None, Mutability::Immutable),
1326        ],
1327        stable_ptr,
1328        stable_ptr.into(),
1329    )
1330}
1331
1332fn compute_expr_tuple_semantic<'db>(
1333    ctx: &mut ComputationContext<'db, '_>,
1334    syntax: &ast::ExprListParenthesized<'db>,
1335) -> Maybe<Expr<'db>> {
1336    let db = ctx.db;
1337
1338    let mut items: Vec<ExprId> = vec![];
1339    let mut types: Vec<TypeId<'_>> = vec![];
1340    for expr_syntax in syntax.expressions(db).elements(db) {
1341        let expr_semantic = compute_expr_semantic(ctx, &expr_syntax);
1342        types.push(ctx.reduce_ty(expr_semantic.ty()));
1343        items.push(expr_semantic.id);
1344    }
1345    Ok(Expr::Tuple(ExprTuple {
1346        items,
1347        ty: TypeLongId::Tuple(types).intern(db),
1348        stable_ptr: syntax.stable_ptr(db).into(),
1349    }))
1350}
1351/// Computes the semantic model of an expression of type [ast::ExprFixedSizeArray].
1352fn compute_expr_fixed_size_array_semantic<'db>(
1353    ctx: &mut ComputationContext<'db, '_>,
1354    syntax: &ast::ExprFixedSizeArray<'db>,
1355) -> Maybe<Expr<'db>> {
1356    let db = ctx.db;
1357    let exprs = syntax.exprs(db).elements_vec(db);
1358    let size_ty = get_usize_ty(db);
1359    let (items, type_id, size) = if let Some(size_const_id) =
1360        extract_fixed_size_array_size(db, ctx.diagnostics, syntax, ctx.resolver)?
1361    {
1362        // Fixed size array with a defined size must have exactly one element.
1363        let [expr] = exprs.as_slice() else {
1364            return Err(ctx
1365                .diagnostics
1366                .report(syntax.stable_ptr(db), FixedSizeArrayNonSingleValue));
1367        };
1368        let expr_semantic = compute_expr_semantic(ctx, expr);
1369        let size = size_const_id
1370            .long(db)
1371            .to_int()
1372            .ok_or_else(|| {
1373                ctx.diagnostics.report(syntax.stable_ptr(db), FixedSizeArrayNonNumericSize)
1374            })?
1375            .to_usize()
1376            .unwrap();
1377        verify_fixed_size_array_size(db, ctx.diagnostics, &size.into(), syntax)?;
1378        (
1379            FixedSizeArrayItems::ValueAndSize(expr_semantic.id, size_const_id),
1380            expr_semantic.ty(),
1381            size_const_id,
1382        )
1383    } else if let Some((first_expr, tail_exprs)) = exprs.split_first() {
1384        let size = ConstValue::Int((tail_exprs.len() + 1).into(), size_ty).intern(db);
1385        let first_expr_semantic = compute_expr_semantic(ctx, first_expr);
1386        let mut items: Vec<ExprId> = vec![first_expr_semantic.id];
1387        // The type of the first expression is the type of the array. All other expressions must
1388        // have the same type.
1389        let first_expr_ty = ctx.reduce_ty(first_expr_semantic.ty());
1390        for expr_syntax in tail_exprs {
1391            let expr_semantic = compute_expr_semantic(ctx, expr_syntax);
1392            let inference = &mut ctx.resolver.inference();
1393            inference.conform_ty_for_diag(
1394                expr_semantic.ty(),
1395                first_expr_ty,
1396                ctx.diagnostics,
1397                || expr_syntax.stable_ptr(db).untyped(),
1398                |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
1399            )?;
1400            items.push(expr_semantic.id);
1401        }
1402        (FixedSizeArrayItems::Items(items), first_expr_ty, size)
1403    } else {
1404        (
1405            FixedSizeArrayItems::Items(vec![]),
1406            ctx.resolver.inference().new_type_var(Some(syntax.stable_ptr(db).untyped())),
1407            ConstValue::Int(0.into(), size_ty).intern(db),
1408        )
1409    };
1410    Ok(Expr::FixedSizeArray(ExprFixedSizeArray {
1411        items,
1412        ty: TypeLongId::FixedSizeArray { type_id, size }.intern(db),
1413        stable_ptr: syntax.stable_ptr(db).into(),
1414    }))
1415}
1416
1417fn compute_expr_function_call_semantic<'db>(
1418    ctx: &mut ComputationContext<'db, '_>,
1419    syntax: &ast::ExprFunctionCall<'db>,
1420) -> Maybe<Expr<'db>> {
1421    let db = ctx.db;
1422
1423    let path = syntax.path(db);
1424    let args_syntax = syntax.arguments(db).arguments(db);
1425    // Check if this is a variable.
1426    let mut is_shadowed_by_variable = false;
1427    if let Some((identifier, is_callsite_prefixed)) = try_extract_identifier_from_path(db, &path) {
1428        let variable_name = identifier.text(ctx.db);
1429        if let Some(var) = get_binded_expr_by_name(
1430            ctx,
1431            variable_name,
1432            is_callsite_prefixed,
1433            path.stable_ptr(ctx.db).into(),
1434        ) {
1435            is_shadowed_by_variable = true;
1436            // if closures are not in context, we want to call the function instead of the variable.
1437            if ctx.are_closures_in_context {
1438                let info = db.core_info();
1439                // TODO(TomerStarkware): find the correct trait based on captured variables.
1440                let fn_once_trait = info.fn_once_trt;
1441                let fn_trait = info.fn_trt;
1442                let self_expr = ExprAndId { expr: var.clone(), id: ctx.arenas.exprs.alloc(var) };
1443                let mut closure_call_data = |call_trait: TraitId<'db>| {
1444                    compute_method_function_call_data(
1445                        ctx,
1446                        &[call_trait],
1447                        SmolStrId::from(ctx.db, "call"),
1448                        self_expr.clone(),
1449                        syntax.stable_ptr(db).untyped(),
1450                        None,
1451                        |ty, _, inference_errors| {
1452                            if call_trait == fn_once_trait {
1453                                Some(CallExpressionRequiresFunction { ty, inference_errors })
1454                            } else {
1455                                None
1456                            }
1457                        },
1458                        |_, _, _| {
1459                            unreachable!(
1460                                "There is one explicit trait, FnOnce trait. No implementations of \
1461                                 the trait, caused by both lack of implementation or multiple \
1462                                 implementations of the trait, are handled in \
1463                                 NoImplementationOfTrait function."
1464                            )
1465                        },
1466                    )
1467                };
1468                let (call_function_id, _, fixed_closure, closure_mutability) =
1469                    closure_call_data(fn_trait).or_else(|_| closure_call_data(fn_once_trait))?;
1470
1471                let args_iter = args_syntax.elements(db);
1472                // Normal parameters
1473                let mut args = vec![];
1474                let mut arg_types = vec![];
1475                for arg_syntax in args_iter {
1476                    let stable_ptr = arg_syntax.stable_ptr(db);
1477                    let arg = compute_named_argument_clause(ctx, arg_syntax, None);
1478                    if arg.2 != Mutability::Immutable {
1479                        return Err(ctx.diagnostics.report(stable_ptr, RefClosureArgument));
1480                    }
1481                    if arg.1.is_some() {
1482                        return Err(ctx
1483                            .diagnostics
1484                            .report(stable_ptr, NamedArgumentsAreNotSupported));
1485                    }
1486                    args.push(arg.0.id);
1487                    arg_types.push(arg.0.ty());
1488                }
1489                let args_expr = Expr::Tuple(ExprTuple {
1490                    items: args,
1491                    ty: TypeLongId::Tuple(arg_types).intern(db),
1492                    stable_ptr: syntax.stable_ptr(db).into(),
1493                });
1494                let args_expr =
1495                    ExprAndId { expr: args_expr.clone(), id: ctx.arenas.exprs.alloc(args_expr) };
1496                let call_ptr = syntax.stable_ptr(db);
1497                return expr_function_call(
1498                    ctx,
1499                    call_function_id,
1500                    vec![
1501                        NamedArg(fixed_closure, None, closure_mutability),
1502                        NamedArg(args_expr, None, Mutability::Immutable),
1503                    ],
1504                    call_ptr,
1505                    call_ptr.into(),
1506                );
1507            }
1508        }
1509    }
1510
1511    let item = ctx
1512        .resolver
1513        .resolve_concrete_path_ex(
1514            ctx.diagnostics,
1515            &path,
1516            NotFoundItemType::Function,
1517            ResolutionContext::Statement(&mut ctx.environment),
1518        )
1519        .inspect_err(|_| {
1520            // Getting better diagnostics and usage metrics for the function args.
1521            for arg in args_syntax.elements(db) {
1522                compute_named_argument_clause(ctx, arg, None);
1523            }
1524        })?;
1525
1526    match item {
1527        ResolvedConcreteItem::Variant(variant) => {
1528            let concrete_enum_type =
1529                TypeLongId::Concrete(ConcreteTypeId::Enum(variant.concrete_enum_id)).intern(db);
1530            if concrete_enum_type.is_phantom(db) {
1531                ctx.diagnostics.report(syntax.stable_ptr(db), CannotCreateInstancesOfPhantomTypes);
1532            }
1533
1534            let named_args: Vec<_> = args_syntax
1535                .elements(db)
1536                .map(|arg_syntax| compute_named_argument_clause(ctx, arg_syntax, None))
1537                .collect();
1538            if named_args.len() != 1 {
1539                return Err(ctx.diagnostics.report(
1540                    syntax.stable_ptr(db),
1541                    WrongNumberOfArguments { expected: 1, actual: named_args.len() },
1542                ));
1543            }
1544            let NamedArg(arg, name_terminal, mutability) = named_args[0].clone();
1545            if let Some(name_terminal) = name_terminal {
1546                ctx.diagnostics.report(name_terminal.stable_ptr(db), NamedArgumentsAreNotSupported);
1547            }
1548            if mutability != Mutability::Immutable {
1549                return Err(ctx
1550                    .diagnostics
1551                    .report(args_syntax.stable_ptr(db), VariantCtorNotImmutable));
1552            }
1553            let inference = &mut ctx.resolver.inference();
1554            inference.conform_ty_for_diag(
1555                arg.ty(),
1556                variant.ty,
1557                ctx.diagnostics,
1558                || args_syntax.stable_ptr(db).untyped(),
1559                |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
1560            )?;
1561            Ok(semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
1562                variant,
1563                value_expr: arg.id,
1564                ty: concrete_enum_type,
1565                stable_ptr: syntax.stable_ptr(db).into(),
1566            }))
1567        }
1568        ResolvedConcreteItem::Function(function) => {
1569            if is_shadowed_by_variable {
1570                return Err(ctx.diagnostics.report(
1571                    path.stable_ptr(ctx.db),
1572                    CallingShadowedFunction {
1573                        shadowed_function_name: path
1574                            .segments(db)
1575                            .elements(db)
1576                            .next()
1577                            .unwrap()
1578                            .identifier(db),
1579                    },
1580                ));
1581            }
1582
1583            // Note there may be n+1 arguments for n parameters, if the last one is a coupon.
1584            let mut args_iter = args_syntax.elements(db);
1585            // Normal parameters
1586            let mut named_args = vec![];
1587            let closure_params = db.concrete_function_closure_params(function)?;
1588            for ty in function_parameter_types(ctx, function)? {
1589                let Some(arg_syntax) = args_iter.next() else {
1590                    continue;
1591                };
1592                named_args.push(compute_named_argument_clause(
1593                    ctx,
1594                    arg_syntax,
1595                    closure_params.get(&ty).copied(),
1596                ));
1597            }
1598
1599            // Maybe coupon
1600            if let Some(arg_syntax) = args_iter.next() {
1601                named_args.push(compute_named_argument_clause(ctx, arg_syntax, None));
1602            }
1603            let call_ptr = syntax.stable_ptr(db);
1604            expr_function_call(ctx, function, named_args, call_ptr, call_ptr.into())
1605        }
1606        _ => Err(ctx.diagnostics.report(
1607            path.stable_ptr(db),
1608            UnexpectedElement { expected: vec![ElementKind::Function], actual: (&item).into() },
1609        )),
1610    }
1611}
1612
1613/// Computes the semantic model of an expression of type [ast::Arg].
1614///
1615/// Returns the value and the optional argument name.
1616pub fn compute_named_argument_clause<'db>(
1617    ctx: &mut ComputationContext<'db, '_>,
1618    arg_syntax: ast::Arg<'db>,
1619    closure_params_tuple_ty: Option<TypeId<'db>>,
1620) -> NamedArg<'db> {
1621    let db = ctx.db;
1622
1623    let mutability =
1624        compute_mutability(ctx.diagnostics, db, &arg_syntax.modifiers(db).elements_vec(db));
1625
1626    let arg_clause = arg_syntax.arg_clause(db);
1627    let (expr, arg_name_identifier) = match arg_clause {
1628        ast::ArgClause::Unnamed(arg_unnamed) => (
1629            handle_possible_closure_expr(ctx, &arg_unnamed.value(db), closure_params_tuple_ty),
1630            None,
1631        ),
1632        ast::ArgClause::Named(arg_named) => (
1633            handle_possible_closure_expr(ctx, &arg_named.value(db), closure_params_tuple_ty),
1634            Some(arg_named.name(db)),
1635        ),
1636        ast::ArgClause::FieldInitShorthand(arg_field_init_shorthand) => {
1637            let name_expr = arg_field_init_shorthand.name(db);
1638            let stable_ptr: ast::ExprPtr<'_> = name_expr.stable_ptr(db).into();
1639            let arg_name_identifier = name_expr.name(db);
1640            let maybe_expr = resolve_variable_by_name(ctx, &arg_name_identifier, stable_ptr);
1641            let expr = wrap_maybe_with_missing(ctx, maybe_expr, stable_ptr);
1642            let expr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
1643            (expr, Some(arg_name_identifier))
1644        }
1645    };
1646    NamedArg(expr, arg_name_identifier, mutability)
1647}
1648
1649/// Handles the semantic computation of a closure expression.
1650/// It processes a closure expression, computes its semantic model,
1651/// allocates it in the expression arena, and ensures that the closure's
1652/// parameter types are conformed if provided.
1653fn handle_possible_closure_expr<'db>(
1654    ctx: &mut ComputationContext<'db, '_>,
1655    expr: &ast::Expr<'db>,
1656    closure_param_types: Option<TypeId<'db>>,
1657) -> ExprAndId<'db> {
1658    if let ast::Expr::Closure(expr_closure) = expr {
1659        let expr = compute_expr_closure_semantic(ctx, expr_closure, closure_param_types);
1660        let expr = wrap_maybe_with_missing(ctx, expr, expr_closure.stable_ptr(ctx.db).into());
1661        let id = ctx.arenas.exprs.alloc(expr.clone());
1662        ExprAndId { expr, id }
1663    } else {
1664        compute_expr_semantic(ctx, expr)
1665    }
1666}
1667
1668pub fn compute_root_expr<'db>(
1669    ctx: &mut ComputationContext<'db, '_>,
1670    syntax: &ast::ExprBlock<'db>,
1671    return_type: TypeId<'db>,
1672) -> Maybe<ExprId> {
1673    // Conform TypeEqual constraints for Associated type bounds.
1674    let inference = &mut ctx.resolver.data.inference_data.inference(ctx.db);
1675    for param in &ctx.resolver.data.generic_params {
1676        let Ok(GenericParam::Impl(imp)) = ctx.db.generic_param_semantic(*param) else {
1677            continue;
1678        };
1679        let Ok(concrete_trait_id) = imp.concrete_trait else {
1680            continue;
1681        };
1682        if crate::corelib::fn_traits(ctx.db).contains(&concrete_trait_id.trait_id(ctx.db)) {
1683            ctx.are_closures_in_context = true;
1684        }
1685    }
1686    let constrains =
1687        ctx.db.generic_params_type_constraints(ctx.resolver.data.generic_params.clone());
1688    inference.conform_generic_params_type_constraints(constrains);
1689
1690    let return_type = ctx.reduce_ty(return_type);
1691    let res = compute_expr_block_semantic(ctx, syntax)?;
1692    let res_ty = ctx.reduce_ty(res.ty());
1693    let res = ctx.arenas.exprs.alloc(res);
1694    let inference = &mut ctx.resolver.inference();
1695    let db = ctx.db;
1696    let _ = inference.conform_ty_for_diag(
1697        res_ty,
1698        return_type,
1699        ctx.diagnostics,
1700        || {
1701            ctx.signature
1702                .map(|s| match s.stable_ptr.lookup(db).ret_ty(db) {
1703                    OptionReturnTypeClause::Empty(_) => syntax.stable_ptr(db).untyped(),
1704                    OptionReturnTypeClause::ReturnTypeClause(return_type_clause) => {
1705                        return_type_clause.ty(db).stable_ptr(db).untyped()
1706                    }
1707                })
1708                .unwrap_or_else(|| syntax.stable_ptr(db).untyped())
1709        },
1710        |actual_ty, expected_ty| WrongReturnType { expected_ty, actual_ty },
1711    );
1712
1713    // Check fully resolved.
1714    inference.finalize(ctx.diagnostics, syntax.stable_ptr(db).untyped());
1715
1716    ctx.apply_inference_rewriter();
1717    if ctx.signature.map(|s| s.is_const) == Some(true) {
1718        validate_const_expr(ctx, res);
1719    }
1720    Ok(res)
1721}
1722
1723/// Computes the semantic model for a list of statements, flattening the result.
1724pub fn compute_statements_semantic_and_extend<'db>(
1725    ctx: &mut ComputationContext<'db, '_>,
1726    statements_syntax: impl Iterator<Item = ast::Statement<'db>>,
1727    statement_ids: &mut Vec<StatementId>,
1728) {
1729    for statement_syntax in statements_syntax {
1730        compute_and_append_statement_semantic(ctx, statement_syntax, statement_ids)
1731            .unwrap_or_default();
1732    }
1733}
1734
1735/// Computes the semantic model of an expression of type [ast::ExprBlock].
1736pub fn compute_expr_block_semantic<'db>(
1737    ctx: &mut ComputationContext<'db, '_>,
1738    syntax: &ast::ExprBlock<'db>,
1739) -> Maybe<Expr<'db>> {
1740    let db = ctx.db;
1741    ctx.run_in_subscope(|new_ctx| {
1742        let (statements, tail) = statements_and_tail(db, syntax.statements(db));
1743        let mut statements_semantic = vec![];
1744        compute_statements_semantic_and_extend(new_ctx, statements, &mut statements_semantic);
1745        let tail_semantic_expr =
1746            tail.map(|tail| compute_tail_semantic(new_ctx, &tail, &mut statements_semantic));
1747        let ty = block_ty(new_ctx, &statements_semantic, &tail_semantic_expr);
1748        Ok(Expr::Block(ExprBlock {
1749            statements: statements_semantic,
1750            tail: tail_semantic_expr.map(|expr| expr.id),
1751            ty,
1752            stable_ptr: syntax.stable_ptr(db).into(),
1753        }))
1754    })
1755}
1756
1757/// The type returned from a block with the given statements and tail.
1758fn block_ty<'db>(
1759    ctx: &ComputationContext<'db, '_>,
1760    statements: &[StatementId],
1761    tail: &Option<ExprAndId<'db>>,
1762) -> TypeId<'db> {
1763    if let Some(tail) = tail {
1764        return tail.ty();
1765    }
1766    let Some(statement) = statements
1767        .iter()
1768        .rev()
1769        .map(|id| &ctx.arenas.statements[*id])
1770        .find(|s| !matches!(s, Statement::Item(_)))
1771    else {
1772        return unit_ty(ctx.db);
1773    };
1774    match statement {
1775        Statement::Item(_) => unreachable!("Was previously filtered out."),
1776        Statement::Let(_) => unit_ty(ctx.db),
1777        Statement::Return(_) | Statement::Break(_) | Statement::Continue(_) => never_ty(ctx.db),
1778        Statement::Expr(expr) => {
1779            let never_ty = never_ty(ctx.db);
1780            if ctx.arenas.exprs[expr.expr].ty() == never_ty { never_ty } else { unit_ty(ctx.db) }
1781        }
1782    }
1783}
1784
1785/// Helper for merging the return types of branch blocks (match or if else).
1786#[derive(Debug, Clone)]
1787struct FlowMergeTypeHelper<'db> {
1788    multi_arm_expr_kind: MultiArmExprKind,
1789    never_type: TypeId<'db>,
1790    final_type: Option<TypeId<'db>>,
1791    /// Whether or not the Helper had a previous type merge error.
1792    had_merge_error: bool,
1793}
1794impl<'db> FlowMergeTypeHelper<'db> {
1795    fn new(db: &'db dyn Database, multi_arm_expr_kind: MultiArmExprKind) -> Self {
1796        Self {
1797            multi_arm_expr_kind,
1798            never_type: never_ty(db),
1799            final_type: None,
1800            had_merge_error: false,
1801        }
1802    }
1803
1804    /// Merge a type into the helper. Returns false on error or if had a previous error.
1805    /// May conform the type to the self.expected_type, if set. Mostly, it is called after
1806    /// the types have already been conformed earlier, in which case it has no external effect.
1807    fn try_merge_types(
1808        &mut self,
1809        db: &'db dyn Database,
1810        diagnostics: &mut SemanticDiagnostics<'db>,
1811        inference: &mut Inference<'db, '_>,
1812        ty: TypeId<'db>,
1813        stable_ptr: SyntaxStablePtrId<'db>,
1814    ) -> bool {
1815        if self.had_merge_error {
1816            return false;
1817        }
1818
1819        // Merging of never type if forbidden in loops but not in other multi-arm expressions.
1820        if (ty == never_ty(db) && self.multi_arm_expr_kind != MultiArmExprKind::Loop)
1821            || ty.is_missing(db)
1822        {
1823            return true;
1824        }
1825
1826        if let Some(pending) = &self.final_type {
1827            if let Err(err_set) = inference.conform_ty(ty, *pending) {
1828                let diag_added = diagnostics.report(
1829                    stable_ptr,
1830                    IncompatibleArms {
1831                        multi_arm_expr_kind: self.multi_arm_expr_kind,
1832                        pending_ty: *pending,
1833                        different_ty: ty,
1834                    },
1835                );
1836                inference.consume_reported_error(err_set, diag_added);
1837                self.had_merge_error = true;
1838                return false;
1839            }
1840        } else {
1841            self.final_type = Some(ty);
1842        }
1843
1844        true
1845    }
1846
1847    /// Returns the merged type.
1848    fn get_final_type(self) -> TypeId<'db> {
1849        self.final_type.unwrap_or(self.never_type)
1850    }
1851}
1852
1853/// Computes the semantic of a match arm pattern and the block expression.
1854fn compute_arm_semantic<'db>(
1855    ctx: &mut ComputationContext<'db, '_>,
1856    expr: &Expr<'db>,
1857    arm_expr_syntax: ast::Expr<'db>,
1858    patterns_syntax: &PatternListOr<'db>,
1859) -> (Vec<PatternAndId<'db>>, ExprAndId<'db>) {
1860    ctx.run_in_subscope(|new_ctx| {
1861        let patterns = compute_pattern_list_or_semantic(new_ctx, expr, patterns_syntax);
1862        let arm_expr = compute_expr_semantic(new_ctx, &arm_expr_syntax);
1863        (patterns, arm_expr)
1864    })
1865}
1866
1867/// Computes the semantic of `PatternListOr` and introducing the pattern variables into the scope.
1868fn compute_pattern_list_or_semantic<'db>(
1869    ctx: &mut ComputationContext<'db, '_>,
1870    expr: &Expr<'db>,
1871    patterns_syntax: &PatternListOr<'db>,
1872) -> Vec<PatternAndId<'db>> {
1873    let db = ctx.db;
1874
1875    // Typecheck the arms's patterns, and introduce the new variables to the subscope.
1876    // Note that if the arm expr is a block, there will be *another* subscope
1877    // for it.
1878    let mut arm_patterns_variables: UnorderedHashMap<SmolStrId<'db>, LocalVariable<'db>> =
1879        UnorderedHashMap::default();
1880
1881    let patterns: Vec<_> = patterns_syntax
1882        .elements(db)
1883        .map(|pattern_syntax| {
1884            let pattern: PatternAndId<'_> =
1885                compute_pattern_semantic(ctx, &pattern_syntax, expr.ty(), &arm_patterns_variables);
1886            let variables = pattern.variables(&ctx.arenas.patterns);
1887            for variable in variables {
1888                match arm_patterns_variables.entry(variable.name) {
1889                    std::collections::hash_map::Entry::Occupied(entry) => {
1890                        let get_location = || variable.stable_ptr.lookup(db).stable_ptr(db);
1891                        let var = entry.get();
1892
1893                        let expected_ty = ctx.reduce_ty(var.ty);
1894                        let actual_ty = ctx.reduce_ty(variable.var.ty);
1895
1896                        let mut has_inference_error = false;
1897                        if !variable.var.ty.is_missing(ctx.db) {
1898                            let inference = &mut ctx.resolver.inference();
1899                            if inference
1900                                .conform_ty_for_diag(
1901                                    actual_ty,
1902                                    expected_ty,
1903                                    ctx.diagnostics,
1904                                    || get_location().untyped(),
1905                                    |actual_ty, expected_ty| WrongType { expected_ty, actual_ty },
1906                                )
1907                                .is_err()
1908                            {
1909                                has_inference_error = true;
1910                            }
1911                        };
1912                        if !has_inference_error && var.is_mut != variable.var.is_mut {
1913                            ctx.diagnostics.report(get_location(), InconsistentBinding);
1914                        }
1915                    }
1916                    std::collections::hash_map::Entry::Vacant(entry) => {
1917                        entry.insert(variable.var.clone());
1918                    }
1919                }
1920            }
1921            pattern
1922        })
1923        .collect();
1924
1925    for (pattern_syntax, pattern) in patterns_syntax.elements(db).zip(patterns.iter()) {
1926        let variables = pattern.variables(&ctx.arenas.patterns);
1927
1928        let mut variable_names_in_pattern = UnorderedHashSet::<_>::default();
1929        for v in variables {
1930            if !variable_names_in_pattern.insert(v.name) {
1931                ctx.diagnostics.report(v.stable_ptr, VariableDefinedMultipleTimesInPattern(v.name));
1932            }
1933            let var_def = Binding::LocalVar(v.var.clone());
1934            let _ = ctx.insert_variable(v.name, var_def);
1935        }
1936
1937        if variable_names_in_pattern.len() != arm_patterns_variables.len() {
1938            ctx.diagnostics.report(pattern_syntax.stable_ptr(db), MissingVariableInPattern);
1939        }
1940    }
1941
1942    patterns
1943}
1944
1945/// Computes the semantic model of an expression of type [ast::ExprMatch].
1946fn compute_expr_match_semantic<'db>(
1947    ctx: &mut ComputationContext<'db, '_>,
1948    syntax: &ast::ExprMatch<'db>,
1949) -> Maybe<Expr<'db>> {
1950    // TODO(yuval): verify exhaustiveness.
1951    let db = ctx.db;
1952    let arms = syntax.arms(db);
1953    let syntax_arms = arms.elements(db);
1954    let expr = compute_expr_semantic(ctx, &syntax.expr(db));
1955    // Run compute_pattern_semantic on every arm, even if other arms failed, to get as many
1956    // diagnostics as possible.
1957    let patterns_and_exprs: Vec<_> = syntax_arms
1958        .map(|syntax_arm| {
1959            compute_arm_semantic(ctx, &expr, syntax_arm.expression(db), &syntax_arm.patterns(db))
1960        })
1961        .collect();
1962    // Unify arm types.
1963    let mut helper = FlowMergeTypeHelper::new(ctx.db, MultiArmExprKind::Match);
1964    for (_, expr) in &patterns_and_exprs {
1965        let expr_ty = ctx.reduce_ty(expr.ty());
1966        if !helper.try_merge_types(
1967            ctx.db,
1968            ctx.diagnostics,
1969            &mut ctx.resolver.inference(),
1970            expr_ty,
1971            expr.stable_ptr().untyped(),
1972        ) {
1973            break;
1974        };
1975    }
1976    // Compute semantic representation of the match arms.
1977    let semantic_arms = patterns_and_exprs
1978        .into_iter()
1979        .map(|(patterns, arm_expr)| MatchArm {
1980            patterns: patterns.iter().map(|pattern| pattern.id).collect(),
1981            expression: arm_expr.id,
1982        })
1983        .collect();
1984    Ok(Expr::Match(ExprMatch {
1985        matched_expr: expr.id,
1986        arms: semantic_arms,
1987        ty: helper.get_final_type(),
1988        stable_ptr: syntax.stable_ptr(db).into(),
1989    }))
1990}
1991
1992/// Computes the semantic model of an expression of type [ast::ExprIf].
1993fn compute_expr_if_semantic<'db>(
1994    ctx: &mut ComputationContext<'db, '_>,
1995    syntax: &ast::ExprIf<'db>,
1996) -> Maybe<Expr<'db>> {
1997    let db = ctx.db;
1998
1999    let (conditions, if_block) = compute_condition_list_semantic(
2000        ctx,
2001        &syntax.conditions(db),
2002        &ast::Expr::Block(syntax.if_block(db)),
2003    );
2004
2005    let (else_block_opt, else_block_ty) = match syntax.else_clause(db) {
2006        ast::OptionElseClause::Empty(_) => (None, unit_ty(ctx.db)),
2007        ast::OptionElseClause::ElseClause(else_clause) => match else_clause.else_block_or_if(db) {
2008            BlockOrIf::Block(block) => {
2009                let else_block = compute_expr_block_semantic(ctx, &block)?;
2010                (Some(else_block.clone()), else_block.ty())
2011            }
2012            BlockOrIf::If(expr_if) => {
2013                let else_if = compute_expr_if_semantic(ctx, &expr_if)?;
2014                (Some(else_if.clone()), else_if.ty())
2015            }
2016        },
2017    };
2018
2019    let mut helper = FlowMergeTypeHelper::new(ctx.db, MultiArmExprKind::If);
2020    let if_block_ty = ctx.reduce_ty(if_block.ty());
2021    let else_block_ty = ctx.reduce_ty(else_block_ty);
2022    let inference = &mut ctx.resolver.inference();
2023    let _ = helper.try_merge_types(
2024        ctx.db,
2025        ctx.diagnostics,
2026        inference,
2027        if_block_ty,
2028        syntax.stable_ptr(db).untyped(),
2029    ) && helper.try_merge_types(
2030        ctx.db,
2031        ctx.diagnostics,
2032        inference,
2033        else_block_ty,
2034        syntax.stable_ptr(db).untyped(),
2035    );
2036    Ok(Expr::If(ExprIf {
2037        conditions,
2038        if_block: if_block.id,
2039        else_block: else_block_opt.map(|else_block| ctx.arenas.exprs.alloc(else_block)),
2040        ty: helper.get_final_type(),
2041        stable_ptr: syntax.stable_ptr(db).into(),
2042    }))
2043}
2044
2045/// Computes the semantic of the given condition list and the given body.
2046///
2047/// Note that pattern variables in the conditions can be used in the body.
2048fn compute_condition_list_semantic<'db>(
2049    ctx: &mut ComputationContext<'db, '_>,
2050    condition_list_syntax: &ConditionListAnd<'db>,
2051    body_syntax: &ast::Expr<'db>,
2052) -> (Vec<Condition>, ExprAndId<'db>) {
2053    let mut conditions = Vec::new();
2054    let conditions_syntax = condition_list_syntax.elements(ctx.db);
2055    conditions.reserve(conditions_syntax.len());
2056
2057    let body = compute_condition_list_semantic_helper(
2058        ctx,
2059        conditions_syntax,
2060        &mut conditions,
2061        body_syntax,
2062    );
2063
2064    (conditions, body)
2065}
2066
2067/// Computes the semantic of the given condition list and the given body.
2068///
2069/// Note that pattern variables in the conditions can be used in the body.
2070fn compute_condition_list_semantic_helper<'db>(
2071    ctx: &mut ComputationContext<'db, '_>,
2072    mut conditions_syntax: impl Iterator<Item = ast::Condition<'db>>,
2073    conditions: &mut Vec<Condition>,
2074    body_syntax: &ast::Expr<'db>,
2075) -> ExprAndId<'db> {
2076    match conditions_syntax.next() {
2077        None => compute_expr_semantic(ctx, body_syntax),
2078        Some(ast::Condition::Let(condition)) => {
2079            let expr = compute_expr_semantic(ctx, &condition.expr(ctx.db));
2080
2081            ctx.run_in_subscope(|new_ctx| {
2082                let patterns =
2083                    compute_pattern_list_or_semantic(new_ctx, &expr, &condition.patterns(ctx.db));
2084                conditions.push(Condition::Let(
2085                    expr.id,
2086                    patterns.iter().map(|pattern| pattern.id).collect(),
2087                ));
2088                compute_condition_list_semantic_helper(
2089                    new_ctx,
2090                    conditions_syntax,
2091                    conditions,
2092                    body_syntax,
2093                )
2094            })
2095        }
2096        Some(ast::Condition::Expr(expr)) => {
2097            conditions.push(Condition::BoolExpr(
2098                compute_bool_condition_semantic(ctx, &expr.expr(ctx.db)).id,
2099            ));
2100
2101            compute_condition_list_semantic_helper(ctx, conditions_syntax, conditions, body_syntax)
2102        }
2103    }
2104}
2105
2106/// Computes the semantic model of an expression of type [ast::ExprLoop].
2107fn compute_expr_loop_semantic<'db>(
2108    ctx: &mut ComputationContext<'db, '_>,
2109    syntax: &ast::ExprLoop<'db>,
2110) -> Maybe<Expr<'db>> {
2111    let db = ctx.db;
2112
2113    let (body, inner_ctx) = compute_loop_body_semantic(
2114        ctx,
2115        syntax.body(db),
2116        InnerContextKind::Loop {
2117            type_merger: FlowMergeTypeHelper::new(db, MultiArmExprKind::Loop),
2118        },
2119    );
2120
2121    let InnerContext { kind: InnerContextKind::Loop { type_merger, .. }, .. } = inner_ctx else {
2122        unreachable!("Expected loop context");
2123    };
2124    Ok(Expr::Loop(ExprLoop {
2125        body,
2126        ty: type_merger.get_final_type(),
2127        stable_ptr: syntax.stable_ptr(db).into(),
2128    }))
2129}
2130
2131/// Computes the semantic model of an expression of type [ast::ExprWhile].
2132fn compute_expr_while_semantic<'db>(
2133    ctx: &mut ComputationContext<'db, '_>,
2134    syntax: &ast::ExprWhile<'db>,
2135) -> Maybe<Expr<'db>> {
2136    let db = ctx.db;
2137
2138    let Some([condition_syntax]) = &syntax.conditions(db).elements(db).collect_array() else {
2139        return Err(ctx.diagnostics.report(syntax.conditions(db).stable_ptr(db), Unsupported));
2140    };
2141
2142    let (condition, body) = match condition_syntax {
2143        ast::Condition::Let(condition) => {
2144            let expr = compute_expr_semantic(ctx, &condition.expr(db));
2145
2146            let (patterns, body) = ctx.run_in_subscope(|new_ctx| {
2147                let patterns =
2148                    compute_pattern_list_or_semantic(new_ctx, &expr, &condition.patterns(db));
2149
2150                let (id, _) =
2151                    compute_loop_body_semantic(new_ctx, syntax.body(db), InnerContextKind::While);
2152                let expr = new_ctx.arenas.exprs[id].clone();
2153                (patterns, ExprAndId { expr, id })
2154            });
2155
2156            (Condition::Let(expr.id, patterns.iter().map(|pattern| pattern.id).collect()), body.id)
2157        }
2158        ast::Condition::Expr(expr) => {
2159            let (body, _inner_ctx) =
2160                compute_loop_body_semantic(ctx, syntax.body(db), InnerContextKind::While);
2161            (Condition::BoolExpr(compute_bool_condition_semantic(ctx, &expr.expr(db)).id), body)
2162        }
2163    };
2164
2165    Ok(Expr::While(ExprWhile {
2166        condition,
2167        body,
2168        ty: unit_ty(ctx.db),
2169        stable_ptr: syntax.stable_ptr(db).into(),
2170    }))
2171}
2172
2173/// Computes the semantic model of an expression of type [ast::ExprFor].
2174fn compute_expr_for_semantic<'db>(
2175    ctx: &mut ComputationContext<'db, '_>,
2176    syntax: &ast::ExprFor<'db>,
2177) -> Maybe<Expr<'db>> {
2178    let db = ctx.db;
2179    let expr_ptr = syntax.expr(db).stable_ptr(db);
2180    let expr = compute_expr_semantic(ctx, &syntax.expr(db));
2181    let into_iterator_trait = ctx.db.core_info().into_iterator_trt;
2182
2183    let (into_iterator_function_id, _, fixed_into_iter_var, into_iter_mutability) =
2184        compute_method_function_call_data(
2185            ctx,
2186            &[into_iterator_trait],
2187            SmolStrId::from(ctx.db, "into_iter"),
2188            expr,
2189            expr_ptr.into(),
2190            None,
2191            |ty, _, inference_errors| {
2192                Some(NoImplementationOfTrait {
2193                    ty,
2194                    inference_errors,
2195                    trait_name: SmolStrId::from(ctx.db, "IntoIterator"),
2196                })
2197            },
2198            |_, _, _| {
2199                unreachable!(
2200                    "There is one explicit trait, IntoIterator trait. No implementations of the \
2201                     trait, caused by both lack of implementation or multiple implementations of \
2202                     the trait, are handled in NoImplementationOfTrait function."
2203                )
2204            },
2205        )?;
2206    let expr_id = fixed_into_iter_var.id;
2207    let into_iter_call = expr_function_call(
2208        ctx,
2209        into_iterator_function_id,
2210        vec![NamedArg(fixed_into_iter_var, None, into_iter_mutability)],
2211        expr_ptr,
2212        expr_ptr,
2213    )?;
2214
2215    let into_iter_variable =
2216        LocalVarLongId(ctx.resolver.module_id, syntax.identifier(db).stable_ptr(db)).intern(ctx.db);
2217
2218    let into_iter_expr = Expr::Var(ExprVar {
2219        var: VarId::Local(into_iter_variable),
2220        ty: into_iter_call.ty(),
2221        stable_ptr: into_iter_call.stable_ptr(),
2222    });
2223    let into_iter_member_path = ExprVarMemberPath::Var(ExprVar {
2224        var: VarId::Local(into_iter_variable),
2225        ty: into_iter_call.ty(),
2226        stable_ptr: into_iter_call.stable_ptr(),
2227    });
2228    let into_iter_expr_id = ctx.arenas.exprs.alloc(into_iter_expr.clone());
2229
2230    let iterator_trait = ctx.db.core_info().iterator_trt;
2231
2232    let (next_function_id, _, _, _) = compute_method_function_call_data(
2233        ctx,
2234        &[iterator_trait],
2235        SmolStrId::from(ctx.db, "next"),
2236        ExprAndId { expr: into_iter_expr, id: into_iter_expr_id },
2237        expr_ptr.into(),
2238        None,
2239        |ty, _, inference_errors| {
2240            Some(NoImplementationOfTrait {
2241                ty,
2242                inference_errors,
2243                trait_name: SmolStrId::from(ctx.db, "Iterator"),
2244            })
2245        },
2246        |_, _, _| {
2247            unreachable!(
2248                "There is one explicit trait, Iterator trait. No implementations of the trait, \
2249                 caused by both lack of implementation or multiple implementations of the trait, \
2250                 are handled in NoImplementationOfTrait function."
2251            )
2252        },
2253    )?;
2254
2255    let next_success_variant =
2256        match db.concrete_function_signature(next_function_id)?.return_type.long(db) {
2257            TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(enm)) => {
2258                assert_eq!(enm.enum_id(db).name(db).long(db), "Option");
2259                db.concrete_enum_variants(*enm).unwrap().into_iter().next().unwrap()
2260            }
2261            _ => unreachable!(),
2262        };
2263    let (body_id, pattern) = ctx.run_in_subscope(|new_ctx| {
2264        let inner_pattern = compute_pattern_semantic(
2265            new_ctx,
2266            &syntax.pattern(db),
2267            next_success_variant.ty,
2268            &UnorderedHashMap::default(),
2269        );
2270        let variables = inner_pattern.variables(&new_ctx.arenas.patterns);
2271        let mut variable_names_in_pattern = UnorderedHashSet::<_>::default();
2272        for v in variables {
2273            if !variable_names_in_pattern.insert(v.name) {
2274                new_ctx
2275                    .diagnostics
2276                    .report(v.stable_ptr, VariableDefinedMultipleTimesInPattern(v.name));
2277            }
2278            let var_def = Binding::LocalVar(v.var.clone());
2279            let _ = new_ctx.insert_variable(v.name, var_def);
2280        }
2281        let (body, _inner_ctx) =
2282            compute_loop_body_semantic(new_ctx, syntax.body(db), InnerContextKind::For);
2283        (body, new_ctx.arenas.patterns.alloc(inner_pattern.pattern))
2284    });
2285    Ok(Expr::For(ExprFor {
2286        into_iter: into_iterator_function_id,
2287        into_iter_member_path,
2288        next_function_id,
2289        expr_id,
2290        pattern,
2291        body: body_id,
2292        ty: unit_ty(ctx.db),
2293        stable_ptr: syntax.stable_ptr(db).into(),
2294    }))
2295}
2296
2297/// Computes the semantic model for a body of a loop.
2298fn compute_loop_body_semantic<'db>(
2299    ctx: &mut ComputationContext<'db, '_>,
2300    syntax: ast::ExprBlock<'db>,
2301    kind: InnerContextKind<'db>,
2302) -> (ExprId, InnerContext<'db>) {
2303    let db: &dyn Database = ctx.db;
2304    ctx.run_in_subscope(|new_ctx| {
2305        let return_type = new_ctx.get_return_type().unwrap();
2306        let old_inner_ctx = new_ctx.inner_ctx.replace(InnerContext { return_type, kind });
2307        let (statements, tail) = statements_and_tail(ctx.db, syntax.statements(db));
2308        let mut statements_semantic = vec![];
2309        compute_statements_semantic_and_extend(new_ctx, statements, &mut statements_semantic);
2310        let tail_semantic_expr =
2311            tail.map(|tail| compute_tail_semantic(new_ctx, &tail, &mut statements_semantic));
2312        if let Some(tail) = &tail_semantic_expr
2313            && !tail.ty().is_missing(db)
2314            && !tail.ty().is_unit(db)
2315            && tail.ty() != never_ty(db)
2316        {
2317            new_ctx.diagnostics.report(tail.deref(), TailExpressionNotAllowedInLoop);
2318        }
2319        let inner_ctx = std::mem::replace(&mut new_ctx.inner_ctx, old_inner_ctx).unwrap();
2320        let body = new_ctx.arenas.exprs.alloc(Expr::Block(ExprBlock {
2321            statements: statements_semantic,
2322            tail: tail_semantic_expr.map(|expr| expr.id),
2323            ty: unit_ty(db),
2324            stable_ptr: syntax.stable_ptr(db).into(),
2325        }));
2326        (body, inner_ctx)
2327    })
2328}
2329
2330/// Computes the semantic model of an expression of type [ast::ExprClosure].
2331fn compute_expr_closure_semantic<'db>(
2332    ctx: &mut ComputationContext<'db, '_>,
2333    syntax: &ast::ExprClosure<'db>,
2334    params_tuple_ty: Option<TypeId<'db>>,
2335) -> Maybe<Expr<'db>> {
2336    ctx.are_closures_in_context = true;
2337    let db = ctx.db;
2338    let (params, ret_ty, body) = ctx.run_in_subscope(|new_ctx| {
2339        let params = if let ClosureParamWrapper::NAry(params) = syntax.wrapper(db) {
2340            function_signature_params(
2341                new_ctx.diagnostics,
2342                new_ctx.db,
2343                new_ctx.resolver,
2344                params.params(db).elements(db),
2345                None,
2346                &mut new_ctx.environment,
2347            )
2348            .into_iter()
2349            .collect()
2350        } else {
2351            vec![]
2352        };
2353        let closure_type =
2354            TypeLongId::Tuple(params.iter().map(|param| param.ty).collect()).intern(new_ctx.db);
2355        if let Some(param_types) = params_tuple_ty
2356            && let Err(err_set) = new_ctx.resolver.inference().conform_ty(closure_type, param_types)
2357        {
2358            new_ctx.resolver.inference().report_on_pending_error(
2359                err_set,
2360                new_ctx.diagnostics,
2361                syntax.stable_ptr(db).untyped(),
2362            );
2363        }
2364
2365        params.iter().filter(|param| param.mutability == Mutability::Reference).for_each(|param| {
2366            new_ctx.diagnostics.report(param.stable_ptr(ctx.db), RefClosureParam);
2367        });
2368
2369        new_ctx.variable_tracker.extend_from_environment(&new_ctx.environment);
2370
2371        let return_type = match syntax.ret_ty(db) {
2372            OptionReturnTypeClause::ReturnTypeClause(ty_syntax) => resolve_type_ex(
2373                new_ctx.db,
2374                new_ctx.diagnostics,
2375                new_ctx.resolver,
2376                &ty_syntax.ty(db),
2377                ResolutionContext::Statement(&mut new_ctx.environment),
2378            ),
2379            OptionReturnTypeClause::Empty(missing) => {
2380                new_ctx.resolver.inference().new_type_var(Some(missing.stable_ptr(db).untyped()))
2381            }
2382        };
2383
2384        let old_inner_ctx = new_ctx
2385            .inner_ctx
2386            .replace(InnerContext { return_type, kind: InnerContextKind::Closure });
2387        let body = match syntax.expr(db) {
2388            ast::Expr::Block(syntax) => compute_closure_body_semantic(new_ctx, syntax),
2389            _ => compute_expr_semantic(new_ctx, &syntax.expr(db)).id,
2390        };
2391        std::mem::replace(&mut new_ctx.inner_ctx, old_inner_ctx).unwrap();
2392        let mut inference = new_ctx.resolver.inference();
2393        let _ = inference.conform_ty_for_diag(
2394            new_ctx.arenas.exprs[body].ty(),
2395            return_type,
2396            new_ctx.diagnostics,
2397            || match syntax.ret_ty(ctx.db).stable_ptr(db).lookup(ctx.db) {
2398                OptionReturnTypeClause::Empty(_) => syntax.expr(db).stable_ptr(db).untyped(),
2399                OptionReturnTypeClause::ReturnTypeClause(return_type_clause) => {
2400                    return_type_clause.ty(ctx.db).stable_ptr(db).untyped()
2401                }
2402            },
2403            |actual_ty, expected_ty| WrongReturnType { expected_ty, actual_ty },
2404        );
2405        (params, return_type, body)
2406    });
2407    let parent_function = match ctx.function_id {
2408        ContextFunction::Global => {
2409            Maybe::Err(ctx.diagnostics.report(syntax.stable_ptr(db), ClosureInGlobalScope))
2410        }
2411        ContextFunction::Function(function_id) => function_id,
2412    };
2413    if matches!(ctx.function_id, ContextFunction::Global) {
2414        ctx.diagnostics.report(syntax.stable_ptr(db), ClosureInGlobalScope);
2415    }
2416
2417    let mut usages = Usages { usages: Default::default() };
2418    let usage = usages.handle_closure(&ctx.arenas, &params, body);
2419    let mut reported = UnorderedHashSet::<_>::default();
2420    // TODO(TomerStarkware): Add support for capturing mutable variables when then we have borrow.
2421    for (captured_var, expr) in
2422        chain!(usage.usage.iter(), usage.snap_usage.iter(), usage.changes.iter())
2423    {
2424        let base_var = &captured_var.base_var();
2425        if ctx.variable_tracker.is_mut(base_var) && reported.insert(expr.stable_ptr()) {
2426            ctx.diagnostics.report(expr.stable_ptr(), MutableCapturedVariable);
2427        }
2428    }
2429
2430    let captured_types = chain!(
2431        chain!(usage.usage.values(), usage.changes.values()).map(|item| item.ty()),
2432        usage.snap_usage.values().map(|item| wrap_in_snapshots(ctx.db, item.ty(), 1)),
2433    )
2434    .collect_vec();
2435
2436    let ty = TypeLongId::Closure(ClosureTypeLongId {
2437        param_tys: params.iter().map(|param| param.ty).collect(),
2438        ret_ty,
2439        captured_types,
2440        parent_function,
2441        wrapper_location: StableLocation::new(syntax.wrapper(db).stable_ptr(db).into()),
2442    })
2443    .intern(ctx.db);
2444
2445    Ok(Expr::ExprClosure(ExprClosure {
2446        body,
2447        params: params.to_vec(),
2448        stable_ptr: syntax.stable_ptr(db).into(),
2449        ty,
2450    }))
2451}
2452
2453/// Computes the semantic model for a body of a closure.
2454fn compute_closure_body_semantic<'db>(
2455    ctx: &mut ComputationContext<'db, '_>,
2456    syntax: ast::ExprBlock<'db>,
2457) -> ExprId {
2458    let db = ctx.db;
2459    let (statements, tail) = statements_and_tail(db, syntax.statements(db));
2460    let mut statements_semantic = vec![];
2461    compute_statements_semantic_and_extend(ctx, statements, &mut statements_semantic);
2462    let tail_semantic_expr =
2463        tail.map(|tail| compute_tail_semantic(ctx, &tail, &mut statements_semantic));
2464    let ty = block_ty(ctx, &statements_semantic, &tail_semantic_expr);
2465    ctx.arenas.exprs.alloc(Expr::Block(ExprBlock {
2466        statements: statements_semantic,
2467        tail: tail_semantic_expr.map(|expr| expr.id),
2468        ty,
2469        stable_ptr: syntax.stable_ptr(db).into(),
2470    }))
2471}
2472
2473/// Computes the semantic model of an expression of type [ast::ExprErrorPropagate].
2474fn compute_expr_error_propagate_semantic<'db>(
2475    ctx: &mut ComputationContext<'db, '_>,
2476    syntax: &ast::ExprErrorPropagate<'db>,
2477) -> Maybe<Expr<'db>> {
2478    let db = ctx.db;
2479
2480    let return_type = ctx.get_return_type().ok_or_else(|| {
2481        ctx.diagnostics.report(
2482            syntax.stable_ptr(db),
2483            UnsupportedOutsideOfFunction(UnsupportedOutsideOfFunctionFeatureName::ErrorPropagate),
2484        )
2485    })?;
2486
2487    let func_err_prop_ty = unwrap_error_propagation_type(ctx.db, return_type).ok_or_else(|| {
2488        ctx.diagnostics.report(syntax.stable_ptr(db), ReturnTypeNotErrorPropagateType)
2489    })?;
2490
2491    // `inner_expr` is the expr inside the `?`.
2492    let inner_expr = match &func_err_prop_ty {
2493        crate::corelib::ErrorPropagationType::Option { .. } => {
2494            compute_expr_semantic(ctx, &syntax.expr(db))
2495        }
2496        crate::corelib::ErrorPropagationType::Result { .. } => {
2497            compute_expr_semantic(ctx, &syntax.expr(db))
2498        }
2499    };
2500    let func_err_variant = func_err_prop_ty.err_variant();
2501
2502    // Runs solver to get as much info as possible about the return type.
2503    ctx.resolver.inference().solve().ok();
2504    let inner_expr_ty = ctx.reduce_ty(inner_expr.ty());
2505    inner_expr_ty.check_not_missing(ctx.db)?;
2506    let inner_expr_err_prop_ty =
2507        unwrap_error_propagation_type(ctx.db, inner_expr_ty).ok_or_else(|| {
2508            ctx.diagnostics
2509                .report(syntax.stable_ptr(db), ErrorPropagateOnNonErrorType(inner_expr_ty))
2510        })?;
2511    let inner_expr_err_variant = inner_expr_err_prop_ty.err_variant();
2512
2513    let conformed_err_variant_ty =
2514        ctx.resolver.inference().conform_ty(func_err_variant.ty, inner_expr_err_variant.ty);
2515    // If conforming the types failed, the next check will fail and a better diagnostic will be
2516    // added.
2517    let err_variant_ty = match conformed_err_variant_ty {
2518        Ok(ty) => ty,
2519        Err(err_set) => {
2520            ctx.resolver.inference().consume_error_without_reporting(err_set);
2521            inner_expr_err_variant.ty
2522        }
2523    };
2524    // TODO(orizi): When auto conversion of types is added, try to convert the error type.
2525    if func_err_variant.ty != err_variant_ty
2526        || func_err_variant.concrete_enum_id.enum_id(ctx.db)
2527            != inner_expr_err_variant.concrete_enum_id.enum_id(ctx.db)
2528    {
2529        ctx.diagnostics.report(
2530            syntax.stable_ptr(db),
2531            IncompatibleErrorPropagateType {
2532                return_ty: return_type,
2533                err_ty: inner_expr_err_variant.ty,
2534            },
2535        );
2536    }
2537    Ok(Expr::PropagateError(ExprPropagateError {
2538        inner: inner_expr.id,
2539        ok_variant: *inner_expr_err_prop_ty.ok_variant(),
2540        err_variant: *inner_expr_err_variant,
2541        func_err_variant: *func_err_variant,
2542        stable_ptr: syntax.stable_ptr(db).into(),
2543    }))
2544}
2545
2546/// Computes the semantic model of an expression of type [ast::ExprIndexed].
2547fn compute_expr_indexed_semantic<'db>(
2548    ctx: &mut ComputationContext<'db, '_>,
2549    syntax: &ast::ExprIndexed<'db>,
2550) -> Maybe<Expr<'db>> {
2551    let db = ctx.db;
2552    let expr = compute_expr_semantic(ctx, &syntax.expr(db));
2553    let index_expr_syntax = &syntax.index_expr(db);
2554    let index_expr = compute_expr_semantic(ctx, index_expr_syntax);
2555    if !ctx.reduce_ty(expr.ty()).is_var_free(ctx.db) {
2556        // Make sure the maximal amount of types is known when trying to access. Ignoring the
2557        // returned value, as any errors will be reported later.
2558        ctx.resolver.inference().solve().ok();
2559    }
2560    let info = ctx.db.core_info();
2561    let candidate_traits = [info.index_trt, info.index_view_trt];
2562    let (function_id, _, fixed_expr, mutability) = compute_method_function_call_data(
2563        ctx,
2564        &candidate_traits[..],
2565        SmolStrId::from(db, "index"),
2566        expr,
2567        syntax.stable_ptr(db).untyped(),
2568        None,
2569        |ty, _, inference_errors| Some(NoImplementationOfIndexOperator { ty, inference_errors }),
2570        |ty, _, _| Some(MultipleImplementationOfIndexOperator(ty)),
2571    )?;
2572
2573    expr_function_call(
2574        ctx,
2575        function_id,
2576        vec![
2577            NamedArg(fixed_expr, None, mutability),
2578            NamedArg(index_expr, None, Mutability::Immutable),
2579        ],
2580        syntax.stable_ptr(db),
2581        index_expr_syntax.stable_ptr(db),
2582    )
2583}
2584
2585/// Computes the data needed for a method function call, and similar exprs (index operator). Method
2586/// call and Index operator differs in the diagnostics they emit. The function returns the
2587/// function_id to call, the trait containing the function, the self argument, with snapshots added
2588/// if needed, and the mutability of the self argument.
2589#[expect(clippy::too_many_arguments)]
2590fn compute_method_function_call_data<'db>(
2591    ctx: &mut ComputationContext<'db, '_>,
2592    candidate_traits: &[TraitId<'db>],
2593    func_name: SmolStrId<'db>,
2594    self_expr: ExprAndId<'db>,
2595    method_syntax: SyntaxStablePtrId<'db>,
2596    generic_args_syntax: Option<Vec<ast::GenericArg<'db>>>,
2597    no_implementation_diagnostic: impl Fn(
2598        TypeId<'db>,
2599        SmolStrId<'db>,
2600        TraitInferenceErrors<'db>,
2601    ) -> Option<SemanticDiagnosticKind<'db>>,
2602    multiple_trait_diagnostic: fn(
2603        TypeId<'db>,
2604        TraitFunctionId<'db>,
2605        TraitFunctionId<'db>,
2606    ) -> Option<SemanticDiagnosticKind<'db>>,
2607) -> Maybe<(FunctionId<'db>, TraitId<'db>, ExprAndId<'db>, Mutability)> {
2608    let expr_ptr = self_expr.stable_ptr();
2609    let self_ty = ctx.reduce_ty(self_expr.ty());
2610    // Inference errors found when looking for candidates. Only relevant in the case of 0 candidates
2611    // found. If >0 candidates are found these are ignored as they may describe, e.g., "errors"
2612    // indicating certain traits/impls/functions don't match, which is OK as we only look for one.
2613    let mut inference_errors = vec![];
2614    let (candidates, mut fixed_expr, fixed_ty, deref_used) = get_method_function_candidates(
2615        ctx,
2616        candidate_traits,
2617        func_name,
2618        self_expr,
2619        method_syntax,
2620        expr_ptr,
2621        self_ty,
2622        &mut inference_errors,
2623    )?;
2624
2625    let trait_function_id = match candidates[..] {
2626        [] => {
2627            return Err(no_implementation_diagnostic(
2628                self_ty,
2629                func_name,
2630                TraitInferenceErrors { traits_and_errors: inference_errors },
2631            )
2632            .map(|diag| ctx.diagnostics.report(method_syntax, diag))
2633            .unwrap_or_else(skip_diagnostic));
2634        }
2635        [trait_function_id] => trait_function_id,
2636        [trait_function_id0, trait_function_id1, ..] => {
2637            return Err(multiple_trait_diagnostic(
2638                fixed_ty,
2639                trait_function_id0,
2640                trait_function_id1,
2641            )
2642            .map(|diag| ctx.diagnostics.report(method_syntax, diag))
2643            .unwrap_or_else(skip_diagnostic));
2644        }
2645    };
2646    let (function_id, n_snapshots) =
2647        infer_impl_by_self(ctx, trait_function_id, fixed_ty, method_syntax, generic_args_syntax)?;
2648
2649    let signature = ctx.db.trait_function_signature(trait_function_id).unwrap();
2650    let first_param = signature.params.first().unwrap();
2651
2652    if deref_used && first_param.mutability == Mutability::Reference {
2653        return Err(no_implementation_diagnostic(
2654            self_ty,
2655            func_name,
2656            TraitInferenceErrors { traits_and_errors: inference_errors },
2657        )
2658        .map(|diag| ctx.diagnostics.report(method_syntax, diag))
2659        .unwrap_or_else(skip_diagnostic));
2660    }
2661
2662    for _ in 0..n_snapshots {
2663        let ty = TypeLongId::Snapshot(fixed_expr.ty()).intern(ctx.db);
2664        let expr = Expr::Snapshot(ExprSnapshot { inner: fixed_expr.id, ty, stable_ptr: expr_ptr });
2665        fixed_expr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
2666    }
2667
2668    Ok((function_id, trait_function_id.trait_id(ctx.db), fixed_expr, first_param.mutability))
2669}
2670
2671/// Return candidates for method functions that match the given arguments.
2672/// Also returns the expression to be used as self for the method call, its type and whether deref
2673/// was used.
2674#[expect(clippy::too_many_arguments)]
2675fn get_method_function_candidates<'db>(
2676    ctx: &mut ComputationContext<'db, '_>,
2677    candidate_traits: &[TraitId<'db>],
2678    func_name: SmolStrId<'db>,
2679    self_expr: ExprAndId<'db>,
2680    method_syntax: SyntaxStablePtrId<'db>,
2681    expr_ptr: ExprPtr<'db>,
2682    self_ty: TypeId<'db>,
2683    inference_errors: &mut Vec<(TraitFunctionId<'db>, InferenceError<'db>)>,
2684) -> Result<
2685    (Vec<TraitFunctionId<'db>>, ExprAndId<'db>, TypeId<'db>, bool),
2686    cairo_lang_diagnostics::DiagnosticAdded,
2687> {
2688    let mut candidates = filter_candidate_traits(
2689        ctx,
2690        inference_errors,
2691        self_ty,
2692        candidate_traits,
2693        func_name,
2694        method_syntax,
2695    );
2696    if !candidates.is_empty() {
2697        return Ok((candidates, self_expr, self_ty, false));
2698    }
2699
2700    let deref_chain = ctx.db.deref_chain(
2701        self_ty,
2702        ctx.resolver.owning_crate_id,
2703        ctx.variable_tracker.is_mut_expr(&self_expr),
2704    )?;
2705
2706    // Find the first deref that contains the method.
2707    let Some(last_deref_index) = deref_chain.derefs.iter().position(|deref_info| {
2708        candidates = filter_candidate_traits(
2709            ctx,
2710            inference_errors,
2711            deref_info.target_ty,
2712            candidate_traits,
2713            func_name,
2714            method_syntax,
2715        );
2716        !candidates.is_empty()
2717    }) else {
2718        // Not found in any deref.
2719        return Ok((candidates, self_expr, self_ty, false));
2720    };
2721
2722    // Found in a deref - generating deref expressions.
2723    let mut fixed_expr = self_expr;
2724    for deref_info in &deref_chain.derefs[0..=last_deref_index] {
2725        let derefed_expr = expr_function_call(
2726            ctx,
2727            deref_info.function_id,
2728            vec![NamedArg(fixed_expr, None, deref_info.self_mutability)],
2729            method_syntax,
2730            expr_ptr,
2731        )?;
2732
2733        fixed_expr =
2734            ExprAndId { expr: derefed_expr.clone(), id: ctx.arenas.exprs.alloc(derefed_expr) };
2735    }
2736    Ok((candidates, fixed_expr, deref_chain.derefs[last_deref_index].target_ty, true))
2737}
2738
2739/// Computes the semantic model of a pattern.
2740/// Note that this pattern will always be "registered" in the arena, so it can be looked up in the
2741/// language server.
2742pub fn compute_pattern_semantic<'db>(
2743    ctx: &mut ComputationContext<'db, '_>,
2744    syntax: &ast::Pattern<'db>,
2745    ty: TypeId<'db>,
2746    or_pattern_variables_map: &UnorderedHashMap<SmolStrId<'db>, LocalVariable<'db>>,
2747) -> PatternAndId<'db> {
2748    let pat = maybe_compute_pattern_semantic(ctx, syntax, ty, or_pattern_variables_map);
2749    let pat = pat.unwrap_or_else(|diag_added| {
2750        Pattern::Missing(PatternMissing {
2751            ty: TypeId::missing(ctx.db, diag_added),
2752            stable_ptr: syntax.stable_ptr(ctx.db),
2753            diag_added,
2754        })
2755    });
2756    let id = ctx.arenas.patterns.alloc(pat.clone());
2757    PatternAndId { pattern: pat, id }
2758}
2759
2760/// Computes the semantic model of a pattern, or None if invalid.
2761fn maybe_compute_pattern_semantic<'db>(
2762    ctx: &mut ComputationContext<'db, '_>,
2763    pattern_syntax: &ast::Pattern<'db>,
2764    ty: TypeId<'db>,
2765    or_pattern_variables_map: &UnorderedHashMap<SmolStrId<'db>, LocalVariable<'db>>,
2766) -> Maybe<Pattern<'db>> {
2767    // TODO(spapini): Check for missing type, and don't reemit an error.
2768    let db = ctx.db;
2769    let ty = ctx.reduce_ty(ty);
2770    let stable_ptr = pattern_syntax.stable_ptr(db).untyped();
2771    let pattern = match pattern_syntax {
2772        ast::Pattern::Underscore(otherwise_pattern) => Pattern::Otherwise(PatternOtherwise {
2773            ty,
2774            stable_ptr: otherwise_pattern.stable_ptr(db),
2775        }),
2776        ast::Pattern::Literal(literal_pattern) => {
2777            let literal = literal_to_semantic(ctx, literal_pattern)?;
2778            Pattern::Literal(PatternLiteral {
2779                literal,
2780                stable_ptr: literal_pattern.stable_ptr(db).into(),
2781            })
2782        }
2783        ast::Pattern::ShortString(short_string_pattern) => {
2784            let literal = short_string_to_semantic(ctx, short_string_pattern)?;
2785            Pattern::Literal(PatternLiteral {
2786                literal,
2787                stable_ptr: short_string_pattern.stable_ptr(db).into(),
2788            })
2789        }
2790        ast::Pattern::String(string_pattern) => {
2791            let string_literal = string_literal_to_semantic(ctx, string_pattern)?;
2792            Pattern::StringLiteral(PatternStringLiteral {
2793                string_literal,
2794                stable_ptr: string_pattern.stable_ptr(db).into(),
2795            })
2796        }
2797        ast::Pattern::Enum(enum_pattern) => {
2798            let path = enum_pattern.path(db);
2799            let item = ctx.resolver.resolve_concrete_path_ex(
2800                ctx.diagnostics,
2801                &path,
2802                NotFoundItemType::Identifier,
2803                ResolutionContext::Statement(&mut ctx.environment),
2804            )?;
2805            let concrete_variant = try_extract_matches!(item, ResolvedConcreteItem::Variant)
2806                .ok_or_else(|| ctx.diagnostics.report(path.stable_ptr(db), NotAVariant))?;
2807
2808            let n_snapshots =
2809                validate_pattern_type_and_args(ctx, pattern_syntax, ty, concrete_variant)?;
2810
2811            // Compute inner pattern.
2812            let inner_ty = wrap_in_snapshots(ctx.db, concrete_variant.ty, n_snapshots);
2813
2814            let inner_pattern = match enum_pattern.pattern(db) {
2815                ast::OptionPatternEnumInnerPattern::Empty(_) => None,
2816                ast::OptionPatternEnumInnerPattern::PatternEnumInnerPattern(p) => {
2817                    let pattern = compute_pattern_semantic(
2818                        ctx,
2819                        &p.pattern(db),
2820                        inner_ty,
2821                        or_pattern_variables_map,
2822                    );
2823                    Some(pattern.id)
2824                }
2825            };
2826
2827            Pattern::EnumVariant(PatternEnumVariant {
2828                variant: concrete_variant,
2829                inner_pattern,
2830                ty,
2831                stable_ptr: enum_pattern.stable_ptr(db).into(),
2832            })
2833        }
2834        ast::Pattern::Path(path) => {
2835            let item_result = ctx.resolver.resolve_generic_path_with_args(
2836                &mut Default::default(),
2837                path,
2838                NotFoundItemType::Identifier,
2839                ResolutionContext::Statement(&mut ctx.environment),
2840            );
2841            if let Ok(ResolvedGenericItem::Variant(_)) = item_result {
2842                // If the path resolves to a variant, it might still be a generic param, so we
2843                // resolve it as a concrete path.
2844                // Resolveing as concrete path first might create vars which will not be inferred so
2845                // we use the generic path first.
2846                let item = ctx.resolver.resolve_concrete_path_ex(
2847                    &mut Default::default(),
2848                    path,
2849                    NotFoundItemType::Identifier,
2850                    ResolutionContext::Statement(&mut ctx.environment),
2851                );
2852                if let Ok(ResolvedConcreteItem::Variant(concrete_variant)) = item {
2853                    let _n_snapshots =
2854                        validate_pattern_type_and_args(ctx, pattern_syntax, ty, concrete_variant)?;
2855                    return Ok(Pattern::EnumVariant(PatternEnumVariant {
2856                        variant: concrete_variant,
2857                        inner_pattern: None,
2858                        ty,
2859                        stable_ptr: path.stable_ptr(db).into(),
2860                    }));
2861                }
2862            }
2863
2864            // Paths with a single element are treated as identifiers, which will result in a
2865            // variable pattern if no matching enum variant is found. If a matching enum
2866            // variant exists, it is resolved to the corresponding concrete variant.
2867            let segments_var = path.segments(db);
2868            let mut segments = segments_var.elements(db);
2869            if segments.len() > 1 {
2870                return Err(ctx.diagnostics.report(path.stable_ptr(ctx.db), Unsupported));
2871            }
2872            // TODO(spapini): Make sure this is a simple identifier. In particular, no generics.
2873            let identifier = segments.next().unwrap().identifier_ast(db);
2874            create_variable_pattern(
2875                ctx,
2876                identifier,
2877                &[],
2878                ty,
2879                path.stable_ptr(db).into(),
2880                or_pattern_variables_map,
2881            )
2882        }
2883        ast::Pattern::Identifier(identifier) => create_variable_pattern(
2884            ctx,
2885            identifier.name(db),
2886            &identifier.modifiers(db).elements_vec(db),
2887            ty,
2888            identifier.stable_ptr(db).into(),
2889            or_pattern_variables_map,
2890        ),
2891        ast::Pattern::Struct(pattern_struct) => {
2892            let pattern_ty = try_extract_matches!(
2893                ctx.resolver.resolve_concrete_path_ex(
2894                    ctx.diagnostics,
2895                    &pattern_struct.path(db),
2896                    NotFoundItemType::Type,
2897                    ResolutionContext::Statement(&mut ctx.environment)
2898                )?,
2899                ResolvedConcreteItem::Type
2900            )
2901            .ok_or_else(|| {
2902                ctx.diagnostics.report(pattern_struct.path(db).stable_ptr(db), NotAType)
2903            })?;
2904            let inference = &mut ctx.resolver.inference();
2905            inference.conform_ty(pattern_ty, peel_snapshots(ctx.db, ty).1.intern(ctx.db)).map_err(
2906                |err_set| inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr),
2907            )?;
2908            let ty = ctx.reduce_ty(ty);
2909            // Peel all snapshot wrappers.
2910            let (n_snapshots, long_ty) = peel_snapshots(ctx.db, ty);
2911
2912            // Check that type is an struct, and get the concrete struct from it.
2913            let concrete_struct_id = try_extract_matches!(long_ty, TypeLongId::Concrete)
2914                .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2915                .ok_or(())
2916                .or_else(|_| {
2917                    // Don't add a diagnostic if the type is missing.
2918                    // A diagnostic should've already been added.
2919                    ty.check_not_missing(ctx.db)?;
2920                    Err(ctx
2921                        .diagnostics
2922                        .report(pattern_struct.stable_ptr(db), UnexpectedStructPattern(ty)))
2923                })?;
2924            let params = pattern_struct.params(db);
2925            let pattern_param_asts = params.elements(db);
2926            let struct_id = concrete_struct_id.struct_id(ctx.db);
2927            let mut members = ctx.db.concrete_struct_members(concrete_struct_id)?.clone();
2928            let mut used_members = UnorderedHashSet::<_>::default();
2929            let mut get_member =
2930                |ctx: &mut ComputationContext<'db, '_>,
2931                 member_name: SmolStrId<'db>,
2932                 stable_ptr: SyntaxStablePtrId<'db>| {
2933                    let member = members.swap_remove(&member_name).on_none(|| {
2934                        ctx.diagnostics.report(
2935                            stable_ptr,
2936                            if used_members.contains(&member_name) {
2937                                StructMemberRedefinition { struct_id, member_name }
2938                            } else {
2939                                NoSuchStructMember { struct_id, member_name }
2940                            },
2941                        );
2942                    })?;
2943                    check_struct_member_is_visible(ctx, &member, stable_ptr, member_name);
2944                    used_members.insert(member_name);
2945                    Some(member)
2946                };
2947            let mut field_patterns = vec![];
2948            let mut has_tail = false;
2949            for pattern_param_ast in pattern_param_asts {
2950                match pattern_param_ast {
2951                    PatternStructParam::Single(single) => {
2952                        let name = single.name(db);
2953                        let name_id = name.text(ctx.db);
2954                        let Some(member) = get_member(ctx, name_id, name.stable_ptr(db).untyped())
2955                        else {
2956                            continue;
2957                        };
2958                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2959                        let pattern = create_variable_pattern(
2960                            ctx,
2961                            name,
2962                            &single.modifiers(db).elements_vec(db),
2963                            ty,
2964                            single.stable_ptr(db).into(),
2965                            or_pattern_variables_map,
2966                        );
2967                        field_patterns.push((ctx.arenas.patterns.alloc(pattern), member));
2968                    }
2969                    PatternStructParam::WithExpr(with_expr) => {
2970                        let name = with_expr.name(db);
2971                        let name_id = name.text(ctx.db);
2972                        let Some(member) = get_member(ctx, name_id, name.stable_ptr(db).untyped())
2973                        else {
2974                            continue;
2975                        };
2976                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2977                        let pattern = compute_pattern_semantic(
2978                            ctx,
2979                            &with_expr.pattern(db),
2980                            ty,
2981                            or_pattern_variables_map,
2982                        );
2983                        field_patterns.push((pattern.id, member));
2984                    }
2985                    PatternStructParam::Tail(_) => {
2986                        has_tail = true;
2987                    }
2988                }
2989            }
2990            if !has_tail {
2991                for (member_name, _) in members.iter() {
2992                    ctx.diagnostics
2993                        .report(pattern_struct.stable_ptr(db), MissingMember(*member_name));
2994                }
2995            }
2996            Pattern::Struct(PatternStruct {
2997                concrete_struct_id,
2998                field_patterns,
2999                ty,
3000                n_snapshots,
3001                stable_ptr: pattern_struct.stable_ptr(db),
3002            })
3003        }
3004        ast::Pattern::Tuple(_) => compute_tuple_like_pattern_semantic(
3005            ctx,
3006            pattern_syntax,
3007            ty,
3008            or_pattern_variables_map,
3009            |ty: TypeId<'db>| UnexpectedTuplePattern(ty),
3010            |expected, actual| WrongNumberOfTupleElements { expected, actual },
3011        ),
3012        ast::Pattern::FixedSizeArray(_) => compute_tuple_like_pattern_semantic(
3013            ctx,
3014            pattern_syntax,
3015            ty,
3016            or_pattern_variables_map,
3017            |ty: TypeId<'db>| UnexpectedFixedSizeArrayPattern(ty),
3018            |expected, actual| WrongNumberOfFixedSizeArrayElements { expected, actual },
3019        ),
3020        ast::Pattern::False(pattern_false) => {
3021            let enum_expr = extract_matches!(
3022                false_literal_expr(ctx, pattern_false.stable_ptr(db).into()),
3023                Expr::EnumVariantCtor
3024            );
3025
3026            extract_concrete_enum_from_pattern_and_validate(
3027                ctx,
3028                pattern_syntax,
3029                ty,
3030                enum_expr.variant.concrete_enum_id,
3031            )?;
3032
3033            Pattern::EnumVariant(PatternEnumVariant {
3034                variant: enum_expr.variant,
3035                stable_ptr: pattern_false.stable_ptr(db).into(),
3036                ty,
3037                inner_pattern: None,
3038            })
3039        }
3040        ast::Pattern::True(pattern_true) => {
3041            let enum_expr = extract_matches!(
3042                true_literal_expr(ctx, pattern_true.stable_ptr(db).into()),
3043                Expr::EnumVariantCtor
3044            );
3045            extract_concrete_enum_from_pattern_and_validate(
3046                ctx,
3047                pattern_syntax,
3048                ty,
3049                enum_expr.variant.concrete_enum_id,
3050            )?;
3051
3052            Pattern::EnumVariant(PatternEnumVariant {
3053                variant: enum_expr.variant,
3054                stable_ptr: pattern_true.stable_ptr(db).into(),
3055                ty,
3056                inner_pattern: None,
3057            })
3058        }
3059    };
3060    let inference = &mut ctx.resolver.inference();
3061    inference.conform_ty(pattern.ty(), ty).map_err(|err_set| {
3062        inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr)
3063    })?;
3064    Ok(pattern)
3065}
3066
3067/// Computes the semantic model of a pattern of a tuple or a fixed size array. Assumes that the
3068/// pattern is one of these types.
3069fn compute_tuple_like_pattern_semantic<'db>(
3070    ctx: &mut ComputationContext<'db, '_>,
3071    pattern_syntax: &ast::Pattern<'db>,
3072    ty: TypeId<'db>,
3073    or_pattern_variables_map: &UnorderedHashMap<SmolStrId<'db>, LocalVariable<'db>>,
3074    unexpected_pattern: fn(TypeId<'db>) -> SemanticDiagnosticKind<'db>,
3075    wrong_number_of_elements: fn(usize, usize) -> SemanticDiagnosticKind<'db>,
3076) -> Pattern<'db> {
3077    let db = ctx.db;
3078    let (n_snapshots, mut long_ty) =
3079        match finalized_snapshot_peeled_ty(ctx, ty, pattern_syntax.stable_ptr(db)) {
3080            Ok(value) => value,
3081            Err(diag_added) => (0, TypeLongId::Missing(diag_added)),
3082        };
3083    // Assert that the pattern is of the same type as the expr.
3084    match (pattern_syntax, &long_ty) {
3085        (_, TypeLongId::Var(_) | TypeLongId::Missing(_))
3086        | (ast::Pattern::Tuple(_), TypeLongId::Tuple(_))
3087        | (ast::Pattern::FixedSizeArray(_), TypeLongId::FixedSizeArray { .. }) => {}
3088        _ => {
3089            long_ty = TypeLongId::Missing(
3090                ctx.diagnostics.report(pattern_syntax.stable_ptr(db), unexpected_pattern(ty)),
3091            );
3092        }
3093    };
3094    let patterns_syntax = match pattern_syntax {
3095        ast::Pattern::Tuple(pattern_tuple) => pattern_tuple.patterns(db).elements_vec(db),
3096        ast::Pattern::FixedSizeArray(pattern_fixed_size_array) => {
3097            pattern_fixed_size_array.patterns(db).elements_vec(db)
3098        }
3099        _ => unreachable!(),
3100    };
3101    let mut inner_tys = match long_ty {
3102        TypeLongId::Tuple(inner_tys) => inner_tys,
3103        TypeLongId::FixedSizeArray { type_id: inner_ty, size } => {
3104            let size = if let ConstValue::Int(value, _) = size.long(db) {
3105                value.to_usize().expect("Fixed sized array size must always be usize.")
3106            } else {
3107                let inference = &mut ctx.resolver.inference();
3108                let expected_size =
3109                    ConstValue::Int(patterns_syntax.len().into(), get_usize_ty(db)).intern(db);
3110                if let Err(err) = inference.conform_const(size, expected_size) {
3111                    let _ = inference.report_on_pending_error(
3112                        err,
3113                        ctx.diagnostics,
3114                        pattern_syntax.stable_ptr(db).untyped(),
3115                    );
3116                }
3117                patterns_syntax.len()
3118            };
3119
3120            [inner_ty].repeat(size)
3121        }
3122        TypeLongId::Var(_) => {
3123            let inference = &mut ctx.resolver.inference();
3124            let (inner_tys, tuple_like_ty) = if matches!(pattern_syntax, ast::Pattern::Tuple(_)) {
3125                let inner_tys: Vec<_> = patterns_syntax
3126                    .iter()
3127                    .map(|e| inference.new_type_var(Some(e.stable_ptr(db).untyped())))
3128                    .collect();
3129                (inner_tys.clone(), TypeLongId::Tuple(inner_tys))
3130            } else {
3131                let var = inference.new_type_var(Some(pattern_syntax.stable_ptr(db).untyped()));
3132                (
3133                    vec![var; patterns_syntax.len()],
3134                    TypeLongId::FixedSizeArray {
3135                        type_id: var,
3136                        size: ConstValue::Int(patterns_syntax.len().into(), get_usize_ty(db))
3137                            .intern(db),
3138                    },
3139                )
3140            };
3141            match inference.conform_ty(long_ty.intern(db), tuple_like_ty.intern(db)) {
3142                Ok(_) => {}
3143                Err(_) => unreachable!("As the type is a var, conforming should always succeed."),
3144            }
3145            inner_tys
3146        }
3147        TypeLongId::Missing(diag_added) => {
3148            let missing = TypeId::missing(db, diag_added);
3149            vec![missing; patterns_syntax.len()]
3150        }
3151        _ => unreachable!(),
3152    };
3153    let size = inner_tys.len();
3154    if size != patterns_syntax.len() {
3155        let diag_added = ctx.diagnostics.report(
3156            pattern_syntax.stable_ptr(db),
3157            wrong_number_of_elements(size, patterns_syntax.len()),
3158        );
3159        let missing = TypeId::missing(db, diag_added);
3160
3161        inner_tys = vec![missing; patterns_syntax.len()];
3162    }
3163    let subpatterns = zip_eq(patterns_syntax, inner_tys)
3164        .map(|(pattern_ast, ty)| {
3165            let ty = wrap_in_snapshots(db, ty, n_snapshots);
3166            compute_pattern_semantic(ctx, &pattern_ast, ty, or_pattern_variables_map).id
3167        })
3168        .collect();
3169    match pattern_syntax {
3170        ast::Pattern::Tuple(syntax) => Pattern::Tuple(PatternTuple {
3171            field_patterns: subpatterns,
3172            ty,
3173            stable_ptr: syntax.stable_ptr(db),
3174        }),
3175        ast::Pattern::FixedSizeArray(syntax) => Pattern::FixedSizeArray(PatternFixedSizeArray {
3176            elements_patterns: subpatterns,
3177            ty,
3178            stable_ptr: syntax.stable_ptr(db),
3179        }),
3180        _ => unreachable!(),
3181    }
3182}
3183
3184/// Validates that the semantic type of an enum pattern is an enum, and returns the concrete enum.
3185fn extract_concrete_enum_from_pattern_and_validate<'db>(
3186    ctx: &mut ComputationContext<'db, '_>,
3187    pattern: &ast::Pattern<'db>,
3188    ty: TypeId<'db>,
3189    concrete_enum_id: ConcreteEnumId<'db>,
3190) -> Maybe<(ConcreteEnumId<'db>, usize)> {
3191    // Peel all snapshot wrappers.
3192    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern.stable_ptr(ctx.db))?;
3193
3194    // Check that type is an enum, and get the concrete enum from it.
3195    let concrete_enum = try_extract_matches!(long_ty, TypeLongId::Concrete)
3196        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Enum))
3197        .ok_or(())
3198        .or_else(|_| {
3199            // Don't add a diagnostic if the type is missing.
3200            // A diagnostic should've already been added.
3201            ty.check_not_missing(ctx.db)?;
3202            Err(ctx.diagnostics.report(pattern.stable_ptr(ctx.db), UnexpectedEnumPattern(ty)))
3203        })?;
3204    // Check that these are the same enums.
3205    let pattern_enum = concrete_enum_id.enum_id(ctx.db);
3206    if pattern_enum != concrete_enum.enum_id(ctx.db) {
3207        return Err(ctx.diagnostics.report(
3208            pattern.stable_ptr(ctx.db),
3209            WrongEnum { expected_enum: concrete_enum.enum_id(ctx.db), actual_enum: pattern_enum },
3210        ));
3211    }
3212    Ok((concrete_enum, n_snapshots))
3213}
3214
3215/// Validates that the semantic type of an enum pattern is an enum.
3216/// After that finds the concrete variant in the pattern, and verifies it has args if needed.
3217/// Returns the number of snapshots.
3218fn validate_pattern_type_and_args<'db>(
3219    ctx: &mut ComputationContext<'db, '_>,
3220    pattern: &ast::Pattern<'db>,
3221    ty: TypeId<'db>,
3222    concrete_variant: ConcreteVariant<'db>,
3223) -> Maybe<usize> {
3224    let db = ctx.db;
3225
3226    let (concrete_enum, n_snapshots) = extract_concrete_enum_from_pattern_and_validate(
3227        ctx,
3228        pattern,
3229        ty,
3230        concrete_variant.concrete_enum_id,
3231    )?;
3232
3233    if let Err(err_set) = ctx.resolver.inference().conform_generic_args(
3234        &concrete_variant.concrete_enum_id.long(db).generic_args,
3235        &concrete_enum.long(db).generic_args,
3236    ) {
3237        let diag_added = ctx.diagnostics.report(
3238            pattern.stable_ptr(db),
3239            InternalInferenceError(InferenceError::TypeKindMismatch {
3240                ty0: ty,
3241                ty1: TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_variant.concrete_enum_id))
3242                    .intern(db),
3243            }),
3244        );
3245        ctx.resolver.inference().consume_reported_error(err_set, diag_added);
3246    };
3247
3248    let generic_variant = db
3249        .variant_semantic(concrete_variant.concrete_enum_id.enum_id(db), concrete_variant.id)
3250        .expect("concrete variant has to exist");
3251
3252    let needs_args = generic_variant.ty != unit_ty(db);
3253    let has_args = matches!(
3254        pattern,
3255        ast::Pattern::Enum(inner)
3256            if matches!(
3257                inner.pattern(db),
3258                ast::OptionPatternEnumInnerPattern::PatternEnumInnerPattern(_)
3259            )
3260    );
3261
3262    if needs_args && !has_args {
3263        let path = match pattern {
3264            ast::Pattern::Enum(pattern) => pattern.path(db),
3265            ast::Pattern::Path(p) => p.clone(),
3266            _ => unreachable!("Expected enum pattern in variant extraction."),
3267        };
3268        ctx.diagnostics.report(pattern.stable_ptr(db), PatternMissingArgs(path));
3269    }
3270
3271    Ok(n_snapshots)
3272}
3273
3274/// Creates a local variable pattern.
3275fn create_variable_pattern<'db>(
3276    ctx: &mut ComputationContext<'db, '_>,
3277    identifier: ast::TerminalIdentifier<'db>,
3278    modifier_list: &[ast::Modifier<'db>],
3279    ty: TypeId<'db>,
3280    stable_ptr: ast::PatternPtr<'db>,
3281    or_pattern_variables_map: &UnorderedHashMap<SmolStrId<'db>, LocalVariable<'db>>,
3282) -> Pattern<'db> {
3283    let db = ctx.db;
3284
3285    let var_id = match or_pattern_variables_map.get(&identifier.text(db)) {
3286        Some(var) => var.id,
3287        None => LocalVarLongId(ctx.resolver.module_id, identifier.stable_ptr(db)).intern(ctx.db),
3288    };
3289    let is_mut = match compute_mutability(ctx.diagnostics, db, modifier_list) {
3290        Mutability::Immutable => false,
3291        Mutability::Mutable => true,
3292        Mutability::Reference => {
3293            ctx.diagnostics.report(identifier.stable_ptr(db), ReferenceLocalVariable);
3294            false
3295        }
3296    };
3297    let allow_unused = ctx
3298        .resolver
3299        .data
3300        .feature_config
3301        .allowed_lints
3302        .contains(&SmolStrId::from(db, UNUSED_VARIABLES));
3303    Pattern::Variable(PatternVariable {
3304        name: identifier.text(db),
3305        var: LocalVariable { id: var_id, ty, is_mut, allow_unused },
3306        stable_ptr,
3307    })
3308}
3309
3310/// Creates a struct constructor semantic expression from its AST.
3311fn struct_ctor_expr<'db>(
3312    ctx: &mut ComputationContext<'db, '_>,
3313    ctor_syntax: &ast::ExprStructCtorCall<'db>,
3314) -> Maybe<Expr<'db>> {
3315    let db = ctx.db;
3316    let path = ctor_syntax.path(db);
3317
3318    // Extract struct.
3319    let ty = resolve_type_ex(
3320        db,
3321        ctx.diagnostics,
3322        ctx.resolver,
3323        &ast::Expr::Path(path.clone()),
3324        ResolutionContext::Statement(&mut ctx.environment),
3325    );
3326    ty.check_not_missing(db)?;
3327
3328    let concrete_struct_id = *try_extract_matches!(ty.long(ctx.db), TypeLongId::Concrete)
3329        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
3330        .ok_or_else(|| ctx.diagnostics.report(path.stable_ptr(db), NotAStruct))?;
3331
3332    if ty.is_phantom(db) {
3333        ctx.diagnostics.report(ctor_syntax.stable_ptr(db), CannotCreateInstancesOfPhantomTypes);
3334    }
3335
3336    let members = db.concrete_struct_members(concrete_struct_id)?;
3337    let mut member_exprs: OrderedHashMap<MemberId<'_>, Option<ExprId>> = OrderedHashMap::default();
3338    let mut base_struct = None;
3339
3340    for (index, arg) in ctor_syntax.arguments(db).arguments(db).elements(db).enumerate() {
3341        // TODO: Extract to a function for results.
3342        match arg {
3343            ast::StructArg::StructArgSingle(arg) => {
3344                let arg_identifier = arg.identifier(db);
3345                let arg_name = arg_identifier.text(db);
3346
3347                // Find struct member by name.
3348                let Some(member) = members.get(&arg_name) else {
3349                    ctx.diagnostics.report(arg_identifier.stable_ptr(db), UnknownMember);
3350                    continue;
3351                };
3352                check_struct_member_is_visible(
3353                    ctx,
3354                    member,
3355                    arg_identifier.stable_ptr(db).untyped(),
3356                    arg_name,
3357                );
3358
3359                // Extract expression.
3360                let arg_expr = match arg.arg_expr(db) {
3361                    ast::OptionStructArgExpr::Empty(_) => {
3362                        let Ok(expr) = resolve_variable_by_name(
3363                            ctx,
3364                            &arg_identifier,
3365                            path.stable_ptr(db).into(),
3366                        ) else {
3367                            // Insert only the member id, for correct duplicate member reporting.
3368                            if member_exprs.insert(member.id, None).is_some() {
3369                                ctx.diagnostics.report(
3370                                    arg_identifier.stable_ptr(db),
3371                                    MemberSpecifiedMoreThanOnce,
3372                                );
3373                            }
3374                            continue;
3375                        };
3376                        ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) }
3377                    }
3378                    ast::OptionStructArgExpr::StructArgExpr(arg_expr) => {
3379                        compute_expr_semantic(ctx, &arg_expr.expr(db))
3380                    }
3381                };
3382
3383                // Insert and check for duplicates.
3384                if member_exprs.insert(member.id, Some(arg_expr.id)).is_some() {
3385                    ctx.diagnostics
3386                        .report(arg_identifier.stable_ptr(db), MemberSpecifiedMoreThanOnce);
3387                }
3388
3389                // Check types.
3390                let inference = &mut ctx.resolver.inference();
3391                if inference
3392                    .conform_ty_for_diag(
3393                        arg_expr.ty(),
3394                        member.ty,
3395                        ctx.diagnostics,
3396                        || arg_expr.stable_ptr().untyped(),
3397                        |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
3398                    )
3399                    .is_err()
3400                {
3401                    continue;
3402                }
3403            }
3404            ast::StructArg::StructArgTail(base_struct_syntax) => {
3405                // TODO(TomerStarkware): remove tail expression from argument list.
3406                if index != ctor_syntax.arguments(db).arguments(db).elements(db).len() - 1 {
3407                    ctx.diagnostics.report(
3408                        base_struct_syntax.stable_ptr(db),
3409                        StructBaseStructExpressionNotLast,
3410                    );
3411                    continue;
3412                }
3413                let base_struct_expr =
3414                    compute_expr_semantic(ctx, &base_struct_syntax.expression(db));
3415                let inference = &mut ctx.resolver.inference();
3416                if inference
3417                    .conform_ty_for_diag(
3418                        base_struct_expr.ty(),
3419                        ty,
3420                        ctx.diagnostics,
3421                        || base_struct_syntax.expression(db).stable_ptr(db).untyped(),
3422                        |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
3423                    )
3424                    .is_err()
3425                {
3426                    continue;
3427                }
3428
3429                base_struct = Some((base_struct_expr.id, base_struct_syntax));
3430            }
3431        };
3432    }
3433
3434    // Report errors for missing members.
3435    let missing_members: Vec<_> = members
3436        .iter()
3437        .filter(|(_, member)| !member_exprs.contains_key(&member.id))
3438        .map(|(member_name, member)| (*member_name, member))
3439        .collect();
3440    for (member_name, member) in missing_members {
3441        if base_struct.is_some() {
3442            check_struct_member_is_visible(
3443                ctx,
3444                member,
3445                base_struct.clone().unwrap().1.stable_ptr(db).untyped(),
3446                member_name,
3447            );
3448        } else {
3449            ctx.diagnostics.report(ctor_syntax.stable_ptr(db), MissingMember(member_name));
3450        }
3451    }
3452    if members.len() == member_exprs.len()
3453        && let Some((_, base_struct_syntax)) = base_struct
3454    {
3455        return Err(ctx
3456            .diagnostics
3457            .report(base_struct_syntax.stable_ptr(db), StructBaseStructExpressionNoEffect));
3458    }
3459    Ok(Expr::StructCtor(ExprStructCtor {
3460        concrete_struct_id,
3461        members: member_exprs.into_iter().filter_map(|(x, y)| Some((y?, x))).collect(),
3462        base_struct: base_struct.map(|(x, _)| x),
3463        ty: TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)).intern(db),
3464        stable_ptr: ctor_syntax.stable_ptr(db).into(),
3465    }))
3466}
3467
3468/// Splits the statements into a tail expression (if exists) and the rest of the statements.
3469/// A tail expression is the last statement in the list, if it is an expression and
3470/// it does not end with a semicolon.
3471fn statements_and_tail<'a>(
3472    db: &'a dyn Database,
3473    syntax: ast::StatementList<'a>,
3474) -> (impl Iterator<Item = ast::Statement<'a>> + 'a, Option<ast::StatementExpr<'a>>) {
3475    let mut statements = syntax.elements(db);
3476    let last = statements.next_back();
3477    if let Some(ast::Statement::Expr(expr)) = &last {
3478        // If the last statement is an expression, check if it is a tail expression.
3479        if matches!(expr.semicolon(db), ast::OptionTerminalSemicolon::Empty(_)) {
3480            return (chain!(statements, None), Some(expr.clone()));
3481        }
3482    }
3483    (chain!(statements, last), None)
3484}
3485
3486/// Creates a new numeric literal expression.
3487fn new_literal_expr<'db>(
3488    ctx: &mut ComputationContext<'db, '_>,
3489    ty: Option<SmolStrId<'db>>,
3490    value: BigInt,
3491    stable_ptr: ExprPtr<'db>,
3492) -> Maybe<ExprLiteral<'db>> {
3493    if let Some(ty_str) = ty {
3494        // Requires specific blocking as `NonZero` now has NumericLiteral support.
3495        if ty_str.long(ctx.db) == "NonZero" {
3496            return Err(ctx.diagnostics.report(
3497                stable_ptr.untyped(),
3498                SemanticDiagnosticKind::WrongNumberOfArguments { expected: 1, actual: 0 },
3499            ));
3500        }
3501        let ty = try_get_core_ty_by_name(ctx.db, ty_str, vec![])
3502            .map_err(|err| ctx.diagnostics.report(stable_ptr.untyped(), err))?;
3503        if let Err(err) = validate_literal(ctx.db, ty, &value) {
3504            ctx.diagnostics.report(stable_ptr, SemanticDiagnosticKind::LiteralError(err));
3505        }
3506        return Ok(ExprLiteral { value, ty, stable_ptr });
3507    };
3508    let ty = ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()));
3509
3510    // Numeric trait.
3511    let trait_id = ctx.db.core_info().numeric_literal_trt;
3512    let generic_args = vec![GenericArgumentId::Type(ty)];
3513    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
3514    let lookup_context = ctx.resolver.impl_lookup_context();
3515    let inference = &mut ctx.resolver.inference();
3516    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
3517
3518    Ok(ExprLiteral { value, ty, stable_ptr })
3519}
3520
3521/// Creates the semantic model of a literal expression from its AST.
3522fn literal_to_semantic<'db>(
3523    ctx: &mut ComputationContext<'db, '_>,
3524    literal_syntax: &ast::TerminalLiteralNumber<'db>,
3525) -> Maybe<ExprLiteral<'db>> {
3526    let db = ctx.db;
3527
3528    let (value, ty) = literal_syntax.numeric_value_and_suffix(db).unwrap_or_default();
3529
3530    new_literal_expr(ctx, ty, value, literal_syntax.stable_ptr(db).into())
3531}
3532
3533/// Creates the semantic model of a short string from its AST.
3534fn short_string_to_semantic<'db>(
3535    ctx: &mut ComputationContext<'db, '_>,
3536    short_string_syntax: &ast::TerminalShortString<'db>,
3537) -> Maybe<ExprLiteral<'db>> {
3538    let db = ctx.db;
3539
3540    let value = short_string_syntax.numeric_value(db).unwrap_or_default();
3541    let suffix = short_string_syntax.suffix(db);
3542    new_literal_expr(
3543        ctx,
3544        suffix.map(|s| SmolStrId::from(db, s)),
3545        value,
3546        short_string_syntax.stable_ptr(db).into(),
3547    )
3548}
3549
3550/// Creates a new string literal expression.
3551fn new_string_literal_expr<'db>(
3552    ctx: &mut ComputationContext<'db, '_>,
3553    value: String,
3554    stable_ptr: ExprPtr<'db>,
3555) -> Maybe<ExprStringLiteral<'db>> {
3556    let ty = ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()));
3557
3558    let trait_id = ctx.db.core_info().string_literal_trt;
3559    let generic_args = vec![GenericArgumentId::Type(ty)];
3560    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
3561    let lookup_context = ctx.resolver.impl_lookup_context();
3562    let inference = &mut ctx.resolver.inference();
3563    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
3564
3565    Ok(ExprStringLiteral { value, ty, stable_ptr })
3566}
3567
3568/// Creates the semantic model of a string literal from its AST.
3569fn string_literal_to_semantic<'db>(
3570    ctx: &mut ComputationContext<'db, '_>,
3571    string_syntax: &ast::TerminalString<'db>,
3572) -> Maybe<ExprStringLiteral<'db>> {
3573    let db = ctx.db;
3574    let stable_ptr = string_syntax.stable_ptr(db);
3575
3576    let value = string_syntax.string_value(db).unwrap_or_default();
3577    // TODO(yuval): support prefixes/suffixes for explicit types?
3578
3579    new_string_literal_expr(ctx, value, stable_ptr.into())
3580}
3581/// Given path, if it's a single segment or a $callsite-prefixed segment,
3582/// returns a tuple of (identifier, is_callsite_prefixed).
3583/// Otherwise, returns None.
3584fn try_extract_identifier_from_path<'a>(
3585    db: &'a dyn Database,
3586    path: &ast::ExprPath<'a>,
3587) -> Option<(TerminalIdentifier<'a>, bool)> {
3588    let segments_var = path.segments(db);
3589    let mut segments = segments_var.elements(db);
3590    require(segments.len() <= 2)?;
3591    let Some(PathSegment::Simple(first)) = segments.next() else {
3592        return None;
3593    };
3594    let Some(second) = segments.next() else {
3595        return Some((first.ident(db), false));
3596    };
3597    let second = try_extract_matches!(second, PathSegment::Simple)?;
3598    if first.identifier(db).long(db) == MACRO_CALL_SITE && path.placeholder_marker(db).is_some() {
3599        Some((second.ident(db), true))
3600    } else {
3601        None
3602    }
3603}
3604
3605/// Given an expression syntax, if it's an identifier, returns it. Otherwise, returns the proper
3606/// error.
3607fn expr_as_identifier<'db>(
3608    ctx: &mut ComputationContext<'db, '_>,
3609    path: &ast::ExprPath<'db>,
3610    db: &'db dyn Database,
3611) -> Maybe<SmolStrId<'db>> {
3612    let segments_var = path.segments(db);
3613    let mut segments = segments_var.elements(db);
3614    if segments.len() == 1 {
3615        Ok(segments.next().unwrap().identifier(db))
3616    } else {
3617        Err(ctx.diagnostics.report(path.stable_ptr(db), InvalidMemberExpression))
3618    }
3619}
3620
3621// TODO(spapini): Consider moving some checks here to the responsibility of the parser.
3622/// Computes the semantic expression for a dot expression.
3623fn dot_expr<'db>(
3624    ctx: &mut ComputationContext<'db, '_>,
3625    lexpr: ExprAndId<'db>,
3626    rhs_syntax: ast::Expr<'db>,
3627    stable_ptr: ast::ExprPtr<'db>,
3628) -> Maybe<Expr<'db>> {
3629    // Find MemberId.
3630    match rhs_syntax {
3631        ast::Expr::Path(expr) => member_access_expr(ctx, lexpr, expr, stable_ptr),
3632        ast::Expr::FunctionCall(expr) => method_call_expr(ctx, lexpr, expr, stable_ptr),
3633        _ => Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(ctx.db), InvalidMemberExpression)),
3634    }
3635}
3636
3637/// Finds all the trait ids usable in the current context.
3638fn traits_in_context<'db>(
3639    ctx: &mut ComputationContext<'db, '_>,
3640) -> OrderedHashMap<TraitId<'db>, LookupItemId<'db>> {
3641    let mut traits = ctx.db.module_usable_trait_ids(ctx.resolver.prelude_submodule()).clone();
3642    traits.extend(
3643        ctx.db.module_usable_trait_ids(ctx.resolver.module_id).iter().map(|(k, v)| (*k, *v)),
3644    );
3645    traits
3646}
3647
3648/// Computes the semantic model of a method call expression (e.g. "expr.method(..)").
3649/// Finds all traits with at least one candidate impl with a matching `self` param.
3650/// If more/less than 1 such trait exists, fails.
3651fn method_call_expr<'db>(
3652    ctx: &mut ComputationContext<'db, '_>,
3653    lexpr: ExprAndId<'db>,
3654    expr: ast::ExprFunctionCall<'db>,
3655    stable_ptr: ast::ExprPtr<'db>,
3656) -> Maybe<Expr<'db>> {
3657    // TODO(spapini): Add ctx.module_id.
3658    // TODO(spapini): Look also in uses.
3659    let db = ctx.db;
3660    let path = expr.path(db);
3661    let Some([segment]) = path.segments(db).elements(db).collect_array() else {
3662        return Err(ctx.diagnostics.report(expr.stable_ptr(ctx.db), InvalidMemberExpression));
3663    };
3664    let func_name = segment.identifier(db);
3665    let generic_args_syntax = segment.generic_args(db);
3666
3667    if !ctx.reduce_ty(lexpr.ty()).is_var_free(ctx.db) {
3668        // Run solver to get as much info on the type as possible.
3669        // Ignore the result of the `solve()` call - the error, if any, will be
3670        // reported later.
3671        ctx.resolver.inference().solve().ok();
3672    }
3673
3674    let mut candidate_traits = traits_in_context(ctx);
3675
3676    // Add traits from impl generic args in the context.
3677    for generic_param in &ctx.resolver.data.generic_params {
3678        if generic_param.kind(ctx.db) == GenericKind::Impl {
3679            let Ok(trait_id) = ctx.db.generic_impl_param_trait(*generic_param) else {
3680                continue;
3681            };
3682            candidate_traits
3683                .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
3684        }
3685    }
3686
3687    // Extracting the possible traits that should be imported, in order to use the method.
3688    let module_id = ctx.resolver.module_id;
3689    let lookup_context = ctx.resolver.impl_lookup_context();
3690    let lexpr_clone = lexpr.clone();
3691    let db = ctx.db;
3692    let (function_id, actual_trait_id, fixed_lexpr, mutability) =
3693        compute_method_function_call_data(
3694            ctx,
3695            candidate_traits.keys().copied().collect_vec().as_slice(),
3696            func_name,
3697            lexpr,
3698            path.stable_ptr(db).untyped(),
3699            generic_args_syntax,
3700            |ty, method_name, inference_errors| {
3701                let relevant_traits = if !inference_errors.is_empty() {
3702                    vec![]
3703                } else {
3704                    match_method_to_traits(
3705                        db,
3706                        ty,
3707                        method_name,
3708                        lookup_context,
3709                        module_id,
3710                        lexpr_clone.stable_ptr().untyped(),
3711                    )
3712                };
3713                Some(CannotCallMethod { ty, method_name, inference_errors, relevant_traits })
3714            },
3715            |_, trait_function_id0, trait_function_id1| {
3716                Some(AmbiguousTrait { trait_function_id0, trait_function_id1 })
3717            },
3718        )
3719        .inspect_err(|_| {
3720            // Getting better diagnostics and usage metrics for the function args.
3721            for arg in expr.arguments(db).arguments(db).elements(db) {
3722                compute_named_argument_clause(ctx, arg, None);
3723            }
3724        })?;
3725
3726    if let Ok(Some(trait_item_info)) = ctx.db.trait_item_info_by_name(actual_trait_id, func_name) {
3727        ctx.resolver.validate_feature_constraints(
3728            ctx.diagnostics,
3729            &segment.identifier_ast(db),
3730            &trait_item_info,
3731        );
3732    }
3733    if let LookupItemId::ModuleItem(item_id) = candidate_traits[&actual_trait_id] {
3734        ctx.resolver.insert_used_use(item_id);
3735    }
3736    ctx.resolver.data.resolved_items.mark_concrete(
3737        ctx.db,
3738        &segment,
3739        ResolvedConcreteItem::Function(function_id),
3740    );
3741
3742    // Note there may be n+1 arguments for n parameters, if the last one is a coupon.
3743    let arguments_var = expr.arguments(db).arguments(db);
3744    let mut args_iter = arguments_var.elements(db);
3745    // Self argument.
3746    let mut named_args = vec![NamedArg(fixed_lexpr, None, mutability)];
3747    // Other arguments.
3748    let closure_params: OrderedHashMap<TypeId<'db>, TypeId<'_>> =
3749        ctx.db.concrete_function_closure_params(function_id)?;
3750    for ty in function_parameter_types(ctx, function_id)?.skip(1) {
3751        let Some(arg_syntax) = args_iter.next() else {
3752            break;
3753        };
3754        named_args.push(compute_named_argument_clause(
3755            ctx,
3756            arg_syntax,
3757            closure_params.get(&ty).copied(),
3758        ));
3759    }
3760
3761    // Maybe coupon
3762    if let Some(arg_syntax) = args_iter.next() {
3763        named_args.push(compute_named_argument_clause(ctx, arg_syntax, None));
3764    }
3765
3766    expr_function_call(ctx, function_id, named_args, expr.stable_ptr(db), stable_ptr)
3767}
3768
3769/// Computes the semantic model of a member access expression (e.g. "expr.member").
3770fn member_access_expr<'db>(
3771    ctx: &mut ComputationContext<'db, '_>,
3772    lexpr: ExprAndId<'db>,
3773    rhs_syntax: ast::ExprPath<'db>,
3774    stable_ptr: ast::ExprPtr<'db>,
3775) -> Maybe<Expr<'db>> {
3776    let db = ctx.db;
3777
3778    // Find MemberId.
3779    let member_name = expr_as_identifier(ctx, &rhs_syntax, db)?;
3780    let (n_snapshots, long_ty) =
3781        finalized_snapshot_peeled_ty(ctx, lexpr.ty(), rhs_syntax.stable_ptr(db))?;
3782
3783    match &long_ty {
3784        TypeLongId::Concrete(_) | TypeLongId::Tuple(_) | TypeLongId::FixedSizeArray { .. } => {
3785            let Some(EnrichedTypeMemberAccess { member, deref_functions }) =
3786                get_enriched_type_member_access(ctx, lexpr.clone(), stable_ptr, member_name)?
3787            else {
3788                return Err(ctx.diagnostics.report(
3789                    rhs_syntax.stable_ptr(db),
3790                    NoSuchTypeMember { ty: long_ty.intern(ctx.db), member_name },
3791                ));
3792            };
3793            check_struct_member_is_visible(
3794                ctx,
3795                &member,
3796                rhs_syntax.stable_ptr(db).untyped(),
3797                member_name,
3798            );
3799            let member_path = match &long_ty {
3800                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id))
3801                    if n_snapshots == 0 && deref_functions.is_empty() =>
3802                {
3803                    lexpr.as_member_path().map(|parent| ExprVarMemberPath::Member {
3804                        parent: Box::new(parent),
3805                        member_id: member.id,
3806                        stable_ptr,
3807                        concrete_struct_id: *concrete_struct_id,
3808                        ty: member.ty,
3809                    })
3810                }
3811                _ => None,
3812            };
3813            let mut derefed_expr: ExprAndId<'_> = lexpr;
3814            for (deref_function, mutability) in &deref_functions {
3815                let cur_expr = expr_function_call(
3816                    ctx,
3817                    *deref_function,
3818                    vec![NamedArg(derefed_expr, None, *mutability)],
3819                    stable_ptr,
3820                    stable_ptr,
3821                )
3822                .unwrap();
3823
3824                derefed_expr =
3825                    ExprAndId { expr: cur_expr.clone(), id: ctx.arenas.exprs.alloc(cur_expr) };
3826            }
3827            let (_, long_ty) =
3828                finalized_snapshot_peeled_ty(ctx, derefed_expr.ty(), rhs_syntax.stable_ptr(db))?;
3829            let derefed_expr_concrete_struct_id = match long_ty {
3830                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
3831                    concrete_struct_id
3832                }
3833                _ => unreachable!(),
3834            };
3835            let ty = if !deref_functions.is_empty() {
3836                member.ty
3837            } else {
3838                wrap_in_snapshots(ctx.db, member.ty, n_snapshots)
3839            };
3840            Ok(Expr::MemberAccess(ExprMemberAccess {
3841                expr: derefed_expr.id,
3842                concrete_struct_id: derefed_expr_concrete_struct_id,
3843                member: member.id,
3844                ty,
3845                member_path,
3846                n_snapshots,
3847                stable_ptr,
3848            }))
3849        }
3850
3851        TypeLongId::Snapshot(_) => {
3852            // TODO(spapini): Handle snapshot members.
3853            Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(db), Unsupported))
3854        }
3855        TypeLongId::Closure(_) => {
3856            Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(db), Unsupported))
3857        }
3858        TypeLongId::ImplType(impl_type_id) => {
3859            unreachable!("Impl type should've been reduced {:?}.", impl_type_id.debug(ctx.db))
3860        }
3861        TypeLongId::Var(_) => Err(ctx.diagnostics.report(
3862            rhs_syntax.stable_ptr(db),
3863            InternalInferenceError(InferenceError::TypeNotInferred(long_ty.intern(ctx.db))),
3864        )),
3865        TypeLongId::GenericParameter(_) | TypeLongId::Coupon(_) => Err(ctx.diagnostics.report(
3866            rhs_syntax.stable_ptr(db),
3867            TypeHasNoMembers { ty: long_ty.intern(ctx.db), member_name },
3868        )),
3869        TypeLongId::Missing(diag_added) => Err(*diag_added),
3870    }
3871}
3872
3873/// Returns the member and the deref operations needed for its access.
3874///
3875/// Enriched members include both direct members (in case of a struct), and members of derefed types
3876/// if the type implements the Deref trait into a struct.
3877fn get_enriched_type_member_access<'db>(
3878    ctx: &mut ComputationContext<'db, '_>,
3879    expr: ExprAndId<'db>,
3880    stable_ptr: ast::ExprPtr<'db>,
3881    accessed_member_name: SmolStrId<'db>,
3882) -> Maybe<Option<EnrichedTypeMemberAccess<'db>>> {
3883    let mut ty = ctx.reduce_ty(expr.ty());
3884    if !ty.is_var_free(ctx.db) {
3885        // Run solver to get as much info on the type as possible.
3886        // Ignore the result of the `solve()` call - the error, if any, will be
3887        // reported later.
3888        ctx.resolver.inference().solve().ok();
3889        ty = ctx.reduce_ty(ty);
3890    }
3891
3892    let is_mut_var = ctx.variable_tracker.is_mut_expr(&expr);
3893    let key = (ty, is_mut_var);
3894    let mut enriched_members = match ctx.resolver.type_enriched_members.entry(key) {
3895        Entry::Occupied(entry) => {
3896            let e = entry.get();
3897            match e.get_member(accessed_member_name) {
3898                Some(value) => return Ok(Some(value)),
3899                None => {
3900                    if e.deref_chain.len() == e.explored_derefs {
3901                        // There's no further exploration to be done, and member was not found.
3902                        return Ok(None);
3903                    }
3904                }
3905            }
3906            // Moving out of the map to call `enrich_members` and insert back with updated value.
3907            entry.swap_remove()
3908        }
3909        Entry::Vacant(_) => {
3910            let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
3911            let members =
3912                if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
3913                    let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
3914                    if let Some(member) = members.get(&accessed_member_name) {
3915                        // Found direct member access - so directly returning it.
3916                        return Ok(Some(EnrichedTypeMemberAccess {
3917                            member: member.clone(),
3918                            deref_functions: vec![],
3919                        }));
3920                    }
3921                    members.iter().map(|(k, v)| (*k, (v.clone(), 0))).collect()
3922                } else {
3923                    Default::default()
3924                };
3925
3926            EnrichedMembers {
3927                members,
3928                deref_chain: ctx
3929                    .db
3930                    .deref_chain(ty, ctx.resolver.owning_crate_id, is_mut_var)?
3931                    .derefs
3932                    .clone(),
3933                explored_derefs: 0,
3934            }
3935        }
3936    };
3937    enrich_members(ctx, &mut enriched_members, stable_ptr, accessed_member_name)?;
3938    let e = ctx.resolver.type_enriched_members.entry(key).or_insert(enriched_members);
3939    Ok(e.get_member(accessed_member_name))
3940}
3941
3942/// Enriches the `enriched_members` with members from "deref"s of the current type.
3943///
3944/// The function will stop enriching if it encounters a cycle in the deref chain, or if the
3945/// requested member is found.
3946fn enrich_members<'db>(
3947    ctx: &mut ComputationContext<'db, '_>,
3948    enriched_members: &mut EnrichedMembers<'db>,
3949    stable_ptr: ast::ExprPtr<'db>,
3950    accessed_member_name: SmolStrId<'db>,
3951) -> Maybe<()> {
3952    let EnrichedMembers { members: enriched, deref_chain, explored_derefs } = enriched_members;
3953
3954    // Add members of derefed types.
3955    for deref_info in deref_chain.iter().skip(*explored_derefs).cloned() {
3956        *explored_derefs += 1;
3957        let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, deref_info.target_ty, stable_ptr)?;
3958        if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
3959            let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
3960            for (member_name, member) in members.iter() {
3961                // Insert member if there is not already a member with the same name.
3962                enriched.entry(*member_name).or_insert_with(|| (member.clone(), *explored_derefs));
3963            }
3964            // If member is contained we can stop the calculation post the lookup.
3965            if members.contains_key(&accessed_member_name) {
3966                // Found member, so exploration isn't done.
3967                break;
3968            }
3969        }
3970    }
3971    Ok::<(), cairo_lang_diagnostics::DiagnosticAdded>(())
3972}
3973
3974/// Peels snapshots from a type and making sure it is fully not a variable type.
3975fn finalized_snapshot_peeled_ty<'db>(
3976    ctx: &mut ComputationContext<'db, '_>,
3977    ty: TypeId<'db>,
3978    stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
3979) -> Maybe<(usize, TypeLongId<'db>)> {
3980    let ty = ctx.reduce_ty(ty);
3981    let (base_snapshots, mut long_ty) = peel_snapshots(ctx.db, ty);
3982    if let TypeLongId::ImplType(impl_type_id) = long_ty {
3983        let inference = &mut ctx.resolver.inference();
3984        let Ok(ty) = inference.reduce_impl_ty(impl_type_id) else {
3985            return Err(ctx
3986                .diagnostics
3987                .report(stable_ptr, InternalInferenceError(InferenceError::TypeNotInferred(ty))));
3988        };
3989        long_ty = ty.long(ctx.db).clone();
3990    }
3991    if matches!(long_ty, TypeLongId::Var(_)) {
3992        // Save some work. ignore the result. The error, if any, will be reported later.
3993        ctx.resolver.inference().solve().ok();
3994        long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
3995    }
3996    let (additional_snapshots, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
3997    Ok((base_snapshots + additional_snapshots, long_ty))
3998}
3999
4000/// Resolves a variable or a constant given a context and a path expression.
4001fn resolve_expr_path<'db>(
4002    ctx: &mut ComputationContext<'db, '_>,
4003    path: &ast::ExprPath<'db>,
4004) -> Maybe<Expr<'db>> {
4005    let db = ctx.db;
4006    if path.segments(db).elements(db).len() == 0 {
4007        return Err(ctx.diagnostics.report(path.stable_ptr(db), Unsupported));
4008    }
4009
4010    // Check if this is a variable.
4011    if let Some((identifier, is_callsite_prefixed)) = try_extract_identifier_from_path(db, path) {
4012        let variable_name = identifier.text(ctx.db);
4013        if let Some(res) = get_binded_expr_by_name(
4014            ctx,
4015            variable_name,
4016            is_callsite_prefixed,
4017            path.stable_ptr(ctx.db).into(),
4018        ) {
4019            match res.clone() {
4020                Expr::Var(expr_var) => {
4021                    let item = ResolvedGenericItem::Variable(expr_var.var);
4022                    ctx.resolver
4023                        .data
4024                        .resolved_items
4025                        .generic
4026                        .insert(identifier.stable_ptr(db), item);
4027                }
4028                Expr::Constant(expr_const) => {
4029                    let item = ResolvedConcreteItem::Constant(expr_const.const_value_id);
4030                    ctx.resolver
4031                        .data
4032                        .resolved_items
4033                        .concrete
4034                        .insert(identifier.stable_ptr(db), item);
4035                }
4036                _ => unreachable!(
4037                    "get_binded_expr_by_name should only return variables or constants"
4038                ),
4039            };
4040            return Ok(res);
4041        }
4042    }
4043
4044    let resolved_item: ResolvedConcreteItem<'_> = ctx.resolver.resolve_concrete_path_ex(
4045        ctx.diagnostics,
4046        path,
4047        NotFoundItemType::Identifier,
4048        ResolutionContext::Statement(&mut ctx.environment),
4049    )?;
4050
4051    match resolved_item {
4052        ResolvedConcreteItem::Constant(const_value_id) => Ok(Expr::Constant(ExprConstant {
4053            const_value_id,
4054            ty: const_value_id.ty(db)?,
4055            stable_ptr: path.stable_ptr(db).into(),
4056        })),
4057
4058        ResolvedConcreteItem::Variant(variant) if variant.ty == unit_ty(db) => {
4059            let stable_ptr = path.stable_ptr(db).into();
4060            let concrete_enum_id = variant.concrete_enum_id;
4061            Ok(semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
4062                variant,
4063                value_expr: unit_expr(ctx, stable_ptr),
4064                ty: TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)).intern(db),
4065                stable_ptr,
4066            }))
4067        }
4068        resolved_item => Err(ctx.diagnostics.report(
4069            path.stable_ptr(db),
4070            UnexpectedElement {
4071                expected: vec![ElementKind::Variable, ElementKind::Constant],
4072                actual: (&resolved_item).into(),
4073            },
4074        )),
4075    }
4076}
4077
4078/// Resolves a variable given a context and a simple name.
4079/// It is used where resolving a variable where only a single identifier is allowed, specifically
4080/// named function call and struct constructor arguments.
4081///
4082/// Reports a diagnostic if the variable was not found.
4083pub fn resolve_variable_by_name<'db>(
4084    ctx: &mut ComputationContext<'db, '_>,
4085    identifier: &ast::TerminalIdentifier<'db>,
4086    stable_ptr: ast::ExprPtr<'db>,
4087) -> Maybe<Expr<'db>> {
4088    let variable_name = identifier.text(ctx.db);
4089    let res = get_binded_expr_by_name(ctx, variable_name, false, stable_ptr).ok_or_else(|| {
4090        ctx.diagnostics.report(identifier.stable_ptr(ctx.db), VariableNotFound(variable_name))
4091    })?;
4092    let item = ResolvedGenericItem::Variable(extract_matches!(&res, Expr::Var).var);
4093    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(ctx.db), item);
4094    Ok(res)
4095}
4096
4097/// Returns the requested variable from the environment if it exists. Returns None otherwise.
4098pub fn get_binded_expr_by_name<'db>(
4099    ctx: &mut ComputationContext<'db, '_>,
4100    variable_name: SmolStrId<'db>,
4101    is_callsite_prefixed: bool,
4102    stable_ptr: ast::ExprPtr<'db>,
4103) -> Option<Expr<'db>> {
4104    let mut maybe_env = Some(&mut *ctx.environment);
4105    let mut cur_offset =
4106        ExpansionOffset::new(stable_ptr.lookup(ctx.db).as_syntax_node().offset(ctx.db));
4107    let mut found_callsite_scope = false;
4108    while let Some(env) = maybe_env {
4109        // If a variable is from an expanded macro placeholder, we need to look for it in the parent
4110        // env.
4111        if let Some(macro_info) = &env.macro_info
4112            && let Some(new_offset) = cur_offset.mapped(&macro_info.mappings)
4113        {
4114            maybe_env = env.parent.as_deref_mut();
4115            cur_offset = new_offset;
4116            continue;
4117        }
4118        if (!is_callsite_prefixed || found_callsite_scope)
4119            && let Some(var) = env.variables.get(&variable_name)
4120        {
4121            env.used_variables.insert(var.id());
4122            return match var {
4123                Binding::LocalItem(local_const) => match local_const.kind.clone() {
4124                    crate::StatementItemKind::Constant(const_value_id, ty) => {
4125                        Some(Expr::Constant(ExprConstant { const_value_id, ty, stable_ptr }))
4126                    }
4127                },
4128                Binding::LocalVar(_) | Binding::Param(_) => {
4129                    Some(Expr::Var(ExprVar { var: var.id(), ty: var.ty(), stable_ptr }))
4130                }
4131            };
4132        }
4133
4134        // Don't look inside a callsite environment unless explicitly stated.
4135        if env.macro_info.is_some() {
4136            if is_callsite_prefixed && !found_callsite_scope {
4137                found_callsite_scope = true;
4138            } else {
4139                break;
4140            }
4141        }
4142        maybe_env = env.parent.as_deref_mut();
4143    }
4144    None
4145}
4146
4147/// Typechecks a function call.
4148fn expr_function_call<'db>(
4149    ctx: &mut ComputationContext<'db, '_>,
4150    function_id: FunctionId<'db>,
4151    mut named_args: Vec<NamedArg<'db>>,
4152    call_ptr: impl Into<SyntaxStablePtrId<'db>>,
4153    stable_ptr: ast::ExprPtr<'db>,
4154) -> Maybe<Expr<'db>> {
4155    let coupon_arg = maybe_pop_coupon_argument(ctx, &mut named_args, function_id);
4156
4157    let signature = ctx.db.concrete_function_signature(function_id)?;
4158
4159    // TODO(spapini): Better location for these diagnostics after the refactor for generics resolve.
4160    if named_args.len() != signature.params.len() {
4161        return Err(ctx.diagnostics.report(
4162            call_ptr,
4163            WrongNumberOfArguments { expected: signature.params.len(), actual: named_args.len() },
4164        ));
4165    }
4166
4167    // Check argument names and types.
4168    check_named_arguments(&named_args, signature, ctx)?;
4169
4170    let inference = &mut ctx.resolver.inference();
4171    let mut args = Vec::new();
4172    for (NamedArg(arg, _name, mutability), param) in
4173        named_args.into_iter().zip(signature.params.iter())
4174    {
4175        let arg_ty = arg.ty();
4176        let param_ty = inference.rewrite(param.ty).no_err();
4177        // Don't add diagnostic if the type is missing (a diagnostic should have already been
4178        // added).
4179        // TODO(lior): Add a test to missing type once possible.
4180        if !arg_ty.is_missing(ctx.db) {
4181            let _ = inference.conform_ty_for_diag(
4182                arg_ty,
4183                param_ty,
4184                ctx.diagnostics,
4185                || arg.stable_ptr().untyped(),
4186                |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
4187            );
4188        }
4189
4190        args.push(if param.mutability == Mutability::Reference {
4191            // Verify the argument is a variable.
4192            let Some(ref_arg) = arg.as_member_path() else {
4193                return Err(ctx.diagnostics.report(arg.deref(), RefArgNotAVariable));
4194            };
4195            // Verify the variable is mutable and hasn't been referenced.
4196            ctx.variable_tracker.report_var_mutability_error(
4197                ctx.db,
4198                ctx.diagnostics,
4199                &ref_arg.base_var(),
4200                arg.deref(),
4201                RefArgNotMutable,
4202            );
4203            // Verify that it is passed explicitly as 'ref'.
4204            if mutability != Mutability::Reference {
4205                ctx.diagnostics.report(arg.deref(), RefArgNotExplicit);
4206            }
4207            ExprFunctionCallArg::Reference(ref_arg)
4208        } else {
4209            // Verify that it is passed without modifiers.
4210            if mutability != Mutability::Immutable {
4211                ctx.diagnostics.report(arg.deref(), ImmutableArgWithModifiers);
4212            }
4213            ExprFunctionCallArg::Value(arg.id)
4214        });
4215    }
4216
4217    let expr_function_call = ExprFunctionCall {
4218        function: function_id,
4219        args,
4220        coupon_arg,
4221        ty: inference.rewrite(signature.return_type).no_err(),
4222        stable_ptr,
4223    };
4224    // Check panicable.
4225    if signature.panicable && has_panic_incompatibility(ctx) {
4226        // TODO(spapini): Delay this check until after inference, to allow resolving specific
4227        //   impls first.
4228        return Err(ctx.diagnostics.report(call_ptr, PanicableFromNonPanicable));
4229    }
4230    Ok(Expr::FunctionCall(expr_function_call))
4231}
4232
4233/// Checks if the last item in `named_args`, has the argument name `__coupon__`, and removes and
4234/// returns it if so.
4235fn maybe_pop_coupon_argument<'db>(
4236    ctx: &mut ComputationContext<'db, '_>,
4237    named_args: &mut Vec<NamedArg<'db>>,
4238    function_id: FunctionId<'db>,
4239) -> Option<ExprId> {
4240    let mut coupon_arg: Option<ExprId> = None;
4241    if let Some(NamedArg(arg, Some(name_terminal), mutability)) = named_args.last() {
4242        let coupons_enabled = are_coupons_enabled(ctx.db, ctx.resolver.module_id);
4243        if name_terminal.text(ctx.db).long(ctx.db) == "__coupon__" && coupons_enabled {
4244            // Check that the argument type is correct.
4245            let expected_ty = TypeLongId::Coupon(function_id).intern(ctx.db);
4246            let arg_ty = arg.ty();
4247            if !arg_ty.is_missing(ctx.db) {
4248                let inference = &mut ctx.resolver.inference();
4249                let _ = inference.conform_ty_for_diag(
4250                    arg_ty,
4251                    expected_ty,
4252                    ctx.diagnostics,
4253                    || arg.stable_ptr().untyped(),
4254                    |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
4255                );
4256            }
4257
4258            // Check that the argument is not mutable/reference.
4259            if *mutability != Mutability::Immutable {
4260                ctx.diagnostics.report(arg.deref(), CouponArgumentNoModifiers);
4261            }
4262
4263            coupon_arg = Some(arg.id);
4264
4265            // Remove the __coupon__ argument from the argument list.
4266            named_args.pop();
4267        }
4268    }
4269    coupon_arg
4270}
4271
4272/// Checks if a panicable function is called from a disallowed context.
4273fn has_panic_incompatibility(ctx: &mut ComputationContext<'_, '_>) -> bool {
4274    if let Some(signature) = ctx.signature {
4275        // If the caller is nopanic, then this is a panic incompatibility.
4276        !signature.panicable
4277    } else {
4278        false
4279    }
4280}
4281
4282/// Checks the correctness of the named arguments, and outputs diagnostics on errors.
4283fn check_named_arguments<'db>(
4284    named_args: &[NamedArg<'db>],
4285    signature: &Signature<'db>,
4286    ctx: &mut ComputationContext<'db, '_>,
4287) -> Maybe<()> {
4288    let mut res: Maybe<()> = Ok(());
4289
4290    // Indicates whether we saw a named argument. Used to report a diagnostic if an unnamed argument
4291    // will follow it.
4292    let mut seen_named_arguments: bool = false;
4293    // Indicates whether a [UnnamedArgumentFollowsNamed] diagnostic was reported. Used to prevent
4294    // multiple similar diagnostics.
4295    let mut reported_unnamed_argument_follows_named: bool = false;
4296    for (NamedArg(arg, name_opt, _mutability), param) in
4297        named_args.iter().zip(signature.params.iter())
4298    {
4299        // Check name.
4300        if let Some(name_terminal) = name_opt {
4301            seen_named_arguments = true;
4302            let name = name_terminal.text(ctx.db);
4303            if param.name != name {
4304                res = Err(ctx.diagnostics.report(
4305                    name_terminal.stable_ptr(ctx.db),
4306                    NamedArgumentMismatch { expected: param.name, found: name },
4307                ));
4308            }
4309        } else if seen_named_arguments && !reported_unnamed_argument_follows_named {
4310            reported_unnamed_argument_follows_named = true;
4311            res = Err(ctx.diagnostics.report(arg.deref(), UnnamedArgumentFollowsNamed));
4312        }
4313    }
4314    res
4315}
4316
4317/// Computes the semantic model for a statement and appends the resulting statement IDs to the
4318/// provided vector.
4319pub fn compute_and_append_statement_semantic<'db>(
4320    ctx: &mut ComputationContext<'db, '_>,
4321    syntax: ast::Statement<'db>,
4322    statements: &mut Vec<StatementId>,
4323) -> Maybe<()> {
4324    // Push the statement's attributes into the context, restored after the computation is resolved.
4325    let feature_restore = ctx.add_features_from_statement(&syntax);
4326
4327    let db = ctx.db;
4328    let _ = match &syntax {
4329        ast::Statement::Let(let_syntax) => {
4330            let rhs_syntax = &let_syntax.rhs(db);
4331            let (rhs_expr, ty) = match let_syntax.type_clause(db) {
4332                ast::OptionTypeClause::Empty(_) => {
4333                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
4334                    let inferred_type = rhs_expr.ty();
4335                    (rhs_expr, inferred_type)
4336                }
4337                ast::OptionTypeClause::TypeClause(type_clause) => {
4338                    let var_type_path = type_clause.ty(db);
4339                    let explicit_type = resolve_type_ex(
4340                        db,
4341                        ctx.diagnostics,
4342                        ctx.resolver,
4343                        &var_type_path,
4344                        ResolutionContext::Statement(&mut ctx.environment),
4345                    );
4346
4347                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
4348                    let inferred_type = ctx.reduce_ty(rhs_expr.ty());
4349                    if !inferred_type.is_missing(db) {
4350                        let inference = &mut ctx.resolver.inference();
4351                        let _ = inference.conform_ty_for_diag(
4352                            inferred_type,
4353                            explicit_type,
4354                            ctx.diagnostics,
4355                            || rhs_syntax.stable_ptr(db).untyped(),
4356                            |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
4357                        );
4358                    }
4359                    (rhs_expr, explicit_type)
4360                }
4361            };
4362            let rhs_expr_id = rhs_expr.id;
4363
4364            let else_clause = match let_syntax.let_else_clause(db) {
4365                ast::OptionLetElseClause::Empty(_) => None,
4366                ast::OptionLetElseClause::LetElseClause(else_clause) => {
4367                    let else_block_syntax = else_clause.else_block(db);
4368                    let else_block_stable_ptr = else_block_syntax.stable_ptr(db);
4369
4370                    let else_block =
4371                        compute_expr_semantic(ctx, &ast::Expr::Block(else_block_syntax));
4372
4373                    if else_block.ty() != never_ty(db) {
4374                        // Report the error, but continue processing.
4375                        ctx.diagnostics.report(else_block_stable_ptr, NonNeverLetElseType);
4376                    }
4377
4378                    Some(else_block.id)
4379                }
4380            };
4381
4382            let pattern = compute_pattern_semantic(
4383                ctx,
4384                &let_syntax.pattern(db),
4385                ty,
4386                &UnorderedHashMap::default(),
4387            );
4388            let variables = pattern.variables(&ctx.arenas.patterns);
4389            let mut variable_names_in_pattern = UnorderedHashSet::<_>::default();
4390            for v in variables {
4391                if !variable_names_in_pattern.insert(v.name) {
4392                    ctx.diagnostics
4393                        .report(v.stable_ptr, VariableDefinedMultipleTimesInPattern(v.name));
4394                }
4395                let var_def = Binding::LocalVar(v.var.clone());
4396                if let Some(old_var) = ctx.environment.variables.insert(v.name, var_def.clone()) {
4397                    if matches!(old_var, Binding::LocalItem(_)) {
4398                        return Err(ctx
4399                            .diagnostics
4400                            .report(v.stable_ptr, MultipleDefinitionforBinding(v.name)));
4401                    }
4402                    add_unused_binding_warning(
4403                        ctx.diagnostics,
4404                        ctx.db,
4405                        &ctx.environment.used_variables,
4406                        v.name,
4407                        &old_var,
4408                        &ctx.resolver.data.feature_config,
4409                    );
4410                }
4411                if ctx.macro_defined_var_unhygienic
4412                    && let Some(macro_info) = &mut ctx.environment.macro_info
4413                {
4414                    macro_info.vars_to_expose.push((v.name, var_def.clone()));
4415                }
4416                let _ = ctx.variable_tracker.insert(var_def);
4417            }
4418            statements.push(ctx.arenas.statements.alloc(semantic::Statement::Let(
4419                semantic::StatementLet {
4420                    pattern: pattern.id,
4421                    expr: rhs_expr_id,
4422                    else_clause,
4423                    stable_ptr: syntax.stable_ptr(db),
4424                },
4425            )));
4426            Ok(()) as Maybe<()>
4427        }
4428        ast::Statement::Expr(stmt_expr_syntax) => {
4429            let expr_syntax = stmt_expr_syntax.expr(db);
4430            if let ast::Expr::InlineMacro(inline_macro_syntax) = &expr_syntax {
4431                expand_macro_for_statement(ctx, inline_macro_syntax, false, statements)?;
4432            } else {
4433                let expr = compute_expr_semantic(ctx, &expr_syntax);
4434                if matches!(stmt_expr_syntax.semicolon(db), ast::OptionTerminalSemicolon::Empty(_))
4435                    && !matches!(
4436                        expr_syntax,
4437                        ast::Expr::Block(_)
4438                            | ast::Expr::If(_)
4439                            | ast::Expr::Match(_)
4440                            | ast::Expr::Loop(_)
4441                            | ast::Expr::While(_)
4442                            | ast::Expr::For(_)
4443                    )
4444                {
4445                    ctx.diagnostics.report_after(expr_syntax.stable_ptr(db), MissingSemicolon);
4446                }
4447                let ty: TypeId<'_> = expr.ty();
4448                if let TypeLongId::Concrete(concrete) = ty.long(db)
4449                    && concrete.is_must_use(db)?
4450                {
4451                    ctx.diagnostics.report(expr_syntax.stable_ptr(db), UnhandledMustUseType(ty));
4452                }
4453                if let Expr::FunctionCall(expr_function_call) = &expr.expr {
4454                    let generic_function_id =
4455                        expr_function_call.function.long(db).function.generic_function;
4456                    if generic_function_id.is_must_use(db)? {
4457                        ctx.diagnostics
4458                            .report(expr_syntax.stable_ptr(db), UnhandledMustUseFunction);
4459                    }
4460                }
4461                statements.push(ctx.arenas.statements.alloc(semantic::Statement::Expr(
4462                    semantic::StatementExpr { expr: expr.id, stable_ptr: syntax.stable_ptr(db) },
4463                )));
4464            }
4465            Ok(())
4466        }
4467        ast::Statement::Continue(continue_syntax) => {
4468            if !ctx.is_inside_loop() {
4469                return Err(ctx
4470                    .diagnostics
4471                    .report(continue_syntax.stable_ptr(db), ContinueOnlyAllowedInsideALoop));
4472            }
4473            statements.push(ctx.arenas.statements.alloc(semantic::Statement::Continue(
4474                semantic::StatementContinue { stable_ptr: syntax.stable_ptr(db) },
4475            )));
4476            Ok(())
4477        }
4478        ast::Statement::Return(return_syntax) => {
4479            let (expr_option, expr_ty, stable_ptr) = match return_syntax.expr_clause(db) {
4480                ast::OptionExprClause::Empty(empty_clause) => {
4481                    (None, unit_ty(db), empty_clause.stable_ptr(db).untyped())
4482                }
4483                ast::OptionExprClause::ExprClause(expr_clause) => {
4484                    let expr_syntax = expr_clause.expr(db);
4485                    let expr = compute_expr_semantic(ctx, &expr_syntax);
4486                    (Some(expr.id), expr.ty(), expr_syntax.stable_ptr(db).untyped())
4487                }
4488            };
4489            let expected_ty = match &ctx.inner_ctx {
4490                None => ctx.get_return_type().ok_or_else(|| {
4491                    ctx.diagnostics.report(
4492                        return_syntax.stable_ptr(db),
4493                        UnsupportedOutsideOfFunction(
4494                            UnsupportedOutsideOfFunctionFeatureName::ReturnStatement,
4495                        ),
4496                    )
4497                })?,
4498                Some(ctx) => ctx.return_type,
4499            };
4500
4501            let expected_ty = ctx.reduce_ty(expected_ty);
4502            let expr_ty = ctx.reduce_ty(expr_ty);
4503            if !expected_ty.is_missing(db) && !expr_ty.is_missing(db) {
4504                let inference = &mut ctx.resolver.inference();
4505                let _ = inference.conform_ty_for_diag(
4506                    expr_ty,
4507                    expected_ty,
4508                    ctx.diagnostics,
4509                    || stable_ptr,
4510                    |actual_ty, expected_ty| WrongReturnType { expected_ty, actual_ty },
4511                );
4512            }
4513            statements.push(ctx.arenas.statements.alloc(semantic::Statement::Return(
4514                semantic::StatementReturn { expr_option, stable_ptr: syntax.stable_ptr(db) },
4515            )));
4516            Ok(())
4517        }
4518        ast::Statement::Break(break_syntax) => {
4519            let (expr_option, ty, stable_ptr) = match break_syntax.expr_clause(db) {
4520                ast::OptionExprClause::Empty(expr_empty) => {
4521                    (None, unit_ty(db), expr_empty.stable_ptr(db).untyped())
4522                }
4523                ast::OptionExprClause::ExprClause(expr_clause) => {
4524                    let expr_syntax = expr_clause.expr(db);
4525                    let expr = compute_expr_semantic(ctx, &expr_syntax);
4526
4527                    (Some(expr.id), expr.ty(), expr.stable_ptr().untyped())
4528                }
4529            };
4530            let ty = ctx.reduce_ty(ty);
4531
4532            if !ctx.is_inside_loop() {
4533                return Err(ctx
4534                    .diagnostics
4535                    .report(break_syntax.stable_ptr(db), BreakOnlyAllowedInsideALoop));
4536            }
4537
4538            if let Some(inner_ctx) = &mut ctx.inner_ctx {
4539                match &mut inner_ctx.kind {
4540                    InnerContextKind::Loop { type_merger, .. } => {
4541                        type_merger.try_merge_types(
4542                            ctx.db,
4543                            ctx.diagnostics,
4544                            &mut ctx.resolver.inference(),
4545                            ty,
4546                            stable_ptr,
4547                        );
4548                    }
4549                    InnerContextKind::While | InnerContextKind::For => {
4550                        if expr_option.is_some() {
4551                            ctx.diagnostics.report(
4552                                break_syntax.stable_ptr(db),
4553                                BreakWithValueOnlyAllowedInsideALoop,
4554                            );
4555                        };
4556                    }
4557                    InnerContextKind::Closure => unreachable!("Not inside a loop."),
4558                }
4559            }
4560
4561            statements.push(ctx.arenas.statements.alloc(semantic::Statement::Break(
4562                semantic::StatementBreak { expr_option, stable_ptr: syntax.stable_ptr(db) },
4563            )));
4564            Ok(())
4565        }
4566        ast::Statement::Item(stmt_item_syntax) => {
4567            let item_syntax = &stmt_item_syntax.item(db);
4568            match item_syntax {
4569                ast::ModuleItem::Constant(const_syntax) => {
4570                    let lhs = const_syntax.type_clause(db).ty(db);
4571                    let rhs = const_syntax.value(db);
4572                    let rhs_expr = compute_expr_semantic(ctx, &rhs);
4573                    let explicit_type = resolve_type_ex(
4574                        db,
4575                        ctx.diagnostics,
4576                        ctx.resolver,
4577                        &lhs,
4578                        ResolutionContext::Statement(&mut ctx.environment),
4579                    );
4580                    let rhs_resolved_expr = resolve_const_expr_and_evaluate(
4581                        db,
4582                        ctx,
4583                        &rhs_expr,
4584                        stmt_item_syntax.stable_ptr(db).untyped(),
4585                        explicit_type,
4586                        false,
4587                    );
4588                    let name_syntax = const_syntax.name(db);
4589                    let name = name_syntax.text(db);
4590                    let rhs_id =
4591                        StatementConstLongId(ctx.resolver.module_id, const_syntax.stable_ptr(db));
4592                    let var_def = Binding::LocalItem(LocalItem {
4593                        id: StatementItemId::Constant(rhs_id.intern(db)),
4594                        kind: StatementItemKind::Constant(
4595                            rhs_resolved_expr,
4596                            rhs_resolved_expr.ty(db)?,
4597                        ),
4598                    });
4599                    add_value_to_statement_environment(
4600                        ctx,
4601                        name,
4602                        var_def,
4603                        name_syntax.stable_ptr(db),
4604                    );
4605                }
4606                ast::ModuleItem::Use(use_syntax) => {
4607                    for leaf in get_all_path_leaves(db, use_syntax) {
4608                        let stable_ptr = leaf.stable_ptr(db);
4609                        let resolved_item = ctx.resolver.resolve_use_path(
4610                            ctx.diagnostics,
4611                            ast::UsePath::Leaf(leaf),
4612                            ResolutionContext::Statement(&mut ctx.environment),
4613                        )?;
4614                        let var_def_id = StatementItemId::Use(
4615                            StatementUseLongId(ctx.resolver.module_id, stable_ptr).intern(db),
4616                        );
4617                        let name = var_def_id.name(db);
4618                        match resolved_item {
4619                            ResolvedGenericItem::GenericConstant(const_id) => {
4620                                let const_value_id = db.constant_const_value(const_id)?;
4621                                let var_def = Binding::LocalItem(LocalItem {
4622                                    id: var_def_id,
4623                                    kind: StatementItemKind::Constant(
4624                                        const_value_id,
4625                                        const_value_id.ty(db)?,
4626                                    ),
4627                                });
4628                                add_value_to_statement_environment(ctx, name, var_def, stable_ptr);
4629                            }
4630                            item @ (ResolvedGenericItem::GenericType(_)
4631                            | ResolvedGenericItem::Module(_)) => {
4632                                add_item_to_statement_environment(ctx, name, item, stable_ptr);
4633                            }
4634                            ResolvedGenericItem::GenericFunction(_)
4635                            | ResolvedGenericItem::GenericTypeAlias(_)
4636                            | ResolvedGenericItem::GenericImplAlias(_)
4637                            | ResolvedGenericItem::Variant(_)
4638                            | ResolvedGenericItem::Trait(_)
4639                            | ResolvedGenericItem::Impl(_)
4640                            | ResolvedGenericItem::Variable(_)
4641                            | ResolvedGenericItem::TraitItem(_)
4642                            | ResolvedGenericItem::Macro(_) => {
4643                                return Err(ctx
4644                                    .diagnostics
4645                                    .report(stable_ptr, UnsupportedUseItemInStatement));
4646                            }
4647                        }
4648                    }
4649                }
4650                ast::ModuleItem::Module(_) => {
4651                    unreachable!("Modules are not supported inside a function.")
4652                }
4653                ast::ModuleItem::FreeFunction(_) => {
4654                    unreachable!("FreeFunction type not supported.")
4655                }
4656                ast::ModuleItem::ExternFunction(_) => {
4657                    unreachable!("ExternFunction type not supported.")
4658                }
4659                ast::ModuleItem::ExternType(_) => unreachable!("ExternType type not supported."),
4660                ast::ModuleItem::Trait(_) => unreachable!("Trait type not supported."),
4661                ast::ModuleItem::Impl(_) => unreachable!("Impl type not supported."),
4662                ast::ModuleItem::ImplAlias(_) => unreachable!("ImplAlias type not supported."),
4663                ast::ModuleItem::Struct(_) => unreachable!("Struct type not supported."),
4664                ast::ModuleItem::Enum(_) => unreachable!("Enum type not supported."),
4665                ast::ModuleItem::TypeAlias(_) => unreachable!("TypeAlias type not supported."),
4666                ast::ModuleItem::InlineMacro(_) => unreachable!("InlineMacro type not supported."),
4667                ast::ModuleItem::HeaderDoc(_) => unreachable!("HeaderDoc type not supported."),
4668                ast::ModuleItem::MacroDeclaration(_) => {
4669                    unreachable!("MacroDeclaration type not supported.")
4670                }
4671                ast::ModuleItem::Missing(_) => unreachable!("Missing type not supported."),
4672            }
4673            statements.push(ctx.arenas.statements.alloc(semantic::Statement::Item(
4674                semantic::StatementItem { stable_ptr: syntax.stable_ptr(db) },
4675            )));
4676            Ok(())
4677        }
4678        // Dianogstics reported on syntax level already.
4679        ast::Statement::Missing(_) => return Err(skip_diagnostic()),
4680    };
4681    ctx.restore_features(feature_restore);
4682    Ok(())
4683}
4684/// Adds an item to the statement environment and reports a diagnostic if the item is already
4685/// defined.
4686fn add_value_to_statement_environment<'db>(
4687    ctx: &mut ComputationContext<'db, '_>,
4688    name: SmolStrId<'db>,
4689    var_def: Binding<'db>,
4690    stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
4691) {
4692    if let Some(old_var) = ctx.insert_variable(name, var_def) {
4693        ctx.diagnostics.report(
4694            stable_ptr,
4695            match old_var {
4696                Binding::LocalItem(_) => MultipleConstantDefinition(name),
4697                Binding::LocalVar(_) | Binding::Param(_) => MultipleDefinitionforBinding(name),
4698            },
4699        );
4700    }
4701}
4702
4703/// Adds an item to the statement environment and reports a diagnostic if the type is already
4704/// defined.
4705fn add_item_to_statement_environment<'db>(
4706    ctx: &mut ComputationContext<'db, '_>,
4707    name: SmolStrId<'db>,
4708    resolved_generic_item: ResolvedGenericItem<'db>,
4709    stable_ptr: impl Into<SyntaxStablePtrId<'db>> + std::marker::Copy,
4710) {
4711    if ctx
4712        .environment
4713        .use_items
4714        .insert(
4715            name,
4716            StatementGenericItemData { resolved_generic_item, stable_ptr: stable_ptr.into() },
4717        )
4718        .is_some()
4719    {
4720        ctx.diagnostics.report(stable_ptr, MultipleGenericItemDefinition(name));
4721    }
4722}
4723
4724/// Computes the semantic model of an expression and reports diagnostics if the expression does not
4725/// evaluate to a boolean value.
4726fn compute_bool_condition_semantic<'db>(
4727    ctx: &mut ComputationContext<'db, '_>,
4728    condition_syntax: &ast::Expr<'db>,
4729) -> ExprAndId<'db> {
4730    let condition = compute_expr_semantic(ctx, condition_syntax);
4731    let inference = &mut ctx.resolver.inference();
4732    let _ = inference.conform_ty_for_diag(
4733        condition.ty(),
4734        core_bool_ty(ctx.db),
4735        ctx.diagnostics,
4736        || condition.stable_ptr().untyped(),
4737        |condition_ty, _expected_ty| ConditionNotBool(condition_ty),
4738    );
4739    condition
4740}
4741
4742/// Validates a struct member is visible and otherwise adds a diagnostic.
4743fn check_struct_member_is_visible<'db>(
4744    ctx: &mut ComputationContext<'db, '_>,
4745    member: &Member<'db>,
4746    stable_ptr: SyntaxStablePtrId<'db>,
4747    member_name: SmolStrId<'db>,
4748) {
4749    let db = ctx.db;
4750    let containing_module_id = member.id.parent_module(db);
4751    if ctx.resolver.ignore_visibility_checks(containing_module_id) {
4752        return;
4753    }
4754    let user_module_id = ctx.resolver.module_id;
4755    if !visibility::peek_visible_in(db, member.visibility, containing_module_id, user_module_id) {
4756        ctx.diagnostics.report(stable_ptr, MemberNotVisible(member_name));
4757    }
4758}
4759
4760/// Verifies that the statement attributes are valid statements attributes, if not a diagnostic is
4761/// reported.
4762fn validate_statement_attributes<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
4763    ctx: &mut ComputationContext<'db, '_>,
4764    item: &Item,
4765) {
4766    let allowed_attributes = ctx.db.allowed_statement_attributes();
4767    let mut diagnostics = vec![];
4768    validate_attributes_flat(
4769        ctx.db,
4770        allowed_attributes,
4771        &OrderedHashSet::default(),
4772        item,
4773        &mut diagnostics,
4774    );
4775    // Translate the plugin diagnostics to semantic diagnostics.
4776    for diagnostic in diagnostics {
4777        ctx.diagnostics
4778            .report(diagnostic.stable_ptr, SemanticDiagnosticKind::UnknownStatementAttribute);
4779    }
4780}
4781
4782/// Gets an iterator with the types of the parameters of the given function.
4783fn function_parameter_types<'db>(
4784    ctx: &mut ComputationContext<'db, '_>,
4785    function: FunctionId<'db>,
4786) -> Maybe<impl Iterator<Item = TypeId<'db>> + use<'db>> {
4787    let signature = ctx.db.concrete_function_signature(function)?;
4788    let param_types = signature.params.iter().map(|param| param.ty);
4789    Ok(param_types)
4790}
4791
4792/// Finds traits which contain a method matching the given name and type.
4793/// This function checks for visible traits in the specified module file and filters
4794/// methods based on their association with the given type and method name.
4795fn match_method_to_traits<'db>(
4796    db: &dyn Database,
4797    ty: semantic::TypeId<'db>,
4798    method_name: SmolStrId<'db>,
4799    lookup_context: ImplLookupContextId<'db>,
4800    module_id: ModuleId<'db>,
4801    stable_ptr: SyntaxStablePtrId<'db>,
4802) -> Vec<String> {
4803    let visible_traits = db
4804        .visible_traits_from_module(module_id)
4805        .unwrap_or_else(|| Arc::new(OrderedHashMap::default()));
4806
4807    visible_traits
4808        .iter()
4809        .filter_map(|(trait_id, path)| {
4810            let mut data = InferenceData::new(InferenceId::NoContext);
4811            let mut inference = data.inference(db);
4812            let trait_function = db.trait_function_by_name(*trait_id, method_name).ok()??;
4813            let (concrete_trait_id, _) = inference.infer_concrete_trait_by_self_without_errors(
4814                trait_function,
4815                ty,
4816                lookup_context,
4817                Some(stable_ptr),
4818            )?;
4819            inference.solve().ok();
4820            match inference.trait_solution_set(
4821                concrete_trait_id,
4822                ImplVarTraitItemMappings::default(),
4823                lookup_context,
4824            ) {
4825                Ok(SolutionSet::Unique(_) | SolutionSet::Ambiguous(_)) => Some(path.clone()),
4826                _ => None,
4827            }
4828        })
4829        .collect()
4830}