Not only for this specific case, but in general x in a..b is compiled into x >= a && x <= b. It will create a range object if we actually create a local variable, pass a range between functions, etc.
it.x in a()..b() doesn’t break it, meaning that it still doesn’t create a range, but do: it.x >= a() && it.x <= b(). Of course, we call a and b with each iteration, but we asked for this.
Also, please note it.x is acquired only once. So this is not exactly it.x >= a && it.x <= b, but more like: val x = it.x; x >= a && x <= b.
In the interest of learning to fish : you can look at the Kotlin bytecode to help you answer this kind of question. In IntelliJ or Android studio, you can show it with Tools > Kotlin > Show Kotlin bytecode.
In this case, given the following code on the JVM
fun foo(l : List<Int>) : List<Int> {
return l.filter { it in 1..100 }
}
Here you can see that the bytecode is first loading the value from the boxed int into var 8 (Number.intValue() then ISTORE 8), then pushing 100 onto the stack BIPUSH 100 and then pushing 1 ICONST 1, after which it pushes the number in var 8 twice (ILOAD 8).
That done it compares the number to 100, which are the two top stack elements IF_ICMPLE L11 (int compare lower or equal) and if the comparison succeeds it pushes the number onto the stack again and compares it to 1 IF_ICMPLT L12. If that comparison succeeds again, it will go on to call Collection.add().
This way, you can directly observe that broot was correct saying that this is compiled into x >= a && x <= b.
As a side note, on the performance side notice how this function directly invokes .iterator() and hasNext() and next() right in this function, courtesy of filter being an inline function.