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