Skip to content

Commit ca14d6c

Browse files
committed
Take away some of ValueDecl::getFormalAccess's capabilities
...to push people towards getFormalAccessScope. The one use case that isn't covered by that is checking whether a declaration behaves as 'open' in the current file; I've added ValueDecl::hasOpenAccess to handle that specific case. No intended functionality change.
1 parent 48eb400 commit ca14d6c

File tree

8 files changed

+127
-81
lines changed

8 files changed

+127
-81
lines changed

include/swift/AST/Decl.h

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,19 +2347,14 @@ class ValueDecl : public Decl {
23472347
/// Returns the access level specified explicitly by the user, or provided by
23482348
/// default according to language rules.
23492349
///
2350-
/// This is the access used when calculating if access control is being used
2351-
/// consistently. If \p useDC is provided (the location where the value is
2352-
/// being used), features that affect formal access such as \c \@testable are
2353-
/// taken into account.
2354-
///
2355-
/// If \p treatUsableFromInlineAsPublic is true, declarations marked with the
2356-
/// \c @usableFromInline attribute are treated as public. This is normally
2357-
/// false for name lookup and other source language concerns, but true when
2358-
/// computing the linkage of generated functions.
2350+
/// Most of the time this is not the interesting value to check; access is
2351+
/// limited by enclosing scopes per SE-0025. Use #getFormalAccessScope to
2352+
/// check if access control is being used consistently, and to take features
2353+
/// such as \c \@testable and \c \@usableFromInline into account.
23592354
///
23602355
/// \sa getFormalAccessScope
2361-
AccessLevel getFormalAccess(const DeclContext *useDC = nullptr,
2362-
bool treatUsableFromInlineAsPublic = false) const;
2356+
/// \sa hasOpenAccess
2357+
AccessLevel getFormalAccess() const;
23632358

23642359
/// If this declaration is a member of a protocol extension, return the
23652360
/// minimum of the given access level and the protocol's access level.
@@ -2373,7 +2368,8 @@ class ValueDecl : public Decl {
23732368
/// visible.
23742369
AccessLevel adjustAccessLevelForProtocolExtension(AccessLevel access) const;
23752370

2376-
/// Determine whether this Decl has either Private or FilePrivate access.
2371+
/// Determine whether this Decl has either Private or FilePrivate access,
2372+
/// and its DeclContext does not.
23772373
bool isOutermostPrivateOrFilePrivateScope() const;
23782374

23792375
/// Returns the outermost DeclContext from which this declaration can be
@@ -2395,6 +2391,7 @@ class ValueDecl : public Decl {
23952391
///
23962392
/// \sa getFormalAccess
23972393
/// \sa isAccessibleFrom
2394+
/// \sa hasOpenAccess
23982395
AccessScope
23992396
getFormalAccessScope(const DeclContext *useDC = nullptr,
24002397
bool treatUsableFromInlineAsPublic = false) const;
@@ -2448,6 +2445,14 @@ class ValueDecl : public Decl {
24482445
bool isAccessibleFrom(const DeclContext *DC,
24492446
bool forConformance = false) const;
24502447

2448+
/// Returns whether this declaration should be treated as \c open from
2449+
/// \p useDC. This is very similar to #getFormalAccess, but takes
2450+
/// \c \@testable into account.
2451+
///
2452+
/// This is mostly only useful when considering requirements on an override:
2453+
/// if the base declaration is \c open, the override might have to be too.
2454+
bool hasOpenAccess(const DeclContext *useDC) const;
2455+
24512456
/// Retrieve the "interface" type of this value, which uses
24522457
/// GenericTypeParamType if the declaration is generic. For a generic
24532458
/// function, this will have a GenericFunctionType with a

lib/AST/Decl.cpp

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,10 +2280,30 @@ static AccessLevel getTestableAccess(const ValueDecl *decl) {
22802280
return AccessLevel::Public;
22812281
}
22822282

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();
2289+
if (treatUsableFromInlineAsPublic &&
2290+
result == AccessLevel::Internal &&
2291+
VD->isUsableFromInline()) {
2292+
return AccessLevel::Public;
2293+
}
2294+
if (useDC && (result == AccessLevel::Internal ||
2295+
result == AccessLevel::Public)) {
2296+
if (auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext()))
2297+
if (useSF->hasTestableImport(VD->getModuleContext()))
2298+
return getTestableAccess(VD);
2299+
}
2300+
return result;
2301+
}
2302+
22832303
AccessLevel ValueDecl::getEffectiveAccess() const {
22842304
auto effectiveAccess =
2285-
getFormalAccess(/*useDC=*/nullptr,
2286-
/*treatUsableFromInlineAsPublic=*/true);
2305+
getAdjustedFormalAccess(this, /*useDC=*/nullptr,
2306+
/*treatUsableFromInlineAsPublic=*/true);
22872307

22882308
// Handle @testable.
22892309
switch (effectiveAccess) {
@@ -2335,38 +2355,36 @@ AccessLevel ValueDecl::getEffectiveAccess() const {
23352355
return effectiveAccess;
23362356
}
23372357

2338-
AccessLevel ValueDecl::getFormalAccess(const DeclContext *useDC,
2339-
bool treatUsableFromInlineAsPublic) const {
2358+
AccessLevel ValueDecl::getFormalAccess() const {
23402359
ASTContext &ctx = getASTContext();
2341-
AccessLevel result = ctx.evaluator(AccessLevelRequest{const_cast<ValueDecl *>(this)});
2342-
if (treatUsableFromInlineAsPublic &&
2343-
result == AccessLevel::Internal &&
2344-
isUsableFromInline()) {
2345-
return AccessLevel::Public;
2346-
}
2347-
if (useDC && (result == AccessLevel::Internal ||
2348-
result == AccessLevel::Public)) {
2349-
if (auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext()))
2350-
if (useSF->hasTestableImport(getModuleContext()))
2351-
return getTestableAccess(this);
2352-
}
2353-
return result;
2360+
return ctx.evaluator(AccessLevelRequest{const_cast<ValueDecl *>(this)});
2361+
}
2362+
2363+
bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
2364+
assert(isa<ClassDecl>(this) || isa<ConstructorDecl>(this) ||
2365+
isPotentiallyOverridable());
2366+
2367+
AccessLevel access =
2368+
getAdjustedFormalAccess(this, useDC,
2369+
/*treatUsableFromInlineAsPublic*/false);
2370+
return access == AccessLevel::Open;
23542371
}
23552372

23562373
AccessScope
23572374
ValueDecl::getFormalAccessScope(const DeclContext *useDC,
23582375
bool treatUsableFromInlineAsPublic) const {
23592376
const DeclContext *result = getDeclContext();
2360-
AccessLevel access = getFormalAccess(useDC, treatUsableFromInlineAsPublic);
2377+
AccessLevel access = getAdjustedFormalAccess(this, useDC,
2378+
treatUsableFromInlineAsPublic);
23612379

23622380
while (!result->isModuleScopeContext()) {
23632381
if (result->isLocalContext() || access == AccessLevel::Private)
23642382
return AccessScope(result, true);
23652383

23662384
if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(result)) {
23672385
auto enclosingAccess =
2368-
enclosingNominal->getFormalAccess(useDC,
2369-
treatUsableFromInlineAsPublic);
2386+
getAdjustedFormalAccess(enclosingNominal, useDC,
2387+
treatUsableFromInlineAsPublic);
23702388
access = std::min(access, enclosingAccess);
23712389

23722390
} else if (auto enclosingExt = dyn_cast<ExtensionDecl>(result)) {
@@ -2375,8 +2393,8 @@ ValueDecl::getFormalAccessScope(const DeclContext *useDC,
23752393
if (auto extendedTy = enclosingExt->getExtendedType()) {
23762394
if (auto nominal = extendedTy->getAnyNominal()) {
23772395
auto nominalAccess =
2378-
nominal->getFormalAccess(useDC,
2379-
treatUsableFromInlineAsPublic);
2396+
getAdjustedFormalAccess(nominal, useDC,
2397+
treatUsableFromInlineAsPublic);
23802398
access = std::min(access, nominalAccess);
23812399
}
23822400
}

lib/AST/NameLookup.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,11 +1529,12 @@ ValueDecl::adjustAccessLevelForProtocolExtension(AccessLevel access) const {
15291529
// and expects these extension methods to witness public protocol
15301530
// requirements. Which works at the ABI level, so let's keep
15311531
// supporting that here by passing 'isUsageFromInline'.
1532-
auto protoAccess = protocol->getFormalAccess(/*useDC=*/nullptr,
1533-
/*isUsageFromInline=*/true);
1534-
if (protoAccess == AccessLevel::Private)
1535-
protoAccess = AccessLevel::FilePrivate;
1536-
access = std::min(access, protoAccess);
1532+
auto protoAccess =
1533+
protocol->getFormalAccessScope(/*useDC=*/nullptr,
1534+
/*isUsageFromInline=*/true);
1535+
// FIXME: The calling code should be written in terms of AccessScope,
1536+
// so that this can just use AccessScope::intersectWith.
1537+
access = std::min(access, protoAccess.requiredAccessForDiagnostics());
15371538
}
15381539
}
15391540

lib/Sema/CodeSynthesis.cpp

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,10 +2341,12 @@ configureGenericDesignatedInitOverride(ASTContext &ctx,
23412341
return std::make_tuple(genericSig, genericEnv, genericParams, subMap);
23422342
}
23432343

2344-
static void configureDesignatedInitAttributes(TypeChecker &tc,
2345-
ClassDecl *classDecl,
2346-
ConstructorDecl *ctor,
2347-
ConstructorDecl *superclassCtor) {
2344+
static void
2345+
configureInheritedDesignatedInitAttributes(TypeChecker &tc,
2346+
ClassDecl *classDecl,
2347+
ConstructorDecl *ctor,
2348+
ConstructorDecl *superclassCtor) {
2349+
assert(ctor->getDeclContext() == classDecl);
23482350
auto &ctx = tc.Context;
23492351

23502352
AccessLevel access = classDecl->getFormalAccess();
@@ -2353,24 +2355,18 @@ static void configureDesignatedInitAttributes(TypeChecker &tc,
23532355

23542356
ctor->setAccess(access);
23552357

2356-
// Inherit the @inlinable attribute.
2357-
if (superclassCtor->getFormalAccess(/*useDC=*/nullptr,
2358-
/*treatUsableFromInlineAsPublic=*/true)
2359-
>= AccessLevel::Public) {
2358+
AccessScope superclassInliningAccessScope =
2359+
superclassCtor->getFormalAccessScope(/*useDC*/nullptr,
2360+
/*usableFromInlineAsPublic=*/true);
2361+
2362+
if (superclassInliningAccessScope.isPublic()) {
23602363
if (superclassCtor->getAttrs().hasAttribute<InlinableAttr>()) {
2364+
// Inherit the @inlinable attribute.
23612365
auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true);
23622366
ctor->getAttrs().add(clonedAttr);
2363-
}
2364-
}
23652367

2366-
// Inherit the @usableFromInline attribute. We need better abstractions
2367-
// for dealing with @usableFromInline.
2368-
if (superclassCtor->getFormalAccess(/*useDC=*/nullptr,
2369-
/*treatUsableFromInlineAsPublic=*/true)
2370-
>= AccessLevel::Public) {
2371-
if (access == AccessLevel::Internal &&
2372-
!superclassCtor->isDynamic() &&
2373-
!ctor->getAttrs().hasAttribute<InlinableAttr>()) {
2368+
} else if (access == AccessLevel::Internal && !superclassCtor->isDynamic()){
2369+
// Inherit the @usableFromInline attribute.
23742370
auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);
23752371
ctor->getAttrs().add(clonedAttr);
23762372
}
@@ -2482,8 +2478,8 @@ swift::createDesignatedInitOverride(TypeChecker &tc,
24822478
tc.configureInterfaceType(ctor, genericSig);
24832479
ctor->setValidationToChecked();
24842480

2485-
configureDesignatedInitAttributes(tc, classDecl,
2486-
ctor, superclassCtor);
2481+
configureInheritedDesignatedInitAttributes(tc, classDecl, ctor,
2482+
superclassCtor);
24872483

24882484
if (kind == DesignatedInitKind::Stub) {
24892485
// Make this a stub implementation.

lib/Sema/TypeCheckAttr.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,11 +1934,10 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
19341934
void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) {
19351935
auto *VD = cast<ValueDecl>(D);
19361936

1937-
auto access = VD->getFormalAccess(/*useDC=*/nullptr,
1938-
/*treatUsableFromInlineAsPublic=*/true);
1939-
if (access < AccessLevel::Public) {
1937+
if (VD->getFormalAccess() < AccessLevel::Public &&
1938+
!VD->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
19401939
diagnoseAndRemoveAttr(attr, diag::fixed_layout_attr_on_internal_type,
1941-
VD->getFullName(), access);
1940+
VD->getFullName(), VD->getFormalAccess());
19421941
}
19431942
}
19441943

@@ -2093,9 +2092,8 @@ void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) {
20932092
break;
20942093
}
20952094

2096-
auto access = ED->getFormalAccess(/*useDC=*/nullptr,
2097-
/*treatUsableFromInlineAsPublic=*/true);
2098-
if (access < AccessLevel::Public) {
2095+
if (ED->getFormalAccess() < AccessLevel::Public &&
2096+
!ED->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
20992097
diagnoseAndRemoveAttr(attr, diag::enum_frozen_nonpublic, attr);
21002098
}
21012099
}

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,8 +2957,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
29572957
// already complained about the class being inherently
29582958
// un-subclassable.
29592959
if (!isInvalidSuperclass &&
2960-
Super->getFormalAccess(CD->getDeclContext())
2961-
< AccessLevel::Open &&
2960+
!Super->hasOpenAccess(CD->getDeclContext()) &&
29622961
Super->getModuleContext() != CD->getModuleContext()) {
29632962
TC.diagnose(CD, diag::superclass_not_open, superclassTy);
29642963
isInvalidSuperclass = true;

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -794,17 +794,16 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
794794
// defining module. This is not required for constructors, which are
795795
// never really "overridden" in the intended sense here, because of
796796
// course derived classes will change how the class is initialized.
797-
AccessLevel matchAccess = baseDecl->getFormalAccess(dc);
797+
bool baseHasOpenAccess = baseDecl->hasOpenAccess(dc);
798798
if (!isAccessor &&
799-
matchAccess < AccessLevel::Open &&
799+
!baseHasOpenAccess &&
800800
baseDecl->getModuleContext() != decl->getModuleContext() &&
801801
!isa<ConstructorDecl>(decl)) {
802802
diags.diagnose(decl, diag::override_of_non_open,
803803
decl->getDescriptiveKind());
804804

805-
} else if (matchAccess == AccessLevel::Open &&
806-
classDecl->getFormalAccess(dc) ==
807-
AccessLevel::Open &&
805+
} else if (baseHasOpenAccess &&
806+
classDecl->hasOpenAccess(dc) &&
808807
decl->getFormalAccess() != AccessLevel::Open &&
809808
!decl->isFinal()) {
810809
{
@@ -847,7 +846,7 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
847846
if (shouldDiagnose || shouldDiagnoseSetter) {
848847
bool overriddenForcesAccess =
849848
(requiredAccessScope->hasEqualDeclContextWith(matchAccessScope) &&
850-
matchAccess != AccessLevel::Open);
849+
!baseHasOpenAccess);
851850
AccessLevel requiredAccess =
852851
requiredAccessScope->requiredAccessForDiagnostics();
853852
{

test/SILGen/inlinable_attribute.swift

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
2-
// RUN: %target-swift-emit-silgen -module-name inlinable_attribute -enable-sil-ownership -emit-verbose-sil %s | %FileCheck %s
1+
// RUN: %target-swift-emit-silgen -module-name inlinable_attribute -enable-sil-ownership -emit-verbose-sil -warnings-as-errors %s | %FileCheck %s
32

43
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute15fragileFunctionyyF : $@convention(thin) () -> ()
54
@inlinable public func fragileFunction() {
@@ -79,23 +78,54 @@ public class Horse {
7978
_ = h.gallop
8079
}
8180

81+
@_fixed_layout
82+
public class PublicBase {
83+
@inlinable
84+
public init(horse: Horse) {}
85+
}
86+
8287
@usableFromInline
8388
@_fixed_layout
84-
class Base {
89+
class UFIBase {
8590
@usableFromInline
8691
@inlinable
8792
init(horse: Horse) {}
8893
}
8994

90-
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute7DerivedCfd : $@convention(method) (@guaranteed Derived) -> @owned Builtin.NativeObject
91-
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute7DerivedCfD : $@convention(method) (@owned Derived) -> ()
95+
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute017PublicDerivedFromC0Cfd : $@convention(method) (@guaranteed PublicDerivedFromPublic) -> @owned Builtin.NativeObject
96+
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute017PublicDerivedFromC0CfD : $@convention(method) (@owned PublicDerivedFromPublic) -> ()
9297

9398
// Make sure the synthesized delegating initializer is inlinable also
9499

95-
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute7DerivedC5horseAcA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned Derived) -> @owned Derived
100+
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute017PublicDerivedFromC0C5horseAcA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned PublicDerivedFromPublic) -> @owned PublicDerivedFromPublic
101+
@_fixed_layout
102+
public class PublicDerivedFromPublic : PublicBase {
103+
// Allow @inlinable deinits
104+
@inlinable deinit {}
105+
}
106+
107+
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute20UFIDerivedFromPublicC5horseAcA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned UFIDerivedFromPublic) -> @owned UFIDerivedFromPublic
108+
@usableFromInline
109+
@_fixed_layout
110+
class UFIDerivedFromPublic : PublicBase {
111+
}
112+
113+
// CHECK-LABEL: sil [serialized] @$S19inlinable_attribute17UFIDerivedFromUFIC5horseAcA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned UFIDerivedFromUFI) -> @owned UFIDerivedFromUFI
96114
@usableFromInline
97115
@_fixed_layout
98-
class Derived : Base {
116+
class UFIDerivedFromUFI : UFIBase {
99117
// Allow @inlinable deinits
100118
@inlinable deinit {}
101119
}
120+
121+
// CHECK-LABEL: sil hidden @$S19inlinable_attribute25InternalDerivedFromPublicC5horseAcA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned InternalDerivedFromPublic) -> @owned InternalDerivedFromPublic
122+
class InternalDerivedFromPublic : PublicBase {}
123+
124+
// CHECK-LABEL: sil hidden @$S19inlinable_attribute22InternalDerivedFromUFIC5horseAcA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned InternalDerivedFromUFI) -> @owned InternalDerivedFromUFI
125+
class InternalDerivedFromUFI : UFIBase {}
126+
127+
// CHECK-LABEL: sil private @$S19inlinable_attribute24PrivateDerivedFromPublic{{.+}}LLC5horseAdA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned PrivateDerivedFromPublic) -> @owned PrivateDerivedFromPublic
128+
private class PrivateDerivedFromPublic : PublicBase {}
129+
130+
// CHECK-LABEL: sil private @$S19inlinable_attribute21PrivateDerivedFromUFI{{.+}}LLC5horseAdA5HorseC_tcfc : $@convention(method) (@owned Horse, @owned PrivateDerivedFromUFI) -> @owned PrivateDerivedFromUFI
131+
private class PrivateDerivedFromUFI : UFIBase {}

0 commit comments

Comments
 (0)