129 changes: 111 additions & 18 deletions src/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -11468,7 +11468,9 @@ public:
//printf("e2->op = %d, '%s'\n", e2->op, Token::toChars(e2->op));
if (type)
return this;

Expression e1old = e1;

if (e2.op == TOKcomma)
{
/* Rewrite to get rid of the comma from rvalue
Expand All @@ -11478,6 +11480,7 @@ public:
Expression e = Expression.combine(e0, this);
return e.semantic(sc);
}

/* Look for operator overloading of a[arguments] = e2.
* Do it before e1->semantic() otherwise the ArrayExp will have been
* converted to unary operator overloading already.
Expand All @@ -11488,8 +11491,12 @@ public:
ArrayExp ae = cast(ArrayExp)e1;
ae.e1 = ae.e1.semantic(sc);
ae.e1 = resolveProperties(sc, ae.e1);

Expression ae1old = ae.e1;
const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOKinterval);
const(bool) maybeSlice =
(ae.arguments.dim == 0 ||
ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOKinterval);

IntervalExp ie = null;
if (maybeSlice && ae.arguments.dim)
{
Expand All @@ -11503,6 +11510,7 @@ public:
Expression e0 = null;
Expression ae1save = ae.e1;
ae.lengthVar = null;

Type t1b = ae.e1.type.toBasetype();
AggregateDeclaration ad = isAggregate(t1b);
if (!ad)
Expand All @@ -11515,10 +11523,12 @@ public:
goto Lfallback;
if (result.op == TOKerror)
return result;

result = e2.semantic(sc);
if (result.op == TOKerror)
return result;
e2 = result;

/* Rewrite (a[arguments] = e2) as:
* a.opIndexAssign(e2, arguments)
*/
Expand All @@ -11536,17 +11546,20 @@ public:
return result;
}
}

Lfallback:
if (maybeSlice && search_function(ad, Id.sliceass))
{
// Deal with $
result = resolveOpDollar(sc, ae, ie, &e0);
if (result.op == TOKerror)
return result;

result = e2.semantic(sc);
if (result.op == TOKerror)
return result;
e2 = result;

/* Rewrite (a[i..j] = e2) as:
* a.opSliceAssign(e2, i, j)
*/
Expand All @@ -11563,6 +11576,7 @@ public:
result = Expression.combine(e0, result);
return result;
}

// No operator overloading member function found yet, but
// there might be an alias this to try.
if (ad.aliasthis && t1b != ae.att1)
Expand All @@ -11581,10 +11595,12 @@ public:
ae.e1 = ae1old; // recovery
ae.lengthVar = null;
}

/* Run this->e1 semantic.
*/
{
Expression e1x = e1;

/* With UFCS, e.f = value
* Could mean:
* .f(e, value)
Expand Down Expand Up @@ -11618,6 +11634,7 @@ public:
}
else
e1x = e1x.semantic(sc);

/* We have f = value.
* Could mean:
* f(value)
Expand All @@ -11632,6 +11649,7 @@ public:
assert(e1.type);
}
Type t1 = e1.type.toBasetype();

/* Run this->e2 semantic.
* Different from other binary expressions, the analysis of e2
* depends on the result of e1 in assignments.
Expand All @@ -11646,10 +11664,12 @@ public:
return new ErrorExp();
e2 = e2x;
}

/* Rewrite tuple assignment as a tuple of assignments.
*/
{
Expression e2x = e2;

Ltupleassign:
if (e1.op == TOKtuple && e2x.op == TOKtuple)
{
Expand Down Expand Up @@ -11682,15 +11702,18 @@ public:
}
return e.semantic(sc);
}

/* Look for form: e1 = e2->aliasthis.
*/
if (e1.op == TOKtuple)
{
TupleDeclaration td = isAliasThisTuple(e2x);
if (!td)
goto Lnomatch;

assert(e1.type.ty == Ttuple);
TypeTuple tt = cast(TypeTuple)e1.type;

Identifier id = Identifier.generateId("__tup");
auto ei = new ExpInitializer(e2x.loc, e2x);
auto v = new VarDeclaration(e2x.loc, null, id, ei);
Expand All @@ -11700,16 +11723,19 @@ public:
Expression e0 = new DeclarationExp(e2x.loc, v);
Expression ev = new VarExp(e2x.loc, v);
ev.type = e2x.type;

auto iexps = new Expressions();
iexps.push(ev);
for (size_t u = 0; u < iexps.dim; u++)
{
Lexpand:
Expression e = (*iexps)[u];

Parameter arg = Parameter.getNth(tt.arguments, u);
//printf("[%d] iexps->dim = %d, ", u, iexps->dim);
//printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
//printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());

if (!arg || !e.type.implicitConvTo(arg.type))
{
// expand initializer to tuple
Expand All @@ -11731,19 +11757,22 @@ public:
}
Lnomatch:
}

/* Inside constructor, if this is the first assignment of object field,
* rewrite this to initializing the field.
*/
if (op == TOKassign && e1.checkModifiable(sc) == 2)
{
//printf("[%s] change to init - %s\n", loc.toChars(), toChars());
op = TOKconstruct;

if (e1.op == TOKvar && (cast(VarExp)e1).var.storage_class & (STCout | STCref))
{
// Bugzilla 14944, even if e1 is a ref variable,
// make an initialization of referenced memory.
ismemset |= 2;
}

// Bugzilla 13515: set Index::modifiable flag for complex AA element initialization
if (e1.op == TOKindex)
{
Expand All @@ -11752,10 +11781,13 @@ public:
return e1x;
}
}

/* If it is an assignment from a 'foreign' type,
* check for operator overloading.
*/
if (op == TOKconstruct && e1.op == TOKvar && (cast(VarExp)e1).var.storage_class & (STCout | STCref) && !(ismemset & 2))
if (op == TOKconstruct && e1.op == TOKvar &&
(cast(VarExp)e1).var.storage_class & (STCout | STCref) &&
!(ismemset & 2))
{
// If this is an initialization of a reference,
// do nothing
Expand All @@ -11765,18 +11797,24 @@ public:
Expression e1x = e1;
Expression e2x = e2;
StructDeclaration sd = (cast(TypeStruct)t1).sym;

if (op == TOKconstruct)
{
Type t2 = e2x.type.toBasetype();
if (t2.ty == Tstruct && sd == (cast(TypeStruct)t2).sym)
{
CallExp ce;
DotVarExp dve;
if (sd.ctor && e2x.op == TOKcall && (ce = cast(CallExp)e2x, ce.e1.op == TOKdotvar) && (dve = cast(DotVarExp)ce.e1, dve.var.isCtorDeclaration()) && e2x.type.implicitConvTo(t1))
if (sd.ctor &&
e2x.op == TOKcall &&
(ce = cast(CallExp)e2x, ce.e1.op == TOKdotvar) &&
(dve = cast(DotVarExp)ce.e1, dve.var.isCtorDeclaration()) &&
e2x.type.implicitConvTo(t1))
{
/* Look for form of constructor call which is:
* __ctmp.ctor(arguments...)
*/

/* Before calling the constructor, initialize
* variable with a bit copy of the default
* initializer
Expand All @@ -11793,6 +11831,7 @@ public:
ae.e2 = sd.isNested() ? t1.defaultInitLiteral(loc) : t1.defaultInit(loc);
}
ae.type = e1x.type;

/* Replace __ctmp being constructed with e1.
* We need to copy constructor call expression,
* because it may be used in other place.
Expand All @@ -11801,6 +11840,7 @@ public:
dvx.e1 = e1x;
CallExp cx = cast(CallExp)ce.copy();
cx.e1 = dvx;

Expression e = new CommaExp(loc, ae, cx);
e = e.semantic(sc);
return e;
Expand All @@ -11820,13 +11860,16 @@ public:
Expression e = new CondExp(loc, econd.econd, ea1, ea2);
return e.semantic(sc);
}

if (e2x.isLvalue())
{
if (!e2x.type.implicitConvTo(e1x.type))
{
error("conversion error from %s to %s", e2x.type.toChars(), e1x.type.toChars());
error("conversion error from %s to %s",
e2x.type.toChars(), e1x.type.toChars());
return new ErrorExp();
}

/* Rewrite as:
* (e1 = e2).postblit();
*
Expand Down Expand Up @@ -11860,6 +11903,7 @@ public:
Expression einit;
einit = new BlitExp(loc, e1x, e1x.type.defaultInit(loc));
einit.type = e1x.type;

Expression e;
e = new DotIdExp(loc, e1x, Id.ctor);
e = new CallExp(loc, e, e2x);
Expand All @@ -11876,6 +11920,7 @@ public:
*/
e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call);
e2x = new CallExp(loc, e2x, this.e2);

e2x = e2x.semantic(sc);
e2x = resolveProperties(sc, e2x);
if (e2x.op == TOKerror)
Expand Down Expand Up @@ -11917,35 +11962,46 @@ public:
IndexExp ie = cast(IndexExp)e1x;
Type t2 = e2x.type.toBasetype();
Expression e0 = null;

Expression ea = ie.e1;
Expression ek = ie.e2;
Expression ev = e2x;
if (!isTrivialExp(ea))
{
auto v = new VarDeclaration(loc, ie.e1.type, Identifier.generateId("__aatmp"), new ExpInitializer(loc, ie.e1));
v.storage_class |= STCtemp | STCctfe | (ea.isLvalue() ? STCforeach | STCref : STCrvalue);
auto v = new VarDeclaration(loc, ie.e1.type,
Identifier.generateId("__aatmp"),
new ExpInitializer(loc, ie.e1));
v.storage_class |= STCtemp | STCctfe
| (ea.isLvalue() ? STCforeach | STCref : STCrvalue);
v.semantic(sc);
e0 = combine(e0, new DeclarationExp(loc, v));
ea = new VarExp(loc, v);
}
if (!isTrivialExp(ek))
{
auto v = new VarDeclaration(loc, ie.e2.type, Identifier.generateId("__aakey"), new ExpInitializer(loc, ie.e2));
v.storage_class |= STCtemp | STCctfe | (ek.isLvalue() ? STCforeach | STCref : STCrvalue);
auto v = new VarDeclaration(loc, ie.e2.type,
Identifier.generateId("__aakey"),
new ExpInitializer(loc, ie.e2));
v.storage_class |= STCtemp | STCctfe
| (ek.isLvalue() ? STCforeach | STCref : STCrvalue);
v.semantic(sc);
e0 = combine(e0, new DeclarationExp(loc, v));
ek = new VarExp(loc, v);
}
if (!isTrivialExp(ev))
{
auto v = new VarDeclaration(loc, e2x.type, Identifier.generateId("__aaval"), new ExpInitializer(loc, e2x));
v.storage_class |= STCtemp | STCctfe | (ev.isLvalue() ? STCforeach | STCref : STCrvalue);
auto v = new VarDeclaration(loc, e2x.type,
Identifier.generateId("__aaval"),
new ExpInitializer(loc, e2x));
v.storage_class |= STCtemp | STCctfe
| (ev.isLvalue() ? STCforeach | STCref : STCrvalue);
v.semantic(sc);
e0 = combine(e0, new DeclarationExp(loc, v));
ev = new VarExp(loc, v);
}
if (e0)
e0 = e0.semantic(sc);

AssignExp ae = cast(AssignExp)copy();
ae.e1 = new IndexExp(loc, ea, ek);
ae.e1 = ae.e1.semantic(sc);
Expand Down Expand Up @@ -11975,11 +12031,13 @@ public:
ex = ex.semantic(sc);
ex = ex.optimize(WANTvalue);
ex = ex.modifiableLvalue(sc, ex); // allocate new slot

ey = new ConstructExp(loc, ex, ey);
ey = ey.semantic(sc);
if (ey.op == TOKerror)
return ey;
ex = e;

// Bugzilla 14144: The whole expression should have the common type
// of opAssign() return and assigned AA entry.
// Even if there's no common type, expression should be typed as void.
Expand All @@ -12005,6 +12063,7 @@ public:
}
else
assert(op == TOKblit);

e1 = e1x;
e2 = e2x;
}
Expand Down Expand Up @@ -12142,16 +12201,21 @@ public:
return e1x;
e1 = e1x;
}

/* Tweak e2 based on the type of e1.
*/
Expression e2x = e2;
Type t2 = e2x.type.toBasetype();

// If it is a array, get the element type. Note that it may be
// multi-dimensional.
Type telem = t1;
while (telem.ty == Tarray)
telem = telem.nextOf();
if (e1.op == TOKslice && t1.nextOf() && (telem.ty != Tvoid || e2x.op == TOKnull) && e2x.implicitConvTo(t1.nextOf()))

if (e1.op == TOKslice && t1.nextOf() &&
(telem.ty != Tvoid || e2x.op == TOKnull) &&
e2x.implicitConvTo(t1.nextOf()))
{
// Check for block assignment. If it is of type void[], void[][], etc,
// '= null' is the only allowable block assignment (Bug 7493)
Expand All @@ -12163,9 +12227,12 @@ public:
return new ErrorExp();
}
}
else if (e1.op == TOKslice && (t2.ty == Tarray || t2.ty == Tsarray) && t2.nextOf().implicitConvTo(t1.nextOf()))
else if (e1.op == TOKslice &&
(t2.ty == Tarray || t2.ty == Tsarray) &&
t2.nextOf().implicitConvTo(t1.nextOf()))
{
// Check element-wise assignment.

/* If assigned elements number is known at compile time,
* check the mismatch.
*/
Expand All @@ -12188,21 +12255,36 @@ public:
return new ErrorExp();
}
}
if (op != TOKblit && (e2x.op == TOKslice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == TOKcast && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != TOKslice && e2x.isLvalue()))

if (op != TOKblit &&
(e2x.op == TOKslice && (cast(UnaExp)e2x).e1.isLvalue() ||
e2x.op == TOKcast && (cast(UnaExp)e2x).e1.isLvalue() ||
e2x.op != TOKslice && e2x.isLvalue()))
{
if (e1.checkPostblit(sc, t1.nextOf()))
return new ErrorExp();
}
if (0 && global.params.warnings && !global.gag && op == TOKassign && e2x.op != TOKslice && e2x.op != TOKassign && e2x.op != TOKarrayliteral && e2x.op != TOKstring && !(e2x.op == TOKadd || e2x.op == TOKmin || e2x.op == TOKmul || e2x.op == TOKdiv || e2x.op == TOKmod || e2x.op == TOKxor || e2x.op == TOKand || e2x.op == TOKor || e2x.op == TOKpow || e2x.op == TOKtilde || e2x.op == TOKneg))

if (0 && global.params.warnings && !global.gag && op == TOKassign &&
e2x.op != TOKslice && e2x.op != TOKassign &&
e2x.op != TOKarrayliteral && e2x.op != TOKstring &&
!(e2x.op == TOKadd || e2x.op == TOKmin ||
e2x.op == TOKmul || e2x.op == TOKdiv ||
e2x.op == TOKmod || e2x.op == TOKxor ||
e2x.op == TOKand || e2x.op == TOKor ||
e2x.op == TOKpow ||
e2x.op == TOKtilde || e2x.op == TOKneg))
{
const(char)* e1str = e1.toChars();
const(char)* e2str = e2x.toChars();
warning("explicit element-wise assignment %s = (%s)[] is better than %s = %s", e1str, e2str, e1str, e2str);
}

Type t2n = t2.nextOf();
Type t1n = t1.nextOf();
int offset;
if (t2n.equivalent(t1n) || t1n.isBaseOf(t2n, &offset) && offset == 0)
if (t2n.equivalent(t1n) ||
t1n.isBaseOf(t2n, &offset) && offset == 0)
{
/* Allow copy of distinct qualifier elements.
* eg.
Expand All @@ -12227,7 +12309,10 @@ public:
}
else
{
if (0 && global.params.warnings && !global.gag && op == TOKassign && t1.ty == Tarray && t2.ty == Tsarray && e2x.op != TOKslice && t2.implicitConvTo(t1))
if (0 && global.params.warnings && !global.gag && op == TOKassign &&
t1.ty == Tarray && t2.ty == Tsarray &&
e2x.op != TOKslice &&
t2.implicitConvTo(t1))
{
// Disallow ar[] = sa (Converted to ar[] = sa[])
// Disallow da = sa (Converted to da = sa[])
Expand All @@ -12245,33 +12330,41 @@ public:
return e2x;
e2 = e2x;
t2 = e2.type.toBasetype();

/* Look for array operations
*/
if ((t2.ty == Tarray || t2.ty == Tsarray) && isArrayOpValid(e2))
{
// Look for valid array operations
if (!(ismemset & 1) && e1.op == TOKslice && (isUnaArrayOp(e2.op) || isBinArrayOp(e2.op)))
if (!(ismemset & 1) &&
e1.op == TOKslice &&
(isUnaArrayOp(e2.op) || isBinArrayOp(e2.op)))
{
type = e1.type;
if (op == TOKconstruct) // Bugzilla 10282: tweak mutability of e1 element
e1.type = e1.type.nextOf().mutableOf().arrayOf();
return arrayOp(this, sc);
}

// Drop invalid array operations in e2
// d = a[] + b[], d = (a[] + b[])[0..2], etc
if (checkNonAssignmentArrayOp(e2, !(ismemset & 1) && op == TOKassign))
return new ErrorExp();

// Remains valid array assignments
// d = d[], d = [1,2,3], etc
}
if (e1.op == TOKvar && ((cast(VarExp)e1).var.storage_class & STCscope) && op == TOKassign)
if (e1.op == TOKvar &&
((cast(VarExp)e1).var.storage_class & STCscope) &&
op == TOKassign)
{
error("cannot rebind scope variables");
}
if (e1.op == TOKvar && (cast(VarExp)e1).var.ident == Id.ctfe)
{
error("cannot modify compiler-generated variable __ctfe");
}

type = e1.type;
assert(type);
return op == TOKassign ? reorderSettingAAElem(sc) : this;
Expand Down
2 changes: 2 additions & 0 deletions src/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,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 @@ -1817,6 +1818,7 @@ public:
else
{
exp = exp.optimize(WANTvalue);

/* Bugzilla 10789:
* If NRVO is not possible, all returned lvalues should call their postblits.
*/
Expand Down
92 changes: 75 additions & 17 deletions src/inline.d
Original file line number Diff line number Diff line change
Expand Up @@ -1735,13 +1735,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 @@ -1750,6 +1753,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
}
return false;
}

