Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Issue 7437,7980,8053 - Partial fix for stack overflow with recursive alias this #1028

Merged
merged 8 commits into from

3 participants

@9rnsr
Collaborator

Issue 7437 - DMD enters infinite loop during overload resolution
Issue 7980 - Stack overflow / recursive expansion with alias this
Issue 8053 - Recursive alias this causes infinite loop

Compiler should detect recursive alias this dependencies and avoid stack overflow / infinite loop.

This pull request is not complete (there is still problems around CastExp and castTo), but able to detect many cases.

src/cast.c
@@ -1904,6 +1904,9 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
assert(t1);
Type *t = t1;
+ Type *att1 = NULL;
@WalterBright Owner

Please, some comments for what these variables represent.

@9rnsr Collaborator
9rnsr added a note

OK. WIll do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@WalterBright WalterBright commented on the diff
src/expression.h
@@ -780,6 +781,9 @@ struct BinExp : Expression
Expression *e1;
Expression *e2;
+ Type *att1; // Save alias this type to detect recursion
+ Type *att2; // Save alias this type to detect recursion
@WalterBright Owner

Does this really need to be for every expression?

@9rnsr Collaborator
9rnsr added a note

No. Some expressions

  • FileExp, AssertExp, DelegateExp ... derived from UnaExp
  • IndexExp, RemoveExp ... derived from BinExp

might not need save starting of alias this recursion.
But other expressions always need to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/mtype.c
((18 lines not shown))
+ }
+ else if (tb->ty == Tclass)
+ {
+ TypeClass *tc = (TypeClass *)tb;
+ if (tc->isrec == 2)
+ {
+ if (Type *att = aliasthisOf())
+ tc->isrec = att->implicitConvTo(this) ? 1 : 0;
+ else
+ tc->isrec = 0;
+ }
+ return tc->isrec;
+ }
+ else
+ return 0;
+}
@WalterBright Owner

Should use OOP rather than if-elseif

@9rnsr Collaborator
9rnsr added a note

