frxstrem / cain

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Improper identifier hygiene in match arm patterns

frxstrem opened this issue · comments

Reported by Reddit user u/pinespear in this comment:

You have some problems with hygiene of identifiers. Basically, identifiers from inner scope can pollute outer scope and it leads to functionally different logic being executed.

Example is a bit artificial, because I tried to minimize it to show the idea:

This works without panic:

let val = Some(vec![0_usize]);

let mut v = vec![1];

match val {
    Some(mut v) => drop(v.pop()),
    None => {}
};

v.pop().unwrap();

This panics:

let val = Some(vec![0_usize]);

let mut v = vec![1];

cain! {
    let _ = match val {
        Some(mut v) => drop(v.pop()),
        None => {}
    };

    v.pop().unwrap();
}

Reason it panics is it expands to:

let val = Some(<[_]>::into_vec(box [0_usize]));
let mut v = <[_]>::into_vec(box [1]);
{
    match val {
        Some(mut v) => {
            let _ = drop(v.pop());
            v.pop().unwrap();
        }
        None => {
            let _ = {};
            v.pop().unwrap();
        }
    }
}

and second v.pop() gets executed on inner identifier v except of outer identifier v

This is trivial case which can be solved by "sanitizing" identifiers in arms and "un-sanitizing" them in the scope so they won't get leaked:

    match val {
        Some(mut v_randomized213345) => {
            let _ = {
                let mut v = v_randomized213345;
                drop(v.pop())
            };
            v.pop().unwrap();
        }
        None => {
            let _ = {};
            v.pop().unwrap();
        }
    }

but there may be other cases which are harder to notice, I'm not sure how can you formally guarantee that code logic and behavior won't get affected.