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

[REG2.067] Issue 14508 - compiling with -unittest instantiates templates in non-root modules #4626

Closed
wants to merge 5 commits into from

Conversation

9rnsr
Copy link
Contributor

@9rnsr 9rnsr commented May 2, 2015

https://issues.dlang.org/show_bug.cgi?id=14508

All template instances outside of version(unittest){} or debug(num){} need to use the normal instantiation strategy.

When -unittest or -debug(=num) switches specified, the instantiation strategy will be tweaked - a conditionally instantiated template code by version(unittest){} or debug(num){} will be generated conservatively, even if it's done in non root module.

It can be formalized as follows:

R = templates instantiated in root module
N = templates instantiated in non root module
S = templates instantiated speculatively

  • Default instantiation strategy:
    codegen = R - (R ∩ N)
  • The new instantiation strategy for -unittest or -debug=num:
    codegen = Rd - (Rd ∩ N)
    Rd = R ∪ Nd
    Nd = version(unittest) and debug template instances in non-root modules
  • The old instantiation strategy for -allInst:
    codegen = R ∪ N

@9rnsr
Copy link
Contributor Author

9rnsr commented May 2, 2015

@WalterBright This is also an answer for the instantiation strategy workaround that you implemented in #2661, to fix issue 11239.
Please review this.

@MartinNowak
Copy link
Member

Not sure that this or #2661 are good ideas. You can't generally expect that linking code compiled with different debug/version settings works.

struct Foo
{
    debug size_t cnt;
}
void bar(Foo* foo) { debug ++foo.cnt; }

@9rnsr
Copy link
Contributor Author

9rnsr commented May 3, 2015

@MartinNowak Yes, it generally doesn't work. But for the instantiated code we can relax the restriction.
Normally we don't provide unittest version library, as such debug/release version libraries. Therefore the relaxation would be useful.

@9rnsr
Copy link
Contributor Author

9rnsr commented May 3, 2015

Auto-tester now shows all green.

The previous Phobos failure in Win64 was another link-failure issue 14541.
Now this PR also fix it.

@MartinNowak
Copy link
Member

Shouldn't the codegen be like this instead?

codegen = R - (R ∩ N \ Nd)

Nd = version(unittest) and debug template instances in non-root modules

@9rnsr
Copy link
Contributor Author

9rnsr commented May 4, 2015

Pushed additional fix commit. I'll squash it after the review finished.

@9rnsr
Copy link
Contributor Author

9rnsr commented May 4, 2015

Shouldn't the codegen be like this instead?

By using Nd, it would be formalized like:

  codegen = Rd - (Rd ∩ N)
  Rd = R ∪ Nd
  Nd = version(unittest) and debug template instances in non-root modules

@9rnsr
Copy link
Contributor Author

9rnsr commented May 4, 2015

Sit, Making instantiation strategy more strict will directly hit the root of issue 12661. We need to fix it radically, at first.

@9rnsr 9rnsr changed the title [REG2.067] Issue 14508 - compiling with -unittest instantiates templates in non-root modules [WIP][REG2.067] Issue 14508 - compiling with -unittest instantiates templates in non-root modules May 5, 2015
@9rnsr 9rnsr force-pushed the fix14508 branch 4 times, most recently from 71bacba to d93f17b Compare May 5, 2015 09:38
@9rnsr
Copy link
Contributor Author

9rnsr commented May 5, 2015

OK, I added one more feature "force instantiation mechanism".


More feature for root of issue 12661 - Add forceInst mechanism

  • If an instantiated function contains debug{} or version(unittest){}, the instance code will be forcibly generated.
  • If an instantiated function calls other forcibly instantiated function and its attribute is influenced by inference, that code is also generated forcibly.

@9rnsr 9rnsr changed the title [WIP][REG2.067] Issue 14508 - compiling with -unittest instantiates templates in non-root modules [REG2.067] Issue 14508 - compiling with -unittest instantiates templates in non-root modules May 5, 2015
@WalterBright
Copy link
Member

I'm not in favor of reordering code as in f0080e8 because it makes it very difficult to compare code that crosses such a diff. Can you please back that out?

@MartinNowak
Copy link
Member

I'm a bit worried, that doing it transitively will generate an awful amount of code.
Using version (unittest) or debug (PRINTF) in a template is not that uncommon.
Might be worth to think about alternatives, like shipping a debug phobos library.

If an instantiated function contains debug{} or version(unittest){}, the instance code will be forcibly generated.

How can you know that function contains such conditions before instantiating it?
Is this just looking at the AST?

@MartinNowak
Copy link
Member

I also have a suggestion to fix part of the instantiation bloat.
https://github.com/D-Programming-Language/dmd/pull/4384/files#r29910422.

@WalterBright
Copy link
Member

Does this resolve the compilation slowdown caused by #4384 ?

@WalterBright
Copy link
Member

