1 /**
2  * A scoped C++ namespace symbol
3  *
4  * D supports the following syntax to declare symbol(s) as being part of a
5  * C++ namespace:
6  * ---
7  * extern (C++, "myNamespace") { /+ Symbols +/ }       // String variant
8  * extern (C++, SomeNamespace) { /+ Other symbols +/ } // Identifier variant
9  * ---
10  * The first form is an attribute and only affects mangling, and is implemented
11  * in `dmd.attrib`.
12  * The second form introduces a named scope and allows symbols to be refered
13  * to with or without the namespace name, much like a named template mixin,
14  * and is implemented in this module.
15  * ---
16  * extern (C++, Basket)
17  * {
18  *     struct StrawBerry;
19  *     void swapFood (Strawberry* f1, Strawberry* f2);
20  * }
21  * void main ()
22  * {
23  *     Basket.StrawBerry fruit1;
24  *     StrawBerry fruit2;
25  *     Basket.swapFood(fruit1, fruit2);
26  *     swapFood(fruit1, fruit2);
27  * }
28  * ---
29  * Hence the `Nspace` symbol implements the usual `ScopeDsymbol` semantics.
30  *
31  * Note that it implies `extern(C++)` so it cannot be used as a generic
32  * named scope. Additionally, `Nspace` with the same `Identifier` can be
33  * defined in different module (as C++ allows a namespace to be spread accross
34  * translation units), but symbols in it should be considered
35  * part of the same scope. Lastly, not all possible C++ namespace names
36  * are valid D identifier.
37  *
38  * See_Also:    https://github.com/dlang/dmd/pull/10031
39  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
40  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
41  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
42  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/nspace.d, _nspace.d)
43  * Documentation:  https://dlang.org/phobos/dmd_nspace.html
44  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nspace.d
45  */
46 
47 module dmd.nspace;
48 
49 import dmd.aggregate;
50 import dmd.arraytypes;
51 import dmd.dscope;
52 import dmd.dsymbol;
53 import dmd.dsymbolsem;
54 import dmd.expression;
55 import dmd.globals;
56 import dmd.identifier;
57 import dmd.visitor;
58 import core.stdc.stdio;
59 
60 private enum LOG = false;
61 
62 /// Ditto
63 extern (C++) final class Nspace : ScopeDsymbol
64 {
65     /**
66      * Namespace identifier resolved during semantic.
67      */
68     Expression identExp;
69 
70     extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members)
71     {
72         super(loc, ident);
73         //printf("Nspace::Nspace(ident = %s)\n", ident.toChars());
74         this.members = members;
75         this.identExp = identExp;
76     }
77 
78     override Nspace syntaxCopy(Dsymbol s)
79     {
80         auto ns = new Nspace(loc, ident, identExp, null);
81         ScopeDsymbol.syntaxCopy(ns);
82         return ns;
83     }
84 
85     override void addMember(Scope* sc, ScopeDsymbol sds)
86     {
87         ScopeDsymbol.addMember(sc, sds);
88 
89         if (members)
90         {
91             if (!symtab)
92                 symtab = new DsymbolTable();
93             // The namespace becomes 'imported' into the enclosing scope
94             for (Scope* sce = sc; 1; sce = sce.enclosing)
95             {
96                 ScopeDsymbol sds2 = sce.scopesym;
97                 if (sds2)
98                 {
99                     sds2.importScope(this, Visibility(Visibility.Kind.public_));
100                     break;
101                 }
102             }
103             assert(sc);
104             sc = sc.push(this);
105             sc.linkage = LINK.cpp; // namespaces default to C++ linkage
106             sc.parent = this;
107             members.foreachDsymbol(s => s.addMember(sc, this));
108             sc.pop();
109         }
110     }
111 
112     override void setScope(Scope* sc)
113     {
114         ScopeDsymbol.setScope(sc);
115         if (members)
116         {
117             assert(sc);
118             sc = sc.push(this);
119             sc.linkage = LINK.cpp; // namespaces default to C++ linkage
120             sc.parent = this;
121             members.foreachDsymbol(s => s.setScope(sc));
122             sc.pop();
123         }
124     }
125 
126     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
127     {
128         //printf("%s.Nspace.search('%s')\n", toChars(), ident.toChars());
129         if (_scope && !symtab)
130             dsymbolSemantic(this, _scope);
131 
132         if (!members || !symtab) // opaque or semantic() is not yet called
133         {
134             error("is forward referenced when looking for `%s`", ident.toChars());
135             return null;
136         }
137 
138         return ScopeDsymbol.search(loc, ident, flags);
139     }
140 
141     override bool hasPointers()
142     {
143         //printf("Nspace::hasPointers() %s\n", toChars());
144         return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
145     }
146 
147     override void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion)
148     {
149         //printf("Nspace::setFieldOffset() %s\n", toChars());
150         if (_scope) // if fwd reference
151             dsymbolSemantic(this, null); // try to resolve it
152         members.foreachDsymbol( s => s.setFieldOffset(ad, poffset, isunion) );
153     }
154 
155     override const(char)* kind() const
156     {
157         return "namespace";
158     }
159 
160     override inout(Nspace) isNspace() inout
161     {
162         return this;
163     }
164 
165     override void accept(Visitor v)
166     {
167         v.visit(this);
168     }
169 }