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