That's why relying on shadowing is usually a bad idea, even if the language allows that. It exists for simple copies and for macro authors, but using it willy-nilly is just inviting subtle errors.
Because you cannot match on variables in patterns (including match and if let), you can only match on constants, and create new variable bindings. Since if let-chains are still unstable, let's rewrite your code with match:
let product = i * j;
let some_even = Even::new(product);
match some_even {
Some(x) => {
let product = x;
/* do stuff */
}
...
That's what your code is equivalent to. If you need to compare the contents of Some with a variable, you need to either do the comparison explicitly in the match arm, or use a match guard:
let product = i * j;
let some_even = Even::new(product);
match some_even {
Some(x) if x == product => {
/* do stuff */
}
...
The reason you can't match against variables is that match expressions are optimized to highly efficient variant selection, which wouldn't be possible with arbitrary equality comparisons, and also because == operator is overloadable and can execute arbitrary code. This would make it impossible to guarantee match exhaustiveness checking. While you can still run the same code in match guards, you are explicitly opting into more complicated semantics.
The binding shadowing footgun is exactly why the naming convention for constants is SCREAMING_CASE. This way it is immediately distinguishable whether a symbol in a match pattern means comparison with a constant or a new variable binding.