340 changes: 144 additions & 196 deletions src/expression.c

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ bool arrayExpressionSemantic(Expressions *exps, Scope *sc);
TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s);
Expression *valueNoDtor(Expression *e);
int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1);
Expression *resolveAliasThis(Scope *sc, Expression *e);
Expression *resolveAliasThis(Scope *sc, Expression *e, bool gag = false);
Expression *callCpCtor(Scope *sc, Expression *e);
Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0);
Expression *resolveOpDollar(Scope *sc, SliceExp *se, Expression **pe0);
Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0);
Expression *integralPromotions(Expression *e, Scope *sc);
void discardValue(Expression *e);
bool isTrivialExp(Expression *e);
Expand Down Expand Up @@ -1019,6 +1019,7 @@ class SliceExp : public UnaExp
bool upperIsInBounds; // true if upr <= e1.length
bool lowerIsLessThanUpper; // true if lwr <= upr

SliceExp(Loc loc, Expression *e1, IntervalExp *ie);
SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr);
Expression *syntaxCopy();
Expression *semantic(Scope *sc);
Expand Down Expand Up @@ -1082,7 +1083,8 @@ class ArrayExp : public UnaExp
size_t currentDimension; // for opDollar
VarDeclaration *lengthVar;

ArrayExp(Loc loc, Expression *e1, Expressions *arguments);
ArrayExp(Loc loc, Expression *e1, Expression *index = NULL);
ArrayExp(Loc loc, Expression *e1, Expressions *args);
Expression *syntaxCopy();
Expression *semantic(Scope *sc);
bool isLvalue();
Expand Down
52 changes: 38 additions & 14 deletions src/func.c
Original file line number Diff line number Diff line change
Expand Up @@ -4120,20 +4120,50 @@ const char *FuncDeclaration::kind()
bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
if (parent && parent != sc->parent && this->isNested() &&
this->ident != Id::require && this->ident != Id::ensure)
if (!parent || parent == sc->parent)
return false;
if (ident == Id::require || ident == Id::ensure)
return false;
if (!isThis() && !isNested())
return false;

// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
if (!fdthis)
return false; // out of function scope

Dsymbol *p = toParent2();

// Function literals from fdthis to p must be delegates
// TODO: here is similar to checkFrameAccess.
for (Dsymbol *s = fdthis; s && s != p; s = s->toParent2())
{
// The function that this function is in
FuncDeclaration *fdv2 = toParent2()->isFuncDeclaration();
// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;

if (FuncDeclaration *fd = s->isFuncDeclaration())
{
if (!fd->isThis() && !fd->isNested())
break;
}
if (AggregateDeclaration *ad2 = s->isAggregateDeclaration())
{
if (ad2->storage_class & STCstatic)
break;
}
}

// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
if (isNested())
{
// The function that this function is in
FuncDeclaration *fdv2 = p->isFuncDeclaration();

//printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars());
//printf("fdv2 = %s in [%s]\n", fdv2->toChars(), fdv2->loc.toChars());
//printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars());

if (fdv2 && fdthis && fdv2 != fdthis)
if (fdv2 && fdv2 != fdthis)
{
// Add this function to the list of those which called us
if (fdthis != this)
Expand All @@ -4153,7 +4183,7 @@ bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
}
}

FuncDeclaration *fdv = toParent2()->isFuncDeclaration();
FuncDeclaration *fdv = p->isFuncDeclaration();
if (fdv && fdthis && fdv != fdthis)
{
int lv = fdthis->getLevel(loc, sc, fdv);
Expand All @@ -4165,12 +4195,6 @@ bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
return false; // same level call

// Uplevel call

// BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does

// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;
}
}
return false;
Expand Down
63 changes: 63 additions & 0 deletions src/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,26 @@ Expression *doInline(Expression *e, InlineDoState *ids)
ne->newargs = arrayExpressiondoInline(e->newargs);
ne->arguments = arrayExpressiondoInline(e->arguments);
result = ne;

semanticTypeInfo(NULL, e->type);
}

void visit(DeleteExp *e)
{
visit((UnaExp *)e);

Type *tb = e->e1->type->toBasetype();
if (tb->ty == Tarray)
{
Type *tv = tb->nextOf()->baseElemOf();
if (tv->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (sd->dtor)
semanticTypeInfo(NULL, ts);
}
}
}

void visit(UnaExp *e)
Expand Down Expand Up @@ -957,6 +977,37 @@ Expression *doInline(Expression *e, InlineDoState *ids)
result = ce;
}

void visit(AssignExp *e)
{
visit((BinExp *)e);

if (e->e1->op == TOKarraylength)
{
ArrayLengthExp *ale = (ArrayLengthExp *)e->e1;
Type *tn = ale->e1->type->toBasetype()->nextOf();
semanticTypeInfo(NULL, tn);
}
}

void visit(EqualExp *e)
{
visit((BinExp *)e);

Type *t1 = e->e1->type->toBasetype();
if (t1->ty == Tarray || t1->ty == Tsarray)
{
Type *t = t1->nextOf()->toBasetype();
while (t->toBasetype()->nextOf())
t = t->nextOf()->toBasetype();
if (t->ty == Tstruct)
semanticTypeInfo(NULL, t);
}
else if (t1->ty == Taarray)
{
semanticTypeInfo(NULL, t1);
}
}

void visit(IndexExp *e)
{
IndexExp *are = (IndexExp *)e->copy();
Expand Down Expand Up @@ -1042,6 +1093,8 @@ Expression *doInline(Expression *e, InlineDoState *ids)

ce->elements = arrayExpressiondoInline(e->elements);
result = ce;

semanticTypeInfo(NULL, e->type);
}

void visit(AssocArrayLiteralExp *e)
Expand All @@ -1051,6 +1104,8 @@ Expression *doInline(Expression *e, InlineDoState *ids)
ce->keys = arrayExpressiondoInline(e->keys);
ce->values = arrayExpressiondoInline(e->values);
result = ce;

semanticTypeInfo(NULL, e->type);
}

