212 changes: 159 additions & 53 deletions src/statement.d
Original file line number Diff line number Diff line change
Expand Up @@ -2023,7 +2023,11 @@ public:

override Statement syntaxCopy()
{
return new ForeachStatement(loc, op, Parameter.arraySyntaxCopy(parameters), aggr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc);
return new ForeachStatement(loc, op,
Parameter.arraySyntaxCopy(parameters),
aggr.syntaxCopy(),
_body ? _body.syntaxCopy() : null,
endloc);
}

override Statement semantic(Scope* sc)
Expand All @@ -2034,26 +2038,31 @@ public:
size_t dim = parameters.dim;
TypeAArray taa = null;
Dsymbol sapply = null;

Type tn = null;
Type tnv = null;

func = sc.func;
if (func.fes)
func = func.fes.func;

VarDeclaration vinit = null;
aggr = aggr.semantic(sc);
aggr = resolveProperties(sc, aggr);
aggr = aggr.optimize(WANTvalue);
if (aggr.op == TOKerror)
return new ErrorStatement();
Expression oaggr = aggr;
if (aggr.type && aggr.type.toBasetype().ty == Tstruct && aggr.op != TOKtype && !aggr.isLvalue())
if (aggr.type && aggr.type.toBasetype().ty == Tstruct &&
aggr.op != TOKtype && !aggr.isLvalue())
{
// Bugzilla 14653: Extend the life of rvalue aggregate till the end of foreach.
vinit = new VarDeclaration(loc, aggr.type, Identifier.generateId("__aggr"), new ExpInitializer(loc, aggr));
vinit.storage_class |= STCtemp;
vinit.semantic(sc);
aggr = new VarExp(aggr.loc, vinit);
}

if (!inferAggregate(this, sc, sapply))
{
const(char)* msg = "";
Expand All @@ -2064,7 +2073,9 @@ public:
error("invalid foreach aggregate %s%s", oaggr.toChars(), msg);
return new ErrorStatement();
}

Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors

/* Check for inference errors
*/
if (!inferApplyArgTypes(this, sc, sapply))
Expand All @@ -2081,11 +2092,14 @@ public:
{
int fvarargs; // ignored (opApply shouldn't take variadics)
Parameters* fparameters = fd.getParameters(&fvarargs);

if (Parameter.dim(fparameters) == 1)
{
// first param should be the callback function
Parameter fparam = Parameter.getNth(fparameters, 0);
if ((fparam.type.ty == Tpointer || fparam.type.ty == Tdelegate) && fparam.type.nextOf().ty == Tfunction)
if ((fparam.type.ty == Tpointer ||
fparam.type.ty == Tdelegate) &&
fparam.type.nextOf().ty == Tfunction)
{
TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
foreachParamCount = Parameter.dim(tf.parameters);
Expand All @@ -2094,31 +2108,38 @@ public:
}
}
}

//printf("dim = %d, parameters->dim = %d\n", dim, parameters->dim);
if (foundMismatch && dim != foreachParamCount)
{
const(char)* plural = foreachParamCount > 1 ? "s" : "";
error("cannot infer argument types, expected %d argument%s, not %d", foreachParamCount, plural, dim);
error("cannot infer argument types, expected %d argument%s, not %d",
foreachParamCount, plural, dim);
}
else
error("cannot uniquely infer foreach argument types");

return new ErrorStatement();
}

Type tab = aggr.type.toBasetype();

