From ad856d2851ad13bcdf8af487b98f333460d2992a Mon Sep 17 00:00:00 2001 From: Daniel Frederico Lins Leite Date: Mon, 31 Jul 2023 15:22:01 +0100 Subject: [PATCH] Support for u256 constants (#4887) ## Description This PR is part of https://github.com/FuelLabs/sway/pull/4794. It implements the bare minimum to support `u256` constants across the stack. They are represented in sway in hex literals with `u256` suffixes. Hex without suffix will still be `b256`, for backward compatibility until we implement `u256` completely. Then we can decide what to do. There are multiple places in the code that will fail if the literal does not fit in u64. This will be fixed later in a specific PR for big u256. Some places have temporary `unwrap()`, that will be removed later. I can try to remove them now, if necessary. I am leaving all documentation updates for when everything is implemented. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: Anton Trunov --- forc-plugins/forc-client/src/util/encode.rs | 10 +++++- .../forc-doc/src/render/item/type_anchor.rs | 1 + sway-ast/src/literal.rs | 1 + sway-core/src/abi_generation/evm_abi.rs | 2 ++ sway-core/src/abi_generation/fuel_abi.rs | 1 + .../src/asm_generation/fuel/data_section.rs | 8 ++++- .../miden_vm/miden_vm_asm_builder.rs | 1 + sway-core/src/ir_generation/convert.rs | 5 ++- sway-core/src/language/literal.rs | 8 +++++ .../match_expression/analysis/pattern.rs | 1 + .../ast_node/expression/typed_expression.rs | 13 +++++++ .../semantic_analysis/node_dependencies.rs | 1 + .../to_parsed_lang/convert_parse_tree.rs | 12 +++++++ sway-core/src/type_system/info.rs | 3 ++ sway-error/src/lex_error.rs | 2 ++ sway-ir/src/constant.rs | 11 ++++-- sway-ir/src/irtype.rs | 22 +++++++----- sway-ir/src/parser.rs | 35 ++++++++++++++---- sway-ir/src/printer.rs | 14 ++++++++ sway-lsp/src/traverse/parsed_tree.rs | 1 + sway-lsp/src/utils/debug.rs | 1 + sway-parse/src/token.rs | 36 ++++++++++++++----- sway-types/src/integer_bits.rs | 2 ++ test/src/e2e_vm_tests/harness.rs | 5 +++ test/src/e2e_vm_tests/mod.rs | 2 +- .../test_programs/should_fail/u256/Forc.lock | 13 +++++++ .../test_programs/should_fail/u256/Forc.toml | 8 +++++ .../should_fail/u256/src/main.sw | 5 +++ .../test_programs/should_fail/u256/test.toml | 4 +++ .../should_pass/language/u256/Forc.lock | 13 +++++++ .../should_pass/language/u256/Forc.toml | 8 +++++ .../language/u256/json_abi_oracle.json | 25 +++++++++++++ .../should_pass/language/u256/src/main.sw | 5 +++ .../should_pass/language/u256/test.toml | 3 ++ test/src/ir_generation/mod.rs | 1 + test/src/ir_generation/tests/u256.sw | 11 ++++++ 36 files changed, 263 insertions(+), 31 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/u256/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/u256/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/u256/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/u256/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/u256/test.toml create mode 100644 test/src/ir_generation/tests/u256.sw diff --git a/forc-plugins/forc-client/src/util/encode.rs b/forc-plugins/forc-client/src/util/encode.rs index 35c90b5fc98..699a78a2abf 100644 --- a/forc-plugins/forc-client/src/util/encode.rs +++ b/forc-plugins/forc-client/src/util/encode.rs @@ -49,6 +49,7 @@ pub(crate) enum Type { U16, U32, U64, + U256, Bool, } @@ -86,6 +87,11 @@ impl Token { let u64_val = value.parse::()?; Ok(Token(fuels_core::types::Token::U64(u64_val))) } + //TODO u256 limited to u64 value + Type::U256 => { + let u64_val = value.parse::()?; + Ok(Token(fuels_core::types::Token::U256(u64_val.into()))) + } Type::Bool => { let bool_val = value.parse::()?; Ok(Token(fuels_core::types::Token::Bool(bool_val))) @@ -104,6 +110,7 @@ impl FromStr for Type { "u16" => Ok(Type::U16), "u32" => Ok(Type::U32), "u64" => Ok(Type::U64), + "u256" => Ok(Type::U256), "bool" => Ok(Type::Bool), other => anyhow::bail!("{other} type is not supported."), } @@ -142,7 +149,7 @@ mod tests { #[test] fn test_type_generation_success() { - let possible_type_list = ["()", "u8", "u16", "u32", "u64", "bool"]; + let possible_type_list = ["()", "u8", "u16", "u32", "u64", "u256", "bool"]; let types = possible_type_list .iter() .map(|type_str| Type::from_str(type_str)) @@ -155,6 +162,7 @@ mod tests { Type::U16, Type::U32, Type::U64, + Type::U256, Type::Bool, ]; assert_eq!(types, expected_types) diff --git a/forc-plugins/forc-doc/src/render/item/type_anchor.rs b/forc-plugins/forc-doc/src/render/item/type_anchor.rs index f57d67e2f7a..94ccf077c64 100644 --- a/forc-plugins/forc-doc/src/render/item/type_anchor.rs +++ b/forc-plugins/forc-doc/src/render/item/type_anchor.rs @@ -96,6 +96,7 @@ pub(crate) fn render_type_anchor( IntegerBits::Sixteen => "u16", IntegerBits::ThirtyTwo => "u32", IntegerBits::SixtyFour => "u64", + IntegerBits::V256 => "u256", }; Ok(box_html! { : uint; diff --git a/sway-ast/src/literal.rs b/sway-ast/src/literal.rs index d2e8f4e94ae..c04700e2107 100644 --- a/sway-ast/src/literal.rs +++ b/sway-ast/src/literal.rs @@ -25,6 +25,7 @@ pub enum LitIntType { U16, U32, U64, + U256, I8, I16, I32, diff --git a/sway-core/src/abi_generation/evm_abi.rs b/sway-core/src/abi_generation/evm_abi.rs index a326aba9380..fbf273aff51 100644 --- a/sway-core/src/abi_generation/evm_abi.rs +++ b/sway-core/src/abi_generation/evm_abi.rs @@ -89,6 +89,7 @@ pub fn abi_str(type_info: &TypeInfo, type_engine: &TypeEngine, decl_engine: &Dec IntegerBits::Sixteen => "uint16", IntegerBits::ThirtyTwo => "uint32", IntegerBits::SixtyFour => "uint64", + IntegerBits::V256 => "uint256", } .into(), Boolean => "bool".into(), @@ -149,6 +150,7 @@ pub fn abi_param_type( IntegerBits::Sixteen => ethabi::ParamType::Uint(16), IntegerBits::ThirtyTwo => ethabi::ParamType::Uint(32), IntegerBits::SixtyFour => ethabi::ParamType::Uint(64), + IntegerBits::V256 => ethabi::ParamType::Uint(256), }, Boolean => ethabi::ParamType::Bool, B256 => ethabi::ParamType::Uint(256), diff --git a/sway-core/src/abi_generation/fuel_abi.rs b/sway-core/src/abi_generation/fuel_abi.rs index 1d4439adc58..30d59b0a8dd 100644 --- a/sway-core/src/abi_generation/fuel_abi.rs +++ b/sway-core/src/abi_generation/fuel_abi.rs @@ -790,6 +790,7 @@ impl TypeInfo { IntegerBits::Sixteen => "u16", IntegerBits::ThirtyTwo => "u32", IntegerBits::SixtyFour => "u64", + IntegerBits::V256 => "u256", } .into(), Boolean => "bool".into(), diff --git a/sway-core/src/asm_generation/fuel/data_section.rs b/sway-core/src/asm_generation/fuel/data_section.rs index ff079dfa33f..8df1b89dc9d 100644 --- a/sway-core/src/asm_generation/fuel/data_section.rs +++ b/sway-core/src/asm_generation/fuel/data_section.rs @@ -100,7 +100,13 @@ impl Entry { ConstantValue::Undef | ConstantValue::Unit => Entry::new_word(0, size, name), ConstantValue::Bool(b) => Entry::new_word(u64::from(*b), size, name), ConstantValue::Uint(u) => Entry::new_word(*u, size, name), - + ConstantValue::U256(u) => { + // TODO u256 limited to u64 + let mut bytes = vec![0u8; 24]; + bytes.extend(&u.to_be_bytes()); + assert!(bytes.len() == 32); + Entry::new_byte_array(bytes.to_vec(), size, name) + } ConstantValue::B256(bs) => Entry::new_byte_array(bs.to_vec(), size, name), ConstantValue::String(bs) => Entry::new_byte_array(bs.clone(), size, name), diff --git a/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs b/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs index b4ae572e713..83bf6c8441e 100644 --- a/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs +++ b/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs @@ -653,6 +653,7 @@ impl<'ir, 'eng> MidenVMAsmBuilder<'ir, 'eng> { Unit => vec![DirectOp::push(MidenStackValue::Unit)], Bool(b) => vec![DirectOp::push(b)], Uint(x) => vec![DirectOp::push(x)], + U256(x) => todo!(), B256(_) => todo!(), String(_) => todo!(), Array(_) => todo!(), diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index 0f26a80a472..476973025fc 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -9,7 +9,7 @@ use super::types::{create_tagged_union_type, create_tuple_aggregate}; use sway_error::error::CompileError; use sway_ir::{Constant, Context, Type, Value}; -use sway_types::span::Span; +use sway_types::{integer_bits::IntegerBits, span::Span}; pub(super) fn convert_literal_to_value(context: &mut Context, ast_literal: &Literal) -> Value { match ast_literal { @@ -25,6 +25,7 @@ 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::U256(n) => Constant::get_uint(context, 256, *n), 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 +43,7 @@ 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::U256(n) => Constant::new_uint(context, 256, *n), 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), @@ -99,6 +101,7 @@ fn convert_resolved_type( Ok(match ast_type { // All integers are `u64`, see comment in convert_literal_to_value() above. + TypeInfo::UnsignedInteger(IntegerBits::V256) => Type::get_uint256(context), TypeInfo::UnsignedInteger(_) => Type::get_uint64(context), TypeInfo::Numeric => Type::get_uint64(context), TypeInfo::Boolean => Type::get_bool(context), diff --git a/sway-core/src/language/literal.rs b/sway-core/src/language/literal.rs index 83714384e14..8f198511195 100644 --- a/sway-core/src/language/literal.rs +++ b/sway-core/src/language/literal.rs @@ -15,6 +15,7 @@ pub enum Literal { U16(u16), U32(u32), U64(u64), + U256(u64), String(span::Span), Numeric(u64), Boolean(bool), @@ -41,6 +42,10 @@ impl Hash for Literal { state.write_u8(4); x.hash(state); } + U256(x) => { + state.write_u8(4); + x.hash(state); + } Numeric(x) => { state.write_u8(5); x.hash(state); @@ -68,6 +73,7 @@ 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::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 +90,7 @@ impl fmt::Display for Literal { Literal::U16(content) => content.to_string(), Literal::U32(content) => content.to_string(), Literal::U64(content) => content.to_string(), + Literal::U256(content) => content.to_string(), Literal::Numeric(content) => content.to_string(), Literal::String(content) => content.as_str().to_string(), Literal::Boolean(content) => content.to_string(), @@ -132,6 +139,7 @@ impl Literal { Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), Literal::U64(_) => TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + Literal::U256(_) => TypeInfo::UnsignedInteger(IntegerBits::V256), 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 36447d31b2c..e996c836dc7 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 @@ -173,6 +173,7 @@ 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::U256(x) => Pattern::U64(Range::from_single(x)), 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 1ebc9533dbb..c2be1c070fc 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 @@ -415,6 +415,7 @@ impl ty::TyExpression { Literal::U16(_) => TypeInfo::UnsignedInteger(IntegerBits::Sixteen), Literal::U32(_) => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), Literal::U64(_) => TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + Literal::U256(_) => TypeInfo::UnsignedInteger(IntegerBits::V256), Literal::Boolean(_) => TypeInfo::Boolean, Literal::B256(_) => TypeInfo::B256, }; @@ -1846,6 +1847,18 @@ impl ty::TyExpression { }), new_type, ), + //TODO u256 limited to u64 literals + IntegerBits::V256 => ( + num.to_string().parse().map(Literal::U256).map_err(|e| { + Literal::handle_parse_int_error( + engines, + e, + TypeInfo::UnsignedInteger(IntegerBits::V256), + span.clone(), + ) + }), + new_type, + ), }, TypeInfo::Numeric => ( num.to_string().parse().map(Literal::Numeric).map_err(|e| { diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 5197c261689..5f718814627 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -852,6 +852,7 @@ fn type_info_name(type_info: &TypeInfo) -> String { IntegerBits::Sixteen => "uint16", IntegerBits::ThirtyTwo => "uint32", IntegerBits::SixtyFour => "uint64", + IntegerBits::V256 => "uint256", }, TypeInfo::Boolean => "bool", TypeInfo::Custom { 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 00f96eab05e..bb5a13a4aab 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 @@ -1257,6 +1257,7 @@ pub(crate) fn type_name_to_type_info_opt(name: &Ident) -> Option { "u16" => Some(TypeInfo::UnsignedInteger(IntegerBits::Sixteen)), "u32" => Some(TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo)), "u64" => Some(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)), + "u256" => Some(TypeInfo::UnsignedInteger(IntegerBits::V256)), "bool" => Some(TypeInfo::Boolean), "unit" => Some(TypeInfo::Tuple(Vec::new())), "b256" => Some(TypeInfo::B256), @@ -2887,6 +2888,17 @@ fn literal_to_literal( }; Literal::U64(value) } + // TODO u256 are limited to u64 literals for the moment + LitIntType::U256 => { + let value = match u64::try_from(parsed) { + Ok(value) => value, + Err(..) => { + let error = ConvertParseTreeError::U64LiteralOutOfRange { span }; + return Err(handler.emit_err(error.into())); + } + }; + Literal::U256(value) + } LitIntType::I8 | LitIntType::I16 | LitIntType::I32 | LitIntType::I64 => { let error = ConvertParseTreeError::SignedIntegersNotSupported { span }; return Err(handler.emit_err(error.into())); diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index bc01cf005d4..711db7ab1d2 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -435,6 +435,7 @@ impl DisplayWithEngines for TypeInfo { IntegerBits::Sixteen => "u16", IntegerBits::ThirtyTwo => "u32", IntegerBits::SixtyFour => "u64", + IntegerBits::V256 => "u256", } .into(), Boolean => "bool".into(), @@ -500,6 +501,7 @@ impl DebugWithEngines for TypeInfo { IntegerBits::Sixteen => "u16", IntegerBits::ThirtyTwo => "u32", IntegerBits::SixtyFour => "u64", + IntegerBits::V256 => "u256", } .into(), Boolean => "bool".into(), @@ -617,6 +619,7 @@ impl TypeInfo { Sixteen => "u16", ThirtyTwo => "u32", SixtyFour => "u64", + V256 => "u256", } .into() } diff --git a/sway-error/src/lex_error.rs b/sway-error/src/lex_error.rs index 64902101c53..e43e45ead4e 100644 --- a/sway-error/src/lex_error.rs +++ b/sway-error/src/lex_error.rs @@ -58,6 +58,8 @@ pub enum LexErrorKind { UnicodeEscapeInvalidCharValue { span: Span }, #[error("invalid escape code")] InvalidEscapeCode { position: usize }, + #[error("invalid u256. Only hex literals are supported")] + U256NotInHex, } impl Spanned for LexError { diff --git a/sway-ir/src/constant.rs b/sway-ir/src/constant.rs index 7bb29383366..33955a808ed 100644 --- a/sway-ir/src/constant.rs +++ b/sway-ir/src/constant.rs @@ -16,6 +16,7 @@ pub enum ConstantValue { Unit, Bool(bool), Uint(u64), + U256(u64), // TODO u256 limited to u64 values B256([u8; 32]), String(Vec), Array(Vec), @@ -37,10 +38,13 @@ impl Constant { } } - pub fn new_uint(context: &mut Context, nbits: u8, n: u64) -> Self { + pub fn new_uint(context: &mut Context, nbits: u16, n: u64) -> Self { Constant { ty: Type::new_uint(context, nbits), - value: ConstantValue::Uint(n), + value: match nbits { + 256 => ConstantValue::U256(n), + _ => ConstantValue::Uint(n), + }, } } @@ -89,7 +93,7 @@ impl Constant { Value::new_constant(context, new_const) } - pub fn get_uint(context: &mut Context, nbits: u8, value: u64) -> Value { + pub fn get_uint(context: &mut Context, nbits: u16, value: u64) -> Value { let new_const = Constant::new_uint(context, nbits, value); Value::new_constant(context, new_const) } @@ -125,6 +129,7 @@ impl Constant { (ConstantValue::Unit, ConstantValue::Unit) => true, (ConstantValue::Bool(l0), ConstantValue::Bool(r0)) => l0 == r0, (ConstantValue::Uint(l0), ConstantValue::Uint(r0)) => l0 == r0, + (ConstantValue::U256(l0), ConstantValue::U256(r0)) => l0 == r0, (ConstantValue::B256(l0), ConstantValue::B256(r0)) => l0 == r0, (ConstantValue::String(l0), ConstantValue::String(r0)) => l0 == r0, (ConstantValue::Array(l0), ConstantValue::Array(r0)) diff --git a/sway-ir/src/irtype.rs b/sway-ir/src/irtype.rs index 3289003fd3a..0e88a4cc5a0 100644 --- a/sway-ir/src/irtype.rs +++ b/sway-ir/src/irtype.rs @@ -18,7 +18,7 @@ pub struct Type(pub generational_arena::Index); pub enum TypeContent { Unit, Bool, - Uint(u8), // XXX u256 is not unreasonable and can't fit in a `u8`. + Uint(u16), B256, String(u64), Array(Type, u64), @@ -51,6 +51,7 @@ impl Type { Self::get_or_create_unique_type(context, TypeContent::Bool); Self::get_or_create_unique_type(context, TypeContent::Uint(8)); Self::get_or_create_unique_type(context, TypeContent::Uint(64)); + Self::get_or_create_unique_type(context, TypeContent::Uint(256)); Self::get_or_create_unique_type(context, TypeContent::B256); Self::get_or_create_unique_type(context, TypeContent::Slice); } @@ -71,7 +72,7 @@ impl Type { } /// New unsigned integer type - pub fn new_uint(context: &mut Context, width: u8) -> Type { + pub fn new_uint(context: &mut Context, width: u16) -> Type { Self::get_or_create_unique_type(context, TypeContent::Uint(width)) } @@ -85,8 +86,13 @@ impl Type { Self::get_type(context, &TypeContent::Uint(64)).expect("create_basic_types not called") } + /// New u64 type + pub fn get_uint256(context: &Context) -> Type { + Self::get_type(context, &TypeContent::Uint(256)).expect("create_basic_types not called") + } + /// Get unsigned integer type - pub fn get_uint(context: &Context, width: u8) -> Option { + pub fn get_uint(context: &Context, width: u16) -> Option { Self::get_type(context, &TypeContent::Uint(width)) } @@ -213,7 +219,7 @@ impl Type { } /// Is unsigned integer type of specific width - pub fn is_uint_of(&self, context: &Context, width: u8) -> bool { + pub fn is_uint_of(&self, context: &Context, width: u16) -> bool { matches!(*self.get_content(context), TypeContent::Uint(width_) if width == width_) } @@ -267,7 +273,7 @@ impl Type { } /// Get width of an integer type. - pub fn get_uint_width(&self, context: &Context) -> Option { + pub fn get_uint_width(&self, context: &Context) -> Option { if let TypeContent::Uint(width) = self.get_content(context) { Some(*width) } else { @@ -382,10 +388,8 @@ impl Type { pub fn size_in_bytes(&self, context: &Context) -> u64 { match self.get_content(context) { - TypeContent::Unit - | TypeContent::Bool - | TypeContent::Uint(_) - | TypeContent::Pointer(_) => 8, + TypeContent::Unit | TypeContent::Bool | TypeContent::Pointer(_) => 8, + TypeContent::Uint(bits) => (*bits as u64) / 8, TypeContent::Slice => 16, TypeContent::B256 => 32, TypeContent::String(n) => super::size_bytes_round_up_to_word_alignment!(*n), diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index 17071441d47..4c17c15b640 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -435,7 +435,7 @@ mod ir_builder { / "true" _ { IrAstConstValue::Bool(true) } / "false" _ { IrAstConstValue::Bool(false) } / "0x" s:$(hex_digit()*<64>) _ { - IrAstConstValue::B256(string_to_hex::<32>(s)) + IrAstConstValue::Hex256(string_to_hex::<32>(s)) } / n:decimal() { IrAstConstValue::Number(n) } / string_const() @@ -494,6 +494,7 @@ mod ir_builder { = ("unit" / "()") _ { IrAstTy::Unit } / "bool" _ { IrAstTy::Bool } / "u64" _ { IrAstTy::U64 } + / "u256" _ { IrAstTy::U256 } / "b256" _ { IrAstTy::B256 } / "string" _ "<" _ sz:decimal() ">" _ { IrAstTy::String(sz) } / array_ty() @@ -723,7 +724,7 @@ mod ir_builder { Undef(IrAstTy), Unit, Bool(bool), - B256([u8; 32]), + Hex256([u8; 32]), Number(u64), String(Vec), Array(IrAstTy, Vec), @@ -745,12 +746,22 @@ mod ir_builder { } impl IrAstConstValue { - fn as_constant_value(&self, context: &mut Context) -> ConstantValue { + fn as_constant_value(&self, context: &mut Context, val_ty: IrAstTy) -> ConstantValue { match self { IrAstConstValue::Undef(_) => ConstantValue::Undef, IrAstConstValue::Unit => ConstantValue::Unit, IrAstConstValue::Bool(b) => ConstantValue::Bool(*b), - IrAstConstValue::B256(bs) => ConstantValue::B256(*bs), + IrAstConstValue::Hex256(bs) => { + match val_ty { + IrAstTy::U256 => { + // TODO u256 limited to u64 + let n = u64::from_be_bytes(bs[24..].try_into().unwrap()); + ConstantValue::U256(n) + } + IrAstTy::B256 => ConstantValue::B256(*bs), + _ => unreachable!("invalid type for hex number"), + } + } IrAstConstValue::Number(n) => ConstantValue::Uint(*n), IrAstConstValue::String(bs) => ConstantValue::String(bs.clone()), IrAstConstValue::Array(el_ty, els) => { @@ -773,7 +784,7 @@ mod ir_builder { fn as_constant(&self, context: &mut Context, val_ty: IrAstTy) -> Constant { Constant { ty: val_ty.to_ir_type(context), - value: self.as_constant_value(context), + value: self.as_constant_value(context, val_ty), } } @@ -782,7 +793,17 @@ mod ir_builder { IrAstConstValue::Undef(_) => unreachable!("Can't convert 'undef' to a value."), IrAstConstValue::Unit => Constant::get_unit(context), IrAstConstValue::Bool(b) => Constant::get_bool(context, *b), - IrAstConstValue::B256(bs) => Constant::get_b256(context, *bs), + IrAstConstValue::Hex256(bs) => { + match val_ty { + IrAstTy::U256 => { + // TODO u256 limited to u64 + let n = u64::from_be_bytes(bs[24..].try_into().unwrap()); + Constant::get_uint(context, 256, n) + } + IrAstTy::B256 => Constant::get_b256(context, *bs), + _ => unreachable!("invalid type for hex number"), + } + } IrAstConstValue::Number(n) => Constant::get_uint(context, 64, *n), IrAstConstValue::String(s) => Constant::get_string(context, s.clone()), IrAstConstValue::Array(..) => { @@ -802,6 +823,7 @@ mod ir_builder { Unit, Bool, U64, + U256, B256, String(u64), Array(Box, u64), @@ -816,6 +838,7 @@ mod ir_builder { IrAstTy::Unit => Type::get_unit(context), IrAstTy::Bool => Type::get_bool(context), IrAstTy::U64 => Type::get_uint64(context), + IrAstTy::U256 => Type::get_uint256(context), IrAstTy::B256 => Type::get_b256(context), IrAstTy::String(n) => Type::new_string(context, *n), IrAstTy::Array(el_ty, count) => { diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 13052c50dc8..6eaf5a3363e 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -929,6 +929,20 @@ impl Constant { ConstantValue::Unit => "unit ()".into(), ConstantValue::Bool(b) => format!("bool {}", if *b { "true" } else { "false" }), ConstantValue::Uint(v) => format!("{} {}", self.ty.as_string(context), v), + ConstantValue::U256(v) => { + // TODO u256 limited to u64 + let mut bytes = vec![0u8; 24]; + bytes.extend(&v.to_be_bytes()); + assert!(bytes.len() == 32); + format!( + "u256 0x{}", + bytes + .iter() + .map(|b| format!("{b:02x}")) + .collect::>() + .concat() + ) + } ConstantValue::B256(bs) => format!( "b256 0x{}", bs.iter() diff --git a/sway-lsp/src/traverse/parsed_tree.rs b/sway-lsp/src/traverse/parsed_tree.rs index f36128e997c..271f15de592 100644 --- a/sway-lsp/src/traverse/parsed_tree.rs +++ b/sway-lsp/src/traverse/parsed_tree.rs @@ -1092,6 +1092,7 @@ fn literal_to_symbol_kind(value: &Literal) -> SymbolKind { | Literal::U16(..) | Literal::U32(..) | Literal::U64(..) + | Literal::U256(..) | Literal::Numeric(..) => SymbolKind::NumericLiteral, Literal::String(..) => SymbolKind::StringLiteral, Literal::B256(..) => SymbolKind::ByteLiteral, diff --git a/sway-lsp/src/utils/debug.rs b/sway-lsp/src/utils/debug.rs index 7baf327709e..d4579947037 100644 --- a/sway-lsp/src/utils/debug.rs +++ b/sway-lsp/src/utils/debug.rs @@ -51,6 +51,7 @@ fn literal_to_string(literal: &Literal) -> String { Literal::U16(_) => "u16".into(), Literal::U32(_) => "u32".into(), Literal::U64(_) => "u64".into(), + Literal::U256(_) => "u256".into(), Literal::Numeric(_) => "u64".into(), Literal::String(len) => format!("str[{}]", len.as_str().len()), Literal::Boolean(_) => "bool".into(), diff --git a/sway-parse/src/token.rs b/sway-parse/src/token.rs index 846539ba2a5..168358561fc 100644 --- a/sway-parse/src/token.rs +++ b/sway-parse/src/token.rs @@ -645,7 +645,7 @@ fn lex_int_lit( let end_opt = parse_digits(&mut big_uint, l, 10); (big_uint, end_opt) }; - let (big_uint, end_opt) = if digit == 0 { + let (radix, (big_uint, end_opt)) = if digit == 0 { let prefixed_int_lit = |l: &mut Lexer<'_>, radix| { let _ = l.stream.next(); let d = l.stream.next(); @@ -669,21 +669,38 @@ fn lex_int_lit( }; match l.stream.peek() { - Some((_, 'x')) => prefixed_int_lit(l, 16)?, - Some((_, 'o')) => prefixed_int_lit(l, 8)?, - Some((_, 'b')) => prefixed_int_lit(l, 2)?, - Some((_, '_' | '0'..='9')) => decimal_int_lit(l, 0), - Some(&(next_index, _)) => (BigUint::from(0u32), Some(next_index)), - None => (BigUint::from(0u32), None), + Some((_, 'x')) => (16, prefixed_int_lit(l, 16)?), + Some((_, 'o')) => (8, prefixed_int_lit(l, 8)?), + Some((_, 'b')) => (2, prefixed_int_lit(l, 2)?), + Some((_, '_' | '0'..='9')) => (10, decimal_int_lit(l, 0)), + Some(&(next_index, _)) => (10, (BigUint::from(0u32), Some(next_index))), + None => (10, (BigUint::from(0u32), None)), } } else { - decimal_int_lit(l, digit) + (10, decimal_int_lit(l, digit)) }; + + let ty_opt = lex_int_ty_opt(l)?; + + // Only accepts u256 literals in hex form + if let Some((LitIntType::U256, span)) = &ty_opt { + if radix != 16 { + return Err(error( + l.handler, + LexError { + kind: LexErrorKind::U256NotInHex, + span: span.clone(), + }, + )); + } + } + let literal = Literal::Int(LitInt { span: span(l, index, end_opt.unwrap_or(l.src.len())), parsed: big_uint, - ty_opt: lex_int_ty_opt(l)?, + ty_opt, }); + Ok(Some(CommentedTokenTree::Tree(literal.into()))) } @@ -726,6 +743,7 @@ pub fn parse_int_suffix(suffix: &str) -> Option { "u16" => LitIntType::U16, "u32" => LitIntType::U32, "u64" => LitIntType::U64, + "u256" => LitIntType::U256, "i8" => LitIntType::I8, "i16" => LitIntType::I16, "i32" => LitIntType::I32, diff --git a/sway-types/src/integer_bits.rs b/sway-types/src/integer_bits.rs index 0b2f30dc7b9..0f405136fb2 100644 --- a/sway-types/src/integer_bits.rs +++ b/sway-types/src/integer_bits.rs @@ -6,6 +6,7 @@ pub enum IntegerBits { Sixteen, ThirtyTwo, SixtyFour, + V256, } impl fmt::Display for IntegerBits { @@ -16,6 +17,7 @@ impl fmt::Display for IntegerBits { Sixteen => "sixteen", ThirtyTwo => "thirty two", SixtyFour => "sixty four", + V256 => "256", }; write!(f, "{s}") } diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 999e9528dda..05999d95735 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -312,6 +312,11 @@ pub(crate) fn test_json_abi(file_name: &str, built_package: &BuiltPackage) -> Re let output_contents = fs::read_to_string(output_path).expect("Something went wrong reading the file."); if oracle_contents != output_contents { + println!("Mismatched ABI JSON output."); + println!( + "{}", + prettydiff::diff_lines(&oracle_contents, &output_contents) + ); bail!("Mismatched ABI JSON output."); } Ok(()) diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 80d5272934b..8076dc5f2b7 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -187,8 +187,8 @@ impl TestContext { harness::test_json_abi(&name, &compiled) }) .await; - result?; output.push_str(&out); + result?; } Ok(()) } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.lock new file mode 100644 index 00000000000..863f10c5516 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = 'core' +source = 'path+from-root-969A3D730A6A09C1' + +[[package]] +name = 'ops' +source = 'member' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-969A3D730A6A09C1' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.toml new file mode 100644 index 00000000000..47a2f0891f9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/u256/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "ops" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/u256/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/u256/src/main.sw new file mode 100644 index 00000000000..0d86c4435e7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/u256/src/main.sw @@ -0,0 +1,5 @@ +script; + +fn main() -> u256 { + 1u256 +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/u256/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/u256/test.toml new file mode 100644 index 00000000000..f68926fdef0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/u256/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: $()error +# check: $()invalid u256. Only hex literals are supported diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.lock new file mode 100644 index 00000000000..863f10c5516 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = 'core' +source = 'path+from-root-969A3D730A6A09C1' + +[[package]] +name = 'ops' +source = 'member' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-969A3D730A6A09C1' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.toml new file mode 100644 index 00000000000..068421d279a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "ops" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/json_abi_oracle.json new file mode 100644 index 00000000000..dfa4d35b95e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/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": "u256", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/src/main.sw new file mode 100644 index 00000000000..3f0955917c5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/src/main.sw @@ -0,0 +1,5 @@ +script; + +fn main() -> u256 { + 0x0000000000000000000000000000000000000000000000000000000000000001u256 +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/test.toml new file mode 100644 index 00000000000..5857e81b302 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u256/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = "0000000000000000000000000000000000000000000000000000000000000001" } +validate_abi = true diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index 49674ebf3db..81eaeba396e 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -443,6 +443,7 @@ pub(super) async fn run(filter_regex: Option<®ex::Regex>, verbose: bool) -> R .unwrap_or_else(|e| panic!("{}: {e}\n{ir_output}", path.display())); let parsed_ir_output = sway_ir::printer::to_string(&parsed_ir); if ir_output != parsed_ir_output { + println!("Deserialized IR:"); tracing::error!("{}", prettydiff::diff_lines(&ir_output, &parsed_ir_output)); panic!("{} failed IR (de)serialization.", path.display()); } diff --git a/test/src/ir_generation/tests/u256.sw b/test/src/ir_generation/tests/u256.sw new file mode 100644 index 00000000000..d742d59fcd4 --- /dev/null +++ b/test/src/ir_generation/tests/u256.sw @@ -0,0 +1,11 @@ +script; + +fn main() -> u256 { + 0x0000000000000000000000000000000000000000000000000000000000000001u256 +} + +// ::check-ir:: + +// check: entry fn main() -> u256 +// check: v0 = const u256 0x0000000000000000000000000000000000000000000000000000000000000001, +// check: ret u256 v0 \ No newline at end of file