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