if (tab.ty == Ttuple) // don't generate new scope for tuple loops
{
if (dim < 1 || dim > 2)
{
error("only one (value) or two (key,value) arguments for tuple foreach");
return new ErrorStatement();
}

Type paramtype = (*parameters)[dim - 1].type;
if (paramtype)
{
paramtype = paramtype.semantic(loc, sc);
if (paramtype.ty == Terror)
return new ErrorStatement();
}

TypeTuple tuple = cast(TypeTuple)tab;
auto statements = new Statements();
//printf("aggr: op = %d, %s\n", aggr->op, aggr->toChars());
Expand Down Expand Up @@ -2146,6 +2167,7 @@ public:
t = Parameter.getNth(tuple.arguments, k).type;
Parameter p = (*parameters)[0];
auto st = new Statements();

if (dim == 2)
{
// Declare key
Expand Down Expand Up @@ -2179,7 +2201,8 @@ public:
p = (*parameters)[1]; // value
}
// Declare value
if (p.storageClass & (STCout | STClazy) || p.storageClass & STCref && !te)
if (p.storageClass & (STCout | STClazy) ||
p.storageClass & STCref && !te)
{
error("no storage class for value %s", p.ident.toChars());
return new ErrorStatement();
Expand All @@ -2197,9 +2220,10 @@ public:
ds = (cast(ScopeExp)e).sds;
else if (e.op == TOKfunction)
{
FuncExp fe = cast(FuncExp)e;
auto fe = cast(FuncExp)e;
ds = fe.td ? cast(Dsymbol)fe.td : fe.fd;
}

if (ds)
{
var = new AliasDeclaration(loc, p.ident, ds);
Expand Down Expand Up @@ -2232,7 +2256,10 @@ public:
auto v = new VarDeclaration(loc, p.type, p.ident, ie);
if (p.storageClass & STCref)
v.storage_class |= STCref | STCforeach;
if (e.isConst() || e.op == TOKstring || e.op == TOKstructliteral || e.op == TOKarrayliteral)
if (e.isConst() ||
e.op == TOKstring ||
e.op == TOKstructliteral ||
e.op == TOKarrayliteral)
{
if (v.storage_class & STCref)
{
Expand All @@ -2255,11 +2282,13 @@ public:
}
}
st.push(new ExpStatement(loc, var));

st.push(_body.syntaxCopy());
s = new CompoundStatement(loc, st);
s = new ScopeStatement(loc, s);
statements.push(s);
}

s = new UnrolledLoopStatement(loc, statements);
if (LabelStatement ls = checkLabeledLoop(sc, this))
ls.gotoTarget = s;
Expand All @@ -2270,22 +2299,27 @@ public:
s = s.semantic(sc);
return s;
}

sym = new ScopeDsymbol();
sym.parent = sc.scopesym;
sc = sc.push(sym);

sc.noctor++;

switch (tab.ty)
{
case Tarray:
case Tsarray:
{
if (checkForArgTypes())
return this;

if (dim < 1 || dim > 2)
{
error("only one or two arguments for array foreach");
goto Lerror2;
}

/* Look for special case of parsing char types out of char type
* array.
*/
Expand All @@ -2297,7 +2331,8 @@ public:
p.type = p.type.semantic(loc, sc);
p.type = p.type.addStorageClass(p.storageClass);
tnv = p.type.toBasetype();
if (tnv.ty != tn.ty && (tnv.ty == Tchar || tnv.ty == Twchar || tnv.ty == Tdchar))
if (tnv.ty != tn.ty &&
(tnv.ty == Tchar || tnv.ty == Twchar || tnv.ty == Tdchar))
{
if (p.storageClass & STCref)
{
Expand All @@ -2316,25 +2351,29 @@ public:
goto Lapply;
}
}

foreach (i; 0 .. dim)
{
// Declare parameterss
Parameter p = (*parameters)[i];
p.type = p.type.semantic(loc, sc);
p.type = p.type.addStorageClass(p.storageClass);
VarDeclaration var;

if (dim == 2 && i == 0)
{
var = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
var.storage_class |= STCtemp | STCforeach;
if (var.storage_class & (STCref | STCout))
var.storage_class |= STCnodtor;

key = var;
if (p.storageClass & STCref)
{
if (var.type.constConv(p.type) <= MATCHnomatch)
{
error("key type mismatch, %s to ref %s", var.type.toChars(), p.type.toChars());
error("key type mismatch, %s to ref %s",
var.type.toChars(), p.type.toChars());
goto Lerror2;
}
}
Expand All @@ -2344,7 +2383,8 @@ public:
IntRange dimrange = getIntRange(ta.dim);
if (!IntRange.fromType(var.type).contains(dimrange))
{
error("index type '%s' cannot cover index range 0..%llu", p.type.toChars(), ta.dim.toInteger());
error("index type '%s' cannot cover index range 0..%llu",
p.type.toChars(), ta.dim.toInteger());
goto Lerror2;
}
key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
Expand All @@ -2357,20 +2397,24 @@ public:
var.storage_class |= p.storageClass & (STCin | STCout | STCref | STC_TYPECTOR);
if (var.storage_class & (STCref | STCout))
var.storage_class |= STCnodtor;

value = var;
if (var.storage_class & STCref)
{
if (aggr.checkModifiable(sc, 1) == 2)
var.storage_class |= STCctorinit;

Type t = tab.nextOf();
if (t.constConv(p.type) <= MATCHnomatch)
{
error("argument type mismatch, %s to ref %s", t.toChars(), p.type.toChars());
error("argument type mismatch, %s to ref %s",
t.toChars(), p.type.toChars());
goto Lerror2;
}
}
}
}

/* Convert to a ForStatement
* foreach (key, value; a) body =>
* for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
Expand All @@ -2383,18 +2427,22 @@ public:
Identifier id = Identifier.generateId("__r");
auto ie = new ExpInitializer(loc, new SliceExp(loc, aggr, null, null));
VarDeclaration tmp;
if (aggr.op == TOKarrayliteral && !((*parameters)[dim - 1].storageClass & STCref))
if (aggr.op == TOKarrayliteral &&
!((*parameters)[dim - 1].storageClass & STCref))
{
ArrayLiteralExp ale = cast(ArrayLiteralExp)aggr;
auto ale = cast(ArrayLiteralExp)aggr;
size_t edim = ale.elements ? ale.elements.dim : 0;
aggr.type = tab.nextOf().sarrayOf(edim);

// for (T[edim] tmp = a, ...)
tmp = new VarDeclaration(loc, aggr.type, id, ie);
}
else
tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
tmp.storage_class |= STCtemp;

Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);

if (!key)
{
Identifier idkey = Identifier.generateId("__key");
Expand All @@ -2405,12 +2453,14 @@ public:
key._init = new ExpInitializer(loc, tmp_length);
else
key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, key.type));

auto cs = new Statements();
if (vinit)
cs.push(new ExpStatement(loc, vinit));
cs.push(new ExpStatement(loc, tmp));
cs.push(new ExpStatement(loc, key));
Statement forinit = new CompoundDeclarationStatement(loc, cs);

Expression cond;
if (op == TOKforeach_reverse)
{
Expand All @@ -2422,15 +2472,18 @@ public:
// key < tmp.length
cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), tmp_length);
}

Expression increment = null;
if (op == TOKforeach)
{
// key += 1
increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(loc, 1, key.type));
}

// T value = tmp[key];
value._init = new ExpInitializer(loc, new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, key)));
Statement ds = new ExpStatement(loc, value);

if (dim == 2)
{
Parameter p = (*parameters)[0];
Expand All @@ -2455,6 +2508,7 @@ public:
}
}
_body = new CompoundStatement(loc, ds, _body);

s = new ForStatement(loc, forinit, cond, increment, _body, endloc);
if (LabelStatement ls = checkLabeledLoop(sc, this))
ls.gotoTarget = s;
Expand All @@ -2466,13 +2520,15 @@ public:
warning("cannot use foreach_reverse with an associative array");
if (checkForArgTypes())
return this;

taa = cast(TypeAArray)tab;
if (dim < 1 || dim > 2)
{
error("only one or two arguments for associative array foreach");
goto Lerror2;
}
goto Lapply;

case Tclass:
case Tstruct:
/* Prefer using opApply, if it exists
Expand All @@ -2489,7 +2545,9 @@ public:
* ...
* }
*/
AggregateDeclaration ad = (tab.ty == Tclass) ? cast(AggregateDeclaration)(cast(TypeClass)tab).sym : cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
auto ad = (tab.ty == Tclass) ?
cast(AggregateDeclaration)(cast(TypeClass)tab).sym :
cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
Identifier idfront;
Identifier idpopFront;
if (op == TOKforeach)
Expand All @@ -2502,9 +2560,10 @@ public:
idfront = Id.Fback;
idpopFront = Id.FpopBack;
}
Dsymbol sfront = ad.search(Loc(), idfront);
auto sfront = ad.search(Loc(), idfront);
if (!sfront)
goto Lapply;

/* Generate a temporary __r and initialize it with the aggregate.
*/
VarDeclaration r;
Expand All @@ -2516,20 +2575,23 @@ public:
}
else
{
Identifier rid = Identifier.generateId("__r");
auto rid = Identifier.generateId("__r");
r = new VarDeclaration(loc, null, rid, new ExpInitializer(loc, aggr));
r.storage_class |= STCtemp;
_init = new ExpStatement(loc, r);
if (vinit)
_init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
}

// !__r.empty
Expression e = new VarExp(loc, r);
e = new DotIdExp(loc, e, Id.Fempty);
Expression condition = new NotExp(loc, e);

// __r.idpopFront()
e = new VarExp(loc, r);
Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));

