Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
93a9459
trivial check attributes
xunilrj Mar 17, 2026
aff48d6
fmt and clippy issues
xunilrj Mar 17, 2026
d146418
update tests
xunilrj Mar 17, 2026
05c7be4
error message improvements
xunilrj Mar 19, 2026
a6af3b7
improve docs
xunilrj Mar 19, 2026
e95a148
improve docs
xunilrj Mar 19, 2026
4c21b92
improve docs
xunilrj Mar 20, 2026
16e9ec8
better error message
xunilrj Mar 20, 2026
7a83a5d
TrivialBool and TrivialEnum
xunilrj Mar 24, 2026
3263e62
fmt and clippy issues
xunilrj Mar 24, 2026
ab14d18
remove sway-lib-std warnings
xunilrj Mar 24, 2026
eac268d
better error message for tuples and arrays
xunilrj Mar 25, 2026
2d0b975
TrivialBool and TrivialEnum using auto-impl
xunilrj Mar 30, 2026
dffb58a
TrivialBool and TrivialEnum using auto-impl
xunilrj Mar 30, 2026
c030457
fmt and clippy issues
xunilrj Mar 30, 2026
5c8c433
correctly call is_decode_trivial
xunilrj Apr 1, 2026
98fcc0a
correctly call is_decode_trivial
xunilrj Apr 1, 2026
2bdca40
fmt and clippy issues
xunilrj Apr 1, 2026
1598670
PR fixes
xunilrj Apr 1, 2026
76f0067
fmt and clippy issues
xunilrj Apr 1, 2026
f201c3b
runninf forc-fmt
xunilrj Apr 1, 2026
b793bb8
PR fixes
xunilrj Apr 1, 2026
99f1484
fmt and clippy issues
xunilrj Apr 1, 2026
0512177
fmt and clippy issues
xunilrj Apr 1, 2026
acb1fa3
update tests
xunilrj Apr 1, 2026
4dfa273
fix tests
xunilrj Apr 1, 2026
5d10452
update tests
xunilrj Apr 1, 2026
5c95a4d
link to the final documentation url
xunilrj Apr 1, 2026
5bff24f
link to the final documentation url
xunilrj Apr 1, 2026
5fa34a3
fixing typos
xunilrj Apr 1, 2026
2c624de
error improvements and gas benchmark
xunilrj Apr 2, 2026
ba1b543
improve error message
xunilrj Apr 2, 2026
f1fbc33
removing range from memory representation
xunilrj Apr 3, 2026
dab9588
fmt and clippy issues
xunilrj Apr 3, 2026
c3dd664
update tests
xunilrj Apr 3, 2026
2f42b09
fmt and clippy issues
xunilrj Apr 3, 2026
0828be0
update tests
xunilrj Apr 3, 2026
2aef0db
fixing typos
xunilrj Apr 3, 2026
be5bc00
moving ir check to inside the type phase
xunilrj Apr 9, 2026
eab029c
rebase issues
xunilrj Apr 9, 2026
89bf63f
rebase issues
xunilrj Apr 9, 2026
ba9e791
removing intrinsic
xunilrj Apr 9, 2026
f3cb23d
check abi fns
xunilrj Apr 11, 2026
2c50019
require attribute for abis
xunilrj Apr 11, 2026
c4a5d5a
update tests
xunilrj Apr 11, 2026
46123e7
ignore for gc test: it needs experimental features
xunilrj Apr 11, 2026
ad6537e
checking abi function for enums
xunilrj Apr 13, 2026
46fdce1
also check return type
xunilrj Apr 13, 2026
60d0680
small refactor to the check code
xunilrj Apr 20, 2026
988f91b
more tests
xunilrj Apr 20, 2026
48bd60e
fmt and clippy issues
xunilrj Apr 20, 2026
20deb6d
update tests
xunilrj Apr 20, 2026
e988ca8
update tests
xunilrj Apr 20, 2026
89c25a9
fmt and clippy issues
xunilrj Apr 20, 2026
07a5072
remove redundant manager
xunilrj Apr 21, 2026
50894e1
TrivialVec and tests
xunilrj May 3, 2026
cc0c155
rollback TrivialVec
xunilrj May 19, 2026
50a2638
fmt and clippy issues
xunilrj May 19, 2026
7b2b315
new values for the attribute require
xunilrj May 20, 2026
3c432ee
fmt and clippy
xunilrj May 20, 2026
52d4f77
update tests
xunilrj May 20, 2026
8a24b03
PR adjustments
xunilrj May 21, 2026
c1943fd
pr adjustments
xunilrj May 21, 2026
0c2a1e7
fmt and clippy issues
xunilrj May 21, 2026
511867b
fmt and clippy issues
xunilrj May 21, 2026
094041d
fix attribute args quantity
xunilrj May 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/book/spell-check-custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,9 @@ Preload
preloads
Preloads
VM's
Decodable
Encodable
callee
decodable
encodable
Vec
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
- [Generics and Trait Constraints](./advanced/generics_and_trait_constraints.md)
- [Assembly](./advanced/assembly.md)
- [Never Type](./advanced/never_type.md)
- [Trivial Encoding](./advanced/trivial_encoding.md)
- [Common Collections](./common-collections/index.md)
- [Vectors on the Heap](./common-collections/vec.md)
- [Storage Vectors](./common-collections/storage_vec.md)
Expand Down
99 changes: 99 additions & 0 deletions docs/book/src/advanced/trivial_encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Trivially Encodable & Decodable Types

