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    if !ctx.reduce_ty(expr.ty()).is_var_free(ctx.db) {
1958        // Make sure the maximal amount of types is known when trying to access. Ignoring the
1959        // returned value, as any errors will be reported later.
1960        ctx.resolver.inference().solve().ok();
1961    }
1962    let info = ctx.db.core_info();
1963    let candidate_traits = [info.index_trt, info.index_view_trt];
1964    let (function_id, _, fixed_expr, mutability) = compute_method_function_call_data(
1965        ctx,
1966        &candidate_traits[..],
1967        "index".into(),
1968        expr,
1969        syntax.into(),
1970        None,
1971        |ty, _, inference_errors| Some(NoImplementationOfIndexOperator { ty, inference_errors }),
1972        |ty, _, _| Some(MultipleImplementationOfIndexOperator(ty)),
1973    )?;
1974
1975    expr_function_call(
1976        ctx,
1977        function_id,
1978        vec![
1979            NamedArg(fixed_expr, None, mutability),
1980            NamedArg(index_expr, None, Mutability::Immutable),
1981        ],
1982        syntax,
1983        index_expr_syntax.stable_ptr(),
1984    )
1985}
1986
1987/// Computes the data needed for a method function call, and similar exprs (index operator). Method
1988/// call and Index operator differs in the diagnostics they emit. The function returns the
1989/// function_id to call, the trait containing the function, the self argument, with snapshots added
1990/// if needed, and the mutability of the self argument.
1991#[expect(clippy::too_many_arguments)]
1992fn compute_method_function_call_data(
1993    ctx: &mut ComputationContext<'_>,
1994    candidate_traits: &[TraitId],
1995    func_name: SmolStr,
1996    self_expr: ExprAndId,
1997    method_syntax: SyntaxStablePtrId,
1998    generic_args_syntax: Option<Vec<ast::GenericArg>>,
1999    no_implementation_diagnostic: impl Fn(
2000        TypeId,
2001        SmolStr,
2002        TraitInferenceErrors,
2003    ) -> Option<SemanticDiagnosticKind>,
2004    multiple_trait_diagnostic: fn(
2005        TypeId,
2006        TraitFunctionId,
2007        TraitFunctionId,
2008    ) -> Option<SemanticDiagnosticKind>,
2009) -> Maybe<(FunctionId, TraitId, ExprAndId, Mutability)> {
2010    let expr_ptr = self_expr.stable_ptr();
2011    let self_ty = ctx.reduce_ty(self_expr.ty());
2012    // Inference errors found when looking for candidates. Only relevant in the case of 0 candidates
2013    // found. If >0 candidates are found these are ignored as they may describe, e.g., "errors"
2014    // indicating certain traits/impls/functions don't match, which is OK as we only look for one.
2015    let mut inference_errors = vec![];
2016    let (candidates, mut fixed_expr, fixed_ty) = get_method_function_candidates(
2017        ctx,
2018        candidate_traits,
2019        &func_name,
2020        self_expr,
2021        method_syntax,
2022        expr_ptr,
2023        self_ty,
2024        &mut inference_errors,
2025    )?;
2026
2027    let trait_function_id = match candidates[..] {
2028        [] => {
2029            return Err(no_implementation_diagnostic(
2030                self_ty,
2031                func_name,
2032                TraitInferenceErrors { traits_and_errors: inference_errors },
2033            )
2034            .map(|diag| ctx.diagnostics.report(method_syntax, diag))
2035            .unwrap_or_else(skip_diagnostic));
2036        }
2037        [trait_function_id] => trait_function_id,
2038        [trait_function_id0, trait_function_id1, ..] => {
2039            return Err(multiple_trait_diagnostic(
2040                fixed_ty,
2041                trait_function_id0,
2042                trait_function_id1,
2043            )
2044            .map(|diag| ctx.diagnostics.report(method_syntax, diag))
2045            .unwrap_or_else(skip_diagnostic));
2046        }
2047    };
2048    let (function_id, n_snapshots) =
2049        infer_impl_by_self(ctx, trait_function_id, fixed_ty, method_syntax, generic_args_syntax)?;
2050
2051    let signature = ctx.db.trait_function_signature(trait_function_id).unwrap();
2052    let first_param = signature.params.into_iter().next().unwrap();
2053    for _ in 0..n_snapshots {
2054        let ty = TypeLongId::Snapshot(fixed_expr.ty()).intern(ctx.db);
2055        let expr = Expr::Snapshot(ExprSnapshot { inner: fixed_expr.id, ty, stable_ptr: expr_ptr });
2056        fixed_expr = ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) };
2057    }
2058
2059    Ok((
2060        function_id,
2061        trait_function_id.trait_id(ctx.db.upcast()),
2062        fixed_expr,
2063        first_param.mutability,
2064    ))
2065}
2066
2067/// Return candidates for method functions that match the given arguments.
2068/// Also returns the expression to be used as self for the method call and its type.
2069#[expect(clippy::too_many_arguments)]
2070fn get_method_function_candidates(
2071    ctx: &mut ComputationContext<'_>,
2072    candidate_traits: &[TraitId],
2073    func_name: &SmolStr,
2074    self_expr: ExprAndId,
2075    method_syntax: SyntaxStablePtrId,
2076    expr_ptr: ExprPtr,
2077    self_ty: TypeId,
2078    inference_errors: &mut Vec<(TraitFunctionId, InferenceError)>,
2079) -> Result<(Vec<TraitFunctionId>, ExprAndId, TypeId), cairo_lang_diagnostics::DiagnosticAdded> {
2080    let mut candidates = filter_candidate_traits(
2081        ctx,
2082        inference_errors,
2083        self_ty,
2084        candidate_traits,
2085        func_name.clone(),
2086        method_syntax,
2087    );
2088    if !candidates.is_empty() {
2089        return Ok((candidates, self_expr, self_ty));
2090    }
2091
2092    let mut fixed_expr = self_expr;
2093    let mut fixed_ty = self_ty;
2094
2095    let base_var = match &fixed_expr.expr {
2096        Expr::Var(expr_var) => Some(expr_var.var),
2097        Expr::MemberAccess(ExprMemberAccess { member_path: Some(member_path), .. }) => {
2098            Some(member_path.base_var())
2099        }
2100        _ => None,
2101    };
2102    let is_mut_var = base_var
2103        .filter(|var_id| matches!(ctx.semantic_defs.get(var_id), Some(var) if var.is_mut()))
2104        .is_some();
2105
2106    let deref_chain = ctx.db.deref_chain(self_ty, is_mut_var)?;
2107
2108    for deref_info in deref_chain.derefs.iter() {
2109        let derefed_expr = expr_function_call(
2110            ctx,
2111            deref_info.function_id,
2112            vec![NamedArg(fixed_expr, None, deref_info.self_mutability)],
2113            method_syntax,
2114            expr_ptr,
2115        )?;
2116
2117        fixed_expr =
2118            ExprAndId { expr: derefed_expr.clone(), id: ctx.arenas.exprs.alloc(derefed_expr) };
2119
2120        candidates = filter_candidate_traits(
2121            ctx,
2122            inference_errors,
2123            deref_info.target_ty,
2124            candidate_traits,
2125            func_name.clone(),
2126            method_syntax,
2127        );
2128        if !candidates.is_empty() {
2129            fixed_ty = deref_info.target_ty;
2130            break;
2131        }
2132    }
2133
2134    Ok((candidates, fixed_expr, fixed_ty))
2135}
2136
2137/// Computes the semantic model of a pattern.
2138/// Note that this pattern will always be "registered" in the arena, so it can be looked up in the
2139/// language server.
2140pub fn compute_pattern_semantic(
2141    ctx: &mut ComputationContext<'_>,
2142    syntax: &ast::Pattern,
2143    ty: TypeId,
2144    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2145) -> PatternAndId {
2146    let pat = maybe_compute_pattern_semantic(ctx, syntax, ty, or_pattern_variables_map);
2147    let pat = pat.unwrap_or_else(|diag_added| {
2148        Pattern::Missing(PatternMissing {
2149            ty: TypeId::missing(ctx.db, diag_added),
2150            stable_ptr: syntax.stable_ptr(),
2151            diag_added,
2152        })
2153    });
2154    let id = ctx.arenas.patterns.alloc(pat.clone());
2155    PatternAndId { pattern: pat, id }
2156}
2157
2158/// Computes the semantic model of a pattern, or None if invalid.
2159fn maybe_compute_pattern_semantic(
2160    ctx: &mut ComputationContext<'_>,
2161    pattern_syntax: &ast::Pattern,
2162    ty: TypeId,
2163    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2164) -> Maybe<Pattern> {
2165    // TODO(spapini): Check for missing type, and don't reemit an error.
2166    let syntax_db = ctx.db.upcast();
2167    let ty = ctx.reduce_ty(ty);
2168    let stable_ptr = pattern_syntax.into();
2169    let pattern = match pattern_syntax {
2170        ast::Pattern::Underscore(otherwise_pattern) => {
2171            Pattern::Otherwise(PatternOtherwise { ty, stable_ptr: otherwise_pattern.stable_ptr() })
2172        }
2173        ast::Pattern::Literal(literal_pattern) => {
2174            let literal = literal_to_semantic(ctx, literal_pattern)?;
2175            Pattern::Literal(PatternLiteral {
2176                literal,
2177                stable_ptr: literal_pattern.stable_ptr().into(),
2178            })
2179        }
2180        ast::Pattern::ShortString(short_string_pattern) => {
2181            let literal = short_string_to_semantic(ctx, short_string_pattern)?;
2182            Pattern::Literal(PatternLiteral {
2183                literal,
2184                stable_ptr: short_string_pattern.stable_ptr().into(),
2185            })
2186        }
2187        ast::Pattern::String(string_pattern) => {
2188            let string_literal = string_literal_to_semantic(ctx, string_pattern)?;
2189            Pattern::StringLiteral(PatternStringLiteral {
2190                string_literal,
2191                stable_ptr: string_pattern.stable_ptr().into(),
2192            })
2193        }
2194        ast::Pattern::Enum(enum_pattern) => {
2195            let path = enum_pattern.path(syntax_db);
2196            let item = ctx.resolver.resolve_generic_path(
2197                ctx.diagnostics,
2198                &path,
2199                NotFoundItemType::Identifier,
2200                Some(&mut ctx.environment),
2201            )?;
2202            let generic_variant = try_extract_matches!(item, ResolvedGenericItem::Variant)
2203                .ok_or_else(|| ctx.diagnostics.report(&path, NotAVariant))?;
2204
2205            let (concrete_enum, n_snapshots) = extract_concrete_enum_from_pattern_and_validate(
2206                ctx,
2207                pattern_syntax,
2208                ty,
2209                generic_variant.enum_id,
2210            )?;
2211
2212            // TODO(lior): Should we report a diagnostic here?
2213            let concrete_variant = ctx
2214                .db
2215                .concrete_enum_variant(concrete_enum, &generic_variant)
2216                .map_err(|_| ctx.diagnostics.report(&path, UnknownEnum))?;
2217
2218            // Compute inner pattern.
2219            let inner_ty = wrap_in_snapshots(ctx.db, concrete_variant.ty, n_snapshots);
2220
2221            let inner_pattern = match enum_pattern.pattern(syntax_db) {
2222                ast::OptionPatternEnumInnerPattern::Empty(_) => None,
2223                ast::OptionPatternEnumInnerPattern::PatternEnumInnerPattern(p) => {
2224                    let pattern = compute_pattern_semantic(
2225                        ctx,
2226                        &p.pattern(syntax_db),
2227                        inner_ty,
2228                        or_pattern_variables_map,
2229                    );
2230                    Some(pattern.id)
2231                }
2232            };
2233
2234            Pattern::EnumVariant(PatternEnumVariant {
2235                variant: concrete_variant,
2236                inner_pattern,
2237                ty,
2238                stable_ptr: enum_pattern.stable_ptr().into(),
2239            })
2240        }
2241        ast::Pattern::Path(path) => {
2242            let item_result = ctx.resolver.resolve_generic_path(
2243                &mut Default::default(),
2244                path,
2245                NotFoundItemType::Identifier,
2246                Some(&mut ctx.environment),
2247            );
2248            if let Ok(item) = item_result {
2249                if let Some(generic_variant) =
2250                    try_extract_matches!(item, ResolvedGenericItem::Variant)
2251                {
2252                    let (concrete_enum, _n_snapshots) =
2253                        extract_concrete_enum_from_pattern_and_validate(
2254                            ctx,
2255                            pattern_syntax,
2256                            ty,
2257                            generic_variant.enum_id,
2258                        )?;
2259                    let concrete_variant = ctx
2260                        .db
2261                        .concrete_enum_variant(concrete_enum, &generic_variant)
2262                        .map_err(|_| ctx.diagnostics.report(path, UnknownEnum))?;
2263                    return Ok(Pattern::EnumVariant(PatternEnumVariant {
2264                        variant: concrete_variant,
2265                        inner_pattern: None,
2266                        ty,
2267                        stable_ptr: path.stable_ptr().into(),
2268                    }));
2269                }
2270            }
2271
2272            // Paths with a single element are treated as identifiers, which will result in a
2273            // variable pattern if no matching enum variant is found. If a matching enum
2274            // variant exists, it is resolved to the corresponding concrete variant.
2275            if path.elements(syntax_db).len() > 1 {
2276                return Err(ctx.diagnostics.report(path, Unsupported));
2277            }
2278            // TODO(spapini): Make sure this is a simple identifier. In particular, no generics.
2279            let identifier = path.elements(syntax_db)[0].identifier_ast(syntax_db);
2280            create_variable_pattern(
2281                ctx,
2282                identifier,
2283                &[],
2284                ty,
2285                path.stable_ptr().into(),
2286                or_pattern_variables_map,
2287            )
2288        }
2289        ast::Pattern::Identifier(identifier) => create_variable_pattern(
2290            ctx,
2291            identifier.name(syntax_db),
2292            &identifier.modifiers(syntax_db).elements(syntax_db),
2293            ty,
2294            identifier.stable_ptr().into(),
2295            or_pattern_variables_map,
2296        ),
2297        ast::Pattern::Struct(pattern_struct) => {
2298            let pattern_ty = try_extract_matches!(
2299                ctx.resolver.resolve_concrete_path_ex(
2300                    ctx.diagnostics,
2301                    &pattern_struct.path(syntax_db),
2302                    NotFoundItemType::Type,
2303                    Some(&mut ctx.environment)
2304                )?,
2305                ResolvedConcreteItem::Type
2306            )
2307            .ok_or_else(|| ctx.diagnostics.report(&pattern_struct.path(syntax_db), NotAType))?;
2308            let inference = &mut ctx.resolver.inference();
2309            inference.conform_ty(pattern_ty, peel_snapshots(ctx.db, ty).1.intern(ctx.db)).map_err(
2310                |err_set| inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr),
2311            )?;
2312            let ty = ctx.reduce_ty(ty);
2313            // Peel all snapshot wrappers.
2314            let (n_snapshots, long_ty) = peel_snapshots(ctx.db, ty);
2315
2316            // Check that type is an struct, and get the concrete struct from it.
2317            let concrete_struct_id = try_extract_matches!(long_ty, TypeLongId::Concrete)
2318                .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2319                .ok_or(())
2320                .or_else(|_| {
2321                    // Don't add a diagnostic if the type is missing.
2322                    // A diagnostic should've already been added.
2323                    ty.check_not_missing(ctx.db)?;
2324                    Err(ctx.diagnostics.report(pattern_struct, UnexpectedStructPattern(ty)))
2325                })?;
2326            let pattern_param_asts = pattern_struct.params(syntax_db).elements(syntax_db);
2327            let struct_id = concrete_struct_id.struct_id(ctx.db);
2328            let mut members = ctx.db.concrete_struct_members(concrete_struct_id)?.as_ref().clone();
2329            let mut used_members = UnorderedHashSet::<_>::default();
2330            let mut get_member = |ctx: &mut ComputationContext<'_>,
2331                                  member_name: SmolStr,
2332                                  stable_ptr: SyntaxStablePtrId| {
2333                let member = members.swap_remove(&member_name).on_none(|| {
2334                    ctx.diagnostics.report(
2335                        stable_ptr,
2336                        if used_members.contains(&member_name) {
2337                            StructMemberRedefinition { struct_id, member_name: member_name.clone() }
2338                        } else {
2339                            NoSuchStructMember { struct_id, member_name: member_name.clone() }
2340                        },
2341                    );
2342                })?;
2343                check_struct_member_is_visible(ctx, &member, stable_ptr, &member_name);
2344                used_members.insert(member_name);
2345                Some(member)
2346            };
2347            let mut field_patterns = vec![];
2348            let mut has_tail = false;
2349            for pattern_param_ast in pattern_param_asts {
2350                match pattern_param_ast {
2351                    PatternStructParam::Single(single) => {
2352                        let name = single.name(syntax_db);
2353                        let Some(member) =
2354                            get_member(ctx, name.text(syntax_db), name.stable_ptr().untyped())
2355                        else {
2356                            continue;
2357                        };
2358                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2359                        let pattern = create_variable_pattern(
2360                            ctx,
2361                            name,
2362                            &single.modifiers(syntax_db).elements(syntax_db),
2363                            ty,
2364                            single.stable_ptr().into(),
2365                            or_pattern_variables_map,
2366                        );
2367                        field_patterns.push((member, ctx.arenas.patterns.alloc(pattern)));
2368                    }
2369                    PatternStructParam::WithExpr(with_expr) => {
2370                        let name = with_expr.name(syntax_db);
2371                        let Some(member) =
2372                            get_member(ctx, name.text(syntax_db), name.stable_ptr().untyped())
2373                        else {
2374                            continue;
2375                        };
2376                        let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
2377                        let pattern = compute_pattern_semantic(
2378                            ctx,
2379                            &with_expr.pattern(syntax_db),
2380                            ty,
2381                            or_pattern_variables_map,
2382                        );
2383                        field_patterns.push((member, pattern.id));
2384                    }
2385                    PatternStructParam::Tail(_) => {
2386                        has_tail = true;
2387                    }
2388                }
2389            }
2390            if !has_tail {
2391                for (member_name, _) in members.iter() {
2392                    ctx.diagnostics.report(pattern_struct, MissingMember(member_name.clone()));
2393                }
2394            }
2395            Pattern::Struct(PatternStruct {
2396                concrete_struct_id,
2397                field_patterns,
2398                ty,
2399                n_snapshots,
2400                stable_ptr: pattern_struct.stable_ptr(),
2401            })
2402        }
2403        ast::Pattern::Tuple(_) => maybe_compute_tuple_like_pattern_semantic(
2404            ctx,
2405            pattern_syntax,
2406            ty,
2407            or_pattern_variables_map,
2408            |ty: TypeId| UnexpectedTuplePattern(ty),
2409            |expected, actual| WrongNumberOfTupleElements { expected, actual },
2410        )?,
2411        ast::Pattern::FixedSizeArray(_) => maybe_compute_tuple_like_pattern_semantic(
2412            ctx,
2413            pattern_syntax,
2414            ty,
2415            or_pattern_variables_map,
2416            |ty: TypeId| UnexpectedFixedSizeArrayPattern(ty),
2417            |expected, actual| WrongNumberOfFixedSizeArrayElements { expected, actual },
2418        )?,
2419        ast::Pattern::False(pattern_false) => {
2420            let enum_expr = extract_matches!(
2421                false_literal_expr(ctx, pattern_false.stable_ptr().into()),
2422                Expr::EnumVariantCtor
2423            );
2424
2425            extract_concrete_enum_from_pattern_and_validate(
2426                ctx,
2427                pattern_syntax,
2428                ty,
2429                enum_expr.variant.concrete_enum_id.enum_id(ctx.db),
2430            )?;
2431
2432            Pattern::EnumVariant(PatternEnumVariant {
2433                variant: enum_expr.variant,
2434                stable_ptr: pattern_false.stable_ptr().into(),
2435                ty,
2436                inner_pattern: None,
2437            })
2438        }
2439        ast::Pattern::True(pattern_true) => {
2440            let enum_expr = extract_matches!(
2441                true_literal_expr(ctx, pattern_true.stable_ptr().into()),
2442                Expr::EnumVariantCtor
2443            );
2444            extract_concrete_enum_from_pattern_and_validate(
2445                ctx,
2446                pattern_syntax,
2447                ty,
2448                enum_expr.variant.concrete_enum_id.enum_id(ctx.db),
2449            )?;
2450
2451            Pattern::EnumVariant(PatternEnumVariant {
2452                variant: enum_expr.variant,
2453                stable_ptr: pattern_true.stable_ptr().into(),
2454                ty,
2455                inner_pattern: None,
2456            })
2457        }
2458    };
2459    let inference = &mut ctx.resolver.inference();
2460    inference.conform_ty(pattern.ty(), ty).map_err(|err_set| {
2461        inference.report_on_pending_error(err_set, ctx.diagnostics, stable_ptr)
2462    })?;
2463    Ok(pattern)
2464}
2465
2466/// Computes the semantic model of a pattern of a tuple or a fixed size array. Assumes that the
2467/// pattern is one of these types.
2468fn maybe_compute_tuple_like_pattern_semantic(
2469    ctx: &mut ComputationContext<'_>,
2470    pattern_syntax: &ast::Pattern,
2471    ty: TypeId,
2472    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2473    unexpected_pattern: fn(TypeId) -> SemanticDiagnosticKind,
2474    wrong_number_of_elements: fn(usize, usize) -> SemanticDiagnosticKind,
2475) -> Maybe<Pattern> {
2476    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern_syntax)?;
2477    // Assert that the pattern is of the same type as the expr.
2478    match (pattern_syntax, &long_ty) {
2479        (ast::Pattern::Tuple(_), TypeLongId::Tuple(_) | TypeLongId::Var(_))
2480        | (
2481            ast::Pattern::FixedSizeArray(_),
2482            TypeLongId::FixedSizeArray { .. } | TypeLongId::Var(_),
2483        ) => {}
2484        _ => {
2485            return Err(ctx.diagnostics.report(pattern_syntax, unexpected_pattern(ty)));
2486        }
2487    };
2488    let patterns_syntax = match pattern_syntax {
2489        ast::Pattern::Tuple(pattern_tuple) => {
2490            pattern_tuple.patterns(ctx.db.upcast()).elements(ctx.db.upcast())
2491        }
2492        ast::Pattern::FixedSizeArray(pattern_fixed_size_array) => {
2493            pattern_fixed_size_array.patterns(ctx.db.upcast()).elements(ctx.db.upcast())
2494        }
2495        _ => unreachable!(),
2496    };
2497    let inner_tys = match long_ty {
2498        TypeLongId::Tuple(inner_tys) => inner_tys,
2499        TypeLongId::FixedSizeArray { type_id: inner_ty, size } => {
2500            let size = if let ConstValue::Int(value, _) = size.lookup_intern(ctx.db) {
2501                value.to_usize().expect("Fixed sized array size must always be usize.")
2502            } else {
2503                let inference = &mut ctx.resolver.inference();
2504                let expected_size =
2505                    ConstValue::Int(patterns_syntax.len().into(), get_usize_ty(ctx.db))
2506                        .intern(ctx.db);
2507                if let Err(err) = inference.conform_const(size, expected_size) {
2508                    let _ = inference.report_on_pending_error(
2509                        err,
2510                        ctx.diagnostics,
2511                        pattern_syntax.stable_ptr().untyped(),
2512                    );
2513                }
2514                patterns_syntax.len()
2515            };
2516
2517            [inner_ty].repeat(size)
2518        }
2519        TypeLongId::Var(_) => {
2520            let inference = &mut ctx.resolver.inference();
2521            let (inner_tys, tuple_like_ty) = if matches!(pattern_syntax, ast::Pattern::Tuple(_)) {
2522                let inner_tys: Vec<_> = patterns_syntax
2523                    .iter()
2524                    .map(|e| inference.new_type_var(Some(e.stable_ptr().untyped())))
2525                    .collect();
2526                (inner_tys.clone(), TypeLongId::Tuple(inner_tys))
2527            } else {
2528                let var = inference.new_type_var(Some(pattern_syntax.stable_ptr().untyped()));
2529                (
2530                    vec![var; patterns_syntax.len()],
2531                    TypeLongId::FixedSizeArray {
2532                        type_id: var,
2533                        size: ConstValue::Int(patterns_syntax.len().into(), get_usize_ty(ctx.db))
2534                            .intern(ctx.db),
2535                    },
2536                )
2537            };
2538            match inference.conform_ty(ty, tuple_like_ty.intern(ctx.db)) {
2539                Ok(_) => {}
2540                Err(_) => unreachable!("As the type is a var, conforming should always succeed."),
2541            }
2542            inner_tys
2543        }
2544        _ => unreachable!(),
2545    };
2546    let size = inner_tys.len();
2547    if size != patterns_syntax.len() {
2548        return Err(ctx
2549            .diagnostics
2550            .report(pattern_syntax, wrong_number_of_elements(size, patterns_syntax.len())));
2551    }
2552    let pattern_options = zip_eq(patterns_syntax, inner_tys).map(|(pattern_ast, ty)| {
2553        let ty = wrap_in_snapshots(ctx.db, ty, n_snapshots);
2554        let pattern = compute_pattern_semantic(ctx, &pattern_ast, ty, or_pattern_variables_map);
2555        Ok(pattern.id)
2556    });
2557    // If all are Some, collect into a Vec.
2558    let field_patterns: Vec<_> = pattern_options.collect::<Maybe<_>>()?;
2559    Ok(match pattern_syntax {
2560        ast::Pattern::Tuple(syntax) => {
2561            Pattern::Tuple(PatternTuple { field_patterns, ty, stable_ptr: syntax.stable_ptr() })
2562        }
2563        ast::Pattern::FixedSizeArray(syntax) => Pattern::FixedSizeArray(PatternFixedSizeArray {
2564            elements_patterns: field_patterns,
2565            ty,
2566            stable_ptr: syntax.stable_ptr(),
2567        }),
2568        _ => unreachable!(),
2569    })
2570}
2571
2572/// Validates that the semantic type of an enum pattern is an enum, and returns the concrete enum.
2573fn extract_concrete_enum_from_pattern_and_validate(
2574    ctx: &mut ComputationContext<'_>,
2575    pattern: &ast::Pattern,
2576    ty: TypeId,
2577    enum_id: EnumId,
2578) -> Maybe<(ConcreteEnumId, usize)> {
2579    // Peel all snapshot wrappers.
2580    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, pattern)?;
2581
2582    // Check that type is an enum, and get the concrete enum from it.
2583    let concrete_enum = try_extract_matches!(long_ty, TypeLongId::Concrete)
2584        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Enum))
2585        .ok_or(())
2586        .or_else(|_| {
2587            // Don't add a diagnostic if the type is missing.
2588            // A diagnostic should've already been added.
2589            ty.check_not_missing(ctx.db)?;
2590            Err(ctx.diagnostics.report(pattern, UnexpectedEnumPattern(ty)))
2591        })?;
2592    // Check that these are the same enums.
2593    if enum_id != concrete_enum.enum_id(ctx.db) {
2594        return Err(ctx.diagnostics.report(
2595            pattern,
2596            WrongEnum { expected_enum: concrete_enum.enum_id(ctx.db), actual_enum: enum_id },
2597        ));
2598    }
2599    Ok((concrete_enum, n_snapshots))
2600}
2601
2602/// Creates a local variable pattern.
2603fn create_variable_pattern(
2604    ctx: &mut ComputationContext<'_>,
2605    identifier: ast::TerminalIdentifier,
2606    modifier_list: &[ast::Modifier],
2607    ty: TypeId,
2608    stable_ptr: ast::PatternPtr,
2609    or_pattern_variables_map: &mut UnorderedHashMap<SmolStr, LocalVariable>,
2610) -> Pattern {
2611    let syntax_db = ctx.db.upcast();
2612
2613    let var_id = match or_pattern_variables_map.get(&identifier.text(syntax_db)) {
2614        Some(var) => var.id,
2615        None => LocalVarLongId(ctx.resolver.module_file_id, identifier.stable_ptr()).intern(ctx.db),
2616    };
2617    let is_mut = match compute_mutability(ctx.diagnostics, syntax_db, modifier_list) {
2618        Mutability::Immutable => false,
2619        Mutability::Mutable => true,
2620        Mutability::Reference => {
2621            ctx.diagnostics.report(&identifier, ReferenceLocalVariable);
2622            false
2623        }
2624    };
2625    Pattern::Variable(PatternVariable {
2626        name: identifier.text(syntax_db),
2627        var: LocalVariable { id: var_id, ty, is_mut },
2628        stable_ptr,
2629    })
2630}
2631
2632/// Creates a struct constructor semantic expression from its AST.
2633fn struct_ctor_expr(
2634    ctx: &mut ComputationContext<'_>,
2635    ctor_syntax: &ast::ExprStructCtorCall,
2636) -> Maybe<Expr> {
2637    let db = ctx.db;
2638    let syntax_db = db.upcast();
2639    let path = ctor_syntax.path(syntax_db);
2640
2641    // Extract struct.
2642    let ty = resolve_type_with_environment(
2643        db,
2644        ctx.diagnostics,
2645        &mut ctx.resolver,
2646        &ast::Expr::Path(path.clone()),
2647        Some(&mut ctx.environment),
2648    );
2649    ty.check_not_missing(db)?;
2650
2651    let concrete_struct_id = try_extract_matches!(ty.lookup_intern(ctx.db), TypeLongId::Concrete)
2652        .and_then(|c| try_extract_matches!(c, ConcreteTypeId::Struct))
2653        .ok_or_else(|| ctx.diagnostics.report(&path, NotAStruct))?;
2654
2655    if ty.is_phantom(db) {
2656        ctx.diagnostics.report(ctor_syntax, CannotCreateInstancesOfPhantomTypes);
2657    }
2658
2659    let members = db.concrete_struct_members(concrete_struct_id)?;
2660    let mut member_exprs: OrderedHashMap<MemberId, Option<ExprId>> = OrderedHashMap::default();
2661    let mut base_struct = None;
2662
2663    for (index, arg) in ctor_syntax
2664        .arguments(syntax_db)
2665        .arguments(syntax_db)
2666        .elements(syntax_db)
2667        .into_iter()
2668        .enumerate()
2669    {
2670        // TODO: Extract to a function for results.
2671        match arg {
2672            ast::StructArg::StructArgSingle(arg) => {
2673                let arg_identifier = arg.identifier(syntax_db);
2674                let arg_name = arg_identifier.text(syntax_db);
2675
2676                // Find struct member by name.
2677                let Some(member) = members.get(&arg_name) else {
2678                    ctx.diagnostics.report(&arg_identifier, UnknownMember);
2679                    continue;
2680                };
2681                check_struct_member_is_visible(
2682                    ctx,
2683                    member,
2684                    arg_identifier.stable_ptr().untyped(),
2685                    &arg_name,
2686                );
2687
2688                // Extract expression.
2689                let arg_expr = match arg.arg_expr(syntax_db) {
2690                    ast::OptionStructArgExpr::Empty(_) => {
2691                        let Ok(expr) = resolve_variable_by_name(
2692                            ctx,
2693                            &arg_identifier,
2694                            path.stable_ptr().into(),
2695                        ) else {
2696                            // Insert only the member id, for correct duplicate member reporting.
2697                            if member_exprs.insert(member.id, None).is_some() {
2698                                ctx.diagnostics
2699                                    .report(&arg_identifier, MemberSpecifiedMoreThanOnce);
2700                            }
2701                            continue;
2702                        };
2703                        ExprAndId { expr: expr.clone(), id: ctx.arenas.exprs.alloc(expr) }
2704                    }
2705                    ast::OptionStructArgExpr::StructArgExpr(arg_expr) => {
2706                        compute_expr_semantic(ctx, &arg_expr.expr(syntax_db))
2707                    }
2708                };
2709
2710                // Insert and check for duplicates.
2711                if member_exprs.insert(member.id, Some(arg_expr.id)).is_some() {
2712                    ctx.diagnostics.report(&arg_identifier, MemberSpecifiedMoreThanOnce);
2713                }
2714
2715                // Check types.
2716                let inference = &mut ctx.resolver.inference();
2717                if inference
2718                    .conform_ty_for_diag(
2719                        arg_expr.ty(),
2720                        member.ty,
2721                        ctx.diagnostics,
2722                        || arg_identifier.stable_ptr().untyped(),
2723                        |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
2724                    )
2725                    .is_err()
2726                {
2727                    continue;
2728                }
2729            }
2730            ast::StructArg::StructArgTail(base_struct_syntax) => {
2731                // TODO(TomerStarkware): remove tail expression from argument list.
2732                if index
2733                    != ctor_syntax
2734                        .arguments(syntax_db)
2735                        .arguments(syntax_db)
2736                        .elements(syntax_db)
2737                        .len()
2738                        - 1
2739                {
2740                    ctx.diagnostics.report(&base_struct_syntax, StructBaseStructExpressionNotLast);
2741                    continue;
2742                }
2743                let base_struct_expr =
2744                    compute_expr_semantic(ctx, &base_struct_syntax.expression(syntax_db));
2745                let inference = &mut ctx.resolver.inference();
2746                if inference
2747                    .conform_ty_for_diag(
2748                        base_struct_expr.ty(),
2749                        ty,
2750                        ctx.diagnostics,
2751                        || base_struct_syntax.expression(syntax_db).stable_ptr().untyped(),
2752                        |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
2753                    )
2754                    .is_err()
2755                {
2756                    continue;
2757                }
2758
2759                base_struct = Some((base_struct_expr.id, base_struct_syntax));
2760            }
2761        };
2762    }
2763
2764    // Report errors for missing members.
2765    for (member_name, member) in members.iter() {
2766        if !member_exprs.contains_key(&member.id) {
2767            if base_struct.is_some() {
2768                check_struct_member_is_visible(
2769                    ctx,
2770                    member,
2771                    base_struct.clone().unwrap().1.stable_ptr().untyped(),
2772                    member_name,
2773                );
2774            } else {
2775                ctx.diagnostics.report(ctor_syntax, MissingMember(member_name.clone()));
2776            }
2777        }
2778    }
2779    if members.len() == member_exprs.len() {
2780        if let Some((_, base_struct_syntax)) = base_struct {
2781            return Err(ctx
2782                .diagnostics
2783                .report(&base_struct_syntax, StructBaseStructExpressionNoEffect));
2784        }
2785    }
2786    Ok(Expr::StructCtor(ExprStructCtor {
2787        concrete_struct_id,
2788        members: member_exprs.into_iter().filter_map(|(x, y)| Some((x, y?))).collect(),
2789        base_struct: base_struct.map(|(x, _)| x),
2790        ty: TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)).intern(db),
2791        stable_ptr: ctor_syntax.stable_ptr().into(),
2792    }))
2793}
2794
2795/// Returns the tail expression of the given list of statements, if exists.
2796/// A tail expression is the last statement in the list, if it is an expression and
2797/// it does not end with a semicolon.
2798fn get_tail_expression(
2799    syntax_db: &dyn SyntaxGroup,
2800    statements: &[ast::Statement],
2801) -> Option<ast::Expr> {
2802    let last = statements.last()?;
2803    let statement_expr = try_extract_matches!(last, ast::Statement::Expr)?;
2804    try_extract_matches!(statement_expr.semicolon(syntax_db), ast::OptionTerminalSemicolon::Empty)?;
2805    Some(statement_expr.expr(syntax_db))
2806}
2807
2808/// Creates a new numeric literal expression.
2809fn new_literal_expr(
2810    ctx: &mut ComputationContext<'_>,
2811    ty: Option<&str>,
2812    value: BigInt,
2813    stable_ptr: ExprPtr,
2814) -> Maybe<ExprLiteral> {
2815    if let Some(ty_str) = ty {
2816        // Requires specific blocking as `NonZero` now has NumericLiteral support.
2817        if ty_str == "NonZero" {
2818            return Err(ctx.diagnostics.report(
2819                stable_ptr.untyped(),
2820                SemanticDiagnosticKind::WrongNumberOfArguments { expected: 1, actual: 0 },
2821            ));
2822        }
2823        let ty = try_get_core_ty_by_name(ctx.db, ty_str.into(), vec![])
2824            .map_err(|err| ctx.diagnostics.report(stable_ptr.untyped(), err))?;
2825        if let Err(err) = validate_literal(ctx.db, ty, &value) {
2826            ctx.diagnostics.report(stable_ptr, SemanticDiagnosticKind::LiteralError(err));
2827        }
2828        return Ok(ExprLiteral { value, ty, stable_ptr });
2829    };
2830    let ty = ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()));
2831
2832    // Numeric trait.
2833    let trait_id = ctx.db.core_info().numeric_literal_trt;
2834    let generic_args = vec![GenericArgumentId::Type(ty)];
2835    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
2836    let lookup_context = ctx.resolver.impl_lookup_context();
2837    let inference = &mut ctx.resolver.inference();
2838    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
2839
2840    Ok(ExprLiteral { value, ty, stable_ptr })
2841}
2842
2843/// Creates the semantic model of a literal expression from its AST.
2844fn literal_to_semantic(
2845    ctx: &mut ComputationContext<'_>,
2846    literal_syntax: &ast::TerminalLiteralNumber,
2847) -> Maybe<ExprLiteral> {
2848    let db = ctx.db;
2849    let syntax_db = db.upcast();
2850
2851    let (value, ty) = literal_syntax.numeric_value_and_suffix(syntax_db).unwrap_or_default();
2852    let ty = ty.as_ref().map(SmolStr::as_str);
2853
2854    new_literal_expr(ctx, ty, value, literal_syntax.stable_ptr().into())
2855}
2856
2857/// Creates the semantic model of a short string from its AST.
2858fn short_string_to_semantic(
2859    ctx: &mut ComputationContext<'_>,
2860    short_string_syntax: &ast::TerminalShortString,
2861) -> Maybe<ExprLiteral> {
2862    let db = ctx.db;
2863    let syntax_db = db.upcast();
2864
2865    let value = short_string_syntax.numeric_value(syntax_db).unwrap_or_default();
2866
2867    let suffix = short_string_syntax.suffix(syntax_db);
2868    let suffix = suffix.as_ref().map(SmolStr::as_str);
2869
2870    new_literal_expr(ctx, suffix, value, short_string_syntax.stable_ptr().into())
2871}
2872
2873/// Creates a new string literal expression.
2874fn new_string_literal_expr(
2875    ctx: &mut ComputationContext<'_>,
2876    value: String,
2877    stable_ptr: ExprPtr,
2878) -> Maybe<ExprStringLiteral> {
2879    let ty = ctx.resolver.inference().new_type_var(Some(stable_ptr.untyped()));
2880
2881    let trait_id = ctx.db.core_info().string_literal_trt;
2882    let generic_args = vec![GenericArgumentId::Type(ty)];
2883    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(ctx.db);
2884    let lookup_context = ctx.resolver.impl_lookup_context();
2885    let inference = &mut ctx.resolver.inference();
2886    inference.new_impl_var(concrete_trait_id, Some(stable_ptr.untyped()), lookup_context);
2887
2888    Ok(ExprStringLiteral { value, ty, stable_ptr })
2889}
2890
2891/// Creates the semantic model of a string literal from its AST.
2892fn string_literal_to_semantic(
2893    ctx: &mut ComputationContext<'_>,
2894    string_syntax: &ast::TerminalString,
2895) -> Maybe<ExprStringLiteral> {
2896    let db = ctx.db;
2897    let syntax_db = db.upcast();
2898    let stable_ptr = string_syntax.stable_ptr();
2899
2900    let value = string_syntax.string_value(syntax_db).unwrap_or_default();
2901    // TODO(yuval): support prefixes/suffixes for explicit types?
2902
2903    new_string_literal_expr(ctx, value, stable_ptr.into())
2904}
2905
2906/// Given an expression syntax, if it's an identifier, returns it. Otherwise, returns the proper
2907/// error.
2908fn expr_as_identifier(
2909    ctx: &mut ComputationContext<'_>,
2910    path: &ast::ExprPath,
2911    syntax_db: &dyn SyntaxGroup,
2912) -> Maybe<SmolStr> {
2913    let segments = path.elements(syntax_db);
2914    if segments.len() == 1 {
2915        return Ok(segments[0].identifier(syntax_db));
2916    }
2917    Err(ctx.diagnostics.report(path, InvalidMemberExpression))
2918}
2919
2920// TODO(spapini): Consider moving some checks here to the responsibility of the parser.
2921/// Computes the semantic expression for a dot expression.
2922fn dot_expr(
2923    ctx: &mut ComputationContext<'_>,
2924    lexpr: ExprAndId,
2925    rhs_syntax: ast::Expr,
2926    stable_ptr: ast::ExprPtr,
2927) -> Maybe<Expr> {
2928    // Find MemberId.
2929    match rhs_syntax {
2930        ast::Expr::Path(expr) => member_access_expr(ctx, lexpr, expr, stable_ptr),
2931        ast::Expr::FunctionCall(expr) => method_call_expr(ctx, lexpr, expr, stable_ptr),
2932        _ => Err(ctx.diagnostics.report(&rhs_syntax, InvalidMemberExpression)),
2933    }
2934}
2935
2936/// Finds all the trait ids usable in the current context.
2937fn traits_in_context(
2938    ctx: &mut ComputationContext<'_>,
2939) -> Maybe<OrderedHashMap<TraitId, LookupItemId>> {
2940    let mut traits =
2941        ctx.db.module_usable_trait_ids(ctx.resolver.prelude_submodule())?.deref().clone();
2942    traits.extend(
2943        ctx.db
2944            .module_usable_trait_ids(ctx.resolver.module_file_id.0)?
2945            .iter()
2946            .map(|(k, v)| (*k, *v)),
2947    );
2948    Ok(traits)
2949}
2950
2951/// Computes the semantic model of a method call expression (e.g. "expr.method(..)").
2952/// Finds all traits with at least one candidate impl with a matching `self` param.
2953/// If more/less than 1 such trait exists, fails.
2954fn method_call_expr(
2955    ctx: &mut ComputationContext<'_>,
2956    lexpr: ExprAndId,
2957    expr: ast::ExprFunctionCall,
2958    stable_ptr: ast::ExprPtr,
2959) -> Maybe<Expr> {
2960    // TODO(spapini): Add ctx.module_id.
2961    // TODO(spapini): Look also in uses.
2962    let syntax_db = ctx.db.upcast();
2963    let path = expr.path(syntax_db);
2964    let Ok([segment]): Result<[_; 1], _> = path.elements(syntax_db).try_into() else {
2965        return Err(ctx.diagnostics.report(&expr, InvalidMemberExpression));
2966    };
2967    let func_name = segment.identifier(syntax_db);
2968    let generic_args_syntax = segment.generic_args(syntax_db);
2969
2970    if !ctx.reduce_ty(lexpr.ty()).is_var_free(ctx.db) {
2971        // Run solver to get as much info on the type as possible.
2972        // Ignore the result of the `solve()` call - the error, if any, will be
2973        // reported later.
2974        ctx.resolver.inference().solve().ok();
2975    }
2976
2977    let mut candidate_traits = traits_in_context(ctx)?;
2978
2979    // Add traits from impl generic args in the context.
2980    for generic_param in &ctx.resolver.data.generic_params {
2981        if generic_param.kind(ctx.db.upcast()) == GenericKind::Impl {
2982            let Ok(trait_id) = ctx.db.generic_impl_param_trait(*generic_param) else {
2983                continue;
2984            };
2985            candidate_traits
2986                .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
2987        }
2988    }
2989
2990    // Extracting the possible traits that should be imported, in order to use the method.
2991    let module_file_id = ctx.resolver.module_file_id;
2992    let lookup_context = ctx.resolver.impl_lookup_context();
2993    let lexpr_clone = lexpr.clone();
2994    let db = ctx.db;
2995    let (function_id, actual_trait_id, fixed_lexpr, mutability) =
2996        compute_method_function_call_data(
2997            ctx,
2998            candidate_traits.keys().copied().collect_vec().as_slice(),
2999            func_name.clone(),
3000            lexpr,
3001            path.stable_ptr().untyped(),
3002            generic_args_syntax,
3003            |ty, method_name, inference_errors| {
3004                let relevant_traits = if !inference_errors.is_empty() {
3005                    vec![]
3006                } else {
3007                    match_method_to_traits(
3008                        db,
3009                        ty,
3010                        &method_name,
3011                        lookup_context.clone(),
3012                        module_file_id,
3013                        lexpr_clone.stable_ptr().untyped(),
3014                    )
3015                };
3016                Some(CannotCallMethod { ty, method_name, inference_errors, relevant_traits })
3017            },
3018            |_, trait_function_id0, trait_function_id1| {
3019                Some(AmbiguousTrait { trait_function_id0, trait_function_id1 })
3020            },
3021        )?;
3022
3023    if let Ok(trait_definition_data) = ctx.db.priv_trait_definition_data(actual_trait_id) {
3024        if let Some(trait_item_info) = trait_definition_data.get_trait_item_info(&func_name) {
3025            ctx.resolver.validate_feature_constraints(
3026                ctx.diagnostics,
3027                &segment.identifier_ast(db.upcast()),
3028                &trait_item_info,
3029            );
3030        }
3031    }
3032    ctx.resolver.data.used_items.insert(candidate_traits[&actual_trait_id]);
3033    ctx.resolver.data.resolved_items.mark_concrete(
3034        ctx.db,
3035        &segment,
3036        ResolvedConcreteItem::Function(function_id),
3037    );
3038
3039    // Note there may be n+1 arguments for n parameters, if the last one is a coupon.
3040    let mut args_iter =
3041        expr.arguments(syntax_db).arguments(syntax_db).elements(syntax_db).into_iter();
3042    // Self argument.
3043    let mut named_args = vec![NamedArg(fixed_lexpr, None, mutability)];
3044    // Other arguments.
3045    let closure_params: OrderedHashMap<TypeId, TypeId> =
3046        concrete_function_closure_params(ctx.db, function_id)?;
3047    for ty in function_parameter_types(ctx, function_id)?.skip(1) {
3048        let Some(arg_syntax) = args_iter.next() else {
3049            break;
3050        };
3051        named_args.push(compute_named_argument_clause(
3052            ctx,
3053            arg_syntax,
3054            closure_params.get(&ty).copied(),
3055        ));
3056    }
3057
3058    // Maybe coupon
3059    if let Some(arg_syntax) = args_iter.next() {
3060        named_args.push(compute_named_argument_clause(ctx, arg_syntax, None));
3061    }
3062
3063    expr_function_call(ctx, function_id, named_args, &expr, stable_ptr)
3064}
3065
3066/// Computes the semantic model of a member access expression (e.g. "expr.member").
3067fn member_access_expr(
3068    ctx: &mut ComputationContext<'_>,
3069    lexpr: ExprAndId,
3070    rhs_syntax: ast::ExprPath,
3071    stable_ptr: ast::ExprPtr,
3072) -> Maybe<Expr> {
3073    let syntax_db = ctx.db.upcast();
3074
3075    // Find MemberId.
3076    let member_name = expr_as_identifier(ctx, &rhs_syntax, syntax_db)?;
3077    let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, lexpr.ty(), &rhs_syntax)?;
3078
3079    match &long_ty {
3080        TypeLongId::Concrete(_) | TypeLongId::Tuple(_) | TypeLongId::FixedSizeArray { .. } => {
3081            let Some(EnrichedTypeMemberAccess { member, deref_functions }) =
3082                get_enriched_type_member_access(ctx, lexpr.clone(), stable_ptr, &member_name)?
3083            else {
3084                return Err(ctx.diagnostics.report(
3085                    &rhs_syntax,
3086                    NoSuchTypeMember { ty: long_ty.intern(ctx.db), member_name },
3087                ));
3088            };
3089            check_struct_member_is_visible(
3090                ctx,
3091                &member,
3092                rhs_syntax.stable_ptr().untyped(),
3093                &member_name,
3094            );
3095            let member_path = match &long_ty {
3096                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id))
3097                    if n_snapshots == 0 && deref_functions.is_empty() =>
3098                {
3099                    lexpr.as_member_path().map(|parent| ExprVarMemberPath::Member {
3100                        parent: Box::new(parent),
3101                        member_id: member.id,
3102                        stable_ptr,
3103                        concrete_struct_id: *concrete_struct_id,
3104                        ty: member.ty,
3105                    })
3106                }
3107                _ => None,
3108            };
3109            let mut derefed_expr: ExprAndId = lexpr;
3110            for (deref_function, mutability) in &deref_functions {
3111                let cur_expr = expr_function_call(
3112                    ctx,
3113                    *deref_function,
3114                    vec![NamedArg(derefed_expr, None, *mutability)],
3115                    stable_ptr,
3116                    stable_ptr,
3117                )
3118                .unwrap();
3119
3120                derefed_expr =
3121                    ExprAndId { expr: cur_expr.clone(), id: ctx.arenas.exprs.alloc(cur_expr) };
3122            }
3123            let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, derefed_expr.ty(), &rhs_syntax)?;
3124            let derefed_expr_concrete_struct_id = match long_ty {
3125                TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
3126                    concrete_struct_id
3127                }
3128                _ => unreachable!(),
3129            };
3130            let ty = if !deref_functions.is_empty() {
3131                member.ty
3132            } else {
3133                wrap_in_snapshots(ctx.db, member.ty, n_snapshots)
3134            };
3135            Ok(Expr::MemberAccess(ExprMemberAccess {
3136                expr: derefed_expr.id,
3137                concrete_struct_id: derefed_expr_concrete_struct_id,
3138                member: member.id,
3139                ty,
3140                member_path,
3141                n_snapshots,
3142                stable_ptr,
3143            }))
3144        }
3145
3146        TypeLongId::Snapshot(_) => {
3147            // TODO(spapini): Handle snapshot members.
3148            Err(ctx.diagnostics.report(&rhs_syntax, Unsupported))
3149        }
3150        TypeLongId::Closure(_) => Err(ctx.diagnostics.report(&rhs_syntax, Unsupported)),
3151        TypeLongId::ImplType(impl_type_id) => {
3152            unreachable!(
3153                "Impl type should've been reduced {:?}.",
3154                impl_type_id.debug(ctx.db.elongate())
3155            )
3156        }
3157        TypeLongId::Var(_) => Err(ctx.diagnostics.report(
3158            &rhs_syntax,
3159            InternalInferenceError(InferenceError::TypeNotInferred(long_ty.intern(ctx.db))),
3160        )),
3161        TypeLongId::GenericParameter(_) | TypeLongId::Coupon(_) => Err(ctx
3162            .diagnostics
3163            .report(&rhs_syntax, TypeHasNoMembers { ty: long_ty.intern(ctx.db), member_name })),
3164        TypeLongId::Missing(diag_added) => Err(*diag_added),
3165    }
3166}
3167
3168/// Returns the member and the deref operations needed for its access.
3169///
3170/// Enriched members include both direct members (in case of a struct), and members of derefed types
3171/// if the type implements the Deref trait into a struct.
3172fn get_enriched_type_member_access(
3173    ctx: &mut ComputationContext<'_>,
3174    expr: ExprAndId,
3175    stable_ptr: ast::ExprPtr,
3176    accessed_member_name: &str,
3177) -> Maybe<Option<EnrichedTypeMemberAccess>> {
3178    let mut ty = ctx.reduce_ty(expr.ty());
3179    if !ty.is_var_free(ctx.db) {
3180        // Run solver to get as much info on the type as possible.
3181        // Ignore the result of the `solve()` call - the error, if any, will be
3182        // reported later.
3183        ctx.resolver.inference().solve().ok();
3184        ty = ctx.reduce_ty(ty);
3185    }
3186    let base_var = match &expr.expr {
3187        Expr::Var(expr_var) => Some(expr_var.var),
3188        Expr::MemberAccess(ExprMemberAccess { member_path: Some(member_path), .. }) => {
3189            Some(member_path.base_var())
3190        }
3191        _ => None,
3192    };
3193    let is_mut_var = base_var
3194        .filter(|var_id| matches!(ctx.semantic_defs.get(var_id), Some(var) if var.is_mut()))
3195        .is_some();
3196    let key = (ty, is_mut_var);
3197    let mut enriched_members = match ctx.resolver.type_enriched_members.entry(key) {
3198        Entry::Occupied(entry) => {
3199            let e = entry.get();
3200            match e.get_member(accessed_member_name) {
3201                Some(value) => return Ok(Some(value)),
3202                None => {
3203                    if e.deref_chain.len() == e.explored_derefs {
3204                        // There's no further exploration to be done, and member was not found.
3205                        return Ok(None);
3206                    }
3207                }
3208            }
3209            // Moving out of the map to call `enrich_members` and insert back with updated value.
3210            entry.swap_remove()
3211        }
3212        Entry::Vacant(_) => {
3213            let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
3214            let members =
3215                if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
3216                    let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
3217                    if let Some(member) = members.get(accessed_member_name) {
3218                        // Found direct member access - so directly returning it.
3219                        return Ok(Some(EnrichedTypeMemberAccess {
3220                            member: member.clone(),
3221                            deref_functions: vec![],
3222                        }));
3223                    }
3224                    members.iter().map(|(k, v)| (k.clone(), (v.clone(), 0))).collect()
3225                } else {
3226                    Default::default()
3227                };
3228
3229            EnrichedMembers {
3230                members,
3231                deref_chain: ctx.db.deref_chain(ty, is_mut_var)?.derefs,
3232                explored_derefs: 0,
3233            }
3234        }
3235    };
3236    enrich_members(ctx, &mut enriched_members, stable_ptr, accessed_member_name)?;
3237    let e = ctx.resolver.type_enriched_members.entry(key).or_insert(enriched_members);
3238    Ok(e.get_member(accessed_member_name))
3239}
3240
3241/// Enriches the `enriched_members` with members from "deref"s of the current type.
3242///
3243/// The function will stop enriching if it encounters a cycle in the deref chain, or if the
3244/// requested member is found.
3245fn enrich_members(
3246    ctx: &mut ComputationContext<'_>,
3247    enriched_members: &mut EnrichedMembers,
3248    stable_ptr: ast::ExprPtr,
3249    accessed_member_name: &str,
3250) -> Maybe<()> {
3251    let EnrichedMembers { members: enriched, deref_chain, explored_derefs } = enriched_members;
3252
3253    // Add members of derefed types.
3254    for deref_info in deref_chain.iter().skip(*explored_derefs).cloned() {
3255        *explored_derefs += 1;
3256        let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, deref_info.target_ty, stable_ptr)?;
3257        if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
3258            let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
3259            for (member_name, member) in members.iter() {
3260                // Insert member if there is not already a member with the same name.
3261                enriched
3262                    .entry(member_name.clone())
3263                    .or_insert_with(|| (member.clone(), *explored_derefs));
3264            }
3265            // If member is contained we can stop the calculation post the lookup.
3266            if members.contains_key(accessed_member_name) {
3267                // Found member, so exploration isn't done.
3268                break;
3269            }
3270        }
3271    }
3272    Ok(())
3273}
3274
3275/// Peels snapshots from a type and making sure it is fully not a variable type.
3276fn finalized_snapshot_peeled_ty(
3277    ctx: &mut ComputationContext<'_>,
3278    ty: TypeId,
3279    stable_ptr: impl Into<SyntaxStablePtrId>,
3280) -> Maybe<(usize, TypeLongId)> {
3281    let ty = ctx.reduce_ty(ty);
3282    let (base_snapshots, mut long_ty) = peel_snapshots(ctx.db, ty);
3283    if let TypeLongId::ImplType(impl_type_id) = long_ty {
3284        let inference = &mut ctx.resolver.inference();
3285        let Ok(ty) = inference.reduce_impl_ty(impl_type_id) else {
3286            return Err(ctx
3287                .diagnostics
3288                .report(stable_ptr, InternalInferenceError(InferenceError::TypeNotInferred(ty))));
3289        };
3290        long_ty = ty.lookup_intern(ctx.db);
3291    }
3292    if matches!(long_ty, TypeLongId::Var(_)) {
3293        // Save some work. ignore the result. The error, if any, will be reported later.
3294        ctx.resolver.inference().solve().ok();
3295        long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
3296    }
3297    let (additional_snapshots, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
3298    Ok((base_snapshots + additional_snapshots, long_ty))
3299}
3300
3301/// Resolves a variable or a constant given a context and a path expression.
3302fn resolve_expr_path(ctx: &mut ComputationContext<'_>, path: &ast::ExprPath) -> Maybe<Expr> {
3303    let db = ctx.db;
3304    let syntax_db = db.upcast();
3305    let segments = path.elements(syntax_db);
3306    if segments.is_empty() {
3307        return Err(ctx.diagnostics.report(path, Unsupported));
3308    }
3309
3310    // Check if this is a variable.
3311    if let [PathSegment::Simple(ident_segment)] = &segments[..] {
3312        let identifier = ident_segment.ident(syntax_db);
3313        let variable_name = identifier.text(ctx.db.upcast());
3314        if let Some(res) = get_binded_expr_by_name(ctx, &variable_name, path.stable_ptr().into()) {
3315            match res.clone() {
3316                Expr::Var(expr_var) => {
3317                    let item = ResolvedGenericItem::Variable(expr_var.var);
3318                    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(), item);
3319                }
3320                Expr::Constant(expr_const) => {
3321                    let item = ResolvedConcreteItem::Constant(expr_const.const_value_id);
3322                    ctx.resolver.data.resolved_items.concrete.insert(identifier.stable_ptr(), item);
3323                }
3324                _ => unreachable!(
3325                    "get_binded_expr_by_name should only return variables or constants"
3326                ),
3327            };
3328            return Ok(res);
3329        }
3330    }
3331
3332    let resolved_item: ResolvedConcreteItem = ctx.resolver.resolve_concrete_path_ex(
3333        ctx.diagnostics,
3334        path,
3335        NotFoundItemType::Identifier,
3336        Some(&mut ctx.environment),
3337    )?;
3338
3339    match resolved_item {
3340        ResolvedConcreteItem::Constant(const_value_id) => Ok(Expr::Constant(ExprConstant {
3341            const_value_id,
3342            ty: const_value_id.ty(db)?,
3343            stable_ptr: path.stable_ptr().into(),
3344        })),
3345
3346        ResolvedConcreteItem::Variant(variant) if variant.ty == unit_ty(db) => {
3347            let stable_ptr = path.stable_ptr().into();
3348            let concrete_enum_id = variant.concrete_enum_id;
3349            Ok(semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
3350                variant,
3351                value_expr: unit_expr(ctx, stable_ptr),
3352                ty: TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)).intern(db),
3353                stable_ptr,
3354            }))
3355        }
3356        resolved_item => Err(ctx.diagnostics.report(
3357            path,
3358            UnexpectedElement {
3359                expected: vec![ElementKind::Variable, ElementKind::Constant],
3360                actual: (&resolved_item).into(),
3361            },
3362        )),
3363    }
3364}
3365
3366/// Resolves a variable given a context and a simple name.
3367///
3368/// Reports a diagnostic if the variable was not found.
3369pub fn resolve_variable_by_name(
3370    ctx: &mut ComputationContext<'_>,
3371    identifier: &ast::TerminalIdentifier,
3372    stable_ptr: ast::ExprPtr,
3373) -> Maybe<Expr> {
3374    let variable_name = identifier.text(ctx.db.upcast());
3375    let res = get_binded_expr_by_name(ctx, &variable_name, stable_ptr)
3376        .ok_or_else(|| ctx.diagnostics.report(identifier, VariableNotFound(variable_name)))?;
3377    let item = ResolvedGenericItem::Variable(extract_matches!(&res, Expr::Var).var);
3378    ctx.resolver.data.resolved_items.generic.insert(identifier.stable_ptr(), item);
3379    Ok(res)
3380}
3381
3382/// Returns the requested variable from the environment if it exists. Returns None otherwise.
3383pub fn get_binded_expr_by_name(
3384    ctx: &mut ComputationContext<'_>,
3385    variable_name: &SmolStr,
3386    stable_ptr: ast::ExprPtr,
3387) -> Option<Expr> {
3388    let mut maybe_env = Some(&mut *ctx.environment);
3389    while let Some(env) = maybe_env {
3390        if let Some(var) = env.variables.get(variable_name) {
3391            env.used_variables.insert(var.id());
3392            return match var {
3393                Binding::LocalItem(local_const) => match local_const.kind.clone() {
3394                    crate::StatementItemKind::Constant(const_value_id, ty) => {
3395                        Some(Expr::Constant(ExprConstant { const_value_id, ty, stable_ptr }))
3396                    }
3397                },
3398                Binding::LocalVar(_) | Binding::Param(_) => {
3399                    Some(Expr::Var(ExprVar { var: var.id(), ty: var.ty(), stable_ptr }))
3400                }
3401            };
3402        }
3403        maybe_env = env.parent.as_deref_mut();
3404    }
3405    None
3406}
3407
3408/// Typechecks a function call.
3409fn expr_function_call(
3410    ctx: &mut ComputationContext<'_>,
3411    function_id: FunctionId,
3412    mut named_args: Vec<NamedArg>,
3413    call_ptr: impl Into<SyntaxStablePtrId>,
3414    stable_ptr: ast::ExprPtr,
3415) -> Maybe<Expr> {
3416    let coupon_arg = maybe_pop_coupon_argument(ctx, &mut named_args, function_id);
3417
3418    let signature = ctx.db.concrete_function_signature(function_id)?;
3419    let signature = ctx.resolver.inference().rewrite(signature).unwrap();
3420
3421    // TODO(spapini): Better location for these diagnostics after the refactor for generics resolve.
3422    if named_args.len() != signature.params.len() {
3423        return Err(ctx.diagnostics.report(
3424            call_ptr,
3425            WrongNumberOfArguments { expected: signature.params.len(), actual: named_args.len() },
3426        ));
3427    }
3428
3429    // Check argument names and types.
3430    check_named_arguments(&named_args, &signature, ctx)?;
3431
3432    let mut args = Vec::new();
3433    for (NamedArg(arg, _name, mutability), param) in
3434        named_args.into_iter().zip(signature.params.iter())
3435    {
3436        let arg_ty = arg.ty();
3437        let param_ty = param.ty;
3438        // Don't add diagnostic if the type is missing (a diagnostic should have already been
3439        // added).
3440        // TODO(lior): Add a test to missing type once possible.
3441        if !arg_ty.is_missing(ctx.db) {
3442            let inference = &mut ctx.resolver.inference();
3443            let _ = inference.conform_ty_for_diag(
3444                arg_ty,
3445                param_ty,
3446                ctx.diagnostics,
3447                || arg.stable_ptr().untyped(),
3448                |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
3449            );
3450        }
3451
3452        args.push(if param.mutability == Mutability::Reference {
3453            // Verify the argument is a variable.
3454            let Some(ref_arg) = arg.as_member_path() else {
3455                return Err(ctx.diagnostics.report(arg.deref(), RefArgNotAVariable));
3456            };
3457            // Verify the variable argument is mutable.
3458            if !ctx.semantic_defs[&ref_arg.base_var()].is_mut() {
3459                ctx.diagnostics.report(arg.deref(), RefArgNotMutable);
3460            }
3461            // Verify that it is passed explicitly as 'ref'.
3462            if mutability != Mutability::Reference {
3463                ctx.diagnostics.report(arg.deref(), RefArgNotExplicit);
3464            }
3465            ExprFunctionCallArg::Reference(ref_arg)
3466        } else {
3467            // Verify that it is passed without modifiers.
3468            if mutability != Mutability::Immutable {
3469                ctx.diagnostics.report(arg.deref(), ImmutableArgWithModifiers);
3470            }
3471            ExprFunctionCallArg::Value(arg.id)
3472        });
3473    }
3474
3475    let expr_function_call = ExprFunctionCall {
3476        function: function_id,
3477        args,
3478        coupon_arg,
3479        ty: signature.return_type,
3480        stable_ptr,
3481    };
3482    // Check panicable.
3483    if signature.panicable && has_panic_incompatibility(ctx) {
3484        // TODO(spapini): Delay this check until after inference, to allow resolving specific
3485        //   impls first.
3486        return Err(ctx.diagnostics.report(call_ptr, PanicableFromNonPanicable));
3487    }
3488    Ok(Expr::FunctionCall(expr_function_call))
3489}
3490
3491/// Checks if the last item in `named_args`, has the argument name `__coupon__`, and removes and
3492/// returns it if so.
3493fn maybe_pop_coupon_argument(
3494    ctx: &mut ComputationContext<'_>,
3495    named_args: &mut Vec<NamedArg>,
3496    function_id: FunctionId,
3497) -> Option<id_arena::Id<Expr>> {
3498    let mut coupon_arg: Option<ExprId> = None;
3499    if let Some(NamedArg(arg, Some(name_terminal), mutability)) = named_args.last() {
3500        let coupons_enabled = are_coupons_enabled(ctx.db, ctx.resolver.module_file_id);
3501        if name_terminal.text(ctx.db.upcast()) == "__coupon__" && coupons_enabled {
3502            // Check that the argument type is correct.
3503            let expected_ty = TypeLongId::Coupon(function_id).intern(ctx.db);
3504            let arg_ty = arg.ty();
3505            if !arg_ty.is_missing(ctx.db) {
3506                let inference = &mut ctx.resolver.inference();
3507                let _ = inference.conform_ty_for_diag(
3508                    arg_ty,
3509                    expected_ty,
3510                    ctx.diagnostics,
3511                    || arg.stable_ptr().untyped(),
3512                    |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
3513                );
3514            }
3515
3516            // Check that the argument is not mutable/reference.
3517            if *mutability != Mutability::Immutable {
3518                ctx.diagnostics.report(arg.deref(), CouponArgumentNoModifiers);
3519            }
3520
3521            coupon_arg = Some(arg.id);
3522
3523            // Remove the __coupon__ argument from the argument list.
3524            named_args.pop();
3525        }
3526    }
3527    coupon_arg
3528}
3529
3530/// Checks if a panicable function is called from a disallowed context.
3531fn has_panic_incompatibility(ctx: &mut ComputationContext<'_>) -> bool {
3532    if let Some(signature) = ctx.signature {
3533        // If the caller is nopanic, then this is a panic incompatibility.
3534        !signature.panicable
3535    } else {
3536        false
3537    }
3538}
3539
3540/// Checks the correctness of the named arguments, and outputs diagnostics on errors.
3541fn check_named_arguments(
3542    named_args: &[NamedArg],
3543    signature: &Signature,
3544    ctx: &mut ComputationContext<'_>,
3545) -> Maybe<()> {
3546    let mut res: Maybe<()> = Ok(());
3547
3548    // Indicates whether we saw a named argument. Used to report a diagnostic if an unnamed argument
3549    // will follow it.
3550    let mut seen_named_arguments: bool = false;
3551    // Indicates whether a [UnnamedArgumentFollowsNamed] diagnostic was reported. Used to prevent
3552    // multiple similar diagnostics.
3553    let mut reported_unnamed_argument_follows_named: bool = false;
3554    for (NamedArg(arg, name_opt, _mutability), param) in
3555        named_args.iter().zip(signature.params.iter())
3556    {
3557        // Check name.
3558        if let Some(name_terminal) = name_opt {
3559            seen_named_arguments = true;
3560            let name = name_terminal.text(ctx.db.upcast());
3561            if param.name != name.clone() {
3562                res = Err(ctx.diagnostics.report(
3563                    name_terminal,
3564                    NamedArgumentMismatch { expected: param.name.clone(), found: name },
3565                ));
3566            }
3567        } else if seen_named_arguments && !reported_unnamed_argument_follows_named {
3568            reported_unnamed_argument_follows_named = true;
3569            res = Err(ctx.diagnostics.report(arg.deref(), UnnamedArgumentFollowsNamed));
3570        }
3571    }
3572    res
3573}
3574
3575/// Computes the semantic model of a statement (excluding tail-expression).
3576pub fn compute_statement_semantic(
3577    ctx: &mut ComputationContext<'_>,
3578    syntax: ast::Statement,
3579) -> Maybe<StatementId> {
3580    let db = ctx.db;
3581    let syntax_db = db.upcast();
3582
3583    let crate_id = ctx.resolver.owning_crate_id;
3584
3585    // As for now, statement attributes does not have any semantic affect, so we only validate they
3586    // are allowed.
3587    validate_statement_attributes(ctx, &syntax);
3588    let feature_restore = ctx
3589        .resolver
3590        .data
3591        .feature_config
3592        .override_with(extract_item_feature_config(db, crate_id, &syntax, ctx.diagnostics));
3593    let statement = match &syntax {
3594        ast::Statement::Let(let_syntax) => {
3595            let rhs_syntax = &let_syntax.rhs(syntax_db);
3596            let (rhs_expr, ty) = match let_syntax.type_clause(syntax_db) {
3597                ast::OptionTypeClause::Empty(_) => {
3598                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
3599                    let inferred_type = rhs_expr.ty();
3600                    (rhs_expr, inferred_type)
3601                }
3602                ast::OptionTypeClause::TypeClause(type_clause) => {
3603                    let var_type_path = type_clause.ty(syntax_db);
3604                    let explicit_type = resolve_type_with_environment(
3605                        db,
3606                        ctx.diagnostics,
3607                        &mut ctx.resolver,
3608                        &var_type_path,
3609                        Some(&mut ctx.environment),
3610                    );
3611
3612                    let rhs_expr = compute_expr_semantic(ctx, rhs_syntax);
3613                    let inferred_type = ctx.reduce_ty(rhs_expr.ty());
3614                    if !inferred_type.is_missing(db) {
3615                        let inference = &mut ctx.resolver.inference();
3616                        let _ = inference.conform_ty_for_diag(
3617                            inferred_type,
3618                            explicit_type,
3619                            ctx.diagnostics,
3620                            || rhs_syntax.into(),
3621                            |actual_ty, expected_ty| WrongArgumentType { expected_ty, actual_ty },
3622                        );
3623                    }
3624                    (rhs_expr, explicit_type)
3625                }
3626            };
3627            let rhs_expr_id = rhs_expr.id;
3628
3629            let pattern = compute_pattern_semantic(
3630                ctx,
3631                &let_syntax.pattern(syntax_db),
3632                ty,
3633                &mut UnorderedHashMap::default(),
3634            );
3635            let variables = pattern.variables(&ctx.arenas.patterns);
3636            for v in variables {
3637                let var_def = Binding::LocalVar(v.var.clone());
3638                if let Some(old_var) =
3639                    ctx.environment.variables.insert(v.name.clone(), var_def.clone())
3640                {
3641                    if matches!(old_var, Binding::LocalItem(_)) {
3642                        return Err(ctx
3643                            .diagnostics
3644                            .report(v.stable_ptr, MultipleDefinitionforBinding(v.name.clone())));
3645                    }
3646                    ctx.add_unused_binding_warning(&v.name, &old_var);
3647                }
3648                ctx.semantic_defs.insert(var_def.id(), var_def);
3649            }
3650            semantic::Statement::Let(semantic::StatementLet {
3651                pattern: pattern.id,
3652                expr: rhs_expr_id,
3653                stable_ptr: syntax.stable_ptr(),
3654            })
3655        }
3656        ast::Statement::Expr(stmt_expr_syntax) => {
3657            let expr_syntax = stmt_expr_syntax.expr(syntax_db);
3658            let expr = compute_expr_semantic(ctx, &expr_syntax);
3659            if matches!(
3660                stmt_expr_syntax.semicolon(syntax_db),
3661                ast::OptionTerminalSemicolon::Empty(_)
3662            ) && !matches!(
3663                expr_syntax,
3664                ast::Expr::Block(_)
3665                    | ast::Expr::If(_)
3666                    | ast::Expr::Match(_)
3667                    | ast::Expr::Loop(_)
3668                    | ast::Expr::While(_)
3669                    | ast::Expr::For(_)
3670            ) {
3671                // Point to after the expression, where the semicolon is missing.
3672                ctx.diagnostics.report_after(&expr_syntax, MissingSemicolon);
3673            }
3674            let ty: TypeId = expr.ty();
3675            if let TypeLongId::Concrete(concrete) = ty.lookup_intern(db) {
3676                if concrete.is_must_use(db)? {
3677                    ctx.diagnostics.report(&expr_syntax, UnhandledMustUseType(ty));
3678                }
3679            }
3680            if let Expr::FunctionCall(expr_function_call) = &expr.expr {
3681                let generic_function_id =
3682                    expr_function_call.function.lookup_intern(db).function.generic_function;
3683                if generic_function_id.is_must_use(db)? {
3684                    ctx.diagnostics.report(&expr_syntax, UnhandledMustUseFunction);
3685                }
3686            }
3687            semantic::Statement::Expr(semantic::StatementExpr {
3688                expr: expr.id,
3689                stable_ptr: syntax.stable_ptr(),
3690            })
3691        }
3692        ast::Statement::Continue(continue_syntax) => {
3693            if !ctx.is_inside_loop() {
3694                return Err(ctx
3695                    .diagnostics
3696                    .report(continue_syntax, ContinueOnlyAllowedInsideALoop));
3697            }
3698            semantic::Statement::Continue(semantic::StatementContinue {
3699                stable_ptr: syntax.stable_ptr(),
3700            })
3701        }
3702        ast::Statement::Return(return_syntax) => {
3703            let (expr_option, expr_ty, stable_ptr) = match return_syntax.expr_clause(syntax_db) {
3704                ast::OptionExprClause::Empty(empty_clause) => {
3705                    (None, unit_ty(db), empty_clause.stable_ptr().untyped())
3706                }
3707                ast::OptionExprClause::ExprClause(expr_clause) => {
3708                    let expr_syntax = expr_clause.expr(syntax_db);
3709                    let expr = compute_expr_semantic(ctx, &expr_syntax);
3710                    (Some(expr.id), expr.ty(), expr_syntax.stable_ptr().untyped())
3711                }
3712            };
3713            let expected_ty = match &ctx.inner_ctx {
3714                None => ctx.get_return_type().ok_or_else(|| {
3715                    ctx.diagnostics.report(
3716                        return_syntax,
3717                        UnsupportedOutsideOfFunction(
3718                            UnsupportedOutsideOfFunctionFeatureName::ReturnStatement,
3719                        ),
3720                    )
3721                })?,
3722                Some(ctx) => ctx.return_type,
3723            };
3724
3725            let expected_ty = ctx.reduce_ty(expected_ty);
3726            let expr_ty = ctx.reduce_ty(expr_ty);
3727            if !expected_ty.is_missing(db) && !expr_ty.is_missing(db) {
3728                let inference = &mut ctx.resolver.inference();
3729                let _ = inference.conform_ty_for_diag(
3730                    expr_ty,
3731                    expected_ty,
3732                    ctx.diagnostics,
3733                    || stable_ptr,
3734                    |actual_ty, expected_ty| WrongReturnType { expected_ty, actual_ty },
3735                );
3736            }
3737            semantic::Statement::Return(semantic::StatementReturn {
3738                expr_option,
3739                stable_ptr: syntax.stable_ptr(),
3740            })
3741        }
3742        ast::Statement::Break(break_syntax) => {
3743            let (expr_option, ty, stable_ptr) = match break_syntax.expr_clause(syntax_db) {
3744                ast::OptionExprClause::Empty(expr_empty) => {
3745                    (None, unit_ty(db), expr_empty.stable_ptr().untyped())
3746                }
3747                ast::OptionExprClause::ExprClause(expr_clause) => {
3748                    let expr_syntax = expr_clause.expr(syntax_db);
3749                    let expr = compute_expr_semantic(ctx, &expr_syntax);
3750
3751                    (Some(expr.id), expr.ty(), expr.stable_ptr().untyped())
3752                }
3753            };
3754            let ty = ctx.reduce_ty(ty);
3755
3756            if !ctx.is_inside_loop() {
3757                return Err(ctx.diagnostics.report(break_syntax, BreakOnlyAllowedInsideALoop));
3758            }
3759
3760            if let Some(inner_ctx) = &mut ctx.inner_ctx {
3761                match &mut inner_ctx.kind {
3762                    InnerContextKind::Loop { type_merger, .. } => {
3763                        type_merger.try_merge_types(
3764                            ctx.db,
3765                            ctx.diagnostics,
3766                            &mut ctx.resolver.inference(),
3767                            ty,
3768                            stable_ptr,
3769                        );
3770                    }
3771                    InnerContextKind::While | InnerContextKind::For => {
3772                        if expr_option.is_some() {
3773                            ctx.diagnostics
3774                                .report(break_syntax, BreakWithValueOnlyAllowedInsideALoop);
3775                        };
3776                    }
3777                    InnerContextKind::Closure => unreachable!("Not inside a loop."),
3778                }
3779            }
3780
3781            semantic::Statement::Break(semantic::StatementBreak {
3782                expr_option,
3783                stable_ptr: syntax.stable_ptr(),
3784            })
3785        }
3786        ast::Statement::Item(stmt_item_syntax) => {
3787            let item_syntax = &stmt_item_syntax.item(syntax_db);
3788            match item_syntax {
3789                ast::ModuleItem::Constant(const_syntax) => {
3790                    let lhs = const_syntax.type_clause(db.upcast()).ty(db.upcast());
3791                    let rhs = const_syntax.value(db.upcast());
3792                    let rhs_expr = compute_expr_semantic(ctx, &rhs);
3793                    let explicit_type = resolve_type_with_environment(
3794                        db,
3795                        ctx.diagnostics,
3796                        &mut ctx.resolver,
3797                        &lhs,
3798                        Some(&mut ctx.environment),
3799                    );
3800                    let rhs_resolved_expr = resolve_const_expr_and_evaluate(
3801                        db,
3802                        ctx,
3803                        &rhs_expr,
3804                        stmt_item_syntax.stable_ptr().untyped(),
3805                        explicit_type,
3806                        false,
3807                    );
3808                    let name_syntax = const_syntax.name(syntax_db);
3809                    let name = name_syntax.text(db.upcast());
3810                    let rhs_id = StatementConstLongId(
3811                        ctx.resolver.module_file_id,
3812                        const_syntax.stable_ptr(),
3813                    );
3814                    let var_def = Binding::LocalItem(LocalItem {
3815                        id: StatementItemId::Constant(rhs_id.intern(db)),
3816                        kind: StatementItemKind::Constant(
3817                            db.intern_const_value(rhs_resolved_expr.clone()),
3818                            rhs_resolved_expr.ty(db.upcast())?,
3819                        ),
3820                    });
3821                    add_item_to_statement_environment(ctx, name, var_def, &name_syntax);
3822                }
3823                ast::ModuleItem::Use(use_syntax) => {
3824                    for leaf in get_all_path_leaves(syntax_db, use_syntax) {
3825                        let stable_ptr = leaf.stable_ptr();
3826                        let segments = get_use_path_segments(syntax_db, ast::UsePath::Leaf(leaf))?;
3827                        let resolved_item = ctx.resolver.resolve_generic_path(
3828                            ctx.diagnostics,
3829                            segments,
3830                            NotFoundItemType::Identifier,
3831                            Some(&mut ctx.environment),
3832                        )?;
3833                        let var_def_id = StatementItemId::Use(
3834                            StatementUseLongId(ctx.resolver.module_file_id, stable_ptr).intern(db),
3835                        );
3836                        let name = var_def_id.name(db.upcast());
3837                        match resolved_item {
3838                            ResolvedGenericItem::GenericConstant(const_id) => {
3839                                let var_def = Binding::LocalItem(LocalItem {
3840                                    id: var_def_id,
3841                                    kind: StatementItemKind::Constant(
3842                                        db.constant_const_value(const_id)?,
3843                                        db.constant_const_type(const_id)?,
3844                                    ),
3845                                });
3846                                add_item_to_statement_environment(ctx, name, var_def, stable_ptr);
3847                            }
3848                            ResolvedGenericItem::GenericType(generic_type_id) => {
3849                                add_type_to_statement_environment(
3850                                    ctx,
3851                                    name,
3852                                    ResolvedGenericItem::GenericType(generic_type_id),
3853                                    stable_ptr,
3854                                );
3855                            }
3856                            ResolvedGenericItem::Module(_)
3857                            | ResolvedGenericItem::GenericFunction(_)
3858                            | ResolvedGenericItem::GenericTypeAlias(_)
3859                            | ResolvedGenericItem::GenericImplAlias(_)
3860                            | ResolvedGenericItem::Variant(_)
3861                            | ResolvedGenericItem::Trait(_)
3862                            | ResolvedGenericItem::Impl(_)
3863                            | ResolvedGenericItem::Variable(_) => {
3864                                return Err(ctx
3865                                    .diagnostics
3866                                    .report(stable_ptr, UnsupportedUseItemInStatement));
3867                            }
3868                        }
3869                    }
3870                }
3871                ast::ModuleItem::Module(_) => {
3872                    unreachable!("Modules are not supported inside a function.")
3873                }
3874                ast::ModuleItem::FreeFunction(_) => {
3875                    unreachable!("FreeFunction type not supported.")
3876                }
3877                ast::ModuleItem::ExternFunction(_) => {
3878                    unreachable!("ExternFunction type not supported.")
3879                }
3880                ast::ModuleItem::ExternType(_) => unreachable!("ExternType type not supported."),
3881                ast::ModuleItem::Trait(_) => unreachable!("Trait type not supported."),
3882                ast::ModuleItem::Impl(_) => unreachable!("Impl type not supported."),
3883                ast::ModuleItem::ImplAlias(_) => unreachable!("ImplAlias type not supported."),
3884                ast::ModuleItem::Struct(_) => unreachable!("Struct type not supported."),
3885                ast::ModuleItem::Enum(_) => unreachable!("Enum type not supported."),
3886                ast::ModuleItem::TypeAlias(_) => unreachable!("TypeAlias type not supported."),
3887                ast::ModuleItem::InlineMacro(_) => unreachable!("InlineMacro type not supported."),
3888                ast::ModuleItem::HeaderDoc(_) => unreachable!("HeaderDoc type not supported."),
3889                ast::ModuleItem::Missing(_) => unreachable!("Missing type not supported."),
3890            }
3891            semantic::Statement::Item(semantic::StatementItem { stable_ptr: syntax.stable_ptr() })
3892        }
3893        ast::Statement::Missing(_) => todo!(),
3894    };
3895    ctx.resolver.data.feature_config.restore(feature_restore);
3896    Ok(ctx.arenas.statements.alloc(statement))
3897}
3898
3899/// Adds an item to the statement environment and reports a diagnostic if the item is already
3900/// defined.
3901fn add_item_to_statement_environment(
3902    ctx: &mut ComputationContext<'_>,
3903    name: SmolStr,
3904    var_def: Binding,
3905    stable_ptr: impl Into<SyntaxStablePtrId>,
3906) {
3907    if let Some(old_var) = ctx.environment.variables.insert(name.clone(), var_def.clone()) {
3908        ctx.diagnostics.report(
3909            stable_ptr,
3910            match old_var {
3911                Binding::LocalItem(_) => MultipleConstantDefinition(name),
3912                Binding::LocalVar(_) | Binding::Param(_) => MultipleDefinitionforBinding(name),
3913            },
3914        );
3915    }
3916    ctx.semantic_defs.insert(var_def.id(), var_def);
3917}
3918
3919/// Adds a type to the statement environment and reports a diagnostic if the type is already
3920/// defined.
3921fn add_type_to_statement_environment(
3922    ctx: &mut ComputationContext<'_>,
3923    name: SmolStr,
3924    resolved_generic_item: ResolvedGenericItem,
3925    stable_ptr: impl Into<SyntaxStablePtrId> + std::marker::Copy,
3926) {
3927    if ctx
3928        .environment
3929        .use_items
3930        .insert(
3931            name.clone(),
3932            StatementGenericItemData { resolved_generic_item, stable_ptr: stable_ptr.into() },
3933        )
3934        .is_some()
3935    {
3936        ctx.diagnostics.report(stable_ptr, MultipleGenericItemDefinition(name));
3937    }
3938}
3939
3940/// Computes the semantic model of an expression and reports diagnostics if the expression does not
3941/// evaluate to a boolean value.
3942fn compute_bool_condition_semantic(
3943    ctx: &mut ComputationContext<'_>,
3944    condition_syntax: &ast::Expr,
3945) -> ExprAndId {
3946    let condition = compute_expr_semantic(ctx, condition_syntax);
3947    let inference = &mut ctx.resolver.inference();
3948    let _ = inference.conform_ty_for_diag(
3949        condition.ty(),
3950        core_bool_ty(ctx.db),
3951        ctx.diagnostics,
3952        || condition.stable_ptr().untyped(),
3953        |condition_ty, _expected_ty| ConditionNotBool(condition_ty),
3954    );
3955    condition
3956}
3957
3958/// Validates a struct member is visible and otherwise adds a diagnostic.
3959fn check_struct_member_is_visible(
3960    ctx: &mut ComputationContext<'_>,
3961    member: &Member,
3962    stable_ptr: SyntaxStablePtrId,
3963    member_name: &SmolStr,
3964) {
3965    let db = ctx.db.upcast();
3966    let containing_module_id = member.id.parent_module(db);
3967    if ctx.resolver.ignore_visibility_checks(containing_module_id) {
3968        return;
3969    }
3970    let user_module_id = ctx.resolver.module_file_id.0;
3971    if !visibility::peek_visible_in(db, member.visibility, containing_module_id, user_module_id) {
3972        ctx.diagnostics.report(stable_ptr, MemberNotVisible(member_name.clone()));
3973    }
3974}
3975
3976/// Verifies that the statement attributes are valid statements attributes, if not a diagnostic is
3977/// reported.
3978fn validate_statement_attributes(ctx: &mut ComputationContext<'_>, syntax: &ast::Statement) {
3979    let allowed_attributes = ctx.db.allowed_statement_attributes();
3980    let mut diagnostics = vec![];
3981    validate_attributes_flat(
3982        ctx.db.upcast(),
3983        &allowed_attributes,
3984        &OrderedHashSet::default(),
3985        syntax,
3986        &mut diagnostics,
3987    );
3988    // Translate the plugin diagnostics to semantic diagnostics.
3989    for diagnostic in diagnostics {
3990        ctx.diagnostics
3991            .report(diagnostic.stable_ptr, SemanticDiagnosticKind::UnknownStatementAttribute);
3992    }
3993}
3994
3995/// Gets an iterator with the types of the parameters of the given function.
3996fn function_parameter_types(
3997    ctx: &mut ComputationContext<'_>,
3998    function: FunctionId,
3999) -> Maybe<impl Iterator<Item = TypeId>> {
4000    let signature = ctx.db.concrete_function_signature(function)?;
4001    let param_types = signature.params.into_iter().map(|param| param.ty);
4002    Ok(param_types)
4003}
4004
4005/// Finds traits which contain a method matching the given name and type.
4006/// This function checks for visible traits in the specified module file and filters
4007/// methods based on their association with the given type and method name.
4008fn match_method_to_traits(
4009    db: &dyn SemanticGroup,
4010    ty: semantic::TypeId,
4011    method_name: &SmolStr,
4012    lookup_context: ImplLookupContext,
4013    module_file_id: ModuleFileId,
4014    stable_ptr: SyntaxStablePtrId,
4015) -> Vec<String> {
4016    let visible_traits = db
4017        .visible_traits_from_module(module_file_id)
4018        .unwrap_or_else(|| Arc::new(OrderedHashMap::default()));
4019
4020    visible_traits
4021        .iter()
4022        .filter_map(|(trait_id, path)| {
4023            let mut data = InferenceData::new(InferenceId::NoContext);
4024            let mut inference = data.inference(db);
4025            let trait_function =
4026                db.trait_function_by_name(*trait_id, method_name.clone()).ok()??;
4027            let (concrete_trait_id, _) = inference.infer_concrete_trait_by_self(
4028                trait_function,
4029                ty,
4030                &lookup_context,
4031                Some(stable_ptr),
4032                |_| {},
4033            )?;
4034            inference.solve().ok();
4035            match inference.trait_solution_set(
4036                concrete_trait_id,
4037                ImplVarTraitItemMappings::default(),
4038                lookup_context.clone(),
4039            ) {
4040                Ok(SolutionSet::Unique(_) | SolutionSet::Ambiguous(_)) => Some(path.clone()),
4041                _ => None,
4042            }
4043        })
4044        .collect()
4045}