Skip to content

EnumSet with more variants than bits in uint silently corrupts data #13756

Closed
@SimonSapin

Description

@SimonSapin

Test case:

extern crate collections;
use collections::enum_set::{EnumSet, CLike};
use std::cast;

#[repr(uint)]
enum Foo {
    V00, V01, V02, V03, V04, V05, V06, V07, V08, V09,
    V10, V11, V12, V13, V14, V15, V16, V17, V18, V19,
    V20, V21, V22, V23, V24, V25, V26, V27, V28, V29,
    V30, V31, V32, V33, V34, V35, V36, V37, V38, V39,
    V40, V41, V42, V43, V44, V45, V46, V47, V48, V49,
    V50, V51, V52, V53, V54, V55, V56, V57, V58, V59,
    V60, V61, V62, V63, V64, V65, V66, V67, V68, V69,
}

impl CLike for Foo {
    fn to_uint(&self) -> uint {
        *self as uint
    }

    fn from_uint(v: uint) -> Foo {
        unsafe { cast::transmute(v) }
    }
}

fn main() {
    let mut set: EnumSet<Foo> = EnumSet::empty();
    set.add(V63);
    set.add(V64);
    set.add(V65);
    println!("{:?}", set.iter().collect::<~[Foo]>())
}

Expected result, in decreasing order of preference: ~[V63, V64, V65], compile-time failure, or run-time failure.

Actual result with rustc eea4909 on a 64 bit system: ~[V00, V01, V63]

Use case: Servo currently handles CSS declarations in order, later ones overwriting earlier ones. I’d like instead to process declarations in reverse order, and skip those for properties for which we already have a value. "Already have a value" would be stored in an EnumSet with one variant for every CSS property. There are a few hundreds of them. (The enum is automatically generated.) (Values can not be Options, because they’re initialized to the inherited or initial value rather than None.)

Possible fixes:

  1. To at least avoid silently corrupting data, have the bit function assert (non-disablable) that e.to_uint() < (size_of<uint>() * 8)
  2. Do 1., and also add a BigEnumSet type that uses Vec<uint> or ~[uint] internally for storage rather than just a single uint.
  3. Change EnumSet to decide at run-time to use ~[uint] or a single uint for storage, like collections::bitv::Bitv does.
  4. Like 3., but decide at compile-time what kind of storage to use. (Since the generic EnumSet is being monomorphized anyway.) I don’t know if this is possible.

3 or 4 might need the CLike trait to gain a max_value associated function (or something), which would ideally be based on introspection rather than leaving the counting to the user.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions