1 // This file is part of Visual D
2 //
3 // Visual D integrates the D programming language into Visual Studio
4 // Copyright (c) 2010-2011 by Rainer Schuetze, All Rights Reserved
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
8 
9 module vdc.ast.decl;
10 
11 import vdc.util;
12 import vdc.lexer;
13 import vdc.semantic;
14 import vdc.logger;
15 import vdc.interpret;
16 
17 import vdc.ast.node;
18 import vdc.ast.expr;
19 import vdc.ast.misc;
20 import vdc.ast.aggr;
21 import vdc.ast.tmpl;
22 import vdc.ast.stmt;
23 import vdc.ast.type;
24 import vdc.ast.mod;
25 import vdc.ast.writer;
26 
27 import std.conv;
28 import stdext.util;
29 
30 version(obsolete)
31 {
32 //Declaration:
33 //    alias Decl
34 //    Decl
35 class Declaration : Node
36 {
37     mixin ForwardCtor!();
38 }
39 }
40 
41 // AliasDeclaration:
42 //    [Decl]
43 class AliasDeclaration : Node
44 {
45     mixin ForwardCtor!();
46 
47     override void toD(CodeWriter writer)
48     {
49         if(writer.writeDeclarations)
50             writer("alias ", getMember(0));
51     }
52     override void addSymbols(Scope sc)
53     {
54         getMember(0).addSymbols(sc);
55     }
56 }
57 
58 //Decl:
59 //    attributes annotations [Type Declarators FunctionBody_opt]
60 class Decl : Node
61 {
62     mixin ForwardCtor!();
63 
64     bool hasSemi;
65     bool isAlias;
66 
67     Type getType() { return getMember!Type(0); }
68     Declarators getDeclarators() { return getMember!Declarators(1); }
69     FunctionBody getFunctionBody() { return getMember!FunctionBody(2); }
70 
71     override Decl clone()
72     {
73         Decl n = static_cast!Decl(super.clone());
74         n.hasSemi = hasSemi;
75         n.isAlias = isAlias;
76         return n;
77     }
78 
79     override bool compare(const(Node) n) const
80     {
81         if(!super.compare(n))
82             return false;
83 
84         auto tn = static_cast!(typeof(this))(n);
85         return tn.hasSemi == hasSemi
86             && tn.isAlias == isAlias;
87     }
88 
89     override void toD(CodeWriter writer)
90     {
91         if(isAlias)
92             writer(TOK_alias, " ");
93         writer.writeAttributesAndAnnotations(attr, annotation);
94 
95         writer(getType(), " ", getDeclarators());
96         bool semi = true;
97         if(auto fn = getFunctionBody())
98         {
99             if(writer.writeImplementations)
100             {
101                 writer.nl;
102                 writer(fn);
103                 semi = hasSemi;
104             }
105         }
106         if(semi)
107         {
108             writer(";");
109             writer.nl();
110         }
111     }
112 
113     override void toC(CodeWriter writer)
114     {
115         bool addExtern = false;
116         if(!isAlias && writer.writeDeclarations && !(attr & Attr_ExternC))
117         {
118             Node p = parent;
119             while(p && !cast(Aggregate) p && !cast(TemplateDeclaration) p && !cast(Statement) p)
120                 p = p.parent;
121 
122             if(!p)
123                 addExtern = true;
124         }
125         if(auto fn = getFunctionBody())
126         {
127             if(writer.writeReferencedOnly && getDeclarators().getDeclarator(0).semanticSearches == 0)
128                 return;
129 
130             writer.nl;
131             if(isAlias)
132                 writer(TOK_alias, " ");
133             writer.writeAttributesAndAnnotations(attr | (addExtern ? Attr_Extern : 0), annotation);
134 
135             bool semi = true;
136             writer(getType(), " ", getDeclarators());
137             if(writer.writeImplementations)
138             {
139                 writer.nl;
140                 writer(fn);
141                 semi = hasSemi;
142             }
143             if(semi)
144             {
145                 writer(";");
146                 writer.nl();
147             }
148         }
149         else
150         {
151             foreach(i, d; getDeclarators().members)
152             {
153                 if(writer.writeReferencedOnly && getDeclarators().getDeclarator(i).semanticSearches == 0)
154                     continue;
155 
156                 if(isAlias)
157                     writer(TOK_alias, " ");
158                 writer.writeAttributesAndAnnotations(attr | (addExtern ? Attr_Extern : 0), annotation);
159 
160                 writer(getType(), " ", d, ";");
161                 writer.nl();
162             }
163         }
164     }
165 
166     override Type calcType()
167     {
168         return getType().calcType();
169     }
170 
171     override void addSymbols(Scope sc)
172     {
173         getDeclarators().addSymbols(sc);
174     }
175 
176     override void _semantic(Scope sc)
177     {
178         bool isTemplate = false;
179         if(auto fn = getFunctionBody())
180         {
181             // if it is a function declaration, create a new scope including function parameters
182             scop = sc.push(scop);
183             scop.node = this;
184             if(auto decls = getDeclarators())
185                 if(auto decl = decls.getDeclarator(0))
186                 {
187                     foreach(m; decl.members) // template parameters and function parameters and constraint
188                     {
189                         if(cast(TemplateParameterList) m)
190                         {
191                             // it does not make sense to add symbols for unexpanded templates
192                             isTemplate = decl._isTemplate = true;
193                             break;
194                         }
195                         m.addSymbols(scop);
196                     }
197                 }
198 
199             if(!isTemplate)
200                 super._semantic(scop);
201             //fn.semantic(scop);
202             sc = scop.pop();
203         }
204         else
205             super._semantic(sc);
206     }
207 }
208 
209 //Declarators:
210 //    [DeclaratorInitializer|Declarator...]
211 class Declarators : Node
212 {
213     mixin ForwardCtor!();
214 
215     Declarator getDeclarator(size_t n)
216     {
217         if(auto decl = cast(Declarator) getMember(n))
218             return decl;
219 
220         return getMember!DeclaratorInitializer(n).getDeclarator();
221     }
222 
223     override void toD(CodeWriter writer)
224     {
225         writer(getMember(0));
226         foreach(decl; members[1..$])
227             writer(", ", decl);
228     }
229     override void addSymbols(Scope sc)
230     {
231         foreach(decl; members)
232             decl.addSymbols(sc);
233     }
234 }
235 
236 //DeclaratorInitializer:
237 //    [Declarator Initializer_opt]
238 class DeclaratorInitializer : Node
239 {
240     mixin ForwardCtor!();
241 
242     Declarator getDeclarator() { return getMember!Declarator(0); }
243     Expression getInitializer() { return getMember!Expression(1); }
244 
245     override void toD(CodeWriter writer)
246     {
247         writer(getMember(0));
248         if(Expression expr = getInitializer())
249         {
250             if(expr.getPrecedence() <= PREC.assign)
251                 writer(" = (", expr, ")");
252             else
253                 writer(" = ", getMember(1));
254         }
255     }
256 
257     override Type calcType()
258     {
259         return getInitializer().calcType();
260     }
261 
262     override void addSymbols(Scope sc)
263     {
264         getDeclarator().addSymbols(sc);
265     }
266 }
267 
268 // unused
269 class DeclaratorIdentifierList : Node
270 {
271     mixin ForwardCtor!();
272 
273     override void toD(CodeWriter writer)
274     {
275         assert(false);
276     }
277 }
278 
279 // unused
280 class DeclaratorIdentifier : Node
281 {
282     mixin ForwardCtor!();
283 
284     override void toD(CodeWriter writer)
285     {
286         assert(false);
287     }
288 }
289 
290 class Initializer : Expression
291 {
292     mixin ForwardCtor!();
293 }
294 
295 //Declarator:
296 //    Identifier [DeclaratorSuffixes...]
297 class Declarator : Identifier, CallableNode
298 {
299     mixin ForwardCtorTok!();
300 
301     Type type;
302     Value value;
303     Node aliasTo;
304     ParameterList parameterList;
305     bool isAlias;
306     bool isRef;
307     bool needsContext;
308     bool _isTemplate;
309     bool _inReinit;
310     TemplateInstantiation[] tmpl;
311 
312     override Declarator clone()
313     {
314         Declarator n = static_cast!Declarator(super.clone());
315         n.type = type;
316         return n;
317     }
318 
319     final Declarator cloneDeclaratorShallow()
320     {
321         auto decl = static_cast!Declarator(_cloneShallow());
322         decl.ident = ident;
323         if(parameterList)
324             decl.addMember(parameterList.clone());
325         // copy info from applySuffixes
326         return decl;
327     }
328 
329     Expression getInitializer()
330     {
331         if(auto di = cast(DeclaratorInitializer) parent)
332             return di.getInitializer();
333         return null;
334     }
335 
336     override void toD(CodeWriter writer)
337     {
338         super.toD(writer);
339         foreach(m; members) // template parameters and function parameters and constraint
340             writer(m);
341     }
342 
343     bool _isAlias()
344     {
345         for(Node p = parent; p; p = p.parent)
346             if(auto decl = cast(Decl) p)
347                 return decl.isAlias;
348             else if(auto pdecl = cast(ParameterDeclarator) p)
349                 break;
350         return false;
351     }
352     // returns 0 for never, 1 for non-static statement declaration, 2 for yes
353     bool _needsContext()
354     {
355         for(Node p = parent; p; p = p.parent)
356             if(auto decl = cast(Decl) p)
357             {
358                 if(!decl.parent || cast(Module)decl.parent)
359                     return false;
360                 if (decl.attr & (Attr_Static | Attr_Shared | Attr_Gshared))
361                     return false;
362                 return true;
363             }
364             else if(auto pdecl = cast(ParameterDeclarator) p)
365                 return true;
366 
367         return false;
368     }
369 
370     override bool isTemplate()
371     {
372         return _isTemplate;
373     }
374 
375     override Node expandTemplate(Scope sc, TemplateArgumentList args)
376     {
377         assert(_isTemplate);
378 
379         calcType(); // ensure suffixes applied
380         TemplateParameterList tpl = getTemplateParameterList();
381 
382         ArgMatch[] vargs = matchTemplateArgs(ident, sc, args, tpl);
383         if(vargs is null)
384             return this;
385 
386         if(auto impl = getTemplateInstantiation(vargs))
387             return impl.getDeclarator();
388 
389         // new instantiation has template parameters as parameterlist and contains
390         //  a copy of the function declaration without template arguments
391         auto tmpl = new TemplateInstantiation(this, vargs);
392         addMember(tmpl); // add as suffix
393         tmpl.semantic(getScope());
394         return tmpl.getDeclarator();
395     }
396 
397     TemplateInstantiation getTemplateInstantiation(ArgMatch[] args)
398     {
399         return null;
400     }
401 
402     TemplateParameterList getTemplateParameterList()
403     {
404         for(int m = 0; m < members.length; m++)
405         {
406             auto member = members[0];
407             if(auto tpl = cast(TemplateParameterList) member)
408                 return tpl;
409         }
410         return null;
411     }
412 
413     override void addSymbols(Scope sc)
414     {
415         sc.addSymbol(ident, this);
416     }
417 
418     Type applySuffixes(Type t)
419     {
420         // assumed to be only called once by calcType
421         isAlias = _isAlias();
422         needsContext = _needsContext();
423 
424         // template parameters and function parameters and constraint
425         size_t mlen = members.length;
426         for(int m = 0; m < members.length; m++)
427         {
428             auto member = members[m];
429             if(auto pl = cast(ParameterList) member)
430             {
431                 auto tf = needsContext ? new TypeDelegate(pl.id, pl.span) : new TypeFunction(pl.id, pl.span);
432                 tf.funcDecl = this;
433                 tf.returnType = t; // need clones?
434                 tf.paramList = pl;
435                 tf.scop = getScope(); // not fully added to node tree
436                 t = tf;
437                 parameterList = pl;
438             }
439             else if(auto sa = cast(SuffixArray) member)
440             {
441                 auto idx = sa.getMember(0);
442                 if(auto tidx = cast(Type) idx) // TODO: need to resolve identifiers?
443                 {
444                     auto taa = new TypeAssocArray(sa.id, sa.span);
445                     taa.setNextType(t);  // need clones?
446                     taa.keyType = tidx;
447                     t = taa;
448                 }
449                 else
450                 {
451                     auto tsa = new TypeStaticArray(sa.id, sa.span);
452                     tsa.setNextType(t);  // need clones?
453                     tsa.dimExpr = static_cast!Expression(idx);
454                     t = tsa;
455                 }
456                 t.scop = getScope(); // not fully added to node tree
457             }
458             else if(auto sda = cast(SuffixDynamicArray) member)
459             {
460                 auto tda = new TypeDynamicArray(sda.id, sda.span);
461                 tda.addMember(t.clone());
462                 t = tda;
463                 tda.scop = getScope(); // not fully added to node tree
464             }
465             // TODO: slice suffix? template parameters, constraint
466         }
467 
468         //// after removal, limit span to remaining members
469         //if(mlen > members.length)
470         //{
471         //    fulspan = span;
472         //    foreach(m; members)
473         //        extendSpan(m.fulspan);
474         //}
475         return t;
476     }
477 
478     override Node resolve()
479     {
480         return this;
481     }
482 
483     override Type calcType()
484     {
485         if(type)
486             return type;
487         if(aliasTo)
488             return aliasTo.calcType();
489 
490         for(Node p = parent; p; p = p.parent)
491         {
492             if(auto decl = cast(Decl) p)
493             {
494                 type = decl.getType();
495                 if(cast(AutoType)type)
496                 {
497                     if(auto expr = getInitializer())
498                         type = expr.calcType();
499                 }
500                 if(type)
501                 {
502                     type = applySuffixes(type);
503                     type = type.calcType();
504                 }
505                 return type;
506             }
507             else if(auto pdecl = cast(ParameterDeclarator) p)
508             {
509                 type = pdecl.getType();
510                 if(type)
511                 {
512                     type = applySuffixes(type);
513                     type = type.calcType();
514                 }
515                 return type;
516             }
517         }
518         type = semanticErrorType("cannot find Declarator type");
519         return type;
520     }
521 
522     override Value interpret(Context sc)
523     {
524         if(value)
525             return value;
526         if(aliasTo)
527             return aliasTo.interpret(sc); // TODO: alias not restricted to types
528         Type type = calcType();
529         if(isAlias)
530             return type.interpret(sc); // TODO: alias not restricted to types
531         else if(needsContext)
532         {
533             if(!sc)
534                 return semanticErrorValue("evaluating ", ident, " needs context pointer");
535             if(auto v = sc.getValue(this))
536                 return v;
537         }
538         return interpretReinit(sc);
539     }
540 
541     Value interpretReinit(Context sc)
542     {
543         if(_inReinit)
544             return semanticErrorValue("initializing ", ident, " refers to itself");
545         _inReinit = true;
546         scope(exit) _inReinit = false;
547 
548         if(aliasTo)
549             return aliasTo.interpret(sc); // TODO: alias not restricted to types
550         Type type = calcType();
551         if(isAlias)
552             return type.interpret(sc); // TODO: alias not restricted to types
553         else if(needsContext)
554         {
555             if(!sc)
556                 return semanticErrorValue("evaluating ", ident, " needs context pointer");
557             Value v;
558             if(auto expr = getInitializer())
559             {
560                 v = expr.interpret(sc);
561                 if(!v.getType().compare(type))
562                     type.createValue(sc, v);
563             }
564             else
565                 v = type.createValue(sc, null);
566             debug v.ident = ident;
567             sc.setValue(this, v);
568             return v;
569         }
570         else if(auto expr = getInitializer())
571             value = type.createValue(sc, expr.interpret(sc));
572         else
573             value = type.createValue(sc, null);
574         debug value.ident = ident;
575         return value;
576     }
577 
578     override ParameterList getParameterList()
579     {
580         calcType();
581         return parameterList;
582     }
583 
584     override FunctionBody getFunctionBody()
585     {
586         for(Node p = parent; p; p = p.parent)
587             if(auto decl = cast(Decl) p)
588                 if(auto fbody = decl.getFunctionBody())
589                     return fbody;
590         return null;
591     }
592 
593     override Value interpretCall(Context sc)
594     {
595         logInfo("calling %s", ident);
596 
597         if(auto fbody = getFunctionBody())
598             return fbody.interpret(sc);
599 
600         return semanticErrorValue(ident, " is not an interpretable function");
601     }
602 }
603 
604 //TemplateInstantiation:
605 //    [ParameterList Decl]
606 class TemplateInstantiation : Node
607 {
608     ArgMatch[] args;
609     Declarator dec;
610 
611     override ParameterList getParameterList() { return getMember!ParameterList(0); }
612     Declarator getDeclarator() { return dec; }
613 
614     this(Declarator ddec, ArgMatch[] vargs)
615     {
616         Decl decl = new Decl;
617         dec = ddec.cloneDeclaratorShallow();
618         args = vargs;
619 
620         for(Node p = ddec.parent; p; p = p.parent)
621             if(auto ddecl = cast(Decl) p)
622             {
623                 if(auto type = ddecl.getType())
624                     decl.addMember(type.clone());
625                 Declarators decs = new Declarators;
626                 decs.addMember(dec);
627                 decl.addMember(decs);
628                 if(auto fbody = ddecl.getFunctionBody())
629                     decl.addMember(fbody.clone());
630                 break;
631             }
632         assert(decl.members.length > 0);
633 
634         if(auto tpl = ddec.getTemplateParameterList())
635             addMember(createTemplateParameterList(vargs));
636         else
637             addMember(new ParameterList);
638         addMember(decl);
639         logInfo("created template instance of ", dec.ident, " with args ", vargs);
640     }
641 
642     override void toD(CodeWriter writer)
643     {
644         // suppress output (add a flag to the writer to enable output of expanded template?)
645     }
646 
647     override void addSymbols(Scope sc)
648     {
649         getParameterList().addSymbols(sc);
650     }
651 
652     override bool createsScope() const { return true; }
653 
654     override void _semantic(Scope sc)
655     {
656         sc = enterScope(sc);
657         super._semantic(sc);
658         sc = scop.pop();
659     }
660 }
661 
662 //IdentifierList:
663 //    [IdentifierOrTemplateInstance...]
664 class IdentifierList : Node
665 {
666     mixin ForwardCtor!();
667 
668     bool global;
669 
670     // semantic data
671     Node resolved;
672 
673     override IdentifierList clone()
674     {
675         IdentifierList n = static_cast!IdentifierList(super.clone());
676         n.global = global;
677         return n;
678     }
679 
680     override bool compare(const(Node) n) const
681     {
682         if(!super.compare(n))
683             return false;
684 
685         auto tn = static_cast!(typeof(this))(n);
686         return tn.global == global;
687     }
688 
689     Node doResolve(bool isMixin)
690     {
691         // TODO: does not work for package qualified symbols
692         Scope sc;
693         if(global)
694             sc = getModule().scop;
695         else if(auto bc = cast(BaseClass) parent)
696             if(auto clss = bc.parent)
697                 if(auto p = clss.parent)
698                     sc = p.getScope();
699         if(!sc)
700             sc = getScope();
701 
702         Node res;
703         for(int m = 0; sc && m < members.length; m++)
704         {
705             auto id = getMember!Identifier(m);
706             res = sc.resolveWithTemplate(id.ident, sc, id);
707             sc = (res ? res.getScope() : null);
708         }
709         if(!sc && !isMixin)
710             res = semanticErrorType("cannot resolve ", writeD(this));
711         return res;
712     }
713 
714     override Node resolve()
715     {
716         if(resolved)
717             return resolved;
718 
719         resolved = doResolve(false);
720         return resolved;
721     }
722 
723     override Type calcType()
724     {
725         if(Node n = resolve())
726             return n.calcType();
727         return semanticErrorType("cannot resolve type of ", writeD(this));
728     }
729 
730     override Value interpret(Context sc)
731     {
732         if(Node n = resolve())
733             return n.interpret(sc);
734         return semanticErrorValue("cannot resolve ", writeD(this));
735     }
736 
737     override void _semantic(Scope sc)
738     {
739         resolve();
740     }
741 
742     override void toD(CodeWriter writer)
743     {
744         if(global)
745             writer(".");
746         writer.writeArray(members, ".");
747     }
748 }
749 
750 class Identifier : Node
751 {
752     string ident;
753 
754     this() {} // default constructor needed for clone()
755 
756     this(Token tok)
757     {
758         super(tok);
759         ident = tok.txt;
760     }
761 
762     override Identifier clone()
763     {
764         Identifier n = static_cast!Identifier(super.clone());
765         n.ident = ident;
766         return n;
767     }
768 
769     override bool compare(const(Node) n) const
770     {
771         if(!super.compare(n))
772             return false;
773 
774         auto tn = static_cast!(typeof(this))(n);
775         return tn.ident == ident;
776     }
777 
778     override void toD(CodeWriter writer)
779     {
780         writer.writeIdentifier(ident);
781     }
782 
783     override ArgumentList getFunctionArguments()
784     {
785         if(parent)
786             return parent.getFunctionArguments();
787         return null;
788     }
789 
790     override Node resolve()
791     {
792         if(parent)
793             return parent.resolve();
794         return super.resolve();
795     }
796 
797     override Type calcType()
798     {
799         if(auto p = cast(IdentifierList) parent)
800             return p.calcType();
801         if(auto p = cast(IdentifierExpression) parent)
802             return p.calcType();
803         if(auto p = cast(DotExpression) parent)
804             return p.calcType();
805         if(auto p = cast(ModuleFullyQualifiedName) parent)
806         {
807         }
808         if(auto p = cast(ForeachType) parent)
809             return parent.calcType();
810         return super.calcType();
811     }
812 
813     override Value interpret(Context ctx)
814     {
815         if(auto p = cast(IdentifierList) parent)
816             return p.interpret(ctx);
817         if(auto p = cast(IdentifierExpression) parent)
818             return p.interpret(ctx);
819         if(auto p = cast(DotExpression) parent)
820             return p.interpret(ctx);
821         if(auto p = cast(ModuleFullyQualifiedName) parent)
822         {
823         }
824         if(auto p = cast(ForeachType) parent)
825             return parent.interpret(ctx);
826         return super.interpret(ctx);
827     }
828 }
829 
830 //ParameterList:
831 //    [Parameter...] attributes
832 class ParameterList : Node
833 {
834     mixin ForwardCtor!();
835 
836     Parameter getParameter(size_t i) { return getMember!Parameter(i); }
837 
838     bool varargs;
839     bool anonymous_varargs;
840 
841     override ParameterList clone()
842     {
843         ParameterList n = static_cast!ParameterList(super.clone());
844         n.varargs = varargs;
845         n.anonymous_varargs = anonymous_varargs;
846         return n;
847     }
848 
849     override bool compare(const(Node) n) const
850     {
851         if(!super.compare(n))
852             return false;
853 
854         auto tn = static_cast!(typeof(this))(n);
855         return tn.varargs == varargs && tn.anonymous_varargs == anonymous_varargs;
856     }
857 
858     override void toD(CodeWriter writer)
859     {
860         writer("(");
861         writer.writeArray(members);
862         if(anonymous_varargs)
863             writer(", ...");
864         else if(varargs)
865             writer("...");
866         writer(")");
867         if(attr)
868         {
869             writer.writeAttributesAndAnnotations(attr, annotation, true);
870         }
871     }
872 
873     override void addSymbols(Scope sc)
874     {
875         foreach(m; members)
876             m.addSymbols(sc);
877     }
878 }
879 
880 //Parameter:
881 //    io [ParameterDeclarator Expression_opt]
882 class Parameter : Node
883 {
884     mixin ForwardCtor!();
885 
886     TokenId io;
887 
888     ParameterDeclarator getParameterDeclarator() { return getMember!ParameterDeclarator(0); }
889     Expression getInitializer() { return getMember!Expression(1); }
890 
891     override Parameter clone()
892     {
893         Parameter n = static_cast!Parameter(super.clone());
894         n.io = io;
895         return n;
896     }
897 
898     override bool compare(const(Node) n) const
899     {
900         if(!super.compare(n))
901             return false;
902 
903         auto tn = static_cast!(typeof(this))(n);
904         return tn.io == io;
905     }
906 
907     override void toD(CodeWriter writer)
908     {
909         if(io)
910             writer(io, " ");
911         writer(getMember(0));
912         if(members.length > 1)
913             writer(" = ", getMember(1));
914     }
915 
916     override void addSymbols(Scope sc)
917     {
918         getParameterDeclarator().addSymbols(sc);
919     }
920 
921     override Type calcType()
922     {
923         return getParameterDeclarator().calcType();
924     }
925 }
926 
927 //ParameterDeclarator:
928 //    attributes [Type Declarator]
929 class ParameterDeclarator : Node
930 {
931     mixin ForwardCtor!();
932 
933     Type getType() { return getMember!Type(0); }
934     Declarator getDeclarator() { return members.length > 1 ? getMember!Declarator(1) : null; }
935 
936     override void toD(CodeWriter writer)
937     {
938         writer.writeAttributesAndAnnotations(attr, annotation);
939         writer(getType());
940         if(auto decl = getDeclarator())
941             writer(" ", decl);
942     }
943 
944     override void addSymbols(Scope sc)
945     {
946         if (auto decl = getDeclarator())
947             decl.addSymbols(sc);
948     }
949 
950     override Type calcType()
951     {
952         if (auto decl = getDeclarator())
953             return decl.calcType();
954         return getType().calcType();
955     }
956 }