35 changes: 11 additions & 24 deletions src/glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Symbol *toModuleAssert(Module *m);
Symbol *toModuleUnittest(Module *m);
Symbol *toModuleArray(Module *m);
Symbol *toSymbolX(Dsymbol *ds, const char *prefix, int sclass, type *t, const char *suffix);
void genhelpers(Module *m, bool iscomdat);
static void genhelpers(Module *m);

elem *eictor;
symbol *ictorlocalgot;
Expand Down Expand Up @@ -475,34 +475,23 @@ void genObjFile(Module *m, bool multiobj)
return;
}

if (global.params.multiobj)
{
/* This is necessary because the main .obj for this module is written
* first, but determining whether marray or massert or munittest are needed is done
* possibly later in the doppelganger modules.
* Another way to fix it is do the main one last.
*/
toModuleAssert(m);
toModuleUnittest(m);
toModuleArray(m);
}

/* Always generate module info, because of templates and -cov.
* But module info needs the runtime library, so disable it for betterC.
*/
if (!global.params.betterC /*|| needModuleInfo()*/)
genModuleInfo(m);

genhelpers(m, false);
/* Always generate helper functions b/c of later templates instantiations
* with different -release/-debug/-boundscheck/-unittest flags.
*/
if (!global.params.betterC)
genhelpers(m);

objmod->termfile();
}

void genhelpers(Module *m, bool iscomdat)
static void genhelpers(Module *m)
{
if (global.params.betterC)
return;

// If module assert
for (int i = 0; i < 3; i++)
{
Expand All @@ -511,9 +500,9 @@ void genhelpers(Module *m, bool iscomdat)
unsigned bc;
switch (i)
{
case 0: ma = m->marray; rt = RTLSYM_DARRAY; bc = BCexit; break;
case 1: ma = m->massert; rt = RTLSYM_DASSERT; bc = BCexit; break;
case 2: ma = m->munittest; rt = RTLSYM_DUNITTEST; bc = BCret; break;
case 0: ma = toModuleArray(m); rt = RTLSYM_DARRAY; bc = BCexit; break;
case 1: ma = toModuleAssert(m); rt = RTLSYM_DASSERT; bc = BCexit; break;
case 2: ma = toModuleUnittest(m); rt = RTLSYM_DUNITTEST; bc = BCret; break;
default: assert(0);
}

Expand Down Expand Up @@ -553,7 +542,7 @@ void genhelpers(Module *m, bool iscomdat)
b->Belem = e;
ma->Sfunc->Fstartline.Sfilename = m->arg;
ma->Sfunc->Fstartblock = b;
ma->Sclass = iscomdat ? SCcomdat : SCglobal;
ma->Sclass = SCglobal;
ma->Sfl = 0;
ma->Sflags |= rtlsym[rt]->Sflags & SFLexit;
writefunc(ma);
Expand Down Expand Up @@ -1551,5 +1540,3 @@ elem *toEfilename(Module *m)
// Turn static array into dynamic array
return el_pair(TYdarray, el_long(TYsize_t, len), el_ptr(m->sfilename));
}


5 changes: 0 additions & 5 deletions src/gluestub.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,6 @@ void genObjFile(Module *m, bool multiobj)
{
}

void genhelpers(Module *m, bool iscomdat)
{
assert(0);
}

// msc

void backend_init()
Expand Down
2 changes: 2 additions & 0 deletions src/idgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ Msgtable[] msgtable =
{ "criticalenter", "_d_criticalenter" },
{ "criticalexit", "_d_criticalexit" },
{ "_ArrayEq" },
{ "_ArrayPostblit" },
{ "_ArrayDtor" },

// For pragma's
{ "Pinline", "inline" },
Expand Down
67 changes: 30 additions & 37 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -4759,48 +4759,41 @@ class Interpreter : public Visitor

if (pthis->op == TOKdottype)
pthis = ((DotTypeExp *)dve->e1)->e1;

// Special handling for: typeid(T[n]).destroy(ea)
if (pthis->op == TOKtypeid)
{
TypeidExp *tie = (TypeidExp *)pthis;
Type *t = isType(tie->obj);
if (t &&
t->toBasetype()->ty == Tsarray &&
fd->ident == Id::destroy &&
e->arguments->dim == 1)
{
Type *tb = t->baseElemOf();
if (tb->ty == Tstruct && ((TypeStruct *)tb)->sym->dtor)
{
Expression *ea = (*e->arguments)[0];
// ea would be:
// &var <-- SymOffExp
// cast(void*)&var
// cast(void*)&this.field
// etc.
if (ea->op == TOKcast)
ea = ((CastExp *)ea)->e1;
if (ea->op == TOKsymoff)
result = getVarExp(e->loc, istate, ((SymOffExp *)ea)->var, ctfeNeedRvalue);
else if (ea->op == TOKaddress)
result = interpret(((AddrExp *)ea)->e1, istate);
else
assert(0);
if (CTFEExp::isCantExp(result))
return;
result = evaluateDtor(istate, result);
if (!result)
result = CTFEExp::voidexp;
return;
}
}
}
}
else if (ecall->op == TOKvar)
{
fd = ((VarExp *)ecall)->var->isFuncDeclaration();
assert(fd);

if (fd->ident == Id::_ArrayPostblit ||
fd->ident == Id::_ArrayDtor)
{
assert(e->arguments->dim == 1);
Expression *ea = (*e->arguments)[0];
//printf("1 ea = %s %s\n", ea->type->toChars(), ea->toChars());
if (ea->op == TOKslice)
ea = ((SliceExp *)ea)->e1;
if (ea->op == TOKcast)
ea = ((CastExp *)ea)->e1;

//printf("2 ea = %s, %s %s\n", ea->type->toChars(), Token::toChars(ea->op), ea->toChars());
if (ea->op == TOKvar || ea->op == TOKsymoff)
result = getVarExp(e->loc, istate, ((SymbolExp *)ea)->var, ctfeNeedRvalue);
else if (ea->op == TOKaddress)
result = interpret(((AddrExp *)ea)->e1, istate);
else
assert(0);
if (CTFEExp::isCantExp(result))
return;

if (fd->ident == Id::_ArrayPostblit)
result = evaluatePostblit(istate, result);
else
result = evaluateDtor(istate, result);
if (!result)
result = CTFEExp::voidexp;
return;
}
}
else if (ecall->op == TOKsymoff)
{
Expand Down
13 changes: 0 additions & 13 deletions src/mars.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ void updateRealEnvironment(StringTable *environment);
void parseConfFile(StringTable *environment, const char *path, size_t len, unsigned char *buffer, Strings *sections);

void genObjFile(Module *m, bool multiobj);
void genhelpers(Module *m, bool iscomdat);

/** Normalize path by turning forward slashes into backslashes */
const char * toWinPath(const char *src)
Expand Down Expand Up @@ -1674,12 +1673,6 @@ Language changes listed by -transition=id:\n\
if (entrypoint && m == rootHasMain)
genObjFile(entrypoint, false);
}
for (size_t i = 0; i < Module::amodules.dim; i++)
{
Module *m = Module::amodules[i];
if (!m->isRoot() && (m->marray || m->massert || m->munittest))
genhelpers(m, true);
}
if (!global.errors && modules.dim)
{
obj_end(library, modules[0]->objfile);
Expand All @@ -1697,12 +1690,6 @@ Language changes listed by -transition=id:\n\
genObjFile(m, global.params.multiobj);
if (entrypoint && m == rootHasMain)
genObjFile(entrypoint, global.params.multiobj);
for (size_t j = 0; j < Module::amodules.dim; j++)
{
Module *mx = Module::amodules[j];
if (mx != m && mx->importedFrom == m && (mx->marray || mx->massert || mx->munittest))
genhelpers(mx, true);
}
obj_end(library, m->objfile);
obj_write_deferred(library);

Expand Down
63 changes: 63 additions & 0 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,69 @@ Module *Module::parse()
error("has non-identifier characters in filename, use module declaration instead");
}

// Add internal used functions in 'object' module members.
if (!parent && ident == Id::object)
{
static const utf8_t code_ArrayEq[] =
"bool _ArrayEq(T1, T2)(T1[] a, T2[] b) {\n"
" if (a.length != b.length) return false;\n"
" foreach (size_t i; 0 .. a.length) { if (a[i] != b[i]) return false; }\n"
" return true; }\n";

static const utf8_t code_ArrayPostblit[] =
"void _ArrayPostblit(T)(T[] a) { foreach (ref T e; a) e.__xpostblit(); }\n";

static const utf8_t code_ArrayDtor[] =
"void _ArrayDtor(T)(T[] a) { foreach_reverse (ref T e; a) e.__xdtor(); }\n";

static const utf8_t code_xopEquals[] =
"bool _xopEquals(in void*, in void*) { throw new Error(\"TypeInfo.equals is not implemented\"); }\n";

static const utf8_t code_xopCmp[] =
"bool _xopCmp(in void*, in void*) { throw new Error(\"TypeInfo.compare is not implemented\"); }\n";

Identifier *arreq = Id::_ArrayEq;
Identifier *xopeq = Identifier::idPool("_xopEquals");
Identifier *xopcmp = Identifier::idPool("_xopCmp");
for (size_t i = 0; i < members->dim; i++)
{
Dsymbol *sx = (*members)[i];
if (!sx) continue;
if (arreq && sx->ident == arreq) arreq = NULL;
if (xopeq && sx->ident == xopeq) xopeq = NULL;
if (xopcmp && sx->ident == xopcmp) xopcmp = NULL;
}

if (arreq)
{
Parser p(loc, this, code_ArrayEq, strlen((const char *)code_ArrayEq), 0);
p.nextToken();
members->append(p.parseDeclDefs(0));
}
{
Parser p(loc, this, code_ArrayPostblit, strlen((const char *)code_ArrayPostblit), 0);
p.nextToken();
members->append(p.parseDeclDefs(0));
}
{
Parser p(loc, this, code_ArrayDtor, strlen((const char *)code_ArrayDtor), 0);
p.nextToken();
members->append(p.parseDeclDefs(0));
}
if (xopeq)
{
Parser p(loc, this, code_xopEquals, strlen((const char *)code_xopEquals), 0);
p.nextToken();
members->append(p.parseDeclDefs(0));
}
if (xopcmp)
{
Parser p(loc, this, code_xopCmp, strlen((const char *)code_xopCmp), 0);
p.nextToken();
members->append(p.parseDeclDefs(0));
}
}

// Insert module into the symbol table
Dsymbol *s = this;
if (isPackageFile)
Expand Down
43 changes: 35 additions & 8 deletions src/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,13 +729,10 @@ void StructDeclaration::semantic(Scope *sc)
error("structs, unions cannot be abstract");
userAttribDecl = sc->userAttribDecl;
}
else if (symtab)
else if (symtab && !scx)
{
if (sizeok == SIZEOKdone || !scx)
{
semanticRun = PASSsemanticdone;
return;
}
semanticRun = PASSsemanticdone;
return;
}
semanticRun = PASSsemantic;

Expand All @@ -757,7 +754,6 @@ void StructDeclaration::semantic(Scope *sc)
}
}

sizeok = SIZEOKnone;
Scope *sc2 = sc->push(this);
sc2->stc &= STCsafe | STCtrusted | STCsystem;
sc2->parent = this;
Expand All @@ -768,6 +764,11 @@ void StructDeclaration::semantic(Scope *sc)
sc2->structalign = STRUCTALIGN_DEFAULT;
sc2->userAttribDecl = NULL;

if (sizeok == SIZEOKdone)
goto LafterSizeok;

sizeok = SIZEOKnone;

/* Set scope so if there are forward references, we still might be able to
* resolve individual members like enums.
*/
Expand Down Expand Up @@ -816,10 +817,33 @@ void StructDeclaration::semantic(Scope *sc)
}

Module::dprogress++;
semanticRun = PASSsemanticdone;

//printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars());

LafterSizeok:
// The additions of special member functions should have its own
// sub-semantic analysis pass, and have to be deferred sometimes.
// See the case in compilable/test14838.d
for (size_t i = 0; i < fields.dim; i++)
{
VarDeclaration *v = fields[i];
Type *tb = v->type->baseElemOf();
if (tb->ty != Tstruct)
continue;
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
if (sd->semanticRun >= PASSsemanticdone)
continue;

sc2->pop();

scope = scx ? scx : sc->copy();
scope->setNoFree();
scope->module->addDeferredSemantic(this);

//printf("\tdeferring %s\n", toChars());
return;
}

/* Look for special member functions.
*/
aggNew = (NewDeclaration *)search(Loc(), Id::classNew);
Expand Down Expand Up @@ -871,6 +895,9 @@ void StructDeclaration::semantic(Scope *sc)
}
}

Module::dprogress++;
semanticRun = PASSsemanticdone;

TypeTuple *tup = toArgTypes(type);
size_t dim = tup->arguments->dim;
if (dim >= 1)
Expand Down
48 changes: 24 additions & 24 deletions src/template.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam);
MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam);
void mangleToBuffer(Expression *e, OutBuffer *buf);

// Glue layer
Symbol *toModuleAssert(Module *m);
Symbol *toModuleUnittest(Module *m);
Symbol *toModuleArray(Module *m);