void visit(StructLiteralExp *e)
Expand Down Expand Up @@ -1838,6 +1893,14 @@ static Expression *expandInline(FuncDeclaration *fd, FuncDeclaration *parent,
ids.parent = parent;
ids.fd = fd;

// When the function is actually expanded
if (TemplateInstance *ti = fd->isInstantiated())
{
// change ti to non-speculative instance
if (!ti->minst)
ti->minst = ti->tempdecl->getModule();
}

if (ps)
as = new Statements();

Expand Down
4 changes: 3 additions & 1 deletion src/magicport.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
"aggregate",
"argtypes",
"arraytypes",
"backend",
"clone",
"declaration",
"dmodule",
Expand Down Expand Up @@ -2736,7 +2737,7 @@
"function needDirectEq",
"function extractOpDollarSideEffect",
"function resolveOpDollarScope*ArrayExp*Expression**",
"function resolveOpDollarScope*SliceExp*Expression**",
"function resolveOpDollarScope*ArrayExp*IntervalExp*Expression**",
"enum CtfeGoal",
"enum OwnedBy",
"variable WANTvalue",
Expand Down Expand Up @@ -2889,6 +2890,7 @@
"imports" :
[
"aggregate",
"aliasthis",
"arraytypes",
"core.stdc.stdio",
"core.stdc.string",
Expand Down
239 changes: 129 additions & 110 deletions src/mtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -3905,12 +3905,9 @@ void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol
if (*pe)
{
// It's really an index expression
Expressions *exps = new Expressions();
exps->setDim(1);
(*exps)[0] = dim;
if (Dsymbol *s = getDsymbol(*pe))
*pe = new DsymbolExp(loc, s, 1);
*pe = new ArrayExp(loc, *pe, exps);
*pe = new ArrayExp(loc, *pe, dim);
}
else if (*ps)
{
Expand Down Expand Up @@ -4283,10 +4280,7 @@ Expression *TypeSArray::toExpression()
{
Expression *e = next->toExpression();
if (e)
{ Expressions *arguments = new Expressions();
arguments->push(dim);
e = new ArrayExp(dim->loc, e, arguments);
}
e = new ArrayExp(dim->loc, e, dim);
return e;
}

Expand Down Expand Up @@ -4383,7 +4377,7 @@ void TypeDArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol
// It's really a slice expression
if (Dsymbol *s = getDsymbol(*pe))
*pe = new DsymbolExp(loc, s, 1);
*pe = new SliceExp(loc, *pe, NULL, NULL);
*pe = new ArrayExp(loc, *pe);
}
else if (*ps)
{
Expand Down Expand Up @@ -4826,11 +4820,7 @@ Expression *TypeAArray::toExpression()
{
Expression *ei = index->toExpression();
if (ei)
{
Expressions *arguments = new Expressions();
arguments->push(ei);
return new ArrayExp(loc, e, arguments);
}
return new ArrayExp(loc, e, ei);
}
return NULL;
}
Expand Down Expand Up @@ -6401,45 +6391,122 @@ d_uns64 TypeQualified::size(Loc loc)
}

/*************************************
* Resolve a TypeTuple index.
* Resolve a tuple index.
*/
bool TypeQualified::resolveTypeTupleIndex(Loc loc, Scope *sc, Dsymbol **s, Type **pt, Dsymbol **ps, RootObject *id, Expression *indexExpr)
void TypeQualified::resolveTupleIndex(Loc loc, Scope *sc, Dsymbol *s,
Expression **pe, Type **pt, Dsymbol **ps, RootObject *oindex)
{
TupleDeclaration *td = (*s)->isTupleDeclaration();
*pt = NULL;
*ps = NULL;
*pe = NULL;

TupleDeclaration *td = s->isTupleDeclaration();

Expression *eindex = isExpression(oindex);
Type *tindex = isType(oindex);
Dsymbol *sindex = isDsymbol(oindex);

if (!td)
{
error(loc, "expected TypeTuple when indexing ('[%s]'), got '%s'.",
id->toChars(), (*s)->toChars());
// It's really an index expression
if (tindex)
eindex = new TypeExp(loc, tindex);
else if (sindex)
eindex = new DsymbolExp(loc, sindex);
Expression *e = new IndexExp(loc, new DsymbolExp(loc, s), eindex);
e = e->semantic(sc);
if (e->op == TOKerror)
*pt = Type::terror;
else if (e->op == TOKtype)
*pt = ((TypeExp *)e)->type;
else
*pe = e;
return;
}

// Convert oindex to Expression, then try to resolve to constant.
if (tindex)
tindex->resolve(loc, sc, &eindex, &tindex, &sindex);
if (sindex)
eindex = new DsymbolExp(loc, sindex);
if (!eindex)
{
::error(loc, "index is %s not an expression", oindex->toChars());
*pt = Type::terror;
return false;
return;
}
sc = sc->startCTFE();
indexExpr = indexExpr->semantic(sc);
eindex = eindex->semantic(sc);
sc = sc->endCTFE();

indexExpr = indexExpr->ctfeInterpret();
const uinteger_t d = indexExpr->toUInteger();
eindex = eindex->ctfeInterpret();
if (eindex->op == TOKerror)
{
*pt = Type::terror;
return;
}

const uinteger_t d = eindex->toUInteger();
if (d >= td->objects->dim)
{
error(loc, "tuple index %llu exceeds length %u", d, td->objects->dim);
::error(loc, "tuple index %llu exceeds length %u", d, td->objects->dim);
*pt = Type::terror;
return false;
return;
}

RootObject *o = (*td->objects)[(size_t)d];
if (o->dyncast() == DYNCAST_TYPE)
*pt = isType(o);
*ps = isDsymbol(o);
*pe = isExpression(o);

if (*pt)
*pt = (*pt)->semantic(loc, sc);
}

void TypeQualified::resolveExprType(Loc loc, Scope *sc,
Expression *e, size_t i, Expression **pe, Type **pt)
{
//printf("resolveExprType(e = %s %s, type = %s)\n", Token::toChars(e->op), e->toChars(), e->type->toChars());

e = e->semantic(sc);

for (; i < idents.dim; i++)
{
*ps = NULL;
*pt = ((Type *)o)->addMod(this->mod);
*s = (*pt)->toDsymbol(sc)->toAlias();
if (e->op == TOKerror)
break;

RootObject *id = idents[i];
//printf("e: '%s', id: '%s', type = %s\n", e->toChars(), id->toChars(), e->type->toChars());
if (id->dyncast() == DYNCAST_IDENTIFIER)
{
DotIdExp *die = new DotIdExp(e->loc, e, (Identifier *)id);
e = die->semanticY(sc, 0);
}
else if (id->dyncast() == DYNCAST_TYPE) // Bugzilla 1215
{
e = new IndexExp(loc, e, new TypeExp(loc, (Type *)id));
e = e->semantic(sc);
}
else if (id->dyncast() == DYNCAST_EXPRESSION) // Bugzilla 1215
{
e = new IndexExp(loc, e, (Expression *)id);
e = e->semantic(sc);
}
else
{
assert(id->dyncast() == DYNCAST_DSYMBOL);
TemplateInstance *ti = ((Dsymbol *)id)->isTemplateInstance();
assert(ti);
DotTemplateInstanceExp *dte = new DotTemplateInstanceExp(e->loc, e, ti->name, ti->tiargs);
e = dte->semanticY(sc, 0);
}
}
if (e->op == TOKerror)
*pt = Type::terror;
else if (e->op == TOKtype)
*pt = e->type;
else
{
assert(o->dyncast() == DYNCAST_DSYMBOL);
*ps = (Dsymbol *)o;
*s = (*ps)->toAlias();
}
return true;
*pe = e;
}

/*************************************
Expand All @@ -6449,7 +6516,6 @@ bool TypeQualified::resolveTypeTupleIndex(Loc loc, Scope *sc, Dsymbol **s, Type
* if expression, *pe is set
* if type, *pt is set
*/

void TypeQualified::resolveHelper(Loc loc, Scope *sc,
Dsymbol *s, Dsymbol *scopesym,
Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
Expand All @@ -6471,43 +6537,26 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc,
for (size_t i = 0; i < idents.dim; i++)
{
RootObject *id = idents[i];
if (id->dyncast() == DYNCAST_EXPRESSION)

if (id->dyncast() == DYNCAST_EXPRESSION ||
id->dyncast() == DYNCAST_TYPE)
{
if (!resolveTypeTupleIndex(loc, sc, &s, pt, ps, id, (Expression *)id))
Type *tx;
Expression *ex;
Dsymbol *sx;
resolveTupleIndex(loc, sc, s, &ex, &tx, &sx, id);
if (sx)
{
return;
s = sx->toAlias();
continue;
}
continue;
if (tx)
ex = new TypeExp(loc, tx);
assert(ex);
resolveExprType(loc, sc, ex, i + 1, pe, pt);
return;
}
else if (id->dyncast() == DYNCAST_TYPE)
{
Type *index = (Type *)id;
Expression *expr = NULL;
Type *t = NULL;
Dsymbol *sym = NULL;

index->resolve(loc, sc, &expr, &t, &sym);
if (expr)
{
if (!resolveTypeTupleIndex(loc, sc, &s, pt, ps, id, expr))
{
return;
}
}
else if (t)
{
index->error(loc, "Expected an expression as index, got a type (%s)", t->toChars());
*pt = Type::terror;
return;
}
else
{
index->error(loc, "index is not a an expression");
*pt = Type::terror;
return;
}
continue;
}
Type *t = s->getType(); // type symbol, type alias, or type tuple?
unsigned errorsave = global.errors;
Dsymbol *sm = s->searchX(loc, sc, id);
Expand Down Expand Up @@ -6562,31 +6611,9 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc,
e = new DsymbolExp(loc, s);
else
e = new VarExp(loc, s->isDeclaration());
e = e->semantic(sc);
for (; i < idents.dim; i++)
{
RootObject *id2 = idents[i];
//printf("e: '%s', id: '%s', type = %s\n", e->toChars(), id2->toChars(), e->type->toChars());
if (id2->dyncast() == DYNCAST_IDENTIFIER)
{
DotIdExp *die = new DotIdExp(e->loc, e, (Identifier *)id2);
e = die->semanticY(sc, 0);
}
else
{
assert(id2->dyncast() == DYNCAST_DSYMBOL);
TemplateInstance *ti = ((Dsymbol *)id2)->isTemplateInstance();
assert(ti);
DotTemplateInstanceExp *dte = new DotTemplateInstanceExp(e->loc, e, ti->name, ti->tiargs);
e = dte->semanticY(sc, 0);
}
}
if (e->op == TOKtype)
*pt = e->type;
else if (e->op == TOKerror)
*pt = Type::terror;
else
*pe = e;

resolveExprType(loc, sc, e, i, pe, pt);
return;
}
else
{
Expand Down Expand Up @@ -6812,23 +6839,15 @@ Dsymbol *TypeIdentifier::toDsymbol(Scope *sc)
//printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
if (!sc)
return NULL;
//printf("ident = '%s'\n", ident->toChars());

Dsymbol *scopesym;
Dsymbol *s = sc->search(loc, ident, &scopesym);
if (s)
{
for (size_t i = 0; i < idents.dim; i++)
{
RootObject *id = idents[i];
s = s->searchX(loc, sc, id);
if (!s) // failed to find a symbol
{
//printf("\tdidn't find a symbol\n");
break;
}
}
}
Type *t;
Expression *e;
Dsymbol *s;

resolve(loc, sc, &e, &t, &s);
if (t && t->ty != Tident)
s = t->toDsymbol(sc);

return s;
}

Expand Down Expand Up @@ -8905,7 +8924,7 @@ void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol
// It's really a slice expression
if (Dsymbol *s = getDsymbol(*pe))
*pe = new DsymbolExp(loc, s, 1);
*pe = new SliceExp(loc, *pe, lwr, upr);
*pe = new ArrayExp(loc, *pe, new IntervalExp(loc, lwr, upr));
}
else if (*ps)
{
Expand Down
9 changes: 6 additions & 3 deletions src/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,12 +673,15 @@ class TypeQualified : public Type
void addInst(TemplateInstance *inst);
void addIndex(RootObject *expr);
d_uns64 size(Loc loc);

void resolveTupleIndex(Loc loc, Scope *sc, Dsymbol *s,
Expression **pe, Type **pt, Dsymbol **ps, RootObject *oindex);
void resolveExprType(Loc loc, Scope *sc, Expression *e, size_t i,
Expression **pe, Type **pt);
void resolveHelper(Loc loc, Scope *sc, Dsymbol *s, Dsymbol *scopesym,
Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
void accept(Visitor *v) { v->visit(this); }

private:
bool resolveTypeTupleIndex(Loc loc, Scope *sc, Dsymbol **s, Type **pt, Dsymbol **ps, RootObject *id, Expression *indexExpr);
void accept(Visitor *v) { v->visit(this); }
};

class TypeIdentifier : public TypeQualified
Expand Down
7 changes: 1 addition & 6 deletions src/nogc.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,8 @@ class NOGCVisitor : public StoppableVisitor
VarDeclaration *v = e->declaration->isVarDeclaration();
if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->init)
{
if (v->init->isVoidInitializer())
if (ExpInitializer *ei = v->init->isExpInitializer())
{
}
else
{
ExpInitializer *ei = v->init->isExpInitializer();
assert(ei);
doCond(ei->exp);
}
}
Expand Down
523 changes: 247 additions & 276 deletions src/opover.c

Large diffs are not rendered by default.

51 changes: 38 additions & 13 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -3028,7 +3028,7 @@ Type *Parser::parseType(Identifier **pident, TemplateParameters **ptpl)
return t;
}

