So strange result of wp_query and have_posts

If we look at have_posts() in source, we can see why this happens:

function have_posts() {
    if ( $this->current_post + 1 < $this->post_count ) {
        return true;
    } elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
        do_action_ref_array('loop_end', array(&$this));
        // Do some cleaning up after the loop
        $this->rewind_posts();
    }

    $this->in_the_loop = false;
    return false;
}

When it reaches the end of the loop, it calls rewind_posts, which resets current_post back to -1, so the next call to have_posts will return true again.

function rewind_posts() {
    $this->current_post = -1;
    if ( $this->post_count > 0 ) {
        $this->post = $this->posts[0];
    }
}