Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Issue 6365 - Multiple var declaration (AutoTupleDeclaration) #341

Closed
wants to merge 23 commits into from
Hara Kenji
Collaborator

http://d.puremagic.com/issues/show_bug.cgi?id=6365

Based on #321, declare multiple variables at once, and initialize them in the corresponding tuple fields.

Syntax:

TupleDeclaration:
    StorageClasses ( TupleTypeList ) = Initializer ;
    ( TupleTypeList ) = Initializer ;

TupleTypeList:
    TupleType
    TupleType , TupleTypeList

TupleType:
    StorageClasses BasicType Declarator
    BasicType Declarator
    StorageClasses Identifier
    Identifier

TupleTypeList is similar to ForeachTypeList.

If you want to write left parenthesis at beginning, you cannot omit the type or storage class of first variable.

(x, y) = getCoordinate();  // NG, this is not multiple var declaration
(auto x, y) = getCoordinate();  // OK
(double x, y) = getCoordinate();  // OK, types are specified explicitly
David Simcha

What about functions that return multiple values of different types? Would this work?

(double x, int y) = fun();
Hara Kenji
Collaborator

Yes. In your case, fun() should return tuple type, and

(double x, int y) = fun();

is just translated to

auto __tup = fun();
double x = __tup[0];  // __tup[0] has a type that is implicitly convertible to double
int y = __tup[1];     // ditto

Therefore, fun() can return Tuple!(int, int) or Tuple!(double, int).

David Simcha

I love this idea. Is there any language design-level reason why it shouldn't be included or are we just waiting for Walter to get around to merging it?

Hara Kenji 9rnsr closed this November 14, 2011
Hara Kenji 9rnsr reopened this November 15, 2011
Damian Ziemba

Bump, this is really sexy.
Same question as @dsimcha : Is there any language design-level reason why it shouldn't be included?

David Simcha

On looking at this again one concern I see is the implicit temporary. This is caused by creating __tup and would cause postblits to run an extra time in cases where structs are being copied. This may not matter, though, since we're trying to discourage designs where structs can be arbitrarily expensive to copy.

David Herberth

Bump, any news on this?

Damian Ziemba

@WalterBright what ya think about this?
@9rnsr has done amazing job to make work with Tuples great experience!

Daniel Cousens

Bump, really looking forward to seeing this merged in. The simplicity of tuples is not going away :).

Bump, bump, bump. Do want!

robik

This is just awesome.

I have a question, does it work on function declarations? Like public (int, int) getCoordinates() ?

Damian Ziemba

@robik I don't think so, but probably @9rnsr will know better

Hara Kenji
Collaborator

@robik Unfortunately, no. This enhancement just supports distributing tuple fields to each variables in initializing.
I think your implication is "returning multiple values from function without packing (like Tuple!(int, int))", and it is completely different thing. And it's hard to implement, because it requires ABI change.

Hara Kenji 9rnsr referenced this pull request in D-Programming-Language/dlang.org September 24, 2012
Closed

[enh] Documentation for Multiple variable declaration #160

meh.

+1.

Hara Kenji
Collaborator

Documentation for this enhancement: D-Programming-Language/dlang.org#160

Andrei Alexandrescu
Owner

Just to give a bit of feedback - we're looking into this. If we conclude that this language addition solves most of the issues with tuples (so no radical redesign is needed), we'll pull this in. We don't want to pull this thinking that it might hurt a better future design.

Hara Kenji
Collaborator

We don't want to pull this thinking that it might hurt a better future design.

What might be hurt in the future? Syntax? Semantics? or ABI issue?
I'd like to know that you worried in current.

Andrei Alexandrescu
Owner

Syntax and semantics mostly. Walter and I are worried about overarching design more than anything. Consider that the "perfect" tuples for D are six good design decisions away. This pull request deals with destructuring and makes one decision, which is shaping all future decisions. If we later conclude tuples need some additional form of destructuring, or something different entirely, we'll have to add that too. And then some other stuff on top of that. So instead of making one move at a time and then analyzing the result, we want to think the strategy forward so as to have a good overal design, not (only) a good design for destructuring.

Now, it's quite possible that destructuring is about all we need, or that whatever else we need doesn't interact badly with destructuring. In that case this request will be pulled, and everybody will be happy.

Hara Kenji
Collaborator

Thanks for your answer, @andralex . I cannot reply to your worry immediately, but I also would like to think about it.

Andrej Mitrovic
Collaborator

I'd like to be able to ignore a return value if that's possible. For example

(int a, void) = fun() -> discards second value

That way I don't have to forcefully introduce new symbol names into the current scope (using _ is a nogo because there might already be such a symbol, e.g. in foreach loops or a previous tuple destruction).

Brad Roberts braddr referenced this pull request from a commit October 21, 2012
Commit has since been removed from the repository and is no longer available.
Walter Bright

I agree with Andrei, while I don't see anything obviously wrong with this proposal, I'd like to see a complete tuple design before committing ourselves to one aspect of it.

and others added some commits December 10, 2012
Andrej Mitrovic Fixes Issue 9136 - Add isNested trait for aggregates and functions. 6ee244b
fix Issue 8902 - Unexpected "duplicate union initialization for X" error 840d88a
Daniel Murphy Remove unused struct 'Environment' 1b7f937
Daniel Murphy ddoc strings const correct 4d2aaa3
Daniel Murphy Explicity take address of function 10b1ecd
Daniel Murphy Don't take address of function without & fc89fa4
Daniel Murphy Remove unimplemented method 001a936
Daniel Murphy Copy-paste error 31129aa
Walter Bright Merge pull request #1720 from yebblies/globalcopypaste
Copy-paste error
ce3c4ec
Walter Bright Merge pull request #1719 from yebblies/unimpprop
Remove unimplemented method
0b5cdfb
Walter Bright Merge pull request #1717 from yebblies/ddocconst
ddoc strings const correct
8ce00b3
Walter Bright Merge pull request #1715 from yebblies/removeenv
Remove unused struct 'Environment'
7f23ab1
Walter Bright Merge pull request #1718 from yebblies/takeaddress
Take address explicitly
4ce6282
Daniel Murphy Merge pull request #1721 from AndrejMitrovic/Fix9635
Issue 9635 - Improve message on missing 'this' diagnostics.
d6db1b8
Walter Bright Merge pull request #1369 from 9rnsr/fix8902
Issue 8902 - Unexpected "duplicate union initialization for X" error
a975ed6
Martin Nowak Merge pull request #1362 from AndrejMitrovic/IsNestedTrait
Issue 9136 - Add isNested trait for aggregates and functions
051c8be
Add TypeNone for type inference 5054891
isParameters now can check needId f035d61
Separate statement level and decldefs level cd6ece1
Update parser 12524be
Update initializer expansion process 3128f85
Add test cases 8e78e6d
Remove array expansion feature on initializer e748243
Hara Kenji 9rnsr closed this April 03, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 23 unique commits by 5 authors.

Feb 27, 2013
Andrej Mitrovic Fixes Issue 9136 - Add isNested trait for aggregates and functions. 6ee244b
Feb 28, 2013
fix Issue 8902 - Unexpected "duplicate union initialization for X" error 840d88a
Mar 04, 2013
Walter Bright Merge pull request #1720 from yebblies/globalcopypaste
Copy-paste error
ce3c4ec
Walter Bright Merge pull request #1719 from yebblies/unimpprop
Remove unimplemented method
0b5cdfb
Walter Bright Merge pull request #1717 from yebblies/ddocconst
ddoc strings const correct
8ce00b3
Walter Bright Merge pull request #1715 from yebblies/removeenv
Remove unused struct 'Environment'
7f23ab1
Walter Bright Merge pull request #1718 from yebblies/takeaddress
Take address explicitly
4ce6282
Mar 05, 2013
Daniel Murphy Remove unused struct 'Environment' 1b7f937
Daniel Murphy ddoc strings const correct 4d2aaa3
Daniel Murphy Explicity take address of function 10b1ecd
Daniel Murphy Don't take address of function without & fc89fa4
Daniel Murphy Remove unimplemented method 001a936
Daniel Murphy Copy-paste error 31129aa
Daniel Murphy Merge pull request #1721 from AndrejMitrovic/Fix9635
Issue 9635 - Improve message on missing 'this' diagnostics.
d6db1b8
Walter Bright Merge pull request #1369 from 9rnsr/fix8902
Issue 8902 - Unexpected "duplicate union initialization for X" error
a975ed6
Martin Nowak Merge pull request #1362 from AndrejMitrovic/IsNestedTrait
Issue 9136 - Add isNested trait for aggregates and functions
051c8be
Mar 06, 2013
Add TypeNone for type inference 5054891
isParameters now can check needId f035d61
Separate statement level and decldefs level cd6ece1
Update parser 12524be
Update initializer expansion process 3128f85
Add test cases 8e78e6d
Remove array expansion feature on initializer e748243
This page is out of date. Refresh to see the latest.
18  src/declaration.c
@@ -994,7 +994,9 @@ void VarDeclaration::semantic(Scope *sc)
994 994
                 {
995 995
                 if (iexps->dim > nelems)
996 996
                     goto Lnomatch;
997  
-                if (e->type->implicitConvTo(arg->type))
  997
+                if (arg->type->ty != Tnone && e->type->implicitConvTo(arg->type))
  998
+                    continue;
  999
+                if (arg->type->ty == Tnone && iexps->dim == nelems)
998 1000
                     continue;
999 1001
                 }
1000 1002
 
@@ -1034,7 +1036,9 @@ void VarDeclaration::semantic(Scope *sc)
1034 1036
                         size_t iexps_dim = iexps->dim - 1 + exps->dim;
1035 1037
                         if (iexps_dim > nelems)
1036 1038
                             goto Lnomatch;
1037  
-                        if (ee->type->implicitConvTo(arg->type))
  1039
+                        if (arg->type->ty != Tnone && ee->type->implicitConvTo(arg->type))
  1040
+                            continue;
  1041
+                        if (arg->type->ty == Tnone && iexps_dim == nelems)
1038 1042
                             continue;
1039 1043
 
1040 1044
                         if (expandAliasThisTuples(exps, u) != -1)
@@ -1056,6 +1060,13 @@ void VarDeclaration::semantic(Scope *sc)
1056 1060
             if (iexps->dim < nelems)
1057 1061
                 goto Lnomatch;
1058 1062
 
  1063
+            for (size_t i = 0; i < iexps->dim; i++)
  1064
+            {
  1065
+                Parameter *arg = Parameter::getNth(tt->arguments, i);
  1066
+                if (arg->type->ty != Tnone && !iexps->tdata()[i]->type->implicitConvTo(arg->type))
  1067
+                    goto Lnomatch;
  1068
+            }
  1069
+
1059 1070
             ie = new TupleExp(init->loc, iexps);
1060 1071
         }
1061 1072
 Lnomatch:
@@ -1071,6 +1082,7 @@ void VarDeclaration::semantic(Scope *sc)
1071 1082
 
1072 1083
         for (size_t i = 0; i < nelems; i++)
1073 1084
         {   Parameter *arg = Parameter::getNth(tt->arguments, i);
  1085
+            Type *argtype = arg->type->ty == Tnone ? NULL : arg->type;
1074 1086
 
1075 1087
             OutBuffer buf;
1076 1088
             buf.printf("_%s_field_%llu", ident->toChars(), (ulonglong)i);
@@ -1087,7 +1099,7 @@ void VarDeclaration::semantic(Scope *sc)
1087 1099
             {   ti = new ExpInitializer(einit->loc, einit);
1088 1100
             }
1089 1101
 
1090  
-            VarDeclaration *v = new VarDeclaration(loc, arg->type, id, ti);
  1102
+            VarDeclaration *v = new VarDeclaration(loc, argtype, id, ti);
1091 1103
             if (arg->storageClass & STCparameter)
1092 1104
                 v->storage_class |= arg->storageClass;
1093 1105
             //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars());
10  src/doc.c
@@ -109,7 +109,7 @@ int isIdTail(unsigned char *p);
109 109
 int isIndentWS(unsigned char *p);
110 110
 int utfStride(unsigned char *p);
111 111
 
112  
-static unsigned char ddoc_default[] = "\
  112
+static const char ddoc_default[] = "\
113 113
 DDOC =  <html><head>\n\
114 114
         <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
115 115
         <title>$(TITLE)</title>\n\
@@ -200,11 +200,11 @@ ESCAPES = /</&lt;/\n\
200 200
           /&/&amp;/\n\
201 201
 ";
202 202
 
203  
-static char ddoc_decl_s[] = "$(DDOC_DECL ";
204  
-static char ddoc_decl_e[] = ")\n";
  203
+static const char ddoc_decl_s[] = "$(DDOC_DECL ";
  204
+static const char ddoc_decl_e[] = ")\n";
205 205
 
206  
-static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
207  
-static char ddoc_decl_dd_e[] = ")\n";
  206
+static const char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
  207
+static const char ddoc_decl_dd_e[] = ")\n";
208 208
 
209 209
 
210 210
 /****************************************************
4  src/glue.c
@@ -36,10 +36,6 @@
36 36
 #include "outbuf.h"
37 37
 #include "irstate.h"
38 38
 
39  
-struct Environment;
40  
-
41  
-Environment *benv;
42  
-
43 39
 void slist_add(Symbol *s);
44 40
 void slist_reset();
45 41
 void clearStringTab();
1  src/idgen.c
@@ -307,6 +307,7 @@ Msgtable msgtable[] =
307 307
     { "isAssociativeArray" },
308 308
     { "isFinalClass" },
309 309
     { "isPOD" },
  310
+    { "isNested" },
310 311
     { "isFloating" },
311 312
     { "isIntegral" },
312 313
     { "isScalar" },
1  src/json.c
@@ -68,7 +68,6 @@ struct JsonOut
68 68
     void property(const char *name, Type* type);
69 69
     void property(const char *name, const char *deconame, Type* type);
70 70
     void property(const char *name, Parameters* parameters);
71  
-    void property(const char *name, Expressions* expressions);
72 71
     void property(const char *name, enum TRUST trust);
73 72
     void property(const char *name, enum PURE purity);
74 73
     void property(const char *name, enum LINK linkage);
2  src/mars.c
@@ -96,7 +96,7 @@ Global::Global()
96 96
 #include "verstr.h"
97 97
     ;
98 98
 
99  
-    global.structalign = STRUCTALIGN_DEFAULT;
  99
+    structalign = STRUCTALIGN_DEFAULT;
100 100
 
101 101
     memset(&params, 0, sizeof(Param));
102 102
 }
30  src/mtype.c
@@ -187,7 +187,6 @@ void Type::init()
187 187
     mangleChar[Ttypedef] = 'T';
188 188
     mangleChar[Tdelegate] = 'D';
189 189
 
190  
-    mangleChar[Tnone] = 'n';
191 190
     mangleChar[Tvoid] = 'v';
192 191
     mangleChar[Tint8] = 'g';
193 192
     mangleChar[Tuns8] = 'h';
@@ -223,8 +222,9 @@ void Type::init()
223 222
     mangleChar[Tvector] = '@';
224 223
     mangleChar[Tint128] = '@';
225 224
     mangleChar[Tuns128] = '@';
  225
+    mangleChar[Tnone] = '@';
226 226
 
227  
-    mangleChar[Tnull] = 'n';    // same as TypeNone
  227
+    mangleChar[Tnull] = 'n';
228 228
 
229 229
     for (size_t i = 0; i < TMAX; i++)
230 230
     {   if (!mangleChar[i])
@@ -249,6 +249,9 @@ void Type::init()
249 249
     }
250 250
     basic[Terror] = new TypeError();
251 251
 
  252
+    tnone = new TypeNone();
  253
+    tnone->deco = tnone->merge()->deco;
  254
+
252 255
     tnull = new TypeNull();
253 256
     tnull->deco = tnull->merge()->deco;
254 257
 
@@ -2259,6 +2262,23 @@ uinteger_t Type::sizemask()
2259 2262
     return m;
2260 2263
 }
2261 2264
 
  2265
+/* ============================= TypeNone =========================== */
  2266
+
  2267
+TypeNone::TypeNone()
  2268
+        : Type(Tnone)
  2269
+{
  2270
+}
  2271
+
  2272
+Type *TypeNone::syntaxCopy()
  2273
+{
  2274
+    return this;
  2275
+}
  2276
+
  2277
+void TypeNone::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
  2278
+{
  2279
+    buf->writestring("_none_");
  2280
+}
  2281
+
2262 2282
 /* ============================= TypeError =========================== */
2263 2283
 
2264 2284
 TypeError::TypeError()
@@ -8116,11 +8136,14 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc)
8116 8136
     //    return defaultInit(loc);
8117 8137
     Expressions *structelems = new Expressions();
8118 8138
     structelems->setDim(sym->fields.dim - sym->isnested);
  8139
+    unsigned offset = 0;
8119 8140
     for (size_t j = 0; j < structelems->dim; j++)
8120 8141
     {
8121 8142
         VarDeclaration *vd = sym->fields[j];
8122 8143
         Expression *e;
8123  
-        if (vd->init)
  8144
+        if (vd->offset < offset)
  8145
+            e = NULL;
  8146
+        else if (vd->init)
8124 8147
         {   if (vd->init->isVoidInitializer())
8125 8148
                 e = NULL;
8126 8149
             else
@@ -8145,6 +8168,7 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc)
8145 8168
 
8146 8169
             e = e->implicitCastTo(vd->scope, telem);
8147 8170
         }
  8171
+        offset = vd->offset + vd->type->size();
8148 8172
         (*structelems)[j] = e;
8149 8173
     }
8150 8174
     StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems);
9  src/mtype.h
@@ -182,6 +182,7 @@ struct Type : Object
182 182
     static Type *tvoidptr;              // void*
183 183
     static Type *tstring;               // immutable(char)[]
184 184
     static Type *tvalist;               // va_list alias
  185
+    #define tnone       basic[Tnone]    // for type inference
185 186
     #define terror      basic[Terror]   // for error recovery
186 187
 
187 188
     #define tnull       basic[Tnull]    // for null type
@@ -345,6 +346,14 @@ struct Type : Object
345 346
     virtual TypeBasic *isTypeBasic();
346 347
 };
347 348
 
  349
+struct TypeNone : Type
  350
+{
  351
+    TypeNone();
  352
+
  353
+    Type *syntaxCopy();
  354
+    void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs);
  355
+};
  356
+
348 357
 struct TypeError : Type
349 358
 {
350 359
     TypeError();
6  src/optimize.c
@@ -801,19 +801,19 @@ Expression *shift_optimize(int result, BinExp *e, Expression *(*shift)(Type *, E
801 801
 Expression *ShlExp::optimize(int result, bool keepLvalue)
802 802
 {
803 803
     //printf("ShlExp::optimize(result = %d) %s\n", result, toChars());
804  
-    return shift_optimize(result, this, Shl);
  804
+    return shift_optimize(result, this, &Shl);
805 805
 }
806 806
 
807 807
 Expression *ShrExp::optimize(int result, bool keepLvalue)
808 808
 {
809 809
     //printf("ShrExp::optimize(result = %d) %s\n", result, toChars());
810  
-    return shift_optimize(result, this, Shr);
  810
+    return shift_optimize(result, this, &Shr);
811 811
 }
812 812
 
813 813
 Expression *UshrExp::optimize(int result, bool keepLvalue)
814 814
 {
815 815
     //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
816  
-    return shift_optimize(result, this, Ushr);
  816
+    return shift_optimize(result, this, &Ushr);
817 817
 }
818 818
 
819 819
 Expression *AndExp::optimize(int result, bool keepLvalue)
274  src/parse.c
@@ -63,6 +63,7 @@ Parser::Parser(Module *module, unsigned char *base, size_t length, int doDocComm
63 63
     linkage = LINKd;
64 64
     endloc = 0;
65 65
     inBrackets = 0;
  66
+    inStatements = 0;
66 67
     lookingForElse = 0;
67 68
     //nextToken();              // start up the scanner
68 69
 }
@@ -154,6 +155,8 @@ Dsymbols *Parser::parseDeclDefs(int once)
154 155
     Condition *condition;
155 156
     unsigned char *comment;
156 157
 
  158
+    inStatements = 0;
  159
+
157 160
     //printf("Parser::parseDeclDefs()\n");
158 161
     decldefs = new Dsymbols();
159 162
     do
@@ -230,6 +233,7 @@ Dsymbols *Parser::parseDeclDefs(int once)
230 233
             case TOKunion:
231 234
             case TOKclass:
232 235
             case TOKinterface:
  236
+            case TOKlparen:
233 237
             Ldeclaration:
234 238
                 a = parseDeclarations(STCundefined, NULL);
235 239
                 decldefs->append(a);
@@ -421,8 +425,10 @@ Dsymbols *Parser::parseDeclDefs(int once)
421 425
                     case TOKimmutable:
422 426
                     case TOKwild:
423 427
                         // If followed by a (, it is not a storage class
424  
-                        if (peek(&token)->value == TOKlparen)
425  
-                            break;
  428
+                        Token *tk;
  429
+                        if ((tk = peek(&token))->value == TOKlparen)
  430
+                            if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
  431
+                                break;
426 432
                         if (token.value == TOKconst)
427 433
                             stc = STCconst;
428 434
                         else if (token.value == TOKshared)
@@ -475,9 +481,23 @@ Dsymbols *Parser::parseDeclDefs(int once)
475 481
                     continue;
476 482
                 }
477 483
 
478  
-                /* Look for return type inference for template functions.
  484
+                /* Look for multiple var initializers:
  485
+                 *      storage_class ( identifier, ... ) = initializer;
479 486
                  */
480 487
                 Token *tk;
  488
+                if (storageClass &&
  489
+                    token.value == TOKlparen &&
  490
+                    peekNext() != TOKrparen &&
  491
+                    (tk = &token, skipParens(tk, &tk)) &&
  492
+                    peek(tk)->value == TOKassign)
  493
+                {
  494
+                    a = parseMultiVarDeclaration(storageClass, comment);
  495
+                    decldefs->append(a);
  496
+                    continue;
  497
+                }
  498
+
  499
+                /* Look for return type inference for template functions.
  500
+                 */
481 501
                 if (token.value == TOKidentifier &&
482 502
                     (tk = peek(&token))->value == TOKlparen &&
483 503
                     skipParens(tk, &tk) &&
@@ -2830,6 +2850,8 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
2830 2850
     Loc loc = this->loc;
2831 2851
     Expressions *udas = NULL;
2832 2852
 
  2853
+    Token *tk;
  2854
+
2833 2855
     //printf("parseDeclarations() %s\n", token.toChars());
2834 2856
     if (!comment)
2835 2857
         comment = token.blockComment;
@@ -2922,6 +2944,10 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
2922 2944
             tok = token.value;
2923 2945
             nextToken();
2924 2946
             break;
  2947
+        case TOKlparen:
  2948
+            if (peek(&token)->value != TOKrparen)
  2949
+                return parseMultiVarDeclaration(storage_class, comment);
  2950
+            break;
2925 2951
         default: break;
2926 2952
     }
2927 2953
 
@@ -2931,29 +2957,33 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
2931 2957
         switch (token.value)
2932 2958
         {
2933 2959
             case TOKconst:
2934  
-                if (peek(&token)->value == TOKlparen)
2935  
-                    break;              // const as type constructor
  2960
+                if ((tk = peek(&token))->value == TOKlparen)
  2961
+                    if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
  2962
+                        break;          // const as type constructor
2936 2963
                 stc = STCconst;         // const as storage class
2937 2964
                 goto L1;
2938 2965
 
2939 2966
             case TOKinvariant:
2940 2967
             case TOKimmutable:
2941  
-                if (peek(&token)->value == TOKlparen)
2942  
-                    break;
  2968
+                if ((tk = peek(&token))->value == TOKlparen)
  2969
+                    if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
  2970
+                        break;
2943 2971
                 if (token.value == TOKinvariant)
2944 2972
                     deprecation("use of 'invariant' rather than 'immutable' is deprecated");
2945 2973
                 stc = STCimmutable;
2946 2974
                 goto L1;
2947 2975
 
2948 2976
             case TOKshared:
2949  
-                if (peek(&token)->value == TOKlparen)
2950  
-                    break;
  2977
+                if ((tk = peek(&token))->value == TOKlparen)
  2978
+                    if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
  2979
+                        break;
2951 2980
                 stc = STCshared;
2952 2981
                 goto L1;
2953 2982
 
2954 2983
             case TOKwild:
2955  
-                if (peek(&token)->value == TOKlparen)
2956  
-                    break;
  2984
+                if ((tk = peek(&token))->value == TOKlparen)
  2985
+                    if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
  2986
+                        break;
2957 2987
                 stc = STCwild;
2958 2988
                 goto L1;
2959 2989
 
@@ -3075,10 +3105,26 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
3075 3105
         return parseAutoDeclarations(storage_class, comment);
3076 3106
     }
3077 3107
 
  3108
+    /* Look for multiple var declaration:
  3109
+     *  storage_class ( identifier, ... ) = initializer;
  3110
+     */
  3111
+    if (storage_class &&
  3112
+        token.value == TOKlparen &&
  3113
+        peekNext() != TOKrparen &&
  3114
+        (tk = &token, skipParens(tk, &tk)) &&
  3115
+        (peek(tk)->value == TOKassign))
  3116
+    {
  3117
+        if (udas)
  3118
+        {
  3119
+            // Need to improve this
  3120
+            error("user defined attributes not allowed for auto declarations");
  3121
+        }
  3122
+        return parseMultiVarDeclaration(storage_class, comment);
  3123
+    }
  3124
+
3078 3125
     /* Look for return type inference for template functions.
3079 3126
      */
3080 3127
     {
3081  
-    Token *tk;
3082 3128
     if (storage_class &&
3083 3129
         token.value == TOKidentifier &&
3084 3130
         (tk = peek(&token))->value == TOKlparen &&
@@ -3315,6 +3361,160 @@ Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, unsigned char
3315 3361
 }
3316 3362
 #endif
3317 3363
 
  3364
+#if DMDV2
  3365
+Dsymbols *makeMultiVarDeclaration(Loc loc, Parameters *params, Initializer *init, int inStatements)
  3366
+{
  3367
+#if 0
  3368
+    for (size_t i = 0; i < params->dim; i++)
  3369
+    {
  3370
+        Parameter *prm = params->tdata()[i];
  3371
+        printf("prms[%d] = %s %s, stc = %llx\n", i,
  3372
+            (prm->type ? prm->type->toChars() : "(null)"),
  3373
+            prm->ident->toChars(),
  3374
+            prm->storageClass);
  3375
+    }
  3376
+    printf("init = %s\n", init->toChars());
  3377
+#endif
  3378
+
  3379
+    Dsymbols *a = new Dsymbols();
  3380
+
  3381
+    for (size_t i = 0; i < params->dim; i++)
  3382
+    {
  3383
+        Parameter *prm = params->tdata()[i];
  3384
+        if (!prm->type)
  3385
+            prm->type = Type::tnone;
  3386
+    }
  3387
+
  3388
+    Expression *ve;
  3389
+    if (inStatements)
  3390
+    {
  3391
+        TypeTuple *tt = new TypeTuple(params);
  3392
+        Identifier *id = Lexer::uniqueId("__tup");
  3393
+        VarDeclaration *v = new VarDeclaration(loc, tt, id, init);
  3394
+        a->push(v);
  3395
+
  3396
+        ve = new IdentifierExp(loc, id);
  3397
+    }
  3398
+    else
  3399
+        ve = init->toExpression();
  3400
+    assert(ve);
  3401
+
  3402
+    for (size_t i = 0; i < params->dim; i++)
  3403
+    {
  3404
+        Expressions *exps = new Expressions();
  3405
+        exps->push(new IntegerExp(i));
  3406
+        ExpInitializer *ei = new ExpInitializer(loc, new ArrayExp(loc, ve, exps));
  3407
+
  3408
+        Parameter *prm = params->tdata()[i];
  3409
+        VarDeclaration *v = new VarDeclaration(loc, NULL, prm->ident, ei);
  3410
+        v->storage_class |= prm->storageClass;
  3411
+        if (inStatements)
  3412
+            v->storage_class |= STCref | STCforeach;
  3413
+        a->push(v);
  3414
+    }
  3415
+
  3416
+    return a;
  3417
+}
  3418
+
  3419
+Dsymbols *Parser::parseMultiVarDeclaration(StorageClass storageClass, unsigned char *comment)
  3420
+{
  3421
+    //printf("parseMultiVarDeclaration, stc = %x\n", storageClass);
  3422
+
  3423
+    Parameters *prms = new Parameters();
  3424
+    Loc loc = this->loc;
  3425
+
  3426
+    // ( StorageClass TupleTypeList ) = Initializer ;
  3427
+    // StorageClasses ( TupleTypeList ) = Initializer ;
  3428
+    check(TOKlparen);
  3429
+    while (1)
  3430
+    {
  3431
+        if (token.value == TOKrparen)
  3432
+            break;
  3433
+
  3434
+        Identifier *ai = NULL;
  3435
+        Type *at;
  3436
+
  3437
+        StorageClass storage_class = storageClass;
  3438
+        StorageClass stc;
  3439
+        while (1)
  3440
+        {
  3441
+            switch (token.value)
  3442
+            {
  3443
+                case TOKconst:
  3444
+                    if (peek(&token)->value == TOKlparen)
  3445
+                        break;              // const as type constructor
  3446
+                    stc = STCconst;         // const as storage class
  3447
+                    goto L1;
  3448
+
  3449
+                case TOKinvariant:
  3450
+                case TOKimmutable:
  3451
+                    if (peek(&token)->value == TOKlparen)
  3452
+                        break;
  3453
+                    stc = STCimmutable;
  3454
+                    goto L1;
  3455
+
  3456
+                case TOKshared:
  3457
+                    if (peek(&token)->value == TOKlparen)
  3458
+                        break;
  3459
+                    stc = STCshared;
  3460
+                    goto L1;
  3461
+
  3462
+                case TOKwild:
  3463
+                    if (peek(&token)->value == TOKlparen)
  3464
+                        break;
  3465
+                    stc = STCwild;
  3466
+                    goto L1;
  3467
+
  3468
+            //  case TOKstatic:     stc = STCstatic;         goto L1;
  3469
+                case TOKauto:       stc = STCauto;           goto L1;
  3470
+                case TOKscope:      stc = STCscope;          goto L1;
  3471
+
  3472
+                L1:
  3473
+                    if (storage_class & stc)
  3474
+                        error("redundant storage class '%s'", token.toChars());
  3475
+                    storage_class = storage_class | stc;
  3476
+                    composeStorageClass(storage_class);
  3477
+                    nextToken();
  3478
+                    continue;
  3479
+
  3480
+                default:
  3481
+                    break;
  3482
+            }
  3483
+            break;
  3484
+        }
  3485
+
  3486
+        if (token.value == TOKidentifier)
  3487
+        {
  3488
+            Token *t = peek(&token);
  3489
+            if (t->value == TOKcomma || t->value == TOKrparen)
  3490
+            {   ai = token.ident;
  3491
+                at = NULL;              // infer argument type
  3492
+                nextToken();
  3493
+                goto Larg;
  3494
+            }
  3495
+        }
  3496
+        at = parseType(&ai);
  3497
+        if (!ai)
  3498
+            error("no identifier for declarator %s", at->toChars());
  3499
+      Larg:
  3500
+        Parameter *a = new Parameter(storage_class, at, ai, NULL);
  3501
+        prms->push(a);
  3502
+        if (token.value == TOKcomma)
  3503
+            nextToken();
  3504
+    }
  3505
+    check(TOKrparen);
  3506
+    check(TOKassign);
  3507
+
  3508
+    Initializer *init = parseInitializer();
  3509
+
  3510
+    if (token.value != TOKsemicolon)
  3511
+        error("semicolon expected following tuple declaration, not '%s'", token.toChars());
  3512
+    nextToken();
  3513
+
  3514
+    return makeMultiVarDeclaration(loc, prms, init, inStatements);
  3515
+}
  3516
+#endif
  3517
+
3318 3518
 /*****************************************
3319 3519
  * Parse contracts following function declaration.
3320 3520
  */
@@ -3687,6 +3887,8 @@ Statement *Parser::parseStatement(int flags)
3687 3887
     bool isfinal;
3688 3888
     Loc loc = this->loc;
3689 3889
 
  3890
+    inStatements = 1;
  3891
+
3690 3892
     //printf("parseStatement()\n");
3691 3893
 
3692 3894
     if (flags & PScurly && token.value != TOKlcurly)
@@ -3720,6 +3922,30 @@ Statement *Parser::parseStatement(int flags)
3720 3922
                 goto Lexp;
3721 3923
             break;
3722 3924
 
  3925
+        case TOKlparen:
  3926
+        {
  3927
+            Token *t = &token;
  3928
+            switch (peek(&token)->value)
  3929
+            {
  3930
+                case TOKrparen:
  3931
+                    break;
  3932
+
  3933
+                case TOKconst:
  3934
+                case TOKinvariant:
  3935
+                case TOKimmutable:
  3936
+                case TOKshared:
  3937
+                case TOKwild:
  3938
+
  3939
+            //  case TOKstatic:
  3940
+                case TOKauto:
  3941
+                case TOKscope:
  3942
+                    goto Ldeclaration;
  3943
+
  3944
+                default:
  3945
+                    if (isParameters(&t, 2) && t->value == TOKassign)
  3946
+                        goto Ldeclaration;
  3947
+            }
  3948
+        }
3723 3949
         case TOKassert:
3724 3950
         case TOKthis:
3725 3951
         case TOKsuper:
@@ -3740,7 +3966,6 @@ Statement *Parser::parseStatement(int flags)
3740 3966
         case TOKtrue:
3741 3967
         case TOKfalse:
3742 3968
         case TOKstring:
3743  
-        case TOKlparen:
3744 3969
         case TOKcast:
3745 3970
         case TOKmul:
3746 3971
         case TOKmin:
@@ -4704,6 +4929,8 @@ Statement *Parser::parseStatement(int flags)
4704 4929
             break;
4705 4930
     }
4706 4931
 
  4932
+    inStatements = 0;
  4933
+
4707 4934
     return s;
4708 4935
 }
4709 4936
 
@@ -5088,7 +5315,13 @@ int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok)
5088 5315
 }
5089 5316
 
5090 5317
 
5091  
-int Parser::isParameters(Token **pt)
  5318
+/************************************
  5319
+ * Input:
  5320
+ *      needId  0       no identifier
  5321
+ *              1       identifier optional
  5322
+ *              2       must have identifier
  5323
+ */
  5324
+int Parser::isParameters(Token **pt, int needId)
5092 5325
 {   // This code parallels parseParameters()
5093 5326
     Token *t = *pt;
5094 5327
 
@@ -5156,10 +5389,19 @@ int Parser::isParameters(Token **pt)
5156 5389
             {   if (!isBasicType(&t))
5157 5390
                     return FALSE;
5158 5391
             L2:
5159  
-                int tmp = FALSE;
  5392
+                int haveId = 0;
5160 5393
                 if (t->value != TOKdotdotdot &&
5161  
-                    !isDeclarator(&t, &tmp, TOKreserved))
  5394
+                    !isDeclarator(&t, &haveId, TOKreserved))
5162 5395
                     return FALSE;
  5396
+
  5397
+                if ( needId == 1 ||
  5398
+                    (needId == 0 && !haveId) ||
  5399
+                    (needId == 2 &&  haveId))
  5400
+                {
  5401
+                }
  5402
+                else
  5403
+                    return FALSE;
  5404
+
5163 5405
                 if (t->value == TOKassign)
5164 5406
                 {   t = peek(t);
5165 5407
                     if (!isExpression(&t))
4  src/parse.h
@@ -66,6 +66,7 @@ struct Parser : Lexer
66 66
     enum LINK linkage;
67 67
     Loc endloc;                 // set to location of last right curly
68 68
     int inBrackets;             // inside [] of array index or slice
  69
+    int inStatements;           // inside runnable code
69 70
     Loc lookingForElse;         // location of lonely if looking for an else
70 71
 
71 72
     Parser(Module *module, unsigned char *base, size_t length, int doDocComment);
@@ -73,6 +74,7 @@ struct Parser : Lexer
73 74
     Dsymbols *parseModule();
74 75
     Dsymbols *parseDeclDefs(int once);
75 76
     Dsymbols *parseAutoDeclarations(StorageClass storageClass, unsigned char *comment);
  77
+    Dsymbols *parseMultiVarDeclaration(StorageClass storageClass, unsigned char *comment);
76 78
     Dsymbols *parseBlock();
77 79
     void composeStorageClass(StorageClass stc);
78 80
     StorageClass parseAttribute(Expressions **pexps);
@@ -124,7 +126,7 @@ struct Parser : Lexer
124 126
     int isDeclaration(Token *t, int needId, enum TOK endtok, Token **pt);
125 127
     int isBasicType(Token **pt);
126 128
     int isDeclarator(Token **pt, int *haveId, enum TOK endtok);
127  
-    int isParameters(Token **pt);
  129
+    int isParameters(Token **pt, int needId = 1);
128 130
     int isExpression(Token **pt);
129 131
     int skipParens(Token *t, Token **pt);
130 132
     int skipAttributes(Token *t, Token **pt);
30  src/traits.c
@@ -168,6 +168,34 @@ Expression *TraitsExp::semantic(Scope *sc)
168 168
         }
169 169
         goto Ltrue;
170 170
     }
  171
+    else if (ident == Id::isNested)
  172
+    {
  173
+        if (dim != 1)
  174
+            goto Ldimerror;
  175
+        Object *o = (*args)[0];
  176
+        Dsymbol *s = getDsymbol(o);
  177
+        AggregateDeclaration *a;
  178
+        FuncDeclaration *f;
  179
+
  180
+        if (!s) { }
  181
+        else if ((a = s->isAggregateDeclaration()) != NULL)
  182
+        {
  183
+            if (a->isnested)
  184
+                goto Ltrue;
  185
+            else
  186
+                goto Lfalse;
  187
+        }
  188
+        else if ((f = s->isFuncDeclaration()) != NULL)
  189
+        {
  190
+            if (f->isNested())
  191
+                goto Ltrue;
  192
+            else
  193
+                goto Lfalse;
  194
+        }
  195
+
  196
+        error("aggregate or function expected instead of '%s'", o->toChars());
  197
+        goto Lfalse;
  198
+    }
171 199
     else if (ident == Id::isAbstractFunction)
172 200
     {
173 201
         FuncDeclaration *f;
@@ -379,7 +407,7 @@ Expression *TraitsExp::semantic(Scope *sc)
379 407
             p.exps = exps;
380 408
             p.e1 = e;
381 409
             p.ident = ident;
382  
-            overloadApply(f, fptraits, &p);
  410
+            overloadApply(f, &fptraits, &p);
383 411
 
384 412
             TupleExp *tup = new TupleExp(loc, exps);
385 413
             return tup->semantic(sc);
218  test/runnable/multivar.d
... ...
@@ -0,0 +1,218 @@
  1
+
  2
+extern (C) int printf(const(char*) fmt, ...);
  3
+
  4
+struct Tup(T...)
  5
+{
  6
+    T field;
  7
+    alias field this;
  8
+
  9
+    bool opEquals()(auto ref Tup rhs) const
  10
+    {
  11
+        foreach (i, _; T)
  12
+            if (field[i] != rhs.field[i])
  13
+                return false;
  14
+        return true;
  15
+    }
  16
+}
  17
+
  18
+Tup!T tup(T...)(T fields)
  19
+{
  20
+    return typeof(return)(fields);
  21
+}
  22
+
  23
+template Seq(T...)
  24
+{
  25
+    alias T Seq;
  26
+}
  27
+
  28
+/**********************************************/
  29
+
  30
+void test1()
  31
+{
  32
+    auto (num, str) = Seq!(10, "str");
  33
+    assert(num == 10);
  34
+    assert(str == "str");
  35
+
  36
+    int eval = 0;
  37
+    auto (n, t) = (eval++, tup(10, tup("str", [1,2])));
  38
+    assert(eval == 1);
  39
+    assert(n == 10);
  40
+    eval = 0;
  41
+    auto (s, a) = (eval++, t);
  42
+    assert(eval == 1);
  43
+    assert(s == "str");
  44
+    assert(a == [1,2]);
  45
+
  46
+    auto (i, j) = 10;
  47
+    assert(i == 10);
  48
+    assert(j == 10);
  49
+
  50
+    auto (t1, t2) = tup(1, "str", [1,2]);
  51
+    assert(t1 == tup(1, "str", [1,2]));
  52
+    assert(t2 == tup(1, "str", [1,2]));
  53
+
  54
+    auto (x) = 1;
  55
+    assert(x == 1);
  56
+}
  57
+
  58
+/**********************************************/
  59
+
  60
+void test2()
  61
+{
  62
+    //auto (n, m) = [1,2];
  63
+    //assert(n == 1);
  64
+    //assert(m == 2);
  65
+
  66
+    //int[2] sa = [1,2];
  67
+    //auto (x, y) = sa;
  68
+    //assert(x == 1);
  69
+    //assert(y == 2);
  70
+}
  71
+
  72
+/**********************************************/
  73
+
  74
+void test3()
  75
+{
  76
+    alias tup tuple;
  77
+    alias Seq TypeTuple;
  78
+
  79
+    // Example of 1
  80
+    {
  81
+        auto (i, j) = tuple(10, "a");
  82
+        static assert(is(typeof(i) == int));
  83
+        static assert(is(typeof(j) == string));
  84
+
  85
+        const (x, y) = TypeTuple!(1, 2);
  86
+        static assert(is(typeof(x) == const(int)));
  87
+        static assert(is(typeof(y) == const(int)));
  88
+    }
  89
+
  90
+    {
  91
+        // Example of 2-1
  92
+        (int i, string j) = tuple(10, "a");
  93
+        static assert(is(typeof(i) == int));
  94
+        static assert(is(typeof(j) == string));
  95
+
  96
+        // Example of 2-2
  97
+        (auto c, r) = TypeTuple!('c', "har");
  98
+        static assert(is(typeof(c) == char));
  99
+        static assert(is(typeof(r) == string));
  100
+
  101
+        (const x, auto y) = TypeTuple!(1, 2);
  102
+        static assert(is(typeof(x) == const(int)));
  103
+        static assert(is(typeof(y) == int));
  104
+
  105
+        (auto a1, const int[] a2) = TypeTuple!([1], [2,3]);
  106
+        static assert(is(typeof(a1) == int[]));
  107
+        static assert(is(typeof(a2) == const(int[])));
  108
+    }
  109
+}
  110
+
  111
+/**********************************************/
  112
+
  113
+void test4()
  114
+{
  115
+    auto (x, y, z) = Seq!(10, tup("a", [1,2]));
  116
+    assert(x == 10);
  117
+    assert(y == "a");
  118
+    assert(z == [1,2]);
  119
+}
  120
+
  121
+/**********************************************/
  122
+
  123
+void test5()
  124
+{
  125
+    auto t = tup(10, "a");
  126
+
  127
+    auto (a1) = t[0..1];
  128
+    assert(a1 == 10);
  129
+
  130
+    (auto a2) = t[0..1];
  131
+    assert(a2 == 10);
  132
+
  133
+    //auto (x1) = [10];
  134
+    //assert(x1 == 10);
  135
+
  136
+    (auto x2) = tup(10);
  137
+    assert(x2 == 10);
  138
+
  139
+    (auto x3) = tup(10)[0..1];
  140
+    assert(x3 == 10);
  141
+
  142
+    (int x4) = Seq!(10);
  143
+    assert(x4 == 10);
  144
+
  145
+    // isolated comma parsing
  146
+    (int n,) = tup(10);
  147
+    assert(n == 10);
  148
+
  149
+    auto (x, y,) = tup(10, 20);
  150
+    assert(x == 10);
  151
+    assert(y == 20);
  152
+}
  153
+
  154
+/**********************************************/
  155
+
  156
+void test6()
  157
+{
  158
+    const(int x, string y) = tup(10, "a");
  159
+    static assert(is(typeof(x) == const(int)));
  160
+    static assert(is(typeof(y) == const(string)));
  161
+    assert(x == 10);
  162
+    assert(y == "a");
  163
+
  164
+    (int i, double j, string k) = tup(tup(10, 2.2), "a");
  165
+    static assert(is(typeof(i) == int));
  166
+    static assert(is(typeof(j) == double));
  167
+    static assert(is(typeof(k) == string));
  168
+    assert(i == 10);
  169
+    assert(j == 2.2);
  170
+    assert(k == "a");
  171
+}
  172
+
  173
+/**********************************************/
  174
+
  175
+Tup!(int, string) func7(){ return tup(10, "str"); }
  176
+
  177
+auto (num71, str71) = func7();
  178
+const (num72, str72) = Seq!(10, "str");
  179
+
  180
+enum (num73, str73) = func7();
  181
+
  182
+void test7()
  183
+{
  184
+    assert(num71 == 10);
  185
+    assert(str71 == "str");
  186
+    num71 = 20;
  187
+    str71 = "hello";
  188
+
  189
+    assert(num72 == 10);
  190
+    assert(str72 == "str");
  191
+    static assert(!__traits(compiles, num72 = 20));
  192
+    static assert(!__traits(compiles, str72 = "hello"));
  193
+    auto pnum72 = &num72;
  194
+    auto pstr72 = &str72;
  195
+
  196
+    static assert(num73 == 10);
  197
+    static assert(str73 == "str");
  198
+    static assert(!__traits(compiles, num73 = 20));
  199
+//  static assert(!__traits(compiles, str73 = "hello"));
  200
+    static assert(!__traits(compiles, &num73));
  201
+//  static assert(!__traits(compiles, &str73));
  202
+}
  203
+
  204
+/**********************************************/
  205
+
  206
+int main()
  207
+{
  208
+    test1();
  209
+    test2();
  210
+    test3();
  211
+    test4();
  212
+    test5();
  213
+    test6();
  214
+    test7();
  215
+
  216
+    printf("Success\n");
  217
+    return 0;
  218
+}
19  test/runnable/structlit.d
@@ -589,6 +589,24 @@ void test8763()
589 589
 }
590 590
 
591 591
 /********************************************/
  592
+// 8902
  593
+
  594
+union U8902 { int a, b; }
  595
+
  596
+enum U8902 u8902a = U8902.init; // No errors
  597
+U8902 u8902b;                   // No errors
  598
+U8902 u8902c = U8902.init;      // Error: duplicate union initialization for b
  599
+
  600
+void test8902()
  601
+{
  602
+    U8902 u8902d = U8902.init;                  // No errors
  603
+    immutable U8902 u8902e = U8902.init;        // No errors
  604
+    immutable static U8902 u8902f = U8902.init; // Error: duplicate union...
  605
+    static U8902 u8902g = u8902e;               // Error: duplicate union...
  606
+    static U8902 u8902h = U8902.init;           // Error: duplicate union...
  607
+}
  608
+
  609
+/********************************************/
592 610
 // 9116
593 611
 
594 612
 void test9116()
@@ -672,6 +690,7 @@ int main()
672 690
     test7929();
673 691
     test7021();
674 692
     test8763();
  693
+    test8902();
675 694
     test9116();
676 695
     test9293();
677 696
     test9566();
30  test/runnable/traits.d
@@ -1088,6 +1088,35 @@ void test9552()
1088 1088
 
1089 1089
 /*************************************************************/