When a contract calls another contract, all arguments are **encoded** just before the call is actually executed,
and the callee **decodes** these arguments right before the target method starts.
This adds a small but non‑negligible gas cost, from hundreds to thousands of gas depending on the complexity of the arguments.

The Sway compiler mitigates this overhead for a subset of types that can be **trivially encoded** and/or **trivially decoded** –
that is, types that their *runtime representation*, how the type bytes are laid out inside the VM, is *identical* to their *encoded representation*,
how their bytes are laid out in the encoded buffer.

For such types the compiler can skip the encoding/decoding process entirely, saving gas and simplifying the generated code.

> **Trivial encoding** – encoding is replaced with a simple "transmute".
> **Trivial decoding** – encoding is replaced with a simple "transmute".

The compiler can skip each individually, but the whole gain comes only when both are skipped together.

## Checking Triviality

Each struct that should be treated as trivially encodable/decodable can be annotated with the `#[trivial]` attribute:

```sway
#[trivial(encode = "require", decode = "require")]
pub struct SomeArgument {
a: bool,
b: SomeEnum,
}
```

- `encode = "require"` – the compiler will check if the type is trivially encodable; if not, the build fails.
- `decode = "require"` – similarly for decoding.

Possible values are:

- required: compiler will check and error if the check fails;
- optional: compiler will only warn non-compliance;
- any: nothing will be checked.

This attributed can be used directly on types, but also on entry points such as "main" function for scripts and predicates; and contract methods for contracts.

## Which Types Are Trivial?

| Type | Trivially Encodable | Trivially Decodable | Notes |
|------|---------------------|---------------------|-------|
| `bool` | ✅ | ❌ | `bool` encodes to a single byte (`0` or `1`), but decoding must validate that the byte is a legal value. |
| `u8`, `u64`, `u256`, `b256` | ✅ | ✅ | |
| `u16`, `u32` | ❌ | ❌ | Their runtime representation is actually a `u64` |
| Structs | ✅ If all their members are trivial | ✅ If all their member are trivial | Recursively evaluated. |
| Enums | ✅ If all variants are trivial | ❌ | Enums have an `u64` discriminant that cannot be trivially decodable. |
| Arrays | ✅ If the item type is trivial | ✅ if the item type is trivial |
| String Arrays | ✅ See * | ✅ See * | |
| Vec, Dictionary, String, etc. | ❌ | ❌ | Data Structures are never trivial |

Only when the feature "str_array_no_padding" is turned on. When the feature toggle is off, only string arrays that its length is multiple of 8.

### Why `bool` and `enum` are not trivially decodable

Probably the most surprising non trivial base data type is `bool`. Mainly because `bool` is obviously trivially encodable. But there is no guarantee
that buffer does not have a value like `2`, that being "transmuted" into a bool would be allow its runtime representation to be `2`, which is **undefined behaviour**.

The same limitation applies to enums. Enums are implemented as "tagged unions" which means that their runtime representation has a discriminant value as `u64`. There
is no guarantee that the buffer would have a valid value for its discriminant.

---

## 3. Workaround for Non‑trivial Types

If you need to expose a `bool` or an enum as a public argument, you can either:

1. **Manual validation** – expose a raw `u64` (or `u8`) and check its value in the callee.

```sway
#[trivial(encode = "require", decode = "require")]
pub struct Flag(u8); // manually validate that value <= 1
```

