You can actually do this in C as well. The Windows API has all sorts of handle types that were originally all one type: HANDLE; but by wrapping a HANDLE in various one-member structs were able to derive different handle types that couldn't be intermixed with each other in a type-safe way without some casting jiggery-pokery.
It's just much, much easier and more ergonomic in Ada.
Fun fact, that many are not aware, mostly because this is Windows 3.x knowledge and one needed the right source to learn about this.
There was a header only library on the Windows SDK that would wrap those HANDLEs into more specific types, that would still be compatible, while providing a more high level API to use them from C.
Unfortunely there is not much left on the Internet about it, but this article provides some insight,
https://www.codeguru.com/windows/using-message-crackers-in-t...
Naturally it was saner just to use TP/C++ alongside OWL, C++ with MFC back then, or VB.