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-semantic/src/diagnostic.rs b/crates/cairo-lang-semantic/src/diagnostic.rs index 5447498c4c5..7daac6abc4e 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, @@ -1295,6 +1299,7 @@ impl From<&ResolvedConcreteItem> for ElementKind { ResolvedConcreteItem::Variant(_) => ElementKind::Variant, ResolvedConcreteItem::Trait(_) => ElementKind::Trait, ResolvedConcreteItem::Impl(_) => ElementKind::Impl, + ResolvedConcreteItem::Statement(_) => ElementKind::Type, } } } diff --git a/crates/cairo-lang-semantic/src/expr/compute.rs b/crates/cairo-lang-semantic/src/expr/compute.rs index 20f6a16b886..4fcbefe6916 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, @@ -77,8 +78,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::{ @@ -203,6 +204,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 types if required. + for (ty_name, statement_ty) in std::mem::take(&mut self.environment.use_types) { + if !self.environment.used_use_types.contains(&ty_name) && !ty_name.starts_with('_') { + self.diagnostics.report(statement_ty.stable_ptr, UnusedUse); + } + } self.environment = parent.unwrap(); res } @@ -278,6 +285,16 @@ impl<'ctx> ComputationContext<'ctx> { // TODO(ilya): Change value to VarId. pub type EnvVariables = OrderedHashMap; +type EnvGenericItems = 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. @@ -286,6 +303,8 @@ pub struct Environment { parent: Option>, variables: EnvVariables, used_variables: UnorderedHashSet, + use_types: EnvGenericItems, + used_use_types: UnorderedHashSet, } impl Environment { /// Adds a parameter to the environment. @@ -310,8 +329,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_types: Default::default(), + used_use_types: Default::default(), + } + } +} + +/// Returns the requested type from the environment if it exists. Returns None otherwise. +pub fn get_statement_type_by_name( + env: &mut Environment, + type_name: &SmolStr, +) -> Option { + let mut maybe_env = Some(&mut *env); + while let Some(curr_env) = maybe_env { + if let Some(var) = curr_env.use_types.get(type_name) { + curr_env.used_use_types.insert(type_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. @@ -856,8 +897,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) => { @@ -1619,11 +1664,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())) @@ -1989,6 +2035,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))?; @@ -2056,10 +2103,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 ) @@ -2359,7 +2407,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) @@ -3053,8 +3107,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 { @@ -3328,8 +3386,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()); @@ -3506,7 +3569,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, @@ -3537,6 +3606,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), @@ -3553,10 +3623,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(_) @@ -3618,6 +3695,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_types + .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..7396d2e38e9 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,776 @@ 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: warnings_only) + +//! > 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 +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:9:9 + let y = RR { a: 3 }; + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:11:9 + let x = R { a: 4 }; + ^ + +//! > ========================================================================== + +//! > Test use type struct generic usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: warnings_only) + +//! > 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 +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:9:9 + let y = RR:: { a: 3 }; + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:11:9 + let x = R:: { a: 4 }; + ^ + +//! > ========================================================================== + +//! > Test use type enum usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: warnings_only) + +//! > 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 +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:10:9 + let y = RR::A(2); + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:12:9 + let x = R::B(3); + ^ + +//! > ========================================================================== + +//! > Test use type enum generic usage + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: warnings_only) + +//! > 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 +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:10:9 + let y = RR::::A(2); + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:12:9 + let x = R::::B(3); + ^ + +//! > ========================================================================== + +//! > Test use type extern 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 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:16 + let y = R::C; + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:14:9 + let y = R::C; + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:15:9 + let x = R::B(3); + ^ + +//! > ========================================================================== + +//! > 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:16 + let x = R::B(3); + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:11:9 + let y = R; + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:12:9 + let x = R::B(3); + ^ + +//! > ========================================================================== + +//! > Test use type enum and const inside statement + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: warnings_only) + +//! > 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 +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:13:9 + let y = R; + ^ + +warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. + --> lib.cairo:14:9 + let x = R::B(3); + ^ 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 Option { Some(match self { - ResolvedConcreteItem::Constant(_) => return None, + ResolvedConcreteItem::Constant(_) | &ResolvedConcreteItem::Statement(_) => return None, ResolvedConcreteItem::Module(item) => ResolvedGenericItem::Module(*item), ResolvedConcreteItem::Function(function) => ResolvedGenericItem::GenericFunction( function.lookup_intern(db).function.generic_function, diff --git a/crates/cairo-lang-semantic/src/resolve/mod.rs b/crates/cairo-lang-semantic/src/resolve/mod.rs index 1ebd5ba6e88..19ea2c66d8a 100644 --- a/crates/cairo-lang-semantic/src/resolve/mod.rs +++ b/crates/cairo-lang-semantic/src/resolve/mod.rs @@ -30,7 +30,8 @@ use crate::db::SemanticGroup; use crate::diagnostic::SemanticDiagnosticKind::{self, *}; use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder}; use crate::expr::compute::{ - compute_expr_semantic, ComputationContext, ContextFunction, Environment, + compute_expr_semantic, get_statement_type_by_name, ComputationContext, ContextFunction, + Environment, }; use crate::expr::inference::canonic::ResultNoErrEx; use crate::expr::inference::conform::InferenceConform; @@ -281,12 +282,14 @@ impl<'db> 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<'_>, @@ -310,8 +313,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() { @@ -337,20 +344,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( @@ -373,6 +398,7 @@ impl<'db> Resolver<'db> { &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?)); @@ -384,12 +410,18 @@ 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) => { + ResolvedConcreteItem::Statement(generic_item) + } } } syntax::node::ast::PathSegment::Simple(simple_segment) => { @@ -406,7 +438,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( @@ -414,6 +446,9 @@ impl<'db> Resolver<'db> { segments.next().unwrap(), ResolvedConcreteItem::Module(ModuleId::CrateRoot(crate_id)), ), + ResolvedBase::StatementEnvironment(generic_item) => { + ResolvedConcreteItem::Statement(generic_item) + } } } } @@ -427,8 +462,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. @@ -437,8 +473,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. @@ -451,6 +488,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 { @@ -463,13 +501,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| { @@ -496,6 +536,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?)); @@ -510,17 +551,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( @@ -528,6 +573,7 @@ impl<'db> Resolver<'db> { segments.next().unwrap(), ResolvedGenericItem::Module(ModuleId::CrateRoot(crate_id)), ), + ResolvedBase::StatementEnvironment(generic_item) => generic_item, } } }) @@ -582,12 +628,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 = @@ -606,6 +653,22 @@ impl<'db> Resolver<'db> { ); Ok(specialized_item) } + ResolvedConcreteItem::Statement(inner_generic_item) => { + 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(), + )?; + self.warn_same_impl_trait( + diagnostics, + &specialized_item, + &generic_args_syntax.unwrap_or_default(), + segment_stable_ptr, + ); + Ok(specialized_item) + } ResolvedConcreteItem::Type(ty) => { if let TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)) = ty.lookup_intern(self.db) @@ -978,10 +1041,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_type_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); @@ -1028,7 +1101,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)?; @@ -1048,7 +1120,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)?; @@ -1065,7 +1136,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)?; @@ -1085,7 +1155,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)?; @@ -1109,6 +1178,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, @@ -1207,6 +1279,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, @@ -1268,7 +1343,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))?; @@ -1504,7 +1579,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, @@ -1580,6 +1654,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`. @@ -1589,6 +1665,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,