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 identifierv
except of outer identifierv
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.