xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/access.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /**
2  * Enforce visibility contrains such as `public` and `private`.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes)
5  *
6  * Copyright:   Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/access.d, _access.d)
10  * Documentation:  https://dlang.org/phobos/dmd_access.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/access.d
12  */
13 
14 module dmd.access;
15 
16 import dmd.aggregate;
17 import dmd.astenums;
18 import dmd.dclass;
19 import dmd.declaration;
20 import dmd.dmodule;
21 import dmd.dscope;
22 import dmd.dstruct;
23 import dmd.dsymbol;
24 import dmd.errors;
25 import dmd.expression;
26 import dmd.func;
27 import dmd.globals;
28 import dmd.mtype;
29 import dmd.tokens;
30 
31 private enum LOG = false;
32 
33 
34 /*******************************
35  * Do access check for member of this class, this class being the
36  * type of the 'this' pointer used to access smember.
37  * Returns true if the member is not accessible.
38  */
checkAccess(AggregateDeclaration ad,Loc loc,Scope * sc,Dsymbol smember)39 bool checkAccess(AggregateDeclaration ad, Loc loc, Scope* sc, Dsymbol smember)
40 {
41     static if (LOG)
42     {
43         printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", ad.toChars(), smember.toChars(), f ? f.toChars() : null, cdscope ? cdscope.toChars() : null);
44     }
45 
46     const p = smember.toParent();
47     if (p && p.isTemplateInstance())
48     {
49         return false; // for backward compatibility
50     }
51 
52     if (!symbolIsVisible(sc, smember))
53     {
54         ad.error(loc, "%s `%s` is not accessible", smember.kind(), smember.toChars());
55         //printf("smember = %s %s, vis = %d, semanticRun = %d\n",
56         //        smember.kind(), smember.toPrettyChars(), smember.visible() smember.semanticRun);
57         return true;
58     }
59     return false;
60 }
61 
62 /****************************************
63  * Determine if scope sc has package level access to s.
64  */
hasPackageAccess(Scope * sc,Dsymbol s)65 private bool hasPackageAccess(Scope* sc, Dsymbol s)
66 {
67     return hasPackageAccess(sc._module, s);
68 }
69 
hasPackageAccess(Module mod,Dsymbol s)70 private bool hasPackageAccess(Module mod, Dsymbol s)
71 {
72     static if (LOG)
73     {
74         printf("hasPackageAccess(s = '%s', mod = '%s', s.visibility.pkg = '%s')\n", s.toChars(), mod.toChars(), s.visible().pkg ? s.visible().pkg.toChars() : "NULL");
75     }
76     Package pkg = null;
77     if (s.visible().pkg)
78         pkg = s.visible().pkg;
79     else
80     {
81         // no explicit package for visibility, inferring most qualified one
82         for (; s; s = s.parent)
83         {
84             if (auto m = s.isModule())
85             {
86                 DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null);
87                 assert(dst);
88                 Dsymbol s2 = dst.lookup(m.ident);
89                 assert(s2);
90                 Package p = s2.isPackage();
91                 if (p && p.isPackageMod())
92                 {
93                     pkg = p;
94                     break;
95                 }
96             }
97             else if ((pkg = s.isPackage()) !is null)
98                 break;
99         }
100     }
101     static if (LOG)
102     {
103         if (pkg)
104             printf("\tsymbol access binds to package '%s'\n", pkg.toChars());
105     }
106     if (pkg)
107     {
108         if (pkg == mod.parent)
109         {
110             static if (LOG)
111             {
112                 printf("\tsc is in permitted package for s\n");
113             }
114             return true;
115         }
116         if (pkg.isPackageMod() == mod)
117         {
118             static if (LOG)
119             {
120                 printf("\ts is in same package.d module as sc\n");
121             }
122             return true;
123         }
124         Dsymbol ancestor = mod.parent;
125         for (; ancestor; ancestor = ancestor.parent)
126         {
127             if (ancestor == pkg)
128             {
129                 static if (LOG)
130                 {
131                     printf("\tsc is in permitted ancestor package for s\n");
132                 }
133                 return true;
134             }
135         }
136     }
137     static if (LOG)
138     {
139         printf("\tno package access\n");
140     }
141     return false;
142 }
143 
144 /****************************************
145  * Determine if scope sc has protected level access to cd.
146  */
hasProtectedAccess(Scope * sc,Dsymbol s)147 private bool hasProtectedAccess(Scope *sc, Dsymbol s)
148 {
149     if (auto cd = s.isClassMember()) // also includes interfaces
150     {
151         for (auto scx = sc; scx; scx = scx.enclosing)
152         {
153             if (!scx.scopesym)
154                 continue;
155             auto cd2 = scx.scopesym.isClassDeclaration();
156             if (cd2 && cd.isBaseOf(cd2, null))
157                 return true;
158         }
159     }
160     return sc._module == s.getAccessModule();
161 }
162 
163 /****************************************
164  * Check access to d for expression e.d
165  * Returns true if the declaration is not accessible.
166  */
checkAccess(Loc loc,Scope * sc,Expression e,Dsymbol d)167 bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d)
168 {
169     if (sc.flags & SCOPE.noaccesscheck)
170         return false;
171     static if (LOG)
172     {
173         if (e)
174         {
175             printf("checkAccess(%s . %s)\n", e.toChars(), d.toChars());
176             printf("\te.type = %s\n", e.type.toChars());
177         }
178         else
179         {
180             printf("checkAccess(%s)\n", d.toPrettyChars());
181         }
182     }
183     if (d.isUnitTestDeclaration())
184     {
185         // Unittests are always accessible.
186         return false;
187     }
188 
189     if (!e)
190         return false;
191 
192     if (auto tc = e.type.isTypeClass())
193     {
194         // Do access check
195         ClassDeclaration cd = tc.sym;
196         if (e.op == EXP.super_)
197         {
198             if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
199                 cd = cd2;
200         }
201         return checkAccess(cd, loc, sc, d);
202     }
203     else if (auto ts = e.type.isTypeStruct())
204     {
205         // Do access check
206         StructDeclaration cd = ts.sym;
207         return checkAccess(cd, loc, sc, d);
208     }
209     return false;
210 }
211 
212 /****************************************
213  * Check access to package/module `p` from scope `sc`.
214  *
215  * Params:
216  *   sc = scope from which to access to a fully qualified package name
217  *   p = the package/module to check access for
218  * Returns: true if the package is not accessible.
219  *
220  * Because a global symbol table tree is used for imported packages/modules,
221  * access to them needs to be checked based on the imports in the scope chain
222  * (see https://issues.dlang.org/show_bug.cgi?id=313).
223  *
224  */
checkAccess(Scope * sc,Package p)225 bool checkAccess(Scope* sc, Package p)
226 {
227     if (sc._module == p)
228         return false;
229     for (; sc; sc = sc.enclosing)
230     {
231         if (sc.scopesym && sc.scopesym.isPackageAccessible(p, Visibility(Visibility.Kind.private_)))
232             return false;
233     }
234 
235     return true;
236 }
237 
238 /**
239  * Check whether symbols `s` is visible in `mod`.
240  *
241  * Params:
242  *  mod = lookup origin
243  *  s = symbol to check for visibility
244  * Returns: true if s is visible in mod
245  */
symbolIsVisible(Module mod,Dsymbol s)246 bool symbolIsVisible(Module mod, Dsymbol s)
247 {
248     // should sort overloads by ascending visibility instead of iterating here
249     s = mostVisibleOverload(s);
250     final switch (s.visible().kind)
251     {
252     case Visibility.Kind.undefined: return true;
253     case Visibility.Kind.none: return false; // no access
254     case Visibility.Kind.private_: return s.getAccessModule() == mod;
255     case Visibility.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
256     case Visibility.Kind.protected_: return s.getAccessModule() == mod;
257     case Visibility.Kind.public_, Visibility.Kind.export_: return true;
258     }
259 }
260 
261 /**
262  * Same as above, but determines the lookup module from symbols `origin`.
263  */
symbolIsVisible(Dsymbol origin,Dsymbol s)264 bool symbolIsVisible(Dsymbol origin, Dsymbol s)
265 {
266     return symbolIsVisible(origin.getAccessModule(), s);
267 }
268 
269 /**
270  * Same as above but also checks for protected symbols visible from scope `sc`.
271  * Used for qualified name lookup.
272  *
273  * Params:
274  *  sc = lookup scope
275  *  s = symbol to check for visibility
276  * Returns: true if s is visible by origin
277  */
symbolIsVisible(Scope * sc,Dsymbol s)278 bool symbolIsVisible(Scope *sc, Dsymbol s)
279 {
280     s = mostVisibleOverload(s);
281     return checkSymbolAccess(sc, s);
282 }
283 
284 /**
285  * Check if a symbol is visible from a given scope without taking
286  * into account the most visible overload.
287  *
288  * Params:
289  *  sc = lookup scope
290  *  s = symbol to check for visibility
291  * Returns: true if s is visible by origin
292  */
checkSymbolAccess(Scope * sc,Dsymbol s)293 bool checkSymbolAccess(Scope *sc, Dsymbol s)
294 {
295     final switch (s.visible().kind)
296     {
297     case Visibility.Kind.undefined: return true;
298     case Visibility.Kind.none: return false; // no access
299     case Visibility.Kind.private_: return sc._module == s.getAccessModule();
300     case Visibility.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
301     case Visibility.Kind.protected_: return hasProtectedAccess(sc, s);
302     case Visibility.Kind.public_, Visibility.Kind.export_: return true;
303     }
304 }
305 
306 /**
307  * Use the most visible overload to check visibility. Later perform an access
308  * check on the resolved overload.  This function is similar to overloadApply,
309  * but doesn't recurse nor resolve aliases because visibility is an
310  * attribute of the alias not the aliasee.
311  */
312 public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null)
313 {
314     if (!s.isOverloadable())
315         return s;
316 
317     Dsymbol next, fstart = s, mostVisible = s;
318     for (; s; s = next)
319     {
320         // void func() {}
321         // private void func(int) {}
322         if (auto fd = s.isFuncDeclaration())
323             next = fd.overnext;
324         // template temp(T) {}
325         // private template temp(T:int) {}
326         else if (auto td = s.isTemplateDeclaration())
327             next = td.overnext;
328         // alias common = mod1.func1;
329         // alias common = mod2.func2;
330         else if (auto fa = s.isFuncAliasDeclaration())
331             next = fa.overnext;
332         // alias common = mod1.templ1;
333         // alias common = mod2.templ2;
334         else if (auto od = s.isOverDeclaration())
335             next = od.overnext;
336         // alias name = sym;
337         // private void name(int) {}
338         else if (auto ad = s.isAliasDeclaration())
339         {
340             assert(ad.isOverloadable || ad.type && ad.type.ty == Terror,
341                 "Non overloadable Aliasee in overload list");
342             // Yet unresolved aliases store overloads in overnext.
343             if (ad.semanticRun < PASS.semanticdone)
344                 next = ad.overnext;
345             else
346             {
347                 /* This is a bit messy due to the complicated implementation of
348                  * alias.  Aliases aren't overloadable themselves, but if their
349                  * Aliasee is overloadable they can be converted to an overloadable
350                  * alias.
351                  *
352                  * This is done by replacing the Aliasee w/ FuncAliasDeclaration
353                  * (for functions) or OverDeclaration (for templates) which are
354                  * simply overloadable aliases w/ weird names.
355                  *
356                  * Usually aliases should not be resolved for visibility checking
357                  * b/c public aliases to private symbols are public. But for the
358                  * overloadable alias situation, the Alias (_ad_) has been moved
359                  * into its own Aliasee, leaving a shell that we peel away here.
360                  */
361                 auto aliasee = ad.toAlias();
362                 if (aliasee.isFuncAliasDeclaration || aliasee.isOverDeclaration)
363                     next = aliasee;
364                 else
365                 {
366                     /* A simple alias can be at the end of a function or template overload chain.
367                      * It can't have further overloads b/c it would have been
368                      * converted to an overloadable alias.
369                      */
370                     assert(ad.overnext is null, "Unresolved overload of alias");
371                     break;
372                 }
373             }
374             // handled by dmd.func.overloadApply for unknown reason
375             assert(next !is ad); // should not alias itself
376             assert(next !is fstart); // should not alias the overload list itself
377         }
378         else
379             break;
380 
381         /**
382         * Return the "effective" visibility attribute of a symbol when accessed in a module.
383         * The effective visibility attribute is the same as the regular visibility attribute,
384         * except package() is "private" if the module is outside the package;
385         * otherwise, "public".
386         */
387         static Visibility visibilitySeenFromModule(Dsymbol d, Module mod = null)
388         {
389             Visibility vis = d.visible();
390             if (mod && vis.kind == Visibility.Kind.package_)
391             {
392                 return hasPackageAccess(mod, d) ? Visibility(Visibility.Kind.public_) : Visibility(Visibility.Kind.private_);
393             }
394             return vis;
395         }
396 
397         if (next &&
398             visibilitySeenFromModule(mostVisible, mod) < visibilitySeenFromModule(next, mod))
399             mostVisible = next;
400     }
401     return mostVisible;
402 }
403