zend_portability: Add ZEND_CONTAINER_OF()#21903
Conversation
|
I think this relies on UB (pointer arithmetic on NULL) |
That branch is never evaluated; it's also the “accepted” solution to define |
This doesn't matter for UB.
The Linux kernel is compiled with various flags that makes various UB defined, so I'm not so sure this is a valid comparison. |
|
I believe it's not UB unless executed, but we can probably achieve something similar with static assets? |
The issue is that compilers can assume UB never happens, and then start removing stuff or doing whatever they want if that code is present (see famous blog article about UB: https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633) |
|
So, I have a different understanding.
Yes, but this branch is already provably dead. UB allows the compiler to make some assumptions. For example:
In other words, my understanding is: UB in dead branches has an effect on the result of the program, i.e. the compiler can assume the program is correct, and if the branch were reachable the program would not be correct, so the branch is unreachable. But that doesn't mean UB in dead branches makes the behavior of your program undefined if the branches are actually never reached. That's my understanding, though to be honest, the C spec is not nearly explicit enough about this... |
|
Unsure, but now I wonder if we even need this. |
It likely would, but my understanding is that it would be equally UB. You can't just cast between pointers of different types. (and this already affected the original implementation, you may go to My suggestion would be to just go with the established way of writing a |
|
Okay then |
|
Ah. C++. I'll see if I can add a template-variant. |
Changes made with Coccinelle:
@@
type T_container;
identifier member;
expression e;
@@
- (T_container *)(((char *)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
@@
type T_container;
identifier member;
expression e;
typedef uintptr_t;
@@
- (T_container *)(((uintptr_t)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
@@
type T_container;
identifier member;
expression e;
@@
- (const T_container *)(((char *)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
@@
type T_container;
identifier member;
expression e;
typedef uintptr_t;
@@
- (const T_container *)(((uintptr_t)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
A follow-up change to the new `ZEND_CONTAINER_OF()` macro will preserve the `const`-ness of the input pointer. By wrapping this macro inside a macro rather an an inline function we can preserve the `const`-ness across all layers.
Changes made with Coccinelle: