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