diff --git a/Cargo.lock b/Cargo.lock index 4662816f85d..9246fdadadf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5527,6 +5527,7 @@ dependencies = [ "itertools", "lazy_static", "miden-core", + "num-bigint", "pest", "pest_derive", "petgraph", diff --git a/sway-ast/src/literal.rs b/sway-ast/src/literal.rs index d2e8f4e94ae..79a27e00295 100644 --- a/sway-ast/src/literal.rs +++ b/sway-ast/src/literal.rs @@ -25,10 +25,14 @@ pub enum LitIntType { U16, U32, U64, + U128, + U256, I8, I16, I32, I64, + I128, + I256, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash, Serialize)] diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index 66f3da2a850..c43feab0899 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -27,6 +27,7 @@ im = "15.0" itertools = "0.10" lazy_static = "1.4" miden-core = "0.3.0" +num-bigint = { version = "0.4.3", features = ["serde"] } pest = "2.1.3" pest_derive = "2.1" petgraph = "0.6" diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index 0f26a80a472..ff981ae8e77 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -25,6 +25,8 @@ pub(super) fn convert_literal_to_value(context: &mut Context, ast_literal: &Lite Literal::U16(n) => Constant::get_uint(context, 64, *n as u64), Literal::U32(n) => Constant::get_uint(context, 64, *n as u64), Literal::U64(n) => Constant::get_uint(context, 64, *n), + Literal::U128(_) => todo!(), + Literal::U256(_) => todo!(), Literal::Numeric(n) => Constant::get_uint(context, 64, *n), Literal::String(s) => Constant::get_string(context, s.as_str().as_bytes().to_vec()), Literal::Boolean(b) => Constant::get_bool(context, *b), @@ -42,6 +44,8 @@ pub(super) fn convert_literal_to_constant( Literal::U16(n) => Constant::new_uint(context, 64, *n as u64), Literal::U32(n) => Constant::new_uint(context, 64, *n as u64), Literal::U64(n) => Constant::new_uint(context, 64, *n), + Literal::U128(_) => todo!(), + Literal::U256(_) => todo!(), Literal::Numeric(n) => Constant::new_uint(context, 64, *n), Literal::String(s) => Constant::new_string(context, s.as_str().as_bytes().to_vec()), Literal::Boolean(b) => Constant::new_bool(context, *b), diff --git a/sway-core/src/language/literal.rs b/sway-core/src/language/literal.rs index 83714384e14..71101fb19fc 100644 --- a/sway-core/src/language/literal.rs +++ b/sway-core/src/language/literal.rs @@ -1,5 +1,6 @@ use crate::{type_system::*, Engines}; +use num_bigint::BigUint; use sway_error::error::CompileError; use sway_types::{integer_bits::IntegerBits, span}; @@ -15,6 +16,8 @@ pub enum Literal { U16(u16), U32(u32), U64(u64), + U128(u128), + U256(BigUint), String(span::Span), Numeric(u64), Boolean(bool), @@ -41,6 +44,8 @@ impl Hash for Literal { state.write_u8(4); x.hash(state); } + U128(_) => todo!(), + U256(_) => todo!(), Numeric(x) => { state.write_u8(5); x.hash(state); @@ -68,6 +73,8 @@ impl PartialEq for Literal { (Self::U16(l0), Self::U16(r0)) => l0 == r0, (Self::U32(l0), Self::U32(r0)) => l0 == r0, (Self::U64(l0), Self::U64(r0)) => l0 == r0, + (Self::U128(l0), Self::U128(r0)) => l0 == r0, + (Self::U256(l0), Self::U256(r0)) => l0 == r0, (Self::String(l0), Self::String(r0)) => *l0.as_str() == *r0.as_str(), (Self::Numeric(l0), Self::Numeric(r0)) => l0 == r0, (Self::Boolean(l0), Self::Boolean(r0)) => l0 == r0, @@ -84,6 +91,8 @@ impl fmt::Display for Literal { Literal::U16(content) => content.to_string(), Literal::U32(content) => content.to_string(), Literal::U64(content) => content.to_string(), + Literal::U128(_) => todo!(), + Literal::U256(_) => todo!(), Literal::Numeric(content) => content.to_string(), Literal::String(content) => content.as_str().to_string(), Literal::Boolean(content) => content.to_string(), @@ -132,6 +141,8 @@ impl Literal { Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), Literal::U64(_) => TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + Literal::U128(_) => todo!(), + Literal::U256(_) => todo!(), Literal::Boolean(_) => TypeInfo::Boolean, Literal::B256(_) => TypeInfo::B256, } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs index f36ce58c6dd..3634f8a93ee 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs @@ -194,6 +194,8 @@ impl Pattern { Literal::U16(x) => Pattern::U16(Range::from_single(x)), Literal::U32(x) => Pattern::U32(Range::from_single(x)), Literal::U64(x) => Pattern::U64(Range::from_single(x)), + Literal::U128(_) => todo!(), + Literal::U256(_) => todo!(), Literal::B256(x) => Pattern::B256(x), Literal::Boolean(b) => Pattern::Boolean(b), Literal::Numeric(x) => Pattern::Numeric(Range::from_single(x)), 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 8c50e645543..79ac7bec671 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 @@ -393,6 +393,8 @@ impl ty::TyExpression { Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), Literal::U64(_) => TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + Literal::U128(_) => todo!(), + Literal::U256(_) => todo!(), Literal::Boolean(_) => TypeInfo::Boolean, Literal::B256(_) => TypeInfo::B256, }; diff --git a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs index 4d97fc04d5c..420db65f71a 100644 --- a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs +++ b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs @@ -6,6 +6,7 @@ use crate::{ }; use itertools::Itertools; +use num_bigint::BigUint; use sway_ast::{ attribute::Annotated, expr::{LoopControlFlow, ReassignmentOp, ReassignmentOpVariant}, @@ -1773,7 +1774,7 @@ fn expr_to_expression( }, Expr::Path(path_expr) => path_expr_to_expression(context, handler, engines, path_expr)?, Expr::Literal(literal) => Expression { - kind: ExpressionKind::Literal(literal_to_literal(context, handler, literal)?), + kind: ExpressionKind::Literal(literal_to_literal(handler, literal)?), span, }, Expr::AbiCast { args, .. } => { @@ -2774,7 +2775,6 @@ fn path_root_opt_to_bool_and_qualified_path_root( } fn literal_to_literal( - _context: &mut Context, handler: &Handler, literal: sway_ast::Literal, ) -> Result { @@ -2887,10 +2887,38 @@ fn literal_to_literal( }; Literal::U64(value) } + LitIntType::U128 => { + let value = match u128::try_from(parsed) { + Ok(value) => value, + Err(..) => { + let error = ConvertParseTreeError::U128LiteralOutOfRange { span }; + return Err(handler.emit_err(error.into())); + } + }; + Literal::U128(value) + } + LitIntType::U256 => { + let mut bytes = parsed.to_bytes_le(); + + // Normalize removing zeros from the most signicants positions + if let Some(&0) = bytes.last() { + let len = bytes.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1); + bytes.truncate(len); + } + + if bytes.len() <= 32 { + Literal::U256(BigUint::from_bytes_le(&bytes)) + } else { + let error = ConvertParseTreeError::U256LiteralOutOfRange { span }; + return Err(handler.emit_err(error.into())); + } + } LitIntType::I8 | LitIntType::I16 | LitIntType::I32 | LitIntType::I64 => { let error = ConvertParseTreeError::SignedIntegersNotSupported { span }; return Err(handler.emit_err(error.into())); } + LitIntType::I128 => todo!(), + LitIntType::I256 => todo!(), }, } } @@ -3551,7 +3579,7 @@ fn pattern_to_scrutinee( } Pattern::AmbiguousSingleIdent(ident) => Scrutinee::AmbiguousSingleIdent(ident), Pattern::Literal(literal) => Scrutinee::Literal { - value: literal_to_literal(context, handler, literal)?, + value: literal_to_literal(handler, literal)?, span, }, Pattern::Constant(path_expr) => { @@ -4204,3 +4232,53 @@ pub fn cfg_eval( } Ok(true) } + +#[cfg(test)] +mod tests { + use super::*; + use num_bigint::{BigUint, ToBigUint}; + + fn assert(ty: LitIntType, value: impl ToBigUint, expected: Literal) { + let actual = literal_to_literal( + &Handler::default(), + sway_ast::Literal::Int(LitInt { + span: Span::dummy(), + parsed: value.to_biguint().unwrap(), + ty_opt: Some((ty, Span::dummy())), + }), + ); + + if !matches!(&actual, Ok(l) if *l == expected) { + dbg!(&actual, &expected); + assert!(matches!(actual, Ok(l) if l == expected)); + } + } + + #[test] + fn ok_literal_to_literal() { + assert(LitIntType::U8, 0u8, Literal::U8(0)); + assert(LitIntType::U8, u8::MAX, Literal::U8(u8::MAX)); + + assert(LitIntType::U16, 0u16, Literal::U16(0)); + assert(LitIntType::U16, u16::MAX, Literal::U16(u16::MAX)); + + assert(LitIntType::U32, 0u32, Literal::U32(0)); + assert(LitIntType::U32, u32::MAX, Literal::U32(u32::MAX)); + + assert(LitIntType::U64, 0u64, Literal::U64(0)); + assert(LitIntType::U64, u64::MAX, Literal::U64(u64::MAX)); + + assert(LitIntType::U128, 0u128, Literal::U128(0)); + assert(LitIntType::U128, u128::MAX, Literal::U128(u128::MAX)); + + let zero = BigUint::from_bytes_le(&[0u8; 32]); + let max_value = BigUint::from_bytes_le(&[255u8; 32]); + assert(LitIntType::U256, 0, Literal::U256(zero)); + assert( + LitIntType::U256, + //115 quattuorvigintillion, if you are curious + max_value.clone(), + Literal::U256(max_value), + ); + } +} diff --git a/sway-error/src/convert_parse_tree_error.rs b/sway-error/src/convert_parse_tree_error.rs index 19d24d82bde..3a53c5dc524 100644 --- a/sway-error/src/convert_parse_tree_error.rs +++ b/sway-error/src/convert_parse_tree_error.rs @@ -41,6 +41,10 @@ pub enum ConvertParseTreeError { U32LiteralOutOfRange { span: Span }, #[error("u64 literal out of range")] U64LiteralOutOfRange { span: Span }, + #[error("u128 literal out of range")] + U128LiteralOutOfRange { span: Span }, + #[error("u256 literal out of range")] + U256LiteralOutOfRange { span: Span }, #[error("signed integers are not supported")] SignedIntegersNotSupported { span: Span }, #[error("ref variables are not supported")] @@ -147,6 +151,8 @@ impl Spanned for ConvertParseTreeError { ConvertParseTreeError::U16LiteralOutOfRange { span } => span.clone(), ConvertParseTreeError::U32LiteralOutOfRange { span } => span.clone(), ConvertParseTreeError::U64LiteralOutOfRange { span } => span.clone(), + ConvertParseTreeError::U128LiteralOutOfRange { span } => span.clone(), + ConvertParseTreeError::U256LiteralOutOfRange { span } => span.clone(), ConvertParseTreeError::SignedIntegersNotSupported { span } => span.clone(), ConvertParseTreeError::RefVariablesNotSupported { span } => span.clone(), ConvertParseTreeError::LiteralPatternsNotSupportedHere { span } => span.clone(), diff --git a/sway-parse/src/token.rs b/sway-parse/src/token.rs index 846539ba2a5..1f792b53562 100644 --- a/sway-parse/src/token.rs +++ b/sway-parse/src/token.rs @@ -703,6 +703,7 @@ fn lex_int_ty_opt(l: &mut Lexer<'_>) -> Result> { None => break l.src.len(), } }; + // Parse the suffix to a known one, or if unknown, recover by throwing it away. let ty = match parse_int_suffix(&suffix) { Some(s) => s, @@ -726,10 +727,14 @@ pub fn parse_int_suffix(suffix: &str) -> Option { "u16" => LitIntType::U16, "u32" => LitIntType::U32, "u64" => LitIntType::U64, + "u128" => LitIntType::U128, + "u256" => LitIntType::U256, "i8" => LitIntType::I8, "i16" => LitIntType::I16, "i32" => LitIntType::I32, "i64" => LitIntType::I64, + "i128" => LitIntType::I128, + "i256" => LitIntType::I256, _ => return None, }) } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/Forc.lock new file mode 100644 index 00000000000..ea70d258450 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = 'b256_ops' +source = 'member' +dependencies = ['std'] + +[[package]] +name = 'core' +source = 'path+from-root-963ABD293AAA1DE4' + +[[package]] +name = 'std' +source = 'path+from-root-963ABD293AAA1DE4' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/Forc.toml new file mode 100644 index 00000000000..6c2998759cf --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "b256_ops" +entry = "main.sw" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/json_abi_oracle.json new file mode 100644 index 00000000000..ad50b55d54c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/json_abi_oracle.json @@ -0,0 +1,25 @@ +{ + "configurables": [], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": null, + "type": "u64", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/src/main.sw new file mode 100644 index 00000000000..b056eb54ac1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/src/main.sw @@ -0,0 +1,6 @@ +script; + +fn main() -> u64 { + let aaaa = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau256; + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/test.toml new file mode 100644 index 00000000000..2297098e50e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256_ops/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 100 } +validate_abi = true