[prev in list] [next in list] [prev in thread] [next in thread]
List: openjdk-serviceability-dev
Subject: Integrated: 8267464: Circular-dependency resilient inline headers
From: Stefan Karlsson <stefank () openjdk ! java ! net>
Date: 2021-05-31 9:02:32
Message-ID: H9v57pFhdfqtfzMpdJfBS5Ed3hNBQg6hSM3XuNYmGcY=.3f812158-2b34-418e-8172-5d47af7fa8cc () github ! com
[Download RAW message or body]
On Thu, 20 May 2021 11:55:05 GMT, Stefan Karlsson <stefank@openjdk.org> wrote:
> I'd like to propose a small adjustment to how we write .inline.hpp files, in the \
> hope to reduce include problems because of circular dependencies between inline \
> headers.
> This is a small, contrived example to show the problem:
>
> // a.hpp
> #pragma once
>
> void a1();
> void a2();
>
>
> // a.inline.hpp
> #pragma once
>
> #include "a.hpp"
> #include "b.inline.hpp"
>
> inline void a1() {
> b1();
> }
>
> inline void a2() {
> }
>
>
> // b.hpp
> #pragma once
>
> void b1();
> void b2();
>
>
> // b.inline.hpp
> #pragma once
>
> #include "a.inline.hpp"
> #include "b.hpp"
>
> inline void b1() {
> }
>
> inline void b2() {
> a1();
> }
>
> The following code compiles fine:
>
> // a.cpp
> #include "a.inline.hpp"
>
> int main() {
> a1();
>
> return 0;
> }
>
> But the following:
>
> // b.cpp
> #include "b.inline.hpp"
>
> int main() {
> b1();
>
> return 0;
> }
>
>
> fails with the this error message:
>
> In file included from b.inline.hpp:3,
> from b.cpp:1:
> a.inline.hpp: In function ‘void a1()':
> a.inline.hpp:8:3: error: ‘b1' was not declared in this scope; did you mean \
> ‘a1'?
> We can see the problem with g++ -E:
>
> # 1 "a.inline.hpp" 1
> # 1 "a.hpp" 1
>
> void a1();
> void a2();
>
> # 4 "a.inline.hpp" 2
>
> inline void a1() {
> b1();
> }
>
> inline void a2() {
> }
>
> # 4 "b.inline.hpp" 2
> # 1 "b.hpp" 1
>
> void b1();
> void b2();
>
> # 5 "b.inline.hpp" 2
>
> inline void b1() {
> }
>
> inline void b2() {
> a1();
> }
>
> # 2 "b.cpp" 2
>
> int main() {
> b1();
>
> return 0;
> }
>
>
> b1() is called before it has been declared. b.inline.hpp inlined a.inline.hpp, \
> which uses functions declared in b.hpp. However, at that point we've not included \
> enough of b.inline.hpp to have declared b1().
> This is a small and easy example. In the JVM the circular dependencies usually \
> involves more than two files, and often from different sub-systems of the JVM. We \
> have so-far solved this by restructuring the code, making functions out-of-line, \
> creating proxy objects, etc. However, the more we use templates, the more this is \
> going to become a reoccurring nuisance. And in experiments with lambdas, which \
> requires templates, this very quickly showed up as a problem.
> I propose that we solve most (all?) of these issues by adding a rule that states \
> that .inline.hpp files should start by including the corresponding .hpp, and that \
> the .hpp should contain the declarations of all externally used parts of the \
> .inline.hpp file. This should guarantee that the declarations are always present \
> before any subsequent include can create a circular include dependency back to the \
> original file.
> In the example above, b.inline.hpp would become:
>
> // b.inline.hpp
> #pragma once
>
> #include "b.hpp"
> #include "a.inline.hpp"
>
> inline void b1() {
> }
>
> inline void b2() {
> a1();
> }
>
> and now both a.cpp and b.cpp compiles. The generated output now looks like this:
>
> # 1 "b.inline.hpp" 1
> # 1 "b.hpp" 1
>
> void b1();
> void b2();
>
> # 4 "b.inline.hpp" 2
> # 1 "a.inline.hpp" 1
>
> # 1 "a.hpp" 1
>
> void a1();
> void a2();
>
> # 4 "a.inline.hpp" 2
>
> inline void a1() {
> b1();
> }
>
> inline void a2() {
> }
>
> # 5 "b.inline.hpp" 2
>
> inline void b1() {
> }
>
> inline void b2() {
> a1();
> }
>
> # 2 "b.cpp" 2
>
> int main() {
> b1();
>
> return 0;
> }
>
> The declarations come first, and the compiler is happy.
>
> An alternative to this, that has been proposed by other HotSpot GC devs have been \
> to always include all .hpp files first, and then have a section of .inline.hpp \
> includes. I've experimented with that as well, but I think it requires more \
> maintenance and vigilance of *users* .inline.hpp files (add .hpp include to the \
> first section, add .inline.hpp section to second). The proposed structures only \
> requires extra care from *writers* of .inline.hpp files. All others can include \
> .hpp and .inline.hpp as we've been doing for a long time now.
> Some notes about this patch:
> 1) Some externally-used declarations have been placed in .inline.hpp files, and not \
> in the .hpp. Those should be moved. I have not done that. 2) Some .inline.hpp files \
> don't have a corresponding .hpp file. I could either try to fix that in this patch, \
> or we could take care of that as separate patches later. 3) The style I chose was \
> to add the .hpp include and a extra blank line, separating it from the rest of the \
> includes. I think I like that style, because it makes it easy for those writing \
> .inline.hpp to recognize this pattern. And it removes the confusion why this \
> include isn't sorted into the other includes. 4) We have a few *special* platform \
> dependent header files. Both those that simply are included into platform \
> independent files, and those that inject code *into* the platform independent \
> classes. Extra care, as always, need to be taken around those files. 5) Mostly \
> tested locally, but I'll test on more platforms if the idea is accepted.
> What do you think?
This pull request has now been integrated.
Changeset: 64f0f689
Author: Stefan Karlsson <stefank@openjdk.org>
URL: https://git.openjdk.java.net/jdk/commit/64f0f68958a74d4ee34c4b738759fc2278fa8b47
Stats: 490 lines in 242 files changed: 349 ins; 118 del; 23 mod
8267464: Circular-dependency resilient inline headers
Reviewed-by: kbarrett, eosterlund, dholmes, kvn
-------------
PR: https://git.openjdk.java.net/jdk/pull/4127
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic