That’s where we differ I guess. For me, for is the construct that does the introducing of the binding for me. I read a for, new binding. I read an if, it’s an expression, and thus doesn’t introduce a new binding.
At least currently, $ident in $expr is not an expression. The construct is for ... in ... { ... }, and in is not its own thing; for is the actual driving force in this situation.
Let’s be explicit: what is your suggested desugar for each of: (and consequently, which pass typeck)
Given a pre-existing variable it, statement context
x in it;
x in it;
x in it;
iter::once(x in it);
let y = x in it;
let y = x in it;
let z = x in it;
if x in it {
do_something(x);
}
let x = if x in it {
do_something(x)
} else {
default()
};
if x in it {
do_something(x);
}
if x in it {
do_something(x);
}
if x in iter::once(x in it) {
do_something(x);
}
let y = iter::once(x in it);
if x in y {
do_something(x);
}
(Repeat every case of if ... in ... { with for and while instead of if)
Illegal construct is a viable answer. Please make sure that you provide a reasoning for what is and isn’t legal, though, and it cannot depend on types – that’s typeck, which is after the parse.
EDIT 1: since you seem to be implying that x in iter would be an expression, throw in some other combinations like logical operators as well, x1 in it1 && x2 in it2, x in it1 || x in it2, in the above contexts as well.
EDIT 2: if you can provide a solid consistent rule for how in is desugared that can be applied to all of these cases, you don’t have to enumerate them; I/we can pick out the problematic ones then.