[ModuleInterface] Propagate availability for synthesized extensions (#24753)

Otherwise, we can synthesize an extension that's extending a type
that's unavailable on a particular platform, or that conforms to a
protocol that hasn't been introduced on the minimum deployment target.
This commit is contained in:
Jordan Rose
2019-05-13 19:55:00 -07:00
committed by GitHub
parent b9f42b31dc
commit e43a9f5475
2 changed files with 92 additions and 34 deletions

View File

@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/DiagnosticsSema.h"
@@ -163,47 +164,69 @@ namespace {
class InheritedProtocolCollector {
static const StringLiteral DummyProtocolName;
using AvailableAttrList = TinyPtrVector<const AvailableAttr *>;
using ProtocolAndAvailability =
std::pair<ProtocolDecl *, AvailableAttrList>;
/// Protocols that will be included by the ASTPrinter without any extra work.
SmallVector<ProtocolDecl *, 8> IncludedProtocols;
/// Protocols that will not be printed by the ASTPrinter.
SmallVector<ProtocolDecl *, 8> ExtraProtocols;
/// Protocols that will not be printed by the ASTPrinter, along with the
/// availability they were declared with.
SmallVector<ProtocolAndAvailability, 8> ExtraProtocols;
/// Protocols that can be printed, but whose conformances are constrained with
/// something that \e can't be printed.
SmallVector<const ProtocolType *, 8> ConditionalConformanceProtocols;
/// Helper to extract the `@available` attributes on a decl.
AvailableAttrList getAvailabilityAttrs(const Decl *D) {
AvailableAttrList result;
auto attrRange = D->getAttrs().getAttributes<AvailableAttr>();
result.insert(result.begin(), attrRange.begin(), attrRange.end());
return result;
}
/// For each type in \p directlyInherited, classify the protocols it refers to
/// as included for printing or not, and record them in the appropriate
/// vectors.
void recordProtocols(ArrayRef<TypeLoc> directlyInherited) {
void recordProtocols(ArrayRef<TypeLoc> directlyInherited, const Decl *D) {
Optional<AvailableAttrList> availableAttrs;
for (TypeLoc inherited : directlyInherited) {
Type inheritedTy = inherited.getType();
if (!inheritedTy || !inheritedTy->isExistentialType())
continue;
bool canPrintNormally = isPublicOrUsableFromInline(inheritedTy);
SmallVectorImpl<ProtocolDecl *> &whichProtocols =
canPrintNormally ? IncludedProtocols : ExtraProtocols;
if (!canPrintNormally && !availableAttrs.hasValue())
availableAttrs = getAvailabilityAttrs(D);
ExistentialLayout layout = inheritedTy->getExistentialLayout();
for (ProtocolType *protoTy : layout.getProtocols())
whichProtocols.push_back(protoTy->getDecl());
for (ProtocolType *protoTy : layout.getProtocols()) {
if (canPrintNormally)
IncludedProtocols.push_back(protoTy->getDecl());
else
ExtraProtocols.push_back({protoTy->getDecl(),
availableAttrs.getValue()});
}
// FIXME: This ignores layout constraints, but currently we don't support
// any of those besides 'AnyObject'.
}
}
/// For each type in \p directlyInherited, record any protocols that we would
/// have printed in ConditionalConformanceProtocols.
void recordConditionalConformances(ArrayRef<TypeLoc> directlyInherited) {
for (TypeLoc inherited : directlyInherited) {
/// For each type directly inherited by \p extension, record any protocols
/// that we would have printed in ConditionalConformanceProtocols.
void recordConditionalConformances(const ExtensionDecl *extension) {
for (TypeLoc inherited : extension->getInherited()) {
Type inheritedTy = inherited.getType();
if (!inheritedTy || !inheritedTy->isExistentialType())
continue;
ExistentialLayout layout = inheritedTy->getExistentialLayout();
for (ProtocolType *protoTy : layout.getProtocols())
if (isPublicOrUsableFromInline(protoTy))
ConditionalConformanceProtocols.push_back(protoTy);
for (ProtocolType *protoTy : layout.getProtocols()) {
if (!isPublicOrUsableFromInline(protoTy))
continue;
ConditionalConformanceProtocols.push_back(protoTy);
}
// FIXME: This ignores layout constraints, but currently we don't support
// any of those besides 'AnyObject'.
}
@@ -243,7 +266,7 @@ public:
if (!isPublicOrUsableFromInline(nominal))
return;
map[nominal].recordProtocols(directlyInherited);
map[nominal].recordProtocols(directlyInherited, D);
// Recurse to find any nested types.
for (const Decl *member : memberContext->getMembers())
@@ -264,7 +287,7 @@ public:
if (!isPublicOrUsableFromInline(nominal))
return;
map[nominal].recordConditionalConformances(extension->getInherited());
map[nominal].recordConditionalConformances(extension);
// No recursion here because extensions are never nested.
}
@@ -307,16 +330,19 @@ public:
// Note: We could do this in one pass, but the logic is easier to
// understand if we build up the list and then print it, even if it takes
// a bit more memory.
SmallVector<ProtocolDecl *, 16> protocolsToPrint;
for (ProtocolDecl *proto : ExtraProtocols) {
proto->walkInheritedProtocols(
// FIXME: This will pick the availability attributes from the first sight
// of a protocol rather than the maximally available case.
SmallVector<std::pair<ProtocolDecl *, AvailableAttrList>, 16>
protocolsToPrint;
for (const auto &protoAndAvailability : ExtraProtocols) {
protoAndAvailability.first->walkInheritedProtocols(
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
if (!handledProtocols.insert(inherited).second)
return TypeWalker::Action::SkipChildren;
if (isPublicOrUsableFromInline(inherited) &&
conformanceDeclaredInModule(M, nominal, inherited)) {
protocolsToPrint.push_back(inherited);
protocolsToPrint.push_back({inherited, protoAndAvailability.second});
return TypeWalker::Action::SkipChildren;
}
@@ -326,14 +352,20 @@ public:
if (protocolsToPrint.empty())
return;
out << "extension ";
nominal->getDeclaredType().print(out, printOptions);
out << " : ";
swift::interleave(protocolsToPrint,
[&out, &printOptions](ProtocolDecl *proto) {
proto->getDeclaredType()->print(out, printOptions);
}, [&out] { out << ", "; });
out << " {}\n";
for (const auto &protoAndAvailability : protocolsToPrint) {
StreamPrinter printer(out);
for (const AvailableAttr *attr : protoAndAvailability.second)
attr->print(printer, printOptions);
printer << "extension ";
nominal->getDeclaredType().print(printer, printOptions);
printer << " : ";
ProtocolDecl *proto = protoAndAvailability.first;
proto->getDeclaredType()->print(printer, printOptions);
printer << " {}\n";
}
}
/// If there were any conditional conformances that couldn't be printed,
@@ -346,7 +378,7 @@ public:
return false;
assert(nominal->isGenericContext());
out << "extension ";
out << "@available(*, unavailable)\nextension ";
nominal->getDeclaredType().print(out, printOptions);
out << " : ";
swift::interleave(ConditionalConformanceProtocols,

View File

@@ -87,12 +87,14 @@ public struct OuterGeneric<T> {
public protocol ConditionallyConformed {}
public protocol ConditionallyConformedAgain {}
// CHECK-END: extension conformances.OuterGeneric : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
// CHECK-END: @available(*, unavailable)
// CHECK-END-NEXT: extension conformances.OuterGeneric : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
extension OuterGeneric: ConditionallyConformed where T: PrivateProto {}
extension OuterGeneric: ConditionallyConformedAgain where T == PrivateProto {}
// CHECK-END: extension conformances.OuterGeneric.Inner : conformances.PublicBaseProto {}
// CHECK-END: extension conformances.OuterGeneric.Inner : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
// CHECK-END: @available(*, unavailable)
// CHECK-END-NEXT: extension conformances.OuterGeneric.Inner : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
extension OuterGeneric.Inner: ConditionallyConformed where T: PrivateProto {}
extension OuterGeneric.Inner: ConditionallyConformedAgain where T == PrivateProto {}
@@ -120,10 +122,12 @@ public struct D1: PublicSubProto, PrivateSubProto {}
// NEGATIVE-NOT: extension conformances.D2
public struct D2: PrivateSubProto, PublicSubProto {}
// CHECK: public struct D3 {
// CHECK-END: extension conformances.D3 : conformances.PublicBaseProto, conformances.PublicSubProto {}
// CHECK-END: extension conformances.D3 : conformances.PublicBaseProto {}
// CHECK-END: extension conformances.D3 : conformances.PublicSubProto {}
public struct D3: PrivateSubProto & PublicSubProto {}
// CHECK: public struct D4 {
// CHECK-END: extension conformances.D4 : conformances.APublicSubProto, conformances.PublicBaseProto {}
// CHECK-END: extension conformances.D4 : conformances.APublicSubProto {}
// CHECK-END: extension conformances.D4 : conformances.PublicBaseProto {}
public struct D4: APublicSubProto & PrivateSubProto {}
// CHECK: public struct D5 {
// CHECK: extension D5 : PublicSubProto {
@@ -165,7 +169,8 @@ public struct MultiGeneric<T, U, V> {}
extension MultiGeneric: PublicProto where U: PrivateProto {}
// CHECK: public struct MultiGeneric<T, U, V> {
// CHECK-END: extension conformances.MultiGeneric : conformances.PublicProto where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
// CHECK-END: @available(*, unavailable)
// CHECK-END-NEXT: extension conformances.MultiGeneric : conformances.PublicProto where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
internal struct InternalImpl_BAD: PrivateSubProto {}
@@ -186,6 +191,27 @@ extension WrapperForInternal.InternalImplConstrained2_BAD: PublicProto where T:
internal protocol ExtraHashable: Hashable {}
extension Bool: ExtraHashable {}
@available(iOS, unavailable)
@available(macOS, unavailable)
public struct CoolTVType: PrivateSubProto {}
// CHECK: public struct CoolTVType {
// CHECK-END: @available(OSX, unavailable)
// CHECK-END-NEXT: @available(iOS, unavailable)
// CHECK-END-NEXT: extension conformances.CoolTVType : conformances.PublicBaseProto {}
@available(macOS 10.99, *)
public struct VeryNewMacType: PrivateSubProto {}
// CHECK: public struct VeryNewMacType {
// CHECK-END: @available(OSX, introduced: 10.99)
// CHECK-END-NEXT: extension conformances.VeryNewMacType : conformances.PublicBaseProto {}
public struct VeryNewMacProto {}
@available(macOS 10.98, *)
extension VeryNewMacProto: PrivateSubProto {}
// CHECK: public struct VeryNewMacProto {
// CHECK-END: @available(OSX, introduced: 10.98)
// CHECK-END-NEXT: extension conformances.VeryNewMacProto : conformances.PublicBaseProto {}
// NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Hashable
// NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Equatable