1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity low)
4 module dil.semantic.Pass1;
5 
6 import dil.ast.Visitor,
7        dil.ast.Node,
8        dil.ast.Declarations,
9        dil.ast.Expressions,
10        dil.ast.Statements,
11        dil.ast.Types,
12        dil.ast.Parameters;
13 import dil.semantic.Symbol,
14        dil.semantic.Symbols,
15        dil.semantic.Types,
16        dil.semantic.Scope,
17        dil.semantic.Module,
18        dil.semantic.Analysis;
19 import dil.lexer.IdTable;
20 import dil.i18n.Messages;
21 import dil.Compilation,
22        dil.Diagnostics,
23        dil.Enums;
24 import util.Path;
25 import common;
26 
27 /// The first pass is the declaration pass.
28 ///
29 /// The basic task of this class is to traverse the parse tree,
30 /// find all kinds of declarations and add them
31 /// to the symbol tables of their respective scopes.
32 class SemanticPass1 : Visitor
33 {
34   Scope scop; /// The current scope.
35   Module modul; /// The module to be semantically checked.
36   CompilationContext context; /// The compilation context.
37   Module delegate(cstring) importModule; /// Called when importing a module.
38 
39   // Attributes:
40   LinkageType linkageType; /// Current linkage type.
41   Protection protection; /// Current protection attribute.
42   StorageClass storageClass; /// Current storage classes.
43   uint alignSize; /// Current align size.
44 
45   /// Constructs a SemanticPass1 object.
46   /// Params:
47   ///   modul = The module to be processed.
48   ///   context = The compilation context.
49   this(Module modul, CompilationContext context)
50   {
51     this.modul = modul;
52     this.context = new CompilationContext(context);
53     this.alignSize = context.structAlign;
54   }
55 
56   /// Starts processing the module.
57   void run()
58   {
59     assert(modul.root !is null);
60     // Create module scope.
61     scop = new Scope(null, modul);
62     modul.semanticPass = 1;
63     visit(modul.root);
64   }
65 
66   /// Enters a new scope.
67   void enterScope(ScopeSymbol s)
68   {
69     scop = scop.enter(s);
70   }
71 
72   /// Exits the current scope.
73   void exitScope()
74   {
75     scop = scop.exit();
76   }
77 
78   /// Returns true if this is the module scope.
79   bool isModuleScope()
80   {
81     return scop.symbol.isModule();
82   }
83 
84   /// Inserts a symbol into the current scope.
85   void insert(Symbol symbol)
86   {
87     insert(symbol, symbol.name);
88   }
89 
90   /// Inserts a symbol into the current scope.
91   void insert(Symbol symbol, Identifier* name)
92   {
93     auto symX = scop.symbol.lookup(name);
94     if (symX)
95       reportSymbolConflict(symbol, symX, name);
96     else
97       scop.symbol.insert(symbol, name);
98     // Set the current scope symbol as the parent.
99     symbol.parent = scop.symbol;
100   }
101 
102   /// Inserts a symbol into scopeSym.
103   void insert(Symbol symbol, ScopeSymbol scopeSym)
104   {
105     auto symX = scopeSym.lookup(symbol.name);
106     if (symX)
107       reportSymbolConflict(symbol, symX, symbol.name);
108     else
109       scopeSym.insert(symbol, symbol.name);
110     // Set the current scope symbol as the parent.
111     symbol.parent = scopeSym;
112   }
113 
114   /// Inserts a symbol, overloading on the name, into the current scope.
115   void insertOverload(Symbol sym)
116   {
117     auto name = sym.name;
118     auto sym2 = scop.symbol.lookup(name);
119     if (sym2)
120     {
121       if (sym2.isOverloadSet)
122         (cast(OverloadSet)cast(void*)sym2).add(sym);
123       else
124         reportSymbolConflict(sym, sym2, name);
125     }
126     else
127       // Create a new overload set.
128       scop.symbol.insert(new OverloadSet(name, sym.loc), name);
129     // Set the current scope symbol as the parent.
130     sym.parent = scop.symbol;
131   }
132 
133   /// Reports an error: new symbol s1 conflicts with existing symbol s2.
134   void reportSymbolConflict(Symbol s1, Symbol s2, Identifier* name)
135   {
136     auto loc = s2.loc.t.getErrorLocation(modul.filePath());
137     auto locString = modul.cc.diag.format("{}({},{})",
138       loc.filePath, loc.lineNum, loc.colNum);
139     error(s1.loc.t, MID.DeclConflictsWithDecl, name.str, locString);
140   }
141 
142   /// Creates an error report.
143   void error(Token* token, MID mid, ...)
144   {
145     auto location = token.getErrorLocation(modul.filePath());
146     auto msg = modul.cc.diag.formatMsg(mid, _arguments, _argptr);
147     modul.cc.diag ~= new SemanticError(location, msg);
148   }
149 
150   /// Collects info about nodes which have to be evaluated later.
151   static class Deferred
152   {
153     Node node;
154     ScopeSymbol symbol;
155     // Saved attributes.
156     LinkageType linkageType;
157     Protection protection;
158     StorageClass storageClass;
159     uint alignSize;
160   }
161 
162   /// List of mixin, static if, static assert and pragma(msg,...) declarations.
163   ///
164   /// Their analysis must be deferred because they entail
165   /// evaluation of expressions.
166   Deferred[] deferred;
167 
168   /// Adds a deferred node to the list.
169   void addDeferred(Node node)
170   {
171     auto d = new Deferred;
172     d.node = node;
173     d.symbol = scop.symbol;
174     d.linkageType = linkageType;
175     d.protection = protection;
176     d.storageClass = storageClass;
177     d.alignSize = alignSize;
178     deferred ~= d;
179   }
180 
181   private alias D = Declaration; /// A handy alias. Saves typing.
182 
183 override
184 {
185   alias visit = super.visit;
186 
187   D visit(CompoundDecl d)
188   {
189     foreach (decl; d.decls)
190       visitD(decl);
191     return d;
192   }
193 
194   D visit(IllegalDecl)
195   { assert(0, "semantic pass on invalid AST"); return null; }
196 
197   // D visit(EmptyDecl ed)
198   // { return ed; }
199 
200   // D visit(ModuleDecl)
201   // { return null; }
202 
203   D visit(ImportDecl d)
204   {
205     if (importModule is null)
206       return d;
207     foreach (moduleFQNPath; d.getModuleFQNs(dirSep))
208     {
209       auto importedModule = importModule(moduleFQNPath);
210       if (importedModule is null)
211         error(d.begin, MID.CouldntLoadModule, moduleFQNPath ~ ".d");
212       modul.modules ~= importedModule;
213     }
214     return d;
215   }
216 
217   D visit(AliasDecl ad)
218   {
219     return ad;
220   }
221 
222   D visit(TypedefDecl td)
223   {
224     return td;
225   }
226 
227   D visit(EnumDecl d)
228   {
229     if (d.symbol)
230       return d;
231 
232     // Create the symbol.
233     d.symbol = new EnumSymbol(d.nameId, SLoc(d.name ? d.name : d.begin, d));
234 
235     bool isAnonymous = d.symbol.isAnonymous;
236     if (isAnonymous)
237       d.symbol.name = context.tables.idents.genAnonEnumID();
238 
239     insert(d.symbol);
240 
241     auto parentScopeSymbol = scop.symbol;
242     auto enumSymbol = d.symbol;
243     enterScope(d.symbol);
244     // Declare members.
245     foreach (member; d.members)
246     {
247       visitD(member);
248 
249       if (isAnonymous) // Also insert into parent scope if enum is anonymous.
250         insert(member.symbol, parentScopeSymbol);
251 
252       member.symbol.type = enumSymbol.type; // Assign TypeEnum.
253     }
254     exitScope();
255     return d;
256   }
257 
258   D visit(EnumMemberDecl d)
259   {
260     d.symbol = new EnumMemberSymbol(
261       d.name.ident, protection, storageClass, linkageType, SLoc(d.name, d));
262     insert(d.symbol);
263     return d;
264   }
265 
266   D visit(ClassDecl d)
267   {
268     if (d.symbol)
269       return d;
270     // Create the symbol.
271     d.symbol = new ClassSymbol(d.nameId, SLoc(d.name, d));
272     // Insert into current scope.
273     insert(d.symbol);
274     enterScope(d.symbol);
275     // Continue semantic analysis.
276     d.decls && visitD(d.decls);
277     exitScope();
278     return d;
279   }
280 
281   D visit(InterfaceDecl d)
282   {
283     if (d.symbol)
284       return d;
285     // Create the symbol.
286     d.symbol = new InterfaceSymbol(d.nameId, SLoc(d.name, d));
287     // Insert into current scope.
288     insert(d.symbol);
289     enterScope(d.symbol);
290       // Continue semantic analysis.
291       d.decls && visitD(d.decls);
292     exitScope();
293     return d;
294   }
295 
296   D visit(StructDecl d)
297   {
298     if (d.symbol)
299       return d;
300     // Create the symbol.
301     d.symbol = new StructSymbol(d.nameId, SLoc(d.name ? d.name : d.begin, d));
302 
303     if (d.symbol.isAnonymous)
304       d.symbol.name = context.tables.idents.genAnonStructID();
305     // Insert into current scope.
306     insert(d.symbol);
307 
308     enterScope(d.symbol);
309       // Continue semantic analysis.
310       d.decls && visitD(d.decls);
311     exitScope();
312 
313     if (d.symbol.isAnonymous)
314       // Insert members into parent scope as well.
315       foreach (member; d.symbol.members)
316         insert(member);
317     return d;
318   }
319 
320   D visit(UnionDecl d)
321   {
322     if (d.symbol)
323       return d;
324     // Create the symbol.
325     d.symbol = new UnionSymbol(d.nameId, SLoc(d.name ? d.name : d.begin, d));
326 
327     if (d.symbol.isAnonymous)
328       d.symbol.name = context.tables.idents.genAnonUnionID();
329 
330     // Insert into current scope.
331     insert(d.symbol);
332 
333     enterScope(d.symbol);
334       // Continue semantic analysis.
335       d.decls && visitD(d.decls);
336     exitScope();
337 
338     if (d.symbol.isAnonymous)
339       // Insert members into parent scope as well.
340       foreach (member; d.symbol.members)
341         insert(member);
342     return d;
343   }
344 
345   D visit(ConstructorDecl d)
346   {
347     auto func = new FunctionSymbol(Ident.Ctor, SLoc(d.begin, d));
348     insertOverload(func);
349     return d;
350   }
351 
352   D visit(StaticCtorDecl d)
353   {
354     auto func = new FunctionSymbol(Ident.Ctor, SLoc(d.begin, d));
355     insertOverload(func);
356     return d;
357   }
358 
359   D visit(DestructorDecl d)
360   {
361     auto func = new FunctionSymbol(Ident.Dtor, SLoc(d.begin, d));
362     insertOverload(func);
363     return d;
364   }
365 
366   D visit(StaticDtorDecl d)
367   {
368     auto func = new FunctionSymbol(Ident.Dtor, SLoc(d.begin, d));
369     insertOverload(func);
370     return d;
371   }
372 
373   D visit(FunctionDecl d)
374   {
375     auto func = new FunctionSymbol(d.name.ident, SLoc(d.name, d));
376     insertOverload(func);
377     return d;
378   }
379 
380   D visit(VariablesDecl vd)
381   {
382     // Error if we are in an interface.
383     if (scop.symbol.isInterface &&
384         !(vd.isStatic || vd.isConst || vd.isManifest))
385       return error(vd.begin, MID.InterfaceCantHaveVariables), vd;
386 
387     // Insert variable symbols in this declaration into the symbol table.
388     vd.variables = new VariableSymbol[vd.names.length];
389     foreach (i, name; vd.names)
390     {
391       auto variable = new VariableSymbol(name.ident, protection, storageClass,
392         linkageType, SLoc(name, vd));
393       variable.value = vd.inits[i];
394       vd.variables[i] = variable;
395       insert(variable);
396     }
397     return vd;
398   }
399 
400   D visit(InvariantDecl d)
401   {
402     auto func = new FunctionSymbol(Ident.InvariantFn, SLoc(d.begin, d));
403     insert(func);
404     return d;
405   }
406 
407   D visit(UnittestDecl d)
408   {
409     auto func = new FunctionSymbol(Ident.UnittestFn, SLoc(d.begin, d));
410     insertOverload(func);
411     return d;
412   }
413 
414   D visit(DebugDecl d)
415   {
416     if (d.isSpecification)
417     { // debug = Id | Int
418       if (!isModuleScope())
419         error(d.begin, MID.DebugSpecModuleLevel, d.spec.text);
420       else if (d.spec.kind == TOK.Identifier)
421         context.addDebugId(d.spec.ident.str);
422       else
423         context.debugLevel = d.spec.uint_;
424     }
425     else
426     { // debug ( Condition )
427       if (debugBranchChoice(d.cond, context))
428         d.compiledDecls = d.decls;
429       else
430         d.compiledDecls = d.elseDecls;
431       d.compiledDecls && visitD(d.compiledDecls);
432     }
433     return d;
434   }
435 
436   D visit(VersionDecl d)
437   {
438     if (d.isSpecification)
439     { // version = Id | Int
440       if (!isModuleScope())
441         error(d.begin, MID.VersionSpecModuleLevel, d.spec.text);
442       else if (d.spec.kind == TOK.Identifier)
443         context.addVersionId(d.spec.ident.str);
444       else
445         context.versionLevel = d.spec.uint_;
446     }
447     else
448     { // version ( Condition )
449       if (versionBranchChoice(d.cond, context))
450         d.compiledDecls = d.decls;
451       else
452         d.compiledDecls = d.elseDecls;
453       d.compiledDecls && visitD(d.compiledDecls);
454     }
455     return d;
456   }
457 
458   D visit(TemplateDecl d)
459   {
460     if (d.symbol)
461       return d;
462     // Create the symbol.
463     d.symbol = new TemplateSymbol(d.nameId, SLoc(d.name, d));
464     // Insert into current scope.
465     insertOverload(d.symbol);
466     return d;
467   }
468 
469   D visit(NewDecl d)
470   {
471     auto func = new FunctionSymbol(Ident.NewFn, SLoc(d.begin, d));
472     insert(func);
473     return d;
474   }
475 
476   D visit(DeleteDecl d)
477   {
478     auto func = new FunctionSymbol(Ident.DeleteFn, SLoc(d.begin, d));
479     insert(func);
480     return d;
481   }
482 
483   // Attributes:
484 
485   D visit(ProtectionDecl d)
486   {
487     auto saved = protection; // Save.
488     protection = d.prot; // Set.
489     visitD(d.decls);
490     protection = saved; // Restore.
491     return d;
492   }
493 
494   D visit(StorageClassDecl d)
495   {
496     auto saved = storageClass; // Save.
497     storageClass = d.stc; // Set.
498     visitD(d.decls);
499     storageClass = saved; // Restore.
500     return d;
501   }
502 
503   D visit(LinkageDecl d)
504   {
505     auto saved = linkageType; // Save.
506     linkageType = d.linkageType; // Set.
507     visitD(d.decls);
508     linkageType = saved; // Restore.
509     return d;
510   }
511 
512   D visit(AlignDecl d)
513   {
514     auto saved = alignSize; // Save.
515     alignSize = d.size; // Set.
516     visitD(d.decls);
517     alignSize = saved; // Restore.
518     return d;
519   }
520 
521   // Deferred declarations:
522 
523   D visit(StaticAssertDecl d)
524   {
525     addDeferred(d);
526     return d;
527   }
528 
529   D visit(StaticIfDecl d)
530   {
531     addDeferred(d);
532     return d;
533   }
534 
535   D visit(MixinDecl d)
536   {
537     addDeferred(d);
538     return d;
539   }
540 
541   D visit(PragmaDecl d)
542   {
543     if (d.name.ident is Ident.msg)
544       addDeferred(d);
545     else
546     {
547       pragmaSemantic(scop, d.begin, d.name.ident, d.args);
548       visitD(d.decls);
549     }
550     return d;
551   }
552 } // override
553 }