Skip to content

Commit

Permalink
Support for u256 constants (FuelLabs#4887)
Browse files Browse the repository at this point in the history
## Description

This PR is part of FuelLabs#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 <[email protected]>
  • Loading branch information
xunilrj and anton-trunov authored Jul 31, 2023
1 parent f5f8566 commit ad856d2
Show file tree
Hide file tree
Showing 36 changed files with 263 additions and 31 deletions.
10 changes: 9 additions & 1 deletion forc-plugins/forc-client/src/util/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub(crate) enum Type {
U16,
U32,
U64,
U256,
Bool,
}

Expand Down Expand Up @@ -86,6 +87,11 @@ impl Token {
let u64_val = value.parse::<u64>()?;
Ok(Token(fuels_core::types::Token::U64(u64_val)))
}
//TODO u256 limited to u64 value
Type::U256 => {
let u64_val = value.parse::<u64>()?;
Ok(Token(fuels_core::types::Token::U256(u64_val.into())))
}
Type::Bool => {
let bool_val = value.parse::<bool>()?;
Ok(Token(fuels_core::types::Token::Bool(bool_val)))
Expand All @@ -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."),
}
Expand Down Expand Up @@ -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))
Expand All @@ -155,6 +162,7 @@ mod tests {
Type::U16,
Type::U32,
Type::U64,
Type::U256,
Type::Bool,
];
assert_eq!(types, expected_types)
Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-doc/src/render/item/type_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions sway-ast/src/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub enum LitIntType {
U16,
U32,
U64,
U256,
I8,
I16,
I32,
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/abi_generation/evm_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/abi_generation/fuel_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ impl TypeInfo {
IntegerBits::Sixteen => "u16",
IntegerBits::ThirtyTwo => "u32",
IntegerBits::SixtyFour => "u64",
IntegerBits::V256 => "u256",
}
.into(),
Boolean => "bool".into(),
Expand Down
8 changes: 7 additions & 1 deletion sway-core/src/asm_generation/fuel/data_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!(),
Expand Down
5 changes: 4 additions & 1 deletion sway-core/src/ir_generation/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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),
Expand Down
8 changes: 8 additions & 0 deletions sway-core/src/language/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum Literal {
U16(u16),
U32(u32),
U64(u64),
U256(u64),
String(span::Span),
Numeric(u64),
Boolean(bool),
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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(),
Expand Down Expand Up @@ -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,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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| {
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/semantic_analysis/node_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,7 @@ pub(crate) fn type_name_to_type_info_opt(name: &Ident) -> Option<TypeInfo> {
"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),
Expand Down Expand Up @@ -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()));
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/type_system/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ impl DisplayWithEngines for TypeInfo {
IntegerBits::Sixteen => "u16",
IntegerBits::ThirtyTwo => "u32",
IntegerBits::SixtyFour => "u64",
IntegerBits::V256 => "u256",
}
.into(),
Boolean => "bool".into(),
Expand Down Expand Up @@ -500,6 +501,7 @@ impl DebugWithEngines for TypeInfo {
IntegerBits::Sixteen => "u16",
IntegerBits::ThirtyTwo => "u32",
IntegerBits::SixtyFour => "u64",
IntegerBits::V256 => "u256",
}
.into(),
Boolean => "bool".into(),
Expand Down Expand Up @@ -617,6 +619,7 @@ impl TypeInfo {
Sixteen => "u16",
ThirtyTwo => "u32",
SixtyFour => "u64",
V256 => "u256",
}
.into()
}
Expand Down
2 changes: 2 additions & 0 deletions sway-error/src/lex_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 8 additions & 3 deletions sway-ir/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>),
Array(Vec<Constant>),
Expand All @@ -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),
},
}
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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))
Expand Down
22 changes: 13 additions & 9 deletions sway-ir/src/irtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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);
}
Expand All @@ -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))
}

Expand All @@ -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<Type> {
pub fn get_uint(context: &Context, width: u16) -> Option<Type> {
Self::get_type(context, &TypeContent::Uint(width))
}

Expand Down Expand Up @@ -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_)
}

Expand Down Expand Up @@ -267,7 +273,7 @@ impl Type {
}

/// Get width of an integer type.
pub fn get_uint_width(&self, context: &Context) -> Option<u8> {
pub fn get_uint_width(&self, context: &Context) -> Option<u16> {
if let TypeContent::Uint(width) = self.get_content(context) {
Some(*width)
} else {
Expand Down Expand Up @@ -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),
Expand Down
Loading

0 comments on commit ad856d2

Please sign in to comment.