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.

Reply via email to