/* Declaration statement for e:
* auto e = __r.idfront;
*/
Expand All @@ -2538,33 +2600,54 @@ public:
Statement makeargs, forbody;
if (dim == 1)
{
Parameter p = (*parameters)[0];
auto p = (*parameters)[0];
auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
ve.storage_class |= STCforeach;
ve.storage_class |= p.storageClass & (STCin | STCout | STCref | STC_TYPECTOR);

makeargs = new ExpStatement(loc, ve);
}
else
{
Identifier id = Identifier.generateId("__front");
auto id = Identifier.generateId("__front");
auto ei = new ExpInitializer(loc, einit);
auto vd = new VarDeclaration(loc, null, id, ei);
vd.storage_class |= STCtemp | STCctfe | STCref | STCforeach;

makeargs = new ExpStatement(loc, vd);
Declaration d = sfront.isDeclaration();
if (FuncDeclaration f = d.isFuncDeclaration())

Type tfront;
if (auto fd = sfront.isFuncDeclaration())
{
if (!f.functionSemantic())
if (!fd.functionSemantic())
goto Lrangeerr;
tfront = fd.type;
}
Expression ve = new VarExp(loc, vd);
ve.type = d.type;
if (ve.type.toBasetype().ty == Tfunction)
ve.type = ve.type.toBasetype().nextOf();
if (!ve.type || ve.type.ty == Terror)
else if (auto td = sfront.isTemplateDeclaration())
{
Expressions a;
if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, 1))
tfront = f.type;
}
else if (auto d = sfront.isDeclaration())
{
tfront = d.type;
}
if (!tfront || tfront.ty == Terror)
goto Lrangeerr;
if (tfront.toBasetype().ty == Tfunction)
tfront = tfront.toBasetype().nextOf();
if (tfront.ty == Tvoid)
{
error("%s.front is void and has no value", oaggr.toChars());
goto Lerror2;
}
// Resolve inout qualifier of front type
ve.type = ve.type.substWildTo(tab.mod);
tfront = tfront.substWildTo(tab.mod);

Expression ve = new VarExp(loc, vd);
ve.type = tfront;

auto exps = new Expressions();
exps.push(ve);
int pos = 0;
Expand All @@ -2577,31 +2660,39 @@ public:
if (exps.dim != dim)
{
const(char)* plural = exps.dim > 1 ? "s" : "";
error("cannot infer argument types, expected %d argument%s, not %d", exps.dim, plural, dim);
error("cannot infer argument types, expected %d argument%s, not %d",
exps.dim, plural, dim);
goto Lerror2;
}

foreach (i; 0 .. dim)
{
Parameter p = (*parameters)[i];
Expression exp = (*exps)[i];
auto p = (*parameters)[i];
auto exp = (*exps)[i];
version (none)
{
printf("[%d] p = %s %s, exp = %s %s\n", i, p.type ? p.type.toChars() : "?", p.ident.toChars(), exp.type.toChars(), exp.toChars());
printf("[%d] p = %s %s, exp = %s %s\n", i,
p.type ? p.type.toChars() : "?", p.ident.toChars(),
exp.type.toChars(), exp.toChars());
}
if (!p.type)
p.type = exp.type;
p.type = p.type.addStorageClass(p.storageClass).semantic(loc, sc);
if (!exp.implicitConvTo(p.type))
goto Lrangeerr;

auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
var.storage_class |= STCctfe | STCref | STCforeach;
makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
}
}

forbody = new CompoundStatement(loc, makeargs, this._body);

s = new ForStatement(loc, _init, condition, increment, forbody, endloc);
if (LabelStatement ls = checkLabeledLoop(sc, this))
if (auto ls = checkLabeledLoop(sc, this))
ls.gotoTarget = s;

version (none)
{
printf("init: %s\n", _init.toChars());
Expand All @@ -2611,6 +2702,7 @@ public:
}
s = s.semantic(sc);
break;

Lrangeerr:
error("cannot infer argument types");
goto Lerror2;
Expand All @@ -2625,6 +2717,7 @@ public:
_body = _body.semanticNoScope(sc);
return this;
}

TypeFunction tfld = null;
if (sapply)
{
Expand Down Expand Up @@ -2652,6 +2745,7 @@ public:
}
}
}

/* Turn body into the function literal:
* int delegate(ref T param) { body }
*/
Expand All @@ -2661,6 +2755,7 @@ public:
Parameter p = (*parameters)[i];
StorageClass stc = STCref;
Identifier id;

p.type = p.type.semantic(loc, sc);
p.type = p.type.addStorageClass(p.storageClass);
if (tfld)
Expand Down Expand Up @@ -2691,6 +2786,7 @@ public:
// a reference.
LcopyArg:
id = Identifier.generateId("__applyArg", cast(int)i);

Initializer ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id));
auto v = new VarDeclaration(Loc(), p.type, p.ident, ie);
v.storage_class |= STCtemp;
Expand All @@ -2709,6 +2805,7 @@ public:
Expression flde = new FuncExp(loc, fld);
flde = flde.semantic(sc);
fld.tookAddressOf = 0;

// Resolve any forward referenced goto's
foreach (i; 0 .. gotos.dim)
{
Expand All @@ -2721,6 +2818,7 @@ public:
(*gotos)[i].statement = s;
}
}

Expression e = null;
Expression ec;
if (vinit)
Expand All @@ -2730,6 +2828,7 @@ public:
if (e.op == TOKerror)
goto Lerror2;
}

