Skip to content

Commit 69d4e94

Browse files
authored
[5.7] Fix issues blocking enablement of -target-min-inlining-version min (swiftlang#59176)
Cherry-pick and squash of the following PRs to release/5.7: swiftlang#42585 swiftlang#58417 swiftlang#58654 swiftlang#58680 swiftlang#58707 swiftlang#58870 swiftlang#58963 swiftlang#59040 swiftlang#59065 * Sema: Fix `isExported()` for extension decls in order to correct availability diagnostics when `-target-min-inlining-version min` is specified. Resolves rdar://91382040 * Sema: Avoid diagnosing potential unavailability of type components (extension nominal type, superclass, etc.) on declarations that are explicitly unavailable. Resolves rdar://92179327 * Sema: Downgrade diagnostics about potential unavailability of the extended type in an extension declaration when the following conditions are met: 1. The extension is missing explicit availability. 2. The required availability is before the deployment target. Resolves rdar://92621567 * NFC: Expand `-target-min-inlining-versiong` tests to cover a number of corner cases: - unavailable declarations and unavailable containers - SPI declarations and spi containers - property initializer expressions - property wrappers * Sema: When computing potential unavailability of a decl, first check whether the decl is explicitly unavailable and the context is also unavailable. If those conditions are met, treat the decl as if it were always available since unavailable code is allowed to reference unavailable decls. Resolves rdar://92551870 * Sema: Use the deployment target when checking availability for SPI and unavailable API declarations with `-target-min-inlining-version min` specified. There's not much benefit to more accurate enforcement of availability in these decls since API clients can't use them and there's a lot of existing code that would be needlessly diagnosed without these exceptions. Resolves rdar://92716633 * Sema: Teach the compiler to refine `VarDecl` initializer expressions using the deployment target when the init would not be exposed to module clients. Without this, the initializers of public properties in API modules could be misdiagnosed as potentially unavailable to clients of the module, even though the expression will only ever execute on the deployment target or higher. Resolves rdar://92713589 * Tests: Update `attr_inlinable_available.swift` to require macOS and remove the OS versions for other platforms from availability attributes. * Tests: Add test cases for potential unavailability in class inheritance when is specified. * Sema: Reword diagnostics about potentially unavailable decls to improve clarity for authors of API libraries. When decls are diagnosed as potentially unavailable on an OS earlier than the deployment target, the message will now indicate that the issue would be faced by clients of the module. Resolves rdar://93466875 * Sema: Use the availability of the extended nominal as a floor for the availability of extensions. The primary motivation for this change is to reduce unnecessary availability diagnostics for API library authors. Many API libraries contain existing extension decls that lack declared availability where the extension introduces additional members to the extended type in the same release that the extended type was declared. Others contain extensions where the extension itself does not have declared availability but each of the members do. In both cases, the code is safe as written so the extra diagnostics would be a nuisance. Resolves rdar://93630782 * Sema: Downgrade diagnostics about inheritance from a less available type when `-target-min-inlining-version min` is specified. As a concession to source compatibility for API libraries, downgrade diagnostics about inheritance from a less available type when the following conditions are met: 1. The inherited type is only potentially unavailable before the deployment target. 2. The inheriting type is `@usableFromInline`. Resolves rdar://92747826
1 parent 4e208a6 commit 69d4e94

15 files changed

+1694
-1102
lines changed

include/swift/AST/Availability.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ class VersionRange {
9393
return getLowerEndpoint() >= Other.getLowerEndpoint();
9494
}
9595

96+
// Returns true if all the versions in the Other range are versions in this
97+
// range and the ranges are not equal.
98+
bool isSupersetOf(const VersionRange &Other) const {
99+
if (isEmpty() || Other.isAll())
100+
return false;
101+
102+
if (isAll() || Other.isEmpty())
103+
return true;
104+
105+
return getLowerEndpoint() < Other.getLowerEndpoint();
106+
}
107+
96108
/// Mutates this range to be a best-effort underapproximation of
97109
/// the intersection of itself and Other. This is the
98110
/// meet operation (greatest lower bound) in the version range lattice.
@@ -244,10 +256,17 @@ class AvailabilityContext {
244256
/// Returns true if \p other makes stronger guarantees than this context.
245257
///
246258
/// That is, `a.isContainedIn(b)` implies `a.union(b) == b`.
247-
bool isContainedIn(AvailabilityContext other) const {
259+
bool isContainedIn(const AvailabilityContext &other) const {
248260
return OSVersion.isContainedIn(other.OSVersion);
249261
}
250262

263+
/// Returns true if \p other is a strict subset of this context.
264+
///
265+
/// That is, `a.isSupersetOf(b)` implies `a != b` and `a.union(b) == a`.
266+
bool isSupersetOf(const AvailabilityContext &other) const {
267+
return OSVersion.isSupersetOf(other.OSVersion);
268+
}
269+
251270
/// Returns true if this context has constraints that make it impossible to
252271
/// actually occur.
253272
///
@@ -272,7 +291,7 @@ class AvailabilityContext {
272291
///
273292
/// As an example, this is used when figuring out the required availability
274293
/// for a type that references multiple nominal decls.
275-
void intersectWith(AvailabilityContext other) {
294+
void intersectWith(const AvailabilityContext &other) {
276295
OSVersion.intersectWith(other.getOSVersion());
277296
}
278297

@@ -283,7 +302,7 @@ class AvailabilityContext {
283302
/// treating some invalid deployment environments as available.
284303
///
285304
/// As an example, this is used for the true branch of `#available`.
286-
void constrainWith(AvailabilityContext other) {
305+
void constrainWith(const AvailabilityContext &other) {
287306
OSVersion.constrainWith(other.getOSVersion());
288307
}
289308

@@ -295,7 +314,7 @@ class AvailabilityContext {
295314
///
296315
/// As an example, this is used for the else branch of a conditional with
297316
/// multiple `#available` checks.
298-
void unionWith(AvailabilityContext other) {
317+
void unionWith(const AvailabilityContext &other) {
299318
OSVersion.unionWith(other.getOSVersion());
300319
}
301320

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5582,6 +5582,16 @@ ERROR(availability_decl_only_version_newer, none,
55825582
"%0 is only available in %1 %2 or newer",
55835583
(DeclName, StringRef, llvm::VersionTuple))
55845584

5585+
ERROR(availability_decl_only_version_newer_for_clients, none,
5586+
"%0 is only available in %1 %2 or newer; clients of %3 may have a lower"
5587+
" deployment target",
5588+
(DeclName, StringRef, llvm::VersionTuple, ModuleDecl *))
5589+
5590+
WARNING(availability_decl_only_version_newer_for_clients_warn, none,
5591+
"%0 is only available in %1 %2 or newer; clients of %3 may have a lower"
5592+
" deployment target",
5593+
(DeclName, StringRef, llvm::VersionTuple, ModuleDecl *))
5594+
55855595
ERROR(availability_opaque_types_only_version_newer, none,
55865596
"'some' return types are only available in %0 %1 or newer",
55875597
(StringRef, llvm::VersionTuple))

include/swift/AST/TypeRefinementContext.h

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,19 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
5454
/// The root refinement context.
5555
Root,
5656

57-
/// The context was introduced by a declaration (e.g., the body of a
58-
/// function declaration or the contents of a class declaration).
57+
/// The context was introduced by a declaration with an explicit
58+
/// availability attribute. The context contains both the signature and the
59+
/// body of the declaration.
5960
Decl,
6061

61-
/// The context was introduced by a resilience boundary; that is, we are in
62-
/// a module with library evolution enabled and the parent context's
63-
/// contents can be visible to the module's clients, but this context's
64-
/// contents are not.
65-
ResilienceBoundary,
62+
/// The context was introduced implicitly by a declaration. The context may
63+
/// cover the entire declaration or it may cover a subset of it. For
64+
/// example, a public, non-inlinable function declaration in an API module
65+
/// will have at least two associated contexts: one for the entire
66+
/// declaration at the declared availability of the API and a nested
67+
/// implicit context for the body of the function, which will always run at
68+
/// the deployment target of the library.
69+
DeclImplicit,
6670

6771
/// The context was introduced for the Then branch of an IfStmt.
6872
IfStmtThenBranch,
@@ -132,7 +136,7 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
132136

133137
Decl *getAsDecl() const {
134138
assert(IntroReason == Reason::Decl ||
135-
IntroReason == Reason::ResilienceBoundary);
139+
IntroReason == Reason::DeclImplicit);
136140
return D;
137141
}
138142

@@ -164,8 +168,8 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
164168

165169
SourceRange SrcRange;
166170

167-
/// A canonical availiability info for this context, computed top-down from the root
168-
/// context (compilation deployment target).
171+
/// A canonical availability info for this context, computed top-down from the
172+
/// root context.
169173
AvailabilityContext AvailabilityInfo;
170174

171175
/// If this context was annotated with an availability attribute, this property captures that.
@@ -195,10 +199,8 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
195199

196200
/// Create a refinement context for the given declaration.
197201
static TypeRefinementContext *
198-
createForResilienceBoundary(ASTContext &Ctx, Decl *D,
199-
TypeRefinementContext *Parent,
200-
const AvailabilityContext &Info,
201-
SourceRange SrcRange);
202+
createForDeclImplicit(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent,
203+
const AvailabilityContext &Info, SourceRange SrcRange);
202204

203205
/// Create a refinement context for the Then branch of the given IfStmt.
204206
static TypeRefinementContext *

lib/AST/TypeRefinementContext.cpp

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,14 @@ TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D,
6464
TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, ExplicitInfo);
6565
}
6666

67-
TypeRefinementContext *
68-
TypeRefinementContext::createForResilienceBoundary(ASTContext &Ctx, Decl *D,
69-
TypeRefinementContext *Parent,
70-
const AvailabilityContext &Info,
71-
SourceRange SrcRange) {
67+
TypeRefinementContext *TypeRefinementContext::createForDeclImplicit(
68+
ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent,
69+
const AvailabilityContext &Info, SourceRange SrcRange) {
7270
assert(D);
7371
assert(Parent);
74-
return new (Ctx)
75-
TypeRefinementContext(Ctx, IntroNode(D, Reason::ResilienceBoundary),
76-
Parent, SrcRange, Info,
77-
AvailabilityContext::alwaysAvailable());
78-
72+
return new (Ctx) TypeRefinementContext(
73+
Ctx, IntroNode(D, Reason::DeclImplicit), Parent, SrcRange, Info,
74+
AvailabilityContext::alwaysAvailable());
7975
}
8076

8177
TypeRefinementContext *
@@ -184,7 +180,7 @@ void TypeRefinementContext::dump(raw_ostream &OS, SourceManager &SrcMgr) const {
184180
SourceLoc TypeRefinementContext::getIntroductionLoc() const {
185181
switch (getReason()) {
186182
case Reason::Decl:
187-
case Reason::ResilienceBoundary:
183+
case Reason::DeclImplicit:
188184
return Node.getAsDecl()->getLoc();
189185

190186
case Reason::IfStmtThenBranch:
@@ -298,7 +294,7 @@ TypeRefinementContext::getAvailabilityConditionVersionSourceRange(
298294
Node.getAsWhileStmt()->getCond(), Platform, Version);
299295

300296
case Reason::Root:
301-
case Reason::ResilienceBoundary:
297+
case Reason::DeclImplicit:
302298
return SourceRange();
303299
}
304300

@@ -312,8 +308,7 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
312308

313309
OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString();
314310

315-
if (getReason() == Reason::Decl
316-
|| getReason() == Reason::ResilienceBoundary) {
311+
if (getReason() == Reason::Decl || getReason() == Reason::DeclImplicit) {
317312
Decl *D = Node.getAsDecl();
318313
OS << " decl=";
319314
if (auto VD = dyn_cast<ValueDecl>(D)) {
@@ -355,8 +350,8 @@ StringRef TypeRefinementContext::getReasonName(Reason R) {
355350
case Reason::Decl:
356351
return "decl";
357352

358-
case Reason::ResilienceBoundary:
359-
return "resilience_boundary";
353+
case Reason::DeclImplicit:
354+
return "decl_implicit";
360355

361356
case Reason::IfStmtThenBranch:
362357
return "if_then";

lib/Sema/TypeCheckAccess.cpp

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,22 +1579,15 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
15791579

15801580
void checkType(Type type, const TypeRepr *typeRepr, const Decl *context,
15811581
ExportabilityReason reason=ExportabilityReason::General,
1582-
bool allowUnavailableProtocol=false) {
1582+
DeclAvailabilityFlags flags=None) {
15831583
// Don't bother checking errors.
15841584
if (type && type->hasError())
15851585
return;
1586-
1587-
DeclAvailabilityFlags flags = None;
1588-
1589-
// We allow a type to conform to a protocol that is less available than
1590-
// the type itself. This enables a type to retroactively model or directly
1591-
// conform to a protocol only available on newer OSes and yet still be used on
1592-
// older OSes.
1593-
//
1594-
// To support this, inside inheritance clauses we allow references to
1595-
// protocols that are unavailable in the current type refinement context.
1596-
if (allowUnavailableProtocol)
1597-
flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol;
1586+
1587+
// If the decl which references this type is unavailable on the current
1588+
// platform, don't diagnose the availability of the type.
1589+
if (AvailableAttr::isUnavailable(context))
1590+
return;
15981591

15991592
diagnoseTypeAvailability(typeRepr, type, context->getLoc(),
16001593
Where.withReason(reason), flags);
@@ -1753,20 +1746,29 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
17531746
void visitNominalTypeDecl(const NominalTypeDecl *nominal) {
17541747
checkGenericParams(nominal, nominal);
17551748

1756-
llvm::for_each(nominal->getInherited(),
1757-
[&](TypeLoc inherited) {
1758-
checkType(inherited.getType(), inherited.getTypeRepr(),
1759-
nominal, ExportabilityReason::General,
1760-
/*allowUnavailableProtocol=*/true);
1749+
DeclAvailabilityFlags flags =
1750+
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol;
1751+
1752+
// As a concession to source compatibility for API libraries, downgrade
1753+
// diagnostics about inheritance from a less available type when the
1754+
// following conditions are met:
1755+
// 1. The inherited type is only potentially unavailable before the
1756+
// deployment target.
1757+
// 2. The inheriting type is `@usableFromInline`.
1758+
if (nominal->getAttrs().hasAttribute<UsableFromInlineAttr>())
1759+
flags |= DeclAvailabilityFlag::
1760+
WarnForPotentialUnavailabilityBeforeDeploymentTarget;
1761+
1762+
llvm::for_each(nominal->getInherited(), [&](TypeLoc inherited) {
1763+
checkType(inherited.getType(), inherited.getTypeRepr(), nominal,
1764+
ExportabilityReason::General, flags);
17611765
});
17621766
}
17631767

17641768
void visitProtocolDecl(ProtocolDecl *proto) {
1765-
llvm::for_each(proto->getInherited(),
1766-
[&](TypeLoc requirement) {
1769+
llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) {
17671770
checkType(requirement.getType(), requirement.getTypeRepr(), proto,
1768-
ExportabilityReason::General,
1769-
/*allowUnavailableProtocol=*/false);
1771+
ExportabilityReason::General);
17701772
});
17711773

17721774
if (proto->getTrailingWhereClause()) {
@@ -1841,11 +1843,10 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
18411843
//
18421844
// 1) If the extension defines conformances, the conformed-to protocols
18431845
// must be exported.
1844-
llvm::for_each(ED->getInherited(),
1845-
[&](TypeLoc inherited) {
1846-
checkType(inherited.getType(), inherited.getTypeRepr(),
1847-
ED, ExportabilityReason::General,
1848-
/*allowUnavailableProtocol=*/true);
1846+
llvm::for_each(ED->getInherited(), [&](TypeLoc inherited) {
1847+
checkType(inherited.getType(), inherited.getTypeRepr(), ED,
1848+
ExportabilityReason::General,
1849+
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol);
18491850
});
18501851

18511852
auto wasWhere = Where;

0 commit comments

Comments
 (0)