Bug 22674 – ImportC: compatible types declared in different translation units are not treated equivalent in D.

Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2022-01-14T07:37:50Z
Last change time
2022-09-22T07:00:55Z
Keywords
ImportC, pull
Assigned to
No Owner
Creator
dave287091

Comments

Comment #0 by dave287091 — 2022-01-14T07:37:50Z
This situation comes up if you are consuming two C headers that included a type from a header common to both of them. Consider the following files: // foo_def.h typedef struct Foo *FooRef; // maker.h #include "foo_def.h" FooRef make_foo(void); // freer.h #include "foo_def.h" void free_foo(FooRef foo); Which then preprocesses to: // maker.i typedef struct Foo *FooRef; FooRef make_foo(void); // freer.i typedef struct Foo *FooRef; void free_foo(FooRef foo); You then try to use it in your D program: // use_foo.d import maker; import freer; void do_foo(){ FooRef f = make_foo(); // use_foo.d(5) free_foo(f); // use_foo.d(6) } use_foo.d(5): Error: alias `maker.FooRef` at maker.c(1) conflicts with alias `freer.FooRef` at freer.i(1) use_foo.d(6): Error: function `freer.free_foo(Foo* foo)` is not callable using argument types `(Foo*)` use_foo.d(6): cannot pass argument `f` of type `maker.Foo*` to parameter `freer.Foo* foo` C11’s rules for when types are considered compatible are in "6.2.7 Compatible type and composite type". You can currently work around this by including both C headers in a single mega translation unit that you then import, but it would be nicer if the compiler could understand that as they are C declarations, they are actually the same type.
Comment #1 by bugzilla — 2022-01-22T04:53:11Z
I looked into making an adjustment to the compiler, which would be regarding types in C imports with the same name be the same type. Unfortunately, the D compiler is designed around types are the same if they have the same address. Changing this would be very difficult, and would impair speed even if it never actually happens. But there is a solution. Accessing things from D can be qualified, so: import maker; import freer; void do_foo(){ auto f = cast(freer.FooRef)make_foo(); free_foo(f); } Doing this in ImportC is slightly trickier, as C doesn't have name qualification: __import maker; __import freer : FooRef; void do_foo(){ FooRef f = (FooRef)make_foo(); free_foo(f); } Once could go further by creating a D file that merges the duplicate declarations: import maker; import freer; alias FooRef = maker.FooRef; extern (C) void free_foo(FooRef foo); and then importing that module. This works because the declaration of free_foo() will link without problems with the definition of free_foo() where ever it may be, as C name mangling does not mangle in the types. This merged D file may be imported either by D code or ImportC code. Hence, I'm going to mark this as WONTFIX because it is too disruptive, and accommodations are available that aren't too bad.
Comment #2 by dave287091 — 2022-05-16T15:55:41Z
If the goal is to have seamless C importing where you can just import c headers and the compiler even runs the preprocessor for you, I can’t see how this is an acceptable outcome. Even basic thing’s like stdio’s FILE* fall victim to this bug.
Comment #3 by destructionator — 2022-05-16T17:57:03Z
i wanna subscribe to this too as i also wrote about it: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_05_16.html#importc-and-module-namespaces Working on isolated unittests and one day toys is one thing. Working in a real world project that has more than one piece is another. My proposal there is to bring the importC declarations into a global implementation-defined module, then the other names work like `mixin templates` or `aliases` for disambiguation purposes.
Comment #4 by maxhaton — 2022-05-16T18:23:04Z
This bug is an important canary for ImportC be anything other than toy. Note that resolving this issue may not be a fix for this particular bug but rather completely reframing how C code works.
Comment #5 by dave287091 — 2022-07-30T16:48:17Z
C23 has actually changed the rules and even allows compatible redeclaration of types *within* a translation unit to be treated as the same.
Comment #6 by dlang-bot — 2022-09-21T01:44:00Z
@WalterBright updated dlang/dmd pull request #14461 "fix Issue 22674 - ImportC: compatible types declared in different tra…" fixing this issue: - fix Issue 22674 - ImportC: compatible types declared in different translation units are not treated equivalent in D https://github.com/dlang/dmd/pull/14461
Comment #7 by dlang-bot — 2022-09-21T18:03:04Z
dlang/dmd pull request #14461 "fix Issue 22674 - ImportC: compatible types declared in different tra…" was merged into master: - b9c117f38171eca71f092863356cf851532392bc by Walter Bright: fix Issue 22674 - ImportC: compatible types declared in different translation units are not treated equivalent in D https://github.com/dlang/dmd/pull/14461
Comment #8 by dave287091 — 2022-09-22T07:00:55Z