Patch updated and committed to trunk as r270301.

-- 
Iain
---
2019-04-12  Iain Buclaw  <ibuc...@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.

---
diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
index 0b3c5eddedd..cff832cc645 100644
--- a/gcc/d/d-tree.h
+++ b/gcc/d/d-tree.h
@@ -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,
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index fffed97727f..f6c863988e3 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -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
diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt
index 523f73c90de..f65be444d45 100644
--- a/gcc/d/lang.opt
+++ b/gcc/d/lang.opt
@@ -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
diff --git a/gcc/d/modules.cc b/gcc/d/modules.cc
index e9bd44115f9..315f5d82356 100644
--- a/gcc/d/modules.cc
+++ b/gcc/d/modules.cc
@@ -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);
     }
 }
 

Reply via email to