Skip to content

[5.7] Fix issues blocking enablement of -target-min-inlining-version min #59176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions include/swift/AST/Availability.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ class VersionRange {
return getLowerEndpoint() >= Other.getLowerEndpoint();
}

// Returns true if all the versions in the Other range are versions in this
// range and the ranges are not equal.
bool isSupersetOf(const VersionRange &Other) const {
if (isEmpty() || Other.isAll())
return false;

if (isAll() || Other.isEmpty())
return true;

return getLowerEndpoint() < Other.getLowerEndpoint();
}

/// Mutates this range to be a best-effort underapproximation of
/// the intersection of itself and Other. This is the
/// meet operation (greatest lower bound) in the version range lattice.
Expand Down Expand Up @@ -244,10 +256,17 @@ class AvailabilityContext {
/// Returns true if \p other makes stronger guarantees than this context.
///
/// That is, `a.isContainedIn(b)` implies `a.union(b) == b`.
bool isContainedIn(AvailabilityContext other) const {
bool isContainedIn(const AvailabilityContext &other) const {
return OSVersion.isContainedIn(other.OSVersion);
}

/// Returns true if \p other is a strict subset of this context.
///
/// That is, `a.isSupersetOf(b)` implies `a != b` and `a.union(b) == a`.
bool isSupersetOf(const AvailabilityContext &other) const {
return OSVersion.isSupersetOf(other.OSVersion);
}

/// Returns true if this context has constraints that make it impossible to
/// actually occur.
///
Expand All @@ -272,7 +291,7 @@ class AvailabilityContext {
///
/// As an example, this is used when figuring out the required availability
/// for a type that references multiple nominal decls.
void intersectWith(AvailabilityContext other) {
void intersectWith(const AvailabilityContext &other) {
OSVersion.intersectWith(other.getOSVersion());
}

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

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

Expand Down
10 changes: 10 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5584,6 +5584,16 @@ ERROR(availability_decl_only_version_newer, none,
"%0 is only available in %1 %2 or newer",
(DeclName, StringRef, llvm::VersionTuple))

ERROR(availability_decl_only_version_newer_for_clients, none,
"%0 is only available in %1 %2 or newer; clients of %3 may have a lower"
" deployment target",
(DeclName, StringRef, llvm::VersionTuple, ModuleDecl *))

WARNING(availability_decl_only_version_newer_for_clients_warn, none,
"%0 is only available in %1 %2 or newer; clients of %3 may have a lower"
" deployment target",
(DeclName, StringRef, llvm::VersionTuple, ModuleDecl *))

ERROR(availability_opaque_types_only_version_newer, none,
"'some' return types are only available in %0 %1 or newer",
(StringRef, llvm::VersionTuple))
Expand Down
30 changes: 16 additions & 14 deletions include/swift/AST/TypeRefinementContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,19 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
/// The root refinement context.
Root,

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

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

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

Decl *getAsDecl() const {
assert(IntroReason == Reason::Decl ||
IntroReason == Reason::ResilienceBoundary);
IntroReason == Reason::DeclImplicit);
return D;
}

Expand Down Expand Up @@ -164,8 +168,8 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {

SourceRange SrcRange;

/// A canonical availiability info for this context, computed top-down from the root
/// context (compilation deployment target).
/// A canonical availability info for this context, computed top-down from the
/// root context.
AvailabilityContext AvailabilityInfo;

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

/// Create a refinement context for the given declaration.
static TypeRefinementContext *
createForResilienceBoundary(ASTContext &Ctx, Decl *D,
TypeRefinementContext *Parent,
const AvailabilityContext &Info,
SourceRange SrcRange);
createForDeclImplicit(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent,
const AvailabilityContext &Info, SourceRange SrcRange);

/// Create a refinement context for the Then branch of the given IfStmt.
static TypeRefinementContext *
Expand Down
27 changes: 11 additions & 16 deletions lib/AST/TypeRefinementContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,14 @@ TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D,
TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, ExplicitInfo);
}

TypeRefinementContext *
TypeRefinementContext::createForResilienceBoundary(ASTContext &Ctx, Decl *D,
TypeRefinementContext *Parent,
const AvailabilityContext &Info,
SourceRange SrcRange) {
TypeRefinementContext *TypeRefinementContext::createForDeclImplicit(
ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent,
const AvailabilityContext &Info, SourceRange SrcRange) {
assert(D);
assert(Parent);
return new (Ctx)
TypeRefinementContext(Ctx, IntroNode(D, Reason::ResilienceBoundary),
Parent, SrcRange, Info,
AvailabilityContext::alwaysAvailable());

return new (Ctx) TypeRefinementContext(
Ctx, IntroNode(D, Reason::DeclImplicit), Parent, SrcRange, Info,
AvailabilityContext::alwaysAvailable());
}

TypeRefinementContext *
Expand Down Expand Up @@ -184,7 +180,7 @@ void TypeRefinementContext::dump(raw_ostream &OS, SourceManager &SrcMgr) const {
SourceLoc TypeRefinementContext::getIntroductionLoc() const {
switch (getReason()) {
case Reason::Decl:
case Reason::ResilienceBoundary:
case Reason::DeclImplicit:
return Node.getAsDecl()->getLoc();

case Reason::IfStmtThenBranch:
Expand Down Expand Up @@ -298,7 +294,7 @@ TypeRefinementContext::getAvailabilityConditionVersionSourceRange(
Node.getAsWhileStmt()->getCond(), Platform, Version);

case Reason::Root:
case Reason::ResilienceBoundary:
case Reason::DeclImplicit:
return SourceRange();
}

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

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

if (getReason() == Reason::Decl
|| getReason() == Reason::ResilienceBoundary) {
if (getReason() == Reason::Decl || getReason() == Reason::DeclImplicit) {
Decl *D = Node.getAsDecl();
OS << " decl=";
if (auto VD = dyn_cast<ValueDecl>(D)) {
Expand Down Expand Up @@ -355,8 +350,8 @@ StringRef TypeRefinementContext::getReasonName(Reason R) {
case Reason::Decl:
return "decl";

case Reason::ResilienceBoundary:
return "resilience_boundary";
case Reason::DeclImplicit:
return "decl_implicit";

case Reason::IfStmtThenBranch:
return "if_then";
Expand Down
55 changes: 28 additions & 27 deletions lib/Sema/TypeCheckAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1579,22 +1579,15 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {

void checkType(Type type, const TypeRepr *typeRepr, const Decl *context,
ExportabilityReason reason=ExportabilityReason::General,
bool allowUnavailableProtocol=false) {
DeclAvailabilityFlags flags=None) {
// Don't bother checking errors.
if (type && type->hasError())
return;

DeclAvailabilityFlags flags = None;

// We allow a type to conform to a protocol that is less available than
// the type itself. This enables a type to retroactively model or directly
// conform to a protocol only available on newer OSes and yet still be used on
// older OSes.
//
// To support this, inside inheritance clauses we allow references to
// protocols that are unavailable in the current type refinement context.
if (allowUnavailableProtocol)
flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol;

// If the decl which references this type is unavailable on the current
// platform, don't diagnose the availability of the type.
if (AvailableAttr::isUnavailable(context))
return;

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

llvm::for_each(nominal->getInherited(),
[&](TypeLoc inherited) {
checkType(inherited.getType(), inherited.getTypeRepr(),
nominal, ExportabilityReason::General,
/*allowUnavailableProtocol=*/true);
DeclAvailabilityFlags flags =
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol;

// 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`.
if (nominal->getAttrs().hasAttribute<UsableFromInlineAttr>())
flags |= DeclAvailabilityFlag::
WarnForPotentialUnavailabilityBeforeDeploymentTarget;

llvm::for_each(nominal->getInherited(), [&](TypeLoc inherited) {
checkType(inherited.getType(), inherited.getTypeRepr(), nominal,
ExportabilityReason::General, flags);
});
}

void visitProtocolDecl(ProtocolDecl *proto) {
llvm::for_each(proto->getInherited(),
[&](TypeLoc requirement) {
llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) {
checkType(requirement.getType(), requirement.getTypeRepr(), proto,
ExportabilityReason::General,
/*allowUnavailableProtocol=*/false);
ExportabilityReason::General);
});

if (proto->getTrailingWhereClause()) {
Expand Down Expand Up @@ -1841,11 +1843,10 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
//
// 1) If the extension defines conformances, the conformed-to protocols
// must be exported.
llvm::for_each(ED->getInherited(),
[&](TypeLoc inherited) {
checkType(inherited.getType(), inherited.getTypeRepr(),
ED, ExportabilityReason::General,
/*allowUnavailableProtocol=*/true);
llvm::for_each(ED->getInherited(), [&](TypeLoc inherited) {
checkType(inherited.getType(), inherited.getTypeRepr(), ED,
ExportabilityReason::General,
DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol);
});

auto wasWhere = Where;
Expand Down
Loading