Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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

Merged
merged 8 commits into from about 1 year ago

3 participants

Hara Kenji Walter Bright Trass3r
Hara Kenji
Collaborator
9rnsr commented

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
1904 1904
     assert(t1);
1905 1905
     Type *t = t1;
1906 1906
 
  1907
+    Type *att1 = NULL;
2
Walter Bright Owner

Please, some comments for what these variables represent.

Hara Kenji 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
Walter Bright WalterBright commented on the diff
src/expression.h
... ...
@@ -780,6 +781,9 @@ struct BinExp : Expression
780 781
     Expression *e1;
781 782
     Expression *e2;
782 783
 
  784
+    Type *att1; // Save alias this type to detect recursion
  785
+    Type *att2; // Save alias this type to detect recursion
2
Walter Bright Owner

Does this really need to be for every expression?

Hara Kenji 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))
  1362
+    }
  1363
+    else if (tb->ty == Tclass)
  1364
+    {
  1365
+        TypeClass *tc = (TypeClass *)tb;
  1366
+        if (tc->isrec == 2)
  1367
+        {
  1368
+            if (Type *att = aliasthisOf())
  1369
+                tc->isrec = att->implicitConvTo(this) ? 1 : 0;
  1370
+            else
  1371
+                tc->isrec = 0;
  1372
+        }
  1373
+        return tc->isrec;
  1374
+    }
  1375
+    else
  1376
+        return 0;
  1377
+}
2
Walter Bright Owner

Should use OOP rather than if-elseif

Hara Kenji 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
752 753
 struct TypeStruct : Type
753 754
 {
754 755
     StructDeclaration *sym;
  756
+    int att, isrec;
3
Walter Bright Owner

what are these for? Comments, please.

Trass3r
Trass3r added a note

Also looks like isrec should be an enum.

Hara Kenji 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
Walter Bright
Owner

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

Hara Kenji
Collaborator
9rnsr commented

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...

Hara Kenji
Collaborator
9rnsr commented

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...

Walter Bright WalterBright merged commit ef9a73c into from
Walter Bright WalterBright closed this
Deleted user Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Deleted user Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Deleted user Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Deleted user 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.
38  src/cast.c
@@ -2047,6 +2047,14 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
2047 2047
     assert(t1);
2048 2048
     Type *t = t1;
2049 2049
 
  2050
+    /* The start type of alias this type recursion.
  2051
+     * In following case, we should save A, and stop recursion
  2052
+     * if it appears again.
  2053
+     *      X -> Y -> [A] -> B -> A -> B -> ...
  2054
+     */
  2055
+    Type *att1 = NULL;
  2056
+    Type *att2 = NULL;
  2057
+
2050 2058
     //if (t1) printf("\tt1 = %s\n", t1->toChars());
2051 2059
     //if (t2) printf("\tt2 = %s\n", t2->toChars());
2052 2060
 #ifdef DEBUG
@@ -2350,12 +2358,22 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
2350 2358
             }
2351 2359
             else if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis)
2352 2360
             {
  2361
+                if (att1 && e1->type == att1)
  2362
+                    goto Lincompatible;
  2363
+                if (!att1 && e1->type->checkAliasThisRec())
  2364
+                    att1 = e1->type;
  2365
+                //printf("att tmerge(c || c) e1 = %s\n", e1->type->toChars());
2353 2366
                 e1 = resolveAliasThis(sc, e1);
2354 2367
                 t1 = e1->type;
2355 2368
                 continue;
2356 2369
             }
2357 2370
             else if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis)
2358 2371
             {
  2372
+                if (att2 && e2->type == att2)
  2373
+                    goto Lincompatible;
  2374
+                if (!att2 && e2->type->checkAliasThisRec())
  2375
+                    att2 = e2->type;
  2376
+                //printf("att tmerge(c || c) e2 = %s\n", e2->type->toChars());
2359 2377
                 e2 = resolveAliasThis(sc, e2);
2360 2378
                 t2 = e2->type;
2361 2379
                 continue;
@@ -2391,11 +2409,21 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
2391 2409
             Expression *e2b = NULL;
2392 2410
             if (ts2->sym->aliasthis)
2393 2411
             {
  2412
+                if (att2 && e2->type == att2)
  2413
+                    goto Lincompatible;
  2414
+                if (!att2 && e2->type->checkAliasThisRec())
  2415
+                    att2 = e2->type;
  2416
+                //printf("att tmerge(s && s) e2 = %s\n", e2->type->toChars());
2394 2417
                 e2b = resolveAliasThis(sc, e2);
2395 2418
                 i1 = e2b->implicitConvTo(t1);
2396 2419
             }
2397 2420
             if (ts1->sym->aliasthis)
2398 2421
             {
  2422
+                if (att1 && e1->type == att1)
  2423
+                    goto Lincompatible;
  2424
+                if (!att1 && e1->type->checkAliasThisRec())
  2425
+                    att1 = e1->type;
  2426
+                //printf("att tmerge(s && s) e1 = %s\n", e1->type->toChars());
2399 2427
                 e1b = resolveAliasThis(sc, e1);
2400 2428
                 i2 = e1b->implicitConvTo(t2);
2401 2429
             }
@@ -2423,6 +2451,11 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
2423 2451
     {
2424 2452
         if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis)
2425 2453
         {
  2454
+            if (att1 && e1->type == att1)
  2455
+                goto Lincompatible;
  2456
+            if (!att1 && e1->type->checkAliasThisRec())
  2457
+                att1 = e1->type;
  2458
+            //printf("att tmerge(s || s) e1 = %s\n", e1->type->toChars());
2426 2459
             e1 = resolveAliasThis(sc, e1);
2427 2460
             t1 = e1->type;
2428 2461
             t = t1;
@@ -2430,6 +2463,11 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression
2430 2463
         }
2431 2464
         if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis)
2432 2465
         {
  2466
+            if (att2 && e2->type == att2)
  2467
+                goto Lincompatible;
  2468
+            if (!att2 && e2->type->checkAliasThisRec())
  2469
+                att2 = e2->type;
  2470
+            //printf("att tmerge(s || s) e2 = %s\n", e2->type->toChars());
2433 2471
             e2 = resolveAliasThis(sc, e2);
2434 2472
             t2 = e2->type;
2435 2473
             t = t2;
85  src/expression.c
@@ -1949,8 +1949,12 @@ Expression *Expression::checkToBoolean(Scope *sc)
1949 1949
     assert(type);
1950 1950
 #endif
1951 1951
 
1952  
-    // Structs can be converted to bool using opCast(bool)()
  1952
+    Expression *e = this;
  1953
+    Type *t = type;
1953 1954
     Type *tb = type->toBasetype();
  1955
+    Type *att = NULL;
  1956
+Lagain:
  1957
+    // Structs can be converted to bool using opCast(bool)()
1954 1958
     if (tb->ty == Tstruct)
1955 1959
     {   AggregateDeclaration *ad = ((TypeStruct *)tb)->sym;
1956 1960
         /* Don't really need to check for opCast first, but by doing so we
@@ -1959,26 +1963,29 @@ Expression *Expression::checkToBoolean(Scope *sc)
1959 1963
         Dsymbol *fd = search_function(ad, Id::cast);
1960 1964
         if (fd)
1961 1965
         {
1962  
-            Expression *e = new CastExp(loc, this, Type::tbool);
  1966
+            e = new CastExp(loc, e, Type::tbool);
1963 1967
             e = e->semantic(sc);
1964 1968
             return e;
1965 1969
         }
1966 1970
 
1967 1971
         // Forward to aliasthis.
1968  
-        if (ad->aliasthis)
  1972
+        if (ad->aliasthis && tb != att)
1969 1973
         {
1970  
-            Expression *e = resolveAliasThis(sc, this);
1971  
-            e = e->checkToBoolean(sc);
1972  
-            return e;
  1974
+            if (!att && tb->checkAliasThisRec())
  1975
+                att = tb;
  1976
+            e = resolveAliasThis(sc, e);
  1977
+            t = e->type;
  1978
+            tb = e->type->toBasetype();
  1979
+            goto Lagain;
1973 1980
         }
1974 1981
     }
1975 1982
 
1976  
-    if (!type->checkBoolean())
1977  
-    {   if (type->toBasetype() != Type::terror)
1978  
-            error("expression %s of type %s does not have a boolean value", toChars(), type->toChars());
  1983
+    if (!t->checkBoolean())
  1984
+    {   if (tb != Type::terror)
  1985
+            error("expression %s of type %s does not have a boolean value", toChars(), t->toChars());
1979 1986
         return new ErrorExp();
1980 1987
     }
1981  
-    return this;
  1988
+    return e;
1982 1989
 }
1983 1990
 
1984 1991
 /****************************
@@ -6179,6 +6186,7 @@ UnaExp::UnaExp(Loc loc, enum TOK op, int size, Expression *e1)
6179 6186
         : Expression(loc, op, size)
6180 6187
 {
6181 6188
     this->e1 = e1;
  6189
+    this->att1 = NULL;
6182 6190
 }
6183 6191
 
6184 6192
 Expression *UnaExp::syntaxCopy()
@@ -6219,6 +6227,9 @@ BinExp::BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2)
6219 6227
 {
6220 6228
     this->e1 = e1;
6221 6229
     this->e2 = e2;
  6230
+
  6231
+    this->att1 = NULL;
  6232
+    this->att2 = NULL;
6222 6233
 }
6223 6234
 
6224 6235
 Expression *BinExp::syntaxCopy()
@@ -8062,8 +8073,10 @@ Expression *CallExp::semantic(Scope *sc)
8062 8073
 
8063 8074
             if (e1->op != TOKtype)
8064 8075
             {
8065  
-                if (ad->aliasthis)
  8076
+                if (ad->aliasthis && e1->type != att1)
8066 8077
                 {
  8078
+                    if (!att1 && e1->type->checkAliasThisRec())
  8079
+                        att1 = e1->type;
8067 8080
                     e1 = resolveAliasThis(sc, e1);
8068 8081
                     goto Lagain;
8069 8082
                 }
@@ -9524,8 +9537,10 @@ Expression *SliceExp::semantic(Scope *sc)
9524 9537
             e = e->semantic(sc);
9525 9538
             return e;
9526 9539
         }
9527  
-        if (ad->aliasthis)
  9540
+        if (ad->aliasthis && e1->type != att1)
9528 9541
         {
  9542
+            if (!att1 && e1->type->checkAliasThisRec())
  9543
+                att1 = e1->type;
9529 9544
             e1 = resolveAliasThis(sc, e1);
9530 9545
             goto Lagain;
9531 9546
         }
@@ -10384,6 +10399,7 @@ Expression *AssignExp::semantic(Scope *sc)
10384 10399
 
10385 10400
         ae->e1 = ae->e1->semantic(sc);
10386 10401
         ae->e1 = resolveProperties(sc, ae->e1);
  10402
+        Expression *e1 = ae->e1;
10387 10403
         Type *t1 = ae->e1->type->toBasetype();
10388 10404
         if (t1->ty == Tstruct)
10389 10405
         {
@@ -10402,7 +10418,7 @@ Expression *AssignExp::semantic(Scope *sc)
10402 10418
                 Expressions *a = (Expressions *)ae->arguments->copy();
10403 10419
                 a->insert(0, e2);
10404 10420
 
10405  
-                Expression *e = new DotIdExp(loc, ae->e1, Id::indexass);
  10421
+                Expression *e = new DotIdExp(loc, e1, Id::indexass);
10406 10422
                 e = new CallExp(loc, e, a);
10407 10423
                 e = e->semantic(sc);
10408 10424
                 return e;
@@ -10410,18 +10426,20 @@ Expression *AssignExp::semantic(Scope *sc)
10410 10426
         }
10411 10427
 
10412 10428
         // No opIndexAssign found yet, but there might be an alias this to try.
10413  
-        if (ad && ad->aliasthis)
10414  
-        {   Expression *e = resolveAliasThis(sc, ae->e1);
10415  
-            Type *t = e->type->toBasetype();
10416  
-
10417  
-            if (t->ty == Tstruct)
  10429
+        if (ad && ad->aliasthis && t1 != att1)
  10430
+        {
  10431
+            if (!att1 && t1->checkAliasThisRec())
  10432
+                att1 = t1;
  10433
+            e1 = resolveAliasThis(sc, e1);
  10434
+            t1 = e1->type->toBasetype();
  10435
+            if (t1->ty == Tstruct)
10418 10436
             {
10419  
-                ad = ((TypeStruct *)t)->sym;
  10437
+                ad = ((TypeStruct *)t1)->sym;
10420 10438
                 goto L1;
10421 10439
             }
10422  
-            else if (t->ty == Tclass)
  10440
+            else if (t1->ty == Tclass)
10423 10441
             {
10424  
-                ad = ((TypeClass *)t)->sym;
  10442
+                ad = ((TypeClass *)t1)->sym;
10425 10443
                 goto L1;
10426 10444
             }
10427 10445
         }
@@ -10431,14 +10449,15 @@ Expression *AssignExp::semantic(Scope *sc)
10431 10449
      * converted to a.opSlice() already.
10432 10450
      */
10433 10451
     if (e1->op == TOKslice)
10434  
-    {   Type *t1;
  10452
+    {
10435 10453
         SliceExp *ae = (SliceExp *)e1;
10436 10454
         AggregateDeclaration *ad = NULL;
10437 10455
         Identifier *id = Id::index;
10438 10456
 
10439 10457
         ae->e1 = ae->e1->semantic(sc);
10440 10458
         ae->e1 = resolveProperties(sc, ae->e1);
10441  
-        t1 = ae->e1->type->toBasetype();
  10459
+        Expression *e1 = ae->e1;
  10460
+        Type *t1 = ae->e1->type->toBasetype();
10442 10461
         if (t1->ty == Tstruct)
10443 10462
         {
10444 10463
             ad = ((TypeStruct *)t1)->sym;
@@ -10459,7 +10478,7 @@ Expression *AssignExp::semantic(Scope *sc)
10459 10478
                 {   a->push(ae->lwr);
10460 10479
                     a->push(ae->upr);
10461 10480
                 }
10462  
-                Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass);
  10481
+                Expression *e = new DotIdExp(loc, e1, Id::sliceass);
10463 10482
                 e = new CallExp(loc, e, a);
10464 10483
                 e = e->semantic(sc);
10465 10484
                 return e;
@@ -10467,18 +10486,20 @@ Expression *AssignExp::semantic(Scope *sc)
10467 10486
         }
10468 10487
 
10469 10488
         // No opSliceAssign found yet, but there might be an alias this to try.
10470  
-        if (ad && ad->aliasthis)
10471  
-        {   Expression *e = resolveAliasThis(sc, ae->e1);
10472  
-            Type *t = e->type->toBasetype();
10473  
-
10474  
-            if (t->ty == Tstruct)
  10489
+        if (ad && ad->aliasthis && t1 != att1)
  10490
+        {
  10491
+            if (!att1 && t1->checkAliasThisRec())
  10492
+                att1 = t1;
  10493
+            e1 = resolveAliasThis(sc, e1);
  10494
+            t1 = e1->type->toBasetype();
  10495
+            if (t1->ty == Tstruct)
10475 10496
             {
10476  
-                ad = ((TypeStruct *)t)->sym;
  10497
+                ad = ((TypeStruct *)t1)->sym;
10477 10498
                 goto L2;
10478 10499
             }
10479  
-            else if (t->ty == Tclass)
  10500
+            else if (t1->ty == Tclass)
10480 10501
             {
10481  
-                ad = ((TypeClass *)t)->sym;
  10502
+                ad = ((TypeClass *)t1)->sym;
10482 10503
                 goto L2;
10483 10504
             }
10484 10505
         }
4  src/expression.h
@@ -769,6 +769,7 @@ struct IsExp : Expression
769 769
 struct UnaExp : Expression
770 770
 {
771 771
     Expression *e1;
  772
+    Type *att1; // Save alias this type to detect recursion
772 773
 
773 774
     UnaExp(Loc loc, enum TOK op, int size, Expression *e1);
774 775
     Expression *syntaxCopy();
@@ -792,6 +793,9 @@ struct BinExp : Expression
792 793
     Expression *e1;
793 794
     Expression *e2;
794 795
 
  796
+    Type *att1; // Save alias this type to detect recursion
  797
+    Type *att2; // Save alias this type to detect recursion
  798
+
795 799
     BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2);
796 800
     Expression *syntaxCopy();
797 801
     int apply(apply_fp_t fp, void *param);
60  src/mtype.c
@@ -1368,6 +1368,27 @@ Type *Type::aliasthisOf()
1368 1368
     return NULL;
1369 1369
 }
1370 1370
 
  1371
+int Type::checkAliasThisRec()
  1372
+{
  1373
+    Type *tb = toBasetype();
  1374
+    enum AliasThisRec* pflag;
  1375
+    if (tb->ty == Tstruct)
  1376
+        pflag = &((TypeStruct *)tb)->att;
  1377
+    else if (tb->ty == Tclass)
  1378
+        pflag = &((TypeClass *)tb)->att;
  1379
+    else
  1380
+        return 0;
  1381
+
  1382
+    enum AliasThisRec flag = (enum AliasThisRec)(*pflag & ~RECtracing);
  1383
+    if (flag == RECfwdref)
  1384
+    {
  1385
+        Type *att = aliasthisOf();
  1386
+        flag = att && att->implicitConvTo(this) ? RECyes : RECno;
  1387
+    }
  1388
+    *pflag = (AliasThisRec)(flag | (*pflag & RECtracing));
  1389
+    return flag == RECyes;
  1390
+}
  1391
+
1371 1392
 Dsymbol *Type::toDsymbol(Scope *sc)
1372 1393
 {
1373 1394
     return NULL;
@@ -7911,6 +7932,7 @@ TypeStruct::TypeStruct(StructDeclaration *sym)
7911 7932
         : Type(Tstruct)
7912 7933
 {
7913 7934
     this->sym = sym;
  7935
+    this->att = RECfwdref;
7914 7936
 }
7915 7937
 
7916 7938
 const char *TypeStruct::kind()
@@ -8415,8 +8437,12 @@ MATCH TypeStruct::implicitConvTo(Type *to)
8415 8437
             }
8416 8438
         }
8417 8439
     }
8418  
-    else if (sym->aliasthis)
  8440
+    else if (sym->aliasthis && !(att & RECtracing))
  8441
+    {
  8442
+        att = (AliasThisRec)(att | RECtracing);
8419 8443
         m = aliasthisOf()->implicitConvTo(to);
  8444
+        att = (AliasThisRec)(att & ~RECtracing);
  8445
+    }
8420 8446
     else
8421 8447
         m = MATCHnomatch;       // no match
8422 8448
     return m;
@@ -8437,13 +8463,16 @@ unsigned TypeStruct::wildConvTo(Type *tprm)
8437 8463
     if (ty == tprm->ty && sym == ((TypeStruct *)tprm)->sym)
8438 8464
         return Type::wildConvTo(tprm);
8439 8465
 
8440  
-    if (sym->aliasthis)
8441  
-    {   Type *t = aliasthisOf();
8442  
-        assert(t);
8443  
-        return t->wildConvTo(tprm);
  8466
+    unsigned mod = 0;
  8467
+
  8468
+    if (sym->aliasthis && !(att & RECtracing))
  8469
+    {
  8470
+        att = (AliasThisRec)(att | RECtracing);
  8471
+        mod = aliasthisOf()->wildConvTo(tprm);
  8472
+        att = (AliasThisRec)(att & ~RECtracing);
8444 8473
     }
8445 8474
 
8446  
-    return 0;
  8475
+    return mod;
8447 8476
 }
8448 8477
 
8449 8478
 Type *TypeStruct::toHeadMutable()
@@ -8458,6 +8487,7 @@ TypeClass::TypeClass(ClassDeclaration *sym)
8458 8487
         : Type(Tclass)
8459 8488
 {
8460 8489
     this->sym = sym;
  8490
+    this->att = RECfwdref;
8461 8491
 }
8462 8492
 
8463 8493
 const char *TypeClass::kind()
@@ -8936,8 +8966,12 @@ MATCH TypeClass::implicitConvTo(Type *to)
8936 8966
     }
8937 8967
 
8938 8968
     m = MATCHnomatch;
8939  
-    if (sym->aliasthis)
  8969
+    if (sym->aliasthis && !(att & RECtracing))
  8970
+    {
  8971
+        att = (AliasThisRec)(att | RECtracing);
8940 8972
         m = aliasthisOf()->implicitConvTo(to);
  8973
+        att = (AliasThisRec)(att & ~RECtracing);
  8974
+    }
8941 8975
 
8942 8976
     return m;
8943 8977
 }
@@ -8970,10 +9004,16 @@ unsigned TypeClass::wildConvTo(Type *tprm)
8970 9004
     if (cdprm && cdprm->isBaseOf(sym, NULL))
8971 9005
         return Type::wildConvTo(tprm);
8972 9006
 
8973  
-    if (sym->aliasthis)
8974  
-        return aliasthisOf()->wildConvTo(tprm);
  9007
+    unsigned mod = 0;
8975 9008
 
8976  
-    return 0;
  9009
+    if (sym->aliasthis && !(att & RECtracing))
  9010
+    {
  9011
+        att = (AliasThisRec)(att | RECtracing);
  9012
+        mod = aliasthisOf()->wildConvTo(tprm);
  9013
+        att = (AliasThisRec)(att & ~RECtracing);
  9014
+    }
  9015
+
  9016
+    return mod;
8977 9017
 }
8978 9018
 
8979 9019
 Type *TypeClass::toHeadMutable()
13  src/mtype.h
@@ -289,6 +289,7 @@ struct Type : Object
289 289
     Type *referenceTo();
290 290
     Type *arrayOf();
291 291
     Type *aliasthisOf();
  292
+    int checkAliasThisRec();
292 293
     virtual Type *makeConst();
293 294
     virtual Type *makeInvariant();
294 295
     virtual Type *makeShared();
@@ -789,9 +790,20 @@ struct TypeReturn : TypeQualified
789 790
     void toJson(JsonOut *json);
790 791
 };
791 792
 
  793
+// Whether alias this dependency is recursive or not.
  794
+enum AliasThisRec
  795
+{
  796
+    RECno = 0,      // no alias this recursion
  797
+    RECyes = 1,     // alias this has recursive dependency
  798
+    RECfwdref = 2,  // not yet known
  799
+
  800
+    RECtracing = 0x4, // mark in progress of implicitConvTo/wildConvTo
  801
+};
  802
+
792 803
 struct TypeStruct : Type
793 804
 {
794 805
     StructDeclaration *sym;
  806
+    enum AliasThisRec att;
795 807
 
796 808
     TypeStruct(StructDeclaration *sym);
797 809
     const char *kind();
@@ -927,6 +939,7 @@ struct TypeTypedef : Type
927 939
 struct TypeClass : Type
928 940
 {
929 941
     ClassDeclaration *sym;
  942
+    enum AliasThisRec att;
930 943
 
931 944
     TypeClass(ClassDeclaration *sym);
932 945
     const char *kind();
156  src/opover.c
@@ -245,18 +245,21 @@ Expression *UnaExp::op_overload(Scope *sc)
245 245
             }
246 246
 
247 247
             // Didn't find it. Forward to aliasthis
248  
-            if (ad->aliasthis)
  248
+            if (ad->aliasthis && ae->e1->type != att1)
249 249
             {
250 250
                 /* Rewrite op(a[arguments]) as:
251 251
                  *      op(a.aliasthis[arguments])
252 252
                  */
253 253
                 Expression *e1 = ae->copy();
254 254
                 ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident);
255  
-                Expression *e = copy();
256  
-                ((UnaExp *)e)->e1 = e1;
257  
-                e = e->trySemantic(sc);
258  
-                return e;
  255
+                UnaExp *ue = (UnaExp *)copy();
  256
+                if (!ue->att1 && ae->e1->type->checkAliasThisRec())
  257
+                    ue->att1 = ae->e1->type;
  258
+                ue->e1 = e1;
  259
+                if (Expression *e = ue->trySemantic(sc))
  260
+                    return e;
259 261
             }
  262
+            att1 = NULL;
260 263
         }
261 264
     }
262 265
     else if (e1->op == TOKslice)
@@ -289,18 +292,21 @@ Expression *UnaExp::op_overload(Scope *sc)
289 292
             }
290 293
 
291 294
             // Didn't find it. Forward to aliasthis
292  
-            if (ad->aliasthis)
  295
+            if (ad->aliasthis && se->e1->type != att1)
293 296
             {
294 297
                 /* Rewrite op(a[lwr..upr]) as:
295 298
                  *      op(a.aliasthis[lwr..upr])
296 299
                  */
297 300
                 Expression *e1 = se->copy();
298 301
                 ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident);
299  
-                Expression *e = copy();
300  
-                ((UnaExp *)e)->e1 = e1;
301  
-                e = e->trySemantic(sc);
302  
-                return e;
  302
+                UnaExp *ue = (UnaExp *)copy();
  303
+                if (!ue->att1 && se->e1->type->checkAliasThisRec())
  304
+                    ue->att1 = se->e1->type;
  305
+                ue->e1 = e1;
  306
+                if (Expression *e = ue->trySemantic(sc))
  307
+                    return e;
303 308
             }
  309
+            att1 = NULL;
304 310
         }
305 311
     }
306 312
 #endif
@@ -352,16 +358,18 @@ Expression *UnaExp::op_overload(Scope *sc)
352 358
         }
353 359
 
354 360
         // Didn't find it. Forward to aliasthis
355  
-        if (ad->aliasthis)
  361
+        if (ad->aliasthis && this->e1->type != att1)
356 362
         {
357 363
             /* Rewrite op(e1) as:
358 364
              *  op(e1.aliasthis)
359 365
              */
  366
+            //printf("att una %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars());
360 367
             Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident);
361  
-            Expression *e = copy();
362  
-            ((UnaExp *)e)->e1 = e1;
363  
-            e = e->trySemantic(sc);
364  
-            return e;
  368
+            UnaExp *ue = (UnaExp *)copy();
  369
+            if (!ue->att1 && this->e1->type->checkAliasThisRec())
  370
+                ue->att1 = this->e1->type;
  371
+            ue->e1 = e1;
  372
+            return ue->trySemantic(sc);
365 373
         }
366 374
 #endif
367 375
     }
@@ -388,16 +396,18 @@ Expression *ArrayExp::op_overload(Scope *sc)
388 396
         }
389 397
 
390 398
         // Didn't find it. Forward to aliasthis
391  
-        if (ad->aliasthis)
  399
+        if (ad->aliasthis && this->e1->type != att1)
392 400
         {
393 401
             /* Rewrite op(e1) as:
394 402
              *  op(e1.aliasthis)
395 403
              */
  404
+            //printf("att arr e1 = %s\n", this->e1->type->toChars());
396 405
             Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident);
397  
-            Expression *e = copy();
398  
-            ((UnaExp *)e)->e1 = e1;
399  
-            e = e->trySemantic(sc);
400  
-            return e;
  406
+            UnaExp *ue = (UnaExp *)copy();
  407
+            if (!ue->att1 && this->e1->type->checkAliasThisRec())
  408
+                ue->att1 = this->e1->type;
  409
+            ue->e1 = e1;
  410
+            return ue->trySemantic(sc);
401 411
         }
402 412
     }
403 413
     return NULL;
@@ -700,11 +710,15 @@ Expression *BinExp::op_overload(Scope *sc)
700 710
         /* Rewrite (e1 op e2) as:
701 711
          *      (e1.aliasthis op e2)
702 712
          */
  713
+        if (att1 && this->e1->type == att1)
  714
+            return NULL;
  715
+        //printf("att bin e1 = %s\n", this->e1->type->toChars());
703 716
         Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident);
704  
-        Expression *e = copy();
705  
-        ((BinExp *)e)->e1 = e1;
706  
-        e = e->trySemantic(sc);
707  
-        return e;
  717
+        BinExp *be = (BinExp *)copy();
  718
+        if (!be->att1 && this->e1->type->checkAliasThisRec())
  719
+            be->att1 = this->e1->type;
  720
+        be->e1 = e1;
  721
+        return be->trySemantic(sc);
708 722
     }
709 723
 
710 724
     // Try alias this on second operand
@@ -717,11 +731,15 @@ Expression *BinExp::op_overload(Scope *sc)
717 731
         /* Rewrite (e1 op e2) as:
718 732
          *      (e1 op e2.aliasthis)
719 733
          */
  734
+        if (att2 && this->e2->type == att2)
  735
+            return NULL;
  736
+        //printf("att bin e2 = %s\n", this->e2->type->toChars());
720 737
         Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident);
721  
-        Expression *e = copy();
722  
-        ((BinExp *)e)->e2 = e2;
723  
-        e = e->trySemantic(sc);
724  
-        return e;
  738
+        BinExp *be = (BinExp *)copy();
  739
+        if (!be->att2 && this->e2->type->checkAliasThisRec())
  740
+            be->att2 = this->e2->type;
  741
+        be->e2 = e2;
  742
+        return be->trySemantic(sc);
725 743
     }
726 744
 #endif
727 745
     return NULL;
@@ -873,11 +891,15 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id)
873 891
         /* Rewrite (e1 op e2) as:
874 892
          *      (e1.aliasthis op e2)
875 893
          */
  894
+        if (att1 && this->e1->type == att1)
  895
+            return NULL;
  896
+        //printf("att cmp_bin e1 = %s\n", this->e1->type->toChars());
876 897
         Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident);
877  
-        Expression *e = copy();
878  
-        ((BinExp *)e)->e1 = e1;
879  
-        e = e->trySemantic(sc);
880  
-        return e;
  898
+        BinExp *be = (BinExp *)copy();
  899
+        if (!be->att1 && this->e1->type->checkAliasThisRec())
  900
+            be->att1 = this->e1->type;
  901
+        be->e1 = e1;
  902
+        return be->trySemantic(sc);
881 903
     }
882 904
 
883 905
     // Try alias this on second operand
@@ -886,11 +908,15 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id)
886 908
         /* Rewrite (e1 op e2) as:
887 909
          *      (e1 op e2.aliasthis)
888 910
          */
  911
+        if (att2 && this->e2->type == att2)
  912
+            return NULL;
  913
+        //printf("att cmp_bin e2 = %s\n", this->e2->type->toChars());
889 914
         Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident);
890  
-        Expression *e = copy();
891  
-        ((BinExp *)e)->e2 = e2;
892  
-        e = e->trySemantic(sc);
893  
-        return e;
  915
+        BinExp *be = (BinExp *)copy();
  916
+        if (!be->att2 && this->e2->type->checkAliasThisRec())
  917
+            be->att2 = this->e2->type;
  918
+        be->e2 = e2;
  919
+        return be->trySemantic(sc);
894 920
     }
895 921
 
896 922
     return NULL;
@@ -978,18 +1004,21 @@ Expression *BinAssignExp::op_overload(Scope *sc)
978 1004
             }
979 1005
 
980 1006
             // Didn't find it. Forward to aliasthis
981  
-            if (ad->aliasthis)
  1007
+            if (ad->aliasthis && ae->e1->type != att1)
982 1008
             {
983 1009
                 /* Rewrite a[arguments] op= e2 as:
984 1010
                  *      a.aliasthis[arguments] op= e2
985 1011
                  */
986 1012
                 Expression *e1 = ae->copy();
987 1013
                 ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident);
988  
-                Expression *e = copy();
989  
-                ((UnaExp *)e)->e1 = e1;
990  
-                e = e->trySemantic(sc);
991  
-                return e;
  1014
+                BinExp *be = (BinExp *)copy();
  1015
+                if (!be->att1 && ae->e1->type->checkAliasThisRec())
  1016
+                    be->att1 = ae->e1->type;
  1017
+                be->e1 = e1;
  1018
+                if (Expression *e = be->trySemantic(sc))
  1019
+                    return e;
992 1020
             }
  1021
+            att1 = NULL;
993 1022
         }
994 1023
     }
995 1024
     else if (e1->op == TOKslice)
@@ -1024,18 +1053,21 @@ Expression *BinAssignExp::op_overload(Scope *sc)
1024 1053
             }
1025 1054
 
1026 1055
             // Didn't find it. Forward to aliasthis
1027  
-            if (ad->aliasthis)
  1056
+            if (ad->aliasthis && se->e1->type != att1)
1028 1057
             {
1029 1058
                 /* Rewrite a[lwr..upr] op= e2 as:
1030 1059
                  *      a.aliasthis[lwr..upr] op= e2
1031 1060
                  */
1032 1061
                 Expression *e1 = se->copy();
1033 1062
                 ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident);
1034  
-                Expression *e = copy();
1035  
-                ((UnaExp *)e)->e1 = e1;
1036  
-                e = e->trySemantic(sc);
1037  
-                return e;
  1063
+                BinExp *be = (BinExp *)copy();
  1064
+                if (!be->att1 && se->e1->type->checkAliasThisRec())
  1065
+                    be->att1 = se->e1->type;
  1066
+                be->e1 = e1;
  1067
+                if (Expression *e = be->trySemantic(sc))
  1068
+                    return e;
1038 1069
             }
  1070
+            att1 = NULL;
1039 1071
         }
1040 1072
     }
1041 1073
 #endif
@@ -1134,11 +1166,15 @@ Expression *BinAssignExp::op_overload(Scope *sc)
1134 1166
         /* Rewrite (e1 op e2) as:
1135 1167
          *      (e1.aliasthis op e2)
1136 1168
          */
  1169
+        if (att1 && this->e1->type == att1)
  1170
+            return NULL;
  1171
+        //printf("att %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars());
1137 1172
         Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident);
1138  
-        Expression *e = copy();
1139  
-        ((BinExp *)e)->e1 = e1;
1140  
-        e = e->trySemantic(sc);
1141  
-        return e;
  1173
+        BinExp *be = (BinExp *)copy();
  1174
+        if (!be->att1 && this->e1->type->checkAliasThisRec())
  1175
+            be->att1 = this->e1->type;
  1176
+        be->e1 = e1;
  1177
+        return be->trySemantic(sc);
1142 1178
     }
1143 1179
 
1144 1180
     // Try alias this on second operand
@@ -1148,11 +1184,15 @@ Expression *BinAssignExp::op_overload(Scope *sc)
1148 1184
         /* Rewrite (e1 op e2) as:
1149 1185
          *      (e1 op e2.aliasthis)
1150 1186
          */
  1187
+        if (att2 && this->e2->type == att2)
  1188
+            return NULL;
  1189
+        //printf("att %s e2 = %s\n", Token::toChars(op), this->e2->type->toChars());
1151 1190
         Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident);
