mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[PrintAsObjC] Handle circularities introduced by ObjC generics.
Like Swift generics, Objective-C generics may have constraints; unlike Swift generics, Objective-C doesn't do separate parsing and type-checking passes. This means that any generic arguments for constrained generic parameters must be fully-defined, in order to check that they satisfy the constraints. This commit addresses this problem with three different mechanisms, one for each kind of declaration that might run into this issue: - For classes, if a member references a type with constrained generic parameter, and the corresponding argument type hasn't been printed yet, that member is "delayed", which means it is put into a category at the end of the file. - Protocols cannot have categories, so for protocols we instead see if we can print the definition of the other type first. To break circular dependencies, the printer will not attempt this if both the type and the protocol are already being depended on. This isn't perfect (see below). - Rather than delaying members of extensions, we just delay them wholesale. This keeps related members together, but also has problems (see below). These approaches solve the most common cases while still not crashing in the uncommon ones. However, there are still a number of problems: - The protocol heuristic is overly negative, which means we may generate an invalid header even when there's a reasonable ordering. For example, a single class might inherit from a class A and conform to protocol P, and protocol P depends on class A as a generic argument. In this case, defining class A first is the right thing to do, but it's possible for the printer to decide that there's circularity here and just forward- declare A instead. - Protocols really can be circular. This can be fixed by printing a forward-declared protocol alongside the generic constraints, i.e. 'id <MoreThanNSCopying, NSCopying>' instead of just 'id <MoreThanNSCopying>'. - Extensions can introduce protocols as well. This is not modeled at all; if a member depends on a protocol conformance, it's assumed that simply printing the class would be sufficient. This could be fixed by checking how a generic argument satisfies its constraints, possibly delaying individual members from extensions in order to print them sooner. - More cases I haven't thought about. Test cases for some of these problems are in the new circularity-errors.swift file, mostly to make sure the ObjC printer doesn't crash when it encounters them. rdar://problem/27109377
This commit is contained in:
@@ -118,6 +118,8 @@ static bool isMistakableForInit(ObjCSelector selector) {
|
|||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
using DelayedMemberSet = llvm::SmallSetVector<const ValueDecl *, 32>;
|
||||||
|
|
||||||
class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
|
class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
|
||||||
private TypeVisitor<ObjCPrinter, void,
|
private TypeVisitor<ObjCPrinter, void,
|
||||||
Optional<OptionalTypeKind>>
|
Optional<OptionalTypeKind>>
|
||||||
@@ -134,6 +136,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
|
|||||||
raw_ostream &os;
|
raw_ostream &os;
|
||||||
|
|
||||||
SmallVector<const FunctionType *, 4> openFunctionTypes;
|
SmallVector<const FunctionType *, 4> openFunctionTypes;
|
||||||
|
const DelayedMemberSet &delayedMembers;
|
||||||
|
|
||||||
Accessibility minRequiredAccess;
|
Accessibility minRequiredAccess;
|
||||||
bool protocolMembersOptional = false;
|
bool protocolMembersOptional = false;
|
||||||
@@ -144,14 +147,31 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
|
|||||||
friend TypeVisitor<ObjCPrinter>;
|
friend TypeVisitor<ObjCPrinter>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ObjCPrinter(Module &mod, raw_ostream &out, Accessibility access)
|
explicit ObjCPrinter(Module &mod, raw_ostream &out,
|
||||||
: M(mod), os(out), minRequiredAccess(access) {}
|
DelayedMemberSet &delayed, Accessibility access)
|
||||||
|
: M(mod), os(out), delayedMembers(delayed), minRequiredAccess(access) {}
|
||||||
|
|
||||||
void print(const Decl *D) {
|
void print(const Decl *D) {
|
||||||
PrettyStackTraceDecl trace("printing", D);
|
PrettyStackTraceDecl trace("printing", D);
|
||||||
visit(const_cast<Decl *>(D));
|
visit(const_cast<Decl *>(D));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printAdHocCategory(iterator_range<const ValueDecl * const *> members) {
|
||||||
|
assert(members.begin() != members.end());
|
||||||
|
|
||||||
|
const DeclContext *origDC = (*members.begin())->getDeclContext();
|
||||||
|
auto *baseClass = dyn_cast<ClassDecl>(origDC);
|
||||||
|
if (!baseClass) {
|
||||||
|
Type extendedTy = cast<ExtensionDecl>(origDC)->getExtendedType();
|
||||||
|
baseClass = extendedTy->getClassOrBoundGenericClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
os << "@interface " << getNameForObjC(baseClass)
|
||||||
|
<< " (SWIFT_EXTENSION(" << origDC->getParentModule()->getName() << "))\n";
|
||||||
|
printMembers</*allowDelayed*/true>(members);
|
||||||
|
os << "@end\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
bool shouldInclude(const ValueDecl *VD, bool checkParent = true) {
|
bool shouldInclude(const ValueDecl *VD, bool checkParent = true) {
|
||||||
if (!(VD->isObjC() || VD->getAttrs().hasAttribute<CDeclAttr>()))
|
if (!(VD->isObjC() || VD->getAttrs().hasAttribute<CDeclAttr>()))
|
||||||
return false;
|
return false;
|
||||||
@@ -198,19 +218,24 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prints the members of a class, extension, or protocol.
|
/// Prints the members of a class, extension, or protocol.
|
||||||
void printMembers(DeclRange members) {
|
template <bool AllowDelayed = false, typename R>
|
||||||
for (auto member : members) {
|
void printMembers(R &&members) {
|
||||||
|
for (const Decl *member : members) {
|
||||||
auto VD = dyn_cast<ValueDecl>(member);
|
auto VD = dyn_cast<ValueDecl>(member);
|
||||||
if (!VD || !shouldInclude(VD) || isa<TypeDecl>(VD))
|
if (!VD || !shouldInclude(VD) || isa<TypeDecl>(VD))
|
||||||
continue;
|
continue;
|
||||||
if (auto FD = dyn_cast<FuncDecl>(VD))
|
if (auto FD = dyn_cast<FuncDecl>(VD))
|
||||||
if (FD->isAccessor())
|
if (FD->isAccessor())
|
||||||
continue;
|
continue;
|
||||||
|
if (!AllowDelayed && delayedMembers.count(VD)) {
|
||||||
|
os << "// '" << VD->getFullName() << "' below\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (VD->getAttrs().hasAttribute<OptionalAttr>() != protocolMembersOptional) {
|
if (VD->getAttrs().hasAttribute<OptionalAttr>() != protocolMembersOptional) {
|
||||||
protocolMembersOptional = VD->getAttrs().hasAttribute<OptionalAttr>();
|
protocolMembersOptional = VD->getAttrs().hasAttribute<OptionalAttr>();
|
||||||
os << (protocolMembersOptional ? "@optional\n" : "@required\n");
|
os << (protocolMembersOptional ? "@optional\n" : "@required\n");
|
||||||
}
|
}
|
||||||
visit(VD);
|
visit(const_cast<ValueDecl*>(VD));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1509,6 +1534,7 @@ class ReferencedTypeFinder : private TypeVisitor<ReferencedTypeFinder> {
|
|||||||
friend TypeVisitor;
|
friend TypeVisitor;
|
||||||
|
|
||||||
llvm::function_ref<void(ReferencedTypeFinder &, const TypeDecl *)> Callback;
|
llvm::function_ref<void(ReferencedTypeFinder &, const TypeDecl *)> Callback;
|
||||||
|
bool IsWithinConstrainedObjCGeneric = false;
|
||||||
|
|
||||||
ReferencedTypeFinder(decltype(Callback) callback) : Callback(callback) {}
|
ReferencedTypeFinder(decltype(Callback) callback) : Callback(callback) {}
|
||||||
|
|
||||||
@@ -1568,18 +1594,48 @@ class ReferencedTypeFinder : private TypeVisitor<ReferencedTypeFinder> {
|
|||||||
visit(inout->getObjectType());
|
visit(inout->getObjectType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if \p archetype has any constraints other than being
|
||||||
|
/// class-bound ("conforms to" AnyObject).
|
||||||
|
static bool isConstrainedArchetype(const ArchetypeType *archetype) {
|
||||||
|
if (archetype->getSuperclass())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (archetype->getConformsTo().size() > 1)
|
||||||
|
return true;
|
||||||
|
if (archetype->getConformsTo().size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const ProtocolDecl *proto = archetype->getConformsTo().front();
|
||||||
|
if (auto knownKind = proto->getKnownProtocolKind())
|
||||||
|
return knownKind.getValue() != KnownProtocolKind::AnyObject;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void visitBoundGenericType(BoundGenericType *boundGeneric) {
|
void visitBoundGenericType(BoundGenericType *boundGeneric) {
|
||||||
for (auto argTy : boundGeneric->getGenericArgs())
|
bool isObjCGeneric = boundGeneric->getDecl()->hasClangNode();
|
||||||
|
|
||||||
|
for_each(boundGeneric->getGenericArgs(),
|
||||||
|
boundGeneric->getDecl()->getGenericParams()->getPrimaryArchetypes(),
|
||||||
|
[&](Type argTy, const ArchetypeType *archetype) {
|
||||||
|
if (isObjCGeneric && isConstrainedArchetype(archetype))
|
||||||
|
IsWithinConstrainedObjCGeneric = true;
|
||||||
visit(argTy);
|
visit(argTy);
|
||||||
// Ignore the base type; that can't be exposed to Objective-C. Every
|
IsWithinConstrainedObjCGeneric = false;
|
||||||
// bound generic type we care about gets mapped to a particular construct
|
});
|
||||||
// in Objective-C we care about. (For example, Optional<NSFoo> is mapped to
|
|
||||||
// NSFoo *.)
|
// Ignore the base type; that either can't be exposed to Objective-C or
|
||||||
|
// was an Objective-C type to begin with. Every bound generic Swift type we
|
||||||
|
// care about gets mapped to a particular construct in Objective-C.
|
||||||
|
// (For example, Optional<NSFoo> is mapped to NSFoo *.)
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using TypeVisitor::visit;
|
using TypeVisitor::visit;
|
||||||
|
|
||||||
|
bool isWithinConstrainedObjCGeneric() const {
|
||||||
|
return IsWithinConstrainedObjCGeneric;
|
||||||
|
}
|
||||||
|
|
||||||
static void walk(Type ty, decltype(Callback) callback) {
|
static void walk(Type ty, decltype(Callback) callback) {
|
||||||
ReferencedTypeFinder(callback).visit(ty);
|
ReferencedTypeFinder(callback).visit(ty);
|
||||||
}
|
}
|
||||||
@@ -1603,13 +1659,14 @@ struct PointerLikeComparator {
|
|||||||
|
|
||||||
class ModuleWriter {
|
class ModuleWriter {
|
||||||
enum class EmissionState {
|
enum class EmissionState {
|
||||||
DefinitionRequested = 0,
|
NotYetDefined = 0,
|
||||||
DefinitionInProgress,
|
DefinitionRequested,
|
||||||
Defined
|
Defined
|
||||||
};
|
};
|
||||||
|
|
||||||
llvm::DenseMap<const TypeDecl *, std::pair<EmissionState, bool>> seenTypes;
|
llvm::DenseMap<const TypeDecl *, std::pair<EmissionState, bool>> seenTypes;
|
||||||
std::vector<const Decl *> declsToWrite;
|
std::vector<const Decl *> declsToWrite;
|
||||||
|
DelayedMemberSet delayedMembers;
|
||||||
|
|
||||||
using ImportModuleTy = PointerUnion<Module*, const clang::Module*>;
|
using ImportModuleTy = PointerUnion<Module*, const clang::Module*>;
|
||||||
SmallSetVector<ImportModuleTy, 8,
|
SmallSetVector<ImportModuleTy, 8,
|
||||||
@@ -1623,7 +1680,7 @@ class ModuleWriter {
|
|||||||
ObjCPrinter printer;
|
ObjCPrinter printer;
|
||||||
public:
|
public:
|
||||||
ModuleWriter(Module &mod, StringRef header, Accessibility access)
|
ModuleWriter(Module &mod, StringRef header, Accessibility access)
|
||||||
: M(mod), bridgingHeader(header), printer(M, os, access) {}
|
: M(mod), bridgingHeader(header), printer(M, os, delayedMembers, access) {}
|
||||||
|
|
||||||
/// Returns true if we added the decl's module to the import set, false if
|
/// Returns true if we added the decl's module to the import set, false if
|
||||||
/// the decl is a local decl.
|
/// the decl is a local decl.
|
||||||
@@ -1660,6 +1717,19 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasBeenRequested(const TypeDecl *D) const {
|
||||||
|
return seenTypes.lookup(D).first >= EmissionState::DefinitionRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tryRequire(const TypeDecl *D) {
|
||||||
|
if (addImport(D)) {
|
||||||
|
seenTypes[D] = { EmissionState::Defined, true };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto &state = seenTypes[D];
|
||||||
|
return state.first == EmissionState::Defined;
|
||||||
|
}
|
||||||
|
|
||||||
bool require(const TypeDecl *D) {
|
bool require(const TypeDecl *D) {
|
||||||
if (addImport(D)) {
|
if (addImport(D)) {
|
||||||
seenTypes[D] = { EmissionState::Defined, true };
|
seenTypes[D] = { EmissionState::Defined, true };
|
||||||
@@ -1668,11 +1738,11 @@ public:
|
|||||||
|
|
||||||
auto &state = seenTypes[D];
|
auto &state = seenTypes[D];
|
||||||
switch (state.first) {
|
switch (state.first) {
|
||||||
|
case EmissionState::NotYetDefined:
|
||||||
case EmissionState::DefinitionRequested:
|
case EmissionState::DefinitionRequested:
|
||||||
|
state.first = EmissionState::DefinitionRequested;
|
||||||
declsToWrite.push_back(D);
|
declsToWrite.push_back(D);
|
||||||
return false;
|
return false;
|
||||||
case EmissionState::DefinitionInProgress:
|
|
||||||
llvm_unreachable("circular requirements");
|
|
||||||
case EmissionState::Defined:
|
case EmissionState::Defined:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1717,7 +1787,17 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void forwardDeclareMemberTypes(DeclRange members) {
|
bool forwardDeclareMemberTypes(DeclRange members, const Decl *container) {
|
||||||
|
switch (container->getKind()) {
|
||||||
|
case DeclKind::Class:
|
||||||
|
case DeclKind::Protocol:
|
||||||
|
case DeclKind::Extension:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
llvm_unreachable("unexpected container kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hadAnyDelayedMembers = false;
|
||||||
SmallVector<ValueDecl *, 4> nestedTypes;
|
SmallVector<ValueDecl *, 4> nestedTypes;
|
||||||
for (auto member : members) {
|
for (auto member : members) {
|
||||||
auto VD = dyn_cast<ValueDecl>(member);
|
auto VD = dyn_cast<ValueDecl>(member);
|
||||||
@@ -1738,9 +1818,44 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool needsToBeIndividuallyDelayed = false;
|
||||||
ReferencedTypeFinder::walk(VD->getType(),
|
ReferencedTypeFinder::walk(VD->getType(),
|
||||||
[this](ReferencedTypeFinder &finder,
|
[&](ReferencedTypeFinder &finder,
|
||||||
const TypeDecl *TD) {
|
const TypeDecl *TD) {
|
||||||
|
if (TD == container)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (finder.isWithinConstrainedObjCGeneric()) {
|
||||||
|
// We can delay individual members of classes; do so if necessary.
|
||||||
|
if (isa<ClassDecl>(container)) {
|
||||||
|
if (!tryRequire(TD)) {
|
||||||
|
needsToBeIndividuallyDelayed = true;
|
||||||
|
hadAnyDelayedMembers = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extensions can always be delayed wholesale.
|
||||||
|
if (isa<ExtensionDecl>(container)) {
|
||||||
|
if (!require(TD))
|
||||||
|
hadAnyDelayedMembers = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocols should be delayed wholesale unless we might have a cycle.
|
||||||
|
auto *proto = cast<ProtocolDecl>(container);
|
||||||
|
if (!hasBeenRequested(proto) || !hasBeenRequested(TD)) {
|
||||||
|
if (!require(TD))
|
||||||
|
hadAnyDelayedMembers = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we have a cyclic dependency. Give up and continue with
|
||||||
|
// regular forward-declarations even though this will lead to an
|
||||||
|
// error; there's nothing we can do here.
|
||||||
|
// FIXME: It would be nice to diagnose this.
|
||||||
|
}
|
||||||
|
|
||||||
if (auto CD = dyn_cast<ClassDecl>(TD)) {
|
if (auto CD = dyn_cast<ClassDecl>(TD)) {
|
||||||
if (!forwardDeclare(CD)) {
|
if (!forwardDeclare(CD)) {
|
||||||
(void)addImport(CD);
|
(void)addImport(CD);
|
||||||
@@ -1758,13 +1873,18 @@ public:
|
|||||||
else
|
else
|
||||||
assert(false && "unknown local type decl");
|
assert(false && "unknown local type decl");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (needsToBeIndividuallyDelayed) {
|
||||||
|
assert(isa<ClassDecl>(container));
|
||||||
|
delayedMembers.insert(VD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declsToWrite.insert(declsToWrite.end()-1, nestedTypes.rbegin(),
|
declsToWrite.insert(declsToWrite.end()-1, nestedTypes.rbegin(),
|
||||||
nestedTypes.rend());
|
nestedTypes.rend());
|
||||||
|
|
||||||
// Separate forward declarations from the class itself.
|
// Separate forward declarations from the class itself.
|
||||||
os << '\n';
|
return !hadAnyDelayedMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool writeClass(const ClassDecl *CD) {
|
bool writeClass(const ClassDecl *CD) {
|
||||||
@@ -1789,8 +1909,9 @@ public:
|
|||||||
if (!allRequirementsSatisfied)
|
if (!allRequirementsSatisfied)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
(void)forwardDeclareMemberTypes(CD->getMembers(), CD);
|
||||||
seenTypes[CD] = { EmissionState::Defined, true };
|
seenTypes[CD] = { EmissionState::Defined, true };
|
||||||
forwardDeclareMemberTypes(CD->getMembers());
|
os << '\n';
|
||||||
printer.print(CD);
|
printer.print(CD);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1824,8 +1945,11 @@ public:
|
|||||||
if (!allRequirementsSatisfied)
|
if (!allRequirementsSatisfied)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!forwardDeclareMemberTypes(PD->getMembers(), PD))
|
||||||
|
return false;
|
||||||
|
|
||||||
seenTypes[PD] = { EmissionState::Defined, true };
|
seenTypes[PD] = { EmissionState::Defined, true };
|
||||||
forwardDeclareMemberTypes(PD->getMembers());
|
os << '\n';
|
||||||
printer.print(PD);
|
printer.print(PD);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1842,7 +1966,13 @@ public:
|
|||||||
if (!allRequirementsSatisfied)
|
if (!allRequirementsSatisfied)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
forwardDeclareMemberTypes(ED->getMembers());
|
// This isn't rolled up into the previous set of requirements because
|
||||||
|
// it /also/ prints forward declarations, and the header is a little
|
||||||
|
// prettier if those are as close as possible to the necessary extension.
|
||||||
|
if (!forwardDeclareMemberTypes(ED->getMembers(), ED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
os << '\n';
|
||||||
printer.print(ED);
|
printer.print(ED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2186,6 +2316,17 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!delayedMembers.empty()) {
|
||||||
|
auto groupBegin = delayedMembers.begin();
|
||||||
|
for (auto i = groupBegin, e = delayedMembers.end(); i != e; ++i) {
|
||||||
|
if ((*i)->getDeclContext() != (*groupBegin)->getDeclContext()) {
|
||||||
|
printer.printAdHocCategory(make_range(groupBegin, i));
|
||||||
|
groupBegin = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printer.printAdHocCategory(make_range(groupBegin, delayedMembers.end()));
|
||||||
|
}
|
||||||
|
|
||||||
writePrologue(out);
|
writePrologue(out);
|
||||||
writeImports(out);
|
writeImports(out);
|
||||||
out <<
|
out <<
|
||||||
|
|||||||
22
test/PrintAsObjC/Inputs/circularity.h
Normal file
22
test/PrintAsObjC/Inputs/circularity.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// This file is meant to be used with the mock SDK, not the real one.
|
||||||
|
#import <Foundation.h>
|
||||||
|
|
||||||
|
#define SWIFT_NAME(x) __attribute__((swift_name(#x)))
|
||||||
|
|
||||||
|
@protocol Proto
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ProtoImpl : NSObject <Proto>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface Parent : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface Unconstrained<T> : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NeedsProto<T: id <Proto>> : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NeedsParent<T: Parent *> : NSObject
|
||||||
|
@end
|
||||||
73
test/PrintAsObjC/circularity-errors.swift
Normal file
73
test/PrintAsObjC/circularity-errors.swift
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Please keep this file in alphabetical order!
|
||||||
|
|
||||||
|
// REQUIRES: objc_interop
|
||||||
|
|
||||||
|
// RUN: rm -rf %t && mkdir %t
|
||||||
|
|
||||||
|
// FIXME: BEGIN -enable-source-import hackaround
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift
|
||||||
|
// FIXME: END -enable-source-import hackaround
|
||||||
|
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/circularity.h -emit-module -o %t %s
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/circularity.h -parse-as-library %t/circularity-errors.swiftmodule -parse -emit-objc-header-path %t/circularity-errors.h
|
||||||
|
|
||||||
|
// RUN: FileCheck %s < %t/circularity-errors.h
|
||||||
|
// RUN: not %check-in-clang %t/circularity-errors.h
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// CHECK: @protocol A2;
|
||||||
|
// CHECK-LABEL: @protocol A1 <Proto>
|
||||||
|
@objc protocol A1: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<A2>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol A2 <Proto>
|
||||||
|
@objc protocol A2: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<A1>)
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK: @class B2;
|
||||||
|
// CHECK-LABEL: @protocol B1 <Proto>
|
||||||
|
@objc protocol B1: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
@objc optional func test(_: NeedsProto<B2>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface B2 : ProtoImpl <B1>
|
||||||
|
class B2: ProtoImpl, B1 {
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK: @class C1;
|
||||||
|
// Moved below.
|
||||||
|
class C1: ProtoImpl, C2 {}
|
||||||
|
// CHECK-LABEL: @protocol C2 <Proto>
|
||||||
|
@objc protocol C2: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
@objc optional func test(_: NeedsProto<C1>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface C1 : ProtoImpl <C2>
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK: @protocol D2;
|
||||||
|
// CHECK-LABEL: @protocol D1 <Proto>
|
||||||
|
@objc protocol D1: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<D2>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol D2 <D1>
|
||||||
|
@objc protocol D2: D1 {
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK: @protocol E1;
|
||||||
|
// Moved below.
|
||||||
|
@objc protocol E1: E2 {}
|
||||||
|
// CHECK-LABEL: @protocol E2 <Proto>
|
||||||
|
@objc protocol E2: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<E1>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol E1 <E2>
|
||||||
|
// CHECK: @end
|
||||||
283
test/PrintAsObjC/circularity.swift
Normal file
283
test/PrintAsObjC/circularity.swift
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
// Please keep this file in alphabetical order!
|
||||||
|
|
||||||
|
// REQUIRES: objc_interop
|
||||||
|
|
||||||
|
// RUN: rm -rf %t && mkdir %t
|
||||||
|
|
||||||
|
// FIXME: BEGIN -enable-source-import hackaround
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift
|
||||||
|
// FIXME: END -enable-source-import hackaround
|
||||||
|
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/circularity.h -emit-module -o %t %s
|
||||||
|
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/circularity.h -parse-as-library %t/circularity.swiftmodule -parse -emit-objc-header-path %t/circularity.h
|
||||||
|
|
||||||
|
// RUN: FileCheck %s < %t/circularity.h
|
||||||
|
|
||||||
|
// RUN: %check-in-clang %t/circularity.h
|
||||||
|
// RUN: %check-in-clang -fno-modules -Qunused-arguments %t/circularity.h
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface A1 : ProtoImpl
|
||||||
|
class A1: ProtoImpl {
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
func test(_: NeedsProto<A2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface A2 : ProtoImpl
|
||||||
|
class A2: ProtoImpl {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<A1>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface B1 : ProtoImpl
|
||||||
|
class B1: ProtoImpl {
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
func test(_: NeedsProto<B2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface B2 : ProtoImpl
|
||||||
|
class B2: ProtoImpl {
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface C1 : ProtoImpl
|
||||||
|
class C1: ProtoImpl {
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
func test(_: NeedsProto<C2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol C2 <Proto>
|
||||||
|
@objc protocol C2: Proto {
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface D1 : ProtoImpl
|
||||||
|
class D1: ProtoImpl {
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
func test(_: NeedsProto<D2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol D2 <Proto>
|
||||||
|
@objc protocol D2: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<D1>)
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface D4 : ProtoImpl
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol D3 <Proto>
|
||||||
|
@objc protocol D3: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<D4>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// Moved ahead.
|
||||||
|
class D4: ProtoImpl {
|
||||||
|
func test(_: NeedsProto<D3>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface E2 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol E1 <Proto>
|
||||||
|
@objc protocol E1: Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<E2>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// Moved ahead.
|
||||||
|
class E2: ProtoImpl {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface F1 : ProtoImpl
|
||||||
|
class F1: ProtoImpl {
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL @interface F2 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL @interface F1 (SWIFT_EXTENSION(circularity))
|
||||||
|
extension F1 {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<F2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// Moved ahead.
|
||||||
|
class F2: ProtoImpl {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface G1 : ProtoImpl
|
||||||
|
class G1: ProtoImpl {
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL @protocol G2 <Proto>
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL @interface G1 (SWIFT_EXTENSION(circularity))
|
||||||
|
extension G1 {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<G2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// Moved ahead.
|
||||||
|
@objc protocol G2: Proto {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface H1 : ProtoImpl
|
||||||
|
class H1: ProtoImpl {
|
||||||
|
// CHECK: 'test(_:)' below
|
||||||
|
func test(_: NeedsProto<H2>) {}
|
||||||
|
// CHECK: 'anotherTest(_:)' below
|
||||||
|
func anotherTest(_: NeedsProto<H3>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface H2 : ProtoImpl
|
||||||
|
class H2: ProtoImpl {
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
func test(_: NeedsProto<H3>) {}
|
||||||
|
// CHECK: - (void)anotherTest:
|
||||||
|
func anotherTest(_: NeedsProto<H1>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface H3 : ProtoImpl
|
||||||
|
class H3: ProtoImpl {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsProto<H1>) {}
|
||||||
|
// CHECK: - (void)anotherTest:
|
||||||
|
func anotherTest(_: NeedsProto<H2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface I1 : Parent
|
||||||
|
class I1 : Parent {
|
||||||
|
// CHECK: // 'test(_:)' below
|
||||||
|
func test(_: NeedsParent<I2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface I2 : Parent
|
||||||
|
class I2 : Parent {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: NeedsParent<I1>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface J1 : Parent
|
||||||
|
class J1 : Parent {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: Unconstrained<J2>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface J2 : Parent
|
||||||
|
class J2 : Parent {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: Unconstrained<J1>) {}
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @protocol K1 <Proto>
|
||||||
|
@objc protocol K1 : Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: Unconstrained<K2>)
|
||||||
|
} // CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol K2 <Proto>
|
||||||
|
@objc protocol K2 : Proto {
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
func test(_: Unconstrained<K1>)
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NA2 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol NA1
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface NA3 : NSObject <NA1>
|
||||||
|
// CHECK: @end
|
||||||
|
@objc protocol NA1 {
|
||||||
|
@objc optional func test(_: NeedsProto<NA2>)
|
||||||
|
}
|
||||||
|
class NA2: ProtoImpl {}
|
||||||
|
class NA3: NSObject, NA1 {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NB1 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol NB2
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface NB3 : NSObject <NB2>
|
||||||
|
// CHECK: @end
|
||||||
|
class NB1: ProtoImpl {}
|
||||||
|
@objc protocol NB2 {
|
||||||
|
@objc optional func test(_: NeedsProto<NB1>)
|
||||||
|
}
|
||||||
|
class NB3: NSObject, NB2 {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NC2 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol NC3
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface NC1 : NSObject <NC3>
|
||||||
|
// CHECK: @end
|
||||||
|
class NC1: NSObject, NC3 {}
|
||||||
|
class NC2: ProtoImpl {}
|
||||||
|
@objc protocol NC3 {
|
||||||
|
@objc optional func test(_: NeedsProto<NC2>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface ND3 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol ND2
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface ND1 : NSObject <ND2>
|
||||||
|
// CHECK: @end
|
||||||
|
class ND1: NSObject, ND2 {}
|
||||||
|
@objc protocol ND2 {
|
||||||
|
@objc optional func test(_: NeedsProto<ND3>)
|
||||||
|
}
|
||||||
|
class ND3: ProtoImpl {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NE3 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol NE1
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface NE2 : NSObject <NE1>
|
||||||
|
// CHECK: @end
|
||||||
|
@objc protocol NE1 {
|
||||||
|
@objc optional func test(_: NeedsProto<NE3>)
|
||||||
|
}
|
||||||
|
class NE2: NSObject, NE1 {}
|
||||||
|
class NE3: ProtoImpl {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface NF2 : ProtoImpl
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @protocol NF1
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
// CHECK-LABEL: @interface NF3 : NSObject <NF1>
|
||||||
|
// CHECK: @end
|
||||||
|
@objc protocol NF1 {
|
||||||
|
@objc optional func test(_: NeedsProto<NF2>)
|
||||||
|
}
|
||||||
|
class NF2: ProtoImpl {}
|
||||||
|
class NF3: NSObject, NF1 {}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface ZZZ_EOF : NSObject
|
||||||
|
class ZZZ_EOF : NSObject {
|
||||||
|
} // CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface A1 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface B1 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface C1 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface D1 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface D4 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface H1 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK-NOT: @end
|
||||||
|
// CHECK: - (void)anotherTest:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface H2 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
|
// CHECK-LABEL: @interface I1 (SWIFT_EXTENSION(circularity))
|
||||||
|
// CHECK: - (void)test:
|
||||||
|
// CHECK: @end
|
||||||
|
|
||||||
Reference in New Issue
Block a user