-
-
Notifications
You must be signed in to change notification settings - Fork 606
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 issues 1870 and 12790: Write out string mixin code to a file for debugging purpose #8677
Conversation
Thanks for your pull request and interest in making D better, @thewilsonator! 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 fetch digger
dub run digger -- build "master + dmd#8677" |
doesn't the source file mars.d need to be updated to activate the new option ? |
True I forgot to sync before opening the PR |
a684d9e
to
48dd2a0
Compare
Does it really need to be a compiler switch? Why not do this by default whenever mixins are used? @WalterBright is usually against adding new switches. How hard is it to simply print the offending line without any line number? Also, if this gets merged it needs a changelog entry, updating the documentation and also the messages that dmd outputs if no parameter is provided. |
There is also https://issues.dlang.org/show_bug.cgi?id=5051. My proposed patch writes mixins to a single file, otherwise your object directory might be filled with an ever increasing number of small files. Visual D registers .mixin files with the D language service, so you get syntax highlighting and "auto" watches. I have not tested this since 2010, though.
For windows, there is testpdb.d with some examples, e.g. it should be possible to enumerate line number information for a function. |
A test can consist of a shell script, there are existing examples. Perhaps look at a test for Ddoc. |
src/dmd/globals.d
Outdated
@@ -151,6 +151,7 @@ struct Param | |||
// https://issues.dlang.org/show_bug.cgi?id=16997 | |||
bool vsafe; // use enhanced @safe checking | |||
bool ehnogc; // use @nogc exception handling | |||
bool debugMixins; // write out argument to mixin so that the debuuger has source |
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.
Ddoc comment please.
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.
None of the others do, also there is cli.d
src/dmd/parse.d
Outdated
char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1); | ||
sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); | ||
scanloc.filename = filename; | ||
auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; |
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 like this can be const.
src/dmd/parse.d
Outdated
|
||
if (global.params.debugMixins) | ||
{ | ||
auto tmp = FileName.combine(global.params.objdir,filename.ptr); |
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 like this can be const.
src/dmd/parse.d
Outdated
{ | ||
auto tmp = FileName.combine(global.params.objdir,filename.ptr); | ||
filename = cast(char[])tmp[0 .. strlen(tmp)]; | ||
auto f = File.create(filename.ptr); |
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.
This is certainly better than the status quo.
However, consider the following code:
void foo(string s)() { mixin(s); }
void main()
{
foo!(`writeln(a);`);
foo!(`writeln("hello world");`);
}
This will write writeln(a);
to file scratch.d-mixin-5
, then write writeln("hello world");
to the same file, overwriting what was there previously.
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.
Is that likely to ever occur in real 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.
Any mixin that occurs inside a template that's instantiated more than once. That might be a minority of cases, but it certainly does occur. For instance, std.typecons has at least a handful of these. I wrote some code to automatically extract a postgresql row into a struct, and it contains mixins inside templates that are instantiated multiple times.
Also the standard shortcut for implementing opBinary is with mixins. That's why we switched to opBinary!operator, instead of opAdd and friends.
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.
Is that likely to ever occur in real code?
I think it happens more often than not.
In my experience, most string mixins are wrapped in a mixin template for sanitary reasons, or in a template function. If the enclosing scope is not a template, then there's little motivation for a string mixin... you could just put the code directly. I reckon it's the common case for a mixin to appear in a parametric environment.
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.
When creating PRs for bugzilla issues, please include a link in the issue to the PR. This prevents multiple people from unwittingly working on the same issue. And even if it is rejected, it provides valuable information to anyone trying again.
I'm tired of repeatedly pointing this out to everyone making PRs.
@RazvanN7 This will result in a lot of unnecessary I/O for projects with lots of mixins, not to mention disk space. This is also so that the debugger has source so it needs to be on disk for that.
isn't that what
This is a parameterless switch. |
src/dmd/parse.d
Outdated
|
||
if (global.params.debugMixins) | ||
{ | ||
auto tmp = FileName.combine(global.params.objdir,filename.ptr); |
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.
const tmp = FileName.combine(global.params.objdir.toDString(),filename);
Will give you a slice.
Also, is there no better way to provide this to GDB than writing it to a file ? @ibuclaw ? |
As I understand it, the problem is that source locations are provided to the debugger (GDB/LLDB/whatever MS uses) but the source doesn't exist on disk, because its expanded at compile time, and so |
I understand the problem, and agree that it needs fixing, I'm just wondering if debuggers have another way to cope with this than writing a file to disk. |
Can you embed source in an object file? I can't think of any other way to do it.
Hence this is written to |
src/dmd/parse.d
Outdated
sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); | ||
scanloc.filename = filename; | ||
auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; | ||
char[] filename = cast(char[])mem.xmalloc(len)[0 .. len]; |
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.
Cast to a pointer then slice. Casting arrays incur additional checks.
src/dmd/parse.d
Outdated
filename = cast(char[])tmp[0 .. strlen(tmp)]; | ||
auto f = File.create(filename.ptr); | ||
// We're going to write it out straight away so the cast below is fine | ||
f.setbuffer(cast(void*)input.ptr,input.length); |
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.
Nit: space after comma (other places in the diff as well)
src/dmd/parse.d
Outdated
{ | ||
auto tmp = FileName.combine(global.params.objdir,filename.ptr); | ||
filename = cast(char[])tmp[0 .. strlen(tmp)]; | ||
auto f = File.create(filename.ptr); |
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.
Use new File(filename.ptr)
. create
is a C++ wrapper.
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.
Actually doesn't need to even be new
'd
48dd2a0
to
03de9e7
Compare
Addressed all comments except doc comment for |
03de9e7
to
78ccc41
Compare
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.
Would be nice to avoid creating temporary files that aren't in test_results
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.
Would be nice to avoid creating temporary files that aren't in test_results
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.
Would be nice to avoid creating temporary files that aren't in test_results
@thewilsonator I didn't mean to port the source code to D, but to implement the features you miss in the existing test suite code. |
d40f01e
to
ae8a447
Compare
Rebased. Fixed style. No "mixin stack" yet. While nice, I'm not (yet) convinced that it will be necessary, and that can be done later if it is. |
ae8a447
to
2990f60
Compare
All green except semaphore which is segfaulting. @rainers any idea why? |
Not sure, an earlier build reports "double free or corruption". Maybe it has to with |
Ok, being opt-in should not block it for dmd to progress. I'd love to enable this feature in Visual D by default, but not being able to easily jump to the actual source of an error is probably a deal breaker. |
OutBuffer* mixinOut; // write expanded mixins for debugging | ||
const(char)* mixinFile; // .mixin file output name | ||
int mixinLines; // Number of lines in writeMixins | ||
|
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.
mixinOut and mixinLines should better go into the Global struct rather than Param. I guess you followed the moduleDeps example, but I'd consider it a bad one.
src/dmd/mars.d
Outdated
@@ -1508,6 +1525,21 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param | |||
params.map = true; | |||
else if (arg == "-multiobj") | |||
params.multiobj = true; | |||
else if (startsWith(p + 1, "mixin=")) | |||
{ | |||
if (params.mixinFile) |
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.
This follows the -deps example, too, but no other option disallows overriding previous settings.
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.
Will fix.
@@ -242,7 +279,7 @@ final class Parser(AST) : Lexer | |||
//printf("Parser::Parser()\n"); | |||
scanloc = loc; | |||
|
|||
if (loc.filename) | |||
if (!writeMixin(input, scanloc) && loc.filename) |
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.
Not too happy about writeMixin
being in the parse module. Maybe it would be better to do this (and the existing pseudo-filename creation) at the callsite with a small helper function.
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.
I tried that originally, but that constructor is called from MixinStatement
and MixinDeclaraion
and I didn't really want to duplicate the code.
Aha. I fix the old problem with |
4e34ddc
to
5038008
Compare
@rainers any idea why testing of
or why |
Unrelated adding |
…debugging purpose
5038008
to
3bbb228
Compare
There is no test case for this yet, I'm not sure how to check the contents of generated files with dmd's test suite. I also don't know how to test debug info, any help appreciated.
cc @TurkeyMan