Type *Parser::parseBasicType()
Type *Parser::parseBasicType(bool dontLookDotIdents)
{
Type *t;
Loc loc;
Expand Down Expand Up @@ -3076,22 +3076,22 @@ Type *Parser::parseBasicType()
// ident!(template_arguments)
TemplateInstance *tempinst = new TemplateInstance(loc, id);
tempinst->tiargs = parseTemplateArguments();
t = parseBasicTypeStartingAt(new TypeInstance(loc, tempinst));
t = parseBasicTypeStartingAt(new TypeInstance(loc, tempinst), dontLookDotIdents);
}
else
{
t = parseBasicTypeStartingAt(new TypeIdentifier(loc, id));
t = parseBasicTypeStartingAt(new TypeIdentifier(loc, id), dontLookDotIdents);
}
break;

case TOKdot:
// Leading . as in .foo
t = parseBasicTypeStartingAt(new TypeIdentifier(token.loc, Id::empty));
t = parseBasicTypeStartingAt(new TypeIdentifier(token.loc, Id::empty), dontLookDotIdents);
break;

case TOKtypeof:
// typeof(expression)
t = parseBasicTypeStartingAt(parseTypeof());
t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
break;

case TOKvector:
Expand Down Expand Up @@ -3138,7 +3138,7 @@ Type *Parser::parseBasicType()
return t;
}

Type *Parser::parseBasicTypeStartingAt(TypeQualified *tid)
Type *Parser::parseBasicTypeStartingAt(TypeQualified *tid, bool dontLookDotIdents)
{
Type *maybeArray = NULL;
// See https://issues.dlang.org/show_bug.cgi?id=1215
Expand Down Expand Up @@ -3212,6 +3212,9 @@ Type *Parser::parseBasicTypeStartingAt(TypeQualified *tid)
}
case TOKlbracket:
{
if (dontLookDotIdents) // workaround for Bugzilla 14911
goto Lend;

nextToken();
Type *t = maybeArray ? maybeArray : (Type *)tid;
if (token.value == TOKrbracket)
Expand Down Expand Up @@ -3266,7 +3269,7 @@ Type *Parser::parseBasicTypeStartingAt(TypeQualified *tid)
goto Lend;
}
}
Lend:
Lend:
return maybeArray ? maybeArray : (Type *)tid;
}

Expand Down Expand Up @@ -6014,8 +6017,16 @@ bool Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok)
t = peek(t);
}
else if (isDeclaration(t, 0, TOKrbracket, &t))
{ // It's an associative array declaration
{
// It's an associative array declaration
t = peek(t);

// ...[type].ident
if (t->value == TOKdot && peek(t)->value == TOKidentifier)
{
t = peek(t);
t = peek(t);
}
}
else
{
Expand All @@ -6024,13 +6035,27 @@ bool Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok)
if (!isExpression(&t))
return false;
if (t->value == TOKslice)
{ t = peek(t);
{
t = peek(t);
if (!isExpression(&t))
return false;
if (t->value != TOKrbracket)
return false;
t = peek(t);
}
else
{
if (t->value != TOKrbracket)
return false;
t = peek(t);

// ...[index].ident
if (t->value == TOKdot && peek(t)->value == TOKidentifier)
{
t = peek(t);
t = peek(t);
}
}
if (t->value != TOKrbracket)
return false;
t = peek(t);
}
continue;

Expand Down Expand Up @@ -7754,7 +7779,7 @@ Expression *Parser::parseNewExp(Expression *thisexp)
}

StorageClass stc = parseTypeCtor();
t = parseBasicType();
t = parseBasicType(true);
t = parseBasicType2(t);
t = t->addSTC(stc);
if (t->ty == Taarray)
Expand Down
4 changes: 2 additions & 2 deletions src/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ class Parser : public Lexer
BaseClasses *parseBaseClasses();
Dsymbols *parseImport();
Type *parseType(Identifier **pident = NULL, TemplateParameters **ptpl = NULL);
Type *parseBasicType();
Type *parseBasicTypeStartingAt(TypeQualified *tid);
Type *parseBasicType(bool dontLookDotIdents = false);
Type *parseBasicTypeStartingAt(TypeQualified *tid, bool dontLookDotIdents);
Type *parseBasicType2(Type *t);
Type *parseDeclarator(Type *t, int *alt, Identifier **pident,
TemplateParameters **tpl = NULL, StorageClass storage_class = 0, int *pdisable = NULL, Expressions **pudas = NULL);
Expand Down
14 changes: 10 additions & 4 deletions src/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -4034,11 +4034,16 @@ Statement *ReturnStatement::semantic(Scope *sc)
{
if (tf->next && tf->next->ty != Tvoid)
{
error("mismatched function return type inference of void and %s",
tf->next->toChars());
if (tf->next->ty != Terror)
{
error("mismatched function return type inference of void and %s",
tf->next->toChars());
}
errors = true;
tf->next = Type::terror;
}
tf->next = Type::tvoid;
else
tf->next = Type::tvoid;

tret = tf->next;
tbret = tret->toBasetype();
Expand All @@ -4049,7 +4054,8 @@ Statement *ReturnStatement::semantic(Scope *sc)

if (tbret->ty != Tvoid) // if non-void return
{
error("return expression expected");
if (tbret->ty != Terror)
error("return expression expected");
errors = true;
}
else if (fd->isMain())
Expand Down
29 changes: 25 additions & 4 deletions src/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "template.h"
#include "tokens.h"

Type *getTypeInfoType(Type *t, Scope *sc);
TypeTuple *toArgTypes(Type *t);

FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals
Expand Down Expand Up @@ -100,12 +101,31 @@ void semanticTypeInfo(Scope *sc, Type *t)

// If the struct is in a non-root module, run semantic3 to get
// correct symbols for the member function.
// Note that, all instantiated symbols will run semantic3.
if (sd->inNonRoot())
if (TemplateInstance *ti = sd->isInstantiated())
{
//printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot());
Module::addDeferredSemantic3(sd);
if (ti->minst && !ti->minst->isRoot())
Module::addDeferredSemantic3(sd);
}
else
{
if (sd->inNonRoot())
{
//printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot());
Module::addDeferredSemantic3(sd);
}
}

if (!sc) // inline may request TypeInfo.
{
Scope scx;
scx.module = sd->getModule();
getTypeInfoType(t, &scx);
}
else
getTypeInfoType(t, sc);

if (!sc || sc->minst)
sd->requestTypeInfo = true;
}
void visit(TypeClass *t) { }
void visit(TypeTuple *t)
Expand Down Expand Up @@ -663,6 +683,7 @@ StructDeclaration::StructDeclaration(Loc loc, Identifier *id)
ispod = ISPODfwd;
arg1type = NULL;
arg2type = NULL;
requestTypeInfo = false;

// For forward references
type = new TypeStruct(this);
Expand Down
277 changes: 180 additions & 97 deletions src/template.c
Original file line number Diff line number Diff line change
Expand Up @@ -3280,7 +3280,7 @@ MATCH deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *par
{
if (!s || !s->parent)
goto Lnomatch;
Dsymbol *s2 = s->parent->searchX(Loc(), sc, id);
Dsymbol *s2 = s->parent->search(Loc(), (Identifier *)id);
if (!s2)
goto Lnomatch;
s2 = s2->toAlias();
Expand Down Expand Up @@ -5911,6 +5911,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
* implements the typeargs. If so, just refer to that one instead.
*/
inst = tempdecl->findExistingInstance(this, fargs);
TemplateInstance *errinst = NULL;
if (!inst)
{
// So, we need to implement 'this' instance.
Expand All @@ -5919,6 +5920,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
{
// If the first instantiation had failed, re-run semantic,
// so that error messages are shown.
errinst = inst;
}
else
{
Expand All @@ -5942,22 +5944,46 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
this->tnext = inst->tnext;
inst->tnext = this;

// If the first instantiation was in speculative context, but this is not:
if (tinst && !inst->tinst && !inst->minst)
/* A module can have explicit template instance and its alias
* in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`).
* If the first instantiation 'inst' had happened in non-root module,
* compiler can assume that its instantiated code would be included
* in the separately compiled obj/lib file (e.g. phobos.lib).
*
* However, if 'this' second instantiation happened in root module,
* compiler might need to invoke its codegen (Bugzilla 2500 & 2644).
* But whole import graph is not determined until all semantic pass finished,
* so 'inst' should conservatively finish the semantic3 pass for the codegen.
*/
if (minst && minst->isRoot() && !(inst->minst && inst->minst->isRoot()))
{
// Reconnect the chain if this instantiation is not in speculative context.
/* Swap the position of 'inst' and 'this' in the instantiation graph.
* Then, the primary instance `inst` will be changed to a root instance.
*
* Before:
* non-root -> A!() -> B!()[inst] -> C!()
* |
* root -> D!() -> B!()[this]
*
* After:
* non-root -> A!() -> B!()[this]
* |
* root -> D!() -> B!()[inst] -> C!()
*/
Module *mi = minst;
TemplateInstance *ti = tinst;
while (ti && ti != inst)
ti = ti->tinst;
if (ti != inst) // Bugzilla 13379: Prevent circular chain
inst->tinst = tinst;
}
minst = inst->minst;
tinst = inst->tinst;
inst->minst = mi;
inst->tinst = ti;

// If the first instantiation was speculative, but this is not:
if (!inst->minst)
{
// Mark it is a non-speculative instantiation.
inst->minst = minst;
if (minst) // if inst was not speculative
{
/* Add 'inst' once again to the root module members[], then the
* instance members will get codegen chances.
*/
inst->appendToModuleMember();
}
}

#if LOG
Expand All @@ -5979,66 +6005,10 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)

//getIdent();

// Add 'this' to the enclosing scope's members[] so the semantic routines
// will get called on the instance members. Store the place we added it to
// in target_symbol_list(_idx) so we can remove it later if we encounter
// an error.
#if 1
Dsymbols *target_symbol_list;
size_t target_symbol_list_idx = 0;
//if (sc->scopesym) printf("3: sc is %s %s\n", sc->scopesym->kind(), sc->scopesym->toChars());
if (0 && !tinst && sc->scopesym && sc->scopesym->members)
{
/* A module can have explicit template instance and its alias
* in module scope (e,g, `alias Base64Impl!('+', '/') Base64;`).
* When the module is just imported, normally compiler can assume that
* its instantiated code would be contained in the separately compiled
* obj/lib file (e.g. phobos.lib).
* Bugzilla 2644: However, if the template is instantiated in both
* modules of root and non-root, compiler should generate its objcode.
* Therefore, always conservatively insert this instance to the member of
* a root module, then calculate the necessity by TemplateInstance::needsCodegen().
*/
//if (sc->scopesym->isModule())
// printf("module level instance %s\n", toChars());

//printf("\t1: adding to %s %s\n", sc->scopesym->kind(), sc->scopesym->toChars());
target_symbol_list = sc->scopesym->members;
}
else
{
Dsymbol *s = enclosing ? enclosing : tempdecl->parent;
while (s && !s->isModule())
s = s->toParent2();
assert(s);
Module *m = (Module *)s;
if (!m->isRoot())
m = m->importedFrom;

//printf("\t2: adding to module %s instead of module %s\n", m->toChars(), sc->module->toChars());
target_symbol_list = m->members;

/* Defer semantic3 running in order to avoid mutual forward reference.
* See test/runnable/test10736.d
*/
if (m->semanticRun >= PASSsemantic3done)
Module::addDeferredSemantic3(this);
}
for (size_t i = 0; 1; i++)
{
if (i == target_symbol_list->dim)
{
target_symbol_list_idx = i;
target_symbol_list->push(this);
break;
}
if (this == (*target_symbol_list)[i]) // if already in Array
{
target_symbol_list = NULL;
break;
}
}
#endif
// Store the place we added it to in target_symbol_list(_idx) so we can
// remove it later if we encounter an error.
Dsymbols *target_symbol_list = appendToModuleMember();
size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list->dim - 1 : 0;

// Copy the syntax trees from the TemplateDeclaration
members = Dsymbol::arraySyntaxCopy(tempdecl->members);
Expand Down Expand Up @@ -6319,6 +6289,27 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
symtab = NULL;
}
}
else if (errinst)
{
/* Bugzilla 14541: If the previous gagged instance had failed by
* circular references, currrent "error reproduction instantiation"
* might succeed, because of the difference of instantiated context.
* On such case, the cached error instance needs to be overridden by the
* succeeded instance.
*/
size_t bi = hash % tempdecl->buckets.dim;
TemplateInstances *instances = tempdecl->buckets[bi];
assert(instances);
for (size_t i = 0; i < instances->dim; i++)
{
TemplateInstance *ti = (*instances)[i];
if (ti == errinst)
{
(*instances)[i] = this; // override
break;
}
}
}

#if LOG
printf("-TemplateInstance::semantic('%s', this=%p)\n", toChars(), this);
Expand Down Expand Up @@ -7288,6 +7279,64 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic)
return nested != 0;
}

/*****************************************
* Append 'this' to the specific module members[]
*/
Dsymbols *TemplateInstance::appendToModuleMember()
{
Module *mi = minst; // instantiated -> inserted module

if (global.params.useUnitTests ||
global.params.debuglevel)
{
// Turn all non-root instances to speculative
if (mi && !mi->isRoot())
mi = NULL;
}

if (!mi || mi->isRoot())
{
/* If the instantiated module is speculative or root, insert to the
* member of a root module. Then:
* - semantic3 pass will get called on the instance members.
* - codegen pass will get a selection chance to do/skip it.
*/

// insert target is made stable by using the module
// where tempdecl is declared.
mi = tempdecl->getModule();
if (!mi->isRoot())
mi = mi->importedFrom;
assert(mi->isRoot());
}
else
{
/* If the instantiated module is non-root, insert to the member of the
* non-root module. Then:
* - semantic3 pass won't be called on the instance.
* - codegen pass won't reach to the instance.
*/
}

Dsymbols *a = mi->members;
for (size_t i = 0; 1; i++)
{
if (i == a->dim)
{
a->push(this);
if (mi->semanticRun >= PASSsemantic3done && mi->isRoot())
Module::addDeferredSemantic3(this);
break;
}
if (this == (*a)[i]) // if already in Array
{
a = NULL;
break;
}
}
return a;
}

/****************************************
* This instance needs an identifier for name mangling purposes.
* Create one by taking the template declaration name and adding
Expand Down Expand Up @@ -7837,15 +7886,8 @@ void unSpeculative(Scope *sc, RootObject *o)
*/
bool TemplateInstance::needsCodegen()
{
/* The issue is that if the importee is compiled with a different -debug
* setting than the importer, the importer may believe it exists
* in the compiled importee when it does not, when the instantiation
* is behind a conditional debug declaration.
*/
// workaround for Bugzilla 11239
if (global.params.useUnitTests ||
global.params.allInst ||
global.params.debuglevel)
// Now -allInst is just for the backward compatibility.
if (global.params.allInst)
{
//printf("%s minst = %s, enclosing (%s)->isNonRoot = %d\n",
// toPrettyChars(), minst ? minst->toChars() : NULL,
Expand Down Expand Up @@ -7876,9 +7918,12 @@ bool TemplateInstance::needsCodegen()
return true;
}

// If this may be a speculative instantiation:
if (!minst)
{
// If this is a speculative instantiation,
// 1. do codegen if ancestors really needs codegen.
// 2. become non-speculative if siblings are not speculative

TemplateInstance *tnext = this->tnext;
TemplateInstance *tinst = this->tinst;
// At first, disconnect chain first to prevent infinite recursion.
Expand All @@ -7893,51 +7938,89 @@ bool TemplateInstance::needsCodegen()
assert(minst->isRoot() || minst->rootImports());
return true;
}
if (tnext && tnext->needsCodegen())
if (tnext && (tnext->needsCodegen() || tnext->minst))
{
minst = tnext->minst; // cache result
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
return minst->isRoot() || minst->rootImports();
}

// Elide codegen because this is really speculative.
return false;
}

if (minst->isRoot())
/* The issue is that if the importee is compiled with a different -debug
* setting than the importer, the importer may believe it exists
* in the compiled importee when it does not, when the instantiation
* is behind a conditional debug declaration.
*/
// workaround for Bugzilla 11239
if (global.params.useUnitTests ||
global.params.debuglevel)
{
// Prefer instantiation in non-root module, to minimize object code size
// Prefer instantiations from root modules, to maximize link-ability.
if (minst->isRoot())
return true;

TemplateInstance *tnext = this->tnext;
TemplateInstance *tinst = this->tinst;
this->tnext = NULL;
this->tinst = NULL;

if (tnext && !tnext->needsCodegen() && tnext->minst)
if (tinst && tinst->needsCodegen())
{
minst = tinst->minst; // cache result
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}
if (tnext && tnext->needsCodegen())
{
minst = tnext->minst; // cache result
assert(!minst->isRoot());
return false;
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}

// Bugzilla 2500 case
if (minst->rootImports())
return true;

// Elide codegen because this is not included in root instances.
return false;
}
else
{
// Prefer instantiations from non-root module, to minimize object code size.

/* If a TemplateInstance is ever instantiated by non-root modules,
* we do not have to generate code for it,
* because it will be generated when the non-root module is compiled.
*
* But, if minst imports any root modules, we still need to generate the code.
* But, if the non-root 'minst' imports any root modules, it might still need codegen.
*
* The problem is if A imports B, and B imports A, and both A
* and B instantiate the same template, does the compilation of A
* or the compilation of B do the actual instantiation?
*
* See bugzilla 2500.
* See Bugzilla 2500.
*/
if (!minst->isRoot() && !minst->rootImports())
return false;

if (!minst->rootImports())
TemplateInstance *tnext = this->tnext;
this->tnext = NULL;

if (tnext && !tnext->needsCodegen() && tnext->minst)
{
//printf("instantiated by %s %s\n", minst->toChars(), toChars());
minst = tnext->minst; // cache result
assert(!minst->isRoot());
return false;
}

// Do codegen because this is not included in non-root instances.
return true;
}
return true;
}

/* ======================== TemplateMixin ================================ */
Expand Down
1 change: 1 addition & 0 deletions src/template.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ class TemplateInstance : public ScopeDsymbol
bool findBestMatch(Scope *sc, Expressions *fargs);
bool needsTypeInference(Scope *sc, int flag = 0);
bool hasNestedArgs(Objects *tiargs, bool isstatic);
Dsymbols *appendToModuleMember();
void declareParameters(Scope *sc);
Identifier *genIdent(Objects *args);
void expandMembers(Scope *sc);
Expand Down
6 changes: 6 additions & 0 deletions src/toobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Symbol *toVtblSymbol(ClassDeclaration *cd);
Symbol *toInitializer(AggregateDeclaration *ad);
Symbol *toInitializer(EnumDeclaration *ed);
void genTypeInfo(Type *t, Scope *sc);
bool isSpeculativeType(Type *t);

void toDebug(EnumDeclaration *ed);
void toDebug(StructDeclaration *sd);
Expand Down Expand Up @@ -1010,6 +1011,11 @@ void toObjFile(Dsymbol *ds, bool multiobj)

void visit(TypeInfoDeclaration *tid)
{
if (isSpeculativeType(tid->tinfo))
{
//printf("-speculative '%s'\n", tid->toPrettyChars());
return;
}
//printf("TypeInfoDeclaration::toObjFile(%p '%s') protection %d\n", tid, tid->toChars(), tid->protection);

if (multiobj)
Expand Down
129 changes: 109 additions & 20 deletions src/typinf.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,9 @@ void genTypeInfo(Type *torig, Scope *sc)
// Generate COMDAT
if (sc) // if in semantic() pass
{
if (sc->func && sc->func->inNonRoot())
{
// Bugzilla 13043: Avoid linking TypeInfo if it's not
// necessary for root module compilation
}
else
{
// Find module that will go all the way to an object file
Module *m = sc->module->importedFrom;
m->members->push(t->vtinfo);

semanticTypeInfo(sc, t);
}
// Find module that will go all the way to an object file
Module *m = sc->module->importedFrom;
m->members->push(t->vtinfo);
}
else // if in obj generation pass
{
Expand Down Expand Up @@ -135,6 +125,92 @@ TypeInfoDeclaration *getTypeInfoDeclaration(Type *t)
}
}

bool isSpeculativeType(Type *t)
{
class SpeculativeTypeVisitor : public Visitor
{
public:
bool result;

SpeculativeTypeVisitor() : result(false) {}

void visit(Type *t)
{
Type *tb = t->toBasetype();
if (tb != t)
tb->accept(this);
}
void visit(TypeNext *t)
{
if (t->next)
t->next->accept(this);
}
void visit(TypeBasic *t) { }
void visit(TypeVector *t)
{
t->basetype->accept(this);
}
void visit(TypeAArray *t)
{
t->index->accept(this);
visit((TypeNext *)t);
}
void visit(TypeFunction *t)
{
visit((TypeNext *)t);
// Currently TypeInfo_Function doesn't store parameter types.
}
void visit(TypeStruct *t)
{
StructDeclaration *sd = t->sym;
if (TemplateInstance *ti = sd->isInstantiated())
{
if (!ti->needsCodegen())
{
if (ti->minst || sd->requestTypeInfo)
return;

/* Bugzilla 14425: TypeInfo_Struct would refer the members of
* struct (e.g. opEquals via xopEquals field), so if it's instantiated
* in speculative context, TypeInfo creation should also be
* stopped to avoid 'unresolved symbol' linker errors.
*/
/* When -debug/-unittest is specified, all of non-root instances are
* automatically changed to speculative, and here is always reached
* from those instantiated non-root structs.
* Therefore, if the TypeInfo is not auctually requested,
* we have to elide its codegen.
*/
result |= true;
return;
}
}
else
{
//assert(!sd->inNonRoot() || sd->requestTypeInfo); // valid?
}
}
void visit(TypeClass *t) { }
void visit(TypeTuple *t)
{
if (t->arguments)
{
for (size_t i = 0; i < t->arguments->dim; i++)
{
Type *tprm = (*t->arguments)[i]->type;
if (tprm)
tprm->accept(this);
if (result)
return;
}
}
}
};
SpeculativeTypeVisitor v;
t->accept(&v);
return v.result;
}

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

Expand Down Expand Up @@ -422,13 +498,26 @@ class TypeInfoDtVisitor : public Visitor

if (TemplateInstance *ti = sd->isInstantiated())
{
/* Bugzilla 14425: TypeInfo_Struct would refer the members of
* struct (e.g. opEquals via xopEquals field), so if it's instantiated
* in speculative context, TypeInfo creation should also be
* stopped to avoid 'unresolved symbol' linker errors.
*/
if (!ti->needsCodegen() && !ti->minst)
return;
if (!ti->needsCodegen())
{
assert(ti->minst || sd->requestTypeInfo);

/* ti->toObjFile() won't get called. So, store these
* member functions into object file in here.
*/
if (sd->xeq && sd->xeq != StructDeclaration::xerreq)
toObjFile(sd->xeq, global.params.multiobj);
if (sd->xcmp && sd->xcmp != StructDeclaration::xerrcmp)
toObjFile(sd->xcmp, global.params.multiobj);
if (FuncDeclaration *ftostr = search_toString(sd))
toObjFile(ftostr, global.params.multiobj);
if (sd->xhash)
toObjFile(sd->xhash, global.params.multiobj);
if (sd->postblit)
toObjFile(sd->postblit, global.params.multiobj);
if (sd->dtor)
toObjFile(sd->dtor, global.params.multiobj);
}
}

/* Put out:
Expand Down
49 changes: 49 additions & 0 deletions test/compilable/b1215.d
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,52 @@ struct C(Args...)
}

alias Z = A!(B,B,C!(B,B));

/***************************************************/
// 14889

struct A14889(alias Exc)
{
alias ExceptionType = Exc;
}
alias TT14889(Args...) = Args;

alias X14889a = TT14889!(A14889!Throwable());
alias Y14889a = X14889a[0].ExceptionType;

alias X14889b = TT14889!(A14889!Throwable);
alias Y14889b = X14889b[0].ExceptionType;

/***************************************************/
// 14889

alias TypeTuple14900(T...) = T;

struct S14900
{
alias T = int;
alias U = TypeTuple14900!(long,string);
}

alias Types14900 = TypeTuple14900!(S14900, S14900);

Types14900[0].T a14900; // Types[0] == S, then typeof(a) == S.T == int
Types14900[0].U[1] b14900; // Types[0].U == S.U, then typeof(b) == S.U[1] == string

void test14900()
{
Types14900[0].T a; // Types[0] == S, then typeof(a) == S.T == int
Types14900[0].U[1] b; // Types[0].U == S.U, then typeof(b) == S.U[1] == string
}

/***************************************************/
// 14911

void test14911()
{
struct S {}

int* buf1 = new int[2].ptr; // OK
S* buf2 = (new S[2]).ptr; // OK
S* buf3 = new S[2].ptr; // OK <- broken
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice12554.d(18): Error: pure function 'ice12554.main.__lambda1' cannot call impure function 'ice12554.array!(MapResult!((y) => x)).array'
fail_compilation/ice12554.d(37): instantiated from here: MapResult!((x) => foo.map!(MapResultS, (y) => x).array)
fail_compilation/ice12554.d(18): instantiated from here: map!(int[])
fail_compilation/ice12554.d(21): Error: pure function 'ice12554.main.__lambda2' cannot call impure function 'ice12554.array!(MapResult!((y) => x)).array'
fail_compilation/ice12554.d(37): instantiated from here: MapResult!((x) => foo.map!(MapResultC, (y) => x).array)
fail_compilation/ice12554.d(21): instantiated from here: map!(int[])
---
*/
// REQUIRED_ARGS: -o-
// PERMUTE_ARGS:

void main() pure
{
Expand Down
38 changes: 38 additions & 0 deletions test/compilable/test14781.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// REQUIRED_ARGS: -o-
// PERMUTE_ARGS:

void impure() {} // impure

auto fb1(T)() pure
{
int x;
struct A(S)
{
void fc(T2)()
{
x = 1; // accessing pure function context is just ok
impure(); // impure function call makes fc as impure
}
this(S a) {}
}
return A!int();
}
auto fb2(T)() pure
{
int x;
struct A(S)
{
void fc(T2)()
{
impure(); // impure function call makes fc as impure
x = 1; // accessing pure function context is just ok
}
this(S a) {}
}
return A!int();
}
void test1()
{
fb1!int().fc!int();
fb2!int().fc!int();
}
41 changes: 41 additions & 0 deletions test/compilable/test14962.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
template map(fun...)
{
auto map(R)(R r)
{
return MapResult!(fun, R)(r);
}
}

struct MapResult(alias fun, R)
{
R _input;

@property bool empty() { return _input.length == 0; }
@property auto front() { return fun(_input[0]); }
void popFront() { _input = _input[1..$]; }
}

struct Foo
{
int baz(int v)
{
static int id;
return v + id++;
}
void bar()
{
auto arr1 = [1, 2, 3];
auto arr2 = [4, 5, 6];
arr1.map!(
// lambda1
i =>
arr2.map!(
// lambda2
j =>
baz(i + j)
)
);
}
}

void main() {}
54 changes: 54 additions & 0 deletions test/compilable/test14973.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
template map(fun...)
{
auto map(R)(R r)
{
return MapResult!(fun, R)(r);
}
}

struct MapResult(alias fun, R)
{
R _input;

@property bool empty() { return _input.length == 0; }
@property auto front() { return fun(_input[0]); }
void popFront() { _input = _input[1..$]; }
}

class Foo
{
int baz() { return 1; }
void bar()
{
auto s = [1].map!(i => baz()); // compiles
auto r = [1].map!(
// lambda1
i =>
[1].map!(
// lambda2
j =>
baz()
)
); // compiles <- error
}
}

class Bar
{
int baz;
void bar()
{
auto s = [1].map!(i => baz); // compiles
auto r = [1].map!(
// lambda1
i =>
[1].map!(
// lambda2
j =>
baz
)
); // compiles <- error
}
}

void main() {}
2 changes: 1 addition & 1 deletion test/fail_compilation/diag9831.d
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/diag9831.d(12): Error: function diag9831.main.__lambda1 cannot access frame of function D main
fail_compilation/diag9831.d(12): Error: cannot match delegate literal to function pointer type 'int function(int x)'
---
*/

Expand Down
17 changes: 0 additions & 17 deletions test/fail_compilation/fail1.d

This file was deleted.

4 changes: 2 additions & 2 deletions test/fail_compilation/fail11545.d
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail11545.d(14): Error: need 'this' for 'x' of type 'int'
fail_compilation/fail11545.d(18): Error: need 'this' for 'x' of type 'int'
fail_compilation/fail11545.d(17): Error: cannot implicitly convert expression (__lambda5) of type int delegate() pure nothrow @nogc @safe to int function()
fail_compilation/fail11545.d(17): Error: cannot implicitly convert expression (__lambda5) of type int delegate() pure nothrow @nogc @safe to int function()
---
*/

Expand Down
4 changes: 2 additions & 2 deletions test/fail_compilation/fail120.d
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail120.d(12): Error: need 'this' for 'nodes' of type 'int[2]'
fail_compilation/fail120.d(13): Error: need 'this' for 'nodes' of type 'int[2]'
fail_compilation/fail120.d(12): Error: non-constant nested delegate literal expression __lambda4
fail_compilation/fail120.d(13): Error: non-constant nested delegate literal expression __lambda5
---
*/

Expand Down
17 changes: 0 additions & 17 deletions test/fail_compilation/fail2.d

This file was deleted.

2 changes: 1 addition & 1 deletion test/fail_compilation/fail39.d
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail39.d(11): Error: function fail39.main.__funcliteral2 cannot access frame of function D main
fail_compilation/fail39.d(11): Error: function fail39.main.foo is a nested function and cannot be accessed from fail39.main.__funcliteral2
---
*/

Expand Down
4 changes: 1 addition & 3 deletions test/fail_compilation/fail9.d
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail9.d(25): Error: no property 'Vector' for type 'fail9.Vector!int'
fail_compilation/fail9.d(25): Error: no property 'Vector' for type 'fail9.Vector!int'
fail_compilation/fail9.d(23): Error: no property 'Vector' for type 'fail9.Vector!int'
---
*/


template Vector(T)
{
int x;
Expand Down
46 changes: 0 additions & 46 deletions test/fail_compilation/fail9148.d

This file was deleted.

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

/*
TEST_OUTPUT:
---
fail_compilation/fail_opover.d(13): Error: no [] operator overload for type object.Object
fail_compilation/fail_opover.d(17): Error: no [] operator overload for type TestS
---
*/
void test1()
{
Object m;
m[] = error;

struct TestS {}
TestS s;
s[] = error;
}

/*
TEST_OUTPUT:
---
fail_compilation/fail_opover.d(46): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(47): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(48): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(49): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(50): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(51): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(52): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(53): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(54): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(55): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(56): Error: no [] operator overload for type S
fail_compilation/fail_opover.d(57): Error: no [] operator overload for type S
---
*/
void test2()
{
struct S
{
void func(int) {}
alias func this;
}
S s;
// The errors failing aliasthis access need to be gagged for better error messages.
s[]; // in ArrayExp::op_overload()
s[1]; // ditto
s[1..2]; // ditto
+s[]; // in UnaExp::op_overload()
+s[1]; // ditto
+s[1..2]; // ditto
s[] = 3; // in AssignExp::semantic()
s[1] = 3; // ditto
s[1..2] = 3; // ditto
s[] += 3; // in BinAssignExp::op_overload()
s[1] += 3; // ditto
s[1..2] += 3; // ditto
}
30 changes: 30 additions & 0 deletions test/fail_compilation/ice14621.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice14621.d(22): Error: static assert (false) is false
fail_compilation/ice14621.d(28): instantiated from here: erroneousTemplateInstantiation!()
---
*/

void main()
{
S s;
s.foo();
}

struct S
{
float[] array;
alias array this;

template erroneousTemplateInstantiation()
{
static assert(false);
}

void foo()
{
S ret;
ret[] = erroneousTemplateInstantiation!();
}
}
28 changes: 28 additions & 0 deletions test/fail_compilation/ice14923.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice14923.d(21): Error: function ice14923.parse (C a) is not callable using argument types (A)
fail_compilation/ice14923.d(21): instantiated from here: bar!((b) => parse(b))
---
*/

auto bar(alias fun)()
{
size_t counter;
scope(exit) counter++;

Object a2;
if (auto ai = cast(A)a2) return fun(ai);
if (auto ai = cast(B)a2) return fun(ai);
}

void parse(C a)
{
bar!(b => parse(b))();
}

class A {}

class C {}

class B : C {}
95 changes: 95 additions & 0 deletions test/fail_compilation/ice14929.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice14929.d(44): Error: cast(Node)(*this.current).items[this.index] is not an lvalue
fail_compilation/ice14929.d(87): Error: template instance ice14929.HashMap!(ulong, int).HashMap.opBinaryRight!"in" error instantiating
fail_compilation/ice14929.d(91): instantiated from here: HashmapComponentStorage!int
fail_compilation/ice14929.d(91): Error: template instance ice14929.isComponentStorage!(HashmapComponentStorage!int, int) error instantiating
---
*/

struct HashMap(K, V)
{
V* opBinaryRight(string op)(K key) const if (op == "in")
{
size_t index;
foreach (ref node; buckets[index].range)
{
return &(node.value);
}
return null;
}

struct Node
{
K key;
V value;
}

alias Bucket = UnrolledList!(Node);
Bucket[] buckets;
}

struct UnrolledList(T)
{
Range range() const pure
{
return Range(_front);
}

static struct Range
{
ref T front() const @property
{
return cast(T) current.items[index];
}
void popFront() pure
{
}
bool empty() const pure @property
{
return true;
}
const(Node)* current;
size_t index;
}

Node* _front;

static struct Node
{
ContainerStorageType!T[10] items;
}
}

template ContainerStorageType(T)
{
alias ContainerStorageType = T;
}

template isComponentStorage(CS, C)
{
enum bool isComponentStorage = is(typeof(
(inout int = 0)
{
CS cs = CS.init;
ulong eid;
cs.add(eid, c);
}));
}

struct HashmapComponentStorage(ComponentType)
{
private HashMap!(ulong, ComponentType) components;

void add(ulong eid, ComponentType component)
{
assert(eid !in components);
}
}

static assert(isComponentStorage!(HashmapComponentStorage!int, int));

void main()
{
}
4 changes: 2 additions & 2 deletions test/fail_compilation/ice9865.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
TEST_OUTPUT:
---
fail_compilation/ice9865.d(9): Error: struct ice9865.Foo no size yet for forward reference
fail_compilation/ice9865.d(8): Error: alias ice9865.Baz cannot resolve
fail_compilation/ice9865.d(8): Error: alias ice9865.Baz recursive alias declaration
---
*/
import imports.ice9865b : Baz;
import imports.ice9865b : Baz;
struct Foo { Baz f; }
16 changes: 16 additions & 0 deletions test/runnable/extra-files/linkdebug.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module linkdebug;

void main()
{
import linkdebug_uni;
import linkdebug_range;

// OK
//SortedRangeX!(uint[], "a <= b") SR;

CodepointSet set;
set.addInterval(1, 2);

// NG, order dependent.
SortedRange!(uint[], "a <= b") SR;
}
13 changes: 13 additions & 0 deletions test/runnable/extra-files/linkdebug_primitives.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module linkdebug_primitives;

size_t popBackN(R)(ref R r, size_t n)
{
n = cast(size_t) (n < r.length ? n : r.length);
r = r[0 .. $ - n];
return n;
}

auto moveAt(R, I)(R r, I i)
{
return r[i];
}
48 changes: 48 additions & 0 deletions test/runnable/extra-files/linkdebug_range.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module linkdebug_range;

import linkdebug_primitives : popBackN, moveAt;

auto stride(R)(R r)
{
static struct Result
{
R source;

void popBack()
{
popBackN(source, 0);
}

uint moveAt(size_t n)
{
return .moveAt(source, n);
}
}
return Result(r);
}

struct SortedRange(Range, alias pred = "a < b")
{
this(Range input)
out
{
dbgVerifySorted();
}
body
{
}

void dbgVerifySorted()
{
debug
{
uint[] _input;
auto st = stride(_input);
}
}
}

auto assumeSorted(alias pred = "a < b", R)(R r)
{
return SortedRange!(R, pred)(r);
}
19 changes: 19 additions & 0 deletions test/runnable/extra-files/linkdebug_uni.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module linkdebug_uni;

import linkdebug_range;

struct GcPolicy {}

alias CodepointSet = InversionList!();

struct InversionList(SP = GcPolicy)
{
@trusted:
size_t addInterval(int a, int b, size_t hint = 0)
{
auto data = new uint[](0); // affects to the number of missimg symbol
auto range = assumeSorted(data[]); // NG
//SortedRange!(uint[], "a < b") SR; // OK
return 1;
}
}
5 changes: 4 additions & 1 deletion test/runnable/extra-files/test14198.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ struct S
to!string(false);
// [1] to!string(bool src) should be deduced to pure @safe, and the function will be mangled to:
// --> _D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya
// [2] its object code should be stored in the library file, because it's instantiated in std14188.uni:
// [2] its object code would be stored in the library file, because it's instantiated in std14188.uni:
// --> FormatSpec!char --> to!string(bool src) in FormatSpec!char.toString()
// But semanti3 of FormatSpec!char.toString() won't get called from this module compilation,
// so the instantiaion is invisible.
// Then, the object code is also stored in test14198.obj, and the link will succeed.
}
else
static assert(0);
Expand Down
54 changes: 54 additions & 0 deletions test/runnable/imports/link14541traits.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module imports.link14541traits;

template hasElaborateAssign(S)
{
static if (is(S == struct))
{
extern __gshared S lvalue;

enum hasElaborateAssign = is(typeof(S.init.opAssign(S.init))) ||
is(typeof(S.init.opAssign(lvalue)));
}
else
{
enum bool hasElaborateAssign = false;
}
}

void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc
{
static if (hasElaborateAssign!T)
{
}
else
{
}
}

template Tuple(Types...)
{
struct Tuple
{
Types field;
alias field this;

this(Types values)
{
field[] = values[];
}

void opAssign(R)(auto ref R rhs)
{
static if (is(R : Tuple!Types) && !__traits(isRef, rhs))
{
// Use swap-and-destroy to optimize rvalue assignment
swap!(Tuple!Types)(this, rhs);
}
else
{
// Do not swap; opAssign should be called on the fields.
field[] = rhs.field[];
}
}
}
}
33 changes: 33 additions & 0 deletions test/runnable/imports/linktypeinfo_file.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module imports.linktypeinfo_file;

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

struct FilterResult(alias pred, R)
{
R r;
bool empty() { return r.empty; }
auto front() { return r.front; }
void popFront()
{
while (!r.empty && pred(r.front))
r.popFront();
}
}

struct DirIterator
{
int[] r;
@property bool empty() { return r.length == 0; }
@property auto front() { return r[0]; }
void popFront() { r = r[1..$]; }

}

auto dirEntries(string path)
{
bool f(int de) { return 1; }
return filter!f(DirIterator());
}
21 changes: 21 additions & 0 deletions test/runnable/imports/test14901a.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module imports.test14901a;

//extern(C) int printf(const char*, ...);

extern extern(C) __gshared static int initCount;

int make(string s)()
{
__gshared static int value;

struct WithCtor
{
shared static this()
{
//printf("%s\n", s.ptr);
initCount++;
}
}

return value;
}
13 changes: 13 additions & 0 deletions test/runnable/imports/test14901b.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module imports.test14901b;

import imports.test14901a;

alias bar = make!"bar";

struct User(int id)
{
int foo()
{
return bar;
}
}
10 changes: 10 additions & 0 deletions test/runnable/imports/test14901c.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module imports.test14901c;

import imports.test14901b;

shared static this() {}

void caller1()
{
User!1 u;
}
8 changes: 8 additions & 0 deletions test/runnable/imports/test14901d.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module imports.test14901d;

import imports.test14901b;

void caller2()
{
User!2 u;
}
6 changes: 3 additions & 3 deletions test/runnable/link14198b.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ else
fi
libname=${dir}${SEP}lib14198b${LIBEXT}

# Do link failure without library file.
# Do not link failure even without library file.

$DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198b${EXE} ${src}${SEP}test14198.d > ${output_file} 2>&1
grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} || exit 1
grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} && exit 1

$DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198b${EXE} -version=bug14198 ${src}${SEP}test14198.d > ${output_file} 2>&1
grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} || exit 1
grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} && exit 1

rm ${dir}/{test14198b${OBJ},test14198b${EXE}}

Expand Down
42 changes: 42 additions & 0 deletions test/runnable/link14541.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import imports.link14541traits;

void main()
{
Tuple!(int, int) result;

alias T = typeof(result);
static assert(hasElaborateAssign!T);
// hasElaborateAssign!(Tuple(int, int)):
// 1. instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue]
// 2. instantiates swap!(Tuple!(int, int))
// 3. instantiates hasElaborateAssign!(Tuple!(int, int))
// --> forward reference error
// --> swap!(Tuple!(int, int)) fails to instantiate
// --> Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = rvalue] fails to instantiate
// 4. instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Lvalue]
// --> succeeds
// hasElaborateAssign!(Tuple(int, int)) succeeds to instantiate (result is 'true')

// Instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue], but
// it's already done in gagged context, so this is made an error reproduction instantiation.
// But, the forward reference of hasElaborateAssign!(Tuple(int, int)) is already resolved, so
// the instantiation will succeeds.
result = Tuple!(int, int)(0, 0); // --> 1st error reproduction instantiation
result = Tuple!(int, int)(0, 0); // --> 2nd error reproduction instantiation

// The two error reproduction instantiations generate the function:
// Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue]
// twice, then it will cause duplicate COMDAT error in Win64 platform.
}