I suspect the rule we should be using is that all templates in non-root modules should be instantiated lazily, meaning only if a particular instantiation is actually needed. No eager instantiations. Thus certainly would resolve the particular case in https://issues.dlang.org/show_bug.cgi?id=14508

I did this for enum members a while back, and the results have been very satisfactory. It also resolves order-of-evaluation issues.

@9rnsr
Copy link
Contributor Author

9rnsr commented May 12, 2015

I'm a bit worried, that doing it transitively will generate an awful amount of code.
Using version (unittest) or debug (PRINTF) in a template is not that uncommon.

An alternate suggestion would be to change the behavior of -unittest and -debug switches. For example:
"If a template which defined in non-root module is instantiated, unittest {} blocks and version(unittest){} conditions in it are just ignored even if -unittest switch is specified. Same strategy will be applied to -debug."

But, it would prohibit to define a module just for unit-testing/debug utilities. I think it would need a design decision to the D module system.

Might be worth to think about alternatives, like shipping a debug phobos library.

It would be horrible idea. How many combinations would be necessary for the user code conditional compilations?
phobos.lib phobos_unittest.lib phobos_debug1.lib phobos_debug2.lib phobos_debug_PRINTF.lib ...

@9rnsr
Copy link
Contributor Author

9rnsr commented May 12, 2015

Does this resolve the compilation slowdown caused by #4384 ?

No. It makes more slow down compilation speed to make linking more stable.

@9rnsr
Copy link
Contributor Author

9rnsr commented May 12, 2015

If an instantiated function contains debug{} or version(unittest){}, the instance code will be forcibly generated.

How can you know that function contains such conditions before instantiating it?

There's no general way, so a condition can depend on the semantic result. See the following example code:

// this function template may or may not depend on -unittest
void foo(string code)() {
    mixin(code);
}
foo!"version(unittest) unsafeCheckFunc();"();

@9rnsr
Copy link
Contributor Author

9rnsr commented May 13, 2015

I removed commits for code cleanup in order to concentrate on bug fix.

@MartinNowak
Copy link
Member

I suspect the rule we should be using is that all templates in non-root modules should be instantiated lazily, meaning only if a particular instantiation is actually needed. No eager instantiations. Thus certainly would resolve the particular case in https://issues.dlang.org/show_bug.cgi?id=14508

See my comment here, #4384 (comment).
We should establish a rule which module is responsible for instantiating a template if that's ambiguous (e.g. mutual import). A simple rule would be to use the module with the lexicographically smaller module name.

@MartinNowak
Copy link
Member

I suspect the rule we should be using is that all templates in non-root modules should be instantiated lazily

Sounds good, but is that feasible for the next release? It wouldn't solve linkage issues when the template is used though.

An alternate suggestion would be to change the behavior of -unittest and -debug switches. For example:
"If a template which defined in non-root module is instantiated, unittest {} blocks and version(unittest){} conditions in it are just ignored even if -unittest switch is specified. Same strategy will be applied to -debug."

That would actually solve the problem we had with MonoTimeImpl and vibe.d.

But, it would prohibit to define a module just for unit-testing/debug utilities. I think it would need a design decision to the D module system.

How is that? You mean for separate compilation?
That build mode is mostly used to mitigate the CTFE mem consumption.

@WalterBright
Copy link
Member

Does this resolve the compilation slowdown caused by #4384 ?
No. It makes more slow down compilation speed to make linking more stable.

There's got to be a better answer than a 30% slowdown. What about the idea of lazy instantiation?

9rnsr added 5 commits June 7, 2015 20:52
…orward reference in Tuple.opAssign

The "error reproduction instantiation" might succeed to finish semantic analysis by the forward reference resolution. When it happens, the cached error instance needs to be overridden by the succeeded instance.
…non-root modules

All template instances outside of `version(unittest){}` or `debug(num){}` need to use the normal instantiation strategy.

When `-unittest` or `-debug(=num)` switches specified, the instantiation strategy will be tweaked - a conditionally instantiated template code by `version(unittest){}` or `debug(num){}` will be generated conservatively, even if it's done in non root module.

It can be formalized as follows:

R = templates instantiated in root module
N = templates instantiated in non root module
S = templates instantiated speculatively

Default instantiation strategy:
  codegen = R - (R ∩ N)
The new instantiation strategy for -unittest or -debug=num:
  codegen = Rd - (Rd ∩ N)
  Rd = R ∪ Nd
  Nd = version(unittest) and debug template instances in non-root modules
and debug(num){} blocks.
The old instantiation strategy for -allInst:
  codegen = R ∪ N
- If an instantiated function contains debug{} or version(unittest){}, the instance code will be forcibly generated.
- If an instantiated function calls other forcibly instantiated function and its attribute is influenced by inference, that code is also generated forcibly.
@9rnsr
Copy link
Contributor Author

9rnsr commented Jun 29, 2015

Superseded by: #4784

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

Successfully merging this pull request may close these issues.

3 participants