1090 1090
 
  1091
+void test9136()
  1092
+{
  1093
+    int x;
  1094
+    struct S1 { void f() { x++; } }
  1095
+    struct U1 { void f() { x++; } }
  1096
+    static struct S2 { }
  1097
+    static struct S3 { S1 s; }
  1098
+    static struct U2 { }
  1099
+    void f1() { x++; }
  1100
+    static void f2() { }
  1101
+
  1102
+    static assert(__traits(isNested, S1));
  1103
+    static assert(__traits(isNested, U1));
  1104
+    static assert(!__traits(isNested, S2));
  1105
+    static assert(!__traits(isNested, S3));
  1106
+    static assert(!__traits(isNested, U2));
  1107
+    static assert(!__traits(compiles, __traits(isNested, int) ));
  1108
+    static assert(!__traits(compiles, __traits(isNested, f1, f2) ));
  1109
+    static assert(__traits(isNested, f1));
  1110
+    static assert(!__traits(isNested, f2));
  1111
+
  1112
+    static class A { static class SC { } class NC { } }
  1113
+    static assert(!__traits(isNested, A));
  1114
+    static assert(!__traits(isNested, A.SC));
  1115
+    static assert(__traits(isNested, A.NC));
  1116
+}
  1117
+
  1118
+/********************************************************/
  1119
+
1091 1120
 int main()
1092 1121
 {
1093 1122
     test1();
@@ -1121,6 +1150,7 @@ int main()