January 14, 2022
https://issues.dlang.org/show_bug.cgi?id=22674

          Issue ID: 22674
           Summary: ImportC: compatible types declared in different
                    translation units are not treated equivalent in D.
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: dave287091@gmail.com

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.

--