because in your code snippet above, the compiler doesn't need to know the size of struct foo, just the size of a pointer to struct foo, which is independent of the actual definition of the structure.
Now, if you had written:
struct foo *t = malloc(sizeof(struct foo));
That would be a different story, since now the compiler needs to know how much memory to allocate.
Additionally, if you at an point you try to access a member of a struct foo* (or dereference a pointer to a foo):
((struct foo*)t)->x = 3;
The compiler would also complain, since at this point it needs to know the offset into the structure of x.
As an aside, this property is useful to implement an Opaque Pointer.