Skip to content

Commit fe89764

Browse files
authored
Merge pull request #34390 from slavapestov/misc-availability-fixes
Another pile of availability checking fixes
2 parents fa9d4fd + 8af4405 commit fe89764

20 files changed

+359
-199
lines changed

include/swift/AST/Decl.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4763,11 +4763,19 @@ class VarDecl : public AbstractStorageDecl {
47634763

47644764
/// Determines if this var has an initializer expression that should be
47654765
/// exposed to clients.
4766+
///
47664767
/// There's a very narrow case when we would: if the decl is an instance
47674768
/// member with an initializer expression and the parent type is
47684769
/// @frozen and resides in a resilient module.
47694770
bool isInitExposedToClients() const;
4770-
4771+
4772+
/// Determines if this var is exposed as part of the layout of a
4773+
/// @frozen struct.
4774+
///
4775+
/// From the standpoint of access control and exportability checking, this
4776+
/// var will behave as if it was public, even if it is internal or private.
4777+
bool isLayoutExposedToClients() const;
4778+
47714779
/// Is this a special debugger variable?
47724780
bool isDebuggerVar() const { return Bits.VarDecl.IsDebuggerVar; }
47734781
void setDebuggerVar(bool IsDebuggerVar) {

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5061,12 +5061,12 @@ ERROR(local_type_in_inlinable_function,
50615061
(Identifier, unsigned))
50625062

50635063
ERROR(resilience_decl_unavailable,
5064-
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and "
5064+
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and "
50655065
"cannot be referenced from " FRAGILE_FUNC_KIND "3",
50665066
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))
50675067

50685068
WARNING(resilience_decl_unavailable_warn,
5069-
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and "
5069+
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and "
50705070
"should not be referenced from " FRAGILE_FUNC_KIND "3",
50715071
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))
50725072

lib/AST/Decl.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,12 +1582,32 @@ VarDecl *PatternBindingDecl::getAnchoringVarDecl(unsigned i) const {
15821582
}
15831583

15841584
bool VarDecl::isInitExposedToClients() const {
1585+
// 'lazy' initializers are emitted inside the getter, which is never
1586+
// @inlinable.
1587+
if (getAttrs().hasAttribute<LazyAttr>())
1588+
return false;
1589+
1590+
return hasInitialValue() && isLayoutExposedToClients();
1591+
}
1592+
1593+
bool VarDecl::isLayoutExposedToClients() const {
15851594
auto parent = dyn_cast<NominalTypeDecl>(getDeclContext());
15861595
if (!parent) return false;
1587-
if (!hasInitialValue()) return false;
15881596
if (isStatic()) return false;
1589-
return parent->getAttrs().hasAttribute<FrozenAttr>() ||
1590-
parent->getAttrs().hasAttribute<FixedLayoutAttr>();
1597+
1598+
if (!hasStorage() &&
1599+
!getAttrs().hasAttribute<LazyAttr>() &&
1600+
!hasAttachedPropertyWrapper()) {
1601+
return false;
1602+
}
1603+
1604+
auto nominalAccess =
1605+
parent->getFormalAccessScope(/*useDC=*/nullptr,
1606+
/*treatUsableFromInlineAsPublic=*/true);
1607+
if (!nominalAccess.isPublic()) return false;
1608+
1609+
return (parent->getAttrs().hasAttribute<FrozenAttr>() ||
1610+
parent->getAttrs().hasAttribute<FixedLayoutAttr>());
15911611
}
15921612

15931613
/// Check whether the given type representation will be

lib/AST/DeclContext.cpp

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -368,21 +368,17 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
368368

369369
// Stored property initializer contexts use minimal resilience expansion
370370
// if the type is formally fixed layout.
371-
if (isa<PatternBindingInitializer>(dc)) {
372-
if (auto *NTD = dyn_cast<NominalTypeDecl>(dc->getParent())) {
373-
auto nominalAccess =
374-
NTD->getFormalAccessScope(/*useDC=*/nullptr,
375-
/*treatUsableFromInlineAsPublic=*/true);
376-
if (!nominalAccess.isPublic())
377-
return {FragileFunctionKind::None,
378-
/*allowUsableFromInline=*/false};
379-
380-
if (NTD->isFormallyResilient())
381-
return {FragileFunctionKind::None,
382-
/*allowUsableFromInline=*/false};
383-
384-
return {FragileFunctionKind::PropertyInitializer, true};
371+
if (auto *init = dyn_cast <PatternBindingInitializer>(dc)) {
372+
auto bindingIndex = init->getBindingIndex();
373+
if (auto *varDecl = init->getBinding()->getAnchoringVarDecl(bindingIndex)) {
374+
if (varDecl->isInitExposedToClients()) {
375+
return {FragileFunctionKind::PropertyInitializer,
376+
/*allowUsableFromInline=*/true};
377+
}
385378
}
379+
380+
return {FragileFunctionKind::None,
381+
/*allowUsableFromInline=*/false};
386382
}
387383

388384
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc)) {
@@ -434,7 +430,8 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
434430
}
435431
}
436432

