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); } }