From: Phillip Wood Date: Sat, 17 Jan 2026 13:55:46 GMT Subject: Re: [PATCH 01/10] ivec: introduce the C side of ivec Message-ID: In-Reply-To: On 16/01/2026 20:19, René Scharfe wrote: > On 1/16/26 11:39 AM, Phillip Wood wrote: >> I've Cc'd Peff and René for a second opinion if you have time please. >> >> On 15/01/2026 15:55, Ezekiel Newren wrote: >>> On Thu, Jan 8, 2026 at 7:34 AM Phillip Wood wrote: >>> >>>>> +static void _set_capacity(void *self_, size_t new_capacity) >>>>> +{ >>>>> +     struct IVec_c_void *self = self_; >>>> >>>> Passing any of the ivec variants defined below to this function invokes >>>> undefined behavior because we're not casting the pointer back to the >>>> orginal type. However I think on the platforms we care about >>>> sizeof(void*) == sizeof(T*) for all T so maybe we can look the other way. >>> >>> If someone finds that this code does not work because of this >>> assumption I'd like to know. But I can't fathom a case where it >>> wouldn't work. >> >> So we have two different structs >> >> struct IVec_c_void { >>     void *ptr; >>     size_t length; >>     size_t capacity; >>     size_t element_size; >> } >> >> and >> >> struct Ivec_u8 { >>     uint8_t *ptr; >>     size_t length; >>     size_t capacity; >>     size_t element_size; >> } >> >> One the platforms we care about they will have the same memory >> layout as all pointers have the same representation. However I don't >> think they are "compatible types" in the language of the C standard >> because the type of the "ptr" member differs. That means casting >> IVec_u8* to IVec_c_void* either directly or via void* is undefined >> and so >> >>     struct IVec_u8 vec; >>     ivec_init(&vec, sizeof(*vec.ptr)); >> >> is undefined. For the compiler to see the undefined cast it needs to >> look across translation units because the implementation of >> ivec_init() will be in a separate file to where it is called. Maybe >> that and the fact they have the same memory layout saves us from >> having to worry too much though I'm always nervous of undefined >> behavior. > > True. The GCC docs give a fun example of what a compiler might do > when using different struct types to access the same memory: > > https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Aliasing-Type-Rules.html Thanks for the link > Not sure it applies to this case, but the point is that compilers > can and will do terrifying things when they smell UB, with little > concern for safety or original intent. > >> An alternative would be to pass the individual struct members as function parameters >> >>     void ivec_init(void **vec, size_t &length, size_t &capacity, >>                size_t &element_size_, size_t element_size) >>     { >>         *vec = NULL; >>         *length = 0; >>         *capacity = 0; >>         *element_size_ = element_size; >>     } > > The ampersands (&) should be asterisks (*), right? Indeed, that's embarrassing - I must have been thinking of the caller. >> and have DEFINE_IVEC_TYPE create typesafe wrappers >> >>     static inline void ivec_u8_init(struct IVec_u8 *vec) >>     { >>         void *ptr = vec->ptr; >>         ivec_init(&ptr, &v->length, &v->capacity, >>               &v->element_size, sizeof(*(v->ptr)); >>         vec->ptr = ptr; >>     } > > Mixes "v" and "vec", misses a closing parenthesis. Looks viable, > though, and this method should be applicable to the rest of the > functions as well (on the C side). > > I guess this doesn't require an element_size member anymore as > each wrapper can pass in the sizeof value. Good point >> That's safe because we cast the "ptr" member to "void*" and then >> back to the original type. On the rust side the implementation of >> IVec would also need to split out the individual struct members >> when it calls ivec_init() etc. It's all a bit more effort but the >> benefit is that we don't have any undefined behavior and we have a >> nice typesafe C interface to 'struct IVec_*'. > Right. No idea how ugly this would be on the Rust side, though. I'm hoping it's not too bad and `impl IVec` just contains the equivalent of the wrappers generated by DEFINE_IVEC_TYPE() Thanks Phillip > > René >