In the case of free functions, its always valid, since any generic variables that it could use will be on the type signature, either directly (e.g <var X: usize>) or indirectly (the function takes a generic T and T has a generic variable). This means that once the function has been monomorphised, the compiler knows exactly which variables to pass in as arguments for each monomorphisation.
For methods/associated functions, it's a bit trickier. The rule would have to be "you can't use generic variables defined on an impl block in method bodies inside that impl block, unless the type of the impl block is directly mentioned in the signature of the method.". If that wasn't the case, you could create an associated function that uses generic variables defined on the type in its body, but otherwise takes no arguments. Then you could cast it to a function pointer, and now their is no indication on the type signature that is uses generic variables, or which generic variables.
Consider this example:
fn get_num() -> usize {
2
}
struct Foo<'a, var X: 'a usize>;
impl<'a, var X: 'a usize> Foo<'a, X> {
fn get_generic() -> usize {
X
}
}
fn assign_fn_ptr() {
var X: usize = 10;
let mut ptr: fn() -> usize = get_num;
ptr = Foo::<X>::get_generic;
}
Here, the free function get_num takes no arguments, no generic arguments, and returns a constant, 2. The associated function get_generic on Foo copies the generic variable X defined on Foo and returns it. Both get_num and get_generic have the same type signature, so they can be assigned to the same variable. This works fine for get_num because it doesn't capture anything. But for get_generic, because it captures the generic variable X on Foo, X must be passed as an argument for it to function. But the compiler can't distinguish between get_num and get_generic when they are cast to function pointers. For get_generic to be valid, it would need to have Foo appear somewhere in it's type signature, either as a regular argument, as self, or in the return type. This could also include appearing as a generic parameter on another type, for instance returning a Result<Self, Error> also counts as 'appearing in the type signature', since it appears as a generic parameter on Result. What wouldn't count, would be being used as a concrete field on another type. That could work in theory, but would create fragile code which depends on the compiler 'seeing inside' private types/fields to do type checking.
With this rule in place we can still use generic variables on traits like Default, which return a Self and Drop which takes an &mut self, but it precludes their use trait methods where Self doesn't appear as part of the type signature.