145
|
1 /* Compiler implementation of the D programming language
|
|
2 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
|
|
3 * written by Walter Bright
|
|
4 * http://www.digitalmars.com
|
|
5 * Distributed under the Boost Software License, Version 1.0.
|
|
6 * http://www.boost.org/LICENSE_1_0.txt
|
|
7 * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c
|
|
8 */
|
|
9
|
|
10 #include "root/dsystem.h"
|
|
11 #include "root/root.h"
|
|
12 #include "root/rmem.h"
|
|
13
|
|
14 #include "errors.h"
|
|
15 #include "enum.h"
|
|
16 #include "aggregate.h"
|
|
17 #include "init.h"
|
|
18 #include "attrib.h"
|
|
19 #include "scope.h"
|
|
20 #include "id.h"
|
|
21 #include "mtype.h"
|
|
22 #include "declaration.h"
|
|
23 #include "aggregate.h"
|
|
24 #include "expression.h"
|
|
25 #include "module.h"
|
|
26 #include "template.h"
|
|
27
|
|
28 /* Code to do access checks
|
|
29 */
|
|
30
|
|
31 bool hasPackageAccess(Scope *sc, Dsymbol *s);
|
|
32 bool hasPackageAccess(Module *mod, Dsymbol *s);
|
|
33 bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember);
|
|
34 bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd);
|
|
35 static Dsymbol *mostVisibleOverload(Dsymbol *s);
|
|
36
|
|
37 /****************************************
|
|
38 * Return Prot access for Dsymbol smember in this declaration.
|
|
39 */
|
|
40 Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember)
|
|
41 {
|
|
42 Prot access_ret = Prot(PROTnone);
|
|
43
|
|
44 assert(ad->isStructDeclaration() || ad->isClassDeclaration());
|
|
45 if (smember->toParent() == ad)
|
|
46 {
|
|
47 access_ret = smember->prot();
|
|
48 }
|
|
49 else if (smember->isDeclaration()->isStatic())
|
|
50 {
|
|
51 access_ret = smember->prot();
|
|
52 }
|
|
53 if (ClassDeclaration *cd = ad->isClassDeclaration())
|
|
54 {
|
|
55 for (size_t i = 0; i < cd->baseclasses->dim; i++)
|
|
56 {
|
|
57 BaseClass *b = (*cd->baseclasses)[i];
|
|
58
|
|
59 Prot access = getAccess(b->sym, smember);
|
|
60 switch (access.kind)
|
|
61 {
|
|
62 case PROTnone:
|
|
63 break;
|
|
64
|
|
65 case PROTprivate:
|
|
66 access_ret = Prot(PROTnone); // private members of base class not accessible
|
|
67 break;
|
|
68
|
|
69 case PROTpackage:
|
|
70 case PROTprotected:
|
|
71 case PROTpublic:
|
|
72 case PROTexport:
|
|
73 // If access is to be tightened
|
|
74 if (PROTpublic < access.kind)
|
|
75 access = Prot(PROTpublic);
|
|
76
|
|
77 // Pick path with loosest access
|
|
78 if (access_ret.isMoreRestrictiveThan(access))
|
|
79 access_ret = access;
|
|
80 break;
|
|
81
|
|
82 default:
|
|
83 assert(0);
|
|
84 }
|
|
85 }
|
|
86 }
|
|
87
|
|
88 return access_ret;
|
|
89 }
|
|
90
|
|
91 /********************************************************
|
|
92 * Helper function for checkAccess()
|
|
93 * Returns:
|
|
94 * false is not accessible
|
|
95 * true is accessible
|
|
96 */
|
|
97 static bool isAccessible(
|
|
98 Dsymbol *smember,
|
|
99 Dsymbol *sfunc,
|
|
100 AggregateDeclaration *dthis,
|
|
101 AggregateDeclaration *cdscope)
|
|
102 {
|
|
103 assert(dthis);
|
|
104
|
|
105 if (hasPrivateAccess(dthis, sfunc) ||
|
|
106 isFriendOf(dthis, cdscope))
|
|
107 {
|
|
108 if (smember->toParent() == dthis)
|
|
109 return true;
|
|
110
|
|
111 if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
|
|
112 {
|
|
113 for (size_t i = 0; i < cdthis->baseclasses->dim; i++)
|
|
114 {
|
|
115 BaseClass *b = (*cdthis->baseclasses)[i];
|
|
116 Prot access = getAccess(b->sym, smember);
|
|
117 if (access.kind >= PROTprotected ||
|
|
118 isAccessible(smember, sfunc, b->sym, cdscope))
|
|
119 {
|
|
120 return true;
|
|
121 }
|
|
122 }
|
|
123 }
|
|
124 }
|
|
125 else
|
|
126 {
|
|
127 if (smember->toParent() != dthis)
|
|
128 {
|
|
129 if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
|
|
130 {
|
|
131 for (size_t i = 0; i < cdthis->baseclasses->dim; i++)
|
|
132 {
|
|
133 BaseClass *b = (*cdthis->baseclasses)[i];
|
|
134 if (isAccessible(smember, sfunc, b->sym, cdscope))
|
|
135 return true;
|
|
136 }
|
|
137 }
|
|
138 }
|
|
139 }
|
|
140 return false;
|
|
141 }
|
|
142
|
|
143 /*******************************
|
|
144 * Do access check for member of this class, this class being the
|
|
145 * type of the 'this' pointer used to access smember.
|
|
146 * Returns true if the member is not accessible.
|
|
147 */
|
|
148 bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember)
|
|
149 {
|
|
150 FuncDeclaration *f = sc->func;
|
|
151 AggregateDeclaration *cdscope = sc->getStructClassScope();
|
|
152
|
|
153 Dsymbol *smemberparent = smember->toParent();
|
|
154 if (!smemberparent || !smemberparent->isAggregateDeclaration())
|
|
155 {
|
|
156 return false; // then it is accessible
|
|
157 }
|
|
158
|
|
159 // BUG: should enable this check
|
|
160 //assert(smember->parent->isBaseOf(this, NULL));
|
|
161
|
|
162 bool result;
|
|
163 Prot access;
|
|
164 if (smemberparent == ad)
|
|
165 {
|
|
166 access = smember->prot();
|
|
167 result = access.kind >= PROTpublic ||
|
|
168 hasPrivateAccess(ad, f) ||
|
|
169 isFriendOf(ad, cdscope) ||
|
|
170 (access.kind == PROTpackage && hasPackageAccess(sc, smember)) ||
|
|
171 ad->getAccessModule() == sc->_module;
|
|
172 }
|
|
173 else if ((access = getAccess(ad, smember)).kind >= PROTpublic)
|
|
174 {
|
|
175 result = true;
|
|
176 }
|
|
177 else if (access.kind == PROTpackage && hasPackageAccess(sc, ad))
|
|
178 {
|
|
179 result = true;
|
|
180 }
|
|
181 else
|
|
182 {
|
|
183 result = isAccessible(smember, f, ad, cdscope);
|
|
184 }
|
|
185 if (!result)
|
|
186 {
|
|
187 ad->error(loc, "member %s is not accessible", smember->toChars());
|
|
188 //printf("smember = %s %s, prot = %d, semanticRun = %d\n",
|
|
189 // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun);
|
|
190 return true;
|
|
191 }
|
|
192 return false;
|
|
193 }
|
|
194
|
|
195 /****************************************
|
|
196 * Determine if this is the same or friend of cd.
|
|
197 */
|
|
198 bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd)
|
|
199 {
|
|
200 if (ad == cd)
|
|
201 return true;
|
|
202
|
|
203 // Friends if both are in the same module
|
|
204 //if (toParent() == cd->toParent())
|
|
205 if (cd && ad->getAccessModule() == cd->getAccessModule())
|
|
206 {
|
|
207 return true;
|
|
208 }
|
|
209
|
|
210 return false;
|
|
211 }
|
|
212
|
|
213 /****************************************
|
|
214 * Determine if scope sc has package level access to s.
|
|
215 */
|
|
216 bool hasPackageAccess(Scope *sc, Dsymbol *s)
|
|
217 {
|
|
218 return hasPackageAccess(sc->_module, s);
|
|
219 }
|
|
220
|
|
221 bool hasPackageAccess(Module *mod, Dsymbol *s)
|
|
222 {
|
|
223 Package *pkg = NULL;
|
|
224
|
|
225 if (s->prot().pkg)
|
|
226 pkg = s->prot().pkg;
|
|
227 else
|
|
228 {
|
|
229 // no explicit package for protection, inferring most qualified one
|
|
230 for (; s; s = s->parent)
|
|
231 {
|
|
232 if (Module *m = s->isModule())
|
|
233 {
|
|
234 DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL);
|
|
235 assert(dst);
|
|
236 Dsymbol *s2 = dst->lookup(m->ident);
|
|
237 assert(s2);
|
|
238 Package *p = s2->isPackage();
|
|
239 if (p && p->isPackageMod())
|
|
240 {
|
|
241 pkg = p;
|
|
242 break;
|
|
243 }
|
|
244 }
|
|
245 else if ((pkg = s->isPackage()) != NULL)
|
|
246 break;
|
|
247 }
|
|
248 }
|
|
249
|
|
250 if (pkg)
|
|
251 {
|
|
252 if (pkg == mod->parent)
|
|
253 {
|
|
254 return true;
|
|
255 }
|
|
256 if (pkg->isPackageMod() == mod)
|
|
257 {
|
|
258 return true;
|
|
259 }
|
|
260 Dsymbol* ancestor = mod->parent;
|
|
261 for (; ancestor; ancestor = ancestor->parent)
|
|
262 {
|
|
263 if (ancestor == pkg)
|
|
264 {
|
|
265 return true;
|
|
266 }
|
|
267 }
|
|
268 }
|
|
269
|
|
270 return false;
|
|
271 }
|
|
272
|
|
273 /****************************************
|
|
274 * Determine if scope sc has protected level access to cd.
|
|
275 */
|
|
276 bool hasProtectedAccess(Scope *sc, Dsymbol *s)
|
|
277 {
|
|
278 if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces
|
|
279 {
|
|
280 for (Scope *scx = sc; scx; scx = scx->enclosing)
|
|
281 {
|
|
282 if (!scx->scopesym)
|
|
283 continue;
|
|
284 ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration();
|
|
285 if (cd2 && cd->isBaseOf(cd2, NULL))
|
|
286 return true;
|
|
287 }
|
|
288 }
|
|
289 return sc->_module == s->getAccessModule();
|
|
290 }
|
|
291
|
|
292 /**********************************
|
|
293 * Determine if smember has access to private members of this declaration.
|
|
294 */
|
|
295 bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember)
|
|
296 {
|
|
297 if (smember)
|
|
298 {
|
|
299 AggregateDeclaration *cd = NULL;
|
|
300 Dsymbol *smemberparent = smember->toParent();
|
|
301 if (smemberparent)
|
|
302 cd = smemberparent->isAggregateDeclaration();
|
|
303
|
|
304 if (ad == cd) // smember is a member of this class
|
|
305 {
|
|
306 return true; // so we get private access
|
|
307 }
|
|
308
|
|
309 // If both are members of the same module, grant access
|
|
310 while (1)
|
|
311 {
|
|
312 Dsymbol *sp = smember->toParent();
|
|
313 if (sp->isFuncDeclaration() && smember->isFuncDeclaration())
|
|
314 smember = sp;
|
|
315 else
|
|
316 break;
|
|
317 }
|
|
318 if (!cd && ad->toParent() == smember->toParent())
|
|
319 {
|
|
320 return true;
|
|
321 }
|
|
322 if (!cd && ad->getAccessModule() == smember->getAccessModule())
|
|
323 {
|
|
324 return true;
|
|
325 }
|
|
326 }
|
|
327 return false;
|
|
328 }
|
|
329
|
|
330 /****************************************
|
|
331 * Check access to d for expression e.d
|
|
332 * Returns true if the declaration is not accessible.
|
|
333 */
|
|
334 bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d)
|
|
335 {
|
|
336 if (sc->flags & SCOPEnoaccesscheck)
|
|
337 return false;
|
|
338
|
|
339 if (d->isUnitTestDeclaration())
|
|
340 {
|
|
341 // Unittests are always accessible.
|
|
342 return false;
|
|
343 }
|
|
344 if (!e)
|
|
345 {
|
|
346 if ((d->prot().kind == PROTprivate && d->getAccessModule() != sc->_module) ||
|
|
347 (d->prot().kind == PROTpackage && !hasPackageAccess(sc, d)))
|
|
348 {
|
|
349 error(loc, "%s %s is not accessible from module %s",
|
|
350 d->kind(), d->toPrettyChars(), sc->_module->toChars());
|
|
351 return true;
|
|
352 }
|
|
353 }
|
|
354 else if (e->type->ty == Tclass)
|
|
355 {
|
|
356 // Do access check
|
|
357 ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym);
|
|
358 if (e->op == TOKsuper)
|
|
359 {
|
|
360 ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration();
|
|
361 if (cd2)
|
|
362 cd = cd2;
|
|
363 }
|
|
364 return checkAccess(cd, loc, sc, d);
|
|
365 }
|
|
366 else if (e->type->ty == Tstruct)
|
|
367 {
|
|
368 // Do access check
|
|
369 StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym);
|
|
370 return checkAccess(cd, loc, sc, d);
|
|
371 }
|
|
372 return false;
|
|
373 }
|
|
374
|
|
375 /****************************************
|
|
376 * Check access to package/module `p` from scope `sc`.
|
|
377 *
|
|
378 * Params:
|
|
379 * loc = source location for issued error message
|
|
380 * sc = scope from which to access to a fully qualified package name
|
|
381 * p = the package/module to check access for
|
|
382 * Returns: true if the package is not accessible.
|
|
383 *
|
|
384 * Because a global symbol table tree is used for imported packages/modules,
|
|
385 * access to them needs to be checked based on the imports in the scope chain
|
|
386 * (see Bugzilla 313).
|
|
387 *
|
|
388 */
|
|
389 bool checkAccess(Loc loc, Scope *sc, Package *p)
|
|
390 {
|
|
391 if (sc->_module == p)
|
|
392 return false;
|
|
393 for (; sc; sc = sc->enclosing)
|
|
394 {
|
|
395 if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(PROTprivate)))
|
|
396 return false;
|
|
397 }
|
|
398 const char *name = p->toPrettyChars();
|
|
399 if (p->isPkgMod == PKGmodule || p->isModule())
|
|
400 deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", p->kind(), name, name);
|
|
401 else
|
|
402 deprecation(loc, "%s %s is not accessible here", p->kind(), name);
|
|
403 return true;
|
|
404 }
|
|
405
|
|
406 /**
|
|
407 * Check whether symbols `s` is visible in `mod`.
|
|
408 *
|
|
409 * Params:
|
|
410 * mod = lookup origin
|
|
411 * s = symbol to check for visibility
|
|
412 * Returns: true if s is visible in mod
|
|
413 */
|
|
414 bool symbolIsVisible(Module *mod, Dsymbol *s)
|
|
415 {
|
|
416 // should sort overloads by ascending protection instead of iterating here
|
|
417 s = mostVisibleOverload(s);
|
|
418
|
|
419 switch (s->prot().kind)
|
|
420 {
|
|
421 case PROTundefined:
|
|
422 return true;
|
|
423 case PROTnone:
|
|
424 return false; // no access
|
|
425 case PROTprivate:
|
|
426 return s->getAccessModule() == mod;
|
|
427 case PROTpackage:
|
|
428 return s->getAccessModule() == mod || hasPackageAccess(mod, s);
|
|
429 case PROTprotected:
|
|
430 return s->getAccessModule() == mod;
|
|
431 case PROTpublic:
|
|
432 case PROTexport:
|
|
433 return true;
|
|
434 default:
|
|
435 assert(0);
|
|
436 }
|
|
437 }
|
|
438
|
|
439 /**
|
|
440 * Same as above, but determines the lookup module from symbols `origin`.
|
|
441 */
|
|
442 bool symbolIsVisible(Dsymbol *origin, Dsymbol *s)
|
|
443 {
|
|
444 return symbolIsVisible(origin->getAccessModule(), s);
|
|
445 }
|
|
446
|
|
447 /**
|
|
448 * Same as above but also checks for protected symbols visible from scope `sc`.
|
|
449 * Used for qualified name lookup.
|
|
450 *
|
|
451 * Params:
|
|
452 * sc = lookup scope
|
|
453 * s = symbol to check for visibility
|
|
454 * Returns: true if s is visible by origin
|
|
455 */
|
|
456 bool symbolIsVisible(Scope *sc, Dsymbol *s)
|
|
457 {
|
|
458 s = mostVisibleOverload(s);
|
|
459
|
|
460 switch (s->prot().kind)
|
|
461 {
|
|
462 case PROTundefined:
|
|
463 return true;
|
|
464 case PROTnone:
|
|
465 return false; // no access
|
|
466 case PROTprivate:
|
|
467 return sc->_module == s->getAccessModule();
|
|
468 case PROTpackage:
|
|
469 return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s);
|
|
470 case PROTprotected:
|
|
471 return hasProtectedAccess(sc, s);
|
|
472 case PROTpublic:
|
|
473 case PROTexport:
|
|
474 return true;
|
|
475 default:
|
|
476 assert(0);
|
|
477 }
|
|
478 }
|
|
479
|
|
480 /**
|
|
481 * Use the most visible overload to check visibility. Later perform an access
|
|
482 * check on the resolved overload. This function is similar to overloadApply,
|
|
483 * but doesn't recurse nor resolve aliases because protection/visibility is an
|
|
484 * attribute of the alias not the aliasee.
|
|
485 */
|
|
486 static Dsymbol *mostVisibleOverload(Dsymbol *s)
|
|
487 {
|
|
488 if (!s->isOverloadable())
|
|
489 return s;
|
|
490
|
|
491 Dsymbol *next = NULL;
|
|
492 Dsymbol *fstart = s;
|
|
493 Dsymbol *mostVisible = s;
|
|
494 for (; s; s = next)
|
|
495 {
|
|
496 // void func() {}
|
|
497 // private void func(int) {}
|
|
498 if (FuncDeclaration *fd = s->isFuncDeclaration())
|
|
499 next = fd->overnext;
|
|
500 // template temp(T) {}
|
|
501 // private template temp(T:int) {}
|
|
502 else if (TemplateDeclaration *td = s->isTemplateDeclaration())
|
|
503 next = td->overnext;
|
|
504 // alias common = mod1.func1;
|
|
505 // alias common = mod2.func2;
|
|
506 else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration())
|
|
507 next = fa->overnext;
|
|
508 // alias common = mod1.templ1;
|
|
509 // alias common = mod2.templ2;
|
|
510 else if (OverDeclaration *od = s->isOverDeclaration())
|
|
511 next = od->overnext;
|
|
512 // alias name = sym;
|
|
513 // private void name(int) {}
|
|
514 else if (AliasDeclaration *ad = s->isAliasDeclaration())
|
|
515 {
|
|
516 if (!ad->isOverloadable())
|
|
517 {
|
|
518 //printf("Non overloadable Aliasee in overload list\n");
|
|
519 assert(0);
|
|
520 }
|
|
521 // Yet unresolved aliases store overloads in overnext.
|
|
522 if (ad->semanticRun < PASSsemanticdone)
|
|
523 next = ad->overnext;
|
|
524 else
|
|
525 {
|
|
526 /* This is a bit messy due to the complicated implementation of
|
|
527 * alias. Aliases aren't overloadable themselves, but if their
|
|
528 * Aliasee is overloadable they can be converted to an overloadable
|
|
529 * alias.
|
|
530 *
|
|
531 * This is done by replacing the Aliasee w/ FuncAliasDeclaration
|
|
532 * (for functions) or OverDeclaration (for templates) which are
|
|
533 * simply overloadable aliases w/ weird names.
|
|
534 *
|
|
535 * Usually aliases should not be resolved for visibility checking
|
|
536 * b/c public aliases to private symbols are public. But for the
|
|
537 * overloadable alias situation, the Alias (_ad_) has been moved
|
|
538 * into it's own Aliasee, leaving a shell that we peel away here.
|
|
539 */
|
|
540 Dsymbol *aliasee = ad->toAlias();
|
|
541 if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration())
|
|
542 next = aliasee;
|
|
543 else
|
|
544 {
|
|
545 /* A simple alias can be at the end of a function or template overload chain.
|
|
546 * It can't have further overloads b/c it would have been
|
|
547 * converted to an overloadable alias.
|
|
548 */
|
|
549 if (ad->overnext)
|
|
550 {
|
|
551 //printf("Unresolved overload of alias\n");
|
|
552 assert(0);
|
|
553 }
|
|
554 break;
|
|
555 }
|
|
556 }
|
|
557
|
|
558 // handled by overloadApply for unknown reason
|
|
559 assert(next != ad); // should not alias itself
|
|
560 assert(next != fstart); // should not alias the overload list itself
|
|
561 }
|
|
562 else
|
|
563 break;
|
|
564
|
|
565 if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot()))
|
|
566 mostVisible = next;
|
|
567 }
|
|
568 return mostVisible;
|
|
569 }
|