Skip to content

Commit

Permalink
Slice/Array intrinsics: __slice and __elem_at (#6282)
Browse files Browse the repository at this point in the history
## Description

This PR is part of #5110 and
introduces two new intrinsic: `__slice` and `__elem_at`.

`__slice` allows the creation of slices by slicing arrays or other
slices. Whilst `__elem_at` returns a reference to an item inside the
slice or the array.

## Out of bounds checks

These intrinsic will not generate any runtime checks, these must be done
manually, when and where appropriate; but they do a complete static
analysis of all indices, to avoid runtime buffer overflows, when
possible.

That means that at runtime, it is possible to do a buffer overflow when
reading/writing, which is an "undefined behaviour" as to what will
happen.

## Empty Array

This PR also solves a problem with empty arrays. Before empty arrays
such as `let a = []` were being type-checked as `[Never; 0]`, which
means that any code after them was being marked as dead.

Now we correctly type check them as `[Unknown; 0]` and return a more
friendly error.

```
          4 |
          5 |     // Empty array
          6 |     let a = [];
            |             ^^ Type must be known at this point
          7 | }
            |
          ____
```

## Check of constants inside fns

This PR also solves a problem with not checking `const` expressions
inside `fns`. We, for example, do not allow slices in constants, but we
were only checking globals. Now we check constants also inside
functions, methods etc...

## Small improvements for our e2e

We can now `dbg` inside our e2e harness and get results like the ones
below. One needs to include the lib `test/src/e2e_vm_tests/utils` and
cal `something.dbg()` or `something.dbgln()`. There is no magic, and
structs/enums will need to manually implement the `Dbg` trait.

This is only to facilitate the debugging of our e2e tests.


![image](https://github.com/user-attachments/assets/2f25c50e-b7b3-4199-8bf4-699473919e6c)


## Checklist

- [ ] I have linked to any relevant issues.
- [ ] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [ ] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [ ] I have requested a review from the relevant team or maintainers.
  • Loading branch information
xunilrj authored and esdrubal committed Aug 13, 2024
1 parent 9042994 commit b82745c
Show file tree
Hide file tree
Showing 68 changed files with 1,919 additions and 253 deletions.
113 changes: 80 additions & 33 deletions docs/book/src/reference/compiler_intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The Sway compiler supports a list of intrinsics that perform various low level operations that are useful for building libraries. Compiler intrinsics should rarely be used but are preferred over `asm` blocks because they are type-checked and are safer overall. Below is a list of all available compiler intrinsics:

___
---

```sway
__size_of_val<T>(val: T) -> u64
Expand All @@ -12,7 +12,7 @@ __size_of_val<T>(val: T) -> u64

**Constraints:** None.

___
---

```sway
__size_of<T>() -> u64
Expand All @@ -22,7 +22,7 @@ __size_of<T>() -> u64

**Constraints:** None.

___
---

```sway
__size_of_str_array<T>() -> u64
Expand All @@ -32,7 +32,7 @@ __size_of_str_array<T>() -> u64

**Constraints:** None.

___
---

```sway
__assert_is_str_array<T>()
Expand All @@ -42,7 +42,7 @@ __assert_is_str_array<T>()

**Constraints:** None.

___
---

```sway
__to_str_array(s: str) -> str[N]
Expand All @@ -52,7 +52,7 @@ __to_str_array(s: str) -> str[N]

**Constraints:** None.

___
---

```sway
__is_reference_type<T>() -> bool
Expand All @@ -62,7 +62,7 @@ __is_reference_type<T>() -> bool

**Constraints:** None.

___
---

```sway
__is_str_array<T>() -> bool
Expand All @@ -72,7 +72,7 @@ __is_str_array<T>() -> bool

**Constraints:** None.

___
---

```sway
__eq<T>(lhs: T, rhs: T) -> bool
Expand All @@ -82,7 +82,7 @@ __eq<T>(lhs: T, rhs: T) -> bool

**Constraints:** `T` is `bool`, `u8`, `u16`, `u32`, `u64`, `u256`, `b256` or `raw_ptr`.

___
---

```sway
__gt<T>(lhs: T, rhs: T) -> bool
Expand All @@ -91,7 +91,8 @@ __gt<T>(lhs: T, rhs: T) -> bool
**Description:** Returns whether `lhs` is greater than `rhs`.

**Constraints:** `T` is `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.
___

---

```sway
__lt<T>(lhs: T, rhs: T) -> bool
Expand All @@ -100,7 +101,8 @@ __lt<T>(lhs: T, rhs: T) -> bool
**Description:** Returns whether `lhs` is less than `rhs`.

**Constraints:** `T` is `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.
___

---

```sway
__gtf<T>(index: u64, tx_field_id: u64) -> T
Expand All @@ -110,7 +112,7 @@ __gtf<T>(index: u64, tx_field_id: u64) -> T

**Constraints:** None.

___
---

```sway
__addr_of<T>(val: T) -> raw_ptr
Expand All @@ -120,7 +122,7 @@ __addr_of<T>(val: T) -> raw_ptr

**Constraints:** `T` is a reference type.

___
---

```sway
__state_load_word(key: b256) -> u64
Expand All @@ -130,7 +132,7 @@ __state_load_word(key: b256) -> u64

**Constraints:** None.

___
---

```sway
__state_load_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool
Expand All @@ -140,7 +142,7 @@ __state_load_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool

**Constraints:** None.

___
---

```sway
__state_store_word(key: b256, val: u64) -> bool
Expand All @@ -150,7 +152,7 @@ __state_store_word(key: b256, val: u64) -> bool

**Constraints:** None.

___
---

```sway
__state_store_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool
Expand All @@ -160,7 +162,7 @@ __state_store_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool

**Constraints:** None.

___
---

```sway
__log<T>(val: T)
Expand All @@ -170,7 +172,7 @@ __log<T>(val: T)

**Constraints:** None.

___
---

```sway
__add<T>(lhs: T, rhs: T) -> T
Expand All @@ -180,7 +182,7 @@ __add<T>(lhs: T, rhs: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`.

___
---

```sway
__sub<T>(lhs: T, rhs: T) -> T
Expand All @@ -190,7 +192,7 @@ __sub<T>(lhs: T, rhs: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`.

___
---

```sway
__mul<T>(lhs: T, rhs: T) -> T
Expand All @@ -200,7 +202,7 @@ __mul<T>(lhs: T, rhs: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`.

___
---

```sway
__div<T>(lhs: T, rhs: T) -> T
Expand All @@ -210,7 +212,7 @@ __div<T>(lhs: T, rhs: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`.

___
---

```sway
__and<T>(lhs: T, rhs: T) -> T
Expand All @@ -220,7 +222,7 @@ __and<T>(lhs: T, rhs: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.

___
---

```sway
__or<T>(lhs: T, rhs: T) -> T
Expand All @@ -230,7 +232,7 @@ __or<T>(lhs: T, rhs: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.

___
---

```sway
__xor<T>(lhs: T, rhs: T) -> T
Expand All @@ -239,7 +241,8 @@ __xor<T>(lhs: T, rhs: T) -> T
**Description:** Bitwise XOR `lhs` and `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.
___

---

```sway
__mod<T>(lhs: T, rhs: T) -> T
Expand All @@ -248,7 +251,8 @@ __mod<T>(lhs: T, rhs: T) -> T
**Description:** Modulo of `lhs` by `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`.
___

---

```sway
__rsh<T>(lhs: T, rhs: u64) -> T
Expand All @@ -257,7 +261,8 @@ __rsh<T>(lhs: T, rhs: u64) -> T
**Description:** Logical right shift of `lhs` by `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.
___

---

```sway
__lsh<T>(lhs: T, rhs: u64) -> T
Expand All @@ -266,7 +271,8 @@ __lsh<T>(lhs: T, rhs: u64) -> T
**Description:** Logical left shift of `lhs` by `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.
___

---

```sway
__revert(code: u64)
Expand All @@ -276,7 +282,7 @@ __revert(code: u64)

**Constraints:** None.

___
---

```sway
__ptr_add(ptr: raw_ptr, offset: u64)
Expand All @@ -286,7 +292,7 @@ __ptr_add(ptr: raw_ptr, offset: u64)

**Constraints:** None.

___
---

```sway
__ptr_sub(ptr: raw_ptr, offset: u64)
Expand All @@ -296,7 +302,7 @@ __ptr_sub(ptr: raw_ptr, offset: u64)

**Constraints:** None.

___
---

```sway
__smo<T>(recipient: b256, data: T, coins: u64)
Expand All @@ -306,7 +312,7 @@ __smo<T>(recipient: b256, data: T, coins: u64)

**Constraints:** None.

___
---

```sway
__not(op: T) -> T
Expand All @@ -316,7 +322,7 @@ __not(op: T) -> T

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`.

___
---

```sway
__jmp_mem()
Expand All @@ -325,3 +331,44 @@ __jmp_mem()
**Description:** Jumps to `MEM[$hp]`.

**Constraints:** None.

---

```sway
__slice(item: &[T; N], start: u64, end: u64) -> &[T]
__slice(item: &[T], start: u64, end: u64) -> &[T]
__slice(item: &mut [T; N], start: u64, end: u64) -> &mut [T]
__slice(item: &mut [T], start: u64, end: u64) -> &mut [T]
```

**Description:** Slices an array or another slice.

This intrinsic returns a reference to a slice containing the range of elements inside `item`.
The mutability of reference is defined by the first parameter mutability.

Runtime bound checks are not generated, and must be done manually when and where appropriated. Compile time bound checks are done when possible.

**Constraints:**

- `item` is an array or a slice;
- when `start` is a literal, it must be smaller than `item` length;
- when `end` is a literal, it must be smaller than or equal to `item` length;
- `end` must be greater than or equal to `start`

---

```sway
__elem_at(item: &[T; N], index: u64) -> &T
__elem_at(item: &[T], index: u64) -> &T
__elem_at(item: &mut [T; N], index: u64) -> &mut T
__elem_at(item: &mut [T], index: u64) -> &mut T
```

**Description:** Returns a reference to the indexed element. The mutability of reference is defined by the first parameter mutability.

Runtime bound checks are not generated, and must be done manually when and where appropriated. Compile time bound checks are done when possible.

**Constraints:**

- `item` is a reference to an array or a reference to a slice;
- when `index` is a literal, it must be smaller than `item` length;
2 changes: 2 additions & 0 deletions docs/reference/src/code/language/built-ins/slices/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
14 changes: 14 additions & 0 deletions docs/reference/src/code/language/built-ins/slices/Forc.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[package]]
name = 'arrays'
source = 'root'
dependencies = ['std']

[[package]]
name = 'core'
source = 'path+from-root-54F92B42A645EA2B'
dependencies = []

[[package]]
name = 'std'
source = 'git+https://github.com/fuellabs/sway?tag=v0.24.5#e695606d8884a18664f6231681333a784e623bc9'
dependencies = ['core']
8 changes: 8 additions & 0 deletions docs/reference/src/code/language/built-ins/slices/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "lib.sw"
license = "Apache-2.0"
name = "slices"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
7 changes: 7 additions & 0 deletions docs/reference/src/code/language/built-ins/slices/src/lib.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
library;

// ANCHOR: syntax
fn syntax(s: &[u64]) -> u64 {
s.len()
}
// ANCHOR_END: syntax
2 changes: 2 additions & 0 deletions docs/reference/src/documentation/language/built-ins/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ Sway has the following primitive types:
1. `str[n]` (fixed-length string of size n)
4. [Bytes](b256.md)
1. `b256` (256 bits / 32 bytes, i.e. a hash)
5. [Slices](slices.md)

<!-- TODO: The following sentence does not belong here. We need to convey the default size, including word size, somewhere however not on this page -->

The default numeric type is `u64`. The FuelVM's word size is 64 bits, and the cases where using a smaller numeric type to save space are minimal.

All other types in Sway are built up of these primitive types, or references to these primitive types.
Expand Down
Loading

0 comments on commit b82745c

Please sign in to comment.