d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / gcc / d / dmd / access.d
blobd8a65179a9e3fc550358b107a0ce3dfa572b0522
1 /**
2 * Enforce visibility contrains such as `public` and `private`.
4 * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes)
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://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
14 module dmd.access;
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;
31 private enum LOG = false;
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.
39 bool checkAccess(AggregateDeclaration ad, Loc loc, Scope* sc, Dsymbol smember)
41 static if (LOG)
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);
46 const p = smember.toParent();
47 if (p && p.isTemplateInstance())
49 return false; // for backward compatibility
52 if (!symbolIsVisible(sc, smember))
54 // when in @safe code or with -preview=dip1000
55 if (sc.flags & SCOPE.onlysafeaccess)
57 // if there is a func. ask for it's opinion of safety, and if it considers the access @safe accept it.
58 if (sc.func && !sc.func.setUnsafe())
59 return false;
62 ad.error(loc, "%s `%s` is not accessible%s", smember.kind(), smember.toChars(), (sc.flags & SCOPE.onlysafeaccess) ? " from `@safe` code".ptr : "".ptr);
63 //printf("smember = %s %s, vis = %d, semanticRun = %d\n",
64 // smember.kind(), smember.toPrettyChars(), smember.visible() smember.semanticRun);
65 return true;
67 return false;
70 /****************************************
71 * Determine if scope sc has package level access to s.
73 private bool hasPackageAccess(Scope* sc, Dsymbol s)
75 return hasPackageAccess(sc._module, s);
78 private bool hasPackageAccess(Module mod, Dsymbol s)
80 static if (LOG)
82 printf("hasPackageAccess(s = '%s', mod = '%s', s.visibility.pkg = '%s')\n", s.toChars(), mod.toChars(), s.visible().pkg ? s.visible().pkg.toChars() : "NULL");
84 Package pkg = null;
85 if (s.visible().pkg)
86 pkg = s.visible().pkg;
87 else
89 // no explicit package for visibility, inferring most qualified one
90 for (; s; s = s.parent)
92 if (auto m = s.isModule())
94 DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null);
95 assert(dst);
96 Dsymbol s2 = dst.lookup(m.ident);
97 assert(s2);
98 Package p = s2.isPackage();
99 if (p && p.isPackageMod())
101 pkg = p;
102 break;
105 else if ((pkg = s.isPackage()) !is null)
106 break;
109 static if (LOG)
111 if (pkg)
112 printf("\tsymbol access binds to package '%s'\n", pkg.toChars());
114 if (pkg)
116 if (pkg == mod.parent)
118 static if (LOG)
120 printf("\tsc is in permitted package for s\n");
122 return true;
124 if (pkg.isPackageMod() == mod)
126 static if (LOG)
128 printf("\ts is in same package.d module as sc\n");
130 return true;
132 Dsymbol ancestor = mod.parent;
133 for (; ancestor; ancestor = ancestor.parent)
135 if (ancestor == pkg)
137 static if (LOG)
139 printf("\tsc is in permitted ancestor package for s\n");
141 return true;
145 static if (LOG)
147 printf("\tno package access\n");
149 return false;
152 /****************************************
153 * Determine if scope sc has protected level access to cd.
155 private bool hasProtectedAccess(Scope *sc, Dsymbol s)
157 if (auto cd = s.isClassMember()) // also includes interfaces
159 for (auto scx = sc; scx; scx = scx.enclosing)
161 if (!scx.scopesym)
162 continue;
163 auto cd2 = scx.scopesym.isClassDeclaration();
164 if (cd2 && cd.isBaseOf(cd2, null))
165 return true;
168 return sc._module == s.getAccessModule();
171 /****************************************
172 * Check access to d for expression e.d
173 * Returns true if the declaration is not accessible.
175 bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d)
177 if (sc.flags & SCOPE.noaccesscheck)
178 return false;
179 static if (LOG)
181 if (e)
183 printf("checkAccess(%s . %s)\n", e.toChars(), d.toChars());
184 printf("\te.type = %s\n", e.type.toChars());
186 else
188 printf("checkAccess(%s)\n", d.toPrettyChars());
191 if (d.isUnitTestDeclaration())
193 // Unittests are always accessible.
194 return false;
197 if (!e)
198 return false;
200 if (auto tc = e.type.isTypeClass())
202 // Do access check
203 ClassDeclaration cd = tc.sym;
204 if (e.op == EXP.super_)
206 if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
207 cd = cd2;
209 return checkAccess(cd, loc, sc, d);
211 else if (auto ts = e.type.isTypeStruct())
213 // Do access check
214 StructDeclaration cd = ts.sym;
215 return checkAccess(cd, loc, sc, d);
217 return false;
220 /****************************************
221 * Check access to package/module `p` from scope `sc`.
223 * Params:
224 * sc = scope from which to access to a fully qualified package name
225 * p = the package/module to check access for
226 * Returns: true if the package is not accessible.
228 * Because a global symbol table tree is used for imported packages/modules,
229 * access to them needs to be checked based on the imports in the scope chain
230 * (see https://issues.dlang.org/show_bug.cgi?id=313).
233 bool checkAccess(Scope* sc, Package p)
235 if (sc._module == p)
236 return false;
237 for (; sc; sc = sc.enclosing)
239 if (sc.scopesym && sc.scopesym.isPackageAccessible(p, Visibility(Visibility.Kind.private_)))
240 return false;
243 return true;
247 * Check whether symbols `s` is visible in `mod`.
249 * Params:
250 * mod = lookup origin
251 * s = symbol to check for visibility
252 * Returns: true if s is visible in mod
254 bool symbolIsVisible(Module mod, Dsymbol s)
256 // should sort overloads by ascending visibility instead of iterating here
257 s = mostVisibleOverload(s);
258 final switch (s.visible().kind)
260 case Visibility.Kind.undefined: return true;
261 case Visibility.Kind.none: return false; // no access
262 case Visibility.Kind.private_: return s.getAccessModule() == mod;
263 case Visibility.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
264 case Visibility.Kind.protected_: return s.getAccessModule() == mod;
265 case Visibility.Kind.public_, Visibility.Kind.export_: return true;
270 * Same as above, but determines the lookup module from symbols `origin`.
272 bool symbolIsVisible(Dsymbol origin, Dsymbol s)
274 return symbolIsVisible(origin.getAccessModule(), s);
278 * Same as above but also checks for protected symbols visible from scope `sc`.
279 * Used for qualified name lookup.
281 * Params:
282 * sc = lookup scope
283 * s = symbol to check for visibility
284 * Returns: true if s is visible by origin
286 bool symbolIsVisible(Scope *sc, Dsymbol s)
288 s = mostVisibleOverload(s);
289 return checkSymbolAccess(sc, s);
293 * Check if a symbol is visible from a given scope without taking
294 * into account the most visible overload.
296 * Params:
297 * sc = lookup scope
298 * s = symbol to check for visibility
299 * Returns: true if s is visible by origin
301 bool checkSymbolAccess(Scope *sc, Dsymbol s)
303 final switch (s.visible().kind)
305 case Visibility.Kind.undefined: return true;
306 case Visibility.Kind.none: return false; // no access
307 case Visibility.Kind.private_: return sc._module == s.getAccessModule();
308 case Visibility.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
309 case Visibility.Kind.protected_: return hasProtectedAccess(sc, s);
310 case Visibility.Kind.public_, Visibility.Kind.export_: return true;
315 * Use the most visible overload to check visibility. Later perform an access
316 * check on the resolved overload. This function is similar to overloadApply,
317 * but doesn't recurse nor resolve aliases because visibility is an
318 * attribute of the alias not the aliasee.
320 public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null)
322 if (!s.isOverloadable())
323 return s;
325 Dsymbol next, fstart = s, mostVisible = s;
326 for (; s; s = next)
328 // void func() {}
329 // private void func(int) {}
330 if (auto fd = s.isFuncDeclaration())
331 next = fd.overnext;
332 // template temp(T) {}
333 // private template temp(T:int) {}
334 else if (auto td = s.isTemplateDeclaration())
335 next = td.overnext;
336 // alias common = mod1.func1;
337 // alias common = mod2.func2;
338 else if (auto fa = s.isFuncAliasDeclaration())
339 next = fa.overnext;
340 // alias common = mod1.templ1;
341 // alias common = mod2.templ2;
342 else if (auto od = s.isOverDeclaration())
343 next = od.overnext;
344 // alias name = sym;
345 // private void name(int) {}
346 else if (auto ad = s.isAliasDeclaration())
348 assert(ad.isOverloadable || ad.type && ad.type.ty == Terror,
349 "Non overloadable Aliasee in overload list");
350 // Yet unresolved aliases store overloads in overnext.
351 if (ad.semanticRun < PASS.semanticdone)
352 next = ad.overnext;
353 else
355 /* This is a bit messy due to the complicated implementation of
356 * alias. Aliases aren't overloadable themselves, but if their
357 * Aliasee is overloadable they can be converted to an overloadable
358 * alias.
360 * This is done by replacing the Aliasee w/ FuncAliasDeclaration
361 * (for functions) or OverDeclaration (for templates) which are
362 * simply overloadable aliases w/ weird names.
364 * Usually aliases should not be resolved for visibility checking
365 * b/c public aliases to private symbols are public. But for the
366 * overloadable alias situation, the Alias (_ad_) has been moved
367 * into its own Aliasee, leaving a shell that we peel away here.
369 auto aliasee = ad.toAlias();
370 if (aliasee.isFuncAliasDeclaration || aliasee.isOverDeclaration)
371 next = aliasee;
372 else
374 /* A simple alias can be at the end of a function or template overload chain.
375 * It can't have further overloads b/c it would have been
376 * converted to an overloadable alias.
378 assert(ad.overnext is null, "Unresolved overload of alias");
379 break;
382 // handled by dmd.func.overloadApply for unknown reason
383 assert(next !is ad); // should not alias itself
384 assert(next !is fstart); // should not alias the overload list itself
386 else
387 break;
390 * Return the "effective" visibility attribute of a symbol when accessed in a module.
391 * The effective visibility attribute is the same as the regular visibility attribute,
392 * except package() is "private" if the module is outside the package;
393 * otherwise, "public".
395 static Visibility visibilitySeenFromModule(Dsymbol d, Module mod = null)
397 Visibility vis = d.visible();
398 if (mod && vis.kind == Visibility.Kind.package_)
400 return hasPackageAccess(mod, d) ? Visibility(Visibility.Kind.public_) : Visibility(Visibility.Kind.private_);
402 return vis;
405 if (next &&
406 visibilitySeenFromModule(mostVisible, mod) < visibilitySeenFromModule(next, mod))
407 mostVisible = next;
409 return mostVisible;