Just only TypeStruct and TypeClass requre this check, and there is no common super class of the two.
In this case, I'd like to collect codes in one function.
Really need to separate function to TypeStruct::checkAliasThisRec and TypeClass::checkAliasThisRec with adding virtual function call overhead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/mtype.h
@@ -752,6 +753,7 @@ struct TypeReturn : TypeQualified
struct TypeStruct : Type
{
StructDeclaration *sym;
+ int att, isrec;
@WalterBright Owner

what are these for? Comments, please.

@Trass3r
Trass3r added a note

Also looks like isrec should be an enum.

@9rnsr Collaborator
9rnsr added a note

OK. Will do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@WalterBright

Basically, I find the intrusiveness of this a bit intimidating. I hope there's a simpler way.

@9rnsr
Collaborator

Add two commits: improve comments, reduce TypeStruct and TypeClass size with packing field, and use enum.

Basically, I find the intrusiveness of this a bit intimidating. I hope there's a simpler way.

I think this is difficult...

@9rnsr
Collaborator

Now, auto tester fails only in Windows platform, and it is reproduced in my PC.
From error message, it fails in Outbuffer::reserve, but I cannot understand why this occurs...

@WalterBright WalterBright merged commit ef9a73c into D-Programming-Language:master
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
38 src/cast.c
@@ -2047,6 +2047,14 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
assert(t1);
Type *t = t1;
+ /* The start type of alias this type recursion.
+ * In following case, we should save A, and stop recursion
+ * if it appears again.
+ * X -> Y -> [A] -> B -> A -> B -> ...
+ */
+ Type *att1 = NULL;
+ Type *att2 = NULL;
+
//if (t1) printf("\tt1 = %s\n", t1->toChars());
//if (t2) printf("\tt2 = %s\n", t2->toChars());
#ifdef DEBUG
@@ -2350,12 +2358,22 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
}
else if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis)
{
+ if (att1 && e1->type == att1)
+ goto Lincompatible;
+ if (!att1 && e1->type->checkAliasThisRec())
+ att1 = e1->type;
+ //printf("att tmerge(c || c) e1 = %s\n", e1->type->toChars());
e1 = resolveAliasThis(sc, e1);
t1 = e1->type;
continue;
}
else if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis)
{
+ if (att2 && e2->type == att2)
+ goto Lincompatible;
+ if (!att2 && e2->type->checkAliasThisRec())
+ att2 = e2->type;
+ //printf("att tmerge(c || c) e2 = %s\n", e2->type->toChars());
e2 = resolveAliasThis(sc, e2);
t2 = e2->type;
continue;
@@ -2391,11 +2409,21 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
Expression *e2b = NULL;
if (ts2->sym->aliasthis)
{
+ if (att2 && e2->type == att2)
+ goto Lincompatible;
+ if (!att2 && e2->type->checkAliasThisRec())
+ att2 = e2->type;
+ //printf("att tmerge(s && s) e2 = %s\n", e2->type->toChars());
e2b = resolveAliasThis(sc, e2);
i1 = e2b->implicitConvTo(t1);
}
if (ts1->sym->aliasthis)
{
+ if (att1 && e1->type == att1)
+ goto Lincompatible;
+ if (!att1 && e1->type->checkAliasThisRec())
+ att1 = e1->type;
+ //printf("att tmerge(s && s) e1 = %s\n", e1->type->toChars());
e1b = resolveAliasThis(sc, e1);
i2 = e1b->implicitConvTo(t2);
}
@@ -2423,6 +2451,11 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
{
if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis)
{
+ if (att1 && e1->type == att1)
+ goto Lincompatible;
+ if (!att1 && e1->type->checkAliasThisRec())
+ att1 = e1->type;
+ //printf("att tmerge(s || s) e1 = %s\n", e1->type->toChars());
e1 = resolveAliasThis(sc, e1);
t1 = e1->type;
t = t1;
@@ -2430,6 +2463,11 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
}
if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis)
{
+ if (att2 && e2->type == att2)
+ goto Lincompatible;
+ if (!att2 && e2->type->checkAliasThisRec())
+ att2 = e2->type;
+ //printf("att tmerge(s || s) e2 = %s\n", e2->type->toChars());
e2 = resolveAliasThis(sc, e2);
t2 = e2->type;
t = t2;
View
85 src/expression.c
@@ -1949,8 +1949,12 @@ Expression *Expression::checkToBoolean(Scope *sc)
assert(type);
#endif
- // Structs can be converted to bool using opCast(bool)()
+ Expression *e = this;
+ Type *t = type;
Type *tb = type->toBasetype();
+ Type *att = NULL;
+Lagain:
+ // Structs can be converted to bool using opCast(bool)()
if (tb->ty == Tstruct)
{ AggregateDeclaration *ad = ((TypeStruct *)tb)->sym;
/* Don't really need to check for opCast first, but by doing so we
@@ -1959,26 +1963,29 @@ Expression *Expression::checkToBoolean(Scope *sc)
Dsymbol *fd = search_function(ad, Id::cast);
if (fd)
{
- Expression *e = new CastExp(loc, this, Type::tbool);
+ e = new CastExp(loc, e, Type::tbool);
e = e->semantic(sc);
return e;
}
// Forward to aliasthis.
- if (ad->aliasthis)
+ if (ad->aliasthis && tb != att)
{
- Expression *e = resolveAliasThis(sc, this);
- e = e->checkToBoolean(sc);
- return e;
+ if (!att && tb->checkAliasThisRec())
+ att = tb;
+ e = resolveAliasThis(sc, e);
+ t = e->type;
+ tb = e->type->toBasetype();
+ goto Lagain;
}
}
- if (!type->checkBoolean())
- { if (type->toBasetype() != Type::terror)
- error("expression %s of type %s does not have a boolean value", toChars(), type->toChars());
+ if (!t->checkBoolean())
+ { if (tb != Type::terror)
+ error("expression %s of type %s does not have a boolean value", toChars(), t->toChars());
return new ErrorExp();
}
- return this;
+ return e;
}
/****************************
@@ -6179,6 +6186,7 @@ UnaExp::UnaExp(Loc loc, enum TOK op, int size, Expression *e1)
: Expression(loc, op, size)
{
this->e1 = e1;
+ this->att1 = NULL;
}
Expression *UnaExp::syntaxCopy()
@@ -6219,6 +6227,9 @@ BinExp::BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2)
{
this->e1 = e1;
this->e2 = e2;
+
+ this->att1 = NULL;
+ this->att2 = NULL;
}
Expression *BinExp::syntaxCopy()
@@ -8062,8 +8073,10 @@ Expression *CallExp::semantic(Scope *sc)
if (e1->op != TOKtype)
{
- if (ad->aliasthis)
+ if (ad->aliasthis && e1->type != att1)
{
+ if (!att1 && e1->type->checkAliasThisRec())
+ att1 = e1->type;
e1 = resolveAliasThis(sc, e1);
goto Lagain;
}
@@ -9524,8 +9537,10 @@ Expression *SliceExp::semantic(Scope *sc)
e = e->semantic(sc);
return e;
}
- if (ad->aliasthis)
+ if (ad->aliasthis && e1->type != att1)
{
+ if (!att1 && e1->type->checkAliasThisRec())
+ att1 = e1->type;
e1 = resolveAliasThis(sc, e1);
goto Lagain;
}
@@ -10384,6 +10399,7 @@ Expression *AssignExp::semantic(Scope *sc)
ae->e1 = ae->e1->semantic(sc);
ae->e1 = resolveProperties(sc, ae->e1);
+ Expression *e1 = ae->e1;
Type *t1 = ae->e1->type->toBasetype();
if (t1->ty == Tstruct)
{
@@ -10402,7 +10418,7 @@ Expression *AssignExp::semantic(Scope *sc)
Expressions *a = (Expressions *)ae->arguments->copy();
a->insert(0, e2);
- Expression *e = new DotIdExp(loc, ae->e1, Id::indexass);
+ Expression *e = new DotIdExp(loc, e1, Id::indexass);
e = new CallExp(loc, e, a);
e = e->semantic(sc);
return e;
@@ -10410,18 +10426,20 @@ Expression *AssignExp::semantic(Scope *sc)
}
// No opIndexAssign found yet, but there might be an alias this to try.
- if (ad && ad->aliasthis)
- { Expression *e = resolveAliasThis(sc, ae->e1);
- Type *t = e->type->toBasetype();
-
- if (t->ty == Tstruct)
+ if (ad && ad->aliasthis && t1 != att1)
+ {
+ if (!att1 && t1->checkAliasThisRec())
+ att1 = t1;
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1->type->toBasetype();
+ if (t1->ty == Tstruct)
{
- ad = ((TypeStruct *)t)->sym;
+ ad = ((TypeStruct *)t1)->sym;
goto L1;
}
- else if (t->ty == Tclass)
+ else if (t1->ty == Tclass)
{
- ad = ((TypeClass *)t)->sym;
+ ad = ((TypeClass *)t1)->sym;
goto L1;
}
}
@@ -10431,14 +10449,15 @@ Expression *AssignExp::semantic(Scope *sc)
* converted to a.opSlice() already.
*/
if (e1->op == TOKslice)
- { Type *t1;
+ {
SliceExp *ae = (SliceExp *)e1;
AggregateDeclaration *ad = NULL;
Identifier *id = Id::index;
ae->e1 = ae->e1->semantic(sc);
ae->e1 = resolveProperties(sc, ae->e1);
- t1 = ae->e1->type->toBasetype();
+ Expression *e1 = ae->e1;
+ Type *t1 = ae->e1->type->toBasetype();
if (t1->ty == Tstruct)
{
ad = ((TypeStruct *)t1)->sym;
@@ -10459,7 +10478,7 @@ Expression *AssignExp::semantic(Scope *sc)
{ a->push(ae->lwr);
a->push(ae->upr);
}
- Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass);
+ Expression *e = new DotIdExp(loc, e1, Id::sliceass);
e = new CallExp(loc, e, a);
e = e->semantic(sc);
return e;
@@ -10467,18 +10486,20 @@ Expression *AssignExp::semantic(Scope *sc)
}
// No opSliceAssign found yet, but there might be an alias this to try.
- if (ad && ad->aliasthis)
- { Expression *e = resolveAliasThis(sc, ae->e1);
- Type *t = e->type->toBasetype();
-
- if (t->ty == Tstruct)
+ if (ad && ad->aliasthis && t1 != att1)
+ {
+ if (!att1 && t1->checkAliasThisRec())
+ att1 = t1;
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1->type->toBasetype();
+ if (t1->ty == Tstruct)
{
- ad = ((TypeStruct *)t)->sym;
+ ad = ((TypeStruct *)t1)->sym;
goto L2;
}
- else if (t->ty == Tclass)
+ else if (t1->ty == Tclass)
{
- ad = ((TypeClass *)t)->sym;
+ ad = ((TypeClass *)t1)->sym;
goto L2;
}
}
View
4 src/expression.h
@@ -769,6 +769,7 @@ struct IsExp : Expression
struct UnaExp : Expression
{
Expression *e1;
+ Type *att1; // Save alias this type to detect recursion
UnaExp(Loc loc, enum TOK op, int size, Expression *e1);
Expression *syntaxCopy();
@@ -792,6 +793,9 @@ struct BinExp : Expression
Expression *e1;
Expression *e2;
+ Type *att1; // Save alias this type to detect recursion
+ Type *att2; // Save alias this type to detect recursion
@WalterBright Owner

Does this really need to be for every expression?

@9rnsr Collaborator
9rnsr added a note

No. Some expressions

  • FileExp, AssertExp, DelegateExp ... derived from UnaExp
  • IndexExp, RemoveExp ... derived from BinExp

might not need save starting of alias this recursion.
But other expressions always need to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2);
Expression *syntaxCopy();
int apply(apply_fp_t fp, void *param);
View
60 src/mtype.c
@@ -1368,6 +1368,27 @@ Type *Type::aliasthisOf()
return NULL;
}
+int Type::checkAliasThisRec()
+{
+ Type *tb = toBasetype();
+ enum AliasThisRec* pflag;
+ if (tb->ty == Tstruct)
+ pflag = &((TypeStruct *)tb)->att;
+ else if (tb->ty == Tclass)
+ pflag = &((TypeClass *)tb)->att;
+ else
+ return 0;
+
+ enum AliasThisRec flag = (enum AliasThisRec)(*pflag & ~RECtracing);
+ if (flag == RECfwdref)
+ {
+ Type *att = aliasthisOf();
+ flag = att && att->implicitConvTo(this) ? RECyes : RECno;
+ }
+ *pflag = (AliasThisRec)(flag | (*pflag & RECtracing));
+ return flag == RECyes;
+}
+
Dsymbol *Type::toDsymbol(Scope *sc)
{
return NULL;
@@ -7911,6 +7932,7 @@ TypeStruct::TypeStruct(StructDeclaration *sym)
: Type(Tstruct)
{
this->sym = sym;
+ this->att = RECfwdref;
}
const char *TypeStruct::kind()
@@ -8415,8 +8437,12 @@ MATCH TypeStruct::implicitConvTo(Type *to)
}
}
}
- else if (sym->aliasthis)
+ else if (sym->aliasthis && !(att & RECtracing))
+ {
+ att = (AliasThisRec)(att | RECtracing);
m = aliasthisOf()->implicitConvTo(to);
+ att = (AliasThisRec)(att & ~RECtracing);
+ }
else
m = MATCHnomatch; // no match
return m;
@@ -8437,13 +8463,16 @@ unsigned TypeStruct::wildConvTo(Type *tprm)
if (ty == tprm->ty && sym == ((TypeStruct *)tprm)->sym)
return Type::wildConvTo(tprm);
- if (sym->aliasthis)
- { Type *t = aliasthisOf();
- assert(t);
- return t->wildConvTo(tprm);
+ unsigned mod = 0;
+
+ if (sym->aliasthis && !(att & RECtracing))
+ {
+ att = (AliasThisRec)(att | RECtracing);
+ mod = aliasthisOf()->wildConvTo(tprm);
+ att = (AliasThisRec)(att & ~RECtracing);
}
- return 0;
+ return mod;
}
Type *TypeStruct::toHeadMutable()
@@ -8458,6 +8487,7 @@ TypeClass::TypeClass(ClassDeclaration *sym)
: Type(Tclass)
{
this->sym = sym;
+ this->att = RECfwdref;
}
const char *TypeClass::kind()
@@ -8936,8 +8966,12 @@ MATCH TypeClass::implicitConvTo(Type *to)
}
m = MATCHnomatch;
- if (sym->aliasthis)
+ if (sym->aliasthis && !(att & RECtracing))
+ {
+ att = (AliasThisRec)(att | RECtracing);
m = aliasthisOf()->implicitConvTo(to);
+ att = (AliasThisRec)(att & ~RECtracing);
+ }
return m;
}
@@ -8970,10 +9004,16 @@ unsigned TypeClass::wildConvTo(Type *tprm)
if (cdprm && cdprm->isBaseOf(sym, NULL))
return Type::wildConvTo(tprm);
- if (sym->aliasthis)
- return aliasthisOf()->wildConvTo(tprm);
+ unsigned mod = 0;
- return 0;
+ if (sym->aliasthis && !(att & RECtracing))
+ {
+ att = (AliasThisRec)(att | RECtracing);
+ mod = aliasthisOf()->wildConvTo(tprm);
+ att = (AliasThisRec)(att & ~RECtracing);
+ }
+
+ return mod;
}
Type *TypeClass::toHeadMutable()
View
13 src/mtype.h
@@ -289,6 +289,7 @@ struct Type : Object
Type *referenceTo();
Type *arrayOf();
Type *aliasthisOf();
+ int checkAliasThisRec();
virtual Type *makeConst();
virtual Type *makeInvariant();
virtual Type *makeShared();
@@ -789,9 +790,20 @@ struct TypeReturn : TypeQualified
void toJson(JsonOut *json);
};
+// Whether alias this dependency is recursive or not.
+enum AliasThisRec
+{
+ RECno = 0, // no alias this recursion
+ RECyes = 1, // alias this has recursive dependency
+ RECfwdref = 2, // not yet known
+
+ RECtracing = 0x4, // mark in progress of implicitConvTo/wildConvTo
+};
+
struct TypeStruct : Type
{
StructDeclaration *sym;
+ enum AliasThisRec att;
TypeStruct(StructDeclaration *sym);
const char *kind();
@@ -927,6 +939,7 @@ struct TypeTypedef : Type
struct TypeClass : Type
{
ClassDeclaration *sym;
+ enum AliasThisRec att;
TypeClass(ClassDeclaration *sym);
const char *kind();
View
156 src/opover.c
@@ -245,18 +245,21 @@ Expression *UnaExp::op_overload(Scope *sc)
}
// Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
+ if (ad->aliasthis && ae->e1->type != att1)
{
/* Rewrite op(a[arguments]) as:
* op(a.aliasthis[arguments])
*/
Expression *e1 = ae->copy();
((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident);
- Expression *e = copy();
- ((UnaExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ UnaExp *ue = (UnaExp *)copy();
+ if (!ue->att1 && ae->e1->type->checkAliasThisRec())
+ ue->att1 = ae->e1->type;
+ ue->e1 = e1;
+ if (Expression *e = ue->trySemantic(sc))
+ return e;
}
+ att1 = NULL;
}
}
else if (e1->op == TOKslice)
@@ -289,18 +292,21 @@ Expression *UnaExp::op_overload(Scope *sc)
}
// Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
+ if (ad->aliasthis && se->e1->type != att1)
{
/* Rewrite op(a[lwr..upr]) as:
* op(a.aliasthis[lwr..upr])
*/
Expression *e1 = se->copy();
((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident);
- Expression *e = copy();
- ((UnaExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ UnaExp *ue = (UnaExp *)copy();
+ if (!ue->att1 && se->e1->type->checkAliasThisRec())
+ ue->att1 = se->e1->type;
+ ue->e1 = e1;
+ if (Expression *e = ue->trySemantic(sc))
+ return e;
}
+ att1 = NULL;
}
}
#endif
@@ -352,16 +358,18 @@ Expression *UnaExp::op_overload(Scope *sc)
}
// Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
+ if (ad->aliasthis && this->e1->type != att1)
{
/* Rewrite op(e1) as:
* op(e1.aliasthis)
*/
+ //printf("att una %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars());
Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident);
- Expression *e = copy();
- ((UnaExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ UnaExp *ue = (UnaExp *)copy();
+ if (!ue->att1 && this->e1->type->checkAliasThisRec())
+ ue->att1 = this->e1->type;
+ ue->e1 = e1;
+ return ue->trySemantic(sc);
}
#endif
}
@@ -388,16 +396,18 @@ Expression *ArrayExp::op_overload(Scope *sc)
}
// Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
+ if (ad->aliasthis && this->e1->type != att1)
{
/* Rewrite op(e1) as:
* op(e1.aliasthis)
*/
+ //printf("att arr e1 = %s\n", this->e1->type->toChars());
Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident);
- Expression *e = copy();
- ((UnaExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ UnaExp *ue = (UnaExp *)copy();
+ if (!ue->att1 && this->e1->type->checkAliasThisRec())
+ ue->att1 = this->e1->type;
+ ue->e1 = e1;
+ return ue->trySemantic(sc);
}
}
return NULL;
@@ -700,11 +710,15 @@ Expression *BinExp::op_overload(Scope *sc)
/* Rewrite (e1 op e2) as:
* (e1.aliasthis op e2)
*/
+ if (att1 && this->e1->type == att1)
+ return NULL;
+ //printf("att bin e1 = %s\n", this->e1->type->toChars());
Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident);
- Expression *e = copy();
- ((BinExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att1 && this->e1->type->checkAliasThisRec())
+ be->att1 = this->e1->type;
+ be->e1 = e1;
+ return be->trySemantic(sc);
}
// Try alias this on second operand
@@ -717,11 +731,15 @@ Expression *BinExp::op_overload(Scope *sc)
/* Rewrite (e1 op e2) as:
* (e1 op e2.aliasthis)
*/
+ if (att2 && this->e2->type == att2)
+ return NULL;
+ //printf("att bin e2 = %s\n", this->e2->type->toChars());
Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident);
- Expression *e = copy();
- ((BinExp *)e)->e2 = e2;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att2 && this->e2->type->checkAliasThisRec())
+ be->att2 = this->e2->type;
+ be->e2 = e2;
+ return be->trySemantic(sc);
}
#endif
return NULL;
@@ -873,11 +891,15 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id)
/* Rewrite (e1 op e2) as:
* (e1.aliasthis op e2)
*/
+ if (att1 && this->e1->type == att1)
+ return NULL;
+ //printf("att cmp_bin e1 = %s\n", this->e1->type->toChars());
Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident);
- Expression *e = copy();
- ((BinExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att1 && this->e1->type->checkAliasThisRec())
+ be->att1 = this->e1->type;
+ be->e1 = e1;
+ return be->trySemantic(sc);
}
// Try alias this on second operand
@@ -886,11 +908,15 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id)
/* Rewrite (e1 op e2) as:
* (e1 op e2.aliasthis)
*/
+ if (att2 && this->e2->type == att2)
+ return NULL;
+ //printf("att cmp_bin e2 = %s\n", this->e2->type->toChars());
Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident);
- Expression *e = copy();
- ((BinExp *)e)->e2 = e2;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att2 && this->e2->type->checkAliasThisRec())
+ be->att2 = this->e2->type;
+ be->e2 = e2;
+ return be->trySemantic(sc);
}
return NULL;
@@ -978,18 +1004,21 @@ Expression *BinAssignExp::op_overload(Scope *sc)
}
// Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
+ if (ad->aliasthis && ae->e1->type != att1)
{
/* Rewrite a[arguments] op= e2 as:
* a.aliasthis[arguments] op= e2
*/
Expression *e1 = ae->copy();
((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident);
- Expression *e = copy();
- ((UnaExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att1 && ae->e1->type->checkAliasThisRec())
+ be->att1 = ae->e1->type;
+ be->e1 = e1;
+ if (Expression *e = be->trySemantic(sc))
+ return e;
}
+ att1 = NULL;
}
}
else if (e1->op == TOKslice)
@@ -1024,18 +1053,21 @@ Expression *BinAssignExp::op_overload(Scope *sc)
}
// Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
+ if (ad->aliasthis && se->e1->type != att1)
{
/* Rewrite a[lwr..upr] op= e2 as:
* a.aliasthis[lwr..upr] op= e2
*/
Expression *e1 = se->copy();
((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident);
- Expression *e = copy();
- ((UnaExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att1 && se->e1->type->checkAliasThisRec())
+ be->att1 = se->e1->type;
+ be->e1 = e1;
+ if (Expression *e = be->trySemantic(sc))
+ return e;
}
+ att1 = NULL;
}
}
#endif
@@ -1134,11 +1166,15 @@ Expression *BinAssignExp::op_overload(Scope *sc)
/* Rewrite (e1 op e2) as:
* (e1.aliasthis op e2)
*/
+ if (att1 && this->e1->type == att1)
+ return NULL;
+ //printf("att %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars());
Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident);
- Expression *e = copy();
- ((BinExp *)e)->e1 = e1;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att1 && this->e1->type->checkAliasThisRec())
+ be->att1 = this->e1->type;
+ be->e1 = e1;
+ return be->trySemantic(sc);
}
// Try alias this on second operand
@@ -1148,11 +1184,15 @@ Expression *BinAssignExp::op_overload(Scope *sc)
/* Rewrite (e1 op e2) as:
* (e1 op e2.aliasthis)
*/
+ if (att2 && this->e2->type == att2)
+ return NULL;
+ //printf("att %s e2 = %s\n", Token::toChars(op), this->e2->type->toChars());
Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident);
- Expression *e = copy();
- ((BinExp *)e)->e2 = e2;
- e = e->trySemantic(sc);
- return e;
+ BinExp *be = (BinExp *)copy();
+ if (!be->att2 && this->e2->type->checkAliasThisRec())
+ be->att2 = this->e2->type;
+ be->e2 = e2;
+ return be->trySemantic(sc);
}
#endif
return NULL;
@@ -1219,6 +1259,8 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply)
int sliced = 0;
#endif
Type *tab;
+ Type *att = NULL;
+ Expression *org_aggr = aggr;
AggregateDeclaration *ad;
while (1)
@@ -1230,6 +1272,10 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply)
goto Lerr;
tab = aggr->type->toBasetype();
+ if (att == tab)
+ { aggr = org_aggr;
+ goto Lerr;
+ }
switch (tab->ty)
{
case Tarray:
@@ -1275,6 +1321,8 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply)
if (ad->aliasthis)
{
+ if (!att && tab->checkAliasThisRec())
+ att = tab;
aggr = new DotIdExp(aggr->loc, aggr, ad->aliasthis->ident);
continue;
}
View
158 test/runnable/aliasthis.d
@@ -186,6 +186,163 @@ void test6() {
}
/**********************************************/
+// recursive alias this detection
+
+class C0 {}
+
+class C1 { C2 c; alias c this; }
+class C2 { C1 c; alias c this; }
+
+class C3 { C2 c; alias c this; }
+
+struct S0 {}
+
+struct S1 { S2* ps; @property ref get(){return *ps;} alias get this; }
+struct S2 { S1* ps; @property ref get(){return *ps;} alias get this; }
+
+struct S3 { S2* ps; @property ref get(){return *ps;} alias get this; }
+
+struct S4 { S5* ps; @property ref get(){return *ps;} alias get this; }
+struct S5 { S4* ps; @property ref get(){return *ps;} alias get this; }
+
+struct S6 { S5* ps; @property ref get(){return *ps;} alias get this; }
+
+void test7()
+{
+ // Able to check a type is implicitly convertible within a finite time.
+ static assert(!is(C1 : C0));
+ static assert( is(C2 : C1));
+ static assert( is(C1 : C2));
+ static assert(!is(C3 : C0));
+ static assert( is(C3 : C1));
+ static assert( is(C3 : C2));
+
+ static assert(!is(S1 : S0));
+ static assert( is(S2 : S1));
+ static assert( is(S1 : S2));
+ static assert(!is(S3 : S0));
+ static assert( is(S3 : S1));
+ static assert( is(S3 : S2));
+
+ C0 c0; C1 c1; C3 c3;
+ S0 s0; S1 s1; S3 s3; S4 s4; S6 s6;
+
+ // Allow merging types that contains alias this recursion.
+ static assert( __traits(compiles, c0 is c1)); // typeMerge(c || c) e2->implicitConvTo(t1);
+ static assert( __traits(compiles, c0 is c3)); // typeMerge(c || c) e2->implicitConvTo(t1);
+ static assert( __traits(compiles, c1 is c0)); // typeMerge(c || c) e1->implicitConvTo(t2);
+ static assert( __traits(compiles, c3 is c0)); // typeMerge(c || c) e1->implicitConvTo(t2);
+ static assert(!__traits(compiles, s1 is c0)); // typeMerge(c || c) e1
+ static assert(!__traits(compiles, s3 is c0)); // typeMerge(c || c) e1
+ static assert(!__traits(compiles, c0 is s1)); // typeMerge(c || c) e2
+ static assert(!__traits(compiles, c0 is s3)); // typeMerge(c || c) e2
+
+ static assert(!__traits(compiles, s1 is s0)); // typeMerge(s && s) e1
+ static assert(!__traits(compiles, s3 is s0)); // typeMerge(s && s) e1
+ static assert(!__traits(compiles, s0 is s1)); // typeMerge(s && s) e2
+ static assert(!__traits(compiles, s0 is s3)); // typeMerge(s && s) e2
+ static assert(!__traits(compiles, s1 is s4)); // typeMerge(s && s) e1 + e2
+ static assert(!__traits(compiles, s3 is s6)); // typeMerge(s && s) e1 + e2
+
+ static assert(!__traits(compiles, s1 is 10)); // typeMerge(s || s) e1
+ static assert(!__traits(compiles, s3 is 10)); // typeMerge(s || s) e1
+ static assert(!__traits(compiles, 10 is s1)); // typeMerge(s || s) e2
+ static assert(!__traits(compiles, 10 is s3)); // typeMerge(s || s) e2
+
+ // SliceExp::semantic
+ static assert(!__traits(compiles, c1[]));
+ static assert(!__traits(compiles, c3[]));
+ static assert(!__traits(compiles, s1[]));
+ static assert(!__traits(compiles, s3[]));
+
+ // CallExp::semantic
+// static assert(!__traits(compiles, c1()));
+// static assert(!__traits(compiles, c3()));
+ static assert(!__traits(compiles, s1()));
+ static assert(!__traits(compiles, s3()));
+
+ // AssignExp::semantic
+ static assert(!__traits(compiles, { c1[1] = 0; }));
+ static assert(!__traits(compiles, { c3[1] = 0; }));
+ static assert(!__traits(compiles, { s1[1] = 0; }));
+ static assert(!__traits(compiles, { s3[1] = 0; }));
+ static assert(!__traits(compiles, { c1[ ] = 0; }));
+ static assert(!__traits(compiles, { c3[ ] = 0; }));
+ static assert(!__traits(compiles, { s1[ ] = 0; }));
+ static assert(!__traits(compiles, { s3[ ] = 0; }));
+
+ // UnaExp::op_overload
+ static assert(!__traits(compiles, +c1[1]));
+ static assert(!__traits(compiles, +c3[1]));
+ static assert(!__traits(compiles, +s1[1]));
+ static assert(!__traits(compiles, +s3[1]));
+ static assert(!__traits(compiles, +c1[ ]));
+ static assert(!__traits(compiles, +c3[ ]));
+ static assert(!__traits(compiles, +s1[ ]));
+ static assert(!__traits(compiles, +s3[ ]));
+ static assert(!__traits(compiles, +c1));
+ static assert(!__traits(compiles, +c3));
+ static assert(!__traits(compiles, +s1));
+ static assert(!__traits(compiles, +s3));
+
+ // ArrayExp::op_overload
+ static assert(!__traits(compiles, c1[1]));
+ static assert(!__traits(compiles, c3[1]));
+ static assert(!__traits(compiles, s1[1]));
+ static assert(!__traits(compiles, s3[1]));
+
+ // BinExp::op_overload
+ static assert(!__traits(compiles, c1 + 10)); // e1
+ static assert(!__traits(compiles, c3 + 10)); // e1
+ static assert(!__traits(compiles, 10 + c1)); // e2
+ static assert(!__traits(compiles, 10 + c3)); // e2
+ static assert(!__traits(compiles, s1 + 10)); // e1
+ static assert(!__traits(compiles, s3 + 10)); // e1
+ static assert(!__traits(compiles, 10 + s1)); // e2
+ static assert(!__traits(compiles, 10 + s3)); // e2
+
+ // BinExp::compare_overload
+ static assert(!__traits(compiles, c1 < 10)); // (Object.opCmp(int) is invalid)
+ static assert(!__traits(compiles, c3 < 10)); // (Object.opCmp(int) is invalid)
+ static assert(!__traits(compiles, 10 < c1)); // (Object.opCmp(int) is invalid)
+ static assert(!__traits(compiles, 10 < c3)); // (Object.opCmp(int) is invalid)
+ static assert(!__traits(compiles, s1 < 10)); // e1
+ static assert(!__traits(compiles, s3 < 10)); // e1
+ static assert(!__traits(compiles, 10 < s1)); // e2
+ static assert(!__traits(compiles, 10 < s3)); // e2
+
+ // BinAssignExp::op_overload
+ static assert(!__traits(compiles, c1[1] += 1));
+ static assert(!__traits(compiles, c3[1] += 1));
+ static assert(!__traits(compiles, s1[1] += 1));
+ static assert(!__traits(compiles, s3[1] += 1));
+ static assert(!__traits(compiles, c1[ ] += 1));
+ static assert(!__traits(compiles, c3[ ] += 1));
+ static assert(!__traits(compiles, s1[ ] += 1));
+ static assert(!__traits(compiles, s3[ ] += 1));
+ static assert(!__traits(compiles, c1 += c0)); // e1
+ static assert(!__traits(compiles, c3 += c0)); // e1
+ static assert(!__traits(compiles, s1 += s0)); // e1
+ static assert(!__traits(compiles, s3 += s0)); // e1
+ static assert(!__traits(compiles, c0 += c1)); // e2
+ static assert(!__traits(compiles, c0 += c3)); // e2
+ static assert(!__traits(compiles, s0 += s1)); // e2
+ static assert(!__traits(compiles, s0 += s3)); // e2
+ static assert(!__traits(compiles, c1 += s1)); // e1 + e2
+ static assert(!__traits(compiles, c3 += s3)); // e1 + e2
+
+ // ForeachStatement::inferAggregate
+ static assert(!__traits(compiles, { foreach (e; s1){} }));
+ static assert(!__traits(compiles, { foreach (e; s3){} }));
+ static assert(!__traits(compiles, { foreach (e; c1){} }));
+ static assert(!__traits(compiles, { foreach (e; c3){} }));
+
+ // Expression::checkToBoolean
+ static assert(!__traits(compiles, { if (s1){} }));
+ static assert(!__traits(compiles, { if (s3){} }));
+}
+
+/***************************************************/
// 2781
struct Tuple2781a(T...) {
@@ -901,6 +1058,7 @@ int main()
test4773();
test5188();
test6();
+ test7();
test2781();
test6546();
test6736();
Something went wrong with that request. Please try again.