@@ -2280,24 +2280,38 @@ static AccessLevel getTestableAccess(const ValueDecl *decl) {
2280
2280
return AccessLevel::Public;
2281
2281
}
2282
2282
2283
- // / Like ValueDecl::getFormalAccess, but takes into account the parameters that
2284
- // / ValueDecl::getFormalAccessScope is aware of.
2285
- static AccessLevel
2286
- getAdjustedFormalAccess (const ValueDecl *VD, const DeclContext *useDC,
2287
- bool treatUsableFromInlineAsPublic) {
2288
- AccessLevel result = VD->getFormalAccess ();
2283
+ // / Adjust \p access based on whether \p VD is \@usableFromInline or has been
2284
+ // / testably imported from \p useDC.
2285
+ // /
2286
+ // / \p access isn't always just `VD->getFormalAccess()` because this adjustment
2287
+ // / may be for a write, in which case the setter's access might be used instead.
2288
+ static AccessLevel getAdjustedFormalAccess (const ValueDecl *VD,
2289
+ AccessLevel access,
2290
+ const DeclContext *useDC,
2291
+ bool treatUsableFromInlineAsPublic) {
2289
2292
if (treatUsableFromInlineAsPublic &&
2290
- result == AccessLevel::Internal &&
2293
+ access == AccessLevel::Internal &&
2291
2294
VD->isUsableFromInline ()) {
2292
2295
return AccessLevel::Public;
2293
2296
}
2294
- if (useDC && (result == AccessLevel::Internal ||
2295
- result == AccessLevel::Public)) {
2297
+
2298
+ if (useDC && (access == AccessLevel::Internal ||
2299
+ access == AccessLevel::Public)) {
2296
2300
if (auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext ()))
2297
2301
if (useSF->hasTestableImport (VD->getModuleContext ()))
2298
2302
return getTestableAccess (VD);
2299
2303
}
2300
- return result;
2304
+
2305
+ return access;
2306
+ }
2307
+
2308
+ // / Convenience overload that uses `VD->getFormalAccess()` as the access to
2309
+ // / adjust.
2310
+ static AccessLevel
2311
+ getAdjustedFormalAccess (const ValueDecl *VD, const DeclContext *useDC,
2312
+ bool treatUsableFromInlineAsPublic) {
2313
+ return getAdjustedFormalAccess (VD, VD->getFormalAccess (), useDC,
2314
+ treatUsableFromInlineAsPublic);
2301
2315
}
2302
2316
2303
2317
AccessLevel ValueDecl::getEffectiveAccess () const {
@@ -2370,31 +2384,39 @@ bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
2370
2384
return access == AccessLevel::Open;
2371
2385
}
2372
2386
2373
- AccessScope
2374
- ValueDecl::getFormalAccessScope (const DeclContext *useDC,
2375
- bool treatUsableFromInlineAsPublic) const {
2376
- const DeclContext *result = getDeclContext ();
2377
- AccessLevel access = getAdjustedFormalAccess (this , useDC,
2387
+ // / Given the formal access level for using \p VD, compute the scope where
2388
+ // / \p VD may be accessed, taking \@usableFromInline, \@testable imports,
2389
+ // / and enclosing access levels into account.
2390
+ // /
2391
+ // / \p access isn't always just `VD->getFormalAccess()` because this adjustment
2392
+ // / may be for a write, in which case the setter's access might be used instead.
2393
+ static AccessScope
2394
+ getAccessScopeForFormalAccess (const ValueDecl *VD,
2395
+ AccessLevel formalAccess,
2396
+ const DeclContext *useDC,
2397
+ bool treatUsableFromInlineAsPublic) {
2398
+ AccessLevel access = getAdjustedFormalAccess (VD, formalAccess, useDC,
2378
2399
treatUsableFromInlineAsPublic);
2400
+ const DeclContext *resultDC = VD->getDeclContext ();
2379
2401
2380
- while (!result ->isModuleScopeContext ()) {
2381
- if (result ->isLocalContext () || access == AccessLevel::Private)
2382
- return AccessScope (result, true );
2402
+ while (!resultDC ->isModuleScopeContext ()) {
2403
+ if (resultDC ->isLocalContext () || access == AccessLevel::Private)
2404
+ return AccessScope (resultDC, /* private */ true );
2383
2405
2384
- if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(result )) {
2406
+ if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(resultDC )) {
2385
2407
auto enclosingAccess =
2386
- getAdjustedFormalAccess (enclosingNominal, useDC,
2387
- treatUsableFromInlineAsPublic);
2408
+ getAdjustedFormalAccess (enclosingNominal, useDC,
2409
+ treatUsableFromInlineAsPublic);
2388
2410
access = std::min (access, enclosingAccess);
2389
2411
2390
- } else if (auto enclosingExt = dyn_cast<ExtensionDecl>(result )) {
2412
+ } else if (auto enclosingExt = dyn_cast<ExtensionDecl>(resultDC )) {
2391
2413
// Just check the base type. If it's a constrained extension, Sema should
2392
2414
// have already enforced access more strictly.
2393
2415
if (auto extendedTy = enclosingExt->getExtendedType ()) {
2394
2416
if (auto nominal = extendedTy->getAnyNominal ()) {
2395
2417
auto nominalAccess =
2396
- getAdjustedFormalAccess (nominal, useDC,
2397
- treatUsableFromInlineAsPublic);
2418
+ getAdjustedFormalAccess (nominal, useDC,
2419
+ treatUsableFromInlineAsPublic);
2398
2420
access = std::min (access, nominalAccess);
2399
2421
}
2400
2422
}
@@ -2403,16 +2425,16 @@ ValueDecl::getFormalAccessScope(const DeclContext *useDC,
2403
2425
llvm_unreachable (" unknown DeclContext kind" );
2404
2426
}
2405
2427
2406
- result = result ->getParent ();
2428
+ resultDC = resultDC ->getParent ();
2407
2429
}
2408
2430
2409
2431
switch (access) {
2410
2432
case AccessLevel::Private:
2411
2433
case AccessLevel::FilePrivate:
2412
- assert (result ->isModuleScopeContext ());
2413
- return AccessScope (result , access == AccessLevel::Private);
2434
+ assert (resultDC ->isModuleScopeContext ());
2435
+ return AccessScope (resultDC , access == AccessLevel::Private);
2414
2436
case AccessLevel::Internal:
2415
- return AccessScope (result ->getParentModule ());
2437
+ return AccessScope (resultDC ->getParentModule ());
2416
2438
case AccessLevel::Public:
2417
2439
case AccessLevel::Open:
2418
2440
return AccessScope::getPublic ();
@@ -2421,6 +2443,128 @@ ValueDecl::getFormalAccessScope(const DeclContext *useDC,
2421
2443
llvm_unreachable (" unknown access level" );
2422
2444
}
2423
2445
2446
+ AccessScope
2447
+ ValueDecl::getFormalAccessScope (const DeclContext *useDC,
2448
+ bool treatUsableFromInlineAsPublic) const {
2449
+ return getAccessScopeForFormalAccess (this , getFormalAccess (), useDC,
2450
+ treatUsableFromInlineAsPublic);
2451
+ }
2452
+
2453
+ // / Checks if \p VD may be used from \p useDC, taking \@testable imports into
2454
+ // / account.
2455
+ // /
2456
+ // / Whenever the enclosing context of \p VD is usable from \p useDC, this
2457
+ // / should compute the same result as checkAccess, below, but more slowly.
2458
+ // /
2459
+ // / See ValueDecl::isAccessibleFrom for a description of \p forConformance.
2460
+ static bool checkAccessUsingAccessScopes (const DeclContext *useDC,
2461
+ const ValueDecl *VD,
2462
+ AccessLevel access) {
2463
+ AccessScope accessScope =
2464
+ getAccessScopeForFormalAccess (VD, access, useDC,
2465
+ /* treatUsableFromInlineAsPublic*/ false );
2466
+ return accessScope.getDeclContext () == useDC ||
2467
+ AccessScope (useDC).isChildOf (accessScope);
2468
+ }
2469
+
2470
+ // / Checks if \p VD may be used from \p useDC, taking \@testable imports into
2471
+ // / account.
2472
+ // /
2473
+ // / When \p access is the same as `VD->getFormalAccess()` and the enclosing
2474
+ // / context of \p VD is usable from \p useDC, this ought to be the same as
2475
+ // / getting the AccessScope for `VD` and checking if \p useDC is within it.
2476
+ // / However, there's a source compatibility hack around protocol extensions
2477
+ // / that makes it not quite the same.
2478
+ // /
2479
+ // / See ValueDecl::isAccessibleFrom for a description of \p forConformance.
2480
+ static bool checkAccess (const DeclContext *useDC, const ValueDecl *VD,
2481
+ AccessLevel access, bool forConformance) {
2482
+ auto *sourceDC = VD->getDeclContext ();
2483
+
2484
+ if (!forConformance) {
2485
+ if (auto *proto = sourceDC->getAsProtocolOrProtocolExtensionContext ()) {
2486
+ // FIXME: Swift 4.1 allowed accessing protocol extension methods that were
2487
+ // marked 'public' if the protocol was '@_versioned' (now
2488
+ // '@usableFromInline'). Which works at the ABI level, so let's keep
2489
+ // supporting that here by explicitly checking for it.
2490
+ if (access == AccessLevel::Public) {
2491
+ assert (proto->getDeclContext ()->isModuleScopeContext () &&
2492
+ " if we get nested protocols, this should not apply to them" );
2493
+ if (proto->getFormalAccess () == AccessLevel::Internal &&
2494
+ proto->isUsableFromInline ()) {
2495
+ return true ;
2496
+ }
2497
+ }
2498
+
2499
+ // Skip the fast path below and just compare access scopes.
2500
+ return checkAccessUsingAccessScopes (useDC, VD, access);
2501
+ }
2502
+ }
2503
+
2504
+ // Fast path: assume that the client context already has access to our parent
2505
+ // DeclContext, and only check what might be different about this declaration.
2506
+ if (!useDC)
2507
+ return access >= AccessLevel::Public;
2508
+
2509
+ switch (access) {
2510
+ case AccessLevel::Private:
2511
+ return (useDC == sourceDC ||
2512
+ AccessScope::allowsPrivateAccess (useDC, sourceDC));
2513
+ case AccessLevel::FilePrivate:
2514
+ return useDC->getModuleScopeContext () == sourceDC->getModuleScopeContext ();
2515
+ case AccessLevel::Internal: {
2516
+ const ModuleDecl *sourceModule = sourceDC->getParentModule ();
2517
+ const DeclContext *useFile = useDC->getModuleScopeContext ();
2518
+ if (useFile->getParentModule () == sourceModule)
2519
+ return true ;
2520
+ if (auto *useSF = dyn_cast<SourceFile>(useFile))
2521
+ if (useSF->hasTestableImport (sourceModule))
2522
+ return true ;
2523
+ return false ;
2524
+ }
2525
+ case AccessLevel::Public:
2526
+ case AccessLevel::Open:
2527
+ return true ;
2528
+ }
2529
+ llvm_unreachable (" bad access level" );
2530
+ }
2531
+
2532
+ bool ValueDecl::isAccessibleFrom (const DeclContext *useDC,
2533
+ bool forConformance) const {
2534
+ auto access = getFormalAccess ();
2535
+ bool result = checkAccess (useDC, this , access, forConformance);
2536
+
2537
+ // For everything outside of protocols and operators, we should get the same
2538
+ // result using either implementation of checkAccess, because useDC must
2539
+ // already have access to this declaration's DeclContext.
2540
+ // FIXME: Arguably, we're doing the wrong thing for operators here too,
2541
+ // because we're finding internal operators within private types. Fortunately
2542
+ // we have a requirement that a member operator take the enclosing type as an
2543
+ // argument, so it won't ever match.
2544
+ assert (getDeclContext ()->getAsProtocolOrProtocolExtensionContext () ||
2545
+ isOperator () ||
2546
+ result == checkAccessUsingAccessScopes (useDC, this , access));
2547
+
2548
+ return result;
2549
+ }
2550
+
2551
+ bool AbstractStorageDecl::isSetterAccessibleFrom (const DeclContext *DC,
2552
+ bool forConformance) const {
2553
+ assert (isSettable (DC));
2554
+
2555
+ // If a stored property does not have a setter, it is still settable from the
2556
+ // designated initializer constructor. In this case, don't check setter
2557
+ // access; it is not set.
2558
+ if (hasStorage () && !isSettable (nullptr ))
2559
+ return true ;
2560
+
2561
+ if (isa<ParamDecl>(this ))
2562
+ return true ;
2563
+
2564
+ auto access = getSetterFormalAccess ();
2565
+ return checkAccess (DC, this , access, forConformance);
2566
+ }
2567
+
2424
2568
void ValueDecl::copyFormalAccessFrom (const ValueDecl *source,
2425
2569
bool sourceIsParentContext) {
2426
2570
if (!hasAccess ()) {
0 commit comments