Fix issue 16995 - __traits(getUnitTests) works with separate compilation#6727
Fix issue 16995 - __traits(getUnitTests) works with separate compilation#6727WalterBright merged 2 commits intodlang:masterfrom
Conversation
6d072cc to
b868e89
Compare
c6e5691 to
b426b7b
Compare
f31977e to
5b92947
Compare
d7cac27 to
8d7d387
Compare
src/ddmd/func.d
Outdated
| extern (C++) static Identifier unitTestId(Loc loc) | ||
| { | ||
| // there's a counter per module in case there are two unit tests on one line | ||
| static __gshared int[const(char)*] counterPerModule; |
There was a problem hiding this comment.
I don't want AA's in DMD :)
That we use them in dtemplate is a mistake.
There was a problem hiding this comment.
You are quite far from the point where "I don't want AA's [sic] in DMD" is enough of a justification – it wouldn't even be for Walter. ;) Why should we maintain a separate (mostly worse) AA implementation in addition to the druntime one?
Also, please consider helping the more inexperienced DMD contributors by suggesting solutions rather than just saying "this is wrong". In this particular case, it seems like the best solution would be to delay setting the actual function name until semantic(), where a unique string can be generated from the Scope (similar to how things are done for lambdas, or just by looking up the Module and keeping a counter there).
(I presume the problems comes with template instantiations; if two test on one line were the issue as the comment suggests, the easiest solution would just be to add the column number as well.)
There was a problem hiding this comment.
@klickverbot
You are quite far from the point where "I don't want AA's [sic] in DMD" is enough of a justification – it wouldn't even be for Walter. ;)
I am not sure if this was meant jokingly or condescending ... that aside.
relying on d-runtime is not great because then the performance of the compiler is determined be the host druntime...
though I guess for a feature such as this it should not have too much of a impact.
There was a problem hiding this comment.
I am not sure if this was meant jokingly or condescending
At least as jokingly as your "I don't want […]" must have been, of course. ;)
relying on d-runtime is not great because then the performance of the compiler is determined be the host druntime...
It already is determined by the host compiler to great extent (compare a LDC LTO build against a DMD-compiled one if you're curious). But if you are worried, you can always do a multi-stage build and have the compiler self-host.
There was a problem hiding this comment.
I very much doubt that any discernible difference will exist in performance due to naming unittest blocks. Delaying where the function name is set seems to be quite involved work - I'm not sure it's worth it. Especially since we're talking about how unittests are named here.
Of course, if needs be...
There was a problem hiding this comment.
@klickverbot Perhaps, but that's well beyond the scope of this PR - it's an overhaul, however minor, of the compiler. Also, __traits(getUnitTests) is a bit... special. It seems unlikely (to me anyway) that the "resetability" will affect any other features in a similar way. Lambdas aren't referenced by name by other modules.
There was a problem hiding this comment.
Lambdas aren't referenced by name by other modules.
They are quite frequently when using templates. This is why the problem needed to be solved properly for them already.
There was a problem hiding this comment.
@klickverbot In a way that causes linker errors? I just tried a 1000-line lambda (I assume this means it won't be inlined) across modules and separate compilation was fine.
If this counter issue manifests in other ways when compiling separately, then I'm all for doing it right: it's pointless to whack one mole and leave the others. However, unless there's a clear bug elsewhere and not just with __traits(getUnitTests) then I think it's a big ask for me to learn the compiler internals to refactor it. Would it be nicer another way? Probably, but that's a different issue. If there are known bugs relating to this please let me know.
This patch fixes the bug, allows unittest builds with __traits(getUnitTests) to be built by package as is recommended and IMHO isn't entirely horrible.
There was a problem hiding this comment.
In a way that causes linker errors? I just tried a 1000-line lambda (I assume this means it won't be inlined) across modules and separate compilation was fine.
Yes, it doesn't cause any linker errors precisely because lambdas are already implemented properly.
If this counter issue manifests in other ways when compiling separately, then I'm all for doing it right: it's pointless to whack one mole and leave the others.
It manifests itself in templated unit tests:
module foo;
struct Foo(T) { unittest {} }module bar1;
import foo;
pragma(msg, "bar1/int: ", __traits(getUnitTests, Foo!int));module bar2;
import foo;
pragma(msg, "bar2/float: ", __traits(getUnitTests, Foo!float));
pragma(msg, "bar2/int: ", __traits(getUnitTests, Foo!int));bar1/int: tuple(__unittest_foo_d_2_1)
bar2/float: tuple(__unittest_foo_d_2_1)
bar2/int: tuple(__unittest_foo_d_2_2)
(Had I realized this sooner, I would have pointed it out before, of course.)
There was a problem hiding this comment.
@klickverbot That's a user case which is highly unlikely to ever happen in practice, but I see your point.
|
@atilaneves: If you exclude the For more infos: https://github.com/dlang-bots/dlang-bot#automated-references |
wilzbach
left a comment
There was a problem hiding this comment.
I guess you want to remove foo.txt or is this for testing/triggering a CI?
4748c5b to
f6818da
Compare
|
Thanks for your pull request, @atilaneves! We are looking forward to reviewing it, and you should be hearing from a maintainer soon. Some tips to help speed things up:
Bear in mind that large or tricky changes may require multiple rounds of review and revision. Please see CONTRIBUTING.md for more information. Bugzilla references
|
|
@wilzbach Yes, forgot to delete foo.txt. I changed the commit message as suggested. |
f6818da to
caff09a
Compare
caff09a to
ea52245
Compare
|
@klickverbot @UplinkCoder I changed the PR as requested - the identifier is now generated at |
|
@atilaneves the .gitignore changes are unrelated, please pull them out. |
e53b44a to
b918e1d
Compare
|
LGTM. |
|
(@ibuclaw you need to mark this PR as approved, as now it reads "ibuclaw requested changes") |
|
@ZombineDev - you had your own nit. |
|
Yes, my comment still stands, I was just trying to "clean-up" the pull request so that it's more clear what's left to be done. |
5c106d2 to
c4b113f
Compare
|
@ZombineDev I added |
c4b113f to
c405271
Compare
|
@ibuclaw this should be now good to go. |
c405271 to
f527810
Compare
src/ddmd/dsymbolsem.d
Outdated
| { | ||
| // the identifier has to be generated here in order to be able to link | ||
| // or not the files are compiled separately or all at once. | ||
| // See bugzilla #16995 |
There was a problem hiding this comment.
Use actual link:
// https://issues.dlang.org/show_bug.cgi?id=16995
src/ddmd/dsymbolsem.d
Outdated
| override void visit(UnitTestDeclaration utd) | ||
| { | ||
| // the identifier has to be generated here in order to be able to link | ||
| // or not the files are compiled separately or all at once. |
There was a problem hiding this comment.
I can guess what the sentence means, but it is just a guess. Please fix grammar!
There was a problem hiding this comment.
I think that was a rebase issue. Fixing it all the same.
src/ddmd/dsymbolsem.d
Outdated
| // the identifier has to be generated here in order to be able to link | ||
| // or not the files are compiled separately or all at once. | ||
| // See bugzilla #16995 | ||
| utd.setIdentifier; |
There was a problem hiding this comment.
Being a function, please call it with utd.setIdentifier();
|
|
||
| final void setIdentifier() | ||
| { | ||
| ident = createIdentifier(loc, _scope); |
There was a problem hiding this comment.
Does calling this replace the temporary one above? Is it always called? Need Ddoc comment for setIdentifier.
There was a problem hiding this comment.
It does. I added ddoc to explain that.
| super(loc, endloc, unitTestId(loc), stc, null); | ||
| // Id.empty can cause certain things to fail, so we create a | ||
| // temporary one here that serves for most purposes with | ||
| // createIdentifier. There is no scope to pass so we pass null. |
There was a problem hiding this comment.
It makes me uneasy to start with one identifier, and finish with another. What fails with an empty identifier?
There was a problem hiding this comment.
I didn't like having to do it either, my first attempt was to use Id.empty, but when I did that the unit tests failed. I can't remember which ones or why now, but they do.
| bool isPackageFile; // if it is a package.d | ||
| int needmoduleinfo; | ||
|
|
||
| uint unitTestCounter; // how many unittests have been seen so far |
There was a problem hiding this comment.
Document that unit test counter is per module, rather than global, so that it is reproducible regardless of how it is compiled.
| // replace characters that demangle can't handle | ||
| auto str = buf.peekString; | ||
| for(int i = 0; str[i] != 0; ++i) | ||
| if(str[i] == '/' || str[i] == '\\' || str[i] == '.') str[i] = '_'; |
There was a problem hiding this comment.
More efficient as:
foreach (ref c; buf.peekSlice())
{
if (c == '/' || c == ...) c = '_';
}
There was a problem hiding this comment.
Or even: buf.peekSlice()[11 .. $-4] :-)
There was a problem hiding this comment.
peekSlice returns const(char)[] so that doesn't compile. The latter version is also too hardcoded, even though I think it's unlikely anybody will change the way unit tests are named in the future.
There was a problem hiding this comment.
here's one of those places where among is useful (though we can't use it here)
There was a problem hiding this comment.
so that doesn't compile
Oopsie-daisy!
There was a problem hiding this comment.
This test is not strong enough and various testsuite tests fail if compiling to assembly (such as what gdc does).
This logic should match what is done for pragma(mangle).
Lines 3033 to 3045 in 32830de
There was a problem hiding this comment.
For instance, unittests compiled that are inside the directory extra-files fail because the assembler stumbles over on the dash symbol.
|
Am I correct in surmising that this change just replaces a global counter with a per-module one? |
|
@WalterBright Yes, the change replaces a global counter with a per-module one. |
andralex
left a comment
There was a problem hiding this comment.
I'm good with this, imperfect but leaves a bugzilla for a related improvement. @WalterBright?
| { | ||
| OutBuffer buf; | ||
| auto index = sc ? sc._module.unitTestCounter++ : 0; | ||
| buf.printf("__unittest_%s_%u_%d", loc.filename, loc.linnum, index); |
There was a problem hiding this comment.
There's a difference without a distinction between %u and %d - just use %u for both, or better yet %s for all (assuming they're all unsigned as they should).
There was a problem hiding this comment.
printf doesn't work with %s for integers :-)
There was a problem hiding this comment.
Oh wait I thought it's writef...
| // replace characters that demangle can't handle | ||
| auto str = buf.peekString; | ||
| for(int i = 0; str[i] != 0; ++i) | ||
| if(str[i] == '/' || str[i] == '\\' || str[i] == '.') str[i] = '_'; |
There was a problem hiding this comment.
here's one of those places where among is useful (though we can't use it here)
|
@atilaneves Could you take a look at this bug report? https://issues.dlang.org/show_bug.cgi?id=18097 Much obliged. |
|
@JohanEngelen I looked at it. I'd have to debug to figure it out. There's another problem with my fix (I haven't filed the bug yet) so I'll talke a look at it when I do that. |
Before this patch,
__traits(getUnitTests)only worked when compiling all source files at once. This fix enables it to work for separate compilation as well.