mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 21:21:48 +03:00
Add @standalone
attribute for module constructors (#15537)
* Add `@__standalone` attribute * Remove underscores from __standalone
This commit is contained in:
parent
d05cf6e9b0
commit
14ecea9ea7
13 changed files with 156 additions and 20 deletions
32
changelog/dmd.standalone-attribute.dd
Normal file
32
changelog/dmd.standalone-attribute.dd
Normal file
|
@ -0,0 +1,32 @@
|
|||
Added `@standalone` for module constructors
|
||||
|
||||
When two modules import each other and both have module constructors,
|
||||
druntime would throw an error because it can't determine which to run first.
|
||||
|
||||
This could be circumvented by using `pragma(crt_constructor)` instead, but in C runtime constructors, druntime isn't initialized.
|
||||
Therefore the Garbage Collector can't be used in such constructors.
|
||||
|
||||
`@standalone` is a new attribute that can be used to mark module constructors that run after druntime has been initialized,
|
||||
but do not depend on any other module constructors being run before it, so it will not cause a cyclic dependency error.
|
||||
It must be imported from `core.attribute`.
|
||||
|
||||
The compiler doesn't verify that the module constructor truly doesn't depend on other variables being initialized, so it must be enforced manually.
|
||||
Because of this, they must be marked `@system` or `@trusted`.
|
||||
|
||||
---
|
||||
import core.attribute : standalone;
|
||||
|
||||
immutable int* x;
|
||||
|
||||
@standalone @system shared static this()
|
||||
{
|
||||
x = new int(10);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
assert(*x == 10);
|
||||
}
|
||||
---
|
||||
|
||||
If possible, prefer to solve cyclic dependency errors by putting the offending module constructors into their own smaller modules instead of using `@standalone`.
|
|
@ -1296,3 +1296,27 @@ int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns: true if the given expression is an enum from `core.attribute` named `id`
|
||||
*/
|
||||
bool isEnumAttribute(Expression e, Identifier id)
|
||||
{
|
||||
import dmd.attrib : isCoreUda;
|
||||
import dmd.id : Id;
|
||||
|
||||
// Logic based on dmd.objc.Supported.declaredAsOptionalCount
|
||||
auto typeExp = e.isTypeExp;
|
||||
if (!typeExp)
|
||||
return false;
|
||||
|
||||
auto typeEnum = typeExp.type.isTypeEnum();
|
||||
if (!typeEnum)
|
||||
return false;
|
||||
|
||||
if (isCoreUda(typeEnum.sym, id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -843,6 +843,7 @@ public:
|
|||
class SharedStaticCtorDeclaration final : public StaticCtorDeclaration
|
||||
{
|
||||
public:
|
||||
bool standalone;
|
||||
SharedStaticCtorDeclaration *syntaxCopy(Dsymbol *) override;
|
||||
|
||||
SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() override { return this; }
|
||||
|
|
|
@ -4592,6 +4592,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
|||
m.needmoduleinfo = 1;
|
||||
//printf("module1 %s needs moduleinfo\n", m.toChars());
|
||||
}
|
||||
|
||||
foreachUda(scd, sc, (Expression e) {
|
||||
import dmd.attrib : isEnumAttribute;
|
||||
if (!isEnumAttribute(e, Id.udaStandalone))
|
||||
return 0;
|
||||
|
||||
if (auto sharedCtor = scd.isSharedStaticCtorDeclaration())
|
||||
{
|
||||
auto trust = sharedCtor.type.isTypeFunction().trust;
|
||||
if (trust != TRUST.system && trust != TRUST.trusted)
|
||||
error(e.loc, "a module constructor using `@%s` must be `@system` or `@trusted`", Id.udaStandalone.toChars());
|
||||
sharedCtor.standalone = true;
|
||||
}
|
||||
else
|
||||
.error(e.loc, "`@%s` can only be used on shared static constructors", Id.udaStandalone.toChars());
|
||||
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
override void visit(StaticDtorDeclaration sdd)
|
||||
|
|
|
@ -4005,6 +4005,7 @@ public:
|
|||
class SharedStaticCtorDeclaration final : public StaticCtorDeclaration
|
||||
{
|
||||
public:
|
||||
bool standalone;
|
||||
SharedStaticCtorDeclaration* syntaxCopy(Dsymbol* s) override;
|
||||
SharedStaticCtorDeclaration* isSharedStaticCtorDeclaration() override;
|
||||
void accept(Visitor* v) override;
|
||||
|
@ -8825,6 +8826,7 @@ struct Id final
|
|||
static Identifier* udaSelector;
|
||||
static Identifier* udaOptional;
|
||||
static Identifier* udaMustUse;
|
||||
static Identifier* udaStandalone;
|
||||
static Identifier* TRUE;
|
||||
static Identifier* FALSE;
|
||||
static Identifier* ImportC;
|
||||
|
|
|
@ -4253,6 +4253,9 @@ extern (C++) class StaticCtorDeclaration : FuncDeclaration
|
|||
*/
|
||||
extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration
|
||||
{
|
||||
/// Exclude this constructor from cyclic dependency check
|
||||
bool standalone;
|
||||
|
||||
extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc)
|
||||
{
|
||||
super(loc, endloc, "_sharedStaticCtor", stc);
|
||||
|
|
|
@ -203,12 +203,13 @@ struct Glue
|
|||
elem *eictor;
|
||||
Symbol *ictorlocalgot;
|
||||
|
||||
symbols sctors;
|
||||
symbols sctors; // static constructorss
|
||||
StaticDtorDeclarations ectorgates;
|
||||
symbols sdtors;
|
||||
symbols stests;
|
||||
|
||||
symbols ssharedctors;
|
||||
symbols ssharedctors; // shared static constructors
|
||||
symbols sisharedctors; // standalone shared static constructors
|
||||
SharedStaticDtorDeclarations esharedctorgates;
|
||||
symbols sshareddtors;
|
||||
|
||||
|
@ -637,7 +638,7 @@ private void genObjFile(Module m, bool multiobj)
|
|||
|
||||
// If coverage / static constructor / destructor / unittest calls
|
||||
if (glue.eictor || glue.sctors.length || glue.ectorgates.length || glue.sdtors.length ||
|
||||
glue.ssharedctors.length || glue.esharedctorgates.length || glue.sshareddtors.length || glue.stests.length)
|
||||
glue.ssharedctors.length || glue.esharedctorgates.length || glue.sshareddtors.length || glue.stests.length || glue.sisharedctors.length)
|
||||
{
|
||||
if (glue.eictor)
|
||||
{
|
||||
|
@ -654,6 +655,13 @@ private void genObjFile(Module m, bool multiobj)
|
|||
m.sctor = callFuncsAndGates(m, glue.sctors[], glue.ectorgates[], "__modctor");
|
||||
m.sdtor = callFuncsAndGates(m, glue.sdtors[], null, "__moddtor");
|
||||
|
||||
if (glue.sisharedctors.length > 0)
|
||||
{
|
||||
if (m.sictor)
|
||||
glue.sisharedctors.shift(m.sictor);
|
||||
m.sictor = callFuncsAndGates(m, glue.sisharedctors[], null, "__modsharedictor");
|
||||
}
|
||||
|
||||
m.ssharedctor = callFuncsAndGates(m, glue.ssharedctors[], cast(StaticDtorDeclaration[])glue.esharedctorgates[], "__modsharedctor");
|
||||
m.sshareddtor = callFuncsAndGates(m, glue.sshareddtors[], null, "__modshareddtor");
|
||||
m.stest = callFuncsAndGates(m, glue.stests[], null, "__modtest");
|
||||
|
@ -1198,8 +1206,11 @@ public void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
|
|||
insertFinallyBlockCalls(f.Fstartblock);
|
||||
|
||||
// If static constructor
|
||||
if (fd.isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration
|
||||
if (auto sctor = fd.isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration
|
||||
{
|
||||
if (sctor.standalone)
|
||||
glue.sisharedctors.push(s);
|
||||
else
|
||||
glue.ssharedctors.push(s);
|
||||
}
|
||||
else if (fd.isStaticCtorDeclaration())
|
||||
|
|
|
@ -521,6 +521,7 @@ immutable Msgtable[] msgtable =
|
|||
{ "udaSelector", "selector" },
|
||||
{ "udaOptional", "optional"},
|
||||
{ "udaMustUse", "mustuse" },
|
||||
{ "udaStandalone", "standalone" },
|
||||
|
||||
// C names, for undefined identifier error messages
|
||||
{ "NULL" },
|
||||
|
|
|
@ -222,20 +222,7 @@ private bool hasMustUseAttribute(Dsymbol sym, Scope* sc)
|
|||
*/
|
||||
private bool isMustUseAttribute(Expression e)
|
||||
{
|
||||
import dmd.attrib : isCoreUda;
|
||||
import dmd.attrib : isEnumAttribute;
|
||||
import dmd.id : Id;
|
||||
|
||||
// Logic based on dmd.objc.Supported.declaredAsOptionalCount
|
||||
auto typeExp = e.isTypeExp;
|
||||
if (!typeExp)
|
||||
return false;
|
||||
|
||||
auto typeEnum = typeExp.type.isTypeEnum();
|
||||
if (!typeEnum)
|
||||
return false;
|
||||
|
||||
if (isCoreUda(typeEnum.sym, Id.udaMustUse))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return isEnumAttribute(e, Id.udaMustUse);
|
||||
}
|
||||
|
|
15
compiler/test/fail_compilation/standalone_modctor.d
Normal file
15
compiler/test/fail_compilation/standalone_modctor.d
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/standalone_modctor.d(11): Error: `@standalone` can only be used on shared static constructors
|
||||
fail_compilation/standalone_modctor.d(12): Error: a module constructor using `@standalone` must be `@system` or `@trusted`
|
||||
fail_compilation/standalone_modctor.d(13): Error: a module constructor using `@standalone` must be `@system` or `@trusted`
|
||||
---
|
||||
*/
|
||||
import core.attribute : standalone;
|
||||
|
||||
@standalone static this() {}
|
||||
@standalone shared static this() {}
|
||||
@standalone shared static this() @safe {}
|
||||
@standalone shared static this() @trusted {}
|
||||
@standalone shared static this() @system {}
|
11
compiler/test/runnable/imports/standalone_b.d
Normal file
11
compiler/test/runnable/imports/standalone_b.d
Normal file
|
@ -0,0 +1,11 @@
|
|||
module standalone_b;
|
||||
|
||||
import standalone_modctor;
|
||||
import core.attribute : standalone;
|
||||
|
||||
immutable int* y;
|
||||
|
||||
@standalone @system shared static this()
|
||||
{
|
||||
y = new int(2);
|
||||
}
|
19
compiler/test/runnable/standalone_modctor.d
Normal file
19
compiler/test/runnable/standalone_modctor.d
Normal file
|
@ -0,0 +1,19 @@
|
|||
// REQUIRED_ARGS: -Irunnable/imports
|
||||
// EXTRA_SOURCES: imports/standalone_b.d
|
||||
// PERMUTE_ARGS: -cov
|
||||
|
||||
import standalone_b;
|
||||
import core.attribute : standalone;
|
||||
|
||||
immutable int* x;
|
||||
|
||||
@standalone @system shared static this()
|
||||
{
|
||||
x = new int(1);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
assert(*x == 1);
|
||||
assert(*y == 2);
|
||||
}
|
|
@ -290,3 +290,15 @@ version (UdaGNUAbiTag) struct gnuAbiTag
|
|||
* ---
|
||||
*/
|
||||
enum mustuse;
|
||||
|
||||
/**
|
||||
* Use this attribute to indicate that a shared module constructor does not depend on any
|
||||
* other module constructor being run first. This avoids errors on cyclic module constructors.
|
||||
*
|
||||
* However, it is now up to the user to enforce safety.
|
||||
* The module constructor must be marked `@system` as a result.
|
||||
* Prefer to refactor the module constructor causing the cycle so it's in its own module if possible.
|
||||
*
|
||||
* This is only allowed on `shared` static constructors, not thread-local module constructors.
|
||||
*/
|
||||
enum standalone;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue