1 /**
2  * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
3  *
4  * Specification: C11
5  *
6  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d, _cparse.d)
10  * Documentation:  https://dlang.org/phobos/dmd_cparse.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d
12  */
13 
14 module dmd.cparse;
15 
16 import core.stdc.stdio;
17 import core.stdc.string : memcpy;
18 
19 import dmd.astenums;
20 import dmd.errorsink;
21 import dmd.id;
22 import dmd.identifier;
23 import dmd.lexer;
24 import dmd.location;
25 import dmd.parse;
26 import dmd.root.array;
27 import dmd.common.outbuffer;
28 import dmd.root.rmem;
29 import dmd.tokens;
30 
31 /***********************************************************
32  */
33 final class CParser(AST) : Parser!AST
34 {
35     AST.Dsymbols* symbols;      // symbols declared in current scope
36 
37     bool addFuncName;           /// add declaration of __func__ to function symbol table
38     bool importBuiltins;        /// seen use of C compiler builtins, so import __builtins;
39 
40     private
41     {
42         structalign_t packalign;        // current state of #pragma pack alignment
43 
44         // #pragma pack stack
45         Array!Identifier* records;      // identifers (or null)
46         Array!structalign_t* packs;     // parallel alignment values
47     }
48 
49     /* C cannot be parsed without determining if an identifier is a type or a variable.
50      * For expressions like `(T)-3`, is it a cast or a minus expression?
51      * It also occurs with `typedef int (F)(); F fun;`
52      * but to build the AST we need to distinguish `fun` being a function as opposed to a variable.
53      * To fix, build a symbol table for the typedefs.
54      * Symbol table of typedefs indexed by Identifier cast to void*.
55      * 1. if an identifier is a typedef, then it will return a non-null Type
56      * 2. if an identifier is not a typedef, then it will return null
57      */
58     Array!(void*) typedefTab;  /// Array of AST.Type[Identifier], typedef's indexed by Identifier
59 
60     /* This is passed in as a list of #define lines, as generated by the C preprocessor with the
61      * appropriate switch to emit them. We append to it any #define's and #undef's encountered in the source
62      * file, as cpp with the -dD embeds them in the preprocessed output file.
63      * Once the file is parsed, then the #define's are converted to D symbols and appended to the array
64      * of Dsymbols returned by parseModule().
65      */
66     OutBuffer* defines;
67 
68     extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment,
69                             ErrorSink errorSink,
70                             const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope
71     {
72         const bool doUnittests = false;
73         super(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
74 
75         //printf("CParser.this()\n");
76         mod = _module;
77         linkage = LINK.c;
78         Ccompile = true;
79         this.packalign.setDefault();
80         this.defines = defines;
81 
82         // Configure sizes for C `long`, `long double`, `wchar_t`, ...
83         this.boolsize = target.boolsize;
84         this.shortsize = target.shortsize;
85         this.intsize = target.intsize;
86         this.longsize = target.longsize;
87         this.long_longsize = target.long_longsize;
88         this.long_doublesize = target.long_doublesize;
89         this.wchar_tsize = target.wchar_tsize;
90 
91         // C `char` is always unsigned in ImportC
92     }
93 
94     /********************************************
95      * Parse translation unit.
96      * C11 6.9
97      * translation-unit:
98      *    external-declaration
99      *    translation-unit external-declaration
100      *
101      * external-declaration:
102      *    function-definition
103      *    declaration
104      * Returns:
105      *  array of Dsymbols that were declared
106      */
107     override AST.Dsymbols* parseModule()
108     {
109         //printf("cparseTranslationUnit()\n");
110         symbols = new AST.Dsymbols();
111         typedefTab.push(null);  // C11 6.2.1-3 symbol table for "file scope"
112         while (1)
113         {
114             if (token.value == TOK.endOfFile)
115             {
116                 addDefines();   // convert #define's to Dsymbols
117 
118                 // wrap the symbols in `extern (C) { symbols }`
119                 auto wrap = new AST.Dsymbols();
120                 auto ld = new AST.LinkDeclaration(token.loc, LINK.c, symbols);
121                 wrap.push(ld);
122 
123                 if (importBuiltins)
124                 {
125                     /* Seen references to C builtin functions.
126                      * Import their definitions
127                      */
128                     auto s = new AST.Import(Loc.initial, null, Id.builtins, null, false);
129                     wrap.push(s);
130                 }
131 
132                 // end of file scope
133                 typedefTab.pop();
134                 assert(typedefTab.length == 0);
135 
136                 return wrap;
137             }
138 
139             cparseDeclaration(LVL.global);
140         }
141     }
142 
143     /******************************************************************************/
144     /********************************* Statement Parser ***************************/
145     //{
146 
147     /**********************
148      * C11 6.8
149      * statement:
150      *    labeled-statement
151      *    compound-statement
152      *    expression-statement
153      *    selection-statement
154      *    iteration-statement
155      *    jump-statement
156      *
157      * Params:
158      *      flags = PSxxxx
159      *      endPtr = store location of closing brace
160      *      pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
161      * Returns:
162      *      parsed statement
163      */
164     AST.Statement cparseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
165     {
166         AST.Statement s;
167         const loc = token.loc;
168 
169         //printf("cparseStatement()\n");
170 
171         const typedefTabLengthSave = typedefTab.length;
172         auto symbolsSave = symbols;
173         if (flags & ParseStatementFlags.scope_)
174         {
175             typedefTab.push(null);      // introduce new block scope
176         }
177 
178         if (!(flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope)))
179         {
180             symbols = new AST.Dsymbols();
181         }
182 
183         switch (token.value)
184         {
185         case TOK.identifier:
186             /* A leading identifier can be a declaration, label, or expression.
187              * A quick check of the next token can disambiguate most cases.
188              */
189             switch (peekNext())
190             {
191                 case TOK.colon:
192                 {
193                     // It's a label
194                     auto ident = token.ident;
195                     nextToken();    // advance to `:`
196                     nextToken();    // advance past `:`
197                     if (token.value == TOK.rightCurly)
198                         s = null;
199                     else if (token.value == TOK.leftCurly)
200                         s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
201                     else
202                         s = cparseStatement(0);
203                     s = new AST.LabelStatement(loc, ident, s);
204                     break;
205                 }
206 
207                 case TOK.dot:
208                 case TOK.arrow:
209                 case TOK.plusPlus:
210                 case TOK.minusMinus:
211                 case TOK.leftBracket:
212                 case TOK.question:
213                 case TOK.assign:
214                 case TOK.addAssign:
215                 case TOK.minAssign:
216                 case TOK.mulAssign:
217                 case TOK.divAssign:
218                 case TOK.modAssign:
219                 case TOK.andAssign:
220                 case TOK.orAssign:
221                 case TOK.xorAssign:
222                 case TOK.leftShiftAssign:
223                 case TOK.rightShiftAssign:
224                     goto Lexp;
225 
226                 case TOK.leftParenthesis:
227                     if (auto pt = lookupTypedef(token.ident))
228                     {
229                         if (*pt)
230                             goto Ldeclaration;
231                     }
232                     goto Lexp;  // function call
233 
234                 case TOK.semicolon:
235                     goto Lexp;
236 
237                 default:
238                 {
239                     /* If tokens look like a declaration, assume it is one
240                      */
241                     auto tk = &token;
242                     if (isCDeclaration(tk))
243                         goto Ldeclaration;
244                     goto Lexp;
245                 }
246             }
247             break;
248 
249         case TOK.charLiteral:
250         case TOK.int32Literal:
251         case TOK.uns32Literal:
252         case TOK.int64Literal:
253         case TOK.uns64Literal:
254         case TOK.int128Literal:
255         case TOK.uns128Literal:
256         case TOK.float32Literal:
257         case TOK.float64Literal:
258         case TOK.float80Literal:
259         case TOK.imaginary32Literal:
260         case TOK.imaginary64Literal:
261         case TOK.imaginary80Literal:
262         case TOK.leftParenthesis:
263         case TOK.and:
264         case TOK.mul:
265         case TOK.min:
266         case TOK.add:
267         case TOK.tilde:
268         case TOK.not:
269         case TOK.plusPlus:
270         case TOK.minusMinus:
271         case TOK.sizeof_:
272         case TOK._Generic:
273         case TOK._assert:
274         Lexp:
275             auto exp = cparseExpression();
276             if (token.value == TOK.identifier && exp.op == EXP.identifier)
277             {
278                 error(token.loc, "found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
279                 nextToken();
280             }
281             else
282                 check(TOK.semicolon, "statement");
283             s = new AST.ExpStatement(loc, exp);
284             break;
285 
286         // type-specifiers
287         case TOK.void_:
288         case TOK.char_:
289         case TOK.int16:
290         case TOK.int32:
291         case TOK.int64:
292         case TOK.__int128:
293         case TOK.float32:
294         case TOK.float64:
295         case TOK.signed:
296         case TOK.unsigned:
297         case TOK._Bool:
298         //case TOK._Imaginary:
299         case TOK._Complex:
300         case TOK.struct_:
301         case TOK.union_:
302         case TOK.enum_:
303         case TOK.typeof_:
304 
305         // storage-class-specifiers
306         case TOK.typedef_:
307         case TOK.extern_:
308         case TOK.static_:
309         case TOK._Thread_local:
310         case TOK.__thread:
311         case TOK.auto_:
312         case TOK.register:
313 
314         // function-specifiers
315         case TOK.inline:
316         case TOK._Noreturn:
317 
318         // type-qualifiers
319         case TOK.const_:
320         case TOK..volatile:
321         case TOK.restrict:
322         case TOK.__stdcall:
323 
324         // alignment-specifier
325         case TOK._Alignas:
326 
327         // atomic-type-specifier or type_qualifier
328         case TOK._Atomic:
329 
330         case TOK.__attribute__:
331 
332         Ldeclaration:
333         {
334             cparseDeclaration(LVL.local);
335             if (symbols.length > 1)
336             {
337                 auto as = new AST.Statements();
338                 as.reserve(symbols.length);
339                 foreach (d; (*symbols)[])
340                 {
341                     s = new AST.ExpStatement(loc, d);
342                     as.push(s);
343                 }
344                 s = new AST.CompoundDeclarationStatement(loc, as);
345                 symbols.setDim(0);
346             }
347             else if (symbols.length == 1)
348             {
349                 auto d = (*symbols)[0];
350                 s = new AST.ExpStatement(loc, d);
351                 symbols.setDim(0);
352             }
353             else
354                 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
355             if (flags & ParseStatementFlags.scope_)
356                 s = new AST.ScopeStatement(loc, s, token.loc);
357             break;
358         }
359 
360         case TOK._Static_assert:        // _Static_assert ( constant-expression, string-literal ) ;
361             s = new AST.StaticAssertStatement(cparseStaticAssert());
362             break;
363 
364         case TOK.leftCurly:
365         {
366             /* C11 6.8.2
367              * compound-statement:
368              *    { block-item-list (opt) }
369              *
370              * block-item-list:
371              *    block-item
372              *    block-item-list block-item
373              *
374              * block-item:
375              *    declaration
376              *    statement
377              */
378             nextToken();
379             auto statements = new AST.Statements();
380             while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
381             {
382                 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
383             }
384             if (endPtr)
385                 *endPtr = token.ptr;
386             endloc = token.loc;
387             if (pEndloc)
388             {
389                 *pEndloc = token.loc;
390                 pEndloc = null; // don't set it again
391             }
392             s = new AST.CompoundStatement(loc, statements);
393             if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
394                 s = new AST.ScopeStatement(loc, s, token.loc);
395             check(TOK.rightCurly, "compound statement");
396             break;
397         }
398 
399         case TOK.while_:
400         {
401             nextToken();
402             check(TOK.leftParenthesis);
403             auto condition = cparseExpression();
404             check(TOK.rightParenthesis);
405             Loc endloc;
406             auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
407             s = new AST.WhileStatement(loc, condition, _body, endloc, null);
408             break;
409         }
410 
411         case TOK.semicolon:
412             /* C11 6.8.3 null statement
413              */
414             nextToken();
415             s = new AST.ExpStatement(loc, cast(AST.Expression)null);
416             break;
417 
418         case TOK.do_:
419         {
420             nextToken();
421             auto _body = cparseStatement(ParseStatementFlags.scope_);
422             check(TOK.while_);
423             check(TOK.leftParenthesis);
424             auto condition = cparseExpression();
425             check(TOK.rightParenthesis);
426             check(TOK.semicolon, "terminating `;` required after do-while statement");
427             s = new AST.DoStatement(loc, _body, condition, token.loc);
428             break;
429         }
430 
431         case TOK.for_:
432         {
433             AST.Statement _init;
434             AST.Expression condition;
435             AST.Expression increment;
436 
437             nextToken();
438             check(TOK.leftParenthesis);
439             if (token.value == TOK.semicolon)
440             {
441                 _init = null;
442                 nextToken();
443             }
444             else
445             {
446                 _init = cparseStatement(0);
447             }
448             if (token.value == TOK.semicolon)
449             {
450                 condition = null;
451                 nextToken();
452             }
453             else
454             {
455                 condition = cparseExpression();
456                 check(TOK.semicolon, "`for` condition");
457             }
458             if (token.value == TOK.rightParenthesis)
459             {
460                 increment = null;
461                 nextToken();
462             }
463             else
464             {
465                 increment = cparseExpression();
466                 check(TOK.rightParenthesis);
467             }
468             Loc endloc;
469             auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
470             s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
471             break;
472         }
473 
474         case TOK.if_:
475         {
476             nextToken();
477             check(TOK.leftParenthesis);
478             auto condition = cparseExpression();
479             check(TOK.rightParenthesis);
480             auto ifbody = cparseStatement(ParseStatementFlags.scope_);
481             AST.Statement elsebody;
482             if (token.value == TOK.else_)
483             {
484                 nextToken();
485                 elsebody = cparseStatement(ParseStatementFlags.scope_);
486             }
487             else
488                 elsebody = null;
489             if (condition && ifbody)
490                 s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc);
491             else
492                 s = null; // don't propagate parsing errors
493             break;
494         }
495 
496         case TOK.else_:
497             error("found `else` without a corresponding `if` statement");
498             goto Lerror;
499 
500         case TOK.switch_:
501         {
502             nextToken();
503             check(TOK.leftParenthesis);
504             auto condition = cparseExpression();
505             check(TOK.rightParenthesis);
506             auto _body = cparseStatement(ParseStatementFlags.scope_);
507             s = new AST.SwitchStatement(loc, null, condition, _body, false, token.loc);
508             break;
509         }
510 
511         case TOK.case_:
512         {
513 
514             nextToken();
515             auto exp = cparseAssignExp();
516             AST.Expression expHigh;
517             if (token.value == TOK.dotDotDot)
518             {
519                 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
520                  */
521                 nextToken();
522                 expHigh = cparseAssignExp();
523             }
524             check(TOK.colon);
525 
526             if (flags & ParseStatementFlags.curlyScope)
527             {
528                 auto statements = new AST.Statements();
529                 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
530                 {
531                     auto cur = cparseStatement(ParseStatementFlags.curlyScope);
532                     statements.push(cur);
533 
534                     // https://issues.dlang.org/show_bug.cgi?id=21739
535                     // Stop at the last break s.t. the following non-case statements are
536                     // not merged into the current case. This can happen for
537                     // case 1: ... break;
538                     // debug { case 2: ... }
539                     if (cur && cur.isBreakStatement())
540                         break;
541                 }
542                 s = new AST.CompoundStatement(loc, statements);
543             }
544             else
545             {
546                 s = cparseStatement(0);
547             }
548             s = new AST.ScopeStatement(loc, s, token.loc);
549             if (expHigh)
550                 s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
551             else
552                 s = new AST.CaseStatement(loc, exp, s);
553             break;
554         }
555 
556         case TOK.default_:
557         {
558             nextToken();
559             check(TOK.colon);
560 
561             if (flags & ParseStatementFlags.curlyScope)
562             {
563                 auto statements = new AST.Statements();
564                 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
565                 {
566                     statements.push(cparseStatement(ParseStatementFlags.curlyScope));
567                 }
568                 s = new AST.CompoundStatement(loc, statements);
569             }
570             else
571                 s = cparseStatement(0);
572             s = new AST.ScopeStatement(loc, s, token.loc);
573             s = new AST.DefaultStatement(loc, s);
574             break;
575         }
576 
577         case TOK.return_:
578         {
579             /*  return ;
580              *  return expression ;
581              */
582             nextToken();
583             auto exp = token.value == TOK.semicolon ? null : cparseExpression();
584             check(TOK.semicolon, "`return` statement");
585             s = new AST.ReturnStatement(loc, exp);
586             break;
587         }
588 
589         case TOK.break_:
590             nextToken();
591             check(TOK.semicolon, "`break` statement");
592             s = new AST.BreakStatement(loc, null);
593             break;
594 
595         case TOK.continue_:
596             nextToken();
597             check(TOK.semicolon, "`continue` statement");
598             s = new AST.ContinueStatement(loc, null);
599             break;
600 
601         case TOK.goto_:
602         {
603             Identifier ident;
604             nextToken();
605             if (token.value != TOK.identifier)
606             {
607                 error("identifier expected following `goto`");
608                 ident = null;
609             }
610             else
611             {
612                 ident = token.ident;
613                 nextToken();
614             }
615             s = new AST.GotoStatement(loc, ident);
616             check(TOK.semicolon, "`goto` statement");
617             break;
618         }
619 
620         case TOK.asm_:
621             switch (peekNext())
622             {
623                 case TOK.goto_:
624                 case TOK.inline:
625                 case TOK..volatile:
626                 case TOK.leftParenthesis:
627                     s = cparseGnuAsm();
628                     break;
629 
630                 default:
631                     // ImportC extensions: parse as a D asm block.
632                     s = parseAsm(compileEnv.masm);
633                     break;
634             }
635             break;
636 
637         default:
638             error("found `%s` instead of statement", token.toChars());
639             goto Lerror;
640 
641         Lerror:
642             panic();
643             if (token.value == TOK.semicolon)
644                 nextToken();
645             s = null;
646             break;
647         }
648         if (pEndloc)
649             *pEndloc = prevloc;
650         symbols = symbolsSave;
651         typedefTab.setDim(typedefTabLengthSave);
652         return s;
653     }
654 
655     //}
656     /*******************************************************************************/
657     /********************************* Expression Parser ***************************/
658     //{
659 
660     /**************
661      * C11 6.5.17
662      * expression:
663      *  assignment-expression
664      *  expression , assignment-expression
665      */
666     AST.Expression cparseExpression()
667     {
668         auto loc = token.loc;
669 
670         //printf("cparseExpression() loc = %d\n", loc.linnum);
671         auto e = cparseAssignExp();
672         while (token.value == TOK.comma)
673         {
674             nextToken();
675             auto e2 = cparseAssignExp();
676             e = new AST.CommaExp(loc, e, e2, false);
677             loc = token.loc;
678         }
679         return e;
680     }
681 
682 
683     /*********************
684      * C11 6.5.1
685      * primary-expression:
686      *    identifier
687      *    constant
688      *    string-literal
689      *    ( expression )
690      *    generic-selection
691      *    __builtin_va_arg(assign_expression, type)
692      */
693     AST.Expression cparsePrimaryExp()
694     {
695         AST.Expression e;
696         const loc = token.loc;
697 
698         //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
699         switch (token.value)
700         {
701         case TOK.identifier:
702             const id = token.ident.toString();
703             if (id.length > 2 && id[0] == '_' && id[1] == '_')  // leading double underscore
704             {
705                 if (token.ident is Id.__func__)
706                 {
707                     addFuncName = true;     // implicitly declare __func__
708                 }
709                 else if (token.ident is Id.builtin_va_arg)
710                 {
711                     e = cparseBuiltin_va_arg();
712                     break;
713                 }
714                 else
715                     importBuiltins = true;  // probably one of those compiler extensions
716             }
717             e = new AST.IdentifierExp(loc, token.ident);
718             nextToken();
719             break;
720 
721         case TOK.charLiteral:
722         case TOK.int32Literal:
723             e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
724             nextToken();
725             break;
726 
727         case TOK.uns32Literal:
728             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
729             nextToken();
730             break;
731 
732         case TOK.int64Literal:
733             e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
734             nextToken();
735             break;
736 
737         case TOK.uns64Literal:
738             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
739             nextToken();
740             break;
741 
742         case TOK.float32Literal:
743             e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
744             nextToken();
745             break;
746 
747         case TOK.float64Literal:
748             e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
749             nextToken();
750             break;
751 
752         case TOK.float80Literal:
753             e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
754             nextToken();
755             break;
756 
757         case TOK.imaginary32Literal:
758             e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
759             nextToken();
760             break;
761 
762         case TOK.imaginary64Literal:
763             e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
764             nextToken();
765             break;
766 
767         case TOK.imaginary80Literal:
768             e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
769             nextToken();
770             break;
771 
772         case TOK.string_:
773         {
774             // cat adjacent strings
775             auto s = token.ustring;
776             auto len = token.len;
777             auto postfix = token.postfix;
778             while (1)
779             {
780                 nextToken();
781                 if (token.value == TOK.string_)
782                 {
783                     if (token.postfix)
784                     {
785                         if (token.postfix != postfix)
786                             error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
787                         postfix = token.postfix;
788                     }
789 
790                     const len1 = len;
791                     const len2 = token.len;
792                     len = len1 + len2;
793                     auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
794                     memcpy(s2, s, len1 * char.sizeof);
795                     memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
796                     s = s2;
797                 }
798                 else
799                     break;
800             }
801             e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
802             break;
803         }
804 
805         case TOK.leftParenthesis:
806             nextToken();
807             if (token.value == TOK.leftCurly)
808                 e = cparseStatementExpression();        // gcc extension
809             else
810                 e = cparseExpression();
811             check(TOK.rightParenthesis);
812             break;
813 
814         case TOK._Generic:
815             e = cparseGenericSelection();
816             break;
817 
818         case TOK._assert:  // __check(assign-exp) extension
819             nextToken();
820             check(TOK.leftParenthesis, "`__check`");
821             e = parseAssignExp();
822             check(TOK.rightParenthesis);
823             e = new AST.AssertExp(loc, e, null);
824             break;
825 
826         default:
827             error("expression expected, not `%s`", token.toChars());
828             // Anything for e, as long as it's not NULL
829             e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
830             nextToken();
831             break;
832         }
833         return e;
834     }
835 
836     /*********************************
837      * C11 6.5.2
838      * postfix-expression:
839      *    primary-expression
840      *    postfix-expression [ expression ]
841      *    postfix-expression ( argument-expression-list (opt) )
842      *    postfix-expression . identifier
843      *    postfix-expression -> identifier
844      *    postfix-expression ++
845      *    postfix-expression --
846      *    ( type-name ) { initializer-list }
847      *    ( type-name ) { initializer-list , }
848      *
849      * argument-expression-list:
850      *    assignment-expression
851      *    argument-expression-list , assignment-expression
852      */
853     private AST.Expression cparsePostfixExp(AST.Expression e)
854     {
855         e = cparsePrimaryExp();
856         return cparsePostfixOperators(e);
857     }
858 
859     /********************************
860      * C11 6.5.2
861      * Parse a series of operators for a postfix expression after already parsing
862      * a primary-expression or compound literal expression.
863      * Params:
864      *      e = parsed primary or compound literal expression
865      * Returns:
866      *      parsed postfix expression
867      */
868     private AST.Expression cparsePostfixOperators(AST.Expression e)
869     {
870         while (1)
871         {
872             const loc = token.loc;
873             switch (token.value)
874             {
875             case TOK.dot:
876                 nextToken();
877                 if (token.value == TOK.identifier)
878                 {
879                     Identifier id = token.ident;
880                     e = new AST.DotIdExp(loc, e, id);
881                     break;
882                 }
883                 error("identifier expected following `.`, not `%s`", token.toChars());
884                 break;
885 
886             case TOK.arrow:
887                 nextToken();
888                 if (token.value == TOK.identifier)
889                 {
890                     Identifier id = token.ident;
891                     auto die = new AST.DotIdExp(loc, e, id);
892                     die.arrow = true;
893                     e = die;
894                     break;
895                 }
896                 error("identifier expected following `->`, not `%s`", token.toChars());
897                 break;
898 
899             case TOK.plusPlus:
900                 e = new AST.PostExp(EXP.plusPlus, loc, e);
901                 break;
902 
903             case TOK.minusMinus:
904                 e = new AST.PostExp(EXP.minusMinus, loc, e);
905                 break;
906 
907             case TOK.leftParenthesis:
908                 e = new AST.CallExp(loc, e, cparseArguments());
909                 continue;
910 
911             case TOK.leftBracket:
912                 {
913                     // array dereferences:
914                     //      array[index]
915                     AST.Expression index;
916                     auto arguments = new AST.Expressions();
917 
918                     inBrackets++;
919                     nextToken();
920                     index = cparseAssignExp();
921                     arguments.push(index);
922                     check(TOK.rightBracket);
923                     inBrackets--;
924                     e = new AST.ArrayExp(loc, e, arguments);
925                     continue;
926                 }
927             default:
928                 return e;
929             }
930             nextToken();
931         }
932     }
933 
934     /************************
935      * C11 6.5.3
936      * unary-expression:
937      *    postfix-expression
938      *    ++ unary-expression
939      *    -- unary-expression
940      *    unary-operator cast-expression
941      *    sizeof unary-expression
942      *    sizeof ( type-name )
943      *    _Alignof ( type-name )
944      *
945      * unary-operator:
946      *    & * + - ~ !
947      */
948     private AST.Expression cparseUnaryExp()
949     {
950         AST.Expression e;
951         const loc = token.loc;
952 
953         switch (token.value)
954         {
955         case TOK.plusPlus:
956             nextToken();
957             // Parse `++` as an unary operator so that cast expressions only give
958             // an error for being non-lvalues.
959             e = cparseCastExp();
960             e = new AST.PreExp(EXP.prePlusPlus, loc, e);
961             break;
962 
963         case TOK.minusMinus:
964             nextToken();
965             // Parse `--` as an unary operator, same as prefix increment.
966             e = cparseCastExp();
967             e = new AST.PreExp(EXP.preMinusMinus, loc, e);
968             break;
969 
970         case TOK.and:
971             nextToken();
972             e = cparseCastExp();
973             e = new AST.AddrExp(loc, e);
974             break;
975 
976         case TOK.mul:
977             nextToken();
978             e = cparseCastExp();
979             e = new AST.PtrExp(loc, e);
980             break;
981 
982         case TOK.min:
983             nextToken();
984             e = cparseCastExp();
985             e = new AST.NegExp(loc, e);
986             break;
987 
988         case TOK.add:
989             nextToken();
990             e = cparseCastExp();
991             e = new AST.UAddExp(loc, e);
992             break;
993 
994         case TOK.not:
995             nextToken();
996             e = cparseCastExp();
997             e = new AST.NotExp(loc, e);
998             break;
999 
1000         case TOK.tilde:
1001             nextToken();
1002             e = cparseCastExp();
1003             e = new AST.ComExp(loc, e);
1004             break;
1005 
1006         case TOK.sizeof_:
1007         {
1008             nextToken();
1009             if (token.value == TOK.leftParenthesis)
1010             {
1011                 auto tk = peek(&token);
1012                 if (isTypeName(tk))
1013                 {
1014                     /* Expression may be either be requesting the sizeof a type-name
1015                      * or a compound literal, which requires checking whether
1016                      * the next token is leftCurly
1017                      */
1018                     nextToken();
1019                     auto t = cparseTypeName();
1020                     check(TOK.rightParenthesis);
1021                     if (token.value == TOK.leftCurly)
1022                     {
1023                         // ( type-name ) { initializer-list }
1024                         auto ci = cparseInitializer();
1025                         e = new AST.CompoundLiteralExp(loc, t, ci);
1026                         e = cparsePostfixOperators(e);
1027                     }
1028                     else
1029                     {
1030                         // ( type-name )
1031                         e = new AST.TypeExp(loc, t);
1032                     }
1033                 }
1034                 else
1035                 {
1036                     // must be an expression
1037                     e = cparseUnaryExp();
1038                 }
1039             }
1040             else
1041             {
1042                 //C11 6.5.3
1043                 e = cparseUnaryExp();
1044             }
1045 
1046             e = new AST.DotIdExp(loc, e, Id.__sizeof);
1047             break;
1048         }
1049 
1050         case TOK._Alignof:
1051         {
1052             nextToken();
1053             check(TOK.leftParenthesis);
1054             auto t = cparseTypeName();
1055             check(TOK.rightParenthesis);
1056             e = new AST.TypeExp(loc, t);
1057             e = new AST.DotIdExp(loc, e, Id.__xalignof);
1058             break;
1059         }
1060 
1061         default:
1062             e = cparsePostfixExp(e);
1063             break;
1064         }
1065         assert(e);
1066         return e;
1067     }
1068 
1069     /**************
1070      * C11 6.5.4
1071      * cast-expression
1072      *    unary-expression
1073      *    ( type-name ) cast-expression
1074      */
1075     private AST.Expression cparseCastExp()
1076     {
1077         if (token.value == TOK.leftParenthesis)
1078         {
1079             //printf("cparseCastExp()\n");
1080             auto tk = peek(&token);
1081             bool iscast;
1082             bool isexp;
1083             if (tk.value == TOK.identifier)
1084             {
1085                 iscast = isTypedef(tk.ident);
1086                 isexp = !iscast;
1087             }
1088             if (isexp)
1089             {
1090                 // ( identifier ) is an expression
1091                 return cparseUnaryExp();
1092             }
1093 
1094             // If ( type-name )
1095             auto pt = &token;
1096 
1097             if (isCastExpression(pt))
1098             {
1099                 // Expression may be either a cast or a compound literal, which
1100                 // requires checking whether the next token is leftCurly
1101                 const loc = token.loc;
1102                 nextToken();
1103                 auto t = cparseTypeName();
1104                 check(TOK.rightParenthesis);
1105                 pt = &token;
1106 
1107                 if (token.value == TOK.leftCurly)
1108                 {
1109                     // C11 6.5.2.5 ( type-name ) { initializer-list }
1110                     auto ci = cparseInitializer();
1111                     auto ce = new AST.CompoundLiteralExp(loc, t, ci);
1112                     return cparsePostfixOperators(ce);
1113                 }
1114 
1115                 if (iscast)
1116                 {
1117                     // ( type-name ) cast-expression
1118                     auto ce = cparseCastExp();
1119                     return new AST.CastExp(loc, ce, t);
1120                 }
1121 
1122                 if (t.isTypeIdentifier() &&
1123                     isexp &&
1124                     token.value == TOK.leftParenthesis &&
1125                     !isCastExpression(pt))
1126                 {
1127                     /* (t)(...)... might be a cast expression or a function call,
1128                      * with different grammars: a cast would be cparseCastExp(),
1129                      * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1130                      * We can't know until t is known. So, parse it as a function call
1131                      * and let semantic() rewrite the AST as a CastExp if it turns out
1132                      * to be a type.
1133                      */
1134                     auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
1135                     ie.parens = true;    // let semantic know it might be a CastExp
1136                     AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
1137                     return cparsePostfixOperators(e);
1138                 }
1139 
1140                 // ( type-name ) cast-expression
1141                 auto ce = cparseCastExp();
1142                 return new AST.CastExp(loc, ce, t);
1143             }
1144         }
1145         return cparseUnaryExp();
1146     }
1147 
1148     /**************
1149      * C11 6.5.5
1150      * multiplicative-expression
1151      *    cast-expression
1152      *    multiplicative-expression * cast-expression
1153      *    multiplicative-expression / cast-expression
1154      *    multiplicative-expression % cast-expression
1155      */
1156     private AST.Expression cparseMulExp()
1157     {
1158         const loc = token.loc;
1159         auto e = cparseCastExp();
1160 
1161         while (1)
1162         {
1163             switch (token.value)
1164             {
1165             case TOK.mul:
1166                 nextToken();
1167                 auto e2 = cparseCastExp();
1168                 e = new AST.MulExp(loc, e, e2);
1169                 continue;
1170 
1171             case TOK.div:
1172                 nextToken();
1173                 auto e2 = cparseCastExp();
1174                 e = new AST.DivExp(loc, e, e2);
1175                 continue;
1176 
1177             case TOK.mod:
1178                 nextToken();
1179                 auto e2 = cparseCastExp();
1180                 e = new AST.ModExp(loc, e, e2);
1181                 continue;
1182 
1183             default:
1184                 break;
1185             }
1186             break;
1187         }
1188         return e;
1189     }
1190 
1191     /**************
1192      * C11 6.5.6
1193      * additive-expression
1194      *    multiplicative-expression
1195      *    additive-expression + multiplicative-expression
1196      *    additive-expression - multiplicative-expression
1197      */
1198     private AST.Expression cparseAddExp()
1199     {
1200         const loc = token.loc;
1201         auto e = cparseMulExp();
1202 
1203         while (1)
1204         {
1205             switch (token.value)
1206             {
1207             case TOK.add:
1208                 nextToken();
1209                 auto e2 = cparseMulExp();
1210                 e = new AST.AddExp(loc, e, e2);
1211                 continue;
1212 
1213             case TOK.min:
1214                 nextToken();
1215                 auto e2 = cparseMulExp();
1216                 e = new AST.MinExp(loc, e, e2);
1217                 continue;
1218 
1219             default:
1220                 break;
1221             }
1222             break;
1223         }
1224         return e;
1225     }
1226 
1227     /**************
1228      * C11 6.5.7
1229      * shift-expression
1230      *    additive-expression
1231      *    shift-expression << additive-expression
1232      *    shift-expression >> additive-expression
1233      */
1234     private AST.Expression cparseShiftExp()
1235     {
1236         const loc = token.loc;
1237         auto e = cparseAddExp();
1238 
1239         while (1)
1240         {
1241             switch (token.value)
1242             {
1243             case TOK.leftShift:
1244                 nextToken();
1245                 auto e2 = cparseAddExp();
1246                 e = new AST.ShlExp(loc, e, e2);
1247                 continue;
1248 
1249             case TOK.rightShift:
1250                 nextToken();
1251                 auto e2 = cparseAddExp();
1252                 e = new AST.ShrExp(loc, e, e2);
1253                 continue;
1254 
1255             default:
1256                 break;
1257             }
1258             break;
1259         }
1260         return e;
1261     }
1262 
1263     /**************
1264      * C11 6.5.8
1265      * relational-expression
1266      *    shift-expression
1267      *    relational-expression < shift-expression
1268      *    relational-expression > shift-expression
1269      *    relational-expression <= shift-expression
1270      *    relational-expression >= shift-expression
1271      */
1272     private AST.Expression cparseRelationalExp()
1273     {
1274         const loc = token.loc;
1275 
1276         auto e = cparseShiftExp();
1277 
1278         EXP op = EXP.reserved;
1279         switch (token.value)
1280         {
1281         case TOK.lessThan:       op = EXP.lessThan; goto Lcmp;
1282         case TOK.lessOrEqual:    op = EXP.lessOrEqual; goto Lcmp;
1283         case TOK.greaterThan:    op = EXP.greaterThan; goto Lcmp;
1284         case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
1285         Lcmp:
1286             nextToken();
1287             auto e2 = cparseShiftExp();
1288             e = new AST.CmpExp(op, loc, e, e2);
1289             break;
1290 
1291         default:
1292             break;
1293         }
1294         return e;
1295     }
1296 
1297     /**************
1298      * C11 6.5.9
1299      * equality-expression
1300      *    relational-expression
1301      *    equality-expression == relational-expression
1302      *    equality-expression != relational-expression
1303      */
1304     private AST.Expression cparseEqualityExp()
1305     {
1306         const loc = token.loc;
1307 
1308         auto e = cparseRelationalExp();
1309 
1310         EXP op = EXP.reserved;
1311         switch (token.value)
1312         {
1313         case TOK.equal:         op = EXP.equal;    goto Lequal;
1314         case TOK.notEqual:      op = EXP.notEqual; goto Lequal;
1315         Lequal:
1316             nextToken();
1317             auto e2 = cparseRelationalExp();
1318             e = new AST.EqualExp(op, loc, e, e2);
1319             break;
1320 
1321         default:
1322             break;
1323         }
1324         return e;
1325     }
1326 
1327     /**************
1328      * C11 6.5.10
1329      * AND-expression
1330      *    equality-expression
1331      *    AND-expression & equality-expression
1332      */
1333     private AST.Expression cparseAndExp()
1334     {
1335         Loc loc = token.loc;
1336         auto e = cparseEqualityExp();
1337         while (token.value == TOK.and)
1338         {
1339             nextToken();
1340             auto e2 = cparseEqualityExp();
1341             e = new AST.AndExp(loc, e, e2);
1342             loc = token.loc;
1343         }
1344         return e;
1345     }
1346 
1347     /**************
1348      * C11 6.5.11
1349      * exclusive-OR-expression
1350      *    AND-expression
1351      *    exclusive-OR-expression ^ AND-expression
1352      */
1353     private AST.Expression cparseXorExp()
1354     {
1355         const loc = token.loc;
1356 
1357         auto e = cparseAndExp();
1358         while (token.value == TOK.xor)
1359         {
1360             nextToken();
1361             auto e2 = cparseAndExp();
1362             e = new AST.XorExp(loc, e, e2);
1363         }
1364         return e;
1365     }
1366 
1367     /**************
1368      * C11 6.5.12
1369      * inclusive-OR-expression
1370      *    exclusive-OR-expression
1371      *    inclusive-OR-expression | exclusive-OR-expression
1372      */
1373     private AST.Expression cparseOrExp()
1374     {
1375         const loc = token.loc;
1376 
1377         auto e = cparseXorExp();
1378         while (token.value == TOK.or)
1379         {
1380             nextToken();
1381             auto e2 = cparseXorExp();
1382             e = new AST.OrExp(loc, e, e2);
1383         }
1384         return e;
1385     }
1386 
1387     /**************
1388      * C11 6.5.13
1389      * logical-AND-expression
1390      *    inclusive-OR-expression
1391      *    logical-AND-expression && inclusive-OR-expression
1392      */
1393     private AST.Expression cparseAndAndExp()
1394     {
1395         const loc = token.loc;
1396 
1397         auto e = cparseOrExp();
1398         while (token.value == TOK.andAnd)
1399         {
1400             nextToken();
1401             auto e2 = cparseOrExp();
1402             e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
1403         }
1404         return e;
1405     }
1406 
1407     /**************
1408      * C11 6.5.14
1409      * logical-OR-expression
1410      *    logical-AND-expression
1411      *    logical-OR-expression || logical-AND-expression
1412      */
1413     private AST.Expression cparseOrOrExp()
1414     {
1415         const loc = token.loc;
1416 
1417         auto e = cparseAndAndExp();
1418         while (token.value == TOK.orOr)
1419         {
1420             nextToken();
1421             auto e2 = cparseAndAndExp();
1422             e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
1423         }
1424         return e;
1425     }
1426 
1427     /**************
1428      * C11 6.5.15
1429      * conditional-expression:
1430      *    logical-OR-expression
1431      *    logical-OR-expression ? expression : conditional-expression
1432      */
1433     private AST.Expression cparseCondExp()
1434     {
1435         const loc = token.loc;
1436 
1437         auto e = cparseOrOrExp();
1438         if (token.value == TOK.question)
1439         {
1440             nextToken();
1441             auto e1 = cparseExpression();
1442             check(TOK.colon);
1443             auto e2 = cparseCondExp();
1444             e = new AST.CondExp(loc, e, e1, e2);
1445         }
1446         return e;
1447     }
1448 
1449     /**************
1450      * C11 6.5.16
1451      * assignment-expression:
1452      *    conditional-expression
1453      *    unary-expression assignment-operator assignment-expression
1454      *
1455      * assignment-operator:
1456      *    = *= /= %= += -= <<= >>= &= ^= |=
1457      */
1458     AST.Expression cparseAssignExp()
1459     {
1460         AST.Expression e;
1461         e = cparseCondExp(); // constrain it to being unary-expression in semantic pass
1462         if (e is null)
1463             return e;
1464 
1465         const loc = token.loc;
1466         switch (token.value)
1467         {
1468         case TOK.assign:
1469             nextToken();
1470             auto e2 = cparseAssignExp();
1471             e = new AST.AssignExp(loc, e, e2);
1472             break;
1473 
1474         case TOK.addAssign:
1475             nextToken();
1476             auto e2 = cparseAssignExp();
1477             e = new AST.AddAssignExp(loc, e, e2);
1478             break;
1479 
1480         case TOK.minAssign:
1481             nextToken();
1482             auto e2 = cparseAssignExp();
1483             e = new AST.MinAssignExp(loc, e, e2);
1484             break;
1485 
1486         case TOK.mulAssign:
1487             nextToken();
1488             auto e2 = cparseAssignExp();
1489             e = new AST.MulAssignExp(loc, e, e2);
1490             break;
1491 
1492         case TOK.divAssign:
1493             nextToken();
1494             auto e2 = cparseAssignExp();
1495             e = new AST.DivAssignExp(loc, e, e2);
1496             break;
1497 
1498         case TOK.modAssign:
1499             nextToken();
1500             auto e2 = cparseAssignExp();
1501             e = new AST.ModAssignExp(loc, e, e2);
1502             break;
1503 
1504         case TOK.andAssign:
1505             nextToken();
1506             auto e2 = cparseAssignExp();
1507             e = new AST.AndAssignExp(loc, e, e2);
1508             break;
1509 
1510         case TOK.orAssign:
1511             nextToken();
1512             auto e2 = cparseAssignExp();
1513             e = new AST.OrAssignExp(loc, e, e2);
1514             break;
1515 
1516         case TOK.xorAssign:
1517             nextToken();
1518             auto e2 = cparseAssignExp();
1519             e = new AST.XorAssignExp(loc, e, e2);
1520             break;
1521 
1522         case TOK.leftShiftAssign:
1523             nextToken();
1524             auto e2 = cparseAssignExp();
1525             e = new AST.ShlAssignExp(loc, e, e2);
1526             break;
1527 
1528         case TOK.rightShiftAssign:
1529             nextToken();
1530             auto e2 = cparseAssignExp();
1531             e = new AST.ShrAssignExp(loc, e, e2);
1532             break;
1533 
1534         default:
1535             break;
1536         }
1537 
1538         return e;
1539     }
1540 
1541     /***********************
1542      * C11 6.5.1.1
1543      * _Generic ( assignment-expression, generic-assoc-list )
1544      *
1545      * generic-assoc-list:
1546      *   generic-association
1547      *   generic-assoc-list generic-association
1548      *
1549      * generic-association:
1550      *   type-name : assignment-expression
1551      *   default : assignment-expression
1552      */
1553     private AST.Expression cparseGenericSelection()
1554     {
1555         const loc = token.loc;
1556         nextToken();
1557         check(TOK.leftParenthesis);
1558         auto cntlExp = cparseAssignExp();
1559         check(TOK.comma);
1560         auto types = new AST.Types();
1561         auto exps = new AST.Expressions();
1562         bool sawDefault;
1563         while (1)
1564         {
1565             AST.Type t;
1566             if (token.value == TOK.default_)
1567             {
1568                 nextToken();
1569                 if (sawDefault)
1570                     error("only one `default` allowed in generic-assoc-list");
1571                 sawDefault = true;
1572                 t = null;
1573             }
1574             else
1575                 t = cparseTypeName();
1576             types.push(t);
1577 
1578             check(TOK.colon);
1579             auto e = cparseAssignExp();
1580             exps.push(e);
1581             if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
1582                 break;
1583             check(TOK.comma);
1584         }
1585         check(TOK.rightParenthesis);
1586         return new AST.GenericExp(loc, cntlExp, types, exps);
1587     }
1588 
1589     /***********************
1590      * C11 6.6 Constant expressions
1591      * constant-expression:
1592      *   conditional-expression
1593      */
1594     private AST.Expression cparseConstantExp()
1595     {
1596         return cparseAssignExp();
1597     }
1598 
1599     /*****************************
1600      * gcc extension:
1601      *    type __builtin_va_arg(assign-expression, type)
1602      * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1603      *    va_arg!(type)(assign-expression);
1604      * Lexer is on `__builtin_va_arg`
1605      */
1606     private AST.Expression cparseBuiltin_va_arg()
1607     {
1608         importBuiltins = true;  // need core.stdc.stdarg
1609 
1610         nextToken();
1611         check(TOK.leftParenthesis);
1612 
1613         auto arguments = new AST.Expressions();
1614         auto arg = cparseAssignExp();
1615         arguments.push(arg);
1616 
1617         check(TOK.comma);
1618 
1619         auto t = cparseTypeName();
1620         auto tiargs = new AST.Objects();
1621         tiargs.push(t);
1622 
1623         const loc = loc;
1624         auto ti = new AST.TemplateInstance(loc, Id.va_arg, tiargs);
1625         auto tie = new AST.ScopeExp(loc, ti);
1626 
1627         AST.Expression e = new AST.CallExp(loc, tie, arguments);
1628 
1629         check(TOK.rightParenthesis);
1630         return e;
1631     }
1632 
1633     /*****************************
1634      * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1635      * Represent as a function literal, then call the function literal.
1636      * Parser is on opening curly brace.
1637      */
1638     private AST.Expression cparseStatementExpression()
1639     {
1640         AST.ParameterList parameterList;
1641         StorageClass stc = 0;
1642         const loc = token.loc;
1643         typedefTab.push(null);
1644         auto fbody = cparseStatement(ParseStatementFlags.scope_);
1645         typedefTab.pop();                                        // end of function scope
1646 
1647         // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1648         auto ss = fbody.isScopeStatement();
1649         auto cs = ss.statement.isCompoundStatement();
1650         assert(cs);
1651         if (const len = (*cs.statements).length)
1652         {
1653             auto s = (*cs.statements)[len - 1];
1654             if (auto es = s.isExpStatement())
1655                 (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
1656         }
1657 
1658         auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
1659         auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
1660         fd.fbody = fbody;
1661 
1662         auto fe = new AST.FuncExp(loc, fd);
1663         auto args = new AST.Expressions();
1664         auto e = new AST.CallExp(loc, fe, args);   // call the function literal
1665         return e;
1666     }
1667 
1668     //}
1669     /********************************************************************************/
1670     /********************************* Declaration Parser ***************************/
1671     //{
1672 
1673     /*************************************
1674      * C11 6.7
1675      * declaration:
1676      *    declaration-specifiers init-declarator-list (opt) ;
1677      *    static_assert-declaration
1678      *
1679      * init-declarator-list:
1680      *    init-declarator
1681      *    init-declarator-list , init-declarator
1682      *
1683      * init-declarator:
1684      *    declarator
1685      *    declarator = initializer
1686      *
1687      * Params:
1688      *    level = declaration context
1689      */
1690     void cparseDeclaration(LVL level)
1691     {
1692         //printf("cparseDeclaration(level = %d)\n", level);
1693         if (token.value == TOK._Static_assert)
1694         {
1695             auto s = cparseStaticAssert();
1696             symbols.push(s);
1697             return;
1698         }
1699 
1700         if (token.value == TOK.__pragma)
1701         {
1702             uupragmaDirective(scanloc);
1703             return;
1704         }
1705 
1706         if (token.value == TOK._import) // import declaration extension
1707         {
1708             auto a = parseImport();
1709             if (a && a.length)
1710                 symbols.append(a);
1711             return;
1712         }
1713 
1714         const typedefTabLengthSave = typedefTab.length;
1715         auto symbolsSave = symbols;
1716         Specifier specifier;
1717         specifier.packalign = this.packalign;
1718         auto tspec = cparseDeclarationSpecifiers(level, specifier);
1719 
1720         AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
1721         {
1722             /* `struct tag;` and `struct tag { ... };`
1723              * always result in a declaration in the current scope
1724              */
1725             auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
1726                         (tt.tok == TOK.union_)  ? new AST.UnionDeclaration(tt.loc, tt.id) :
1727                                                   new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
1728             if (!tt.packalign.isUnknown())
1729             {
1730                 // saw `struct __declspec(align(N)) Tag ...`
1731                 auto st = stag.isStructDeclaration();
1732                 st.alignment = tt.packalign;
1733             }
1734             stag.members = tt.members;
1735             tt.members = null;
1736             if (!symbols)
1737                 symbols = new AST.Dsymbols();
1738             auto stags = applySpecifier(stag, specifier);
1739             symbols.push(stags);
1740             return stags;
1741         }
1742 
1743         /* If a declarator does not follow, it is unnamed
1744          */
1745         if (token.value == TOK.semicolon)
1746         {
1747             if (!tspec)
1748             {
1749                 nextToken();
1750                 return;         // accept empty declaration as an extension
1751             }
1752 
1753             if (auto ti = tspec.isTypeIdentifier())
1754             {
1755                 // C11 6.7.2-2
1756                 error("type-specifier missing for declaration of `%s`", ti.ident.toChars());
1757                 nextToken();
1758                 return;
1759             }
1760 
1761             nextToken();
1762             auto tt = tspec.isTypeTag();
1763             if (!tt ||
1764                 !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
1765                 return; // legal but meaningless empty declaration, ignore it
1766 
1767             auto stags = declareTag(tt, specifier);
1768 
1769             if (0 && tt.tok == TOK.enum_)    // C11 proscribes enums with no members, but we allow it
1770             {
1771                 if (!tt.members)
1772                     error(tt.loc, "`enum %s` has no members", stags.toChars());
1773             }
1774             return;
1775         }
1776 
1777         if (!tspec)
1778         {
1779             error("no type for declarator before `%s`", token.toChars());
1780             panic();
1781             nextToken();
1782             return;
1783         }
1784 
1785         if (tspec && specifier.mod & MOD.xconst)
1786         {
1787             tspec = toConst(tspec);
1788             specifier.mod &= ~MOD.xnone;          // 'used' it
1789         }
1790 
1791         void scanPastSemicolon()
1792         {
1793             while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1794                 nextToken();
1795             nextToken();
1796         }
1797 
1798         if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier())
1799         {
1800             /* C11 6.7.2-2
1801              * Special check for `const b = 1;` because some compilers allow it
1802              */
1803             error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars());
1804             return scanPastSemicolon();
1805         }
1806 
1807         bool first = true;
1808         while (1)
1809         {
1810             Identifier id;
1811             AST.StringExp asmName;
1812             auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
1813             if (!dt)
1814             {
1815                 panic();
1816                 nextToken();
1817                 break;          // error recovery
1818             }
1819 
1820             /* GNU Extensions
1821              * init-declarator:
1822              *    declarator simple-asm-expr (opt) gnu-attributes (opt)
1823              *    declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1824              */
1825             switch (token.value)
1826             {
1827                 case TOK.assign:
1828                 case TOK.comma:
1829                 case TOK.semicolon:
1830                 case TOK.asm_:
1831                 case TOK.__attribute__:
1832                     if (token.value == TOK.asm_)
1833                         asmName = cparseGnuAsmLabel();
1834                     if (token.value == TOK.__attribute__)
1835                     {
1836                         cparseGnuAttributes(specifier);
1837                         if (token.value == TOK.leftCurly)
1838                             break;              // function definition
1839                     }
1840                     /* This is a data definition, there cannot now be a
1841                      * function definition.
1842                      */
1843                     first = false;
1844                     break;
1845 
1846                 default:
1847                     break;
1848             }
1849 
1850             if (specifier.alignExps && dt.isTypeFunction())
1851                 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1852             if (specifier.alignExps && specifier.scw == SCW.xregister)
1853                 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1854 
1855             /* C11 6.9.1 Function Definitions
1856              * function-definition:
1857              *   declaration-specifiers declarator declaration-list (opt) compound-statement
1858              *
1859              * declaration-list:
1860              *    declaration
1861              *    declaration-list declaration
1862              */
1863             auto t = &token;
1864             if (first &&                   // first declarator
1865                 id &&
1866                 dt.isTypeFunction() &&     // function type not inherited from a typedef
1867                 isDeclarationList(t) &&    // optional declaration-list
1868                 level == LVL.global &&     // function definitions only at global scope
1869                 t.value == TOK.leftCurly)  // start of compound-statement
1870             {
1871                 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
1872                 typedefTab.setDim(typedefTabLengthSave);
1873                 symbols = symbolsSave;
1874                 symbols.push(s);
1875                 return;
1876             }
1877             AST.Dsymbol s = null;
1878             typedefTab.setDim(typedefTabLengthSave);
1879             symbols = symbolsSave;
1880             if (!symbols)
1881                 symbols = new AST.Dsymbols;     // lazilly create it
1882 
1883             if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
1884                 error("declaration-specifier-seq required");
1885             else if (specifier.scw == SCW.xtypedef)
1886             {
1887                 if (token.value == TOK.assign)
1888                     error("no initializer for typedef declaration");
1889                 if (specifier.alignExps)
1890                     error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1891 
1892                 bool isalias = true;
1893                 if (auto ts = dt.isTypeStruct())
1894                 {
1895                     if (ts.sym.isAnonymous())
1896                     {
1897                         // This is a typedef for an anonymous struct-or-union.
1898                         // Directly set the ident for the struct-or-union.
1899                         ts.sym.ident = id;
1900                         isalias = false;
1901                     }
1902                 }
1903                 else if (auto te = dt.isTypeEnum())
1904                 {
1905                     if (te.sym.isAnonymous())
1906                     {
1907                         // This is a typedef for an anonymous enum.
1908                         te.sym.ident = id;
1909                         isalias = false;
1910                     }
1911                 }
1912                 else if (auto tt = dt.isTypeTag())
1913                 {
1914                     if (tt.id || tt.tok == TOK.enum_)
1915                     {
1916                         if (!tt.id && id)
1917                             /* This applies for enums declared as
1918                              * typedef enum {A} E;
1919                              */
1920                             tt.id = id;
1921                         Specifier spec;
1922                         declareTag(tt, spec);
1923                     }
1924                 }
1925                 if (isalias)
1926                 {
1927                     auto ad = new AST.AliasDeclaration(token.loc, id, dt);
1928                     ad.adFlags |= ad.hidden; // do not print when generating .di files
1929                     s = ad;
1930                 }
1931 
1932                 insertTypedefToTypedefTab(id, dt);       // remember typedefs
1933             }
1934             else if (id)
1935             {
1936                 if (auto tt = dt.isTypeTag())
1937                 {
1938                     if (tt.members && (tt.id || tt.tok == TOK.enum_))
1939                     {
1940                         Specifier spec;
1941                         declareTag(tt, spec);
1942                     }
1943                 }
1944 
1945                 if (level == LVL.prototype)
1946                     break;      // declared later as Parameter, not VarDeclaration
1947 
1948                 if (dt.ty == AST.Tvoid)
1949                     error("`void` has no value");
1950 
1951                 AST.Initializer initializer;
1952                 bool hasInitializer;
1953                 if (token.value == TOK.assign)
1954                 {
1955                     nextToken();
1956                     hasInitializer = true;
1957                     initializer = cparseInitializer();
1958                 }
1959                 // declare the symbol
1960                 assert(id);
1961 
1962                 if (isFunctionTypedef(dt))
1963                 {
1964                     if (hasInitializer)
1965                         error("no initializer for function declaration");
1966                     if (specifier.scw & SCW.x_Thread_local)
1967                         error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1968                     StorageClass stc = specifiersToSTC(level, specifier);
1969                     stc &= ~STC.gshared;        // no gshared functions
1970                     auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn);
1971                     specifiersToFuncDeclaration(fd, specifier);
1972                     s = fd;
1973                 }
1974                 else
1975                 {
1976                     // Give non-extern variables an implicit void initializer
1977                     // if one has not been explicitly set.
1978                     if (!hasInitializer &&
1979                         !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
1980                         initializer = new AST.VoidInitializer(token.loc);
1981                     auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
1982                     specifiersToVarDeclaration(vd, specifier);
1983                     s = vd;
1984                 }
1985                 if (level != LVL.global)
1986                     insertIdToTypedefTab(id);   // non-typedef declarations can hide typedefs in outer scopes
1987             }
1988             if (s !is null)
1989             {
1990                 // Saw `asm("name")` in the function, type, or variable definition.
1991                 // This is equivalent to `pragma(mangle, "name")` in D
1992                 if (asmName)
1993                 {
1994                     /*
1995                     https://issues.dlang.org/show_bug.cgi?id=23012
1996                     Ideally this would be translated to a pragma(mangle)
1997                     decl. This is not possible because ImportC symbols are
1998                     (currently) merged before semantic analysis is performed,
1999                     so the pragma(mangle) never effects any change on the declarations
2000                     it pertains too.
2001 
2002                     Writing to mangleOverride directly avoids this, and is possible
2003                     because C only a StringExp is allowed unlike a full fat pragma(mangle)
2004                     which is more liberal.
2005                     */
2006                     if (auto p = s.isDeclaration())
2007                     {
2008                         auto str = asmName.peekString();
2009                         p.mangleOverride = str;
2010 //                      p.adFlags |= AST.VarDeclaration.nounderscore;
2011                         p.adFlags |= 4; // cannot get above line to compile on Ubuntu
2012                     }
2013                 }
2014                 s = applySpecifier(s, specifier);
2015                 if (level == LVL.local)
2016                 {
2017                     // Wrap the declaration in `extern (C) { declaration }`
2018                     // Necessary for function pointers, but harmless to apply to all.
2019                     auto decls = new AST.Dsymbols(1);
2020                     (*decls)[0] = s;
2021                     s = new AST.LinkDeclaration(s.loc, linkage, decls);
2022                 }
2023                 symbols.push(s);
2024             }
2025             first = false;
2026 
2027             switch (token.value)
2028             {
2029                 case TOK.identifier:
2030                     if (s)
2031                     {
2032                         error(token.loc, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
2033                         goto Lend;
2034                     }
2035                     goto default;
2036 
2037                 case TOK.semicolon:
2038                     nextToken();
2039                     return;
2040 
2041                 case TOK.comma:
2042                     if (!symbolsSave)
2043                         symbolsSave = symbols;
2044                     nextToken();
2045                     break;
2046 
2047                 default:
2048                     error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars());
2049                 Lend:
2050                     return scanPastSemicolon();
2051             }
2052         }
2053     }
2054 
2055     /***************************************
2056      * C11 Function Definitions
2057      * function-definition
2058      *    declaration-specifiers declarator declaration-list (opt) compound-statement
2059      *
2060      * declaration-list:
2061      *    declaration
2062      *    declaration-list declaration
2063      *
2064      * It's already been parsed up to the declaration-list (opt).
2065      * Pick it up from there.
2066      * Params:
2067      *    id = function identifier
2068      *    ft = function type
2069      *    specifier = function specifiers
2070      * Returns:
2071      *  Dsymbol for the function
2072      */
2073     AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
2074     {
2075         /* Start function scope
2076          */
2077         typedefTab.push(null);
2078 
2079         if (token.value != TOK.leftCurly)       // if not start of a compound-statement
2080         {
2081             // Do declaration-list
2082             do
2083             {
2084                 cparseDeclaration(LVL.parameter);
2085             } while (token.value != TOK.leftCurly);
2086 
2087             /* Since there were declarations, the parameter-list must have been
2088              * an identifier-list.
2089              */
2090             ft.parameterList.hasIdentifierList = true;        // semantic needs to know to adjust parameter types
2091             auto pl = ft.parameterList;
2092             if (pl.varargs != AST.VarArg.none && pl.length)
2093                 error("function identifier-list cannot end with `...`");
2094             ft.parameterList.varargs = AST.VarArg.KRvariadic;   // but C11 allows extra arguments
2095             auto plLength = pl.length;
2096             if (symbols.length != plLength)
2097                 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
2098 
2099             /* Transfer the types and storage classes from symbols[] to pl[]
2100              */
2101             foreach (i; 0 .. plLength)
2102             {
2103                 auto p = pl[i];  // yes, quadratic
2104 
2105                 // Convert typedef-identifier to identifier
2106                 if (p.type)
2107                 {
2108                     if (auto t = p.type.isTypeIdentifier())
2109                     {
2110                         p.ident = t.ident;
2111                         p.type = null;
2112                     }
2113                 }
2114 
2115                 if (p.type || !(p.storageClass & STC.parameter))
2116                     error("storage class and type are not allowed in identifier-list");
2117                 foreach (s; (*symbols)[]) // yes, quadratic
2118                 {
2119                     auto ad = s.isAttribDeclaration();
2120                     if (ad)
2121                         s = (*ad.decl)[0];      // AlignDeclaration wrapping the declaration
2122 
2123                     auto d = s.isDeclaration();
2124                     if (d && p.ident == d.ident && d.type)
2125                     {
2126                         p.type = d.type;
2127                         p.storageClass = d.storage_class;
2128                         d.type = null; // don't reuse
2129                         break;
2130                     }
2131                 }
2132                 if (!p.type)
2133                 {
2134                     error("no declaration for identifier `%s`", p.ident.toChars());
2135                     p.type = AST.Type.terror;
2136                 }
2137             }
2138         }
2139 
2140         addFuncName = false;    // gets set to true if somebody references __func__ in this function
2141         const locFunc = token.loc;
2142 
2143         auto body = cparseStatement(ParseStatementFlags.curly);  // don't start a new scope; continue with parameter scope
2144         typedefTab.pop();                                        // end of function scope
2145 
2146         StorageClass stc = specifiersToSTC(LVL.global, specifier);
2147         stc &= ~STC.gshared;    // no gshared functions
2148         auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn);
2149         specifiersToFuncDeclaration(fd, specifier);
2150 
2151         if (addFuncName)
2152         {
2153             auto s = createFuncName(locFunc, id);
2154             body = new AST.CompoundStatement(locFunc, s, body);
2155         }
2156         fd.fbody = body;
2157 
2158         // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2159 
2160         return fd;
2161     }
2162 
2163     /***************************************
2164      * C11 Initialization
2165      * initializer:
2166      *    assignment-expression
2167      *    { initializer-list }
2168      *    { initializer-list , }
2169      *
2170      * initializer-list:
2171      *    designation (opt) initializer
2172      *    initializer-list , designation (opt) initializer
2173      *
2174      * designation:
2175      *    designator-list =
2176      *
2177      * designator-list:
2178      *    designator
2179      *    designator-list designator
2180      *
2181      * designator:
2182      *    [ constant-expression ]
2183      *    . identifier
2184      * Returns:
2185      *    initializer
2186      */
2187     AST.Initializer cparseInitializer()
2188     {
2189         if (token.value != TOK.leftCurly)
2190         {
2191             auto ae = cparseAssignExp();        // assignment-expression
2192             return new AST.ExpInitializer(token.loc, ae);
2193         }
2194         nextToken();
2195         const loc = token.loc;
2196 
2197         /* Collect one or more `designation (opt) initializer`
2198          * into ci.initializerList, but lazily create ci
2199          */
2200         AST.CInitializer ci;
2201         while (1)
2202         {
2203             /* There can be 0 or more designators preceding an initializer.
2204              * Collect them in desigInit
2205              */
2206             AST.DesigInit desigInit;
2207             while (1)
2208             {
2209                 if (token.value == TOK.leftBracket)     // [ constant-expression ]
2210                 {
2211                     nextToken();
2212                     auto e = cparseConstantExp();
2213                     check(TOK.rightBracket);
2214                     if (!desigInit.designatorList)
2215                         desigInit.designatorList = new AST.Designators;
2216                     desigInit.designatorList.push(AST.Designator(e));
2217                 }
2218                 else if (token.value == TOK.dot)        // . identifier
2219                 {
2220                     nextToken();
2221                     if (token.value != TOK.identifier)
2222                     {
2223                         error("identifier expected following `.` designator");
2224                         break;
2225                     }
2226                     if (!desigInit.designatorList)
2227                         desigInit.designatorList = new AST.Designators;
2228                     desigInit.designatorList.push(AST.Designator(token.ident));
2229                     nextToken();
2230                 }
2231                 else
2232                 {
2233                     if (desigInit.designatorList)
2234                         check(TOK.assign);
2235                     break;
2236                 }
2237             }
2238 
2239             desigInit.initializer = cparseInitializer();
2240             if (!ci)
2241                 ci = new AST.CInitializer(loc);
2242             ci.initializerList.push(desigInit);
2243             if (token.value == TOK.comma)
2244             {
2245                 nextToken();
2246                 if (token.value != TOK.rightCurly)
2247                     continue;
2248             }
2249             break;
2250         }
2251         check(TOK.rightCurly);
2252         //printf("ci: %s\n", ci.toChars());
2253         return ci;
2254     }
2255 
2256     /*************************************
2257      * C11 6.7
2258      * declaration-specifier:
2259      *    storage-class-specifier declaration-specifiers (opt)
2260      *    type-specifier declaration-specifiers (opt)
2261      *    type-qualifier declaration-specifiers (opt)
2262      *    function-specifier declaration-specifiers (opt)
2263      *    alignment-specifier declaration-specifiers (opt)
2264      * Params:
2265      *  level = declaration context
2266      *  specifier = specifiers in and out
2267      * Returns:
2268      *  resulting type, null if not specified
2269      */
2270     private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier)
2271     {
2272         enum TKW : uint
2273         {
2274             xnone      = 0,
2275             xchar      = 1,
2276             xsigned    = 2,
2277             xunsigned  = 4,
2278             xshort     = 8,
2279             xint       = 0x10,
2280             xlong      = 0x20,
2281             xllong     = 0x40,
2282             xfloat     = 0x80,
2283             xdouble    = 0x100,
2284             xldouble   = 0x200,
2285             xtag       = 0x400,
2286             xident     = 0x800,
2287             xvoid      = 0x1000,
2288             xbool      = 0x4000,
2289             ximaginary = 0x8000,
2290             xcomplex   = 0x10000,
2291             x_Atomic   = 0x20000,
2292             xint128    = 0x40000,
2293         }
2294 
2295         AST.Type t;
2296         Loc loc;
2297         //printf("parseDeclarationSpecifiers()\n");
2298 
2299         TKW tkw;
2300         SCW scw = specifier.scw & SCW.xtypedef;
2301         MOD mod;
2302         Identifier id;
2303         Identifier previd;
2304 
2305     Lwhile:
2306         while (1)
2307         {
2308             //printf("token %s\n", token.toChars());
2309             TKW tkwx;
2310             SCW scwx;
2311             MOD modx;
2312             switch (token.value)
2313             {
2314                 // Storage class specifiers
2315                 case TOK.static_:    scwx = SCW.xstatic;    break;
2316                 case TOK.extern_:    scwx = SCW.xextern;    break;
2317                 case TOK.auto_:      scwx = SCW.xauto;      break;
2318                 case TOK.register:   scwx = SCW.xregister;  break;
2319                 case TOK.typedef_:   scwx = SCW.xtypedef;   break;
2320                 case TOK.inline:     scwx = SCW.xinline;    break;
2321                 case TOK._Noreturn:  scwx = SCW.x_Noreturn; break;
2322                 case TOK.__thread:
2323                 case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
2324 
2325                 // Type qualifiers
2326                 case TOK.const_:     modx = MOD.xconst;     break;
2327                 case TOK..volatile:   modx = MOD.xvolatile;  break;
2328                 case TOK.restrict:   modx = MOD.xrestrict;  break;
2329                 case TOK.__stdcall:  modx = MOD.x__stdcall; break;
2330 
2331                 // Type specifiers
2332                 case TOK.char_:      tkwx = TKW.xchar;      break;
2333                 case TOK.signed:     tkwx = TKW.xsigned;    break;
2334                 case TOK.unsigned:   tkwx = TKW.xunsigned;  break;
2335                 case TOK.int16:      tkwx = TKW.xshort;     break;
2336                 case TOK.int32:      tkwx = TKW.xint;       break;
2337                 case TOK.int64:      tkwx = TKW.xlong;      break;
2338                 case TOK.__int128:   tkwx = TKW.xint128;    break;
2339                 case TOK.float32:    tkwx = TKW.xfloat;     break;
2340                 case TOK.float64:    tkwx = TKW.xdouble;    break;
2341                 case TOK.void_:      tkwx = TKW.xvoid;      break;
2342                 case TOK._Bool:      tkwx = TKW.xbool;      break;
2343                 case TOK._Imaginary: tkwx = TKW.ximaginary; break;
2344                 case TOK._Complex:   tkwx = TKW.xcomplex;   break;
2345 
2346                 case TOK.identifier:
2347                     tkwx = TKW.xident;
2348                     id = token.ident;
2349                     break;
2350 
2351                 case TOK.struct_:
2352                 case TOK.union_:
2353                 {
2354                     const structOrUnion = token.value;
2355                     const sloc = token.loc;
2356                     nextToken();
2357 
2358                     Specifier tagSpecifier;
2359 
2360                     /* GNU Extensions
2361                      * struct-or-union-specifier:
2362                      *    struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2363                      *    struct-or-union gnu-attribute (opt) identifier
2364                      */
2365                     while (1)
2366                     {
2367                         if (token.value == TOK.__attribute__)
2368                             cparseGnuAttributes(tagSpecifier);
2369                         else if (token.value == TOK.__declspec)
2370                             cparseDeclspec(tagSpecifier);
2371                         else if (token.value == TOK.__pragma)
2372                             uupragmaDirective(sloc);
2373                         else
2374                             break;
2375                     }
2376                     t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols);
2377                     tkwx = TKW.xtag;
2378                     break;
2379                 }
2380 
2381                 case TOK.enum_:
2382                     t = cparseEnum(symbols);
2383                     tkwx = TKW.xtag;
2384                     break;
2385 
2386                 case TOK._Atomic:
2387                 {
2388                     // C11 6.7.2.4
2389                     // type-specifier if followed by `( type-name )`
2390                     auto tk = peek(&token);
2391                     if (tk.value == TOK.leftParenthesis)
2392                     {
2393                         tk = peek(tk);
2394                         if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
2395                         {
2396                             nextToken();
2397                             nextToken();
2398                             t = cparseTypeName();
2399                             tkwx = TKW.x_Atomic;
2400                             break;
2401                         }
2402                     }
2403                     // C11 6.7.3 type-qualifier if not
2404                     modx = MOD.x_Atomic;
2405                     break;
2406                 }
2407 
2408                 case TOK._Alignas:
2409                 {
2410                     /* C11 6.7.5
2411                      * _Alignas ( type-name )
2412                      * _Alignas ( constant-expression )
2413                      */
2414 
2415                     if (level & (LVL.parameter | LVL.prototype))
2416                         error("no alignment-specifier for parameters"); // C11 6.7.5-2
2417 
2418                     nextToken();
2419                     check(TOK.leftParenthesis);
2420                     AST.Expression exp;
2421                     auto tk = &token;
2422                     if (isTypeName(tk))  // _Alignas ( type-name )
2423                     {
2424                         auto talign = cparseTypeName();
2425                         /* Convert type to expression: `talign.alignof`
2426                          */
2427                         auto e = new AST.TypeExp(loc, talign);
2428                         exp = new AST.DotIdExp(loc, e, Id.__xalignof);
2429                     }
2430                     else  // _Alignas ( constant-expression )
2431                     {
2432                         exp = cparseConstantExp();
2433                     }
2434 
2435                     if (!specifier.alignExps)
2436                         specifier.alignExps = new AST.Expressions(0);
2437                     specifier.alignExps.push(exp);
2438 
2439                     check(TOK.rightParenthesis);
2440                     break;
2441                 }
2442 
2443                 case TOK.__attribute__:
2444                 {
2445                     /* GNU Extensions
2446                      * declaration-specifiers:
2447                      *    gnu-attributes declaration-specifiers (opt)
2448                      */
2449                     cparseGnuAttributes(specifier);
2450                     break;
2451                 }
2452 
2453                 case TOK.__declspec:
2454                 {
2455                     /* Microsoft extension
2456                      */
2457                     cparseDeclspec(specifier);
2458                     break;
2459                 }
2460 
2461                 case TOK.typeof_:
2462                 {
2463                     nextToken();
2464                     check(TOK.leftParenthesis);
2465 
2466                     auto tk = &token;
2467                     AST.Expression e;
2468                     if (isTypeName(tk))
2469                         e = new AST.TypeExp(loc, cparseTypeName());
2470                     else
2471                         e = cparseExpression();
2472                     t = new AST.TypeTypeof(loc, e);
2473 
2474                     if(token.value == TOK.rightParenthesis)
2475                         nextToken();
2476                     else
2477                     {
2478                         t = AST.Type.terror;
2479                         error("`typeof` operator expects an expression or type name in parentheses");
2480 
2481                         // skipParens et. al expect to be on the opening parenthesis
2482                         int parens;
2483                         loop: while(1)
2484                         {
2485                             switch(token.value)
2486                             {
2487                                 case TOK.leftParenthesis:
2488                                     parens++;
2489                                     break;
2490                                 case TOK.rightParenthesis:
2491                                     parens--;
2492                                     if(parens < 0)
2493                                         goto case;
2494                                     break;
2495                                 case TOK.endOfFile:
2496                                     break loop;
2497                                 default:
2498                             }
2499                             nextToken();
2500                         }
2501                     }
2502 
2503                     tkwx = TKW.xtag;
2504                     break;
2505                 }
2506 
2507                 default:
2508                     break Lwhile;
2509             }
2510 
2511             if (tkwx)
2512             {
2513                 if (tkw & TKW.xlong && tkwx & TKW.xlong)
2514                 {
2515                     tkw &= ~TKW.xlong;
2516                     tkwx = TKW.xllong;
2517                 }
2518                 if (tkw && tkwx & TKW.xident)
2519                 {
2520                     // 2nd identifier can't be a typedef
2521                     break Lwhile; // leave parser on the identifier for the following declarator
2522                 }
2523                 else if (tkwx & TKW.xident)
2524                 {
2525                     // 1st identifier, save it for TypeIdentifier
2526                     previd = id;
2527                 }
2528                 if (tkw & TKW.xident && tkwx ||  // typedef-name followed by type-specifier
2529                     tkw & tkwx)                  // duplicate type-specifiers
2530                 {
2531                     error("illegal combination of type specifiers");
2532                     tkwx = TKW.init;
2533                 }
2534                 tkw |= tkwx;
2535                 if (!(tkwx & TKW.xtag)) // if parser already advanced
2536                     nextToken();
2537                 continue;
2538             }
2539 
2540             if (modx)
2541             {
2542                 mod |= modx;
2543                 nextToken();
2544                 continue;
2545             }
2546 
2547             if (scwx)
2548             {
2549                 if (scw & scwx)
2550                     error("duplicate storage class");
2551                 scw |= scwx;
2552                 // C11 6.7.1-2 At most one storage-class may be given, except that
2553                 // _Thread_local may appear with static or extern.
2554                 const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef);
2555                 if (scw2 & (scw2 - 1) ||
2556                     scw & (SCW.x_Thread_local) && scw & (SCW.xauto | SCW.xregister | SCW.xtypedef))
2557                 {
2558                     error("multiple storage classes in declaration specifiers");
2559                     scw &= ~scwx;
2560                 }
2561                 if (level == LVL.local &&
2562                     scw & (SCW.x_Thread_local) && scw & (SCW.xinline | SCW.x_Noreturn))
2563                 {
2564                     error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2565                     scw &= ~scwx;
2566                 }
2567                 if (level == LVL.local &&
2568                     scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
2569                 {
2570                     error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2571                     scw &= ~scwx;
2572                 }
2573                 if (level & (LVL.parameter | LVL.prototype) &&
2574                     scw & ~SCW.xregister)
2575                 {
2576                     error("only `register` storage class allowed for function parameters");
2577                     scw &= ~scwx;
2578                 }
2579                 if (level == LVL.global &&
2580                     scw & (SCW.xauto | SCW.xregister))
2581                 {
2582                     error("`auto` and `register` storage class not allowed for global");
2583                     scw &= ~scwx;
2584                 }
2585                 nextToken();
2586                 continue;
2587             }
2588         }
2589 
2590         specifier.scw = scw;
2591         specifier.mod = mod;
2592 
2593         // Convert TKW bits to type t
2594         switch (tkw)
2595         {
2596             case TKW.xnone:                     t = null; break;
2597 
2598             case TKW.xchar:                     t = AST.Type.tchar; break;
2599             case TKW.xsigned | TKW.xchar:       t = AST.Type.tint8; break;
2600             case TKW.xunsigned | TKW.xchar:     t = AST.Type.tuns8; break;
2601 
2602             case TKW.xshort:
2603             case TKW.xsigned | TKW.xshort:
2604             case TKW.xsigned | TKW.xshort | TKW.xint:
2605             case TKW.xshort | TKW.xint:         t = integerTypeForSize(shortsize); break;
2606 
2607             case TKW.xunsigned | TKW.xshort | TKW.xint:
2608             case TKW.xunsigned | TKW.xshort:    t = unsignedTypeForSize(shortsize); break;
2609 
2610             case TKW.xint:
2611             case TKW.xsigned:
2612             case TKW.xsigned | TKW.xint:        t = integerTypeForSize(intsize); break;
2613 
2614             case TKW.xunsigned:
2615             case TKW.xunsigned | TKW.xint:      t = unsignedTypeForSize(intsize); break;
2616 
2617             case TKW.xlong:
2618             case TKW.xsigned | TKW.xlong:
2619             case TKW.xsigned | TKW.xlong | TKW.xint:
2620             case TKW.xlong | TKW.xint:          t = integerTypeForSize(longsize); break;
2621 
2622             case TKW.xunsigned | TKW.xlong | TKW.xint:
2623             case TKW.xunsigned | TKW.xlong:     t = unsignedTypeForSize(longsize); break;
2624 
2625             case TKW.xllong:
2626             case TKW.xsigned | TKW.xllong:
2627             case TKW.xsigned | TKW.xllong | TKW.xint:
2628             case TKW.xllong | TKW.xint:          t = integerTypeForSize(long_longsize); break;
2629 
2630             case TKW.xunsigned | TKW.xllong | TKW.xint:
2631             case TKW.xunsigned | TKW.xllong:     t = unsignedTypeForSize(long_longsize); break;
2632 
2633             case TKW.xint128:
2634             case TKW.xsigned | TKW.xint128:     t = integerTypeForSize(16); break;
2635 
2636             case TKW.xunsigned | TKW.xint128:   t = unsignedTypeForSize(16); break;
2637 
2638             case TKW.xvoid:                     t = AST.Type.tvoid; break;
2639             case TKW.xbool:                     t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break;
2640 
2641             case TKW.xfloat:                    t = AST.Type.tfloat32; break;
2642             case TKW.xdouble:                   t = AST.Type.tfloat64; break;
2643             case TKW.xlong | TKW.xdouble:       t = realType(RTFlags.realfloat); break;
2644 
2645             case TKW.ximaginary | TKW.xfloat:              t = AST.Type.timaginary32; break;
2646             case TKW.ximaginary | TKW.xdouble:             t = AST.Type.timaginary64; break;
2647             case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break;
2648 
2649             case TKW.xcomplex | TKW.xfloat:                t = AST.Type.tcomplex32; break;
2650             case TKW.xcomplex | TKW.xdouble:               t = AST.Type.tcomplex64; break;
2651             case TKW.xcomplex | TKW.xlong | TKW.xdouble:   t = realType(RTFlags.complex); break;
2652 
2653             case TKW.xident:
2654             {
2655                 const idx = previd.toString();
2656                 if (idx.length > 2 && idx[0] == '_' && idx[1] == '_')  // leading double underscore
2657                     importBuiltins = true;  // probably one of those compiler extensions
2658                 t = null;
2659 
2660                 /* Punch through to what the typedef is, to support things like:
2661                  *  typedef T* T;
2662                  */
2663                 auto pt = lookupTypedef(previd);
2664                 if (pt && *pt)      // if previd is a known typedef
2665                     t = *pt;
2666 
2667                 if (!t)
2668                     t = new AST.TypeIdentifier(loc, previd);
2669                 break;
2670             }
2671 
2672             case TKW.xtag:
2673             case TKW.x_Atomic:  // no atomics for you
2674                 break;          // t is already set
2675 
2676             default:
2677                 error("illegal type combination");
2678                 t = AST.Type.terror;
2679                 break;
2680         }
2681 
2682         return t;
2683     }
2684 
2685     /********************************
2686      * C11 6.7.6
2687      * Parse a declarator (including function definitions).
2688      * declarator:
2689      *    pointer (opt) direct-declarator
2690      *
2691      * direct-declarator :
2692      *    identifier
2693      *    ( declarator )
2694      *    direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2695      *    direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2696      *    direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2697      *    direct-declarator [ type-qualifier-list (opt) * ]
2698      *    direct-declarator ( parameter-type-list )
2699      *    direct-declarator ( identifier-list (opt) )
2700      *
2701      * pointer :
2702      *    * type-qualifier-list (opt)
2703      *    * type-qualifier-list (opt) pointer
2704      *
2705      * type-qualifier-list :
2706      *    type-qualifier
2707      *    type-qualifier-list type-qualifier
2708      *
2709      * parameter-type-list :
2710      *    parameter-list
2711      *    parameter-list , ...
2712      *
2713      * parameter-list :
2714      *    parameter-declaration
2715      *    parameter-list , parameter-declaration
2716      *
2717      * parameter-declaration :
2718      *    declaration-specifiers declarator
2719      *    declaration-specifiers abstract-declarator (opt)
2720      *
2721      * identifier-list :
2722      *    identifier
2723      *    identifier-list , identifier
2724      *
2725      * Params:
2726      *  declarator   = declarator kind
2727      *  tbase        = base type to start with
2728      *  pident       = set to Identifier if there is one, null if not
2729      *  specifier    = specifiers in and out
2730      * Returns:
2731      *  type declared. If a TypeFunction is returned, this.symbols is the
2732      *  symbol table for the parameter-type-list, which will contain any
2733      *  declared struct, union or enum tags.
2734      */
2735     private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
2736         out Identifier pident, ref Specifier specifier)
2737     {
2738         //printf("cparseDeclarator(%d, %p)\n", declarator, t);
2739         AST.Types constTypes; // all the Types that will need `const` applied to them
2740 
2741         /* Insert tx -> t into
2742          *   ts -> ... -> t
2743          * so that
2744          *   ts -> ... -> tx -> t
2745          */
2746         static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t)
2747         {
2748             AST.Type* pt;
2749             for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
2750             {
2751             }
2752             *pt = tx;
2753         }
2754 
2755         AST.Type parseDecl(AST.Type t)
2756         {
2757             AST.Type ts;
2758             while (1)
2759             {
2760                 switch (token.value)
2761                 {
2762                 case TOK.identifier:        // identifier
2763                     //printf("identifier %s\n", token.ident.toChars());
2764                     if (declarator == DTR.xabstract)
2765                         error("identifier not allowed in abstract-declarator");
2766                     pident = token.ident;
2767                     ts = t;
2768                     nextToken();
2769                     break;
2770 
2771                 case TOK.leftParenthesis:   // ( declarator )
2772                     /* like: T (*fp)();
2773                      *       T ((*fp))();
2774                      */
2775                     nextToken();
2776 
2777                     if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
2778                     {
2779                         specifier.mod |= MOD.x__stdcall;
2780                         nextToken();
2781                     }
2782 
2783                     ts = parseDecl(t);
2784                     check(TOK.rightParenthesis);
2785                     break;
2786 
2787                 case TOK.mul:               // pointer
2788                     t = new AST.TypePointer(t);
2789                     nextToken();
2790                     // add post fixes const/volatile/restrict/_Atomic
2791                     const mod = cparseTypeQualifierList();
2792                     if (mod & MOD.xconst)
2793                         constTypes.push(t);
2794                     if (token.value == TOK.__attribute__)
2795                         cparseGnuAttributes(specifier);
2796                     continue;
2797 
2798                 default:
2799                     if (declarator == DTR.xdirect)
2800                     {
2801                         if (!t || t.isTypeIdentifier())
2802                         {
2803                             // const arr[1];
2804                             error("no type-specifier for declarator");
2805                             t = AST.Type.tint32;
2806                         }
2807                         else
2808                             error("identifier or `(` expected"); // )
2809                         panic();
2810                     }
2811                     ts = t;
2812                     break;
2813                 }
2814                 break;
2815             }
2816 
2817             // parse DeclaratorSuffixes
2818             while (1)
2819             {
2820                 switch (token.value)
2821                 {
2822                     case TOK.leftBracket:
2823                     {
2824                         // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2825                         AST.Type ta;
2826                         nextToken();
2827 
2828                         auto mod = cparseTypeQualifierList();   // const/volatile/restrict/_Atomic
2829 
2830                         bool isStatic;
2831                         bool isVLA;
2832                         if (token.value == TOK.static_)
2833                         {
2834                             isStatic = true;    // `static`
2835                             nextToken();
2836                             if (!mod)           // type qualifiers after `static`
2837                                 mod = cparseTypeQualifierList();
2838                         }
2839                         else if (token.value == TOK.mul)
2840                         {
2841                             if (peekNext() == TOK.rightBracket)
2842                             {
2843                                 isVLA = true;   // `*`
2844                                 nextToken();
2845                             }
2846                         }
2847 
2848                         if (isStatic || token.value != TOK.rightBracket)
2849                         {
2850                             //printf("It's a static array\n");
2851                             AST.Expression e = cparseAssignExp(); // [ expression ]
2852                             ta = new AST.TypeSArray(t, e);
2853                         }
2854                         else
2855                         {
2856                             /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2857                              */
2858                             ta = new AST.TypeSArray(t);
2859                         }
2860                         check(TOK.rightBracket);
2861 
2862                         // Issue errors for unsupported types.
2863                         if (isVLA) // C11 6.7.6.2
2864                         {
2865                             error("variable length arrays are not supported");
2866                         }
2867                         if (isStatic) // C11 6.7.6.3
2868                         {
2869                             error("static array parameters are not supported");
2870                         }
2871                         if (declarator != DTR.xparameter)
2872                         {
2873                             /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2874                              */
2875                             if (isVLA)
2876                                 error("variable length array used outside of function prototype");
2877                             /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2878                              * in a declaration of a function parameter with an array type.
2879                              */
2880                             if (isStatic || mod)
2881                                 error("static or type qualifier used outside of function prototype");
2882                         }
2883                         if (ts.isTypeSArray() || ts.isTypeDArray())
2884                         {
2885                             /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2886                              * in the outermost array type derivation.
2887                              */
2888                             if (isStatic || mod)
2889                                 error("static or type qualifier used in non-outermost array type derivation");
2890                             /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2891                              * function type.
2892                              */
2893                             if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
2894                                 error("array type has incomplete element type `%s`", ta.toChars());
2895                         }
2896 
2897                         // Apply type qualifiers to the constructed type.
2898                         if (mod & MOD.xconst) // ignore the other bits
2899                             ta = toConst(ta);
2900                         insertTx(ts, ta, t);  // ts -> ... -> ta -> t
2901                         continue;
2902                     }
2903 
2904                     case TOK.leftParenthesis:
2905                     {
2906                         // New symbol table for parameter-list
2907                         auto symbolsSave = this.symbols;
2908                         this.symbols = null;
2909 
2910                         auto parameterList = cparseParameterList();
2911                         const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage;
2912                         StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
2913                         if (specifier._pure)
2914                             stc |= STC.pure_;
2915                         AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
2916     //                  tf = tf.addSTC(storageClass);  // TODO
2917                         insertTx(ts, tf, t);  // ts -> ... -> tf -> t
2918 
2919                         if (ts != tf)
2920                             this.symbols = symbolsSave;
2921                         break;
2922                     }
2923 
2924                     default:
2925                         break;
2926                 }
2927                 break;
2928             }
2929             return ts;
2930         }
2931 
2932         auto t = parseDecl(tbase);
2933 
2934         if (specifier.vector_size)
2935         {
2936             auto length = new AST.IntegerExp(token.loc, specifier.vector_size / tbase.size(), AST.Type.tuns32);
2937             auto tsa = new AST.TypeSArray(tbase, length);
2938             AST.Type tv = new AST.TypeVector(tsa);
2939             specifier.vector_size = 0;          // used it up
2940 
2941             insertTx(t, tv, tbase);     // replace tbase with tv
2942         }
2943 
2944         /* Because const is transitive, cannot assemble types from
2945          * fragments. Instead, types to be annotated with const are put
2946          * in constTypes[], and a bottom up scan of t is done to apply
2947          * const
2948          */
2949         if (constTypes.length)
2950         {
2951             AST.Type constApply(AST.Type t)
2952             {
2953                 if (t.nextOf())
2954                 {
2955                     auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this
2956                     tn.next = constApply(tn.next);
2957                 }
2958                 foreach (tc; constTypes[])
2959                 {
2960                     if (tc is t)
2961                     {
2962                         return toConst(t);
2963                     }
2964                 }
2965                 return t;
2966             }
2967 
2968             if (declarator == DTR.xparameter &&
2969                 t.isTypePointer())
2970             {
2971                 /* Because there are instances in .h files of "const pointer to mutable",
2972                  * skip applying transitive `const`
2973                  * https://issues.dlang.org/show_bug.cgi?id=22534
2974                  */
2975                 auto tn = cast(AST.TypeNext)t;
2976                 tn.next = constApply(tn.next);
2977             }
2978             else
2979                 t = constApply(t);
2980         }
2981 
2982         //printf("result: %s\n", t.toChars());
2983         return t;
2984     }
2985 
2986     /******************************
2987      * C11 6.7.3
2988      * type-qualifier:
2989      *    const
2990      *    restrict
2991      *    volatile
2992      *    _Atomic
2993      *    __stdcall
2994      */
2995     MOD cparseTypeQualifierList()
2996     {
2997         MOD mod;
2998         while (1)
2999         {
3000             switch (token.value)
3001             {
3002                 case TOK.const_:     mod |= MOD.xconst;     break;
3003                 case TOK..volatile:   mod |= MOD.xvolatile;  break;
3004                 case TOK.restrict:   mod |= MOD.xrestrict;  break;
3005                 case TOK._Atomic:    mod |= MOD.x_Atomic;   break;
3006                 case TOK.__stdcall:  mod |= MOD.x__stdcall; break;
3007 
3008                 default:
3009                     return mod;
3010             }
3011             nextToken();
3012         }
3013     }
3014 
3015     /***********************************
3016      * C11 6.7.7
3017      */
3018     AST.Type cparseTypeName()
3019     {
3020         Specifier specifier;
3021         specifier.packalign.setDefault();
3022         auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
3023         if (!tspec)
3024         {
3025             error("type-specifier is missing");
3026             tspec = AST.Type.tint32;
3027         }
3028         if (tspec && specifier.mod & MOD.xconst)
3029         {
3030             tspec = toConst(tspec);
3031             specifier.mod = MOD.xnone;      // 'used' it
3032         }
3033         Identifier id;
3034         return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
3035     }
3036 
3037     /***********************************
3038      * C11 6.7.2.1
3039      * specifier-qualifier-list:
3040      *    type-specifier specifier-qualifier-list (opt)
3041      *    type-qualifier specifier-qualifier-list (opt)
3042      * Params:
3043      *  level = declaration context
3044      *  specifier = specifiers in and out
3045      * Returns:
3046      *  resulting type, null if not specified
3047      */
3048     AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier)
3049     {
3050         auto t = cparseDeclarationSpecifiers(level, specifier);
3051         if (specifier.scw)
3052             error("storage class not allowed in specifier-qualified-list");
3053         return t;
3054     }
3055 
3056     /***********************************
3057      * C11 6.7.6.3
3058      * ( parameter-type-list )
3059      * ( identifier-list (opt) )
3060      */
3061     AST.ParameterList cparseParameterList()
3062     {
3063         auto parameters = new AST.Parameters();
3064         AST.VarArg varargs = AST.VarArg.none;
3065         StorageClass varargsStc;
3066 
3067         check(TOK.leftParenthesis);
3068         if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
3069         {
3070             nextToken();
3071             nextToken();
3072             return AST.ParameterList(parameters, varargs, varargsStc);
3073         }
3074 
3075         if (token.value == TOK.rightParenthesis)        // func()
3076         {
3077             nextToken();
3078             return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc);
3079         }
3080 
3081         /* Create function prototype scope
3082          */
3083         typedefTab.push(null);
3084 
3085         AST.ParameterList finish()
3086         {
3087             typedefTab.pop();
3088             return AST.ParameterList(parameters, varargs, varargsStc);
3089         }
3090 
3091         /* The check for identifier-list comes later,
3092          * when doing the trailing declaration-list (opt)
3093          */
3094         while (1)
3095         {
3096             if (token.value == TOK.rightParenthesis)
3097                 break;
3098             if (token.value == TOK.dotDotDot)
3099             {
3100                 if (parameters.length == 0)     // func(...)
3101                     error("named parameter required before `...`");
3102                 importBuiltins = true;          // will need __va_list_tag
3103                 varargs = AST.VarArg.variadic;  // C-style variadics
3104                 nextToken();
3105                 check(TOK.rightParenthesis);
3106                 return finish();
3107             }
3108 
3109             Specifier specifier;
3110             specifier.packalign.setDefault();
3111             auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
3112             if (!tspec)
3113             {
3114                 error("no type-specifier for parameter");
3115                 tspec = AST.Type.tint32;
3116             }
3117 
3118             if (specifier.mod & MOD.xconst)
3119             {
3120                 if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) &&
3121                     tspec.isTypeIdentifier())
3122                     error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars());
3123 
3124                 tspec = toConst(tspec);
3125                 specifier.mod = MOD.xnone;      // 'used' it
3126             }
3127 
3128             Identifier id;
3129             const paramLoc = token.loc;
3130             auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
3131             if (token.value == TOK.__attribute__)
3132                 cparseGnuAttributes(specifier);
3133             if (specifier.mod & MOD.xconst)
3134                 t = toConst(t);
3135             auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
3136                                            t, id, null, null);
3137             parameters.push(param);
3138             if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
3139                 break;
3140             check(TOK.comma);
3141         }
3142         check(TOK.rightParenthesis);
3143         return finish();
3144     }
3145 
3146     /***********************************
3147      * C11 6.7.10
3148      * _Static_assert ( constant-expression , string-literal ) ;
3149      */
3150     private AST.StaticAssert cparseStaticAssert()
3151     {
3152         const loc = token.loc;
3153 
3154         //printf("cparseStaticAssert()\n");
3155         nextToken();
3156         check(TOK.leftParenthesis);
3157         auto exp = cparseConstantExp();
3158         check(TOK.comma);
3159         if (token.value != TOK.string_)
3160             error("string literal expected");
3161         auto msg = cparsePrimaryExp();
3162         check(TOK.rightParenthesis);
3163         check(TOK.semicolon);
3164         return new AST.StaticAssert(loc, exp, msg);
3165     }
3166 
3167     /*************************
3168      * Collect argument list.
3169      * Parser is on opening parenthesis.
3170      * Returns:
3171      *    the arguments
3172      */
3173     private AST.Expressions* cparseArguments()
3174     {
3175         nextToken();
3176         auto arguments = new AST.Expressions();
3177         while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile)
3178         {
3179             auto arg = cparseAssignExp();
3180             arguments.push(arg);
3181             if (token.value != TOK.comma)
3182                 break;
3183 
3184             nextToken(); // consume comma
3185         }
3186 
3187         check(TOK.rightParenthesis);
3188 
3189         return arguments;
3190     }
3191 
3192     /*************************
3193      * __declspec parser
3194      * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3195      * decl-specifier:
3196      *    __declspec ( extended-decl-modifier-seq )
3197      *
3198      * extended-decl-modifier-seq:
3199      *    extended-decl-modifier (opt)
3200      *    extended-decl-modifier extended-decl-modifier-seq
3201      *
3202      * extended-decl-modifier:
3203      *    align(number)
3204      *    deprecated(depMsg)
3205      *    dllimport
3206      *    dllexport
3207      *    naked
3208      *    noinline
3209      *    noreturn
3210      *    nothrow
3211      *    thread
3212      * Params:
3213      *  specifier = filled in with the attribute(s)
3214      */
3215     private void cparseDeclspec(ref Specifier specifier)
3216     {
3217         //printf("cparseDeclspec()\n");
3218         /* Check for dllexport, dllimport
3219          * Ignore the rest
3220          */
3221         nextToken();     // move past __declspec
3222         check(TOK.leftParenthesis);
3223         while (1)
3224         {
3225             if (token.value == TOK.rightParenthesis)
3226             {
3227                 nextToken();
3228                 break;
3229             }
3230             else if (token.value == TOK.endOfFile)
3231                 break;
3232             else if (token.value == TOK.identifier)
3233             {
3234                 if (token.ident == Id.dllimport)
3235                 {
3236                     specifier.dllimport = true;
3237                     nextToken();
3238                 }
3239                 else if (token.ident == Id.dllexport)
3240                 {
3241                     specifier.dllexport = true;
3242                     nextToken();
3243                 }
3244                 else if (token.ident == Id.naked)
3245                 {
3246                     specifier.naked = true;
3247                     nextToken();
3248                 }
3249                 else if (token.ident == Id.noinline)
3250                 {
3251                     specifier.scw |= SCW.xnoinline;
3252                     nextToken();
3253                 }
3254                 else if (token.ident == Id.noreturn)
3255                 {
3256                     specifier.noreturn = true;
3257                     nextToken();
3258                 }
3259                 else if (token.ident == Id._nothrow)
3260                 {
3261                     specifier._nothrow = true;
3262                     nextToken();
3263                 }
3264                 else if (token.ident == Id.thread)
3265                 {
3266                     specifier.scw |= SCW.x_Thread_local;
3267                     nextToken();
3268                 }
3269                 else if (token.ident == Id._align)
3270                 {
3271                     // Microsoft spec is very imprecise as to how this actually works
3272                     nextToken();
3273                     check(TOK.leftParenthesis);
3274                     if (token.value == TOK.int32Literal)
3275                     {
3276                         const n = token.unsvalue;
3277                         if (n < 1 || n & (n - 1) || 8192 < n)
3278                             error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
3279                         specifier.packalign.set(cast(uint)n);
3280                         specifier.packalign.setPack(true);
3281                         nextToken();
3282                     }
3283                     else
3284                     {
3285                         error("alignment value expected, not `%s`", token.toChars());
3286                         nextToken();
3287                     }
3288 
3289                     check(TOK.rightParenthesis);
3290                 }
3291                 else if (token.ident == Id._deprecated)
3292                 {
3293                     specifier._deprecated = true;
3294                     nextToken();
3295                     if (token.value == TOK.leftParenthesis)  // optional deprecation message
3296                     {
3297                         nextToken();
3298                         specifier.depMsg = cparseExpression();
3299                         check(TOK.rightParenthesis);
3300                     }
3301                 }
3302                 else
3303                 {
3304                     nextToken();
3305                     if (token.value == TOK.leftParenthesis)
3306                         cparseParens();
3307                 }
3308             }
3309             else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3310                 nextToken();
3311             else
3312             {
3313                 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token.toChars());
3314                 nextToken();
3315                 if (token.value != TOK.rightParenthesis)
3316                     break;
3317             }
3318         }
3319     }
3320 
3321     /*************************
3322      * Parser for asm label. It appears after the declarator, and has apparently
3323      * nothing to do with inline assembler.
3324      * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3325      * simple-asm-expr:
3326      *   asm ( asm-string-literal )
3327      *
3328      * asm-string-literal:
3329      *   string-literal
3330      */
3331     private AST.StringExp cparseGnuAsmLabel()
3332     {
3333         nextToken();     // move past asm
3334         check(TOK.leftParenthesis);
3335         if (token.value != TOK.string_)
3336             error("string literal expected for Asm Label, not `%s`", token.toChars());
3337         auto label = cparsePrimaryExp();
3338         check(TOK.rightParenthesis);
3339         return cast(AST.StringExp) label;
3340     }
3341 
3342     /********************
3343      * Parse C inline assembler statement in Gnu format.
3344      * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3345      *   asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3346      * Current token is on the `asm`.
3347      * Returns:
3348      *   inline assembler expression as a Statement
3349      */
3350     private AST.Statement cparseGnuAsm()
3351     {
3352         // Defer parsing of AsmStatements until semantic processing.
3353         const loc = token.loc;
3354 
3355         nextToken();
3356 
3357         // Consume all asm-qualifiers. As a future optimization, we could record
3358         // the `inline` and `volatile` storage classes against the statement.
3359         while (token.value == TOK.goto_ ||
3360                token.value == TOK.inline ||
3361                token.value == TOK..volatile)
3362             nextToken();
3363 
3364         check(TOK.leftParenthesis);
3365         if (token.value != TOK.string_)
3366             error("string literal expected for Assembler Template, not `%s`", token.toChars());
3367         Token* toklist = null;
3368         Token** ptoklist = &toklist;
3369         //Identifier label = null;
3370         auto statements = new AST.Statements();
3371 
3372         int parens;
3373         while (1)
3374         {
3375             switch (token.value)
3376             {
3377                 case TOK.leftParenthesis:
3378                     ++parens;
3379                     goto default;
3380 
3381                 case TOK.rightParenthesis:
3382                     --parens;
3383                     if (parens >= 0)
3384                         goto default;
3385                     break;
3386 
3387                 case TOK.semicolon:
3388                     error("matching `)` expected, not `;`");
3389                     break;
3390 
3391                 case TOK.endOfFile:
3392                     /* ( */
3393                     error("matching `)` expected, not end of file");
3394                     break;
3395 
3396                 case TOK.colonColon:  // treat as two separate : tokens for iasmgcc
3397                     *ptoklist = allocateToken();
3398                     memcpy(*ptoklist, &token, Token.sizeof);
3399                     (*ptoklist).value = TOK.colon;
3400                     ptoklist = &(*ptoklist).next;
3401 
3402                     *ptoklist = allocateToken();
3403                     memcpy(*ptoklist, &token, Token.sizeof);
3404                     (*ptoklist).value = TOK.colon;
3405                     ptoklist = &(*ptoklist).next;
3406 
3407                     *ptoklist = null;
3408                     nextToken();
3409                     continue;
3410 
3411                 default:
3412                     *ptoklist = allocateToken();
3413                     memcpy(*ptoklist, &token, Token.sizeof);
3414                     ptoklist = &(*ptoklist).next;
3415                     *ptoklist = null;
3416                     nextToken();
3417                     continue;
3418             }
3419             if (toklist)
3420             {
3421                 // Create AsmStatement from list of tokens we've saved
3422                 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
3423                 statements.push(s);
3424             }
3425             break;
3426         }
3427         nextToken();
3428         auto s = new AST.CompoundAsmStatement(loc, statements, 0);
3429         return s;
3430     }
3431 
3432     /*************************
3433      * __attribute__ parser
3434      * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3435      * gnu-attributes:
3436      *   gnu-attributes gnu-attribute-specifier
3437      *
3438      * gnu-attribute-specifier:
3439      *    __attribute__ (( gnu-attribute-list ))
3440      *
3441      * gnu-attribute-list:
3442      *    gnu-attribute (opt)
3443      *    gnu-attribute-list , gnu-attribute
3444      *
3445      * Params:
3446      *  specifier = filled in with the attribute(s)
3447      */
3448     private void cparseGnuAttributes(ref Specifier specifier)
3449     {
3450         while (token.value == TOK.__attribute__)
3451         {
3452             nextToken();     // move past __attribute__
3453             check(TOK.leftParenthesis);
3454             check(TOK.leftParenthesis);
3455 
3456             if (token.value != TOK.rightParenthesis)
3457             {
3458                 while (1)
3459                 {
3460                     cparseGnuAttribute(specifier);
3461                     if (token.value != TOK.comma)
3462                         break;
3463                     nextToken();
3464                 }
3465             }
3466 
3467             check(TOK.rightParenthesis);
3468             check(TOK.rightParenthesis);
3469         }
3470     }
3471 
3472     /*************************
3473      * Parse a single GNU attribute
3474      * gnu-attribute:
3475      *    gnu-attribute-name
3476      *    gnu-attribute-name ( identifier )
3477      *    gnu-attribute-name ( identifier , expression-list )
3478      *    gnu-attribute-name ( expression-list (opt) )
3479      *
3480      * gnu-attribute-name:
3481      *    keyword
3482      *    identifier
3483      *
3484      * expression-list:
3485      *    constant-expression
3486      *    expression-list , constant-expression
3487      *
3488      * Params:
3489      *  specifier = filled in with the attribute(s)
3490      */
3491     private void cparseGnuAttribute(ref Specifier specifier)
3492     {
3493         /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3494          * Ignore the rest
3495          */
3496         if (!isGnuAttributeName())
3497             return;
3498 
3499         if (token.value == TOK.identifier)
3500         {
3501             if (token.ident == Id.aligned)
3502             {
3503                 nextToken();
3504                 if (token.value == TOK.leftParenthesis)
3505                 {
3506                     nextToken();
3507                     if (token.value == TOK.int32Literal)
3508                     {
3509                         const n = token.unsvalue;
3510                         if (n < 1 || n & (n - 1) || ushort.max < n)
3511                             error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3512                         specifier.packalign.set(cast(uint)n);
3513                         specifier.packalign.setPack(true);
3514                         nextToken();
3515                     }
3516                     else
3517                     {
3518                         error("alignment value expected, not `%s`", token.toChars());
3519                         nextToken();
3520                     }
3521 
3522                     check(TOK.rightParenthesis);
3523                 }
3524                 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3525                  * type on the target machine. It's the opposite of __attribute__((packed))
3526                  */
3527             }
3528             else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3529             {
3530                 specifier.scw |= SCW.xinline;
3531                 nextToken();
3532             }
3533             else if (token.ident == Id._deprecated)
3534             {
3535                 specifier._deprecated = true;
3536                 nextToken();
3537                 if (token.value == TOK.leftParenthesis)  // optional deprecation message
3538                 {
3539                     nextToken();
3540                     specifier.depMsg = cparseExpression();
3541                     check(TOK.rightParenthesis);
3542                 }
3543             }
3544             else if (token.ident == Id.dllimport)
3545             {
3546                 specifier.dllimport = true;
3547                 nextToken();
3548             }
3549             else if (token.ident == Id.dllexport)
3550             {
3551                 specifier.dllexport = true;
3552                 nextToken();
3553             }
3554             else if (token.ident == Id.naked)
3555             {
3556                 specifier.naked = true;
3557                 nextToken();
3558             }
3559             else if (token.ident == Id.noinline)
3560             {
3561                 specifier.scw |= SCW.xnoinline;
3562                 nextToken();
3563             }
3564             else if (token.ident == Id.noreturn)
3565             {
3566                 specifier.noreturn = true;
3567                 nextToken();
3568             }
3569             else if (token.ident == Id._nothrow)
3570             {
3571                 specifier._nothrow = true;
3572                 nextToken();
3573             }
3574             else if (token.ident == Id._pure)
3575             {
3576                 specifier._pure = true;
3577                 nextToken();
3578             }
3579             else if (token.ident == Id.vector_size)
3580             {
3581                 nextToken();
3582                 check(TOK.leftParenthesis);
3583                 if (token.value == TOK.int32Literal)
3584                 {
3585                     const n = token.unsvalue;
3586                     if (n < 1 || n & (n - 1) || ushort.max < n)
3587                         error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3588                     specifier.vector_size = cast(uint) n;
3589                     nextToken();
3590                 }
3591                 else
3592                 {
3593                     error("value for vector_size expected, not `%s`", token.toChars());
3594                     nextToken();
3595                 }
3596                 check(TOK.rightParenthesis);
3597             }
3598             else
3599             {
3600                 nextToken();
3601                 if (token.value == TOK.leftParenthesis)
3602                     cparseParens();
3603             }
3604         }
3605         else
3606         {
3607             nextToken();
3608             if (token.value == TOK.leftParenthesis)
3609                 cparseParens();
3610         }
3611     }
3612 
3613     /*************************
3614      * See if match for GNU attribute name, which may be any identifier,
3615      * storage-class-specifier, type-specifier, or type-qualifier.
3616      * Returns:
3617      *  true if a valid GNU attribute name
3618      */
3619     private bool isGnuAttributeName()
3620     {
3621         switch (token.value)
3622         {
3623             case TOK.identifier:
3624             case TOK.static_:
3625             case TOK.unsigned:
3626             case TOK.int64:
3627             case TOK.const_:
3628             case TOK.extern_:
3629             case TOK.register:
3630             case TOK.typedef_:
3631             case TOK.int16:
3632             case TOK.inline:
3633             case TOK._Noreturn:
3634             case TOK..volatile:
3635             case TOK.signed:
3636             case TOK.auto_:
3637             case TOK.restrict:
3638             case TOK._Complex:
3639             case TOK._Thread_local:
3640             case TOK.int32:
3641             case TOK.__int128:
3642             case TOK.char_:
3643             case TOK.float32:
3644             case TOK.float64:
3645             case TOK.void_:
3646             case TOK._Bool:
3647             case TOK._Atomic:
3648                 return true;
3649 
3650             default:
3651                 return false;
3652         }
3653     }
3654 
3655     /***************************
3656      * Like skipParens(), but consume the tokens.
3657      */
3658     private void cparseParens()
3659     {
3660         check(TOK.leftParenthesis);
3661         int parens = 1;
3662 
3663         while (1)
3664         {
3665             switch (token.value)
3666             {
3667                 case TOK.leftParenthesis:
3668                     ++parens;
3669                     break;
3670 
3671                 case TOK.rightParenthesis:
3672                     --parens;
3673                     if (parens < 0)
3674                     {
3675                         error("extra right parenthesis");
3676                         return;
3677                     }
3678                     if (parens == 0)
3679                     {
3680                         nextToken();
3681                         return;
3682                     }
3683                     break;
3684 
3685                 case TOK.endOfFile:
3686                     error("end of file found before right parenthesis");
3687                     return;
3688 
3689                 default:
3690                     break;
3691             }
3692             nextToken();
3693         }
3694     }
3695 
3696     //}
3697     /******************************************************************************/
3698     /***************************** Struct & Enum Parser ***************************/
3699     //{
3700 
3701     /*************************************
3702      * C11 6.7.2.2
3703      * enum-specifier:
3704      *    enum identifier (opt) { enumerator-list }
3705      *    enum identifier (opt) { enumerator-list , }
3706      *    enum identifier
3707      *
3708      * enumerator-list:
3709      *    enumerator
3710      *    enumerator-list , enumerator
3711      *
3712      * enumerator:
3713      *    enumeration-constant
3714      *    enumeration-constant = constant-expression
3715      *
3716      * enumeration-constant:
3717      *    identifier
3718      *
3719      * Params:
3720      *  symbols = symbols to add enum declaration to
3721      * Returns:
3722      *  type of the enum
3723      */
3724     private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
3725     {
3726         const loc = token.loc;
3727         nextToken();
3728 
3729         /* GNU Extensions
3730          * enum-specifier:
3731          *    enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3732          *    enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3733          *    enum gnu-attributes (opt) identifier
3734          */
3735         Specifier specifier;
3736         specifier.packalign.setDefault();
3737         if (token.value == TOK.__attribute__)
3738             cparseGnuAttributes(specifier);
3739 
3740         Identifier tag;
3741         if (token.value == TOK.identifier)
3742         {
3743             tag = token.ident;
3744             nextToken();
3745         }
3746 
3747         /* clang extension: add optional base type after the identifier
3748          * https://en.cppreference.com/w/cpp/language/enum
3749          *   enum Identifier : Type
3750          */
3751         //AST.Type base = AST.Type.tint32;  // C11 6.7.2.2-4 implementation defined default base type
3752         AST.Type base = null;               // C23 says base type is determined by enum member values
3753         if (token.value == TOK.colon)
3754         {
3755             nextToken();
3756             base = cparseTypeName();
3757         }
3758 
3759         AST.Dsymbols* members;
3760         if (token.value == TOK.leftCurly)
3761         {
3762             nextToken();
3763             members = new AST.Dsymbols();
3764 
3765             if (token.value == TOK.rightCurly)  // C11 6.7.2.2-1
3766             {
3767                 if (tag)
3768                     error("no members for `enum %s`", tag.toChars());
3769                 else
3770                     error("no members for anonymous enum");
3771             }
3772 
3773             while (token.value == TOK.identifier)
3774             {
3775                 auto ident = token.ident;  // enumeration-constant
3776                 nextToken();
3777                 auto mloc = token.loc;
3778 
3779                 if (token.value == TOK.__attribute__)
3780                 {
3781                     /* gnu-attributes can appear here, but just scan and ignore them
3782                      * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3783                      */
3784                     Specifier specifierx;
3785                     specifierx.packalign.setDefault();
3786                     cparseGnuAttributes(specifierx);
3787                 }
3788 
3789                 AST.Expression value;
3790                 if (token.value == TOK.assign)
3791                 {
3792                     nextToken();
3793                     value = cparseConstantExp();
3794                     // TODO C11 6.7.2.2-2 value must fit into an int
3795                 }
3796 
3797                 if (token.value == TOK.__attribute__)
3798                 {
3799                     /* gnu-attributes can appear here, but just scan and ignore them
3800                      * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3801                      */
3802                     Specifier specifierx;
3803                     specifierx.packalign.setDefault();
3804                     cparseGnuAttributes(specifierx);
3805                 }
3806 
3807                 auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
3808                 members.push(em);
3809 
3810                 if (token.value == TOK.comma)
3811                 {
3812                     nextToken();
3813                     continue;
3814                 }
3815                 break;
3816             }
3817             check(TOK.rightCurly);
3818 
3819             /* GNU Extensions
3820              * Parse the postfix gnu-attributes (opt)
3821              */
3822             if (token.value == TOK.__attribute__)
3823                 cparseGnuAttributes(specifier);
3824         }
3825         else if (!tag)
3826             error("missing `identifier` after `enum`");
3827 
3828         /* Need semantic information to determine if this is a declaration,
3829          * redeclaration, or reference to existing declaration.
3830          * Defer to the semantic() pass with a TypeTag.
3831          */
3832         return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
3833     }
3834 
3835     /*************************************
3836      * C11 6.7.2.1
3837      * Parse struct and union specifiers.
3838      * Parser is advanced to the tag identifier or brace.
3839      * struct-or-union-specifier:
3840      *    struct-or-union identifier (opt) { struct-declaration-list }
3841      *    struct-or-union identifier
3842      *
3843      * struct-or-union:
3844      *    struct
3845      *    union
3846      *
3847      * struct-declaration-list:
3848      *    struct-declaration
3849      *    struct-declaration-list struct-declaration
3850      *
3851      * Params:
3852      *  loc = location of `struct` or `union`
3853      *  structOrUnion = TOK.struct_ or TOK.union_
3854      *  packalign = alignment to use for struct members
3855      *  symbols = symbols to add struct-or-union declaration to
3856      * Returns:
3857      *  type of the struct
3858      */
3859     private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
3860     {
3861         Identifier tag;
3862 
3863         if (token.value == TOK.identifier)
3864         {
3865             tag = token.ident;
3866             nextToken();
3867         }
3868 
3869         AST.Dsymbols* members;
3870         if (token.value == TOK.leftCurly)
3871         {
3872             nextToken();
3873             members = new AST.Dsymbols();          // so `members` will be non-null even with 0 members
3874             while (token.value != TOK.rightCurly)
3875             {
3876                 cparseStructDeclaration(members);
3877 
3878                 if (token.value == TOK.endOfFile)
3879                     break;
3880             }
3881             check(TOK.rightCurly);
3882 
3883             if ((*members).length == 0) // C11 6.7.2.1-8
3884             {
3885                 /* allow empty structs as an extension
3886                  *  struct-declarator-list:
3887                  *    struct-declarator (opt)
3888                  */
3889             }
3890         }
3891         else if (!tag)
3892             error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
3893 
3894         // many ways and places to declare alignment
3895         if (packalign.isUnknown() && !this.packalign.isUnknown())
3896             packalign.set(this.packalign.get());
3897 
3898         /* Need semantic information to determine if this is a declaration,
3899          * redeclaration, or reference to existing declaration.
3900          * Defer to the semantic() pass with a TypeTag.
3901          */
3902         return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
3903     }
3904 
3905     /*************************************
3906      * C11 6.7.2.1
3907      * Parse a struct declaration member.
3908      * struct-declaration:
3909      *    specifier-qualifier-list struct-declarator-list (opt) ;
3910      *    static_assert-declaration
3911      *
3912      * struct-declarator-list:
3913      *    struct-declarator
3914      *    struct-declarator-list , struct-declarator
3915      *
3916      * struct-declarator:
3917      *    declarator
3918      *    declarator (opt) : constant-expression
3919      * Params:
3920      *    members = where to put the fields (members)
3921      */
3922     void cparseStructDeclaration(AST.Dsymbols* members)
3923     {
3924         //printf("cparseStructDeclaration()\n");
3925         if (token.value == TOK._Static_assert)
3926         {
3927             auto s = cparseStaticAssert();
3928             members.push(s);
3929             return;
3930         }
3931 
3932         Specifier specifier;
3933         specifier.packalign = this.packalign;
3934         auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
3935         if (!tspec)
3936         {
3937             error("no type-specifier for struct member");
3938             tspec = AST.Type.tint32;
3939         }
3940         if (specifier.mod & MOD.xconst)
3941         {
3942             tspec = toConst(tspec);
3943             specifier.mod = MOD.xnone;          // 'used' it
3944         }
3945 
3946         /* If a declarator does not follow, it is unnamed
3947          */
3948         if (token.value == TOK.semicolon && tspec)
3949         {
3950             nextToken();
3951             auto tt = tspec.isTypeTag();
3952             if (!tt)
3953             {
3954                 if (auto ti = tspec.isTypeIdentifier())
3955                 {
3956                     error("type-specifier omitted before declaration of `%s`", ti.ident.toChars());
3957                 }
3958                 return; // legal but meaningless empty declaration
3959             }
3960 
3961             /* If anonymous struct declaration
3962              *   struct { ... members ... };
3963              * C11 6.7.2.1-13
3964              */
3965             if (!tt.id && tt.members)
3966             {
3967                 /* members of anonymous struct are considered members of
3968                  * the containing struct
3969                  */
3970                 auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
3971                 auto s = applySpecifier(ad, specifier);
3972                 members.push(s);
3973                 return;
3974             }
3975             if (!tt.id && !tt.members)
3976                 return; // already gave error in cparseStruct()
3977 
3978             /* `struct tag;` and `struct tag { ... };`
3979              * always result in a declaration in the current scope
3980              */
3981             // TODO: merge in specifier
3982             auto stag = (tt.tok == TOK.struct_)
3983                 ? new AST.StructDeclaration(tt.loc, tt.id, false)
3984                 : new AST.UnionDeclaration(tt.loc, tt.id);
3985             stag.members = tt.members;
3986             if (!symbols)
3987                 symbols = new AST.Dsymbols();
3988             auto s = applySpecifier(stag, specifier);
3989             symbols.push(s);
3990             return;
3991         }
3992 
3993         while (1)
3994         {
3995             Identifier id;
3996             AST.Type dt;
3997             if (token.value == TOK.colon)
3998             {
3999                 if (auto ti = tspec.isTypeIdentifier())
4000                 {
4001                     error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars());
4002                     tspec = AST.Type.tint32;
4003                 }
4004 
4005                 // C11 6.7.2.1-12 unnamed bit-field
4006                 id = Identifier.generateAnonymousId("BitField");
4007                 dt = tspec;
4008             }
4009             else
4010             {
4011                 dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
4012                 if (!dt)
4013                 {
4014                     panic();
4015                     nextToken();
4016                     break;          // error recovery
4017                 }
4018             }
4019 
4020             AST.Expression width;
4021             if (token.value == TOK.colon)
4022             {
4023                 // C11 6.7.2.1-10 bit-field
4024                 nextToken();
4025                 width = cparseConstantExp();
4026             }
4027 
4028             /* GNU Extensions
4029              * struct-declarator:
4030              *    declarator gnu-attributes (opt)
4031              *    declarator (opt) : constant-expression gnu-attributes (opt)
4032              */
4033             if (token.value == TOK.__attribute__)
4034                 cparseGnuAttributes(specifier);
4035 
4036             if (!tspec && !specifier.scw && !specifier.mod)
4037                 error("specifier-qualifier-list required");
4038             else if (width)
4039             {
4040                 if (specifier.alignExps)
4041                     error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4042                 auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
4043                 members.push(s);
4044             }
4045             else if (id)
4046             {
4047                 if (dt.ty == AST.Tvoid)
4048                     error("`void` has no value");
4049 
4050                 // declare the symbol
4051                 // Give member variables an implicit void initializer
4052                 auto initializer = new AST.VoidInitializer(token.loc);
4053                 AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
4054                 s = applySpecifier(s, specifier);
4055                 members.push(s);
4056             }
4057 
4058             switch (token.value)
4059             {
4060                 case TOK.identifier:
4061                     error("missing comma");
4062                     goto default;
4063 
4064                 case TOK.semicolon:
4065                     nextToken();
4066                     return;
4067 
4068                 case TOK.comma:
4069                     nextToken();
4070                     break;
4071 
4072                 default:
4073                     error("`;` or `,` expected");
4074                     while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
4075                         nextToken();
4076                     nextToken();
4077                     return;
4078             }
4079         }
4080     }
4081 
4082     //}
4083     /******************************************************************************/
4084     /********************************* Lookahead Parser ***************************/
4085     //{
4086 
4087     /************************************
4088      * Determine if the scanner is sitting on the start of a declaration.
4089      * Params:
4090      *      t       = current token of the scanner
4091      *      needId  = flag with additional requirements for a declaration
4092      *      endtok  = ending token
4093      *      pt      = will be set ending token (if not null)
4094      * Returns:
4095      *      true at start of a declaration
4096      */
4097     private bool isCDeclaration(ref Token* pt)
4098     {
4099         auto t = pt;
4100         //printf("isCDeclaration() %s\n", t.toChars());
4101         if (!isDeclarationSpecifiers(t))
4102             return false;
4103 
4104         while (1)
4105         {
4106             if (t.value == TOK.semicolon)
4107             {
4108                 t = peek(t);
4109                 pt = t;
4110                 return true;
4111             }
4112             if (!isCDeclarator(t, DTR.xdirect))
4113                 return false;
4114             if (t.value == TOK.asm_)
4115             {
4116                 t = peek(t);
4117                 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4118                     return false;
4119             }
4120             if (t.value == TOK.__attribute__)
4121             {
4122                 t = peek(t);
4123                 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4124                     return false;
4125             }
4126             if (t.value == TOK.assign)
4127             {
4128                 t = peek(t);
4129                 if (!isInitializer(t))
4130                     return false;
4131             }
4132             switch (t.value)
4133             {
4134                 case TOK.comma:
4135                     t = peek(t);
4136                     break;
4137 
4138                 case TOK.semicolon:
4139                     t = peek(t);
4140                     pt = t;
4141                     return true;
4142 
4143                 default:
4144                     return false;
4145             }
4146         }
4147     }
4148 
4149     /********************************
4150      * See if match for initializer.
4151      * Params:
4152      *  pt = starting token, updated to one past end of initializer if true
4153      * Returns:
4154      *  true if initializer
4155      */
4156     private bool isInitializer(ref Token* pt)
4157     {
4158         //printf("isInitializer()\n");
4159         auto t = pt;
4160 
4161         if (t.value == TOK.leftCurly)
4162         {
4163             if (!skipBraces(t))
4164                 return false;
4165             pt = t;
4166             return true;
4167         }
4168 
4169         // skip over assignment-expression, ending before comma or semiColon or EOF
4170         if (!isAssignmentExpression(t))
4171             return false;
4172         pt = t;
4173         return true;
4174     }
4175 
4176     /********************************
4177      * See if match for:
4178      *    postfix-expression ( argument-expression-list(opt) )
4179      * Params:
4180      *  pt = starting token, updated to one past end of initializer if true
4181      * Returns:
4182      *  true if function call
4183      */
4184     private bool isFunctionCall(ref Token* pt)
4185     {
4186         //printf("isFunctionCall()\n");
4187         auto t = pt;
4188 
4189         if (!isPrimaryExpression(t))
4190             return false;
4191         if (t.value != TOK.leftParenthesis)
4192             return false;
4193         t = peek(t);
4194         while (1)
4195         {
4196             if (!isAssignmentExpression(t))
4197                 return false;
4198             if (t.value == TOK.comma)
4199             {
4200                 t = peek(t);
4201                 continue;
4202             }
4203             if (t.value == TOK.rightParenthesis)
4204             {
4205                 t = peek(t);
4206                 break;
4207             }
4208             return false;
4209         }
4210         if (t.value != TOK.semicolon)
4211             return false;
4212         pt = t;
4213         return true;
4214     }
4215 
4216     /********************************
4217      * See if match for assignment-expression.
4218      * Params:
4219      *  pt = starting token, updated to one past end of assignment-expression if true
4220      * Returns:
4221      *  true if assignment-expression
4222      */
4223     private bool isAssignmentExpression(ref Token* pt)
4224     {
4225         auto t = pt;
4226         //printf("isAssignmentExpression() %s\n", t.toChars());
4227 
4228         /* This doesn't actually check for grammar matching an
4229          * assignment-expression. It just matches ( ) [ ] looking for
4230          * an ending token that would terminate one.
4231          */
4232         bool any;
4233         while (1)
4234         {
4235             switch (t.value)
4236             {
4237                 case TOK.comma:
4238                 case TOK.semicolon:
4239                 case TOK.rightParenthesis:
4240                 case TOK.rightBracket:
4241                 case TOK.endOfFile:
4242                     if (!any)
4243                         return false;
4244                     break;
4245 
4246                 case TOK.leftParenthesis:
4247                     if (!skipParens(t, &t))
4248                         return false;
4249                     /*
4250                         https://issues.dlang.org/show_bug.cgi?id=22267
4251                         If the parser encounters the following
4252                             `identifier variableName = (expression);`
4253                         the initializer is not identified as such since the parentheses
4254                         cause the parser to keep walking indefinitely
4255                         (whereas `(1) + 1` would not be affected.).
4256                     */
4257                     any = true;
4258                     continue;
4259 
4260                 case TOK.leftBracket:
4261                     if (!skipBrackets(t))
4262                         return false;
4263                     continue;
4264 
4265                 case TOK.leftCurly:
4266                     if (!skipBraces(t))
4267                         return false;
4268                     continue;
4269 
4270                 default:
4271                     any = true;   // assume token was part of an a-e
4272                     t = peek(t);
4273                     continue;
4274             }
4275             pt = t;
4276             return true;
4277         }
4278     }
4279 
4280     /********************************
4281      * See if match for constant-expression.
4282      * Params:
4283      *  pt = starting token, updated to one past end of constant-expression if true
4284      * Returns:
4285      *  true if constant-expression
4286      */
4287     private bool isConstantExpression(ref Token* pt)
4288     {
4289         return isAssignmentExpression(pt);
4290     }
4291 
4292     /********************************
4293      * See if match for declaration-specifiers.
4294      * No errors are diagnosed.
4295      * Params:
4296      *  pt = starting token, updated to one past end of declaration-specifiers if true
4297      * Returns:
4298      *  true if declaration-specifiers
4299      */
4300     private bool isDeclarationSpecifiers(ref Token* pt)
4301     {
4302         //printf("isDeclarationSpecifiers()\n");
4303 
4304         auto t = pt;
4305 
4306         bool seenType;
4307         bool any;
4308         while (1)
4309         {
4310             switch (t.value)
4311             {
4312                 // type-specifiers
4313                 case TOK.void_:
4314                 case TOK.char_:
4315                 case TOK.int16:
4316                 case TOK.int32:
4317                 case TOK.int64:
4318                 case TOK.__int128:
4319                 case TOK.float32:
4320                 case TOK.float64:
4321                 case TOK.signed:
4322                 case TOK.unsigned:
4323                 case TOK._Bool:
4324                 //case TOK._Imaginary:
4325                 case TOK._Complex:
4326                     t = peek(t);
4327                     seenType = true;
4328                     any = true;
4329                     continue;
4330 
4331                 case TOK.identifier: // typedef-name
4332                     if (!seenType)
4333                     {
4334                         t = peek(t);
4335                         seenType = true;
4336                         any = true;
4337                         continue;
4338                     }
4339                     break;
4340 
4341                 case TOK.struct_:
4342                 case TOK.union_:
4343                 case TOK.enum_:
4344                     t = peek(t);
4345                     if (t.value == TOK.__attribute__ ||
4346                         t.value == TOK.__declspec)
4347                     {
4348                         t = peek(t);
4349                         if (!skipParens(t, &t))
4350                             return false;
4351                     }
4352                     if (t.value == TOK.identifier)
4353                     {
4354                         t = peek(t);
4355                         if (t.value == TOK.leftCurly)
4356                         {
4357                             if (!skipBraces(t))
4358                                 return false;
4359                         }
4360                     }
4361                     else if (t.value == TOK.leftCurly)
4362                     {
4363                         if (!skipBraces(t))
4364                             return false;
4365                     }
4366                     else
4367                         return false;
4368                     any = true;
4369                     continue;
4370 
4371                 // storage-class-specifiers
4372                 case TOK.typedef_:
4373                 case TOK.extern_:
4374                 case TOK.static_:
4375                 case TOK.__thread:
4376                 case TOK._Thread_local:
4377                 case TOK.auto_:
4378                 case TOK.register:
4379 
4380                 // function-specifiers
4381                 case TOK.inline:
4382                 case TOK._Noreturn:
4383 
4384                 // type-qualifiers
4385                 case TOK.const_:
4386                 case TOK..volatile:
4387                 case TOK.restrict:
4388                 case TOK.__stdcall:
4389                     t = peek(t);
4390                     any = true;
4391                     continue;
4392 
4393                 case TOK._Alignas:      // alignment-specifier
4394                 case TOK.__declspec:    // decl-specifier
4395                 case TOK.__attribute__: // attribute-specifier
4396                     t = peek(t);
4397                     if (!skipParens(t, &t))
4398                         return false;
4399                     any = true;
4400                     continue;
4401 
4402                 // either atomic-type-specifier or type_qualifier
4403                 case TOK._Atomic:  // TODO _Atomic ( type-name )
4404                     t = peek(t);
4405                     if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
4406                     {
4407                         auto tsave = t;
4408                         t = peek(t);
4409                         if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4410                         {   // it's a type-qualifier
4411                             t = tsave;  // back up parser
4412                             any = true;
4413                             continue;
4414                         }
4415                         t = peek(t);    // move past right parenthesis of atomic-type-specifier
4416                     }
4417                     any = true;
4418                     continue;
4419 
4420                 default:
4421                     break;
4422             }
4423             break;
4424         }
4425 
4426         if (any)
4427         {
4428             pt = t;
4429             return true;
4430         }
4431         return false;
4432     }
4433 
4434     /**************************************
4435      * See if declaration-list is present.
4436      * Returns:
4437      *    true if declaration-list is present, even an empty one
4438      */
4439     bool isDeclarationList(ref Token* pt)
4440     {
4441         auto t = pt;
4442         while (1)
4443         {
4444             if (t.value == TOK.leftCurly)
4445             {
4446                 pt = t;
4447                 return true;
4448             }
4449             if (!isCDeclaration(t))
4450                 return false;
4451         }
4452     }
4453 
4454     /*******************************************
4455      * Skip braces.
4456      * Params:
4457      *      pt = enters on left brace, set to token past right bracket on true
4458      * Returns:
4459      *      true if successful
4460      */
4461     private bool skipBraces(ref Token* pt)
4462     {
4463         auto t = pt;
4464         if (t.value != TOK.leftCurly)
4465             return false;
4466 
4467         int braces = 0;
4468 
4469         while (1)
4470         {
4471             switch (t.value)
4472             {
4473                 case TOK.leftCurly:
4474                     ++braces;
4475                     t = peek(t);
4476                     continue;
4477 
4478                 case TOK.rightCurly:
4479                     --braces;
4480                     if (braces == 0)
4481                     {
4482                         pt = peek(t);
4483                         return true;
4484                     }
4485                     if (braces < 0)
4486                         return false;
4487 
4488                     t = peek(t);
4489                     continue;
4490 
4491                 case TOK.endOfFile:
4492                     return false;
4493 
4494                 default:
4495                     t = peek(t);
4496                     continue;
4497             }
4498         }
4499     }
4500 
4501     /*******************************************
4502      * Skip brackets.
4503      * Params:
4504      *      pt = enters on left bracket, set to token past right bracket on true
4505      * Returns:
4506      *      true if successful
4507      */
4508     private bool skipBrackets(ref Token* pt)
4509     {
4510         auto t = pt;
4511         if (t.value != TOK.leftBracket)
4512             return false;
4513 
4514         int brackets = 0;
4515 
4516         while (1)
4517         {
4518             switch (t.value)
4519             {
4520                 case TOK.leftBracket:
4521                     ++brackets;
4522                     t = peek(t);
4523                     continue;
4524 
4525                 case TOK.rightBracket:
4526                     --brackets;
4527                     if (brackets == 0)
4528                     {
4529                         pt = peek(t);
4530                         return true;
4531                     }
4532                     if (brackets < 0)
4533                         return false;
4534 
4535                     t = peek(t);
4536                     continue;
4537 
4538                 case TOK.endOfFile:
4539                     return false;
4540 
4541                 default:
4542                     t = peek(t);
4543                     continue;
4544             }
4545         }
4546     }
4547 
4548     /*********************************
4549      * Check to see if tokens starting with *pt form a declarator.
4550      * Params:
4551      *  pt = pointer to starting token, updated to point past declarator if true is returned
4552      *  declarator = declarator kind
4553      * Returns:
4554      *  true if it does
4555      */
4556     private bool isCDeclarator(ref Token* pt, DTR declarator)
4557     {
4558         auto t = pt;
4559         while (1)
4560         {
4561             if (t.value == TOK.mul)     // pointer
4562             {
4563                 t = peek(t);
4564                 if (!isTypeQualifierList(t))
4565                     return false;
4566             }
4567             else
4568                 break;
4569         }
4570 
4571         if (t.value == TOK.identifier)
4572         {
4573             if (declarator == DTR.xabstract)
4574                 return false;
4575             t = peek(t);
4576         }
4577         else if (t.value == TOK.leftParenthesis)
4578         {
4579             t = peek(t);
4580             if (!isCDeclarator(t, declarator))
4581                 return false;
4582             if (t.value != TOK.rightParenthesis)
4583                 return false;
4584             t = peek(t);
4585         }
4586         else if (declarator == DTR.xdirect)
4587         {
4588             return false;
4589         }
4590 
4591         while (1)
4592         {
4593             if (t.value == TOK.leftBracket)
4594             {
4595                 if (!skipBrackets(t))
4596                     return false;
4597             }
4598             else if (t.value == TOK.leftParenthesis)
4599             {
4600                 if (!skipParens(t, &t))
4601                     return false;
4602             }
4603             else
4604                 break;
4605         }
4606         pt = t;
4607         return true;
4608     }
4609 
4610     /***************************
4611      * Is this the start of a type-qualifier-list?
4612      * (Can be empty.)
4613      * Params:
4614      *  pt = first token; updated with past end of type-qualifier-list if true
4615      * Returns:
4616      *  true if start of type-qualifier-list
4617      */
4618     private bool isTypeQualifierList(ref Token* pt)
4619     {
4620         auto t = pt;
4621         while (1)
4622         {
4623             switch (t.value)
4624             {
4625                 case TOK.const_:
4626                 case TOK.restrict:
4627                 case TOK..volatile:
4628                 case TOK._Atomic:
4629                 case TOK.__stdcall:
4630                     t = peek(t);
4631                     continue;
4632 
4633                 default:
4634                     break;
4635             }
4636             break;
4637         }
4638         pt = t;
4639         return true;
4640     }
4641 
4642     /***************************
4643      * Is this the start of a type-name?
4644      * Params:
4645      *  pt = first token; updated with past end of type-name if true
4646      * Returns:
4647      *  true if start of type-name
4648      */
4649     private bool isTypeName(ref Token* pt)
4650     {
4651         auto t = pt;
4652         //printf("isTypeName() %s\n", t.toChars());
4653         if (!isSpecifierQualifierList(t))
4654             return false;
4655         if (!isCDeclarator(t, DTR.xabstract))
4656             return false;
4657         if (t.value != TOK.rightParenthesis)
4658             return false;
4659         pt = t;
4660         return true;
4661     }
4662 
4663     /***************************
4664      * Is this the start of a specifier-qualifier-list?
4665      * Params:
4666      *  pt = first token; updated with past end of specifier-qualifier-list if true
4667      * Returns:
4668      *  true if start of specifier-qualifier-list
4669      */
4670     private bool isSpecifierQualifierList(ref Token* pt)
4671     {
4672         auto t = pt;
4673         bool result;
4674         while (1)
4675         {
4676             switch (t.value)
4677             {
4678                 // Type Qualifiers
4679                 case TOK.const_:
4680                 case TOK.restrict:
4681                 case TOK..volatile:
4682                 case TOK.__stdcall:
4683 
4684                 // Type Specifiers
4685                 case TOK.char_:
4686                 case TOK.signed:
4687                 case TOK.unsigned:
4688                 case TOK.int16:
4689                 case TOK.int32:
4690                 case TOK.int64:
4691                 case TOK.__int128:
4692                 case TOK.float32:
4693                 case TOK.float64:
4694                 case TOK.void_:
4695                 case TOK._Bool:
4696                 //case TOK._Imaginary: // ? missing in Spec
4697                 case TOK._Complex:
4698                     t = peek(t);
4699                     break;
4700 
4701                 case TOK.identifier:
4702                     // Use typedef table to disambiguate
4703                     if (isTypedef(t.ident))
4704                     {
4705                         t = peek(t);
4706                         break;
4707                     }
4708                     else
4709                     {
4710                         return false;
4711                     }
4712 
4713                 // struct-or-union-specifier
4714                 // enum-specifier
4715                 case TOK.struct_:
4716                 case TOK.union_:
4717                 case TOK.enum_:
4718                     t = peek(t);
4719                     if (t.value == TOK.identifier)
4720                     {
4721                         t = peek(t);
4722                         if (t.value == TOK.leftCurly)
4723                         {
4724                             if (!skipBraces(t))
4725                                 return false;
4726                         }
4727                     }
4728                     else if (t.value == TOK.leftCurly)
4729                     {
4730                         if (!skipBraces(t))
4731                             return false;
4732                     }
4733                     else
4734                         return false;
4735                     break;
4736 
4737                 // atomic-type-specifier
4738                 case TOK._Atomic:
4739                 case TOK.typeof_:
4740                 case TOK.__attribute__:
4741                     t = peek(t);
4742                     if (t.value != TOK.leftParenthesis ||
4743                         !skipParens(t, &t))
4744                         return false;
4745                     break;
4746 
4747                 default:
4748                     if (result)
4749                         pt = t;
4750                     return result;
4751             }
4752             result = true;
4753         }
4754     }
4755 
4756     /************************************
4757      * Looking at the leading left parenthesis, and determine if it is
4758      * either of the following:
4759      *    ( type-name ) cast-expression
4760      *    ( type-name ) { initializer-list }
4761      * as opposed to:
4762      *    ( expression )
4763      * Params:
4764      *    pt = starting token, updated to one past end of constant-expression if true
4765      *    afterParenType = true if already seen `( type-name )`
4766      * Returns:
4767      *    true if matches ( type-name ) ...
4768      */
4769     private bool isCastExpression(ref Token* pt, bool afterParenType = false)
4770     {
4771         enum log = false;
4772         if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
4773         auto t = pt;
4774         switch (t.value)
4775         {
4776             case TOK.leftParenthesis:
4777                 auto tk = peek(t);  // move past left parenthesis
4778                 if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
4779                 {
4780                     if (afterParenType)
4781                         goto default; // could be ( type-name ) ( unary-expression )
4782                     return false;
4783                 }
4784                 tk = peek(tk);  // move past right parenthesis
4785 
4786                 if (tk.value == TOK.leftCurly)
4787                 {
4788                     // ( type-name ) { initializer-list }
4789                     if (!isInitializer(tk))
4790                     {
4791                         return false;
4792                     }
4793                     t = tk;
4794                     break;
4795                 }
4796 
4797                 if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
4798                 {
4799                     return false;    // (type-name)() is not a cast (it might be a function call)
4800                 }
4801 
4802                 if (!isCastExpression(tk, true))
4803                 {
4804                     if (afterParenType) // could be ( type-name ) ( unary-expression )
4805                         goto default;   // where unary-expression also matched type-name
4806                     return true;
4807                 }
4808                 // ( type-name ) cast-expression
4809                 t = tk;
4810                 break;
4811 
4812             default:
4813                 if (!afterParenType || !isUnaryExpression(t, afterParenType))
4814                 {
4815                     return false;
4816                 }
4817                 // if we've already seen ( type-name ), then this is a cast
4818                 break;
4819         }
4820         pt = t;
4821         if (log) printf("isCastExpression true\n");
4822         return true;
4823     }
4824 
4825     /********************************
4826      * See if match for unary-expression.
4827      * Params:
4828      *    pt = starting token, updated to one past end of constant-expression if true
4829      *    afterParenType = true if already seen ( type-name ) of a cast-expression
4830      * Returns:
4831      *    true if unary-expression
4832      */
4833     private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
4834     {
4835         auto t = pt;
4836         switch (t.value)
4837         {
4838             case TOK.plusPlus:
4839             case TOK.minusMinus:
4840                 t = peek(t);
4841                 if (!isUnaryExpression(t, afterParenType))
4842                     return false;
4843                 break;
4844 
4845             case TOK.and:
4846             case TOK.mul:
4847             case TOK.min:
4848             case TOK.add:
4849             case TOK.not:
4850             case TOK.tilde:
4851                 t = peek(t);
4852                 if (!isCastExpression(t, afterParenType))
4853                     return false;
4854                 break;
4855 
4856             case TOK.sizeof_:
4857                 t = peek(t);
4858                 if (t.value == TOK.leftParenthesis)
4859                 {
4860                     auto tk = peek(t);
4861                     if (isTypeName(tk))
4862                     {
4863                         if (tk.value != TOK.rightParenthesis)
4864                             return false;
4865                         t = peek(tk);
4866                         break;
4867                     }
4868                 }
4869                 if (!isUnaryExpression(t, afterParenType))
4870                     return false;
4871                 break;
4872 
4873             case TOK._Alignof:
4874                 t = peek(t);
4875                 if (t.value != TOK.leftParenthesis)
4876                     return false;
4877                 t = peek(t);
4878                 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4879                     return false;
4880                 break;
4881 
4882             default:
4883                 // Compound literals are handled by cast and sizeof expressions,
4884                 // so be content with just seeing a primary expression.
4885                 if (!isPrimaryExpression(t))
4886                     return false;
4887                 break;
4888         }
4889         pt = t;
4890         return true;
4891     }
4892 
4893     /********************************
4894      * See if match for primary-expression.
4895      * Params:
4896      *    pt = starting token, updated to one past end of constant-expression if true
4897      * Returns:
4898      *    true if primary-expression
4899      */
4900     private bool isPrimaryExpression(ref Token* pt)
4901     {
4902         auto t = pt;
4903         switch (t.value)
4904         {
4905             case TOK.identifier:
4906             case TOK.charLiteral:
4907             case TOK.int32Literal:
4908             case TOK.uns32Literal:
4909             case TOK.int64Literal:
4910             case TOK.uns64Literal:
4911             case TOK.float32Literal:
4912             case TOK.float64Literal:
4913             case TOK.float80Literal:
4914             case TOK.imaginary32Literal:
4915             case TOK.imaginary64Literal:
4916             case TOK.imaginary80Literal:
4917             case TOK.string_:
4918                 t = peek(t);
4919                 break;
4920 
4921             case TOK.leftParenthesis:
4922                 // ( expression )
4923                 if (!skipParens(t, &t))
4924                     return false;
4925                 break;
4926 
4927             case TOK._Generic:
4928                 t = peek(t);
4929                 if (!skipParens(t, &t))
4930                     return false;
4931                 break;
4932 
4933             default:
4934                 return false;
4935         }
4936         pt = t;
4937         return true;
4938     }
4939 
4940     //}
4941     /******************************************************************************/
4942     /********************************* More ***************************************/
4943     //{
4944 
4945     /**************
4946      * Declaration context
4947      */
4948     enum LVL
4949     {
4950         global    = 1,    /// global
4951         parameter = 2,    /// function parameter (declarations for function identifier-list)
4952         prototype = 4,    /// function prototype
4953         local     = 8,    /// local
4954         member    = 0x10, /// struct member
4955     }
4956 
4957     /// Types of declarator to parse
4958     enum DTR
4959     {
4960         xdirect    = 1, /// C11 6.7.6 direct-declarator
4961         xabstract  = 2, /// C11 6.7.7 abstract-declarator
4962         xparameter = 3, /// parameter declarator may be either direct or abstract
4963     }
4964 
4965     /// C11 6.7.1 Storage-class specifiers
4966     enum SCW : uint
4967     {
4968         xnone      = 0,
4969         xtypedef   = 1,
4970         xextern    = 2,
4971         xstatic    = 4,
4972         x_Thread_local = 8,
4973         xauto      = 0x10,
4974         xregister  = 0x20,
4975         // C11 6.7.4 Function specifiers
4976         xinline    = 0x40,
4977         x_Noreturn = 0x80,
4978 
4979         xnoinline  = 0x100,
4980     }
4981 
4982     /// C11 6.7.3 Type qualifiers
4983     enum MOD : uint
4984     {
4985         xnone     = 0,
4986         xconst    = 1,
4987         xvolatile = 2,
4988         xrestrict = 4,
4989         x_Atomic  = 8,
4990         x__stdcall = 0x10, // Windows linkage extension
4991     }
4992 
4993     /**********************************
4994      * Aggregate for all the various specifiers
4995      */
4996     struct Specifier
4997     {
4998         bool noreturn;  /// noreturn attribute
4999         bool naked;     /// naked attribute
5000         bool _nothrow;  /// nothrow attribute
5001         bool _pure;     /// pure attribute
5002         bool dllimport; /// dllimport attribute
5003         bool dllexport; /// dllexport attribute
5004         bool _deprecated;       /// deprecated attribute
5005         AST.Expression depMsg;  /// deprecated message
5006         uint vector_size;       /// positive power of 2 multipe of base type size
5007 
5008         SCW scw;        /// storage-class specifiers
5009         MOD mod;        /// type qualifiers
5010         AST.Expressions*  alignExps;  /// alignment
5011         structalign_t packalign;  /// #pragma pack alignment value
5012     }
5013 
5014     /***********************
5015      * Convert from C specifiers to D storage class
5016      * Params:
5017      *  level = declaration context
5018      *  specifier = specifiers, context, etc.
5019      * Returns:
5020      *  corresponding D storage class
5021      */
5022     StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
5023     {
5024         StorageClass stc;
5025         if (specifier.scw & SCW.x_Thread_local)
5026         {
5027             if (level == LVL.global)
5028             {
5029                 if (specifier.scw & SCW.xextern)
5030                     stc = AST.STC.extern_;
5031                 else if (specifier.scw & SCW.xstatic)
5032                     stc = AST.STC.static_;
5033             }
5034             else if (level == LVL.local)
5035             {
5036                 if (specifier.scw & SCW.xextern)
5037                     stc = AST.STC.extern_;
5038                 else if (specifier.scw & SCW.xstatic)
5039                     stc = AST.STC.static_;
5040             }
5041             else if (level == LVL.member)
5042             {
5043                 if (specifier.scw & SCW.xextern)
5044                     stc = AST.STC.extern_;
5045                 else if (specifier.scw & SCW.xstatic)
5046                     stc = AST.STC.static_;
5047             }
5048         }
5049         else
5050         {
5051             if (level == LVL.global)
5052             {
5053                 if (specifier.scw & SCW.xextern)
5054                     stc = AST.STC.extern_ | AST.STC.gshared;
5055                 else if (specifier.scw & SCW.xstatic)
5056                     stc = AST.STC.gshared | AST.STC.static_;
5057                 else
5058                     stc = AST.STC.gshared;
5059             }
5060             else if (level == LVL.local)
5061             {
5062                 if (specifier.scw & SCW.xextern)
5063                     stc = AST.STC.extern_ | AST.STC.gshared;
5064                 else if (specifier.scw & SCW.xstatic)
5065                     stc = AST.STC.gshared;
5066                 else if (specifier.scw & SCW.xregister)
5067                     stc = AST.STC.register;
5068             }
5069             else if (level == LVL.parameter)
5070             {
5071                 if (specifier.scw & SCW.xregister)
5072                     stc = AST.STC.register | AST.STC.parameter;
5073                 else
5074                     stc = AST.STC.parameter;
5075             }
5076             else if (level == LVL.member)
5077             {
5078                 if (specifier.scw & SCW.xextern)
5079                     stc = AST.STC.extern_ | AST.STC.gshared;
5080                 else if (specifier.scw & SCW.xstatic)
5081                     stc = AST.STC.gshared;
5082             }
5083         }
5084         if (specifier._deprecated && !specifier.depMsg)
5085             stc |= AST.STC.deprecated_;
5086         return stc;
5087     }
5088 
5089     /***********************
5090      * Add attributes from Specifier to function
5091      * Params:
5092      *  fd = function to apply them to
5093      *  specifier = specifiers
5094      */
5095     void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
5096     {
5097         fd.isNaked = specifier.naked;
5098         fd.dllImport = specifier.dllimport;
5099         fd.dllExport = specifier.dllexport;
5100 
5101         if (specifier.scw & SCW.xnoinline)
5102             fd.inlining = PINLINE.never;
5103         else if (specifier.scw & SCW.xinline)
5104             fd.inlining = PINLINE.always;
5105     }
5106 
5107     /***********************
5108      * Add attributes from Specifier to variable
5109      * Params:
5110      *  vd = function to apply them to
5111      *  specifier = specifiers
5112      */
5113     void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
5114     {
5115         vd.dllImport = specifier.dllimport;
5116         vd.dllExport = specifier.dllexport;
5117     }
5118 
5119     /***********************
5120      * Return suitable signed integer type for the given size
5121      * Params:
5122      *  size = size of type
5123      * Returns:
5124      *  corresponding signed D integer type
5125      */
5126     private AST.Type integerTypeForSize(ubyte size)
5127     {
5128         if (size <= 1)
5129             return AST.Type.tint8;
5130         if (size <= 2)
5131             return AST.Type.tint16;
5132         if (size <= 4)
5133             return AST.Type.tint32;
5134         if (size <= 8)
5135             return AST.Type.tint64;
5136         if (size == 16)
5137         {
5138             error("__int128 not supported");
5139             return AST.Type.terror;
5140         }
5141         error("unsupported integer type");
5142         return AST.Type.terror;
5143     }
5144 
5145     /***********************
5146      * Return suitable unsigned integer type for the given size
5147      * Params:
5148      *  size = size of type
5149      * Returns:
5150      *  corresponding unsigned D integer type
5151      */
5152     private AST.Type unsignedTypeForSize(ubyte size)
5153     {
5154         if (size <= 1)
5155             return AST.Type.tuns8;
5156         if (size <= 2)
5157             return AST.Type.tuns16;
5158         if (size <= 4)
5159             return AST.Type.tuns32;
5160         if (size <= 8)
5161             return AST.Type.tuns64;
5162         if (size == 16)
5163         {
5164             error("unsigned __int128 not supported");
5165             return AST.Type.terror;
5166         }
5167         error("unsupported integer type");
5168         return AST.Type.terror;
5169     }
5170 
5171     /***********************
5172      * Return suitable D float type for C `long double`
5173      * Params:
5174      *  flags = kind of float to return (real, imaginary, complex).
5175      * Returns:
5176      *  corresponding D type
5177      */
5178     private AST.Type realType(RTFlags flags)
5179     {
5180         if (long_doublesize == AST.Type.tfloat80.size())
5181         {
5182             // On GDC and LDC, D `real` types map to C `long double`, so never
5183             // return a double type when real.sizeof == double.sizeof.
5184             final switch (flags)
5185             {
5186                 case RTFlags.realfloat: return AST.Type.tfloat80;
5187                 case RTFlags.imaginary: return AST.Type.timaginary80;
5188                 case RTFlags.complex:   return AST.Type.tcomplex80;
5189             }
5190         }
5191         else
5192         {
5193             final switch (flags)
5194             {
5195                 case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
5196                 case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
5197                 case RTFlags.complex:   return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
5198             }
5199         }
5200     }
5201 
5202     /**************
5203      * Flags for realType
5204      */
5205     private enum RTFlags
5206     {
5207         realfloat,
5208         imaginary,
5209         complex,
5210     }
5211 
5212     /********************
5213      * C11 6.4.2.2 Create declaration to predefine __func__
5214      *    `static const char __func__[] = " function-name ";`
5215      * Params:
5216      *    loc = location for this declaration
5217      *    id = identifier of function
5218      * Returns:
5219      *    statement representing the declaration of __func__
5220      */
5221     private AST.Statement createFuncName(Loc loc, Identifier id)
5222     {
5223         const fn = id.toString();  // function-name
5224         auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
5225         auto ifn = new AST.ExpInitializer(loc, efn);
5226         auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
5227         auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
5228         efn.type = tfn.immutableOf();
5229         efn.committed = true;
5230         auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
5231         auto e = new AST.DeclarationExp(loc, sfn);
5232         return new AST.ExpStatement(loc, e);
5233     }
5234 
5235     /************************
5236      * After encountering an error, scan forward until a right brace or ; is found
5237      * or the end of the file.
5238      */
5239     void panic()
5240     {
5241         while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
5242             nextToken();
5243     }
5244 
5245     /**************************
5246      * Apply `const` to a type.
5247      * Params:
5248      *    t = type to add const to
5249      * Returns:
5250      *    resulting type
5251      */
5252     private AST.Type toConst(AST.Type t)
5253     {
5254         // `const` is always applied to the return type, not the
5255         // type function itself.
5256         if (auto tf = t.isTypeFunction())
5257             tf.next = tf.next.addSTC(STC.const_);
5258         else if (auto tt = t.isTypeTag())
5259             tt.mod |= MODFlags.const_;
5260         else
5261         {
5262             /* Ignore const if the result would be const pointer to mutable
5263              */
5264             auto tn = t.nextOf();
5265             if (!tn || tn.isConst())
5266                 t = t.addSTC(STC.const_);
5267         }
5268         return t;
5269     }
5270 
5271     /***************************
5272      * Apply specifier to a Dsymbol.
5273      * Params:
5274      *  s = Dsymbol
5275      *  specifier = specifiers to apply
5276      * Returns:
5277      *  Dsymbol with specifiers applied
5278      */
5279     private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
5280     {
5281         //printf("applySpecifier() %s\n", s.toChars());
5282         if (specifier._deprecated)
5283         {
5284             if (specifier.depMsg)
5285             {
5286                 // Wrap declaration in a DeprecatedDeclaration
5287                 auto decls = new AST.Dsymbols(1);
5288                 (*decls)[0] = s;
5289                 s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
5290             }
5291         }
5292 
5293         if (specifier.alignExps)
5294         {
5295             //printf("  applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5296             // Wrap declaration in an AlignDeclaration
5297             auto decls = new AST.Dsymbols(1);
5298             (*decls)[0] = s;
5299             s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
5300         }
5301         else if (!specifier.packalign.isDefault() && !specifier.packalign.isUnknown())
5302         {
5303             //printf("  applying packalign %d\n", cast(int)specifier.packalign);
5304             // Wrap #pragma pack in an AlignDeclaration
5305             auto decls = new AST.Dsymbols(1);
5306             (*decls)[0] = s;
5307             s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
5308         }
5309         return s;
5310     }
5311 
5312     //}
5313 
5314     /******************************************************************************/
5315     /************************** typedefTab symbol table ***************************/
5316     //{
5317 
5318     /********************************
5319      * Determines if type t is a function type.
5320      * Params:
5321      *  t = type to test
5322      * Returns:
5323      *  true if it represents a function
5324      */
5325     bool isFunctionTypedef(AST.Type t)
5326     {
5327         //printf("isFunctionTypedef() %s\n", t.toChars());
5328         if (t.isTypeFunction())
5329             return true;
5330         if (auto tid = t.isTypeIdentifier())
5331         {
5332             auto pt = lookupTypedef(tid.ident);
5333             if (pt && *pt)
5334             {
5335                 return (*pt).isTypeFunction() !is null;
5336             }
5337         }
5338         return false;
5339     }
5340 
5341     /********************************
5342      * Determine if `id` is a symbol for a Typedef.
5343      * Params:
5344      *  id = possible typedef
5345      * Returns:
5346      *  true if id is a Type
5347      */
5348     bool isTypedef(Identifier id)
5349     {
5350         auto pt = lookupTypedef(id);
5351         return (pt && *pt);
5352     }
5353 
5354     /*******************************
5355      * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5356      * Params: id = identifier for non-typedef symbol
5357      */
5358     void insertIdToTypedefTab(Identifier id)
5359     {
5360         //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5361         if (isTypedef(id))  // if existing typedef
5362         {
5363             /* Add id as null, so we can later distinguish it from a non-null typedef
5364              */
5365             auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5366             tab[cast(void*)id] = cast(void*)null;
5367         }
5368     }
5369 
5370     /*******************************
5371      * Add `id` to typedefTab[]
5372      * Params:
5373      *  id = identifier for typedef symbol
5374      *  t = type of the typedef symbol
5375      */
5376     void insertTypedefToTypedefTab(Identifier id, AST.Type t)
5377     {
5378         //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5379         if (auto tid = t.isTypeIdentifier())
5380         {
5381             // Try to resolve the TypeIdentifier to its type
5382             auto pt = lookupTypedef(tid.ident);
5383             if (pt && *pt)
5384                 t = *pt;
5385         }
5386         auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5387         tab[cast(void*)id] = cast(void*)t;
5388         typedefTab[$ - 1] = cast(void*)tab;
5389     }
5390 
5391     /*********************************
5392      * Lookup id in typedefTab[].
5393      * Returns:
5394      *  if not found, then null.
5395      *  if found, then Type*. Deferencing it will yield null if it is not
5396      *  a typedef, and a type if it is a typedef.
5397      */
5398     AST.Type* lookupTypedef(Identifier id)
5399     {
5400         foreach_reverse (tab; typedefTab[])
5401         {
5402             if (auto pt = cast(void*)id in cast(void*[void*])tab)
5403             {
5404                 return cast(AST.Type*)pt;
5405             }
5406         }
5407         return null; // not found
5408     }
5409 
5410     //}
5411 
5412     /******************************************************************************/
5413     /********************************* Directive Parser ***************************/
5414     //{
5415 
5416     override bool parseSpecialTokenSequence()
5417     {
5418         Token n;
5419         scan(&n);
5420         if (n.value == TOK.int32Literal)
5421         {
5422             poundLine(n, true);
5423             return true;
5424         }
5425         if (n.value == TOK.identifier)
5426         {
5427             if (n.ident == Id.line)
5428             {
5429                 poundLine(n, false);
5430                 return true;
5431             }
5432             else if (defines && (n.ident == Id.define || n.ident == Id.undef))
5433             {
5434                 /* Append this line to `defines`.
5435                  * Not canonicalizing it - assume it already is
5436                  */
5437                 defines.writeByte('#');
5438                 defines.writestring(n.ident.toString());
5439                 skipToNextLine(defines);
5440                 defines.writeByte('\n');
5441                 return true;
5442             }
5443             else if (n.ident == Id.__pragma)
5444             {
5445                 pragmaDirective(scanloc);
5446                 return true;
5447             }
5448             else if (n.ident == Id.ident) // #ident "string"
5449             {
5450                 scan(&n);
5451                 if (n.value == TOK.string_ && n.ptr[0] == '"' && n.postfix == 0)
5452                 {
5453                     /* gcc inserts string into the .comment section in the object file.
5454                      * Just ignore it for now, but can support it later by writing
5455                      * the string to obj_exestr()
5456                      */
5457                     //auto comment = n.ustring;
5458 
5459                     scan(&n);
5460                     if (n.value == TOK.endOfFile || n.value == TOK.endOfLine)
5461                         return true;
5462                 }
5463                 error("\"string\" expected after `#ident`");
5464                 return false;
5465             }
5466         }
5467         if (n.ident != Id.undef)
5468             error("C preprocessor directive `#%s` is not supported", n.toChars());
5469         return false;
5470     }
5471 
5472     /*********************************************
5473      * VC __pragma
5474      * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5475      * Scanner is on the `__pragma`
5476      * Params:
5477      *  startloc = location to use for error messages
5478      */
5479     private void uupragmaDirective(const ref Loc startloc)
5480     {
5481         const loc = startloc;
5482         nextToken();    // move past __pragma
5483         if (token.value != TOK.leftParenthesis)
5484         {
5485             error(loc, "left parenthesis expected to follow `__pragma` instead of `%s`", token.toChars());
5486             nextToken();
5487             return;
5488         }
5489         nextToken();
5490 
5491         if (token.value == TOK.identifier)
5492         {
5493             if (token.ident == Id.pack)
5494                 pragmaPack(startloc, false);
5495             else
5496             {
5497                 nextToken();
5498                 if (token.value == TOK.leftParenthesis)
5499                     cparseParens();
5500             }
5501 
5502         }
5503         else if (token.value == TOK.endOfFile)
5504         {
5505         }
5506         else if (token.value == TOK.rightParenthesis)
5507         {
5508         }
5509         else
5510             error(loc, "unrecognized `__pragma(%s)`", token.toChars());
5511 
5512         if (token.value != TOK.rightParenthesis)
5513         {
5514             error(loc, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token.toChars());
5515             return;
5516         }
5517         nextToken();
5518     }
5519 
5520     /*********************************************
5521      * C11 6.10.6 Pragma directive
5522      * # pragma pp-tokens(opt) new-line
5523      * The C preprocessor sometimes leaves pragma directives in
5524      * the preprocessed output. Ignore them.
5525      * Upon return, p is at start of next line.
5526      */
5527     private void pragmaDirective(const ref Loc loc)
5528     {
5529         Token n;
5530         scan(&n);
5531         if (n.value == TOK.identifier && n.ident == Id.pack)
5532             return pragmaPack(loc, true);
5533         if (n.value != TOK.endOfLine)
5534             skipToNextLine();
5535     }
5536 
5537     /*********
5538      * # pragma pack
5539      * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5540      * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5541      * Scanner is on the `pack`
5542      * Params:
5543      *  startloc = location to use for error messages
5544      *  useScan = use scan() to retrieve next token, instead of nextToken()
5545      */
5546     private void pragmaPack(const ref Loc startloc, bool useScan)
5547     {
5548         const loc = startloc;
5549 
5550         /* Pull tokens from scan() or nextToken()
5551          */
5552         void scan(Token* t)
5553         {
5554             if (useScan)
5555             {
5556                 Lexer.scan(t);
5557             }
5558             else
5559             {
5560                 nextToken();
5561                 *t = token;
5562             }
5563         }
5564 
5565         Token n;
5566         scan(&n);
5567         if (n.value != TOK.leftParenthesis)
5568         {
5569             error(loc, "left parenthesis expected to follow `#pragma pack`");
5570             if (n.value != TOK.endOfLine)
5571                 skipToNextLine();
5572             return;
5573         }
5574 
5575         void closingParen()
5576         {
5577             if (n.value != TOK.rightParenthesis)
5578             {
5579                 error(loc, "right parenthesis expected to close `#pragma pack(`");
5580             }
5581             if (n.value != TOK.endOfLine)
5582                 skipToNextLine();
5583         }
5584 
5585         void setPackAlign(ref const Token t)
5586         {
5587             const n = t.unsvalue;
5588             if (n < 1 || n & (n - 1) || ushort.max < n)
5589                 error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
5590             packalign.set(cast(uint)n);
5591             packalign.setPack(true);
5592         }
5593 
5594         scan(&n);
5595 
5596         if (!records)
5597         {
5598             records = new Array!Identifier;
5599             packs = new Array!structalign_t;
5600         }
5601 
5602         /* # pragma pack ( show )
5603          */
5604         if (n.value == TOK.identifier && n.ident == Id.show)
5605         {
5606             if (packalign.isDefault())
5607                 eSink.warning(startloc, "current pack attribute is default");
5608             else
5609                 eSink.warning(startloc, "current pack attribute is %d", packalign.get());
5610             scan(&n);
5611             return closingParen();
5612         }
5613         /* # pragma pack ( push )
5614          * # pragma pack ( push , identifier )
5615          * # pragma pack ( push , integer )
5616          * # pragma pack ( push , identifier , integer )
5617          */
5618         if (n.value == TOK.identifier && n.ident == Id.push)
5619         {
5620             scan(&n);
5621             Identifier record = null;
5622             if (n.value == TOK.comma)
5623             {
5624                 scan(&n);
5625                 if (n.value == TOK.identifier)
5626                 {
5627                     record = n.ident;
5628                     scan(&n);
5629                     if (n.value == TOK.comma)
5630                     {
5631                         scan(&n);
5632                         if (n.value == TOK.int32Literal)
5633                         {
5634                             setPackAlign(n);
5635                             scan(&n);
5636                         }
5637                         else
5638                             error(loc, "alignment value expected, not `%s`", n.toChars());
5639                     }
5640                 }
5641                 else if (n.value == TOK.int32Literal)
5642                 {
5643                     setPackAlign(n);
5644                     scan(&n);
5645                 }
5646                 else
5647                     error(loc, "alignment value expected, not `%s`", n.toChars());
5648             }
5649             this.records.push(record);
5650             this.packs.push(packalign);
5651             return closingParen();
5652         }
5653         /* # pragma pack ( pop )
5654          * # pragma pack ( pop PopList )
5655          * PopList :
5656          *    , IdentifierOrInteger
5657          *    , IdentifierOrInteger PopList
5658          * IdentifierOrInteger:
5659          *      identifier
5660          *      integer
5661          */
5662         if (n.value == TOK.identifier && n.ident == Id.pop)
5663         {
5664             scan(&n);
5665             size_t len = this.records.length;
5666             if (n.value == TOK.rightParenthesis) // #pragma pack ( pop )
5667             {
5668                 if (len == 0)   // nothing to pop
5669                     return closingParen();
5670 
5671                 this.records.setDim(len - 1);
5672                 this.packs.setDim(len - 1);
5673                 if (len == 1)   // stack is now empty
5674                     packalign.setDefault();
5675                 else
5676                     packalign = (*this.packs)[len - 1];
5677                 return closingParen();
5678             }
5679             while (n.value == TOK.comma)        // #pragma pack ( pop ,
5680             {
5681                 scan(&n);
5682                 if (n.value == TOK.identifier)
5683                 {
5684                     /* pragma pack(pop, identifier
5685                      * Pop until identifier is found, pop that one too, and set
5686                      * alignment to the new top of the stack.
5687                      * If identifier is not found, do nothing.
5688                      */
5689                     for ( ; len; --len)
5690                     {
5691                         if ((*this.records)[len - 1] == n.ident)
5692                         {
5693                             this.records.setDim(len - 1);
5694                             this.packs.setDim(len - 1);
5695                             if (len > 1)
5696                                 packalign = (*this.packs)[len - 2];
5697                             else
5698                                 packalign.setDefault(); // stack empty, use default
5699                             break;
5700                         }
5701                     }
5702                     scan(&n);
5703                 }
5704                 else if (n.value == TOK.int32Literal)
5705                 {
5706                     setPackAlign(n);
5707                     scan(&n);
5708                 }
5709                 else
5710                 {
5711                     error(loc, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n.toChars());
5712                     scan(&n);
5713                 }
5714             }
5715             return closingParen();
5716         }
5717         /* # pragma pack ( integer )
5718          * Sets alignment to integer
5719          */
5720         if (n.value == TOK.int32Literal)
5721         {
5722             setPackAlign(n);
5723             scan(&n);
5724             return closingParen();
5725         }
5726         /* # pragma pack ( )
5727          * Sets alignment to default
5728          */
5729         if (n.value == TOK.rightParenthesis)
5730         {
5731             packalign.setDefault();
5732             return closingParen();
5733         }
5734 
5735         error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
5736         if (n.value != TOK.endOfLine)
5737             skipToNextLine();
5738     }
5739 
5740     //}
5741 
5742     /******************************************************************************/
5743     /********************************* #define Parser *****************************/
5744     //{
5745 
5746     /**
5747      * Go through the #define's in the defines buffer and see what we can convert
5748      * to Dsymbols, which are then appended to symbols[]
5749      */
5750     void addDefines()
5751     {
5752         if (!defines || defines.length < 10)  // minimum length of a #define line
5753             return;
5754         OutBuffer* buf = defines;
5755         defines = null;                 // prevent skipToNextLine() and parseSpecialTokenSequence()
5756                                         // from appending to slice[]
5757         const length = buf.length;
5758         buf.writeByte(0);
5759         auto slice = buf.peekChars()[0 .. length];
5760         resetDefineLines(slice);                // reset lexer
5761 
5762         const(char)* endp = &slice[length - 7];
5763 
5764         size_t[void*] defineTab;    // hash table of #define's turned into Symbol's
5765                                     // indexed by Identifier, returns index into symbols[]
5766                                     // The memory for this is leaked
5767 
5768         void addVar(AST.VarDeclaration v)
5769         {
5770             //printf("addVar() %s\n", v.toChars());
5771             v.isCmacro(true);           // mark it as coming from a C #define
5772             /* If it's already defined, replace the earlier
5773              * definition
5774              */
5775             if (size_t* pd = cast(void*)v.ident in defineTab)
5776             {
5777                 //printf("replacing %s\n", v.toChars());
5778                 (*symbols)[*pd] = v;
5779                 return;
5780             }
5781             defineTab[cast(void*)v.ident] = symbols.length;
5782             symbols.push(v);
5783         }
5784 
5785         Token n;
5786 
5787         while (p < endp)
5788         {
5789             if (p[0 .. 7] == "#define")
5790             {
5791                 p += 7;
5792                 scan(&n);
5793                 //printf("%s\n", n.toChars());
5794                 if (n.value == TOK.identifier)
5795                 {
5796                     auto id = n.ident;
5797                     scan(&n);
5798 
5799                     AST.Type t;
5800 
5801                     switch (n.value)
5802                     {
5803                         case TOK.endOfLine:     // #define identifier
5804                             nextDefineLine();
5805                             continue;
5806 
5807                         case TOK.int32Literal:
5808                         case TOK.charLiteral:       t = AST.Type.tint32;    goto Linteger;
5809                         case TOK.uns32Literal:      t = AST.Type.tuns32;    goto Linteger;
5810                         case TOK.int64Literal:      t = AST.Type.tint64;    goto Linteger;
5811                         case TOK.uns64Literal:      t = AST.Type.tuns64;    goto Linteger;
5812 
5813                         Linteger:
5814                             const intvalue = n.intvalue;
5815                             scan(&n);
5816                             if (n.value == TOK.endOfLine)
5817                             {
5818                                 /* Declare manifest constant:
5819                                  *  enum id = intvalue;
5820                                  */
5821                                 AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
5822                                 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5823                                 addVar(v);
5824                                 nextDefineLine();
5825                                 continue;
5826                             }
5827                             break;
5828 
5829                         case TOK.float32Literal:      t = AST.Type.tfloat32;     goto Lfloat;
5830                         case TOK.float64Literal:      t = AST.Type.tfloat64;     goto Lfloat;
5831                         case TOK.float80Literal:      t = AST.Type.tfloat80;     goto Lfloat;
5832                         case TOK.imaginary32Literal:  t = AST.Type.timaginary32; goto Lfloat;
5833                         case TOK.imaginary64Literal:  t = AST.Type.timaginary64; goto Lfloat;
5834                         case TOK.imaginary80Literal:  t = AST.Type.timaginary80; goto Lfloat;
5835 
5836                         Lfloat:
5837                             const floatvalue = n.floatvalue;
5838                             scan(&n);
5839                             if (n.value == TOK.endOfLine)
5840                             {
5841                                 /* Declare manifest constant:
5842                                  *  enum id = floatvalue;
5843                                  */
5844                                 AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
5845                                 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5846                                 addVar(v);
5847                                 nextDefineLine();
5848                                 continue;
5849                             }
5850                             break;
5851 
5852                         case TOK.string_:
5853                             const str = n.ustring;
5854                             const len = n.len;
5855                             const postfix = n.postfix;
5856                             scan(&n);
5857                             if (n.value == TOK.endOfLine)
5858                             {
5859                                 /* Declare manifest constant:
5860                                  *  enum id = "string";
5861                                  */
5862                                 AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
5863                                 auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5864                                 addVar(v);
5865                                 nextDefineLine();
5866                                 continue;
5867                             }
5868                             break;
5869 
5870                         default:
5871                             break;
5872                     }
5873                 }
5874                 skipToNextLine();
5875             }
5876             else
5877             {
5878                 scan(&n);
5879                 if (n.value != TOK.endOfLine)
5880                 {
5881                     skipToNextLine();
5882                 }
5883             }
5884             nextDefineLine();
5885         }
5886 
5887         defines = buf;
5888     }
5889 
5890     //}
5891 }