Skip to content

Releases: ethereum/fe

v0.26.0

03 Nov 10:53
Compare
Choose a tag to compare

0.26.0 "Zircon" (2023-11-03)

Features

  • Give option to produce runtime bytecode as compilation artifact

    Previously, the compiler could only produce the bytecode that is used
    for the deployment of the contract. Now it can also produce the runtime
    bytecode which is the bytecode that is saved to storage.

    Being able to obtain the runtime bytecode is useful for contract
    verification.

    To obtain the runtime bytecode use the runtime-bytecode option
    of the --emit flag (multiple options allowed).

    Example Output:

    • mycontract.bin (bytecode for deployment)
    • mycontract.runtime.bin (runtime bytecode) (#947)
  • New verify command to verify onchain contracts against local source code.

    People need to be able to verify that a deployed contract matches the source code
    that the author claims was used to deploy it. Previously, there was no simple
    way to achieve this.

    These are the steps to verify a contract with the verify command:

    1. Obtain the project's source code locally.
    2. Ensure it is the same source code that was used to deploy the contract. (e.g. check out a specific tag)
    3. From the project directory run fe verify <contract-address> <json-rpc-url>

    Example:

    $ fe verify 0xf0adbb9ed4135d1509ad039505bada942d18755f https://example-eth-mainnet-rpc.com
    It's a match!✨
    
    Onchain contract:
    Address: 0xf0adbb9ed4135d1509ad039505bada942d18755f
    Bytecode: 0x60008..76b90
    
    Local contract:
    Contract name: SimpleDAO
    Source file: /home/work/ef/simple_dao/fe_contracts/simpledao/src/main.fe
    Bytecode: 0x60008..76b90
    
    Hint: Run with --verbose to see the contract's source code.

    (#948)

    Improved Documentation

  • Added a new page on EVM precompiles (#944)

v0.25.0

26 Oct 10:36
Compare
Choose a tag to compare

0.25.0 "Yoshiokaite" (2023-10-26)

Features

  • Use the project root as default path for fe test

    Just run fe test from any directory of the project. (#913)

  • Completed std::buf::MemoryBuffer refactor. (#917)

  • Allow filtering tests to run via fe test --filter <some-filter

    E.g. Running fe test --filter foo will run all tests that contain foo in their name. (#919)

  • Logs for successfully ran tests can be printed with the --logs parameter.

    example:

    // test_log.fe
    
    use std::evm::log0
    use std::buf::MemoryBuffer
    
    struct MyEvent {
      pub foo: u256
      pub baz: bool
      pub bar: u256
    }
    
    #test
    fn test_log(mut ctx: Context) {
      ctx.emit(MyEvent(foo: 42, baz: false, bar: 26))
      unsafe { log0(buf: MemoryBuffer::new(len: 42)) }
    }
    
    
    $ fe test --logs test_log.fe
    executing 1 test in test_log:
      test_log ... passed
    
    test_log produced the following logs:
      MyEvent emitted by 0x0000…002a with the following parameters [foo: 2a, baz: false, bar: 1a]
      Log { address: 0x000000000000000000000000000000000000002a, topics: [], data: b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\0\0\0\0\0\0\0" }
    
    
    1 test passed; 0 tests failed; 1 test executed
    

    Note: Logs are not collected for failing tests. (#933)

  • Adds 'functions' section to docs with information on self and Context. (#937)

Bugfixes

  • Yul codegen was failing to include string literals used in test assertions. This resulted in a compiler error.

    Example:

    #test
    fn foo() {
        assert false, "oops"
    }
    

    The example code above was failing to compile, but now it compiles and executes as expected. (#926)

Improved Documentation

  • Added a new tutorial: Open Auction (#930)

v0.24.0

10 Aug 19:04
Compare
Choose a tag to compare

0.24.0 "Xenotime" (2023-08-10)

Features

  • Added support for project manifests and project dependencies.

    Example:

    my_project
    ├── fe.toml
    └── src
        └── main.fe
    
    # fe.toml
    name = "my_project"
    version = "1.0"
    
    [dependencies]
    my_lib = { path = "../path/to/my_lib", version = "1.0" }
    my_other_lib = "../path/to/my_other_lib"
    

    Note: The current implementation supports circular dependencies. (#908)

Performance improvements

  • MemoryBuffer now allocates an extra 31 bytes. This removes the need for runtime checks and bitshifting needed to ensure safe writing to a MemoryBuffer's region. (#898)

Improved Documentation

  • Link to vs-code extension in Quickstart Guide (#910)

v0.23.0

01 Jun 15:27
Compare
Choose a tag to compare

0.23.0 "Wiluite" (2023-06-01)

Features

  • Fixed an issue where generic parameters that were mut could not be satisfied at callsite.

    For instance, the following code would previously cause a compile error but now works as expected:

    struct Runner {
      pub fn run<T: Computable>(self, mut _ val: T) -> u256 {
        return val.compute(val: 1000)
      }
    }
    
    contract Example {
      pub fn run_test(self) {
        let runner: Runner = Runner();
        let mut mac: Mac = Mac();
    
        assert runner.run(mac) == 1001
      }
    }

    (#865)

  • The ctx parameter can now be passed into test functions.

    example:

    #test
    fn my_test(ctx: Context) {
        assert ctx.block_number() == 0
    }
    

    (#880)

  • The following has been added to the standard library:

    Memory buffer abstraction

    example:

    use std::buf::{MemoryBuffer, MemoryBufferReader, MemoryBufferWriter}
    use std::traits::Max
    
    #test
    fn test_buf_rw() {
        let mut buf: MemoryBuffer = MemoryBuffer::new(len: 161) 
        let mut writer: MemoryBufferWriter = buf.writer()
        let mut reader: MemoryBufferReader = buf.reader()
    
        writer.write(value: 42)
        writer.write(value: 42)
        writer.write(value: 26)
        writer.write(value: u8(26))
        writer.write(value: u256::max())
        writer.write(value: u128::max())
        writer.write(value: u64::max())
        writer.write(value: u32::max())
        writer.write(value: u16::max())
        writer.write(value: u8::max())
        writer.write(value: u8(0))
    
        assert reader.read_u256() == 42
        assert reader.read_u256() == 42
        assert reader.read_u256() == 26
        assert reader.read_u8() == 26
        assert reader.read_u256() == u256::max()
        assert reader.read_u128() == u128::max()
        assert reader.read_u64() == u64::max()
        assert reader.read_u32() == u32::max()
        assert reader.read_u16() == u16::max()
        assert reader.read_u8() == u8::max()
        assert reader.read_u8() == 0
    }
    

    Precompiles

    example:

    use std::precompiles
    use std::buf::{MemoryBuffer, MemoryBufferReader, MemoryBufferWriter}
    
    #test
    fn test_ec_recover() {
        let result: address = precompiles::ec_recover(
            hash: 0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3,
            v: 28,
            r: 0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608,
            s: 0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada
        )
    
        assert result == address(0x7156526fbd7a3c72969b54f64e42c10fbb768c8a)
    }
    

    ctx.raw_call()

    example:

    use std::buf::{
        RawCallBuffer,
        MemoryBufferReader, 
        MemoryBufferWriter
    }
    use std::evm
    
    contract Foo {
        pub unsafe fn __call__() {
            if evm::call_data_load(offset: 0) == 42 {
                evm::mstore(offset: 0, value: 26)
                evm::return_mem(offset: 0, len: 32)
            } else if evm::call_data_load(offset: 0) == 26 {
                revert
            }
        }
    }
    
    #test
    fn test_raw_call(mut ctx: Context) {
        let foo: Foo = Foo.create(ctx, 0)
    
        let mut buf: RawCallBuffer = RawCallBuffer::new(
            input_len: 32, 
            output_len: 32
        )
        let mut writer: MemoryBufferWriter = buf.writer()
    
        writer.write(value: 42)
        assert ctx.raw_call(addr: address(foo), value: 0, buf)
      
        let mut reader: MemoryBufferReader = buf.reader()
        assert reader.read_u256() == 26
    
        assert not ctx.raw_call(addr: address(foo), value: 0, buf)
    }
    

    (#885)

Bugfixes

  • Fixed an ICE when using aggregate types with aggregate type fields in public functions

    This code would previously cause an ICE:

    struct Tx {
      pub data: Array<u8, 320>
    }
    
    contract Foo {
      pub fn bar(mut tx: Tx) {}
    }
    

    (#867)

  • Fixed a regression where the compiler would not reject a method call on a struct in storage.

    E.g. the follwing code should be rejected as it is missing a to_mem() call:

    struct Bar {
        pub x: u256
    
        pub fn get_x(self) -> u256{
            return self.x
        }
    }
    
    contract Foo {
        bar: Bar
    
        pub fn __init__(mut self) {
            self.bar = Bar( x: 2 )
        }
        fn yay(self) {
            self.bar.get_x()
        }
    }
    

    The compiler will now reject the code and suggest a to_mem() before callingget_x(). (#881)

v0.22.0

05 Apr 14:48
Compare
Choose a tag to compare

0.22.0 "Vulcanite" (2023-04-05)

This is the first non-alpha release of Fe. Read our announcement for more details.

Features

  • Support for tests.

    example:

    #test
    fn my_test() {
        assert 26 + 16 == 42
    }
    

    Tests can be executed using the test subcommand.

    example:

    $ fe test foo.fe (#807)

  • Fixed broken trait orphan rule

    Fe has an orphan rule for Traits similar to Rust's that requires
    that either the trait or the type that we are implementing the trait for
    are located in the same ingot as the impl. This rule was implemented
    incorrectly so that instead of requiring them to be in the same ingot,
    they were required to be in the same module. This change fixes this
    so that the orphan rule is enforced correctly.
    (#863)

  • address values can now be specified with a number literal, with no explicit
    cast. So instead of let t: address = address(0xfe), one can now write
    let t: address = 0xfe. This also means that it's possible to define const
    addresses: const SOME_KNOWN_CONTRACT: address = 0xfefefefe
    (#864)

Bugfixes

  • Fixed resolving of generic arguments to associated functions.

    For example, this code would previously crash the compiler:

    ...
      // This function doesn't take self
      pub fn run_static<T: Computable>(_ val: T) -> u256 {
        return val.compute(val: 1000)
      }
    ...
    
    // Invoking it would previously crash the compiler
    Runner::run_static(Mac())
    ...

    (#861)

Improved Documentation

  • Changed the Deployment tutorial to use foundry and the Sepolia network (#853)

v0.21.0-alpha

01 Mar 09:34
Compare
Choose a tag to compare
v0.21.0-alpha Pre-release
Pre-release

0.21.0-alpha "Ussingite" (2023-02-28)

Features

  • Support for Self type

    With this change Self (with capital S) can be used to refer
    to the enclosing type in contracts, structs, impls and traits.

    E.g.

    trait Min {
      fn min() -> Self;
    }
    
    impl Min for u8 {
      fn min() -> u8 { // Both `u8` or `Self` are valid here
        return 0
      }
    }
    

    Usage: u8::min() (#803)

  • Added Min and Max traits to the std library.
    The std library implements the traits for all numeric types.

    Example

    use std::traits::{Min, Max}
    ...
    
    assert u8::min() < u8::max()
    ``` ([#836](https://github.com/ethereum/fe/issues/836))
    
    
  • Upgraded underlying solc compiler to version 0.8.18

Bugfixes

  • the release contains minor bugfixes

v0.20.0-alpha

05 Dec 18:27
Compare
Choose a tag to compare
v0.20.0-alpha Pre-release
Pre-release

0.20.0-alpha "Tokyoite" (2022-12-05)

Features

  • Removed the event type as well as the emit keyword.
    Instead the struct type now automatically implements
    the Emittable trait and can be emitted via ctx.emit(..).

    Indexed fields can be annotated via the #indexed attribute.

    E.g.

    struct Signed {
        book_msg: String<100>
    }
    
    contract GuestBook {
        messages: Map<address, String<100>>
    
        pub fn sign(mut self, mut ctx: Context, book_msg: String<100>) {
            self.messages[ctx.msg_sender()] = book_msg
            ctx.emit(Signed(book_msg))
        }
    }
    

    (#717)

  • Allow to call trait methods on types when trait is in scope

    So far traits were only useful as bounds for generic functions.
    With this change traits can also be used as illustrated with
    the following example:

    trait Double {
      fn double(self) -> u256;
    }
    
    impl Double for (u256, u256) {
      fn double(self) -> u256 {
        return (self.item0 + self.item1) * 2
      }
    }
    
    contract Example {
    
      pub fn run_test(self) {
        assert (0, 1).double() == 2
      }
    }
    

    If a call turns out to be ambigious the compiler currently asks the
    user to disambiguate via renaming. In the future we will likely
    introduce a syntax to allow to disambiguate at the callsite. (#757)

  • Allow contract associated functions to be called via ContractName::function_name() syntax. (#767)

  • Add enum types and match statement.

    enum can now be defined, e.g.,

    pub enum MyEnum {
        Unit
        Tuple(u32, u256, bool)
      
        fn unit() -> MyEnum {
            return MyEnum::Unit
        }
    }
    

    Also, match statement is introduced, e.g.,

    pub fn eval_enum()  -> u256{
        match MyEnum {
            MyEnum::Unit => { 
                return 0
            }
          
            MyEnum::Tuple(a, _, false) => {
                return u256(a)
            }
          
            MyEnum::Tuple(.., true) => {
                return u256(1)
            }
        }
    }
    

    For now, available patterns are restricted to

    • Wildcard(_), which matches all patterns: _
    • Named variable, which matches all patterns and binds the value to make the value usable in the arm. e.g., a, b and c in MyEnum::Tuple(a, b, c)
    • Boolean literal(true and false)
    • Enum variant. e.g., MyEnum::Tuple(a, b, c)
    • Tuple pattern. e.g., (a, b, c)
    • Struct pattern. e.g., MyStruct {x: x1, y: y1, b: true}
    • Rest pattern(..), which matches the rest of the pattern. e.g., MyEnum::Tuple(.., true)
    • Or pattern(|). e.g., MyEnum::Unit | MyEnum::Tuple(.., true)

    Fe compiler performs the exhaustiveness and usefulness checks for match statement.
    So the compiler will emit an error when all patterns are not covered or an unreachable arm are detected. (#770)

  • Changed comments to use // instead of # (#776)

  • Added the mut keyword, to mark things as mutable. Any variable or function parameter
    not marked mut is now immutable.

    contract Counter {
        count: u256
    
        pub fn increment(mut self) -> u256 {
            // `self` is mutable, so storage can be modified
            self.count += 1
            return self.count
        }
    }
    
    struct Point {
        pub x: u32
        pub y: u32
    
        pub fn add(mut self, _ other: Point) {
            self.x += other.x
            self.y += other.y
    
            // other.x = 1000 // ERROR: `other` is not mutable
        }
    }
    
    fn pointless() {
        let origin: Point = Point(x: 0, y: 0)
        // origin.x = 10 // ERROR: origin is not mutable
    
        let x: u32 = 10
        // x_coord = 100 // ERROR: `x_coord` is not mutable
        let mut y: u32 = 0
        y = 10 // OK
    
        let mut p: Point = origin // copies `origin`
        p.x = 10 // OK, doesn't modify `origin`
    
        let mut q: Point = p // copies `p`
        q.x = 100            // doesn't modify `p`
    
        p.add(q)
        assert p.x == 110
    }
    

    Note that, in this release, primitive type function parameters
    can't be mut. This restriction might be lifted in a future release.

    For example:

    fn increment(mut x: u256) { // ERROR: primitive type parameters can't be mut
        x += 1
    }
    

    (#777)

  • The contents of the std::prelude module (currently just the Context struct)
    are now automatically used by every module, so use std::context::Context is
    no longer required. (#779)

  • When the Fe compiler generates a JSON ABI file for a contract, the
    "stateMutability" field for each function now reflects whether the function can
    read or modify chain or contract state, based on the presence or absence of the
    self and ctx parameters, and whether those parameters are mutable.

    If a function doesn't take self or ctx, it's "pure".
    If a function takes self or ctx immutably, it can read state but not mutate
    state, so it's a "view"
    If a function takes mut self or mut ctx, it can mutate state, and is thus
    marked "payable".

    Note that we're following the convention set by Solidity for this field, which
    isn't a perfect fit for Fe. The primary issue is that Fe doesn't currently
    distinguish between "payable" and "nonpayable" functions; if you want a function
    to revert when Eth is sent, you need to do it manually
    (eg assert ctx.msg_value() == 0). (#783)

  • Trait associated functions

    This change allows trait functions that do not take a self parameter.
    The following demonstrates a possible trait associated function and its usage:

    trait Max {
      fn max(self) -> u8;
    }
    
    impl Max for u8 {
      fn max() -> u8 {
        return u8(255)
      }
    }
    
    contract Example {
    
      pub fn run_test(self) {
        assert u8::max() == 255
      }
    }
    

    (#805)

Bugfixes

  • Fix issue where calls to assiciated functions did not enforce visibility rules.

    E.g the following code should be rejected but previously wasn't:

    struct Foo {
        fn do_private_things() {
        }
    }
    
    contract Bar {
        fn test() {
            Foo::do_private_things()
        }
    }
    

    With this change, the above code is now rejected because do_private_things is not pub. (#767)

  • Padding on bytes and string ABI types is zeroed out. (#769)

  • Ensure traits from other modules or even ingots can be implemented (#773)

  • Certain cases where the compiler would not reject pure functions
    being called on instances are now properly rejected. (#775)

  • Reject calling to_mem() on primitive types in storage (#801)

  • Disallow importing private type via use

    The following was previously allowed but will now error:

    use foo::PrivateStruct (#815)

v0.19.1-alpha

06 Jul 12:36
Compare
Choose a tag to compare
v0.19.1-alpha Pre-release
Pre-release

0.19.1-alpha "Sunstone" (2022-07-06)

Features

  • Support returning nested struct.

    Example:

    pub struct InnerStruct {
        pub inner_s: String<10>
        pub inner_x: i256
    }
    
    pub struct NestedStruct {
        pub inner: InnerStruct
        pub outer_x: i256
    }
    
    contract Foo {
        pub fn return_nested_struct() -> NestedStruct {
            ...
        }
    }
    

    (#635)

  • Made some small changes to how the Context object is used.

    • ctx is not required when casting an address to a contract type. Eg let foo: Foo = Foo(address(0))
    • ctx is required when calling an external contract function that requires ctx

    Example:

    use std::context::Context # see issue #679
    
    contract Foo {
      pub fn emit_stuff(ctx: Context) {
        emit Stuff(ctx)  # will be `ctx.emit(Stuff{})` someday
      }
    }
    contract Bar {
      pub fn call_foo_emit_stuff(ctx: Context) {
        Foo(address(0)).emit_stuff(ctx)
      }
    }
    event Stuff {}
    

    (#703)

  • Braces! Fe has abandoned python-style significant whitespace in favor of the
    trusty curly brace.

    In addition, elif is now spelled else if, and the pass
    statement no longer exists.

    Example:

    pub struct SomeError {}
    
    contract Foo {
      x: u8
      y: u16
    
      pub fn f(a: u8) -> u8 {
        if a > 10 {
          let x: u8 = 5
          return a + x
        } else if a == 0 {
          revert SomeError()
        } else {
          return a * 10
        }
      }
    
      pub fn noop() {}
    }
    

    (#707)

  • traits and generic function parameter

    Traits can now be defined, e.g:

    trait Computable {
      fn compute(self, val: u256) -> u256;
    }
    

    The mechanism to implement a trait is via an impl block e.g:

    struct Linux {
      pub counter: u256
      pub fn get_counter(self) -> u256 {
        return self.counter
      }
      pub fn something_static() -> u256 {
        return 5
      }
    }
    
    impl Computable for Linux {
      fn compute(self, val: u256) -> u256 {
        return val + Linux::something_static() + self.get_counter()
      }
    }
    

    Traits can only appear as bounds for generic functions e.g.:

    struct Runner {
    
      pub fn run<T: Computable>(self, _ val: T) -> u256 {
        return val.compute(val: 1000)
      }
    }
    

    Only struct functions (not contract functions) can have generic parameters.
    The run method of Runner can be called with any type that implements Computable e.g.

    contract Example {
    
      pub fn generic_compute(self) {
        let runner: Runner = Runner();
        assert runner.run(Mac()) == 1001
        assert runner.run(Linux(counter: 10)) == 1015
      }
    }
    

    (#710)

  • Generate artifacts for all contracts of an ingot, not just for contracts that are defined in main.fe (#726)

  • Allow using complex type as array element type.

    Example:

    contract Foo {
        pub fn bar() -> i256 {
            let my_array: Array<Pair, 3> = [Pair::new(1, 0), Pair::new(2, 0), Pair::new(3, 0)]
    
            let sum: i256 = 0
            for pair in my_array {
                sum += pair.x
            }
    
            return sum
        }
    }
    
    struct Pair {
        pub x: i256
        pub y: i256
      
        pub fn new(_ x: i256, _ y: i256) -> Pair {
            return Pair(x, y)
        }
    }
    

    (#730)

  • The fe CLI now has subcommands:

    fe new myproject - creates a new project structure
    fe check . - analyzes fe source code and prints errors
    fe build . - builds a fe project (#732)

  • Support passing nested struct types to public functions.

    Example:

    pub struct InnerStruct {
        pub inner_s: String<10>
        pub inner_x: i256
    }
    
    pub struct NestedStruct {
        pub inner: InnerStruct
        pub outer_x: i256
    }
    
    contract Foo {
        pub fn f(arg: NestedStruct) {
            ...
        }
    }
    

    (#733)

  • Added support for repeat expressions ([VALUE; LENGTH]).

    e.g.

    let my_array: Array<bool, 42> = [bool; 42] 
    

    Also added checks to ensure array and struct types are initialized. These checks are currently performed at the declaration site, but will be loosened in the future. (#747)

Bugfixes

  • Fix a bug that incorrect instruction is selected when the operands of a comp instruction are a signed type. (#734)

  • Fix issue where a negative constant leads to an ICE

    E.g. the following code would previously crash the compiler but shouldn't:

    const INIT_VAL: i8 = -1
    contract Foo {
      pub fn init_bar() {
        let x: i8 = INIT_VAL
      }
    }
    

    (#745)

  • Fix a bug that causes ICE when nested if-statement has multiple exit point.

    E.g. the following code would previously crash the compiler but shouldn't:

     pub fn foo(self) {
        if true {
            if self.something { 
                return 
            }
        }
        if true {
            if self.something { 
                return 
            }
        }
    }
    

    (#749)

v0.18.0-alpha

27 May 09:28
Compare
Choose a tag to compare
v0.18.0-alpha Pre-release
Pre-release

WARNING: All Fe releases are alpha releases and only meant to share the development progress with developers and enthusiasts. It is NOT yet ready for production usage.

0.18.0-alpha "Ruby" (2022-05-27)

Features

  • Added support for parsing of attribute calls with generic arguments (e.g. foo.bar<Baz>()). (#719)

Bugfixes

  • Fix a regression where the stateMutability field would not be included in the generated ABI (#722)
  • Fix two regressions introduced in 0.17.0
    • Properly lower right shift operation to yul's sar if operand is signed type
    • Properly lower negate operation to call safe_sub (#723)

v0.17.0-alpha "Quartz"

26 May 11:41
Compare
Choose a tag to compare
Pre-release

WARNING: All Fe releases are alpha releases and only meant to share the development progress with developers and enthusiasts. It is NOT yet ready for production usage.

0.17.0-alpha "Quartz" (2022-05-26)

Features

  • Support for underscores in numbers to improve readability e.g. 100_000.

    Example

        let num: u256 = 1000_000_000_000
    

    (#149)

  • Optimized access of struct fields in storage (#249)

  • Unit type () is now ABI encodable (#442)

  • Temporary default stateMutability to payable in ABI

    The ABI metadata that the compiler previously generated did not include the stateMutability field. This piece of information is important for tooling such as hardhat because it determines whether a function needs to be called with or without sending a transaction.

    As soon as we have support for mut self and mut ctx we will be able to derive that information from the function signature. In the meantime we now default to payable. (#705)

Bugfixes

  • Fixed a crash caused by certain memory to memory assignments.

    E.g. the following code would previously lead to a compiler crash:

    my_struct.x = my_struct.y
    

    (#590)

  • Reject unary minus operation if the target type is an unsigned integer number.

    Code below should be reject by fe compiler:

    contract Foo:
        pub fn bar(self) -> u32:
            let unsigned: u32 = 1
            return -unsigned
      
        pub fn foo():
            let a: i32 = 1
            let b: u32 = -a

    (#651)

  • Fixed crash when passing a struct that contains an array

    E.g. the following would previously result in a compiler crash:

    struct MyArray:
        pub x: Array<i32, 2>
      
      
    contract Foo:
        pub fn bar(my_arr: MyArray):
            pass
    

    (#681)

  • reject infinite size struct definitions.

    Fe structs having infinite size due to recursive definitions were not rejected earlier and would cause ICE in the analyzer since they were not properly handled. Now structs having infinite size are properly identified by detecting cycles in the dependency graph of the struct field definitions and an error is thrown by the analyzer. (#682)

  • Return instead of revert when contract is called without data.

    If a contract is called without data so that no function is invoked,
    we would previously revert but that would leave us without a
    way to send ETH to a contract so instead it will cause a return now. (#694)

  • Resolve compiler crash when using certain reserved YUL words as struct field names.

    E.g. the following would previously lead to a compiler crash because numer is
    a reserved keyword in YUL.

    struct Foo:
      pub number: u256
    
    contract Meh:
    
      pub fn yay() -> Foo:
        return Foo(number:2)
    

    (#709)