1152  
-        Expression *e = copy();
1153  
-        ((BinExp *)e)->e2 = e2;
1154  
-        e = e->trySemantic(sc);
1155  
-        return e;
  1191
+        BinExp *be = (BinExp *)copy();
  1192
+        if (!be->att2 && this->e2->type->checkAliasThisRec())
  1193
+            be->att2 = this->e2->type;
  1194
+        be->e2 = e2;
  1195
+        return be->trySemantic(sc);
1156 1196
     }
1157 1197
 #endif
1158 1198
     return NULL;
@@ -1219,6 +1259,8 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply)
1219 1259
     int sliced = 0;
1220 1260
 #endif
1221 1261
     Type *tab;
  1262
+    Type *att = NULL;
  1263
+    Expression *org_aggr = aggr;
1222 1264
     AggregateDeclaration *ad;
1223 1265
 
1224 1266
     while (1)
@@ -1230,6 +1272,10 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply)
1230 1272
             goto Lerr;
1231 1273
 
1232 1274
         tab = aggr->type->toBasetype();
  1275
+        if (att == tab)
  1276
+        {   aggr = org_aggr;
  1277
+            goto Lerr;
  1278
+        }
1233 1279
         switch (tab->ty)
1234 1280
         {
1235 1281
             case Tarray:
@@ -1275,6 +1321,8 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply)
1275 1321
 
1276 1322
                 if (ad->aliasthis)
1277 1323
                 {
  1324
+                    if (!att && tab->checkAliasThisRec())
  1325
+                        att = tab;
1278 1326
                     aggr = new DotIdExp(aggr->loc, aggr, ad->aliasthis->ident);
1279 1327
                     continue;
1280 1328
                 }
158  test/runnable/aliasthis.d
@@ -186,6 +186,163 @@ void test6() {
186 186
 }
187 187
 
188 188
 /**********************************************/
  189
+// recursive alias this detection
  190
+
  191
+class C0 {}
  192
+
  193
+class C1 { C2 c; alias c this; }
  194
+class C2 { C1 c; alias c this; }
  195
+
  196
+class C3 { C2 c; alias c this; }
  197
+
  198
+struct S0 {}
  199
+
  200
+struct S1 { S2* ps; @property ref get(){return *ps;} alias get this; }
  201
+struct S2 { S1* ps; @property ref get(){return *ps;} alias get this; }
  202
+
  203
+struct S3 { S2* ps; @property ref get(){return *ps;} alias get this; }
  204
+
  205
+struct S4 { S5* ps; @property ref get(){return *ps;} alias get this; }
  206
+struct S5 { S4* ps; @property ref get(){return *ps;} alias get this; }
  207
+
  208
+struct S6 { S5* ps; @property ref get(){return *ps;} alias get this; }
  209
+
  210
+void test7()
  211
+{
  212
+    // Able to check a type is implicitly convertible within a finite time.
  213
+    static assert(!is(C1 : C0));
  214
+    static assert( is(C2 : C1));
  215
+    static assert( is(C1 : C2));
  216
+    static assert(!is(C3 : C0));
  217
+    static assert( is(C3 : C1));
  218
+    static assert( is(C3 : C2));
  219
+
  220
+    static assert(!is(S1 : S0));
  221
+    static assert( is(S2 : S1));
  222
+    static assert( is(S1 : S2));
  223
+    static assert(!is(S3 : S0));
  224
+    static assert( is(S3 : S1));
  225
+    static assert( is(S3 : S2));
  226
+
  227
+    C0 c0;  C1 c1;  C3 c3;
  228
+    S0 s0;  S1 s1;  S3 s3;  S4 s4;  S6 s6;
  229
+
  230
+    // Allow merging types that contains alias this recursion.
  231
+    static assert( __traits(compiles, c0 is c1));   // typeMerge(c || c) e2->implicitConvTo(t1);
  232
+    static assert( __traits(compiles, c0 is c3));   // typeMerge(c || c) e2->implicitConvTo(t1);
  233
+    static assert( __traits(compiles, c1 is c0));   // typeMerge(c || c) e1->implicitConvTo(t2);
  234
+    static assert( __traits(compiles, c3 is c0));   // typeMerge(c || c) e1->implicitConvTo(t2);
  235
+    static assert(!__traits(compiles, s1 is c0));   // typeMerge(c || c) e1
  236
+    static assert(!__traits(compiles, s3 is c0));   // typeMerge(c || c) e1
  237
+    static assert(!__traits(compiles, c0 is s1));   // typeMerge(c || c) e2
  238
+    static assert(!__traits(compiles, c0 is s3));   // typeMerge(c || c) e2
  239
+
  240
+    static assert(!__traits(compiles, s1 is s0));   // typeMerge(s && s) e1
  241
+    static assert(!__traits(compiles, s3 is s0));   // typeMerge(s && s) e1
  242
+    static assert(!__traits(compiles, s0 is s1));   // typeMerge(s && s) e2
  243
+    static assert(!__traits(compiles, s0 is s3));   // typeMerge(s && s) e2
  244
+    static assert(!__traits(compiles, s1 is s4));   // typeMerge(s && s) e1 + e2
  245
+    static assert(!__traits(compiles, s3 is s6));   // typeMerge(s && s) e1 + e2
  246
+
  247
+    static assert(!__traits(compiles, s1 is 10));   // typeMerge(s || s) e1
  248
+    static assert(!__traits(compiles, s3 is 10));   // typeMerge(s || s) e1
  249
+    static assert(!__traits(compiles, 10 is s1));   // typeMerge(s || s) e2
  250
+    static assert(!__traits(compiles, 10 is s3));   // typeMerge(s || s) e2
  251
+
  252
+    // SliceExp::semantic
  253
+    static assert(!__traits(compiles, c1[]));
  254
+    static assert(!__traits(compiles, c3[]));
  255
+    static assert(!__traits(compiles, s1[]));
  256
+    static assert(!__traits(compiles, s3[]));
  257
+
  258
+    // CallExp::semantic
  259
+//  static assert(!__traits(compiles, c1()));
  260
+//  static assert(!__traits(compiles, c3()));
  261
+    static assert(!__traits(compiles, s1()));
  262
+    static assert(!__traits(compiles, s3()));
  263
+
  264
+    // AssignExp::semantic
  265
+    static assert(!__traits(compiles, { c1[1] = 0; }));
  266
+    static assert(!__traits(compiles, { c3[1] = 0; }));
  267
+    static assert(!__traits(compiles, { s1[1] = 0; }));
  268
+    static assert(!__traits(compiles, { s3[1] = 0; }));
  269
+    static assert(!__traits(compiles, { c1[ ] = 0; }));
  270
+    static assert(!__traits(compiles, { c3[ ] = 0; }));
  271
+    static assert(!__traits(compiles, { s1[ ] = 0; }));
  272
+    static assert(!__traits(compiles, { s3[ ] = 0; }));
  273
+
  274
+    // UnaExp::op_overload
  275
+    static assert(!__traits(compiles, +c1[1]));
  276
+    static assert(!__traits(compiles, +c3[1]));
  277
+    static assert(!__traits(compiles, +s1[1]));
  278
+    static assert(!__traits(compiles, +s3[1]));
  279
+    static assert(!__traits(compiles, +c1[ ]));
  280
+    static assert(!__traits(compiles, +c3[ ]));
  281
+    static assert(!__traits(compiles, +s1[ ]));
  282
+    static assert(!__traits(compiles, +s3[ ]));
  283
+    static assert(!__traits(compiles, +c1));
  284
+    static assert(!__traits(compiles, +c3));
  285
+    static assert(!__traits(compiles, +s1));
  286
+    static assert(!__traits(compiles, +s3));
  287
+
  288
+    // ArrayExp::op_overload
  289
+    static assert(!__traits(compiles, c1[1]));
  290
+    static assert(!__traits(compiles, c3[1]));
  291
+    static assert(!__traits(compiles, s1[1]));
  292
+    static assert(!__traits(compiles, s3[1]));
  293
+
  294
+    // BinExp::op_overload
  295
+    static assert(!__traits(compiles, c1 + 10));    // e1
  296
+    static assert(!__traits(compiles, c3 + 10));    // e1
  297
+    static assert(!__traits(compiles, 10 + c1));    // e2
  298
+    static assert(!__traits(compiles, 10 + c3));    // e2
  299
+    static assert(!__traits(compiles, s1 + 10));    // e1
  300
+    static assert(!__traits(compiles, s3 + 10));    // e1
  301
+    static assert(!__traits(compiles, 10 + s1));    // e2
  302
+    static assert(!__traits(compiles, 10 + s3));    // e2
  303
+
  304
+    // BinExp::compare_overload
  305
+    static assert(!__traits(compiles, c1 < 10));    // (Object.opCmp(int) is invalid)
  306
+    static assert(!__traits(compiles, c3 < 10));    // (Object.opCmp(int) is invalid)
  307
+    static assert(!__traits(compiles, 10 < c1));    // (Object.opCmp(int) is invalid)
  308
+    static assert(!__traits(compiles, 10 < c3));    // (Object.opCmp(int) is invalid)
  309
+    static assert(!__traits(compiles, s1 < 10));    // e1
  310
+    static assert(!__traits(compiles, s3 < 10));    // e1
  311
+    static assert(!__traits(compiles, 10 < s1));    // e2
  312
+    static assert(!__traits(compiles, 10 < s3));    // e2
  313
+
  314
+    // BinAssignExp::op_overload
  315
+    static assert(!__traits(compiles, c1[1] += 1));
  316
+    static assert(!__traits(compiles, c3[1] += 1));
  317
+    static assert(!__traits(compiles, s1[1] += 1));
  318
+    static assert(!__traits(compiles, s3[1] += 1));
  319
+    static assert(!__traits(compiles, c1[ ] += 1));
  320
+    static assert(!__traits(compiles, c3[ ] += 1));
  321
+    static assert(!__traits(compiles, s1[ ] += 1));
  322
+    static assert(!__traits(compiles, s3[ ] += 1));
  323
+    static assert(!__traits(compiles, c1 += c0));   // e1
  324
+    static assert(!__traits(compiles, c3 += c0));   // e1
  325
+    static assert(!__traits(compiles, s1 += s0));   // e1
  326
+    static assert(!__traits(compiles, s3 += s0));   // e1
  327
+    static assert(!__traits(compiles, c0 += c1));   // e2
  328
+    static assert(!__traits(compiles, c0 += c3));   // e2
  329
+    static assert(!__traits(compiles, s0 += s1));   // e2
  330
+    static assert(!__traits(compiles, s0 += s3));   // e2
  331
+    static assert(!__traits(compiles, c1 += s1));   // e1 + e2
  332
+    static assert(!__traits(compiles, c3 += s3));   // e1 + e2
  333
+
  334
+    // ForeachStatement::inferAggregate
  335
+    static assert(!__traits(compiles, { foreach (e; s1){} }));
  336
+    static assert(!__traits(compiles, { foreach (e; s3){} }));
  337
+    static assert(!__traits(compiles, { foreach (e; c1){} }));
  338
+    static assert(!__traits(compiles, { foreach (e; c3){} }));
  339
+
  340
+    // Expression::checkToBoolean
  341
+    static assert(!__traits(compiles, { if (s1){} }));
  342
+    static assert(!__traits(compiles, { if (s3){} }));
  343
+}
  344
+
  345
+/***************************************************/
189 346
 // 2781
190 347
 
191 348
 struct Tuple2781a(T...) {
@@ -901,6 +1058,7 @@ int main()
901 1058
     test4773();
902 1059
     test5188();
903 1060
     test6();
  1061
+    test7();
904 1062
     test2781();
905 1063
     test6546();
906 1064
     test6736();
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.