https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103964

--- Comment #9 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
(In reply to Ilya Maximets from comment #7)
> (In reply to Jakub Jelinek from comment #6)
> > What is the reason why OVS (and kernel) doesn't use 2 variables, one for the
> > iterator that is a pointer to the prev/next structure only and one assigned
> > e.g. in the condition from the iterator that is used only when it isn't the
> > start?
> > At least if targetting C99 and above (or C++) one can declare one of those
> > iterators in the for loop init expression...
> 
> The problem is that we need 2 variables and one of them need to be accessible
> outside of the loop.  And I don't think it is possible to declare one
> variable
> and only initialize another one.

If you only need to use one variable outside of the loop and not the other one,
that should be doable.  If you use one in some spots and another in other spots
but never both, you could have different macros for those 2 versions.

E.g. the one where the list iterator is used inside of the loop only and the
var pointing to the containing objects could look like:
        for (struct ovs_list *iter = (pos = NULL, &start); iter != (&start)
             && (((pos) = ((typeof(pos))(void*)((uintptr_t)((iter->next) -
                                           __builtin_offsetof(struct member,
                                                              elem))))), 1);
             iter = iter->next, pos = NULL)
which would get you after the loop pos = NULL if break; wasn't done and
pos non-NULL otherwise.
But in your testcase you actually need to use the other var after the loop,
so it would need to be done the other way around, but then the user variable
would need to be struct ovs_list * and the macro would need to be told in a
different way what type the var declared in the loop should have.

> One thing that is not clear to me is if the following code has an UB or not:
> 
>     struct member* pos;
>     struct ovs_list start;
> 
>     pos = (struct member *)(void*)((uintptr_t)(&start) - 64);
>     ovs_list_insert((void *)((uintptr_t)pos + 64), &member->elem);

It still creates a pointer out of something that doesn't point to such an
object or points to some unrelated one, doesn't necessarily have sufficient
alignment etc., so I think it is UB too, but perhaps the compiler at least now
will handle it the way you expect.  A lot of programs including e.g. POSIX rely
on at least
(void *) -1 and similar pointer constants to be usable in equality comparisons
(e.g. MAP_FAILED macro).

Reply via email to