/********************************************
* These functions substitute for dynamic_cast. dynamic_cast does not work
* on earlier versions of gcc.
Expand Down Expand Up @@ -1927,10 +1922,10 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(
{
// if tuple parameter and
// tuple parameter was not in function parameter list and
// we're one argument short (i.e. no tuple argument)
if (tp &&
// we're one or more arguments short (i.e. no tuple argument)
if (tparam == tp &&
fptupindex == IDX_NOTFOUND &&
ntargs == dedargs->dim - 1)
ntargs <= dedargs->dim - 1)
{
// make tuple argument an empty tuple
oded = (RootObject *)new Tuple();
Expand Down Expand Up @@ -1965,7 +1960,8 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(

/* Bugzilla 7469: Normalize ti->tiargs for the correct mangling of template instance.
*/
if (Tuple *va = isTuple(oded))
Tuple *va = isTuple(oded);
if (va && va->objects.dim)
{
dedargs->setDim(parameters->dim - 1 + va->objects.dim);
for (size_t j = 0; j < va->objects.dim; j++)
Expand Down Expand Up @@ -5911,16 +5907,6 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
if (errors)
goto Lerror;

if (Module *m = tempdecl->scope->module) // should use getModule() instead?
{
// Generate these functions as they may be used
// when template is instantiated in other modules
// even if assertions or bounds checking are disabled in this module
toModuleArray(m);
toModuleAssert(m);
toModuleUnittest(m);
}

/* See if there is an existing TemplateInstantiation that already
* implements the typeargs. If so, just refer to that one instead.
*/
Expand Down Expand Up @@ -7864,14 +7850,28 @@ bool TemplateInstance::needsCodegen()
//printf("%s minst = %s, enclosing (%s)->isNonRoot = %d\n",
// toPrettyChars(), minst ? minst->toChars() : NULL,
// enclosing ? enclosing->toPrettyChars() : NULL, enclosing && enclosing->inNonRoot());
if (enclosing && !tinst)
if (enclosing)
{
// Bugzilla 13415: If and only if the enclosing scope needs codegen,
// the nested templates would need code generation.
// Bugzilla 14588: If the captured context is not a function
// (e.g. class), the instance layout determination is guaranteed,
// because the semantic/semantic2 pass will be executed
// even for non-root instances.
if (!enclosing->isFuncDeclaration())
return true;

// Bugzilla 14834: If the captured context is a function,
// this excessive instantiation may cause ODR violation, because
// -allInst and others doesn't guarantee the semantic3 execution
// for that function.

// If the enclosing is also an instantiated function,
// we have to rely on the ancestor's needsCodegen() result.
if (TemplateInstance *ti = enclosing->isInstantiated())
return ti->needsCodegen();
else
return !enclosing->inNonRoot();

// Bugzilla 13415: If and only if the enclosing scope needs codegen,
// this nested templates would also need code generation.
return !enclosing->inNonRoot();
}
return true;
}
Expand Down
9 changes: 4 additions & 5 deletions src/traits.c
Original file line number Diff line number Diff line change
Expand Up @@ -945,19 +945,18 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
goto Ldimerror;
RootObject *o = (*e->args)[0];
Dsymbol *s = getDsymbol(o);
ScopeDsymbol *sds;
if (!s)
{
e->error("argument has no members");
goto Lfalse;
}
Import *import;
if ((import = s->isImport()) != NULL)
if (Import *imp = s->isImport())
{
// Bugzilla 9692
sds = import->mod;
s = imp->mod;
}
else if ((sds = s->isScopeDsymbol()) == NULL)
ScopeDsymbol *sds = s->isScopeDsymbol();
if (!sds || sds->isTemplateDeclaration())
{
e->error("%s %s has no members", s->kind(), s->toChars());
goto Lfalse;
Expand Down
91 changes: 91 additions & 0 deletions test/compilable/test14838.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// PERMUTE_ARGS:

struct A(T) { ~this() {} }
class C { A!int[1] array; }

void test14838() pure nothrow @nogc @safe
{
C c;
c.__xdtor(); // C.~this() will also be inferred to
// pure nothrow @nogc @safe

A!int[1] array;
// scope destructor call does not cause attribute violation.
}

// ----

/*
* This is a reduced test case comes from std.container.Array template,
* to fix the semantic analysis order issue for correct destructor attribute inference.
*
* Before the bugfix:
* 1. StructDeclaration('Array!int')->semantic() instantiates
* RangeT!(Array!int) at the `alias Range = ...;`, but
* StructDeclaration('RangeT!(Array!int)')->semantic() exits
* with sizeok == SIZEOKfwd, because the size of _outer_ field is not yet determined.
* 2. StructDeclaration('Array!int')->semantic() succeeds to determine the size
* (sizeok = SIZEOKdone).
* 3. StructDeclaration('Array!int')->buildOpAssign() will generate opAssign because
* Array!int._data field has identity opAssign member function.
* 4. The semantic3 will get called for the generated opAssign, then
* 6-1. Array!int.~this() semantic3, and
* 6-2. RefCounted!(Array!int.Payload).~this() semantic3
* will also get called to infer their attributes.
* 5. In RefCounted!(Array!int.Payload).~this(), destroy(t) will be instantiated.
* At that, TemplateInstance::expandMembers() will invoke runDeferredSemantic()
* and it will re-run StructDeclaration('RangeT!(Array!int)')->semantic().
* 6. StructDeclaration('RangeT!(Array!int)')->semantic() determines the size
* (sizeok = SIZEOKdone). Then, it will generate identity opAssign and run its semantic3.
* It will need to infer RangeT!(Array!int).~this() attribute, then it requires the
* correct attribute of Array!int.~this().
*
* However, the Array!int.~this() attribute is not yet determined! [bug]
* -> it's wongly handled as impure/system/throwable/gc-able.
*
* -> then, the attribute inference results for
* RangeT!(Array!int).~this() and Array!int.~this() will be incorrect.
*
* After the bugfix:
* In 6, StructDeclaration('RangeT!(Array!int)')->semantic() will check that:
* all base struct types of the instance fields have completed addition of
* special functions (dtor, opAssign, etc).
* If not, it will defer the completion of its semantic pass.
*/

void destroy14838(S)(ref S s) if (is(S == struct))
{
s.__xdtor();
}

struct RefCounted14838(T)
{
~this()
{
T t;
.destroy14838(t);
}

void opAssign(typeof(this) rhs) {}
}

struct RangeT14838(A)
{
A[1] _outer_;
}

struct Array14838(T)
{
struct Payload
{
~this() {}
}
RefCounted14838!Payload _data;

alias Range = RangeT14838!Array14838;
}

class Test14838
{
Array14838!int[1] field;
}
21 changes: 21 additions & 0 deletions test/fail_compilation/ice14844.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice14844.d(20): Error: template opDispatch(string name) has no members
---
*/

struct Typedef
{
template opDispatch(string name)
{
static if (true)
{
}
}
}

void runUnitTestsImpl()
{
auto x = [__traits(allMembers, Typedef.opDispatch)];
}
15 changes: 15 additions & 0 deletions test/runnable/arrayop.d
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,20 @@ void test14649()
assert(r == [('a'+'d'), ('b'+'e'), ('c'+'f')]);
}

/************************************************************************/
// 14851

void test14851()
{
int[8] a, b, c;

c = a[] | b[]; // OK <- NG from 2.068.0-b2
c = a[] ^ b[]; // OK <- NG from 2.068.0-b2

c[] = a[] | b[]; // OK
c[] = a[] ^ b[]; // OK
}

/************************************************************************/

int main()
Expand All @@ -905,6 +919,7 @@ int main()
test12780();
test13497();
test14649();
test14851();

printf("Success\n");
return 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module imports.link846a;
module link846a;

template ElemTypeOf(T)
{
Expand Down
57 changes: 57 additions & 0 deletions test/runnable/extra-files/link14834a.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module link14834a;

struct DirIterator
{
int i = 1;

@property bool empty() { return i == 0; }
@property int front() { return 10; }
void popFront() { --i; }
}

auto dirEntries(string path)
{
bool f(int x)
{
assert(path == "."); // should pass
return true;
}
return filter!f(DirIterator());
}

template filter(alias pred)
{
auto filter(R)(R range)
{
return FilterResult!(pred, R)(range);
}
}

struct FilterResult(alias pred, R)
{
R input;

this(R r)
{
input = r;
while (!input.empty && !pred(input.front))
{
input.popFront();
}
}

@property bool empty() { return input.empty; }

@property auto ref front()
{
return input.front;
}

void popFront()
{
do
{
input.popFront();
} while (!input.empty && !pred(input.front));
}
}
9 changes: 9 additions & 0 deletions test/runnable/extra-files/link14834b.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import link14834a;

void main()
{
foreach (n; dirEntries("."))
{
assert(n == 10);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import imports.link846a;
import lib846;

void main()
{
Expand Down
2 changes: 1 addition & 1 deletion test/runnable/ice10857.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// EXTRA_SOURCES: imports/ice10857b.d
// EXTRA_SOURCES: imports/ice10857a.d imports/ice10857b.d

import imports.ice10857a;
25 changes: 25 additions & 0 deletions test/runnable/link14834.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash

src=runnable${SEP}extra-files
dir=${RESULTS_DIR}${SEP}runnable
output_file=${dir}${SEP}link14834.sh.out

rm -f ${output_file}

if [ $OS == "win32" -o $OS == "win64" ]; then
LIBEXT=.lib
else
LIBEXT=.a
fi

libname=${dir}${SEP}link14834${LIBEXT}
exename=${dir}${SEP}link14834${EXE}

$DMD -m${MODEL} -I${src} -lib -of${libname} ${src}${SEP}link14834a.d > ${output_file} || exit 1
$DMD -m${MODEL} -I${src} -inline -debug -of${exename} ${src}${SEP}link14834b.d ${libname} > ${output_file} || exit 1

${dir}/link14834 || exit 1

rm ${libname} ${exename} ${dir}${SEP}link14834${OBJ}

echo Success > ${output_file}
25 changes: 25 additions & 0 deletions test/runnable/link846.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash

set -e

src=runnable${SEP}extra-files
dir=${RESULTS_DIR}${SEP}runnable
output_file=${dir}/link846.sh.out

if [ $OS == "win32" -o $OS == "win64" ]; then
LIBEXT=.lib
else
LIBEXT=.a
fi
libname=${dir}${SEP}link846${LIBEXT}

# build library with -release
$DMD -m${MODEL} -I${src} -of${libname} -release -boundscheck=off -lib ${src}${SEP}lib846.d

# use lib with -debug
$DMD -m${MODEL} -I${src} -of${dir}${SEP}link846${EXE} -debug ${src}${SEP}main846.d ${libname}

rm ${libname}
rm ${dir}/{link846${OBJ},link846${EXE}}

echo Success > ${output_file}
21 changes: 21 additions & 0 deletions test/runnable/mars1.d
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,26 @@ char[] dup(char[] a)
return a;
}

////////////////////////////////////////////////////////////////////////

int stripLeft(int str, int dc)
{
while (true)
{
int a = str;
int s = a;
str += 1;
if (dc) return s;
}
}

void test14829()
{
if (stripLeft(3, 1) != 3) // fails with -O
assert(0);
}


////////////////////////////////////////////////////////////////////////

int main()
Expand Down Expand Up @@ -1368,6 +1388,7 @@ int main()
test12057();
test13784();
test14220();
test14829();
printf("Success\n");
return 0;
}
54 changes: 54 additions & 0 deletions test/runnable/sdtor.d
Original file line number Diff line number Diff line change
Expand Up @@ -3942,6 +3942,59 @@ void test14264()
assert(dtor == 4);
}

/**********************************/
// 14838

int test14838() pure nothrow @safe
{
int dtor;

struct S14838(T)
{
~this() { ++dtor; }
}
struct X14838
{
S14838!int ms;
const S14838!int cs;

S14838!int[2] ma;
const S14838!int[2] ca;

S14838!int[2][2] ma2x2;
const S14838!int[2][2] ca2x2;

// number of S14838 = 1*2 + 2*2 + 4*2 = 14
}

void test(Dg)(scope Dg code)
{
dtor = 0;
code();
}

test(delegate{ S14838!int a; }); assert(dtor == 1);
test(delegate{ const S14838!int a; }); assert(dtor == 1);

test(delegate{ S14838!int[2] a; }); assert(dtor == 2);
test(delegate{ const S14838!int[2] a; }); assert(dtor == 2);

test(delegate{ S14838!int[2][2] a; }); assert(dtor == 4);
test(delegate{ const S14838!int[2][2] a; }); assert(dtor == 4);

test(delegate{ X14838 a; }); assert(dtor == 1 * 14);
test(delegate{ const X14838 a; }); assert(dtor == 1 * 14);

test(delegate{ X14838[2] a; }); assert(dtor == 2 * 14);
test(delegate{ const X14838[2] a; }); assert(dtor == 2 * 14);

test(delegate{ X14838[2][2] a; }); assert(dtor == 4 * 14);
test(delegate{ const X14838[2][2] a; }); assert(dtor == 4 * 14);

return 1;
}
static assert(test14838());

/**********************************/

int main()
Expand Down Expand Up @@ -4058,6 +4111,7 @@ int main()
test13669();
test13095();
test14264();
test14838();

printf("Success\n");
return 0;
Expand Down
24 changes: 24 additions & 0 deletions test/runnable/template9.d
Original file line number Diff line number Diff line change
Expand Up @@ -4485,6 +4485,29 @@ void test14174()
accepter14174b(); // error
}

