-
Notifications
You must be signed in to change notification settings - Fork 4
A principled approach to erasing typedef
s from C types (and canonicalising C types)
#1145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
typedef
s from C types (and canonicalising C types)
26816dd
to
477c7ec
Compare
9649cbd
to
c6204d8
Compare
c6204d8
to
604361b
Compare
5f5adbd
to
58f3462
Compare
#1139 merged :) |
4295aff
to
feffc1c
Compare
feffc1c
to
b8082f1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, feel free to merge this once the changes we discussed are done.
For the record, dealing correctly with macros is outsourced to #1200.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments from a review together with @edsko
hs-bindgen/src-internal/HsBindgen/Frontend/Pass/Parse/IsPass.hs
Outdated
Show resolved
Hide resolved
hs-bindgen/src-internal/HsBindgen/Frontend/Pass/Parse/IsPass.hs
Outdated
Show resolved
Hide resolved
C.TypeTypedef (TypedefRefRegular nm uTy) -> do | ||
(uTyDepIds, uTy') <- resolve uTy | ||
let mkTypeTypedef = C.TypeTypedef . flip TypedefRefRegular uTy' | ||
(tdDepIds, td) <- auxN mkTypeTypedef nm C.NameKindOrdinary | ||
pure (uTyDepIds `Set.union` tdDepIds, td) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C.TypeTypedef (TypedefRefRegular nm uTy) -> do | |
(uTyDepIds, uTy') <- resolve uTy | |
let mkTypeTypedef = C.TypeTypedef . flip TypedefRefRegular uTy' | |
(tdDepIds, td) <- auxN mkTypeTypedef nm C.NameKindOrdinary | |
pure (uTyDepIds `Set.union` tdDepIds, td) | |
C.TypeTypedef (TypedefRefRegular nm uTy) -> do | |
(_uTyDepIds, uTy') <- resolve uTy | |
let mkTypeTypedef = C.TypeTypedef . flip TypedefRefRegular uTy' | |
(tdDepIds, td) <- auxN mkTypeTypedef nm C.NameKindOrdinary | |
pure (tdDepIds, td) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TravisCardwell does it make sense that we do not include the dependencies of the underlying type in the result?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some context: references to typedefs are annotated with the type Type p
underlying the typedef. Applying pass p'
after p
, we still have to convert the annotation from Type p
to Type p'
. We do this here by calling resolve
on the annotation, but not returning the dependencies.
b7386ff
to
8761811
Compare
So that we can check whether we erase typedefs succesfully in later commits.
This makes typedef erasure a local operation, and now we no longer need a mapping of typedef names to typedef definitions. Moreover, we add type-level tags to the `Type` (representing C types) datatype in the backend. These tags allow us to statically guarantee that some type constructs are omitted from the `Type` datatype. We add simple algorithms to map full C `Type`s to erased (no typedefs) and canonical (no typedefs and const qualifiers) types. These erased/canonical types and their mappings are now used instead of ad-hoc typedef erasure using incomplete information.
8761811
to
768950a
Compare
The Example: typedef struct point {
double x;
double y;
} point;
struct rect {
point tl;
point br;
}; graph TD;
v1["PrelimDeclIdNamed "point""]
v0["PrelimDeclIdNamed "point""]
v2["PrelimDeclIdNamed "rect""]
v1-->|"UsedInTypedef ByValue"|v0
v2-->|"UsedInField ByValue "br""|v1
v2-->|"UsedInField ByValue "tl""|v1
Imagine the Returning the dependencies of the underlying type is indeed not necessary, as there are no edges between Hmmm, that BTW, I implemented the |
Not traversing isn't really an option, unless we do some type trickery. Perhaps we should have a chat about this with the three of us. |
I don't understand the resolve bindings spec pass well enough to understand why it would be incorrect to recurse into the underlying type. Is it harmful? We're mostly piggy-backing off of the recursive call to |
Ack! Recursing on the underlying type is indeed needed when processing the |
Resolves #1142.
Resolves #1050.
This PR builds on top of PR #1139, so that one should be merged first.
This PR is an experiment to come up with a more principle approach to erasing
typedef
s from C types where necessary. One such example occurs when we want to check if a type is a function type so that we can decide whether to usePtr
orFunPtr
in the Haskell code for the bindings. To do that reliably we should erasetypedefs
andconst
qualifiers.Most of the effort in this PR is related to adding some type safety when inspecting C types. Meaning, if you want to check that a type is a canonical function type, one would first map the full C type to a canonical type and then check if it is a function type. There is currently nothing preventing us from inspecting the full C type when we should be inspecting the corresponding canonical C type instead, but the types should hopefully make it clearer that we should do the mapping before the inspecting.
There are two commits in this PR, each with a separate attempt at implementing this. Attempt 1 was with higher-kinded datatypes, which I didn't like so much, and it is not so complete as Attempt 2. Attempt 2 uses a Trees That Grow style, though it's more like a Trees That Shrink style.