That's (usually) only true for the very first '->' in a chain and as you said, depends on the compiler figuring out if the pointer indirection can be resolved at compile time.
A chain of '.' on the other hand is always guaranteed to be resolved into a single offset at compile time.
> Why does C even have a two member selection operators?
Because using `(*ptr).member` everywhere is annoying. There's plenty of times you want or need to have direct access to a member rather than always dereferencing a pointer.
It's too bad C's pointer-deref operator is prefix instead of postfix. In Pascal it's ^ so you write ptr^.member and there's no special -> operator. Even better, declarations and expressions would read intuitively left-to-right instead of spiraling out through stars on the left and brackets on the right.
C was practically a portable assembler when it was designed, and it was likely helpful for performance reasoning that all indirections were clearly visible.
Anyway it means you can't cut and paste code from one place to another without changing '.' to '->' or vice-versa.