Skip to content

Make lambda symbols stable post a3abf1187ec019fac1ae98ffe78d68d7523b5665#17102

Merged
dkorpel merged 1 commit intodlang:masterfrom
yanok:lambda-mangle2
Dec 4, 2024
Merged

Make lambda symbols stable post a3abf1187ec019fac1ae98ffe78d68d7523b5665#17102
dkorpel merged 1 commit intodlang:masterfrom
yanok:lambda-mangle2

Conversation

@yanok
Copy link
Copy Markdown

@yanok yanok commented Dec 2, 2024

Commit a3abf11 fixes some cases of lambdas having unstable symbol names between compilation units by using generateIdWithLoc to generate stable lambda names, however since LOC doesn't uniquely identify a lambda instance (because templates, mixins, static foreach and foreach unrolling), generateIdWithLoc adds a counter, so there is still some instability going on.

generateIdWithLoc makes the name uniq per file+loc, by adding adding a numeric suffix. But the order of instantiations might be different across compilation units, so with this counting scheme we are back to unstable names, so one module might have

t!0.__lambda_LOC and
t!1.__lambda_LOC_1

while another one has

t!1.__lambda_LOC

This is not a critical problem, but at very least the code gets duplicated for no reason. I also have an example where it leads to linking error, but since it's not a small one and fails to minimize further, I suspect it's a result of interaction with some other bug.

The thing is we don't even need uniqueness for those lambdas inside templates/mixins: their final names will have the instantiation prefix anyway. But we can't also just disable this uniqueness check completely: static foreach as well as unrollings of the normal foreach with lambdas in the loop body will have several copies of a single lambda with the same file+loc. So here we do want to keep making them unique. Fortunately, I don't think a foreach could be iterated in different order in different compilation units, so hopefully if we limit the counting to this case only, it won't make symbols unstable.

To implement this idea, I've added an extra parent argument to generateIdWithLoc: it works like using parent ~ prefix prefix, but without adding parent to the final output.

@dlang-bot
Copy link
Copy Markdown
Contributor

Thanks for your pull request and interest in making D better, @yanok! 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

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

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#17102"

@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 2, 2024

/cc @dkorpel

@dkorpel
Copy link
Copy Markdown
Contributor

dkorpel commented Dec 2, 2024

Looks good so far, are you going to make use of it or is that for a next Pull Request?

@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 3, 2024

Looks good so far, are you going to make use of it or is that for a next Pull Request?

I definitely want this for our local branch:

  1. Without it there is even more code duplication, and we have a lot of duplicated code already :)
  2. I have one case there linking breaks without this fix: module A gets an unresolved symbol for a lambda inside a template and module B has a definition of that symbol, but with a different suffix. This might be LDC specific or another bug, I still plan to debug it more, but didn't have time recently.

But I also think it's the right thing to merge into the main branch as well, since it finally stabilize lambda symbols across compilation unit.

@dkorpel
Copy link
Copy Markdown
Contributor

dkorpel commented Dec 3, 2024

But in this Pull Request, the new parent parameter is yet unused, or am I missing something?

@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 3, 2024

But in this Pull Request, the new parent parameter is yet unused, or am I missing something?

Ouch, you are right, sorry. Must have been lost with the merge. Let me add it back and update the PR.

@yanok yanok force-pushed the lambda-mangle2 branch 2 times, most recently from 5436517 to 44429c2 Compare December 3, 2024 10:41
Copy link
Copy Markdown
Contributor

@dkorpel dkorpel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a test case for this?

@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 3, 2024

Do you have a test case for this?

I have a nice small test case for lambda getting another name, but that example works in a sense: the lambda just gets emitted twice, but the binary links and runs. So I can only detect it with nm and not by compiling and running. Is it possible to use nm from a shell test?

@dkorpel
Copy link
Copy Markdown
Contributor

dkorpel commented Dec 3, 2024

Is it possible to use nm from a shell test?

Probably, but I'm not a fan of shell tests for specific implementation details, because they break easily and are hard to debug. I was hoping maybe there's an easy .mangleof test or something, but if not, I'm fine with merging as is.

@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 3, 2024

I was hoping maybe there's an easy .mangleof test or something, but if not, I'm fine with merging as is.

