rust-lang / rust-lang/rust

Niche in Arc<..> variant of enum not used by Option<..>

Open
#125,363 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-layout C-optimization T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Consider the following code (playground):

#![allow(dead_code)]
use std::mem::size_of;
use std::sync::Arc;

enum Foo {
  A(&'static str),
  B(Arc<()>),
}

enum Bar {
  A(&'static str),
  B(Option<Arc<()>>),
}

fn main() {
    println!("sizes of Foo: {} w/ Option {}", size_of::<Foo>(), size_of::<Option<Foo>>());
    print!("repr of Foo::A(..):            ");
    dump_repr(Foo::A("hello"));
    print!("repr of Foo::B(..):            ");
    dump_repr(Foo::B(Arc::new(())));
    print!("repr of Option<Foo>::None:     ");
    dump_repr(Option::<Foo>::None);
    print!("repr of Option<Foo>::Some(..): ");
    dump_repr(Option::<Foo>::Some(Foo::A("hello")));
    println!("sizes of Bar: {} w/ Option {}", size_of::<Bar>(), size_of::<Option<Bar>>());
    print!("repr of Bar::A(..):            ");
    dump_repr(Bar::A("hello"));
    print!("repr of Bar::B(Some(..)):      ");
    dump_repr(Bar::B(Some(Arc::new(()))));
    print!("repr of Bar::B(None):          ");
    dump_repr(Bar::B(None));
}

fn dump_repr<T>(a: T) {
    let sz = size_of::<T>();
    let usize_sz = size_of::<usize>();
    assert_eq!(sz % usize_sz, 0);
    let n_usize = sz / usize_sz;
    let b = &a as *const T as *const usize;
    let b = unsafe { std::slice::from_raw_parts(b, n_usize) };
    for word in b {
        print!("{:016x} ", word);
    }
    println!();
}

The Foo enum contains two niches:

  • The slice in Foo::A(&str) has a non-nullable field.
  • The Arc in Foo::B(Arc<_>) has a non-nullable field.

The compiler correctly places the distinction between the variants A and B in one of these fields, concretely in the non-nullable field of the slice. The pointer for the Arc is then placed in the second half of the memory, which is otherwise the slice's length.

Unfortunately, wrapping Foo in Option<_> does not make use of the remaining niche: It could set both niches to null (basically selecting the Foo::B variant and nulling the Arc's NotNull pointer) to indicate the None variant.

Instead, we get a separate discriminant field, as can be seen by running the above example.

Note that the niche in Arc is not used at all by Foo. This can be seen in the Bar version of the enum, whose size does not increase by changing the Arc<_> into an Option<Arc<_>>.

I honestly have no idea about how niche tracking works and how much work it is to implement this, but I thought I'd leave this here as a kind of inspiration or tracking issue.

I note that there are a couple issues around this, but I think that this is not a duplicate of:

  • #101567 because that discusses the use of multiple niches within the same enum, which I understand has invisible performance drawbacks (needs more than one load to understand which variant we're looking at).

    I think that does not apply because Option<Foo>, as it stands, also needs two loads to figure out which Foo variant (if any) we're looking at: One to see if the Option is None, and a second one to see if the slice's ptr is null (Foo::B) or not (Foo::A).

  • #119507 could be related, but it's about structs. Not sure if that qualifies as dup.

  • #121333 seems to be about attempting to exploit "opposite" niches in order to unify types.

Thank you for reading.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Run the linked Rust Playground example and use the Foo/Bar size_of and dump_repr output as the baseline. Compare the behavior with related issues #101567, #119507, and #121333; done means Option uses the remaining Arc niche without an extra discriminant while preserving the enum representations.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.