Skip to content

Confusing out-of-bounds error for arrays declared using const generics #7605

@ironcev

Description

@ironcev

Currently, we emit ArrayOutOfBounds error on the IR level, in the compile_array_index.

At that point, we know the size of the array, but not how it was declared.

This means, if the below function with array defined using const generic N is called like, e.g., literal_index_const_generic::<0> we will emit the out-of-bounds error:

fn literal_index_const_generic<const N: u64>() -> u64 {
    let array = [1; N];
    array[4]
    ^ Index out of bounds; the length is 0 but the index is 4.
}

This is confusing, because it is not clear at all why would the length be 0.

E.g., in such cases Rust does not emit compile time error, but rather panics at runtime.

Which possible options do we have?

  1. Same as Rust, do not emit any error.
    Out-of-bounds errors that don't lead to reverts in smart contract languages are a hairy thing. I don't feel comfortable about compiler knowing that there is an issue in code and ignoring it altogether. With the FuelVM memory model as-is, getting out-of-bounds access to valid memory, and thus no reverts, is likely.

    Also, if we just remove the error, we will end up in the same issue, currently still open, with SROA, described in Index out of bounds error wrongly uses const eval of outer variables #7521.

  2. Emit error, but a helpful one.
    Unlike Rust, we might for runnable programs and tests emit an out-of-bound error, but to avoid any confusion, we provide an expressive diagnostics that also points to the callsite at which the length is actually defined.

    Of course, in an arbitrary case this is tricky, because a literal length can be passed somewhere upper in the call chain, or be declared in a trait impl, etc Considering that we currently do not run IR checks when compiling libraries, we can even have a situation where the root cause of the error is outside of the developer's code.

  3. Inject runtime boundary check
    One possibility would be for the compiler to inject runtime boundary check in these places. Still, I would at least expect an explanation saying why. Which likely means emitting a warning, which poses the same complexity and questions as the point 2), except the code will still compile even if the root-cause is not in developer's code and cannot be fixed there.

Metadata

Metadata

Assignees

No one assigned

    Labels

    compilerGeneral compiler. Should eventually become more specific as the issue is triagedcompiler: frontendEverything to do with type checking, control flow analysis, and everything between parsing and IRgencompiler: irIRgen and sway-ir including optimization passes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions