Skip to content

Add const generics support for fixed-size arrays in LUMOS #51

@rz1989s

Description

@rz1989s

Goal

Support const generic parameters for fixed-size arrays, enabling flexible array sizes without code duplication.

Phase: 5.3 Advanced Type System


Problem

Current array syntax is limited:

  • ❌ Only supports [T] (dynamic Vec)
  • ❌ No fixed-size array support [T; N]
  • ❌ Can't express Solana's common patterns (e.g., [u8; 32] for hashes)
  • ❌ Have to use Vec even when size is known

Solution

Add fixed-size array syntax with const generics.

Syntax

#[solana]
struct PlayerAccount {
    wallet: PublicKey,        // Already supported (32 bytes internally)
    inventory: [PublicKey; 10], // NEW: Fixed-size array
    data: [u8; 32],            // NEW: Fixed 32 bytes
}

Generated Code

Rust

#[account]
pub struct PlayerAccount {
    pub wallet: Pubkey,
    pub inventory: [Pubkey; 10],
    pub data: [u8; 32],
}

TypeScript

export interface PlayerAccount {
  wallet: PublicKey;
  inventory: PublicKey[]; // Array of exactly 10 items
  data: number[];         // Array of exactly 32 items
}

// Borsh schema
export const PlayerAccountBorshSchema = borsh.struct([
  borsh.publicKey('wallet'),
  borsh.array(borsh.publicKey(), 10, 'inventory'),
  borsh.array(borsh.u8(), 32, 'data'),
]);

Implementation

1. Parser Changes

Location: packages/core/src/parser.rs

pub enum Type {
    // Existing...
    Vec(Box<Type>),
    
    // NEW
    Array {
        element: Box<Type>,
        size: usize,
    },
}

impl Type {
    fn parse_array(input: &str) -> Result<Self, Error> {
        // Parse [T; N]
        // Extract element type T
        // Extract size N (must be const)
    }
}

2. Validation

Location: packages/core/src/transform.rs

fn validate_array_size(size: usize) -> Result<(), Error> {
    if size == 0 {
        return Err("Array size must be > 0");
    }
    if size > 1024 {
        return Err("Array size too large (max 1024)");
    }
    Ok(())
}

3. Rust Generator

Location: packages/core/src/generators/rust.rs

fn generate_type(ty: &Type) -> String {
    match ty {
        Type::Array { element, size } => {
            format!("[{}; {}]", generate_type(element), size)
        }
        // ... other cases
    }
}

4. TypeScript Generator

Location: packages/core/src/generators/typescript.rs

fn generate_borsh_schema_field(field: &Field) -> String {
    match &field.ty {
        Type::Array { element, size } => {
            let elem_borsh = generate_borsh_type(element);
            format!("borsh.array({}, {}, '{}')", elem_borsh, size, field.name)
        }
        // ... other cases
    }
}

Examples

Example 1: Solana Hash

struct Transaction {
    hash: [u8; 32],
}

Example 2: Fixed Inventory

struct Player {
    inventory_slots: [PublicKey; 50],
}

Example 3: Nested Arrays

struct Grid {
    cells: [[u8; 10]; 10], // 10x10 grid
}

Borsh Serialization

Fixed arrays in Borsh:

  • No length prefix (size known at compile time)
  • More efficient than Vec
  • Exact byte count
// Vec<u8> serialization: [length: 4 bytes] + [data: N bytes]
// [u8; 32] serialization: [data: 32 bytes] (no length prefix)

Const Generics (Future Enhancement)

For now, only literal sizes supported:

inventory: [PublicKey; 10]  // ✓ Supported

Future: Generic sizes

struct Holder<const N: usize> {
    items: [PublicKey; N],  // 🚧 Not yet supported
}

Testing

Unit Tests

#[test]
fn test_parse_fixed_array() {
    let ty = parse_type("[u8; 32]");
    assert!(matches!(ty, Type::Array { size: 32, .. }));
}

#[test]
fn test_array_size_validation() {
    assert!(validate_array_size(0).is_err());
    assert!(validate_array_size(10).is_ok());
    assert!(validate_array_size(2000).is_err());
}

#[test]
fn test_generate_rust_array() {
    let ty = Type::Array {
        element: Box::new(Type::U8),
        size: 32,
    };
    assert_eq!(generate_type(&ty), "[u8; 32]");
}

Integration Tests

  • Parse schema with fixed arrays
  • Generate Rust code
  • Compile and verify size
  • Generate TypeScript
  • Verify Borsh serialization

E2E Tests

#[test]
fn test_fixed_array_serialization() {
    let data = PlayerAccount {
        inventory: [Pubkey::default(); 10],
    };
    
    let serialized = data.try_to_vec().unwrap();
    // Verify size = 10 * 32 bytes = 320 bytes (no length prefix)
    assert_eq!(serialized.len(), 320);
}

Success Criteria

  • Parser accepts [T; N] syntax
  • Validates array size (1-1024)
  • Generates correct Rust fixed arrays
  • Generates correct TypeScript Borsh schema
  • Borsh serialization works without length prefix
  • Supports nested arrays
  • All tests passing
  • Documentation with examples

Documentation

Update Files

  • docs/syntax.md - Document fixed array syntax
  • docs/types.md - Add array type section
  • README.md - Add fixed array example
  • CHANGELOG.md - Document new feature
  • ROADMAP.md - Mark Phase 5.3 item as complete ✅

Related

  • ROADMAP.md Phase 5.3 - Advanced Type System

Priority: High (common Solana pattern)
Complexity: Medium
Timeline: 3-4 days


📌 Remember: Update ROADMAP.md after completing this issue!

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreCore compiler (parser, generator, IR, transform)type:enhancementImprovement to existing featuretype:featureNew feature or functionality

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions