diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index c4e8614a474..f0f5e60460c 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -2516,10 +2516,12 @@ fn connect_type_id<'eng: 'cfg, 'cfg>( graph.add_edge(entry_node, enum_idx, "".into()); } for p in &decl.generic_parameters { - let p = p - .as_type_parameter() - .expect("only works with type parameters"); - connect_type_id(engines, p.type_id, graph, entry_node)?; + match p { + crate::TypeParameter::Type(p) => { + connect_type_id(engines, p.type_id, graph, entry_node)?; + } + crate::TypeParameter::Const(_) => {} + } } } TypeInfo::Struct(decl_ref) => { diff --git a/sway-core/src/ir_generation.rs b/sway-core/src/ir_generation.rs index 296bfbb2ef8..09ca2a7e899 100644 --- a/sway-core/src/ir_generation.rs +++ b/sway-core/src/ir_generation.rs @@ -74,13 +74,14 @@ impl CompiledFunctionCache { let new_callee = match item { Some(func) => func, None => { + let name = Ident::new(Span::from_string(format!( + "{}_{}", + decl.name, + context.get_unique_symbol_id() + ))); let callee_fn_decl = ty::TyFunctionDecl { type_parameters: Vec::new(), - name: Ident::new(Span::from_string(format!( - "{}_{}", - decl.name, - context.get_unique_symbol_id() - ))), + name, parameters: decl.parameters.clone(), ..decl.clone() }; diff --git a/sway-core/src/ir_generation/types.rs b/sway-core/src/ir_generation/types.rs index f0c1c35b194..c87f2749850 100644 --- a/sway-core/src/ir_generation/types.rs +++ b/sway-core/src/ir_generation/types.rs @@ -22,12 +22,12 @@ pub(super) fn create_tagged_union_type( // getting one here anyway. They don't need to be a tagged union either. let field_types: Vec<_> = variants .iter() - .map(|tev| { + .map(|variant| { convert_resolved_typeid_no_span( type_engine, decl_engine, context, - tev.type_argument.type_id(), + variant.type_argument.type_id(), ) }) .collect::, CompileError>>()?; diff --git a/sway-core/src/language/ty/ast_node.rs b/sway-core/src/language/ty/ast_node.rs index 78485b16d8f..4f029ee8577 100644 --- a/sway-core/src/language/ty/ast_node.rs +++ b/sway-core/src/language/ty/ast_node.rs @@ -10,6 +10,7 @@ use crate::{ type_system::*, types::*, }; +use ast_elements::type_parameter::ConstGenericExpr; use serde::{Deserialize, Serialize}; use std::{ fmt::{self, Debug}, @@ -148,9 +149,24 @@ impl MaterializeConstGenerics for TyAstNode { value: &TyExpression, ) -> Result<(), ErrorEmitted> { match &mut self.content { - TyAstNodeContent::Declaration(TyDecl::VariableDecl(decl)) => decl - .body - .materialize_const_generics(engines, handler, name, value), + TyAstNodeContent::Declaration(TyDecl::VariableDecl(decl)) => { + decl.body + .materialize_const_generics(engines, handler, name, value)?; + decl.return_type + .materialize_const_generics(engines, handler, name, value)?; + match &mut decl.type_ascription { + GenericArgument::Type(arg) => arg + .type_id + .materialize_const_generics(engines, handler, name, value)?, + GenericArgument::Const(arg) => { + if matches!(&arg.expr, ConstGenericExpr::AmbiguousVariableExpression { ident } if ident.as_str() == name) + { + arg.expr = ConstGenericExpr::from_ty_expression(handler, value)?; + } + } + } + Ok(()) + } TyAstNodeContent::Expression(expr) => { expr.materialize_const_generics(engines, handler, name, value) } diff --git a/sway-core/src/language/ty/declaration/enum.rs b/sway-core/src/language/ty/declaration/enum.rs index f63e7e423b5..839fc882dd1 100644 --- a/sway-core/src/language/ty/declaration/enum.rs +++ b/sway-core/src/language/ty/declaration/enum.rs @@ -6,6 +6,7 @@ use crate::{ transform, type_system::*, }; +use ast_elements::type_parameter::ConstGenericExpr; use monomorphization::MonomorphizeHelper; use serde::{Deserialize, Serialize}; use std::{ @@ -107,11 +108,31 @@ impl MonomorphizeHelper for TyEnumDecl { impl MaterializeConstGenerics for TyEnumDecl { fn materialize_const_generics( &mut self, - _engines: &Engines, - _handler: &Handler, - _name: &str, - _value: &crate::language::ty::TyExpression, + engines: &Engines, + handler: &Handler, + name: &str, + value: &crate::language::ty::TyExpression, ) -> Result<(), ErrorEmitted> { + for p in self.generic_parameters.iter_mut() { + match p { + TypeParameter::Const(p) if p.name.as_str() == name => { + p.expr = Some(ConstGenericExpr::from_ty_expression(handler, value)?); + } + TypeParameter::Type(p) => { + p.type_id + .materialize_const_generics(engines, handler, name, value)?; + } + _ => {} + } + } + + for variant in self.variants.iter_mut() { + variant + .type_argument + .type_id_mut() + .materialize_const_generics(engines, handler, name, value)?; + } + Ok(()) } } diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index 7d859df844f..f0e5b3c3ca1 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -793,7 +793,9 @@ impl TyFunctionSig { TyFunctionSigTypeParameter::Const(p) => { match p { ConstGenericExpr::Literal { val, .. } => val.to_string(), - ConstGenericExpr::AmbiguousVariableExpression { .. } => todo!(), + ConstGenericExpr::AmbiguousVariableExpression { ident } => { + ident.as_str().to_string() + } } } }) diff --git a/sway-core/src/language/ty/declaration/struct.rs b/sway-core/src/language/ty/declaration/struct.rs index 4993f8b1e69..7417d0860cb 100644 --- a/sway-core/src/language/ty/declaration/struct.rs +++ b/sway-core/src/language/ty/declaration/struct.rs @@ -101,19 +101,27 @@ impl MonomorphizeHelper for TyStructDecl { impl MaterializeConstGenerics for TyStructDecl { fn materialize_const_generics( &mut self, - _engines: &Engines, - _handler: &Handler, - _name: &str, - _value: &crate::language::ty::TyExpression, + engines: &Engines, + handler: &Handler, + name: &str, + value: &crate::language::ty::TyExpression, ) -> Result<(), ErrorEmitted> { for p in self.generic_parameters.iter_mut() { match p { - TypeParameter::Const(p) if p.name.as_str() == _name => { - p.expr = Some(ConstGenericExpr::from_ty_expression(_handler, _value)?); + TypeParameter::Const(p) if p.name.as_str() == name => { + p.expr = Some(ConstGenericExpr::from_ty_expression(handler, value)?); } _ => {} } } + + for field in self.fields.iter_mut() { + field + .type_argument + .type_id_mut() + .materialize_const_generics(engines, handler, name, value)?; + } + Ok(()) } } diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 41a39d0d721..96308ddf983 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -313,10 +313,12 @@ impl CollectTypesMetadata for TyExpression { } => { let enum_decl = decl_engine.get_enum(enum_ref); for p in enum_decl.generic_parameters.iter() { - let p = p - .as_type_parameter() - .expect("only works for type parameters"); - ctx.call_site_insert(p.type_id, call_path_binding.inner.suffix.span()) + match p { + TypeParameter::Type(p) => { + ctx.call_site_insert(p.type_id, call_path_binding.inner.suffix.span()) + } + TypeParameter::Const(_) => {} + } } if let Some(contents) = contents { res.append(&mut contents.collect_types_metadata(handler, ctx)?); @@ -330,10 +332,12 @@ impl CollectTypesMetadata for TyExpression { ); } for p in enum_decl.generic_parameters.iter() { - let p = p - .as_type_parameter() - .expect("only works for type parameters"); - res.append(&mut p.type_id.collect_types_metadata(handler, ctx)?); + match p { + TypeParameter::Type(p) => { + res.append(&mut p.type_id.collect_types_metadata(handler, ctx)?); + } + TypeParameter::Const(_) => {} + } } } AbiCast { address, .. } => { @@ -488,6 +492,65 @@ impl MaterializeConstGenerics for TyExpression { TyExpressionVariant::Deref(r) => { r.materialize_const_generics(engines, handler, name, value) } + TyExpressionVariant::MatchExp { desugared, .. } => { + desugared.materialize_const_generics(engines, handler, name, value) + } + TyExpressionVariant::EnumInstantiation { contents, .. } => { + if let Some(contents) = contents.as_mut() { + contents.materialize_const_generics(engines, handler, name, value)?; + } + Ok(()) + } + TyExpressionVariant::EnumTag { exp } => { + exp.materialize_const_generics(engines, handler, name, value) + } + TyExpressionVariant::Tuple { fields } => { + for f in fields { + f.materialize_const_generics(engines, handler, name, value)?; + } + Ok(()) + } + TyExpressionVariant::TupleElemAccess { + prefix, + resolved_type_of_parent, + .. + } => { + prefix.materialize_const_generics(engines, handler, name, value)?; + resolved_type_of_parent + .materialize_const_generics(engines, handler, name, value)?; + Ok(()) + } + TyExpressionVariant::LazyOperator { lhs, rhs, .. } => { + lhs.materialize_const_generics(engines, handler, name, value)?; + rhs.materialize_const_generics(engines, handler, name, value) + } + TyExpressionVariant::AsmExpression { registers, .. } => { + for r in registers.iter_mut() { + if let Some(init) = r.initializer.as_mut() { + init.materialize_const_generics(engines, handler, name, value)?; + } + } + Ok(()) + } + TyExpressionVariant::ConstantExpression { .. } => Ok(()), + TyExpressionVariant::StructExpression { fields, .. } => { + for f in fields { + f.value + .materialize_const_generics(engines, handler, name, value)?; + } + Ok(()) + } + TyExpressionVariant::StructFieldAccess { + prefix, + resolved_type_of_parent, + .. + } => { + prefix.materialize_const_generics(engines, handler, name, value)?; + resolved_type_of_parent.materialize_const_generics(engines, handler, name, value) + } + TyExpressionVariant::UnsafeDowncast { exp, .. } => { + exp.materialize_const_generics(engines, handler, name, value) + } _ => Err(handler.emit_err( sway_error::error::CompileError::ConstGenericNotSupportedHere { span: self.span.clone(), diff --git a/sway-core/src/semantic_analysis/ast_node/code_block.rs b/sway-core/src/semantic_analysis/ast_node/code_block.rs index 6bdaf82e3f0..987fd1d2f5c 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -75,7 +75,7 @@ impl ty::TyCodeBlock { Ok(()) })?; - ctx.engines.te().reapply_unifications(ctx.engines()); + ctx.engines.te().reapply_unifications(ctx.engines(), 0); ctx.by_ref() .scoped(handler, Some(code_block.span()), |ctx| { diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs index e1c1bee2cba..3358efae03a 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs @@ -31,22 +31,24 @@ where type_parameters: &[TypeParameter], body: String, ) -> String { + let type_parameters_declaration_expanded = + self.generate_type_parameters_declaration_code(type_parameters, true); let type_parameters_declaration = - self.generate_type_parameters_declaration_code(type_parameters); + self.generate_type_parameters_declaration_code(type_parameters, false); let type_parameters_constraints = self.generate_type_parameters_constraints_code(type_parameters, Some("AbiEncode")); let name = name.as_raw_ident_str(); if body.is_empty() { - format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} AbiEncode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ + format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration_expanded} AbiEncode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ #[allow(dead_code, deprecated)] fn abi_encode(self, buffer: Buffer) -> Buffer {{ buffer }} }}") } else { - format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} AbiEncode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ + format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration_expanded} AbiEncode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ #[allow(dead_code, deprecated)] fn abi_encode(self, buffer: Buffer) -> Buffer {{ {body} @@ -62,22 +64,24 @@ where type_parameters: &[TypeParameter], body: String, ) -> String { + let type_parameters_declaration_expanded = + self.generate_type_parameters_declaration_code(type_parameters, true); let type_parameters_declaration = - self.generate_type_parameters_declaration_code(type_parameters); + self.generate_type_parameters_declaration_code(type_parameters, false); let type_parameters_constraints = self.generate_type_parameters_constraints_code(type_parameters, Some("AbiDecode")); let name = name.as_raw_ident_str(); if body == "Self { }" { - format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} AbiDecode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ + format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration_expanded} AbiDecode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ #[allow(dead_code, deprecated)] fn abi_decode(ref mut _buffer: BufferReader) -> Self {{ {body} }} }}") } else { - format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} AbiDecode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ + format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration_expanded} AbiDecode for {name}{type_parameters_declaration}{type_parameters_constraints} {{ #[allow(dead_code, deprecated)] fn abi_decode(ref mut buffer: BufferReader) -> Self {{ {body} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs index 55cc55f9eae..3540bccb3a2 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs @@ -73,14 +73,16 @@ where type_parameters: &[TypeParameter], body: String, ) -> String { + let type_parameters_declaration_expanded = + self.generate_type_parameters_declaration_code(type_parameters, true); let type_parameters_declaration = - self.generate_type_parameters_declaration_code(type_parameters); + self.generate_type_parameters_declaration_code(type_parameters, false); let type_parameters_constraints = self.generate_type_parameters_constraints_code(type_parameters, Some("Debug")); let name = name.as_raw_ident_str(); - format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} Debug for {name}{type_parameters_declaration}{type_parameters_constraints} {{ + format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration_expanded} Debug for {name}{type_parameters_declaration}{type_parameters_constraints} {{ #[allow(dead_code, deprecated)] fn fmt(self, ref mut _f: Formatter) {{ {body} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs index b4984d853d6..585b8e66800 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs @@ -45,15 +45,17 @@ where return None; } + let type_parameters_declaration_expanded = + self.generate_type_parameters_declaration_code(&enum_decl.generic_parameters, true); let type_parameters_declaration = - self.generate_type_parameters_declaration_code(&enum_decl.generic_parameters); + self.generate_type_parameters_declaration_code(&enum_decl.generic_parameters, false); let type_parameters_constraints = self.generate_type_parameters_constraints_code( &enum_decl.generic_parameters, extra_constraint, ); let impl_marker_trait_code = format!( - "#[allow(dead_code, deprecated)] impl{type_parameters_declaration} {marker_trait_name} for {}{type_parameters_declaration}{type_parameters_constraints} {{ }}", + "#[allow(dead_code, deprecated)] impl{type_parameters_declaration_expanded} {marker_trait_name} for {}{type_parameters_declaration}{type_parameters_constraints} {{ }}", enum_decl.name().as_raw_ident_str() ); diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs index 121f5d0f45f..6aeec23840a 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs @@ -77,10 +77,13 @@ where r.unwrap() } - /// Generates code like: ``. + /// Generates code like: + /// if expanded_const_generics == false, ``. + /// if expanded_const_generics == true, ``. fn generate_type_parameters_declaration_code( &self, type_parameters: &[TypeParameter], + expanded_const_generics: bool, ) -> String { let mut code = String::new(); code.push('<'); @@ -88,7 +91,13 @@ where for p in type_parameters { match p { TypeParameter::Type(p) => code.push_str(p.name.as_str()), - TypeParameter::Const(p) => code.push_str(p.name.as_str()), + TypeParameter::Const(p) => { + if expanded_const_generics { + code.push_str(&format!("const {}: u64", p.name.as_str())); + } else { + code.push_str(p.name.as_str()) + } + } } code.push_str(", "); } @@ -242,7 +251,9 @@ where _ => unreachable!("unexpected node; expected `Declaration::ImplSelfOrTrait`"), }; - assert!(!handler.has_errors(), "{:?}", handler); + if handler.has_errors() { + return Err(handler); + } let mut ctx = self.ctx.by_ref(); let _r = TyDecl::collect( diff --git a/sway-core/src/type_system/ast_elements/type_argument.rs b/sway-core/src/type_system/ast_elements/type_argument.rs index 56ad9a48437..433f3f7e343 100644 --- a/sway-core/src/type_system/ast_elements/type_argument.rs +++ b/sway-core/src/type_system/ast_elements/type_argument.rs @@ -76,6 +76,13 @@ impl GenericArgument { .type_id } + pub fn type_id_mut(&mut self) -> &mut TypeId { + &mut self + .as_type_argument_mut() + .expect("only works with type arguments") + .type_id + } + pub fn initial_type_id(&self) -> TypeId { self.as_type_argument() .expect("only works with type arguments") @@ -100,13 +107,6 @@ impl GenericArgument { } } - pub fn type_id_mut(&mut self) -> &mut TypeId { - &mut self - .as_type_argument_mut() - .expect("only works with type arguments") - .type_id - } - /// Returns true if `self` is annotated by having either /// its [Self::initial_type_id] different from [Self::type_id], /// or [Self::span] different from [Span::dummy] diff --git a/sway-core/src/type_system/ast_elements/type_parameter.rs b/sway-core/src/type_system/ast_elements/type_parameter.rs index 91081f1f022..281b8a2f1c5 100644 --- a/sway-core/src/type_system/ast_elements/type_parameter.rs +++ b/sway-core/src/type_system/ast_elements/type_parameter.rs @@ -2,6 +2,7 @@ use crate::{ abi_generation::abi_str::AbiStrContext, decl_engine::{ parsed_id::ParsedDeclId, DeclEngineInsert as _, DeclMapping, InterfaceItemMap, ItemMap, + ParsedDeclEngineGet as _, }, engine_threading::*, has_changes, @@ -1050,14 +1051,15 @@ pub struct ConstGenericParameter { } impl HashWithEngines for ConstGenericParameter { - fn hash(&self, state: &mut H, _: &Engines) { - match &self.expr { - Some(expr) => { - expr.hash(state); - } - None => { - self.name.hash(state); - } + fn hash(&self, state: &mut H, engines: &Engines) { + let ConstGenericParameter { name, ty, id, .. } = self; + let type_engine = engines.te(); + type_engine.get(*ty).hash(state, engines); + name.hash(state); + if let Some(id) = id.as_ref() { + let decl = engines.pe().get(id); + decl.name.hash(state); + decl.ty.hash(state); } } } diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 88300fa54d5..977602761ef 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -978,7 +978,8 @@ impl TypeEngine { | TypeInfo::Placeholder(_) | TypeInfo::UnknownGeneric { .. } | TypeInfo::Array(.., Length(ConstGenericExpr::AmbiguousVariableExpression { .. })) - | TypeInfo::Struct(_) => true, + | TypeInfo::Struct(_) + | TypeInfo::Enum(_) => true, TypeInfo::ContractCaller { abi_name, address } => { Self::is_replaceable_contract_caller(abi_name, address) } @@ -2171,7 +2172,11 @@ impl TypeEngine { self.unifications.clear(); } - pub(crate) fn reapply_unifications(&self, engines: &Engines) { + pub(crate) fn reapply_unifications(&self, engines: &Engines, depth: usize) { + if depth > 2000 { + panic!("Possible infinite recursion"); + } + let current_last_replace = *self.last_replace.read(); for unification in self.unifications.values() { Self::unify_helper( @@ -2187,7 +2192,7 @@ impl TypeEngine { ) } if *self.last_replace.read() > current_last_replace { - self.reapply_unifications(engines); + self.reapply_unifications(engines, depth + 1); } } diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 265aed836d3..1352acb9116 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -8,7 +8,7 @@ use sway_error::{ use sway_types::{BaseIdent, Named, Span, Spanned}; use crate::{ - decl_engine::{DeclEngineGet, MaterializeConstGenerics}, + decl_engine::{DeclEngineGet, DeclEngineInsert, MaterializeConstGenerics}, engine_threading::{DebugWithEngines, DisplayWithEngines, Engines, WithEngines}, language::CallPath, namespace::TraitMap, @@ -118,7 +118,7 @@ impl MaterializeConstGenerics for TypeId { fn materialize_const_generics( &mut self, engines: &Engines, - _handler: &Handler, + handler: &Handler, name: &str, value: &crate::language::ty::TyExpression, ) -> Result<(), ErrorEmitted> { @@ -136,16 +136,23 @@ impl MaterializeConstGenerics for TypeId { } }; - let new_array = engines.te().insert_array( + *self = engines.te().insert_array( engines, type_argument.clone(), Length(ConstGenericExpr::Literal { val: val as usize, - span: Span::dummy(), + span: value.span.clone(), }), ); + } + TypeInfo::Enum(id) => { + let decl = engines.de().get(id); + let mut decl = (*decl).clone(); + decl.materialize_const_generics(engines, handler, name, value)?; + + let decl_ref = engines.de().insert(decl, None); - *self = new_array; + *self = engines.te().insert_enum(engines, *decl_ref.id()); } _ => {} } @@ -355,20 +362,31 @@ impl TypeId { .iter() .zip(orig_enum_decl.generic_parameters.iter()) { - let orig_type_param = orig_type_param - .as_type_parameter() - .expect("only works with type parameters"); - let type_param = type_param - .as_type_parameter() - .expect("only works with type parameters"); - type_parameters.push((type_param.type_id, orig_type_param.type_id)); - type_param.type_id.extract_type_parameters( - engines, - depth + 1, - type_parameters, - const_generic_parameters, - orig_type_param.type_id, - ); + match (orig_type_param, type_param) { + (TypeParameter::Type(orig_type_param), TypeParameter::Type(type_param)) => { + type_parameters.push((type_param.type_id, orig_type_param.type_id)); + type_param.type_id.extract_type_parameters( + engines, + depth + 1, + type_parameters, + const_generic_parameters, + orig_type_param.type_id, + ); + } + ( + TypeParameter::Const(orig_type_param), + TypeParameter::Const(type_param), + ) => match (orig_type_param.expr.as_ref(), type_param.expr.as_ref()) { + (None, Some(expr)) => { + const_generic_parameters.insert( + orig_type_param.name.as_str().to_string(), + expr.to_ty_expression(engines), + ); + } + _ => todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860"), + }, + _ => {} + } } } (TypeInfo::Struct(struct_id), TypeInfo::Struct(orig_struct_id)) => { @@ -941,10 +959,11 @@ impl TypeId { } TypeInfo::Enum(enum_ref) => { let enum_decl = decl_engine.get_enum(enum_ref); - for type_param in &enum_decl.generic_parameters { - let type_param = type_param - .as_type_parameter() - .expect("only works with type parameters"); + let type_params = enum_decl + .generic_parameters + .iter() + .filter_map(|x| x.as_type_parameter()); + for type_param in type_params { extend( &mut found, type_param.type_id.extract_any_including_self( diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index bc12f1a2364..bf00efec162 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -765,7 +765,18 @@ impl DebugWithEngines for TypeInfo { decl.call_path.suffix.as_str(), decl.generic_parameters.iter().map(|x| match x { TypeParameter::Type(p) => engines.help_out(p.type_id).to_string(), - TypeParameter::Const(p) => p.name.as_str().to_string(), + TypeParameter::Const(p) => { + if let Some(expr) = p.expr.as_ref() { + match expr { + ConstGenericExpr::Literal { val, .. } => val.to_string(), + ConstGenericExpr::AmbiguousVariableExpression { ident } => { + ident.as_str().to_string() + } + } + } else { + p.name.as_str().to_string() + } + } }), ) } @@ -1793,10 +1804,10 @@ impl TypeInfo { decl.generic_parameters .iter() .map(|p| { - let p = p - .as_type_parameter() - .expect("only works with type parameters"); - p.type_id.get_type_str(engines) + match p { + TypeParameter::Type(p) => p.type_id.get_type_str(engines), + TypeParameter::Const(p) => p.name.as_str().to_string(), + } }) .collect::>() .join(",") diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index a9196643e3c..df27e7d2b89 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -511,9 +511,9 @@ impl TypeSubstMap { } for type_param in &mut decl.generic_parameters { - let type_param = type_param - .as_type_parameter_mut() - .expect("only works with type parameters"); + let Some(type_param) = type_param.as_type_parameter_mut() else { + continue; + }; if let Some(type_id) = self.find_match(type_param.type_id, engines) { need_to_create_new = true; type_param.type_id = type_id; diff --git a/sway-core/src/type_system/unify/unifier.rs b/sway-core/src/type_system/unify/unifier.rs index 31cc62455b8..9c46bf8ec39 100644 --- a/sway-core/src/type_system/unify/unifier.rs +++ b/sway-core/src/type_system/unify/unifier.rs @@ -10,7 +10,7 @@ use crate::{ ast_elements::type_parameter::ConstGenericExpr, decl_engine::{DeclEngineGet, DeclId}, engine_threading::{Engines, PartialEqWithEngines, PartialEqWithEnginesContext, WithEngines}, - language::{ty::TyStructDecl, CallPath}, + language::ty::{TyEnumDecl, TyStructDecl}, type_system::{engine::Unification, priv_prelude::*}, }; @@ -139,7 +139,7 @@ impl<'a> Unifier<'a> { (Tuple(rfs), Tuple(efs)) if rfs.len() == efs.len() => { self.unify_tuples(handler, rfs, efs); } - (r @ Array(re, rc), e @ Array(ee, ec)) => { + (Array(re, rc), Array(ee, ec)) => { if self .unify_type_arguments_in_parents(handler, received, expected, span, re, ee) .is_err() @@ -147,18 +147,31 @@ impl<'a> Unifier<'a> { return; } - if matches!( - rc.expr(), - ConstGenericExpr::AmbiguousVariableExpression { .. } - ) { - self.replace_received_with_expected(received, e, span); - } - - if matches!( - ec.expr(), - ConstGenericExpr::AmbiguousVariableExpression { .. } - ) { - self.replace_expected_with_received(expected, r, span); + match (rc.expr(), ec.expr()) { + ( + ConstGenericExpr::Literal { val: r_eval, .. }, + ConstGenericExpr::Literal { val: e_eval, .. }, + ) => { + assert!(r_eval == e_eval); + } + ( + ConstGenericExpr::Literal { .. }, + ConstGenericExpr::AmbiguousVariableExpression { .. }, + ) => { + todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860") + } + ( + ConstGenericExpr::AmbiguousVariableExpression { .. }, + ConstGenericExpr::Literal { .. }, + ) => { + todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860") + } + ( + ConstGenericExpr::AmbiguousVariableExpression { ident: r_ident }, + ConstGenericExpr::AmbiguousVariableExpression { ident: e_ident }, + ) => { + assert!(r_ident.as_str() == e_ident.as_str()); + } } } (Slice(re), Slice(ee)) => { @@ -256,17 +269,7 @@ impl<'a> Unifier<'a> { (_, Alias { ty, .. }) => self.unify(handler, received, ty.type_id(), span, false), (Enum(r_decl_ref), Enum(e_decl_ref)) => { - let r_decl = self.engines.de().get_enum(r_decl_ref); - let e_decl = self.engines.de().get_enum(e_decl_ref); - - self.unify_enums( - handler, - received, - expected, - span, - (r_decl.call_path.clone(), r_decl.generic_parameters.clone()), - (e_decl.call_path.clone(), e_decl.generic_parameters.clone()), - ); + self.unify_enums(handler, received, expected, span, r_decl_ref, e_decl_ref); } // For integers and numerics, we (potentially) unify the numeric @@ -475,26 +478,80 @@ impl<'a> Unifier<'a> { fn unify_enums( &self, handler: &Handler, - received: TypeId, - expected: TypeId, + received_type_id: TypeId, + expected_type_id: TypeId, span: &Span, - r: (CallPath, Vec), - e: (CallPath, Vec), + received_decl_id: &DeclId, + expected_decl_id: &DeclId, ) { - let (rn, rtps) = r; - let (en, etps) = e; - if rn == en && rtps.len() == etps.len() { - rtps.iter().zip(etps.iter()).for_each(|(rtp, etp)| { - let rtp = rtp - .as_type_parameter() - .expect("will only work with type parameters"); - let etp = etp - .as_type_parameter() - .expect("will only work with type parameters"); - self.unify(handler, rtp.type_id, etp.type_id, span, false); - }); + let TyEnumDecl { + call_path: received_call_path, + generic_parameters: received_parameters, + .. + } = &*self.engines.de().get(received_decl_id); + let TyEnumDecl { + call_path: expected_call_path, + generic_parameters: expected_parameters, + .. + } = &*self.engines.de().get(expected_decl_id); + + if received_parameters.len() == expected_parameters.len() + && received_call_path == expected_call_path + { + for (received_parameter, expected_parameter) in + received_parameters.iter().zip(expected_parameters.iter()) + { + match (received_parameter, expected_parameter) { + ( + TypeParameter::Type(received_parameter), + TypeParameter::Type(expected_parameter), + ) => self.unify( + handler, + received_parameter.type_id, + expected_parameter.type_id, + span, + false, + ), + ( + TypeParameter::Const(received_parameter), + TypeParameter::Const(expected_parameter), + ) => { + match (received_parameter.expr.as_ref(), expected_parameter.expr.as_ref()) { + (Some(r), Some(e)) => { + match (r.as_literal_val(), e.as_literal_val()) { + (Some(r), Some(e)) if r == e => {}, + _ => todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860"), + } + } + (Some(_), None) => { + self.replace_expected_with_received( + expected_type_id, + &TypeInfo::Enum(*received_decl_id), + span, + ); + } + (None, Some(_)) => { + self.replace_received_with_expected( + received_type_id, + &TypeInfo::Enum(*expected_decl_id), + span, + ); + } + (None, None) => { + if received_parameter.name == expected_parameter.name { + } else { + todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860") + } + }, + } + } + _ => { + todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860") + } + } + } } else { - let (received, expected) = self.assign_args(received, expected); + let (received, expected) = self.assign_args(received_type_id, expected_type_id); handler.emit_err( TypeError::MismatchedType { expected, diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 1a8bd2286ce..acd764a9a23 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -797,27 +797,38 @@ impl<'a> UnifyCheck<'a> { return false; } - let l_types = left - .generic_parameters - .iter() - .map(|x| { - let x = x - .as_type_parameter() - .expect("will only work with type parameters"); - x.type_id - }) - .collect::>(); - - let r_types = right + let mut l_types = vec![]; + let mut r_types = vec![]; + + for (l, r) in left .generic_parameters .iter() - .map(|x| { - let x = x - .as_type_parameter() - .expect("will only work with type parameters"); - x.type_id - }) - .collect::>(); + .zip(right.generic_parameters.iter()) + { + match (l, r) { + (TypeParameter::Type(l), TypeParameter::Type(r)) => { + l_types.push(l.type_id); + r_types.push(r.type_id); + } + (TypeParameter::Const(l), TypeParameter::Const(r)) => { + match (l.expr.as_ref(), r.expr.as_ref()) { + (None, None) => {} + (None, Some(_)) => {} + (Some(_), None) => {} + ( + Some(ConstGenericExpr::Literal { val: l_val, .. }), + Some(ConstGenericExpr::Literal { val: r_val, .. }), + ) => { + assert!(l_val == r_val); + } + (Some(_), Some(_)) => todo!( + "Will be implemented by https://github.com/FuelLabs/sway/issues/6860" + ), + } + } + _ => return false, + } + } self.check_multiple(&l_types, &r_types) } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/snapshot.toml index 63480f8a132..82778cd8ca8 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/snapshot.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/snapshot.toml @@ -1,4 +1,4 @@ cmds = [ "forc build --path {root} --release", "forc test --path {root} --experimental const_generics --test-threads 1 --logs --revert-codes", -] \ No newline at end of file +] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw index da91ef27c19..f390350b71b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw @@ -7,9 +7,20 @@ trait A { fn my_len(self) -> u64; } +enum LotsOfVariants { + A: u64, + B: u64, + C: u64, + D: u64, +} + impl A for [T; N] { fn my_len(self) -> u64 { - N + match LotsOfVariants::A(N) { + LotsOfVariants::A(_) => N, + LotsOfVariants::B(_) | LotsOfVariants::C(_) => N, + _ => N, + } } } @@ -22,6 +33,29 @@ impl S { } } +// Enum with just one variant +enum OneVariant { + A: [u64; N], +} + +impl OneVariant { + pub fn return_n(self) -> u64 { + N + } +} + +// Enum with more than one variant +enum TwoVariants { + Nothing: (), + Array: [T; N] +} + +impl TwoVariants { + pub fn len_xxx2(self) -> u64 { + N + } +} + #[inline(never)] fn return_n() -> u64 { NNN @@ -39,6 +73,20 @@ fn main(a: [u64; 2]) { let s: S = S { }; let _ = __dbg(s.len_xxx()); + // Check enum with just one variant, with + // all types explicit + let e: OneVariant<3> = OneVariant::<3>::A([1u64, 2u64, 3u64]); + assert(e.return_n() == 3); + let _ = __dbg(e); + + // Check enum with more than one variant, with + // all types explicit + let e: TwoVariants = TwoVariants::::Nothing; + let _ = __dbg(e); + let b = e.len_xxx2(); + assert(b == 3); + //__dbg(e); + let _ = __dbg(return_n::<3>()); let _ = __dbg(return_n::<5>()); } @@ -46,4 +94,4 @@ fn main(a: [u64; 2]) { #[test] fn run_main() { main([1, 2]); -} \ No newline at end of file +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/stdout.snap index 1431f4fe991..a770ab7d417 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/stdout.snap @@ -7,55 +7,115 @@ output: Building test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics Compiling library std (sway-lib-std) Compiling script const_generics (test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics) +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:50:5 + | +48 | enum TwoVariants { +49 | Nothing: (), +50 | Array: [T; N] + | ----- Enum variant Array is never constructed. +51 | } +52 | + | +____ + error - --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:10:15 + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:17:15 | - 8 | } - 9 | -10 | impl A for [T; N] { +15 | } +16 | +17 | impl A for [T; N] { | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. -11 | fn my_len(self) -> u64 { -12 | N +18 | fn my_len(self) -> u64 { +19 | match LotsOfVariants::A(N) { | ____ error - --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:16:19 + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:27:19 | -14 | } -15 | -16 | struct S { +25 | } +26 | +27 | struct S { | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. -17 | } -18 | +28 | } +29 | + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:30:15 + | +28 | } +29 | +30 | impl S { + | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. +31 | pub fn len_xxx(self) -> u64 { +32 | N + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:37:23 + | +35 | +36 | // Enum with just one variant +37 | enum OneVariant { + | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. +38 | A: [u64; N], +39 | } + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:41:12 + | +39 | } +40 | +41 | impl OneVariant { + | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. +42 | pub fn return_n(self) -> u64 { +43 | N | ____ error - --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:19:15 + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:48:27 + | +46 | +47 | // Enum with more than one variant +48 | enum TwoVariants { + | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. +49 | Nothing: (), +50 | Array: [T; N] | -17 | } -18 | -19 | impl S { +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:53:15 + | +51 | } +52 | +53 | impl TwoVariants { | ^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. -20 | pub fn len_xxx(self) -> u64 { -21 | N +54 | pub fn len_xxx2(self) -> u64 { +55 | N | ____ error - --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:26:19 + --> test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics/src/main.sw:60:19 | -24 | -25 | #[inline(never)] -26 | fn return_n() -> u64 { +58 | +59 | #[inline(never)] +60 | fn return_n() -> u64 { | ^^^ This needs "const_generics" to be enabled, but it is currently disabled. For more details go to https://github.com/FuelLabs/sway/issues/6860. -27 | NNN -28 | } +61 | NNN +62 | } | ____ - Aborting due to 4 errors. + Aborting due to 8 errors. error: Failed to compile const_generics > forc test --path test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics --experimental const_generics --test-threads 1 --logs --revert-codes @@ -64,16 +124,18 @@ output: Building test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics Compiling library std (sway-lib-std) Compiling script const_generics (test/src/e2e_vm_tests/test_programs/should_pass/language/const_generics) - Finished debug [unoptimized + fuel] target(s) [2.856 KB] in ??? + Finished debug [unoptimized + fuel] target(s) [5.048 KB] in ??? Running 1 test, filtered 0 tests tested -- const_generics - test run_main ... ok (???, 4184 gas) -[src/main.sw:31:13] a = [1, 2] -[src/main.sw:40:13] s.len_xxx() = 3 -[src/main.sw:42:13] return_n::<3>() = 3 -[src/main.sw:43:13] return_n::<5>() = 5 + test run_main ... ok (???, 8053 gas) +[src/main.sw:65:13] a = [1, 2] +[src/main.sw:74:13] s.len_xxx() = 3 +[src/main.sw:80:13] e = OneVariant([1, 2, 3]) +[src/main.sw:85:13] e = Nothing +[src/main.sw:90:13] return_n::<3>() = 3 +[src/main.sw:91:13] return_n::<5>() = 3 test result: OK. 1 passed; 0 failed; finished in ???