From 15774a6b4e70afd3a274e380ac8577c1e725bf10 Mon Sep 17 00:00:00 2001 From: Tomer Cohen Date: Wed, 18 Sep 2024 10:54:39 +0300 Subject: [PATCH] statement types in resolver --- .../block_level_items_test.cairo | 76 +- .../cairo-lang-lowering/src/test_data/members | 1 + crates/cairo-lang-semantic/src/diagnostic.rs | 4 + .../cairo-lang-semantic/src/expr/compute.rs | 128 ++- .../src/expr/semantic_test_data/use | 910 ++++++++++++++++++ .../src/items/impl_alias.rs | 1 + crates/cairo-lang-semantic/src/items/mod.rs | 1 + crates/cairo-lang-semantic/src/items/us.rs | 8 +- crates/cairo-lang-semantic/src/resolve/mod.rs | 164 +++- crates/cairo-lang-semantic/src/types.rs | 62 +- 10 files changed, 1287 insertions(+), 68 deletions(-) diff --git a/corelib/src/test/language_features/block_level_items_test.cairo b/corelib/src/test/language_features/block_level_items_test.cairo index d7ccde535a5..58bda3abd5a 100644 --- a/corelib/src/test/language_features/block_level_items_test.cairo +++ b/corelib/src/test/language_features/block_level_items_test.cairo @@ -46,25 +46,25 @@ fn test_global_const_to_let_shadowing() { assert_eq!(A, 4); } -pub mod X { +pub mod single_const { pub const A: u8 = 1; } #[test] fn test_use_usage() { - use X::A; + use single_const::A; assert_eq!(A, 1); } #[test] fn test_use_usage_with_alias() { - use X::A as B; + use single_const::A as B; assert_eq!(B, 1); } #[test] fn test_use_constant_shadowing() { - use X::A; + use single_const::A; assert_eq!(A, 1); { const A: u8 = 4; @@ -73,17 +73,17 @@ fn test_use_constant_shadowing() { assert_eq!(A, 1); } -pub mod Y { +pub mod double_const { pub const A: u8 = 4; pub const B: u8 = 6; } #[test] fn test_use_use_shadowing() { - use X::A; + use single_const::A; assert_eq!(A, 1); { - use Y::A; + use double_const::A; assert_eq!(A, 4); } assert_eq!(A, 1); @@ -94,7 +94,7 @@ fn test_const_use_shadowing() { const A: u8 = 1; assert_eq!(A, 1); { - use Y::A; + use double_const::A; assert_eq!(A, 4); } assert_eq!(A, 1); @@ -102,7 +102,7 @@ fn test_const_use_shadowing() { #[test] fn test_use_let_shadowing() { - use X::A; + use single_const::A; assert_eq!(A, 1); { let A = 4; @@ -116,7 +116,7 @@ fn test_let_use_shadowing() { let A = 1; assert_eq!(A, 1); { - use Y::A; + use double_const::A; assert_eq!(A, 4); } assert_eq!(A, 1); @@ -124,7 +124,61 @@ fn test_let_use_shadowing() { #[test] fn test_multiple_use() { - use Y::{A, B}; + use double_const::{A, B}; assert_eq!(A, 4); assert_eq!(B, 6); } + +pub mod generic_type { + pub struct S { + pub x: u8, + } + pub enum E { + A: u8, + B: u16, + } +} + +#[test] +fn test_type_struct_usage() { + use generic_type::S; + let s = S { x: 1 }; + assert_eq!(s.x, 1); +} + +#[test] +fn test_type_enum_usage() { + use generic_type::E; + let e = E::A(1); + match e { + E::A(val) => assert_eq!(val, 1), + E::B(_) => panic!("Shouldn't get here"), + } +} + +pub mod generic_type_generics { + pub struct S { + pub x: T, + } + pub enum E { + A: T, + B: u16, + } +} + +#[test] +fn test_type_struct_generic_usage() { + use generic_type_generics::S; + let s = S:: { x: 1 }; + assert_eq!(s.x, 1); +} + +#[test] +fn test_type_enum_generic_usage() { + use generic_type_generics::E; + let e = E::A::(1); + match e { + E::A(val) => assert_eq!(val, 1), + E::B(_) => panic!("Shouldn't get here"), + } +} diff --git a/crates/cairo-lang-lowering/src/test_data/members b/crates/cairo-lang-lowering/src/test_data/members index f269592da86..d7b9d6079cf 100644 --- a/crates/cairo-lang-lowering/src/test_data/members +++ b/crates/cairo-lang-lowering/src/test_data/members @@ -3,6 +3,7 @@ //! > test_runner_name test_function_lowering(expect_diagnostics: false) + //! > function fn foo(ref a: A) { a.f = 5; diff --git a/crates/cairo-lang-semantic/src/diagnostic.rs b/crates/cairo-lang-semantic/src/diagnostic.rs index 5447498c4c5..ebdee3c261a 100644 --- a/crates/cairo-lang-semantic/src/diagnostic.rs +++ b/crates/cairo-lang-semantic/src/diagnostic.rs @@ -490,6 +490,9 @@ impl DiagnosticEntry for SemanticDiagnostic { identifier_name ) } + SemanticDiagnosticKind::MultipleGenericItemDefinition(type_name) => { + format!(r#"Multiple definitions of an item "{}"."#, type_name) + } SemanticDiagnosticKind::UnsupportedUseItemInStatement => { "Unsupported use item in statement.".into() } @@ -1104,6 +1107,7 @@ pub enum SemanticDiagnosticKind { UnusedUse, MultipleConstantDefinition(SmolStr), MultipleDefinitionforBinding(SmolStr), + MultipleGenericItemDefinition(SmolStr), UnsupportedUseItemInStatement, ConstGenericParamNotSupported, NegativeImplsNotEnabled, diff --git a/crates/cairo-lang-semantic/src/expr/compute.rs b/crates/cairo-lang-semantic/src/expr/compute.rs index f8648f3498b..2cbb5e824b4 100644 --- a/crates/cairo-lang-semantic/src/expr/compute.rs +++ b/crates/cairo-lang-semantic/src/expr/compute.rs @@ -19,6 +19,7 @@ use cairo_lang_defs::plugin::MacroPluginMetadata; use cairo_lang_diagnostics::{skip_diagnostic, Maybe, ToOption}; use cairo_lang_filesystem::cfg::CfgSet; use cairo_lang_filesystem::ids::{FileKind, FileLongId, VirtualFile}; +use cairo_lang_proc_macros::DebugWithDb; use cairo_lang_syntax::node::ast::{ BinaryOperator, BlockOrIf, ClosureParamWrapper, ExprPtr, OptionReturnTypeClause, PatternListOr, PatternStructParam, UnaryOperator, @@ -78,8 +79,8 @@ use crate::semantic::{self, Binding, FunctionId, LocalVariable, TypeId, TypeLong use crate::substitution::SemanticRewriter; use crate::types::{ add_type_based_diagnostics, are_coupons_enabled, extract_fixed_size_array_size, peel_snapshots, - peel_snapshots_ex, resolve_type, verify_fixed_size_array_size, wrap_in_snapshots, - ClosureTypeLongId, ConcreteTypeId, + peel_snapshots_ex, resolve_type_with_environment, verify_fixed_size_array_size, + wrap_in_snapshots, ClosureTypeLongId, ConcreteTypeId, }; use crate::usage::Usages; use crate::{ @@ -202,6 +203,12 @@ impl<'ctx> ComputationContext<'ctx> { for (var_name, var) in std::mem::take(&mut self.environment.variables) { self.add_unused_binding_warning(&var_name, &var); } + // Adds warning for unused items if required. + for (ty_name, statement_ty) in std::mem::take(&mut self.environment.use_items) { + if !self.environment.used_use_items.contains(&ty_name) && !ty_name.starts_with('_') { + self.diagnostics.report(statement_ty.stable_ptr, UnusedUse); + } + } self.environment = parent.unwrap(); res } @@ -277,6 +284,16 @@ impl<'ctx> ComputationContext<'ctx> { // TODO(ilya): Change value to VarId. pub type EnvVariables = OrderedHashMap; +type EnvItems = OrderedHashMap; + +/// Struct that holds the resolved generic type of a statement item. +#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)] +#[debug_db(dyn SemanticGroup + 'static)] +struct StatementGenericItemData { + resolved_generic_item: ResolvedGenericItem, + stable_ptr: SyntaxStablePtrId, +} + // TODO(spapini): Consider using identifiers instead of SmolStr everywhere in the code. /// A state which contains all the variables defined at the current resolver until now, and a /// pointer to the parent environment. @@ -285,6 +302,8 @@ pub struct Environment { parent: Option>, variables: EnvVariables, used_variables: UnorderedHashSet, + use_items: EnvItems, + used_use_items: UnorderedHashSet, } impl Environment { /// Adds a parameter to the environment. @@ -309,8 +328,30 @@ impl Environment { } pub fn empty() -> Self { - Self { parent: None, variables: Default::default(), used_variables: Default::default() } + Self { + parent: None, + variables: Default::default(), + used_variables: Default::default(), + use_items: Default::default(), + used_use_items: Default::default(), + } + } +} + +/// Returns the requested item from the environment if it exists. Returns None otherwise. +pub fn get_statement_item_by_name( + env: &mut Environment, + item_name: &SmolStr, +) -> Option { + let mut maybe_env = Some(&mut *env); + while let Some(curr_env) = maybe_env { + if let Some(var) = curr_env.use_items.get(item_name) { + curr_env.used_use_items.insert(item_name.clone()); + return Some(var.resolved_generic_item.clone()); + } + maybe_env = curr_env.parent.as_deref_mut(); } + None } /// Computes the semantic model of an expression. @@ -872,8 +913,12 @@ fn compute_expr_function_call_semantic( } } - let item = - ctx.resolver.resolve_concrete_path(ctx.diagnostics, &path, NotFoundItemType::Function)?; + let item = ctx.resolver.resolve_concrete_path_ex( + ctx.diagnostics, + &path, + NotFoundItemType::Function, + Some(&mut ctx.environment), + )?; match item { ResolvedConcreteItem::Variant(variant) => { @@ -1635,11 +1680,12 @@ fn compute_expr_closure_semantic( vec![] }; let return_type = match syntax.ret_ty(syntax_db) { - OptionReturnTypeClause::ReturnTypeClause(ty_syntax) => resolve_type( + OptionReturnTypeClause::ReturnTypeClause(ty_syntax) => resolve_type_with_environment( new_ctx.db, new_ctx.diagnostics, &mut new_ctx.resolver, &ty_syntax.ty(syntax_db), + Some(&mut new_ctx.environment), ), OptionReturnTypeClause::Empty(missing) => { new_ctx.resolver.inference().new_type_var(Some(missing.stable_ptr().untyped())) @@ -2005,6 +2051,7 @@ fn maybe_compute_pattern_semantic( ctx.diagnostics, &path, NotFoundItemType::Identifier, + Some(&mut ctx.environment), )?; let generic_variant = try_extract_matches!(item, ResolvedGenericItem::Variant) .ok_or_else(|| ctx.diagnostics.report(&path, NotAVariant))?; @@ -2072,10 +2119,11 @@ fn maybe_compute_pattern_semantic( ), ast::Pattern::Struct(pattern_struct) => { let pattern_ty = try_extract_matches!( - ctx.resolver.resolve_concrete_path( + ctx.resolver.resolve_concrete_path_ex( ctx.diagnostics, &pattern_struct.path(syntax_db), - NotFoundItemType::Type + NotFoundItemType::Type, + Some(&mut ctx.environment) )?, ResolvedConcreteItem::Type ) @@ -2375,7 +2423,13 @@ fn struct_ctor_expr( let path = ctor_syntax.path(syntax_db); // Extract struct. - let ty = resolve_type(db, ctx.diagnostics, &mut ctx.resolver, &ast::Expr::Path(path.clone())); + let ty = resolve_type_with_environment( + db, + ctx.diagnostics, + &mut ctx.resolver, + &ast::Expr::Path(path.clone()), + Some(&mut ctx.environment), + ); ty.check_not_missing(db)?; let concrete_struct_id = try_extract_matches!(ty.lookup_intern(ctx.db), TypeLongId::Concrete) @@ -3069,8 +3123,12 @@ fn resolve_expr_path(ctx: &mut ComputationContext<'_>, path: &ast::ExprPath) -> } } - let resolved_item: ResolvedConcreteItem = - ctx.resolver.resolve_concrete_path(ctx.diagnostics, path, NotFoundItemType::Identifier)?; + let resolved_item: ResolvedConcreteItem = ctx.resolver.resolve_concrete_path_ex( + ctx.diagnostics, + path, + NotFoundItemType::Identifier, + Some(&mut ctx.environment), + )?; match resolved_item { ResolvedConcreteItem::Constant(const_value_id) => Ok(Expr::Constant(ExprConstant { @@ -3344,8 +3402,13 @@ pub fn compute_statement_semantic( } ast::OptionTypeClause::TypeClause(type_clause) => { let var_type_path = type_clause.ty(syntax_db); - let explicit_type = - resolve_type(db, ctx.diagnostics, &mut ctx.resolver, &var_type_path); + let explicit_type = resolve_type_with_environment( + db, + ctx.diagnostics, + &mut ctx.resolver, + &var_type_path, + Some(&mut ctx.environment), + ); let rhs_expr = compute_expr_semantic(ctx, rhs_syntax); let inferred_type = ctx.reduce_ty(rhs_expr.ty()); @@ -3522,7 +3585,13 @@ pub fn compute_statement_semantic( let lhs = const_syntax.type_clause(db.upcast()).ty(db.upcast()); let rhs = const_syntax.value(db.upcast()); let rhs_expr = compute_expr_semantic(ctx, &rhs); - let explicit_type = resolve_type(db, ctx.diagnostics, &mut ctx.resolver, &lhs); + let explicit_type = resolve_type_with_environment( + db, + ctx.diagnostics, + &mut ctx.resolver, + &lhs, + Some(&mut ctx.environment), + ); let rhs_resolved_expr = resolve_const_expr_and_evaluate( db, ctx, @@ -3553,6 +3622,7 @@ pub fn compute_statement_semantic( ctx.diagnostics, segments, NotFoundItemType::Identifier, + Some(&mut ctx.environment), )?; let var_def_id = StatementItemId::Use( StatementUseLongId(ctx.resolver.module_file_id, stable_ptr).intern(db), @@ -3569,10 +3639,17 @@ pub fn compute_statement_semantic( }); add_item_to_statement_environment(ctx, name, var_def, stable_ptr); } + ResolvedGenericItem::GenericType(generic_type_id) => { + add_type_to_statement_environment( + ctx, + name, + ResolvedGenericItem::GenericType(generic_type_id), + stable_ptr, + ); + } ResolvedGenericItem::Module(_) | ResolvedGenericItem::GenericFunction(_) | ResolvedGenericItem::TraitFunction(_) - | ResolvedGenericItem::GenericType(_) | ResolvedGenericItem::GenericTypeAlias(_) | ResolvedGenericItem::GenericImplAlias(_) | ResolvedGenericItem::Variant(_) @@ -3634,6 +3711,27 @@ fn add_item_to_statement_environment( ctx.semantic_defs.insert(var_def.id(), var_def); } +/// Adds a type to the statement environment and reports a diagnostic if the type is already +/// defined. +fn add_type_to_statement_environment( + ctx: &mut ComputationContext<'_>, + name: SmolStr, + resolved_generic_item: ResolvedGenericItem, + stable_ptr: impl Into + std::marker::Copy, +) { + if ctx + .environment + .use_items + .insert( + name.clone(), + StatementGenericItemData { resolved_generic_item, stable_ptr: stable_ptr.into() }, + ) + .is_some() + { + ctx.diagnostics.report(stable_ptr, MultipleGenericItemDefinition(name)); + } +} + /// Computes the semantic model of an expression and reports diagnostics if the expression does not /// evaluate to a boolean value. fn compute_bool_condition_semantic( diff --git a/crates/cairo-lang-semantic/src/expr/semantic_test_data/use b/crates/cairo-lang-semantic/src/expr/semantic_test_data/use index 6bf094af7b0..13a6f96fe21 100644 --- a/crates/cairo-lang-semantic/src/expr/semantic_test_data/use +++ b/crates/cairo-lang-semantic/src/expr/semantic_test_data/use @@ -102,3 +102,913 @@ error: Multiple definitions of constant "A". --> lib.cairo:5:7 const A: u8 = 4; ^ + +//! > ========================================================================== + +//! > Test use type struct usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +mod X { + struct R { + a: u8, + } +} +use X::R as RR; + +//! > function_body + +//! > expr_code +{ + let _y = RR { a: 3 }; + use X::R; + let _x = R { a: 4 }; +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: StructCtor( + ExprStructCtor { + concrete_struct_id: test::X::R, + members: [ + ( + MemberId(test::X::a), + Literal( + ExprLiteral { + value: 3, + ty: core::integer::u8, + }, + ), + ), + ], + base_struct: None, + ty: test::X::R, + }, + ), + }, + ), + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: StructCtor( + ExprStructCtor { + concrete_struct_id: test::X::R, + members: [ + ( + MemberId(test::X::a), + Literal( + ExprLiteral { + value: 4, + ty: core::integer::u8, + }, + ), + ), + ], + base_struct: None, + ty: test::X::R, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Test use type struct generic usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +mod X { + struct R { + a: u8, + } +} +use X::R as RR; + +//! > function_body + +//! > expr_code +{ + let _y = RR:: { a: 3 }; + use X::R; + let _x = R:: { a: 4 }; +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: StructCtor( + ExprStructCtor { + concrete_struct_id: test::X::R::, + members: [ + ( + MemberId(test::X::a), + Literal( + ExprLiteral { + value: 3, + ty: core::integer::u8, + }, + ), + ), + ], + base_struct: None, + ty: test::X::R::, + }, + ), + }, + ), + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: StructCtor( + ExprStructCtor { + concrete_struct_id: test::X::R::, + members: [ + ( + MemberId(test::X::a), + Literal( + ExprLiteral { + value: 4, + ty: core::integer::u8, + }, + ), + ), + ], + base_struct: None, + ty: test::X::R::, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Test use type enum usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +mod X { + enum R { + A: u8, + B: u16, + } +} +use X::R as RR; + +//! > function_body + +//! > expr_code +{ + let _y = RR::A(2); + use X::R; + let _x = R::B(3); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: R::A, + value_expr: Literal( + ExprLiteral { + value: 2, + ty: core::integer::u8, + }, + ), + ty: test::X::R, + }, + ), + }, + ), + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: R::B, + value_expr: Literal( + ExprLiteral { + value: 3, + ty: core::integer::u16, + }, + ), + ty: test::X::R, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Test use type enum generic usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +mod X { + enum R { + A: u8, + B: u16, + } +} +use X::R as RR; + +//! > function_body + +//! > expr_code +{ + let _y = RR::::A(2); + use X::R; + let _x = R::::B(3); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: R::A, + value_expr: Literal( + ExprLiteral { + value: 2, + ty: core::integer::u8, + }, + ), + ty: test::X::R::, + }, + ), + }, + ), + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: R::B, + value_expr: Literal( + ExprLiteral { + value: 3, + ty: core::integer::u16, + }, + ), + ty: test::X::R::, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Test use type extern usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +mod X { + extern type R; + extern fn new_r() -> R nopanic; +} +use X::R as RR; +use X::new_r; + +//! > function_body + +//! > expr_code +{ + let _y: RR = new_r(); + use X::R; + let _x: R = new_r(); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: FunctionCall( + ExprFunctionCall { + function: test::X::new_r, + args: [], + coupon_arg: None, + ty: test::X::R, + }, + ), + }, + ), + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: FunctionCall( + ExprFunctionCall { + function: test::X::new_r, + args: [], + coupon_arg: None, + ty: test::X::R, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Test use type extern generic usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: warnings_only) + +//! > module_code +mod X { + extern type R; + extern fn new_r() -> R nopanic; +} +use X::R as RR; +use X::new_r; + +//! > function_body + +//! > expr_code +{ + let y: RR:: = new_r(); + use X::R; + let x: R:: = new_r(); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + y, + ), + expr: FunctionCall( + ExprFunctionCall { + function: test::X::new_r::, + args: [], + coupon_arg: None, + ty: test::X::R::, + }, + ), + }, + ), + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + x, + ), + expr: FunctionCall( + ExprFunctionCall { + function: test::X::new_r::, + args: [], + coupon_arg: None, + ty: test::X::R::, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:9:9 + let y: RR:: = new_r(); + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:11:9 + let x: R:: = new_r(); + ^ + +//! > ========================================================================== + +//! > Test use type enum and const shadowing + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: true) + +//! > module_code +mod X { + enum R { + A: u8, + B: u16, + } +} + +mod R { + pub const C: u8 = 2; +} + +//! > function_body + +//! > expr_code +{ + use X::R; + let _y = R::C; + let _x = R::B(3); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: Missing( + ExprMissing { + ty: , + }, + ), + }, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: R::B, + value_expr: Literal( + ExprLiteral { + value: 3, + ty: core::integer::u16, + }, + ), + ty: test::X::R, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics +error: Enum "test::X::R" has no variant "C" + --> lib.cairo:14:17 + let _y = R::C; + ^ + +//! > ========================================================================== + +//! > Test use type enum and const together + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: true) + +//! > module_code +mod X { + enum R { + A: u8, + B: u16, + } +} + +const R: u8 = 3; + +//! > function_body + +//! > expr_code +{ + let _y = R; + let _x = R::B(3); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: Constant( + ExprConstant { + const_value_id: 3, + ty: core::integer::u8, + }, + ), + }, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: Missing( + ExprMissing { + ty: , + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics +error: Invalid path. + --> lib.cairo:12:17 + let _x = R::B(3); + ^ + +//! > ========================================================================== + +//! > Test use type enum and const inside statement + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +mod X { + enum R { + A: u8, + B: u16, + } +} + +use X::R; + +//! > function_body + +//! > expr_code +{ + const R: u8 = 3; + + let _y = R; + let _x = R::B(3); +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + _y, + ), + expr: Constant( + ExprConstant { + const_value_id: 3, + ty: core::integer::u8, + }, + ), + }, + ), + Let( + StatementLet { + pattern: Variable( + _x, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: R::B, + value_expr: Literal( + ExprLiteral { + value: 3, + ty: core::integer::u16, + }, + ), + ty: test::X::R, + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Testing use type generic enum + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > module_code +pub mod generic_type { + pub struct S { + pub x: u8, + } + pub enum E { + A: u8, + B: u16, + } +} + +use generic_type::E as EE; + +//! > function_body + +//! > expr_code +{ + use generic_type::E; + let b = EE::B(3); + match b { + EE::A(_) => {}, + EE::B(_) => {}, + } + let e = E::A(1); + match e { + E::A(_) => {}, + E::B(_) => {}, + } +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Item( + StatementItem, + ), + Let( + StatementLet { + pattern: Variable( + b, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: E::B, + value_expr: Literal( + ExprLiteral { + value: 3, + ty: core::integer::u16, + }, + ), + ty: test::generic_type::E, + }, + ), + }, + ), + Expr( + StatementExpr { + expr: Match( + ExprMatch { + matched_expr: Var( + LocalVarId(test::b), + ), + arms: [ + MatchArm { + patterns: [ + EnumVariant( + PatternEnumVariant { + variant: E::A, + inner_pattern: Some( + Otherwise( + PatternOtherwise { + ty: core::integer::u8, + }, + ), + ), + ty: test::generic_type::E, + }, + ), + ], + expression: Block( + ExprBlock { + statements: [], + tail: None, + ty: (), + }, + ), + }, + MatchArm { + patterns: [ + EnumVariant( + PatternEnumVariant { + variant: E::B, + inner_pattern: Some( + Otherwise( + PatternOtherwise { + ty: core::integer::u16, + }, + ), + ), + ty: test::generic_type::E, + }, + ), + ], + expression: Block( + ExprBlock { + statements: [], + tail: None, + ty: (), + }, + ), + }, + ], + ty: (), + }, + ), + }, + ), + Let( + StatementLet { + pattern: Variable( + e, + ), + expr: EnumVariantCtor( + ExprEnumVariantCtor { + variant: E::A, + value_expr: Literal( + ExprLiteral { + value: 1, + ty: core::integer::u8, + }, + ), + ty: test::generic_type::E, + }, + ), + }, + ), + ], + tail: Some( + Match( + ExprMatch { + matched_expr: Var( + LocalVarId(test::e), + ), + arms: [ + MatchArm { + patterns: [ + EnumVariant( + PatternEnumVariant { + variant: E::A, + inner_pattern: Some( + Otherwise( + PatternOtherwise { + ty: core::integer::u8, + }, + ), + ), + ty: test::generic_type::E, + }, + ), + ], + expression: Block( + ExprBlock { + statements: [], + tail: None, + ty: (), + }, + ), + }, + MatchArm { + patterns: [ + EnumVariant( + PatternEnumVariant { + variant: E::B, + inner_pattern: Some( + Otherwise( + PatternOtherwise { + ty: core::integer::u16, + }, + ), + ), + ty: test::generic_type::E, + }, + ), + ], + expression: Block( + ExprBlock { + statements: [], + tail: None, + ty: (), + }, + ), + }, + ], + ty: (), + }, + ), + ), + ty: (), + }, +) + +//! > expected_diagnostics diff --git a/crates/cairo-lang-semantic/src/items/impl_alias.rs b/crates/cairo-lang-semantic/src/items/impl_alias.rs index 6a0be4e48e4..659b53c71f4 100644 --- a/crates/cairo-lang-semantic/src/items/impl_alias.rs +++ b/crates/cairo-lang-semantic/src/items/impl_alias.rs @@ -268,6 +268,7 @@ pub fn impl_alias_impl_def(db: &dyn SemanticGroup, impl_alias_id: ImplAliasId) - &mut diagnostics, &impl_path_syntax, NotFoundItemType::Impl, + None, ) { Ok(ResolvedGenericItem::Impl(imp)) => Ok(imp), Ok(ResolvedGenericItem::GenericImplAlias(impl_alias)) => db.impl_alias_impl_def(impl_alias), diff --git a/crates/cairo-lang-semantic/src/items/mod.rs b/crates/cairo-lang-semantic/src/items/mod.rs index a37224fe3bc..0add5ac7d23 100644 --- a/crates/cairo-lang-semantic/src/items/mod.rs +++ b/crates/cairo-lang-semantic/src/items/mod.rs @@ -46,6 +46,7 @@ fn resolve_trait_path( diagnostics, trait_path_syntax, NotFoundItemType::Trait, + None, )?, ResolvedGenericItem::Trait ) diff --git a/crates/cairo-lang-semantic/src/items/us.rs b/crates/cairo-lang-semantic/src/items/us.rs index d944d7cd78d..909195ae2f1 100644 --- a/crates/cairo-lang-semantic/src/items/us.rs +++ b/crates/cairo-lang-semantic/src/items/us.rs @@ -38,8 +38,12 @@ pub fn priv_use_semantic_data(db: &dyn SemanticGroup, use_id: UseId) -> Maybe Resolver<'db> { diagnostics: &mut SemanticDiagnostics, path: impl AsSegments, item_type: NotFoundItemType, + statement_env: Option<&mut Environment>, mut callbacks: ResolvePathInnerCallbacks< ResolvedItem, impl FnMut( &mut Resolver<'_>, &mut SemanticDiagnostics, &mut Peekable>, + Option<&mut Environment>, ) -> Maybe, impl FnMut( &mut Resolver<'_>, @@ -311,8 +314,12 @@ impl<'db> Resolver<'db> { let mut segments = elements_vec.iter().peekable(); // Find where the first segment lies in. - let mut item: ResolvedItem = - (callbacks.resolve_path_first_segment)(self, diagnostics, &mut segments)?; + let mut item: ResolvedItem = (callbacks.resolve_path_first_segment)( + self, + diagnostics, + &mut segments, + statement_env, + )?; // Follow modules. while let Some(segment) = segments.next() { @@ -338,20 +345,38 @@ impl<'db> Resolver<'db> { /// Resolves a concrete item, given a path. /// Guaranteed to result in at most one diagnostic. + /// Item not inside a statement. pub fn resolve_concrete_path( &mut self, diagnostics: &mut SemanticDiagnostics, path: impl AsSegments, item_type: NotFoundItemType, + ) -> Maybe { + self.resolve_concrete_path_ex(diagnostics, path, item_type, None) + } + + /// Resolves a concrete item, given a path. + /// Guaranteed to result in at most one diagnostic. + pub fn resolve_concrete_path_ex( + &mut self, + diagnostics: &mut SemanticDiagnostics, + path: impl AsSegments, + item_type: NotFoundItemType, + statement_env: Option<&mut Environment>, ) -> Maybe { self.resolve_path_inner::( diagnostics, path, item_type, + statement_env, ResolvePathInnerCallbacks { resolved_item_type: PhantomData, - resolve_path_first_segment: |resolver, diagnostics, segments| { - resolver.resolve_concrete_path_first_segment(diagnostics, segments) + resolve_path_first_segment: |resolver, diagnostics, segments, statement_env| { + resolver.resolve_concrete_path_first_segment( + diagnostics, + segments, + statement_env, + ) }, resolve_path_next_segment: |resolver, diagnostics, item, segment, item_type| { resolver.resolve_path_next_segment_concrete( @@ -369,11 +394,38 @@ impl<'db> Resolver<'db> { ) } + fn generic_arg_to_concrete( + &mut self, + segment: &ast::PathSegment, + diagnostics: &mut SemanticDiagnostics, + identifier: &ast::TerminalIdentifier, + inner_generic_item: ResolvedGenericItem, + generic_args_syntax: Option>, + ) -> ResolvedConcreteItem { + let segment_stable_ptr = segment.stable_ptr().untyped(); + let specialized_item = self + .specialize_generic_module_item( + diagnostics, + identifier, + inner_generic_item.clone(), + generic_args_syntax.clone(), + ) + .unwrap(); + self.warn_same_impl_trait( + diagnostics, + &specialized_item, + &generic_args_syntax.unwrap_or_default(), + segment_stable_ptr, + ); + specialized_item + } + /// Resolves the first segment of a concrete path. fn resolve_concrete_path_first_segment( &mut self, diagnostics: &mut SemanticDiagnostics, segments: &mut Peekable>, + statement_env: Option<&mut Environment>, ) -> Maybe { if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) { return Ok(ResolvedConcreteItem::Module(base_module?)); @@ -385,12 +437,25 @@ impl<'db> Resolver<'db> { syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => { let identifier = generic_segment.ident(syntax_db); // Identifier with generic args cannot be a local item. - if let ResolvedBase::Module(module_id) = self.determine_base(&identifier) { - ResolvedConcreteItem::Module(module_id) - } else { - // Crates do not have generics. - return Err(diagnostics - .report(&generic_segment.generic_args(syntax_db), UnexpectedGenericArgs)); + match self.determine_base(&identifier, statement_env) { + ResolvedBase::Module(module_id) => ResolvedConcreteItem::Module(module_id), + ResolvedBase::Crate(_) => { + // Crates do not have generics. + return Err(diagnostics.report( + &generic_segment.generic_args(syntax_db), + UnexpectedGenericArgs, + )); + } + ResolvedBase::StatementEnvironment(generic_item) => { + let segment = segments.next().unwrap(); + self.generic_arg_to_concrete( + segment, + diagnostics, + &identifier, + generic_item, + segment.generic_args(syntax_db), + ) + } } } syntax::node::ast::PathSegment::Simple(simple_segment) => { @@ -407,7 +472,7 @@ impl<'db> Resolver<'db> { if let Some(local_item) = self.determine_base_item_in_local_scope(&identifier) { self.resolved_items.mark_concrete(db, segments.next().unwrap(), local_item) } else { - match self.determine_base(&identifier) { + match self.determine_base(&identifier, statement_env) { // This item lies inside a module. ResolvedBase::Module(module_id) => ResolvedConcreteItem::Module(module_id), ResolvedBase::Crate(crate_id) => self.resolved_items.mark_concrete( @@ -415,6 +480,16 @@ impl<'db> Resolver<'db> { segments.next().unwrap(), ResolvedConcreteItem::Module(ModuleId::CrateRoot(crate_id)), ), + ResolvedBase::StatementEnvironment(generic_item) => { + let segment = segments.next().unwrap(); + self.generic_arg_to_concrete( + segment, + diagnostics, + &identifier, + generic_item, + segment.generic_args(syntax_db), + ) + } } } } @@ -428,8 +503,9 @@ impl<'db> Resolver<'db> { diagnostics: &mut SemanticDiagnostics, path: impl AsSegments, item_type: NotFoundItemType, + statement_env: Option<&mut Environment>, ) -> Maybe { - self.resolve_generic_path_inner(diagnostics, path, item_type, false) + self.resolve_generic_path_inner(diagnostics, path, item_type, false, statement_env) } /// Resolves a generic item, given a concrete item path, while ignoring the generic args. /// Guaranteed to result in at most one diagnostic. @@ -438,8 +514,9 @@ impl<'db> Resolver<'db> { diagnostics: &mut SemanticDiagnostics, path: impl AsSegments, item_type: NotFoundItemType, + statement_env: Option<&mut Environment>, ) -> Maybe { - self.resolve_generic_path_inner(diagnostics, path, item_type, true) + self.resolve_generic_path_inner(diagnostics, path, item_type, true, statement_env) } /// Resolves a generic item, given a path. @@ -452,6 +529,7 @@ impl<'db> Resolver<'db> { path: impl AsSegments, item_type: NotFoundItemType, allow_generic_args: bool, + statement_env: Option<&mut Environment>, ) -> Maybe { let validate_segment = |diagnostics: &mut SemanticDiagnostics, segment: &ast::PathSegment| match segment { @@ -464,13 +542,15 @@ impl<'db> Resolver<'db> { diagnostics, path, item_type, + statement_env, ResolvePathInnerCallbacks { resolved_item_type: PhantomData, - resolve_path_first_segment: |resolver, diagnostics, segments| { + resolve_path_first_segment: |resolver, diagnostics, segments, statement_env| { resolver.resolve_generic_path_first_segment( diagnostics, segments, allow_generic_args, + statement_env, ) }, resolve_path_next_segment: |resolver, diagnostics, item, segment, item_type| { @@ -497,6 +577,7 @@ impl<'db> Resolver<'db> { diagnostics: &mut SemanticDiagnostics, segments: &mut Peekable>, allow_generic_args: bool, + statement_env: Option<&mut Environment>, ) -> Maybe { if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) { return Ok(ResolvedGenericItem::Module(base_module?)); @@ -511,17 +592,21 @@ impl<'db> Resolver<'db> { } let identifier = generic_segment.ident(syntax_db); // Identifier with generic args cannot be a local item. - if let ResolvedBase::Module(module_id) = self.determine_base(&identifier) { - ResolvedGenericItem::Module(module_id) - } else { - // Crates do not have generics. - return Err(diagnostics - .report(&generic_segment.generic_args(syntax_db), UnexpectedGenericArgs)); + match self.determine_base(&identifier, statement_env) { + ResolvedBase::Module(module_id) => ResolvedGenericItem::Module(module_id), + ResolvedBase::Crate(_) => { + // Crates do not have generics. + return Err(diagnostics.report( + &generic_segment.generic_args(syntax_db), + UnexpectedGenericArgs, + )); + } + ResolvedBase::StatementEnvironment(generic_item) => generic_item, } } syntax::node::ast::PathSegment::Simple(simple_segment) => { let identifier = simple_segment.ident(syntax_db); - match self.determine_base(&identifier) { + match self.determine_base(&identifier, statement_env) { // This item lies inside a module. ResolvedBase::Module(module_id) => ResolvedGenericItem::Module(module_id), ResolvedBase::Crate(crate_id) => self.resolved_items.mark_generic( @@ -529,6 +614,10 @@ impl<'db> Resolver<'db> { segments.next().unwrap(), ResolvedGenericItem::Module(ModuleId::CrateRoot(crate_id)), ), + ResolvedBase::StatementEnvironment(generic_item) => { + segments.next(); + generic_item + } } } }) @@ -583,12 +672,13 @@ impl<'db> Resolver<'db> { if ident == SUPER_KW { return Err(diagnostics.report(identifier, InvalidPath)); } + let segment_stable_ptr = segment.stable_ptr().untyped(); + let inner_item_info = self .db .module_item_info_by_name(*module_id, ident)? .ok_or_else(|| diagnostics.report(identifier, PathNotFound(item_type)))?; - let segment_stable_ptr = segment.stable_ptr().untyped(); self.validate_item_usability(diagnostics, *module_id, identifier, &inner_item_info); self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id)); let inner_generic_item = @@ -979,10 +1069,20 @@ impl<'db> Resolver<'db> { /// Determines the base module or crate for the path resolving. Looks only in non-local scope /// (i.e. current module, or crates). - fn determine_base(&mut self, identifier: &ast::TerminalIdentifier) -> ResolvedBase { + fn determine_base( + &mut self, + identifier: &ast::TerminalIdentifier, + statement_env: Option<&mut Environment>, + ) -> ResolvedBase { let syntax_db = self.db.upcast(); let ident = identifier.text(syntax_db); + if let Some(env) = statement_env { + if let Some(inner_generic_arg) = get_statement_item_by_name(env, &ident) { + return ResolvedBase::StatementEnvironment(inner_generic_arg); + } + } + // If an item with this name is found inside the current module, use the current module. if let Ok(Some(_)) = self.db.module_item_by_name(self.module_file_id.0, ident.clone()) { return ResolvedBase::Module(self.module_file_id.0); @@ -1033,7 +1133,6 @@ impl<'db> Resolver<'db> { .db .trait_generic_params(trait_id) .map_err(|_| diagnostics.report(stable_ptr, UnknownTrait))?; - let generic_args = self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?; @@ -1053,7 +1152,6 @@ impl<'db> Resolver<'db> { .db .impl_def_generic_params(impl_def_id) .map_err(|_| diagnostics.report(stable_ptr, UnknownImpl))?; - let generic_args = self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?; @@ -1070,7 +1168,6 @@ impl<'db> Resolver<'db> { ) -> Maybe { // TODO(lior): Should we report diagnostic if `impl_def_generic_params` failed? let generic_params: Vec<_> = generic_function.generic_params(self.db)?; - let generic_args = self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?; @@ -1090,7 +1187,6 @@ impl<'db> Resolver<'db> { .db .generic_type_generic_params(generic_type) .map_err(|_| diagnostics.report(stable_ptr, UnknownType))?; - let generic_args = self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?; @@ -1114,6 +1210,9 @@ impl<'db> Resolver<'db> { lookup_context } + /// Resolves generic arguments. + /// For each generic argument, if the syntax is provided, it will be resolved by the inference. + /// Otherwise, resolved by type. pub fn resolve_generic_args( &mut self, diagnostics: &mut SemanticDiagnostics, @@ -1212,6 +1311,9 @@ impl<'db> Resolver<'db> { Ok(arg_syntax_per_param) } + /// Resolves a generic argument. + /// If no syntax Expr is provided, inference will be used. + /// If a syntax Expr is provided, it will be resolved by type. fn resolve_generic_arg( &mut self, generic_param: GenericParam, @@ -1273,7 +1375,7 @@ impl<'db> Resolver<'db> { let expr_path = try_extract_matches!(generic_arg_syntax, ast::Expr::Path) .ok_or_else(|| diagnostics.report(generic_arg_syntax, UnknownImpl))?; let resolved_impl = try_extract_matches!( - self.resolve_concrete_path(diagnostics, expr_path, NotFoundItemType::Impl,)?, + self.resolve_concrete_path(diagnostics, expr_path, NotFoundItemType::Impl)?, ResolvedConcreteItem::Impl ) .ok_or_else(|| diagnostics.report(generic_arg_syntax, UnknownImpl))?; @@ -1511,7 +1613,6 @@ impl<'db> Resolver<'db> { if current_segment_generic_args.len() < generic_params.len() { return Err(diagnostics.report(segment_stable_ptr, must_be_explicit_error)); } - let resolved_args = self.resolve_generic_args( diagnostics, &generic_params, @@ -1582,6 +1683,8 @@ enum ResolvedBase { Module(ModuleId), /// The base module is a crate. Crate(CrateId), + /// The base module to address is the statement + StatementEnvironment(ResolvedGenericItem), } /// The callbacks to be used by `resolve_path_inner`. @@ -1591,6 +1694,7 @@ where &mut Resolver<'_>, &mut SemanticDiagnostics, &mut Peekable>, + Option<&mut Environment>, ) -> Maybe, ResolveNext: FnMut( &mut Resolver<'_>, diff --git a/crates/cairo-lang-semantic/src/types.rs b/crates/cairo-lang-semantic/src/types.rs index 4db682ec2a3..d93fb6f6fa2 100644 --- a/crates/cairo-lang-semantic/src/types.rs +++ b/crates/cairo-lang-semantic/src/types.rs @@ -476,14 +476,26 @@ impl DebugWithDb for ImplTypeId { } // TODO(spapini): add a query wrapper. -/// Resolves a type given a module and a path. +/// Resolves a type given a module and a path. Used for resolving from non-statement context. pub fn resolve_type( db: &dyn SemanticGroup, diagnostics: &mut SemanticDiagnostics, resolver: &mut Resolver<'_>, ty_syntax: &ast::Expr, ) -> TypeId { - maybe_resolve_type(db, diagnostics, resolver, ty_syntax) + maybe_resolve_type(db, diagnostics, resolver, ty_syntax, None) + .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added)) +} +/// Resolves a type given a module and a path. `statement_env` should be provided if called from +/// statement context. +pub fn resolve_type_with_environment( + db: &dyn SemanticGroup, + diagnostics: &mut SemanticDiagnostics, + resolver: &mut Resolver<'_>, + ty_syntax: &ast::Expr, + statement_env: Option<&mut Environment>, +) -> TypeId { + maybe_resolve_type(db, diagnostics, resolver, ty_syntax, statement_env) .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added)) } pub fn maybe_resolve_type( @@ -491,39 +503,69 @@ pub fn maybe_resolve_type( diagnostics: &mut SemanticDiagnostics, resolver: &mut Resolver<'_>, ty_syntax: &ast::Expr, + mut statement_env: Option<&mut Environment>, ) -> Maybe { let syntax_db = db.upcast(); Ok(match ty_syntax { ast::Expr::Path(path) => { - match resolver.resolve_concrete_path(diagnostics, path, NotFoundItemType::Type)? { + match resolver.resolve_concrete_path_ex( + diagnostics, + path, + NotFoundItemType::Type, + statement_env, + )? { ResolvedConcreteItem::Type(ty) => ty, _ => { return Err(diagnostics.report(path, NotAType)); } } } - ast::Expr::Parenthesized(expr_syntax) => { - resolve_type(db, diagnostics, resolver, &expr_syntax.expr(syntax_db)) - } + ast::Expr::Parenthesized(expr_syntax) => resolve_type_with_environment( + db, + diagnostics, + resolver, + &expr_syntax.expr(syntax_db), + statement_env, + ), ast::Expr::Tuple(tuple_syntax) => { let sub_tys = tuple_syntax .expressions(syntax_db) .elements(syntax_db) .into_iter() - .map(|subexpr_syntax| resolve_type(db, diagnostics, resolver, &subexpr_syntax)) + .map(|subexpr_syntax| { + resolve_type_with_environment( + db, + diagnostics, + resolver, + &subexpr_syntax, + statement_env.as_deref_mut(), + ) + }) .collect(); TypeLongId::Tuple(sub_tys).intern(db) } ast::Expr::Unary(unary_syntax) if matches!(unary_syntax.op(syntax_db), ast::UnaryOperator::At(_)) => { - let ty = resolve_type(db, diagnostics, resolver, &unary_syntax.expr(syntax_db)); + let ty = resolve_type_with_environment( + db, + diagnostics, + resolver, + &unary_syntax.expr(syntax_db), + statement_env, + ); TypeLongId::Snapshot(ty).intern(db) } ast::Expr::Unary(unary_syntax) if matches!(unary_syntax.op(syntax_db), ast::UnaryOperator::Desnap(_)) => { - let ty = resolve_type(db, diagnostics, resolver, &unary_syntax.expr(syntax_db)); + let ty = resolve_type_with_environment( + db, + diagnostics, + resolver, + &unary_syntax.expr(syntax_db), + statement_env, + ); if let Some(desnapped_ty) = try_extract_matches!(ty.lookup_intern(db), TypeLongId::Snapshot) { @@ -536,7 +578,7 @@ pub fn maybe_resolve_type( let [ty] = &array_syntax.exprs(syntax_db).elements(syntax_db)[..] else { return Err(diagnostics.report(ty_syntax, FixedSizeArrayTypeNonSingleType)); }; - let ty = resolve_type(db, diagnostics, resolver, ty); + let ty = resolve_type_with_environment(db, diagnostics, resolver, ty, statement_env); let size = match extract_fixed_size_array_size(db, diagnostics, array_syntax, resolver)? { Some(size) => size,