Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,21 @@ struct ASTBase
srcfile = new File(srcfilename);
}

/**
A root module is one that will be compiled to object code.
*/
final bool isRoot() const
{
return true;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When does isRoot return false for a module such that its unittests don't compile?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRoot will return false if the module isn't "taken" to object code (see comment). This will happen if the module is not passed on the command line and is not included as a "compiled import"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I'm just asking because this looks pretty constant (final, returns true). What is the type of AST class that represents an import module?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'm seeing that this is duplicated in dmodule.d, but with a different implementation (one that makes sense). I'm not sure what the astbase.d file does, but it sure seems like it's doing something different than dmodule.d. What is this for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was just looking through the comments and saw that your question wasn't answered. The module class you are looking at is missing ALOT of information, it's just as STUB as far as I know probably meant to hold a future refactored version of the current Module class in dmodule.d.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had already figured it out. it seems it's there for when you want to instantiate the parser only, and not actually compile anything. Perhaps if you aren't generating object code.

}

/**
Called by the parser after the module declaration has been determined.
*/
final void afterModuleDeclaration(ModuleDeclaration* md)
{
}

override void accept(Visitor v)
{
v.visit(this);
Expand Down
80 changes: 33 additions & 47 deletions src/dmd/dmodule.d
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,10 @@ extern (C++) final class Module : Package
extern (C++) static __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them
extern (C++) static __gshared uint dprogress; // progress resolving the deferred list
/**
* A callback function that is called once an imported module is
* parsed. If the callback returns true, then it tells the
* frontend that the driver intends on compiling the import.
*/
A callback function that is called when a module is loaded as soon as its
module declaration has been parsed. If the callback returns true, then it tells the
frontend that the driver intends on compiling the import.
*/
extern (C++) static __gshared bool function(Module mod) onImport;

static void _init()
Expand Down Expand Up @@ -370,12 +370,10 @@ extern (C++) final class Module : Package
int searchCacheFlags; // cached flags

/**
* A root module is one that will be compiled all the way to
* object code. This field holds the root module that caused
* this module to be loaded. If this module is a root module,
* then it will be set to `this`. This is used to determine
* ownership of template instantiation.
*/
This field holds the root module that caused this module
to be loaded. If this module is a root module, then it will
be set to `this`.
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless it's no longer true, I'd keep the part of the comment that says "This is used to determine the ownership of templaet instantiation".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The information was just moved to the isRoot function.

Module importedFrom;

Dsymbols* decldefs; // top level declarations for this Module
Expand Down Expand Up @@ -515,41 +513,6 @@ extern (C++) final class Module : Package
message("import %s", buf.peekString());
}
m = m.parse();

// Call onImport here because if the module is going to be compiled then we
// need to determine it early because it affects semantic analysis. This is
// being done after parsing the module so the full module name can be taken
// from whatever was declared in the file.

//!!!!!!!!!!!!!!!!!!!!!!!
// Workaround for bug in dmd version 2.068.2 platform Darwin_64_32.
// This is the compiler version that the autotester uses, and this code
// has been carefully crafted using trial and error to prevent a seg fault
// bug that occurs with that version of the compiler. Note, this segfault
// does not occur on the next version of dmd, namely, version 2.069.0. If
// the autotester upgrades to that version, then this workaround can be removed.
//!!!!!!!!!!!!!!!!!!!!!!!
version(OSX)
{
if (!m.isRoot() && onImport)
{
auto onImportResult = onImport(m);
if(onImportResult)
{
m.importedFrom = m;
assert(m.isRoot());
}
}
}
else
{
if (!m.isRoot() && onImport && onImport(m))
{
m.importedFrom = m;
assert(m.isRoot());
}
}

Target.loadModule(m);
return m;
}
Expand Down Expand Up @@ -853,7 +816,6 @@ extern (C++) final class Module : Package
scope p = new Parser!ASTCodegen(this, buf[0 .. buflen], docfile !is null);
p.nextToken();
members = p.parseModule();
md = p.md;
numlines = p.scanloc.linnum;
if (p.errors)
++global.errors;
Expand Down Expand Up @@ -1288,11 +1250,35 @@ extern (C++) final class Module : Package
return false;
}

bool isRoot()
/**
A root module is one that will be compiled to object code. This is used to
determine ownership of template instantiation and when to compile unittests.
Returns:
true if this is a root module
*/
final bool isRoot() const
{
return this.importedFrom == this;
}

/**
Called by the parser after the module declaration has been determined.
*/
final void afterModuleDeclaration(ModuleDeclaration* md)
{
this.md = md;

// Call onImport here because if the module is going to be compiled then we
// need to determine it early because it affects whether unittests will be parsed
// and semantic analysis. This is being done right after parsing the module delclaration
// so the full module name can be taken from whatever was declared in the file.
if (!isRoot() && onImport && onImport(this))
{
importedFrom = this;
assert(isRoot());
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could move Target.loadModule here also.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the module need to be fully parsed before calling that, or does it just need the module declaration parsed, or does it not need anything to be parsed?

Copy link
Copy Markdown
Contributor Author

@marler8997 marler8997 Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait...I see from the comment it says "post parsing". Which means it shouldn't be called in this function since this function is called in the middle of parsing (right after parsing the module declaration).

Copy link
Copy Markdown
Contributor Author

@marler8997 marler8997 Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to rename this function to something like onModuleParsed in another PR? (I'll let you decide if it's a good idea)

}

// true if the module source file is directly
// listed in command line.
bool isCoreModule(Identifier ident)
Expand Down
5 changes: 2 additions & 3 deletions src/dmd/dtemplate.d
Original file line number Diff line number Diff line change
Expand Up @@ -6360,8 +6360,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
* is behind a conditional debug declaration.
*/
// workaround for https://issues.dlang.org/show_bug.cgi?id=11239
if (global.params.useUnitTests ||
global.params.debuglevel)
if (global.params.debuglevel)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you test the linked bug after making this change? Doesn't look like the CI gets so far along to run it itself.

Copy link
Copy Markdown
Contributor Author

@marler8997 marler8997 Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being tested and is passing on the autotester. It's not until the "test phobos" stage that we get linker errors due to DIP 1000 bugs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ibuclaw the original bug only had to do with -debug switch. As far as I can tell, we have never tried (before this PR) compiling code that doesn't move templates into root modules when -unittest is specified.

{
// Prefer instantiations from root modules, to maximize link-ability.
if (minst.isRoot())
Expand Down Expand Up @@ -7354,7 +7353,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
{
Module mi = minst; // instantiated . inserted module

if (global.params.useUnitTests || global.params.debuglevel)
if (global.params.debuglevel)
{
// Turn all non-root instances to speculative
if (mi && !mi.isRoot())
Expand Down
20 changes: 10 additions & 10 deletions src/dmd/glue.d
Original file line number Diff line number Diff line change
Expand Up @@ -752,8 +752,17 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
if (!fd.fbody)
return;

// Find module m for this function
Module m = null;
for (Dsymbol p = fd.parent; p; p = p.parent)
{
m = p.isModule();
if (m)
break;
}

UnitTestDeclaration ud = fd.isUnitTestDeclaration();
if (ud && !global.params.useUnitTests)
if (ud && (!global.params.useUnitTests || !m.isRoot))
return;

if (multiobj && !fd.isStaticDtorDeclaration() && !fd.isStaticCtorDeclaration())
Expand Down Expand Up @@ -886,15 +895,6 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
symtab_t *symtabsave = cstate.CSpsymtab;
cstate.CSpsymtab = &f.Flocsym;

// Find module m for this function
Module m = null;
for (Dsymbol p = fd.parent; p; p = p.parent)
{
m = p.isModule();
if (m)
break;
}

Dsymbols deferToObj; // write these to OBJ file later
Array!(elem*) varsInScope;
Label*[void*] labels = null;
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class Module : public Package
static void clearCache();
int imports(Module *m);

bool isRoot() { return this->importedFrom == this; }
bool isRoot() const { return this->importedFrom == this; }
// true if the module source file is directly
// listed in command line.
bool isCoreModule(Identifier *ident);
Expand Down
4 changes: 3 additions & 1 deletion src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ final class Parser(AST) : Lexer
}
}

mod.afterModuleDeclaration(md);

decldefs = parseDeclDefs(0, &lastDecl);
if (token.value != TOK.endOfFile)
{
Expand Down Expand Up @@ -547,7 +549,7 @@ final class Parser(AST) : Lexer
break;
}
case TOK.unittest_:
if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
if ( (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration) && mod.isRoot)
{
s = parseUnitTest(pAttrs);
if (*pLastDecl)
Expand Down
17 changes: 17 additions & 0 deletions test/runnable/extra-files/check_bin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

if [ "$2" == "HAS" ]; then
GREP_OPTION=
elif [ "$2" == "DOES_NOT_HAVE" ]; then
GREP_OPTION=-v
else
echo Unknown option "$2"
exit 1
fi

if [ ${OS} != "linux" ]; then
echo Skipping checkbin $2 $3 because os $OS is not linux
exit 0
fi

objdump -t "$1" | grep -q $GREP_OPTION "$3"
6 changes: 6 additions & 0 deletions test/runnable/extra-files/printVersionUnittest.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module printVersionUnittest;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the next module is printingUnittest, this one should be pragmaMsgVersionUnittest, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meh, not a big deal, kind of a long name


version(unittest)
{
pragma(msg, "version unittest");
}
7 changes: 7 additions & 0 deletions test/runnable/extra-files/printingUnittest.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module printingUnittest;

unittest
{
import core.stdc.stdio;
printf("unittesting printingUnittest\n");
}
6 changes: 6 additions & 0 deletions test/runnable/extra-files/selectiveUnittests.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import printingUnittest;
void main()
{
// have to print something so grep works
import core.stdc.stdio; printf("done\n");
}
4 changes: 4 additions & 0 deletions test/runnable/extra-files/selectiveUnittests2.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import printVersionUnittest;
void main()
{
}
29 changes: 29 additions & 0 deletions test/runnable/selectiveUnittests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash

obj_file=${OUTPUT_BASE}/tempobj${OBJ}
exe_file=${OUTPUT_BASE}/tempexe${EXE}

# Compile both modules, make sure unittest compiles/runs
unittest_output="unittesting printingUnittest"
$DMD -m${MODEL} -of=${exe_file} -I=${EXTRA_FILES} -unittest ${EXTRA_FILES}/printingUnittest.d ${EXTRA_FILES}/selectiveUnittests.d
${exe_file} | grep -q "${unittest_output}"
./runnable/extra-files/check_bin.sh ${exe_file} HAS __unittest

# Precompile the module and make sure it's unittest doesn't get compiled/run
$DMD -m${MODEL} -c -of=${obj_file} ${EXTRA_FILES}/printingUnittest.d
./runnable/extra-files/check_bin.sh ${exe_file} DOES_NOT_HAVE __unittest

rm -f ${exe_file}
$DMD -m${MODEL} -of=${exe_file} -I=${EXTRA_FILES} -unittest ${obj_file} ${EXTRA_FILES}/selectiveUnittests.d
${exe_file} | grep -q -v "${unittest_output}"

# Make sure that version(unittest) still get analyzed
unittest_output="version unittest"
$DMD -m${MODEL} -I=${EXTRA_FILES} -o- -unittest ${EXTRA_FILES}/printVersionUnittest.d ${EXTRA_FILES}/selectiveUnittests2.d 2>&1 | grep -q "${unittest_output}"

# Precompile the module and make sure version(unittest) is still enabled
$DMD -m${MODEL} -c -of=${obj_file} ${EXTRA_FILES}/printVersionUnittest.d
$DMD -m${MODEL} -I=${EXTRA_FILES} -o- -unittest ${obj_file} ${EXTRA_FILES}/selectiveUnittests2.d 2>&1 | grep -q "${unittest_output}"

rm -f ${obj_file}
rm -f ${exe_file}