Skip to content

spec shaking v2: type alias for function signatures produces invalid specs #1857

Description

@mootz12

What version are you using?

main with feature expiremental_spec_shaking_v2

What did you do?

Used a Rust type alias in a contract function signature. This applies if the aliased type is a primitive like i128 or a custom type like in the example below.

Minimal example:

#![no_std]

use soroban_sdk::{contract, contractimpl, contracttype};

#[contracttype]
#[derive(Clone)]
pub struct Item {
    pub amount: i128,
}

pub type ItemAlias = Item;

#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
    pub fn echo(item: ItemAlias) -> ItemAlias {
        item
    }
}

Under spec shaking v2, generated boundary code roots reachable types by calling the marker method for the function parameter and return types, conceptually:

<ItemAlias as SpecShakingMarker>::spec_shaking_marker();

But ItemAlias is only a Rust type alias. Rust trait lookup normalizes it to the aliased type, so this call resolves as if it were:

<Item as SpecShakingMarker>::spec_shaking_marker();

That means the surviving marker is the marker for the Item spec entry.

At the same time, the contract spec macro builds the public function spec from the source syntax. It sees the token ItemAlias in the function signature and emits a function spec that references UDT name ItemAlias.

This creates two different views of the same signature:

  • contractspecv0 function entry says the function uses UDT ItemAlias.
  • T::spec_shaking_marker() roots the spec entry for UDT Item.

What did you expect to see?

Either of these would be acceptable:

  1. The SDK rejects source-level type aliases in contract-facing signatures with a clear compile-time error.
  2. The SDK resolves the alias consistently, so both the public function spec and the spec-shaking marker refer to the same UDT, Item.

In either case, spec shaking should not produce a final spec where a function references a UDT name that has no corresponding UDT spec entry.

What did you see instead?

For the example above, the marker machinery keeps Item, but the public function spec can still reference ItemAlias. After shaking, this can produce an invalid or unusable contract spec: generated import/client code expects a type named ItemAlias, but the actual UDT spec entry is named Item.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions