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::{get_all_path_leaves, validate_attributes_flat};
12use cairo_lang_defs::diagnostic_utils::StableLocation;
13use cairo_lang_defs::ids::{
14    EnumId, FunctionTitleId, GenericKind, LanguageElementId, LocalVarLongId, LookupItemId,
15    MemberId, ModuleItemId, NamedLanguageElementId, StatementConstLongId, StatementItemId,
16    StatementUseLongId, TraitFunctionId, TraitId, VarId,
17};
18use cairo_lang_defs::plugin::MacroPluginMetadata;
19use cairo_lang_diagnostics::{Maybe, ToOption, skip_diagnostic};
20use cairo_lang_filesystem::cfg::CfgSet;
21use cairo_lang_filesystem::ids::{FileKind, FileLongId, VirtualFile};
22use cairo_lang_proc_macros::DebugWithDb;
23use cairo_lang_syntax::node::ast::{
24    BinaryOperator, BlockOrIf, ClosureParamWrapper, ExprPtr, OptionReturnTypeClause, PatternListOr,
25    PatternStructParam, UnaryOperator,
26};
27use cairo_lang_syntax::node::db::SyntaxGroup;
28use cairo_lang_syntax::node::helpers::{GetIdentifier, PathSegmentEx};
29use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
30use cairo_lang_syntax::node::kind::SyntaxKind;
31use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
32use cairo_lang_utils as utils;
33use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
34use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
35use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
36use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
37use cairo_lang_utils::{LookupIntern, OptionHelper, extract_matches, try_extract_matches};
38use itertools::{Itertools, chain, zip_eq};
39use num_bigint::BigInt;
40use num_traits::ToPrimitive;
41use salsa::InternKey;
42use smol_str::SmolStr;
43use utils::Intern;
44
45use super::inference::canonic::ResultNoErrEx;
46use super::inference::conform::InferenceConform;
47use super::inference::infers::InferenceEmbeddings;
48use super::inference::{Inference, InferenceError};
49use super::objects::*;
50use super::pattern::{
51    Pattern, PatternEnumVariant, PatternFixedSizeArray, PatternLiteral, PatternMissing,
52    PatternOtherwise, PatternTuple, PatternVariable,
53};
54use crate::corelib::{
55    CoreTraitContext, core_binary_operator, core_bool_ty, core_unary_operator, deref_mut_trait,
56    deref_trait, false_literal_expr, get_core_trait, get_usize_ty, never_ty, numeric_literal_trait,
57    true_literal_expr, try_get_core_ty_by_name, unit_expr, unit_ty, unwrap_error_propagation_type,
58};
59use crate::db::SemanticGroup;
60use crate::diagnostic::SemanticDiagnosticKind::{self, *};
61use crate::diagnostic::{
62    ElementKind, MultiArmExprKind, NotFoundItemType, SemanticDiagnostics,
63    SemanticDiagnosticsBuilder, TraitInferenceErrors, UnsupportedOutsideOfFunctionFeatureName,
64};
65use crate::items::constant::{ConstValue, resolve_const_expr_and_evaluate};
66use crate::items::enm::SemanticEnumEx;
67use crate::items::feature_kind::extract_item_feature_config;
68use crate::items::functions::function_signature_params;
69use crate::items::imp::{filter_candidate_traits, infer_impl_by_self};
70use crate::items::modifiers::compute_mutability;
71use crate::items::us::get_use_path_segments;
72use crate::items::visibility;
73use crate::literals::try_extract_minus_literal;
74use crate::resolve::{
75    EnrichedMembers, EnrichedTypeMemberAccess, ResolvedConcreteItem, ResolvedGenericItem, Resolver,
76};
77use crate::semantic::{self, Binding, FunctionId, LocalVariable, TypeId, TypeLongId};
78use crate::substitution::SemanticRewriter;
79use crate::types::{
80    ClosureTypeLongId, ConcreteTypeId, add_type_based_diagnostics, are_coupons_enabled,
81    extract_fixed_size_array_size, peel_snapshots, peel_snapshots_ex,
82    resolve_type_with_environment, verify_fixed_size_array_size, wrap_in_snapshots,
83};
84use crate::usage::Usages;
85use crate::{
86    ConcreteEnumId, GenericArgumentId, GenericParam, LocalItem, Member, Mutability, Parameter,
87    PatternStringLiteral, PatternStruct, Signature, StatementItemKind,
88};
89
90/// Expression with its id.
91#[derive(Debug, Clone)]
92pub struct ExprAndId {
93    pub expr: Expr,
94    pub id: ExprId,
95}
96impl Deref for ExprAndId {
97    type Target = Expr;
98
99    fn deref(&self) -> &Self::Target {
100        &self.expr
101    }
102}
103
104#[derive(Debug, Clone)]
105pub struct PatternAndId {
106    pub pattern: Pattern,
107    pub id: PatternId,
108}
109impl Deref for PatternAndId {
110    type Target = Pattern;
111
112    fn deref(&self) -> &Self::Target {
113        &self.pattern
114    }
115}
116
117/// Named argument in a function call.
118#[derive(Debug, Clone)]
119pub struct NamedArg(ExprAndId, Option<ast::TerminalIdentifier>, Mutability);
120
121pub enum ContextFunction {
122    Global,
123    Function(Maybe<FunctionId>),
124}
125
126/// Context inside loops or closures.
127#[derive(Debug, Clone)]
128enum InnerContext {
129    /// Context inside a `loop`
130    Loop { type_merger: FlowMergeTypeHelper },
131    /// Context inside a `while` loop
132    While,
133    /// Context inside a `for` loop
134    For,
135    /// Context inside a `closure`
136    Closure { return_type: TypeId },
137}
138
139/// Context for computing the semantic model of expression trees.
140pub struct ComputationContext<'ctx> {
141    pub db: &'ctx dyn SemanticGroup,
142    pub diagnostics: &'ctx mut SemanticDiagnostics,
143    pub resolver: Resolver<'ctx>,
144    signature: Option<&'ctx Signature>,
145    environment: Box<Environment>,
146    /// Arenas of semantic objects.
147    pub arenas: Arenas,
148    function_id: ContextFunction,
149    /// Definitions of semantic variables.
150    pub semantic_defs: UnorderedHashMap<semantic::VarId, semantic::Binding>,
151    inner_ctx: Option<InnerContext>,
152    cfg_set: Arc<CfgSet>,
153    /// whether to look for closures when calling variables.
154    /// TODO(TomerStarkware): Remove this once we disallow calling shadowed functions.
155    are_closures_in_context: bool,
156}
157impl<'ctx> ComputationContext<'ctx> {
158    pub fn new(
159        db: &'ctx dyn SemanticGroup,
160        diagnostics: &'ctx mut SemanticDiagnostics,
161        resolver: Resolver<'ctx>,
162        signature: Option<&'ctx Signature>,
163        environment: Environment,
164        function_id: ContextFunction,
165    ) -> Self {
166        let semantic_defs =
167            environment.variables.values().by_ref().map(|var| (var.id(), var.clone())).collect();
168        let cfg_set =
169            resolver.settings.cfg_set.clone().map(Arc::new).unwrap_or_else(|| db.cfg_set());
170        Self {
171            db,
172            diagnostics,
173            resolver,
174            signature,
175            environment: Box::new(environment),
176            arenas: Default::default(),
177            function_id,
178            semantic_defs,
179            inner_ctx: None,
180            cfg_set,
181            are_closures_in_context: false,
182        }
183    }
184
185    /// Runs a function with a modified context, with a new environment for a subscope.
186    /// This environment holds no variable of its own, but points to the current environment as a
187    /// parent.
188    /// Used for block expressions.
189    fn run_in_subscope<T, F>(&mut self, f: F) -> T
190    where
191        F: FnOnce(&mut Self) -> T,
192    {
193        // Push an environment to the stack.
194        let new_environment = Box::new(Environment::empty());
195        let old_environment = std::mem::replace(&mut self.environment, new_environment);
196        self.environment.parent = Some(old_environment);
197
198        let res = f(self);
199
200        // Pop the environment from the stack.
201        let parent = self.environment.parent.take();
202        for (var_name, var) in std::mem::take(&mut self.environment.variables) {
203            self.add_unused_binding_warning(&var_name, &var);
204        }
205        // Adds warning for unused items if required.
206        for (ty_name, statement_ty) in std::mem::take(&mut self.environment.use_items) {
207            if !self.environment.used_use_items.contains(&ty_name) && !ty_name.starts_with('_') {
208                self.diagnostics.report(statement_ty.stable_ptr, UnusedUse);
209            }
210        }
211        self.environment = parent.unwrap();
212        res
213    }
214
215    /// Adds warning for unused bindings if required.
216    fn add_unused_binding_warning(&mut self, var_name: &str, var: &Binding) {
217        if !self.environment.used_variables.contains(&var.id()) && !var_name.starts_with('_') {
218            match var {
219                Binding::LocalItem(local_item) => match local_item.id {
220                    StatementItemId::Constant(_) => {
221                        self.diagnostics.report(var.stable_ptr(self.db.upcast()), UnusedConstant);
222                    }
223                    StatementItemId::Use(_) => {
224                        self.diagnostics.report(var.stable_ptr(self.db.upcast()), UnusedUse);
225                    }
226                },
227                Binding::LocalVar(_) | Binding::Param(_) => {
228                    self.diagnostics.report(var.stable_ptr(self.db.upcast()), UnusedVariable);
229                }
230            }
231        }
232    }
233
234    /// Returns [Self::signature] if it exists. Otherwise, reports a diagnostic and returns `Err`.
235    fn get_signature(
236        &mut self,
237        stable_ptr: SyntaxStablePtrId,
238        feature_name: UnsupportedOutsideOfFunctionFeatureName,
239    ) -> Maybe<&'ctx Signature> {
240        if let Some(signature) = self.signature {
241            return Ok(signature);
242        }
243
244        Err(self.diagnostics.report(stable_ptr, UnsupportedOutsideOfFunction(feature_name)))
245    }
246
247    fn reduce_ty(&mut self, ty: TypeId) -> TypeId {
248        self.resolver.inference().rewrite(ty).no_err()
249    }
250
251    /// Applies inference rewriter to all the expressions in the computation context, and adds
252    /// errors on types from the final expressions.
253    pub fn apply_inference_rewriter_to_exprs(&mut self) {
254        let mut analyzed_types = UnorderedHashSet::<_>::default();
255        for (_id, expr) in self.arenas.exprs.iter_mut() {
256            self.resolver.inference().internal_rewrite(expr).no_err();
257            // Adding an error only once per type.
258            if analyzed_types.insert(expr.ty()) {
259                add_type_based_diagnostics(self.db, self.diagnostics, expr.ty(), &*expr);
260            }
261        }
262    }
263
264    /// Applies inference rewriter to all the rewritable things in the computation context.
265    fn apply_inference_rewriter(&mut self) {
266        self.apply_inference_rewriter_to_exprs();
267        for (_id, pattern) in self.arenas.patterns.iter_mut() {
268            self.resolver.inference().internal_rewrite(pattern).no_err();
269        }
270        for (_id, stmt) in self.arenas.statements.iter_mut() {
271            self.resolver.inference().internal_rewrite(stmt).no_err();
272        }
273    }
274    /// Returns whether the current context is inside a loop.
275    fn is_inside_loop(&self) -> bool {
276        match self.inner_ctx {
277            None | Some(InnerContext::Closure { .. }) => false,
278            Some(InnerContext::Loop { .. } | InnerContext::While | InnerContext::For) => true,
279        }
280    }
281}
282
283// TODO(ilya): Change value to VarId.
284pub type EnvVariables = OrderedHashMap<SmolStr, Binding>;
285
286type EnvItems = OrderedHashMap<SmolStr, StatementGenericItemData>;
287
288/// Struct that holds the resolved generic type of a statement item.
289#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
290#[debug_db(dyn SemanticGroup + 'static)]
291struct StatementGenericItemData {
292    resolved_generic_item: ResolvedGenericItem,
293    stable_ptr: SyntaxStablePtrId,
294}
295
296// TODO(spapini): Consider using identifiers instead of SmolStr everywhere in the code.
297/// A state which contains all the variables defined at the current resolver until now, and a
298/// pointer to the parent environment.
299#[derive(Clone, Debug, PartialEq, Eq)]
300pub struct Environment {
301    parent: Option<Box<Environment>>,
302    variables: EnvVariables,
303    used_variables: UnorderedHashSet<semantic::VarId>,
304    use_items: EnvItems,
305    used_use_items: UnorderedHashSet<SmolStr>,
306}
307impl Environment {
308    /// Adds a parameter to the environment.
309    pub fn add_param(
310        &mut self,
311        diagnostics: &mut SemanticDiagnostics,
312        semantic_param: Parameter,
313        ast_param: &ast::Param,
314        function_title_id: Option<FunctionTitleId>,
315    ) -> Maybe<()> {
316        if let utils::ordered_hash_map::Entry::Vacant(entry) =
317            self.variables.entry(semantic_param.name.clone())
318        {
319            entry.insert(Binding::Param(semantic_param));
320            Ok(())
321        } else {
322            Err(diagnostics.report(ast_param, ParamNameRedefinition {
323                function_title_id,
324                param_name: semantic_param.name,
325            }))
326        }
327    }
328
329    pub fn empty() -> Self {
330        Self {
331            parent: None,
332            variables: Default::default(),
333            used_variables: Default::default(),
334            use_items: Default::default(),
335            used_use_items: Default::default(),
336        }
337    }
338}
339
340/// Returns the requested item from the environment if it exists. Returns None otherwise.
341pub fn get_statement_item_by_name(
342    env: &mut Environment,
343    item_name: &SmolStr,
344) -> Option<ResolvedGenericItem> {
345    let mut maybe_env = Some(&mut *env);
346    while let Some(curr_env) = maybe_env {
347        if let Some(var) = curr_env.use_items.get(item_name) {
348            curr_env.used_use_items.insert(item_name.clone());
349            return Some(var.resolved_generic_item.clone());
350        }
351        maybe_env = curr_env.parent.as_deref_mut();
352    }
353    None
354}
355
356/// Computes the semantic model of an expression.
357/// Note that this expr will always be "registered" in the arena, so it can be looked up in the
358/// language server.
359pub fn compute_expr_semantic(ctx: &mut ComputationContext<'_>, syntax: &ast::Expr) -> ExprAndId {
360    let expr = maybe_compute_expr_semantic(ctx, syntax);
361    let expr = wrap_maybe_with_missing(ctx, expr, syntax.stable_ptr());
362    let id = ctx.arenas.exprs.alloc(expr.clone());
363    ExprAndId { expr, id }
364}
365
366/// Converts `Maybe<Expr>` to a possibly [missing](ExprMissing) [Expr].
367fn wrap_maybe_with_missing(
368    ctx: &mut ComputationContext<'_>,
369    expr: Maybe<Expr>,
370    stable_ptr: ast::ExprPtr,
371) -> Expr {
372    expr.unwrap_or_else(|diag_added| {
373        Expr::Missing(ExprMissing {
374            ty: TypeId::missing(ctx.db, diag_added),
375            stable_ptr,
376            diag_added,
377        })
378    })
379}
380
381/// Computes the semantic model of an expression, or returns a SemanticDiagnosticKind on error.
382pub fn maybe_compute_expr_semantic(
383    ctx: &mut ComputationContext<'_>,
384    syntax: &ast::Expr,
385) -> Maybe<Expr> {
386    let db = ctx.db;
387    let syntax_db = db.upcast();
388
389    // TODO(spapini): When Expr holds the syntax pointer, add it here as well.
390    match syntax {
391        ast::Expr::Path(path) => resolve_expr_path(ctx, path),
392        ast::Expr::Literal(literal_syntax) => {
393            Ok(Expr::Literal(literal_to_semantic(ctx, literal_syntax)?))
394        }
395        ast::Expr::ShortString(literal_syntax) => {
396            Ok(Expr::Literal(short_string_to_semantic(ctx, literal_syntax)?))
397        }
398        ast::Expr::String(literal_syntax) => {
399            Ok(Expr::StringLiteral(string_literal_to_semantic(ctx, literal_syntax)?))
400        }
401        ast::Expr::False(syntax) => Ok(false_literal_expr(ctx, syntax.stable_ptr().into())),
402        ast::Expr::True(syntax) => Ok(true_literal_expr(ctx, syntax.stable_ptr().into())),
403        ast::Expr::Parenthesized(paren_syntax) => {
404            maybe_compute_expr_semantic(ctx, &paren_syntax.expr(syntax_db))
405        }
406        ast::Expr::Unary(syntax) => compute_expr_unary_semantic(ctx, syntax),
407        ast::Expr::Binary(binary_op_syntax) => compute_expr_binary_semantic(ctx, binary_op_syntax),
408        ast::Expr::Tuple(tuple_syntax) => compute_expr_tuple_semantic(ctx, tuple_syntax),
409        ast::Expr::FunctionCall(call_syntax) => {
410            compute_expr_function_call_semantic(ctx, call_syntax)
411        }
412        ast::Expr::StructCtorCall(ctor_syntax) => struct_ctor_expr(ctx, ctor_syntax),
413        ast::Expr::Block(block_syntax) => compute_expr_block_semantic(ctx, block_syntax),
414        ast::Expr::Match(expr_match) => compute_expr_match_semantic(ctx, expr_match),
415        ast::Expr::If(expr_if) => compute_expr_if_semantic(ctx, expr_if),
416        ast::Expr::Loop(expr_loop) => compute_expr_loop_semantic(ctx, expr_loop),
417        ast::Expr::While(expr_while) => compute_expr_while_semantic(ctx, expr_while),
418        ast::Expr::ErrorPropagate(expr) => compute_expr_error_propagate_semantic(ctx, expr),
419        ast::Expr::InlineMacro(expr) => compute_expr_inline_macro_semantic(ctx, expr),
420        ast::Expr::Missing(_) | ast::Expr::FieldInitShorthand(_) => {
421            Err(ctx.diagnostics.report(syntax, Unsupported))
422        }
423        ast::Expr::Indexed(expr) => compute_expr_indexed_semantic(ctx, expr),
424        ast::Expr::FixedSizeArray(expr) => compute_expr_fixed_size_array_semantic(ctx, expr),
425        ast::Expr::For(expr) => compute_expr_for_semantic(ctx, expr),
426        ast::Expr::Closure(expr) => compute_expr_closure_semantic(ctx, expr),
427    }
428}
429
430fn compute_expr_inline_macro_semantic(
431    ctx: &mut ComputationContext<'_>,
432    syntax: &ast::ExprInlineMacro,
433) -> Maybe<Expr> {
434    let syntax_db = ctx.db.upcast();
435
436    let macro_name = syntax.path(syntax_db).as_syntax_node().get_text_without_trivia(syntax_db);
437    let Some(macro_plugin) = ctx.db.inline_macro_plugins().get(&macro_name).cloned() else {
438        return Err(ctx.diagnostics.report(syntax, InlineMacroNotFound(macro_name.into())));
439    };
440
441    // Skipping expanding an inline macro if it had a parser error.
442    if syntax.as_syntax_node().descendants(syntax_db).any(|node| {
443        matches!(
444            node.kind(syntax_db),
445            SyntaxKind::ExprMissing
446                | SyntaxKind::WrappedArgListMissing
447                | SyntaxKind::StatementMissing
448                | SyntaxKind::ModuleItemMissing
449                | SyntaxKind::TraitItemMissing
450                | SyntaxKind::ImplItemMissing
451                | SyntaxKind::TokenMissing
452                | SyntaxKind::TokenSkipped
453        )
454    }) {
455        return Err(skip_diagnostic());
456    }
457
458    let result = macro_plugin.generate_code(syntax_db, syntax, &MacroPluginMetadata {
459        cfg_set: &ctx.cfg_set,
460        declared_derives: &ctx.db.declared_derives(),
461        allowed_features: &ctx.resolver.data.feature_config.allowed_features,
462        edition: ctx.resolver.settings.edition,
463    });
464    let mut diag_added = None;
465    for diagnostic in result.diagnostics {
466        diag_added =
467            Some(ctx.diagnostics.report(diagnostic.stable_ptr, PluginDiagnostic(diagnostic)));
468    }
469
470    let Some(code) = result.code else {
471        return Err(diag_added.unwrap_or_else(|| {
472            ctx.diagnostics.report(syntax, InlineMacroFailed(macro_name.into()))
473        }));
474    };
475
476    // Create a file
477    let new_file = FileLongId::Virtual(VirtualFile {
478        parent: Some(syntax.stable_ptr().untyped().file_id(ctx.db.upcast())),
479        name: code.name,
480        content: code.content.into(),
481        code_mappings: code.code_mappings.into(),
482        kind: FileKind::Expr,
483    })
484    .intern(ctx.db);
485    let expr_syntax = ctx.db.file_expr_syntax(new_file)?;
486    let expr = compute_expr_semantic(ctx, &expr_syntax);
487    Ok(expr.expr)
488}
489
490fn compute_expr_unary_semantic(
491    ctx: &mut ComputationContext<'_>,
492    syntax: &ast::ExprUnary,
493) -> Maybe<Expr> {
494    let syntax_db = ctx.db.upcast();
495
496    let unary_op = syntax.op(syntax_db);
497    match unary_op {
498        UnaryOperator::At(_) => {
499            let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
500
501            let ty = TypeLongId::Snapshot(expr.ty()).intern(ctx.db);
502            Ok(Expr::Snapshot(ExprSnapshot {
503                inner: expr.id,
504                ty,
505                stable_ptr: syntax.stable_ptr().into(),
506            }))
507        }
508        UnaryOperator::Desnap(_) => {
509            let (desnapped_expr, desnapped_ty) = {
510                // The expr the desnap acts on. E.g. `x` in `*x`.
511                let desnapped_expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
512                let desnapped_expr_type = ctx.reduce_ty(desnapped_expr.ty());
513
514                let desnapped_ty = match desnapped_expr_type.lookup_intern(ctx.db) {
515                    TypeLongId::Var(_) | TypeLongId::ImplType(_) => {
516                        let inference = &mut ctx.resolver.inference();
517                        // The type of the full desnap expr. E.g. the type of `*x` for `*x`.
518                        let desnap_expr_type = inference
519                            .new_type_var(Some(syntax.expr(syntax_db).stable_ptr().untyped()));
520                        let desnapped_expr_type_var =
521                            TypeLongId::Snapshot(desnap_expr_type).intern(ctx.db);
522                        if let Err(err_set) =
523                            inference.conform_ty(desnapped_expr_type_var, desnapped_expr_type)
524                        {
525                            let diag_added = ctx.diagnostics.report(syntax, WrongArgumentType {
526                                expected_ty: desnapped_expr_type_var,
527                                actual_ty: desnapped_expr_type,
528                            });
529                            inference.consume_reported_error(err_set, diag_added);
530                            return Err(diag_added);
531                        };
532                        ctx.reduce_ty(desnap_expr_type)
533                    }
534                    TypeLongId::Snapshot(ty) => ty,
535                    _ => {
536                        return Err(ctx.diagnostics.report(&unary_op, DesnapNonSnapshot));
537                    }
538                };
539                (desnapped_expr, desnapped_ty)
540            };
541
542            Ok(Expr::Desnap(ExprDesnap {
543                inner: desnapped_expr.id,
544                ty: desnapped_ty,
545                stable_ptr: syntax.stable_ptr().into(),
546            }))
547        }
548        _ => {
549            // TODO(yuval): Unary operators may change the type in the future.
550            let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
551
552            let concrete_trait_function = match core_unary_operator(
553                ctx.db,
554                &mut ctx.resolver.inference(),
555                &unary_op,
556                syntax.into(),
557            )? {
558                Err(err_kind) => {
559                    return Err(ctx.diagnostics.report(&unary_op, err_kind));
560                }
561                Ok(function) => function,
562            };
563
564            let impl_lookup_context = ctx.resolver.impl_lookup_context();
565            let inference = &mut ctx.resolver.inference();
566            let function = inference
567                .infer_trait_function(
568                    concrete_trait_function,
569                    &impl_lookup_context,
570                    Some(syntax.into()),
571                )
572                .map_err(|err_set| {
573                    inference.report_on_pending_error(err_set, ctx.diagnostics, syntax.into())
574                })?;
575
576            expr_function_call(
577                ctx,
578                function,
579                vec![NamedArg(expr, None, Mutability::Immutable)],
580                syntax,
581                syntax.stable_ptr().into(),
582            )
583        }
584    }
585}
586
587fn compute_expr_binary_semantic(
588    ctx: &mut ComputationContext<'_>,
589    syntax: &ast::ExprBinary,
590) -> Maybe<Expr> {
591    let db = ctx.db;
592    let syntax_db = db.upcast();
593
594    let stable_ptr = syntax.stable_ptr().into();
595    let binary_op = syntax.op(syntax_db);
596    let lhs_syntax = &syntax.lhs(syntax_db);
597    let rhs_syntax = syntax.rhs(syntax_db);
598
599    match binary_op {
600        ast::BinaryOperator::Dot(_) => {
601            let lexpr = compute_expr_semantic(ctx, lhs_syntax);
602            dot_expr(ctx, lexpr, rhs_syntax, stable_ptr)
603        }
604        ast::BinaryOperator::Eq(_) => {
605            let lexpr = compute_expr_semantic(ctx, lhs_syntax);
606            let rexpr = compute_expr_semantic(ctx, &rhs_syntax);
607
608            let member_path = match lexpr.expr {
609                Expr::Var(expr) => ExprVarMemberPath::Var(expr),
610                Expr::MemberAccess(ExprMemberAccess { member_path: Some(ref_arg), .. }) => ref_arg,
611                _ => return Err(ctx.diagnostics.report(lhs_syntax, InvalidLhsForAssignment)),
612            };
613
614            let inference = &mut ctx.resolver.inference();
615            if let Err((err_set, actual_ty, expected_ty)) =
616                inference.conform_ty_for_diag(rexpr.ty(), member_path.ty())
617            {
618                let diag_added = ctx
619                    .diagnostics
620                    .report(&rhs_syntax, WrongArgumentType { expected_ty, actual_ty });
621                inference.consume_reported_error(err_set, diag_added);
622                return Err(diag_added);
623            }
624            // Verify the variable argument is mutable.
625            if !ctx.semantic_defs[&member_path.base_var()].is_mut() {
626                ctx.diagnostics.report(syntax, AssignmentToImmutableVar);
627            }
628            Ok(Expr::Assignment(ExprAssignment {
629                ref_arg: member_path,
630                rhs: rexpr.id,
631                ty: unit_ty(db),
632                stable_ptr,
633            }))
634        }
635        ast::BinaryOperator::AndAnd(_) | ast::BinaryOperator::OrOr(_) => {
636            let lexpr = compute_expr_semantic(ctx, lhs_syntax);
637            let rexpr = compute_expr_semantic(ctx, &rhs_syntax);
638
639            let op = match binary_op {
640                ast::BinaryOperator::AndAnd(_) => LogicalOperator::AndAnd,
641                ast::BinaryOperator::OrOr(_) => LogicalOperator::OrOr,
642                _ => unreachable!(),
643            };
644
645            let inference = &mut ctx.resolver.inference();
646            let bool_ty = core_bool_ty(db);
647            if let Err((err_set, actual_ty, expected_ty)) =
648                inference.conform_ty_for_diag(lexpr.expr.ty(), bool_ty)
649            {
650                let diag_added =
651                    ctx.diagnostics.report(lhs_syntax, WrongType { expected_ty, actual_ty });
652                inference.consume_reported_error(err_set, diag_added);
653            }
654
655            if let Err((err_set, actual_ty, expected_ty)) =
656                inference.conform_ty_for_diag(rexpr.expr.ty(), bool_ty)
657            {
658                let diag_added =
659                    ctx.diagnostics.report(&rhs_syntax, WrongType { expected_ty, actual_ty });
660                inference.consume_reported_error(err_set, diag_added);
661            }
662
663            Ok(Expr::LogicalOperator(ExprLogicalOperator {
664                lhs: lexpr.id,
665                op,
666                rhs: rexpr.id,
667                ty: bool_ty,
668                stable_ptr,
669            }))
670        }
671        _ => call_core_binary_op(ctx, syntax, lhs_syntax, &rhs_syntax),
672    }
673}
674
675/// Get the function call expression of a binary operation that is defined in the corelib.
676fn call_core_binary_op(
677    ctx: &mut ComputationContext<'_>,
678    syntax: &ast::ExprBinary,
679    lhs_syntax: &ast::Expr,
680    rhs_syntax: &ast::Expr,
681) -> Maybe<Expr> {
682    let db = ctx.db;
683    let stable_ptr = syntax.stable_ptr().into();
684    let binary_op = syntax.op(db.upcast());
685
686    let (concrete_trait_function, snapshot) =
687        match core_binary_operator(db, &mut ctx.resolver.inference(), &binary_op, syntax.into())? {
688            Err(err_kind) => {
689                return Err(ctx.diagnostics.report(&binary_op, err_kind));
690            }
691            Ok(res) => res,
692        };
693
694    let impl_lookup_context = ctx.resolver.impl_lookup_context();
695    let inference = &mut ctx.resolver.inference();
696    let function = inference
697        .infer_trait_function(concrete_trait_function, &impl_lookup_context, Some(syntax.into()))
698        .map_err(|err_set| {
699            inference.report_on_pending_error(err_set, ctx.diagnostics, syntax.into())
700        })?;
701
702    let mut lexpr = compute_expr_semantic(ctx, lhs_syntax);
703
704    if let (Expr::Missing(_), BinaryOperator::LT(_)) = (&lexpr.expr, &binary_op) {
705        return Err(ctx
706            .diagnostics
707            .report(binary_op.stable_ptr(), SemanticDiagnosticKind::MaybeMissingColonColon));
708    }
709
710    let mut rexpr = compute_expr_semantic(ctx, rhs_syntax);
711
712    ctx.reduce_ty(lexpr.ty()).check_not_missing(db)?;
713    ctx.reduce_ty(rexpr.ty()).check_not_missing(db)?;
714
715    if snapshot {
716        let ty = TypeLongId::Snapshot(lexpr.ty()).intern(ctx.db);
717        let expr =
718            Expr::Snapshot(ExprSnapshot { inner: lexpr.id, ty, stable_ptr: lexpr.stable_ptr() });
719        lexpr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
720        let ty = TypeLongId::Snapshot(rexpr.ty()).intern(ctx.db);
721        let expr =
722            Expr::Snapshot(ExprSnapshot { inner: rexpr.id, ty, stable_ptr: rexpr.stable_ptr() });
723        rexpr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
724    }
725
726    let sig = ctx.db.concrete_function_signature(function)?;
727    let first_param = sig.params.into_iter().next().unwrap();
728
729    expr_function_call(
730        ctx,
731        function,
732        vec![
733            NamedArg(lexpr, None, first_param.mutability),
734            NamedArg(rexpr, None, Mutability::Immutable),
735        ],
736        syntax,
737        stable_ptr,
738    )
739}
740
741fn compute_expr_tuple_semantic(
742    ctx: &mut ComputationContext<'_>,
743    syntax: &ast::ExprListParenthesized,
744) -> Maybe<Expr> {
745    let db = ctx.db;
746    let syntax_db = db.upcast();
747
748    let mut items: Vec<ExprId> = vec![];
749    let mut types: Vec<TypeId> = vec![];
750    let expressions_syntax = &syntax.expressions(syntax_db).elements(syntax_db);
751    for expr_syntax in expressions_syntax {
752        let expr_semantic = compute_expr_semantic(ctx, expr_syntax);
753        types.push(ctx.reduce_ty(expr_semantic.ty()));
754        items.push(expr_semantic.id);
755    }
756    Ok(Expr::Tuple(ExprTuple {
757        items,
758        ty: TypeLongId::Tuple(types).intern(db),
759        stable_ptr: syntax.stable_ptr().into(),
760    }))
761}
762/// Computes the semantic model of an expression of type [ast::ExprFixedSizeArray].
763fn compute_expr_fixed_size_array_semantic(
764    ctx: &mut ComputationContext<'_>,
765    syntax: &ast::ExprFixedSizeArray,
766) -> Maybe<Expr> {
767    let db = ctx.db;
768    let syntax_db = db.upcast();
769    let exprs = syntax.exprs(syntax_db).elements(syntax_db);
770    let size_ty = get_usize_ty(db);
771    let (items, type_id, size) = if let Some(size_const_id) =
772        extract_fixed_size_array_size(db, ctx.diagnostics, syntax, &ctx.resolver)?
773    {
774        // Fixed size array with a defined size must have exactly one element.
775        let [expr] = exprs.as_slice() else {
776            return Err(ctx.diagnostics.report(syntax, FixedSizeArrayNonSingleValue));
777        };
778        let expr_semantic = compute_expr_semantic(ctx, expr);
779        let size = size_const_id
780            .lookup_intern(db)
781            .into_int()
782            .ok_or_else(|| ctx.diagnostics.report(syntax, FixedSizeArrayNonNumericSize))?
783            .to_usize()
784            .unwrap();
785        verify_fixed_size_array_size(ctx.diagnostics, &size.into(), syntax)?;
786        (
787            FixedSizeArrayItems::ValueAndSize(expr_semantic.id, size_const_id),
788            expr_semantic.ty(),
789            size_const_id,
790        )
791    } else if let Some((first_expr, tail_exprs)) = exprs.split_first() {
792        let size = ConstValue::Int((tail_exprs.len() + 1).into(), size_ty).intern(db);
793        let first_expr_semantic = compute_expr_semantic(ctx, first_expr);
794        let mut items: Vec<ExprId> = vec![first_expr_semantic.id];
795        // The type of the first expression is the type of the array. All other expressions must
796        // have the same type.
797        let first_expr_ty = ctx.reduce_ty(first_expr_semantic.ty());
798        for expr_syntax in tail_exprs {
799            let expr_semantic = compute_expr_semantic(ctx, expr_syntax);
800            let inference = &mut ctx.resolver.inference();
801            if let Err((err_set, actual_ty, expected_ty)) =
802                inference.conform_ty_for_diag(expr_semantic.ty(), first_expr_ty)
803            {
804                let diag_added = ctx
805                    .diagnostics
806                    .report(expr_syntax, WrongArgumentType { expected_ty, actual_ty });
807                inference.consume_reported_error(err_set, diag_added);
808                return Err(diag_added);
809            }
810            items.push(expr_semantic.id);
811        }
812        (FixedSizeArrayItems::Items(items), first_expr_ty, size)
813    } else {
814        (
815            FixedSizeArrayItems::Items(vec![]),
816            ctx.resolver.inference().new_type_var(Some(syntax.into())),
817            ConstValue::Int(0.into(), size_ty).intern(db),
818        )
819    };
820    Ok(Expr::FixedSizeArray(ExprFixedSizeArray {
821        items,
822        ty: TypeLongId::FixedSizeArray { type_id, size }.intern(db),
823        stable_ptr: syntax.stable_ptr().into(),
824    }))
825}
826
827fn compute_expr_function_call_semantic(
828    ctx: &mut ComputationContext<'_>,
829    syntax: &ast::ExprFunctionCall,
830) -> Maybe<Expr> {
831    let db = ctx.db;
832    let syntax_db = db.upcast();
833
834    let path = syntax.path(syntax_db);
835    let args_syntax = syntax.arguments(syntax_db).arguments(syntax_db);
836    // Check if this is a variable.
837    let segments = path.elements(syntax_db);
838    let mut is_shadowed_by_variable = false;
839    if let [PathSegment::Simple(ident_segment)] = &segments[..] {
840        let identifier = ident_segment.ident(syntax_db);
841        let variable_name = identifier.text(ctx.db.upcast());
842        if let Some(var) = get_binded_expr_by_name(ctx, &variable_name, path.stable_ptr().into()) {
843            is_shadowed_by_variable = true;
844            // if closures are not in context, we want to call the function instead of the variable.
845            if ctx.are_closures_in_context {
846                // TODO(TomerStarkware): find the correct trait based on captured variables.
847                let fn_once_trait = crate::corelib::fn_once_trait(db);
848                let fn_trait = crate::corelib::fn_trait(db);
849                let self_expr = ExprAndId { expr: var.clone(), id: ctx.arenas.exprs.alloc(var) };
850                let mut closure_call_data = |call_trait| {
851                    compute_method_function_call_data(
852                        ctx,
853                        &[call_trait],
854                        "call".into(),
855                        self_expr.clone(),
856                        syntax.into(),
857                        None,
858                        |ty, _, inference_errors| {
859                            if call_trait == fn_once_trait {
860                                Some(CallExpressionRequiresFunction { ty, inference_errors })
861                            } else {
862                                None
863                            }
864                        },
865                        |_, _, _| {
866                            unreachable!(
867                                "There is one explicit trait, FnOnce trait. No implementations of \
868                                 the trait, caused by both lack of implementation or multiple \
869                                 implementations of the trait, are handled in \
870                                 NoImplementationOfTrait function."
871                            )
872                        },
873                    )
874                };
875                let (call_function_id, _, fixed_closure, closure_mutability) =
876                    closure_call_data(fn_trait).or_else(|_| closure_call_data(fn_once_trait))?;
877
878                let args_iter = args_syntax.elements(syntax_db).into_iter();
879                // Normal parameters
880                let mut args = vec![];
881                let mut arg_types = vec![];
882                for arg_syntax in args_iter {
883                    let stable_ptr = arg_syntax.stable_ptr();
884                    let arg = compute_named_argument_clause(ctx, arg_syntax);
885                    if arg.2 != Mutability::Immutable {
886                        return Err(ctx.diagnostics.report(stable_ptr, RefClosureArgument));
887                    }
888                    args.push(arg.0.id);
889                    arg_types.push(arg.0.ty());
890                }
891                let args_expr = Expr::Tuple(ExprTuple {
892                    items: args,
893                    ty: TypeLongId::Tuple(arg_types).intern(db),
894                    stable_ptr: syntax.stable_ptr().into(),
895                });
896                let args_expr =
897                    ExprAndId { expr: args_expr.clone(), id: ctx.arenas.exprs.alloc(args_expr) };
898                return expr_function_call(
899                    ctx,
900                    call_function_id,
901                    vec![
902                        NamedArg(fixed_closure, None, closure_mutability),
903                        NamedArg(args_expr, None, Mutability::Immutable),
904                    ],
905                    syntax,
906                    syntax.stable_ptr().into(),
907                );
908            }
909        }
910    }
911
912    let item = ctx.resolver.resolve_concrete_path_ex(
913        ctx.diagnostics,
914        &path,
915        NotFoundItemType::Function,
916        Some(&mut ctx.environment),
917    )?;
918
919    match item {
920        ResolvedConcreteItem::Variant(variant) => {
921            let concrete_enum_type =
922                TypeLongId::Concrete(ConcreteTypeId::Enum(variant.concrete_enum_id)).intern(db);
923            if concrete_enum_type.is_phantom(db) {
924                ctx.diagnostics.report(syntax, CannotCreateInstancesOfPhantomTypes);
925            }
926
927            // TODO(Gil): Consider not invoking the TraitFunction inference below if there were
928            // errors in argument semantics, in order to avoid unnecessary diagnostics.
929            let named_args: Vec<_> = args_syntax
930                .elements(syntax_db)
931                .into_iter()
932                .map(|arg_syntax| compute_named_argument_clause(ctx, arg_syntax))
933                .collect();
934            if named_args.len() != 1 {
935                return Err(ctx.diagnostics.report(syntax, WrongNumberOfArguments {
936                    expected: 1,
937                    actual: named_args.len(),
938                }));
939            }
940            let NamedArg(arg, name_terminal, mutability) = named_args[0].clone();
941            if let Some(name_terminal) = name_terminal {
942                ctx.diagnostics.report(&name_terminal, NamedArgumentsAreNotSupported);
943            }
944            if mutability != Mutability::Immutable {
945                return Err(ctx.diagnostics.report(&args_syntax, VariantCtorNotImmutable));
946            }
947            let inference = &mut ctx.resolver.inference();
948            if let Err((err_set, actual_ty, expected_ty)) =
949                inference.conform_ty_for_diag(arg.ty(), variant.ty)
950            {
951                let diag_added = ctx
952                    .diagnostics
953                    .report(&args_syntax, WrongArgumentType { expected_ty, actual_ty });
954                inference.consume_reported_error(err_set, diag_added);
955                return Err(diag_added);
956            }
957            Ok(semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
958                variant,
959                value_expr: arg.id,
960                ty: concrete_enum_type,
961                stable_ptr: syntax.stable_ptr().into(),
962            }))
963        }
964        ResolvedConcreteItem::Function(function) => {
965            if is_shadowed_by_variable {
966                return Err(ctx.diagnostics.report(&path, CallingShadowedFunction {
967                    shadowed_function_name: path
968                        .elements(syntax_db)
969                        .first()
970                        .unwrap()
971                        .identifier(syntax_db),
972                }));
973            }
974            // TODO(Gil): Consider not invoking the TraitFunction inference below if there were
975            // errors in argument semantics, in order to avoid unnecessary diagnostics.
976
977            // Note there may be n+1 arguments for n parameters, if the last one is a coupon.
978            let mut args_iter = args_syntax.elements(syntax_db).into_iter();
979            // Normal parameters
980            let mut named_args = vec![];
981            for _ in function_parameter_types(ctx, function)? {
982                let Some(arg_syntax) = args_iter.next() else {
983                    continue;
984                };
985                named_args.push(compute_named_argument_clause(ctx, arg_syntax));
986            }
987
988            // Maybe coupon
989            if let Some(arg_syntax) = args_iter.next() {
990                named_args.push(compute_named_argument_clause(ctx, arg_syntax));
991            }
992
993            expr_function_call(ctx, function, named_args, syntax, syntax.stable_ptr().into())
994        }
995        _ => Err(ctx.diagnostics.report(&path, UnexpectedElement {
996            expected: vec![ElementKind::Function],
997            actual: (&item).into(),
998        })),
999    }
1000}
1001
1002/// Computes the semantic model of an expression of type [ast::Arg].
1003///
1004/// Returns the value and the optional argument name.
1005pub fn compute_named_argument_clause(
1006    ctx: &mut ComputationContext<'_>,
1007    arg_syntax: ast::Arg,
1008) -> NamedArg {
1009    let syntax_db = ctx.db.upcast();
1010
1011    let mutability = compute_mutability(
1012        ctx.diagnostics,
1013        syntax_db,
1014        &arg_syntax.modifiers(syntax_db).elements(syntax_db),
1015    );
1016
1017    let arg_clause = arg_syntax.arg_clause(syntax_db);
1018    let (expr, arg_name_identifier) = match arg_clause {
1019        ast::ArgClause::Unnamed(arg_unnamed) => {
1020            (compute_expr_semantic(ctx, &arg_unnamed.value(syntax_db)), None)
1021        }
1022        ast::ArgClause::Named(arg_named) => (
1023            compute_expr_semantic(ctx, &arg_named.value(syntax_db)),
1024            Some(arg_named.name(syntax_db)),
1025        ),
1026        ast::ArgClause::FieldInitShorthand(arg_field_init_shorthand) => {
1027            let name_expr = arg_field_init_shorthand.name(syntax_db);
1028            let stable_ptr: ast::ExprPtr = name_expr.stable_ptr().into();
1029            let arg_name_identifier = name_expr.name(syntax_db);
1030            let maybe_expr = resolve_variable_by_name(ctx, &arg_name_identifier, stable_ptr);
1031            let expr = wrap_maybe_with_missing(ctx, maybe_expr, stable_ptr);
1032            let expr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
1033            (expr, Some(arg_name_identifier))
1034        }
1035    };
1036
1037    NamedArg(expr, arg_name_identifier, mutability)
1038}
1039
1040pub fn compute_root_expr(
1041    ctx: &mut ComputationContext<'_>,
1042    syntax: &ast::ExprBlock,
1043    return_type: TypeId,
1044) -> Maybe<ExprId> {
1045    // Conform TypeEqual constraints for Associated type bounds.
1046    let inference = &mut ctx.resolver.data.inference_data.inference(ctx.db);
1047    for param in &ctx.resolver.data.generic_params {
1048        let Ok(GenericParam::Impl(imp)) = ctx.db.generic_param_semantic(*param) else {
1049            continue;
1050        };
1051        let Ok(concrete_trait_id) = imp.concrete_trait else {
1052            continue;
1053        };
1054        if crate::corelib::fn_traits(ctx.db).contains(&concrete_trait_id.trait_id(ctx.db)) {
1055            ctx.are_closures_in_context = true;
1056        }
1057    }
1058    let constrains =
1059        ctx.db.generic_params_type_constraints(ctx.resolver.data.generic_params.clone());
1060    inference.conform_generic_params_type_constraints(&constrains);
1061
1062    let return_type = ctx.reduce_ty(return_type);
1063    let res = compute_expr_block_semantic(ctx, syntax)?;
1064    let res_ty = ctx.reduce_ty(res.ty());
1065    let res = ctx.arenas.exprs.alloc(res);
1066    let inference = &mut ctx.resolver.inference();
1067    if let Err((err_set, actual_ty, expected_ty)) =
1068        inference.conform_ty_for_diag(res_ty, return_type)
1069    {
1070        let diag_added = ctx.diagnostics.report(
1071            ctx.signature
1072                .map(|s| match s.stable_ptr.lookup(ctx.db.upcast()).ret_ty(ctx.db.upcast()) {
1073                    OptionReturnTypeClause::Empty(_) => syntax.stable_ptr().untyped(),
1074                    OptionReturnTypeClause::ReturnTypeClause(return_type_clause) => {
1075                        return_type_clause.ty(ctx.db.upcast()).stable_ptr().untyped()
1076                    }
1077                })
1078                .unwrap_or_else(|| syntax.stable_ptr().untyped()),
1079            WrongReturnType { expected_ty, actual_ty },
1080        );
1081        inference.consume_reported_error(err_set, diag_added);
1082    }
1083
1084    // Check fully resolved.
1085    inference.finalize(ctx.diagnostics, syntax.into());
1086
1087    ctx.apply_inference_rewriter();
1088
1089    Ok(res)
1090}
1091
1092/// Computes the semantic model of an expression of type [ast::ExprBlock].
1093pub fn compute_expr_block_semantic(
1094    ctx: &mut ComputationContext<'_>,
1095    syntax: &ast::ExprBlock,
1096) -> Maybe<Expr> {
1097    let db = ctx.db;
1098    let syntax_db = db.upcast();
1099
1100    ctx.run_in_subscope(|new_ctx| {
1101        let mut statements = syntax.statements(syntax_db).elements(syntax_db);
1102        // Remove the tail expression, if exists.
1103        // TODO(spapini): Consider splitting tail expression in the parser.
1104        let tail = get_tail_expression(syntax_db, statements.as_slice());
1105        if tail.is_some() {
1106            statements.pop();
1107        }
1108
1109        // Convert statements to semantic model.
1110        let statements_semantic: Vec<_> = statements
1111            .into_iter()
1112            .filter_map(|statement_syntax| {
1113                compute_statement_semantic(new_ctx, statement_syntax).to_option()
1114            })
1115            .collect();
1116
1117        // Convert tail expression (if exists) to semantic model.
1118        let tail_semantic_expr = tail.map(|tail_expr| compute_expr_semantic(new_ctx, &tail_expr));
1119        let ty = if let Some(t) = &tail_semantic_expr {
1120            t.ty()
1121        } else if let Some(statement) = statements_semantic.last() {
1122            if let Statement::Return(_) | Statement::Break(_) =
1123                &new_ctx.arenas.statements[*statement]
1124            {
1125                never_ty(new_ctx.db)
1126            } else {
1127                unit_ty(db)
1128            }
1129        } else {
1130            unit_ty(db)
1131        };
1132        Ok(Expr::Block(ExprBlock {
1133            statements: statements_semantic,
1134            tail: tail_semantic_expr.map(|expr| expr.id),
1135            ty,
1136            stable_ptr: syntax.stable_ptr().into(),
1137        }))
1138    })
1139}
1140
1141/// Helper for merging the return types of branch blocks (match or if else).
1142#[derive(Debug, Clone)]
1143struct FlowMergeTypeHelper {
1144    multi_arm_expr_kind: MultiArmExprKind,
1145    never_type: TypeId,
1146    final_type: Option<TypeId>,
1147    /// Whether or not the Helper had a previous type merge error.
1148    had_merge_error: bool,
1149}
1150impl FlowMergeTypeHelper {
1151    fn new(db: &dyn SemanticGroup, multi_arm_expr_kind: MultiArmExprKind) -> Self {
1152        Self {
1153            multi_arm_expr_kind,
1154            never_type: never_ty(db),
1155            final_type: None,
1156            had_merge_error: false,
1157        }
1158    }
1159
1160    /// Merge a type into the helper. Returns false on error or if had a previous error.
1161    /// May conform the type to the self.expected_type, if set. Mostly, it is called after
1162    /// the types have already been conformed earlier, in which case it has no external effect.
1163    fn try_merge_types(
1164        &mut self,
1165        db: &dyn SemanticGroup,
1166        diagnostics: &mut SemanticDiagnostics,
1167        inference: &mut Inference<'_>,
1168        ty: TypeId,
1169        stable_ptr: SyntaxStablePtrId,
1170    ) -> bool {
1171        if self.had_merge_error {
1172            return false;
1173        }
1174
1175        if ty != self.never_type && !ty.is_missing(db) {
1176            if let Some(pending) = &self.final_type {
1177                if let Err(err_set) = inference.conform_ty(ty, *pending) {
1178                    let diag_added = diagnostics.report(stable_ptr, IncompatibleArms {
1179                        multi_arm_expr_kind: self.multi_arm_expr_kind,
1180                        pending_ty: *pending,
1181                        different_ty: ty,
1182                    });
1183                    inference.consume_reported_error(err_set, diag_added);
1184                    self.had_merge_error = true;
1185                    return false;
1186                }
1187            } else {
1188                self.final_type = Some(ty);
1189            }
1190        }
1191        true
1192    }
1193
1194    /// Returns the merged type.
1195    fn get_final_type(self) -> TypeId {
1196        self.final_type.unwrap_or(self.never_type)
1197    }
1198}
1199
1200/// computes the semantic of a match arm pattern and the block expression.
1201fn compute_arm_semantic(
1202    ctx: &mut ComputationContext<'_>,
1203    expr: &Expr,
1204    arm_expr_syntax: ast::Expr,
1205    patterns_syntax: &PatternListOr,
1206    // Whether the arm is a while let arm. This case is handled a little differently.
1207    is_while_let_arm: bool,
1208) -> (Vec<PatternAndId>, ExprAndId) {
1209    let db = ctx.db;
1210    let syntax_db = db.upcast();
1211    ctx.run_in_subscope(|new_ctx| {
1212        // Typecheck the arms's patterns, and introduce the new variables to the subscope.
1213        // Note that if the arm expr is a block, there will be *another* subscope
1214        // for it.
1215        let mut arm_patterns_variables: UnorderedHashMap<SmolStr, LocalVariable> =
1216            UnorderedHashMap::default();
1217        let patterns: Vec<_> = patterns_syntax
1218            .elements(syntax_db)
1219            .iter()
1220            .map(|pattern_syntax| {
1221                let pattern: PatternAndId = compute_pattern_semantic(
1222                    new_ctx,
1223                    pattern_syntax,
1224                    expr.ty(),
1225                    &mut arm_patterns_variables,
1226                );
1227                let variables = pattern.variables(&new_ctx.arenas.patterns);
1228                for variable in variables {
1229                    match arm_patterns_variables.entry(variable.name.clone()) {
1230                        std::collections::hash_map::Entry::Occupied(entry) => {
1231                            let get_location = || variable.stable_ptr.lookup(db.upcast());
1232                            let var = entry.get();
1233
1234                            let expected_ty = new_ctx.reduce_ty(var.ty);
1235                            let actual_ty = new_ctx.reduce_ty(variable.var.ty);
1236
1237                            let mut has_inference_error = false;
1238                            if !variable.var.ty.is_missing(new_ctx.db) {
1239                                let inference = &mut new_ctx.resolver.inference();
1240                                if let Err((err_set, actual_ty, expected_ty)) =
1241                                    inference.conform_ty_for_diag(actual_ty, expected_ty)
1242                                {
1243                                    let diag_added =
1244                                        new_ctx.diagnostics.report(&get_location(), WrongType {
1245                                            expected_ty,
1246                                            actual_ty,
1247                                        });
1248                                    inference.consume_reported_error(err_set, diag_added);
1249                                    has_inference_error = true;
1250                                }
1251                            };
1252                            if !has_inference_error && var.is_mut != variable.var.is_mut {
1253                                new_ctx.diagnostics.report(&get_location(), InconsistentBinding);
1254                            }
1255                        }
1256                        std::collections::hash_map::Entry::Vacant(entry) => {
1257                            entry.insert(variable.var.clone());
1258                        }
1259                    }
1260                }
1261                pattern
1262            })
1263            .collect();
1264
1265        for (pattern_syntax, pattern) in
1266            patterns_syntax.elements(syntax_db).iter().zip(patterns.iter())
1267        {
1268            let variables = pattern.variables(&new_ctx.arenas.patterns);
1269
1270            if variables.len() != arm_patterns_variables.len() {
1271                new_ctx.diagnostics.report(pattern_syntax, MissingVariableInPattern);
1272            }
1273
1274            for v in variables {
1275                let var_def = Binding::LocalVar(v.var.clone());
1276                // TODO(spapini): Wrap this in a function to couple with semantic_defs
1277                // insertion.
1278                new_ctx.environment.variables.insert(v.name.clone(), var_def.clone());
1279                new_ctx.semantic_defs.insert(var_def.id(), var_def);
1280            }
1281        }
1282        let arm_expr = if is_while_let_arm {
1283            let ast::Expr::Block(arm_expr_syntax) = arm_expr_syntax else {
1284                unreachable!("Expected a block expression for a loop arm.");
1285            };
1286            let (id, _) = compute_loop_body_semantic(new_ctx, arm_expr_syntax, InnerContext::While);
1287            let expr = new_ctx.arenas.exprs[id].clone();
1288            ExprAndId { expr, id }
1289        } else {
1290            compute_expr_semantic(new_ctx, &arm_expr_syntax)
1291        };
1292        (patterns, arm_expr)
1293    })
1294}
1295
1296/// Computes the semantic model of an expression of type [ast::ExprMatch].
1297fn compute_expr_match_semantic(
1298    ctx: &mut ComputationContext<'_>,
1299    syntax: &ast::ExprMatch,
1300) -> Maybe<Expr> {
1301    // TODO(yuval): verify exhaustiveness.
1302    let db = ctx.db;
1303    let syntax_db = db.upcast();
1304
1305    let syntax_arms = syntax.arms(syntax_db).elements(syntax_db);
1306    let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
1307    // Run compute_pattern_semantic on every arm, even if other arms failed, to get as many
1308    // diagnostics as possible.
1309    let patterns_and_exprs: Vec<_> = syntax_arms
1310        .iter()
1311        .map(|syntax_arm| {
1312            compute_arm_semantic(
1313                ctx,
1314                &expr,
1315                syntax_arm.expression(syntax_db),
1316                &syntax_arm.patterns(syntax_db),
1317                false,
1318            )
1319        })
1320        .collect();
1321    // Unify arm types.
1322    let mut helper = FlowMergeTypeHelper::new(ctx.db, MultiArmExprKind::Match);
1323    for (_, expr) in patterns_and_exprs.iter() {
1324        let expr_ty = ctx.reduce_ty(expr.ty());
1325        if !helper.try_merge_types(
1326            ctx.db,
1327            ctx.diagnostics,
1328            &mut ctx.resolver.inference(),
1329            expr_ty,
1330            expr.stable_ptr().untyped(),
1331        ) {
1332            break;
1333        };
1334    }
1335    // Compute semantic representation of the match arms.
1336    let semantic_arms = patterns_and_exprs
1337        .into_iter()
1338        .map(|(patterns, arm_expr)| MatchArm {
1339            patterns: patterns.iter().map(|pattern| pattern.id).collect(),
1340            expression: arm_expr.id,
1341        })
1342        .collect();
1343    Ok(Expr::Match(ExprMatch {
1344        matched_expr: expr.id,
1345        arms: semantic_arms,
1346        ty: helper.get_final_type(),
1347        stable_ptr: syntax.stable_ptr().into(),
1348    }))
1349}
1350
1351/// Computes the semantic model of an expression of type [ast::ExprIf].
1352fn compute_expr_if_semantic(ctx: &mut ComputationContext<'_>, syntax: &ast::ExprIf) -> Maybe<Expr> {
1353    let syntax_db = ctx.db.upcast();
1354    let (condition, if_block) = match &syntax.condition(syntax_db) {
1355        ast::Condition::Let(condition) => {
1356            let expr = compute_expr_semantic(ctx, &condition.expr(syntax_db));
1357            if let Expr::LogicalOperator(_) = expr.expr {
1358                ctx.diagnostics
1359                    .report(&condition.expr(syntax_db), LogicalOperatorNotAllowedInIfLet);
1360            }
1361
1362            let (patterns, if_block) = compute_arm_semantic(
1363                ctx,
1364                &expr,
1365                ast::Expr::Block(syntax.if_block(syntax_db)),
1366                &condition.patterns(syntax_db),
1367                false,
1368            );
1369            (Condition::Let(expr.id, patterns.iter().map(|pattern| pattern.id).collect()), if_block)
1370        }
1371        ast::Condition::Expr(expr) => {
1372            let if_block = compute_expr_block_semantic(ctx, &syntax.if_block(syntax_db))?;
1373            (
1374                Condition::BoolExpr(compute_bool_condition_semantic(ctx, &expr.expr(syntax_db)).id),
1375                ExprAndId { expr: if_block.clone(), id: ctx.arenas.exprs.alloc(if_block) },
1376            )
1377        }
1378    };
1379
1380    let (else_block_opt, else_block_ty) = match syntax.else_clause(syntax_db) {
1381        ast::OptionElseClause::Empty(_) => (None, unit_ty(ctx.db)),
1382        ast::OptionElseClause::ElseClause(else_clause) => {
1383            match else_clause.else_block_or_if(syntax_db) {
1384                BlockOrIf::Block(block) => {
1385                    let else_block = compute_expr_block_semantic(ctx, &block)?;
1386                    (Some(else_block.clone()), else_block.ty())
1387                }
1388                BlockOrIf::If(expr_if) => {
1389                    let else_if = compute_expr_if_semantic(ctx, &expr_if)?;
1390                    (Some(else_if.clone()), else_if.ty())
1391                }
1392            }
1393        }
1394    };
1395
1396    let mut helper = FlowMergeTypeHelper::new(ctx.db, MultiArmExprKind::If);
1397    let if_block_ty = ctx.reduce_ty(if_block.ty());
1398    let else_block_ty = ctx.reduce_ty(else_block_ty);
1399    let inference = &mut ctx.resolver.inference();
1400    let _ = helper.try_merge_types(ctx.db, ctx.diagnostics, inference, if_block_ty, syntax.into())
1401        && helper.try_merge_types(ctx.db, ctx.diagnostics, inference, else_block_ty, syntax.into());
1402    Ok(Expr::If(ExprIf {
1403        condition,
1404        if_block: if_block.id,
1405        else_block: else_block_opt.map(|else_block| ctx.arenas.exprs.alloc(else_block)),
1406        ty: helper.get_final_type(),
1407        stable_ptr: syntax.stable_ptr().into(),
1408    }))
1409}
1410
1411/// Computes the semantic model of an expression of type [ast::ExprLoop].
1412fn compute_expr_loop_semantic(
1413    ctx: &mut ComputationContext<'_>,
1414    syntax: &ast::ExprLoop,
1415) -> Maybe<Expr> {
1416    let db = ctx.db;
1417    let syntax_db = db.upcast();
1418
1419    let (body, inner_ctx) =
1420        compute_loop_body_semantic(ctx, syntax.body(syntax_db), InnerContext::Loop {
1421            type_merger: FlowMergeTypeHelper::new(db, MultiArmExprKind::Loop),
1422        });
1423    Ok(Expr::Loop(ExprLoop {
1424        body,
1425        ty: match inner_ctx {
1426            InnerContext::Loop { type_merger, .. } => type_merger.get_final_type(),
1427            _ => unreachable!("Expected loop context"),
1428        },
1429        stable_ptr: syntax.stable_ptr().into(),
1430    }))
1431}
1432
1433/// Computes the semantic model of an expression of type [ast::ExprWhile].
1434fn compute_expr_while_semantic(
1435    ctx: &mut ComputationContext<'_>,
1436    syntax: &ast::ExprWhile,
1437) -> Maybe<Expr> {
1438    let db = ctx.db;
1439    let syntax_db = db.upcast();
1440
1441    let (condition, body) = match &syntax.condition(syntax_db) {
1442        ast::Condition::Let(condition) => {
1443            let expr = compute_expr_semantic(ctx, &condition.expr(syntax_db));
1444            if let Expr::LogicalOperator(_) = expr.expr {
1445                ctx.diagnostics
1446                    .report(&condition.expr(syntax_db), LogicalOperatorNotAllowedInWhileLet);
1447            }
1448
1449            let (patterns, body) = compute_arm_semantic(
1450                ctx,
1451                &expr,
1452                ast::Expr::Block(syntax.body(syntax_db)),
1453                &condition.patterns(syntax_db),
1454                true,
1455            );
1456            (Condition::Let(expr.id, patterns.iter().map(|pattern| pattern.id).collect()), body.id)
1457        }
1458        ast::Condition::Expr(expr) => {
1459            let (body, _inner_ctx) =
1460                compute_loop_body_semantic(ctx, syntax.body(syntax_db), InnerContext::While);
1461            (
1462                Condition::BoolExpr(compute_bool_condition_semantic(ctx, &expr.expr(syntax_db)).id),
1463                body,
1464            )
1465        }
1466    };
1467
1468    Ok(Expr::While(ExprWhile {
1469        condition,
1470        body,
1471        ty: unit_ty(ctx.db),
1472        stable_ptr: syntax.stable_ptr().into(),
1473    }))
1474}
1475
1476/// Computes the semantic model of an expression of type [ast::ExprFor].
1477fn compute_expr_for_semantic(
1478    ctx: &mut ComputationContext<'_>,
1479    syntax: &ast::ExprFor,
1480) -> Maybe<Expr> {
1481    let db = ctx.db;
1482    let syntax_db = db.upcast();
1483    let expr_ptr = syntax.expr(syntax_db).stable_ptr();
1484
1485    let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
1486    let expr_id = expr.id;
1487
1488    let into_iterator_trait =
1489        get_core_trait(ctx.db, CoreTraitContext::Iterator, "IntoIterator".into());
1490
1491    let (into_iterator_function_id, _, fixed_into_iter_var, into_iter_mutability) =
1492        compute_method_function_call_data(
1493            ctx,
1494            &[into_iterator_trait],
1495            "into_iter".into(),
1496            expr,
1497            expr_ptr.into(),
1498            None,
1499            |ty, _, inference_errors| {
1500                Some(NoImplementationOfTrait {
1501                    ty,
1502                    inference_errors,
1503                    trait_name: "IntoIterator".into(),
1504                })
1505            },
1506            |_, _, _| {
1507                unreachable!(
1508                    "There is one explicit trait, IntoIterator trait. No implementations of the \
1509                     trait, caused by both lack of implementation or multiple implementations of \
1510                     the trait, are handled in NoImplementationOfTrait function."
1511                )
1512            },
1513        )?;
1514    let into_iter_call = expr_function_call(
1515        ctx,
1516        into_iterator_function_id,
1517        vec![NamedArg(fixed_into_iter_var, None, into_iter_mutability)],
1518        expr_ptr,
1519        expr_ptr,
1520    )?;
1521
1522    let into_iter_variable =
1523        LocalVarLongId(ctx.resolver.module_file_id, syntax.identifier(syntax_db).stable_ptr())
1524            .intern(ctx.db);
1525
1526    let into_iter_expr = Expr::Var(ExprVar {
1527        var: VarId::Local(into_iter_variable),
1528        ty: into_iter_call.ty(),
1529        stable_ptr: into_iter_call.stable_ptr(),
1530    });
1531    let into_iter_member_path = ExprVarMemberPath::Var(ExprVar {
1532        var: VarId::Local(into_iter_variable),
1533        ty: into_iter_call.ty(),
1534        stable_ptr: into_iter_call.stable_ptr(),
1535    });
1536    let into_iter_expr_id = ctx.arenas.exprs.alloc(into_iter_expr.clone());
1537
1538    let iterator_trait = get_core_trait(ctx.db, CoreTraitContext::Iterator, "Iterator".into());
1539
1540    let (next_function_id, _, _, _) = compute_method_function_call_data(
1541        ctx,
1542        &[iterator_trait],
1543        "next".into(),
1544        ExprAndId { expr: into_iter_expr, id: into_iter_expr_id },
1545        expr_ptr.into(),
1546        None,
1547        |ty, _, inference_errors| {
1548            Some(NoImplementationOfTrait { ty, inference_errors, trait_name: "Iterator".into() })
1549        },
1550        |_, _, _| {
1551            unreachable!(
1552                "There is one explicit trait, Iterator trait. No implementations of the trait, \
1553                 caused by both lack of implementation or multiple implementations of the trait, \
1554                 are handled in NoImplementationOfTrait function."
1555            )
1556        },
1557    )?;
1558
1559    let next_success_variant =
1560        match db.concrete_function_signature(next_function_id)?.return_type.lookup_intern(db) {
1561            TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(enm)) => {
1562                assert_eq!(enm.enum_id(db.upcast()).name(db.upcast()), "Option");
1563                db.concrete_enum_variants(enm).unwrap().into_iter().next().unwrap()
1564            }
1565            _ => unreachable!(),
1566        };
1567    let (body_id, pattern) = ctx.run_in_subscope(|new_ctx| {
1568        let inner_pattern = compute_pattern_semantic(
1569            new_ctx,
1570            &syntax.pattern(syntax_db),
1571            next_success_variant.ty,
1572            &mut UnorderedHashMap::default(),
1573        );
1574        let variables = inner_pattern.variables(&new_ctx.arenas.patterns);
1575        for v in variables {
1576            let var_def = Binding::LocalVar(v.var.clone());
1577            new_ctx.environment.variables.insert(v.name.clone(), var_def.clone());
1578            new_ctx.semantic_defs.insert(var_def.id(), var_def);
1579        }
1580        let (body, _inner_ctx) =
1581            compute_loop_body_semantic(new_ctx, syntax.body(syntax_db), InnerContext::For);
1582        (body, new_ctx.arenas.patterns.alloc(inner_pattern.pattern))
1583    });
1584    Ok(Expr::For(ExprFor {
1585        into_iter: into_iterator_function_id,
1586        into_iter_member_path,
1587        next_function_id,
1588        expr_id,
1589        pattern,
1590        body: body_id,
1591        ty: unit_ty(ctx.db),
1592        stable_ptr: syntax.stable_ptr().into(),
1593    }))
1594}
1595
1596/// Computes the semantic model for a body of a loop.
1597fn compute_loop_body_semantic(
1598    ctx: &mut ComputationContext<'_>,
1599    syntax: ast::ExprBlock,
1600    inner_ctx: InnerContext,
1601) -> (ExprId, InnerContext) {
1602    let db = ctx.db;
1603    let syntax_db = db.upcast();
1604
1605    ctx.run_in_subscope(|new_ctx| {
1606        let old_inner_ctx = std::mem::replace(&mut new_ctx.inner_ctx, Some(inner_ctx));
1607
1608        let mut statements = syntax.statements(syntax_db).elements(syntax_db);
1609        // Remove the typed tail expression, if exists.
1610        let tail = get_tail_expression(syntax_db, statements.as_slice());
1611        if tail.is_some() {
1612            statements.pop();
1613        }
1614
1615        // Convert statements to semantic model.
1616        let statements_semantic: Vec<_> = statements
1617            .into_iter()
1618            .filter_map(|statement_syntax| {
1619                compute_statement_semantic(new_ctx, statement_syntax).to_option()
1620            })
1621            .collect();
1622        let tail = tail.map(|tail| compute_expr_semantic(new_ctx, &tail));
1623        if let Some(tail) = &tail {
1624            if !tail.ty().is_missing(db) && !tail.ty().is_unit(db) && tail.ty() != never_ty(db) {
1625                new_ctx.diagnostics.report(tail.deref(), TailExpressionNotAllowedInLoop);
1626            }
1627        }
1628
1629        let inner_ctx = std::mem::replace(&mut new_ctx.inner_ctx, old_inner_ctx).unwrap();
1630        let body = new_ctx.arenas.exprs.alloc(Expr::Block(ExprBlock {
1631            statements: statements_semantic,
1632            tail: tail.map(|tail| tail.id),
1633            ty: unit_ty(db),
1634            stable_ptr: syntax.stable_ptr().into(),
1635        }));
1636
1637        (body, inner_ctx)
1638    })
1639}
1640
1641/// Computes the semantic model of an expression of type [ast::ExprClosure].
1642fn compute_expr_closure_semantic(
1643    ctx: &mut ComputationContext<'_>,
1644    syntax: &ast::ExprClosure,
1645) -> Maybe<Expr> {
1646    ctx.are_closures_in_context = true;
1647    let syntax_db = ctx.db.upcast();
1648    let (params, ret_ty, body) = ctx.run_in_subscope(|new_ctx| {
1649        let params = if let ClosureParamWrapper::NAry(params) = syntax.wrapper(syntax_db) {
1650            function_signature_params(
1651                new_ctx.diagnostics,
1652                new_ctx.db,
1653                &mut new_ctx.resolver,
1654                &params.params(syntax_db).elements(syntax_db),
1655                None,
1656                &mut new_ctx.environment,
1657            )
1658            .into_iter()
1659            .collect()
1660        } else {
1661            vec![]
1662        };
1663        let return_type = match syntax.ret_ty(syntax_db) {
1664            OptionReturnTypeClause::ReturnTypeClause(ty_syntax) => resolve_type_with_environment(
1665                new_ctx.db,
1666                new_ctx.diagnostics,
1667                &mut new_ctx.resolver,
1668                &ty_syntax.ty(syntax_db),
1669                Some(&mut new_ctx.environment),
1670            ),
1671            OptionReturnTypeClause::Empty(missing) => {
1672                new_ctx.resolver.inference().new_type_var(Some(missing.stable_ptr().untyped()))
1673            }
1674        };
1675        let old_inner_ctx =
1676            std::mem::replace(&mut new_ctx.inner_ctx, Some(InnerContext::Closure { return_type }));
1677        let body = match syntax.expr(syntax_db) {
1678            ast::Expr::Block(syntax) => compute_closure_body_semantic(new_ctx, syntax),
1679            _ => compute_expr_semantic(new_ctx, &syntax.expr(syntax_db)).id,
1680        };
1681        std::mem::replace(&mut new_ctx.inner_ctx, old_inner_ctx).unwrap();
1682        let mut inference = new_ctx.resolver.inference();
1683        if let Err((err_set, actual_ty, expected_ty)) =
1684            inference.conform_ty_for_diag(new_ctx.arenas.exprs[body].ty(), return_type)
1685        {
1686            let diag_added = new_ctx.diagnostics.report(
1687                match syntax.ret_ty(ctx.db.upcast()).stable_ptr().lookup(ctx.db.upcast()) {
1688                    OptionReturnTypeClause::Empty(_) => syntax.expr(syntax_db).stable_ptr(),
1689                    OptionReturnTypeClause::ReturnTypeClause(return_type_clause) => {
1690                        return_type_clause.ty(ctx.db.upcast()).stable_ptr()
1691                    }
1692                },
1693                WrongReturnType { expected_ty, actual_ty },
1694            );
1695            inference.consume_reported_error(err_set, diag_added);
1696        }
1697        (params, return_type, body)
1698    });
1699    let parent_function = match ctx.function_id {
1700        ContextFunction::Global => Maybe::Err(ctx.diagnostics.report(syntax, ClosureInGlobalScope)),
1701        ContextFunction::Function(function_id) => function_id,
1702    };
1703    if matches!(ctx.function_id, ContextFunction::Global) {
1704        ctx.diagnostics.report(syntax, ClosureInGlobalScope);
1705    }
1706
1707    let mut usages = Usages { usages: Default::default() };
1708    let usage = usages.handle_closure(&ctx.arenas, &params, body);
1709    let mut reported = UnorderedHashSet::<_>::default();
1710    // TODO(TomerStarkware): Add support for capturing mutable variables when then we have borrow.
1711    for (captured_var, expr) in
1712        chain!(usage.usage.iter(), usage.snap_usage.iter(), usage.changes.iter())
1713    {
1714        let Some(var) = ctx.semantic_defs.get(&captured_var.base_var()) else {
1715            // if the variable is not found in the semantic defs, it is closure parameter.
1716            continue;
1717        };
1718
1719        if var.is_mut() && reported.insert(expr.stable_ptr()) {
1720            ctx.diagnostics.report(expr.stable_ptr(), MutableCapturedVariable);
1721        }
1722    }
1723
1724    let captured_types = chain!(
1725        usage.snap_usage.values().map(|item| wrap_in_snapshots(ctx.db, item.ty(), 1)),
1726        chain!(usage.usage.values(), usage.changes.values()).map(|item| item.ty())
1727    )
1728    .sorted_by_key(|ty| ty.as_intern_id())
1729    .dedup()
1730    .collect_vec();
1731
1732    let ty = TypeLongId::Closure(ClosureTypeLongId {
1733        param_tys: params.iter().map(|param| param.ty).collect(),
1734        ret_ty,
1735        captured_types,
1736        parent_function,
1737        wrapper_location: StableLocation::new(syntax.wrapper(syntax_db).stable_ptr().into()),
1738    })
1739    .intern(ctx.db);
1740
1741    Ok(Expr::ExprClosure(ExprClosure { body, params, stable_ptr: syntax.stable_ptr().into(), ty }))
1742}
1743
1744/// Computes the semantic model for a body of a closure.
1745fn compute_closure_body_semantic(
1746    ctx: &mut ComputationContext<'_>,
1747    syntax: ast::ExprBlock,
1748) -> ExprId {
1749    let syntax_db = ctx.db.upcast();
1750
1751    let mut statements = syntax.statements(syntax_db).elements(syntax_db);
1752    // Remove the typed tail expression, if exists.
1753    let tail = get_tail_expression(syntax_db, statements.as_slice());
1754    if tail.is_some() {
1755        statements.pop();
1756    }
1757
1758    // Convert statements to semantic model.
1759    let statements_semantic: Vec<_> = statements
1760        .into_iter()
1761        .filter_map(|statement_syntax| {
1762            compute_statement_semantic(ctx, statement_syntax).to_option()
1763        })
1764        .collect();
1765    // Convert tail expression (if exists) to semantic model.
1766    let tail_semantic_expr = tail.map(|tail_expr| compute_expr_semantic(ctx, &tail_expr));
1767    let ty = if let Some(t) = &tail_semantic_expr {
1768        t.ty()
1769    } else if let Some(statement) = statements_semantic.last() {
1770        if let Statement::Return(_) | Statement::Break(_) = &ctx.arenas.statements[*statement] {
1771            never_ty(ctx.db)
1772        } else {
1773            unit_ty(ctx.db)
1774        }
1775    } else {
1776        unit_ty(ctx.db)
1777    };
1778    ctx.arenas.exprs.alloc(Expr::Block(ExprBlock {
1779        statements: statements_semantic,
1780        tail: tail_semantic_expr.map(|expr| expr.id),
1781        ty,
1782        stable_ptr: syntax.stable_ptr().into(),
1783    }))
1784}
1785
1786/// Computes the semantic model of an expression of type [ast::ExprErrorPropagate].
1787fn compute_expr_error_propagate_semantic(
1788    ctx: &mut ComputationContext<'_>,
1789    syntax: &ast::ExprErrorPropagate,
1790) -> Maybe<Expr> {
1791    let syntax_db = ctx.db.upcast();
1792
1793    let return_type = match ctx.inner_ctx {
1794        Some(InnerContext::Closure { return_type }) => return_type,
1795        None | Some(InnerContext::Loop { .. } | InnerContext::While | InnerContext::For) => {
1796            ctx.get_signature(
1797                syntax.into(),
1798                UnsupportedOutsideOfFunctionFeatureName::ErrorPropagate,
1799            )?
1800            .return_type
1801        }
1802    };
1803
1804    let func_err_prop_ty = unwrap_error_propagation_type(ctx.db, return_type)
1805        .ok_or_else(|| ctx.diagnostics.report(syntax, ReturnTypeNotErrorPropagateType))?;
1806
1807    // `inner_expr` is the expr inside the `?`.
1808    let inner_expr = match &func_err_prop_ty {
1809        crate::corelib::ErrorPropagationType::Option { .. } => {
1810            compute_expr_semantic(ctx, &syntax.expr(syntax_db))
1811        }
1812        crate::corelib::ErrorPropagationType::Result { .. } => {
1813            compute_expr_semantic(ctx, &syntax.expr(syntax_db))
1814        }
1815    };
1816    let func_err_variant = func_err_prop_ty.err_variant();
1817
1818    let inner_expr_ty = ctx.reduce_ty(inner_expr.ty());
1819    inner_expr_ty.check_not_missing(ctx.db)?;
1820    let inner_expr_err_prop_ty =
1821        unwrap_error_propagation_type(ctx.db, inner_expr_ty).ok_or_else(|| {
1822            ctx.diagnostics.report(syntax, ErrorPropagateOnNonErrorType(inner_expr_ty))
1823        })?;
1824    let inner_expr_err_variant = inner_expr_err_prop_ty.err_variant();
1825
1826    // Disallow error propagation inside a loop.
1827    if ctx.is_inside_loop() {
1828        ctx.diagnostics.report(syntax, SemanticDiagnosticKind::ErrorPropagateNotAllowedInsideALoop);
1829    }
1830
1831    let conformed_err_variant_ty =
1832        ctx.resolver.inference().conform_ty(func_err_variant.ty, inner_expr_err_variant.ty);
1833    // If conforming the types failed, the next check will fail and a better diagnostic will be
1834    // added.
1835    let err_variant_ty = match conformed_err_variant_ty {
1836        Ok(ty) => ty,
1837        Err(err_set) => {
1838            ctx.resolver.inference().consume_error_without_reporting(err_set);
1839            inner_expr_err_variant.ty
1840        }
1841    };
1842    // TODO(orizi): When auto conversion of types is added, try to convert the error type.
1843    if func_err_variant.ty != err_variant_ty
1844        || func_err_variant.concrete_enum_id.enum_id(ctx.db)
1845            != inner_expr_err_variant.concrete_enum_id.enum_id(ctx.db)
1846    {
1847        ctx.diagnostics.report(syntax, IncompatibleErrorPropagateType {
1848            return_ty: return_type,
1849            err_ty: inner_expr_err_variant.ty,
1850        });
1851    }
1852    Ok(Expr::PropagateError(ExprPropagateError {
1853        inner: inner_expr.id,
1854        ok_variant: inner_expr_err_prop_ty.ok_variant().clone(),
1855        err_variant: inner_expr_err_variant.clone(),
1856        func_err_variant: func_err_variant.clone(),
1857        stable_ptr: syntax.stable_ptr().into(),
1858    }))
1859}
1860
1861/// Computes the semantic model of an expression of type [ast::ExprIndexed].
1862fn compute_expr_indexed_semantic(
1863    ctx: &mut ComputationContext<'_>,
1864    syntax: &ast::ExprIndexed,
1865) -> Maybe<Expr> {
1866    let syntax_db = ctx.db.upcast();
1867    let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
1868    let index_expr_syntax = &syntax.index_expr(syntax_db);
1869    let index_expr = compute_expr_semantic(ctx, index_expr_syntax);
1870    // Make sure the maximal amount of types is known when trying to access. Ignoring the returned
1871    // value, as any errors will be reported later.
1872    ctx.resolver.inference().solve().ok();
1873    let candidate_traits: Vec<_> = ["Index", "IndexView"]
1874        .iter()
1875        .map(|trait_name| get_core_trait(ctx.db, CoreTraitContext::Ops, (*trait_name).into()))
1876        .collect();
1877    let (function_id, _, fixed_expr, mutability) = compute_method_function_call_data(
1878        ctx,
1879        &candidate_traits[..],
1880        "index".into(),
1881        expr,
1882        syntax.into(),
1883        None,
1884        |ty, _, inference_errors| Some(NoImplementationOfIndexOperator { ty, inference_errors }),
1885        |ty, _, _| Some(MultipleImplementationOfIndexOperator(ty)),
1886    )?;
1887
1888    expr_function_call(
1889        ctx,
1890        function_id,
1891        vec![
1892            NamedArg(fixed_expr, None, mutability),
1893            NamedArg(index_expr, None, Mutability::Immutable),
1894        ],
1895        syntax,
1896        index_expr_syntax.stable_ptr(),
1897    )
1898}
1899
1900/// Computes the data needed for a method function call, and similar exprs (index operator). Method
1901/// call and Index operator differs in the diagnostics they emit. The function returns the
1902/// function_id to call, the trait containing the function, the self argument, with snapshots added
1903/// if needed, and the mutability of the self argument.
1904#[expect(clippy::too_many_arguments)]
1905fn compute_method_function_call_data(
1906    ctx: &mut ComputationContext<'_>,
1907    candidate_traits: &[TraitId],
1908    func_name: SmolStr,
1909    self_expr: ExprAndId,
1910    method_syntax: SyntaxStablePtrId,
1911    generic_args_syntax: Option<Vec<ast::GenericArg>>,
1912    no_implementation_diagnostic: impl Fn(
1913        TypeId,
1914        SmolStr,
1915        TraitInferenceErrors,
1916    ) -> Option<SemanticDiagnosticKind>,
1917    multiple_trait_diagnostic: fn(
1918        TypeId,
1919        TraitFunctionId,
1920        TraitFunctionId,
1921    ) -> Option<SemanticDiagnosticKind>,
1922) -> Maybe<(FunctionId, TraitId, ExprAndId, Mutability)> {
1923    let self_ty = ctx.reduce_ty(self_expr.ty());
1924    // Inference errors found when looking for candidates. Only relevant in the case of 0 candidates
1925    // found. If >0 candidates are found these are ignored as they may describe, e.g., "errors"
1926    // indicating certain traits/impls/functions don't match, which is OK as we only look for one.
1927    let mut inference_errors = vec![];
1928    let candidates = filter_candidate_traits(
1929        ctx,
1930        &mut inference_errors,
1931        self_ty,
1932        candidate_traits,
1933        func_name.clone(),
1934        self_expr.stable_ptr().untyped(),
1935    );
1936    let trait_function_id = match candidates[..] {
1937        [] => {
1938            return Err(no_implementation_diagnostic(self_ty, func_name, TraitInferenceErrors {
1939                traits_and_errors: inference_errors,
1940            })
1941            .map(|diag| ctx.diagnostics.report(method_syntax, diag))
1942            .unwrap_or_else(skip_diagnostic));
1943        }
1944        [trait_function_id] => trait_function_id,
1945        [trait_function_id0, trait_function_id1, ..] => {
1946            return Err(multiple_trait_diagnostic(self_ty, trait_function_id0, trait_function_id1)
1947                .map(|diag| ctx.diagnostics.report(method_syntax, diag))
1948                .unwrap_or_else(skip_diagnostic));
1949        }
1950    };
1951    let (function_id, n_snapshots) =
1952        infer_impl_by_self(ctx, trait_function_id, self_ty, method_syntax, generic_args_syntax)?;
1953
1954    let signature = ctx.db.trait_function_signature(trait_function_id).unwrap();
1955    let first_param = signature.params.into_iter().next().unwrap();
1956    let mut fixed_expr = self_expr.clone();
1957    for _ in 0..n_snapshots {
1958        let ty = TypeLongId::Snapshot(fixed_expr.ty()).intern(ctx.db);
1959        let expr = Expr::Snapshot(ExprSnapshot {
1960            inner: fixed_expr.id,
1961            ty,
1962            stable_ptr: self_expr.stable_ptr(),
1963        });
1964        fixed_expr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
1965    }
1966
1967    Ok((
1968        function_id,
1969        trait_function_id.trait_id(ctx.db.upcast()),
1970        fixed_expr,
1971        first_param.mutability,
1972    ))
1973}
1974
1975/// Computes the semantic model of a pattern.
1976/// Note that this pattern will always be "registered" in the arena, so it can be looked up in the
1977/// language server.
1978pub fn compute_pattern_semantic(
1979    ctx: &mut ComputationContext<'_>,
1980    syntax: &ast::Pattern,
1981    ty: TypeId,
1982    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
1983) -> PatternAndId {
1984    let pat = maybe_compute_pattern_semantic(ctx, syntax, ty, or_pattern_variables_map);
1985    let pat = pat.unwrap_or_else(|diag_added| {
1986        Pattern::Missing(PatternMissing {
1987            ty: TypeId::missing(ctx.db, diag_added),
1988            stable_ptr: syntax.stable_ptr(),
1989            diag_added,
1990        })
1991    });
1992    let id = ctx.arenas.patterns.alloc(pat.clone());
1993    PatternAndId { pattern: pat, id }
1994}
1995
1996/// Computes the semantic model of a pattern, or None if invalid.
1997fn maybe_compute_pattern_semantic(
1998    ctx: &mut ComputationContext<'_>,
1999    pattern_syntax: &ast::Pattern,
2000    ty: TypeId,
2001    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2002) -> Maybe<Pattern> {
2003    // TODO(spapini): Check for missing type, and don't reemit an error.
2004    let syntax_db = ctx.db.upcast();
2005    let ty = ctx.reduce_ty(ty);
2006    let stable_ptr = pattern_syntax.into();
2007    let pattern = match pattern_syntax {
2008        ast::Pattern::Underscore(otherwise_pattern) => {
2009            Pattern::Otherwise(PatternOtherwise { ty, stable_ptr: otherwise_pattern.stable_ptr() })
2010        }
2011        ast::Pattern::Literal(literal_pattern) => {
2012            let literal = literal_to_semantic(ctx, literal_pattern)?;
2013            Pattern::Literal(PatternLiteral {
2014                literal,
2015                stable_ptr: literal_pattern.stable_ptr().into(),
2016            })
2017        }
2018        ast::Pattern::ShortString(short_string_pattern) => {
2019            let literal = short_string_to_semantic(ctx, short_string_pattern)?;
2020            Pattern::Literal(PatternLiteral {
2021                literal,
2022                stable_ptr: short_string_pattern.stable_ptr().into(),
2023            })
2024        }
2025        ast::Pattern::String(string_pattern) => {
2026            let string_literal = string_literal_to_semantic(ctx, string_pattern)?;
2027            Pattern::StringLiteral(PatternStringLiteral {
2028                string_literal,
2029                stable_ptr: string_pattern.stable_ptr().into(),
2030            })
2031        }
2032        ast::Pattern::Enum(enum_pattern) => {
2033            let path = enum_pattern.path(syntax_db);
2034            let item = ctx.resolver.resolve_generic_path(
2035                ctx.diagnostics,
2036                &path,
2037                NotFoundItemType::Identifier,
2038                Some(&mut ctx.environment),
2039            )?;
2040            let generic_variant = try_extract_matches!(item, ResolvedGenericItem::Variant)
2041                .ok_or_else(|| ctx.diagnostics.report(&path, NotAVariant))?;
2042
2043            let (concrete_enum, n_snapshots) = extract_concrete_enum_from_pattern_and_validate(
2044                ctx,
2045                pattern_syntax,
2046                ty,
2047                generic_variant.enum_id,
2048            )?;
2049
2050            // TODO(lior): Should we report a diagnostic here?
2051            let concrete_variant = ctx
2052                .db
2053                .concrete_enum_variant(concrete_enum, &generic_variant)
2054                .map_err(|_| ctx.diagnostics.report(&path, UnknownEnum))?;
2055
2056            // Compute inner pattern.
2057            let inner_ty = wrap_in_snapshots(ctx.db, concrete_variant.ty, n_snapshots);
2058
2059            let inner_pattern = match enum_pattern.pattern(syntax_db) {
2060                ast::OptionPatternEnumInnerPattern::Empty(_) => None,
2061                ast::OptionPatternEnumInnerPattern::PatternEnumInnerPattern(p) => {
2062                    let pattern = compute_pattern_semantic(
2063                        ctx,
2064                        &p.pattern(syntax_db),
2065                        inner_ty,
2066                        or_pattern_variables_map,
2067                    );
2068                    Some(pattern.id)
2069                }
2070            };
2071
2072            Pattern::EnumVariant(PatternEnumVariant {
2073                variant: concrete_variant,
2074                inner_pattern,
2075                ty,
2076                stable_ptr: enum_pattern.stable_ptr().into(),
2077            })
2078        }
2079        ast::Pattern::Path(path) => {
2080            // A path of length 1 is an identifier, which will result in a variable pattern.
2081            // Currently, other paths are not supported (and not clear if ever will be).
2082            if path.elements(syntax_db).len() > 1 {
2083                return Err(ctx.diagnostics.report(path, Unsupported));
2084            }
2085            // TODO(spapini): Make sure this is a simple identifier. In particular, no generics.
2086            let identifier = path.elements(syntax_db)[0].identifier_ast(syntax_db);
2087            create_variable_pattern(
2088                ctx,
2089                identifier,
2090                &[],
2091                ty,
2092                path.stable_ptr().into(),
2093                or_pattern_variables_map,
2094            )
2095        }
2096        ast::Pattern::Identifier(identifier) => create_variable_pattern(
2097            ctx,
2098            identifier.name(syntax_db),
2099            &identifier.modifiers(syntax_db).elements(syntax_db),
2100            ty,
2101            identifier.stable_ptr().into(),
2102            or_pattern_variables_map,
2103        ),
2104        ast::Pattern::Struct(pattern_struct) => {
2105            let pattern_ty = try_extract_matches!(
2106                ctx.resolver.resolve_concrete_path_ex(
2107                    ctx.diagnostics,
2108                    &pattern_struct.path(syntax_db),
2109                    NotFoundItemType::Type,
2110                    Some(&mut ctx.environment)
2111                )?,
2112                ResolvedConcreteItem::Type
2113            )
2114            .ok_or_else(|| ctx.diagnostics.report(&pattern_struct.path(syntax_db), NotAType))?;
2115            let inference = &mut ctx.resolver.inference();
2116            inference.conform_ty(pattern_ty, peel_snapshots(ctx.db, ty).1.intern(ctx.db)).map_err(
2117                |err_set| inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr),
2118            )?;
2119            let ty = ctx.reduce_ty(ty);
2120            // Peel all snapshot wrappers.
2121            let (n_snapshots, long_ty) = peel_snapshots(ctx.db, ty);
2122
2123            // Check that type is an struct, and get the concrete struct from it.
2124            let concrete_struct_id = try_extract_matches!(long_ty, TypeLongId::Concrete)
2125                .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2126                .ok_or(())
2127                .or_else(|_| {
2128                    // Don't add a diagnostic if the type is missing.
2129                    // A diagnostic should've already been added.
2130                    ty.check_not_missing(ctx.db)?;
2131                    Err(ctx.diagnostics.report(pattern_struct, UnexpectedStructPattern(ty)))
2132                })?;
2133            let pattern_param_asts = pattern_struct.params(syntax_db).elements(syntax_db);
2134            let struct_id = concrete_struct_id.struct_id(ctx.db);
2135            let mut members = ctx.db.concrete_struct_members(concrete_struct_id)?.as_ref().clone();
2136            let mut used_members = UnorderedHashSet::<_>::default();
2137            let mut get_member = |ctx: &mut ComputationContext<'_>,
2138                                  member_name: SmolStr,
2139                                  stable_ptr: SyntaxStablePtrId| {
2140                let member = members.swap_remove(&member_name).on_none(|| {
2141                    ctx.diagnostics.report(
2142                        stable_ptr,
2143                        if used_members.contains(&member_name) {
2144                            StructMemberRedefinition { struct_id, member_name: member_name.clone() }
2145                        } else {
2146                            NoSuchStructMember { struct_id, member_name: member_name.clone() }
2147                        },
2148                    );
2149                })?;
2150                check_struct_member_is_visible(ctx, &member, stable_ptr, &member_name);
2151                used_members.insert(member_name);
2152                Some(member)
2153            };
2154            let mut field_patterns = vec![];
2155            let mut has_tail = false;
2156            for pattern_param_ast in pattern_param_asts {
2157                match pattern_param_ast {
2158                    PatternStructParam::Single(single) => {
2159                        let name = single.name(syntax_db);
2160                        let Some(member) =
2161                            get_member(ctx, name.text(syntax_db), name.stable_ptr().untyped())
2162                        else {
2163                            continue;
2164                        };
2165                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2166                        let pattern = create_variable_pattern(
2167                            ctx,
2168                            name,
2169                            &single.modifiers(syntax_db).elements(syntax_db),
2170                            ty,
2171                            single.stable_ptr().into(),
2172                            or_pattern_variables_map,
2173                        );
2174                        field_patterns.push((member, ctx.arenas.patterns.alloc(pattern)));
2175                    }
2176                    PatternStructParam::WithExpr(with_expr) => {
2177                        let name = with_expr.name(syntax_db);
2178                        let Some(member) =
2179                            get_member(ctx, name.text(syntax_db), name.stable_ptr().untyped())
2180                        else {
2181                            continue;
2182                        };
2183                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2184                        let pattern = compute_pattern_semantic(
2185                            ctx,
2186                            &with_expr.pattern(syntax_db),
2187                            ty,
2188                            or_pattern_variables_map,
2189                        );
2190                        field_patterns.push((member, pattern.id));
2191                    }
2192                    PatternStructParam::Tail(_) => {
2193                        has_tail = true;
2194                    }
2195                }
2196            }
2197            if !has_tail {
2198                for (member_name, _) in members.iter() {
2199                    ctx.diagnostics.report(pattern_struct, MissingMember(member_name.clone()));
2200                }
2201            }
2202            Pattern::Struct(PatternStruct {
2203                concrete_struct_id,
2204                field_patterns,
2205                ty,
2206                n_snapshots,
2207                stable_ptr: pattern_struct.stable_ptr(),
2208            })
2209        }
2210        ast::Pattern::Tuple(_) => maybe_compute_tuple_like_pattern_semantic(
2211            ctx,
2212            pattern_syntax,
2213            ty,
2214            or_pattern_variables_map,
2215            |ty: TypeId| UnexpectedTuplePattern(ty),
2216            |expected, actual| WrongNumberOfTupleElements { expected, actual },
2217        )?,
2218        ast::Pattern::FixedSizeArray(_) => maybe_compute_tuple_like_pattern_semantic(
2219            ctx,
2220            pattern_syntax,
2221            ty,
2222            or_pattern_variables_map,
2223            |ty: TypeId| UnexpectedFixedSizeArrayPattern(ty),
2224            |expected, actual| WrongNumberOfFixedSizeArrayElements { expected, actual },
2225        )?,
2226        ast::Pattern::False(pattern_false) => {
2227            let enum_expr = extract_matches!(
2228                false_literal_expr(ctx, pattern_false.stable_ptr().into()),
2229                Expr::EnumVariantCtor
2230            );
2231
2232            extract_concrete_enum_from_pattern_and_validate(
2233                ctx,
2234                pattern_syntax,
2235                ty,
2236                enum_expr.variant.concrete_enum_id.enum_id(ctx.db),
2237            )?;
2238
2239            Pattern::EnumVariant(PatternEnumVariant {
2240                variant: enum_expr.variant,
2241                stable_ptr: pattern_false.stable_ptr().into(),
2242                ty,
2243                inner_pattern: None,
2244            })
2245        }
2246        ast::Pattern::True(pattern_true) => {
2247            let enum_expr = extract_matches!(
2248                true_literal_expr(ctx, pattern_true.stable_ptr().into()),
2249                Expr::EnumVariantCtor
2250            );
2251            extract_concrete_enum_from_pattern_and_validate(
2252                ctx,
2253                pattern_syntax,
2254                ty,
2255                enum_expr.variant.concrete_enum_id.enum_id(ctx.db),
2256            )?;
2257
2258            Pattern::EnumVariant(PatternEnumVariant {
2259                variant: enum_expr.variant,
2260                stable_ptr: pattern_true.stable_ptr().into(),
2261                ty,
2262                inner_pattern: None,
2263            })
2264        }
2265    };
2266    let inference = &mut ctx.resolver.inference();
2267    inference.conform_ty(pattern.ty(), ty).map_err(|err_set| {
2268        inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr)
2269    })?;
2270    Ok(pattern)
2271}
2272
2273/// Computes the semantic model of a pattern of a tuple or a fixed size array. Assumes that the
2274/// pattern is one of these types.
2275fn maybe_compute_tuple_like_pattern_semantic(
2276    ctx: &mut ComputationContext<'_>,
2277    pattern_syntax: &ast::Pattern,
2278    ty: TypeId,
2279    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2280    unexpected_pattern: fn(TypeId) -> SemanticDiagnosticKind,
2281    wrong_number_of_elements: fn(usize, usize) -> SemanticDiagnosticKind,
2282) -> Maybe<Pattern> {
2283    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern_syntax)?;
2284    // Assert that the pattern is of the same type as the expr.
2285    match (pattern_syntax, &long_ty) {
2286        (ast::Pattern::Tuple(_), TypeLongId::Tuple(_))
2287        | (ast::Pattern::FixedSizeArray(_), TypeLongId::FixedSizeArray { .. }) => {}
2288        _ => {
2289            return Err(ctx.diagnostics.report(pattern_syntax, unexpected_pattern(ty)));
2290        }
2291    };
2292    let inner_tys = match long_ty {
2293        TypeLongId::Tuple(inner_tys) => inner_tys,
2294        TypeLongId::FixedSizeArray { type_id: inner_ty, size } => {
2295            let size = size
2296                .lookup_intern(ctx.db)
2297                .into_int()
2298                .expect("Expected ConstValue::Int for size")
2299                .to_usize()
2300                .unwrap();
2301            [inner_ty].repeat(size)
2302        }
2303        _ => unreachable!(),
2304    };
2305    let patterns_syntax = match pattern_syntax {
2306        ast::Pattern::Tuple(pattern_tuple) => {
2307            pattern_tuple.patterns(ctx.db.upcast()).elements(ctx.db.upcast())
2308        }
2309        ast::Pattern::FixedSizeArray(pattern_fixed_size_array) => {
2310            pattern_fixed_size_array.patterns(ctx.db.upcast()).elements(ctx.db.upcast())
2311        }
2312        _ => unreachable!(),
2313    };
2314    let size = inner_tys.len();
2315    if size != patterns_syntax.len() {
2316        return Err(ctx
2317            .diagnostics
2318            .report(pattern_syntax, wrong_number_of_elements(size, patterns_syntax.len())));
2319    }
2320    let pattern_options = zip_eq(patterns_syntax, inner_tys).map(|(pattern_ast, ty)| {
2321        let ty = wrap_in_snapshots(ctx.db, ty, n_snapshots);
2322        let pattern = compute_pattern_semantic(ctx, &pattern_ast, ty, or_pattern_variables_map);
2323        Ok(pattern.id)
2324    });
2325    // If all are Some, collect into a Vec.
2326    let field_patterns: Vec<_> = pattern_options.collect::<Maybe<_>>()?;
2327    Ok(match pattern_syntax {
2328        ast::Pattern::Tuple(syntax) => {
2329            Pattern::Tuple(PatternTuple { field_patterns, ty, stable_ptr: syntax.stable_ptr() })
2330        }
2331        ast::Pattern::FixedSizeArray(syntax) => Pattern::FixedSizeArray(PatternFixedSizeArray {
2332            elements_patterns: field_patterns,
2333            ty,
2334            stable_ptr: syntax.stable_ptr(),
2335        }),
2336        _ => unreachable!(),
2337    })
2338}
2339
2340/// Validates that the semantic type of an enum pattern is an enum, and returns the concrete enum.
2341fn extract_concrete_enum_from_pattern_and_validate(
2342    ctx: &mut ComputationContext<'_>,
2343    pattern: &ast::Pattern,
2344    ty: TypeId,
2345    enum_id: EnumId,
2346) -> Maybe<(ConcreteEnumId, usize)> {
2347    // Peel all snapshot wrappers.
2348    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern)?;
2349
2350    // Check that type is an enum, and get the concrete enum from it.
2351    let concrete_enum = try_extract_matches!(long_ty, TypeLongId::Concrete)
2352        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Enum))
2353        .ok_or(())
2354        .or_else(|_| {
2355            // Don't add a diagnostic if the type is missing.
2356            // A diagnostic should've already been added.
2357            ty.check_not_missing(ctx.db)?;
2358            Err(ctx.diagnostics.report(pattern, UnexpectedEnumPattern(ty)))
2359        })?;
2360    // Check that these are the same enums.
2361    if enum_id != concrete_enum.enum_id(ctx.db) {
2362        return Err(ctx.diagnostics.report(pattern, WrongEnum {
2363            expected_enum: concrete_enum.enum_id(ctx.db),
2364            actual_enum: enum_id,
2365        }));
2366    }
2367    Ok((concrete_enum, n_snapshots))
2368}
2369
2370/// Creates a local variable pattern.
2371fn create_variable_pattern(
2372    ctx: &mut ComputationContext<'_>,
2373    identifier: ast::TerminalIdentifier,
2374    modifier_list: &[ast::Modifier],
2375    ty: TypeId,
2376    stable_ptr: ast::PatternPtr,
2377    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2378) -> Pattern {
2379    let syntax_db = ctx.db.upcast();
2380
2381    let var_id = match or_pattern_variables_map.get(&identifier.text(syntax_db)) {
2382        Some(var) => var.id,
2383        None => LocalVarLongId(ctx.resolver.module_file_id, identifier.stable_ptr()).intern(ctx.db),
2384    };
2385    let is_mut = match compute_mutability(ctx.diagnostics, syntax_db, modifier_list) {
2386        Mutability::Immutable => false,
2387        Mutability::Mutable => true,
2388        Mutability::Reference => {
2389            ctx.diagnostics.report(&identifier, ReferenceLocalVariable);
2390            false
2391        }
2392    };
2393    Pattern::Variable(PatternVariable {
2394        name: identifier.text(syntax_db),
2395        var: LocalVariable { id: var_id, ty, is_mut },
2396        stable_ptr,
2397    })
2398}
2399
2400/// Creates a struct constructor semantic expression from its AST.
2401fn struct_ctor_expr(
2402    ctx: &mut ComputationContext<'_>,
2403    ctor_syntax: &ast::ExprStructCtorCall,
2404) -> Maybe<Expr> {
2405    let db = ctx.db;
2406    let syntax_db = db.upcast();
2407    let path = ctor_syntax.path(syntax_db);
2408
2409    // Extract struct.
2410    let ty = resolve_type_with_environment(
2411        db,
2412        ctx.diagnostics,
2413        &mut ctx.resolver,
2414        &ast::Expr::Path(path.clone()),
2415        Some(&mut ctx.environment),
2416    );
2417    ty.check_not_missing(db)?;
2418
2419    let concrete_struct_id = try_extract_matches!(ty.lookup_intern(ctx.db), TypeLongId::Concrete)
2420        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2421        .ok_or_else(|| ctx.diagnostics.report(&path, NotAStruct))?;
2422
2423    if ty.is_phantom(db) {
2424        ctx.diagnostics.report(ctor_syntax, CannotCreateInstancesOfPhantomTypes);
2425    }
2426
2427    let members = db.concrete_struct_members(concrete_struct_id)?;
2428    let mut member_exprs: OrderedHashMap<MemberId, Option<ExprId>> = OrderedHashMap::default();
2429    let mut base_struct = None;
2430
2431    for (index, arg) in ctor_syntax
2432        .arguments(syntax_db)
2433        .arguments(syntax_db)
2434        .elements(syntax_db)
2435        .into_iter()
2436        .enumerate()
2437    {
2438        // TODO: Extract to a function for results.
2439        match arg {
2440            ast::StructArg::StructArgSingle(arg) => {
2441                let arg_identifier = arg.identifier(syntax_db);
2442                let arg_name = arg_identifier.text(syntax_db);
2443
2444                // Find struct member by name.
2445                let Some(member) = members.get(&arg_name) else {
2446                    ctx.diagnostics.report(&arg_identifier, UnknownMember);
2447                    continue;
2448                };
2449                check_struct_member_is_visible(
2450                    ctx,
2451                    member,
2452                    arg_identifier.stable_ptr().untyped(),
2453                    &arg_name,
2454                );
2455
2456                // Extract expression.
2457                let arg_expr = match arg.arg_expr(syntax_db) {
2458                    ast::OptionStructArgExpr::Empty(_) => {
2459                        let Ok(expr) = resolve_variable_by_name(
2460                            ctx,
2461                            &arg_identifier,
2462                            path.stable_ptr().into(),
2463                        ) else {
2464                            // Insert only the member id, for correct duplicate member reporting.
2465                            if member_exprs.insert(member.id, None).is_some() {
2466                                ctx.diagnostics
2467                                    .report(&arg_identifier, MemberSpecifiedMoreThanOnce);
2468                            }
2469                            continue;
2470                        };
2471                        ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) }
2472                    }
2473                    ast::OptionStructArgExpr::StructArgExpr(arg_expr) => {
2474                        compute_expr_semantic(ctx, &arg_expr.expr(syntax_db))
2475                    }
2476                };
2477
2478                // Insert and check for duplicates.
2479                if member_exprs.insert(member.id, Some(arg_expr.id)).is_some() {
2480                    ctx.diagnostics.report(&arg_identifier, MemberSpecifiedMoreThanOnce);
2481                }
2482
2483                // Check types.
2484                let inference = &mut ctx.resolver.inference();
2485                if let Err((err_set, actual_ty, expected_ty)) =
2486                    inference.conform_ty_for_diag(arg_expr.ty(), member.ty)
2487                {
2488                    if !expected_ty.is_missing(db) {
2489                        let diag_added = ctx
2490                            .diagnostics
2491                            .report(&arg_identifier, WrongArgumentType { expected_ty, actual_ty });
2492                        inference.consume_reported_error(err_set, diag_added);
2493                    }
2494                    continue;
2495                }
2496            }
2497            ast::StructArg::StructArgTail(base_struct_syntax) => {
2498                // TODO(TomerStarkware): remove tail expression from argument list.
2499                if index
2500                    != ctor_syntax
2501                        .arguments(syntax_db)
2502                        .arguments(syntax_db)
2503                        .elements(syntax_db)
2504                        .len()
2505                        - 1
2506                {
2507                    ctx.diagnostics.report(&base_struct_syntax, StructBaseStructExpressionNotLast);
2508                    continue;
2509                }
2510                let base_struct_expr =
2511                    compute_expr_semantic(ctx, &base_struct_syntax.expression(syntax_db));
2512                let inference = &mut ctx.resolver.inference();
2513                if let Err((err_set, actual_ty, expected_ty)) =
2514                    inference.conform_ty_for_diag(base_struct_expr.ty(), ty)
2515                {
2516                    let diag_added = ctx.diagnostics.report(
2517                        &base_struct_syntax.expression(syntax_db),
2518                        WrongArgumentType { expected_ty, actual_ty },
2519                    );
2520                    inference.consume_reported_error(err_set, diag_added);
2521                    continue;
2522                }
2523
2524                base_struct = Some((base_struct_expr.id, base_struct_syntax));
2525            }
2526        };
2527    }
2528
2529    // Report errors for missing members.
2530    for (member_name, member) in members.iter() {
2531        if !member_exprs.contains_key(&member.id) {
2532            if base_struct.is_some() {
2533                check_struct_member_is_visible(
2534                    ctx,
2535                    member,
2536                    base_struct.clone().unwrap().1.stable_ptr().untyped(),
2537                    member_name,
2538                );
2539            } else {
2540                ctx.diagnostics.report(ctor_syntax, MissingMember(member_name.clone()));
2541            }
2542        }
2543    }
2544    if members.len() == member_exprs.len() {
2545        if let Some((_, base_struct_syntax)) = base_struct {
2546            return Err(ctx
2547                .diagnostics
2548                .report(&base_struct_syntax, StructBaseStructExpressionNoEffect));
2549        }
2550    }
2551    Ok(Expr::StructCtor(ExprStructCtor {
2552        concrete_struct_id,
2553        members: member_exprs.into_iter().filter_map(|(x, y)| Some((x, y?))).collect(),
2554        base_struct: base_struct.map(|(x, _)| x),
2555        ty: TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)).intern(db),
2556        stable_ptr: ctor_syntax.stable_ptr().into(),
2557    }))
2558}
2559
2560/// Returns the tail expression of the given list of statements, if exists.
2561/// A tail expression is the last statement in the list, if it is an expression and
2562/// it does not end with a semicolon.
2563fn get_tail_expression(
2564    syntax_db: &dyn SyntaxGroup,
2565    statements: &[ast::Statement],
2566) -> Option<ast::Expr> {
2567    let last = statements.last()?;
2568    let statement_expr = try_extract_matches!(last, ast::Statement::Expr)?;
2569    try_extract_matches!(statement_expr.semicolon(syntax_db), ast::OptionTerminalSemicolon::Empty)?;
2570    Some(statement_expr.expr(syntax_db))
2571}
2572
2573/// Creates a new numeric literal expression.
2574fn new_literal_expr(
2575    ctx: &mut ComputationContext<'_>,
2576    ty: Option<&str>,
2577    value: BigInt,
2578    stable_ptr: ExprPtr,
2579) -> Maybe<ExprLiteral> {
2580    let ty = if let Some(ty_str) = ty {
2581        // Requires specific blocking as `NonZero` now has NumericLiteral support.
2582        if ty_str == "NonZero" {
2583            return Err(ctx.diagnostics.report(
2584                stable_ptr.untyped(),
2585                SemanticDiagnosticKind::WrongNumberOfArguments { expected: 1, actual: 0 },
2586            ));
2587        }
2588        try_get_core_ty_by_name(ctx.db, ty_str.into(), vec![])
2589            .map_err(|err| ctx.diagnostics.report(stable_ptr.untyped(), err))?
2590    } else {
2591        ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()))
2592    };
2593
2594    // Numeric trait.
2595    let trait_id = numeric_literal_trait(ctx.db);
2596    let generic_args = vec![GenericArgumentId::Type(ty)];
2597    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
2598    let lookup_context = ctx.resolver.impl_lookup_context();
2599    let inference = &mut ctx.resolver.inference();
2600    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
2601
2602    Ok(ExprLiteral { value, ty, stable_ptr })
2603}
2604
2605/// Creates the semantic model of a literal expression from its AST.
2606fn literal_to_semantic(
2607    ctx: &mut ComputationContext<'_>,
2608    literal_syntax: &ast::TerminalLiteralNumber,
2609) -> Maybe<ExprLiteral> {
2610    let db = ctx.db;
2611    let syntax_db = db.upcast();
2612
2613    let (value, ty) = literal_syntax.numeric_value_and_suffix(syntax_db).unwrap_or_default();
2614    let ty = ty.as_ref().map(SmolStr::as_str);
2615
2616    new_literal_expr(ctx, ty, value, literal_syntax.stable_ptr().into())
2617}
2618
2619/// Creates the semantic model of a short string from its AST.
2620fn short_string_to_semantic(
2621    ctx: &mut ComputationContext<'_>,
2622    short_string_syntax: &ast::TerminalShortString,
2623) -> Maybe<ExprLiteral> {
2624    let db = ctx.db;
2625    let syntax_db = db.upcast();
2626
2627    let value = short_string_syntax.numeric_value(syntax_db).unwrap_or_default();
2628
2629    let suffix = short_string_syntax.suffix(syntax_db);
2630    let suffix = suffix.as_ref().map(SmolStr::as_str);
2631
2632    new_literal_expr(ctx, suffix, value, short_string_syntax.stable_ptr().into())
2633}
2634
2635/// Creates a new string literal expression.
2636fn new_string_literal_expr(
2637    ctx: &mut ComputationContext<'_>,
2638    value: String,
2639    stable_ptr: ExprPtr,
2640) -> Maybe<ExprStringLiteral> {
2641    let ty = ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()));
2642
2643    // String trait.
2644    let trait_id = get_core_trait(ctx.db, CoreTraitContext::TopLevel, "StringLiteral".into());
2645    let generic_args = vec![GenericArgumentId::Type(ty)];
2646    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
2647    let lookup_context = ctx.resolver.impl_lookup_context();
2648    let inference = &mut ctx.resolver.inference();
2649    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
2650
2651    Ok(ExprStringLiteral { value, ty, stable_ptr })
2652}
2653
2654/// Creates the semantic model of a string literal from its AST.
2655fn string_literal_to_semantic(
2656    ctx: &mut ComputationContext<'_>,
2657    string_syntax: &ast::TerminalString,
2658) -> Maybe<ExprStringLiteral> {
2659    let db = ctx.db;
2660    let syntax_db = db.upcast();
2661    let stable_ptr = string_syntax.stable_ptr();
2662
2663    let value = string_syntax.string_value(syntax_db).unwrap_or_default();
2664    // TODO(yuval): support prefixes/suffixes for explicit types?
2665
2666    new_string_literal_expr(ctx, value, stable_ptr.into())
2667}
2668
2669/// Given an expression syntax, if it's an identifier, returns it. Otherwise, returns the proper
2670/// error.
2671fn expr_as_identifier(
2672    ctx: &mut ComputationContext<'_>,
2673    path: &ast::ExprPath,
2674    syntax_db: &dyn SyntaxGroup,
2675) -> Maybe<SmolStr> {
2676    let segments = path.elements(syntax_db);
2677    if segments.len() == 1 {
2678        return Ok(segments[0].identifier(syntax_db));
2679    }
2680    Err(ctx.diagnostics.report(path, InvalidMemberExpression))
2681}
2682
2683// TODO(spapini): Consider moving some checks here to the responsibility of the parser.
2684/// Computes the semantic expression for a dot expression.
2685fn dot_expr(
2686    ctx: &mut ComputationContext<'_>,
2687    lexpr: ExprAndId,
2688    rhs_syntax: ast::Expr,
2689    stable_ptr: ast::ExprPtr,
2690) -> Maybe<Expr> {
2691    // Find MemberId.
2692    match rhs_syntax {
2693        ast::Expr::Path(expr) => member_access_expr(ctx, lexpr, expr, stable_ptr),
2694        ast::Expr::FunctionCall(expr) => method_call_expr(ctx, lexpr, expr, stable_ptr),
2695        _ => Err(ctx.diagnostics.report(&rhs_syntax, InvalidMemberExpression)),
2696    }
2697}
2698
2699/// Finds all the trait ids usable in the current context.
2700fn traits_in_context(
2701    ctx: &mut ComputationContext<'_>,
2702) -> Maybe<OrderedHashMap<TraitId, LookupItemId>> {
2703    let mut traits =
2704        ctx.db.module_usable_trait_ids(ctx.resolver.prelude_submodule())?.deref().clone();
2705    traits.extend(
2706        ctx.db
2707            .module_usable_trait_ids(ctx.resolver.module_file_id.0)?
2708            .iter()
2709            .map(|(k, v)| (*k, *v)),
2710    );
2711    Ok(traits)
2712}
2713
2714/// Computes the semantic model of a method call expression (e.g. "expr.method(..)").
2715/// Finds all traits with at least one candidate impl with a matching `self` param.
2716/// If more/less than 1 such trait exists, fails.
2717fn method_call_expr(
2718    ctx: &mut ComputationContext<'_>,
2719    lexpr: ExprAndId,
2720    expr: ast::ExprFunctionCall,
2721    stable_ptr: ast::ExprPtr,
2722) -> Maybe<Expr> {
2723    // TODO(spapini): Add ctx.module_id.
2724    // TODO(spapini): Look also in uses.
2725    let syntax_db = ctx.db.upcast();
2726    let path = expr.path(syntax_db);
2727    let Ok([segment]): Result<[_; 1], _> = path.elements(syntax_db).try_into() else {
2728        return Err(ctx.diagnostics.report(&expr, InvalidMemberExpression));
2729    };
2730    let func_name = segment.identifier(syntax_db);
2731    let generic_args_syntax = segment.generic_args(syntax_db);
2732    // Save some work. ignore the result. The error, if any, will be reported later.
2733    ctx.resolver.inference().solve().ok();
2734
2735    let mut candidate_traits = traits_in_context(ctx)?;
2736
2737    // Add traits from impl generic args in the context.
2738    for generic_param in &ctx.resolver.data.generic_params {
2739        if generic_param.kind(ctx.db.upcast()) == GenericKind::Impl {
2740            let Ok(trait_id) = ctx.db.generic_impl_param_trait(*generic_param) else {
2741                continue;
2742            };
2743            candidate_traits
2744                .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
2745        }
2746    }
2747
2748    let (function_id, actual_trait_id, fixed_lexpr, mutability) =
2749        compute_method_function_call_data(
2750            ctx,
2751            candidate_traits.keys().copied().collect_vec().as_slice(),
2752            func_name,
2753            lexpr,
2754            path.stable_ptr().untyped(),
2755            generic_args_syntax,
2756            |ty, method_name, inference_errors| {
2757                Some(CannotCallMethod { ty, method_name, inference_errors })
2758            },
2759            |_, trait_function_id0, trait_function_id1| {
2760                Some(AmbiguousTrait { trait_function_id0, trait_function_id1 })
2761            },
2762        )?;
2763    ctx.resolver.data.used_items.insert(candidate_traits[&actual_trait_id]);
2764
2765    ctx.resolver.data.resolved_items.mark_concrete(
2766        ctx.db,
2767        &segment,
2768        ResolvedConcreteItem::Function(function_id),
2769    );
2770
2771    // Note there may be n+1 arguments for n parameters, if the last one is a coupon.
2772    let mut args_iter =
2773        expr.arguments(syntax_db).arguments(syntax_db).elements(syntax_db).into_iter();
2774    // Self argument.
2775    let mut named_args = vec![NamedArg(fixed_lexpr, None, mutability)];
2776    // Other arguments.
2777    for _ in function_parameter_types(ctx, function_id)?.skip(1) {
2778        let Some(arg_syntax) = args_iter.next() else {
2779            break;
2780        };
2781        named_args.push(compute_named_argument_clause(ctx, arg_syntax));
2782    }
2783
2784    // Maybe coupon
2785    if let Some(arg_syntax) = args_iter.next() {
2786        named_args.push(compute_named_argument_clause(ctx, arg_syntax));
2787    }
2788
2789    expr_function_call(ctx, function_id, named_args, &expr, stable_ptr)
2790}
2791
2792/// Computes the semantic model of a member access expression (e.g. "expr.member").
2793fn member_access_expr(
2794    ctx: &mut ComputationContext<'_>,
2795    lexpr: ExprAndId,
2796    rhs_syntax: ast::ExprPath,
2797    stable_ptr: ast::ExprPtr,
2798) -> Maybe<Expr> {
2799    let syntax_db = ctx.db.upcast();
2800
2801    // Find MemberId.
2802    let member_name = expr_as_identifier(ctx, &rhs_syntax, syntax_db)?;
2803    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, lexpr.ty(), &rhs_syntax)?;
2804
2805    match &long_ty {
2806        TypeLongId::Concrete(_) | TypeLongId::Tuple(_) | TypeLongId::FixedSizeArray { .. } => {
2807            let Some(EnrichedTypeMemberAccess { member, deref_functions }) =
2808                get_enriched_type_member_access(ctx, lexpr.clone(), stable_ptr, &member_name)?
2809            else {
2810                return Err(ctx.diagnostics.report(&rhs_syntax, NoSuchTypeMember {
2811                    ty: long_ty.intern(ctx.db),
2812                    member_name,
2813                }));
2814            };
2815            check_struct_member_is_visible(
2816                ctx,
2817                &member,
2818                rhs_syntax.stable_ptr().untyped(),
2819                &member_name,
2820            );
2821            let member_path = match &long_ty {
2822                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id))
2823                    if n_snapshots == 0 && deref_functions.is_empty() =>
2824                {
2825                    lexpr.as_member_path().map(|parent| ExprVarMemberPath::Member {
2826                        parent: Box::new(parent),
2827                        member_id: member.id,
2828                        stable_ptr,
2829                        concrete_struct_id: *concrete_struct_id,
2830                        ty: member.ty,
2831                    })
2832                }
2833                _ => None,
2834            };
2835            let mut derefed_expr: ExprAndId = lexpr;
2836            for (deref_function, mutability) in &deref_functions {
2837                let cur_expr = expr_function_call(
2838                    ctx,
2839                    *deref_function,
2840                    vec![NamedArg(derefed_expr, None, *mutability)],
2841                    stable_ptr,
2842                    stable_ptr,
2843                )
2844                .unwrap();
2845
2846                derefed_expr =
2847                    ExprAndId { expr: cur_expr.clone(), id: ctx.arenas.exprs.alloc(cur_expr) };
2848            }
2849            let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, derefed_expr.ty(), &rhs_syntax)?;
2850            let derefed_expr_concrete_struct_id = match long_ty {
2851                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
2852                    concrete_struct_id
2853                }
2854                _ => unreachable!(),
2855            };
2856            let ty = if !deref_functions.is_empty() {
2857                member.ty
2858            } else {
2859                wrap_in_snapshots(ctx.db, member.ty, n_snapshots)
2860            };
2861            Ok(Expr::MemberAccess(ExprMemberAccess {
2862                expr: derefed_expr.id,
2863                concrete_struct_id: derefed_expr_concrete_struct_id,
2864                member: member.id,
2865                ty,
2866                member_path,
2867                n_snapshots,
2868                stable_ptr,
2869            }))
2870        }
2871
2872        TypeLongId::Snapshot(_) => {
2873            // TODO(spapini): Handle snapshot members.
2874            Err(ctx.diagnostics.report(&rhs_syntax, Unsupported))
2875        }
2876        TypeLongId::Closure(_) => Err(ctx.diagnostics.report(&rhs_syntax, Unsupported)),
2877        TypeLongId::ImplType(impl_type_id) => {
2878            unreachable!(
2879                "Impl type should've been reduced {:?}.",
2880                impl_type_id.debug(ctx.db.elongate())
2881            )
2882        }
2883        TypeLongId::Var(_) => Err(ctx.diagnostics.report(
2884            &rhs_syntax,
2885            InternalInferenceError(InferenceError::TypeNotInferred(long_ty.intern(ctx.db))),
2886        )),
2887        TypeLongId::GenericParameter(_) | TypeLongId::Coupon(_) => Err(ctx
2888            .diagnostics
2889            .report(&rhs_syntax, TypeHasNoMembers { ty: long_ty.intern(ctx.db), member_name })),
2890        TypeLongId::Missing(diag_added) => Err(*diag_added),
2891        TypeLongId::TraitType(_) => {
2892            panic!("Trait types should only appear in traits, where there are no function bodies.")
2893        }
2894    }
2895}
2896
2897/// Returns the member and the deref operations needed for its access.
2898///
2899/// Enriched members include both direct members (in case of a struct), and members of derefed types
2900/// if the type implements the Deref trait into a struct.
2901fn get_enriched_type_member_access(
2902    ctx: &mut ComputationContext<'_>,
2903    expr: ExprAndId,
2904    stable_ptr: ast::ExprPtr,
2905    accessed_member_name: &str,
2906) -> Maybe<Option<EnrichedTypeMemberAccess>> {
2907    let (_, mut long_ty) = peel_snapshots(ctx.db, expr.ty());
2908    if matches!(long_ty, TypeLongId::Var(_)) {
2909        // Save some work. ignore the result. The error, if any, will be reported later.
2910        ctx.resolver.inference().solve().ok();
2911        long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
2912    }
2913    let (_, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
2914    let base_var = match &expr.expr {
2915        Expr::Var(expr_var) => Some(expr_var.var),
2916        Expr::MemberAccess(ExprMemberAccess { member_path: Some(member_path), .. }) => {
2917            Some(member_path.base_var())
2918        }
2919        _ => None,
2920    };
2921    let is_mut_var = base_var
2922        .filter(|var_id| matches!(ctx.semantic_defs.get(var_id), Some(var) if var.is_mut()))
2923        .is_some();
2924    let ty = long_ty.clone().intern(ctx.db);
2925    let key = (ty, is_mut_var);
2926    let mut enriched_members = match ctx.resolver.type_enriched_members.entry(key) {
2927        Entry::Occupied(entry) => {
2928            let e = entry.get();
2929            match e.get_member(accessed_member_name) {
2930                Some(value) => return Ok(Some(value)),
2931                None => {
2932                    if e.exploration_tail.is_none() {
2933                        // There's no further exploration to be done, and member was not found.
2934                        return Ok(None);
2935                    }
2936                }
2937            }
2938            // Moving out of the map to call `enrich_members` and insert back with updated value.
2939            entry.swap_remove()
2940        }
2941        Entry::Vacant(_) => {
2942            let members =
2943                if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
2944                    let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
2945                    if let Some(member) = members.get(accessed_member_name) {
2946                        // Found direct member access - so directly returning it.
2947                        return Ok(Some(EnrichedTypeMemberAccess {
2948                            member: member.clone(),
2949                            deref_functions: vec![],
2950                        }));
2951                    }
2952                    members.iter().map(|(k, v)| (k.clone(), (v.clone(), 0))).collect()
2953                } else {
2954                    Default::default()
2955                };
2956            EnrichedMembers { members, deref_functions: vec![], exploration_tail: Some(expr.id) }
2957        }
2958    };
2959    enrich_members(ctx, &mut enriched_members, is_mut_var, stable_ptr, accessed_member_name)?;
2960    let e = ctx.resolver.type_enriched_members.entry(key).or_insert(enriched_members);
2961    Ok(e.get_member(accessed_member_name))
2962}
2963
2964/// Enriches the `enriched_members` with members from "deref"s of the current type.
2965///
2966/// The function will stop enriching if it encounters a cycle in the deref chain, or if the
2967/// requested member is found.
2968fn enrich_members(
2969    ctx: &mut ComputationContext<'_>,
2970    enriched_members: &mut EnrichedMembers,
2971    is_mut_var: bool,
2972    stable_ptr: ast::ExprPtr,
2973    accessed_member_name: &str,
2974) -> Maybe<()> {
2975    let EnrichedMembers { members: enriched, deref_functions, exploration_tail } = enriched_members;
2976    let mut visited_types: OrderedHashSet<TypeId> = OrderedHashSet::default();
2977
2978    let expr_id =
2979        exploration_tail.expect("`enrich_members` should be called with a `calc_tail` value.");
2980    let mut expr = ExprAndId { expr: ctx.arenas.exprs[expr_id].clone(), id: expr_id };
2981
2982    let deref_mut_trait_id = deref_mut_trait(ctx.db);
2983    let deref_trait_id = deref_trait(ctx.db);
2984
2985    let compute_deref_method_function_call_data =
2986        |ctx: &mut ComputationContext<'_>, expr: ExprAndId, use_deref_mut: bool| {
2987            let deref_trait = if use_deref_mut { deref_mut_trait_id } else { deref_trait_id };
2988            compute_method_function_call_data(
2989                ctx,
2990                &[deref_trait],
2991                if use_deref_mut { "deref_mut".into() } else { "deref".into() },
2992                expr.clone(),
2993                stable_ptr.0,
2994                None,
2995                |_, _, _| None,
2996                |_, _, _| None,
2997            )
2998        };
2999
3000    // If the variable is mutable, and implements DerefMut, we use DerefMut in the first iteration.
3001    let mut use_deref_mut = deref_functions.is_empty()
3002        && is_mut_var
3003        && compute_deref_method_function_call_data(ctx, expr.clone(), true).is_ok();
3004
3005    // This function either finds a member and sets `exploration_tail` or finishes the exploration
3006    // and leaves that exploration tail as `None`.
3007    *exploration_tail = None;
3008
3009    // Add members of derefed types.
3010    while let Ok((function_id, _, cur_expr, mutability)) =
3011        compute_deref_method_function_call_data(ctx, expr, use_deref_mut)
3012    {
3013        deref_functions.push((function_id, mutability));
3014        use_deref_mut = false;
3015        let n_deref = deref_functions.len();
3016        expr = cur_expr;
3017        let derefed_expr = expr_function_call(
3018            ctx,
3019            function_id,
3020            vec![NamedArg(expr, None, mutability)],
3021            stable_ptr,
3022            stable_ptr,
3023        )?;
3024        let ty = ctx.reduce_ty(derefed_expr.ty());
3025        let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
3026        // If the type is still a variable we stop looking for derefed members.
3027        if let TypeLongId::Var(_) = long_ty {
3028            break;
3029        }
3030        expr = ExprAndId { expr: derefed_expr.clone(), id: ctx.arenas.exprs.alloc(derefed_expr) };
3031        if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
3032            let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
3033            for (member_name, member) in members.iter() {
3034                // Insert member if there is not already a member with the same name.
3035                enriched.entry(member_name.clone()).or_insert_with(|| (member.clone(), n_deref));
3036            }
3037            // If member is contained we can stop the calculation post the lookup.
3038            if members.contains_key(accessed_member_name) {
3039                // Found member, so exploration isn't done - setting up the tail.
3040                *exploration_tail = Some(expr.id);
3041                break;
3042            }
3043        }
3044        if !visited_types.insert(long_ty.intern(ctx.db)) {
3045            // Break if we have a cycle. A diagnostic will be reported from the impl and not from
3046            // member access.
3047            break;
3048        }
3049    }
3050    Ok(())
3051}
3052
3053/// Peels snapshots from a type and making sure it is fully not a variable type.
3054fn finalized_snapshot_peeled_ty(
3055    ctx: &mut ComputationContext<'_>,
3056    ty: TypeId,
3057    stable_ptr: impl Into<SyntaxStablePtrId>,
3058) -> Maybe<(usize, TypeLongId)> {
3059    let ty = ctx.reduce_ty(ty);
3060    let (base_snapshots, mut long_ty) = peel_snapshots(ctx.db, ty);
3061    if let TypeLongId::ImplType(impl_type_id) = long_ty {
3062        let inference = &mut ctx.resolver.inference();
3063        let Ok(ty) = inference.reduce_impl_ty(impl_type_id) else {
3064            return Err(ctx
3065                .diagnostics
3066                .report(stable_ptr, InternalInferenceError(InferenceError::TypeNotInferred(ty))));
3067        };
3068        long_ty = ty.lookup_intern(ctx.db);
3069    }
3070    if matches!(long_ty, TypeLongId::Var(_)) {
3071        // Save some work. ignore the result. The error, if any, will be reported later.
3072        ctx.resolver.inference().solve().ok();
3073        long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
3074    }
3075    let (additional_snapshots, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
3076    Ok((base_snapshots + additional_snapshots, long_ty))
3077}
3078
3079/// Resolves a variable or a constant given a context and a path expression.
3080fn resolve_expr_path(ctx: &mut ComputationContext<'_>, path: &ast::ExprPath) -> Maybe<Expr> {
3081    let db = ctx.db;
3082    let syntax_db = db.upcast();
3083    let segments = path.elements(syntax_db);
3084    if segments.is_empty() {
3085        return Err(ctx.diagnostics.report(path, Unsupported));
3086    }
3087
3088    // Check if this is a variable.
3089    if let [PathSegment::Simple(ident_segment)] = &segments[..] {
3090        let identifier = ident_segment.ident(syntax_db);
3091        let variable_name = identifier.text(ctx.db.upcast());
3092        if let Some(res) = get_binded_expr_by_name(ctx, &variable_name, path.stable_ptr().into()) {
3093            match res.clone() {
3094                Expr::Var(expr_var) => {
3095                    let item = ResolvedGenericItem::Variable(expr_var.var);
3096                    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(), item);
3097                }
3098                Expr::Constant(expr_const) => {
3099                    let item = ResolvedConcreteItem::Constant(expr_const.const_value_id);
3100                    ctx.resolver.data.resolved_items.concrete.insert(identifier.stable_ptr(), item);
3101                }
3102                _ => unreachable!(
3103                    "get_binded_expr_by_name should only return variables or constants"
3104                ),
3105            };
3106            return Ok(res);
3107        }
3108    }
3109
3110    let resolved_item: ResolvedConcreteItem = ctx.resolver.resolve_concrete_path_ex(
3111        ctx.diagnostics,
3112        path,
3113        NotFoundItemType::Identifier,
3114        Some(&mut ctx.environment),
3115    )?;
3116
3117    match resolved_item {
3118        ResolvedConcreteItem::Constant(const_value_id) => Ok(Expr::Constant(ExprConstant {
3119            const_value_id,
3120            ty: const_value_id.ty(db)?,
3121            stable_ptr: path.stable_ptr().into(),
3122        })),
3123
3124        ResolvedConcreteItem::Variant(variant) if variant.ty == unit_ty(db) => {
3125            let stable_ptr = path.stable_ptr().into();
3126            let concrete_enum_id = variant.concrete_enum_id;
3127            Ok(semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
3128                variant,
3129                value_expr: unit_expr(ctx, stable_ptr),
3130                ty: TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)).intern(db),
3131                stable_ptr,
3132            }))
3133        }
3134        resolved_item => Err(ctx.diagnostics.report(path, UnexpectedElement {
3135            expected: vec![ElementKind::Variable, ElementKind::Constant],
3136            actual: (&resolved_item).into(),
3137        })),
3138    }
3139}
3140
3141/// Resolves a variable given a context and a simple name.
3142///
3143/// Reports a diagnostic if the variable was not found.
3144pub fn resolve_variable_by_name(
3145    ctx: &mut ComputationContext<'_>,
3146    identifier: &ast::TerminalIdentifier,
3147    stable_ptr: ast::ExprPtr,
3148) -> Maybe<Expr> {
3149    let variable_name = identifier.text(ctx.db.upcast());
3150    let res = get_binded_expr_by_name(ctx, &variable_name, stable_ptr)
3151        .ok_or_else(|| ctx.diagnostics.report(identifier, VariableNotFound(variable_name)))?;
3152    let item = ResolvedGenericItem::Variable(extract_matches!(&res, Expr::Var).var);
3153    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(), item);
3154    Ok(res)
3155}
3156
3157/// Returns the requested variable from the environment if it exists. Returns None otherwise.
3158pub fn get_binded_expr_by_name(
3159    ctx: &mut ComputationContext<'_>,
3160    variable_name: &SmolStr,
3161    stable_ptr: ast::ExprPtr,
3162) -> Option<Expr> {
3163    let mut maybe_env = Some(&mut *ctx.environment);
3164    while let Some(env) = maybe_env {
3165        if let Some(var) = env.variables.get(variable_name) {
3166            env.used_variables.insert(var.id());
3167            return match var {
3168                Binding::LocalItem(local_const) => match local_const.kind.clone() {
3169                    crate::StatementItemKind::Constant(const_value_id, ty) => {
3170                        Some(Expr::Constant(ExprConstant { const_value_id, ty, stable_ptr }))
3171                    }
3172                },
3173                Binding::LocalVar(_) | Binding::Param(_) => {
3174                    Some(Expr::Var(ExprVar { var: var.id(), ty: var.ty(), stable_ptr }))
3175                }
3176            };
3177        }
3178        maybe_env = env.parent.as_deref_mut();
3179    }
3180    None
3181}
3182
3183/// Typechecks a function call.
3184fn expr_function_call(
3185    ctx: &mut ComputationContext<'_>,
3186    function_id: FunctionId,
3187    mut named_args: Vec<NamedArg>,
3188    call_ptr: impl Into<SyntaxStablePtrId>,
3189    stable_ptr: ast::ExprPtr,
3190) -> Maybe<Expr> {
3191    let coupon_arg = maybe_pop_coupon_argument(ctx, &mut named_args, function_id);
3192
3193    let signature = ctx.db.concrete_function_signature(function_id)?;
3194    let signature = ctx.resolver.inference().rewrite(signature).unwrap();
3195
3196    // TODO(spapini): Better location for these diagnostics after the refactor for generics resolve.
3197    if named_args.len() != signature.params.len() {
3198        return Err(ctx.diagnostics.report(call_ptr, WrongNumberOfArguments {
3199            expected: signature.params.len(),
3200            actual: named_args.len(),
3201        }));
3202    }
3203
3204    // Check argument names and types.
3205    check_named_arguments(&named_args, &signature, ctx)?;
3206
3207    let mut args = Vec::new();
3208    for (NamedArg(arg, _name, mutability), param) in
3209        named_args.into_iter().zip(signature.params.iter())
3210    {
3211        let arg_typ = arg.ty();
3212        let param_typ = param.ty;
3213        // Don't add diagnostic if the type is missing (a diagnostic should have already been
3214        // added).
3215        // TODO(lior): Add a test to missing type once possible.
3216        if !arg_typ.is_missing(ctx.db) {
3217            let inference = &mut ctx.resolver.inference();
3218            if let Err((err_set, actual_ty, expected_ty)) =
3219                inference.conform_ty_for_diag(arg_typ, param_typ)
3220            {
3221                let diag_added = ctx
3222                    .diagnostics
3223                    .report(arg.deref(), WrongArgumentType { expected_ty, actual_ty });
3224                inference.consume_reported_error(err_set, diag_added);
3225            }
3226        }
3227
3228        args.push(if param.mutability == Mutability::Reference {
3229            // Verify the argument is a variable.
3230            let Some(ref_arg) = arg.as_member_path() else {
3231                return Err(ctx.diagnostics.report(arg.deref(), RefArgNotAVariable));
3232            };
3233            // Verify the variable argument is mutable.
3234            if !ctx.semantic_defs[&ref_arg.base_var()].is_mut() {
3235                ctx.diagnostics.report(arg.deref(), RefArgNotMutable);
3236            }
3237            // Verify that it is passed explicitly as 'ref'.
3238            if mutability != Mutability::Reference {
3239                ctx.diagnostics.report(arg.deref(), RefArgNotExplicit);
3240            }
3241            ExprFunctionCallArg::Reference(ref_arg)
3242        } else {
3243            // Verify that it is passed without modifiers.
3244            if mutability != Mutability::Immutable {
3245                ctx.diagnostics.report(arg.deref(), ImmutableArgWithModifiers);
3246            }
3247            ExprFunctionCallArg::Value(arg.id)
3248        });
3249    }
3250
3251    let expr_function_call = ExprFunctionCall {
3252        function: function_id,
3253        args,
3254        coupon_arg,
3255        ty: signature.return_type,
3256        stable_ptr,
3257    };
3258    // Check panicable.
3259    if signature.panicable && has_panic_incompatibility(ctx, &expr_function_call) {
3260        // TODO(spapini): Delay this check until after inference, to allow resolving specific
3261        //   impls first.
3262        return Err(ctx.diagnostics.report(call_ptr, PanicableFromNonPanicable));
3263    }
3264    Ok(Expr::FunctionCall(expr_function_call))
3265}
3266
3267/// Checks if the last item in `named_args`, has the argument name `__coupon__`, and removes and
3268/// returns it if so.
3269fn maybe_pop_coupon_argument(
3270    ctx: &mut ComputationContext<'_>,
3271    named_args: &mut Vec<NamedArg>,
3272    function_id: FunctionId,
3273) -> Option<id_arena::Id<Expr>> {
3274    let mut coupon_arg: Option<ExprId> = None;
3275    if let Some(NamedArg(arg, Some(name_terminal), mutability)) = named_args.last() {
3276        let coupons_enabled = are_coupons_enabled(ctx.db, ctx.resolver.module_file_id);
3277        if name_terminal.text(ctx.db.upcast()) == "__coupon__" && coupons_enabled {
3278            // Check that the argument type is correct.
3279            let expected_ty = TypeLongId::Coupon(function_id).intern(ctx.db);
3280            let arg_typ = arg.ty();
3281            if !arg_typ.is_missing(ctx.db) {
3282                let inference = &mut ctx.resolver.inference();
3283                if let Err((err_set, actual_ty, expected_ty)) =
3284                    inference.conform_ty_for_diag(arg_typ, expected_ty)
3285                {
3286                    let diag_added = ctx
3287                        .diagnostics
3288                        .report(arg.deref(), WrongArgumentType { expected_ty, actual_ty });
3289                    inference.consume_reported_error(err_set, diag_added);
3290                }
3291            }
3292
3293            // Check that the argument is not mutable/reference.
3294            if *mutability != Mutability::Immutable {
3295                ctx.diagnostics.report(arg.deref(), CouponArgumentNoModifiers);
3296            }
3297
3298            coupon_arg = Some(arg.id);
3299
3300            // Remove the __coupon__ argument from the argument list.
3301            named_args.pop();
3302        }
3303    }
3304    coupon_arg
3305}
3306
3307/// Checks if a panicable function is called from a disallowed context.
3308fn has_panic_incompatibility(
3309    ctx: &mut ComputationContext<'_>,
3310    expr_function_call: &ExprFunctionCall,
3311) -> bool {
3312    // If this is not an actual function call, but actually a minus literal (e.g. -1), then this is
3313    // the same as nopanic.
3314    if try_extract_minus_literal(ctx.db, &ctx.arenas.exprs, expr_function_call).is_some() {
3315        return false;
3316    }
3317    if let Some(signature) = ctx.signature {
3318        // If the caller is nopanic, then this is a panic incompatibility.
3319        !signature.panicable
3320    } else {
3321        false
3322    }
3323}
3324
3325/// Checks the correctness of the named arguments, and outputs diagnostics on errors.
3326fn check_named_arguments(
3327    named_args: &[NamedArg],
3328    signature: &Signature,
3329    ctx: &mut ComputationContext<'_>,
3330) -> Maybe<()> {
3331    let mut res: Maybe<()> = Ok(());
3332
3333    // Indicates whether we saw a named argument. Used to report a diagnostic if an unnamed argument
3334    // will follow it.
3335    let mut seen_named_arguments: bool = false;
3336    // Indicates whether a [UnnamedArgumentFollowsNamed] diagnostic was reported. Used to prevent
3337    // multiple similar diagnostics.
3338    let mut reported_unnamed_argument_follows_named: bool = false;
3339    for (NamedArg(arg, name_opt, _mutability), param) in
3340        named_args.iter().zip(signature.params.iter())
3341    {
3342        // Check name.
3343        if let Some(name_terminal) = name_opt {
3344            seen_named_arguments = true;
3345            let name = name_terminal.text(ctx.db.upcast());
3346            if param.name != name.clone() {
3347                res = Err(ctx.diagnostics.report(name_terminal, NamedArgumentMismatch {
3348                    expected: param.name.clone(),
3349                    found: name,
3350                }));
3351            }
3352        } else if seen_named_arguments && !reported_unnamed_argument_follows_named {
3353            reported_unnamed_argument_follows_named = true;
3354            res = Err(ctx.diagnostics.report(arg.deref(), UnnamedArgumentFollowsNamed));
3355        }
3356    }
3357    res
3358}
3359
3360/// Computes the semantic model of a statement (excluding tail-expression).
3361pub fn compute_statement_semantic(
3362    ctx: &mut ComputationContext<'_>,
3363    syntax: ast::Statement,
3364) -> Maybe<StatementId> {
3365    let db = ctx.db;
3366    let syntax_db = db.upcast();
3367    // As for now, statement attributes does not have any semantic affect, so we only validate they
3368    // are allowed.
3369    validate_statement_attributes(ctx, &syntax);
3370    let feature_restore = ctx
3371        .resolver
3372        .data
3373        .feature_config
3374        .override_with(extract_item_feature_config(db, &syntax, ctx.diagnostics));
3375    let statement = match &syntax {
3376        ast::Statement::Let(let_syntax) => {
3377            let rhs_syntax = &let_syntax.rhs(syntax_db);
3378            let (rhs_expr, ty) = match let_syntax.type_clause(syntax_db) {
3379                ast::OptionTypeClause::Empty(_) => {
3380                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
3381                    let inferred_type = rhs_expr.ty();
3382                    (rhs_expr, inferred_type)
3383                }
3384                ast::OptionTypeClause::TypeClause(type_clause) => {
3385                    let var_type_path = type_clause.ty(syntax_db);
3386                    let explicit_type = resolve_type_with_environment(
3387                        db,
3388                        ctx.diagnostics,
3389                        &mut ctx.resolver,
3390                        &var_type_path,
3391                        Some(&mut ctx.environment),
3392                    );
3393
3394                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
3395                    let inferred_type = ctx.reduce_ty(rhs_expr.ty());
3396                    if !inferred_type.is_missing(db) {
3397                        let inference = &mut ctx.resolver.inference();
3398                        if let Err((err_set, actual_ty, expected_ty)) =
3399                            inference.conform_ty_for_diag(inferred_type, explicit_type)
3400                        {
3401                            let diag_added = ctx
3402                                .diagnostics
3403                                .report(rhs_syntax, WrongArgumentType { expected_ty, actual_ty });
3404                            inference.consume_reported_error(err_set, diag_added);
3405                        }
3406                    }
3407                    (rhs_expr, explicit_type)
3408                }
3409            };
3410            let rhs_expr_id = rhs_expr.id;
3411
3412            let pattern = compute_pattern_semantic(
3413                ctx,
3414                &let_syntax.pattern(syntax_db),
3415                ty,
3416                &mut UnorderedHashMap::default(),
3417            );
3418            let variables = pattern.variables(&ctx.arenas.patterns);
3419            for v in variables {
3420                let var_def = Binding::LocalVar(v.var.clone());
3421                if let Some(old_var) =
3422                    ctx.environment.variables.insert(v.name.clone(), var_def.clone())
3423                {
3424                    if matches!(old_var, Binding::LocalItem(_)) {
3425                        return Err(ctx
3426                            .diagnostics
3427                            .report(v.stable_ptr, MultipleDefinitionforBinding(v.name.clone())));
3428                    }
3429                    ctx.add_unused_binding_warning(&v.name, &old_var);
3430                }
3431                ctx.semantic_defs.insert(var_def.id(), var_def);
3432            }
3433            semantic::Statement::Let(semantic::StatementLet {
3434                pattern: pattern.id,
3435                expr: rhs_expr_id,
3436                stable_ptr: syntax.stable_ptr(),
3437            })
3438        }
3439        ast::Statement::Expr(stmt_expr_syntax) => {
3440            let expr_syntax = stmt_expr_syntax.expr(syntax_db);
3441            let expr = compute_expr_semantic(ctx, &expr_syntax);
3442            if matches!(
3443                stmt_expr_syntax.semicolon(syntax_db),
3444                ast::OptionTerminalSemicolon::Empty(_)
3445            ) && !matches!(
3446                expr_syntax,
3447                ast::Expr::Block(_) | ast::Expr::If(_) | ast::Expr::Match(_)
3448            ) {
3449                // Point to after the expression, where the semicolon is missing.
3450                ctx.diagnostics.report_after(&expr_syntax, MissingSemicolon);
3451            }
3452            let ty: TypeId = expr.ty();
3453            if let TypeLongId::Concrete(concrete) = ty.lookup_intern(db) {
3454                if concrete.is_must_use(db)? {
3455                    ctx.diagnostics.report(&expr_syntax, UnhandledMustUseType(ty));
3456                }
3457            }
3458            if let Expr::FunctionCall(expr_function_call) = &expr.expr {
3459                let generic_function_id =
3460                    expr_function_call.function.lookup_intern(db).function.generic_function;
3461                if generic_function_id.is_must_use(db)? {
3462                    ctx.diagnostics.report(&expr_syntax, UnhandledMustUseFunction);
3463                }
3464            }
3465            semantic::Statement::Expr(semantic::StatementExpr {
3466                expr: expr.id,
3467                stable_ptr: syntax.stable_ptr(),
3468            })
3469        }
3470        ast::Statement::Continue(continue_syntax) => {
3471            if !ctx.is_inside_loop() {
3472                return Err(ctx
3473                    .diagnostics
3474                    .report(continue_syntax, ContinueOnlyAllowedInsideALoop));
3475            }
3476            semantic::Statement::Continue(semantic::StatementContinue {
3477                stable_ptr: syntax.stable_ptr(),
3478            })
3479        }
3480        ast::Statement::Return(return_syntax) => {
3481            if ctx.is_inside_loop() {
3482                return Err(ctx.diagnostics.report(return_syntax, ReturnNotAllowedInsideALoop));
3483            }
3484
3485            let (expr_option, expr_ty, stable_ptr) = match return_syntax.expr_clause(syntax_db) {
3486                ast::OptionExprClause::Empty(empty_clause) => {
3487                    (None, unit_ty(db), empty_clause.stable_ptr().untyped())
3488                }
3489                ast::OptionExprClause::ExprClause(expr_clause) => {
3490                    let expr_syntax = expr_clause.expr(syntax_db);
3491                    let expr = compute_expr_semantic(ctx, &expr_syntax);
3492                    (Some(expr.id), expr.ty(), expr_syntax.stable_ptr().untyped())
3493                }
3494            };
3495            let expected_ty = match ctx.inner_ctx {
3496                None => {
3497                    ctx.get_signature(
3498                        return_syntax.into(),
3499                        UnsupportedOutsideOfFunctionFeatureName::ReturnStatement,
3500                    )?
3501                    .return_type
3502                }
3503                Some(InnerContext::Closure { return_type }) => return_type,
3504                _ => unreachable!("Return statement inside a loop"),
3505            };
3506
3507            let expected_ty = ctx.reduce_ty(expected_ty);
3508            let expr_ty = ctx.reduce_ty(expr_ty);
3509            if !expected_ty.is_missing(db) && !expr_ty.is_missing(db) {
3510                let inference = &mut ctx.resolver.inference();
3511                if let Err((err_set, actual_ty, expected_ty)) =
3512                    inference.conform_ty_for_diag(expr_ty, expected_ty)
3513                {
3514                    let diag_added = ctx
3515                        .diagnostics
3516                        .report(stable_ptr, WrongReturnType { expected_ty, actual_ty });
3517                    inference.consume_reported_error(err_set, diag_added);
3518                }
3519            }
3520            semantic::Statement::Return(semantic::StatementReturn {
3521                expr_option,
3522                stable_ptr: syntax.stable_ptr(),
3523            })
3524        }
3525        ast::Statement::Break(break_syntax) => {
3526            let (expr_option, ty, stable_ptr) = match break_syntax.expr_clause(syntax_db) {
3527                ast::OptionExprClause::Empty(expr_empty) => {
3528                    (None, unit_ty(db), expr_empty.stable_ptr().untyped())
3529                }
3530                ast::OptionExprClause::ExprClause(expr_clause) => {
3531                    let expr_syntax = expr_clause.expr(syntax_db);
3532                    let expr = compute_expr_semantic(ctx, &expr_syntax);
3533
3534                    (Some(expr.id), expr.ty(), expr.stable_ptr().untyped())
3535                }
3536            };
3537            let ty = ctx.reduce_ty(ty);
3538            match &mut ctx.inner_ctx {
3539                None | Some(InnerContext::Closure { .. }) => {
3540                    return Err(ctx.diagnostics.report(break_syntax, BreakOnlyAllowedInsideALoop));
3541                }
3542                Some(InnerContext::Loop { type_merger, .. }) => {
3543                    type_merger.try_merge_types(
3544                        ctx.db,
3545                        ctx.diagnostics,
3546                        &mut ctx.resolver.inference(),
3547                        ty,
3548                        stable_ptr,
3549                    );
3550                }
3551                Some(InnerContext::While | InnerContext::For) => {
3552                    if expr_option.is_some() {
3553                        ctx.diagnostics.report(break_syntax, BreakWithValueOnlyAllowedInsideALoop);
3554                    };
3555                }
3556            };
3557            semantic::Statement::Break(semantic::StatementBreak {
3558                expr_option,
3559                stable_ptr: syntax.stable_ptr(),
3560            })
3561        }
3562        ast::Statement::Item(stmt_item_syntax) => {
3563            let item_syntax = &stmt_item_syntax.item(syntax_db);
3564            match item_syntax {
3565                ast::ModuleItem::Constant(const_syntax) => {
3566                    let lhs = const_syntax.type_clause(db.upcast()).ty(db.upcast());
3567                    let rhs = const_syntax.value(db.upcast());
3568                    let rhs_expr = compute_expr_semantic(ctx, &rhs);
3569                    let explicit_type = resolve_type_with_environment(
3570                        db,
3571                        ctx.diagnostics,
3572                        &mut ctx.resolver,
3573                        &lhs,
3574                        Some(&mut ctx.environment),
3575                    );
3576                    let rhs_resolved_expr = resolve_const_expr_and_evaluate(
3577                        db,
3578                        ctx,
3579                        &rhs_expr,
3580                        stmt_item_syntax.stable_ptr().untyped(),
3581                        explicit_type,
3582                    );
3583                    let name_syntax = const_syntax.name(syntax_db);
3584                    let name = name_syntax.text(db.upcast());
3585                    let rhs_id = StatementConstLongId(
3586                        ctx.resolver.module_file_id,
3587                        const_syntax.stable_ptr(),
3588                    );
3589                    let var_def = Binding::LocalItem(LocalItem {
3590                        id: StatementItemId::Constant(rhs_id.intern(db)),
3591                        kind: StatementItemKind::Constant(
3592                            db.intern_const_value(rhs_resolved_expr.clone()),
3593                            rhs_resolved_expr.ty(db.upcast())?,
3594                        ),
3595                    });
3596                    add_item_to_statement_environment(ctx, name, var_def, &name_syntax);
3597                }
3598                ast::ModuleItem::Use(use_syntax) => {
3599                    for leaf in get_all_path_leaves(syntax_db, use_syntax) {
3600                        let stable_ptr = leaf.stable_ptr();
3601                        let segments = get_use_path_segments(syntax_db, ast::UsePath::Leaf(leaf))?;
3602                        let resolved_item = ctx.resolver.resolve_generic_path(
3603                            ctx.diagnostics,
3604                            segments,
3605                            NotFoundItemType::Identifier,
3606                            Some(&mut ctx.environment),
3607                        )?;
3608                        let var_def_id = StatementItemId::Use(
3609                            StatementUseLongId(ctx.resolver.module_file_id, stable_ptr).intern(db),
3610                        );
3611                        let name = var_def_id.name(db.upcast());
3612                        match resolved_item {
3613                            ResolvedGenericItem::GenericConstant(const_id) => {
3614                                let var_def = Binding::LocalItem(LocalItem {
3615                                    id: var_def_id,
3616                                    kind: StatementItemKind::Constant(
3617                                        db.constant_const_value(const_id)?,
3618                                        db.constant_const_type(const_id)?,
3619                                    ),
3620                                });
3621                                add_item_to_statement_environment(ctx, name, var_def, stable_ptr);
3622                            }
3623                            ResolvedGenericItem::GenericType(generic_type_id) => {
3624                                add_type_to_statement_environment(
3625                                    ctx,
3626                                    name,
3627                                    ResolvedGenericItem::GenericType(generic_type_id),
3628                                    stable_ptr,
3629                                );
3630                            }
3631                            ResolvedGenericItem::Module(_)
3632                            | ResolvedGenericItem::GenericFunction(_)
3633                            | ResolvedGenericItem::TraitFunction(_)
3634                            | ResolvedGenericItem::GenericTypeAlias(_)
3635                            | ResolvedGenericItem::GenericImplAlias(_)
3636                            | ResolvedGenericItem::Variant(_)
3637                            | ResolvedGenericItem::Trait(_)
3638                            | ResolvedGenericItem::Impl(_)
3639                            | ResolvedGenericItem::Variable(_) => {
3640                                return Err(ctx
3641                                    .diagnostics
3642                                    .report(stable_ptr, UnsupportedUseItemInStatement));
3643                            }
3644                        }
3645                    }
3646                }
3647                ast::ModuleItem::Module(_) => {
3648                    unreachable!("Modules are not supported inside a function.")
3649                }
3650                ast::ModuleItem::FreeFunction(_) => {
3651                    unreachable!("FreeFunction type not supported.")
3652                }
3653                ast::ModuleItem::ExternFunction(_) => {
3654                    unreachable!("ExternFunction type not supported.")
3655                }
3656                ast::ModuleItem::ExternType(_) => unreachable!("ExternType type not supported."),
3657                ast::ModuleItem::Trait(_) => unreachable!("Trait type not supported."),
3658                ast::ModuleItem::Impl(_) => unreachable!("Impl type not supported."),
3659                ast::ModuleItem::ImplAlias(_) => unreachable!("ImplAlias type not supported."),
3660                ast::ModuleItem::Struct(_) => unreachable!("Struct type not supported."),
3661                ast::ModuleItem::Enum(_) => unreachable!("Enum type not supported."),
3662                ast::ModuleItem::TypeAlias(_) => unreachable!("TypeAlias type not supported."),
3663                ast::ModuleItem::InlineMacro(_) => unreachable!("InlineMacro type not supported."),
3664                ast::ModuleItem::HeaderDoc(_) => unreachable!("HeaderDoc type not supported."),
3665                ast::ModuleItem::Missing(_) => unreachable!("Missing type not supported."),
3666            }
3667            semantic::Statement::Item(semantic::StatementItem { stable_ptr: syntax.stable_ptr() })
3668        }
3669        ast::Statement::Missing(_) => todo!(),
3670    };
3671    ctx.resolver.data.feature_config.restore(feature_restore);
3672    Ok(ctx.arenas.statements.alloc(statement))
3673}
3674
3675/// Adds an item to the statement environment and reports a diagnostic if the item is already
3676/// defined.
3677fn add_item_to_statement_environment(
3678    ctx: &mut ComputationContext<'_>,
3679    name: SmolStr,
3680    var_def: Binding,
3681    stable_ptr: impl Into<SyntaxStablePtrId>,
3682) {
3683    if let Some(old_var) = ctx.environment.variables.insert(name.clone(), var_def.clone()) {
3684        ctx.diagnostics.report(stable_ptr, match old_var {
3685            Binding::LocalItem(_) => MultipleConstantDefinition(name),
3686            Binding::LocalVar(_) | Binding::Param(_) => MultipleDefinitionforBinding(name),
3687        });
3688    }
3689    ctx.semantic_defs.insert(var_def.id(), var_def);
3690}
3691
3692/// Adds a type to the statement environment and reports a diagnostic if the type is already
3693/// defined.
3694fn add_type_to_statement_environment(
3695    ctx: &mut ComputationContext<'_>,
3696    name: SmolStr,
3697    resolved_generic_item: ResolvedGenericItem,
3698    stable_ptr: impl Into<SyntaxStablePtrId> + std::marker::Copy,
3699) {
3700    if ctx
3701        .environment
3702        .use_items
3703        .insert(name.clone(), StatementGenericItemData {
3704            resolved_generic_item,
3705            stable_ptr: stable_ptr.into(),
3706        })
3707        .is_some()
3708    {
3709        ctx.diagnostics.report(stable_ptr, MultipleGenericItemDefinition(name));
3710    }
3711}
3712
3713/// Computes the semantic model of an expression and reports diagnostics if the expression does not
3714/// evaluate to a boolean value.
3715fn compute_bool_condition_semantic(
3716    ctx: &mut ComputationContext<'_>,
3717    condition_syntax: &ast::Expr,
3718) -> ExprAndId {
3719    let condition = compute_expr_semantic(ctx, condition_syntax);
3720    let inference = &mut ctx.resolver.inference();
3721    if let Err((err_set, condition_ty, _)) =
3722        inference.conform_ty_for_diag(condition.ty(), core_bool_ty(ctx.db))
3723    {
3724        let diag_added = ctx.diagnostics.report(condition.deref(), ConditionNotBool(condition_ty));
3725        inference.consume_reported_error(err_set, diag_added);
3726    }
3727    condition
3728}
3729
3730/// Validates a struct member is visible and otherwise adds a diagnostic.
3731fn check_struct_member_is_visible(
3732    ctx: &mut ComputationContext<'_>,
3733    member: &Member,
3734    stable_ptr: SyntaxStablePtrId,
3735    member_name: &SmolStr,
3736) {
3737    let db = ctx.db.upcast();
3738    let containing_module_id = member.id.parent_module(db);
3739    if ctx.resolver.ignore_visibility_checks(containing_module_id) {
3740        return;
3741    }
3742    let user_module_id = ctx.resolver.module_file_id.0;
3743    if !visibility::peek_visible_in(db, member.visibility, containing_module_id, user_module_id) {
3744        ctx.diagnostics.report(stable_ptr, MemberNotVisible(member_name.clone()));
3745    }
3746}
3747
3748/// Verifies that the statement attributes are valid statements attributes, if not a diagnostic is
3749/// reported.
3750fn validate_statement_attributes(ctx: &mut ComputationContext<'_>, syntax: &ast::Statement) {
3751    let allowed_attributes = ctx.db.allowed_statement_attributes();
3752    let mut diagnostics = vec![];
3753    validate_attributes_flat(ctx.db.upcast(), &allowed_attributes, syntax, &mut diagnostics);
3754    // Translate the plugin diagnostics to semantic diagnostics.
3755    for diagnostic in diagnostics {
3756        ctx.diagnostics
3757            .report(diagnostic.stable_ptr, SemanticDiagnosticKind::UnknownStatementAttribute);
3758    }
3759}
3760
3761/// Gets an iterator with the types of the parameters of the given function.
3762fn function_parameter_types(
3763    ctx: &mut ComputationContext<'_>,
3764    function: FunctionId,
3765) -> Maybe<impl Iterator<Item = TypeId>> {
3766    let signature = ctx.db.concrete_function_signature(function)?;
3767    let param_types = signature.params.into_iter().map(|param| param.ty);
3768    Ok(param_types)
3769}