437-
return {FragileFunctionKind::None, false};
433+
return {FragileFunctionKind::None,
434+
/*allowUsableFromInline=*/false};
438435
}
439436

440437
/// Determine whether the innermost context is generic.

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,48 +27,24 @@
2727

2828
using namespace swift;
2929

30-
bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc,
31-
const ValueDecl *D,
32-
ExportContext where) {
33-
auto fragileKind = where.getFragileFunctionKind();
34-
if (fragileKind.kind == FragileFunctionKind::None)
35-
return false;
36-
37-
// Do some important fast-path checks that apply to all cases.
38-
39-
// Type parameters are OK.
40-
if (isa<AbstractTypeParamDecl>(D))
41-
return false;
42-
43-
// Check whether the declaration is accessible.
44-
if (diagnoseInlinableDeclRefAccess(loc, D, where))
45-
return true;
46-
47-
// Check whether the declaration comes from a publically-imported module.
48-
// Skip this check for accessors because the associated property or subscript
49-
// will also be checked, and will provide a better error message.
50-
if (!isa<AccessorDecl>(D))
51-
if (diagnoseDeclRefExportability(loc, D, where))
52-
return true;
53-
54-
return false;
55-
}
56-
5730
bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
5831
const ValueDecl *D,
5932
ExportContext where) {
60-
auto *DC = where.getDeclContext();
6133
auto fragileKind = where.getFragileFunctionKind();
62-
assert(fragileKind.kind != FragileFunctionKind::None);
34+
if (fragileKind.kind == FragileFunctionKind::None)
35+
return false;
6336

6437
// Local declarations are OK.
6538
if (D->getDeclContext()->isLocalContext())
6639
return false;
6740

68-
// Public declarations or SPI used from SPI are OK.
41+
auto *DC = where.getDeclContext();
42+
43+
// Public declarations are OK, even if they're SPI or came from an
44+
// implementation-only import. We'll diagnose exportability violations
45+
// from diagnoseDeclRefExportability().
6946
if (D->getFormalAccessScope(/*useDC=*/nullptr,
70-
fragileKind.allowUsableFromInline).isPublic() &&
71-
!(D->isSPI() && !DC->getInnermostDeclarationDeclContext()->isSPI()))
47+
fragileKind.allowUsableFromInline).isPublic())
7248
return false;
7349

7450
auto &Context = DC->getASTContext();
@@ -81,14 +57,6 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
8157
return false;
8258
}
8359

84-
// Property initializers that are not exposed to clients are OK.
85-
if (auto pattern = dyn_cast<PatternBindingInitializer>(DC)) {
86-
auto bindingIndex = pattern->getBindingIndex();
87-
auto *varDecl = pattern->getBinding()->getAnchoringVarDecl(bindingIndex);
88-
if (!varDecl->isInitExposedToClients())
89-
return false;
90-
}
91-
9260
DowngradeToWarning downgradeToWarning = DowngradeToWarning::No;
9361

9462
// Swift 4.2 did not perform any checks for type aliases.
@@ -145,6 +113,11 @@ bool
145113
TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
146114
const ValueDecl *D,
147115
ExportContext where) {
116+
// Accessors cannot have exportability that's different than the storage,
117+
// so skip them for now.
118+
if (isa<AccessorDecl>(D))
119+
return false;
120+
148121
if (!where.mustOnlyReferenceExportedDecls())
149122
return false;
150123

lib/Sema/TypeCheckAccess.cpp

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,29 +1051,26 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
10511051
UNINTERESTING(Accessor) // Handled by the Var or Subscript.
10521052
UNINTERESTING(OpaqueType) // Handled by the Var or Subscript.
10531053

1054-
/// If \p PBD declared stored instance properties in a fixed-contents struct,
1055-
/// return said struct.
1056-
static const StructDecl *
1057-
getFixedLayoutStructContext(const PatternBindingDecl *PBD) {
1058-
auto *parentStruct = dyn_cast<StructDecl>(PBD->getDeclContext());
1059-
if (!parentStruct)
1060-
return nullptr;
1061-
if (!(parentStruct->getAttrs().hasAttribute<FrozenAttr>() ||
1062-
parentStruct->getAttrs().hasAttribute<FixedLayoutAttr>()) ||
1063-
PBD->isStatic() || !PBD->hasStorage()) {
1064-
return nullptr;
1065-
}
1066-
// We don't check for "in resilient modules" because there's no reason to
1067-
// write '@_fixedLayout' on a struct in a non-resilient module.
1068-
return parentStruct;
1054+
/// If \p VD's layout is exposed by a @frozen struct or class, return said
1055+
/// struct or class.
1056+
///
1057+
/// Stored instance properties in @frozen structs and classes must always use
1058+
/// public/@usableFromInline types. In these cases, check the access against
1059+
/// the struct instead of the VarDecl, and customize the diagnostics.
1060+
static const ValueDecl *
1061+
getFixedLayoutStructContext(const VarDecl *VD) {
1062+
if (VD->isLayoutExposedToClients())
1063+
return dyn_cast<NominalTypeDecl>(VD->getDeclContext());
1064+
1065+
return nullptr;
10691066
}
10701067

10711068
/// \see visitPatternBindingDecl
10721069
void checkNamedPattern(const NamedPattern *NP,
1073-
const ValueDecl *fixedLayoutStructContext,
10741070
bool isTypeContext,
10751071
const llvm::DenseSet<const VarDecl *> &seenVars) {
10761072
const VarDecl *theVar = NP->getDecl();
1073+
auto *fixedLayoutStructContext = getFixedLayoutStructContext(theVar);
10771074
if (!fixedLayoutStructContext && shouldSkipChecking(theVar))
10781075
return;
10791076
// Only check individual variables if we didn't check an enclosing
@@ -1101,7 +1098,6 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
11011098

11021099
/// \see visitPatternBindingDecl
11031100
void checkTypedPattern(const TypedPattern *TP,
1104-
const ValueDecl *fixedLayoutStructContext,
11051101
bool isTypeContext,
11061102
llvm::DenseSet<const VarDecl *> &seenVars) {
11071103
// FIXME: We need an access level to check against, so we pull one out
@@ -1114,6 +1110,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
11141110
});
11151111
if (!anyVar)
11161112
return;
1113+
auto *fixedLayoutStructContext = getFixedLayoutStructContext(anyVar);
11171114
if (!fixedLayoutStructContext && shouldSkipChecking(anyVar))
11181115
return;
11191116

@@ -1154,27 +1151,18 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
11541151
void visitPatternBindingDecl(PatternBindingDecl *PBD) {
11551152
bool isTypeContext = PBD->getDeclContext()->isTypeContext();
11561153

1157-
// Stored instance properties in public/@usableFromInline fixed-contents
1158-
// structs in resilient modules must always use public/@usableFromInline
1159-
// types. In these cases, check the access against the struct instead of the
1160-
// VarDecl, and customize the diagnostics.
1161-
const ValueDecl *fixedLayoutStructContext =
1162-
getFixedLayoutStructContext(PBD);
1163-
11641154
llvm::DenseSet<const VarDecl *> seenVars;
11651155
for (auto idx : range(PBD->getNumPatternEntries())) {
11661156
PBD->getPattern(idx)->forEachNode([&](const Pattern *P) {
11671157
if (auto *NP = dyn_cast<NamedPattern>(P)) {
1168-
checkNamedPattern(NP, fixedLayoutStructContext, isTypeContext,
1169-
seenVars);
1158+
checkNamedPattern(NP, isTypeContext, seenVars);
11701159
return;
11711160
}
11721161

11731162
auto *TP = dyn_cast<TypedPattern>(P);
11741163
if (!TP)
11751164
return;
1176-
checkTypedPattern(TP, fixedLayoutStructContext, isTypeContext,
1177-
seenVars);
1165+
checkTypedPattern(TP, isTypeContext, seenVars);
11781166
});
11791167
seenVars.clear();
11801168
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -967,14 +967,15 @@ void AttributeChecker::visitSPIAccessControlAttr(SPIAccessControlAttr *attr) {
967967
}
968968

969969
// Forbid stored properties marked SPI in frozen types.
970-
if (auto property = dyn_cast<AbstractStorageDecl>(VD))
971-
if (auto DC = dyn_cast<NominalTypeDecl>(D->getDeclContext()))
972-
if (property->hasStorage() &&
973-
!DC->isFormallyResilient() &&
974-
!DC->isSPI())
970+
if (auto property = dyn_cast<VarDecl>(VD)) {
971+
if (auto NTD = dyn_cast<NominalTypeDecl>(D->getDeclContext())) {
972+
if (property->isLayoutExposedToClients() && !NTD->isSPI()) {
975973
diagnoseAndRemoveAttr(attr,
976974
diag::spi_attribute_on_frozen_stored_properties,
977975
VD->getName());
976+
}
977+
}
978+
}
978979
}
979980
}
980981

0 commit comments

Comments
 (0)