if (taa)
{
// Check types
Expand All @@ -2741,7 +2840,8 @@ public:
Type ti = (isRef ? taa.index.addMod(MODconst) : taa.index);
if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
{
error("foreach: index must be type %s, not %s", ti.toChars(), ta.toChars());
error("foreach: index must be type %s, not %s",
ti.toChars(), ta.toChars());
goto Lerror2;
}
p = (*parameters)[1];
Expand All @@ -2751,9 +2851,11 @@ public:
Type taav = taa.nextOf();
if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
{
error("foreach: value must be type %s, not %s", taav.toChars(), ta.toChars());
error("foreach: value must be type %s, not %s",
taav.toChars(), ta.toChars());
goto Lerror2;
}

/* Call:
* extern(C) int _aaApply(void*, in size_t, int delegate(void*))
* _aaApply(aggr, keysize, flde)
Expand All @@ -2764,6 +2866,7 @@ public:
static __gshared const(char)** name = ["_aaApply", "_aaApply2"];
static __gshared FuncDeclaration* fdapply = [null, null];
static __gshared TypeDelegate* fldeTy = [null, null];

ubyte i = (dim == 2 ? 1 : 0);
if (!fdapply[i])
{
Expand All @@ -2778,6 +2881,7 @@ public:
params.push(new Parameter(0, fldeTy[i], null, null));
fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, name[i]);
}

auto exps = new Expressions();
exps.push(aggr);
size_t keysize = cast(size_t)taa.index.size();
Expand All @@ -2799,41 +2903,37 @@ public:
/* Call:
* _aApply(aggr, flde)
*/
static __gshared const(char)** fntab = ["cc", "cw", "cd", "wc", "cc", "wd", "dc", "dw", "dd"];
static __gshared const(char)** fntab =
[
"cc", "cw", "cd",
"wc", "cc", "wd",
"dc", "dw", "dd"
];

const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
char[BUFFER_LEN] fdname;
int flag;

switch (tn.ty)
{
case Tchar:
flag = 0;
break;
case Twchar:
flag = 3;
break;
case Tdchar:
flag = 6;
break;
case Tchar: flag = 0; break;
case Twchar: flag = 3; break;
case Tdchar: flag = 6; break;
default:
assert(0);
}
switch (tnv.ty)
{
case Tchar:
flag += 0;
break;
case Twchar:
flag += 1;
break;
case Tdchar:
flag += 2;
break;
case Tchar: flag += 0; break;
case Twchar: flag += 1; break;
case Tdchar: flag += 2; break;
default:
assert(0);
}
const(char)* r = (op == TOKforeach_reverse) ? "R" : "";
int j = sprintf(fdname.ptr, "_aApply%s%.*s%llu", r, 2, fntab[flag], cast(ulong)dim);
assert(j < BUFFER_LEN);

FuncDeclaration fdapply;
TypeDelegate dgty;
params = new Parameters();
Expand All @@ -2845,6 +2945,7 @@ public:
dgty = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINKd));
params.push(new Parameter(0, dgty, null, null));
fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);

if (tab.ty == Tsarray)
aggr = aggr.castTo(sc, tn.arrayOf());
// paint delegate argument to the type runtime expects
Expand Down Expand Up @@ -2896,6 +2997,7 @@ public:
}
}
e = Expression.combine(e, ec);

if (!cases.dim)
{
// Easy case, a clean exit from the loop
Expand All @@ -2907,16 +3009,19 @@ public:
// Construct a switch statement around the return value
// of the apply function.
auto a = new Statements();

// default: break; takes care of cases 0 and 1
s = new BreakStatement(Loc(), null);
s = new DefaultStatement(Loc(), s);
a.push(s);

// cases 2...
foreach (i, c; *cases)
{
s = new CaseStatement(Loc(), new IntegerExp(i + 2), c);
a.push(s);
}

s = new CompoundStatement(loc, a);
s = new SwitchStatement(loc, e, s, false);
}
Expand All @@ -2927,6 +3032,7 @@ public:
Lerror2:
s = new ErrorStatement();
break;

default:
error("foreach: %s is not an aggregate type", aggr.type.toChars());
goto Lerror2;
Expand Down
26 changes: 26 additions & 0 deletions test/fail_compilation/ice15441.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice15441.d(22): Error: s1.front is void and has no value
fail_compilation/ice15441.d(25): Error: cannot infer argument types, expected 1 argument, not 2
---
*/

struct S1
{
auto front()() {}
}

struct S2
{
auto front()() { return 1; }
}

void main()
{
S1 s1;
foreach (p, e; s1) {}

S2 s2;
foreach (p, e; s2) {}
}