Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8267464

Circular-dependency resilient inline headers

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Fixed
    • Icon: P4 P4
    • 17
    • 17
    • hotspot
    • None
    • gc
    • b25

      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 following 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 is declared. b.inline.hpp inlined a.inline.hpp, which uses functions declared in b.hpp, but we've not included enough of b.inline.hpp to have declared b1.

      This is a small, 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.

            stefank Stefan Karlsson
            stefank Stefan Karlsson
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: