259 changes: 210 additions & 49 deletions src/dclass.d

Large diffs are not rendered by default.

28 changes: 19 additions & 9 deletions src/declaration.d
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,7 @@ public:
error("reference to scope class must be scope");
}
}

if (!_init && !fd)
{
// If not mutable, initializable by constructor only
Expand All @@ -1505,29 +1506,39 @@ public:
storage_class |= STCinit; // remember we had an explicit initializer
else if (storage_class & STCmanifest)
error("manifest constants must have initializers");

bool isBlit = false;
if (!_init && !sc.inunion && !(storage_class & (STCstatic | STCgshared | STCextern)) && fd && (!(storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult)) || (storage_class & STCout)) && type.size() != 0)
if (!_init &&
!sc.inunion &&
!(storage_class & (STCstatic | STCgshared | STCextern)) &&
fd &&
(!(storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult)) ||
(storage_class & STCout)) &&
type.size() != 0)
{
// Provide a default initializer

//printf("Providing default initializer for '%s'\n", toChars());
if (type.needsNested())
{
Type tv = type;
while (tv.toBasetype().ty == Tsarray)
tv = tv.toBasetype().nextOf();
assert(tv.toBasetype().ty == Tstruct);

/* Nested struct requires valid enclosing frame pointer.
* In StructLiteralExp::toElem(), it's calculated.
*/
checkFrameAccess(loc, sc, (cast(TypeStruct)tv.toBasetype()).sym);

Expression e = tv.defaultInitLiteral(loc);
Expression e1 = new VarExp(loc, this);
e = new BlitExp(loc, e1, e);
e = new BlitExp(loc, new VarExp(loc, this), e);
e = e.semantic(sc);
_init = new ExpInitializer(loc, e);
goto Ldtor;
}
else if (type.ty == Tstruct && (cast(TypeStruct)type).sym.zeroInit == 1)
if (type.ty == Tstruct &&
(cast(TypeStruct)type).sym.zeroInit == 1)
{
/* If a struct is all zeros, as a special case
* set it's initializer to the integer 0.
Expand All @@ -1536,21 +1547,20 @@ public:
* Must do same check in interpreter.
*/
Expression e = new IntegerExp(loc, 0, Type.tint32);
Expression e1;
e1 = new VarExp(loc, this);
e = new BlitExp(loc, e1, e);
e.type = e1.type; // don't type check this, it would fail
e = new BlitExp(loc, new VarExp(loc, this), e);
e.type = type; // don't type check this, it would fail
_init = new ExpInitializer(loc, e);
goto Ldtor;
}
else if (type.baseElemOf().ty == Tvoid)
if (type.baseElemOf().ty == Tvoid)
{
error("%s does not have a default initializer", type.toChars());
}
else
{
_init = getExpInitializer();
}

// Default initializer is always a blit
isBlit = true;
}
Expand Down
45 changes: 43 additions & 2 deletions src/dinterpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -3295,13 +3295,16 @@ public:
printf("%s BinExp::interpretAssignCommon() %s\n", e.loc.toChars(), e.toChars());
}
result = CTFEExp.cantexp;

Expression e1 = e.e1;
if (!istate)
{
e.error("value of %s is not known at compile time", e1.toChars());
return;
}

++CtfeStatus.numAssignments;

/* Before we begin, we need to know if this is a reference assignment
* (dynamic array, AA, or class) or a value assignment.
* Determining this for slice assignments are tricky: we need to know
Expand All @@ -3326,18 +3329,23 @@ public:
}
}
}

// ---------------------------------------
// Deal with reference assignment
// ---------------------------------------
// If it is a construction of a ref variable, it is a ref assignment
if (e.op == TOKconstruct && e1.op == TOKvar && ((cast(VarExp)e1).var.storage_class & STCref) != 0)
if ((e.op == TOKconstruct || e.op == TOKblit) &&
((cast(AssignExp)e).memset & MemorySet.referenceInit))
{
assert(!fp);

Expression newval = interpret(e.e2, istate, ctfeNeedLvalue);
if (exceptionOrCant(newval))
return;

VarDeclaration v = (cast(VarExp)e1).var.isVarDeclaration();
setValue(v, newval);

// Get the value to return. Note that 'newval' is an Lvalue,
// so if we need an Rvalue, we have to interpret again.
if (goal == ctfeNeedRvalue)
Expand All @@ -3346,6 +3354,7 @@ public:
result = e1; // VarExp is a CTFE reference
return;
}

if (fp)
{
while (e1.op == TOKcast)
Expand All @@ -3354,6 +3363,7 @@ public:
e1 = ce.e1;
}
}

// ---------------------------------------
// Interpret left hand side
// ---------------------------------------
Expand All @@ -3380,30 +3390,35 @@ public:
ie = cast(IndexExp)ie.e1;
++depth;
}

// Get the AA value to be modified.
Expression aggregate = interpret(ie.e1, istate);
if (exceptionOrCant(aggregate))
return;
if (aggregate.op == TOKassocarrayliteral)
{
existingAA = cast(AssocArrayLiteralExp)aggregate;

// Normal case, ultimate parent AA already exists
// We need to walk from the deepest index up, checking that an AA literal
// already exists on each level.
lastIndex = interpret((cast(IndexExp)e1).e2, istate);
lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
if (exceptionOrCant(lastIndex))
return;

while (depth > 0)
{
// Walk the syntax tree to find the indexExp at this depth
IndexExp xe = cast(IndexExp)e1;
for (int d = 0; d < depth; ++d)
xe = cast(IndexExp)xe.e1;

Expression ekey = interpret(xe.e2, istate);
if (exceptionOrCant(ekey))
return;
ekey = resolveSlice(ekey); // only happens with AA assignment

// Look up this index in it up in the existing AA, to get the next level of AA.
AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey);
if (exceptionOrCant(newAA))
Expand All @@ -3423,6 +3438,7 @@ public:
existingAA = newAA;
--depth;
}

if (fp)
oldval = findKeyInAA(e.loc, existingAA, lastIndex);
}
Expand All @@ -3437,17 +3453,20 @@ public:
* aa[j] op= newval;
*/
oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();

Expression newaae = oldval;
while (e1.op == TOKindex && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
{
Expression ekey = interpret((cast(IndexExp)e1).e2, istate);
if (exceptionOrCant(ekey))
return;
ekey = resolveSlice(ekey); // only happens with AA assignment

auto keysx = new Expressions();
auto valuesx = new Expressions();
keysx.push(ekey);
valuesx.push(newaae);

auto aae = new AssocArrayLiteralExp(e.loc, keysx, valuesx);
aae.type = (cast(IndexExp)e1).e1.type;
aae.ownedByCtfe = OWNEDctfe;
Expand All @@ -3459,6 +3478,7 @@ public:
newaae = aae;
e1 = (cast(IndexExp)e1).e1;
}

// We must set to aggregate with newaae
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
Expand Down Expand Up @@ -3513,13 +3533,15 @@ public:
lastIndex = ie.e2;
}
}

// If it isn't a simple assignment, we need the existing value
if (fp && !oldval)
{
oldval = interpret(e1, istate);
if (exceptionOrCant(oldval))
return;
}

// ---------------------------------------
// Interpret right hand side
// ---------------------------------------
Expand All @@ -3542,6 +3564,7 @@ public:
if (exceptionOrCant(newval))
return;
}

// ----------------------------------------------------
// Deal with read-modify-write assignments.
// Set 'newval' to the final assignment value
Expand Down Expand Up @@ -3570,9 +3593,14 @@ public:
}
}
oldval = resolveSlice(oldval);

newval = (*fp)(e.loc, e.type, oldval, newval).copy();
}
else if (e.e2.type.isintegral() && (e.op == TOKaddass || e.op == TOKminass || e.op == TOKplusplus || e.op == TOKminusminus))
else if (e.e2.type.isintegral() &&
(e.op == TOKaddass ||
e.op == TOKminass ||
e.op == TOKplusplus ||
e.op == TOKminusminus))
{
newval = pointerArithmetic(e.loc, e.op, e.type, oldval, newval).copy();
}
Expand All @@ -3589,6 +3617,7 @@ public:
return;
}
}

if (existingAA)
{
if (existingAA.ownedByCtfe != OWNEDctfe)
Expand All @@ -3597,9 +3626,11 @@ public:
result = CTFEExp.cantexp;
return;
}

//printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
// __LINE__, existingAA->toChars(), lastIndex->toChars(), oldval ? oldval->toChars() : NULL, newval->toChars());
assignAssocArrayElement(e.loc, existingAA, lastIndex, newval);

// Determine the return value
result = ctfeCast(e.loc, e.type, e.type, fp && post ? oldval : newval);
return;
Expand All @@ -3611,14 +3642,17 @@ public:
* into:
* arr = new_length_array; (result is n)
*/

// Determine the return value
result = ctfeCast(e.loc, e.type, e.type, fp && post ? oldval : newval);
if (exceptionOrCant(result))
return;

size_t oldlen = cast(size_t)oldval.toInteger();
size_t newlen = cast(size_t)newval.toInteger();
if (oldlen == newlen) // no change required -- we're done!
return;

// We have changed it into a reference assignment
// Note that returnValue is still the new length.
e1 = (cast(ArrayLengthExp)e1).e1;
Expand All @@ -3632,19 +3666,23 @@ public:
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;

if (oldlen != 0) // Get the old array literal.
oldval = interpret(e1, istate);
newval = changeArrayLiteralLength(e.loc, cast(TypeArray)t, oldval, oldlen, newlen).copy();

e1 = assignToLvalue(e, e1, newval);
if (exceptionOrCant(e1))
return;

return;
}
if (!isBlockAssignment)
{
newval = ctfeCast(e.loc, e.type, e.type, newval);
if (exceptionOrCant(newval))
return;

// Determine the return value
if (goal == ctfeNeedLvalue) // Bugzilla 14371
result = e1;
Expand All @@ -3655,6 +3693,7 @@ public:
}
if (exceptionOrCant(newval))
return;

static if (LOGASSIGN)
{
printf("ASSIGN: %s=%s\n", e1.toChars(), newval.toChars());
Expand Down Expand Up @@ -3697,10 +3736,12 @@ public:
return;
}
assert(result);

/* Assignment to a CTFE reference.
*/
if (Expression ex = assignToLvalue(e, e1, newval))
result = ex;

return;
}

Expand Down
93 changes: 84 additions & 9 deletions src/dstruct.d
Original file line number Diff line number Diff line change
Expand Up @@ -258,49 +258,64 @@ public:

override Dsymbol syntaxCopy(Dsymbol s)
{
StructDeclaration sd = s ? cast(StructDeclaration)s : new StructDeclaration(loc, ident);
StructDeclaration sd =
s ? cast(StructDeclaration)s
: new StructDeclaration(loc, ident);
return ScopeDsymbol.syntaxCopy(sd);
}

override final void semantic(Scope* sc)
{
//printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok);
//printf("StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent.toChars(), toChars(), sizeok);

//static int count; if (++count == 20) assert(0);

if (semanticRun >= PASSsemanticdone)
return;
uint dprogress_save = Module.dprogress;
int errors = global.errors;

//printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent.toChars(), toChars(), sizeok);
Scope* scx = null;
if (_scope)
{
sc = _scope;
scx = _scope; // save so we don't make redundant copies
_scope = null;
}

if (!parent)
{
assert(sc.parent && sc.func);
parent = sc.parent;
}
assert(parent && !isAnonymous());

if (this.errors)
type = Type.terror;
type = type.semantic(loc, sc);
if (type.ty == Tstruct && (cast(TypeStruct)type).sym != this)
{
TemplateInstance ti = (cast(TypeStruct)type).sym.isInstantiated();
if (ti && isError(ti))
(cast(TypeStruct)type).sym = this;
}

// Ungag errors when not speculative
Ungag ungag = ungagSpeculative();

if (semanticRun == PASSinit)
{
protection = sc.protection;

alignment = sc.structalign;

storage_class |= sc.stc;
if (storage_class & STCdeprecated)
isdeprecated = true;
if (storage_class & STCabstract)
error("structs, unions cannot be abstract");

userAttribDecl = sc.userAttribDecl;
}
else if (symtab && !scx)
Expand All @@ -309,13 +324,15 @@ public:
return;
}
semanticRun = PASSsemantic;

if (!members) // if opaque declaration
{
semanticRun = PASSsemanticdone;
return;
}
if (!symtab)
symtab = new DsymbolTable();

if (sizeok == SIZEOKnone) // if not already done the addMember step
{
for (size_t i = 0; i < members.dim; i++)
Expand All @@ -325,6 +342,7 @@ public:
s.addMember(sc, this);
}
}

Scope* sc2 = sc.push(this);
sc2.stc &= STCsafe | STCtrusted | STCsystem;
sc2.parent = this;
Expand All @@ -334,9 +352,11 @@ public:
sc2.explicitProtection = 0;
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 All @@ -346,17 +366,21 @@ public:
//printf("struct: setScope %s %s\n", s->kind(), s->toChars());
s.setScope(sc2);
}

for (size_t i = 0; i < members.dim; i++)
{
Dsymbol s = (*members)[i];
s.importAll(sc2);
}

for (size_t i = 0; i < members.dim; i++)
{
Dsymbol s = (*members)[i];
s.semantic(sc2);
}

finalizeSize(sc2);

if (sizeok == SIZEOKfwd)
{
// semantic() failed because of forward references.
Expand All @@ -369,16 +393,20 @@ public:
fields.setDim(0);
structsize = 0;
alignsize = 0;

sc2.pop();

_scope = scx ? scx : sc.copy();
_scope.setNoFree();
_scope._module.addDeferredSemantic(this);
Module.dprogress = dprogress_save;
//printf("\tdeferring %s\n", toChars());
return;
}

Module.dprogress++;
//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.
Expand All @@ -392,17 +420,21 @@ public:
StructDeclaration sd = (cast(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 = cast(NewDeclaration)search(Loc(), Id.classNew);
aggDelete = cast(DeleteDeclaration)search(Loc(), Id.classDelete);

// this->ctor is already set in finalizeSize()

dtor = buildDtor(this, sc2);
Expand All @@ -426,7 +458,9 @@ public:
* See semanticTypeInfo().
*/
inv = buildInv(this, sc2);

sc2.pop();

if (ctor)
{
Dsymbol scall = search(Loc(), Id.call);
Expand All @@ -439,15 +473,18 @@ public:
FuncDeclaration fcall = resolveFuncCall(loc, sc, scall, null, null, null, 1);
sc = sc.pop();
global.endGagging(xerrors);

if (fcall && fcall.isStatic())
{
error(fcall.loc, "static opCall is hidden by constructors and can never be called");
errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with static opCall.");
}
}
}

Module.dprogress++;
semanticRun = PASSsemanticdone;

TypeTuple tup = toArgTypes(type);
size_t dim = tup.arguments.dim;
if (dim >= 1)
Expand All @@ -457,8 +494,10 @@ public:
if (dim == 2)
arg2type = (*tup.arguments)[1].type;
}

if (sc.func)
semantic2(sc);

if (global.errors != errors)
{
// The type is no good.
Expand All @@ -467,11 +506,13 @@ public:
if (deferred)
deferred.errors = true;
}

if (deferred && !global.gag)
{
deferred.semantic2(sc);
deferred.semantic3(sc);
}

version (none)
{
if (type.ty == Tstruct && (cast(TypeStruct)type).sym != this)
Expand All @@ -485,34 +526,51 @@ public:

final void semanticTypeInfoMembers()
{
if (xeq && xeq._scope && xeq.semanticRun < PASSsemantic3done)
if (xeq &&
xeq._scope &&
xeq.semanticRun < PASSsemantic3done)
{
uint errors = global.startGagging();
xeq.semantic3(xeq._scope);
if (global.endGagging(errors))
xeq = xerreq;
}
if (xcmp && xcmp._scope && xcmp.semanticRun < PASSsemantic3done)

if (xcmp &&
xcmp._scope &&
xcmp.semanticRun < PASSsemantic3done)
{
uint errors = global.startGagging();
xcmp.semantic3(xcmp._scope);
if (global.endGagging(errors))
xcmp = xerrcmp;
}

FuncDeclaration ftostr = search_toString(this);
if (ftostr && ftostr._scope && ftostr.semanticRun < PASSsemantic3done)
if (ftostr &&
ftostr._scope &&
ftostr.semanticRun < PASSsemantic3done)
{
ftostr.semantic3(ftostr._scope);
}
if (xhash && xhash._scope && xhash.semanticRun < PASSsemantic3done)

if (xhash &&
xhash._scope &&
xhash.semanticRun < PASSsemantic3done)
{
xhash.semantic3(xhash._scope);
}
if (postblit && postblit._scope && postblit.semanticRun < PASSsemantic3done)

if (postblit &&
postblit._scope &&
postblit.semanticRun < PASSsemantic3done)
{
postblit.semantic3(postblit._scope);
}
if (dtor && dtor._scope && dtor.semanticRun < PASSsemantic3done)

if (dtor &&
dtor._scope &&
dtor.semanticRun < PASSsemantic3done)
{
dtor.semantic3(dtor._scope);
}
Expand All @@ -523,11 +581,13 @@ public:
//printf("%s.StructDeclaration::search('%s')\n", toChars(), ident->toChars());
if (_scope && !symtab)
semantic(_scope);

if (!members || !symtab) // opaque or semantic() is not yet called
{
error("is forward referenced when looking for '%s'", ident.toChars());
return null;
}

return ScopeDsymbol.search(loc, ident, flags);
}

Expand Down Expand Up @@ -634,13 +694,15 @@ public:
{
if (!elements)
return true;

size_t nfields = fields.dim - isNested();
size_t offset = 0;
for (size_t i = 0; i < elements.dim; i++)
{
Expression e = (*elements)[i];
if (!e)
continue;

e = resolveProperties(sc, e);
if (i >= nfields)
{
Expand All @@ -659,11 +721,13 @@ public:
return false;
}
offset = cast(uint)(v.offset + v.type.size());

Type t = v.type;
if (stype)
t = t.addMod(stype.mod);
Type origType = t;
Type tb = t.toBasetype();

/* Look for case of initializing a static array with a too-short
* string literal, such as:
* char[5] foo = "abc";
Expand All @@ -675,12 +739,16 @@ public:
StringExp se = cast(StringExp)e;
Type typeb = se.type.toBasetype();
TY tynto = tb.nextOf().ty;
if (!se.committed && (typeb.ty == Tarray || typeb.ty == Tsarray) && (tynto == Tchar || tynto == Twchar || tynto == Tdchar) && se.length(cast(int)tb.nextOf().size()) < (cast(TypeSArray)tb).dim.toInteger())
if (!se.committed &&
(typeb.ty == Tarray || typeb.ty == Tsarray) &&
(tynto == Tchar || tynto == Twchar || tynto == Tdchar) &&
se.length(cast(int)tb.nextOf().size()) < (cast(TypeSArray)tb).dim.toInteger())
{
e = se.castTo(sc, t);
goto L1;
}
}

while (!e.implicitConvTo(t) && tb.ty == Tsarray)
{
/* Static array initialization, as in:
Expand All @@ -691,10 +759,12 @@ public:
}
if (!e.implicitConvTo(t))
t = origType; // restore type for better diagnostic

e = e.implicitCastTo(sc, t);
L1:
if (e.op == TOKerror)
return false;

(*elements)[i] = e.isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e);
}
return true;
Expand All @@ -713,9 +783,12 @@ public:
// If we've already determined whether this struct is POD.
if (ispod != ISPODfwd)
return (ispod == ISPODyes);

ispod = ISPODyes;

if (enclosing || postblit || dtor)
ispod = ISPODno;

// Recursively check all fields are POD.
for (size_t i = 0; i < fields.dim; i++)
{
Expand All @@ -725,6 +798,7 @@ public:
ispod = ISPODno;
break;
}

Type tv = v.type.baseElemOf();
if (tv.ty == Tstruct)
{
Expand All @@ -737,6 +811,7 @@ public:
}
}
}

return (ispod == ISPODyes);
}

Expand Down
4 changes: 4 additions & 0 deletions src/dsymbol.d
Original file line number Diff line number Diff line change
Expand Up @@ -546,12 +546,16 @@ public:
if (!s2.overloadInsert(this))
{
sds.multiplyDefined(Loc(), this, s2);
errors = true;
}
}
if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
{
if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof)
{
error(".%s property cannot be redefined", ident.toChars());
errors = true;
}
}
}
}
Expand Down
38 changes: 26 additions & 12 deletions src/e2ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ void objc_callfunc_setupEp(elem *esel, elem **ep, int reverse);
*/
bool ISREF(Declaration *var, Type *tb)
{
return (var->isParameter() && config.exe == EX_WIN64 && (var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy))
return (config.exe == EX_WIN64 && var->isParameter() &&
(var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy))
|| var->isOut() || var->isRef();
}

Expand All @@ -92,8 +93,8 @@ bool ISREF(Declaration *var, Type *tb)
bool ISWIN64REF(Declaration *var)
{
return (config.exe == EX_WIN64 && var->isParameter() &&
(var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy)) &&
!(var->isOut() || var->isRef());
(var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy))
&& !(var->isOut() || var->isRef());
}

/******************************************
Expand Down Expand Up @@ -946,7 +947,6 @@ elem *toElem(Expression *e, IRState *irs)
void visit(SymbolExp *se)
{
elem *e;
tym_t tym;
Type *tb = (se->op == TOKsymoff) ? se->var->type->toBasetype() : se->type->toBasetype();
int offset = (se->op == TOKsymoff) ? ((SymOffExp*)se)->offset : 0;
VarDeclaration *v = se->var->isVarDeclaration();
Expand Down Expand Up @@ -1090,7 +1090,6 @@ elem *toElem(Expression *e, IRState *irs)
}
else if (ISREF(se->var, tb))
{
// Static arrays are really passed as pointers to the array
// Out parameters are really references
e = el_var(s);
e->Ety = TYnptr;
Expand All @@ -1114,13 +1113,17 @@ elem *toElem(Expression *e, IRState *irs)
e->Ety = TYnptr;
e = el_una(OPind, 0, e);
}
if (tb->ty == Tfunction)
{

tym_t tym;
if (se->var->storage_class & STClazy)
tym = TYdelegate; // Tdelegate as C type
else if (tb->ty == Tfunction)
tym = s->Stype->Tty;
}
else
tym = totym(se->type);

e->Ejty = e->Ety = tym;

if (tybasic(tym) == TYstruct)
{
e->ET = Type_toCtype(se->type);
Expand Down Expand Up @@ -2508,7 +2511,7 @@ elem *toElem(Expression *e, IRState *irs)
Type *ta = are->e1->type->toBasetype();

// which we do if the 'next' types match
if (ae->ismemset & 1)
if (ae->memset & MemorySet_blockAssign)
{
// Do a memset for array[]=v
//printf("Lpair %s\n", ae->toChars());
Expand Down Expand Up @@ -2698,11 +2701,14 @@ elem *toElem(Expression *e, IRState *irs)

/* Look for reference initializations
*/
if (ae->op == TOKconstruct && ae->e1->op == TOKvar && !(ae->ismemset & 2))
if (ae->memset & MemorySet_referenceInit)
{
assert(ae->op == TOKconstruct || ae->op == TOKblit);
assert(ae->e1->op == TOKvar);

VarExp *ve = (VarExp *)ae->e1;
Declaration *s = ve->var;
if (s->storage_class & (STCout | STCref))
Declaration *d = ve->var;
if (d->storage_class & (STCout | STCref))
{
e = toElem(ae->e2, irs);
e = addressElem(e, ae->e2->type);
Expand Down Expand Up @@ -2746,6 +2752,14 @@ elem *toElem(Expression *e, IRState *irs)
//printf("e1x = \n"); elem_print(e1x);
}

// inlining may generate lazy variable initialization
if (ae->e1->op == TOKvar && (((VarExp *)ae->e1)->var->storage_class & STClazy))
{
assert(ae->op == TOKconstruct || ae->op == TOKblit);
e = el_bin(OPeq, tym, e1, toElem(ae->e2, irs));
goto Lret;
}

/* This will work if we can distinguish an assignment from
* an initialization of the lvalue. It'll work if the latter.
* If the former, because of aliasing of the return value with
Expand Down
219 changes: 184 additions & 35 deletions src/expression.d

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions src/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -1166,12 +1166,16 @@ class PreExp : public UnaExp
void accept(Visitor *v) { v->visit(this); }
};

enum MemorySet
{
MemorySet_blockAssign = 1, // setting the contents of an array
MemorySet_referenceInit = 2, // setting the reference of STCref variable
};

class AssignExp : public BinExp
{
public:
// &1 != 0 if setting the contents of an array
// &2 != 0 if setting the content of ref variable
int ismemset;
int memset; // combination of MemorySet flags

AssignExp(Loc loc, Expression *e1, Expression *e2);
Expression *semantic(Scope *sc);
Expand All @@ -1186,13 +1190,15 @@ class ConstructExp : public AssignExp
{
public:
ConstructExp(Loc loc, Expression *e1, Expression *e2);
ConstructExp(Loc loc, VarDeclaration *v, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};

class BlitExp : public AssignExp
{
public:
BlitExp(Loc loc, Expression *e1, Expression *e2);
BlitExp(Loc loc, VarDeclaration *v, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};

Expand Down
21 changes: 8 additions & 13 deletions src/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,7 @@ public:
assert(tret.ty != Tvoid);
if (vresult || returnLabel)
buildResultVar(scout ? scout : sc2, tret);

/* Cannot move this loop into NrvoWalker, because
* returns[i] may be in the nested delegate for foreach-body.
*/
Expand All @@ -1815,6 +1816,7 @@ public:
else
{
exp = exp.optimize(WANTvalue);

/* Bugzilla 10789:
* If NRVO is not possible, all returned lvalues should call their postblits.
*/
Expand All @@ -1826,13 +1828,8 @@ public:
if (vresult)
{
// Create: return vresult = exp;
auto ve = new VarExp(Loc(), vresult);
ve.type = vresult.type;
if (f.isref)
exp = new ConstructExp(rs.loc, ve, exp);
else
exp = new BlitExp(rs.loc, ve, exp);
exp.type = ve.type;
exp = new BlitExp(rs.loc, vresult, exp);
exp.type = vresult.type;
if (rs.caseDim)
exp = Expression.combine(exp, new IntegerExp(rs.caseDim));
}
Expand Down Expand Up @@ -2016,8 +2013,7 @@ public:
*/
Expression e = new VarExp(Loc(), v_arguments);
e = new DotIdExp(Loc(), e, Id.elements);
Expression e1 = new VarExp(Loc(), _arguments);
e = new ConstructExp(Loc(), e1, e);
e = new ConstructExp(Loc(), _arguments, e);
e = e.semantic(sc2);
_arguments._init = new ExpInitializer(Loc(), e);
auto de = new DeclarationExp(Loc(), _arguments);
Expand Down Expand Up @@ -3779,10 +3775,9 @@ extern (C++) Expression addInvariant(Loc loc, Scope* sc, AggregateDeclaration ad
v.type = vthis.type;
if (ad.isStructDeclaration())
v = v.addressOf();
Expression se = new StringExp(Loc(), cast(char*)"null this");
se = se.semantic(sc);
se.type = Type.tchar.arrayOf();
e = new AssertExp(loc, v, se);
e = new StringExp(Loc(), cast(char*)"null this");
e = new AssertExp(loc, v, e);
e = e.semantic(sc);
}
return e;
}
Expand Down
131 changes: 88 additions & 43 deletions src/inline.d
Original file line number Diff line number Diff line change
Expand Up @@ -1771,13 +1771,16 @@ public:
bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo)
{
int cost;

enum CANINLINE_LOG = 0;
static if (CANINLINE_LOG)
{
printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n", hasthis, statementsToo, fd.toPrettyChars());
}

if (fd.needThis() && !hasthis)
return false;

if (fd.inlineNest)
{
static if (CANINLINE_LOG)
Expand All @@ -1786,6 +1789,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
}
return false;
}

if (fd.semanticRun < PASSsemantic3 && !hdrscan)
{
if (!fd.fbody)
Expand All @@ -1797,6 +1801,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
return false;
assert(fd.semanticRun >= PASSsemantic3done);
}

switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp)
{
case ILSyes:
Expand All @@ -1816,6 +1821,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
default:
assert(0);
}

switch (fd.inlining)
{
case PINLINEdefault:
Expand All @@ -1827,23 +1833,33 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
default:
assert(0);
}

if (fd.type)
{
assert(fd.type.ty == Tfunction);
TypeFunction tf = cast(TypeFunction)fd.type;
if (tf.varargs == 1) // no variadic parameter lists

// no variadic parameter lists
if (tf.varargs == 1)
goto Lno;

/* Don't inline a function that returns non-void, but has
* no return expression.
* No statement inlining for non-voids.
*/
if (tf.next && tf.next.ty != Tvoid && (!(fd.hasReturnExp & 1) || statementsToo) && !hdrscan)
if (tf.next && tf.next.ty != Tvoid &&
(!(fd.hasReturnExp & 1) || statementsToo) &&
!hdrscan)
{
goto Lno;
}

/* Bugzilla 14560: If fd returns void, all explicit `return;`s
* must not appear in the expanded result.
* See also ReturnStatement.inlineAsStatement().
*/
}

// cannot inline constructor calls because we need to convert:
// return;
// to:
Expand All @@ -1852,10 +1868,19 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
// require() has magic properties too
// see bug 7699
// no nested references to this frame
if (!fd.fbody || fd.ident == Id.ensure || (fd.ident == Id.require && fd.toParent().isFuncDeclaration() && fd.toParent().isFuncDeclaration().needThis()) || !hdrscan && (fd.isSynchronized() || fd.isImportedSymbol() || fd.hasNestedFrameRefs() || (fd.isVirtual() && !fd.isFinalFunc())))
if (!fd.fbody ||
fd.ident == Id.ensure ||
(fd.ident == Id.require &&
fd.toParent().isFuncDeclaration() &&
fd.toParent().isFuncDeclaration().needThis()) ||
!hdrscan && (fd.isSynchronized() ||
fd.isImportedSymbol() ||
fd.hasNestedFrameRefs() ||
(fd.isVirtual() && !fd.isFinalFunc())))
{
goto Lno;
}

{
scope InlineCostVisitor icv = new InlineCostVisitor();
icv.hasthis = hasthis;
Expand All @@ -1868,19 +1893,23 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
{
printf("cost = %d for %s\n", cost, fd.toChars());
}

if (tooCostly(cost))
goto Lno;
if (!statementsToo && cost > COST_MAX)
goto Lno;

if (!hdrscan)
{
// Don't modify inlineStatus for header content scan
if (statementsToo)
fd.inlineStatusStmt = ILSyes;
else
fd.inlineStatusExp = ILSyes;

scope InlineScanVisitor v = new InlineScanVisitor();
fd.accept(v); // Don't scan recursively for header content scan

if (fd.inlineStatusExp == ILSuninitialized)
{
// Need to redo cost computation, as some statements or expressions have been inlined
Expand All @@ -1894,10 +1923,12 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
{
printf("recomputed cost = %d for %s\n", cost, fd.toChars());
}

if (tooCostly(cost))
goto Lno;
if (!statementsToo && cost > COST_MAX)
goto Lno;

if (statementsToo)
fd.inlineStatusStmt = ILSyes;
else
Expand All @@ -1909,9 +1940,11 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
printf("\t2: yes %s\n", fd.toChars());
}
return true;

Lno:
if (fd.inlining == PINLINEalways)
fd.error("cannot inline function");

if (!hdrscan) // Don't modify inlineStatus for header content scan
{
if (statementsToo)
Expand All @@ -1938,10 +1971,13 @@ public void inlineScanModule(Module m)
if (m.semanticRun != PASSsemantic3done)
return;
m.semanticRun = PASSinline;

// Note that modules get their own scope, from scratch.
// This is so regardless of where in the syntax a module
// gets imported, it is unaffected by context.

//printf("Module = %p\n", m.sc.scopesym);

foreach (i; 0 .. m.members.dim)
{
Dsymbol s = (*m.members)[i];
Expand Down Expand Up @@ -1986,6 +2022,7 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,

if (asStatements)
as = new Statements();

VarDeclaration vret = null;
if (eret)
{
Expand All @@ -2003,55 +2040,50 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
vret.storage_class |= STCtemp | STCforeach | STCref;
vret.linkage = LINKd;
vret.parent = parent;

Expression de = new DeclarationExp(fd.loc, vret);
de.type = Type.tvoid;
e = Expression.combine(e, de);
Expression ex = new VarExp(fd.loc, vret);
ex.type = vret.type;
ex = new ConstructExp(fd.loc, ex, eret);

Expression ex = new ConstructExp(fd.loc, vret, eret);
ex.type = vret.type;
e = Expression.combine(e, ex);
}
}

// Set up vthis
if (ethis)
{
VarDeclaration vthis;
ExpInitializer ei;
VarExp ve;
if (ethis.type.ty == Tpointer)
{
Type t = ethis.type.nextOf();
ethis = new PtrExp(ethis.loc, ethis);
ethis.type = t;
}
ei = new ExpInitializer(ethis.loc, ethis);
vthis = new VarDeclaration(ethis.loc, ethis.type, Id.This, ei);
auto ei = new ExpInitializer(ethis.loc, ethis);

auto vthis = new VarDeclaration(ethis.loc, ethis.type, Id.This, ei);
if (ethis.type.ty != Tclass)
vthis.storage_class = STCref;
else
vthis.storage_class = STCin;
vthis.linkage = LINKd;
vthis.parent = parent;
ve = new VarExp(vthis.loc, vthis);
ve.type = vthis.type;
ei.exp = new AssignExp(vthis.loc, ve, ethis);
ei.exp.type = ve.type;
if (ethis.type.ty != Tclass)
{
/* This is a reference initialization, not a simple assignment.
*/
ei.exp.op = TOKconstruct;
}

ei.exp = new ConstructExp(vthis.loc, vthis, ethis);
ei.exp.type = vthis.type;

ids.vthis = vthis;
}

// Set up parameters
if (ethis)
{
Expression de = new DeclarationExp(Loc(), ids.vthis);
de.type = Type.tvoid;
e = Expression.combine(e, de);
}

if (!asStatements && fd.nrvo_var)
{
if (vret)
Expand All @@ -2067,8 +2099,10 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
vd.storage_class = STCtemp | STCrvalue;
vd.linkage = tf.linkage;
vd.parent = parent;

ids.from.push(fd.nrvo_var);
ids.to.push(vd);

Expression de = new DeclarationExp(Loc(), vd);
de.type = Type.tvoid;
e = Expression.combine(e, de);
Expand All @@ -2079,31 +2113,27 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
assert(fd.parameters.dim == arguments.dim);
foreach (i; 0 .. arguments.dim)
{
VarDeclaration vfrom = (*fd.parameters)[i];
VarDeclaration vto;
Expression arg = (*arguments)[i];
ExpInitializer ei;
VarExp ve;
ei = new ExpInitializer(arg.loc, arg);
vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei);
auto vfrom = (*fd.parameters)[i];

auto arg = (*arguments)[i];
auto ei = new ExpInitializer(arg.loc, arg);

auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei);
vto.storage_class |= vfrom.storage_class & (STCtemp | STCin | STCout | STClazy | STCref);
vto.linkage = vfrom.linkage;
vto.parent = parent;
//printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class);
//printf("vto.parent = '%s'\n", parent.toChars());
ve = new VarExp(vto.loc, vto);
//ve.type = vto.type;
ve.type = arg.type;
if (vfrom.storage_class & (STCout | STCref))
ei.exp = new ConstructExp(vto.loc, ve, arg);
else
ei.exp = new BlitExp(vto.loc, ve, arg);
ei.exp.type = ve.type;
//ve.type.print();

// Even if vto is STClazy, `vto = arg` is handled correctly in glue layer.
ei.exp = new BlitExp(vto.loc, vto, arg);
ei.exp.type = vto.type;
//arg.type.print();
//ei.exp.print();

ids.from.push(vfrom);
ids.to.push(vto);

auto de = new DeclarationExp(Loc(), vto);
de.type = Type.tvoid;
e = Expression.combine(e, de);
Expand All @@ -2119,6 +2149,7 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
}
}
}

if (asStatements)
{
if (e)
Expand All @@ -2141,9 +2172,11 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
//eb.type.print();
//eb.print();
//eb.print();

// Bugzilla 11322:
if (tf.isref)
e = e.toLvalue(null, null);

/* There's a problem if what the function returns is used subsequently as an
* lvalue, as in a struct return that is then used as a 'this'.
* If we take the address of the return value, we will be taking the address
Expand All @@ -2164,25 +2197,37 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
* tret __inlineretval = e;
*/
auto ei = new ExpInitializer(fd.loc, e);

Identifier tmp = Identifier.generateId("__inlineretval");
auto vd = new VarDeclaration(fd.loc, tf.next, tmp, ei);
vd.storage_class = (tf.isref ? STCref : 0) | STCtemp | STCrvalue;
vd.linkage = tf.linkage;
vd.parent = parent;
auto ve = new VarExp(fd.loc, vd);
ve.type = tf.next;
ei.exp = new ConstructExp(fd.loc, ve, e);
ei.exp.type = ve.type;

ei.exp = new ConstructExp(fd.loc, vd, e);
ei.exp.type = vd.type;

auto de = new DeclarationExp(Loc(), vd);
de.type = Type.tvoid;

// Chain the two together:
// ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval
e = Expression.combine(de, ve);
e = Expression.combine(de, new VarExp(fd.loc, vd));

//fprintf(stderr, "CallExp.inlineScan: e = "); e.print();
}

// Bugzilla 15210
if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid)
{
e = new CastExp(e.loc, e, Type.tvoid);
e.type = Type.tvoid;
}

eresult = e;
}
//printf("%s.expandInline = { %s }\n", fd.toChars(), e.toChars());
//printf("[%s] %s expandInline = { %s }\n", fd.loc.toChars(), fd.toPrettyChars(), e.toChars());

// Need to reevaluate whether parent can now be inlined
// in expressions, as we might have inlined statements
parent.inlineStatusExp = ILSuninitialized;
Expand Down
9 changes: 8 additions & 1 deletion src/statement.d
Original file line number Diff line number Diff line change
Expand Up @@ -4238,20 +4238,26 @@ public:
exp = inferType(exp, tret);
else if (fld && fld.treq)
exp = inferType(exp, fld.treq.nextOf().nextOf());

exp = exp.semantic(sc);
exp = resolveProperties(sc, exp);
if (exp.type && exp.type.ty != Tvoid || exp.op == TOKfunction || exp.op == TOKtype || exp.op == TOKtemplate)
if (exp.type && exp.type.ty != Tvoid ||
exp.op == TOKfunction ||
exp.op == TOKtype ||
exp.op == TOKtemplate)
{
// don't make error for void expression
if (exp.checkValue())
exp = new ErrorExp();
}
if (checkNonAssignmentArrayOp(exp))
exp = new ErrorExp();

// Extract side-effect part
exp = Expression.extractLast(exp, &e0);
if (exp.op == TOKcall)
exp = valueNoDtor(exp);

/* Void-return function can have void typed expression
* on return statement.
*/
Expand All @@ -4264,6 +4270,7 @@ public:
exp = new CastExp(loc, exp, Type.tvoid);
exp = exp.semantic(sc);
}

/* Replace:
* return exp;
* with:
Expand Down
2 changes: 1 addition & 1 deletion test/fail_compilation/fail325.d
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail325.d(12): Error: cannot cast template fun(T = int)(int w, int z) to type void function(int, int)
fail_compilation/fail325.d(12): Error: template fun(T = int)(int w, int z) has no value
---
*/

Expand Down
21 changes: 21 additions & 0 deletions test/fail_compilation/fail_casting2.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// REQUIRED_ARGS: -o-

/*
TEST_OUTPUT:
---
fail_compilation/fail_casting2.d(15): Error: type int has no value
fail_compilation/fail_casting2.d(17): Error: template lambda has no value
fail_compilation/fail_casting2.d(20): Error: template Templ() has no value
---
*/

void test15214()
{
alias Type = int;
cast(void)(Type);

cast(void)(x => mixin(x)("mixin(x);"));

template Templ() {}
cast(void)(Templ);
}
20 changes: 20 additions & 0 deletions test/fail_compilation/ice15092.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice15092.d(13): Error: struct ice15092.A.S conflicts with struct ice15092.A.S at fail_compilation/ice15092.d(12)
fail_compilation/ice15092.d(16): Error: class ice15092.A.C conflicts with class ice15092.A.C at fail_compilation/ice15092.d(15)
fail_compilation/ice15092.d(19): Error: interface ice15092.A.I conflicts with interface ice15092.A.I at fail_compilation/ice15092.d(18)
---
*/

class A
{
struct S {}
struct S {}

class C {}
class C {}

interface I {}
interface I {}
}
81 changes: 78 additions & 3 deletions test/runnable/inline.d
Original file line number Diff line number Diff line change
Expand Up @@ -736,20 +736,49 @@ void test14753(string) { }

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

struct S14975 {
struct S14975
{
int bar;

pragma(inline, true) this(int bar) {
pragma(inline, true) this(int bar)
{
this.bar = bar;
}
}

void test14975() {
void test14975()
{
S14975 baz = 1;
if (baz.bar != 1)
assert(0);
}

/**********************************/
// 15210

struct BigInt15210 {}

struct Tuple15210(Types...)
{
Types field;

void opAssign(R)(R rhs)
{
field = rhs.field;
}
}

void test15210()
{
alias X = Tuple15210!BigInt15210;

X[BigInt15210] cache;

auto x = X();

cache[BigInt15210()] = x;
}

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

int foo7625(int v)
Expand Down Expand Up @@ -849,6 +878,50 @@ void test9785_3() @nogc
loop(&func);
}

/**********************************/
// 15207

struct Vec15207
{
float x, y, z;

this(float x_, float y_, float z_)
{
x = x_;
y = y_;
z = z_;
}

Vec15207 clone()
{
// When the variable 'res' is replaced with a STCref temporary,
// this line was accidentally changed to reference initialization.
Vec15207 res = this;

return res;
}
}

class C15207
{
Vec15207 a;

this()
{
a = Vec15207(1, 2, 3).clone();

assert(a.x == 1);
assert(a.y == 2);
assert(a.z == 3);
printf("%f %f %f\n", a.x, a.y, a.z);
}
}

void test15207()
{
auto c = new C15207();
}

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

int main()
Expand Down Expand Up @@ -876,10 +949,12 @@ int main()
test14754();
test14606();
test14975();
test15210();
test7625();
test9785();
test9785_2();
test9785_3();
test15207();

printf("Success\n");
return 0;
Expand Down