From ac1d772307dca3e2657d06c9f5aeeef1716adadd Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 13 May 2022 10:41:26 -0700 Subject: [PATCH] Do not overwrite preceeding numeric type info on cast (#1540) * Do not overwrite preceeding numeric type info on cast * update toml and lockfile --- sway-core/src/type_engine/engine.rs | 14 +- test/src/e2e_vm_tests/mod.rs | 4 + .../test_contracts/issue_1512_repro/Forc.lock | 7 + .../test_contracts/issue_1512_repro/Forc.toml | 9 ++ .../issue_1512_repro/json_abi_oracle.json | 25 ++++ .../issue_1512_repro/src/main.sw | 127 ++++++++++++++++++ 6 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw diff --git a/sway-core/src/type_engine/engine.rs b/sway-core/src/type_engine/engine.rs index 1e784d21565..7644f353ac6 100644 --- a/sway-core/src/type_engine/engine.rs +++ b/sway-core/src/type_engine/engine.rs @@ -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. @@ -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![]) } diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 6cae0038105..c9b075ad75e 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -368,6 +368,10 @@ pub fn run(filter_regex: Option) { "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 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.lock new file mode 100644 index 00000000000..6964baab7bb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.lock @@ -0,0 +1,7 @@ +[[package]] +name = 'core' +dependencies = [] + +[[package]] +name = 'issue_1512_repro' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.toml new file mode 100644 index 00000000000..e4b5416944c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +implicit-std = false +license = "Apache-2.0" +name = "issue_1512_repro" + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.json new file mode 100644 index 00000000000..97104c52676 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.json @@ -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" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw new file mode 100644 index 00000000000..be91cc34561 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw @@ -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, + } +}