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-2023 by The D Language Foundation, All Rights Reserved
40  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
41  * License:     $(LINK2 https://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.astenums;
52 import dmd.dscope;
53 import dmd.dsymbol;
54 import dmd.dsymbolsem;
55 import dmd.expression;
56 import dmd.globals;
57 import dmd.identifier;
58 import dmd.location;
59 import dmd.visitor;
60 import core.stdc.stdio;
61 
62 private enum LOG = false;
63 
64 /// Ditto
65 extern (C++) final class Nspace : ScopeDsymbol
66 {
67     /**
68      * Namespace identifier resolved during semantic.
69      */
70     Expression identExp;
71 
72     extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members)
73     {
74         super(loc, ident);
75         //printf("Nspace::Nspace(ident = %s)\n", ident.toChars());
76         this.members = members;
77         this.identExp = identExp;
78     }
79 
80     override Nspace syntaxCopy(Dsymbol s)
81     {
82         auto ns = new Nspace(loc, ident, identExp, null);
83         ScopeDsymbol.syntaxCopy(ns);
84         return ns;
85     }
86 
87     override void addMember(Scope* sc, ScopeDsymbol sds)
88     {
89         ScopeDsymbol.addMember(sc, sds);
90 
91         if (members)
92         {
93             if (!symtab)
94                 symtab = new DsymbolTable();
95             // The namespace becomes 'imported' into the enclosing scope
96             for (Scope* sce = sc; 1; sce = sce.enclosing)
97             {
98                 ScopeDsymbol sds2 = sce.scopesym;
99                 if (sds2)
100                 {
101                     sds2.importScope(this, Visibility(Visibility.Kind.public_));
102                     break;
103                 }
104             }
105             assert(sc);
106             sc = sc.push(this);
107             sc.linkage = LINK.cpp; // namespaces default to C++ linkage
108             sc.parent = this;
109             members.foreachDsymbol(s => s.addMember(sc, this));
110             sc.pop();
111         }
112     }
113 
114     override void setScope(Scope* sc)
115     {
116         ScopeDsymbol.setScope(sc);
117         if (members)
118         {
119             assert(sc);
120             sc = sc.push(this);
121             sc.linkage = LINK.cpp; // namespaces default to C++ linkage
122             sc.parent = this;
123             members.foreachDsymbol(s => s.setScope(sc));
124             sc.pop();
125         }
126     }
127 
128     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
129     {
130         //printf("%s.Nspace.search('%s')\n", toChars(), ident.toChars());
131         if (_scope && !symtab)
132             dsymbolSemantic(this, _scope);
133 
134         if (!members || !symtab) // opaque or semantic() is not yet called
135         {
136             if (!(flags & IgnoreErrors))
137                 error("is forward referenced when looking for `%s`", ident.toChars());
138             return null;
139         }
140 
141         return ScopeDsymbol.search(loc, ident, flags);
142     }
143 
144     override bool hasPointers()
145     {
146         //printf("Nspace::hasPointers() %s\n", toChars());
147         return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
148     }
149 
150     override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
151     {
152         //printf("Nspace::setFieldOffset() %s\n", toChars());
153         if (_scope) // if fwd reference
154             dsymbolSemantic(this, null); // try to resolve it
155         members.foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) );
156     }
157 
158     override const(char)* kind() const
159     {
160         return "namespace";
161     }
162 
163     override inout(Nspace) isNspace() inout
164     {
165         return this;
166     }
167 
168     override void accept(Visitor v)
169     {
170         v.visit(this);
171     }
172 }