Skip to content

Commit 89dc5af

Browse files
committed
Sema: Targeted fix for bad interaction between resilience checks and -enable-testing
The -enable-testing flag makes ValueDecl::getEffectiveAccess() say that internal declarations are public. This would lead us to emit spurious diagnostics if a default argument of an internal function referenced a private symbol, for example, which is something we actually want to allow. This is a second revision of the patch -- instead of changing getEffectiveAccess() to take an extra parameter, this changes getFormalAccessScope() instead. Fixes <rdar://problem/32592973>.
1 parent 7cfa349 commit 89dc5af

File tree

9 files changed

+53
-36
lines changed

9 files changed

+53
-36
lines changed

include/swift/AST/Decl.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2162,6 +2162,8 @@ class ValueDecl : public Decl {
21622162
/// \see getFormalAccess
21632163
Accessibility getFormalAccessImpl(const DeclContext *useDC) const;
21642164

2165+
bool isVersionedInternalDecl() const;
2166+
21652167
/// Returns the access level specified explicitly by the user, or provided by
21662168
/// default according to language rules.
21672169
///
@@ -2171,9 +2173,16 @@ class ValueDecl : public Decl {
21712173
/// taken into account.
21722174
///
21732175
/// \sa getFormalAccessScope
2174-
Accessibility getFormalAccess(const DeclContext *useDC = nullptr) const {
2176+
Accessibility getFormalAccess(const DeclContext *useDC = nullptr,
2177+
bool respectVersionedAttr = false) const {
21752178
assert(hasAccessibility() && "accessibility not computed yet");
21762179
Accessibility result = TypeAndAccess.getInt().getValue();
2180+
if (respectVersionedAttr &&
2181+
result == Accessibility::Internal &&
2182+
isVersionedInternalDecl()) {
2183+
assert(!useDC);
2184+
return Accessibility::Public;
2185+
}
21772186
if (useDC && (result == Accessibility::Internal ||
21782187
result == Accessibility::Public))
21792188
return getFormalAccessImpl(useDC);
@@ -2194,7 +2203,8 @@ class ValueDecl : public Decl {
21942203
/// \sa getFormalAccess
21952204
/// \sa isAccessibleFrom
21962205
AccessScope
2197-
getFormalAccessScope(const DeclContext *useDC = nullptr) const;
2206+
getFormalAccessScope(const DeclContext *useDC = nullptr,
2207+
bool respectVersionedAttr = false) const;
21982208

21992209
/// Returns the access level that actually controls how a declaration should
22002210
/// be emitted and may be used.

lib/AST/Decl.cpp

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,8 @@ bool AbstractStorageDecl::hasFixedLayout() const {
13591359

13601360
// Private and (unversioned) internal variables always have a
13611361
// fixed layout.
1362-
if (getEffectiveAccess() < Accessibility::Public)
1362+
if (!getFormalAccessScope(/*useDC=*/nullptr,
1363+
/*respectVersionedAttr=*/true).isPublic())
13631364
return true;
13641365

13651366
// Check for an explicit @_fixed_layout attribute.
@@ -1915,18 +1916,18 @@ SourceLoc ValueDecl::getAttributeInsertionLoc(bool forModifier) const {
19151916

19161917
/// Returns true if \p VD needs to be treated as publicly-accessible
19171918
/// at the SIL, LLVM, and machine levels due to being versioned.
1918-
static bool isVersionedInternalDecl(const ValueDecl *VD) {
1919-
assert(VD->getFormalAccess() == Accessibility::Internal);
1919+
bool ValueDecl::isVersionedInternalDecl() const {
1920+
assert(getFormalAccess() == Accessibility::Internal);
19201921

1921-
if (VD->getAttrs().hasAttribute<VersionedAttr>())
1922+
if (getAttrs().hasAttribute<VersionedAttr>())
19221923
return true;
19231924

1924-
if (auto *FD = dyn_cast<FuncDecl>(VD))
1925+
if (auto *FD = dyn_cast<FuncDecl>(this))
19251926
if (auto *ASD = FD->getAccessorStorageDecl())
19261927
if (ASD->getAttrs().hasAttribute<VersionedAttr>())
19271928
return true;
19281929

1929-
if (auto *EED = dyn_cast<EnumElementDecl>(VD))
1930+
if (auto *EED = dyn_cast<EnumElementDecl>(this))
19301931
if (EED->getParentEnum()->getAttrs().hasAttribute<VersionedAttr>())
19311932
return true;
19321933

@@ -1953,21 +1954,17 @@ static Accessibility getTestableAccess(const ValueDecl *decl) {
19531954
}
19541955

19551956
Accessibility ValueDecl::getEffectiveAccess() const {
1956-
Accessibility effectiveAccess = getFormalAccess();
1957+
auto effectiveAccess = getFormalAccess(/*useDC=*/nullptr,
1958+
/*respectVersionedAttr=*/true);
19571959

19581960
// Handle @testable.
19591961
switch (effectiveAccess) {
1960-
case Accessibility::Public:
1961-
if (getModuleContext()->isTestingEnabled())
1962-
effectiveAccess = getTestableAccess(this);
1963-
break;
19641962
case Accessibility::Open:
19651963
break;
1964+
case Accessibility::Public:
19661965
case Accessibility::Internal:
19671966
if (getModuleContext()->isTestingEnabled())
19681967
effectiveAccess = getTestableAccess(this);
1969-
else if (isVersionedInternalDecl(this))
1970-
effectiveAccess = Accessibility::Public;
19711968
break;
19721969
case Accessibility::FilePrivate:
19731970
break;
@@ -2008,23 +2005,28 @@ Accessibility ValueDecl::getFormalAccessImpl(const DeclContext *useDC) const {
20082005
return getFormalAccess();
20092006
}
20102007

2011-
AccessScope ValueDecl::getFormalAccessScope(const DeclContext *useDC) const {
2008+
AccessScope ValueDecl::getFormalAccessScope(const DeclContext *useDC,
2009+
bool respectVersionedAttr) const {
20122010
const DeclContext *result = getDeclContext();
2013-
Accessibility access = getFormalAccess(useDC);
2011+
Accessibility access = getFormalAccess(useDC, respectVersionedAttr);
20142012

20152013
while (!result->isModuleScopeContext()) {
20162014
if (result->isLocalContext() || access == Accessibility::Private)
20172015
return AccessScope(result, true);
20182016

20192017
if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(result)) {
2020-
access = std::min(access, enclosingNominal->getFormalAccess(useDC));
2018+
access = std::min(access,
2019+
enclosingNominal->getFormalAccess(useDC,
2020+
respectVersionedAttr));
20212021

20222022
} else if (auto enclosingExt = dyn_cast<ExtensionDecl>(result)) {
20232023
// Just check the base type. If it's a constrained extension, Sema should
20242024
// have already enforced access more strictly.
20252025
if (auto extendedTy = enclosingExt->getExtendedType()) {
20262026
if (auto nominal = extendedTy->getAnyNominal()) {
2027-
access = std::min(access, nominal->getFormalAccess(useDC));
2027+
access = std::min(access,
2028+
nominal->getFormalAccess(useDC,
2029+
respectVersionedAttr));
20282030
}
20292031
}
20302032

@@ -2117,7 +2119,8 @@ int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) {
21172119
bool NominalTypeDecl::hasFixedLayout() const {
21182120
// Private and (unversioned) internal types always have a
21192121
// fixed layout.
2120-
if (getEffectiveAccess() < Accessibility::Public)
2122+
if (!getFormalAccessScope(/*useDC=*/nullptr,
2123+
/*respectVersionedAttr=*/true).isPublic())
21212124
return true;
21222125

21232126
// Check for an explicit @_fixed_layout attribute.

lib/AST/DeclContext.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
550550

551551
// If the function is not externally visible, we will not be serializing
552552
// its body.
553-
if (AFD->getEffectiveAccess() < Accessibility::Public)
553+
if (!AFD->getFormalAccessScope(/*useDC=*/nullptr,
554+
/*respectVersionedAttr=*/true).isPublic())
554555
break;
555556

556557
// Bodies of public transparent and always-inline functions are

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ bool TypeChecker::diagnoseInlineableDeclRef(SourceLoc loc,
9191
return false;
9292

9393
// Public declarations are OK.
94-
if (D->getEffectiveAccess() >= Accessibility::Public)
94+
if (D->getFormalAccessScope(/*useDC=*/nullptr,
95+
/*respectVersionedAttr=*/true).isPublic())
9596
return false;
9697

9798
// Enum cases are handled as part of their containing enum.
@@ -110,7 +111,8 @@ bool TypeChecker::diagnoseInlineableDeclRef(SourceLoc loc,
110111

111112
diagnose(loc, diag::resilience_decl_unavailable,
112113
D->getDescriptiveKind(), D->getFullName(),
113-
D->getFormalAccess(), getFragileFunctionKind(DC));
114+
D->getFormalAccessScope().accessibilityForDiagnostics(),
115+
getFragileFunctionKind(DC));
114116
diagnose(D, diag::resilience_decl_declared_here,
115117
D->getDescriptiveKind(), D->getFullName());
116118
return true;

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,19 +1824,16 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
18241824
/*allowConcreteGenericParams=*/true);
18251825
}
18261826

1827-
static Accessibility getAccessForDiagnostics(const ValueDecl *D) {
1828-
return std::min(D->getFormalAccess(),
1829-
D->getEffectiveAccess());
1830-
}
1831-
18321827
void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) {
18331828
auto *VD = cast<ValueDecl>(D);
18341829

1835-
if (VD->getEffectiveAccess() < Accessibility::Public) {
1830+
auto access = VD->getFormalAccess(/*useDC=*/nullptr,
1831+
/*respectVersionedAttr=*/true);
1832+
if (access < Accessibility::Public) {
18361833
TC.diagnose(attr->getLocation(),
18371834
diag::fixed_layout_attr_on_internal_type,
18381835
VD->getBaseName(),
1839-
getAccessForDiagnostics(VD))
1836+
access)
18401837
.fixItRemove(attr->getRangeWithAt());
18411838
attr->setInvalid();
18421839
}
@@ -1886,13 +1883,13 @@ void AttributeChecker::visitInlineableAttr(InlineableAttr *attr) {
18861883

18871884
// @_inlineable can only be applied to public or @_versioned
18881885
// declarations.
1889-
if (VD->getFormalAccess() < Accessibility::Internal ||
1890-
(!VD->getAttrs().hasAttribute<VersionedAttr>() &&
1891-
VD->getFormalAccess() < Accessibility::Public)) {
1886+
auto access = VD->getFormalAccess(/*useDC=*/nullptr,
1887+
/*respectVersionedAttr=*/true);
1888+
if (access < Accessibility::Public) {
18921889
TC.diagnose(attr->getLocation(),
18931890
diag::inlineable_decl_not_public,
18941891
VD->getBaseName(),
1895-
getAccessForDiagnostics(VD))
1892+
access)
18961893
.fixItRemove(attr->getRangeWithAt());
18971894
attr->setInvalid();
18981895
return;

lib/Sema/TypeCheckStmt.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1321,7 +1321,8 @@ static void checkDefaultArguments(TypeChecker &tc,
13211321
// caller.
13221322
auto expansion = func->getResilienceExpansion();
13231323
if (!tc.Context.isSwiftVersion3() &&
1324-
func->getEffectiveAccess() == Accessibility::Public)
1324+
func->getFormalAccessScope(/*useDC=*/nullptr,
1325+
/*respectVersionedAttr=*/true).isPublic())
13251326
expansion = ResilienceExpansion::Minimal;
13261327

13271328
for (auto &param : *params) {

test/attr/attr_fixed_layout.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// RUN: %target-swift-frontend -typecheck -verify -dump-ast -enable-resilience %s 2>&1 | %FileCheck --check-prefix=RESILIENCE-ON %s
2+
// RUN: %target-swift-frontend -typecheck -verify -dump-ast -enable-resilience -enable-testing %s 2>&1 | %FileCheck --check-prefix=RESILIENCE-ON %s
23
// RUN: %target-swift-frontend -typecheck -verify -dump-ast %s 2>&1 | %FileCheck --check-prefix=RESILIENCE-OFF %s
4+
// RUN: %target-swift-frontend -typecheck -verify -dump-ast %s -enable-testing 2>&1 | %FileCheck --check-prefix=RESILIENCE-OFF %s
35

46
//
57
// Public types with @_fixed_layout are always fixed layout
@@ -54,7 +56,6 @@ struct Rectangle {
5456
// expected-error@-1 {{'@_fixed_layout' attribute can only be applied to '@_versioned' or public declarations, but 'InternalStruct' is internal}}
5557

5658
@_fixed_layout public struct NestedStruct {}
57-
// expected-error@-1 {{'@_fixed_layout' attribute can only be applied to '@_versioned' or public declarations, but 'NestedStruct' is internal}}
5859
}
5960

6061
@_fixed_layout fileprivate struct FileprivateStruct {}

test/attr/attr_inlineable.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-testing
23

34
@_inlineable struct TestInlineableStruct {}
45
// expected-error@-1 {{@_inlineable cannot be applied to this declaration}}

test/attr/attr_versioned.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-typecheck-verify-swift -enable-testing
23

34
@_versioned private func privateVersioned() {}
45
// expected-error@-1 {{'@_versioned' attribute can only be applied to internal declarations, but 'privateVersioned' is private}}

0 commit comments

Comments
 (0)