On Mon, 18 Mar 2019, Michael Matz wrote: > Hi, > > On Mon, 18 Mar 2019, Richard Biener wrote: > > > > Or, because an enum with these properties could be modelled as a struct > > > containing one member of basetype you could also change > > > record_component_aliases(), though that doesn't allow language specific > > > behaviour differences. > > > > No, you can't. I believe that enum X and int being compatible means > > that accessing an enum X object via an lvalue of effective type int > > is OK _and_ accessing an int object via an lvalue of effective type > > enum X is OK. > > I agree. But the objects type still remains whatever it was: either an > enum or an int; type equivalence/identity isn't affected by compatiblity. > > > That commutativity doesn't hold for struct X { int i; } > > vs. int i and those types are not compatible. > > True, but that's not the important aspect that the aliasing subsetting > expresses: if A subsets B or B subsets A, all accesses between an A and a > B conflict. If A is the "struct S1 {int}" and B is "int" that's exactly > right, an access to the struct and one to an int (_access_, where you > don't necessarily know the declared type) conflict. But if we have a C, > an "struct S2 {int}" the direction of the subsetting starts to matter: > A conflict B, and C conflict B, but !(A conflict C). This is exactly the > situation with A and C being different enums as well. > > The non-transitivity of the 'conflicts' relation is expressed by having a > direction in the alias subset relation: everything that is (or contains) > and A conflicts with everything that is (or contains) an int, everything > that is (or contains) a C conflicts with int, but nothing that is (or > contains) an A conflicts with anything that is (or contains) a C (if not > for other reasons). > > > At least unless Joseph corrects me here and that "type X is compatible > > with type Y" doesn't imply "type Y is compatible with type X" > > (that's not transitivity). > > That's not transitivity but symmetry, and that of course holds for > "compatible". X compat Y, and Z compat Y --> X compat Z would be > transitivity and doesn't hold. > > > Now, we can't currently model precisely this way of non-transitivity > > of C's notion of "compatibility". > > > > enum X { A }; > > enum Y { B }; > > void *ptr = malloc (4); > > *(enum X *)ptr = A; // dynamic type is now X > > foo (*(enum Y *)ptr); // undefined, X and Y are not compatible(?) > > foo (*(int *)ptr); // OK, X and int are compatible > > *(int *)ptr = *(enum X *); // dynamic type is now int > > foo (*(enum Y *)ptr); // OK(?), Y and int are compatible > > I'm pretty sure this can be expressed, in the way I suggested, making X > subset int, Y subset int (or superset? As said, I'm always confused), and > those above would work.
It doesn't really. Consider the following IMHO valid testcase: enum X { A, B }; enum Y { C, D }; void foo (int *p) { *(enum X *)p = A; *(enum Y *)p = D; return *(enum X *)p; } int main() { int storage; if (foo (&storage) != A) abort (); } where in C a store doesn't change the dynamic type of an object with a declared type so the dynamic type stays 'int' always and thus both stores happen via compatible types. But the middle-end just sees stores via type X and Y which are not C-compatible. The middle-end doesn't know anything about those stores not updating the dynamic type which means any valid lvalue access according to 6.5/7 may not change the alias-set. So as I already said, our implementation cannot express this non-transitive compatibility in a safe manner. Richard.