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 14831 - Each function local symbols should have unique mangled name #12235
Fix 14831 - Each function local symbols should have unique mangled name #12235
Conversation
e5472f9
to
b4619da
Compare
Thanks for your pull request and interest in making D better, @MoonlightSentinel! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
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
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "stable + dmd#12235" |
b4619da
to
d0aae10
Compare
if ((s.isFuncDeclaration() || | ||
s.isAggregateDeclaration() || | ||
s.isEnumDeclaration() || | ||
s.isTemplateDeclaration() || | ||
v && v.isDataseg()) && !sc.func.localsymtab.insert(s)) | ||
v |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put this first in the order of conditions, to keep it in order of least to most expensive.
void main() | ||
{ | ||
{ | ||
int x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would ever be the use of mangling a stack variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I missed that earlier in the review. You're right, local vars should not have a mangle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing local variables as template alias parameters, see the test case in #12236:
void main()
{
{
int x = 1;
print!x();
pragma(msg, print!x.mangleof);
}
{
int x = 2;
print!x();
pragma(msg, print!x.mangleof);
}
}
Both instances of print
receive the same mangling (_D9test10619__T5printS_DQw4mainFZ1xiZQwMFNbNiZv
) without this patch.
With this patch:
_D9test10619__T5printS_DQw4mainFZ1xiZQwMFNbNiZv
_D9test10619__T5printS_DQw4mainFZ5__S11xiZQBbMFNbNiZv
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct, though, isn't it? The resultant code could use localized frames/closures if we know that the current scope isn't the top-level function scope.
void main()
{
__closure_main __closure;
{
__scoped_closure_main1 __scoped_closure;
__scoped_closure.x = 1;
print(&__scoped_closure); // print!x()
}
{
__scoped_closure_main2 __scoped_closure;
__scoped_closure.x = 2;
print(&__scoped_closure); // print!x();
}
__closure.y =1;
print(&__closure); // print!y();
}
That the current implementation of closures puts both local x
variables in the same closure record (at different offsets) is an issue of the closure implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To suggest another left field idea, you could also detect local variable name collisions and put them in a union within the closure record.
So you end up with:
void main()
{
struct __closure_main
{
union
{
int x;
long x;
real x;
int x;
}
int y;
}
__closure_main __closure;
{
__closure.x = 1; // int x = 1;
print(&__closure); // print!x();
}
{
__closure.x = 2; // long x = 2;
print(&__closure); // print!x();
}
{
__closure.x = 3.4; // real x = 3.4;
print(&__closure); // print!x();
}
{
__closure.x = 5; // int x = 5;
print(&__closure); // print!x();
}
__closure.y = 6; // int y = 6;
print(&__closure); // print!y();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggestions assume that it's valid to merge both instances IIUC.
This might not be the case for such templates because one can inspect the alias parameter, e.g.
void print(alias symbol)()
{
import core.stdc.stdio : printf;
printf("%d at line %d\n", symbol, __traits(getLocation, symbol)[1]);
}
void main()
{
{
int x = 1;
print!x();
}
{
int x = 2;
print!x();
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, but your suggestion of giving the local variable a different name would mean that you could not use x
to look-up the value in a debugger, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about shadowing detection? This would also bypass that as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, but your suggestion of giving the local variable a different name would mean that you could not use x to look-up the value in a debugger, no?
Debug information for local informations is not affected by this change (but is not correct atm)
Small test case:
/*
https://issues.dlang.org/show_bug.cgi?id=10619
REQUIRED_ARGS: -g
PERMUTE_ARGS:
GDB_SCRIPT:
---
b gdb10619.d:26
b gdb10619.d:30
run
info locals
print x
continue
info locals
print x
continue
---
*/
void main()
{
{
int x = 1;
print!x(); // first breakpoint
}
{
int x = 2;
print!x(); // second breakpoint
}
}
void print(alias symbol)() {}
Stable:
Breakpoint 1 at 0x80b: file runnable/gdb10619.d, line 26.
Breakpoint 2 at 0x819: file runnable/gdb10619.d, line 30.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, D main () at runnable/gdb10619.d:26
26 print!x();
x = 1
x = 0
$1 = 1
Breakpoint 2, D main () at runnable/gdb10619.d:30
30 print!x();
x = 1
x = 2
$2 = 1
[Inferior 1 (process 466) exited normally]
This PR:
Breakpoint 1 at 0x80b: file runnable/gdb10619.d, line 26.
Breakpoint 2 at 0x819: file runnable/gdb10619.d, line 30.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, D main () at runnable/gdb10619.d:26
26 print!x();
x = 1
x = 0
$1 = 1
Breakpoint 2, D main () at runnable/gdb10619.d:30
30 print!x();
x = 1
x = 2
$2 = 1
[Inferior 1 (process 553) exited normally]
EDIT: DMD apparently doesn't emit the actual blocks using DW_TAG_lexical_block
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about shadowing detection? This would also bypass that as well?
Shadowing detection is not affected by this PR, e.g.
void main()
{
int x;
{
int x = 1; // Error: variable `x` is shadowing variable `gdb10619.main.x`
}
}
PR dlang#12119 already fixed collisions for several declarations but missed local variables. The check for `isDataSeg` was a remnant of the old error raised for `static`/`__gshared` variables. Removing it enables the mangling fixup for local variables. Note that this doesn't fix https://issues.dlang.org/show_bug.cgi?id=10619 which is unrelated to the mangling.
d0aae10
to
1d2b5ec
Compare
RUN_OUTPUT: | ||
--- | ||
1 | ||
1 | ||
2 | ||
3 | ||
4 | ||
--- | ||
|
||
print => 2 will be fixed by https://github.com/dlang/dmd/pull/12235 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased to stable to expand on #12236 (as both fix wrong code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me
PR #12119 already fixed collisions for several declarations but missed
local variables.
The check for
isDataSeg
was a remnant of the old error raised forstatic
/__gshared
variables. Removing it enables the mangling fixupfor local variables.
Note that this doesn't fix https://issues.dlang.org/show_bug.cgi?id=10619which is not directly related to the mangling. See #12236.
Changes to other
TEST_OUTPUT
sections are caused by different sizes oflocalsymtab
which defines the sequence number for anonymous symbols.