Skip to main content

cairo_lang_semantic/expr/
compute.rs

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