From eefdcdaa9e210a4421958e6d69936c8c1953e93b Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 12 Aug 2024 15:08:53 +0100 Subject: [PATCH 01/20] constraints for numeric --- sway-core/src/abi_generation/abi_str.rs | 2 +- sway-core/src/abi_generation/evm_abi.rs | 2 +- sway-core/src/ir_generation/convert.rs | 2 +- sway-core/src/language/literal.rs | 2 +- .../ast_node/expression/intrinsic_function.rs | 12 ++--- .../ast_node/expression/typed_expression.rs | 10 ++-- .../semantic_analysis/node_dependencies.rs | 2 +- sway-core/src/type_system/engine.rs | 6 +-- sway-core/src/type_system/id.rs | 4 +- sway-core/src/type_system/info.rs | 54 ++++++++++++++----- .../src/type_system/substitute/subst_map.rs | 4 +- sway-core/src/type_system/unify/unifier.rs | 26 +++++++-- .../src/type_system/unify/unify_check.rs | 14 ++--- sway-error/src/type_error.rs | 11 ++-- .../should_fail/numeric_constraints/Forc.lock | 13 +++++ .../should_fail/numeric_constraints/Forc.toml | 9 ++++ .../numeric_constraints/src/main.sw | 15 ++++++ .../should_fail/numeric_constraints/test.toml | 13 +++++ 18 files changed, 150 insertions(+), 51 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml diff --git a/sway-core/src/abi_generation/abi_str.rs b/sway-core/src/abi_generation/abi_str.rs index 339a9453f81..efbe5f33fa1 100644 --- a/sway-core/src/abi_generation/abi_str.rs +++ b/sway-core/src/abi_generation/abi_str.rs @@ -112,7 +112,7 @@ impl TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric => "u64".into(), // u64 is the default + Numeric { .. } => "u64".into(), // u64 is the default Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { diff --git a/sway-core/src/abi_generation/evm_abi.rs b/sway-core/src/abi_generation/evm_abi.rs index f899e0de209..10dad9c247c 100644 --- a/sway-core/src/abi_generation/evm_abi.rs +++ b/sway-core/src/abi_generation/evm_abi.rs @@ -90,7 +90,7 @@ pub fn abi_str(type_info: &TypeInfo, engines: &Engines) -> String { format!("({})", field_strs.join(", ")) } B256 => "uint256".into(), - Numeric => "u64".into(), // u64 is the default + Numeric { .. } => "u64".into(), // u64 is the default Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index 3aa22401338..0da0eaa2a51 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -98,7 +98,7 @@ fn convert_resolved_type_info( TypeInfo::UnsignedInteger(IntegerBits::Sixteen) | TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) | TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) - | TypeInfo::Numeric => Type::get_uint64(context), + | TypeInfo::Numeric { .. } => Type::get_uint64(context), TypeInfo::Boolean => Type::get_bool(context), TypeInfo::B256 => Type::get_b256(context), TypeInfo::StringSlice => Type::get_slice(context), diff --git a/sway-core/src/language/literal.rs b/sway-core/src/language/literal.rs index afedaad12d0..f97fc50cbd4 100644 --- a/sway-core/src/language/literal.rs +++ b/sway-core/src/language/literal.rs @@ -147,7 +147,7 @@ impl Literal { pub(crate) fn to_typeinfo(&self) -> TypeInfo { match self { Literal::String(_) => TypeInfo::StringSlice, - Literal::Numeric(_) => TypeInfo::Numeric, + Literal::Numeric(v) => TypeInfo::numeric_constrained_by_value(*v), Literal::U8(_) => TypeInfo::UnsignedInteger(IntegerBits::Eight), Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 0df9f72e458..fa686c7fe5f 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -584,7 +584,7 @@ fn type_check_not( let t_arc = engines.te().get(operand_expr.return_type); let t = &*t_arc; match t { - TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => Ok(( + TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } => Ok(( ty::TyIntrinsicFunctionKind { kind, arguments: vec![operand_expr], @@ -908,7 +908,7 @@ fn type_check_cmp( && matches!(arg_ty, TypeInfo::Boolean | TypeInfo::RawUntypedPtr); let is_valid_arg_ty = matches!( arg_ty, - TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric | TypeInfo::B256 + TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } | TypeInfo::B256 ) || is_eq_bool_ptr; if !is_valid_arg_ty { @@ -1439,7 +1439,7 @@ fn type_check_arith_binary_op( })); } - let return_type = type_engine.insert(engines, TypeInfo::Numeric, None); + let return_type = type_engine.insert(engines, TypeInfo::numeric_unconstrained(), None); let mut ctx = ctx .by_ref() .with_type_annotation(return_type) @@ -1501,7 +1501,7 @@ fn type_check_bitwise_binary_op( let t_arc = engines.te().get(lhs.return_type); let t = &*t_arc; match t { - TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => Ok(( + TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { min: None } => Ok(( ty::TyIntrinsicFunctionKind { kind, arguments: vec![lhs, rhs], @@ -1568,14 +1568,14 @@ fn type_check_shift_binary_op( handler, ctx.by_ref() .with_help_text("Incorrect argument type") - .with_type_annotation(engines.te().insert(engines, TypeInfo::Numeric, None)), + .with_type_annotation(engines.te().insert(engines, TypeInfo::numeric_unconstrained(), None)), rhs, )?; let t_arc = engines.te().get(lhs.return_type); let t = &*t_arc; match t { - TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => Ok(( + TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } => Ok(( ty::TyIntrinsicFunctionKind { kind, arguments: vec![lhs, rhs], diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 26c862b21a1..d9ece54a4ed 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -450,7 +450,7 @@ impl ty::TyExpression { if let ty::TyExpressionVariant::Literal(lit) = typed_expression.clone().expression { if let Literal::Numeric(_) = lit { match &*type_engine.get(typed_expression.return_type) { - TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => { + TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } => { typed_expression = Self::resolve_numeric_literal( handler, ctx, @@ -471,7 +471,7 @@ impl ty::TyExpression { let type_engine = engines.te(); let return_type = match &lit { Literal::String(_) => TypeInfo::StringSlice, - Literal::Numeric(_) => TypeInfo::Numeric, + Literal::Numeric(v) => TypeInfo::numeric_constrained_by_value(*v), Literal::U8(_) => TypeInfo::UnsignedInteger(IntegerBits::Eight), Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), @@ -2524,11 +2524,11 @@ impl ty::TyExpression { // Numerics are limited to u64 for now IntegerBits::V256 => (Ok(Literal::U256(U256::from(num))), new_type), }, - TypeInfo::Numeric => ( + n @ TypeInfo::Numeric { .. } => ( num.to_string().parse().map(Literal::Numeric).map_err(|e| { - Literal::handle_parse_int_error(engines, e, TypeInfo::Numeric, span.clone()) + Literal::handle_parse_int_error(engines, e, n.clone(), span.clone()) }), - type_engine.insert(engines, TypeInfo::Numeric, None), + type_engine.insert(engines, n.clone(), None), ), _ => unreachable!("Unexpected type for integer literals"), }, diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 398c590566a..b1ec57334f1 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -1021,7 +1021,7 @@ fn type_info_name(type_info: &TypeInfo) -> String { TypeInfo::Tuple(fields) if fields.is_empty() => "unit", TypeInfo::Tuple(..) => "tuple", TypeInfo::B256 => "b256", - TypeInfo::Numeric => "numeric", + TypeInfo::Numeric { .. } => "numeric", TypeInfo::Contract => "contract", TypeInfo::ErrorRecovery(_) => "err_recov", TypeInfo::Unknown => "unknown", diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 206bed81bb2..0b975ef5aa0 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -309,7 +309,7 @@ impl TypeEngine { | TypeInfo::RawUntypedSlice | TypeInfo::Alias { .. } | TypeInfo::TraitType { .. } => false, - TypeInfo::Numeric => true, + TypeInfo::Numeric { .. } => true, } } @@ -366,7 +366,7 @@ impl TypeEngine { | TypeInfo::RawUntypedSlice | TypeInfo::Alias { .. } | TypeInfo::TraitType { .. } => {} - TypeInfo::Numeric => { + TypeInfo::Numeric { .. } => { self.unify( handler, engines, @@ -405,7 +405,7 @@ fn info_to_source_id(ty: &TypeInfo) -> Option { match ty { TypeInfo::Unknown | TypeInfo::UnsignedInteger(_) - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::Boolean | TypeInfo::B256 | TypeInfo::RawUntypedPtr diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 9410fd2d6ac..a82f1b2e912 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -222,7 +222,7 @@ impl TypeId { | TypeInfo::RawUntypedSlice | TypeInfo::Boolean | TypeInfo::B256 - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::Contract | TypeInfo::ErrorRecovery(_) | TypeInfo::TraitType { .. } => {} @@ -471,7 +471,7 @@ impl TypeId { | TypeInfo::Placeholder(..) | TypeInfo::TraitType { .. } | TypeInfo::TypeParam(..) - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } ), IncludeNumeric::No => matches!( x, diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index eeb22e6a734..3216a74e46f 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -158,7 +158,9 @@ pub enum TypeInfo { B256, /// This means that specific type of a number is not yet known. It will be /// determined via inference at a later time. - Numeric, + Numeric { + min: Option + }, Contract, // used for recovering from errors in the ast ErrorRecovery(ErrorEmitted), @@ -281,8 +283,10 @@ impl HashWithEngines for TypeInfo { to_mutable_value.hash(state); ty.hash(state, engines); } + TypeInfo::Numeric { min } => { + min.hash(state); + } TypeInfo::StringSlice - | TypeInfo::Numeric | TypeInfo::Boolean | TypeInfo::B256 | TypeInfo::Contract @@ -438,7 +442,9 @@ impl PartialEqWithEngines for TypeInfo { .get(l_ty.type_id) .eq(&type_engine.get(r_ty.type_id), ctx)) } - + (Self::Numeric { min: a }, Self::Numeric { min: b }) => { + a.eq(b) + } (l, r) => l.discriminant_value() == r.discriminant_value(), } } @@ -568,6 +574,9 @@ impl OrdWithEngines for TypeInfo { .get(l_ty.type_id) .cmp(&type_engine.get(r_ty.type_id), ctx) }), + (Self::Numeric { min: a }, Self::Numeric { min: b }) => { + a.cmp(b) + } (l, r) => l.discriminant_value().cmp(&r.discriminant_value()), } } @@ -605,7 +614,10 @@ impl DisplayWithEngines for TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric => "numeric".into(), + Numeric { min } => format!("numeric{}", match min { + Some(min) => format!(" {min}"), + None => "".to_string() + }), Contract => "contract".into(), ErrorRecovery(_) => "unknown".into(), Enum(decl_ref) => { @@ -710,7 +722,10 @@ impl DebugWithEngines for TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric => "numeric".into(), + Numeric { min } => format!("numeric{}", match min { + Some(min) => format!(" {min}"), + None => "".to_string() + }), Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { @@ -792,7 +807,7 @@ impl TypeInfo { TypeInfo::ContractCaller { .. } => 9, TypeInfo::Custom { .. } => 10, TypeInfo::B256 => 11, - TypeInfo::Numeric => 12, + TypeInfo::Numeric { .. } => 12, TypeInfo::Contract => 13, TypeInfo::ErrorRecovery(_) => 14, TypeInfo::Array(_, _) => 15, @@ -1151,7 +1166,7 @@ impl TypeInfo { | TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) | TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) | TypeInfo::RawUntypedPtr - | TypeInfo::Numeric // TODO-IG: Should Ptr and Ref also be a copy type? + | TypeInfo::Numeric { .. } // TODO-IG: Should Ptr and Ref also be a copy type? | TypeInfo::Never ) || self.is_unit() } @@ -1256,7 +1271,7 @@ impl TypeInfo { | TypeInfo::Tuple(_) | TypeInfo::ContractCaller { .. } | TypeInfo::B256 - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::RawUntypedPtr | TypeInfo::RawUntypedSlice | TypeInfo::Ptr(_) @@ -1303,7 +1318,7 @@ impl TypeInfo { | TypeInfo::Tuple(_) | TypeInfo::B256 | TypeInfo::UnknownGeneric { .. } - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::Never | TypeInfo::StringSlice => Ok(()), TypeInfo::Alias { ty, .. } => { @@ -1387,7 +1402,7 @@ impl TypeInfo { | TypeInfo::StringSlice | TypeInfo::Array(_, _) | TypeInfo::Contract - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::Alias { .. } | TypeInfo::UnknownGeneric { .. } | TypeInfo::TraitType { .. } @@ -1447,7 +1462,7 @@ impl TypeInfo { | TypeInfo::Slice(_) | TypeInfo::Contract | TypeInfo::Storage { .. } - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::Placeholder(_) | TypeInfo::TypeParam(_) | TypeInfo::Alias { .. } @@ -1571,7 +1586,7 @@ impl TypeInfo { // TODO: We should not be receiving Numeric here. All uints // should be correctly typed here. // https://github.com/FuelLabs/sway/issues/5727 - TypeInfo::Numeric => AbiEncodeSizeHint::Exact(8), + TypeInfo::Numeric { .. } => AbiEncodeSizeHint::Exact(8), TypeInfo::UnsignedInteger(IntegerBits::V256) => AbiEncodeSizeHint::Exact(32), TypeInfo::B256 => AbiEncodeSizeHint::Exact(32), @@ -1680,7 +1695,7 @@ impl TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric => "u64".into(), // u64 is the default + Numeric { .. } => "u64".into(), // u64 is the default Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { @@ -1751,6 +1766,19 @@ impl TypeInfo { } } } + + pub fn numeric_unconstrained() -> TypeInfo { + TypeInfo::Numeric { min: None } + } + + pub fn numeric_constrained_by_value(v: u64) -> TypeInfo { + match v { + 0..=0xFF => TypeInfo::Numeric { min: None }, + 0x100..=0xFFFF => TypeInfo::Numeric { min: Some(IntegerBits::Sixteen) }, + 0x10000..=0xFFFFFFFF => TypeInfo::Numeric { min: Some(IntegerBits::ThirtyTwo) }, + 0x100000000.. => TypeInfo::Numeric { min: Some(IntegerBits::SixtyFour) }, + } + } } #[derive(Debug)] diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index 9eb13a8da3d..822a3fa9b2a 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -267,7 +267,7 @@ impl TypeSubstMap { (TypeInfo::Unknown, TypeInfo::Unknown) | (TypeInfo::Boolean, TypeInfo::Boolean) | (TypeInfo::B256, TypeInfo::B256) - | (TypeInfo::Numeric, TypeInfo::Numeric) + | (TypeInfo::Numeric { .. }, TypeInfo::Numeric { .. }) | (TypeInfo::Contract, TypeInfo::Contract) | (TypeInfo::ErrorRecovery(_), TypeInfo::ErrorRecovery(_)) | (TypeInfo::StringSlice, TypeInfo::StringSlice) @@ -510,7 +510,7 @@ impl TypeSubstMap { | TypeInfo::Boolean | TypeInfo::ContractCaller { .. } | TypeInfo::B256 - | TypeInfo::Numeric + | TypeInfo::Numeric { .. } | TypeInfo::RawUntypedPtr | TypeInfo::RawUntypedSlice | TypeInfo::Contract diff --git a/sway-core/src/type_system/unify/unifier.rs b/sway-core/src/type_system/unify/unifier.rs index c23bb0c3dd2..dc7b5e24c10 100644 --- a/sway-core/src/type_system/unify/unifier.rs +++ b/sway-core/src/type_system/unify/unifier.rs @@ -1,7 +1,7 @@ use std::fmt; use sway_error::{handler::Handler, type_error::TypeError}; -use sway_types::Span; +use sway_types::{integer_bits::IntegerBits, Span}; use crate::{ engine_threading::{Engines, PartialEqWithEngines, PartialEqWithEnginesContext, WithEngines}, @@ -109,7 +109,7 @@ impl<'a> Unifier<'a> { // correctness or perform further unification. (Boolean, Boolean) => (), (B256, B256) => (), - (Numeric, Numeric) => (), + (Numeric { .. }, Numeric { .. }) => (), (Contract, Contract) => (), (RawUntypedPtr, RawUntypedPtr) => (), (RawUntypedSlice, RawUntypedSlice) => (), @@ -252,10 +252,26 @@ impl<'a> Unifier<'a> { // For integers and numerics, we (potentially) unify the numeric // with the integer. (UnsignedInteger(r), UnsignedInteger(e)) if r == e => (), - (Numeric, e @ UnsignedInteger(_)) => { - self.replace_received_with_expected(received, e, span); + (Numeric { min }, e @ UnsignedInteger(expected_size)) => { + match (min, expected_size) { + (Some(IntegerBits::Sixteen), IntegerBits::Eight) + | (Some(IntegerBits::ThirtyTwo), IntegerBits::Eight) + | (Some(IntegerBits::ThirtyTwo), IntegerBits::Sixteen) + | (Some(IntegerBits::SixtyFour), IntegerBits::Eight) + | (Some(IntegerBits::SixtyFour), IntegerBits::Sixteen) + | (Some(IntegerBits::SixtyFour), IntegerBits::ThirtyTwo) => { + handler.emit_err( + TypeError::ConstrainedNumeric { + expected: self.engines.help_out(e).to_string(), + span: span.clone(), + } + .into(), + ); + } + _ => self.replace_received_with_expected(received, e, span), + } } - (r @ UnsignedInteger(_), Numeric) => { + (r @ UnsignedInteger(_), Numeric { .. }) => { self.replace_expected_with_received(expected, r, span); } diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 26a0777bdee..901eef65a5b 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -425,8 +425,8 @@ impl<'a> UnifyCheck<'a> { (_, Unknown) => true, (UnsignedInteger(lb), UnsignedInteger(rb)) => lb == rb, - (Numeric, UnsignedInteger(_)) => true, - (UnsignedInteger(_), Numeric) => true, + (Numeric { .. }, UnsignedInteger(_)) => true, + (UnsignedInteger(_), Numeric { .. }) => true, (StringSlice, StringSlice) => true, (StringArray(l), StringArray(r)) => l.val() == r.val(), @@ -495,7 +495,7 @@ impl<'a> UnifyCheck<'a> { // TypeId, they may later resolve to be different types in the type // engine (TypeInfo::Unknown, TypeInfo::Unknown) => false, - (TypeInfo::Numeric, TypeInfo::Numeric) => false, + (TypeInfo::Numeric { .. }, TypeInfo::Numeric { .. }) => false, (TypeInfo::Storage { .. }, TypeInfo::Storage { .. }) => false, // these cases are able to be directly compared @@ -659,8 +659,8 @@ impl<'a> UnifyCheck<'a> { (&**a, &**b), (_, Placeholder(_)) | (Placeholder(_), _) - | (UnsignedInteger(_), Numeric) - | (Numeric, UnsignedInteger(_)) + | (UnsignedInteger(_), Numeric { .. }) + | (Numeric { .. }, UnsignedInteger(_)) | (_, Unknown) | (Unknown, _) )) @@ -681,8 +681,8 @@ impl<'a> UnifyCheck<'a> { (&**a, &**b), (_, Placeholder(_)) | (Placeholder(_), _) - | (UnsignedInteger(_), Numeric) - | (Numeric, UnsignedInteger(_)) + | (UnsignedInteger(_), Numeric { .. }) + | (Numeric { .. }, UnsignedInteger(_)) | (_, Unknown) | (Unknown, _) )) diff --git a/sway-error/src/type_error.rs b/sway-error/src/type_error.rs index a271db5ce56..54fa30516a8 100644 --- a/sway-error/src/type_error.rs +++ b/sway-error/src/type_error.rs @@ -29,8 +29,13 @@ pub enum TypeError { received: String, span: Span, }, - #[error("Literal would overflow because its value does not fit into \"{expected}\"")] - LiteralOverflow { expected: String, span: Span }, + #[error( + "Literal would overflow because its value do not fit into \"{expected}\"", + )] + ConstrainedNumeric { + expected: String, + span: Span, + }, } impl Spanned for TypeError { @@ -40,7 +45,7 @@ impl Spanned for TypeError { MismatchedType { span, .. } => span.clone(), UnknownType { span } => span.clone(), MatchArmScrutineeWrongType { span, .. } => span.clone(), - LiteralOverflow { span, .. } => span.clone(), + ConstrainedNumeric { span, .. } => span.clone(), } } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock new file mode 100644 index 00000000000..78d05c9f584 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-8C75EDA46478A382" + +[[package]] +name = "numeric_constraints" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-8C75EDA46478A382" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml new file mode 100644 index 00000000000..f80aa77fe86 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +implicit-std = false +license = "Apache-2.0" +name = "numeric_constraints" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw new file mode 100644 index 00000000000..11db245bc60 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw @@ -0,0 +1,15 @@ +script; + +fn main() { + // u8 + let _a = 0x100; + Vec::::new().push(_a); + + // u16 + let _a = 0x10000; + Vec::::new().push(_a); + + // u32 + let _a = 0x100000000; + Vec::::new().push(_a); +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml new file mode 100644 index 00000000000..0c5a332fe8c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml @@ -0,0 +1,13 @@ +category = "fail" + +# check: $()let _a = 0x100; +# nextln: $()Vec::::new().push(_a); +# nextln: $()Literal would overflow because its value do not fit into "u8" + +# check: $()let _a = 0x10000; +# nextln: $()Vec::::new().push(_a); +# nextln: $()Literal would overflow because its value do not fit into "u16" + +# check: $()let _a = 0x100000000; +# nextln: $()Vec::::new().push(_a); +# nextln: $()Literal would overflow because its value do not fit into "u32" From 8ddd6a3ed5b06014195a049105353055f7e645c8 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 12:00:17 +0100 Subject: [PATCH 02/20] check numeric constraints on type_check_analyze phase --- sway-core/src/abi_generation/abi_str.rs | 2 +- sway-core/src/abi_generation/evm_abi.rs | 2 +- sway-core/src/ir_generation/convert.rs | 2 +- sway-core/src/language/literal.rs | 2 +- .../src/language/ty/expression/expression.rs | 41 ++++---------- .../ast_node/expression/intrinsic_function.rs | 12 ++--- .../ast_node/expression/typed_expression.rs | 10 ++-- .../semantic_analysis/node_dependencies.rs | 2 +- sway-core/src/type_system/engine.rs | 6 +-- sway-core/src/type_system/id.rs | 4 +- sway-core/src/type_system/info.rs | 54 +++++-------------- .../src/type_system/substitute/subst_map.rs | 4 +- sway-core/src/type_system/unify/unifier.rs | 26 ++------- .../src/type_system/unify/unify_check.rs | 14 ++--- .../should_fail/numeric_constraints/test.toml | 3 -- 15 files changed, 59 insertions(+), 125 deletions(-) diff --git a/sway-core/src/abi_generation/abi_str.rs b/sway-core/src/abi_generation/abi_str.rs index efbe5f33fa1..339a9453f81 100644 --- a/sway-core/src/abi_generation/abi_str.rs +++ b/sway-core/src/abi_generation/abi_str.rs @@ -112,7 +112,7 @@ impl TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric { .. } => "u64".into(), // u64 is the default + Numeric => "u64".into(), // u64 is the default Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { diff --git a/sway-core/src/abi_generation/evm_abi.rs b/sway-core/src/abi_generation/evm_abi.rs index 10dad9c247c..f899e0de209 100644 --- a/sway-core/src/abi_generation/evm_abi.rs +++ b/sway-core/src/abi_generation/evm_abi.rs @@ -90,7 +90,7 @@ pub fn abi_str(type_info: &TypeInfo, engines: &Engines) -> String { format!("({})", field_strs.join(", ")) } B256 => "uint256".into(), - Numeric { .. } => "u64".into(), // u64 is the default + Numeric => "u64".into(), // u64 is the default Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index 0da0eaa2a51..3aa22401338 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -98,7 +98,7 @@ fn convert_resolved_type_info( TypeInfo::UnsignedInteger(IntegerBits::Sixteen) | TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) | TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) - | TypeInfo::Numeric { .. } => Type::get_uint64(context), + | TypeInfo::Numeric => Type::get_uint64(context), TypeInfo::Boolean => Type::get_bool(context), TypeInfo::B256 => Type::get_b256(context), TypeInfo::StringSlice => Type::get_slice(context), diff --git a/sway-core/src/language/literal.rs b/sway-core/src/language/literal.rs index f97fc50cbd4..afedaad12d0 100644 --- a/sway-core/src/language/literal.rs +++ b/sway-core/src/language/literal.rs @@ -147,7 +147,7 @@ impl Literal { pub(crate) fn to_typeinfo(&self) -> TypeInfo { match self { Literal::String(_) => TypeInfo::StringSlice, - Literal::Numeric(v) => TypeInfo::numeric_constrained_by_value(*v), + Literal::Numeric(_) => TypeInfo::Numeric, Literal::U8(_) => TypeInfo::UnsignedInteger(IntegerBits::Eight), Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index b442e0ba0cd..52546b81250 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -110,41 +110,22 @@ impl TypeCheckAnalysis for TyExpression { handler: &Handler, ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { + // Check literal "fits" into assigned typed. match &self.expression { - // Check literal "fits" into assigned typed. TyExpressionVariant::Literal(Literal::Numeric(literal_value)) => { let t = ctx.engines.te().get(self.return_type); - if let TypeInfo::UnsignedInteger(bits) = &*t { - if bits.would_overflow(*literal_value) { - handler.emit_err(CompileError::TypeError(TypeError::LiteralOverflow { - expected: format!("{:?}", ctx.engines.help_out(t)), - span: self.span.clone(), - })); - } - } - } - // Check all array items are the same - TyExpressionVariant::Array { - elem_type, - contents, - } => { - let array_elem_type = ctx.engines.te().get(*elem_type); - if !matches!(&*array_elem_type, TypeInfo::Never) { - let unify = crate::type_system::unify::unifier::Unifier::new( - ctx.engines, - "", - unify::unifier::UnifyKind::Default, - ); - for element in contents { - let element_type = ctx.engines.te().get(*elem_type); - - // If the element is never, we do not need to check - if matches!(&*element_type, TypeInfo::Never) { - continue; + match &*t { + TypeInfo::UnsignedInteger(bits) => { + if bits.would_overflow(*literal_value) { + handler.emit_err(CompileError::TypeError( + TypeError::ConstrainedNumeric { + expected: format!("{:?}", ctx.engines.help_out(t)), + span: self.span.clone(), + }, + )); } - - unify.unify(handler, element.return_type, *elem_type, &element.span) } + _ => {} } } _ => {} diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index fa686c7fe5f..0df9f72e458 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -584,7 +584,7 @@ fn type_check_not( let t_arc = engines.te().get(operand_expr.return_type); let t = &*t_arc; match t { - TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } => Ok(( + TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => Ok(( ty::TyIntrinsicFunctionKind { kind, arguments: vec![operand_expr], @@ -908,7 +908,7 @@ fn type_check_cmp( && matches!(arg_ty, TypeInfo::Boolean | TypeInfo::RawUntypedPtr); let is_valid_arg_ty = matches!( arg_ty, - TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } | TypeInfo::B256 + TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric | TypeInfo::B256 ) || is_eq_bool_ptr; if !is_valid_arg_ty { @@ -1439,7 +1439,7 @@ fn type_check_arith_binary_op( })); } - let return_type = type_engine.insert(engines, TypeInfo::numeric_unconstrained(), None); + let return_type = type_engine.insert(engines, TypeInfo::Numeric, None); let mut ctx = ctx .by_ref() .with_type_annotation(return_type) @@ -1501,7 +1501,7 @@ fn type_check_bitwise_binary_op( let t_arc = engines.te().get(lhs.return_type); let t = &*t_arc; match t { - TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { min: None } => Ok(( + TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => Ok(( ty::TyIntrinsicFunctionKind { kind, arguments: vec![lhs, rhs], @@ -1568,14 +1568,14 @@ fn type_check_shift_binary_op( handler, ctx.by_ref() .with_help_text("Incorrect argument type") - .with_type_annotation(engines.te().insert(engines, TypeInfo::numeric_unconstrained(), None)), + .with_type_annotation(engines.te().insert(engines, TypeInfo::Numeric, None)), rhs, )?; let t_arc = engines.te().get(lhs.return_type); let t = &*t_arc; match t { - TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } => Ok(( + TypeInfo::B256 | TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => Ok(( ty::TyIntrinsicFunctionKind { kind, arguments: vec![lhs, rhs], diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index d9ece54a4ed..26c862b21a1 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -450,7 +450,7 @@ impl ty::TyExpression { if let ty::TyExpressionVariant::Literal(lit) = typed_expression.clone().expression { if let Literal::Numeric(_) = lit { match &*type_engine.get(typed_expression.return_type) { - TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric { .. } => { + TypeInfo::UnsignedInteger(_) | TypeInfo::Numeric => { typed_expression = Self::resolve_numeric_literal( handler, ctx, @@ -471,7 +471,7 @@ impl ty::TyExpression { let type_engine = engines.te(); let return_type = match &lit { Literal::String(_) => TypeInfo::StringSlice, - Literal::Numeric(v) => TypeInfo::numeric_constrained_by_value(*v), + Literal::Numeric(_) => TypeInfo::Numeric, Literal::U8(_) => TypeInfo::UnsignedInteger(IntegerBits::Eight), Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), @@ -2524,11 +2524,11 @@ impl ty::TyExpression { // Numerics are limited to u64 for now IntegerBits::V256 => (Ok(Literal::U256(U256::from(num))), new_type), }, - n @ TypeInfo::Numeric { .. } => ( + TypeInfo::Numeric => ( num.to_string().parse().map(Literal::Numeric).map_err(|e| { - Literal::handle_parse_int_error(engines, e, n.clone(), span.clone()) + Literal::handle_parse_int_error(engines, e, TypeInfo::Numeric, span.clone()) }), - type_engine.insert(engines, n.clone(), None), + type_engine.insert(engines, TypeInfo::Numeric, None), ), _ => unreachable!("Unexpected type for integer literals"), }, diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index b1ec57334f1..398c590566a 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -1021,7 +1021,7 @@ fn type_info_name(type_info: &TypeInfo) -> String { TypeInfo::Tuple(fields) if fields.is_empty() => "unit", TypeInfo::Tuple(..) => "tuple", TypeInfo::B256 => "b256", - TypeInfo::Numeric { .. } => "numeric", + TypeInfo::Numeric => "numeric", TypeInfo::Contract => "contract", TypeInfo::ErrorRecovery(_) => "err_recov", TypeInfo::Unknown => "unknown", diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 0b975ef5aa0..206bed81bb2 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -309,7 +309,7 @@ impl TypeEngine { | TypeInfo::RawUntypedSlice | TypeInfo::Alias { .. } | TypeInfo::TraitType { .. } => false, - TypeInfo::Numeric { .. } => true, + TypeInfo::Numeric => true, } } @@ -366,7 +366,7 @@ impl TypeEngine { | TypeInfo::RawUntypedSlice | TypeInfo::Alias { .. } | TypeInfo::TraitType { .. } => {} - TypeInfo::Numeric { .. } => { + TypeInfo::Numeric => { self.unify( handler, engines, @@ -405,7 +405,7 @@ fn info_to_source_id(ty: &TypeInfo) -> Option { match ty { TypeInfo::Unknown | TypeInfo::UnsignedInteger(_) - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::Boolean | TypeInfo::B256 | TypeInfo::RawUntypedPtr diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index a82f1b2e912..9410fd2d6ac 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -222,7 +222,7 @@ impl TypeId { | TypeInfo::RawUntypedSlice | TypeInfo::Boolean | TypeInfo::B256 - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::Contract | TypeInfo::ErrorRecovery(_) | TypeInfo::TraitType { .. } => {} @@ -471,7 +471,7 @@ impl TypeId { | TypeInfo::Placeholder(..) | TypeInfo::TraitType { .. } | TypeInfo::TypeParam(..) - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric ), IncludeNumeric::No => matches!( x, diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index 3216a74e46f..eeb22e6a734 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -158,9 +158,7 @@ pub enum TypeInfo { B256, /// This means that specific type of a number is not yet known. It will be /// determined via inference at a later time. - Numeric { - min: Option - }, + Numeric, Contract, // used for recovering from errors in the ast ErrorRecovery(ErrorEmitted), @@ -283,10 +281,8 @@ impl HashWithEngines for TypeInfo { to_mutable_value.hash(state); ty.hash(state, engines); } - TypeInfo::Numeric { min } => { - min.hash(state); - } TypeInfo::StringSlice + | TypeInfo::Numeric | TypeInfo::Boolean | TypeInfo::B256 | TypeInfo::Contract @@ -442,9 +438,7 @@ impl PartialEqWithEngines for TypeInfo { .get(l_ty.type_id) .eq(&type_engine.get(r_ty.type_id), ctx)) } - (Self::Numeric { min: a }, Self::Numeric { min: b }) => { - a.eq(b) - } + (l, r) => l.discriminant_value() == r.discriminant_value(), } } @@ -574,9 +568,6 @@ impl OrdWithEngines for TypeInfo { .get(l_ty.type_id) .cmp(&type_engine.get(r_ty.type_id), ctx) }), - (Self::Numeric { min: a }, Self::Numeric { min: b }) => { - a.cmp(b) - } (l, r) => l.discriminant_value().cmp(&r.discriminant_value()), } } @@ -614,10 +605,7 @@ impl DisplayWithEngines for TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric { min } => format!("numeric{}", match min { - Some(min) => format!(" {min}"), - None => "".to_string() - }), + Numeric => "numeric".into(), Contract => "contract".into(), ErrorRecovery(_) => "unknown".into(), Enum(decl_ref) => { @@ -722,10 +710,7 @@ impl DebugWithEngines for TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric { min } => format!("numeric{}", match min { - Some(min) => format!(" {min}"), - None => "".to_string() - }), + Numeric => "numeric".into(), Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { @@ -807,7 +792,7 @@ impl TypeInfo { TypeInfo::ContractCaller { .. } => 9, TypeInfo::Custom { .. } => 10, TypeInfo::B256 => 11, - TypeInfo::Numeric { .. } => 12, + TypeInfo::Numeric => 12, TypeInfo::Contract => 13, TypeInfo::ErrorRecovery(_) => 14, TypeInfo::Array(_, _) => 15, @@ -1166,7 +1151,7 @@ impl TypeInfo { | TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) | TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) | TypeInfo::RawUntypedPtr - | TypeInfo::Numeric { .. } // TODO-IG: Should Ptr and Ref also be a copy type? + | TypeInfo::Numeric // TODO-IG: Should Ptr and Ref also be a copy type? | TypeInfo::Never ) || self.is_unit() } @@ -1271,7 +1256,7 @@ impl TypeInfo { | TypeInfo::Tuple(_) | TypeInfo::ContractCaller { .. } | TypeInfo::B256 - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::RawUntypedPtr | TypeInfo::RawUntypedSlice | TypeInfo::Ptr(_) @@ -1318,7 +1303,7 @@ impl TypeInfo { | TypeInfo::Tuple(_) | TypeInfo::B256 | TypeInfo::UnknownGeneric { .. } - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::Never | TypeInfo::StringSlice => Ok(()), TypeInfo::Alias { ty, .. } => { @@ -1402,7 +1387,7 @@ impl TypeInfo { | TypeInfo::StringSlice | TypeInfo::Array(_, _) | TypeInfo::Contract - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::Alias { .. } | TypeInfo::UnknownGeneric { .. } | TypeInfo::TraitType { .. } @@ -1462,7 +1447,7 @@ impl TypeInfo { | TypeInfo::Slice(_) | TypeInfo::Contract | TypeInfo::Storage { .. } - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::Placeholder(_) | TypeInfo::TypeParam(_) | TypeInfo::Alias { .. } @@ -1586,7 +1571,7 @@ impl TypeInfo { // TODO: We should not be receiving Numeric here. All uints // should be correctly typed here. // https://github.com/FuelLabs/sway/issues/5727 - TypeInfo::Numeric { .. } => AbiEncodeSizeHint::Exact(8), + TypeInfo::Numeric => AbiEncodeSizeHint::Exact(8), TypeInfo::UnsignedInteger(IntegerBits::V256) => AbiEncodeSizeHint::Exact(32), TypeInfo::B256 => AbiEncodeSizeHint::Exact(32), @@ -1695,7 +1680,7 @@ impl TypeInfo { format!("({})", field_strs.join(", ")) } B256 => "b256".into(), - Numeric { .. } => "u64".into(), // u64 is the default + Numeric => "u64".into(), // u64 is the default Contract => "contract".into(), ErrorRecovery(_) => "unknown due to error".into(), Enum(decl_ref) => { @@ -1766,19 +1751,6 @@ impl TypeInfo { } } } - - pub fn numeric_unconstrained() -> TypeInfo { - TypeInfo::Numeric { min: None } - } - - pub fn numeric_constrained_by_value(v: u64) -> TypeInfo { - match v { - 0..=0xFF => TypeInfo::Numeric { min: None }, - 0x100..=0xFFFF => TypeInfo::Numeric { min: Some(IntegerBits::Sixteen) }, - 0x10000..=0xFFFFFFFF => TypeInfo::Numeric { min: Some(IntegerBits::ThirtyTwo) }, - 0x100000000.. => TypeInfo::Numeric { min: Some(IntegerBits::SixtyFour) }, - } - } } #[derive(Debug)] diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index 822a3fa9b2a..9eb13a8da3d 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -267,7 +267,7 @@ impl TypeSubstMap { (TypeInfo::Unknown, TypeInfo::Unknown) | (TypeInfo::Boolean, TypeInfo::Boolean) | (TypeInfo::B256, TypeInfo::B256) - | (TypeInfo::Numeric { .. }, TypeInfo::Numeric { .. }) + | (TypeInfo::Numeric, TypeInfo::Numeric) | (TypeInfo::Contract, TypeInfo::Contract) | (TypeInfo::ErrorRecovery(_), TypeInfo::ErrorRecovery(_)) | (TypeInfo::StringSlice, TypeInfo::StringSlice) @@ -510,7 +510,7 @@ impl TypeSubstMap { | TypeInfo::Boolean | TypeInfo::ContractCaller { .. } | TypeInfo::B256 - | TypeInfo::Numeric { .. } + | TypeInfo::Numeric | TypeInfo::RawUntypedPtr | TypeInfo::RawUntypedSlice | TypeInfo::Contract diff --git a/sway-core/src/type_system/unify/unifier.rs b/sway-core/src/type_system/unify/unifier.rs index dc7b5e24c10..c23bb0c3dd2 100644 --- a/sway-core/src/type_system/unify/unifier.rs +++ b/sway-core/src/type_system/unify/unifier.rs @@ -1,7 +1,7 @@ use std::fmt; use sway_error::{handler::Handler, type_error::TypeError}; -use sway_types::{integer_bits::IntegerBits, Span}; +use sway_types::Span; use crate::{ engine_threading::{Engines, PartialEqWithEngines, PartialEqWithEnginesContext, WithEngines}, @@ -109,7 +109,7 @@ impl<'a> Unifier<'a> { // correctness or perform further unification. (Boolean, Boolean) => (), (B256, B256) => (), - (Numeric { .. }, Numeric { .. }) => (), + (Numeric, Numeric) => (), (Contract, Contract) => (), (RawUntypedPtr, RawUntypedPtr) => (), (RawUntypedSlice, RawUntypedSlice) => (), @@ -252,26 +252,10 @@ impl<'a> Unifier<'a> { // For integers and numerics, we (potentially) unify the numeric // with the integer. (UnsignedInteger(r), UnsignedInteger(e)) if r == e => (), - (Numeric { min }, e @ UnsignedInteger(expected_size)) => { - match (min, expected_size) { - (Some(IntegerBits::Sixteen), IntegerBits::Eight) - | (Some(IntegerBits::ThirtyTwo), IntegerBits::Eight) - | (Some(IntegerBits::ThirtyTwo), IntegerBits::Sixteen) - | (Some(IntegerBits::SixtyFour), IntegerBits::Eight) - | (Some(IntegerBits::SixtyFour), IntegerBits::Sixteen) - | (Some(IntegerBits::SixtyFour), IntegerBits::ThirtyTwo) => { - handler.emit_err( - TypeError::ConstrainedNumeric { - expected: self.engines.help_out(e).to_string(), - span: span.clone(), - } - .into(), - ); - } - _ => self.replace_received_with_expected(received, e, span), - } + (Numeric, e @ UnsignedInteger(_)) => { + self.replace_received_with_expected(received, e, span); } - (r @ UnsignedInteger(_), Numeric { .. }) => { + (r @ UnsignedInteger(_), Numeric) => { self.replace_expected_with_received(expected, r, span); } diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 901eef65a5b..26a0777bdee 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -425,8 +425,8 @@ impl<'a> UnifyCheck<'a> { (_, Unknown) => true, (UnsignedInteger(lb), UnsignedInteger(rb)) => lb == rb, - (Numeric { .. }, UnsignedInteger(_)) => true, - (UnsignedInteger(_), Numeric { .. }) => true, + (Numeric, UnsignedInteger(_)) => true, + (UnsignedInteger(_), Numeric) => true, (StringSlice, StringSlice) => true, (StringArray(l), StringArray(r)) => l.val() == r.val(), @@ -495,7 +495,7 @@ impl<'a> UnifyCheck<'a> { // TypeId, they may later resolve to be different types in the type // engine (TypeInfo::Unknown, TypeInfo::Unknown) => false, - (TypeInfo::Numeric { .. }, TypeInfo::Numeric { .. }) => false, + (TypeInfo::Numeric, TypeInfo::Numeric) => false, (TypeInfo::Storage { .. }, TypeInfo::Storage { .. }) => false, // these cases are able to be directly compared @@ -659,8 +659,8 @@ impl<'a> UnifyCheck<'a> { (&**a, &**b), (_, Placeholder(_)) | (Placeholder(_), _) - | (UnsignedInteger(_), Numeric { .. }) - | (Numeric { .. }, UnsignedInteger(_)) + | (UnsignedInteger(_), Numeric) + | (Numeric, UnsignedInteger(_)) | (_, Unknown) | (Unknown, _) )) @@ -681,8 +681,8 @@ impl<'a> UnifyCheck<'a> { (&**a, &**b), (_, Placeholder(_)) | (Placeholder(_), _) - | (UnsignedInteger(_), Numeric { .. }) - | (Numeric { .. }, UnsignedInteger(_)) + | (UnsignedInteger(_), Numeric) + | (Numeric, UnsignedInteger(_)) | (_, Unknown) | (Unknown, _) )) diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml index 0c5a332fe8c..44fcb34c4e3 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml @@ -1,13 +1,10 @@ category = "fail" # check: $()let _a = 0x100; -# nextln: $()Vec::::new().push(_a); # nextln: $()Literal would overflow because its value do not fit into "u8" # check: $()let _a = 0x10000; -# nextln: $()Vec::::new().push(_a); # nextln: $()Literal would overflow because its value do not fit into "u16" # check: $()let _a = 0x100000000; -# nextln: $()Vec::::new().push(_a); # nextln: $()Literal would overflow because its value do not fit into "u32" From 161300bef0d39e2424827ccf6f97b01e9ce037ed Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 12:05:36 +0100 Subject: [PATCH 03/20] fmt and clippy issues --- sway-error/src/type_error.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sway-error/src/type_error.rs b/sway-error/src/type_error.rs index 54fa30516a8..93b3844e4e5 100644 --- a/sway-error/src/type_error.rs +++ b/sway-error/src/type_error.rs @@ -29,13 +29,8 @@ pub enum TypeError { received: String, span: Span, }, - #[error( - "Literal would overflow because its value do not fit into \"{expected}\"", - )] - ConstrainedNumeric { - expected: String, - span: Span, - }, + #[error("Literal would overflow because its value do not fit into \"{expected}\"")] + ConstrainedNumeric { expected: String, span: Span }, } impl Spanned for TypeError { From ee5a18517c36219a5d4e37b2a73207f533b309e7 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 12:11:58 +0100 Subject: [PATCH 04/20] fmt and clippy issues --- .../src/language/ty/expression/expression.rs | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 52546b81250..3f4a31b4119 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -111,24 +111,16 @@ impl TypeCheckAnalysis for TyExpression { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { // Check literal "fits" into assigned typed. - match &self.expression { - TyExpressionVariant::Literal(Literal::Numeric(literal_value)) => { - let t = ctx.engines.te().get(self.return_type); - match &*t { - TypeInfo::UnsignedInteger(bits) => { - if bits.would_overflow(*literal_value) { - handler.emit_err(CompileError::TypeError( - TypeError::ConstrainedNumeric { - expected: format!("{:?}", ctx.engines.help_out(t)), - span: self.span.clone(), - }, - )); - } - } - _ => {} + if let TyExpressionVariant::Literal(Literal::Numeric(literal_value)) = &self.expression { + let t = ctx.engines.te().get(self.return_type); + if let TypeInfo::UnsignedInteger(bits) = &*t { + if bits.would_overflow(*literal_value) { + handler.emit_err(CompileError::TypeError(TypeError::ConstrainedNumeric { + expected: format!("{:?}", ctx.engines.help_out(t)), + span: self.span.clone(), + })); } } - _ => {} } self.expression.type_check_analyze(handler, ctx) } From a5fab2813fdd0974746808f5b1a314f9b8b0ec47 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 14:50:46 +0100 Subject: [PATCH 05/20] check all array elements are of the same type --- .../src/language/ty/expression/expression.rs | 50 +++++++++++++++---- .../numeric_constraints/src/main.sw | 5 +- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 3f4a31b4119..880d3ae17a7 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -110,17 +110,49 @@ impl TypeCheckAnalysis for TyExpression { handler: &Handler, ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { - // Check literal "fits" into assigned typed. - if let TyExpressionVariant::Literal(Literal::Numeric(literal_value)) = &self.expression { - let t = ctx.engines.te().get(self.return_type); - if let TypeInfo::UnsignedInteger(bits) = &*t { - if bits.would_overflow(*literal_value) { - handler.emit_err(CompileError::TypeError(TypeError::ConstrainedNumeric { - expected: format!("{:?}", ctx.engines.help_out(t)), - span: self.span.clone(), - })); + match &self.expression { + // Check literal "fits" into assigned typed. + TyExpressionVariant::Literal(Literal::Numeric(literal_value)) => { + let t = ctx.engines.te().get(self.return_type); + if let TypeInfo::UnsignedInteger(bits) = &*t { + if bits.would_overflow(*literal_value) { + handler.emit_err(CompileError::TypeError(TypeError::ConstrainedNumeric { + expected: format!("{:?}", ctx.engines.help_out(t)), + span: self.span.clone(), + })); + } } } + // Check all array items are the same + TyExpressionVariant::Array { + elem_type, + contents, + } => { + let eqctx = PartialEqWithEnginesContext::new(ctx.engines); + let array_elem_type = ctx.engines.te().get(*elem_type); + + // If the array element is never, we do not need to check + if !matches!(&*array_elem_type, TypeInfo::Never) { + for element in contents { + let elem_type = ctx.engines.te().get(element.return_type); + + // If the element is never, we do not need to check + if !matches!(&*array_elem_type, TypeInfo::Never) { + continue; + } + + if !array_elem_type.eq(&*elem_type, &eqctx) { + handler.emit_err(CompileError::TypeError(TypeError::MismatchedType { + expected: format!("{:?}", ctx.engines.help_out(&array_elem_type)), + received: format!("{:?}", ctx.engines.help_out(elem_type)), + help_text: String::new(), + span: element.span.clone(), + })); + } + } + } + } + _ => {} } self.expression.type_check_analyze(handler, ctx) } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw index 11db245bc60..3286688e0b9 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw @@ -12,4 +12,7 @@ fn main() { // u32 let _a = 0x100000000; Vec::::new().push(_a); -} \ No newline at end of file + + // Array + let a = [1, 2, "hello"]; +} From 3230419eb819b0fab27729551209965258749310 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 15:44:13 +0100 Subject: [PATCH 06/20] fix typo --- sway-error/src/type_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway-error/src/type_error.rs b/sway-error/src/type_error.rs index 93b3844e4e5..71bc0499df4 100644 --- a/sway-error/src/type_error.rs +++ b/sway-error/src/type_error.rs @@ -29,7 +29,7 @@ pub enum TypeError { received: String, span: Span, }, - #[error("Literal would overflow because its value do not fit into \"{expected}\"")] + #[error("Literal would overflow because its value does not fit into \"{expected}\"")] ConstrainedNumeric { expected: String, span: Span }, } From 2dbb42b1a0739e888ac8973013a9d6be75d5f1f1 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 17:37:03 +0100 Subject: [PATCH 07/20] better names --- .../src/language/ty/expression/expression.rs | 4 ++-- sway-error/src/type_error.rs | 4 ++-- .../should_fail/numeric_constraints/Forc.lock | 13 ------------- .../should_fail/numeric_constraints/Forc.toml | 9 --------- .../numeric_constraints/src/main.sw | 18 ------------------ .../should_fail/numeric_constraints/test.toml | 10 ---------- 6 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 880d3ae17a7..72157b4615f 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -116,7 +116,7 @@ impl TypeCheckAnalysis for TyExpression { let t = ctx.engines.te().get(self.return_type); if let TypeInfo::UnsignedInteger(bits) = &*t { if bits.would_overflow(*literal_value) { - handler.emit_err(CompileError::TypeError(TypeError::ConstrainedNumeric { + handler.emit_err(CompileError::TypeError(TypeError::LiteralOverflow { expected: format!("{:?}", ctx.engines.help_out(t)), span: self.span.clone(), })); @@ -137,7 +137,7 @@ impl TypeCheckAnalysis for TyExpression { let elem_type = ctx.engines.te().get(element.return_type); // If the element is never, we do not need to check - if !matches!(&*array_elem_type, TypeInfo::Never) { + if matches!(&*array_elem_type, TypeInfo::Never) { continue; } diff --git a/sway-error/src/type_error.rs b/sway-error/src/type_error.rs index 71bc0499df4..a271db5ce56 100644 --- a/sway-error/src/type_error.rs +++ b/sway-error/src/type_error.rs @@ -30,7 +30,7 @@ pub enum TypeError { span: Span, }, #[error("Literal would overflow because its value does not fit into \"{expected}\"")] - ConstrainedNumeric { expected: String, span: Span }, + LiteralOverflow { expected: String, span: Span }, } impl Spanned for TypeError { @@ -40,7 +40,7 @@ impl Spanned for TypeError { MismatchedType { span, .. } => span.clone(), UnknownType { span } => span.clone(), MatchArmScrutineeWrongType { span, .. } => span.clone(), - ConstrainedNumeric { span, .. } => span.clone(), + LiteralOverflow { span, .. } => span.clone(), } } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock deleted file mode 100644 index 78d05c9f584..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.lock +++ /dev/null @@ -1,13 +0,0 @@ -[[package]] -name = "core" -source = "path+from-root-8C75EDA46478A382" - -[[package]] -name = "numeric_constraints" -source = "member" -dependencies = ["std"] - -[[package]] -name = "std" -source = "path+from-root-8C75EDA46478A382" -dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml deleted file mode 100644 index f80aa77fe86..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/Forc.toml +++ /dev/null @@ -1,9 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -implicit-std = false -license = "Apache-2.0" -name = "numeric_constraints" - -[dependencies] -std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw deleted file mode 100644 index 3286688e0b9..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/src/main.sw +++ /dev/null @@ -1,18 +0,0 @@ -script; - -fn main() { - // u8 - let _a = 0x100; - Vec::::new().push(_a); - - // u16 - let _a = 0x10000; - Vec::::new().push(_a); - - // u32 - let _a = 0x100000000; - Vec::::new().push(_a); - - // Array - let a = [1, 2, "hello"]; -} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml deleted file mode 100644 index 44fcb34c4e3..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/numeric_constraints/test.toml +++ /dev/null @@ -1,10 +0,0 @@ -category = "fail" - -# check: $()let _a = 0x100; -# nextln: $()Literal would overflow because its value do not fit into "u8" - -# check: $()let _a = 0x10000; -# nextln: $()Literal would overflow because its value do not fit into "u16" - -# check: $()let _a = 0x100000000; -# nextln: $()Literal would overflow because its value do not fit into "u32" From 59499b7ea300126b04ad33ca0ae036718402d3dd Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 18:14:47 +0100 Subject: [PATCH 08/20] unify array elements --- .../src/language/ty/expression/expression.rs | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 72157b4615f..3857fcced46 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -128,28 +128,13 @@ impl TypeCheckAnalysis for TyExpression { elem_type, contents, } => { - let eqctx = PartialEqWithEnginesContext::new(ctx.engines); - let array_elem_type = ctx.engines.te().get(*elem_type); - - // If the array element is never, we do not need to check - if !matches!(&*array_elem_type, TypeInfo::Never) { - for element in contents { - let elem_type = ctx.engines.te().get(element.return_type); - - // If the element is never, we do not need to check - if matches!(&*array_elem_type, TypeInfo::Never) { - continue; - } - - if !array_elem_type.eq(&*elem_type, &eqctx) { - handler.emit_err(CompileError::TypeError(TypeError::MismatchedType { - expected: format!("{:?}", ctx.engines.help_out(&array_elem_type)), - received: format!("{:?}", ctx.engines.help_out(elem_type)), - help_text: String::new(), - span: element.span.clone(), - })); - } - } + for element in contents { + let unify = crate::type_system::unify::unifier::Unifier::new( + ctx.engines, + "", + unify::unifier::UnifyKind::Default, + ); + unify.unify(handler, element.return_type, *elem_type, &element.span) } } _ => {} From 0ff2ce8f64df2e1b4a5caa146dde77279c8b524a Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 13 Aug 2024 21:02:21 +0100 Subject: [PATCH 09/20] check for never types in arrays --- sway-core/src/language/ty/expression/expression.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 3857fcced46..b442e0ba0cd 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -128,13 +128,23 @@ impl TypeCheckAnalysis for TyExpression { elem_type, contents, } => { - for element in contents { + let array_elem_type = ctx.engines.te().get(*elem_type); + if !matches!(&*array_elem_type, TypeInfo::Never) { let unify = crate::type_system::unify::unifier::Unifier::new( ctx.engines, "", unify::unifier::UnifyKind::Default, ); - unify.unify(handler, element.return_type, *elem_type, &element.span) + for element in contents { + let element_type = ctx.engines.te().get(*elem_type); + + // If the element is never, we do not need to check + if matches!(&*element_type, TypeInfo::Never) { + continue; + } + + unify.unify(handler, element.return_type, *elem_type, &element.span) + } } } _ => {} From 4bc35897d5e4609231aff8dd514734360a32aa5d Mon Sep 17 00:00:00 2001 From: xunilrj Date: Thu, 15 Aug 2024 15:28:54 +0100 Subject: [PATCH 10/20] snapshot tests and fix for array with numerics --- .github/workflows/ci.yml | 68 ++-- Cargo.lock | 90 ++++- .../test/data/standalone_contract/Forc.lock | 13 + .../src/language/ty/declaration/function.rs | 7 +- .../ast_node/expression/intrinsic_function.rs | 2 +- .../ast_node/expression/typed_expression.rs | 25 +- .../typed_expression/method_application.rs | 2 +- sway-core/src/type_system/id.rs | 48 +-- sway-core/src/type_system/priv_prelude.rs | 2 +- test/Cargo.toml | 12 +- test/src/e2e_vm_tests/harness.rs | 2 +- test/src/e2e_vm_tests/mod.rs | 337 ++++++++++++++---- .../type_check_analyze_errors/src/main.sw | 7 + .../stdout@fuel-encoding-v1-debug.snap | 133 +++++++ .../type_check_analyze_errors/test.toml | 15 +- test/src/ir_generation/mod.rs | 4 +- test/src/lib.rs | 163 +++++++++ test/src/main.rs | 168 +-------- test/src/test_consistency.rs | 2 +- test/test/fuel_snapshot_release.rs | 71 ++++ 20 files changed, 858 insertions(+), 313 deletions(-) create mode 100644 forc-plugins/forc-client/test/data/standalone_contract/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout@fuel-encoding-v1-debug.snap create mode 100644 test/src/lib.rs create mode 100644 test/test/fuel_snapshot_release.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76640ea54f7..63d071eb336 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,7 +146,7 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 - + - name: Set up Rust uses: actions-rs/toolchain@v1 with: @@ -194,7 +194,7 @@ jobs: - name: Run mdbook build uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: '0.4.25' + mdbook-version: "0.4.25" - name: Emit book logs to tmp.txt, fail if build logs contain 'ERROR' run: | MDBOOK_preprocessor__FORC_documenter__STRICT="true" mdbook build docs/book 2>&1 | tee tmp.txt @@ -276,10 +276,10 @@ jobs: with: toolchain: ${{ env.RUST_VERSION }} - uses: Swatinem/rust-cache@v2 - - name: 'Build Workspace' + - name: "Build Workspace" run: cargo build --locked --workspace --all-features --all-targets env: - RUSTFLAGS: '-D warnings' + RUSTFLAGS: "-D warnings" cargo-clippy: runs-on: ubuntu-latest @@ -306,7 +306,7 @@ jobs: uses: baptiste0928/cargo-install@v1 with: crate: cargo-toml-lint - version: '0.1' + version: "0.1" - name: Run Cargo.toml linter run: git ls-files | grep Cargo.toml$ | grep -v 'templates/' | xargs --verbose -n 1 cargo-toml-lint @@ -341,9 +341,9 @@ jobs: mv fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core - name: Cargo Run E2E Tests (Fuel VM) run: | - fuel-core run --db-type in-memory --debug & + fuel-core run --db-type in-memory --debug & sleep 5 && - cargo run --locked --release --bin test -- --locked + CI=true cargo run --locked --release -p e2e_tests -- --locked cargo-run-e2e-test-release: runs-on: ubuntu-latest @@ -364,9 +364,9 @@ jobs: mv fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core - name: Cargo Run E2E Tests (Fuel VM) run: | - fuel-core run --db-type in-memory --debug & + fuel-core run --db-type in-memory --debug & sleep 5 && - cargo run --locked --release --bin test -- --locked --release + CI=true cargo run --locked --release --bin test -- --locked --release cargo-run-e2e-test-evm: runs-on: ubuntu-latest @@ -446,8 +446,8 @@ jobs: run: ./benchmark.sh --prepare-for-commit - uses: EndBug/add-and-commit@v9 with: - cwd: './performance-data' - message: 'Updated benchmark data' + cwd: "./performance-data" + message: "Updated benchmark data" default_author: github_actions forc-unit-tests: @@ -598,10 +598,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: '{workflow} has {status_message}' - message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' - footer: '' - notify_when: 'failure' + notification_title: "{workflow} has {status_message}" + message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" + footer: "" + notify_when: "failure" env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -625,7 +625,7 @@ jobs: ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-pkg/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-client/Cargo.toml - ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-debug/Cargo.toml + ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-debug/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-doc/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-fmt/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-lsp/Cargo.toml @@ -651,10 +651,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: '{workflow} has {status_message}' - message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' - footer: '' - notify_when: 'failure' + notification_title: "{workflow} has {status_message}" + message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" + footer: "" + notify_when: "failure" env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -701,10 +701,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: '{workflow} has {status_message}' - message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' - footer: '' - notify_when: 'failure' + notification_title: "{workflow} has {status_message}" + message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" + footer: "" + notify_when: "failure" env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -778,10 +778,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: '{workflow} has {status_message}' - message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' - footer: '' - notify_when: 'failure' + notification_title: "{workflow} has {status_message}" + message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" + footer: "" + notify_when: "failure" env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -833,10 +833,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: '{workflow} has {status_message}' - message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' - footer: '' - notify_when: 'failure' + notification_title: "{workflow} has {status_message}" + message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" + footer: "" + notify_when: "failure" env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -881,13 +881,13 @@ jobs: - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true - key: '${{ matrix.job.target }}' + key: "${{ matrix.job.target }}" - name: Use Cross uses: baptiste0928/cargo-install@v1 with: crate: cross - cache-key: '${{ matrix.job.target }}' + cache-key: "${{ matrix.job.target }}" - name: Build forc binaries run: | diff --git a/Cargo.lock b/Cargo.lock index efe1394c84e..d5614de1b69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1540,6 +1540,44 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +[[package]] +name = "e2e_tests" +version = "0.0.0" +dependencies = [ + "anyhow", + "bytes", + "clap 4.5.9", + "colored", + "filecheck", + "forc", + "forc-client", + "forc-pkg", + "forc-test", + "forc-tracing 0.62.0", + "fuel-vm", + "futures", + "gag", + "hex", + "insta", + "libtest-mimic", + "miden", + "prettydiff 0.6.4", + "rand", + "regex", + "revm", + "serde_json", + "sway-core", + "sway-error", + "sway-ir", + "sway-types", + "sway-utils", + "textwrap 0.16.1", + "tokio", + "toml 0.7.8", + "tracing", + "vte 0.13.0", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -1743,6 +1781,12 @@ dependencies = [ "str-buf", ] +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "escargot" version = "0.5.12" @@ -4042,6 +4086,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libtest-mimic" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58" +dependencies = [ + "clap 4.5.9", + "escape8259", + "termcolor", + "threadpool", +] + [[package]] name = "libz-sys" version = "1.1.19" @@ -7238,6 +7294,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminal_size" version = "0.3.0" @@ -7261,6 +7326,7 @@ dependencies = [ ] [[package]] +<<<<<<< HEAD name = "test" version = "0.0.0" dependencies = [ @@ -7296,6 +7362,8 @@ dependencies = [ ] [[package]] +======= +>>>>>>> 0db1bd6b9 (snapshot tests and fix for array with numerics) name = "test-macros" version = "0.0.0" dependencies = [ @@ -7359,6 +7427,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tikv-jemalloc-sys" version = "0.5.4+5.3.0-patched" @@ -8051,7 +8128,7 @@ dependencies = [ "itoa", "log", "unicode-width", - "vte", + "vte 0.11.1", ] [[package]] @@ -8065,6 +8142,17 @@ dependencies = [ "vte_generate_state_changes", ] +[[package]] +name = "vte" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eb22ae96f050e0c0d6f7ce43feeae26c348fc4dea56928ca81537cfaa6188b" +dependencies = [ + "arrayvec 0.7.4", + "utf8parse", + "vte_generate_state_changes", +] + [[package]] name = "vte_generate_state_changes" version = "0.1.2" diff --git a/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock b/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock new file mode 100644 index 00000000000..7b517045569 --- /dev/null +++ b/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-79BB3EA8498403DE" + +[[package]] +name = "standalone_contract" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-79BB3EA8498403DE" +dependencies = ["core"] diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index f6304845c35..d54b40e8f74 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -572,15 +572,16 @@ impl TyFunctionSig { } pub fn is_concrete(&self, engines: &Engines) -> bool { - self.return_type.is_concrete(engines, IncludeNumeric::No) + self.return_type + .is_concrete(engines, NumericIsNonConcrete::No) && self .parameters .iter() - .all(|p| p.is_concrete(engines, IncludeNumeric::No)) + .all(|p| p.is_concrete(engines, NumericIsNonConcrete::No)) && self .type_parameters .iter() - .all(|p| p.is_concrete(engines, IncludeNumeric::No)) + .all(|p| p.is_concrete(engines, NumericIsNonConcrete::No)) } /// Returns a String representing the function. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 0df9f72e458..90855dd3a68 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -523,7 +523,7 @@ fn type_check_encode_append( }; // only supported types - if item_type.is_concrete(engines, IncludeNumeric::Yes) { + if item_type.is_concrete(engines, NumericIsNonConcrete::Yes) { match &*engines.te().get(item_type) { TypeInfo::Boolean | TypeInfo::UnsignedInteger(IntegerBits::Eight) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 26c862b21a1..bebff36076a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -38,6 +38,7 @@ use crate::{ use ast_node::declaration::{insert_supertraits_into_namespace, SupertraitOf}; use either::Either; use indexmap::IndexMap; +use itertools::Itertools; use rustc_hash::FxHashSet; use std::collections::{HashMap, VecDeque}; use sway_ast::intrinsics::Intrinsic; @@ -1858,7 +1859,29 @@ impl ty::TyExpression { }) .collect(); - let elem_type = typed_contents[0].return_type; + // choose the best type to be the array elem type + use itertools::FoldWhile::{Continue, Done}; + let elem_type = typed_contents + .iter() + .fold_while(None, |last, current| match last { + None => Continue(Some(current.return_type)), + Some(last) => { + if last.is_concrete(engines, NumericIsNonConcrete::Yes) { + return Done(Some(last)); + } + + let last_info = ctx.engines().te().get(last); + let current_info = ctx.engines().te().get(current.return_type); + match (&*last_info, &*current_info) { + (TypeInfo::Numeric, TypeInfo::UnsignedInteger(_)) => { + Done(Some(current.return_type)) + } + _ => Continue(Some(last)), + } + } + }) + .into_inner(); + let elem_type = elem_type.unwrap_or_else(|| typed_contents[0].return_type); let array_count = typed_contents.len(); Ok(ty::TyExpression { 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 ea83cd08847..5058707838d 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 @@ -60,7 +60,7 @@ pub(crate) fn type_check_method_application( x.return_type .extract_inner_types(engines, IncludeSelf::Yes) .iter() - .any(|x| !x.is_concrete(engines, IncludeNumeric::Yes)) + .any(|x| !x.is_concrete(engines, NumericIsNonConcrete::Yes)) }) .unwrap_or_default(); let needs_second_pass = has_errors || is_not_concrete; diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 9410fd2d6ac..59bceba05a3 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -27,7 +27,7 @@ pub enum IncludeSelf { No, } -pub enum IncludeNumeric { +pub enum NumericIsNonConcrete { Yes, No, } @@ -461,27 +461,33 @@ impl TypeId { })) } - pub(crate) fn is_concrete(&self, engines: &Engines, include_numeric: IncludeNumeric) -> bool { + pub(crate) fn is_concrete( + &self, + engines: &Engines, + numeric_non_concrete: NumericIsNonConcrete, + ) -> bool { let nested_types = (*self).extract_nested_types(engines); - !nested_types.into_iter().any(|x| match include_numeric { - IncludeNumeric::Yes => matches!( - x, - TypeInfo::UnknownGeneric { .. } - | TypeInfo::Custom { .. } - | TypeInfo::Placeholder(..) - | TypeInfo::TraitType { .. } - | TypeInfo::TypeParam(..) - | TypeInfo::Numeric - ), - IncludeNumeric::No => matches!( - x, - TypeInfo::UnknownGeneric { .. } - | TypeInfo::Custom { .. } - | TypeInfo::Placeholder(..) - | TypeInfo::TraitType { .. } - | TypeInfo::TypeParam(..) - ), - }) + !nested_types + .into_iter() + .any(|x| match numeric_non_concrete { + NumericIsNonConcrete::Yes => matches!( + x, + TypeInfo::UnknownGeneric { .. } + | TypeInfo::Custom { .. } + | TypeInfo::Placeholder(..) + | TypeInfo::TraitType { .. } + | TypeInfo::TypeParam(..) + | TypeInfo::Numeric + ), + NumericIsNonConcrete::No => matches!( + x, + TypeInfo::UnknownGeneric { .. } + | TypeInfo::Custom { .. } + | TypeInfo::Placeholder(..) + | TypeInfo::TraitType { .. } + | TypeInfo::TypeParam(..) + ), + }) } /// `check_type_parameter_bounds` does two types of checks. Lets use the example below for demonstrating the two checks: diff --git a/sway-core/src/type_system/priv_prelude.rs b/sway-core/src/type_system/priv_prelude.rs index b28ac22152d..56020d3267a 100644 --- a/sway-core/src/type_system/priv_prelude.rs +++ b/sway-core/src/type_system/priv_prelude.rs @@ -16,6 +16,6 @@ pub use super::{ type_parameter::TypeParameter, }, engine::TypeEngine, - id::{IncludeNumeric, IncludeSelf, TypeId}, + id::{IncludeSelf, NumericIsNonConcrete, TypeId}, info::{AbiEncodeSizeHint, AbiName, TypeInfo, TypeSourceInfo}, }; diff --git a/test/Cargo.toml b/test/Cargo.toml index 92479161ca0..151590aa5a9 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test" +name = "e2e_tests" version = "0.0.0" publish = false authors.workspace = true @@ -32,6 +32,14 @@ sway-ir = { path = "../sway-ir" } sway-types = { path = "../sway-types" } sway-utils = { path = "../sway-utils" } textwrap = "0.16.0" -tokio = "1.12" +tokio = { version = "1.12", features = ["full"] } toml = { version = "0.7", features = ["parse"] } tracing = "0.1" +vte = "0.13.0" +insta = "1.39.0" +libtest-mimic = "0.7.3" + +[[test]] +name = "fuel_snapshot_release" +path = "test/fuel_snapshot_release.rs" +harness = false diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index a77a01acd84..65d25457c47 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -298,7 +298,7 @@ pub(crate) async fn compile_to_bytes(file_name: &str, run_config: &RunConfig) -> match std::panic::catch_unwind(|| forc_pkg::build_with_options(&build_opts)) { Ok(result) => { // Print the result of the compilation (i.e., any errors Forc produces). - if let Err(ref e) = result { + if let Err(ref e) = &result { println!("\n{e}"); } result diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 947ad9b20cc..b439ec28ed7 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -4,7 +4,33 @@ mod harness; mod util; use crate::e2e_vm_tests::harness::run_and_capture_output; -use crate::{FilterConfig, RunConfig}; +use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; + +#[derive(Debug, Clone)] +pub struct FilterConfig { + pub include: Option, + pub exclude: Option, + pub skip_until: Option, + pub abi_only: bool, + pub exclude_core: bool, + pub exclude_std: bool, + pub contract_only: bool, + pub first_only: bool, + pub snapshot_only: bool, +} + +#[derive(Debug, Clone)] +pub struct RunConfig { + pub build_target: BuildTarget, + pub locked: bool, + pub verbose: bool, + pub release: bool, + pub experimental: ExperimentalFlags, + pub update_output_files: bool, + pub print_ir: PrintIr, + pub print_asm: PrintAsm, + pub print_bytecode: bool, +} use anyhow::{anyhow, bail, Result}; use colored::*; @@ -15,10 +41,11 @@ use forc_test::decode_log_data; use fuel_vm::fuel_tx; use fuel_vm::fuel_types::canonical::Input; use fuel_vm::prelude::*; +use insta::Settings; use regex::Regex; use std::collections::{BTreeMap, HashSet}; -use std::io::stdout; use std::io::Write; +use std::io::{stdout, Read}; use std::str::FromStr; use std::time::Instant; use std::{ @@ -26,7 +53,6 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use sway_core::BuildTarget; use tokio::sync::Mutex; use tracing::Instrument; @@ -83,8 +109,8 @@ impl FileCheck { } #[derive(Clone)] -struct TestDescription { - name: String, +pub struct TestDescription { + pub name: String, suffix: Option, category: TestCategory, script_data: Option>, @@ -101,17 +127,18 @@ struct TestDescription { unsupported_profiles: Vec<&'static str>, checker: FileCheck, run_config: RunConfig, + snapshot: Option, } #[derive(PartialEq, Eq, Hash)] -struct DeployedContractKey { +pub struct DeployedContractKey { pub contract_path: String, pub new_encoding: bool, } #[derive(Clone)] -struct TestContext { - deployed_contracts: Arc>>, +pub struct TestContext { + pub deployed_contracts: Arc>>, } fn print_receipts(output: &mut String, receipts: &[Receipt]) { @@ -295,7 +322,13 @@ impl TestContext { }) } - async fn run(&self, test: TestDescription, output: &mut String, verbose: bool) -> Result<()> { + async fn run( + &self, + test: TestDescription, + output: &mut String, + verbose: bool, + allow_snapshot_panic: bool, + ) -> Result<()> { let TestDescription { name, category, @@ -311,6 +344,7 @@ impl TestContext { checker, run_config, expected_decoded_test_logs, + snapshot, .. } = test; @@ -328,7 +362,7 @@ impl TestContext { expected_result }; - match category { + let r = match category { TestCategory::Runs => { let expected_result = expected_result.expect("No expected result found. This is likely because test.toml is missing either an \"expected_result_new_encoding\" or \"expected_result\" entry"); @@ -685,15 +719,124 @@ impl TestContext { category => Err(anyhow::Error::msg(format!( "Unexpected test category: {category:?}", ))), + }; + + if let Some(true) = snapshot { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let folder = format!("{}/src/e2e_vm_tests/test_programs/{}/", manifest_dir, name); + + #[derive(Default)] + struct RawText(String); + + impl vte::Perform for RawText { + fn print(&mut self, c: char) { + self.0.push(c); + } + + fn execute(&mut self, _: u8) {} + + fn hook(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} + + fn put(&mut self, b: u8) { + self.0.push(b as char); + } + + fn unhook(&mut self) {} + + fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {} + + fn csi_dispatch(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} + + fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {} + } + + let mut raw = String::new(); + for line in output.lines() { + let mut performer = RawText::default(); + let mut p = vte::Parser::new(); + for b in line.as_bytes() { + p.advance(&mut performer, *b); + } + raw.push_str(&performer.0); + raw.push_str("\n"); + } + + // Remove absolute paths from snapshot tests + let manifest_dir: PathBuf = PathBuf::try_from(manifest_dir).unwrap(); + let parent = manifest_dir.parent().unwrap(); + let raw = raw.replace(&format!("{}/", parent.display()), ""); + + let run_config_suffix = format!( + "{}-{}-{}", + run_config.build_target.to_string(), + if run_config.experimental.new_encoding { + "encoding-v1" + } else { + "encoding-v0" + }, + if run_config.release { + "release" + } else { + "debug" + }, + ); + + // insta uses the fn name for the file. + fn stdout(folder: &str, raw: &str, run_config_suffix: String) { + let mut insta = Settings::new(); + insta.set_snapshot_path(folder); + insta.set_prepend_module_to_snapshot(false); + insta.set_omit_expression(true); + insta.set_snapshot_suffix(run_config_suffix); + let scope = insta.bind_to_scope(); + insta::assert_snapshot!(raw); + drop(scope); + } + + if allow_snapshot_panic { + stdout(&folder, &raw, run_config_suffix); + } else { + let r = std::panic::catch_unwind(|| { + let buf_stdout = gag::BufferRedirect::stdout(); + let buf_stderr = gag::BufferRedirect::stderr(); + + stdout(&folder, &raw, run_config_suffix); + + let mut output = String::new(); + if let Ok(mut buf_stdout) = buf_stdout { + buf_stdout.read_to_string(&mut output).unwrap(); + drop(buf_stdout); + } + if let Ok(mut buf_stderr) = buf_stderr { + buf_stderr.read_to_string(&mut output).unwrap(); + drop(buf_stderr); + } + + output + }); + + if r.is_err() { + let message = format!("Snapshot differ"); + return Err(anyhow::Error::msg(message)); + } + } } + + r } } -pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result<()> { - // Discover tests - let mut tests = discover_test_configs(run_config)?; - let total_number_of_tests = tests.len(); +pub struct FilterTestResult { + skipped_tests: Vec, + disabled_tests: Vec, + included_tests: Vec, + excluded_tests: Vec, +} +pub fn filter_tests( + filter_config: &FilterConfig, + tests: &mut Vec, +) -> FilterTestResult { // Filter tests let skipped_tests = filter_config .skip_until @@ -736,72 +879,132 @@ pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result tests.retain(|t| t.category == TestCategory::RunsWithContract); } if filter_config.first_only && !tests.is_empty() { - tests = vec![tests.remove(0)]; + *tests = vec![tests.remove(0)]; + } + if filter_config.snapshot_only { + tests.retain(|t| matches!(t.snapshot, Some(true))); } - // Run tests - let context = TestContext { - deployed_contracts: Default::default(), - }; - let mut number_of_tests_executed = 0; - let mut number_of_tests_failed = 0; - let mut failed_tests = vec![]; + FilterTestResult { + skipped_tests, + disabled_tests, + included_tests, + excluded_tests, + } +} - let start_time = Instant::now(); - for (i, test) in tests.into_iter().enumerate() { - let cur_profile = if run_config.release { - BuildProfile::RELEASE - } else { - BuildProfile::DEBUG - }; +pub async fn run_test( + context: &TestContext, + run_config: &RunConfig, + filter_config: &FilterConfig, + i: usize, + test: TestDescription, + failed_tests: &mut Vec, + number_of_tests_failed: &mut usize, + allow_snapshot_panic: bool, +) -> usize { + let name = test.name.clone(); + + let cur_profile = if run_config.release { + BuildProfile::RELEASE + } else { + BuildProfile::DEBUG + }; - if test.unsupported_profiles.contains(&cur_profile) { - continue; - } + if test.unsupported_profiles.contains(&cur_profile) { + return 0; + } - let name = if let Some(suffix) = test.suffix.as_ref() { - format!("{} ({})", test.name, suffix) - } else { - test.name.clone() - }; + let name = if let Some(suffix) = test.suffix.as_ref() { + format!("{} ({})", test.name, suffix) + } else { + test.name.clone() + }; + if !allow_snapshot_panic { print!("Testing {} ...", name.clone().bold()); stdout().flush().unwrap(); + } - let mut output = String::new(); + let mut output = String::new(); - // Skip the test if its not compatible with the current build target. - if !test.supported_targets.contains(&run_config.build_target) { - continue; - } + // Skip the test if its not compatible with the current build target. + if !test.supported_targets.contains(&run_config.build_target) { + return 0; + } - use std::fmt::Write; - let _ = writeln!(output, " {}", "Verbose Output".green().bold()); - let result = if !filter_config.first_only { - context - .run(test, &mut output, run_config.verbose) - .instrument(tracing::trace_span!("E2E", i)) - .await - } else { - context.run(test, &mut output, run_config.verbose).await - }; + let test_snapshot = test.snapshot.clone(); - if let Err(err) = result { + use std::fmt::Write; + let _ = writeln!(output, " {}", "Verbose Output".green().bold()); + let result = if !filter_config.first_only { + context + .run(test, &mut output, run_config.verbose, allow_snapshot_panic) + .instrument(tracing::trace_span!("E2E", i)) + .await + } else { + context + .run(test, &mut output, run_config.verbose, allow_snapshot_panic) + .await + }; + + if let Err(err) = result { + if matches!(test_snapshot, Some(true)) { + println!(" {} (snapshot mismatch)", "failed".red().bold()); + } else { println!(" {}", "failed".red().bold()); println!("{}", textwrap::indent(err.to_string().as_str(), " ")); println!("{}", textwrap::indent(&output, " ")); - number_of_tests_failed += 1; - failed_tests.push(name); - } else { - println!(" {}", "ok".green().bold()); + } - // If verbosity is requested then print it out. - if run_config.verbose && !output.is_empty() { - println!("{}", textwrap::indent(&output, " ")); - } + *number_of_tests_failed += 1; + failed_tests.push(name); + } else { + println!(" {}", "ok".green().bold()); + + // If verbosity is requested then print it out. + if run_config.verbose && !output.is_empty() { + println!("{}", textwrap::indent(&output, " ")); } + } + + return 1; +} + +pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result<()> { + // Discover tests + let mut tests = discover_test_configs(run_config)?; + let total_number_of_tests = tests.len(); + + let FilterTestResult { + skipped_tests, + disabled_tests, + included_tests, + excluded_tests, + } = filter_tests(filter_config, &mut tests); - number_of_tests_executed += 1; + // Run tests + let context = TestContext { + deployed_contracts: Default::default(), + }; + let mut number_of_tests_executed = 0; + let mut number_of_tests_failed = 0; + let mut failed_tests = vec![]; + + let start_time = Instant::now(); + for (i, test) in tests.into_iter().enumerate() { + let qty = run_test( + &context, + run_config, + filter_config, + i, + test, + &mut failed_tests, + &mut number_of_tests_failed, + false, + ) + .await; + number_of_tests_executed += qty; } let duration = Instant::now().duration_since(start_time); @@ -887,7 +1090,7 @@ fn exclude_tests_dependency(t: &TestDescription, dep: &str) -> bool { } } -fn discover_test_configs(run_config: &RunConfig) -> Result> { +pub fn discover_test_configs(run_config: &RunConfig) -> Result> { fn recursive_search( path: &Path, run_config: &RunConfig, @@ -985,8 +1188,13 @@ fn parse_test_toml(path: &Path, run_config: &RunConfig) -> Result Result test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:24:18 + | +22 | +23 | let a = [8, 256u16, 8u8]; +24 | let b: u32 = a[2]; + | ^^^^ Mismatched types. +expected: u32 +found: u16. +help: Variable declaration's type annotation does not match up with the assigned expression's type. +25 | } + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:5:14 + | +3 | +4 | // u8 +5 | let _a = 0x100; + | ^^^^^ Literal would overflow because its value does not fit into "u8" +6 | Vec::::new().push(_a); + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:9:14 + | + 7 | + 8 | // u16 + 9 | let _a = 0x10000; + | ^^^^^^^ Literal would overflow because its value does not fit into "u16" +10 | Vec::::new().push(_a); + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:13:14 + | +11 | +12 | // u32 +13 | let _a = 0x100000000; + | ^^^^^^^^^^^ Literal would overflow because its value does not fit into "u32" +14 | Vec::::new().push(_a); + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:17:20 + | +15 | +16 | // Array +17 | let a = [1, 2, "hello"]; + | ^^^^^^^ Mismatched types. +expected: numeric +found: str. + +18 | +19 | // Array - different numerics + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:20:22 + | +18 | +19 | // Array - different numerics +20 | let a = [1, 2u8, 3u16, 4u32, 5u64]; + | ^^^^ Mismatched types. +expected: u8 +found: u16. + +21 | +22 | // Wrong cast + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:20:28 + | +18 | +19 | // Array - different numerics +20 | let a = [1, 2u8, 3u16, 4u32, 5u64]; + | ^^^^ Mismatched types. +expected: u8 +found: u32. + +21 | +22 | // Wrong cast + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:20:34 + | +18 | +19 | // Array - different numerics +20 | let a = [1, 2u8, 3u16, 4u32, 5u64]; + | ^^^^ Mismatched types. +expected: u8 +found: u64. + +21 | +22 | // Wrong cast + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:23:25 + | +21 | +22 | // Wrong cast +23 | let a = [8, 256u16, 8u8]; + | ^^^ Mismatched types. +expected: u16 +found: u8. + +24 | let b: u32 = a[2]; +25 | } + | +____ + + Aborting due to 9 errors. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml index 65ba3c1072b..58d39acb0d9 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml @@ -1,15 +1,2 @@ category = "fail" - -# check: $()let _a = 0x100; -# nextln: $()Literal would overflow because its value does not fit into "u8" - -# check: $()let _a = 0x10000; -# nextln: $()Literal would overflow because its value does not fit into "u16" - -# check: $()let _a = 0x100000000; -# nextln: $()Literal would overflow because its value does not fit into "u32" - -# check: $()let a = [1, 2, "hello"]; -# nextln: $()Mismatched types -# nextln: $()expected: numeric -# nextln: $()found: str +snapshot = true diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index 0c6107aaccf..9de1ab9f2e1 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -31,7 +31,7 @@ impl Checker { /// of the file. /// Example: /// - /// ``` + /// ```sway /// // ::check-ir:: /// // ::check-ir-optimized:: /// // ::check-ir-asm:: @@ -42,7 +42,7 @@ impl Checker { /// Optimized IR checker can be configured with `pass: `. When /// `o1` is chosen, all the configured passes are chosen automatically. /// - /// ``` + /// ```sway /// // ::check-ir-optimized:: /// // pass: o1 /// ``` diff --git a/test/src/lib.rs b/test/src/lib.rs new file mode 100644 index 00000000000..8a5abbafd6e --- /dev/null +++ b/test/src/lib.rs @@ -0,0 +1,163 @@ +pub mod e2e_vm_tests; +mod ir_generation; +mod reduced_std_libs; +pub mod test_consistency; + +use anyhow::Result; +use clap::Parser; +use e2e_vm_tests::{FilterConfig, RunConfig}; +use forc::cli::shared::{PrintAsmCliOpt, PrintIrCliOpt}; +use forc_tracing::init_tracing_subscriber; +use std::str::FromStr; +use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; +use tracing::Instrument; + +#[derive(Parser)] +pub struct Cli { + /// Only run tests matching this regex + #[arg(value_name = "REGEX")] + pub include: Option, + + /// Exclude tests matching this regex + #[arg(long, short, value_name = "REGEX")] + pub exclude: Option, + + /// Skip all tests until a test matches this regex + #[arg(long, short, value_name = "REGEX")] + pub skip_until: Option, + + /// Only run tests with ABI JSON output validation + #[arg(long, visible_alias = "abi")] + pub abi_only: bool, + + /// Only run tests with no core dependencies + #[arg(long, visible_alias = "exclude_core")] + pub exclude_core: bool, + + /// Only run tests with no std dependencies + #[arg(long, visible_alias = "exclude_std")] + pub exclude_std: bool, + + /// Only run tests that deploy contracts + #[arg(long, visible_alias = "contract")] + pub contract_only: bool, + + /// Only run the first test + #[arg(long, visible_alias = "first")] + pub first_only: bool, + + /// Only run snapshot test + #[arg(long, visible_alias = "snapshot")] + pub snapshot_only: bool, + + /// Print out warnings, errors, and output of print options + #[arg(long, env = "SWAY_TEST_VERBOSE")] + pub verbose: bool, + + /// Compile sway code in release mode + #[arg(long)] + pub release: bool, + + /// Intended for use in `CI` to ensure test lock files are up to date + #[arg(long)] + pub locked: bool, + + /// Build target + #[arg(long, visible_alias = "target")] + pub build_target: Option, + + /// Disable the "new encoding" feature + #[arg(long)] + pub no_encoding_v1: bool, + + /// Update all output files + #[arg(long)] + pub update_output_files: bool, + + /// Print out the specified IR (separate options with comma), if the verbose option is on + #[arg(long, num_args(1..=18), value_parser = clap::builder::PossibleValuesParser::new(PrintIrCliOpt::cli_options()))] + pub print_ir: Option>, + + /// Print out the specified ASM (separate options with comma), if the verbose option is on + #[arg(long, num_args(1..=5), value_parser = clap::builder::PossibleValuesParser::new(&PrintAsmCliOpt::CLI_OPTIONS))] + pub print_asm: Option>, + + /// Print out the final bytecode, if the verbose option is on + #[arg(long)] + pub print_bytecode: bool, +} + +pub fn configs_from_cli(cli: &Cli) -> (FilterConfig, RunConfig) { + let filter_config = FilterConfig { + include: cli.include.clone(), + exclude: cli.exclude.clone(), + skip_until: cli.skip_until.clone(), + abi_only: cli.abi_only, + exclude_core: cli.exclude_core, + exclude_std: cli.exclude_std, + contract_only: cli.contract_only, + first_only: cli.first_only, + snapshot_only: cli.snapshot_only, + }; + let build_target = match &cli.build_target { + Some(target) => match BuildTarget::from_str(target.as_str()) { + Ok(target) => target, + _ => panic!("unexpected build target"), + }, + None => BuildTarget::default(), + }; + let run_config = RunConfig { + locked: cli.locked, + verbose: cli.verbose, + release: cli.release, + build_target, + experimental: sway_core::ExperimentalFlags { + new_encoding: !cli.no_encoding_v1, + }, + update_output_files: cli.update_output_files, + print_ir: cli + .print_ir + .as_ref() + .map_or(PrintIr::default(), |opts| PrintIrCliOpt::from(opts).0), + print_asm: cli + .print_asm + .as_ref() + .map_or(PrintAsm::default(), |opts| PrintAsmCliOpt::from(opts).0), + print_bytecode: cli.print_bytecode, + }; + + (filter_config, run_config) +} + +pub async fn run(cli: Cli) -> Result<()> { + init_tracing_subscriber(Default::default()); + + let (filter_config, run_config) = configs_from_cli(&cli); + + // Check that the tests are consistent + test_consistency::check()?; + + // Create reduced versions of the `std` library. + reduced_std_libs::create()?; + + // Run E2E tests + e2e_vm_tests::run(&filter_config, &run_config) + .instrument(tracing::trace_span!("E2E")) + .await?; + + // Run IR tests + if !filter_config.first_only && !filter_config.snapshot_only { + println!("\n"); + ir_generation::run( + filter_config.include.as_ref(), + cli.verbose, + sway_ir::ExperimentalFlags { + new_encoding: run_config.experimental.new_encoding, + }, + ) + .instrument(tracing::trace_span!("IR")) + .await?; + } + + Ok(()) +} diff --git a/test/src/main.rs b/test/src/main.rs index 8dff1419ee7..3d8f0ccbaf0 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -5,175 +5,11 @@ mod test_consistency; use anyhow::Result; use clap::Parser; -use forc::cli::shared::{PrintAsmCliOpt, PrintIrCliOpt}; -use forc_tracing::init_tracing_subscriber; -use std::str::FromStr; -use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; -use tracing::Instrument; - -#[derive(Parser)] -struct Cli { - /// Only run tests matching this regex - #[arg(value_name = "REGEX")] - include: Option, - - /// Exclude tests matching this regex - #[arg(long, short, value_name = "REGEX")] - exclude: Option, - - /// Skip all tests until a test matches this regex - #[arg(long, short, value_name = "REGEX")] - skip_until: Option, - - /// Only run tests with ABI JSON output validation - #[arg(long, visible_alias = "abi")] - abi_only: bool, - - /// Only run tests with no core dependencies - #[arg(long, visible_alias = "exclude_core")] - exclude_core: bool, - - /// Only run tests with no std dependencies - #[arg(long, visible_alias = "exclude_std")] - exclude_std: bool, - - /// Only run tests that deploy contracts - #[arg(long, visible_alias = "contract")] - contract_only: bool, - - /// Only run the first test - #[arg(long, visible_alias = "first")] - first_only: bool, - - /// Print out warnings, errors, and output of print options - #[arg(long, env = "SWAY_TEST_VERBOSE")] - verbose: bool, - - /// Compile sway code in release mode - #[arg(long)] - release: bool, - - /// Intended for use in `CI` to ensure test lock files are up to date - #[arg(long)] - locked: bool, - - /// Build target - #[arg(long, visible_alias = "target")] - build_target: Option, - - /// Disable the "new encoding" feature - #[arg(long)] - no_encoding_v1: bool, - - /// Update all output files - #[arg(long)] - update_output_files: bool, - - /// Print out the specified IR (separate options with comma), if the verbose option is on - #[arg(long, num_args(1..=18), value_parser = clap::builder::PossibleValuesParser::new(PrintIrCliOpt::cli_options()))] - print_ir: Option>, - - /// Print out the specified ASM (separate options with comma), if the verbose option is on - #[arg(long, num_args(1..=5), value_parser = clap::builder::PossibleValuesParser::new(&PrintAsmCliOpt::CLI_OPTIONS))] - print_asm: Option>, - - /// Print out the final bytecode, if the verbose option is on - #[arg(long)] - print_bytecode: bool, -} - -#[derive(Debug, Clone)] -pub struct FilterConfig { - pub include: Option, - pub exclude: Option, - pub skip_until: Option, - pub abi_only: bool, - pub exclude_core: bool, - pub exclude_std: bool, - pub contract_only: bool, - pub first_only: bool, -} - -#[derive(Debug, Clone)] -pub struct RunConfig { - pub build_target: BuildTarget, - pub locked: bool, - pub verbose: bool, - pub release: bool, - pub experimental: ExperimentalFlags, - pub update_output_files: bool, - pub print_ir: PrintIr, - pub print_asm: PrintAsm, - pub print_bytecode: bool, -} +use e2e_tests::*; #[tokio::main] async fn main() -> Result<()> { - init_tracing_subscriber(Default::default()); - - // Parse args let cli = Cli::parse(); - let filter_config = FilterConfig { - include: cli.include, - exclude: cli.exclude, - skip_until: cli.skip_until, - abi_only: cli.abi_only, - exclude_core: cli.exclude_core, - exclude_std: cli.exclude_std, - contract_only: cli.contract_only, - first_only: cli.first_only, - }; - let build_target = match cli.build_target { - Some(target) => match BuildTarget::from_str(target.as_str()) { - Ok(target) => target, - _ => panic!("unexpected build target"), - }, - None => BuildTarget::default(), - }; - let run_config = RunConfig { - locked: cli.locked, - verbose: cli.verbose, - release: cli.release, - build_target, - experimental: sway_core::ExperimentalFlags { - new_encoding: !cli.no_encoding_v1, - }, - update_output_files: cli.update_output_files, - print_ir: cli - .print_ir - .as_ref() - .map_or(PrintIr::default(), |opts| PrintIrCliOpt::from(opts).0), - print_asm: cli - .print_asm - .as_ref() - .map_or(PrintAsm::default(), |opts| PrintAsmCliOpt::from(opts).0), - print_bytecode: cli.print_bytecode, - }; - - // Check that the tests are consistent - test_consistency::check()?; - - // Create reduced versions of the `std` library. - reduced_std_libs::create()?; - - // Run E2E tests - e2e_vm_tests::run(&filter_config, &run_config) - .instrument(tracing::trace_span!("E2E")) - .await?; - - // Run IR tests - if !filter_config.first_only { - println!("\n"); - ir_generation::run( - filter_config.include.as_ref(), - cli.verbose, - sway_ir::ExperimentalFlags { - new_encoding: run_config.experimental.new_encoding, - }, - ) - .instrument(tracing::trace_span!("IR")) - .await?; - } - + let _ = run(cli).await; Ok(()) } diff --git a/test/src/test_consistency.rs b/test/src/test_consistency.rs index 379ba1b7381..477c5d4a92d 100644 --- a/test/src/test_consistency.rs +++ b/test/src/test_consistency.rs @@ -6,7 +6,7 @@ use toml::{Table, Value}; use crate::reduced_std_libs::REDUCED_STD_LIBS_DIR_NAME; -pub(crate) fn check() -> Result<()> { +pub fn check() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let all_tests_dir = format!("{manifest_dir}/src"); diff --git a/test/test/fuel_snapshot_release.rs b/test/test/fuel_snapshot_release.rs new file mode 100644 index 00000000000..300104a4e16 --- /dev/null +++ b/test/test/fuel_snapshot_release.rs @@ -0,0 +1,71 @@ +use std::sync::{Arc, Mutex}; + +use e2e_tests::*; +use e2e_vm_tests::{filter_tests, TestContext}; +use forc_tracing::init_tracing_subscriber; +use libtest_mimic::{Arguments, Trial}; + +pub fn main() { + init_tracing_subscriber(Default::default()); + + let mut args = Arguments::from_args(); + args.nocapture = true; + args.test_threads = Some(1); + + e2e_tests::test_consistency::check().unwrap(); + + let (filter_config, run_config) = e2e_tests::configs_from_cli(&Cli { + include: None, + exclude: None, + skip_until: None, + abi_only: false, + exclude_core: false, + exclude_std: false, + contract_only: false, + first_only: false, + verbose: false, + release: false, + locked: false, + build_target: Some("fuel".into()), + no_encoding_v1: false, + update_output_files: false, + print_ir: None, + print_asm: None, + print_bytecode: false, + snapshot_only: true, + }); + + let mut tests = e2e_tests::e2e_vm_tests::discover_test_configs(&run_config).unwrap(); + filter_tests(&filter_config, &mut tests); + let tests = tests + .into_iter() + .map(|test| { + let run_config = run_config.clone(); + let filter_config = filter_config.clone(); + Trial::test(test.name.clone(), move || { + let rt = tokio::runtime::Runtime::new()?; + rt.block_on(async { + let context = TestContext { + deployed_contracts: Default::default(), + }; + let mut failed_tests = vec![]; + let mut qty_failed = 0; + e2e_vm_tests::run_test( + &context, + &run_config, + &filter_config, + 0, + test, + &mut failed_tests, + &mut qty_failed, + true, + ) + .await; + }); + Ok(()) + }) + }) + .collect(); + + libtest_mimic::run(&args, tests).exit(); +} From 83f21a8e907cf7a79d0b12c76ea14554d7b4738c Mon Sep 17 00:00:00 2001 From: xunilrj Date: Thu, 15 Aug 2024 16:58:18 +0100 Subject: [PATCH 11/20] fix warnings --- test/Cargo.toml | 2 +- test/src/e2e_vm_tests/mod.rs | 16 +++++++--------- test/src/lib.rs | 4 ++-- test/src/main.rs | 5 ----- test/src/reduced_std_libs.rs | 2 +- test/src/test_consistency.rs | 1 + .../{fuel_snapshot_release.rs => e2e_tests.rs} | 3 +-- 7 files changed, 13 insertions(+), 20 deletions(-) rename test/test/{fuel_snapshot_release.rs => e2e_tests.rs} (97%) diff --git a/test/Cargo.toml b/test/Cargo.toml index 151590aa5a9..7510bc611b5 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -41,5 +41,5 @@ libtest-mimic = "0.7.3" [[test]] name = "fuel_snapshot_release" -path = "test/fuel_snapshot_release.rs" +path = "test/e2e_tests.rs" harness = false diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index b439ec28ed7..168d77fcb62 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -758,17 +758,17 @@ impl TestContext { p.advance(&mut performer, *b); } raw.push_str(&performer.0); - raw.push_str("\n"); + raw.push('\n'); } // Remove absolute paths from snapshot tests - let manifest_dir: PathBuf = PathBuf::try_from(manifest_dir).unwrap(); + let manifest_dir: PathBuf = PathBuf::from(manifest_dir); let parent = manifest_dir.parent().unwrap(); let raw = raw.replace(&format!("{}/", parent.display()), ""); let run_config_suffix = format!( "{}-{}-{}", - run_config.build_target.to_string(), + run_config.build_target, if run_config.experimental.new_encoding { "encoding-v1" } else { @@ -816,8 +816,7 @@ impl TestContext { }); if r.is_err() { - let message = format!("Snapshot differ"); - return Err(anyhow::Error::msg(message)); + return Err(anyhow::Error::msg("Snapshot differ")); } } } @@ -893,6 +892,7 @@ pub fn filter_tests( } } +#[allow(clippy::too_many_arguments)] pub async fn run_test( context: &TestContext, run_config: &RunConfig, @@ -903,8 +903,6 @@ pub async fn run_test( number_of_tests_failed: &mut usize, allow_snapshot_panic: bool, ) -> usize { - let name = test.name.clone(); - let cur_profile = if run_config.release { BuildProfile::RELEASE } else { @@ -933,7 +931,7 @@ pub async fn run_test( return 0; } - let test_snapshot = test.snapshot.clone(); + let test_snapshot = test.snapshot; use std::fmt::Write; let _ = writeln!(output, " {}", "Verbose Output".green().bold()); @@ -968,7 +966,7 @@ pub async fn run_test( } } - return 1; + 1 } pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result<()> { diff --git a/test/src/lib.rs b/test/src/lib.rs index 8a5abbafd6e..9cc47dec202 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -1,6 +1,6 @@ pub mod e2e_vm_tests; mod ir_generation; -mod reduced_std_libs; +pub mod reduced_std_libs; pub mod test_consistency; use anyhow::Result; @@ -9,7 +9,7 @@ use e2e_vm_tests::{FilterConfig, RunConfig}; use forc::cli::shared::{PrintAsmCliOpt, PrintIrCliOpt}; use forc_tracing::init_tracing_subscriber; use std::str::FromStr; -use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; +use sway_core::{BuildTarget, PrintAsm, PrintIr}; use tracing::Instrument; #[derive(Parser)] diff --git a/test/src/main.rs b/test/src/main.rs index 3d8f0ccbaf0..12125e3d2ac 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -1,8 +1,3 @@ -mod e2e_vm_tests; -mod ir_generation; -mod reduced_std_libs; -mod test_consistency; - use anyhow::Result; use clap::Parser; use e2e_tests::*; diff --git a/test/src/reduced_std_libs.rs b/test/src/reduced_std_libs.rs index b5a5cfb6e1a..f6edafae1ce 100644 --- a/test/src/reduced_std_libs.rs +++ b/test/src/reduced_std_libs.rs @@ -13,7 +13,7 @@ const REDUCED_LIB_CONFIG_FILE_NAME: &str = "reduced_lib.config"; /// Creates the reduced versions of `std` libraries based on the list of /// modules defined in [REDUCED_LIB_CONFIG_FILE_NAME] file for each reduced library /// available in the [REDUCED_STD_LIBS_DIR_NAME]. -pub(crate) fn create() -> Result<()> { +pub fn create() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let reduced_libs_dir = format!("{manifest_dir}/src/e2e_vm_tests/{REDUCED_STD_LIBS_DIR_NAME}"); let std_lib_src_dir = format!("{manifest_dir}/../sway-lib-std/src"); diff --git a/test/src/test_consistency.rs b/test/src/test_consistency.rs index 477c5d4a92d..21c76d52c74 100644 --- a/test/src/test_consistency.rs +++ b/test/src/test_consistency.rs @@ -6,6 +6,7 @@ use toml::{Table, Value}; use crate::reduced_std_libs::REDUCED_STD_LIBS_DIR_NAME; +#[allow(dead_code)] pub fn check() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let all_tests_dir = format!("{manifest_dir}/src"); diff --git a/test/test/fuel_snapshot_release.rs b/test/test/e2e_tests.rs similarity index 97% rename from test/test/fuel_snapshot_release.rs rename to test/test/e2e_tests.rs index 300104a4e16..dccd268ec58 100644 --- a/test/test/fuel_snapshot_release.rs +++ b/test/test/e2e_tests.rs @@ -1,5 +1,3 @@ -use std::sync::{Arc, Mutex}; - use e2e_tests::*; use e2e_vm_tests::{filter_tests, TestContext}; use forc_tracing::init_tracing_subscriber; @@ -13,6 +11,7 @@ pub fn main() { args.test_threads = Some(1); e2e_tests::test_consistency::check().unwrap(); + e2e_tests::reduced_std_libs::create().unwrap(); let (filter_config, run_config) = e2e_tests::configs_from_cli(&Cli { include: None, From 3b9fad78263cd1c3ebf12fa101c406b396fe6bd1 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Fri, 16 Aug 2024 14:09:40 +0100 Subject: [PATCH 12/20] simpler insta setup --- .github/workflows/ci.yml | 2 +- .gitignore | 3 + Cargo.lock | 63 ++-- test/Cargo.toml | 11 +- test/src/e2e_vm_tests/harness.rs | 2 +- test/src/e2e_vm_tests/mod.rs | 337 ++++-------------- .../type_check_analyze_errors/snapshot.toml | 1 + .../type_check_analyze_errors/src/main.sw | 1 - ...uel-encoding-v1-debug.snap => stdout.snap} | 116 +++--- .../type_check_analyze_errors/test.toml | 2 - test/src/lib.rs | 163 --------- test/src/main.rs | 181 +++++++++- test/src/reduced_std_libs.rs | 2 +- test/src/test_consistency.rs | 3 +- test/test/e2e_tests.rs | 70 ---- test/tests/tests.rs | 124 +++++++ 16 files changed, 466 insertions(+), 615 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/snapshot.toml rename test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/{stdout@fuel-encoding-v1-debug.snap => stdout.snap} (52%) delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml delete mode 100644 test/src/lib.rs delete mode 100644 test/test/e2e_tests.rs create mode 100644 test/tests/tests.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63d071eb336..0534e0e1ce8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -366,7 +366,7 @@ jobs: run: | fuel-core run --db-type in-memory --debug & sleep 5 && - CI=true cargo run --locked --release --bin test -- --locked --release + CI=true cargo run --locked --release -p e2e_tests -- --locked --release cargo-run-e2e-test-evm: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 3de2d377b4e..3b09835e4ac 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ forc-plugins/forc-debug/tests/**/Forc.lock # Generated files in example directories examples/**/*/Forc.lock docs/reference/src/code/examples/**/*/Forc.lock + +# Insta files +*.snap.new diff --git a/Cargo.lock b/Cargo.lock index d5614de1b69..c9b2b183b8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1540,44 +1540,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" -[[package]] -name = "e2e_tests" -version = "0.0.0" -dependencies = [ - "anyhow", - "bytes", - "clap 4.5.9", - "colored", - "filecheck", - "forc", - "forc-client", - "forc-pkg", - "forc-test", - "forc-tracing 0.62.0", - "fuel-vm", - "futures", - "gag", - "hex", - "insta", - "libtest-mimic", - "miden", - "prettydiff 0.6.4", - "rand", - "regex", - "revm", - "serde_json", - "sway-core", - "sway-error", - "sway-ir", - "sway-types", - "sway-utils", - "textwrap 0.16.1", - "tokio", - "toml 0.7.8", - "tracing", - "vte 0.13.0", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -7327,23 +7289,41 @@ dependencies = [ [[package]] <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 7332e18cb (simpler insta setup) name = "test" version = "0.0.0" dependencies = [ "anyhow", "bytes", +<<<<<<< HEAD "clap 4.5.16", +======= + "clap 4.5.9", +>>>>>>> 7332e18cb (simpler insta setup) "colored", "filecheck", "forc", "forc-client", "forc-pkg", "forc-test", +<<<<<<< HEAD "forc-tracing 0.63.1", "fuel-vm", "futures", "gag", "hex", +======= + "forc-tracing 0.62.0", + "fuel-vm", + "futures", + "gag", + "glob", + "hex", + "insta", + "libtest-mimic", +>>>>>>> 7332e18cb (simpler insta setup) "miden", "prettydiff 0.6.4", "rand", @@ -7359,11 +7339,18 @@ dependencies = [ "tokio", "toml 0.7.8", "tracing", +<<<<<<< HEAD ] [[package]] ======= >>>>>>> 0db1bd6b9 (snapshot tests and fix for array with numerics) +======= + "vte 0.13.0", +] + +[[package]] +>>>>>>> 7332e18cb (simpler insta setup) name = "test-macros" version = "0.0.0" dependencies = [ diff --git a/test/Cargo.toml b/test/Cargo.toml index 7510bc611b5..7da6f0033e3 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "e2e_tests" +name = "test" version = "0.0.0" publish = false authors.workspace = true @@ -19,7 +19,10 @@ forc-tracing = { path = "../forc-tracing" } fuel-vm = { workspace = true, features = ["random"] } futures = "0.3.24" gag = "1.0" +glob = "0.3.1" hex = "0.4.3" +insta = "1.39.0" +libtest-mimic = "0.7.3" miden = "0.3.0" prettydiff = "0.6" rand = "0.8" @@ -36,10 +39,8 @@ tokio = { version = "1.12", features = ["full"] } toml = { version = "0.7", features = ["parse"] } tracing = "0.1" vte = "0.13.0" -insta = "1.39.0" -libtest-mimic = "0.7.3" [[test]] -name = "fuel_snapshot_release" -path = "test/e2e_tests.rs" +name = "tests" +path = "tests/tests.rs" harness = false diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 65d25457c47..a77a01acd84 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -298,7 +298,7 @@ pub(crate) async fn compile_to_bytes(file_name: &str, run_config: &RunConfig) -> match std::panic::catch_unwind(|| forc_pkg::build_with_options(&build_opts)) { Ok(result) => { // Print the result of the compilation (i.e., any errors Forc produces). - if let Err(ref e) = &result { + if let Err(ref e) = result { println!("\n{e}"); } result diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 168d77fcb62..f52bba955c6 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -4,33 +4,7 @@ mod harness; mod util; use crate::e2e_vm_tests::harness::run_and_capture_output; -use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; - -#[derive(Debug, Clone)] -pub struct FilterConfig { - pub include: Option, - pub exclude: Option, - pub skip_until: Option, - pub abi_only: bool, - pub exclude_core: bool, - pub exclude_std: bool, - pub contract_only: bool, - pub first_only: bool, - pub snapshot_only: bool, -} - -#[derive(Debug, Clone)] -pub struct RunConfig { - pub build_target: BuildTarget, - pub locked: bool, - pub verbose: bool, - pub release: bool, - pub experimental: ExperimentalFlags, - pub update_output_files: bool, - pub print_ir: PrintIr, - pub print_asm: PrintAsm, - pub print_bytecode: bool, -} +use crate::{FilterConfig, RunConfig}; use anyhow::{anyhow, bail, Result}; use colored::*; @@ -41,11 +15,10 @@ use forc_test::decode_log_data; use fuel_vm::fuel_tx; use fuel_vm::fuel_types::canonical::Input; use fuel_vm::prelude::*; -use insta::Settings; use regex::Regex; use std::collections::{BTreeMap, HashSet}; +use std::io::stdout; use std::io::Write; -use std::io::{stdout, Read}; use std::str::FromStr; use std::time::Instant; use std::{ @@ -53,6 +26,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; +use sway_core::BuildTarget; use tokio::sync::Mutex; use tracing::Instrument; @@ -109,8 +83,8 @@ impl FileCheck { } #[derive(Clone)] -pub struct TestDescription { - pub name: String, +struct TestDescription { + name: String, suffix: Option, category: TestCategory, script_data: Option>, @@ -127,18 +101,17 @@ pub struct TestDescription { unsupported_profiles: Vec<&'static str>, checker: FileCheck, run_config: RunConfig, - snapshot: Option, } #[derive(PartialEq, Eq, Hash)] -pub struct DeployedContractKey { +struct DeployedContractKey { pub contract_path: String, pub new_encoding: bool, } #[derive(Clone)] -pub struct TestContext { - pub deployed_contracts: Arc>>, +struct TestContext { + deployed_contracts: Arc>>, } fn print_receipts(output: &mut String, receipts: &[Receipt]) { @@ -322,13 +295,7 @@ impl TestContext { }) } - async fn run( - &self, - test: TestDescription, - output: &mut String, - verbose: bool, - allow_snapshot_panic: bool, - ) -> Result<()> { + async fn run(&self, test: TestDescription, output: &mut String, verbose: bool) -> Result<()> { let TestDescription { name, category, @@ -344,7 +311,6 @@ impl TestContext { checker, run_config, expected_decoded_test_logs, - snapshot, .. } = test; @@ -362,7 +328,7 @@ impl TestContext { expected_result }; - let r = match category { + match category { TestCategory::Runs => { let expected_result = expected_result.expect("No expected result found. This is likely because test.toml is missing either an \"expected_result_new_encoding\" or \"expected_result\" entry"); @@ -528,8 +494,8 @@ impl TestContext { ) }) .await; - output.push_str(&out); result?; + output.push_str(&out); } } @@ -719,123 +685,15 @@ impl TestContext { category => Err(anyhow::Error::msg(format!( "Unexpected test category: {category:?}", ))), - }; - - if let Some(true) = snapshot { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let folder = format!("{}/src/e2e_vm_tests/test_programs/{}/", manifest_dir, name); - - #[derive(Default)] - struct RawText(String); - - impl vte::Perform for RawText { - fn print(&mut self, c: char) { - self.0.push(c); - } - - fn execute(&mut self, _: u8) {} - - fn hook(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} - - fn put(&mut self, b: u8) { - self.0.push(b as char); - } - - fn unhook(&mut self) {} - - fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {} - - fn csi_dispatch(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} - - fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {} - } - - let mut raw = String::new(); - for line in output.lines() { - let mut performer = RawText::default(); - let mut p = vte::Parser::new(); - for b in line.as_bytes() { - p.advance(&mut performer, *b); - } - raw.push_str(&performer.0); - raw.push('\n'); - } - - // Remove absolute paths from snapshot tests - let manifest_dir: PathBuf = PathBuf::from(manifest_dir); - let parent = manifest_dir.parent().unwrap(); - let raw = raw.replace(&format!("{}/", parent.display()), ""); - - let run_config_suffix = format!( - "{}-{}-{}", - run_config.build_target, - if run_config.experimental.new_encoding { - "encoding-v1" - } else { - "encoding-v0" - }, - if run_config.release { - "release" - } else { - "debug" - }, - ); - - // insta uses the fn name for the file. - fn stdout(folder: &str, raw: &str, run_config_suffix: String) { - let mut insta = Settings::new(); - insta.set_snapshot_path(folder); - insta.set_prepend_module_to_snapshot(false); - insta.set_omit_expression(true); - insta.set_snapshot_suffix(run_config_suffix); - let scope = insta.bind_to_scope(); - insta::assert_snapshot!(raw); - drop(scope); - } - - if allow_snapshot_panic { - stdout(&folder, &raw, run_config_suffix); - } else { - let r = std::panic::catch_unwind(|| { - let buf_stdout = gag::BufferRedirect::stdout(); - let buf_stderr = gag::BufferRedirect::stderr(); - - stdout(&folder, &raw, run_config_suffix); - - let mut output = String::new(); - if let Ok(mut buf_stdout) = buf_stdout { - buf_stdout.read_to_string(&mut output).unwrap(); - drop(buf_stdout); - } - if let Ok(mut buf_stderr) = buf_stderr { - buf_stderr.read_to_string(&mut output).unwrap(); - drop(buf_stderr); - } - - output - }); - - if r.is_err() { - return Err(anyhow::Error::msg("Snapshot differ")); - } - } } - - r } } -pub struct FilterTestResult { - skipped_tests: Vec, - disabled_tests: Vec, - included_tests: Vec, - excluded_tests: Vec, -} +pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result<()> { + // Discover tests + let mut tests = discover_test_configs(run_config)?; + let total_number_of_tests = tests.len(); -pub fn filter_tests( - filter_config: &FilterConfig, - tests: &mut Vec, -) -> FilterTestResult { // Filter tests let skipped_tests = filter_config .skip_until @@ -878,131 +736,72 @@ pub fn filter_tests( tests.retain(|t| t.category == TestCategory::RunsWithContract); } if filter_config.first_only && !tests.is_empty() { - *tests = vec![tests.remove(0)]; + tests = vec![tests.remove(0)]; } - if filter_config.snapshot_only { - tests.retain(|t| matches!(t.snapshot, Some(true))); - } - - FilterTestResult { - skipped_tests, - disabled_tests, - included_tests, - excluded_tests, - } -} -#[allow(clippy::too_many_arguments)] -pub async fn run_test( - context: &TestContext, - run_config: &RunConfig, - filter_config: &FilterConfig, - i: usize, - test: TestDescription, - failed_tests: &mut Vec, - number_of_tests_failed: &mut usize, - allow_snapshot_panic: bool, -) -> usize { - let cur_profile = if run_config.release { - BuildProfile::RELEASE - } else { - BuildProfile::DEBUG + // Run tests + let context = TestContext { + deployed_contracts: Default::default(), }; + let mut number_of_tests_executed = 0; + let mut number_of_tests_failed = 0; + let mut failed_tests = vec![]; - if test.unsupported_profiles.contains(&cur_profile) { - return 0; - } + let start_time = Instant::now(); + for (i, test) in tests.into_iter().enumerate() { + let cur_profile = if run_config.release { + BuildProfile::RELEASE + } else { + BuildProfile::DEBUG + }; - let name = if let Some(suffix) = test.suffix.as_ref() { - format!("{} ({})", test.name, suffix) - } else { - test.name.clone() - }; + if test.unsupported_profiles.contains(&cur_profile) { + continue; + } + + let name = if let Some(suffix) = test.suffix.as_ref() { + format!("{} ({})", test.name, suffix) + } else { + test.name.clone() + }; - if !allow_snapshot_panic { print!("Testing {} ...", name.clone().bold()); stdout().flush().unwrap(); - } - let mut output = String::new(); + let mut output = String::new(); - // Skip the test if its not compatible with the current build target. - if !test.supported_targets.contains(&run_config.build_target) { - return 0; - } - - let test_snapshot = test.snapshot; - - use std::fmt::Write; - let _ = writeln!(output, " {}", "Verbose Output".green().bold()); - let result = if !filter_config.first_only { - context - .run(test, &mut output, run_config.verbose, allow_snapshot_panic) - .instrument(tracing::trace_span!("E2E", i)) - .await - } else { - context - .run(test, &mut output, run_config.verbose, allow_snapshot_panic) - .await - }; + // Skip the test if its not compatible with the current build target. + if !test.supported_targets.contains(&run_config.build_target) { + continue; + } - if let Err(err) = result { - if matches!(test_snapshot, Some(true)) { - println!(" {} (snapshot mismatch)", "failed".red().bold()); + use std::fmt::Write; + let _ = writeln!(output, " {}", "Verbose Output".green().bold()); + let result = if !filter_config.first_only { + context + .run(test, &mut output, run_config.verbose) + .instrument(tracing::trace_span!("E2E", i)) + .await } else { + context.run(test, &mut output, run_config.verbose).await + }; + + if let Err(err) = result { println!(" {}", "failed".red().bold()); println!("{}", textwrap::indent(err.to_string().as_str(), " ")); println!("{}", textwrap::indent(&output, " ")); - } - - *number_of_tests_failed += 1; - failed_tests.push(name); - } else { - println!(" {}", "ok".green().bold()); + number_of_tests_failed += 1; + failed_tests.push(name); + } else { + println!(" {}", "ok".green().bold()); - // If verbosity is requested then print it out. - if run_config.verbose && !output.is_empty() { - println!("{}", textwrap::indent(&output, " ")); + // If verbosity is requested then print it out. + if run_config.verbose && !output.is_empty() { + println!("{}", textwrap::indent(&output, " ")); + } } - } - - 1 -} - -pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result<()> { - // Discover tests - let mut tests = discover_test_configs(run_config)?; - let total_number_of_tests = tests.len(); - - let FilterTestResult { - skipped_tests, - disabled_tests, - included_tests, - excluded_tests, - } = filter_tests(filter_config, &mut tests); - // Run tests - let context = TestContext { - deployed_contracts: Default::default(), - }; - let mut number_of_tests_executed = 0; - let mut number_of_tests_failed = 0; - let mut failed_tests = vec![]; - - let start_time = Instant::now(); - for (i, test) in tests.into_iter().enumerate() { - let qty = run_test( - &context, - run_config, - filter_config, - i, - test, - &mut failed_tests, - &mut number_of_tests_failed, - false, - ) - .await; - number_of_tests_executed += qty; + number_of_tests_executed += 1; } let duration = Instant::now().duration_since(start_time); @@ -1088,7 +887,7 @@ fn exclude_tests_dependency(t: &TestDescription, dep: &str) -> bool { } } -pub fn discover_test_configs(run_config: &RunConfig) -> Result> { +fn discover_test_configs(run_config: &RunConfig) -> Result> { fn recursive_search( path: &Path, run_config: &RunConfig, @@ -1186,13 +985,8 @@ fn parse_test_toml(path: &Path, run_config: &RunConfig) -> Result Result::new().push(_a); diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout@fuel-encoding-v1-debug.snap b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap similarity index 52% rename from test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout@fuel-encoding-v1-debug.snap rename to test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap index 77fd73fcb7d..195ad7855c1 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout@fuel-encoding-v1-debug.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap @@ -1,133 +1,135 @@ --- -source: test/src/e2e_vm_tests/mod.rs +source: test/test/e2e_tests.rs --- -Compiling should_fail/type_check_analyze_errors ... - Building test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors +exit status: 1 +stdout: + Building src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors Compiling library core (sway-lib-core) Compiling library std (sway-lib-std) Compiling script type_check_analyze_errors (test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors) -Failed to compile type_check_analyze_errors +stderr: error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:24:18 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:23:18 | -22 | -23 | let a = [8, 256u16, 8u8]; -24 | let b: u32 = a[2]; +21 | +22 | let a = [8, 256u16, 8u8]; +23 | let b: u32 = a[2]; | ^^^^ Mismatched types. expected: u32 found: u16. help: Variable declaration's type annotation does not match up with the assigned expression's type. -25 | } +24 | } | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:5:14 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:4:14 | -3 | -4 | // u8 -5 | let _a = 0x100; +2 | +3 | fn main() { +4 | let _a = 0x100; | ^^^^^ Literal would overflow because its value does not fit into "u8" -6 | Vec::::new().push(_a); +5 | Vec::::new().push(_a); | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:9:14 - | - 7 | - 8 | // u16 - 9 | let _a = 0x10000; - | ^^^^^^^ Literal would overflow because its value does not fit into "u16" -10 | Vec::::new().push(_a); - | + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:8:14 + | +6 | +7 | // u16 +8 | let _a = 0x10000; + | ^^^^^^^ Literal would overflow because its value does not fit into "u16" +9 | Vec::::new().push(_a); + | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:13:14 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:12:14 | -11 | -12 | // u32 -13 | let _a = 0x100000000; +10 | +11 | // u32 +12 | let _a = 0x100000000; | ^^^^^^^^^^^ Literal would overflow because its value does not fit into "u32" -14 | Vec::::new().push(_a); +13 | Vec::::new().push(_a); | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:17:20 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:16:20 | -15 | -16 | // Array -17 | let a = [1, 2, "hello"]; +14 | +15 | // Array +16 | let a = [1, 2, "hello"]; | ^^^^^^^ Mismatched types. expected: numeric found: str. -18 | -19 | // Array - different numerics +17 | +18 | // Array - different numerics | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:20:22 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:19:22 | -18 | -19 | // Array - different numerics -20 | let a = [1, 2u8, 3u16, 4u32, 5u64]; +17 | +18 | // Array - different numerics +19 | let a = [1, 2u8, 3u16, 4u32, 5u64]; | ^^^^ Mismatched types. expected: u8 found: u16. -21 | -22 | // Wrong cast +20 | +21 | // Wrong cast | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:20:28 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:19:28 | -18 | -19 | // Array - different numerics -20 | let a = [1, 2u8, 3u16, 4u32, 5u64]; +17 | +18 | // Array - different numerics +19 | let a = [1, 2u8, 3u16, 4u32, 5u64]; | ^^^^ Mismatched types. expected: u8 found: u32. -21 | -22 | // Wrong cast +20 | +21 | // Wrong cast | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:20:34 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:19:34 | -18 | -19 | // Array - different numerics -20 | let a = [1, 2u8, 3u16, 4u32, 5u64]; +17 | +18 | // Array - different numerics +19 | let a = [1, 2u8, 3u16, 4u32, 5u64]; | ^^^^ Mismatched types. expected: u8 found: u64. -21 | -22 | // Wrong cast +20 | +21 | // Wrong cast | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:23:25 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:22:25 | -21 | -22 | // Wrong cast -23 | let a = [8, 256u16, 8u8]; +20 | +21 | // Wrong cast +22 | let a = [8, 256u16, 8u8]; | ^^^ Mismatched types. expected: u16 found: u8. -24 | let b: u32 = a[2]; -25 | } +23 | let b: u32 = a[2]; +24 | } | ____ Aborting due to 9 errors. +error: Failed to compile type_check_analyze_errors diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml deleted file mode 100644 index 58d39acb0d9..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml +++ /dev/null @@ -1,2 +0,0 @@ -category = "fail" -snapshot = true diff --git a/test/src/lib.rs b/test/src/lib.rs deleted file mode 100644 index 9cc47dec202..00000000000 --- a/test/src/lib.rs +++ /dev/null @@ -1,163 +0,0 @@ -pub mod e2e_vm_tests; -mod ir_generation; -pub mod reduced_std_libs; -pub mod test_consistency; - -use anyhow::Result; -use clap::Parser; -use e2e_vm_tests::{FilterConfig, RunConfig}; -use forc::cli::shared::{PrintAsmCliOpt, PrintIrCliOpt}; -use forc_tracing::init_tracing_subscriber; -use std::str::FromStr; -use sway_core::{BuildTarget, PrintAsm, PrintIr}; -use tracing::Instrument; - -#[derive(Parser)] -pub struct Cli { - /// Only run tests matching this regex - #[arg(value_name = "REGEX")] - pub include: Option, - - /// Exclude tests matching this regex - #[arg(long, short, value_name = "REGEX")] - pub exclude: Option, - - /// Skip all tests until a test matches this regex - #[arg(long, short, value_name = "REGEX")] - pub skip_until: Option, - - /// Only run tests with ABI JSON output validation - #[arg(long, visible_alias = "abi")] - pub abi_only: bool, - - /// Only run tests with no core dependencies - #[arg(long, visible_alias = "exclude_core")] - pub exclude_core: bool, - - /// Only run tests with no std dependencies - #[arg(long, visible_alias = "exclude_std")] - pub exclude_std: bool, - - /// Only run tests that deploy contracts - #[arg(long, visible_alias = "contract")] - pub contract_only: bool, - - /// Only run the first test - #[arg(long, visible_alias = "first")] - pub first_only: bool, - - /// Only run snapshot test - #[arg(long, visible_alias = "snapshot")] - pub snapshot_only: bool, - - /// Print out warnings, errors, and output of print options - #[arg(long, env = "SWAY_TEST_VERBOSE")] - pub verbose: bool, - - /// Compile sway code in release mode - #[arg(long)] - pub release: bool, - - /// Intended for use in `CI` to ensure test lock files are up to date - #[arg(long)] - pub locked: bool, - - /// Build target - #[arg(long, visible_alias = "target")] - pub build_target: Option, - - /// Disable the "new encoding" feature - #[arg(long)] - pub no_encoding_v1: bool, - - /// Update all output files - #[arg(long)] - pub update_output_files: bool, - - /// Print out the specified IR (separate options with comma), if the verbose option is on - #[arg(long, num_args(1..=18), value_parser = clap::builder::PossibleValuesParser::new(PrintIrCliOpt::cli_options()))] - pub print_ir: Option>, - - /// Print out the specified ASM (separate options with comma), if the verbose option is on - #[arg(long, num_args(1..=5), value_parser = clap::builder::PossibleValuesParser::new(&PrintAsmCliOpt::CLI_OPTIONS))] - pub print_asm: Option>, - - /// Print out the final bytecode, if the verbose option is on - #[arg(long)] - pub print_bytecode: bool, -} - -pub fn configs_from_cli(cli: &Cli) -> (FilterConfig, RunConfig) { - let filter_config = FilterConfig { - include: cli.include.clone(), - exclude: cli.exclude.clone(), - skip_until: cli.skip_until.clone(), - abi_only: cli.abi_only, - exclude_core: cli.exclude_core, - exclude_std: cli.exclude_std, - contract_only: cli.contract_only, - first_only: cli.first_only, - snapshot_only: cli.snapshot_only, - }; - let build_target = match &cli.build_target { - Some(target) => match BuildTarget::from_str(target.as_str()) { - Ok(target) => target, - _ => panic!("unexpected build target"), - }, - None => BuildTarget::default(), - }; - let run_config = RunConfig { - locked: cli.locked, - verbose: cli.verbose, - release: cli.release, - build_target, - experimental: sway_core::ExperimentalFlags { - new_encoding: !cli.no_encoding_v1, - }, - update_output_files: cli.update_output_files, - print_ir: cli - .print_ir - .as_ref() - .map_or(PrintIr::default(), |opts| PrintIrCliOpt::from(opts).0), - print_asm: cli - .print_asm - .as_ref() - .map_or(PrintAsm::default(), |opts| PrintAsmCliOpt::from(opts).0), - print_bytecode: cli.print_bytecode, - }; - - (filter_config, run_config) -} - -pub async fn run(cli: Cli) -> Result<()> { - init_tracing_subscriber(Default::default()); - - let (filter_config, run_config) = configs_from_cli(&cli); - - // Check that the tests are consistent - test_consistency::check()?; - - // Create reduced versions of the `std` library. - reduced_std_libs::create()?; - - // Run E2E tests - e2e_vm_tests::run(&filter_config, &run_config) - .instrument(tracing::trace_span!("E2E")) - .await?; - - // Run IR tests - if !filter_config.first_only && !filter_config.snapshot_only { - println!("\n"); - ir_generation::run( - filter_config.include.as_ref(), - cli.verbose, - sway_ir::ExperimentalFlags { - new_encoding: run_config.experimental.new_encoding, - }, - ) - .instrument(tracing::trace_span!("IR")) - .await?; - } - - Ok(()) -} diff --git a/test/src/main.rs b/test/src/main.rs index 12125e3d2ac..22a6c01c0e1 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -1,10 +1,187 @@ +mod e2e_vm_tests; +mod ir_generation; +mod reduced_std_libs; +mod test_consistency; + use anyhow::Result; use clap::Parser; -use e2e_tests::*; +use forc::cli::shared::{PrintAsmCliOpt, PrintIrCliOpt}; +use forc_tracing::init_tracing_subscriber; +use std::str::FromStr; +use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; +use tracing::Instrument; + +#[derive(Parser)] +struct Cli { + /// Only run tests matching this regex + #[arg(value_name = "REGEX")] + include: Option, + + /// Exclude tests matching this regex + #[arg(long, short, value_name = "REGEX")] + exclude: Option, + + /// Skip all tests until a test matches this regex + #[arg(long, short, value_name = "REGEX")] + skip_until: Option, + + /// Only run tests with ABI JSON output validation + #[arg(long, visible_alias = "abi")] + abi_only: bool, + + /// Only run tests with no core dependencies + #[arg(long, visible_alias = "exclude_core")] + exclude_core: bool, + + /// Only run tests with no std dependencies + #[arg(long, visible_alias = "exclude_std")] + exclude_std: bool, + + /// Only run tests that deploy contracts + #[arg(long, visible_alias = "contract")] + contract_only: bool, + + /// Only run the first test + #[arg(long, visible_alias = "first")] + first_only: bool, + + /// Print out warnings, errors, and output of print options + #[arg(long, env = "SWAY_TEST_VERBOSE")] + verbose: bool, + + /// Compile sway code in release mode + #[arg(long)] + release: bool, + + /// Intended for use in `CI` to ensure test lock files are up to date + #[arg(long)] + locked: bool, + + /// Build target + #[arg(long, visible_alias = "target")] + build_target: Option, + + /// Disable the "new encoding" feature + #[arg(long)] + no_encoding_v1: bool, + + /// Update all output files + #[arg(long)] + update_output_files: bool, + + /// Print out the specified IR (separate options with comma), if the verbose option is on + #[arg(long, num_args(1..=18), value_parser = clap::builder::PossibleValuesParser::new(PrintIrCliOpt::cli_options()))] + print_ir: Option>, + + /// Print out the specified ASM (separate options with comma), if the verbose option is on + #[arg(long, num_args(1..=5), value_parser = clap::builder::PossibleValuesParser::new(&PrintAsmCliOpt::CLI_OPTIONS))] + print_asm: Option>, + + /// Print out the final bytecode, if the verbose option is on + #[arg(long)] + print_bytecode: bool, +} + +#[derive(Debug, Clone)] +pub struct FilterConfig { + pub include: Option, + pub exclude: Option, + pub skip_until: Option, + pub abi_only: bool, + pub exclude_core: bool, + pub exclude_std: bool, + pub contract_only: bool, + pub first_only: bool, +} + +#[derive(Debug, Clone)] +pub struct RunConfig { + pub build_target: BuildTarget, + pub locked: bool, + pub verbose: bool, + pub release: bool, + pub experimental: ExperimentalFlags, + pub update_output_files: bool, + pub print_ir: PrintIr, + pub print_asm: PrintAsm, + pub print_bytecode: bool, +} #[tokio::main] async fn main() -> Result<()> { + init_tracing_subscriber(Default::default()); + + // Parse args let cli = Cli::parse(); - let _ = run(cli).await; + let filter_config = FilterConfig { + include: cli.include, + exclude: cli.exclude, + skip_until: cli.skip_until, + abi_only: cli.abi_only, + exclude_core: cli.exclude_core, + exclude_std: cli.exclude_std, + contract_only: cli.contract_only, + first_only: cli.first_only, + }; + let build_target = match cli.build_target { + Some(target) => match BuildTarget::from_str(target.as_str()) { + Ok(target) => target, + _ => panic!("unexpected build target"), + }, + None => BuildTarget::default(), + }; + let run_config = RunConfig { + locked: cli.locked, + verbose: cli.verbose, + release: cli.release, + build_target, + experimental: sway_core::ExperimentalFlags { + new_encoding: !cli.no_encoding_v1, + }, + update_output_files: cli.update_output_files, + print_ir: cli + .print_ir + .as_ref() + .map_or(PrintIr::default(), |opts| PrintIrCliOpt::from(opts).0), + print_asm: cli + .print_asm + .as_ref() + .map_or(PrintAsm::default(), |opts| PrintAsmCliOpt::from(opts).0), + print_bytecode: cli.print_bytecode, + }; + + // Check that the tests are consistent + test_consistency::check()?; + + // Create reduced versions of the `std` library. + reduced_std_libs::create()?; + + // Run E2E tests + e2e_vm_tests::run(&filter_config, &run_config) + .instrument(tracing::trace_span!("E2E")) + .await?; + + // Run IR tests + if !filter_config.first_only { + println!("\n"); + ir_generation::run( + filter_config.include.as_ref(), + cli.verbose, + sway_ir::ExperimentalFlags { + new_encoding: run_config.experimental.new_encoding, + }, + ) + .instrument(tracing::trace_span!("IR")) + .await?; + } + + // Run snapshot tests + let args = vec!["t", "--release", "-p", "test"]; + let mut t = std::process::Command::new("cargo") + .args(args) + .spawn() + .unwrap(); + assert!(t.wait().unwrap().success()); + Ok(()) } diff --git a/test/src/reduced_std_libs.rs b/test/src/reduced_std_libs.rs index f6edafae1ce..b5a5cfb6e1a 100644 --- a/test/src/reduced_std_libs.rs +++ b/test/src/reduced_std_libs.rs @@ -13,7 +13,7 @@ const REDUCED_LIB_CONFIG_FILE_NAME: &str = "reduced_lib.config"; /// Creates the reduced versions of `std` libraries based on the list of /// modules defined in [REDUCED_LIB_CONFIG_FILE_NAME] file for each reduced library /// available in the [REDUCED_STD_LIBS_DIR_NAME]. -pub fn create() -> Result<()> { +pub(crate) fn create() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let reduced_libs_dir = format!("{manifest_dir}/src/e2e_vm_tests/{REDUCED_STD_LIBS_DIR_NAME}"); let std_lib_src_dir = format!("{manifest_dir}/../sway-lib-std/src"); diff --git a/test/src/test_consistency.rs b/test/src/test_consistency.rs index 21c76d52c74..379ba1b7381 100644 --- a/test/src/test_consistency.rs +++ b/test/src/test_consistency.rs @@ -6,8 +6,7 @@ use toml::{Table, Value}; use crate::reduced_std_libs::REDUCED_STD_LIBS_DIR_NAME; -#[allow(dead_code)] -pub fn check() -> Result<()> { +pub(crate) fn check() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let all_tests_dir = format!("{manifest_dir}/src"); diff --git a/test/test/e2e_tests.rs b/test/test/e2e_tests.rs deleted file mode 100644 index dccd268ec58..00000000000 --- a/test/test/e2e_tests.rs +++ /dev/null @@ -1,70 +0,0 @@ -use e2e_tests::*; -use e2e_vm_tests::{filter_tests, TestContext}; -use forc_tracing::init_tracing_subscriber; -use libtest_mimic::{Arguments, Trial}; - -pub fn main() { - init_tracing_subscriber(Default::default()); - - let mut args = Arguments::from_args(); - args.nocapture = true; - args.test_threads = Some(1); - - e2e_tests::test_consistency::check().unwrap(); - e2e_tests::reduced_std_libs::create().unwrap(); - - let (filter_config, run_config) = e2e_tests::configs_from_cli(&Cli { - include: None, - exclude: None, - skip_until: None, - abi_only: false, - exclude_core: false, - exclude_std: false, - contract_only: false, - first_only: false, - verbose: false, - release: false, - locked: false, - build_target: Some("fuel".into()), - no_encoding_v1: false, - update_output_files: false, - print_ir: None, - print_asm: None, - print_bytecode: false, - snapshot_only: true, - }); - - let mut tests = e2e_tests::e2e_vm_tests::discover_test_configs(&run_config).unwrap(); - filter_tests(&filter_config, &mut tests); - let tests = tests - .into_iter() - .map(|test| { - let run_config = run_config.clone(); - let filter_config = filter_config.clone(); - Trial::test(test.name.clone(), move || { - let rt = tokio::runtime::Runtime::new()?; - rt.block_on(async { - let context = TestContext { - deployed_contracts: Default::default(), - }; - let mut failed_tests = vec![]; - let mut qty_failed = 0; - e2e_vm_tests::run_test( - &context, - &run_config, - &filter_config, - 0, - test, - &mut failed_tests, - &mut qty_failed, - true, - ) - .await; - }); - Ok(()) - }) - }) - .collect(); - - libtest_mimic::run(&args, tests).exit(); -} diff --git a/test/tests/tests.rs b/test/tests/tests.rs new file mode 100644 index 00000000000..3ff50c306de --- /dev/null +++ b/test/tests/tests.rs @@ -0,0 +1,124 @@ +use libtest_mimic::{Arguments, Trial}; +use std::{path::PathBuf, sync::Once}; + +static FORC_COMPILATION: Once = Once::new(); + +fn compile_forc() { + // e2e_tests::test_consistency::check().unwrap(); + // e2e_tests::reduced_std_libs::create().unwrap(); + + let args = vec!["b", "--release", "-p", "forc"]; + let o = std::process::Command::new("cargo") + .args(args) + .output() + .unwrap(); + assert!(o.status.success()); +} + +pub fn main() { + let mut args = Arguments::from_args(); + args.nocapture = true; + + let tests = discover_test() + .into_iter() + .map(|dir| { + let manifest_dir = "src/e2e_vm_tests/test_programs/"; + let name = dir.to_str().unwrap().to_string().replace(&manifest_dir, ""); + Trial::test(name, move || { + FORC_COMPILATION.call_once(|| { + compile_forc(); + }); + + let root = dir.to_str().unwrap(); + + let args = vec!["build", "--path", root]; + let o = std::process::Command::new("../target/release/forc") + .args(args) + .output() + .unwrap(); + + let snapshot = clean_output(&format!( + "exit status: {}\nstdout:\n{}\nstderr:\n{}", + o.status.code().unwrap(), + String::from_utf8(o.stdout).unwrap(), + String::from_utf8(o.stderr).unwrap() + )); + + fn stdout(root: &str, snapshot: &str) { + let mut insta = insta::Settings::new(); + insta.set_snapshot_path(root); + insta.set_prepend_module_to_snapshot(false); + insta.set_omit_expression(true); + let scope = insta.bind_to_scope(); + insta::assert_snapshot!(snapshot); + drop(scope); + } + stdout(&format!("../{root}"), &snapshot); + + Ok(()) + }) + }) + .collect(); + libtest_mimic::run(&args, tests).exit(); +} + +pub fn discover_test() -> Vec { + use glob::glob; + + let mut entries = vec![]; + + for entry in glob("**/snapshot.toml").expect("Failed to read glob pattern") { + match entry { + Ok(path) => entries.push(path.parent().unwrap().to_owned()), + _ => {} + } + } + + entries +} + +fn clean_output(output: &str) -> String { + #[derive(Default)] + struct RawText(String); + + impl vte::Perform for RawText { + fn print(&mut self, c: char) { + self.0.push(c); + } + + fn execute(&mut self, _: u8) {} + + fn hook(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} + + fn put(&mut self, b: u8) { + self.0.push(b as char); + } + + fn unhook(&mut self) {} + + fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {} + + fn csi_dispatch(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} + + fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {} + } + + let mut raw = String::new(); + for line in output.lines() { + let mut performer = RawText::default(); + let mut p = vte::Parser::new(); + for b in line.as_bytes() { + p.advance(&mut performer, *b); + } + raw.push_str(&performer.0); + raw.push('\n'); + } + + // Remove absolute paths from snapshot tests + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let manifest_dir: PathBuf = PathBuf::from(manifest_dir); + let parent = manifest_dir.parent().unwrap(); + let raw = raw.replace(&format!("{}/", parent.display()), ""); + + raw +} From 7699dce1305aab4f19aa727bc24d0b5e397a9750 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Fri, 16 Aug 2024 14:11:58 +0100 Subject: [PATCH 13/20] mor cleaning --- .github/workflows/ci.yml | 68 +++++++++---------- .../test/data/standalone_contract/Forc.lock | 13 ---- test/src/e2e_vm_tests/mod.rs | 2 +- 3 files changed, 35 insertions(+), 48 deletions(-) delete mode 100644 forc-plugins/forc-client/test/data/standalone_contract/Forc.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0534e0e1ce8..76640ea54f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,7 +146,7 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 - + - name: Set up Rust uses: actions-rs/toolchain@v1 with: @@ -194,7 +194,7 @@ jobs: - name: Run mdbook build uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: "0.4.25" + mdbook-version: '0.4.25' - name: Emit book logs to tmp.txt, fail if build logs contain 'ERROR' run: | MDBOOK_preprocessor__FORC_documenter__STRICT="true" mdbook build docs/book 2>&1 | tee tmp.txt @@ -276,10 +276,10 @@ jobs: with: toolchain: ${{ env.RUST_VERSION }} - uses: Swatinem/rust-cache@v2 - - name: "Build Workspace" + - name: 'Build Workspace' run: cargo build --locked --workspace --all-features --all-targets env: - RUSTFLAGS: "-D warnings" + RUSTFLAGS: '-D warnings' cargo-clippy: runs-on: ubuntu-latest @@ -306,7 +306,7 @@ jobs: uses: baptiste0928/cargo-install@v1 with: crate: cargo-toml-lint - version: "0.1" + version: '0.1' - name: Run Cargo.toml linter run: git ls-files | grep Cargo.toml$ | grep -v 'templates/' | xargs --verbose -n 1 cargo-toml-lint @@ -341,9 +341,9 @@ jobs: mv fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core - name: Cargo Run E2E Tests (Fuel VM) run: | - fuel-core run --db-type in-memory --debug & + fuel-core run --db-type in-memory --debug & sleep 5 && - CI=true cargo run --locked --release -p e2e_tests -- --locked + cargo run --locked --release --bin test -- --locked cargo-run-e2e-test-release: runs-on: ubuntu-latest @@ -364,9 +364,9 @@ jobs: mv fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core - name: Cargo Run E2E Tests (Fuel VM) run: | - fuel-core run --db-type in-memory --debug & + fuel-core run --db-type in-memory --debug & sleep 5 && - CI=true cargo run --locked --release -p e2e_tests -- --locked --release + cargo run --locked --release --bin test -- --locked --release cargo-run-e2e-test-evm: runs-on: ubuntu-latest @@ -446,8 +446,8 @@ jobs: run: ./benchmark.sh --prepare-for-commit - uses: EndBug/add-and-commit@v9 with: - cwd: "./performance-data" - message: "Updated benchmark data" + cwd: './performance-data' + message: 'Updated benchmark data' default_author: github_actions forc-unit-tests: @@ -598,10 +598,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{workflow} has {status_message}" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" - footer: "" - notify_when: "failure" + notification_title: '{workflow} has {status_message}' + message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' + footer: '' + notify_when: 'failure' env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -625,7 +625,7 @@ jobs: ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-pkg/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-client/Cargo.toml - ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-debug/Cargo.toml + ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-debug/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-doc/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-fmt/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-lsp/Cargo.toml @@ -651,10 +651,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{workflow} has {status_message}" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" - footer: "" - notify_when: "failure" + notification_title: '{workflow} has {status_message}' + message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' + footer: '' + notify_when: 'failure' env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -701,10 +701,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{workflow} has {status_message}" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" - footer: "" - notify_when: "failure" + notification_title: '{workflow} has {status_message}' + message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' + footer: '' + notify_when: 'failure' env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -778,10 +778,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{workflow} has {status_message}" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" - footer: "" - notify_when: "failure" + notification_title: '{workflow} has {status_message}' + message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' + footer: '' + notify_when: 'failure' env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -833,10 +833,10 @@ jobs: with: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{workflow} has {status_message}" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>" - footer: "" - notify_when: "failure" + notification_title: '{workflow} has {status_message}' + message_format: '{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : <{run_url}|View Run Results>' + footer: '' + notify_when: 'failure' env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} @@ -881,13 +881,13 @@ jobs: - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true - key: "${{ matrix.job.target }}" + key: '${{ matrix.job.target }}' - name: Use Cross uses: baptiste0928/cargo-install@v1 with: crate: cross - cache-key: "${{ matrix.job.target }}" + cache-key: '${{ matrix.job.target }}' - name: Build forc binaries run: | diff --git a/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock b/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock deleted file mode 100644 index 7b517045569..00000000000 --- a/forc-plugins/forc-client/test/data/standalone_contract/Forc.lock +++ /dev/null @@ -1,13 +0,0 @@ -[[package]] -name = "core" -source = "path+from-root-79BB3EA8498403DE" - -[[package]] -name = "standalone_contract" -source = "member" -dependencies = ["std"] - -[[package]] -name = "std" -source = "path+from-root-79BB3EA8498403DE" -dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index f52bba955c6..947ad9b20cc 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -494,8 +494,8 @@ impl TestContext { ) }) .await; - result?; output.push_str(&out); + result?; } } From aa03c92b249efc26a24fb3e958ea37fcbbd275bb Mon Sep 17 00:00:00 2001 From: xunilrj Date: Fri, 16 Aug 2024 14:30:57 +0100 Subject: [PATCH 14/20] fmt and clippy issues --- test/tests/tests.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/tests/tests.rs b/test/tests/tests.rs index 3ff50c306de..5cec89acd2f 100644 --- a/test/tests/tests.rs +++ b/test/tests/tests.rs @@ -23,7 +23,7 @@ pub fn main() { .into_iter() .map(|dir| { let manifest_dir = "src/e2e_vm_tests/test_programs/"; - let name = dir.to_str().unwrap().to_string().replace(&manifest_dir, ""); + let name = dir.to_str().unwrap().to_string().replace(manifest_dir, ""); Trial::test(name, move || { FORC_COMPILATION.call_once(|| { compile_forc(); @@ -67,11 +67,11 @@ pub fn discover_test() -> Vec { let mut entries = vec![]; - for entry in glob("**/snapshot.toml").expect("Failed to read glob pattern") { - match entry { - Ok(path) => entries.push(path.parent().unwrap().to_owned()), - _ => {} - } + for entry in glob("**/snapshot.toml") + .expect("Failed to read glob pattern") + .flatten() + { + entries.push(entry.parent().unwrap().to_owned()) } entries @@ -118,7 +118,5 @@ fn clean_output(output: &str) -> String { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let manifest_dir: PathBuf = PathBuf::from(manifest_dir); let parent = manifest_dir.parent().unwrap(); - let raw = raw.replace(&format!("{}/", parent.display()), ""); - - raw + raw.replace(&format!("{}/", parent.display()), "") } From fde3c68212581df05464a79d03f784292e19eeee Mon Sep 17 00:00:00 2001 From: xunilrj Date: Fri, 16 Aug 2024 17:08:05 +0100 Subject: [PATCH 15/20] better usage of concrete types in arrays --- .../ast_node/expression/typed_expression.rs | 7 ++ test/Cargo.toml | 2 +- .../type_check_analyze_errors/src/main.sw | 4 ++ .../type_check_analyze_errors/stdout.snap | 68 +++++++++++++------ 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index bebff36076a..e9d2d69a0bc 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -1870,6 +1870,13 @@ impl ty::TyExpression { return Done(Some(last)); } + if current + .return_type + .is_concrete(engines, NumericIsNonConcrete::Yes) + { + return Done(Some(current.return_type)); + } + let last_info = ctx.engines().te().get(last); let current_info = ctx.engines().te().get(current.return_type); match (&*last_info, &*current_info) { diff --git a/test/Cargo.toml b/test/Cargo.toml index 7da6f0033e3..9d1d5248d68 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -35,7 +35,7 @@ sway-ir = { path = "../sway-ir" } sway-types = { path = "../sway-types" } sway-utils = { path = "../sway-utils" } textwrap = "0.16.0" -tokio = { version = "1.12", features = ["full"] } +tokio = "1.12" toml = { version = "0.7", features = ["parse"] } tracing = "0.1" vte = "0.13.0" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw index 4142555d4aa..1b26cfa81c8 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw @@ -18,6 +18,10 @@ fn main() { // Array - different numerics let a = [1, 2u8, 3u16, 4u32, 5u64]; + // Array - unspecified generic structs + let a = [None, Some(1), Some(1u8)]; + let _b: Option = a[1]; + // Wrong cast let a = [8, 256u16, 8u8]; let b: u32 = a[2]; diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap index 195ad7855c1..9a88cbc088e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap @@ -1,5 +1,5 @@ --- -source: test/test/e2e_tests.rs +source: test/tests/tests.rs --- exit status: 1 stdout: @@ -10,16 +10,31 @@ stdout: stderr: error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:23:18 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:23:27 | 21 | -22 | let a = [8, 256u16, 8u8]; -23 | let b: u32 = a[2]; +22 | let a = [None, Some(1), Some(1u8)]; +23 | let _b: Option = a[1]; + | ^^^^ Mismatched types. +expected: Option +found: Option. +help: Variable declaration's type annotation does not match up with the assigned expression's type. +24 | +25 | // Wrong cast + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:27:18 + | +25 | +26 | let a = [8, 256u16, 8u8]; +27 | let b: u32 = a[2]; | ^^^^ Mismatched types. expected: u32 found: u16. help: Variable declaration's type annotation does not match up with the assigned expression's type. -24 | } +28 | } | ____ @@ -57,14 +72,29 @@ error ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:16:20 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:16:14 + | +14 | +15 | // Array +16 | let a = [1, 2, "hello"]; + | ^ Mismatched types. +expected: str +found: numeric. + +17 | +18 | // Array - different numerics + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:16:17 | 14 | 15 | // Array 16 | let a = [1, 2, "hello"]; - | ^^^^^^^ Mismatched types. -expected: numeric -found: str. + | ^ Mismatched types. +expected: str +found: numeric. 17 | 18 | // Array - different numerics @@ -82,7 +112,7 @@ expected: u8 found: u16. 20 | -21 | // Wrong cast +21 | // Array - unspecified generic structs | ____ @@ -97,7 +127,7 @@ expected: u8 found: u32. 20 | -21 | // Wrong cast +21 | // Array - unspecified generic structs | ____ @@ -112,24 +142,24 @@ expected: u8 found: u64. 20 | -21 | // Wrong cast +21 | // Array - unspecified generic structs | ____ error - --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:22:25 + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:26:25 | -20 | -21 | // Wrong cast -22 | let a = [8, 256u16, 8u8]; +24 | +25 | // Wrong cast +26 | let a = [8, 256u16, 8u8]; | ^^^ Mismatched types. expected: u16 found: u8. -23 | let b: u32 = a[2]; -24 | } +27 | let b: u32 = a[2]; +28 | } | ____ - Aborting due to 9 errors. + Aborting due to 11 errors. error: Failed to compile type_check_analyze_errors From fa674d36c79115dd176ba9d2374698b880e6ee74 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 19 Aug 2024 08:12:01 +0100 Subject: [PATCH 16/20] snapshot explanation at readme.md --- test/src/e2e_vm_tests/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/src/e2e_vm_tests/README.md b/test/src/e2e_vm_tests/README.md index fdc8af3fa0a..1f71421bf8a 100644 --- a/test/src/e2e_vm_tests/README.md +++ b/test/src/e2e_vm_tests/README.md @@ -3,6 +3,7 @@ In order to minimize compilation time of individual tests, strive to reduce dependencies in tests. To achieve that, follow these guidelines: + - Use `implicit-std = false` if dependency on `core` is not needed. This is often possible when testing `should_pass/language` features. - If the dependency on `core` is not needed, instead of using the project type `script`, that will, because of the encoding, depend on `core`, try using `library` instead. - Do not use `std` just to conveniently get an arbitrary type or trait. E.g., if a test requires an arbitrary type or trait, go with `struct Dummy {}` or `trait Trait {}` instead of importing `Option` or `Hash`. @@ -87,3 +88,17 @@ SWAY_TEST_VERBOSE=true cargo run [pattern] ``` from the `sway/test` directory. + +# Snapshot tests + +When an "e2e" test has a file named `snapshot.toml` it will run as `cargo insta` snapshot tests. +These tests can be run as normal: `cargo r -p test`, and in two new ways: + +``` +> cargo t -p test +> cargo insta test +``` + +Snapshots can be reviewed using normal "cargo insta" workflow (see [insta.rs](https://insta.rs/)). + +For the moment, there is no configuration for `snapshot.toml`, so they are just empty files. From a5620ceb50811920c923f87f94824f35d2c0b392 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 19 Aug 2024 08:17:46 +0100 Subject: [PATCH 17/20] more array tests --- .../type_check_analyze_errors/src/main.sw | 5 +++++ .../type_check_analyze_errors/stdout.snap | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw index 1b26cfa81c8..e0d26feec0b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw @@ -26,3 +26,8 @@ fn main() { let a = [8, 256u16, 8u8]; let b: u32 = a[2]; } + +fn insufficient_type_check(arg: u64) -> [u32;2] { + let res = [1u32, arg]; + res +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap index 9a88cbc088e..decd4fba792 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap @@ -161,5 +161,20 @@ found: u8. | ____ - Aborting due to 11 errors. +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:31:22 + | +29 | +30 | fn insufficient_type_check(arg: u64) -> [u32;2] { +31 | let res = [1u32, arg]; + | ^^^ Mismatched types. +expected: u32 +found: u64. + +32 | res +33 | } + | +____ + + Aborting due to 12 errors. error: Failed to compile type_check_analyze_errors From e928b9cfa51f75f8d93756ac6741315074946f5d Mon Sep 17 00:00:00 2001 From: xunilrj Date: Wed, 21 Aug 2024 12:30:31 +0100 Subject: [PATCH 18/20] fix rebase issues --- Cargo.lock | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9b2b183b8c..67ddeaada5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4054,7 +4054,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58" dependencies = [ - "clap 4.5.9", + "clap 4.5.16", "escape8259", "termcolor", "threadpool", @@ -4636,6 +4636,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + [[package]] name = "num_enum" version = "0.5.11" @@ -7288,42 +7298,26 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> 7332e18cb (simpler insta setup) name = "test" version = "0.0.0" dependencies = [ "anyhow", "bytes", -<<<<<<< HEAD "clap 4.5.16", -======= - "clap 4.5.9", ->>>>>>> 7332e18cb (simpler insta setup) "colored", "filecheck", "forc", "forc-client", "forc-pkg", "forc-test", -<<<<<<< HEAD "forc-tracing 0.63.1", "fuel-vm", "futures", "gag", - "hex", -======= - "forc-tracing 0.62.0", - "fuel-vm", - "futures", - "gag", "glob", "hex", "insta", "libtest-mimic", ->>>>>>> 7332e18cb (simpler insta setup) "miden", "prettydiff 0.6.4", "rand", @@ -7339,18 +7333,10 @@ dependencies = [ "tokio", "toml 0.7.8", "tracing", -<<<<<<< HEAD -] - -[[package]] -======= ->>>>>>> 0db1bd6b9 (snapshot tests and fix for array with numerics) -======= "vte 0.13.0", ] [[package]] ->>>>>>> 7332e18cb (simpler insta setup) name = "test-macros" version = "0.0.0" dependencies = [ From 9c9c05efec4beb930cfa9e4c3226511a11418f5c Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 26 Aug 2024 13:20:25 +0100 Subject: [PATCH 19/20] removing commented lines --- test/tests/tests.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/tests/tests.rs b/test/tests/tests.rs index 5cec89acd2f..b526159f038 100644 --- a/test/tests/tests.rs +++ b/test/tests/tests.rs @@ -4,9 +4,6 @@ use std::{path::PathBuf, sync::Once}; static FORC_COMPILATION: Once = Once::new(); fn compile_forc() { - // e2e_tests::test_consistency::check().unwrap(); - // e2e_tests::reduced_std_libs::create().unwrap(); - let args = vec!["b", "--release", "-p", "forc"]; let o = std::process::Command::new("cargo") .args(args) From 4e725c2cf875253d0a53793e8070b61828b251b9 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 26 Aug 2024 13:43:41 +0100 Subject: [PATCH 20/20] improve numeric enum name --- sway-core/src/language/ty/declaration/function.rs | 6 +++--- .../ast_node/expression/intrinsic_function.rs | 2 +- .../ast_node/expression/typed_expression.rs | 4 ++-- .../typed_expression/method_application.rs | 2 +- sway-core/src/type_system/id.rs | 12 ++++++------ sway-core/src/type_system/priv_prelude.rs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index d54b40e8f74..cc7f79f75dd 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -573,15 +573,15 @@ impl TyFunctionSig { pub fn is_concrete(&self, engines: &Engines) -> bool { self.return_type - .is_concrete(engines, NumericIsNonConcrete::No) + .is_concrete(engines, TreatNumericAs::Concrete) && self .parameters .iter() - .all(|p| p.is_concrete(engines, NumericIsNonConcrete::No)) + .all(|p| p.is_concrete(engines, TreatNumericAs::Concrete)) && self .type_parameters .iter() - .all(|p| p.is_concrete(engines, NumericIsNonConcrete::No)) + .all(|p| p.is_concrete(engines, TreatNumericAs::Concrete)) } /// Returns a String representing the function. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 90855dd3a68..70c83d9b07a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -523,7 +523,7 @@ fn type_check_encode_append( }; // only supported types - if item_type.is_concrete(engines, NumericIsNonConcrete::Yes) { + if item_type.is_concrete(engines, TreatNumericAs::Abstract) { match &*engines.te().get(item_type) { TypeInfo::Boolean | TypeInfo::UnsignedInteger(IntegerBits::Eight) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index e9d2d69a0bc..0778bb1b19e 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -1866,13 +1866,13 @@ impl ty::TyExpression { .fold_while(None, |last, current| match last { None => Continue(Some(current.return_type)), Some(last) => { - if last.is_concrete(engines, NumericIsNonConcrete::Yes) { + if last.is_concrete(engines, TreatNumericAs::Abstract) { return Done(Some(last)); } if current .return_type - .is_concrete(engines, NumericIsNonConcrete::Yes) + .is_concrete(engines, TreatNumericAs::Abstract) { return Done(Some(current.return_type)); } 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 5058707838d..8cd4b8aac08 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 @@ -60,7 +60,7 @@ pub(crate) fn type_check_method_application( x.return_type .extract_inner_types(engines, IncludeSelf::Yes) .iter() - .any(|x| !x.is_concrete(engines, NumericIsNonConcrete::Yes)) + .any(|x| !x.is_concrete(engines, TreatNumericAs::Abstract)) }) .unwrap_or_default(); let needs_second_pass = has_errors || is_not_concrete; diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 59bceba05a3..273fd1f0366 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -27,9 +27,9 @@ pub enum IncludeSelf { No, } -pub enum NumericIsNonConcrete { - Yes, - No, +pub enum TreatNumericAs { + Abstract, + Concrete, } /// A identifier to uniquely refer to our type terms @@ -464,13 +464,13 @@ impl TypeId { pub(crate) fn is_concrete( &self, engines: &Engines, - numeric_non_concrete: NumericIsNonConcrete, + numeric_non_concrete: TreatNumericAs, ) -> bool { let nested_types = (*self).extract_nested_types(engines); !nested_types .into_iter() .any(|x| match numeric_non_concrete { - NumericIsNonConcrete::Yes => matches!( + TreatNumericAs::Abstract => matches!( x, TypeInfo::UnknownGeneric { .. } | TypeInfo::Custom { .. } @@ -479,7 +479,7 @@ impl TypeId { | TypeInfo::TypeParam(..) | TypeInfo::Numeric ), - NumericIsNonConcrete::No => matches!( + TreatNumericAs::Concrete => matches!( x, TypeInfo::UnknownGeneric { .. } | TypeInfo::Custom { .. } diff --git a/sway-core/src/type_system/priv_prelude.rs b/sway-core/src/type_system/priv_prelude.rs index 56020d3267a..c841399c56c 100644 --- a/sway-core/src/type_system/priv_prelude.rs +++ b/sway-core/src/type_system/priv_prelude.rs @@ -16,6 +16,6 @@ pub use super::{ type_parameter::TypeParameter, }, engine::TypeEngine, - id::{IncludeSelf, NumericIsNonConcrete, TypeId}, + id::{IncludeSelf, TreatNumericAs, TypeId}, info::{AbiEncodeSizeHint, AbiName, TypeInfo, TypeSourceInfo}, };