Skip to content
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

Fix issue 22234 - __traits(getLinkage) returns wrong value for extern(System) functions #3185

Closed
wants to merge 1 commit into from

Conversation

dkorpel
Copy link
Contributor

@dkorpel dkorpel commented Jan 24, 2022

Looking at the test cases that came with the implementation (dlang/dmd#6822), it looks like intentional behavior that __traits(getLinkage) gives the actual linkage of extern(System) instead of "System". It also sounds more useful to me this way and avoids code breakage, hence the spec PR instead of dmd PR.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @dkorpel! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Auto-close Bugzilla Severity Description
22234 normal __traits(getLinkage) returns wrong value for extern(System) functions

@adamdruppe
Copy link
Contributor

I don't remember what I was doing when making the bug report, but I know it bit me somewhat hard - hard enough to check the spec and open the bug report anyway.

@dkorpel
Copy link
Contributor Author

dkorpel commented Jan 24, 2022

Was it important for your code that __traits(getLinkage) returned "System" instead of "Windows" or "C"?

@adamdruppe
Copy link
Contributor

Again, I don't remember exactly what I was doing then, but it was broken enough to spend the time debugging it, comparing against the spec, and filing the bug report. So it obviously was important.

One case where this would make a difference is using built-in reflection to generate a binding file ahead of time. Saying "C" when it is actually "System" means you now get crashes on Windows.

And that reminds me, the linkage is also wrong in the dmd json output, which is sometimes used to generate bindings - the old dtoh program worked that way.

[
 {
  "kind" : "module",
  "file" : "system.d",
  "members" : [
   {
    "name" : "foo",
    "kind" : "function",
    "protection" : "public",
    "line" : 1,
    "char" : 21,
    "linkage" : "c",
    "deco" : "UZv",
    "originalType" : "extern (System) void()",

At least the "originalType" can be tested as a string, but the "linkage" field doesn't show it.

The dmd -H also gets it wrong, meaning if you use the header generation feature and commit the file it makes, you get random crashes.

dmd -HC also gets it wrong. Again, keep that file and you're in trouble.

The root problem here is the same for all of them.

@dkorpel
Copy link
Contributor Author

dkorpel commented Jan 24, 2022

The root problem here is the same for all of them.

Yes, extern(System) is resolved in the constructor of LinkDeclaration, so dmd loses the LINK.system information very early on. Do you think we can break the existing behavior of __traits(getLinkage), or would you want to see __traits(getLinkage2) or an extra parameter / return value or something?

@adamdruppe
Copy link
Contributor

adamdruppe commented Jan 24, 2022 via email

@dkorpel
Copy link
Contributor Author

dkorpel commented Jan 24, 2022

The existing behavior is already broken, not in line with the spec and giving incorrect results. It should obviously just be fixed.

I'm not sure it's obvious, extern(System) is pretty rare. I know it's useful for providing one OpenGL header for both Windows and Linux, but I think headers are usually either consistent or system-specific, not differing in ABI only.

I also don't have a good idea what __traits(getLinkage) is actually used for. If you care about twiddling with parameters in assembly, then it would be useful to get a concrete ABI ('Windows', 'C') instead of a variable one ('System'). In Druntime I see it's used to distinguish extern(C++) classes with regular D ones. Also there's this:

private template hasPlainMangling(FT) if (is(FT == function))
{
    enum lnk = __traits(getLinkage, FT);
    // C || Windows
    enum hasPlainMangling = lnk == "C" || lnk == "Windows";
}

This will break.

I'm not familiar with your use case of 'using built-in reflection to generate a binding file ahead of time', so I'd appreciate an example of that.

@adamdruppe
Copy link
Contributor

adamdruppe commented Jan 24, 2022 via email

@adamdruppe
Copy link
Contributor

adamdruppe commented Jan 24, 2022 via email

@adamdruppe
Copy link
Contributor

adamdruppe commented Jan 24, 2022 via email

@dkorpel
Copy link
Contributor Author

dkorpel commented Jan 24, 2022

The current behavior crashes if you generate any files with it.

I understand, but I didn't know that transforming D headers with __traits(getLinkage) passing along extern(System) was something people did. But thanks for clearing this up, I'll see if I can fix this in DMD.

@dkorpel dkorpel closed this Jan 24, 2022
@dkorpel
Copy link
Contributor Author

dkorpel commented Jan 24, 2022

if you are concerned about silent breakage, the solution is to still
fix the bug,

I'm not concerned for myself since I don't have any skin in this game, I'm just cautious that I'm not just flipping from one broken state to another.

@adamdruppe
Copy link
Contributor

adamdruppe commented Jan 24, 2022 via email

@dkorpel
Copy link
Contributor Author

dkorpel commented Jan 24, 2022

and it is a simple little change it doesn't seem worth it to do two traits.

The difficult part is that it's not clear when exactly System stops being its own thing and starts becoming C/Windows.

extern(System) void fsys();
extern(C) void fc();

static assert( typeof(fsys) == typeof(fc) ); // equal on Linux?
static assert( __traits(getLinkage, fsys) == __traits(getLinkage, fc) ); // but this is not equal on Linux

pragma(msg, fsys.mangleof); // special mangle for System, or do we choose C/Windows now?

@adamdruppe
Copy link
Contributor

adamdruppe commented Jan 24, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants