d: Add -fbuilding-libphobos-tests option
authorIain Buclaw <ibuclaw@gdcproject.org>
Fri, 12 Apr 2019 06:25:04 +0000 (06:25 +0000)
committerIain Buclaw <ibuclaw@gcc.gnu.org>
Fri, 12 Apr 2019 06:25:04 +0000 (06:25 +0000)
Currently, the druntime and phobos unittests are compiled as a separate
check program, then ran by the libphobos.unittest/unittest.exp script.
As PR d/89255 notes, this process lacks proper multilib handling.

As a first step, a new internal option that instructs the compiler to
put the reference to all unittest functions in another symbol has been
added.  This will allow each module to be compiled separately as a
standalone program using dg-runtest without running into collisions in
the D runtime module registry.

gcc/d/ChangeLog:

2019-04-12  Iain Buclaw  <ibuclaw@gdcproject.org>

* d-tree.h (DECL_IN_UNITTEST_CONDITION_P): Define.
* decl.cc (DeclVisitor): Add in_version_unittest_ field.
(DeclVisitor::visit(ConditionalDeclaration)): New override.
(DeclVisitor::visit(FuncDeclaration)): Set
DECL_IN_UNITTEST_CONDITION_P.
* lang.opt (-fbuilding-libphobos-tests): Add option.
* modules.cc (current_testing_module): New static variable.
(build_module_tree): Generate second moduleinfo symbol to hold
reference to unittests if flag_building_libphobos_tests.
(register_module_decl): Check DECL_IN_UNITTEST_CONDITION_P to decide
which moduleinfo the decl should be registered against.

From-SVN: r270301

gcc/d/ChangeLog
gcc/d/d-tree.h
gcc/d/decl.cc
gcc/d/lang.opt
gcc/d/modules.cc

index bb2a2370467569f9e8cbcf0929355d8c2a925703..5623170ae5edc916aa3b0c5c73542240445a7ae9 100644 (file)
@@ -1,3 +1,17 @@
+2019-04-12  Iain Buclaw  <ibuclaw@gdcproject.org>
+
+       * d-tree.h (DECL_IN_UNITTEST_CONDITION_P): Define.
+       * decl.cc (DeclVisitor): Add in_version_unittest_ field.
+       (DeclVisitor::visit(ConditionalDeclaration)): New override.
+       (DeclVisitor::visit(FuncDeclaration)): Set
+       DECL_IN_UNITTEST_CONDITION_P.
+       * lang.opt (-fbuilding-libphobos-tests): Add option.
+       * modules.cc (current_testing_module): New static variable.
+       (build_module_tree): Generate second moduleinfo symbol to hold
+       reference to unittests if flag_building_libphobos_tests.
+       (register_module_decl): Check DECL_IN_UNITTEST_CONDITION_P to decide
+       which moduleinfo the decl should be registered against.
+
 2019-03-31  Iain Buclaw  <ibuclaw@gdcproject.org>
 
        PR d/88462
index 0b3c5eddedd6e2ded70a95e9f70c2633116185d0..cff832cc645499fb613e5ff435c696c680eabf2e 100644 (file)
@@ -59,7 +59,8 @@ typedef Array<Expression *> Expressions;
 
    Usage of DECL_LANG_FLAG_?:
    0: LABEL_VARIABLE_CASE (in LABEL_DECL).
-      DECL_BUILT_IN_CTFE (in FUNCTION_DECL).  */
+      DECL_BUILT_IN_CTFE (in FUNCTION_DECL).
+   1: DECL_IN_UNITTEST_CONDITION_P (in FUNCTION_DECL).  */
 
 /* The kinds of scopes we recognize.  */
 
@@ -380,6 +381,10 @@ lang_tree_node
 #define DECL_BUILT_IN_CTFE(NODE) \
   (DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE)))
 
+/* True if the decl is only compiled in when unittests are turned on.  */
+#define DECL_IN_UNITTEST_CONDITION_P(NODE) \
+  (DECL_LANG_FLAG_1 (FUNCTION_DECL_CHECK (NODE)))
+
 enum d_tree_index
 {
   DTI_VTABLE_ENTRY_TYPE,
index fffed97727f186c9a237fa41b267cc3541288bac..f6c863988e31b7201c5b4e6be7cec8744d08f883 100644 (file)
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "dmd/aggregate.h"
 #include "dmd/attrib.h"
+#include "dmd/cond.h"
 #include "dmd/ctfe.h"
 #include "dmd/declaration.h"
 #include "dmd/enum.h"
@@ -121,9 +122,13 @@ class DeclVisitor : public Visitor
 {
   using Visitor::visit;
 
+  /* If we're lowering the body of a version(unittest) condition.  */
+  bool in_version_unittest_;
+
 public:
   DeclVisitor (void)
   {
+    this->in_version_unittest_ = false;
   }
 
   /* This should be overridden by each declaration class.  */
@@ -241,6 +246,25 @@ public:
     visit ((AttribDeclaration *) d);
   }
 
+  /* Conditional compilation is the process of selecting which code to compile
+     and which code to not compile.  Look for version conditions that may  */
+
+  void visit (ConditionalDeclaration *d)
+  {
+    bool old_condition = this->in_version_unittest_;
+
+    if (global.params.useUnitTests)
+      {
+       VersionCondition *vc = d->condition->isVersionCondition ();
+       if (vc && vc->ident == Identifier::idPool ("unittest"))
+         this->in_version_unittest_ = true;
+      }
+
+    visit ((AttribDeclaration *) d);
+
+    this->in_version_unittest_ = old_condition;
+  }
+
   /* Walk over all members in the namespace scope.  */
 
   void visit (Nspace *d)
@@ -868,6 +892,7 @@ public:
       }
 
     DECL_ARGUMENTS (fndecl) = param_list;
+    DECL_IN_UNITTEST_CONDITION_P (fndecl) = this->in_version_unittest_;
     rest_of_decl_compilation (fndecl, 1, 0);
 
     /* If this is a member function that nested (possibly indirectly) in another
index 523f73c90de06bea8c58da015fb465753692a937..f65be444d4532f52c61f7fbcc0e980dc0cdd0c4f 100644 (file)
@@ -197,6 +197,10 @@ Enum(bounds_check) String(safeonly) Value(1)
 EnumValue
 Enum(bounds_check) String(on) Value(2)
 
+; Generates a secondary ModuleInfo symbol for linking in unittests
+fbuilding-libphobos-tests
+D Undocumented Var(flag_building_libphobos_tests)
+
 fbuiltin
 D Var(flag_no_builtin, 0)
 ; Documented in C
index e9bd44115f9160d7171f820585f232c87ee1cec9..315f5d82356a3818cb77c8e891b96d449f99f23f 100644 (file)
@@ -110,6 +110,11 @@ enum module_info_flags
 
 static module_info *current_moduleinfo;
 
+/* When compiling with -fbuilding-libphobos-tests, this contains information
+   about the module that gets compiled in only when unittests are enabled.  */
+
+static module_info *current_testing_module;
+
 /* The declaration of the current module being compiled.  */
 
 static Module *current_module_decl;
@@ -706,8 +711,10 @@ build_module_tree (Module *decl)
   assert (!current_moduleinfo && !current_module_decl);
 
   module_info mi = module_info ();
+  module_info mitest = module_info ();
 
   current_moduleinfo = &mi;
+  current_testing_module = &mitest;
   current_module_decl = decl;
 
   /* Layout module members.  */
@@ -720,6 +727,53 @@ build_module_tree (Module *decl)
        }
     }
 
+  /* For libphobos-internal use only.  Generate a separate module info symbol
+     that references all compiled in unittests, this allows compiling library
+     modules and linking to libphobos without having run-time conflicts because
+     of two ModuleInfo records with the same name being present in two DSOs.  */
+  if (flag_building_libphobos_tests)
+    {
+      /* Associate the module info symbol with a mock module.  */
+      const char *name = concat (GDC_PREFIX ("modtest__"),
+                                decl->ident->toChars (), NULL);
+      Module *tm = Module::create (decl->arg, Identifier::idPool (name), 0, 0);
+      Dsymbols members;
+
+      /* Setting parent puts module in the same package as the current, to
+        avoid any symbol conflicts.  */
+      tm->parent = decl->parent;
+      tm->needmoduleinfo = decl->needmoduleinfo;
+      tm->members = &members;
+      /* Register the current module as being imported by the mock module.
+        This informs run-time that there is a dependency between the two.  */
+      tm->aimports.push (decl);
+
+      if (mitest.ctors || mitest.ctorgates)
+       tm->sctor = build_funcs_gates_fn (get_identifier ("*__modtestctor"),
+                                         mitest.ctors, mitest.ctorgates);
+
+      if (mitest.dtors)
+       tm->sdtor = build_funcs_gates_fn (get_identifier ("*__modtestdtor"),
+                                         mitest.dtors, NULL);
+
+      if (mitest.sharedctors || mitest.sharedctorgates)
+       tm->ssharedctor
+         = build_funcs_gates_fn (get_identifier ("*__modtestsharedctor"),
+                                 mitest.sharedctors, mitest.sharedctorgates);
+
+      if (mitest.shareddtors)
+       tm->sshareddtor
+         = build_funcs_gates_fn (get_identifier ("*__modtestshareddtor"),
+                                 mitest.shareddtors, NULL);
+
+      if (mi.unitTests)
+       tm->stest = build_funcs_gates_fn (get_identifier ("*__modtest"),
+                                         mi.unitTests, NULL);
+
+      mi.unitTests = NULL;
+      layout_moduleinfo (tm);
+    }
+
   /* Default behavior is to always generate module info because of templates.
      Can be switched off for not compiling against runtime library.  */
   if (!global.params.betterC
@@ -751,6 +805,7 @@ build_module_tree (Module *decl)
     }
 
   current_moduleinfo = NULL;
+  current_testing_module = NULL;
   current_module_decl = NULL;
 }
 
@@ -777,13 +832,24 @@ register_module_decl (Declaration *d)
     {
       tree decl = get_symbol_decl (fd);
 
+      /* Any module constructors or destructors that are only present when
+        compiling in unittests are kept track of separately so they are
+        not omitted when compiling with -fbuilding-libphobos-tests.  */
+      module_info *minfo;
+      if (flag_building_libphobos_tests && DECL_IN_UNITTEST_CONDITION_P (decl))
+       minfo = current_testing_module;
+      else
+       minfo = current_moduleinfo;
+
+      gcc_assert (minfo != NULL);
+
       /* If a static constructor, push into the current ModuleInfo.
         Checks for `shared' first because it derives from the non-shared
         constructor type in the front-end.  */
       if (fd->isSharedStaticCtorDeclaration ())
-       vec_safe_push (current_moduleinfo->sharedctors, decl);
+       vec_safe_push (minfo->sharedctors, decl);
       else if (fd->isStaticCtorDeclaration ())
-       vec_safe_push (current_moduleinfo->ctors, decl);
+       vec_safe_push (minfo->ctors, decl);
 
       /* If a static destructor, do same as with constructors, but also
         increment the destructor's vgate at construction time.  */
@@ -793,9 +859,9 @@ register_module_decl (Declaration *d)
          if (vgate != NULL)
            {
              tree gate = get_symbol_decl (vgate);
-             vec_safe_push (current_moduleinfo->sharedctorgates, gate);
+             vec_safe_push (minfo->sharedctorgates, gate);
            }
-         vec_safe_insert (current_moduleinfo->shareddtors, 0, decl);
+         vec_safe_insert (minfo->shareddtors, 0, decl);
        }
       else if (fd->isStaticDtorDeclaration ())
        {
@@ -803,14 +869,14 @@ register_module_decl (Declaration *d)
          if (vgate != NULL)
            {
              tree gate = get_symbol_decl (vgate);
-             vec_safe_push (current_moduleinfo->ctorgates, gate);
+             vec_safe_push (minfo->ctorgates, gate);
            }
-         vec_safe_insert (current_moduleinfo->dtors, 0, decl);
+         vec_safe_insert (minfo->dtors, 0, decl);
        }
 
       /* If a unittest function.  */
       if (fd->isUnitTestDeclaration ())
-       vec_safe_push (current_moduleinfo->unitTests, decl);
+       vec_safe_push (minfo->unitTests, decl);
     }
 }