Skip to content

Releases: ethereum/fe

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)

v0.16.0-alpha "Pyrope"

05 May 17:13
Compare
Choose a tag to compare
Pre-release

0.16.0-alpha (2022-05-05)

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.

Features

  • Change static function call syntax from Bar.foo() to Bar::foo() (#241)
  • Added support for retrieving the base fee via ctx.base_fee() (#503)

Bugfixes

  • Resolve functions on structs via path (e.g. bi::ba::bums()) (#241)

v0.15.0-alpha "Onyx"

04 Apr 15:30
316e6aa
Compare
Choose a tag to compare
v0.15.0-alpha "Onyx" 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.15.0-alpha "Onyx" (2022-04-04)

Features

  • Labels are now required on function arguments. Labels can be omitted if the
    argument is a variable with a name that matches the label, or if the function
    definition specifies that an argument should have no label. Functions often take
    several arguments of the same type; compiler-checked labels can help prevent
    accidentally providing arguments in the wrong order.

    Example:

    contract CoolCoin:
      balance: Map<address, i256>
      loans: Map<(address, address), i256>
    
      pub fn demo(self, ann: address, bob: address):
        let is_loan: bool = false
        self.give(from: ann, to: bob, 100, is_loan)
    
      fn transfer(self, from sender: address, to recipient: address, _ val: u256, is_loan: bool):
        self.cred[sender] -= val
        self.cred[recipient] += val
        if is_loan:
          self.loans[(sender, recipient)] += val
    

    Note that arguments must be provided in the order specified in the function
    definition.

    A parameter's label defaults to the parameter name, but can be changed by
    specifying a different label to the left of the parameter name. Labels should be
    clear and convenient for the caller, while parameter names are only used in the
    function body, and can thus be longer and more descriptive.
    In the example above, we choose to use sender and recipient as identifiers
    in the body of fn transfer, but use labels from: and to:.

    In cases where it's ideal to not have labels, e.g. if a function takes a single
    argument, or if types are sufficient to differentiate between arguments, use _
    to specify that a given parameter has no label. It's also fine to require labels
    for some arguments, but not others.

    Example:

    fn add(_ x: u256, _ y: u256) -> u256:
      return x + y
    
    contract Foo:
      fn transfer(self, _ to: address, wei: u256):
        pass
    
      pub fn demo(self):
        transfer(address(0), wei: add(1000, 42))
    

(#397)

Bugfixes

  • The region of memory used to compute the slot of a storage map value was not being allocated. (#684)