diff --git a/src/ddmd/backend/rtlsym.c b/src/ddmd/backend/rtlsym.c index 5a1537c3deec..517130aa2819 100644 --- a/src/ddmd/backend/rtlsym.c +++ b/src/ddmd/backend/rtlsym.c @@ -59,11 +59,11 @@ void rtlsym_init() //printf("rtlsym_init(%s)\n", regm_str(FREGSAVED)); -#if MARS type *t = type_fake(TYnfunc); t->Tmangle = mTYman_c; t->Tcount++; +#if MARS // Variadic function type *tv = type_fake(TYnfunc); tv->Tmangle = mTYman_c; diff --git a/src/ddmd/backend/rtlsym.d b/src/ddmd/backend/rtlsym.d index 2deb05016538..c20d25b7d095 100644 --- a/src/ddmd/backend/rtlsym.d +++ b/src/ddmd/backend/rtlsym.d @@ -59,6 +59,7 @@ enum RTLSYM_DSWITCHERR, RTLSYM_DHIDDENFUNC, RTLSYM_NEWCLASS, + RTLSYM_NEWTHROW, RTLSYM_NEWARRAYT, RTLSYM_NEWARRAYIT, RTLSYM_NEWITEMT, diff --git a/src/ddmd/backend/rtlsym.h b/src/ddmd/backend/rtlsym.h index 01b34a326ee0..e5841933dc94 100644 --- a/src/ddmd/backend/rtlsym.h +++ b/src/ddmd/backend/rtlsym.h @@ -82,6 +82,7 @@ SYMBOL_MARS(SWITCH_DSTRING,FLfunc,FREGSAVED,"_d_switch_dstring", 0, t) \ SYMBOL_MARS(DSWITCHERR, FLfunc,FREGSAVED,"_d_switch_error", SFLexit, t) \ SYMBOL_MARS(DHIDDENFUNC, FLfunc,FREGSAVED,"_d_hidden_func", 0, t) \ SYMBOL_MARS(NEWCLASS, FLfunc,FREGSAVED,"_d_newclass", 0, t) \ +SYMBOL_MARS(NEWTHROW, FLfunc,FREGSAVED,"_d_newThrowable", 0, t) \ SYMBOL_MARS(NEWARRAYT, FLfunc,FREGSAVED,"_d_newarrayT", 0, t) \ SYMBOL_MARS(NEWARRAYIT, FLfunc,FREGSAVED,"_d_newarrayiT", 0, t) \ SYMBOL_MARS(NEWITEMT, FLfunc,FREGSAVED,"_d_newitemT", 0, t) \ diff --git a/src/ddmd/declaration.d b/src/ddmd/declaration.d index f3556425a565..d4a7ee7fd5e2 100644 --- a/src/ddmd/declaration.d +++ b/src/ddmd/declaration.d @@ -986,9 +986,10 @@ extern (C++) class VarDeclaration : Declaration uint sequenceNumber; // order the variables are declared __gshared uint nextSequenceNumber; // the counter for sequenceNumber FuncDeclarations nestedrefs; // referenced by these lexically nested functions - bool isargptr; // if parameter that _argptr points to structalign_t alignment; + bool isargptr; // if parameter that _argptr points to bool ctorinit; // it has been initialized in a ctor + bool iscatchvar; // this is the exception object variable in catch() clause // Both these mean the var is not rebindable once assigned, // and the destructor gets run when it goes out of scope @@ -1275,6 +1276,9 @@ extern (C++) class VarDeclaration : Declaration return null; } + if (iscatchvar) + return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here + Expression e = null; // Destructors for structs and arrays of structs Type tv = type.baseElemOf(); diff --git a/src/ddmd/declaration.h b/src/ddmd/declaration.h index 83083e5b7ec5..f380cb6c6a13 100644 --- a/src/ddmd/declaration.h +++ b/src/ddmd/declaration.h @@ -240,9 +240,10 @@ class VarDeclaration : public Declaration unsigned offset; unsigned sequenceNumber; // order the variables are declared FuncDeclarations nestedrefs; // referenced by these lexically nested functions - bool isargptr; // if parameter that _argptr points to structalign_t alignment; + bool isargptr; // if parameter that _argptr points to bool ctorinit; // it has been initialized in a ctor + bool iscatchvar; // this is the exception object variable in catch() clause bool onstack; // it is a class that was allocated on the stack bool mynew; // it is a class new'd with custom operator new int canassign; // it can be assigned to diff --git a/src/ddmd/escape.d b/src/ddmd/escape.d index 663d5f219acf..6c1c6cd133a4 100644 --- a/src/ddmd/escape.d +++ b/src/ddmd/escape.d @@ -489,7 +489,8 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag) Dsymbol p = v.toParent2(); - if (v.isScope()) + if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown + // despite being `scope` { if (sc._module && sc._module.isRoot()) { diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index f5d96ef58b43..4c641d9c95f3 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -4037,10 +4037,12 @@ extern (C++) final class NewExp : Expression Expressions* newargs; // Array of Expression's to call new operator Type newtype; Expressions* arguments; // Array of Expression's + Expression argprefix; // expression to be evaluated just before arguments[] CtorDeclaration member; // constructor function NewDeclaration allocator; // allocator function - int onstack; // allocate on stack + bool onstack; // allocate on stack + bool thrownew; // this NewExp is the expression of a ThrowStatement extern (D) this(Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments) { diff --git a/src/ddmd/expression.h b/src/ddmd/expression.h index 40aa0d5a3906..2dc4bd210ac9 100644 --- a/src/ddmd/expression.h +++ b/src/ddmd/expression.h @@ -518,7 +518,8 @@ class NewExp : public Expression CtorDeclaration *member; // constructor function NewDeclaration *allocator; // allocator function - int onstack; // allocate on stack + bool onstack; // allocate on stack + bool thrownew; // this NewExp is the expression of a ThrowStatement static NewExp *create(Loc loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments); Expression *syntaxCopy(); diff --git a/src/ddmd/globals.d b/src/ddmd/globals.d index 1dd3690fcfc5..30ce37b0b22a 100644 --- a/src/ddmd/globals.d +++ b/src/ddmd/globals.d @@ -136,6 +136,7 @@ struct Param bool fix16997; // fix integral promotions for unary + - ~ operators // https://issues.dlang.org/show_bug.cgi?id=16997 bool vsafe; // use enhanced @safe checking + bool ehnogc; // use @nogc exception handling /** The --transition=safe switch should only be used to show code with * silent semantics changes related to @safe improvements. It should not be * used to hide a feature that will have to go through deprecate-then-error diff --git a/src/ddmd/globals.h b/src/ddmd/globals.h index 98da2e88995b..7cbe67f87b33 100644 --- a/src/ddmd/globals.h +++ b/src/ddmd/globals.h @@ -127,6 +127,7 @@ struct Param bool fix16997; // fix integral promotions for unary + - ~ operators // https://issues.dlang.org/show_bug.cgi?id=16997 bool vsafe; // use enhanced @safe checking + bool ehnogc; // use @nogc exception handling bool showGaggedErrors; // print gagged errors anyway bool manual; // open browser on compiler manual bool usage; // print usage and exit diff --git a/src/ddmd/id.d b/src/ddmd/id.d index a2c7cedd9480..316ecaa15a76 100644 --- a/src/ddmd/id.d +++ b/src/ddmd/id.d @@ -291,6 +291,7 @@ immutable Msgtable[] msgtable = { "_ArrayEq" }, { "_ArrayPostblit" }, { "_ArrayDtor" }, + { "_d_delThrowable" }, // For pragma's { "Pinline", "inline" }, diff --git a/src/ddmd/mars.d b/src/ddmd/mars.d index 3e943bfd0586..608e0e554907 100644 --- a/src/ddmd/mars.d +++ b/src/ddmd/mars.d @@ -133,6 +133,7 @@ Where: "%s" /* placeholder for fpic */ ~ " -dip25 implement http://wiki.dlang.org/DIP25 (experimental) -dip1000 implement http://wiki.dlang.org/DIP1000 (experimental) + -dip1008 implement DIP1008 (experimental) -g add symbolic debug info -gf emit debug info for all referenced types -gs always emit stack frame @@ -1954,6 +1955,10 @@ private bool parseCommandLine(const ref Strings arguments, const size_t argc, re params.useDIP25 = true; params.vsafe = true; } + else if (strcmp(p + 1, "dip1008") == 0) + { + params.ehnogc = true; + } else if (strcmp(p + 1, "lib") == 0) // https://dlang.org/dmd-windows.html#switch-lib params.lib = true; else if (strcmp(p + 1, "nofloat") == 0) diff --git a/src/ddmd/nogc.d b/src/ddmd/nogc.d index 6e48fbe49d38..4e96f308ba5f 100644 --- a/src/ddmd/nogc.d +++ b/src/ddmd/nogc.d @@ -105,6 +105,8 @@ public: return; if (e.allocator) return; + if (global.params.ehnogc && e.thrownew) + return; // separate allocator is called for this, not the GC if (f.setGC()) { e.error("cannot use 'new' in @nogc %s '%s'", diff --git a/src/ddmd/statementsem.d b/src/ddmd/statementsem.d index 09e2eeca174b..b33d6db2e517 100644 --- a/src/ddmd/statementsem.d +++ b/src/ddmd/statementsem.d @@ -3808,6 +3808,12 @@ else FuncDeclaration fd = sc.parent.isFuncDeclaration(); fd.hasReturnExp |= 2; + if (ts.exp.op == TOKnew) + { + NewExp ne = cast(NewExp)ts.exp; + ne.thrownew = true; + } + ts.exp = ts.exp.expressionSemantic(sc); ts.exp = resolveProperties(sc, ts.exp); ts.exp = checkGC(sc, ts.exp); @@ -4010,6 +4016,7 @@ void catchSemantic(Catch c, Scope* sc) c.errors = true; else { + StorageClass stc; auto cd = c.type.toBasetype().isClassHandle(); if (!cd) { @@ -4041,12 +4048,35 @@ void catchSemantic(Catch c, Scope* sc) error(c.loc, "can only catch class objects derived from Exception in @safe code, not `%s`", c.type.toChars()); c.errors = true; } + else if (global.params.ehnogc) + { + stc |= STCscope; + } if (c.ident) { - c.var = new VarDeclaration(c.loc, c.type, c.ident, null); + c.var = new VarDeclaration(c.loc, c.type, c.ident, null, stc); + c.var.iscatchvar = true; c.var.dsymbolSemantic(sc); sc.insert(c.var); + + if (global.params.ehnogc && stc & STCscope) + { + /* Add a destructor for c.var + * try { handler } finally { if (!__ctfe) _d_delThrowable(var); } + */ + assert(!c.var.edtor); // ensure we didn't create one in callScopeDtor() + + Loc loc = c.loc; + Expression e = new VarExp(loc, c.var); + e = new CallExp(loc, new IdentifierExp(loc, Id._d_delThrowable), e); + + Expression ec = new IdentifierExp(loc, Id.ctfe); + ec = new NotExp(loc, ec); + Statement s = new IfStatement(loc, null, ec, new ExpStatement(loc, e), null, loc); + c.handler = new TryFinallyStatement(loc, c.handler, s); + } + } c.handler = c.handler.statementSemantic(sc); if (c.handler && c.handler.isErrorStatement()) diff --git a/test/compilable/testdip1008.d b/test/compilable/testdip1008.d new file mode 100644 index 000000000000..12a32ab27cb7 --- /dev/null +++ b/test/compilable/testdip1008.d @@ -0,0 +1,21 @@ +// PERMUTE_ARGS: +// won't work until druntime is updated, so disable XXXEQUIRED_ARGS: -dip1008 + +int bar() +{ + try + { + throw new Exception("message"); + } + catch (Exception e) + { + return 7; + } +} + + +void foo() +{ + enum r = bar(); + static assert(r == 7); +}