cairo_lang_semantic/expr/
compute.rs

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