if (fd.semanticRun < PASSsemantic3 && !hdrscan)
{
if (!fd.fbody)
Expand All @@ -1761,6 +1765,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 @@ -1780,6 +1785,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
default:
assert(0);
}

switch (fd.inlining)
{
case PINLINEdefault:
Expand All @@ -1791,23 +1797,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 @@ -1816,10 +1832,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 @@ -1832,19 +1857,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 @@ -1858,10 +1887,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 @@ -1873,9 +1904,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 @@ -1902,10 +1935,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 @@ -1950,6 +1986,7 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,

if (asStatements)
as = new Statements();

VarDeclaration vret = null;
if (eret)
{
Expand All @@ -1967,38 +2004,41 @@ 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);
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);

auto 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)
Expand All @@ -2007,15 +2047,18 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
*/
ei.exp.op = TOKconstruct;
}

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 @@ -2031,8 +2074,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 @@ -2043,21 +2088,22 @@ 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);

auto 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
Expand All @@ -2066,8 +2112,10 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
//ve.type.print();
//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 @@ -2083,6 +2131,7 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
}
}
}

if (asStatements)
{
if (e)
Expand All @@ -2105,9 +2154,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 @@ -2128,25 +2179,32 @@ 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;

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);

//fprintf(stderr, "CallExp.inlineScan: e = "); e.print();
}
eresult = e;
}
//printf("%s.expandInline = { %s }\n", fd.toChars(), e.toChars());

// Need to reevaluate whether parent can now be inlined
// in expressions, as we might have inlined statements
parent.inlineStatusExp = ILSuninitialized;
Expand Down