Skip to content

Commit

Permalink
Do not overwrite preceeding numeric type info on cast (FuelLabs#1540)
Browse files Browse the repository at this point in the history
* Do not overwrite preceeding numeric type info on cast

* update toml and lockfile
  • Loading branch information
sezna authored May 13, 2022
1 parent d63a9f0 commit ac1d772
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 7 deletions.
14 changes: 7 additions & 7 deletions sway-core/src/type_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ impl Engine {
(warnings, errors)
}

(
ref received_info @ UnsignedInteger(received_width),
ref expected_info @ UnsignedInteger(expected_width),
) => {
(UnsignedInteger(received_width), UnsignedInteger(expected_width)) => {
// E.g., in a variable declaration `let a: u32 = 10u64` the 'expected' type will be
// the annotation `u32`, and the 'received' type is 'self' of the initialiser, or
// `u64`. So we're casting received TO expected.
Expand All @@ -128,9 +125,12 @@ impl Engine {
}
};

// Cast the expected type to the received type.
self.slab
.replace(received, received_info, expected_info.clone());
// we don't want to do a slab replacement here, because
// we don't want to overwrite the original numeric type with the new one.
// This isn't actually inferencing the original type to the new numeric type.
// We just want to say "up until this point, this was a u32 (eg) and now it is a
// u64 (eg)". If we were to do a slab replace here, we'd be saying "this was always a
// u64 (eg)".
(warnings, vec![])
}

Expand Down
4 changes: 4 additions & 0 deletions test/src/e2e_vm_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ pub fn run(filter_regex: Option<regex::Regex>) {
"should_pass/test_contracts/nested_struct_args_contract",
ProgramState::Revert(0),
),
(
"should_pass/test_contracts/issue_1512_repro",
ProgramState::Revert(0),
),
];

number_of_tests_run += positive_project_names_with_abi
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[[package]]
name = 'core'
dependencies = []

[[package]]
name = 'issue_1512_repro'
dependencies = ['core']
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
implicit-std = false
license = "Apache-2.0"
name = "issue_1512_repro"

[dependencies]
core = { path = "../../../../../../../sway-lib-core" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"inputs": [
{
"components": null,
"name": "a",
"type": "u64"
},
{
"components": null,
"name": "b",
"type": "u64"
}
],
"name": "multiply_u64",
"outputs": [
{
"components": null,
"name": "",
"type": "(u64, u64)"
}
],
"type": "function"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
contract;

abi U128Contract {
fn multiply_u64(a: u64, b: u64) -> (u64, u64);
}

impl U128Contract for Contract {
fn multiply_u64(a: u64, b: u64) -> (u64, u64) {
let result_u128: U128 = mul64(a, b);
(result_u128.upper, result_u128.lower)
}
}

// U128 represented as two components of a base-(2**64) number : (upper, lower) , where value = (2**64)^upper + lower
pub struct U128 {
upper: u64,
lower: u64,
}

pub trait From {
fn from(h: u64, l: u64) -> Self;
} {
}

impl core::ops::Eq for U128 {
fn eq(self, other: Self) -> bool {
self.lower == other.lower && self.upper == other.upper
}
}

/// Function for creating U128 from its u64 components
impl From for U128 {
fn from(h: u64, l: u64) -> U128 {
U128 {
upper: h,
lower: l,
}
}
}

/// Methods on the U128 type
impl U128 {
/// Initializes a new, zeroed U128.
fn new() -> U128 {
U128 {
upper: 0,
lower: 0,
}
}

fn add(self, other: U128) -> U128 {
let lower = self.lower + other.lower;
let mut upper = self.upper + other.upper;

// If overflow has occurred in the lower component addition, carry
if lower <= self.lower {
upper = upper + 1;
};

// If overflow has occurred in the upper component addition, panic
// assert(upper >= self.upper);

U128 {
upper: upper,
lower: lower,
}
}

fn sub(self, other: U128) -> U128 {
let mut upper = self.upper - other.upper;
let mut lower = 0;

// If necessary, borrow and carry for lower subtraction
if self.lower < other.lower {
let max = 18446744073709551615;
let lower = max - (other.lower - self.lower - 1);
upper = upper - 1;
} else {
let lower = self.lower - other.lower;
};

// If upper component has underflowed, panic
// assert(upper < self.upper);

U128 {
upper: upper,
lower: lower,
}
}

// TO DO : mul, div, inequalities, etc.
}

// Downcast from u64 to u32, losing precision
fn u64_to_u32(a: u64) -> u32 {
let result: u32 = a;
result
}

// Multiply two u64 values, producing a U128
pub fn mul64(a: u64, b: u64) -> U128 {
// Split a and b into 32-bit lo and hi components
let a_lo = u64_to_u32(a);
let a_hi = u64_to_u32(a >> 32);
let b_lo = u64_to_u32(b);
let b_hi = u64_to_u32(b >> 32);

// Calculate low, high, and mid multiplications
let ab_hi: u64 = a_hi * b_hi;
let ab_mid: u64 = a_hi * b_lo;
let ba_mid: u64 = b_hi * a_lo;
let ab_lo: u64 = a_lo * b_lo;

// Calculate the carry bit
let carry_bit: u64 = (u64_to_u32(ab_mid) + u64_to_u32(ba_mid) + (ab_lo >> 32)) >> 32;

// low result is what's left after the (overflowing) multiplication of a and b
let result_lo: u64 = a * b;

// High result
let result_hi: u64 = ab_hi + (ab_mid >> 32) + (ba_mid >> 32) + carry_bit;

U128 {
upper: result_hi,
lower: result_lo,
}
}

0 comments on commit ac1d772

Please sign in to comment.