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