Merge remote-tracking branch 'origin/master' into master-next

This commit is contained in:
swift-ci
2018-11-08 10:49:55 -08:00
3 changed files with 307 additions and 7 deletions

View File

@@ -4445,12 +4445,18 @@ swift::getInheritedForPrinting(const Decl *decl,
inherited = ed->getInherited();
}
// Collect explicit inheritted types.
// Collect explicit inherited types.
for (auto TL: inherited) {
if (auto Ty = TL.getType()) {
if (auto NTD = Ty->getAnyNominal())
if (!shouldPrint(NTD))
continue;
if (auto ty = TL.getType()) {
bool foundUnprintable = ty.findIf([shouldPrint](Type subTy) {
if (auto aliasTy = dyn_cast<NameAliasType>(subTy.getPointer()))
return !shouldPrint(aliasTy->getDecl());
if (auto NTD = subTy->getAnyNominal())
return !shouldPrint(NTD);
return false;
});
if (foundUnprintable)
continue;
}
Results.push_back(TL);
}

View File

@@ -14,6 +14,7 @@
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/FileSystem.h"
#include "swift/AST/Module.h"
#include "swift/Frontend/Frontend.h"
@@ -431,6 +432,157 @@ static void printImports(raw_ostream &out, ModuleDecl *M) {
}
}
// FIXME: Copied from ASTPrinter.cpp...
static bool isPublicOrUsableFromInline(const ValueDecl *VD) {
AccessScope scope =
VD->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
return scope.isPublic();
}
static bool isPublicOrUsableFromInline(Type ty) {
// Note the double negative here: we're looking for any referenced decls that
// are *not* public-or-usableFromInline.
return !ty.findIf([](Type typePart) -> bool {
// FIXME: If we have an internal typealias for a non-internal type, we ought
// to be able to print it by desugaring.
if (auto *aliasTy = dyn_cast<NameAliasType>(typePart.getPointer()))
return !isPublicOrUsableFromInline(aliasTy->getDecl());
if (auto *nominal = typePart->getAnyNominal())
return !isPublicOrUsableFromInline(nominal);
return false;
});
}
namespace {
/// Collects protocols that are conformed to by a particular nominal. Since
/// ASTPrinter will only print the public ones, the non-public ones get left by
/// the wayside. This is a problem when a non-public protocol inherits from a
/// public protocol; the generated parseable interface still needs to make that
/// dependency public.
///
/// The solution implemented here is to generate synthetic extensions that
/// declare the extra conformances. This isn't perfect (it loses the sugared
/// spelling of the protocol type, as well as the locality in the file), but it
/// does work.
class InheritedProtocolCollector {
/// 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;
/// 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) {
for (TypeLoc inherited : directlyInherited) {
Type inheritedTy = inherited.getType();
if (!inheritedTy || !inheritedTy->isExistentialType())
continue;
bool canPrintNormally = isPublicOrUsableFromInline(inheritedTy);
SmallVectorImpl<ProtocolDecl *> &whichProtocols =
canPrintNormally ? IncludedProtocols : ExtraProtocols;
ExistentialLayout layout = inheritedTy->getExistentialLayout();
for (ProtocolType *protoTy : layout.getProtocols())
whichProtocols.push_back(protoTy->getDecl());
// FIXME: This ignores layout constraints, but currently we don't support
// any of those besides 'AnyObject'.
}
}
public:
using PerTypeMap = llvm::MapVector<const NominalTypeDecl *,
InheritedProtocolCollector>;
/// Given that we're about to print \p D, record its protocols in \p map.
///
/// \sa recordProtocols
static void collectProtocols(PerTypeMap &map, const Decl *D) {
ArrayRef<TypeLoc> directlyInherited;
const NominalTypeDecl *nominal;
const IterableDeclContext *memberContext;
if ((nominal = dyn_cast<NominalTypeDecl>(D))) {
directlyInherited = nominal->getInherited();
memberContext = nominal;
} else if (auto *extension = dyn_cast<ExtensionDecl>(D)) {
if (extension->isConstrainedExtension()) {
// Conditional conformances never apply to inherited protocols, nor
// can they provide unconditional conformances that might be used in
// other extensions.
return;
}
nominal = extension->getExtendedNominal();
directlyInherited = extension->getInherited();
memberContext = extension;
} else {
return;
}
map[nominal].recordProtocols(directlyInherited);
// Recurse to find any nested types.
for (const Decl *member : memberContext->getMembers())
collectProtocols(map, member);
}
/// If there were any public protocols that need to be printed (i.e. they
/// weren't conformed to explicitly or inherited by another printed protocol),
/// do so now by printing a dummy extension on \p nominal to \p out.
void
printSynthesizedExtensionIfNeeded(raw_ostream &out,
const PrintOptions &printOptions,
const NominalTypeDecl *nominal) const {
if (ExtraProtocols.empty())
return;
SmallPtrSet<ProtocolDecl *, 16> handledProtocols;
// First record all protocols that have already been handled.
for (ProtocolDecl *proto : IncludedProtocols) {
proto->walkInheritedProtocols(
[&handledProtocols](ProtocolDecl *inherited) -> TypeWalker::Action {
handledProtocols.insert(inherited);
return TypeWalker::Action::Continue;
});
}
// Then walk the remaining ones, and see what we need to print.
// 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(
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
if (!handledProtocols.insert(inherited).second)
return TypeWalker::Action::SkipChildren;
if (isPublicOrUsableFromInline(inherited)) {
protocolsToPrint.push_back(inherited);
return TypeWalker::Action::SkipChildren;
}
return TypeWalker::Action::Continue;
});
}
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";
}
};
} // end anonymous namespace
bool swift::emitParseableInterface(raw_ostream &out,
ParseableInterfaceOptions const &Opts,
ModuleDecl *M) {
@@ -440,13 +592,26 @@ bool swift::emitParseableInterface(raw_ostream &out,
printImports(out, M);
const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile();
InheritedProtocolCollector::PerTypeMap inheritedProtocolMap;
SmallVector<Decl *, 16> topLevelDecls;
M->getTopLevelDecls(topLevelDecls);
for (const Decl *D : topLevelDecls) {
if (!D->shouldPrintInContext(printOptions))
continue;
D->print(out, printOptions);
out << "\n";
InheritedProtocolCollector::collectProtocols(inheritedProtocolMap, D);
}
// Print dummy extensions for any protocols that were indirectly conformed to.
for (const auto &nominalAndCollector : inheritedProtocolMap) {
const InheritedProtocolCollector &collector = nominalAndCollector.second;
collector.printSynthesizedExtensionIfNeeded(out, printOptions,
nominalAndCollector.first);
}
return false;
}

View File

@@ -1,5 +1,6 @@
// RUN: %target-swift-frontend-typecheck -emit-parseable-module-interface-path %t.swiftinterface %s
// RUN: %FileCheck %s < %t.swiftinterface
// RUN: %FileCheck -check-prefix CHECK-END %s < %t.swiftinterface
// RUN: %FileCheck -check-prefix NEGATIVE %s < %t.swiftinterface
// CHECK-LABEL: public protocol SimpleProto {
@@ -11,10 +12,138 @@ public protocol SimpleProto {
func inference(_: Inferred)
} // CHECK: {{^}$}}
// CHECK-LABEL: public struct SimplImpl<Element> : SimpleProto {
public struct SimplImpl<Element>: SimpleProto {
// CHECK-LABEL: public struct SimpleImpl<Element> : SimpleProto {
public struct SimpleImpl<Element>: SimpleProto {
// NEGATIVE-NOT: typealias Element =
// CHECK: public func inference(_: Int){{$}}
public func inference(_: Int) {}
// CHECK: public typealias Inferred = Swift.Int
} // CHECK: {{^}$}}
public protocol PublicProto {}
private protocol PrivateProto {}
// CHECK: public struct A1 : PublicProto {
// NEGATIVE-NOT: extension conformances.A1
public struct A1: PublicProto, PrivateProto {}
// CHECK: public struct A2 : PublicProto {
// NEGATIVE-NOT: extension conformances.A2
public struct A2: PrivateProto, PublicProto {}
// CHECK: public struct A3 {
// CHECK-END: extension conformances.A3 : PublicProto {}
public struct A3: PublicProto & PrivateProto {}
// CHECK: public struct A4 {
// CHECK-END: extension conformances.A4 : PublicProto {}
public struct A4: PrivateProto & PublicProto {}
public protocol PublicBaseProto {}
private protocol PrivateSubProto: PublicBaseProto {}
// CHECK: public struct B1 {
// CHECK-END: extension conformances.B1 : PublicBaseProto {}
public struct B1: PrivateSubProto {}
// CHECK: public struct B2 : PublicBaseProto {
// NEGATIVE-NOT: extension conformances.B2
public struct B2: PublicBaseProto, PrivateSubProto {}
// CHECK: public struct B3 {
// CHECK-END: extension conformances.B3 : PublicBaseProto {}
public struct B3: PublicBaseProto & PrivateSubProto {}
// CHECK: public struct B4 : PublicBaseProto {
// CHECK: extension B4 {
// NEGATIVE-NOT: extension conformances.B4
public struct B4: PublicBaseProto {}
extension B4: PrivateSubProto {}
// CHECK: public struct B5 {
// CHECK: extension B5 : PublicBaseProto {
// NEGATIVE-NOT: extension conformances.B5
public struct B5: PrivateSubProto {}
extension B5: PublicBaseProto {}
// CHECK: public struct B6 {
// CHECK: extension B6 {
// CHECK: extension B6 : PublicBaseProto {
// NEGATIVE-NOT: extension conformances.B6
public struct B6 {}
extension B6: PrivateSubProto {}
extension B6: PublicBaseProto {}
// CHECK: public struct B7 {
// CHECK: extension B7 : PublicBaseProto {
// CHECK: extension B7 {
// NEGATIVE-NOT: extension conformances.B7
public struct B7 {}
extension B7: PublicBaseProto {}
extension B7: PrivateSubProto {}
// CHECK-LABEL: public struct OuterGeneric<T> {
public struct OuterGeneric<T> {
// CHECK-NEXT: public struct Inner {
public struct Inner: PrivateSubProto {}
// CHECK-NEXT: {{^ }$}}
}
// CHECK-NEXT: {{^}$}}
// CHECK-END: extension conformances.OuterGeneric.Inner : PublicBaseProto {}
private protocol AnotherPrivateSubProto: PublicBaseProto {}
// CHECK: public struct C1 {
// CHECK-END: extension conformances.C1 : PublicBaseProto {}
public struct C1: PrivateSubProto, AnotherPrivateSubProto {}
// CHECK: public struct C2 {
// CHECK-END: extension conformances.C2 : PublicBaseProto {}
public struct C2: PrivateSubProto & AnotherPrivateSubProto {}
// CHECK: public struct C3 {
// CHECK: extension C3 {
// CHECK-END: extension conformances.C3 : PublicBaseProto {}
public struct C3: PrivateSubProto {}
extension C3: AnotherPrivateSubProto {}
public protocol PublicSubProto: PublicBaseProto {}
public protocol APublicSubProto: PublicBaseProto {}
// CHECK: public struct D1 : PublicSubProto {
// NEGATIVE-NOT: extension conformances.D1
public struct D1: PublicSubProto, PrivateSubProto {}
// CHECK: public struct D2 : PublicSubProto {
// NEGATIVE-NOT: extension conformances.D2
public struct D2: PrivateSubProto, PublicSubProto {}
// CHECK: public struct D3 {
// CHECK-END: extension conformances.D3 : PublicBaseProto, PublicSubProto {}
public struct D3: PrivateSubProto & PublicSubProto {}
// CHECK: public struct D4 {
// CHECK-END: extension conformances.D4 : APublicSubProto, PublicBaseProto {}
public struct D4: APublicSubProto & PrivateSubProto {}
// CHECK: public struct D5 {
// CHECK: extension D5 : PublicSubProto {
// NEGATIVE-NOT: extension conformances.D5
public struct D5: PrivateSubProto {}
extension D5: PublicSubProto {}
// CHECK: public struct D6 : PublicSubProto {
// CHECK: extension D6 {
// NEGATIVE-NOT: extension conformances.D6
public struct D6: PublicSubProto {}
extension D6: PrivateSubProto {}
private typealias PrivateProtoAlias = PublicProto
// CHECK: public struct E1 {
// CHECK-END: extension conformances.E1 : PublicProto {}
public struct E1: PrivateProtoAlias {}
private typealias PrivateSubProtoAlias = PrivateSubProto
// CHECK: public struct F1 {
// CHECK-END: extension conformances.F1 : PublicBaseProto {}
public struct F1: PrivateSubProtoAlias {}
private protocol ClassConstrainedProto: PublicProto, AnyObject {}
public class G1: ClassConstrainedProto {}
// CHECK: public class G1 {
// CHECK-END: extension conformances.G1 : PublicProto {}
public class Base {}
private protocol BaseConstrainedProto: Base, PublicProto {}
public class H1: Base, ClassConstrainedProto {}
// CHECK: public class H1 : Base {
// CHECK-END: extension conformances.H1 : PublicProto {}