It's a cool idea! Unfortunately I've tried and failed :( We need a mangle of a lambda/delegate literal, but it looks like mangleof doesn't work for rvalues: the mangle I get doesn't even contain lambda or template name... And if I put it into a lvalue, I get a mangle of this lvalue, of course.

@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 3, 2024

UPD: I've managed to make an example with mangleof, will add a test tomorrow.

Commit a3abf11 fixes some cases of
lambdas having unstable symbol names between compilation units by
using `generateIdWithLoc` to generate stable lambda names, however since
LOC doesn't uniquely identify a lambda instance (because templates,
mixins, static foreach and foreach unrolling), `generateIdWithLoc`
adds a counter, so there is still some instability going on.

`generateIdWithLoc` makes the name uniq per file+loc, by adding adding a
numeric suffix. But the order of instantiations might be different
across compilation units, so with this counting scheme we are back to
unstable names, so one module might have

`t!0.__lambda_LOC` and
`t!1.__lambda_LOC_1`

while another one has

`t!1.__lambda_LOC`

This is not a critical problem, but at very least the code gets
duplicated for no reason. I also have an example where it leads to
linking error, but since it's not a small one and fails to minimize
further, I suspect it's a result of interaction with some other bug.

The thing is we don't even need uniqueness for those lambdas inside
templates/mixins: their final names will have the instantiation prefix anyway.
But we can't also just disable this uniqueness check completely: `static
foreach` as well as unrollings of the normal `foreach` with lambdas in
the loop body will have several copies of a single lambda with the same
file+loc. So here we do want to keep making them unique. Fortunately, I
don't think a `foreach` could be iterated in different order in
different compilation units, so hopefully if we limit the counting to
this case only, it won't make symbols unstable.

To implement this idea, I've added an extra `parent` argument to
`generateIdWithLoc`: it works like using `parent ~ prefix` prefix, but
without adding `parent` to the final output.

Fixes since last review:
 1. Changed `fromStringz` to `toDString`
 2. Added a test to showcase the problem
@yanok
Copy link
Copy Markdown
Author

yanok commented Dec 4, 2024

I was hoping maybe there's an easy .mangleof test or something, but if not, I'm fine with merging as is.

Added a test, PTAL.

@dkorpel
Copy link
Copy Markdown
Contributor

dkorpel commented Dec 4, 2024

LGTM

@kinke
Copy link
Copy Markdown
Contributor

kinke commented May 5, 2025

This approach apparently suffers from unstable toPrettyChars() results for a specific symbol, depending on semantic analysis: #21331 (comment). Would the sc.parent identity work for Weka too, i.e., just using the sc.parent as void* for the Key.parent field? Edit: #21343.

@yanok
Copy link
Copy Markdown
Author

yanok commented May 5, 2025

This approach apparently suffers from unstable toPrettyChars() results for a specific symbol, depending on semantic analysis: #21331 (comment). Would the sc.parent identity work for Weka too, i.e., just using the sc.parent as void* for the Key.parent field?

@kinke Uh.. sorry for the mess. I think comparing by reference should generally work too... my only concern are template instances, maybe we have to pass sc.parent.inst if it's non-null, so we catch "different but the same" template instances case.

Can't we get the mangle of sc.parent? That would be the safest thing to do, since the ultimate goal of this third key is to avoid adding suffixes to things that would already have different mangles because of different parents. So what really matters is the parent's mangle.

@kinke
Copy link
Copy Markdown
Contributor

kinke commented May 5, 2025

Thx, that's a good idea I think - pushed as 2nd commit in #21343.

JohanEngelen added a commit to weka/ldc that referenced this pull request Jan 4, 2026
…al), to prevent linker errors for separate compilation case where the lambda is not emitted (because supposedly already emitted in linked library). Internal linkage breaks that assumption (because not linkable by other module).

Lambda mangles should be stable now, because of dlang/dmd#17102 (and related earlier fixes).
JohanEngelen added a commit to weka/ldc that referenced this pull request Jan 27, 2026
…al), to prevent linker errors for separate compilation case where the lambda is not emitted (because supposedly already emitted in linked library). Internal linkage breaks that assumption (because not linkable by other module).

Lambda mangles should be stable now, because of dlang/dmd#17102 (and related earlier fixes).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants