Skip to content

Commit

Permalink
lang: Handle arrays with const as length (#968)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomlinton committed Dec 23, 2021
1 parent f4fe7d4 commit 51aeb08
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -23,6 +23,7 @@ incremented for features.
* lang,ts,ci,cli,docs: update solana toolchain to version 1.8.5([#1133](https://github.com/project-serum/anchor/pull/1133))
* ts: Add optional commitment argument to `fetch` and `fetchMultiple` ([#1171](https://github.com/project-serum/anchor/pull/1171))
* lang: Add `set_inner` method to `Account<'a, T>` to enable easy updates ([#1177](https://github.com/project-serum/anchor/pull/1177))
* lang: Handle arrays with const as length ([#968](https://github.com/project-serum/anchor/pull/968)).

### Breaking

Expand Down
34 changes: 33 additions & 1 deletion lang/syn/src/idl/file.rs
Expand Up @@ -402,9 +402,14 @@ fn parse_ty_defs(ctx: &CrateContext) -> Result<Vec<IdlTypeDefinition>> {
.map(|f: &syn::Field| {
let mut tts = proc_macro2::TokenStream::new();
f.ty.to_tokens(&mut tts);
// Handle array sizes that are constants
let mut tts_string = tts.to_string();
if tts_string.starts_with('[') {
tts_string = resolve_variable_array_length(ctx, tts_string);
}
Ok(IdlField {
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
ty: tts.to_string().parse()?,
ty: tts_string.parse()?,
})
})
.collect::<Result<Vec<IdlField>>>(),
Expand Down Expand Up @@ -455,6 +460,33 @@ fn parse_ty_defs(ctx: &CrateContext) -> Result<Vec<IdlTypeDefinition>> {
.collect()
}

// Replace variable array lengths with values
fn resolve_variable_array_length(ctx: &CrateContext, tts_string: String) -> String {
for constant in ctx.consts() {
if constant.ty.to_token_stream().to_string() == "usize"
&& tts_string.contains(&constant.ident.to_string())
{
// Check for the existence of consts existing elsewhere in the
// crate which have the same name, are usize, and have a
// different value. We can't know which was intended for the
// array size from ctx.
if ctx.consts().any(|c| {
c != constant
&& c.ident == constant.ident
&& c.ty == constant.ty
&& c.expr != constant.expr
}) {
panic!("Crate wide unique name required for array size const.");
}
return tts_string.replace(
&constant.ident.to_string(),
&constant.expr.to_token_stream().to_string(),
);
}
}
tts_string
}

fn to_idl_type(f: &syn::Field) -> IdlType {
let mut tts = proc_macro2::TokenStream::new();
f.ty.to_tokens(&mut tts);
Expand Down
7 changes: 7 additions & 0 deletions tests/misc/programs/misc/src/account.rs
@@ -1,5 +1,7 @@
use anchor_lang::prelude::*;

pub const MAX_SIZE: usize = 10;

#[account]
pub struct Data {
pub udata: u128,
Expand Down Expand Up @@ -41,3 +43,8 @@ pub struct DataWithFilter {
pub struct DataMultidimensionalArray {
pub data: [[u8; 10]; 10],
}

#[account]
pub struct DataConstArraySize {
pub data: [u8; MAX_SIZE],
}
6 changes: 6 additions & 0 deletions tests/misc/programs/misc/src/context.rs
Expand Up @@ -330,3 +330,9 @@ pub struct TestMultidimensionalArray<'info> {
#[account(zero)]
pub data: Account<'info, DataMultidimensionalArray>,
}

#[derive(Accounts)]
pub struct TestConstArraySize<'info> {
#[account(zero)]
pub data: Account<'info, DataConstArraySize>,
}
5 changes: 5 additions & 0 deletions tests/misc/programs/misc/src/lib.rs
Expand Up @@ -87,6 +87,11 @@ pub mod misc {
Ok(())
}

pub fn test_const_array_size(ctx: Context<TestConstArraySize>, data: u8) -> ProgramResult {
ctx.accounts.data.data[0] = data;
Ok(())
}

pub fn test_close(_ctx: Context<TestClose>) -> ProgramResult {
Ok(())
}
Expand Down
18 changes: 18 additions & 0 deletions tests/misc/tests/misc.js
Expand Up @@ -836,6 +836,24 @@ describe("misc", () => {
assert.ok(account.data, 3);
});

it("Can use const for array size", async () => {
const data = anchor.web3.Keypair.generate();
const tx = await program.rpc.testConstArraySize(99, {
accounts: {
data: data.publicKey,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
signers: [data],
instructions: [
await program.account.dataConstArraySize.createInstruction(data),
],
});
const dataAccount = await program.account.dataConstArraySize.fetch(
data.publicKey
);
assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]);
});

it("Should include BASE const in IDL", async () => {
assert(
miscIdl.constants.find(
Expand Down

0 comments on commit 51aeb08

Please sign in to comment.