From 52ddd02b74c78f0e086871b6e33a1d8965ccb75c Mon Sep 17 00:00:00 2001 From: Marcos Henrich Date: Wed, 24 Jan 2024 14:39:34 +0000 Subject: [PATCH] Fixes stack overflow when using recursive trait. ReplaceDecls, CollectTypesMetadata, HashWithEngines now have a mechanism that interrupts recursive visiting. The mechanism works by adding TypeIds or DeclIds to a HashMap or HashSet and no longer visiting TypeIds or DeclIds on the passed collection. This was necessary because in some cases while visiting a declaration we can go back to the beginning because of recursive DeclIds. I suspect these changes will also be useful to #3018 This partially fixes #5504, which is still failing because we have yet to fix a recursion problem in the IR side of the compiler. --- sway-core/src/decl_engine/ref.rs | 63 ++++- sway-core/src/decl_engine/replace_decls.rs | 7 +- sway-core/src/engine_threading.rs | 62 ++++- sway-core/src/language/call_path.rs | 21 +- .../src/language/parsed/declaration/trait.rs | 14 +- .../src/language/parsed/expression/mod.rs | 16 +- sway-core/src/language/ty/ast_node.rs | 45 ++-- sway-core/src/language/ty/code_block.rs | 17 +- sway-core/src/language/ty/declaration/abi.rs | 18 +- .../src/language/ty/declaration/constant.rs | 21 +- .../language/ty/declaration/declaration.rs | 76 ++++-- sway-core/src/language/ty/declaration/enum.rs | 21 +- .../src/language/ty/declaration/function.rs | 61 +++-- .../src/language/ty/declaration/impl_trait.rs | 22 +- .../src/language/ty/declaration/storage.rs | 25 +- .../src/language/ty/declaration/struct.rs | 21 +- .../src/language/ty/declaration/trait.rs | 48 ++-- .../src/language/ty/declaration/trait_fn.rs | 18 +- .../src/language/ty/declaration/trait_type.rs | 10 +- .../src/language/ty/declaration/type_alias.rs | 14 +- .../src/language/ty/declaration/variable.rs | 20 +- sway-core/src/language/ty/expression/asm.rs | 14 +- .../src/language/ty/expression/expression.rs | 221 +++++++++++++----- .../ty/expression/expression_variant.rs | 171 ++++++++------ .../ty/expression/intrinsic_function.rs | 25 +- .../language/ty/expression/reassignment.rs | 29 ++- .../src/language/ty/expression/storage.rs | 25 +- .../ty/expression/struct_exp_field.rs | 20 +- sway-core/src/language/ty/program.rs | 24 +- sway-core/src/lib.rs | 3 +- .../semantic_analysis/ast_node/code_block.rs | 4 +- .../ast_node/declaration/impl_trait.rs | 14 +- .../typed_expression/function_application.rs | 7 +- .../typed_expression/method_application.rs | 7 +- .../ast_elements/trait_constraint.rs | 22 +- .../type_system/ast_elements/type_argument.rs | 13 +- .../ast_elements/type_parameter.rs | 15 +- sway-core/src/type_system/engine.rs | 7 +- sway-core/src/type_system/id.rs | 1 + sway-core/src/type_system/info.rs | 45 ++-- .../src/type_system/substitute/subst_list.rs | 10 +- sway-core/src/types/collect_types_metadata.rs | 3 +- .../language/trait_recursive/Forc.lock | 13 ++ .../language/trait_recursive/Forc.toml | 8 + .../language/trait_recursive/src/main.sw | 31 +++ .../language/trait_recursive/test.toml | 4 + 46 files changed, 995 insertions(+), 361 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/test.toml diff --git a/sway-core/src/decl_engine/ref.rs b/sway-core/src/decl_engine/ref.rs index 58c90fd256b..833007fccff 100644 --- a/sway-core/src/decl_engine/ref.rs +++ b/sway-core/src/decl_engine/ref.rs @@ -20,7 +20,10 @@ //! `fn my_function() { .. }`, and to use [DeclRef] for cases like function //! application `my_function()`. -use std::hash::{Hash, Hasher}; +use std::{ + collections::{HashMap, HashSet}, + hash::{Hash, Hasher}, +}; use sway_error::handler::{ErrorEmitted, Handler}; use sway_types::{Ident, Named, Span, Spanned}; @@ -159,20 +162,37 @@ impl DeclRef> where AssociatedItemDeclId: From>, DeclEngine: DeclEngineIndex, - T: Named + Spanned + ReplaceDecls + std::fmt::Debug + Clone, + T: Named + Spanned + ReplaceDecls + std::fmt::Debug + Clone + 'static, { pub(crate) fn replace_decls_and_insert_new_with_parent( &self, decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result { let decl_engine = ctx.engines().de(); + let key = (self.id.inner(), std::any::TypeId::of::()); + if let Some((ref_id, ref_span)) = decls_replaced.get(&key) { + return Ok(DeclRef::new( + Ident::new(ref_span.clone()), + DeclId::::new(*ref_id), + ref_span.clone(), + )); + } let mut decl = (*decl_engine.get(&self.id)).clone(); - decl.replace_decls(decl_mapping, handler, ctx)?; - Ok(decl_engine - .insert(decl) - .with_parent(decl_engine, self.id.into())) + let decl_replacement_ref = decl_engine + .insert(decl.clone()) + .with_parent(decl_engine, self.id.into()); + + let mut decls_replaced_updated = decls_replaced.clone(); + decls_replaced_updated.insert(key, (decl_replacement_ref.id().inner(), self.span())); + + decl.replace_decls(decl_mapping, handler, ctx, &mut decls_replaced_updated)?; + + decl_engine.replace(*decl_replacement_ref.id(), decl); + + Ok(decl_replacement_ref) } } @@ -214,9 +234,14 @@ where impl HashWithEngines for DeclRef> where DeclEngine: DeclEngineIndex, - T: Named + Spanned + HashWithEngines, + T: Named + Spanned + HashWithEngines + 'static, { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let decl_engine = engines.de(); let DeclRef { name, @@ -228,7 +253,15 @@ where subst_list: _, } = self; name.hash(state); - decl_engine.get(id).hash(state, engines); + let key = (id.inner(), std::any::TypeId::of::()); + if already_in_hash.contains(&key) { + return; + } + let mut already_in_hash_updated = already_in_hash.clone(); + already_in_hash_updated.insert(key); + decl_engine + .get(id) + .hash(state, engines, &mut already_in_hash_updated); } } @@ -253,19 +286,24 @@ impl PartialEqWithEngines for DeclRefMixedInterface { } impl HashWithEngines for DeclRefMixedInterface { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { match self.id { InterfaceDeclId::Abi(id) => { state.write_u8(0); let decl_engine = engines.de(); let decl = decl_engine.get(&id); - decl.hash(state, engines); + decl.hash(state, engines, already_in_hash); } InterfaceDeclId::Trait(id) => { state.write_u8(1); let decl_engine = engines.de(); let decl = decl_engine.get(&id); - decl.hash(state, engines); + decl.hash(state, engines, already_in_hash); } } } @@ -296,6 +334,7 @@ impl ReplaceDecls for DeclRefFunction { decl_mapping: &DeclMapping, _handler: &Handler, ctx: &mut TypeCheckContext, + _decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { let engines = ctx.engines(); let decl_engine = engines.de(); diff --git a/sway-core/src/decl_engine/replace_decls.rs b/sway-core/src/decl_engine/replace_decls.rs index b6eb6ff9de2..c685aeeeeea 100644 --- a/sway-core/src/decl_engine/replace_decls.rs +++ b/sway-core/src/decl_engine/replace_decls.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use sway_error::handler::{ErrorEmitted, Handler}; +use sway_types::Span; use crate::{ engine_threading::Engines, @@ -14,6 +17,7 @@ pub trait ReplaceDecls { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted>; fn replace_decls( @@ -21,9 +25,10 @@ pub trait ReplaceDecls { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { if !decl_mapping.is_empty() { - self.replace_decls_inner(decl_mapping, handler, ctx)?; + self.replace_decls_inner(decl_mapping, handler, ctx, decls_replaced)?; } Ok(()) diff --git a/sway-core/src/engine_threading.rs b/sway-core/src/engine_threading.rs index debab75360e..35c2f0bb4c9 100644 --- a/sway-core/src/engine_threading.rs +++ b/sway-core/src/engine_threading.rs @@ -1,6 +1,11 @@ -use crate::{decl_engine::DeclEngine, query_engine::QueryEngine, type_system::TypeEngine}; +use crate::{ + decl_engine::{DeclEngine, DeclIdIndexType}, + query_engine::QueryEngine, + type_system::TypeEngine, +}; use std::{ cmp::Ordering, + collections::HashSet, fmt, hash::{BuildHasher, Hash, Hasher}, }; @@ -89,7 +94,11 @@ impl fmt::Debug for WithEngines<'_, T> { impl Hash for WithEngines<'_, T> { fn hash(&self, state: &mut H) { - self.thing.hash(state, self.engines) + self.thing.hash( + state, + self.engines, + &mut HashSet::<(DeclIdIndexType, std::any::TypeId)>::new(), + ) } } @@ -194,35 +203,60 @@ impl DebugWithEngines for Vec { } pub trait HashWithEngines { - fn hash(&self, state: &mut H, engines: &Engines); + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ); } impl HashWithEngines for &T { - fn hash(&self, state: &mut H, engines: &Engines) { - (*self).hash(state, engines) + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { + (*self).hash(state, engines, already_in_hash) } } impl HashWithEngines for Option { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { match self { None => state.write_u8(0), - Some(x) => x.hash(state, engines), + Some(x) => x.hash(state, engines, already_in_hash), } } } impl HashWithEngines for [T] { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { for x in self { - x.hash(state, engines) + x.hash(state, engines, already_in_hash) } } } impl HashWithEngines for Box { - fn hash(&self, state: &mut H, engines: &Engines) { - (**self).hash(state, engines) + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { + (**self).hash(state, engines, already_in_hash) } } @@ -308,7 +342,11 @@ where { move |key: &K| { let mut state = hash_builder.build_hasher(); - key.hash(&mut state, engines); + key.hash( + &mut state, + engines, + &mut HashSet::<(DeclIdIndexType, std::any::TypeId)>::new(), + ); state.finish() } } diff --git a/sway-core/src/language/call_path.rs b/sway-core/src/language/call_path.rs index 59c70295b90..3d9952cdf0b 100644 --- a/sway-core/src/language/call_path.rs +++ b/sway-core/src/language/call_path.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + collections::HashSet, fmt, hash::{Hash, Hasher}, sync::Arc, @@ -28,13 +29,18 @@ pub struct CallPathTree { } impl HashWithEngines for CallPathTree { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let CallPathTree { qualified_call_path, children, } = self; - qualified_call_path.hash(state, engines); - children.hash(state, engines); + qualified_call_path.hash(state, engines, already_in_hash); + children.hash(state, engines, already_in_hash); } } @@ -109,13 +115,18 @@ impl QualifiedCallPath { } impl HashWithEngines for QualifiedCallPath { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let QualifiedCallPath { call_path, qualified_path_root, } = self; call_path.hash(state); - qualified_path_root.hash(state, engines); + qualified_path_root.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/parsed/declaration/trait.rs b/sway-core/src/language/parsed/declaration/trait.rs index daefb8e0561..b1edbefa1f5 100644 --- a/sway-core/src/language/parsed/declaration/trait.rs +++ b/sway-core/src/language/parsed/declaration/trait.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use super::{ConstantDeclaration, FunctionDeclaration, FunctionParameter}; @@ -57,10 +60,15 @@ impl PartialEqWithEngines for Supertrait { } impl HashWithEngines for Supertrait { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let Supertrait { name, decl_ref } = self; name.hash(state); - decl_ref.hash(state, engines); + decl_ref.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/parsed/expression/mod.rs b/sway-core/src/language/parsed/expression/mod.rs index 281b3ddf99b..9e867933885 100644 --- a/sway-core/src/language/parsed/expression/mod.rs +++ b/sway-core/src/language/parsed/expression/mod.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, fmt, hash::Hasher}; +use std::{cmp::Ordering, collections::HashSet, fmt, hash::Hasher}; use crate::{ engine_threading::{ @@ -119,15 +119,23 @@ pub struct QualifiedPathRootTypes { } impl HashWithEngines for QualifiedPathRootTypes { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let QualifiedPathRootTypes { ty, as_trait, // ignored fields as_trait_span: _, } = self; - ty.hash(state, engines); - engines.te().get(*as_trait).hash(state, engines); + ty.hash(state, engines, already_in_hash); + engines + .te() + .get(*as_trait) + .hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/ty/ast_node.rs b/sway-core/src/language/ty/ast_node.rs index c7704b43b2b..362ad12aca1 100644 --- a/sway-core/src/language/ty/ast_node.rs +++ b/sway-core/src/language/ty/ast_node.rs @@ -1,4 +1,5 @@ use std::{ + collections::{HashMap, HashSet}, fmt::{self, Debug}, hash::{Hash, Hasher}, }; @@ -37,14 +38,19 @@ impl PartialEqWithEngines for TyAstNode { } impl HashWithEngines for TyAstNode { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyAstNode { content, // the span is not hashed because it isn't relevant/a reliable // source of obj v. obj distinction span: _, } = self; - content.hash(state, engines); + content.hash(state, engines, already_in_hash); } } @@ -81,17 +87,18 @@ impl ReplaceDecls for TyAstNode { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { match self.content { TyAstNodeContent::ImplicitReturnExpression(ref mut exp) => { - exp.replace_decls(decl_mapping, handler, ctx) - } - TyAstNodeContent::Declaration(TyDecl::VariableDecl(ref mut decl)) => { - decl.body.replace_decls(decl_mapping, handler, ctx) + exp.replace_decls(decl_mapping, handler, ctx, decls_replaced) } + TyAstNodeContent::Declaration(TyDecl::VariableDecl(ref mut decl)) => decl + .body + .replace_decls(decl_mapping, handler, ctx, decls_replaced), TyAstNodeContent::Declaration(_) => Ok(()), TyAstNodeContent::Expression(ref mut expr) => { - expr.replace_decls(decl_mapping, handler, ctx) + expr.replace_decls(decl_mapping, handler, ctx, decls_replaced) } TyAstNodeContent::SideEffect(_) => Ok(()), TyAstNodeContent::Error(_, _) => Ok(()), @@ -140,8 +147,10 @@ impl CollectTypesMetadata for TyAstNode { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { - self.content.collect_types_metadata(handler, ctx) + self.content + .collect_types_metadata(handler, ctx, already_collected) } } @@ -472,15 +481,20 @@ impl PartialEqWithEngines for TyAstNodeContent { } impl HashWithEngines for TyAstNodeContent { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { use TyAstNodeContent::*; std::mem::discriminant(self).hash(state); match self { Declaration(decl) => { - decl.hash(state, engines); + decl.hash(state, engines, already_in_hash); } Expression(exp) | ImplicitReturnExpression(exp) => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } SideEffect(effect) => { effect.hash(state); @@ -533,12 +547,15 @@ impl CollectTypesMetadata for TyAstNodeContent { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { use TyAstNodeContent::*; match self { - Declaration(decl) => decl.collect_types_metadata(handler, ctx), - Expression(expr) => expr.collect_types_metadata(handler, ctx), - ImplicitReturnExpression(expr) => expr.collect_types_metadata(handler, ctx), + Declaration(decl) => decl.collect_types_metadata(handler, ctx, already_collected), + Expression(expr) => expr.collect_types_metadata(handler, ctx, already_collected), + ImplicitReturnExpression(expr) => { + expr.collect_types_metadata(handler, ctx, already_collected) + } SideEffect(_) => Ok(vec![]), Error(_, _) => Ok(vec![]), } diff --git a/sway-core/src/language/ty/code_block.rs b/sway-core/src/language/ty/code_block.rs index 5a39682c1b8..7786cf774e8 100644 --- a/sway-core/src/language/ty/code_block.rs +++ b/sway-core/src/language/ty/code_block.rs @@ -1,4 +1,7 @@ -use std::hash::Hasher; +use std::{ + collections::{HashMap, HashSet}, + hash::Hasher, +}; use sway_error::handler::{ErrorEmitted, Handler}; use sway_types::Span; @@ -31,9 +34,14 @@ impl PartialEqWithEngines for TyCodeBlock { } impl HashWithEngines for TyCodeBlock { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyCodeBlock { contents, .. } = self; - contents.hash(state, engines); + contents.hash(state, engines, already_in_hash); } } @@ -51,10 +59,11 @@ impl ReplaceDecls for TyCodeBlock { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { handler.scope(|handler| { for x in self.contents.iter_mut() { - match x.replace_decls(decl_mapping, handler, ctx) { + match x.replace_decls(decl_mapping, handler, ctx, decls_replaced) { Ok(res) => res, Err(_) => { continue; diff --git a/sway-core/src/language/ty/declaration/abi.rs b/sway-core/src/language/ty/declaration/abi.rs index 91840b50130..602c28163bb 100644 --- a/sway-core/src/language/ty/declaration/abi.rs +++ b/sway-core/src/language/ty/declaration/abi.rs @@ -1,5 +1,8 @@ use crate::{engine_threading::*, language::parsed, transform, type_system::*}; -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_types::{Ident, Named, Span, Spanned}; @@ -47,7 +50,12 @@ impl PartialEqWithEngines for TyAbiDecl { } impl HashWithEngines for TyAbiDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyAbiDecl { name, interface_surface, @@ -59,9 +67,9 @@ impl HashWithEngines for TyAbiDecl { span: _, } = self; name.hash(state); - interface_surface.hash(state, engines); - items.hash(state, engines); - supertraits.hash(state, engines); + interface_surface.hash(state, engines, already_in_hash); + items.hash(state, engines, already_in_hash); + supertraits.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/ty/declaration/constant.rs b/sway-core/src/language/ty/declaration/constant.rs index 2930e3e3cb5..f4eb75f5de4 100644 --- a/sway-core/src/language/ty/declaration/constant.rs +++ b/sway-core/src/language/ty/declaration/constant.rs @@ -1,4 +1,5 @@ use std::{ + collections::{HashMap, HashSet}, fmt, hash::{Hash, Hasher}, }; @@ -54,7 +55,12 @@ impl PartialEqWithEngines for TyConstantDecl { } impl HashWithEngines for TyConstantDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let type_engine = engines.te(); let TyConstantDecl { call_path, @@ -70,13 +76,15 @@ impl HashWithEngines for TyConstantDecl { span: _, } = self; call_path.hash(state); - value.hash(state, engines); + value.hash(state, engines, already_in_hash); visibility.hash(state); - type_engine.get(*return_type).hash(state, engines); - type_ascription.hash(state, engines); + type_engine + .get(*return_type) + .hash(state, engines, already_in_hash); + type_ascription.hash(state, engines, already_in_hash); is_configurable.hash(state); if let Some(implementing_type) = implementing_type { - (*implementing_type).hash(state, engines); + (*implementing_type).hash(state, engines, already_in_hash); } } } @@ -109,9 +117,10 @@ impl ReplaceDecls for TyConstantDecl { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { if let Some(expr) = &mut self.value { - expr.replace_decls(decl_mapping, handler, ctx) + expr.replace_decls(decl_mapping, handler, ctx, decls_replaced) } else { Ok(()) } diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index 7e41cb3c1ff..e9fad1db4a1 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, fmt, hash::{Hash, Hasher}, }; @@ -240,55 +241,82 @@ impl PartialEqWithEngines for TyDecl { } impl HashWithEngines for TyDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let decl_engine = engines.de(); let type_engine = engines.te(); std::mem::discriminant(self).hash(state); match self { TyDecl::VariableDecl(decl) => { - decl.hash(state, engines); + decl.hash(state, engines, already_in_hash); } TyDecl::ConstantDecl(ConstantDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::TraitTypeDecl(TraitTypeDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::FunctionDecl(FunctionDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::TraitDecl(TraitDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::StructDecl(StructDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::EnumDecl(EnumDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::EnumVariantDecl(EnumVariantDecl { enum_ref, variant_name, .. }) => { - enum_ref.hash(state, engines); + enum_ref.hash(state, engines, already_in_hash); variant_name.hash(state); } TyDecl::ImplTrait(ImplTrait { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::AbiDecl(AbiDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::TypeAliasDecl(TypeAliasDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::StorageDecl(StorageDecl { decl_id, .. }) => { - decl_engine.get(decl_id).hash(state, engines); + decl_engine + .get(decl_id) + .hash(state, engines, already_in_hash); } TyDecl::GenericTypeForFunctionScope(GenericTypeForFunctionScope { name, type_id }) => { name.hash(state); - type_engine.get(*type_id).hash(state, engines); + type_engine + .get(*type_id) + .hash(state, engines, already_in_hash); } TyDecl::ErrorRecovery(..) => {} } @@ -514,28 +542,30 @@ impl CollectTypesMetadata for TyDecl { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { let decl_engine = ctx.engines.de(); let metadata = match self { TyDecl::VariableDecl(decl) => { - let mut body = decl.body.collect_types_metadata(handler, ctx)?; - body.append( - &mut decl - .type_ascription - .type_id - .collect_types_metadata(handler, ctx)?, - ); + let mut body = decl + .body + .collect_types_metadata(handler, ctx, already_collected)?; + body.append(&mut decl.type_ascription.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); body } TyDecl::FunctionDecl(FunctionDecl { decl_id, .. }) => { let decl = decl_engine.get_function(decl_id); - decl.collect_types_metadata(handler, ctx)? + decl.collect_types_metadata(handler, ctx, already_collected)? } TyDecl::ConstantDecl(ConstantDecl { decl_id, .. }) => { let const_decl = decl_engine.get_constant(decl_id); let TyConstantDecl { value, .. } = &*const_decl; if let Some(value) = value { - value.collect_types_metadata(handler, ctx)? + value.collect_types_metadata(handler, ctx, already_collected)? } else { return Ok(vec![]); } diff --git a/sway-core/src/language/ty/declaration/enum.rs b/sway-core/src/language/ty/declaration/enum.rs index b5ceaea1c78..d104b648423 100644 --- a/sway-core/src/language/ty/declaration/enum.rs +++ b/sway-core/src/language/ty/declaration/enum.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + collections::HashSet, hash::{Hash, Hasher}, }; @@ -44,7 +45,12 @@ impl PartialEqWithEngines for TyEnumDecl { } impl HashWithEngines for TyEnumDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyEnumDecl { call_path, type_parameters, @@ -56,8 +62,8 @@ impl HashWithEngines for TyEnumDecl { attributes: _, } = self; call_path.suffix.hash(state); - variants.hash(state, engines); - type_parameters.hash(state, engines); + variants.hash(state, engines, already_in_hash); + type_parameters.hash(state, engines, already_in_hash); visibility.hash(state); } } @@ -130,9 +136,14 @@ pub struct TyEnumVariant { } impl HashWithEngines for TyEnumVariant { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { self.name.hash(state); - self.type_argument.hash(state, engines); + self.type_argument.hash(state, engines, already_in_hash); self.tag.hash(state); } } diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index d5b6530c81f..66aa71e7e70 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -1,5 +1,5 @@ use std::{ - collections::HashSet, + collections::{HashMap, HashSet}, fmt, hash::{Hash, Hasher}, }; @@ -111,7 +111,12 @@ impl PartialEqWithEngines for TyFunctionDecl { } impl HashWithEngines for TyFunctionDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyFunctionDecl { name, body, @@ -131,10 +136,10 @@ impl HashWithEngines for TyFunctionDecl { is_trait_method_dummy: _, } = self; name.hash(state); - body.hash(state, engines); - parameters.hash(state, engines); - return_type.hash(state, engines); - type_parameters.hash(state, engines); + body.hash(state, engines, already_in_hash); + parameters.hash(state, engines, already_in_hash); + return_type.hash(state, engines, already_in_hash); + type_parameters.hash(state, engines, already_in_hash); visibility.hash(state); is_contract_call.hash(state); purity.hash(state); @@ -160,8 +165,10 @@ impl ReplaceDecls for TyFunctionDecl { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { - self.body.replace_decls(decl_mapping, handler, ctx) + self.body + .replace_decls(decl_mapping, handler, ctx, decls_replaced) } } @@ -216,27 +223,30 @@ impl CollectTypesMetadata for TyFunctionDecl { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { let mut body = vec![]; for content in self.body.contents.iter() { - body.append(&mut content.collect_types_metadata(handler, ctx)?); + body.append(&mut content.collect_types_metadata(handler, ctx, already_collected)?); } - body.append( - &mut self - .return_type - .type_id - .collect_types_metadata(handler, ctx)?, - ); + body.append(&mut self.return_type.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); for type_param in self.type_parameters.iter() { - body.append(&mut type_param.type_id.collect_types_metadata(handler, ctx)?); + body.append(&mut type_param.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } for param in self.parameters.iter() { - body.append( - &mut param - .type_argument - .type_id - .collect_types_metadata(handler, ctx)?, - ); + body.append(&mut param.type_argument.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } Ok(body) } @@ -399,7 +409,12 @@ impl PartialEqWithEngines for TyFunctionParameter { } impl HashWithEngines for TyFunctionParameter { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyFunctionParameter { name, is_reference, @@ -410,7 +425,7 @@ impl HashWithEngines for TyFunctionParameter { mutability_span: _, } = self; name.hash(state); - type_argument.hash(state, engines); + type_argument.hash(state, engines, already_in_hash); is_reference.hash(state); is_mutable.hash(state); } diff --git a/sway-core/src/language/ty/declaration/impl_trait.rs b/sway-core/src/language/ty/declaration/impl_trait.rs index f8cddea7f05..0246b7d6615 100644 --- a/sway-core/src/language/ty/declaration/impl_trait.rs +++ b/sway-core/src/language/ty/declaration/impl_trait.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_types::{Ident, Named, Span, Spanned}; @@ -50,7 +53,12 @@ impl PartialEqWithEngines for TyImplTrait { } impl HashWithEngines for TyImplTrait { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyImplTrait { impl_type_parameters, trait_name, @@ -63,11 +71,11 @@ impl HashWithEngines for TyImplTrait { span: _, } = self; trait_name.hash(state); - impl_type_parameters.hash(state, engines); - trait_type_arguments.hash(state, engines); - items.hash(state, engines); - implementing_for.hash(state, engines); - trait_decl_ref.hash(state, engines); + impl_type_parameters.hash(state, engines, already_in_hash); + trait_type_arguments.hash(state, engines, already_in_hash); + items.hash(state, engines, already_in_hash); + implementing_for.hash(state, engines, already_in_hash); + trait_decl_ref.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/ty/declaration/storage.rs b/sway-core/src/language/ty/declaration/storage.rs index f6fd1225a28..96fea1c7b46 100644 --- a/sway-core/src/language/ty/declaration/storage.rs +++ b/sway-core/src/language/ty/declaration/storage.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_error::{ error::CompileError, @@ -32,7 +35,12 @@ impl PartialEqWithEngines for TyStorageDecl { } impl HashWithEngines for TyStorageDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStorageDecl { fields, // these fields are not hashed because they aren't relevant/a @@ -41,7 +49,7 @@ impl HashWithEngines for TyStorageDecl { attributes: _, storage_keyword: _, } = self; - fields.hash(state, engines); + fields.hash(state, engines, already_in_hash); } } @@ -187,7 +195,12 @@ impl PartialEqWithEngines for TyStorageField { } impl HashWithEngines for TyStorageField { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStorageField { name, type_argument, @@ -198,7 +211,7 @@ impl HashWithEngines for TyStorageField { attributes: _, } = self; name.hash(state); - type_argument.hash(state, engines); - initializer.hash(state, engines); + type_argument.hash(state, engines, already_in_hash); + initializer.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/ty/declaration/struct.rs b/sway-core/src/language/ty/declaration/struct.rs index e9994e96f99..ca9c2b02e14 100644 --- a/sway-core/src/language/ty/declaration/struct.rs +++ b/sway-core/src/language/ty/declaration/struct.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + collections::HashSet, hash::{Hash, Hasher}, }; @@ -44,7 +45,12 @@ impl PartialEqWithEngines for TyStructDecl { } impl HashWithEngines for TyStructDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStructDecl { call_path, fields, @@ -56,8 +62,8 @@ impl HashWithEngines for TyStructDecl { attributes: _, } = self; call_path.suffix.hash(state); - fields.hash(state, engines); - type_parameters.hash(state, engines); + fields.hash(state, engines, already_in_hash); + type_parameters.hash(state, engines, already_in_hash); visibility.hash(state); } } @@ -151,7 +157,12 @@ pub struct TyStructField { } impl HashWithEngines for TyStructField { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStructField { name, type_argument, @@ -161,7 +172,7 @@ impl HashWithEngines for TyStructField { attributes: _, } = self; name.hash(state); - type_argument.hash(state, engines); + type_argument.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/ty/declaration/trait.rs b/sway-core/src/language/ty/declaration/trait.rs index 9107e944382..713be464d10 100644 --- a/sway-core/src/language/ty/declaration/trait.rs +++ b/sway-core/src/language/ty/declaration/trait.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, fmt, hash::{Hash, Hasher}, }; @@ -105,7 +106,12 @@ impl PartialEqWithEngines for TyTraitDecl { } impl HashWithEngines for TyTraitDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyTraitDecl { name, type_parameters, @@ -121,11 +127,11 @@ impl HashWithEngines for TyTraitDecl { call_path: _, } = self; name.hash(state); - type_parameters.hash(state, engines); - self_type.hash(state, engines); - interface_surface.hash(state, engines); - items.hash(state, engines); - supertraits.hash(state, engines); + type_parameters.hash(state, engines, already_in_hash); + self_type.hash(state, engines, already_in_hash); + interface_surface.hash(state, engines, already_in_hash); + items.hash(state, engines, already_in_hash); + supertraits.hash(state, engines, already_in_hash); visibility.hash(state); } } @@ -159,21 +165,35 @@ impl PartialEqWithEngines for TyTraitItem { } impl HashWithEngines for TyTraitInterfaceItem { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { match self { - TyTraitInterfaceItem::TraitFn(fn_decl) => fn_decl.hash(state, engines), - TyTraitInterfaceItem::Constant(const_decl) => const_decl.hash(state, engines), - TyTraitInterfaceItem::Type(type_decl) => type_decl.hash(state, engines), + TyTraitInterfaceItem::TraitFn(fn_decl) => fn_decl.hash(state, engines, already_in_hash), + TyTraitInterfaceItem::Constant(const_decl) => { + const_decl.hash(state, engines, already_in_hash) + } + TyTraitInterfaceItem::Type(type_decl) => { + type_decl.hash(state, engines, already_in_hash) + } } } } impl HashWithEngines for TyTraitItem { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { match self { - TyTraitItem::Fn(fn_decl) => fn_decl.hash(state, engines), - TyTraitItem::Constant(const_decl) => const_decl.hash(state, engines), - TyTraitItem::Type(type_decl) => type_decl.hash(state, engines), + TyTraitItem::Fn(fn_decl) => fn_decl.hash(state, engines, already_in_hash), + TyTraitItem::Constant(const_decl) => const_decl.hash(state, engines, already_in_hash), + TyTraitItem::Type(type_decl) => type_decl.hash(state, engines, already_in_hash), } } } diff --git a/sway-core/src/language/ty/declaration/trait_fn.rs b/sway-core/src/language/ty/declaration/trait_fn.rs index d4a53972bcb..0103c7972bd 100644 --- a/sway-core/src/language/ty/declaration/trait_fn.rs +++ b/sway-core/src/language/ty/declaration/trait_fn.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_types::{Ident, Named, Span, Spanned}; @@ -57,7 +60,12 @@ impl PartialEqWithEngines for TyTraitFn { } impl HashWithEngines for TyTraitFn { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyTraitFn { name, purity, @@ -70,8 +78,10 @@ impl HashWithEngines for TyTraitFn { } = self; let type_engine = engines.te(); name.hash(state); - parameters.hash(state, engines); - type_engine.get(return_type.type_id).hash(state, engines); + parameters.hash(state, engines, already_in_hash); + type_engine + .get(return_type.type_id) + .hash(state, engines, already_in_hash); purity.hash(state); } } diff --git a/sway-core/src/language/ty/declaration/trait_type.rs b/sway-core/src/language/ty/declaration/trait_type.rs index 567ea4d1f14..fec3f44c382 100644 --- a/sway-core/src/language/ty/declaration/trait_type.rs +++ b/sway-core/src/language/ty/declaration/trait_type.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, fmt, hash::{Hash, Hasher}, }; @@ -38,7 +39,12 @@ impl PartialEqWithEngines for TyTraitType { } impl HashWithEngines for TyTraitType { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyTraitType { name, ty, @@ -49,7 +55,7 @@ impl HashWithEngines for TyTraitType { attributes: _, } = self; name.hash(state); - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); implementing_type.hash(state); } } diff --git a/sway-core/src/language/ty/declaration/type_alias.rs b/sway-core/src/language/ty/declaration/type_alias.rs index 99d7a6782e5..6bc89faa680 100644 --- a/sway-core/src/language/ty/declaration/type_alias.rs +++ b/sway-core/src/language/ty/declaration/type_alias.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_types::{Ident, Named, Span, Spanned}; @@ -35,7 +38,12 @@ impl PartialEqWithEngines for TyTypeAliasDecl { } impl HashWithEngines for TyTypeAliasDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyTypeAliasDecl { name, ty, @@ -47,7 +55,7 @@ impl HashWithEngines for TyTypeAliasDecl { attributes: _, } = self; name.hash(state); - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); visibility.hash(state); } } diff --git a/sway-core/src/language/ty/declaration/variable.rs b/sway-core/src/language/ty/declaration/variable.rs index e1a9fa166e8..fa527ccdf12 100644 --- a/sway-core/src/language/ty/declaration/variable.rs +++ b/sway-core/src/language/ty/declaration/variable.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_error::handler::{ErrorEmitted, Handler}; use sway_types::Ident; @@ -37,7 +40,12 @@ impl PartialEqWithEngines for TyVariableDecl { } impl HashWithEngines for TyVariableDecl { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyVariableDecl { name, body, @@ -47,9 +55,11 @@ impl HashWithEngines for TyVariableDecl { } = self; let type_engine = engines.te(); name.hash(state); - body.hash(state, engines); - type_engine.get(*return_type).hash(state, engines); - type_ascription.hash(state, engines); + body.hash(state, engines, already_in_hash); + type_engine + .get(*return_type) + .hash(state, engines, already_in_hash); + type_ascription.hash(state, engines, already_in_hash); mutability.hash(state); } } diff --git a/sway-core/src/language/ty/expression/asm.rs b/sway-core/src/language/ty/expression/asm.rs index b1e95610306..069db6a0438 100644 --- a/sway-core/src/language/ty/expression/asm.rs +++ b/sway-core/src/language/ty/expression/asm.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_types::Ident; @@ -22,11 +25,16 @@ impl PartialEqWithEngines for TyAsmRegisterDeclaration { } impl HashWithEngines for TyAsmRegisterDeclaration { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyAsmRegisterDeclaration { initializer, name } = self; name.hash(state); if let Some(x) = initializer.as_ref() { - x.hash(state, engines) + x.hash(state, engines, already_in_hash) } } } diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index c82dc35f75a..c47157a8d4e 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -1,4 +1,8 @@ -use std::{fmt, hash::Hasher}; +use std::{ + collections::{HashMap, HashSet}, + fmt, + hash::Hasher, +}; use sway_error::{ handler::{ErrorEmitted, Handler}, @@ -38,7 +42,12 @@ impl PartialEqWithEngines for TyExpression { } impl HashWithEngines for TyExpression { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyExpression { expression, return_type, @@ -47,8 +56,16 @@ impl HashWithEngines for TyExpression { span: _, } = self; let type_engine = engines.te(); - expression.hash(state, engines); - type_engine.get(*return_type).hash(state, engines); + expression.hash(state, engines, already_in_hash); + let key = (return_type.index(), std::any::TypeId::of::()); + if already_in_hash.contains(&key) { + return; + } + let mut already_in_hash_updated = already_in_hash.clone(); + already_in_hash_updated.insert(key); + type_engine + .get(*return_type) + .hash(state, engines, &mut already_in_hash_updated); } } @@ -65,8 +82,10 @@ impl ReplaceDecls for TyExpression { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { - self.expression.replace_decls(decl_mapping, handler, ctx) + self.expression + .replace_decls(decl_mapping, handler, ctx, decls_replaced) } } @@ -129,10 +148,13 @@ impl CollectTypesMetadata for TyExpression { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { use TyExpressionVariant::*; let decl_engine = ctx.engines.de(); - let mut res = self.return_type.collect_types_metadata(handler, ctx)?; + let mut res = self + .return_type + .collect_types_metadata(handler, ctx, already_collected)?; match &self.expression { FunctionApplication { arguments, @@ -141,29 +163,54 @@ impl CollectTypesMetadata for TyExpression { .. } => { for arg in arguments.iter() { - res.append(&mut arg.1.collect_types_metadata(handler, ctx)?); + res.append(&mut arg.1.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } - let function_decl = decl_engine.get_function(fn_ref); + let key = ( + fn_ref.id().inner(), + std::any::TypeId::of::(), + ); + if !already_collected.contains(&key) { + let mut already_collected_updated = already_collected.clone(); + already_collected_updated.insert(key); - ctx.call_site_push(); - for type_parameter in &function_decl.type_parameters { - ctx.call_site_insert(type_parameter.type_id, call_path.span()) - } + let function_decl = decl_engine.get_function(fn_ref); + + ctx.call_site_push(); + for type_parameter in &function_decl.type_parameters { + ctx.call_site_insert(type_parameter.type_id, call_path.span()) + } - for content in function_decl.body.contents.iter() { - res.append(&mut content.collect_types_metadata(handler, ctx)?); + for content in function_decl.body.contents.iter() { + res.append(&mut content.collect_types_metadata( + handler, + ctx, + &mut already_collected_updated, + )?); + } + ctx.call_site_pop(); } - ctx.call_site_pop(); } Tuple { fields } => { for field in fields.iter() { - res.append(&mut field.collect_types_metadata(handler, ctx)?); + res.append(&mut field.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } AsmExpression { registers, .. } => { for register in registers.iter() { if let Some(init) = register.initializer.as_ref() { - res.append(&mut init.collect_types_metadata(handler, ctx)?); + res.append(&mut init.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } } @@ -184,42 +231,72 @@ impl CollectTypesMetadata for TyExpression { } } for field in fields.iter() { - res.append(&mut field.value.collect_types_metadata(handler, ctx)?); + res.append(&mut field.value.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } LazyOperator { lhs, rhs, .. } => { - res.append(&mut lhs.collect_types_metadata(handler, ctx)?); - res.append(&mut rhs.collect_types_metadata(handler, ctx)?); + res.append(&mut lhs.collect_types_metadata(handler, ctx, already_collected)?); + res.append(&mut rhs.collect_types_metadata(handler, ctx, already_collected)?); } Array { elem_type: _, contents, } => { for content in contents.iter() { - res.append(&mut content.collect_types_metadata(handler, ctx)?); + res.append(&mut content.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } ArrayIndex { prefix, index } => { - res.append(&mut (**prefix).collect_types_metadata(handler, ctx)?); - res.append(&mut (**index).collect_types_metadata(handler, ctx)?); + res.append(&mut (**prefix).collect_types_metadata( + handler, + ctx, + already_collected, + )?); + res.append(&mut (**index).collect_types_metadata( + handler, + ctx, + already_collected, + )?); } CodeBlock(block) => { for content in block.contents.iter() { - res.append(&mut content.collect_types_metadata(handler, ctx)?); + res.append(&mut content.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } - MatchExp { desugared, .. } => { - res.append(&mut desugared.collect_types_metadata(handler, ctx)?) - } + MatchExp { desugared, .. } => res.append(&mut desugared.collect_types_metadata( + handler, + ctx, + already_collected, + )?), IfExp { condition, then, r#else, } => { - res.append(&mut condition.collect_types_metadata(handler, ctx)?); - res.append(&mut then.collect_types_metadata(handler, ctx)?); + res.append(&mut condition.collect_types_metadata( + handler, + ctx, + already_collected, + )?); + res.append(&mut then.collect_types_metadata(handler, ctx, already_collected)?); if let Some(r#else) = r#else { - res.append(&mut r#else.collect_types_metadata(handler, ctx)?); + res.append(&mut r#else.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } StructFieldAccess { @@ -227,16 +304,24 @@ impl CollectTypesMetadata for TyExpression { resolved_type_of_parent, .. } => { - res.append(&mut prefix.collect_types_metadata(handler, ctx)?); - res.append(&mut resolved_type_of_parent.collect_types_metadata(handler, ctx)?); + res.append(&mut prefix.collect_types_metadata(handler, ctx, already_collected)?); + res.append(&mut resolved_type_of_parent.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } TupleElemAccess { prefix, resolved_type_of_parent, .. } => { - res.append(&mut prefix.collect_types_metadata(handler, ctx)?); - res.append(&mut resolved_type_of_parent.collect_types_metadata(handler, ctx)?); + res.append(&mut prefix.collect_types_metadata(handler, ctx, already_collected)?); + res.append(&mut resolved_type_of_parent.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } EnumInstantiation { enum_ref, @@ -249,50 +334,68 @@ impl CollectTypesMetadata for TyExpression { ctx.call_site_insert(type_param.type_id, call_path_binding.inner.suffix.span()) } if let Some(contents) = contents { - res.append(&mut contents.collect_types_metadata(handler, ctx)?); + res.append(&mut contents.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } for variant in enum_decl.variants.iter() { - res.append( - &mut variant - .type_argument - .type_id - .collect_types_metadata(handler, ctx)?, - ); + res.append(&mut variant.type_argument.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } for type_param in enum_decl.type_parameters.iter() { - res.append(&mut type_param.type_id.collect_types_metadata(handler, ctx)?); + res.append(&mut type_param.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } AbiCast { address, .. } => { - res.append(&mut address.collect_types_metadata(handler, ctx)?); + res.append(&mut address.collect_types_metadata(handler, ctx, already_collected)?); } IntrinsicFunction(kind) => { - res.append(&mut kind.collect_types_metadata(handler, ctx)?); + res.append(&mut kind.collect_types_metadata(handler, ctx, already_collected)?); } EnumTag { exp } => { - res.append(&mut exp.collect_types_metadata(handler, ctx)?); + res.append(&mut exp.collect_types_metadata(handler, ctx, already_collected)?); } UnsafeDowncast { exp, variant, call_path_decl: _, } => { - res.append(&mut exp.collect_types_metadata(handler, ctx)?); - res.append( - &mut variant - .type_argument - .type_id - .collect_types_metadata(handler, ctx)?, - ); + res.append(&mut exp.collect_types_metadata(handler, ctx, already_collected)?); + res.append(&mut variant.type_argument.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } WhileLoop { condition, body } => { - res.append(&mut condition.collect_types_metadata(handler, ctx)?); + res.append(&mut condition.collect_types_metadata( + handler, + ctx, + already_collected, + )?); for content in body.contents.iter() { - res.append(&mut content.collect_types_metadata(handler, ctx)?); + res.append(&mut content.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } - Return(exp) => res.append(&mut exp.collect_types_metadata(handler, ctx)?), - Ref(exp) | Deref(exp) => res.append(&mut exp.collect_types_metadata(handler, ctx)?), + Return(exp) => { + res.append(&mut exp.collect_types_metadata(handler, ctx, already_collected)?) + } + Ref(exp) | Deref(exp) => { + res.append(&mut exp.collect_types_metadata(handler, ctx, already_collected)?) + } // storage access can never be generic // variable expressions don't ever have return types themselves, they're stored in // `TyExpression::return_type`. Variable expressions are just names of variables. @@ -305,7 +408,11 @@ impl CollectTypesMetadata for TyExpression { | Continue | FunctionParameter => {} Reassignment(reassignment) => { - res.append(&mut reassignment.rhs.collect_types_metadata(handler, ctx)?); + res.append(&mut reassignment.rhs.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } Ok(res) diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index cc4d22432c1..e78380ddc22 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -1,5 +1,5 @@ use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, fmt::{self, Write}, hash::{Hash, Hasher}, }; @@ -409,7 +409,12 @@ impl PartialEqWithEngines for TyExpressionVariant { } impl HashWithEngines for TyExpressionVariant { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let type_engine = engines.te(); std::mem::discriminant(self).hash(state); match self { @@ -429,23 +434,23 @@ impl HashWithEngines for TyExpressionVariant { deferred_monomorphization: _, } => { call_path.hash(state); - fn_ref.hash(state, engines); + fn_ref.hash(state, engines, already_in_hash); arguments.iter().for_each(|(name, arg)| { name.hash(state); - arg.hash(state, engines); + arg.hash(state, engines, already_in_hash); }); } Self::LazyOperator { op, lhs, rhs } => { op.hash(state); - lhs.hash(state, engines); - rhs.hash(state, engines); + lhs.hash(state, engines, already_in_hash); + rhs.hash(state, engines, already_in_hash); } Self::ConstantExpression { const_decl, span: _, call_path: _, } => { - const_decl.hash(state, engines); + const_decl.hash(state, engines, already_in_hash); } Self::VariableExpression { name, @@ -459,17 +464,17 @@ impl HashWithEngines for TyExpressionVariant { mutability.hash(state); } Self::Tuple { fields } => { - fields.hash(state, engines); + fields.hash(state, engines, already_in_hash); } Self::Array { contents, elem_type: _, } => { - contents.hash(state, engines); + contents.hash(state, engines, already_in_hash); } Self::ArrayIndex { prefix, index } => { - prefix.hash(state, engines); - index.hash(state, engines); + prefix.hash(state, engines, already_in_hash); + index.hash(state, engines, already_in_hash); } Self::StructExpression { struct_ref, @@ -479,11 +484,11 @@ impl HashWithEngines for TyExpressionVariant { instantiation_span: _, call_path_binding: _, } => { - struct_ref.hash(state, engines); - fields.hash(state, engines); + struct_ref.hash(state, engines, already_in_hash); + fields.hash(state, engines, already_in_hash); } Self::CodeBlock(contents) => { - contents.hash(state, engines); + contents.hash(state, engines, already_in_hash); } Self::MatchExp { desugared, @@ -491,17 +496,17 @@ impl HashWithEngines for TyExpressionVariant { // reliable source of obj v. obj distinction scrutinees: _, } => { - desugared.hash(state, engines); + desugared.hash(state, engines, already_in_hash); } Self::IfExp { condition, then, r#else, } => { - condition.hash(state, engines); - then.hash(state, engines); + condition.hash(state, engines, already_in_hash); + then.hash(state, engines, already_in_hash); if let Some(x) = r#else.as_ref() { - x.hash(state, engines) + x.hash(state, engines, already_in_hash) } } Self::AsmExpression { @@ -512,7 +517,7 @@ impl HashWithEngines for TyExpressionVariant { // reliable source of obj v. obj distinction whole_block_span: _, } => { - registers.hash(state, engines); + registers.hash(state, engines, already_in_hash); body.hash(state); returns.hash(state); } @@ -524,11 +529,11 @@ impl HashWithEngines for TyExpressionVariant { // reliable source of obj v. obj distinction field_instantiation_span: _, } => { - prefix.hash(state, engines); - field_to_access.hash(state, engines); + prefix.hash(state, engines, already_in_hash); + field_to_access.hash(state, engines, already_in_hash); type_engine .get(*resolved_type_of_parent) - .hash(state, engines); + .hash(state, engines, already_in_hash); } Self::TupleElemAccess { prefix, @@ -538,11 +543,11 @@ impl HashWithEngines for TyExpressionVariant { // reliable source of obj v. obj distinction elem_to_access_span: _, } => { - prefix.hash(state, engines); + prefix.hash(state, engines, already_in_hash); elem_to_access_num.hash(state); type_engine .get(*resolved_type_of_parent) - .hash(state, engines); + .hash(state, engines, already_in_hash); } Self::EnumInstantiation { enum_ref, @@ -555,11 +560,11 @@ impl HashWithEngines for TyExpressionVariant { call_path_binding: _, call_path_decl: _, } => { - enum_ref.hash(state, engines); + enum_ref.hash(state, engines, already_in_hash); variant_name.hash(state); tag.hash(state); if let Some(x) = contents.as_ref() { - x.hash(state, engines) + x.hash(state, engines, already_in_hash) } } Self::AbiCast { @@ -570,41 +575,41 @@ impl HashWithEngines for TyExpressionVariant { span: _, } => { abi_name.hash(state); - address.hash(state, engines); + address.hash(state, engines, already_in_hash); } Self::StorageAccess(exp) => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } Self::IntrinsicFunction(exp) => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } Self::AbiName(name) => { name.hash(state); } Self::EnumTag { exp } => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } Self::UnsafeDowncast { exp, variant, call_path_decl: _, } => { - exp.hash(state, engines); - variant.hash(state, engines); + exp.hash(state, engines, already_in_hash); + variant.hash(state, engines, already_in_hash); } Self::WhileLoop { condition, body } => { - condition.hash(state, engines); - body.hash(state, engines); + condition.hash(state, engines, already_in_hash); + body.hash(state, engines, already_in_hash); } Self::Break | Self::Continue | Self::FunctionParameter => {} Self::Reassignment(exp) => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } Self::Return(exp) => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } Self::Ref(exp) | Self::Deref(exp) => { - exp.hash(state, engines); + exp.hash(state, engines, already_in_hash); } } } @@ -761,6 +766,7 @@ impl ReplaceDecls for TyExpressionVariant { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { handler.scope(|handler| { use TyExpressionVariant::*; @@ -776,19 +782,25 @@ impl ReplaceDecls for TyExpressionVariant { if let Some(filter_type) = filter_type_opt { let filtered_decl_mapping = decl_mapping.filter_functions_by_self_type(filter_type, ctx.engines()); - fn_ref.replace_decls(&filtered_decl_mapping, handler, ctx)?; + fn_ref.replace_decls( + &filtered_decl_mapping, + handler, + ctx, + decls_replaced, + )?; } else { - fn_ref.replace_decls(decl_mapping, handler, ctx)?; + fn_ref.replace_decls(decl_mapping, handler, ctx, decls_replaced)?; }; let new_decl_ref = fn_ref.clone().replace_decls_and_insert_new_with_parent( decl_mapping, handler, ctx, + decls_replaced, )?; fn_ref.replace_id(*new_decl_ref.id()); for (_, arg) in arguments.iter_mut() { - match arg.replace_decls(decl_mapping, handler, ctx) { + match arg.replace_decls(decl_mapping, handler, ctx, decls_replaced) { Ok(res) => res, Err(_) => { continue; @@ -797,7 +809,7 @@ impl ReplaceDecls for TyExpressionVariant { } let decl_engine = ctx.engines().de(); - let mut method = (*decl_engine.get(fn_ref)).clone(); + let method = (*decl_engine.get(fn_ref)).clone(); // Handle the trait constraints. This includes checking to see if the trait // constraints are satisfied and replacing old decl ids based on the @@ -809,29 +821,35 @@ impl ReplaceDecls for TyExpressionVariant { method.name.as_str(), &method.name.span(), )?; - method.replace_decls(&inner_decl_mapping, handler, ctx)?; - decl_engine.replace(*new_decl_ref.id(), method); + fn_ref.replace_decls(&inner_decl_mapping, handler, ctx, decls_replaced)?; + decl_engine.replace(*new_decl_ref.id(), (*decl_engine.get(fn_ref)).clone()); } LazyOperator { lhs, rhs, .. } => { - (*lhs).replace_decls(decl_mapping, handler, ctx)?; - (*rhs).replace_decls(decl_mapping, handler, ctx)?; + (*lhs).replace_decls(decl_mapping, handler, ctx, decls_replaced)?; + (*rhs).replace_decls(decl_mapping, handler, ctx, decls_replaced)?; } ConstantExpression { const_decl, .. } => { - const_decl.replace_decls(decl_mapping, handler, ctx)? + const_decl.replace_decls(decl_mapping, handler, ctx, decls_replaced)? } VariableExpression { .. } => (), Tuple { fields } => fields.iter_mut().for_each(|x| { - x.replace_decls(decl_mapping, handler, ctx).ok(); + x.replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); }), Array { elem_type: _, contents, } => contents.iter_mut().for_each(|x| { - x.replace_decls(decl_mapping, handler, ctx).ok(); + x.replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); }), ArrayIndex { prefix, index } => { - (*prefix).replace_decls(decl_mapping, handler, ctx).ok(); - (*index).replace_decls(decl_mapping, handler, ctx).ok(); + (*prefix) + .replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); + (*index) + .replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); } StructExpression { struct_ref: _, @@ -839,30 +857,38 @@ impl ReplaceDecls for TyExpressionVariant { instantiation_span: _, call_path_binding: _, } => fields.iter_mut().for_each(|x| { - x.replace_decls(decl_mapping, handler, ctx).ok(); + x.replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); }), - CodeBlock(block) => block.replace_decls(decl_mapping, handler, ctx)?, + CodeBlock(block) => { + block.replace_decls(decl_mapping, handler, ctx, decls_replaced)? + } FunctionParameter => (), MatchExp { desugared, .. } => { - desugared.replace_decls(decl_mapping, handler, ctx)? + desugared.replace_decls(decl_mapping, handler, ctx, decls_replaced)? } IfExp { condition, then, r#else, } => { - condition.replace_decls(decl_mapping, handler, ctx).ok(); - then.replace_decls(decl_mapping, handler, ctx).ok(); + condition + .replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); + then.replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); if let Some(ref mut r#else) = r#else { - r#else.replace_decls(decl_mapping, handler, ctx).ok(); + r#else + .replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); } } AsmExpression { .. } => {} StructFieldAccess { prefix, .. } => { - prefix.replace_decls(decl_mapping, handler, ctx)? + prefix.replace_decls(decl_mapping, handler, ctx, decls_replaced)? } TupleElemAccess { prefix, .. } => { - prefix.replace_decls(decl_mapping, handler, ctx)? + prefix.replace_decls(decl_mapping, handler, ctx, decls_replaced)? } EnumInstantiation { enum_ref: _, @@ -872,33 +898,42 @@ impl ReplaceDecls for TyExpressionVariant { // TODO: replace enum decl //enum_decl.replace_decls(decl_mapping); if let Some(ref mut contents) = contents { - contents.replace_decls(decl_mapping, handler, ctx)?; + contents.replace_decls(decl_mapping, handler, ctx, decls_replaced)?; }; } - AbiCast { address, .. } => address.replace_decls(decl_mapping, handler, ctx)?, + AbiCast { address, .. } => { + address.replace_decls(decl_mapping, handler, ctx, decls_replaced)? + } StorageAccess { .. } => (), IntrinsicFunction(TyIntrinsicFunctionKind { arguments, .. }) => { arguments.iter_mut().for_each(|x| { - x.replace_decls(decl_mapping, handler, ctx).ok(); + x.replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); }) } - EnumTag { exp } => exp.replace_decls(decl_mapping, handler, ctx)?, - UnsafeDowncast { exp, .. } => exp.replace_decls(decl_mapping, handler, ctx)?, + EnumTag { exp } => exp.replace_decls(decl_mapping, handler, ctx, decls_replaced)?, + UnsafeDowncast { exp, .. } => { + exp.replace_decls(decl_mapping, handler, ctx, decls_replaced)? + } AbiName(_) => (), WhileLoop { ref mut condition, ref mut body, } => { - condition.replace_decls(decl_mapping, handler, ctx).ok(); - body.replace_decls(decl_mapping, handler, ctx)?; + condition + .replace_decls(decl_mapping, handler, ctx, decls_replaced) + .ok(); + body.replace_decls(decl_mapping, handler, ctx, decls_replaced)?; } Break => (), Continue => (), Reassignment(reassignment) => { - reassignment.replace_decls(decl_mapping, handler, ctx)? + reassignment.replace_decls(decl_mapping, handler, ctx, decls_replaced)? + } + Return(stmt) => stmt.replace_decls(decl_mapping, handler, ctx, decls_replaced)?, + Ref(exp) | Deref(exp) => { + exp.replace_decls(decl_mapping, handler, ctx, decls_replaced)? } - Return(stmt) => stmt.replace_decls(decl_mapping, handler, ctx)?, - Ref(exp) | Deref(exp) => exp.replace_decls(decl_mapping, handler, ctx)?, } Ok(()) diff --git a/sway-core/src/language/ty/expression/intrinsic_function.rs b/sway-core/src/language/ty/expression/intrinsic_function.rs index 6ead5f94fc1..ce6aea43aa4 100644 --- a/sway-core/src/language/ty/expression/intrinsic_function.rs +++ b/sway-core/src/language/ty/expression/intrinsic_function.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, fmt, hash::{Hash, Hasher}, }; @@ -56,7 +57,12 @@ impl PartialEqWithEngines for TyIntrinsicFunctionKind { } impl HashWithEngines for TyIntrinsicFunctionKind { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyIntrinsicFunctionKind { kind, arguments, @@ -66,8 +72,8 @@ impl HashWithEngines for TyIntrinsicFunctionKind { span: _, } = self; kind.hash(state); - arguments.hash(state, engines); - type_arguments.hash(state, engines); + arguments.hash(state, engines, already_in_hash); + type_arguments.hash(state, engines, already_in_hash); } } @@ -114,13 +120,22 @@ impl CollectTypesMetadata for TyIntrinsicFunctionKind { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { let mut types_metadata = vec![]; for type_arg in self.type_arguments.iter() { - types_metadata.append(&mut type_arg.type_id.collect_types_metadata(handler, ctx)?); + types_metadata.append(&mut type_arg.type_id.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } for arg in self.arguments.iter() { - types_metadata.append(&mut arg.collect_types_metadata(handler, ctx)?); + types_metadata.append(&mut arg.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } match self.kind { diff --git a/sway-core/src/language/ty/expression/reassignment.rs b/sway-core/src/language/ty/expression/reassignment.rs index 6ee30d5949e..6387fdf961c 100644 --- a/sway-core/src/language/ty/expression/reassignment.rs +++ b/sway-core/src/language/ty/expression/reassignment.rs @@ -1,5 +1,6 @@ use std::{ borrow::Cow, + collections::{HashMap, HashSet}, hash::{Hash, Hasher}, }; @@ -41,7 +42,12 @@ impl PartialEqWithEngines for TyReassignment { } impl HashWithEngines for TyReassignment { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyReassignment { lhs_base_name, lhs_type, @@ -50,9 +56,11 @@ impl HashWithEngines for TyReassignment { } = self; let type_engine = engines.te(); lhs_base_name.hash(state); - type_engine.get(*lhs_type).hash(state, engines); - lhs_indices.hash(state, engines); - rhs.hash(state, engines); + type_engine + .get(*lhs_type) + .hash(state, engines, already_in_hash); + lhs_indices.hash(state, engines, already_in_hash); + rhs.hash(state, engines, already_in_hash); } } @@ -69,8 +77,10 @@ impl ReplaceDecls for TyReassignment { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { - self.rhs.replace_decls(decl_mapping, handler, ctx) + self.rhs + .replace_decls(decl_mapping, handler, ctx, decls_replaced) } } @@ -150,7 +160,12 @@ impl PartialEqWithEngines for ProjectionKind { } impl HashWithEngines for ProjectionKind { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { use ProjectionKind::*; std::mem::discriminant(self).hash(state); match self { @@ -167,7 +182,7 @@ impl HashWithEngines for ProjectionKind { // reliable source of obj v. obj distinction index_span: _, } => { - index.hash(state, engines); + index.hash(state, engines, already_in_hash); } } } diff --git a/sway-core/src/language/ty/expression/storage.rs b/sway-core/src/language/ty/expression/storage.rs index bef0bb9a659..05cb7898d6a 100644 --- a/sway-core/src/language/ty/expression/storage.rs +++ b/sway-core/src/language/ty/expression/storage.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; use sway_types::{state::StateIndex, Ident, Span, Spanned}; @@ -22,13 +25,18 @@ impl PartialEqWithEngines for TyStorageAccess { } impl HashWithEngines for TyStorageAccess { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStorageAccess { fields, ix, storage_keyword_span, } = self; - fields.hash(state, engines); + fields.hash(state, engines, already_in_hash); ix.hash(state); storage_keyword_span.hash(state); } @@ -71,7 +79,12 @@ impl PartialEqWithEngines for TyStorageAccessDescriptor { } impl HashWithEngines for TyStorageAccessDescriptor { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStorageAccessDescriptor { name, type_id, @@ -81,6 +94,8 @@ impl HashWithEngines for TyStorageAccessDescriptor { } = self; let type_engine = engines.te(); name.hash(state); - type_engine.get(*type_id).hash(state, engines); + type_engine + .get(*type_id) + .hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/language/ty/expression/struct_exp_field.rs b/sway-core/src/language/ty/expression/struct_exp_field.rs index aca406f4ccb..d3a198922cc 100644 --- a/sway-core/src/language/ty/expression/struct_exp_field.rs +++ b/sway-core/src/language/ty/expression/struct_exp_field.rs @@ -1,7 +1,10 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::{HashMap, HashSet}, + hash::{Hash, Hasher}, +}; use sway_error::handler::{ErrorEmitted, Handler}; -use sway_types::Ident; +use sway_types::{Ident, Span}; use crate::{ decl_engine::*, @@ -25,10 +28,15 @@ impl PartialEqWithEngines for TyStructExpressionField { } impl HashWithEngines for TyStructExpressionField { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TyStructExpressionField { name, value } = self; name.hash(state); - value.hash(state, engines); + value.hash(state, engines, already_in_hash); } } @@ -44,8 +52,10 @@ impl ReplaceDecls for TyStructExpressionField { decl_mapping: &DeclMapping, handler: &Handler, ctx: &mut TypeCheckContext, + decls_replaced: &mut HashMap<(usize, std::any::TypeId), (usize, Span)>, ) -> Result<(), ErrorEmitted> { - self.value.replace_decls(decl_mapping, handler, ctx) + self.value + .replace_decls(decl_mapping, handler, ctx, decls_replaced) } } diff --git a/sway-core/src/language/ty/program.rs b/sway-core/src/language/ty/program.rs index daa66cb3cb9..2189cf66c6d 100644 --- a/sway-core/src/language/ty/program.rs +++ b/sway-core/src/language/ty/program.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; use crate::{ decl_engine::*, @@ -421,6 +421,7 @@ impl CollectTypesMetadata for TyProgram { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { let decl_engine = ctx.engines.de(); let mut metadata = vec![]; @@ -432,14 +433,22 @@ impl CollectTypesMetadata for TyProgram { TyProgramKind::Script { main_function, .. } | TyProgramKind::Predicate { main_function, .. } => { let main_function = decl_engine.get_function(main_function); - metadata.append(&mut main_function.collect_types_metadata(handler, ctx)?); + metadata.append(&mut main_function.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } // For contracts, collect metadata for all the types starting with each ABI method as // an entry point. TyProgramKind::Contract { abi_entries, .. } => { for entry in abi_entries.iter() { let entry = decl_engine.get_function(entry); - metadata.append(&mut entry.collect_types_metadata(handler, ctx)?); + metadata.append(&mut entry.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } // For libraries, collect metadata for all the types starting with each `pub` node as @@ -454,7 +463,8 @@ impl CollectTypesMetadata for TyProgram { for node in module.all_nodes.iter() { let is_generic_function = node.is_generic_function(decl_engine); if node.is_public(decl_engine) { - let node_metadata = node.collect_types_metadata(handler, ctx)?; + let node_metadata = + node.collect_types_metadata(handler, ctx, already_collected)?; metadata.append( &mut node_metadata .iter() @@ -482,7 +492,11 @@ impl CollectTypesMetadata for TyProgram { ) { for node in module.all_nodes.iter() { if node.is_test_function(decl_engine) { - metadata.append(&mut node.collect_types_metadata(handler, ctx)?); + metadata.append(&mut node.collect_types_metadata( + handler, + ctx, + already_collected, + )?); } } } diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 1cb1288f1bb..333d14376dd 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -32,7 +32,7 @@ use control_flow_analysis::ControlFlowGraph; use metadata::MetadataManager; use query_engine::{ModuleCacheKey, ModulePath, ProgramsCacheEntry}; use std::collections::hash_map::DefaultHasher; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -511,6 +511,7 @@ pub fn parsed_to_ast( let types_metadata_result = typed_program.collect_types_metadata( handler, &mut CollectTypesMetadataContext::new(engines, experimental), + &mut HashSet::<(usize, std::any::TypeId)>::new(), ); let types_metadata = match types_metadata_result { Ok(types_metadata) => types_metadata, 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 e3a2e5f8381..a1ac80337e3 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -14,12 +14,12 @@ impl ty::TyCodeBlock { code_block: &CodeBlock, ) -> Result { // Create a temp namespace for checking within the code block scope. - let mut code_block_namespace = ctx.namespace.clone(); + //let mut code_block_namespace = ctx.namespace; //.clone(); let evaluated_contents = code_block .contents .iter() .filter_map(|node| { - let ctx = ctx.by_ref().scoped(&mut code_block_namespace); + let ctx = ctx.by_ref(); //.scoped(&mut code_block_namespace); ty::TyAstNode::type_check(handler, ctx, node.clone()).ok() }) .collect::>(); diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs index 1bae8194e4f..98ad53e1a3f 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs @@ -914,7 +914,12 @@ fn type_check_trait_implementation( .collect::>(), ); - method.replace_decls(&decl_mapping, handler, &mut ctx)?; + method.replace_decls( + &decl_mapping, + handler, + &mut ctx, + &mut HashMap::<(usize, std::any::TypeId), (usize, Span)>::new(), + )?; method.subst(&type_mapping, engines); all_items_refs.push(TyImplItem::Fn( decl_engine @@ -924,7 +929,12 @@ fn type_check_trait_implementation( } TyImplItem::Constant(decl_ref) => { let mut const_decl = (*decl_engine.get_constant(decl_ref)).clone(); - const_decl.replace_decls(&decl_mapping, handler, &mut ctx)?; + const_decl.replace_decls( + &decl_mapping, + handler, + &mut ctx, + &mut HashMap::<(usize, std::any::TypeId), (usize, Span)>::new(), + )?; const_decl.subst(&type_mapping, engines); all_items_refs.push(TyImplItem::Constant(decl_engine.insert(const_decl))); } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index d4fe7a38ab0..140594205a7 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -72,7 +72,12 @@ pub(crate) fn instantiate_function_application( function_decl.name.as_str(), &call_path_binding.span(), )?; - function_decl.replace_decls(&decl_mapping, handler, &mut ctx)?; + function_decl.replace_decls( + &decl_mapping, + handler, + &mut ctx, + &mut HashMap::<(usize, std::any::TypeId), (usize, Span)>::new(), + )?; let return_type = function_decl.return_type.clone(); let new_decl_ref = decl_engine .insert(function_decl) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 8833d61cfb0..6e89cf62742 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -622,7 +622,12 @@ pub(crate) fn monomorphize_method_application( )?; if !ctx.defer_monomorphization() { - method.replace_decls(&decl_mapping, handler, &mut ctx)?; + method.replace_decls( + &decl_mapping, + handler, + &mut ctx, + &mut HashMap::<(usize, std::any::TypeId), (usize, Span)>::new(), + )?; } decl_engine.replace(*fn_ref.id(), method); diff --git a/sway-core/src/type_system/ast_elements/trait_constraint.rs b/sway-core/src/type_system/ast_elements/trait_constraint.rs index 9acebfc7767..a7a259fc920 100644 --- a/sway-core/src/type_system/ast_elements/trait_constraint.rs +++ b/sway-core/src/type_system/ast_elements/trait_constraint.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + collections::HashSet, fmt, hash::{Hash, Hasher}, }; @@ -12,7 +13,11 @@ use sway_types::Spanned; use crate::{ engine_threading::*, - language::{parsed::Supertrait, ty, CallPath}, + language::{ + parsed::Supertrait, + ty::{self}, + CallPath, + }, semantic_analysis::{ declaration::{insert_supertraits_into_namespace, SupertraitOf}, type_check_context::EnforceTypeArguments, @@ -29,9 +34,14 @@ pub struct TraitConstraint { } impl HashWithEngines for TraitConstraint { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { self.trait_name.hash(state); - self.type_arguments.hash(state, engines); + self.type_arguments.hash(state, engines, already_in_hash); } } @@ -105,12 +115,16 @@ impl CollectTypesMetadata for TraitConstraint { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { let mut res = vec![]; handler.scope(|handler| { for type_arg in self.type_arguments.iter() { res.extend( - match type_arg.type_id.collect_types_metadata(handler, ctx) { + match type_arg + .type_id + .collect_types_metadata(handler, ctx, already_collected) + { Ok(res) => res, Err(_) => continue, }, 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 03aba7a30a4..fc079a964f9 100644 --- a/sway-core/src/type_system/ast_elements/type_argument.rs +++ b/sway-core/src/type_system/ast_elements/type_argument.rs @@ -1,5 +1,5 @@ use crate::{engine_threading::*, language::CallPathTree, type_system::priv_prelude::*}; -use std::{cmp::Ordering, fmt, hash::Hasher}; +use std::{cmp::Ordering, collections::HashSet, fmt, hash::Hasher}; use sway_types::{Span, Spanned}; #[derive(Debug, Clone)] @@ -28,7 +28,12 @@ impl Spanned for TypeArgument { } impl HashWithEngines for TypeArgument { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TypeArgument { type_id, // these fields are not hashed because they aren't relevant/a @@ -38,7 +43,9 @@ impl HashWithEngines for TypeArgument { call_path_tree: _, } = self; let type_engine = engines.te(); - type_engine.get(*type_id).hash(state, engines); + type_engine + .get(*type_id) + .hash(state, engines, already_in_hash); } } 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 32594e835de..b39782b3a90 100644 --- a/sway-core/src/type_system/ast_elements/type_parameter.rs +++ b/sway-core/src/type_system/ast_elements/type_parameter.rs @@ -18,7 +18,7 @@ use sway_types::{ident::Ident, span::Span, Spanned}; use std::{ cmp::Ordering, - collections::BTreeMap, + collections::{BTreeMap, HashSet}, fmt, hash::{Hash, Hasher}, }; @@ -34,7 +34,12 @@ pub struct TypeParameter { } impl HashWithEngines for TypeParameter { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { let TypeParameter { type_id, name_ident, @@ -46,9 +51,11 @@ impl HashWithEngines for TypeParameter { is_from_parent: _, } = self; let type_engine = engines.te(); - type_engine.get(*type_id).hash(state, engines); + type_engine + .get(*type_id) + .hash(state, engines, already_in_hash); name_ident.hash(state); - trait_constraints.hash(state, engines); + trait_constraints.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 2f6736e9787..6ac4416fc7e 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -34,12 +34,7 @@ impl Clone for TypeEngine { impl TypeEngine { /// Inserts a [TypeInfo] into the [TypeEngine] and returns a [TypeId] /// referring to that [TypeInfo]. - pub(crate) fn insert( - &self, - engines: &Engines, - ty: TypeInfo, - source_id: Option<&SourceId>, - ) -> TypeId { + pub fn insert(&self, engines: &Engines, ty: TypeInfo, source_id: Option<&SourceId>) -> TypeId { let source_id = source_id .map(Clone::clone) .or_else(|| info_to_source_id(&ty)); diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 52f4efe6e5f..c15e209aa55 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -42,6 +42,7 @@ impl CollectTypesMetadata for TypeId { &self, _handler: &Handler, ctx: &mut CollectTypesMetadataContext, + _already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted> { fn filter_fn(type_info: &TypeInfo) -> bool { matches!(type_info, TypeInfo::UnknownGeneric { .. }) diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index 1b14cc1d59f..e84e21510f5 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -13,6 +13,7 @@ use sway_types::{integer_bits::IntegerBits, span::Span, SourceId, Spanned}; use std::{ cmp::Ordering, + collections::HashSet, fmt, hash::{Hash, Hasher}, sync::Arc, @@ -77,8 +78,13 @@ pub struct TypeSourceInfo { } impl HashWithEngines for TypeSourceInfo { - fn hash(&self, state: &mut H, engines: &Engines) { - self.type_info.hash(state, engines); + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { + self.type_info.hash(state, engines, already_in_hash); self.source_id.hash(state); } } @@ -188,7 +194,12 @@ pub enum TypeInfo { } impl HashWithEngines for TypeInfo { - fn hash(&self, state: &mut H, engines: &Engines) { + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { self.discriminant_value().hash(state); match self { TypeInfo::StringArray(len) => { @@ -198,13 +209,13 @@ impl HashWithEngines for TypeInfo { bits.hash(state); } TypeInfo::Tuple(fields) => { - fields.hash(state, engines); + fields.hash(state, engines, already_in_hash); } TypeInfo::Enum(decl_ref) => { - decl_ref.hash(state, engines); + decl_ref.hash(state, engines, already_in_hash); } TypeInfo::Struct(decl_ref) => { - decl_ref.hash(state, engines); + decl_ref.hash(state, engines, already_in_hash); } TypeInfo::ContractCaller { abi_name, address } => { abi_name.hash(state); @@ -222,39 +233,41 @@ impl HashWithEngines for TypeInfo { // Do not hash trait_constraints as those can point back to this type_info // This avoids infinite hash loop. More collisions should occur but // Eq implementations can disambiguate. - //trait_constraints.hash(state, engines); + //trait_constraints.hash(state, engines, already_in_hash); } TypeInfo::Custom { qualified_call_path: call_path, type_arguments, root_type_id, } => { - call_path.hash(state, engines); - type_arguments.as_deref().hash(state, engines); + call_path.hash(state, engines, already_in_hash); + type_arguments + .as_deref() + .hash(state, engines, already_in_hash); root_type_id.hash(state); } TypeInfo::Storage { fields } => { - fields.hash(state, engines); + fields.hash(state, engines, already_in_hash); } TypeInfo::Array(elem_ty, count) => { - elem_ty.hash(state, engines); + elem_ty.hash(state, engines, already_in_hash); count.hash(state); } TypeInfo::Placeholder(ty) => { - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); } TypeInfo::TypeParam(n) => { n.hash(state); } TypeInfo::Alias { name, ty } => { name.hash(state); - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); } TypeInfo::Ptr(ty) => { - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); } TypeInfo::Slice(ty) => { - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); } TypeInfo::TraitType { name, @@ -264,7 +277,7 @@ impl HashWithEngines for TypeInfo { trait_type_id.hash(state); } TypeInfo::Ref(ty) => { - ty.hash(state, engines); + ty.hash(state, engines, already_in_hash); } TypeInfo::StringSlice | TypeInfo::Numeric diff --git a/sway-core/src/type_system/substitute/subst_list.rs b/sway-core/src/type_system/substitute/subst_list.rs index 828b9875129..50b8f303fa0 100644 --- a/sway-core/src/type_system/substitute/subst_list.rs +++ b/sway-core/src/type_system/substitute/subst_list.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, hash::Hasher, slice::{Iter, IterMut}, vec::IntoIter, @@ -66,8 +67,13 @@ impl PartialEqWithEngines for SubstList { } impl HashWithEngines for SubstList { - fn hash(&self, state: &mut H, engines: &Engines) { - self.list.hash(state, engines); + fn hash( + &self, + state: &mut H, + engines: &Engines, + already_in_hash: &mut HashSet<(usize, std::any::TypeId)>, + ) { + self.list.hash(state, engines, already_in_hash); } } diff --git a/sway-core/src/types/collect_types_metadata.rs b/sway-core/src/types/collect_types_metadata.rs index a62a0c5bff9..bf715a73456 100644 --- a/sway-core/src/types/collect_types_metadata.rs +++ b/sway-core/src/types/collect_types_metadata.rs @@ -4,7 +4,7 @@ //! that is not the case. use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, sync::{Arc, Mutex}, }; @@ -137,5 +137,6 @@ pub(crate) trait CollectTypesMetadata { &self, handler: &Handler, ctx: &mut CollectTypesMetadataContext, + already_collected: &mut HashSet<(usize, std::any::TypeId)>, ) -> Result, ErrorEmitted>; } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.lock new file mode 100644 index 00000000000..07fb7066901 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-32A5EFB68998841E" + +[[package]] +name = "stack_overflow" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-32A5EFB68998841E" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.toml new file mode 100644 index 00000000000..d4197bc060a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "trait_recursive" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/src/main.sw new file mode 100644 index 00000000000..c8a9edb0365 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/src/main.sw @@ -0,0 +1,31 @@ +script; + +trait T1 { + fn trait_fn() -> Self; +} + +impl T1 for u64 { + fn trait_fn() -> u64 { + 0 + } +} + +impl T1 for (A, ) +where + A: T1 +{ + fn trait_fn() -> (A, ) { + (A::trait_fn(), ) + } +} + +fn f () +where + T: T1 +{ + T::trait_fn(); +} + +fn main() { + let a = f::<(u64, )>(); +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/test.toml new file mode 100644 index 00000000000..14012ed8d21 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_recursive/test.toml @@ -0,0 +1,4 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = false +expected_warnings = 3