/******************************************/
// 14836

template a14836x(alias B, C...)
{
int a14836x(D...)() if (D.length == 0) { return 1; }
int a14836x(D...)(D d) if (D.length > 0) { return 2; }
}
template a14836y(alias B, C...)
{
int a14836y(T, D...)(T t) if (D.length == 0) { return 1; }
int a14836y(T, D...)(T t, D d) if (D.length > 0) { return 2; }
}

void test14836()
{
int v;
assert(a14836x!(v)() == 1);
assert(a14836x!(v)(1) == 2);
assert(a14836y!(v)(1) == 1);
assert(a14836y!(v)(1, 2) == 2);
}

/******************************************/
// 14357

Expand Down Expand Up @@ -4707,6 +4730,7 @@ int main()
test13379();
test13484();
test13694();
test14836();
test14735();

printf("Success\n");
Expand Down
33 changes: 33 additions & 0 deletions test/runnable/xtest46.d
Original file line number Diff line number Diff line change
Expand Up @@ -7483,6 +7483,39 @@ class Outer14552
class Inner {}
}

/***************************************************/
// 14853

struct Queue14853(T)
{
struct Node
{
T mfPayload = T.init;
union
{
typeof(this)* mfPrev;
shared(typeof(this)*) mfShPrev;
}
union
{
typeof(this)* mfNext;
shared(typeof(this)*) mfShNext;
}
}

Node root;

void pfPut(T v, Node* r = null)
{
shared n = new Node(v); // problem!
}
}

void test14853()
{
auto b1 = new Queue14853!uint;
}

/***************************************************/

int main()
Expand Down