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::{ImplLongId, 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, ImplTypeId, 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, ConcreteTraitLongId, GenericArgumentId, GenericParam, LocalItem, Member,
87    Mutability, Parameter, 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        for (concrete_trait_type_id, ty1) in imp.type_constraints {
1055            let trait_ty = concrete_trait_type_id.trait_type(ctx.db);
1056            let impl_type = ImplTypeId::new(
1057                ImplLongId::GenericParameter(*param).intern(ctx.db),
1058                trait_ty,
1059                ctx.db,
1060            );
1061            let ty0 = inference.impl_type_assignment(impl_type);
1062            inference.conform_ty(ty0, ty1).ok();
1063        }
1064        let ConcreteTraitLongId { trait_id, generic_args } =
1065            concrete_trait_id.lookup_intern(ctx.db);
1066        if crate::corelib::fn_traits(ctx.db).contains(&trait_id) {
1067            ctx.are_closures_in_context = true;
1068        }
1069        if trait_id != get_core_trait(ctx.db, CoreTraitContext::MetaProgramming, "TypeEqual".into())
1070        {
1071            continue;
1072        }
1073        let [GenericArgumentId::Type(ty0), GenericArgumentId::Type(ty1)] = generic_args.as_slice()
1074        else {
1075            unreachable!("TypeEqual should have 2 arguments");
1076        };
1077        let ty0 = if let TypeLongId::ImplType(impl_type) = ty0.lookup_intern(ctx.db) {
1078            inference.impl_type_assignment(impl_type)
1079        } else {
1080            *ty0
1081        };
1082        let ty1 = if let TypeLongId::ImplType(impl_type) = ty1.lookup_intern(ctx.db) {
1083            inference.impl_type_assignment(impl_type)
1084        } else {
1085            *ty1
1086        };
1087        inference.conform_ty(ty0, ty1).ok();
1088    }
1089    inference.finalize_impl_type_bounds();
1090
1091    let return_type = ctx.reduce_ty(return_type);
1092    let res = compute_expr_block_semantic(ctx, syntax)?;
1093    let res_ty = ctx.reduce_ty(res.ty());
1094    let res = ctx.arenas.exprs.alloc(res);
1095    let inference = &mut ctx.resolver.inference();
1096    if let Err((err_set, actual_ty, expected_ty)) =
1097        inference.conform_ty_for_diag(res_ty, return_type)
1098    {
1099        let diag_added = ctx.diagnostics.report(
1100            ctx.signature
1101                .map(|s| match s.stable_ptr.lookup(ctx.db.upcast()).ret_ty(ctx.db.upcast()) {
1102                    OptionReturnTypeClause::Empty(_) => syntax.stable_ptr().untyped(),
1103                    OptionReturnTypeClause::ReturnTypeClause(return_type_clause) => {
1104                        return_type_clause.ty(ctx.db.upcast()).stable_ptr().untyped()
1105                    }
1106                })
1107                .unwrap_or_else(|| syntax.stable_ptr().untyped()),
1108            WrongReturnType { expected_ty, actual_ty },
1109        );
1110        inference.consume_reported_error(err_set, diag_added);
1111    }
1112
1113    // Check fully resolved.
1114    inference.finalize(ctx.diagnostics, syntax.into());
1115
1116    ctx.apply_inference_rewriter();
1117
1118    Ok(res)
1119}
1120
1121/// Computes the semantic model of an expression of type [ast::ExprBlock].
1122pub fn compute_expr_block_semantic(
1123    ctx: &mut ComputationContext<'_>,
1124    syntax: &ast::ExprBlock,
1125) -> Maybe<Expr> {
1126    let db = ctx.db;
1127    let syntax_db = db.upcast();
1128
1129    ctx.run_in_subscope(|new_ctx| {
1130        let mut statements = syntax.statements(syntax_db).elements(syntax_db);
1131        // Remove the tail expression, if exists.
1132        // TODO(spapini): Consider splitting tail expression in the parser.
1133        let tail = get_tail_expression(syntax_db, statements.as_slice());
1134        if tail.is_some() {
1135            statements.pop();
1136        }
1137
1138        // Convert statements to semantic model.
1139        let statements_semantic: Vec<_> = statements
1140            .into_iter()
1141            .filter_map(|statement_syntax| {
1142                compute_statement_semantic(new_ctx, statement_syntax).to_option()
1143            })
1144            .collect();
1145
1146        // Convert tail expression (if exists) to semantic model.
1147        let tail_semantic_expr = tail.map(|tail_expr| compute_expr_semantic(new_ctx, &tail_expr));
1148        let ty = if let Some(t) = &tail_semantic_expr {
1149            t.ty()
1150        } else if let Some(statement) = statements_semantic.last() {
1151            if let Statement::Return(_) | Statement::Break(_) =
1152                &new_ctx.arenas.statements[*statement]
1153            {
1154                never_ty(new_ctx.db)
1155            } else {
1156                unit_ty(db)
1157            }
1158        } else {
1159            unit_ty(db)
1160        };
1161        Ok(Expr::Block(ExprBlock {
1162            statements: statements_semantic,
1163            tail: tail_semantic_expr.map(|expr| expr.id),
1164            ty,
1165            stable_ptr: syntax.stable_ptr().into(),
1166        }))
1167    })
1168}
1169
1170/// Helper for merging the return types of branch blocks (match or if else).
1171#[derive(Debug, Clone)]
1172struct FlowMergeTypeHelper {
1173    multi_arm_expr_kind: MultiArmExprKind,
1174    never_type: TypeId,
1175    final_type: Option<TypeId>,
1176    /// Whether or not the Helper had a previous type merge error.
1177    had_merge_error: bool,
1178}
1179impl FlowMergeTypeHelper {
1180    fn new(db: &dyn SemanticGroup, multi_arm_expr_kind: MultiArmExprKind) -> Self {
1181        Self {
1182            multi_arm_expr_kind,
1183            never_type: never_ty(db),
1184            final_type: None,
1185            had_merge_error: false,
1186        }
1187    }
1188
1189    /// Merge a type into the helper. Returns false on error or if had a previous error.
1190    /// May conform the type to the self.expected_type, if set. Mostly, it is called after
1191    /// the types have already been conformed earlier, in which case it has no external effect.
1192    fn try_merge_types(
1193        &mut self,
1194        db: &dyn SemanticGroup,
1195        diagnostics: &mut SemanticDiagnostics,
1196        inference: &mut Inference<'_>,
1197        ty: TypeId,
1198        stable_ptr: SyntaxStablePtrId,
1199    ) -> bool {
1200        if self.had_merge_error {
1201            return false;
1202        }
1203
1204        if ty != self.never_type && !ty.is_missing(db) {
1205            if let Some(pending) = &self.final_type {
1206                if let Err(err_set) = inference.conform_ty(ty, *pending) {
1207                    let diag_added = diagnostics.report(stable_ptr, IncompatibleArms {
1208                        multi_arm_expr_kind: self.multi_arm_expr_kind,
1209                        pending_ty: *pending,
1210                        different_ty: ty,
1211                    });
1212                    inference.consume_reported_error(err_set, diag_added);
1213                    self.had_merge_error = true;
1214                    return false;
1215                }
1216            } else {
1217                self.final_type = Some(ty);
1218            }
1219        }
1220        true
1221    }
1222
1223    /// Returns the merged type.
1224    fn get_final_type(self) -> TypeId {
1225        self.final_type.unwrap_or(self.never_type)
1226    }
1227}
1228
1229/// computes the semantic of a match arm pattern and the block expression.
1230fn compute_arm_semantic(
1231    ctx: &mut ComputationContext<'_>,
1232    expr: &Expr,
1233    arm_expr_syntax: ast::Expr,
1234    patterns_syntax: &PatternListOr,
1235    // Whether the arm is a while let arm. This case is handled a little differently.
1236    is_while_let_arm: bool,
1237) -> (Vec<PatternAndId>, ExprAndId) {
1238    let db = ctx.db;
1239    let syntax_db = db.upcast();
1240    ctx.run_in_subscope(|new_ctx| {
1241        // Typecheck the arms's patterns, and introduce the new variables to the subscope.
1242        // Note that if the arm expr is a block, there will be *another* subscope
1243        // for it.
1244        let mut arm_patterns_variables: UnorderedHashMap<SmolStr, LocalVariable> =
1245            UnorderedHashMap::default();
1246        let patterns: Vec<_> = patterns_syntax
1247            .elements(syntax_db)
1248            .iter()
1249            .map(|pattern_syntax| {
1250                let pattern: PatternAndId = compute_pattern_semantic(
1251                    new_ctx,
1252                    pattern_syntax,
1253                    expr.ty(),
1254                    &mut arm_patterns_variables,
1255                );
1256                let variables = pattern.variables(&new_ctx.arenas.patterns);
1257                for variable in variables {
1258                    match arm_patterns_variables.entry(variable.name.clone()) {
1259                        std::collections::hash_map::Entry::Occupied(entry) => {
1260                            let get_location = || variable.stable_ptr.lookup(db.upcast());
1261                            let var = entry.get();
1262
1263                            let expected_ty = new_ctx.reduce_ty(var.ty);
1264                            let actual_ty = new_ctx.reduce_ty(variable.var.ty);
1265
1266                            let mut has_inference_error = false;
1267                            if !variable.var.ty.is_missing(new_ctx.db) {
1268                                let inference = &mut new_ctx.resolver.inference();
1269                                if let Err((err_set, actual_ty, expected_ty)) =
1270                                    inference.conform_ty_for_diag(actual_ty, expected_ty)
1271                                {
1272                                    let diag_added =
1273                                        new_ctx.diagnostics.report(&get_location(), WrongType {
1274                                            expected_ty,
1275                                            actual_ty,
1276                                        });
1277                                    inference.consume_reported_error(err_set, diag_added);
1278                                    has_inference_error = true;
1279                                }
1280                            };
1281                            if !has_inference_error && var.is_mut != variable.var.is_mut {
1282                                new_ctx.diagnostics.report(&get_location(), InconsistentBinding);
1283                            }
1284                        }
1285                        std::collections::hash_map::Entry::Vacant(entry) => {
1286                            entry.insert(variable.var.clone());
1287                        }
1288                    }
1289                }
1290                pattern
1291            })
1292            .collect();
1293
1294        for (pattern_syntax, pattern) in
1295            patterns_syntax.elements(syntax_db).iter().zip(patterns.iter())
1296        {
1297            let variables = pattern.variables(&new_ctx.arenas.patterns);
1298
1299            if variables.len() != arm_patterns_variables.len() {
1300                new_ctx.diagnostics.report(pattern_syntax, MissingVariableInPattern);
1301            }
1302
1303            for v in variables {
1304                let var_def = Binding::LocalVar(v.var.clone());
1305                // TODO(spapini): Wrap this in a function to couple with semantic_defs
1306                // insertion.
1307                new_ctx.environment.variables.insert(v.name.clone(), var_def.clone());
1308                new_ctx.semantic_defs.insert(var_def.id(), var_def);
1309            }
1310        }
1311        let arm_expr = if is_while_let_arm {
1312            let ast::Expr::Block(arm_expr_syntax) = arm_expr_syntax else {
1313                unreachable!("Expected a block expression for a loop arm.");
1314            };
1315            let (id, _) = compute_loop_body_semantic(new_ctx, arm_expr_syntax, InnerContext::While);
1316            let expr = new_ctx.arenas.exprs[id].clone();
1317            ExprAndId { expr, id }
1318        } else {
1319            compute_expr_semantic(new_ctx, &arm_expr_syntax)
1320        };
1321        (patterns, arm_expr)
1322    })
1323}
1324
1325/// Computes the semantic model of an expression of type [ast::ExprMatch].
1326fn compute_expr_match_semantic(
1327    ctx: &mut ComputationContext<'_>,
1328    syntax: &ast::ExprMatch,
1329) -> Maybe<Expr> {
1330    // TODO(yuval): verify exhaustiveness.
1331    let db = ctx.db;
1332    let syntax_db = db.upcast();
1333
1334    let syntax_arms = syntax.arms(syntax_db).elements(syntax_db);
1335    let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
1336    // Run compute_pattern_semantic on every arm, even if other arms failed, to get as many
1337    // diagnostics as possible.
1338    let patterns_and_exprs: Vec<_> = syntax_arms
1339        .iter()
1340        .map(|syntax_arm| {
1341            compute_arm_semantic(
1342                ctx,
1343                &expr,
1344                syntax_arm.expression(syntax_db),
1345                &syntax_arm.patterns(syntax_db),
1346                false,
1347            )
1348        })
1349        .collect();
1350    // Unify arm types.
1351    let mut helper = FlowMergeTypeHelper::new(ctx.db, MultiArmExprKind::Match);
1352    for (_, expr) in patterns_and_exprs.iter() {
1353        let expr_ty = ctx.reduce_ty(expr.ty());
1354        if !helper.try_merge_types(
1355            ctx.db,
1356            ctx.diagnostics,
1357            &mut ctx.resolver.inference(),
1358            expr_ty,
1359            expr.stable_ptr().untyped(),
1360        ) {
1361            break;
1362        };
1363    }
1364    // Compute semantic representation of the match arms.
1365    let semantic_arms = patterns_and_exprs
1366        .into_iter()
1367        .map(|(patterns, arm_expr)| MatchArm {
1368            patterns: patterns.iter().map(|pattern| pattern.id).collect(),
1369            expression: arm_expr.id,
1370        })
1371        .collect();
1372    Ok(Expr::Match(ExprMatch {
1373        matched_expr: expr.id,
1374        arms: semantic_arms,
1375        ty: helper.get_final_type(),
1376        stable_ptr: syntax.stable_ptr().into(),
1377    }))
1378}
1379
1380/// Computes the semantic model of an expression of type [ast::ExprIf].
1381fn compute_expr_if_semantic(ctx: &mut ComputationContext<'_>, syntax: &ast::ExprIf) -> Maybe<Expr> {
1382    let syntax_db = ctx.db.upcast();
1383    let (condition, if_block) = match &syntax.condition(syntax_db) {
1384        ast::Condition::Let(condition) => {
1385            let expr = compute_expr_semantic(ctx, &condition.expr(syntax_db));
1386            if let Expr::LogicalOperator(_) = expr.expr {
1387                ctx.diagnostics
1388                    .report(&condition.expr(syntax_db), LogicalOperatorNotAllowedInIfLet);
1389            }
1390
1391            let (patterns, if_block) = compute_arm_semantic(
1392                ctx,
1393                &expr,
1394                ast::Expr::Block(syntax.if_block(syntax_db)),
1395                &condition.patterns(syntax_db),
1396                false,
1397            );
1398            (Condition::Let(expr.id, patterns.iter().map(|pattern| pattern.id).collect()), if_block)
1399        }
1400        ast::Condition::Expr(expr) => {
1401            let if_block = compute_expr_block_semantic(ctx, &syntax.if_block(syntax_db))?;
1402            (
1403                Condition::BoolExpr(compute_bool_condition_semantic(ctx, &expr.expr(syntax_db)).id),
1404                ExprAndId { expr: if_block.clone(), id: ctx.arenas.exprs.alloc(if_block) },
1405            )
1406        }
1407    };
1408
1409    let (else_block_opt, else_block_ty) = match syntax.else_clause(syntax_db) {
1410        ast::OptionElseClause::Empty(_) => (None, unit_ty(ctx.db)),
1411        ast::OptionElseClause::ElseClause(else_clause) => {
1412            match else_clause.else_block_or_if(syntax_db) {
1413                BlockOrIf::Block(block) => {
1414                    let else_block = compute_expr_block_semantic(ctx, &block)?;
1415                    (Some(else_block.clone()), else_block.ty())
1416                }
1417                BlockOrIf::If(expr_if) => {
1418                    let else_if = compute_expr_if_semantic(ctx, &expr_if)?;
1419                    (Some(else_if.clone()), else_if.ty())
1420                }
1421            }
1422        }
1423    };
1424
1425    let mut helper = FlowMergeTypeHelper::new(ctx.db, MultiArmExprKind::If);
1426    let if_block_ty = ctx.reduce_ty(if_block.ty());
1427    let else_block_ty = ctx.reduce_ty(else_block_ty);
1428    let inference = &mut ctx.resolver.inference();
1429    let _ = helper.try_merge_types(ctx.db, ctx.diagnostics, inference, if_block_ty, syntax.into())
1430        && helper.try_merge_types(ctx.db, ctx.diagnostics, inference, else_block_ty, syntax.into());
1431    Ok(Expr::If(ExprIf {
1432        condition,
1433        if_block: if_block.id,
1434        else_block: else_block_opt.map(|else_block| ctx.arenas.exprs.alloc(else_block)),
1435        ty: helper.get_final_type(),
1436        stable_ptr: syntax.stable_ptr().into(),
1437    }))
1438}
1439
1440/// Computes the semantic model of an expression of type [ast::ExprLoop].
1441fn compute_expr_loop_semantic(
1442    ctx: &mut ComputationContext<'_>,
1443    syntax: &ast::ExprLoop,
1444) -> Maybe<Expr> {
1445    let db = ctx.db;
1446    let syntax_db = db.upcast();
1447
1448    let (body, inner_ctx) =
1449        compute_loop_body_semantic(ctx, syntax.body(syntax_db), InnerContext::Loop {
1450            type_merger: FlowMergeTypeHelper::new(db, MultiArmExprKind::Loop),
1451        });
1452    Ok(Expr::Loop(ExprLoop {
1453        body,
1454        ty: match inner_ctx {
1455            InnerContext::Loop { type_merger, .. } => type_merger.get_final_type(),
1456            _ => unreachable!("Expected loop context"),
1457        },
1458        stable_ptr: syntax.stable_ptr().into(),
1459    }))
1460}
1461
1462/// Computes the semantic model of an expression of type [ast::ExprWhile].
1463fn compute_expr_while_semantic(
1464    ctx: &mut ComputationContext<'_>,
1465    syntax: &ast::ExprWhile,
1466) -> Maybe<Expr> {
1467    let db = ctx.db;
1468    let syntax_db = db.upcast();
1469
1470    let (condition, body) = match &syntax.condition(syntax_db) {
1471        ast::Condition::Let(condition) => {
1472            let expr = compute_expr_semantic(ctx, &condition.expr(syntax_db));
1473            if let Expr::LogicalOperator(_) = expr.expr {
1474                ctx.diagnostics
1475                    .report(&condition.expr(syntax_db), LogicalOperatorNotAllowedInWhileLet);
1476            }
1477
1478            let (patterns, body) = compute_arm_semantic(
1479                ctx,
1480                &expr,
1481                ast::Expr::Block(syntax.body(syntax_db)),
1482                &condition.patterns(syntax_db),
1483                true,
1484            );
1485            (Condition::Let(expr.id, patterns.iter().map(|pattern| pattern.id).collect()), body.id)
1486        }
1487        ast::Condition::Expr(expr) => {
1488            let (body, _inner_ctx) =
1489                compute_loop_body_semantic(ctx, syntax.body(syntax_db), InnerContext::While);
1490            (
1491                Condition::BoolExpr(compute_bool_condition_semantic(ctx, &expr.expr(syntax_db)).id),
1492                body,
1493            )
1494        }
1495    };
1496
1497    Ok(Expr::While(ExprWhile {
1498        condition,
1499        body,
1500        ty: unit_ty(ctx.db),
1501        stable_ptr: syntax.stable_ptr().into(),
1502    }))
1503}
1504
1505/// Computes the semantic model of an expression of type [ast::ExprFor].
1506fn compute_expr_for_semantic(
1507    ctx: &mut ComputationContext<'_>,
1508    syntax: &ast::ExprFor,
1509) -> Maybe<Expr> {
1510    let db = ctx.db;
1511    let syntax_db = db.upcast();
1512    let expr_ptr = syntax.expr(syntax_db).stable_ptr();
1513
1514    let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
1515    let expr_id = expr.id;
1516
1517    let into_iterator_trait =
1518        get_core_trait(ctx.db, CoreTraitContext::Iterator, "IntoIterator".into());
1519
1520    let (into_iterator_function_id, _, fixed_into_iter_var, into_iter_mutability) =
1521        compute_method_function_call_data(
1522            ctx,
1523            &[into_iterator_trait],
1524            "into_iter".into(),
1525            expr,
1526            expr_ptr.into(),
1527            None,
1528            |ty, _, inference_errors| {
1529                Some(NoImplementationOfTrait {
1530                    ty,
1531                    inference_errors,
1532                    trait_name: "IntoIterator".into(),
1533                })
1534            },
1535            |_, _, _| {
1536                unreachable!(
1537                    "There is one explicit trait, IntoIterator trait. No implementations of the \
1538                     trait, caused by both lack of implementation or multiple implementations of \
1539                     the trait, are handled in NoImplementationOfTrait function."
1540                )
1541            },
1542        )?;
1543    let into_iter_call = expr_function_call(
1544        ctx,
1545        into_iterator_function_id,
1546        vec![NamedArg(fixed_into_iter_var, None, into_iter_mutability)],
1547        expr_ptr,
1548        expr_ptr,
1549    )?;
1550
1551    let into_iter_variable =
1552        LocalVarLongId(ctx.resolver.module_file_id, syntax.identifier(syntax_db).stable_ptr())
1553            .intern(ctx.db);
1554
1555    let into_iter_expr = Expr::Var(ExprVar {
1556        var: VarId::Local(into_iter_variable),
1557        ty: into_iter_call.ty(),
1558        stable_ptr: into_iter_call.stable_ptr(),
1559    });
1560    let into_iter_member_path = ExprVarMemberPath::Var(ExprVar {
1561        var: VarId::Local(into_iter_variable),
1562        ty: into_iter_call.ty(),
1563        stable_ptr: into_iter_call.stable_ptr(),
1564    });
1565    let into_iter_expr_id = ctx.arenas.exprs.alloc(into_iter_expr.clone());
1566
1567    let iterator_trait = get_core_trait(ctx.db, CoreTraitContext::Iterator, "Iterator".into());
1568
1569    let (next_function_id, _, _, _) = compute_method_function_call_data(
1570        ctx,
1571        &[iterator_trait],
1572        "next".into(),
1573        ExprAndId { expr: into_iter_expr, id: into_iter_expr_id },
1574        expr_ptr.into(),
1575        None,
1576        |ty, _, inference_errors| {
1577            Some(NoImplementationOfTrait { ty, inference_errors, trait_name: "Iterator".into() })
1578        },
1579        |_, _, _| {
1580            unreachable!(
1581                "There is one explicit trait, Iterator trait. No implementations of the trait, \
1582                 caused by both lack of implementation or multiple implementations of the trait, \
1583                 are handled in NoImplementationOfTrait function."
1584            )
1585        },
1586    )?;
1587
1588    let next_success_variant =
1589        match db.concrete_function_signature(next_function_id)?.return_type.lookup_intern(db) {
1590            TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(enm)) => {
1591                assert_eq!(enm.enum_id(db.upcast()).name(db.upcast()), "Option");
1592                db.concrete_enum_variants(enm).unwrap().into_iter().next().unwrap()
1593            }
1594            _ => unreachable!(),
1595        };
1596    let (body_id, pattern) = ctx.run_in_subscope(|new_ctx| {
1597        let inner_pattern = compute_pattern_semantic(
1598            new_ctx,
1599            &syntax.pattern(syntax_db),
1600            next_success_variant.ty,
1601            &mut UnorderedHashMap::default(),
1602        );
1603        let variables = inner_pattern.variables(&new_ctx.arenas.patterns);
1604        for v in variables {
1605            let var_def = Binding::LocalVar(v.var.clone());
1606            new_ctx.environment.variables.insert(v.name.clone(), var_def.clone());
1607            new_ctx.semantic_defs.insert(var_def.id(), var_def);
1608        }
1609        let (body, _inner_ctx) =
1610            compute_loop_body_semantic(new_ctx, syntax.body(syntax_db), InnerContext::For);
1611        (body, new_ctx.arenas.patterns.alloc(inner_pattern.pattern))
1612    });
1613    Ok(Expr::For(ExprFor {
1614        into_iter: into_iterator_function_id,
1615        into_iter_member_path,
1616        next_function_id,
1617        expr_id,
1618        pattern,
1619        body: body_id,
1620        ty: unit_ty(ctx.db),
1621        stable_ptr: syntax.stable_ptr().into(),
1622    }))
1623}
1624
1625/// Computes the semantic model for a body of a loop.
1626fn compute_loop_body_semantic(
1627    ctx: &mut ComputationContext<'_>,
1628    syntax: ast::ExprBlock,
1629    inner_ctx: InnerContext,
1630) -> (ExprId, InnerContext) {
1631    let db = ctx.db;
1632    let syntax_db = db.upcast();
1633
1634    ctx.run_in_subscope(|new_ctx| {
1635        let old_inner_ctx = std::mem::replace(&mut new_ctx.inner_ctx, Some(inner_ctx));
1636
1637        let mut statements = syntax.statements(syntax_db).elements(syntax_db);
1638        // Remove the typed tail expression, if exists.
1639        let tail = get_tail_expression(syntax_db, statements.as_slice());
1640        if tail.is_some() {
1641            statements.pop();
1642        }
1643
1644        // Convert statements to semantic model.
1645        let statements_semantic: Vec<_> = statements
1646            .into_iter()
1647            .filter_map(|statement_syntax| {
1648                compute_statement_semantic(new_ctx, statement_syntax).to_option()
1649            })
1650            .collect();
1651        let tail = tail.map(|tail| compute_expr_semantic(new_ctx, &tail));
1652        if let Some(tail) = &tail {
1653            if !tail.ty().is_missing(db) && !tail.ty().is_unit(db) && tail.ty() != never_ty(db) {
1654                new_ctx.diagnostics.report(tail.deref(), TailExpressionNotAllowedInLoop);
1655            }
1656        }
1657
1658        let inner_ctx = std::mem::replace(&mut new_ctx.inner_ctx, old_inner_ctx).unwrap();
1659        let body = new_ctx.arenas.exprs.alloc(Expr::Block(ExprBlock {
1660            statements: statements_semantic,
1661            tail: tail.map(|tail| tail.id),
1662            ty: unit_ty(db),
1663            stable_ptr: syntax.stable_ptr().into(),
1664        }));
1665
1666        (body, inner_ctx)
1667    })
1668}
1669
1670/// Computes the semantic model of an expression of type [ast::ExprClosure].
1671fn compute_expr_closure_semantic(
1672    ctx: &mut ComputationContext<'_>,
1673    syntax: &ast::ExprClosure,
1674) -> Maybe<Expr> {
1675    ctx.are_closures_in_context = true;
1676    let syntax_db = ctx.db.upcast();
1677    let (params, ret_ty, body) = ctx.run_in_subscope(|new_ctx| {
1678        let params = if let ClosureParamWrapper::NAry(params) = syntax.wrapper(syntax_db) {
1679            function_signature_params(
1680                new_ctx.diagnostics,
1681                new_ctx.db,
1682                &mut new_ctx.resolver,
1683                &params.params(syntax_db).elements(syntax_db),
1684                None,
1685                &mut new_ctx.environment,
1686            )
1687            .into_iter()
1688            .collect()
1689        } else {
1690            vec![]
1691        };
1692        let return_type = match syntax.ret_ty(syntax_db) {
1693            OptionReturnTypeClause::ReturnTypeClause(ty_syntax) => resolve_type_with_environment(
1694                new_ctx.db,
1695                new_ctx.diagnostics,
1696                &mut new_ctx.resolver,
1697                &ty_syntax.ty(syntax_db),
1698                Some(&mut new_ctx.environment),
1699            ),
1700            OptionReturnTypeClause::Empty(missing) => {
1701                new_ctx.resolver.inference().new_type_var(Some(missing.stable_ptr().untyped()))
1702            }
1703        };
1704        let old_inner_ctx =
1705            std::mem::replace(&mut new_ctx.inner_ctx, Some(InnerContext::Closure { return_type }));
1706        let body = match syntax.expr(syntax_db) {
1707            ast::Expr::Block(syntax) => compute_closure_body_semantic(new_ctx, syntax),
1708            _ => compute_expr_semantic(new_ctx, &syntax.expr(syntax_db)).id,
1709        };
1710        std::mem::replace(&mut new_ctx.inner_ctx, old_inner_ctx).unwrap();
1711        let mut inference = new_ctx.resolver.inference();
1712        if let Err((err_set, actual_ty, expected_ty)) =
1713            inference.conform_ty_for_diag(new_ctx.arenas.exprs[body].ty(), return_type)
1714        {
1715            let diag_added = new_ctx.diagnostics.report(
1716                match syntax.ret_ty(ctx.db.upcast()).stable_ptr().lookup(ctx.db.upcast()) {
1717                    OptionReturnTypeClause::Empty(_) => syntax.expr(syntax_db).stable_ptr(),
1718                    OptionReturnTypeClause::ReturnTypeClause(return_type_clause) => {
1719                        return_type_clause.ty(ctx.db.upcast()).stable_ptr()
1720                    }
1721                },
1722                WrongReturnType { expected_ty, actual_ty },
1723            );
1724            inference.consume_reported_error(err_set, diag_added);
1725        }
1726        (params, return_type, body)
1727    });
1728    let parent_function = match ctx.function_id {
1729        ContextFunction::Global => Maybe::Err(ctx.diagnostics.report(syntax, ClosureInGlobalScope)),
1730        ContextFunction::Function(function_id) => function_id,
1731    };
1732    if matches!(ctx.function_id, ContextFunction::Global) {
1733        ctx.diagnostics.report(syntax, ClosureInGlobalScope);
1734    }
1735
1736    let mut usages = Usages { usages: Default::default() };
1737    let usage = usages.handle_closure(&ctx.arenas, &params, body);
1738    let mut reported = UnorderedHashSet::<_>::default();
1739    // TODO(TomerStarkware): Add support for capturing mutable variables when then we have borrow.
1740    for (captured_var, expr) in
1741        chain!(usage.usage.iter(), usage.snap_usage.iter(), usage.changes.iter())
1742    {
1743        let Some(var) = ctx.semantic_defs.get(&captured_var.base_var()) else {
1744            // if the variable is not found in the semantic defs, it is closure parameter.
1745            continue;
1746        };
1747
1748        if var.is_mut() && reported.insert(expr.stable_ptr()) {
1749            ctx.diagnostics.report(expr.stable_ptr(), MutableCapturedVariable);
1750        }
1751    }
1752
1753    let captured_types = chain!(
1754        usage.snap_usage.values().map(|item| wrap_in_snapshots(ctx.db, item.ty(), 1)),
1755        chain!(usage.usage.values(), usage.changes.values()).map(|item| item.ty())
1756    )
1757    .sorted_by_key(|ty| ty.as_intern_id())
1758    .dedup()
1759    .collect_vec();
1760
1761    let ty = TypeLongId::Closure(ClosureTypeLongId {
1762        param_tys: params.iter().map(|param| param.ty).collect(),
1763        ret_ty,
1764        captured_types,
1765        parent_function,
1766        wrapper_location: StableLocation::new(syntax.wrapper(syntax_db).stable_ptr().into()),
1767    })
1768    .intern(ctx.db);
1769
1770    Ok(Expr::ExprClosure(ExprClosure { body, params, stable_ptr: syntax.stable_ptr().into(), ty }))
1771}
1772
1773/// Computes the semantic model for a body of a closure.
1774fn compute_closure_body_semantic(
1775    ctx: &mut ComputationContext<'_>,
1776    syntax: ast::ExprBlock,
1777) -> ExprId {
1778    let syntax_db = ctx.db.upcast();
1779
1780    let mut statements = syntax.statements(syntax_db).elements(syntax_db);
1781    // Remove the typed tail expression, if exists.
1782    let tail = get_tail_expression(syntax_db, statements.as_slice());
1783    if tail.is_some() {
1784        statements.pop();
1785    }
1786
1787    // Convert statements to semantic model.
1788    let statements_semantic: Vec<_> = statements
1789        .into_iter()
1790        .filter_map(|statement_syntax| {
1791            compute_statement_semantic(ctx, statement_syntax).to_option()
1792        })
1793        .collect();
1794    // Convert tail expression (if exists) to semantic model.
1795    let tail_semantic_expr = tail.map(|tail_expr| compute_expr_semantic(ctx, &tail_expr));
1796    let ty = if let Some(t) = &tail_semantic_expr {
1797        t.ty()
1798    } else if let Some(statement) = statements_semantic.last() {
1799        if let Statement::Return(_) | Statement::Break(_) = &ctx.arenas.statements[*statement] {
1800            never_ty(ctx.db)
1801        } else {
1802            unit_ty(ctx.db)
1803        }
1804    } else {
1805        unit_ty(ctx.db)
1806    };
1807    ctx.arenas.exprs.alloc(Expr::Block(ExprBlock {
1808        statements: statements_semantic,
1809        tail: tail_semantic_expr.map(|expr| expr.id),
1810        ty,
1811        stable_ptr: syntax.stable_ptr().into(),
1812    }))
1813}
1814
1815/// Computes the semantic model of an expression of type [ast::ExprErrorPropagate].
1816fn compute_expr_error_propagate_semantic(
1817    ctx: &mut ComputationContext<'_>,
1818    syntax: &ast::ExprErrorPropagate,
1819) -> Maybe<Expr> {
1820    let syntax_db = ctx.db.upcast();
1821
1822    let return_type = match ctx.inner_ctx {
1823        Some(InnerContext::Closure { return_type }) => return_type,
1824        None | Some(InnerContext::Loop { .. } | InnerContext::While | InnerContext::For) => {
1825            ctx.get_signature(
1826                syntax.into(),
1827                UnsupportedOutsideOfFunctionFeatureName::ErrorPropagate,
1828            )?
1829            .return_type
1830        }
1831    };
1832
1833    let func_err_prop_ty = unwrap_error_propagation_type(ctx.db, return_type)
1834        .ok_or_else(|| ctx.diagnostics.report(syntax, ReturnTypeNotErrorPropagateType))?;
1835
1836    // `inner_expr` is the expr inside the `?`.
1837    let inner_expr = match &func_err_prop_ty {
1838        crate::corelib::ErrorPropagationType::Option { .. } => {
1839            compute_expr_semantic(ctx, &syntax.expr(syntax_db))
1840        }
1841        crate::corelib::ErrorPropagationType::Result { .. } => {
1842            compute_expr_semantic(ctx, &syntax.expr(syntax_db))
1843        }
1844    };
1845    let func_err_variant = func_err_prop_ty.err_variant();
1846
1847    let inner_expr_ty = ctx.reduce_ty(inner_expr.ty());
1848    inner_expr_ty.check_not_missing(ctx.db)?;
1849    let inner_expr_err_prop_ty =
1850        unwrap_error_propagation_type(ctx.db, inner_expr_ty).ok_or_else(|| {
1851            ctx.diagnostics.report(syntax, ErrorPropagateOnNonErrorType(inner_expr_ty))
1852        })?;
1853    let inner_expr_err_variant = inner_expr_err_prop_ty.err_variant();
1854
1855    // Disallow error propagation inside a loop.
1856    if ctx.is_inside_loop() {
1857        ctx.diagnostics.report(syntax, SemanticDiagnosticKind::ErrorPropagateNotAllowedInsideALoop);
1858    }
1859
1860    let conformed_err_variant_ty =
1861        ctx.resolver.inference().conform_ty(func_err_variant.ty, inner_expr_err_variant.ty);
1862    // If conforming the types failed, the next check will fail and a better diagnostic will be
1863    // added.
1864    let err_variant_ty = match conformed_err_variant_ty {
1865        Ok(ty) => ty,
1866        Err(err_set) => {
1867            ctx.resolver.inference().consume_error_without_reporting(err_set);
1868            inner_expr_err_variant.ty
1869        }
1870    };
1871    // TODO(orizi): When auto conversion of types is added, try to convert the error type.
1872    if func_err_variant.ty != err_variant_ty
1873        || func_err_variant.concrete_enum_id.enum_id(ctx.db)
1874            != inner_expr_err_variant.concrete_enum_id.enum_id(ctx.db)
1875    {
1876        ctx.diagnostics.report(syntax, IncompatibleErrorPropagateType {
1877            return_ty: return_type,
1878            err_ty: inner_expr_err_variant.ty,
1879        });
1880    }
1881    Ok(Expr::PropagateError(ExprPropagateError {
1882        inner: inner_expr.id,
1883        ok_variant: inner_expr_err_prop_ty.ok_variant().clone(),
1884        err_variant: inner_expr_err_variant.clone(),
1885        func_err_variant: func_err_variant.clone(),
1886        stable_ptr: syntax.stable_ptr().into(),
1887    }))
1888}
1889
1890/// Computes the semantic model of an expression of type [ast::ExprIndexed].
1891fn compute_expr_indexed_semantic(
1892    ctx: &mut ComputationContext<'_>,
1893    syntax: &ast::ExprIndexed,
1894) -> Maybe<Expr> {
1895    let syntax_db = ctx.db.upcast();
1896    let expr = compute_expr_semantic(ctx, &syntax.expr(syntax_db));
1897    let index_expr_syntax = &syntax.index_expr(syntax_db);
1898    let index_expr = compute_expr_semantic(ctx, index_expr_syntax);
1899    // Make sure the maximal amount of types is known when trying to access. Ignoring the returned
1900    // value, as any errors will be reported later.
1901    ctx.resolver.inference().solve().ok();
1902    let candidate_traits: Vec<_> = ["Index", "IndexView"]
1903        .iter()
1904        .map(|trait_name| get_core_trait(ctx.db, CoreTraitContext::Ops, (*trait_name).into()))
1905        .collect();
1906    let (function_id, _, fixed_expr, mutability) = compute_method_function_call_data(
1907        ctx,
1908        &candidate_traits[..],
1909        "index".into(),
1910        expr,
1911        syntax.into(),
1912        None,
1913        |ty, _, inference_errors| Some(NoImplementationOfIndexOperator { ty, inference_errors }),
1914        |ty, _, _| Some(MultipleImplementationOfIndexOperator(ty)),
1915    )?;
1916
1917    expr_function_call(
1918        ctx,
1919        function_id,
1920        vec![
1921            NamedArg(fixed_expr, None, mutability),
1922            NamedArg(index_expr, None, Mutability::Immutable),
1923        ],
1924        syntax,
1925        index_expr_syntax.stable_ptr(),
1926    )
1927}
1928
1929/// Computes the data needed for a method function call, and similar exprs (index operator). Method
1930/// call and Index operator differs in the diagnostics they emit. The function returns the
1931/// function_id to call, the trait containing the function, the self argument, with snapshots added
1932/// if needed, and the mutability of the self argument.
1933#[expect(clippy::too_many_arguments)]
1934fn compute_method_function_call_data(
1935    ctx: &mut ComputationContext<'_>,
1936    candidate_traits: &[TraitId],
1937    func_name: SmolStr,
1938    self_expr: ExprAndId,
1939    method_syntax: SyntaxStablePtrId,
1940    generic_args_syntax: Option<Vec<ast::GenericArg>>,
1941    no_implementation_diagnostic: impl Fn(
1942        TypeId,
1943        SmolStr,
1944        TraitInferenceErrors,
1945    ) -> Option<SemanticDiagnosticKind>,
1946    multiple_trait_diagnostic: fn(
1947        TypeId,
1948        TraitFunctionId,
1949        TraitFunctionId,
1950    ) -> Option<SemanticDiagnosticKind>,
1951) -> Maybe<(FunctionId, TraitId, ExprAndId, Mutability)> {
1952    let self_ty = ctx.reduce_ty(self_expr.ty());
1953    // Inference errors found when looking for candidates. Only relevant in the case of 0 candidates
1954    // found. If >0 candidates are found these are ignored as they may describe, e.g., "errors"
1955    // indicating certain traits/impls/functions don't match, which is OK as we only look for one.
1956    let mut inference_errors = vec![];
1957    let candidates = filter_candidate_traits(
1958        ctx,
1959        &mut inference_errors,
1960        self_ty,
1961        candidate_traits,
1962        func_name.clone(),
1963        self_expr.stable_ptr().untyped(),
1964    );
1965    let trait_function_id = match candidates[..] {
1966        [] => {
1967            return Err(no_implementation_diagnostic(self_ty, func_name, TraitInferenceErrors {
1968                traits_and_errors: inference_errors,
1969            })
1970            .map(|diag| ctx.diagnostics.report(method_syntax, diag))
1971            .unwrap_or_else(skip_diagnostic));
1972        }
1973        [trait_function_id] => trait_function_id,
1974        [trait_function_id0, trait_function_id1, ..] => {
1975            return Err(multiple_trait_diagnostic(self_ty, trait_function_id0, trait_function_id1)
1976                .map(|diag| ctx.diagnostics.report(method_syntax, diag))
1977                .unwrap_or_else(skip_diagnostic));
1978        }
1979    };
1980    let (function_id, n_snapshots) =
1981        infer_impl_by_self(ctx, trait_function_id, self_ty, method_syntax, generic_args_syntax)?;
1982
1983    let signature = ctx.db.trait_function_signature(trait_function_id).unwrap();
1984    let first_param = signature.params.into_iter().next().unwrap();
1985    let mut fixed_expr = self_expr.clone();
1986    for _ in 0..n_snapshots {
1987        let ty = TypeLongId::Snapshot(fixed_expr.ty()).intern(ctx.db);
1988        let expr = Expr::Snapshot(ExprSnapshot {
1989            inner: fixed_expr.id,
1990            ty,
1991            stable_ptr: self_expr.stable_ptr(),
1992        });
1993        fixed_expr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
1994    }
1995
1996    Ok((
1997        function_id,
1998        trait_function_id.trait_id(ctx.db.upcast()),
1999        fixed_expr,
2000        first_param.mutability,
2001    ))
2002}
2003
2004/// Computes the semantic model of a pattern.
2005/// Note that this pattern will always be "registered" in the arena, so it can be looked up in the
2006/// language server.
2007pub fn compute_pattern_semantic(
2008    ctx: &mut ComputationContext<'_>,
2009    syntax: &ast::Pattern,
2010    ty: TypeId,
2011    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2012) -> PatternAndId {
2013    let pat = maybe_compute_pattern_semantic(ctx, syntax, ty, or_pattern_variables_map);
2014    let pat = pat.unwrap_or_else(|diag_added| {
2015        Pattern::Missing(PatternMissing {
2016            ty: TypeId::missing(ctx.db, diag_added),
2017            stable_ptr: syntax.stable_ptr(),
2018            diag_added,
2019        })
2020    });
2021    let id = ctx.arenas.patterns.alloc(pat.clone());
2022    PatternAndId { pattern: pat, id }
2023}
2024
2025/// Computes the semantic model of a pattern, or None if invalid.
2026fn maybe_compute_pattern_semantic(
2027    ctx: &mut ComputationContext<'_>,
2028    pattern_syntax: &ast::Pattern,
2029    ty: TypeId,
2030    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2031) -> Maybe<Pattern> {
2032    // TODO(spapini): Check for missing type, and don't reemit an error.
2033    let syntax_db = ctx.db.upcast();
2034    let ty = ctx.reduce_ty(ty);
2035    let stable_ptr = pattern_syntax.into();
2036    let pattern = match pattern_syntax {
2037        ast::Pattern::Underscore(otherwise_pattern) => {
2038            Pattern::Otherwise(PatternOtherwise { ty, stable_ptr: otherwise_pattern.stable_ptr() })
2039        }
2040        ast::Pattern::Literal(literal_pattern) => {
2041            let literal = literal_to_semantic(ctx, literal_pattern)?;
2042            Pattern::Literal(PatternLiteral {
2043                literal,
2044                stable_ptr: literal_pattern.stable_ptr().into(),
2045            })
2046        }
2047        ast::Pattern::ShortString(short_string_pattern) => {
2048            let literal = short_string_to_semantic(ctx, short_string_pattern)?;
2049            Pattern::Literal(PatternLiteral {
2050                literal,
2051                stable_ptr: short_string_pattern.stable_ptr().into(),
2052            })
2053        }
2054        ast::Pattern::String(string_pattern) => {
2055            let string_literal = string_literal_to_semantic(ctx, string_pattern)?;
2056            Pattern::StringLiteral(PatternStringLiteral {
2057                string_literal,
2058                stable_ptr: string_pattern.stable_ptr().into(),
2059            })
2060        }
2061        ast::Pattern::Enum(enum_pattern) => {
2062            let path = enum_pattern.path(syntax_db);
2063            let item = ctx.resolver.resolve_generic_path(
2064                ctx.diagnostics,
2065                &path,
2066                NotFoundItemType::Identifier,
2067                Some(&mut ctx.environment),
2068            )?;
2069            let generic_variant = try_extract_matches!(item, ResolvedGenericItem::Variant)
2070                .ok_or_else(|| ctx.diagnostics.report(&path, NotAVariant))?;
2071
2072            let (concrete_enum, n_snapshots) = extract_concrete_enum_from_pattern_and_validate(
2073                ctx,
2074                pattern_syntax,
2075                ty,
2076                generic_variant.enum_id,
2077            )?;
2078
2079            // TODO(lior): Should we report a diagnostic here?
2080            let concrete_variant = ctx
2081                .db
2082                .concrete_enum_variant(concrete_enum, &generic_variant)
2083                .map_err(|_| ctx.diagnostics.report(&path, UnknownEnum))?;
2084
2085            // Compute inner pattern.
2086            let inner_ty = wrap_in_snapshots(ctx.db, concrete_variant.ty, n_snapshots);
2087
2088            let inner_pattern = match enum_pattern.pattern(syntax_db) {
2089                ast::OptionPatternEnumInnerPattern::Empty(_) => None,
2090                ast::OptionPatternEnumInnerPattern::PatternEnumInnerPattern(p) => {
2091                    let pattern = compute_pattern_semantic(
2092                        ctx,
2093                        &p.pattern(syntax_db),
2094                        inner_ty,
2095                        or_pattern_variables_map,
2096                    );
2097                    Some(pattern.id)
2098                }
2099            };
2100
2101            Pattern::EnumVariant(PatternEnumVariant {
2102                variant: concrete_variant,
2103                inner_pattern,
2104                ty,
2105                stable_ptr: enum_pattern.stable_ptr().into(),
2106            })
2107        }
2108        ast::Pattern::Path(path) => {
2109            // A path of length 1 is an identifier, which will result in a variable pattern.
2110            // Currently, other paths are not supported (and not clear if ever will be).
2111            if path.elements(syntax_db).len() > 1 {
2112                return Err(ctx.diagnostics.report(path, Unsupported));
2113            }
2114            // TODO(spapini): Make sure this is a simple identifier. In particular, no generics.
2115            let identifier = path.elements(syntax_db)[0].identifier_ast(syntax_db);
2116            create_variable_pattern(
2117                ctx,
2118                identifier,
2119                &[],
2120                ty,
2121                path.stable_ptr().into(),
2122                or_pattern_variables_map,
2123            )
2124        }
2125        ast::Pattern::Identifier(identifier) => create_variable_pattern(
2126            ctx,
2127            identifier.name(syntax_db),
2128            &identifier.modifiers(syntax_db).elements(syntax_db),
2129            ty,
2130            identifier.stable_ptr().into(),
2131            or_pattern_variables_map,
2132        ),
2133        ast::Pattern::Struct(pattern_struct) => {
2134            let pattern_ty = try_extract_matches!(
2135                ctx.resolver.resolve_concrete_path_ex(
2136                    ctx.diagnostics,
2137                    &pattern_struct.path(syntax_db),
2138                    NotFoundItemType::Type,
2139                    Some(&mut ctx.environment)
2140                )?,
2141                ResolvedConcreteItem::Type
2142            )
2143            .ok_or_else(|| ctx.diagnostics.report(&pattern_struct.path(syntax_db), NotAType))?;
2144            let inference = &mut ctx.resolver.inference();
2145            inference.conform_ty(pattern_ty, peel_snapshots(ctx.db, ty).1.intern(ctx.db)).map_err(
2146                |err_set| inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr),
2147            )?;
2148            let ty = ctx.reduce_ty(ty);
2149            // Peel all snapshot wrappers.
2150            let (n_snapshots, long_ty) = peel_snapshots(ctx.db, ty);
2151
2152            // Check that type is an struct, and get the concrete struct from it.
2153            let concrete_struct_id = try_extract_matches!(long_ty, TypeLongId::Concrete)
2154                .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2155                .ok_or(())
2156                .or_else(|_| {
2157                    // Don't add a diagnostic if the type is missing.
2158                    // A diagnostic should've already been added.
2159                    ty.check_not_missing(ctx.db)?;
2160                    Err(ctx.diagnostics.report(pattern_struct, UnexpectedStructPattern(ty)))
2161                })?;
2162            let pattern_param_asts = pattern_struct.params(syntax_db).elements(syntax_db);
2163            let struct_id = concrete_struct_id.struct_id(ctx.db);
2164            let mut members = ctx.db.concrete_struct_members(concrete_struct_id)?.as_ref().clone();
2165            let mut used_members = UnorderedHashSet::<_>::default();
2166            let mut get_member = |ctx: &mut ComputationContext<'_>,
2167                                  member_name: SmolStr,
2168                                  stable_ptr: SyntaxStablePtrId| {
2169                let member = members.swap_remove(&member_name).on_none(|| {
2170                    ctx.diagnostics.report(
2171                        stable_ptr,
2172                        if used_members.contains(&member_name) {
2173                            StructMemberRedefinition { struct_id, member_name: member_name.clone() }
2174                        } else {
2175                            NoSuchStructMember { struct_id, member_name: member_name.clone() }
2176                        },
2177                    );
2178                })?;
2179                check_struct_member_is_visible(ctx, &member, stable_ptr, &member_name);
2180                used_members.insert(member_name);
2181                Some(member)
2182            };
2183            let mut field_patterns = vec![];
2184            let mut has_tail = false;
2185            for pattern_param_ast in pattern_param_asts {
2186                match pattern_param_ast {
2187                    PatternStructParam::Single(single) => {
2188                        let name = single.name(syntax_db);
2189                        let Some(member) =
2190                            get_member(ctx, name.text(syntax_db), name.stable_ptr().untyped())
2191                        else {
2192                            continue;
2193                        };
2194                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2195                        let pattern = create_variable_pattern(
2196                            ctx,
2197                            name,
2198                            &single.modifiers(syntax_db).elements(syntax_db),
2199                            ty,
2200                            single.stable_ptr().into(),
2201                            or_pattern_variables_map,
2202                        );
2203                        field_patterns.push((member, ctx.arenas.patterns.alloc(pattern)));
2204                    }
2205                    PatternStructParam::WithExpr(with_expr) => {
2206                        let name = with_expr.name(syntax_db);
2207                        let Some(member) =
2208                            get_member(ctx, name.text(syntax_db), name.stable_ptr().untyped())
2209                        else {
2210                            continue;
2211                        };
2212                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2213                        let pattern = compute_pattern_semantic(
2214                            ctx,
2215                            &with_expr.pattern(syntax_db),
2216                            ty,
2217                            or_pattern_variables_map,
2218                        );
2219                        field_patterns.push((member, pattern.id));
2220                    }
2221                    PatternStructParam::Tail(_) => {
2222                        has_tail = true;
2223                    }
2224                }
2225            }
2226            if !has_tail {
2227                for (member_name, _) in members.iter() {
2228                    ctx.diagnostics.report(pattern_struct, MissingMember(member_name.clone()));
2229                }
2230            }
2231            Pattern::Struct(PatternStruct {
2232                concrete_struct_id,
2233                field_patterns,
2234                ty,
2235                n_snapshots,
2236                stable_ptr: pattern_struct.stable_ptr(),
2237            })
2238        }
2239        ast::Pattern::Tuple(_) => maybe_compute_tuple_like_pattern_semantic(
2240            ctx,
2241            pattern_syntax,
2242            ty,
2243            or_pattern_variables_map,
2244            |ty: TypeId| UnexpectedTuplePattern(ty),
2245            |expected, actual| WrongNumberOfTupleElements { expected, actual },
2246        )?,
2247        ast::Pattern::FixedSizeArray(_) => maybe_compute_tuple_like_pattern_semantic(
2248            ctx,
2249            pattern_syntax,
2250            ty,
2251            or_pattern_variables_map,
2252            |ty: TypeId| UnexpectedFixedSizeArrayPattern(ty),
2253            |expected, actual| WrongNumberOfFixedSizeArrayElements { expected, actual },
2254        )?,
2255        ast::Pattern::False(pattern_false) => {
2256            let enum_expr = extract_matches!(
2257                false_literal_expr(ctx, pattern_false.stable_ptr().into()),
2258                Expr::EnumVariantCtor
2259            );
2260
2261            extract_concrete_enum_from_pattern_and_validate(
2262                ctx,
2263                pattern_syntax,
2264                ty,
2265                enum_expr.variant.concrete_enum_id.enum_id(ctx.db),
2266            )?;
2267
2268            Pattern::EnumVariant(PatternEnumVariant {
2269                variant: enum_expr.variant,
2270                stable_ptr: pattern_false.stable_ptr().into(),
2271                ty,
2272                inner_pattern: None,
2273            })
2274        }
2275        ast::Pattern::True(pattern_true) => {
2276            let enum_expr = extract_matches!(
2277                true_literal_expr(ctx, pattern_true.stable_ptr().into()),
2278                Expr::EnumVariantCtor
2279            );
2280            extract_concrete_enum_from_pattern_and_validate(
2281                ctx,
2282                pattern_syntax,
2283                ty,
2284                enum_expr.variant.concrete_enum_id.enum_id(ctx.db),
2285            )?;
2286
2287            Pattern::EnumVariant(PatternEnumVariant {
2288                variant: enum_expr.variant,
2289                stable_ptr: pattern_true.stable_ptr().into(),
2290                ty,
2291                inner_pattern: None,
2292            })
2293        }
2294    };
2295    let inference = &mut ctx.resolver.inference();
2296    inference.conform_ty(pattern.ty(), ty).map_err(|err_set| {
2297        inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr)
2298    })?;
2299    Ok(pattern)
2300}
2301
2302/// Computes the semantic model of a pattern of a tuple or a fixed size array. Assumes that the
2303/// pattern is one of these types.
2304fn maybe_compute_tuple_like_pattern_semantic(
2305    ctx: &mut ComputationContext<'_>,
2306    pattern_syntax: &ast::Pattern,
2307    ty: TypeId,
2308    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2309    unexpected_pattern: fn(TypeId) -> SemanticDiagnosticKind,
2310    wrong_number_of_elements: fn(usize, usize) -> SemanticDiagnosticKind,
2311) -> Maybe<Pattern> {
2312    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern_syntax)?;
2313    // Assert that the pattern is of the same type as the expr.
2314    match (pattern_syntax, &long_ty) {
2315        (ast::Pattern::Tuple(_), TypeLongId::Tuple(_))
2316        | (ast::Pattern::FixedSizeArray(_), TypeLongId::FixedSizeArray { .. }) => {}
2317        _ => {
2318            return Err(ctx.diagnostics.report(pattern_syntax, unexpected_pattern(ty)));
2319        }
2320    };
2321    let inner_tys = match long_ty {
2322        TypeLongId::Tuple(inner_tys) => inner_tys,
2323        TypeLongId::FixedSizeArray { type_id: inner_ty, size } => {
2324            let size = size
2325                .lookup_intern(ctx.db)
2326                .into_int()
2327                .expect("Expected ConstValue::Int for size")
2328                .to_usize()
2329                .unwrap();
2330            [inner_ty].repeat(size)
2331        }
2332        _ => unreachable!(),
2333    };
2334    let patterns_syntax = match pattern_syntax {
2335        ast::Pattern::Tuple(pattern_tuple) => {
2336            pattern_tuple.patterns(ctx.db.upcast()).elements(ctx.db.upcast())
2337        }
2338        ast::Pattern::FixedSizeArray(pattern_fixed_size_array) => {
2339            pattern_fixed_size_array.patterns(ctx.db.upcast()).elements(ctx.db.upcast())
2340        }
2341        _ => unreachable!(),
2342    };
2343    let size = inner_tys.len();
2344    if size != patterns_syntax.len() {
2345        return Err(ctx
2346            .diagnostics
2347            .report(pattern_syntax, wrong_number_of_elements(size, patterns_syntax.len())));
2348    }
2349    let pattern_options = zip_eq(patterns_syntax, inner_tys).map(|(pattern_ast, ty)| {
2350        let ty = wrap_in_snapshots(ctx.db, ty, n_snapshots);
2351        let pattern = compute_pattern_semantic(ctx, &pattern_ast, ty, or_pattern_variables_map);
2352        Ok(pattern.id)
2353    });
2354    // If all are Some, collect into a Vec.
2355    let field_patterns: Vec<_> = pattern_options.collect::<Maybe<_>>()?;
2356    Ok(match pattern_syntax {
2357        ast::Pattern::Tuple(syntax) => {
2358            Pattern::Tuple(PatternTuple { field_patterns, ty, stable_ptr: syntax.stable_ptr() })
2359        }
2360        ast::Pattern::FixedSizeArray(syntax) => Pattern::FixedSizeArray(PatternFixedSizeArray {
2361            elements_patterns: field_patterns,
2362            ty,
2363            stable_ptr: syntax.stable_ptr(),
2364        }),
2365        _ => unreachable!(),
2366    })
2367}
2368
2369/// Validates that the semantic type of an enum pattern is an enum, and returns the concrete enum.
2370fn extract_concrete_enum_from_pattern_and_validate(
2371    ctx: &mut ComputationContext<'_>,
2372    pattern: &ast::Pattern,
2373    ty: TypeId,
2374    enum_id: EnumId,
2375) -> Maybe<(ConcreteEnumId, usize)> {
2376    // Peel all snapshot wrappers.
2377    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern)?;
2378
2379    // Check that type is an enum, and get the concrete enum from it.
2380    let concrete_enum = try_extract_matches!(long_ty, TypeLongId::Concrete)
2381        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Enum))
2382        .ok_or(())
2383        .or_else(|_| {
2384            // Don't add a diagnostic if the type is missing.
2385            // A diagnostic should've already been added.
2386            ty.check_not_missing(ctx.db)?;
2387            Err(ctx.diagnostics.report(pattern, UnexpectedEnumPattern(ty)))
2388        })?;
2389    // Check that these are the same enums.
2390    if enum_id != concrete_enum.enum_id(ctx.db) {
2391        return Err(ctx.diagnostics.report(pattern, WrongEnum {
2392            expected_enum: concrete_enum.enum_id(ctx.db),
2393            actual_enum: enum_id,
2394        }));
2395    }
2396    Ok((concrete_enum, n_snapshots))
2397}
2398
2399/// Creates a local variable pattern.
2400fn create_variable_pattern(
2401    ctx: &mut ComputationContext<'_>,
2402    identifier: ast::TerminalIdentifier,
2403    modifier_list: &[ast::Modifier],
2404    ty: TypeId,
2405    stable_ptr: ast::PatternPtr,
2406    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2407) -> Pattern {
2408    let syntax_db = ctx.db.upcast();
2409
2410    let var_id = match or_pattern_variables_map.get(&identifier.text(syntax_db)) {
2411        Some(var) => var.id,
2412        None => LocalVarLongId(ctx.resolver.module_file_id, identifier.stable_ptr()).intern(ctx.db),
2413    };
2414    let is_mut = match compute_mutability(ctx.diagnostics, syntax_db, modifier_list) {
2415        Mutability::Immutable => false,
2416        Mutability::Mutable => true,
2417        Mutability::Reference => {
2418            ctx.diagnostics.report(&identifier, ReferenceLocalVariable);
2419            false
2420        }
2421    };
2422    Pattern::Variable(PatternVariable {
2423        name: identifier.text(syntax_db),
2424        var: LocalVariable { id: var_id, ty, is_mut },
2425        stable_ptr,
2426    })
2427}
2428
2429/// Creates a struct constructor semantic expression from its AST.
2430fn struct_ctor_expr(
2431    ctx: &mut ComputationContext<'_>,
2432    ctor_syntax: &ast::ExprStructCtorCall,
2433) -> Maybe<Expr> {
2434    let db = ctx.db;
2435    let syntax_db = db.upcast();
2436    let path = ctor_syntax.path(syntax_db);
2437
2438    // Extract struct.
2439    let ty = resolve_type_with_environment(
2440        db,
2441        ctx.diagnostics,
2442        &mut ctx.resolver,
2443        &ast::Expr::Path(path.clone()),
2444        Some(&mut ctx.environment),
2445    );
2446    ty.check_not_missing(db)?;
2447
2448    let concrete_struct_id = try_extract_matches!(ty.lookup_intern(ctx.db), TypeLongId::Concrete)
2449        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2450        .ok_or_else(|| ctx.diagnostics.report(&path, NotAStruct))?;
2451
2452    if ty.is_phantom(db) {
2453        ctx.diagnostics.report(ctor_syntax, CannotCreateInstancesOfPhantomTypes);
2454    }
2455
2456    let members = db.concrete_struct_members(concrete_struct_id)?;
2457    let mut member_exprs: OrderedHashMap<MemberId, Option<ExprId>> = OrderedHashMap::default();
2458    let mut base_struct = None;
2459
2460    for (index, arg) in ctor_syntax
2461        .arguments(syntax_db)
2462        .arguments(syntax_db)
2463        .elements(syntax_db)
2464        .into_iter()
2465        .enumerate()
2466    {
2467        // TODO: Extract to a function for results.
2468        match arg {
2469            ast::StructArg::StructArgSingle(arg) => {
2470                let arg_identifier = arg.identifier(syntax_db);
2471                let arg_name = arg_identifier.text(syntax_db);
2472
2473                // Find struct member by name.
2474                let Some(member) = members.get(&arg_name) else {
2475                    ctx.diagnostics.report(&arg_identifier, UnknownMember);
2476                    continue;
2477                };
2478                check_struct_member_is_visible(
2479                    ctx,
2480                    member,
2481                    arg_identifier.stable_ptr().untyped(),
2482                    &arg_name,
2483                );
2484
2485                // Extract expression.
2486                let arg_expr = match arg.arg_expr(syntax_db) {
2487                    ast::OptionStructArgExpr::Empty(_) => {
2488                        let Ok(expr) = resolve_variable_by_name(
2489                            ctx,
2490                            &arg_identifier,
2491                            path.stable_ptr().into(),
2492                        ) else {
2493                            // Insert only the member id, for correct duplicate member reporting.
2494                            if member_exprs.insert(member.id, None).is_some() {
2495                                ctx.diagnostics
2496                                    .report(&arg_identifier, MemberSpecifiedMoreThanOnce);
2497                            }
2498                            continue;
2499                        };
2500                        ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) }
2501                    }
2502                    ast::OptionStructArgExpr::StructArgExpr(arg_expr) => {
2503                        compute_expr_semantic(ctx, &arg_expr.expr(syntax_db))
2504                    }
2505                };
2506
2507                // Insert and check for duplicates.
2508                if member_exprs.insert(member.id, Some(arg_expr.id)).is_some() {
2509                    ctx.diagnostics.report(&arg_identifier, MemberSpecifiedMoreThanOnce);
2510                }
2511
2512                // Check types.
2513                let inference = &mut ctx.resolver.inference();
2514                if let Err((err_set, actual_ty, expected_ty)) =
2515                    inference.conform_ty_for_diag(arg_expr.ty(), member.ty)
2516                {
2517                    if !expected_ty.is_missing(db) {
2518                        let diag_added = ctx
2519                            .diagnostics
2520                            .report(&arg_identifier, WrongArgumentType { expected_ty, actual_ty });
2521                        inference.consume_reported_error(err_set, diag_added);
2522                    }
2523                    continue;
2524                }
2525            }
2526            ast::StructArg::StructArgTail(base_struct_syntax) => {
2527                // TODO(TomerStarkware): remove tail expression from argument list.
2528                if index
2529                    != ctor_syntax
2530                        .arguments(syntax_db)
2531                        .arguments(syntax_db)
2532                        .elements(syntax_db)
2533                        .len()
2534                        - 1
2535                {
2536                    ctx.diagnostics.report(&base_struct_syntax, StructBaseStructExpressionNotLast);
2537                    continue;
2538                }
2539                let base_struct_expr =
2540                    compute_expr_semantic(ctx, &base_struct_syntax.expression(syntax_db));
2541                let inference = &mut ctx.resolver.inference();
2542                if let Err((err_set, actual_ty, expected_ty)) =
2543                    inference.conform_ty_for_diag(base_struct_expr.ty(), ty)
2544                {
2545                    let diag_added = ctx.diagnostics.report(
2546                        &base_struct_syntax.expression(syntax_db),
2547                        WrongArgumentType { expected_ty, actual_ty },
2548                    );
2549                    inference.consume_reported_error(err_set, diag_added);
2550                    continue;
2551                }
2552
2553                base_struct = Some((base_struct_expr.id, base_struct_syntax));
2554            }
2555        };
2556    }
2557
2558    // Report errors for missing members.
2559    for (member_name, member) in members.iter() {
2560        if !member_exprs.contains_key(&member.id) {
2561            if base_struct.is_some() {
2562                check_struct_member_is_visible(
2563                    ctx,
2564                    member,
2565                    base_struct.clone().unwrap().1.stable_ptr().untyped(),
2566                    member_name,
2567                );
2568            } else {
2569                ctx.diagnostics.report(ctor_syntax, MissingMember(member_name.clone()));
2570            }
2571        }
2572    }
2573    if members.len() == member_exprs.len() {
2574        if let Some((_, base_struct_syntax)) = base_struct {
2575            return Err(ctx
2576                .diagnostics
2577                .report(&base_struct_syntax, StructBaseStructExpressionNoEffect));
2578        }
2579    }
2580    Ok(Expr::StructCtor(ExprStructCtor {
2581        concrete_struct_id,
2582        members: member_exprs.into_iter().filter_map(|(x, y)| Some((x, y?))).collect(),
2583        base_struct: base_struct.map(|(x, _)| x),
2584        ty: TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)).intern(db),
2585        stable_ptr: ctor_syntax.stable_ptr().into(),
2586    }))
2587}
2588
2589/// Returns the tail expression of the given list of statements, if exists.
2590/// A tail expression is the last statement in the list, if it is an expression and
2591/// it does not end with a semicolon.
2592fn get_tail_expression(
2593    syntax_db: &dyn SyntaxGroup,
2594    statements: &[ast::Statement],
2595) -> Option<ast::Expr> {
2596    let last = statements.last()?;
2597    let statement_expr = try_extract_matches!(last, ast::Statement::Expr)?;
2598    try_extract_matches!(statement_expr.semicolon(syntax_db), ast::OptionTerminalSemicolon::Empty)?;
2599    Some(statement_expr.expr(syntax_db))
2600}
2601
2602/// Creates a new numeric literal expression.
2603fn new_literal_expr(
2604    ctx: &mut ComputationContext<'_>,
2605    ty: Option<&str>,
2606    value: BigInt,
2607    stable_ptr: ExprPtr,
2608) -> Maybe<ExprLiteral> {
2609    let ty = if let Some(ty_str) = ty {
2610        // Requires specific blocking as `NonZero` now has NumericLiteral support.
2611        if ty_str == "NonZero" {
2612            return Err(ctx.diagnostics.report(
2613                stable_ptr.untyped(),
2614                SemanticDiagnosticKind::WrongNumberOfArguments { expected: 1, actual: 0 },
2615            ));
2616        }
2617        try_get_core_ty_by_name(ctx.db, ty_str.into(), vec![])
2618            .map_err(|err| ctx.diagnostics.report(stable_ptr.untyped(), err))?
2619    } else {
2620        ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()))
2621    };
2622
2623    // Numeric trait.
2624    let trait_id = numeric_literal_trait(ctx.db);
2625    let generic_args = vec![GenericArgumentId::Type(ty)];
2626    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
2627    let lookup_context = ctx.resolver.impl_lookup_context();
2628    let inference = &mut ctx.resolver.inference();
2629    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
2630
2631    Ok(ExprLiteral { value, ty, stable_ptr })
2632}
2633
2634/// Creates the semantic model of a literal expression from its AST.
2635fn literal_to_semantic(
2636    ctx: &mut ComputationContext<'_>,
2637    literal_syntax: &ast::TerminalLiteralNumber,
2638) -> Maybe<ExprLiteral> {
2639    let db = ctx.db;
2640    let syntax_db = db.upcast();
2641
2642    let (value, ty) = literal_syntax.numeric_value_and_suffix(syntax_db).unwrap_or_default();
2643    let ty = ty.as_ref().map(SmolStr::as_str);
2644
2645    new_literal_expr(ctx, ty, value, literal_syntax.stable_ptr().into())
2646}
2647
2648/// Creates the semantic model of a short string from its AST.
2649fn short_string_to_semantic(
2650    ctx: &mut ComputationContext<'_>,
2651    short_string_syntax: &ast::TerminalShortString,
2652) -> Maybe<ExprLiteral> {
2653    let db = ctx.db;
2654    let syntax_db = db.upcast();
2655
2656    let value = short_string_syntax.numeric_value(syntax_db).unwrap_or_default();
2657
2658    let suffix = short_string_syntax.suffix(syntax_db);
2659    let suffix = suffix.as_ref().map(SmolStr::as_str);
2660
2661    new_literal_expr(ctx, suffix, value, short_string_syntax.stable_ptr().into())
2662}
2663
2664/// Creates a new string literal expression.
2665fn new_string_literal_expr(
2666    ctx: &mut ComputationContext<'_>,
2667    value: String,
2668    stable_ptr: ExprPtr,
2669) -> Maybe<ExprStringLiteral> {
2670    let ty = ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()));
2671
2672    // String trait.
2673    let trait_id = get_core_trait(ctx.db, CoreTraitContext::TopLevel, "StringLiteral".into());
2674    let generic_args = vec![GenericArgumentId::Type(ty)];
2675    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
2676    let lookup_context = ctx.resolver.impl_lookup_context();
2677    let inference = &mut ctx.resolver.inference();
2678    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
2679
2680    Ok(ExprStringLiteral { value, ty, stable_ptr })
2681}
2682
2683/// Creates the semantic model of a string literal from its AST.
2684fn string_literal_to_semantic(
2685    ctx: &mut ComputationContext<'_>,
2686    string_syntax: &ast::TerminalString,
2687) -> Maybe<ExprStringLiteral> {
2688    let db = ctx.db;
2689    let syntax_db = db.upcast();
2690    let stable_ptr = string_syntax.stable_ptr();
2691
2692    let value = string_syntax.string_value(syntax_db).unwrap_or_default();
2693    // TODO(yuval): support prefixes/suffixes for explicit types?
2694
2695    new_string_literal_expr(ctx, value, stable_ptr.into())
2696}
2697
2698/// Given an expression syntax, if it's an identifier, returns it. Otherwise, returns the proper
2699/// error.
2700fn expr_as_identifier(
2701    ctx: &mut ComputationContext<'_>,
2702    path: &ast::ExprPath,
2703    syntax_db: &dyn SyntaxGroup,
2704) -> Maybe<SmolStr> {
2705    let segments = path.elements(syntax_db);
2706    if segments.len() == 1 {
2707        return Ok(segments[0].identifier(syntax_db));
2708    }
2709    Err(ctx.diagnostics.report(path, InvalidMemberExpression))
2710}
2711
2712// TODO(spapini): Consider moving some checks here to the responsibility of the parser.
2713/// Computes the semantic expression for a dot expression.
2714fn dot_expr(
2715    ctx: &mut ComputationContext<'_>,
2716    lexpr: ExprAndId,
2717    rhs_syntax: ast::Expr,
2718    stable_ptr: ast::ExprPtr,
2719) -> Maybe<Expr> {
2720    // Find MemberId.
2721    match rhs_syntax {
2722        ast::Expr::Path(expr) => member_access_expr(ctx, lexpr, expr, stable_ptr),
2723        ast::Expr::FunctionCall(expr) => method_call_expr(ctx, lexpr, expr, stable_ptr),
2724        _ => Err(ctx.diagnostics.report(&rhs_syntax, InvalidMemberExpression)),
2725    }
2726}
2727
2728/// Finds all the trait ids usable in the current context.
2729fn traits_in_context(
2730    ctx: &mut ComputationContext<'_>,
2731) -> Maybe<OrderedHashMap<TraitId, LookupItemId>> {
2732    let mut traits =
2733        ctx.db.module_usable_trait_ids(ctx.resolver.prelude_submodule())?.deref().clone();
2734    traits.extend(
2735        ctx.db
2736            .module_usable_trait_ids(ctx.resolver.module_file_id.0)?
2737            .iter()
2738            .map(|(k, v)| (*k, *v)),
2739    );
2740    Ok(traits)
2741}
2742
2743/// Computes the semantic model of a method call expression (e.g. "expr.method(..)").
2744/// Finds all traits with at least one candidate impl with a matching `self` param.
2745/// If more/less than 1 such trait exists, fails.
2746fn method_call_expr(
2747    ctx: &mut ComputationContext<'_>,
2748    lexpr: ExprAndId,
2749    expr: ast::ExprFunctionCall,
2750    stable_ptr: ast::ExprPtr,
2751) -> Maybe<Expr> {
2752    // TODO(spapini): Add ctx.module_id.
2753    // TODO(spapini): Look also in uses.
2754    let syntax_db = ctx.db.upcast();
2755    let path = expr.path(syntax_db);
2756    let Ok([segment]): Result<[_; 1], _> = path.elements(syntax_db).try_into() else {
2757        return Err(ctx.diagnostics.report(&expr, InvalidMemberExpression));
2758    };
2759    let func_name = segment.identifier(syntax_db);
2760    let generic_args_syntax = segment.generic_args(syntax_db);
2761    // Save some work. ignore the result. The error, if any, will be reported later.
2762    ctx.resolver.inference().solve().ok();
2763
2764    let mut candidate_traits = traits_in_context(ctx)?;
2765
2766    // Add traits from impl generic args in the context.
2767    for generic_param in &ctx.resolver.data.generic_params {
2768        if generic_param.kind(ctx.db.upcast()) == GenericKind::Impl {
2769            let Ok(trait_id) = ctx.db.generic_impl_param_trait(*generic_param) else {
2770                continue;
2771            };
2772            candidate_traits
2773                .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
2774        }
2775    }
2776
2777    let (function_id, actual_trait_id, fixed_lexpr, mutability) =
2778        compute_method_function_call_data(
2779            ctx,
2780            candidate_traits.keys().copied().collect_vec().as_slice(),
2781            func_name,
2782            lexpr,
2783            path.stable_ptr().untyped(),
2784            generic_args_syntax,
2785            |ty, method_name, inference_errors| {
2786                Some(CannotCallMethod { ty, method_name, inference_errors })
2787            },
2788            |_, trait_function_id0, trait_function_id1| {
2789                Some(AmbiguousTrait { trait_function_id0, trait_function_id1 })
2790            },
2791        )?;
2792    ctx.resolver.data.used_items.insert(candidate_traits[&actual_trait_id]);
2793
2794    ctx.resolver.data.resolved_items.mark_concrete(
2795        ctx.db,
2796        &segment,
2797        ResolvedConcreteItem::Function(function_id),
2798    );
2799
2800    // Note there may be n+1 arguments for n parameters, if the last one is a coupon.
2801    let mut args_iter =
2802        expr.arguments(syntax_db).arguments(syntax_db).elements(syntax_db).into_iter();
2803    // Self argument.
2804    let mut named_args = vec![NamedArg(fixed_lexpr, None, mutability)];
2805    // Other arguments.
2806    for _ in function_parameter_types(ctx, function_id)?.skip(1) {
2807        let Some(arg_syntax) = args_iter.next() else {
2808            break;
2809        };
2810        named_args.push(compute_named_argument_clause(ctx, arg_syntax));
2811    }
2812
2813    // Maybe coupon
2814    if let Some(arg_syntax) = args_iter.next() {
2815        named_args.push(compute_named_argument_clause(ctx, arg_syntax));
2816    }
2817
2818    expr_function_call(ctx, function_id, named_args, &expr, stable_ptr)
2819}
2820
2821/// Computes the semantic model of a member access expression (e.g. "expr.member").
2822fn member_access_expr(
2823    ctx: &mut ComputationContext<'_>,
2824    lexpr: ExprAndId,
2825    rhs_syntax: ast::ExprPath,
2826    stable_ptr: ast::ExprPtr,
2827) -> Maybe<Expr> {
2828    let syntax_db = ctx.db.upcast();
2829
2830    // Find MemberId.
2831    let member_name = expr_as_identifier(ctx, &rhs_syntax, syntax_db)?;
2832    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, lexpr.ty(), &rhs_syntax)?;
2833
2834    match &long_ty {
2835        TypeLongId::Concrete(_) | TypeLongId::Tuple(_) | TypeLongId::FixedSizeArray { .. } => {
2836            let Some(EnrichedTypeMemberAccess { member, deref_functions }) =
2837                get_enriched_type_member_access(ctx, lexpr.clone(), stable_ptr, &member_name)?
2838            else {
2839                return Err(ctx.diagnostics.report(&rhs_syntax, NoSuchTypeMember {
2840                    ty: long_ty.intern(ctx.db),
2841                    member_name,
2842                }));
2843            };
2844            check_struct_member_is_visible(
2845                ctx,
2846                &member,
2847                rhs_syntax.stable_ptr().untyped(),
2848                &member_name,
2849            );
2850            let member_path = match &long_ty {
2851                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id))
2852                    if n_snapshots == 0 && deref_functions.is_empty() =>
2853                {
2854                    lexpr.as_member_path().map(|parent| ExprVarMemberPath::Member {
2855                        parent: Box::new(parent),
2856                        member_id: member.id,
2857                        stable_ptr,
2858                        concrete_struct_id: *concrete_struct_id,
2859                        ty: member.ty,
2860                    })
2861                }
2862                _ => None,
2863            };
2864            let mut derefed_expr: ExprAndId = lexpr;
2865            for (deref_function, mutability) in &deref_functions {
2866                let cur_expr = expr_function_call(
2867                    ctx,
2868                    *deref_function,
2869                    vec![NamedArg(derefed_expr, None, *mutability)],
2870                    stable_ptr,
2871                    stable_ptr,
2872                )
2873                .unwrap();
2874
2875                derefed_expr =
2876                    ExprAndId { expr: cur_expr.clone(), id: ctx.arenas.exprs.alloc(cur_expr) };
2877            }
2878            let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, derefed_expr.ty(), &rhs_syntax)?;
2879            let derefed_expr_concrete_struct_id = match long_ty {
2880                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
2881                    concrete_struct_id
2882                }
2883                _ => unreachable!(),
2884            };
2885            let ty = if !deref_functions.is_empty() {
2886                member.ty
2887            } else {
2888                wrap_in_snapshots(ctx.db, member.ty, n_snapshots)
2889            };
2890            Ok(Expr::MemberAccess(ExprMemberAccess {
2891                expr: derefed_expr.id,
2892                concrete_struct_id: derefed_expr_concrete_struct_id,
2893                member: member.id,
2894                ty,
2895                member_path,
2896                n_snapshots,
2897                stable_ptr,
2898            }))
2899        }
2900
2901        TypeLongId::Snapshot(_) => {
2902            // TODO(spapini): Handle snapshot members.
2903            Err(ctx.diagnostics.report(&rhs_syntax, Unsupported))
2904        }
2905        TypeLongId::Closure(_) => Err(ctx.diagnostics.report(&rhs_syntax, Unsupported)),
2906        TypeLongId::ImplType(impl_type_id) => {
2907            unreachable!(
2908                "Impl type should've been reduced {:?}.",
2909                impl_type_id.debug(ctx.db.elongate())
2910            )
2911        }
2912        TypeLongId::Var(_) => Err(ctx.diagnostics.report(
2913            &rhs_syntax,
2914            InternalInferenceError(InferenceError::TypeNotInferred(long_ty.intern(ctx.db))),
2915        )),
2916        TypeLongId::GenericParameter(_) | TypeLongId::Coupon(_) => Err(ctx
2917            .diagnostics
2918            .report(&rhs_syntax, TypeHasNoMembers { ty: long_ty.intern(ctx.db), member_name })),
2919        TypeLongId::Missing(diag_added) => Err(*diag_added),
2920        TypeLongId::TraitType(_) => {
2921            panic!("Trait types should only appear in traits, where there are no function bodies.")
2922        }
2923    }
2924}
2925
2926/// Returns the member and the deref operations needed for its access.
2927///
2928/// Enriched members include both direct members (in case of a struct), and members of derefed types
2929/// if the type implements the Deref trait into a struct.
2930fn get_enriched_type_member_access(
2931    ctx: &mut ComputationContext<'_>,
2932    expr: ExprAndId,
2933    stable_ptr: ast::ExprPtr,
2934    accessed_member_name: &str,
2935) -> Maybe<Option<EnrichedTypeMemberAccess>> {
2936    let (_, mut long_ty) = peel_snapshots(ctx.db, expr.ty());
2937    if matches!(long_ty, TypeLongId::Var(_)) {
2938        // Save some work. ignore the result. The error, if any, will be reported later.
2939        ctx.resolver.inference().solve().ok();
2940        long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
2941    }
2942    let (_, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
2943    let base_var = match &expr.expr {
2944        Expr::Var(expr_var) => Some(expr_var.var),
2945        Expr::MemberAccess(ExprMemberAccess { member_path: Some(member_path), .. }) => {
2946            Some(member_path.base_var())
2947        }
2948        _ => None,
2949    };
2950    let is_mut_var = base_var
2951        .filter(|var_id| matches!(ctx.semantic_defs.get(var_id), Some(var) if var.is_mut()))
2952        .is_some();
2953    let ty = long_ty.clone().intern(ctx.db);
2954    let key = (ty, is_mut_var);
2955    let mut enriched_members = match ctx.resolver.type_enriched_members.entry(key) {
2956        Entry::Occupied(entry) => {
2957            let e = entry.get();
2958            match e.get_member(accessed_member_name) {
2959                Some(value) => return Ok(Some(value)),
2960                None => {
2961                    if e.exploration_tail.is_none() {
2962                        // There's no further exploration to be done, and member was not found.
2963                        return Ok(None);
2964                    }
2965                }
2966            }
2967            // Moving out of the map to call `enrich_members` and insert back with updated value.
2968            entry.swap_remove()
2969        }
2970        Entry::Vacant(_) => {
2971            let members =
2972                if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
2973                    let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
2974                    if let Some(member) = members.get(accessed_member_name) {
2975                        // Found direct member access - so directly returning it.
2976                        return Ok(Some(EnrichedTypeMemberAccess {
2977                            member: member.clone(),
2978                            deref_functions: vec![],
2979                        }));
2980                    }
2981                    members.iter().map(|(k, v)| (k.clone(), (v.clone(), 0))).collect()
2982                } else {
2983                    Default::default()
2984                };
2985            EnrichedMembers { members, deref_functions: vec![], exploration_tail: Some(expr.id) }
2986        }
2987    };
2988    enrich_members(ctx, &mut enriched_members, is_mut_var, stable_ptr, accessed_member_name)?;
2989    let e = ctx.resolver.type_enriched_members.entry(key).or_insert(enriched_members);
2990    Ok(e.get_member(accessed_member_name))
2991}
2992
2993/// Enriches the `enriched_members` with members from "deref"s of the current type.
2994///
2995/// The function will stop enriching if it encounters a cycle in the deref chain, or if the
2996/// requested member is found.
2997fn enrich_members(
2998    ctx: &mut ComputationContext<'_>,
2999    enriched_members: &mut EnrichedMembers,
3000    is_mut_var: bool,
3001    stable_ptr: ast::ExprPtr,
3002    accessed_member_name: &str,
3003) -> Maybe<()> {
3004    let EnrichedMembers { members: enriched, deref_functions, exploration_tail } = enriched_members;
3005    let mut visited_types: OrderedHashSet<TypeId> = OrderedHashSet::default();
3006
3007    let expr_id =
3008        exploration_tail.expect("`enrich_members` should be called with a `calc_tail` value.");
3009    let mut expr = ExprAndId { expr: ctx.arenas.exprs[expr_id].clone(), id: expr_id };
3010
3011    let deref_mut_trait_id = deref_mut_trait(ctx.db);
3012    let deref_trait_id = deref_trait(ctx.db);
3013
3014    let compute_deref_method_function_call_data =
3015        |ctx: &mut ComputationContext<'_>, expr: ExprAndId, use_deref_mut: bool| {
3016            let deref_trait = if use_deref_mut { deref_mut_trait_id } else { deref_trait_id };
3017            compute_method_function_call_data(
3018                ctx,
3019                &[deref_trait],
3020                if use_deref_mut { "deref_mut".into() } else { "deref".into() },
3021                expr.clone(),
3022                stable_ptr.0,
3023                None,
3024                |_, _, _| None,
3025                |_, _, _| None,
3026            )
3027        };
3028
3029    // If the variable is mutable, and implements DerefMut, we use DerefMut in the first iteration.
3030    let mut use_deref_mut = deref_functions.is_empty()
3031        && is_mut_var
3032        && compute_deref_method_function_call_data(ctx, expr.clone(), true).is_ok();
3033
3034    // This function either finds a member and sets `exploration_tail` or finishes the exploration
3035    // and leaves that exploration tail as `None`.
3036    *exploration_tail = None;
3037
3038    // Add members of derefed types.
3039    while let Ok((function_id, _, cur_expr, mutability)) =
3040        compute_deref_method_function_call_data(ctx, expr, use_deref_mut)
3041    {
3042        deref_functions.push((function_id, mutability));
3043        use_deref_mut = false;
3044        let n_deref = deref_functions.len();
3045        expr = cur_expr;
3046        let derefed_expr = expr_function_call(
3047            ctx,
3048            function_id,
3049            vec![NamedArg(expr, None, mutability)],
3050            stable_ptr,
3051            stable_ptr,
3052        )?;
3053        let ty = ctx.reduce_ty(derefed_expr.ty());
3054        let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
3055        // If the type is still a variable we stop looking for derefed members.
3056        if let TypeLongId::Var(_) = long_ty {
3057            break;
3058        }
3059        expr = ExprAndId { expr: derefed_expr.clone(), id: ctx.arenas.exprs.alloc(derefed_expr) };
3060        if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
3061            let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
3062            for (member_name, member) in members.iter() {
3063                // Insert member if there is not already a member with the same name.
3064                enriched.entry(member_name.clone()).or_insert_with(|| (member.clone(), n_deref));
3065            }
3066            // If member is contained we can stop the calculation post the lookup.
3067            if members.contains_key(accessed_member_name) {
3068                // Found member, so exploration isn't done - setting up the tail.
3069                *exploration_tail = Some(expr.id);
3070                break;
3071            }
3072        }
3073        if !visited_types.insert(long_ty.intern(ctx.db)) {
3074            // Break if we have a cycle. A diagnostic will be reported from the impl and not from
3075            // member access.
3076            break;
3077        }
3078    }
3079    Ok(())
3080}
3081
3082/// Peels snapshots from a type and making sure it is fully not a variable type.
3083fn finalized_snapshot_peeled_ty(
3084    ctx: &mut ComputationContext<'_>,
3085    ty: TypeId,
3086    stable_ptr: impl Into<SyntaxStablePtrId>,
3087) -> Maybe<(usize, TypeLongId)> {
3088    let ty = ctx.reduce_ty(ty);
3089    let (base_snapshots, mut long_ty) = peel_snapshots(ctx.db, ty);
3090    if let TypeLongId::ImplType(impl_type_id) = long_ty {
3091        let inference = &mut ctx.resolver.inference();
3092        let Ok(ty) = inference.reduce_impl_ty(impl_type_id) else {
3093            return Err(ctx
3094                .diagnostics
3095                .report(stable_ptr, InternalInferenceError(InferenceError::TypeNotInferred(ty))));
3096        };
3097        long_ty = ty.lookup_intern(ctx.db);
3098    }
3099    if matches!(long_ty, TypeLongId::Var(_)) {
3100        // Save some work. ignore the result. The error, if any, will be reported later.
3101        ctx.resolver.inference().solve().ok();
3102        long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
3103    }
3104    let (additional_snapshots, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
3105    Ok((base_snapshots + additional_snapshots, long_ty))
3106}
3107
3108/// Resolves a variable or a constant given a context and a path expression.
3109fn resolve_expr_path(ctx: &mut ComputationContext<'_>, path: &ast::ExprPath) -> Maybe<Expr> {
3110    let db = ctx.db;
3111    let syntax_db = db.upcast();
3112    let segments = path.elements(syntax_db);
3113    if segments.is_empty() {
3114        return Err(ctx.diagnostics.report(path, Unsupported));
3115    }
3116
3117    // Check if this is a variable.
3118    if let [PathSegment::Simple(ident_segment)] = &segments[..] {
3119        let identifier = ident_segment.ident(syntax_db);
3120        let variable_name = identifier.text(ctx.db.upcast());
3121        if let Some(res) = get_binded_expr_by_name(ctx, &variable_name, path.stable_ptr().into()) {
3122            match res.clone() {
3123                Expr::Var(expr_var) => {
3124                    let item = ResolvedGenericItem::Variable(expr_var.var);
3125                    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(), item);
3126                }
3127                Expr::Constant(expr_const) => {
3128                    let item = ResolvedConcreteItem::Constant(expr_const.const_value_id);
3129                    ctx.resolver.data.resolved_items.concrete.insert(identifier.stable_ptr(), item);
3130                }
3131                _ => unreachable!(
3132                    "get_binded_expr_by_name should only return variables or constants"
3133                ),
3134            };
3135            return Ok(res);
3136        }
3137    }
3138
3139    let resolved_item: ResolvedConcreteItem = ctx.resolver.resolve_concrete_path_ex(
3140        ctx.diagnostics,
3141        path,
3142        NotFoundItemType::Identifier,
3143        Some(&mut ctx.environment),
3144    )?;
3145
3146    match resolved_item {
3147        ResolvedConcreteItem::Constant(const_value_id) => Ok(Expr::Constant(ExprConstant {
3148            const_value_id,
3149            ty: const_value_id.ty(db)?,
3150            stable_ptr: path.stable_ptr().into(),
3151        })),
3152
3153        ResolvedConcreteItem::Variant(variant) if variant.ty == unit_ty(db) => {
3154            let stable_ptr = path.stable_ptr().into();
3155            let concrete_enum_id = variant.concrete_enum_id;
3156            Ok(semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
3157                variant,
3158                value_expr: unit_expr(ctx, stable_ptr),
3159                ty: TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)).intern(db),
3160                stable_ptr,
3161            }))
3162        }
3163        resolved_item => Err(ctx.diagnostics.report(path, UnexpectedElement {
3164            expected: vec![ElementKind::Variable, ElementKind::Constant],
3165            actual: (&resolved_item).into(),
3166        })),
3167    }
3168}
3169
3170/// Resolves a variable given a context and a simple name.
3171///
3172/// Reports a diagnostic if the variable was not found.
3173pub fn resolve_variable_by_name(
3174    ctx: &mut ComputationContext<'_>,
3175    identifier: &ast::TerminalIdentifier,
3176    stable_ptr: ast::ExprPtr,
3177) -> Maybe<Expr> {
3178    let variable_name = identifier.text(ctx.db.upcast());
3179    let res = get_binded_expr_by_name(ctx, &variable_name, stable_ptr)
3180        .ok_or_else(|| ctx.diagnostics.report(identifier, VariableNotFound(variable_name)))?;
3181    let item = ResolvedGenericItem::Variable(extract_matches!(&res, Expr::Var).var);
3182    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(), item);
3183    Ok(res)
3184}
3185
3186/// Returns the requested variable from the environment if it exists. Returns None otherwise.
3187pub fn get_binded_expr_by_name(
3188    ctx: &mut ComputationContext<'_>,
3189    variable_name: &SmolStr,
3190    stable_ptr: ast::ExprPtr,
3191) -> Option<Expr> {
3192    let mut maybe_env = Some(&mut *ctx.environment);
3193    while let Some(env) = maybe_env {
3194        if let Some(var) = env.variables.get(variable_name) {
3195            env.used_variables.insert(var.id());
3196            return match var {
3197                Binding::LocalItem(local_const) => match local_const.kind.clone() {
3198                    crate::StatementItemKind::Constant(const_value_id, ty) => {
3199                        Some(Expr::Constant(ExprConstant { const_value_id, ty, stable_ptr }))
3200                    }
3201                },
3202                Binding::LocalVar(_) | Binding::Param(_) => {
3203                    Some(Expr::Var(ExprVar { var: var.id(), ty: var.ty(), stable_ptr }))
3204                }
3205            };
3206        }
3207        maybe_env = env.parent.as_deref_mut();
3208    }
3209    None
3210}
3211
3212/// Typechecks a function call.
3213fn expr_function_call(
3214    ctx: &mut ComputationContext<'_>,
3215    function_id: FunctionId,
3216    mut named_args: Vec<NamedArg>,
3217    call_ptr: impl Into<SyntaxStablePtrId>,
3218    stable_ptr: ast::ExprPtr,
3219) -> Maybe<Expr> {
3220    let coupon_arg = maybe_pop_coupon_argument(ctx, &mut named_args, function_id);
3221
3222    let signature = ctx.db.concrete_function_signature(function_id)?;
3223    let signature = ctx.resolver.inference().rewrite(signature).unwrap();
3224
3225    // TODO(spapini): Better location for these diagnostics after the refactor for generics resolve.
3226    if named_args.len() != signature.params.len() {
3227        return Err(ctx.diagnostics.report(call_ptr, WrongNumberOfArguments {
3228            expected: signature.params.len(),
3229            actual: named_args.len(),
3230        }));
3231    }
3232
3233    // Check argument names and types.
3234    check_named_arguments(&named_args, &signature, ctx)?;
3235
3236    let mut args = Vec::new();
3237    for (NamedArg(arg, _name, mutability), param) in
3238        named_args.into_iter().zip(signature.params.iter())
3239    {
3240        let arg_typ = arg.ty();
3241        let param_typ = param.ty;
3242        // Don't add diagnostic if the type is missing (a diagnostic should have already been
3243        // added).
3244        // TODO(lior): Add a test to missing type once possible.
3245        if !arg_typ.is_missing(ctx.db) {
3246            let inference = &mut ctx.resolver.inference();
3247            if let Err((err_set, actual_ty, expected_ty)) =
3248                inference.conform_ty_for_diag(arg_typ, param_typ)
3249            {
3250                let diag_added = ctx
3251                    .diagnostics
3252                    .report(arg.deref(), WrongArgumentType { expected_ty, actual_ty });
3253                inference.consume_reported_error(err_set, diag_added);
3254            }
3255        }
3256
3257        args.push(if param.mutability == Mutability::Reference {
3258            // Verify the argument is a variable.
3259            let Some(ref_arg) = arg.as_member_path() else {
3260                return Err(ctx.diagnostics.report(arg.deref(), RefArgNotAVariable));
3261            };
3262            // Verify the variable argument is mutable.
3263            if !ctx.semantic_defs[&ref_arg.base_var()].is_mut() {
3264                ctx.diagnostics.report(arg.deref(), RefArgNotMutable);
3265            }
3266            // Verify that it is passed explicitly as 'ref'.
3267            if mutability != Mutability::Reference {
3268                ctx.diagnostics.report(arg.deref(), RefArgNotExplicit);
3269            }
3270            ExprFunctionCallArg::Reference(ref_arg)
3271        } else {
3272            // Verify that it is passed without modifiers.
3273            if mutability != Mutability::Immutable {
3274                ctx.diagnostics.report(arg.deref(), ImmutableArgWithModifiers);
3275            }
3276            ExprFunctionCallArg::Value(arg.id)
3277        });
3278    }
3279
3280    let expr_function_call = ExprFunctionCall {
3281        function: function_id,
3282        args,
3283        coupon_arg,
3284        ty: signature.return_type,
3285        stable_ptr,
3286    };
3287    // Check panicable.
3288    if signature.panicable && has_panic_incompatibility(ctx, &expr_function_call) {
3289        // TODO(spapini): Delay this check until after inference, to allow resolving specific
3290        //   impls first.
3291        return Err(ctx.diagnostics.report(call_ptr, PanicableFromNonPanicable));
3292    }
3293    Ok(Expr::FunctionCall(expr_function_call))
3294}
3295
3296/// Checks if the last item in `named_args`, has the argument name `__coupon__`, and removes and
3297/// returns it if so.
3298fn maybe_pop_coupon_argument(
3299    ctx: &mut ComputationContext<'_>,
3300    named_args: &mut Vec<NamedArg>,
3301    function_id: FunctionId,
3302) -> Option<id_arena::Id<Expr>> {
3303    let mut coupon_arg: Option<ExprId> = None;
3304    if let Some(NamedArg(arg, Some(name_terminal), mutability)) = named_args.last() {
3305        let coupons_enabled = are_coupons_enabled(ctx.db, ctx.resolver.module_file_id);
3306        if name_terminal.text(ctx.db.upcast()) == "__coupon__" && coupons_enabled {
3307            // Check that the argument type is correct.
3308            let expected_ty = TypeLongId::Coupon(function_id).intern(ctx.db);
3309            let arg_typ = arg.ty();
3310            if !arg_typ.is_missing(ctx.db) {
3311                let inference = &mut ctx.resolver.inference();
3312                if let Err((err_set, actual_ty, expected_ty)) =
3313                    inference.conform_ty_for_diag(arg_typ, expected_ty)
3314                {
3315                    let diag_added = ctx
3316                        .diagnostics
3317                        .report(arg.deref(), WrongArgumentType { expected_ty, actual_ty });
3318                    inference.consume_reported_error(err_set, diag_added);
3319                }
3320            }
3321
3322            // Check that the argument is not mutable/reference.
3323            if *mutability != Mutability::Immutable {
3324                ctx.diagnostics.report(arg.deref(), CouponArgumentNoModifiers);
3325            }
3326
3327            coupon_arg = Some(arg.id);
3328
3329            // Remove the __coupon__ argument from the argument list.
3330            named_args.pop();
3331        }
3332    }
3333    coupon_arg
3334}
3335
3336/// Checks if a panicable function is called from a disallowed context.
3337fn has_panic_incompatibility(
3338    ctx: &mut ComputationContext<'_>,
3339    expr_function_call: &ExprFunctionCall,
3340) -> bool {
3341    // If this is not an actual function call, but actually a minus literal (e.g. -1), then this is
3342    // the same as nopanic.
3343    if try_extract_minus_literal(ctx.db, &ctx.arenas.exprs, expr_function_call).is_some() {
3344        return false;
3345    }
3346    if let Some(signature) = ctx.signature {
3347        // If the caller is nopanic, then this is a panic incompatibility.
3348        !signature.panicable
3349    } else {
3350        false
3351    }
3352}
3353
3354/// Checks the correctness of the named arguments, and outputs diagnostics on errors.
3355fn check_named_arguments(
3356    named_args: &[NamedArg],
3357    signature: &Signature,
3358    ctx: &mut ComputationContext<'_>,
3359) -> Maybe<()> {
3360    let mut res: Maybe<()> = Ok(());
3361
3362    // Indicates whether we saw a named argument. Used to report a diagnostic if an unnamed argument
3363    // will follow it.
3364    let mut seen_named_arguments: bool = false;
3365    // Indicates whether a [UnnamedArgumentFollowsNamed] diagnostic was reported. Used to prevent
3366    // multiple similar diagnostics.
3367    let mut reported_unnamed_argument_follows_named: bool = false;
3368    for (NamedArg(arg, name_opt, _mutability), param) in
3369        named_args.iter().zip(signature.params.iter())
3370    {
3371        // Check name.
3372        if let Some(name_terminal) = name_opt {
3373            seen_named_arguments = true;
3374            let name = name_terminal.text(ctx.db.upcast());
3375            if param.name != name.clone() {
3376                res = Err(ctx.diagnostics.report(name_terminal, NamedArgumentMismatch {
3377                    expected: param.name.clone(),
3378                    found: name,
3379                }));
3380            }
3381        } else if seen_named_arguments && !reported_unnamed_argument_follows_named {
3382            reported_unnamed_argument_follows_named = true;
3383            res = Err(ctx.diagnostics.report(arg.deref(), UnnamedArgumentFollowsNamed));
3384        }
3385    }
3386    res
3387}
3388
3389/// Computes the semantic model of a statement (excluding tail-expression).
3390pub fn compute_statement_semantic(
3391    ctx: &mut ComputationContext<'_>,
3392    syntax: ast::Statement,
3393) -> Maybe<StatementId> {
3394    let db = ctx.db;
3395    let syntax_db = db.upcast();
3396    // As for now, statement attributes does not have any semantic affect, so we only validate they
3397    // are allowed.
3398    validate_statement_attributes(ctx, &syntax);
3399    let feature_restore = ctx
3400        .resolver
3401        .data
3402        .feature_config
3403        .override_with(extract_item_feature_config(db, &syntax, ctx.diagnostics));
3404    let statement = match &syntax {
3405        ast::Statement::Let(let_syntax) => {
3406            let rhs_syntax = &let_syntax.rhs(syntax_db);
3407            let (rhs_expr, ty) = match let_syntax.type_clause(syntax_db) {
3408                ast::OptionTypeClause::Empty(_) => {
3409                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
3410                    let inferred_type = rhs_expr.ty();
3411                    (rhs_expr, inferred_type)
3412                }
3413                ast::OptionTypeClause::TypeClause(type_clause) => {
3414                    let var_type_path = type_clause.ty(syntax_db);
3415                    let explicit_type = resolve_type_with_environment(
3416                        db,
3417                        ctx.diagnostics,
3418                        &mut ctx.resolver,
3419                        &var_type_path,
3420                        Some(&mut ctx.environment),
3421                    );
3422
3423                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
3424                    let inferred_type = ctx.reduce_ty(rhs_expr.ty());
3425                    if !inferred_type.is_missing(db) {
3426                        let inference = &mut ctx.resolver.inference();
3427                        if let Err((err_set, actual_ty, expected_ty)) =
3428                            inference.conform_ty_for_diag(inferred_type, explicit_type)
3429                        {
3430                            let diag_added = ctx
3431                                .diagnostics
3432                                .report(rhs_syntax, WrongArgumentType { expected_ty, actual_ty });
3433                            inference.consume_reported_error(err_set, diag_added);
3434                        }
3435                    }
3436                    (rhs_expr, explicit_type)
3437                }
3438            };
3439            let rhs_expr_id = rhs_expr.id;
3440
3441            let pattern = compute_pattern_semantic(
3442                ctx,
3443                &let_syntax.pattern(syntax_db),
3444                ty,
3445                &mut UnorderedHashMap::default(),
3446            );
3447            let variables = pattern.variables(&ctx.arenas.patterns);
3448            for v in variables {
3449                let var_def = Binding::LocalVar(v.var.clone());
3450                if let Some(old_var) =
3451                    ctx.environment.variables.insert(v.name.clone(), var_def.clone())
3452                {
3453                    if matches!(old_var, Binding::LocalItem(_)) {
3454                        return Err(ctx
3455                            .diagnostics
3456                            .report(v.stable_ptr, MultipleDefinitionforBinding(v.name.clone())));
3457                    }
3458                    ctx.add_unused_binding_warning(&v.name, &old_var);
3459                }
3460                ctx.semantic_defs.insert(var_def.id(), var_def);
3461            }
3462            semantic::Statement::Let(semantic::StatementLet {
3463                pattern: pattern.id,
3464                expr: rhs_expr_id,
3465                stable_ptr: syntax.stable_ptr(),
3466            })
3467        }
3468        ast::Statement::Expr(stmt_expr_syntax) => {
3469            let expr_syntax = stmt_expr_syntax.expr(syntax_db);
3470            let expr = compute_expr_semantic(ctx, &expr_syntax);
3471            if matches!(
3472                stmt_expr_syntax.semicolon(syntax_db),
3473                ast::OptionTerminalSemicolon::Empty(_)
3474            ) && !matches!(
3475                expr_syntax,
3476                ast::Expr::Block(_) | ast::Expr::If(_) | ast::Expr::Match(_)
3477            ) {
3478                // Point to after the expression, where the semicolon is missing.
3479                ctx.diagnostics.report_after(&expr_syntax, MissingSemicolon);
3480            }
3481            let ty: TypeId = expr.ty();
3482            if let TypeLongId::Concrete(concrete) = ty.lookup_intern(db) {
3483                if concrete.is_must_use(db)? {
3484                    ctx.diagnostics.report(&expr_syntax, UnhandledMustUseType(ty));
3485                }
3486            }
3487            if let Expr::FunctionCall(expr_function_call) = &expr.expr {
3488                let generic_function_id =
3489                    expr_function_call.function.lookup_intern(db).function.generic_function;
3490                if generic_function_id.is_must_use(db)? {
3491                    ctx.diagnostics.report(&expr_syntax, UnhandledMustUseFunction);
3492                }
3493            }
3494            semantic::Statement::Expr(semantic::StatementExpr {
3495                expr: expr.id,
3496                stable_ptr: syntax.stable_ptr(),
3497            })
3498        }
3499        ast::Statement::Continue(continue_syntax) => {
3500            if !ctx.is_inside_loop() {
3501                return Err(ctx
3502                    .diagnostics
3503                    .report(continue_syntax, ContinueOnlyAllowedInsideALoop));
3504            }
3505            semantic::Statement::Continue(semantic::StatementContinue {
3506                stable_ptr: syntax.stable_ptr(),
3507            })
3508        }
3509        ast::Statement::Return(return_syntax) => {
3510            if ctx.is_inside_loop() {
3511                return Err(ctx.diagnostics.report(return_syntax, ReturnNotAllowedInsideALoop));
3512            }
3513
3514            let (expr_option, expr_ty, stable_ptr) = match return_syntax.expr_clause(syntax_db) {
3515                ast::OptionExprClause::Empty(empty_clause) => {
3516                    (None, unit_ty(db), empty_clause.stable_ptr().untyped())
3517                }
3518                ast::OptionExprClause::ExprClause(expr_clause) => {
3519                    let expr_syntax = expr_clause.expr(syntax_db);
3520                    let expr = compute_expr_semantic(ctx, &expr_syntax);
3521                    (Some(expr.id), expr.ty(), expr_syntax.stable_ptr().untyped())
3522                }
3523            };
3524            let expected_ty = match ctx.inner_ctx {
3525                None => {
3526                    ctx.get_signature(
3527                        return_syntax.into(),
3528                        UnsupportedOutsideOfFunctionFeatureName::ReturnStatement,
3529                    )?
3530                    .return_type
3531                }
3532                Some(InnerContext::Closure { return_type }) => return_type,
3533                _ => unreachable!("Return statement inside a loop"),
3534            };
3535
3536            let expected_ty = ctx.reduce_ty(expected_ty);
3537            let expr_ty = ctx.reduce_ty(expr_ty);
3538            if !expected_ty.is_missing(db) && !expr_ty.is_missing(db) {
3539                let inference = &mut ctx.resolver.inference();
3540                if let Err((err_set, actual_ty, expected_ty)) =
3541                    inference.conform_ty_for_diag(expr_ty, expected_ty)
3542                {
3543                    let diag_added = ctx
3544                        .diagnostics
3545                        .report(stable_ptr, WrongReturnType { expected_ty, actual_ty });
3546                    inference.consume_reported_error(err_set, diag_added);
3547                }
3548            }
3549            semantic::Statement::Return(semantic::StatementReturn {
3550                expr_option,
3551                stable_ptr: syntax.stable_ptr(),
3552            })
3553        }
3554        ast::Statement::Break(break_syntax) => {
3555            let (expr_option, ty, stable_ptr) = match break_syntax.expr_clause(syntax_db) {
3556                ast::OptionExprClause::Empty(expr_empty) => {
3557                    (None, unit_ty(db), expr_empty.stable_ptr().untyped())
3558                }
3559                ast::OptionExprClause::ExprClause(expr_clause) => {
3560                    let expr_syntax = expr_clause.expr(syntax_db);
3561                    let expr = compute_expr_semantic(ctx, &expr_syntax);
3562
3563                    (Some(expr.id), expr.ty(), expr.stable_ptr().untyped())
3564                }
3565            };
3566            let ty = ctx.reduce_ty(ty);
3567            match &mut ctx.inner_ctx {
3568                None | Some(InnerContext::Closure { .. }) => {
3569                    return Err(ctx.diagnostics.report(break_syntax, BreakOnlyAllowedInsideALoop));
3570                }
3571                Some(InnerContext::Loop { type_merger, .. }) => {
3572                    type_merger.try_merge_types(
3573                        ctx.db,
3574                        ctx.diagnostics,
3575                        &mut ctx.resolver.inference(),
3576                        ty,
3577                        stable_ptr,
3578                    );
3579                }
3580                Some(InnerContext::While | InnerContext::For) => {
3581                    if expr_option.is_some() {
3582                        ctx.diagnostics.report(break_syntax, BreakWithValueOnlyAllowedInsideALoop);
3583                    };
3584                }
3585            };
3586            semantic::Statement::Break(semantic::StatementBreak {
3587                expr_option,
3588                stable_ptr: syntax.stable_ptr(),
3589            })
3590        }
3591        ast::Statement::Item(stmt_item_syntax) => {
3592            let item_syntax = &stmt_item_syntax.item(syntax_db);
3593            match item_syntax {
3594                ast::ModuleItem::Constant(const_syntax) => {
3595                    let lhs = const_syntax.type_clause(db.upcast()).ty(db.upcast());
3596                    let rhs = const_syntax.value(db.upcast());
3597                    let rhs_expr = compute_expr_semantic(ctx, &rhs);
3598                    let explicit_type = resolve_type_with_environment(
3599                        db,
3600                        ctx.diagnostics,
3601                        &mut ctx.resolver,
3602                        &lhs,
3603                        Some(&mut ctx.environment),
3604                    );
3605                    let rhs_resolved_expr = resolve_const_expr_and_evaluate(
3606                        db,
3607                        ctx,
3608                        &rhs_expr,
3609                        stmt_item_syntax.stable_ptr().untyped(),
3610                        explicit_type,
3611                    );
3612                    let name_syntax = const_syntax.name(syntax_db);
3613                    let name = name_syntax.text(db.upcast());
3614                    let rhs_id = StatementConstLongId(
3615                        ctx.resolver.module_file_id,
3616                        const_syntax.stable_ptr(),
3617                    );
3618                    let var_def = Binding::LocalItem(LocalItem {
3619                        id: StatementItemId::Constant(rhs_id.intern(db)),
3620                        kind: StatementItemKind::Constant(
3621                            db.intern_const_value(rhs_resolved_expr.clone()),
3622                            rhs_resolved_expr.ty(db.upcast())?,
3623                        ),
3624                    });
3625                    add_item_to_statement_environment(ctx, name, var_def, &name_syntax);
3626                }
3627                ast::ModuleItem::Use(use_syntax) => {
3628                    for leaf in get_all_path_leaves(syntax_db, use_syntax) {
3629                        let stable_ptr = leaf.stable_ptr();
3630                        let segments = get_use_path_segments(syntax_db, ast::UsePath::Leaf(leaf))?;
3631                        let resolved_item = ctx.resolver.resolve_generic_path(
3632                            ctx.diagnostics,
3633                            segments,
3634                            NotFoundItemType::Identifier,
3635                            Some(&mut ctx.environment),
3636                        )?;
3637                        let var_def_id = StatementItemId::Use(
3638                            StatementUseLongId(ctx.resolver.module_file_id, stable_ptr).intern(db),
3639                        );
3640                        let name = var_def_id.name(db.upcast());
3641                        match resolved_item {
3642                            ResolvedGenericItem::GenericConstant(const_id) => {
3643                                let var_def = Binding::LocalItem(LocalItem {
3644                                    id: var_def_id,
3645                                    kind: StatementItemKind::Constant(
3646                                        db.constant_const_value(const_id)?,
3647                                        db.constant_const_type(const_id)?,
3648                                    ),
3649                                });
3650                                add_item_to_statement_environment(ctx, name, var_def, stable_ptr);
3651                            }
3652                            ResolvedGenericItem::GenericType(generic_type_id) => {
3653                                add_type_to_statement_environment(
3654                                    ctx,
3655                                    name,
3656                                    ResolvedGenericItem::GenericType(generic_type_id),
3657                                    stable_ptr,
3658                                );
3659                            }
3660                            ResolvedGenericItem::Module(_)
3661                            | ResolvedGenericItem::GenericFunction(_)
3662                            | ResolvedGenericItem::TraitFunction(_)
3663                            | ResolvedGenericItem::GenericTypeAlias(_)
3664                            | ResolvedGenericItem::GenericImplAlias(_)
3665                            | ResolvedGenericItem::Variant(_)
3666                            | ResolvedGenericItem::Trait(_)
3667                            | ResolvedGenericItem::Impl(_)
3668                            | ResolvedGenericItem::Variable(_) => {
3669                                return Err(ctx
3670                                    .diagnostics
3671                                    .report(stable_ptr, UnsupportedUseItemInStatement));
3672                            }
3673                        }
3674                    }
3675                }
3676                ast::ModuleItem::Module(_) => {
3677                    unreachable!("Modules are not supported inside a function.")
3678                }
3679                ast::ModuleItem::FreeFunction(_) => {
3680                    unreachable!("FreeFunction type not supported.")
3681                }
3682                ast::ModuleItem::ExternFunction(_) => {
3683                    unreachable!("ExternFunction type not supported.")
3684                }
3685                ast::ModuleItem::ExternType(_) => unreachable!("ExternType type not supported."),
3686                ast::ModuleItem::Trait(_) => unreachable!("Trait type not supported."),
3687                ast::ModuleItem::Impl(_) => unreachable!("Impl type not supported."),
3688                ast::ModuleItem::ImplAlias(_) => unreachable!("ImplAlias type not supported."),
3689                ast::ModuleItem::Struct(_) => unreachable!("Struct type not supported."),
3690                ast::ModuleItem::Enum(_) => unreachable!("Enum type not supported."),
3691                ast::ModuleItem::TypeAlias(_) => unreachable!("TypeAlias type not supported."),
3692                ast::ModuleItem::InlineMacro(_) => unreachable!("InlineMacro type not supported."),
3693                ast::ModuleItem::HeaderDoc(_) => unreachable!("HeaderDoc type not supported."),
3694                ast::ModuleItem::Missing(_) => unreachable!("Missing type not supported."),
3695            }
3696            semantic::Statement::Item(semantic::StatementItem { stable_ptr: syntax.stable_ptr() })
3697        }
3698        ast::Statement::Missing(_) => todo!(),
3699    };
3700    ctx.resolver.data.feature_config.restore(feature_restore);
3701    Ok(ctx.arenas.statements.alloc(statement))
3702}
3703
3704/// Adds an item to the statement environment and reports a diagnostic if the item is already
3705/// defined.
3706fn add_item_to_statement_environment(
3707    ctx: &mut ComputationContext<'_>,
3708    name: SmolStr,
3709    var_def: Binding,
3710    stable_ptr: impl Into<SyntaxStablePtrId>,
3711) {
3712    if let Some(old_var) = ctx.environment.variables.insert(name.clone(), var_def.clone()) {
3713        ctx.diagnostics.report(stable_ptr, match old_var {
3714            Binding::LocalItem(_) => MultipleConstantDefinition(name),
3715            Binding::LocalVar(_) | Binding::Param(_) => MultipleDefinitionforBinding(name),
3716        });
3717    }
3718    ctx.semantic_defs.insert(var_def.id(), var_def);
3719}
3720
3721/// Adds a type to the statement environment and reports a diagnostic if the type is already
3722/// defined.
3723fn add_type_to_statement_environment(
3724    ctx: &mut ComputationContext<'_>,
3725    name: SmolStr,
3726    resolved_generic_item: ResolvedGenericItem,
3727    stable_ptr: impl Into<SyntaxStablePtrId> + std::marker::Copy,
3728) {
3729    if ctx
3730        .environment
3731        .use_items
3732        .insert(name.clone(), StatementGenericItemData {
3733            resolved_generic_item,
3734            stable_ptr: stable_ptr.into(),
3735        })
3736        .is_some()
3737    {
3738        ctx.diagnostics.report(stable_ptr, MultipleGenericItemDefinition(name));
3739    }
3740}
3741
3742/// Computes the semantic model of an expression and reports diagnostics if the expression does not
3743/// evaluate to a boolean value.
3744fn compute_bool_condition_semantic(
3745    ctx: &mut ComputationContext<'_>,
3746    condition_syntax: &ast::Expr,
3747) -> ExprAndId {
3748    let condition = compute_expr_semantic(ctx, condition_syntax);
3749    let inference = &mut ctx.resolver.inference();
3750    if let Err((err_set, condition_ty, _)) =
3751        inference.conform_ty_for_diag(condition.ty(), core_bool_ty(ctx.db))
3752    {
3753        let diag_added = ctx.diagnostics.report(condition.deref(), ConditionNotBool(condition_ty));
3754        inference.consume_reported_error(err_set, diag_added);
3755    }
3756    condition
3757}
3758
3759/// Validates a struct member is visible and otherwise adds a diagnostic.
3760fn check_struct_member_is_visible(
3761    ctx: &mut ComputationContext<'_>,
3762    member: &Member,
3763    stable_ptr: SyntaxStablePtrId,
3764    member_name: &SmolStr,
3765) {
3766    let db = ctx.db.upcast();
3767    let containing_module_id = member.id.parent_module(db);
3768    if ctx.resolver.ignore_visibility_checks(containing_module_id) {
3769        return;
3770    }
3771    let user_module_id = ctx.resolver.module_file_id.0;
3772    if !visibility::peek_visible_in(db, member.visibility, containing_module_id, user_module_id) {
3773        ctx.diagnostics.report(stable_ptr, MemberNotVisible(member_name.clone()));
3774    }
3775}
3776
3777/// Verifies that the statement attributes are valid statements attributes, if not a diagnostic is
3778/// reported.
3779fn validate_statement_attributes(ctx: &mut ComputationContext<'_>, syntax: &ast::Statement) {
3780    let allowed_attributes = ctx.db.allowed_statement_attributes();
3781    let mut diagnostics = vec![];
3782    validate_attributes_flat(ctx.db.upcast(), &allowed_attributes, syntax, &mut diagnostics);
3783    // Translate the plugin diagnostics to semantic diagnostics.
3784    for diagnostic in diagnostics {
3785        ctx.diagnostics
3786            .report(diagnostic.stable_ptr, SemanticDiagnosticKind::UnknownStatementAttribute);
3787    }
3788}
3789
3790/// Gets an iterator with the types of the parameters of the given function.
3791fn function_parameter_types(
3792    ctx: &mut ComputationContext<'_>,
3793    function: FunctionId,
3794) -> Maybe<impl Iterator<Item = TypeId>> {
3795    let signature = ctx.db.concrete_function_signature(function)?;
3796    let param_types = signature.params.into_iter().map(|param| param.ty);
3797    Ok(param_types)
3798}