dmd icon indicating copy to clipboard operation
dmd copied to clipboard

Fix 5309 - Support `extern(D)` symbol refs

Open Geod24 opened this issue 4 years ago • 5 comments

Currently, `extern(D)` is quite incomplete: An `extern(D)` symbol is mangled
as if it was in the module it is declared in, while users usually want to
declare a function or type that lives in another module.

Borrowing from the syntax that was adopted for `extern(C++, name.space)`, we introduce `extern(D, pkg.mod)`.
Note that, unlike `extern(C++)`, no string alternative is needed:
the motivation for the string variant was that C++ namespaces could be D keywords,
hence some namespaces could not be bound with the identifier variant,
a problem which obviously does not apply to `extern(D)` symbols.

The need for this functionality is easily demonstrated by druntime's `externDFunc`.
`core.internal.traits : externDFunc` is a template that defines an `extern(D)` symbol in another module.
This is currently done by using `pragma(mangle)` along with `core.demangle : mangleFunc`.

And as it turns out, the only reason for `core.demangle : mangleFunc` to exists is for `externDFunc`.
Hence, implementing this functionality will greatly simplify a core piece of druntime:
`core.demangle : mangle` (and its derivatives, including `mangleFunc` and `externDFunc`) can be removed
and replaced by `XXX.mangleof`, relying on the compiler instead of a library function which have to be
kept in sync with the compiler implementation.

TL;DR: While we should do things in libraries as much as possible, replicating in a library complex compiler logic (in this case, symbol mangling) is bad. With this in mind, I was puzzled as to why core.demangle : mangle existed in the first place (also consider that the module is called demangle but holds a mangle function, so it was clearly an afterthought). Issue 5309 held the answer, and hopefully after this is merged we can greatly simply core.demangle ("fun" fact: the demangling code is re-used for the mangler, with added boolean logic here and there).

Geod24 avatar Jul 08 '21 12:07 Geod24

Thanks for your pull request, @Geod24!

Bugzilla references

Auto-close Bugzilla Severity Description
5309 enhancement Add language support for external D symbols refs

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#12839"

dlang-bot avatar Jul 08 '21 12:07 dlang-bot

Yeah, externDFunc must die.

ibuclaw avatar Jul 08 '21 12:07 ibuclaw

This is common practice in C. But D tries to get away from that. I fear this sort of thing will have a tendency to break the sanctity(!) of the module system in D. It could be used to insert symbols into modules that aren't in those modules at all.

I've found a need to do this when translating C code to D, as C does that all over the place. But the way I get it to work is by declaring those functions extern(C). While it does take advantage of C mangling being rootless (i.e. not a member of a module), it is clearly ugly, and I use it as a temporary measure while converting existing code to D.

Having this work with extern(D) declarations, however, is blessing a code smell.

I recommend instead one of:

  1. making such functions extern(C) or extern(C++).
  2. using .di imports. That's exactly what they're for.

externDfunc is probably an abomination. Should seriously look into just why it is being used.

WalterBright avatar Jul 11 '21 06:07 WalterBright

This is common practice in C. But D tries to get away from that. I fear this sort of thing will have a tendency to break the sanctity(!) of the module system in D.

It does indeed, and it's not really something we should advertise.

It could be used to insert symbols into modules that aren't in those modules at all.

This part I'm not sure I follow how that'd be possible ?

Having this work with extern(D) declarations, however, is blessing a code smell.

When looking at the issue, I was wondering what was the best way to fix this. I think that we are faced with two solutions:

  1. Deprecate extern(D) extern functions in a module (not .di), because they are completely useless;
  2. Make extern(D) declarations work: This PR;

I went with the later because:

  1. The DLL use case seemed legit;
  2. It makes it consistent with other extern declaration;
  3. Deprecating extern(D) in modules would make the language less consistent, while solution 2 makes it more consistent;

Geod24 avatar Jul 16 '21 06:07 Geod24

@Geod24 I agree with @WalterBright in that I really don't want this complexity in the language if we can avoid it. Do you still want to add this feature?

dkorpel avatar May 09 '23 16:05 dkorpel