cairo_lang_semantic/expr/
compute.rs

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