Skip to content

Ptr::decay does not work correctly with nested containers #425

Description

@joaotgouveia

The following C++ program:

int main() {
    std::vector<std::string> v1 = {"a"};
    assert(v1.back().at(0) == 'a');
    return 0;
}

Is incorrectly translated to the following:

fn main_0() -> i32 {
    let v1: Value<Vec<Vec<u8>>> = Rc::new(RefCell::new(vec![{
        let mut __bytes = Ptr::<u8>::from_string_literal(b"a").to_c_bytes();
        __bytes.push(0);
        __bytes
    }]));
    assert!(
        (((if 0_usize as usize
            >= (*((v1.as_pointer() as Ptr<Vec<u8>>).to_last() as Ptr<Vec<u8>>)
                .upgrade()
                .deref())
            .len()
            .saturating_sub(1)
        {
            panic!("out of bounds access")
        } else {
            ((v1.as_pointer() as Ptr<Vec<u8>>).to_last() as Ptr<Vec<u8>>)
                .decay() // panic: 'ub: invalid decay'
                .offset(0_usize as isize)
        }
        .read()) as i32)
            == (('a' as u8) as i32))
    );
    return 0;
}

This transation is generated using the following string rule:

fn f26(a0: Ptr<Vec<u8>>, a1: usize) -> Ptr<u8> {
    if a1 as usize >= (*a0.upgrade().deref()).len().saturating_sub(1) {
        panic!("out of bounds access")
    } else {
        a0.decay().offset(a1 as isize)
    }
}

This rule is written assuming that a0 is of kind StackSingle/HeapSingle, which is the case whenever this rule is applied to a plain std::string reference. However, if the std::string reference is obtained through a container (for instance, via std::vector::back), the corresponding Ptr will be of kind StackVec/HeapVec, and calling decay on it fails.

Another issue with Ptr::decay is that its invocation can be considered ambiguous under certain circunstances.
The following C++ program:

int main() {
    std::vector<std::vector<int>> v1;
    std::vector<int> v2 = {1};
    v1.push_back(v2);

    assert(v1.back().at(0) == 1);
    return 0;
}

Is translated to:

fn main_0() -> i32 {
    let v1: Value<Vec<Value<Vec<i32>>>> = Rc::new(RefCell::new(Vec::new()));
    let v2: Value<Vec<i32>> = Rc::new(RefCell::new(vec![1]));
    (v1.as_pointer() as Ptr<Vec<Value<Vec<i32>>>>).with_mut(|__v: &mut Vec<Value<Vec<i32>>>| {
        __v.push(Rc::new(RefCell::new((*v2.borrow()).clone())))
    });
    assert!(
        ((((*v1.borrow())[(*v1.borrow()).len() - 1]
            .as_pointer()
            .decay() as Ptr<i32>) // compilation error: 'error[E0034]: multiple applicable items in scope'
            .offset(0_usize as isize)
            .read())
            == 1)
    );
    return 0;
}

The decay in this translation is emitted by the converter itself.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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