1. **Custom wrappers** – Sway ships with `TrivialBool`, `TrivialEnum<T>` and `TrivialVec<T, N>` that enforce the bounds at compile time.

```sway
use sway::codec::TrivialBool;
use sway::codec::TrivialEnum;
use sway::codec::TrivialVec;

#[trivial(encode = "require", decode = "require")]
pub struct SomeArgument {
a: TrivialBool,
b: TrivialEnum<SomeEnum>,
c: TrivialVec<u64, 16>,
}
```

These wrappers automatically provide the guard checks and still let the compiler treat them as trivial.
Their usage is very similar to `Option<bool>`.

```sway
let a: bool = some_argument.a.unwrap();
let b: SomeEnum = some_argument.b.unwrap();
let c: &[u64] = some_argument.c.as_slice();
```
6 changes: 6 additions & 0 deletions sway-ast/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ pub const ABI_NAME_NAME_ARG_NAME: &str = "name";
pub const EVENT_ATTRIBUTE_NAME: &str = "event";
pub const INDEXED_ATTRIBUTE_NAME: &str = "indexed";

// Require Attributes
pub const REQUIRE_ATTRIBUTE_NAME: &str = "require";
pub const REQUIRE_ARG_NAME_TRIVIALLY_ENCODABLE: &str = "trivially_encodable";
Comment thread
xunilrj marked this conversation as resolved.
pub const REQUIRE_ARG_NAME_TRIVIALLY_DECODABLE: &str = "trivially_decodable";

pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
STORAGE_ATTRIBUTE_NAME,
DOC_COMMENT_ATTRIBUTE_NAME,
Expand All @@ -78,6 +83,7 @@ pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
ABI_NAME_ATTRIBUTE_NAME,
EVENT_ATTRIBUTE_NAME,
INDEXED_ATTRIBUTE_NAME,
REQUIRE_ATTRIBUTE_NAME,
];

/// An attribute declaration. Attribute declaration
Expand Down
7 changes: 7 additions & 0 deletions sway-ast/src/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ pub enum Literal {
}

impl Literal {
pub fn as_string(&self) -> Option<&str> {
match self {
Literal::String(v) => Some(v.parsed.as_str()),
_ => None,
}
}

/// Friendly type name string of the [Literal] used for various reportings.
pub fn friendly_type_name(&self) -> &'static str {
use Literal::*;
Expand Down
15 changes: 13 additions & 2 deletions sway-core/src/ir_generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use std::{
collections::HashMap,
hash::{DefaultHasher, Hasher},
};

use sway_error::error::CompileError;
use sway_features::ExperimentalFeatures;
use sway_ir::{
Expand Down Expand Up @@ -359,12 +358,16 @@ pub fn compile_program<'a>(
.collect();

let mut ctx = Context::new(engines.se(), experimental, backtrace);
ctx.program_kind = match kind {
let k = match kind {
ty::TyProgramKind::Script { .. } => Kind::Script,
ty::TyProgramKind::Predicate { .. } => Kind::Predicate,
ty::TyProgramKind::Contract { .. } => Kind::Contract,
ty::TyProgramKind::Library { .. } => Kind::Library,
};
ctx.program_kind = k;

let module = Module::new(&mut ctx, k);
let mut md_mgr = MetadataManager::default();

let mut compiled_fn_cache = CompiledFunctionCache::default();
let mut panicking_fn_cache = PanickingFunctionCache::default();
Expand All @@ -384,6 +387,8 @@ pub fn compile_program<'a>(
&mut panicking_fn_cache,
&test_fns,
&mut compiled_fn_cache,
&mut md_mgr,
module,
),
ty::TyProgramKind::Predicate { entry_function, .. } => compile::compile_predicate(
engines,
Expand All @@ -397,6 +402,8 @@ pub fn compile_program<'a>(
&mut panicking_fn_cache,
&test_fns,
&mut compiled_fn_cache,
&mut md_mgr,
module,
),
ty::TyProgramKind::Contract {
entry_function,
Expand All @@ -415,6 +422,8 @@ pub fn compile_program<'a>(
&test_fns,
engines,
&mut compiled_fn_cache,
&mut md_mgr,
module,
),
ty::TyProgramKind::Library { .. } => compile::compile_library(
engines,
Expand All @@ -427,6 +436,8 @@ pub fn compile_program<'a>(
&mut panicking_fn_cache,
&test_fns,
&mut compiled_fn_cache,
&mut md_mgr,
module,
),
}?;

Expand Down
Loading
Loading