/+
The point is, if instantiated contexts are different, two instantiations may cause different result.
- The 1st Tuple.opAssign instantiation is invoked from hasElaborateAssign template with gagging.
So it has failed, because of the circular reference of hasElaborateAssign template..
- The 2nd Tuple.opAssign instantiation is invoked from main() without gagging.
It does not have circular reference, so the instantiation should succeed.
Therefore, the gagged failure should be overridden by the ungagged success.
+/
21 changes: 21 additions & 0 deletions test/runnable/linkdebug.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash

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

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

$DMD -m${MODEL} -I${src} -of${libname} -lib ${src}${SEP}linkdebug_uni.d ${src}${SEP}linkdebug_range.d ${src}${SEP}linkdebug_primitives.d || exit 1

$DMD -m${MODEL} -I${src} -of${dir}${SEP}linkdebug${EXE} -g -debug ${src}${SEP}linkdebug.d ${libname} || exit 1

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

echo Success > ${output_file}
34 changes: 34 additions & 0 deletions test/runnable/linktypeinfo.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// EXTRA_SOURCES: imports/linktypeinfo_file.d
// PERMUTE_ARGS: -g -inline -unittest -debug
// COMPILE_SEPARATELY

import imports.linktypeinfo_file;

struct Only(T)
{
private T _val;
}

auto only(V)(V v)
{
return Only!V(v);
}

static struct Chain(R...)
{
R source;
}

auto chain(R...)(R rs)
{
return Chain!R(rs);
}

void main()
{
string docRoot;

const r = dirEntries(docRoot);
typeof(r)[] a;
a.length = 0; // require TypeInfo for const(FilterResult!(DirIterator))
}
191 changes: 190 additions & 1 deletion test/runnable/opover2.d
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ bool thrown(E, T)(lazy T val)
catch (E e) { return true; }
}

void stompStack() { int[256] sa = 0xdeadbeef; }

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

class A
Expand Down Expand Up @@ -927,7 +929,7 @@ int test6798a()
auto opIndex(A...)(A indices){ return [[indices]]; }
}
S2 s2;
static assert(!__traits(compiles, s2[] ));
assert(s2[] == [[]]);
assert(s2[1] == [[1]]);
assert(s2[1, 2] == [[1, 2]]);
assert(s2[1..2] == [1, 2]);
Expand Down Expand Up @@ -1828,6 +1830,187 @@ void test14057()

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

struct Tuple20(T...) { T field; alias field this; }

void test20a()
{
// ae1save in in AssignExp::semantic
int a, b;

struct A1
{
void opIndexAssign(int v, Tuple20!(int, int) ) { a = v; }
Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; }
}
struct A2
{
A1 a1;
alias a1 this;
ref int opIndexAssign(int) { return b; }
}

stompStack();
A2 foo() { return A2(); }
foo()[1..2] = 1;
// ref A1 __tmp = foo().a1; __tmp.opIndexAssign(1, __tmp.opSlice!0(1, 2));
assert(a == 1); // should work
assert(b == 0);
}

void test20b()
{
// ae1save in UnaExp::op_overload()
int a, b;

struct A1
{
void opIndexUnary(string op)(Tuple20!(int, int) ) { a = 1; }
Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; }
void dummy() {} // nessary to make A1 nested struct
}
struct A2
{
A1 a1;
alias a1 this;
int opIndexUnary(string op)(int) { return 0; }
}

stompStack();
A2 foo() { return A2(); }
+foo()[1..2];
// ref A1 __tmp = foo().a1; __tmp.opIndexUnary!"+"(__tmp.opSlice!0(1, 2));
assert(a == 1); // should pass
assert(b == 0);
}

void test20c()
{
// ae1save in ArrayExp::op_overload()
int a, b;

struct A1
{
void opIndex(Tuple20!(int, int) ) { a = 1; }
Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; }
}
struct A2
{
A1 a1;
alias a1 this;
int opIndex(int) { return 0; }
}

stompStack();
A2 foo() { return A2(); }
foo()[1..2];
// ref A1 __tmp = foo().a1; __tmp.opIndex(__tmp.opSlice!0(1, 2));
assert(a == 1); // should pass
assert(b == 0);
}

void test20d()
{
// ae1save in BinAssignExp::op_overload()
int a, b;

struct A1
{
void opIndexOpAssign(string op)(int v, Tuple20!(int, int) ) { a = v; }
Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; }
void dummy() {} // nessary to make A1 nested struct
}
struct A2
{
A1 a1;
alias a1 this;
ref int opIndexOpAssign(alias op)(int) { return b; }
}

stompStack();
A2 foo() { return A2(); }
foo()[1..2] += 1;
// ref A1 __tmp = foo().a1; __tmp.opIndexOpAssign!"+"(1, __tmp.opSlice!0(1, 2));
assert(a == 1); // should pass
assert(b == 0);
}

/**************************************/
// 14624

void test14624()
{
struct A1
{
int x;
ref int opIndex() { return x; }
ref int opSlice() { assert(0); }
}
{
A1 a = A1(1);
auto x = a[]; // a.opIndex()
assert(x == a.x);
auto y = -a[]; // -a.opIndex() <-- not found: a.opIndexUnary!"-"
assert(y == -a.x);
a[] = 1; // a.opIndex() = 1; <-- not found: a.opIndexAssign(int)
assert(a.x == 1);
a[] += 1; // a.opIndex() += 1; <-- not found: a.opIndexOpAssign!"+"(int)
assert(a.x == 2);
}

struct A2
{
int x;
ref int opIndex() { x = 10; return x; }
ref int opSlice() { assert(0); }
ref int opSliceUnary(alias op)() { x = 11; return x; }
ref int opSliceAssign(int) { x = 12; return x; }
ref int opSliceOpAssign(alias op)(int) { x = 13; return x; }
}
{
A2 a = A2(1);
auto x = a[]; // a.opIndex()
assert(a.x == 10);
auto y = -a[]; // a.opSliceUnary!"-"() is preferred than: -a.opIndex()
assert(a.x == 11);
a[] = 1; // a.opSliceAssign(1) is preferred than: a.opIndex() = 1;
assert(a.x == 12);
a[] += 1; // a.opSliceOpAssign!"+"(1) is preferred than: a.opIndex() += 1;
assert(a.x == 13);
}
}

/**************************************/
// 14625

void test14625()
{
struct R
{
@property bool empty() { return true; }
@property int front() { return 0; }
void popFront() {}
}

struct C1
{
R opIndex() { return R(); }
R opSlice() { assert(0); }
}
C1 c1;
foreach (e; c1) {} // OK <- asserts in opSlice()
foreach (e; c1[]) {} // OK, opIndex()

struct C2
{
R opIndex() { return R(); }
}
C2 c2;
foreach (e; c2) {} // OK <- rejected
foreach (e; c2[]) {} // OK, opIndex()
}

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

int main()
{
test1();
Expand Down Expand Up @@ -1867,6 +2050,12 @@ int main()
test11062();
test11311();
test14057();
test20a();
test20b();
test20c();
test20d();
test14624();
test14625();

printf("Success\n");
return 0;
Expand Down
20 changes: 20 additions & 0 deletions test/runnable/test14901.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// REQUIRED_ARGS:
// PERMUTE_ARGS: -unittest
// EXTRA_SOURCES: imports/test14901a.d imports/test14901b.d imports/test14901c.d imports/test14901d.d
// COMPILE_SEPARATELY

module test14901;

import imports.test14901c;
import imports.test14901d;

extern(C) __gshared static int initCount;

extern(C) int printf(const char*, ...);

void main()
{
caller1();
caller2();
assert(initCount == 1);
}