Disallow conforming to ObjC protocols with requirements that can't be imported.

For example, variadic ObjC method requirements.

<rdar://problem/17366999>

Swift SVN r19121
This commit is contained in:
Jordan Rose
2014-06-24 01:23:06 +00:00
parent 678bbfe683
commit db3914e94b
12 changed files with 88 additions and 12 deletions

View File

@@ -462,8 +462,12 @@ class alignas(8) Decl {
/// The stage of the circularity check for this protocol. /// The stage of the circularity check for this protocol.
unsigned Circularity : 2; unsigned Circularity : 2;
/// True if the protocol has requirements that cannot be satisfied (e.g.
/// because they could not be imported from Objective-C).
unsigned HasMissingRequirements : 1;
}; };
enum { NumProtocolDeclBits = NumNominalTypeDeclBits + 11 }; enum { NumProtocolDeclBits = NumNominalTypeDeclBits + 12 };
static_assert(NumProtocolDeclBits <= 32, "fits in an unsigned"); static_assert(NumProtocolDeclBits <= 32, "fits in an unsigned");
class ClassDeclBitfields { class ClassDeclBitfields {
@@ -3045,6 +3049,20 @@ public:
ProtocolDeclBits.Circularity = static_cast<unsigned>(circularity); ProtocolDeclBits.Circularity = static_cast<unsigned>(circularity);
} }
/// Returns true if the protocol has requirements that are not listed in its
/// members.
///
/// This can occur, for example, if the protocol is an Objective-C protocol
/// with requirements that cannot be represented in Swift.
bool hasMissingRequirements() const {
(void)getMembers();
return ProtocolDeclBits.HasMissingRequirements;
}
void setHasMissingRequirements(bool newValue) {
ProtocolDeclBits.HasMissingRequirements = newValue;
}
/// Retrieve the name to use for this protocol when interoperating /// Retrieve the name to use for this protocol when interoperating
/// with the Objective-C runtime. /// with the Objective-C runtime.
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const; StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;

View File

@@ -456,6 +456,9 @@ ERROR(type_does_not_inherit,sema_tcd,none,
ERROR(non_class_cannot_conform_to_class_protocol,sema_tcd,none, ERROR(non_class_cannot_conform_to_class_protocol,sema_tcd,none,
"non-class type %0 cannot conform to class protocol %1", "non-class type %0 cannot conform to class protocol %1",
(Type, Type)) (Type, Type))
ERROR(protocol_has_missing_requirements,sema_tcd,none,
"type %0 cannot conform to protocol %1 because it has requirements that "
"cannot be satisfied", (Type, Type))
ERROR(witness_argument_name_mismatch,sema_tcd,none, ERROR(witness_argument_name_mismatch,sema_tcd,none,
"%select{method|initializer}0 %1 has different argument names from those " "%select{method|initializer}0 %1 has different argument names from those "
"required by protocol %2 (%3)", (bool, DeclName, Type, DeclName)) "required by protocol %2 (%3)", (bool, DeclName, Type, DeclName))

View File

@@ -106,8 +106,12 @@ public:
/// \p D. /// \p D.
/// ///
/// The implementation should \em not call setMembers on \p D. /// The implementation should \em not call setMembers on \p D.
virtual ArrayRef<Decl *> loadAllMembers(const Decl *D, ///
uint64_t contextData) { /// \param[out] hasMissingRequiredMembers If present, set to true if any
/// members failed to import and were non-optional protocol requirements.
virtual ArrayRef<Decl *>
loadAllMembers(const Decl *D, uint64_t contextData,
bool *hasMissingRequiredMembers = nullptr) {
llvm_unreachable("unimplemented"); llvm_unreachable("unimplemented");
} }

View File

@@ -524,7 +524,8 @@ public:
GenericParamList *outerParams = nullptr); GenericParamList *outerParams = nullptr);
virtual ArrayRef<Decl *> loadAllMembers(const Decl *D, virtual ArrayRef<Decl *> loadAllMembers(const Decl *D,
uint64_t contextData) override; uint64_t contextData,
bool *ignored) override;
virtual ArrayRef<ProtocolConformance *> virtual ArrayRef<ProtocolConformance *>
loadAllConformances(const Decl *D, uint64_t contextData) override; loadAllConformances(const Decl *D, uint64_t contextData) override;

View File

@@ -1770,6 +1770,7 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
ProtocolDeclBits.KnownProtocol = 0; ProtocolDeclBits.KnownProtocol = 0;
ProtocolDeclBits.Circularity ProtocolDeclBits.Circularity
= static_cast<unsigned>(CircularityCheck::Unchecked); = static_cast<unsigned>(CircularityCheck::Unchecked);
ProtocolDeclBits.HasMissingRequirements = false;
} }
bool ProtocolDecl::inheritsFrom(const ProtocolDecl *Super) const { bool ProtocolDecl::inheritsFrom(const ProtocolDecl *Super) const {

View File

@@ -531,10 +531,16 @@ void IterableDeclContext::loadAllMembers() const {
break; break;
} }
for (auto member : resolver->loadAllMembers(container, contextData)) { bool hasMissingRequiredMembers = false;
for (auto member : resolver->loadAllMembers(container, contextData,
&hasMissingRequiredMembers)) {
const_cast<IterableDeclContext *>(this)->addMember(member); const_cast<IterableDeclContext *>(this)->addMember(member);
} }
if (hasMissingRequiredMembers)
if (auto proto = dyn_cast<ProtocolDecl>(this))
const_cast<ProtocolDecl *>(proto)->setHasMissingRequirements(true);
--NumUnloadedLazyIterableDeclContexts; --NumUnloadedLazyIterableDeclContexts;
} }

View File

@@ -3464,7 +3464,8 @@ namespace {
/// list of corresponding Swift members. /// list of corresponding Swift members.
void importObjCMembers(const clang::ObjCContainerDecl *decl, void importObjCMembers(const clang::ObjCContainerDecl *decl,
DeclContext *swiftContext, DeclContext *swiftContext,
SmallVectorImpl<Decl *> &members) { SmallVectorImpl<Decl *> &members,
bool &hasMissingRequiredMember) {
llvm::SmallPtrSet<Decl *, 4> knownMembers; llvm::SmallPtrSet<Decl *, 4> knownMembers;
for (auto m = decl->decls_begin(), mEnd = decl->decls_end(); for (auto m = decl->decls_begin(), mEnd = decl->decls_end();
m != mEnd; ++m) { m != mEnd; ++m) {
@@ -3473,8 +3474,18 @@ namespace {
continue; continue;
auto member = Impl.importDecl(nd); auto member = Impl.importDecl(nd);
if (!member) if (!member) {
if (auto method = dyn_cast<clang::ObjCMethodDecl>(nd)) {
if (method->getImplementationControl() ==
clang::ObjCMethodDecl::Required)
hasMissingRequiredMember = true;
} else if (auto prop = dyn_cast<clang::ObjCPropertyDecl>(nd)) {
if (prop->getPropertyImplementation() ==
clang::ObjCPropertyDecl::Required)
hasMissingRequiredMember = true;
}
continue; continue;
}
// If this member is a method that is a getter or setter for a property // If this member is a method that is a getter or setter for a property
// that was imported, don't add it to the list of members so it won't // that was imported, don't add it to the list of members so it won't
@@ -4903,7 +4914,8 @@ createUnavailableDecl(Identifier name, DeclContext *dc, Type type,
ArrayRef<Decl *> ArrayRef<Decl *>
ClangImporter::Implementation::loadAllMembers(const Decl *D, uint64_t unused) { ClangImporter::Implementation::loadAllMembers(const Decl *D, uint64_t unused,
bool *hasMissingRequiredMembers) {
assert(D->hasClangNode()); assert(D->hasClangNode());
auto clangDecl = cast<clang::ObjCContainerDecl>(D->getClangDecl()); auto clangDecl = cast<clang::ObjCContainerDecl>(D->getClangDecl());
@@ -4921,8 +4933,13 @@ ClangImporter::Implementation::loadAllMembers(const Decl *D, uint64_t unused) {
} }
ImportingEntityRAII Importing(*this); ImportingEntityRAII Importing(*this);
bool scratch;
if (!hasMissingRequiredMembers)
hasMissingRequiredMembers = &scratch;
*hasMissingRequiredMembers = false;
converter.importObjCMembers(clangDecl, const_cast<DeclContext *>(DC), converter.importObjCMembers(clangDecl, const_cast<DeclContext *>(DC),
members); members, *hasMissingRequiredMembers);
if (auto clangClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) { if (auto clangClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) {
auto swiftClass = cast<ClassDecl>(D); auto swiftClass = cast<ClassDecl>(D);

View File

@@ -924,8 +924,9 @@ public:
typeResolver = newResolver; typeResolver = newResolver;
} }
virtual ArrayRef<Decl *> loadAllMembers(const Decl *D, virtual ArrayRef<Decl *>
uint64_t unused) override; loadAllMembers(const Decl *D, uint64_t unused,
bool *hasMissingRequiredMembers) override;
template <typename DeclTy, typename ...Targs> template <typename DeclTy, typename ...Targs>
DeclTy *createDeclWithClangNode(ClangNode ClangN, Targs &&... Args) { DeclTy *createDeclWithClangNode(ClangNode ClangN, Targs &&... Args) {

View File

@@ -1832,6 +1832,15 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
return conformance; return conformance;
} }
// If the protocol contains missing requirements, it can't be conformed to
// at all.
if (Proto->hasMissingRequirements()) {
TC.diagnose(ComplainLoc, diag::protocol_has_missing_requirements,
T, Proto->getDeclaredType());
conformance->setState(ProtocolConformanceState::Invalid);
return conformance;
}
// Check that T conforms to all inherited protocols. // Check that T conforms to all inherited protocols.
for (auto InheritedProto : Proto->getProtocols()) { for (auto InheritedProto : Proto->getProtocols()) {
ProtocolConformance *InheritedConformance = nullptr; ProtocolConformance *InheritedConformance = nullptr;

View File

@@ -3207,7 +3207,8 @@ Type ModuleFile::getType(TypeID TID) {
} }
ArrayRef<Decl *> ModuleFile::loadAllMembers(const Decl *D, ArrayRef<Decl *> ModuleFile::loadAllMembers(const Decl *D,
uint64_t contextData) { uint64_t contextData,
bool *) {
// FIXME: Add PrettyStackTrace. // FIXME: Add PrettyStackTrace.
BCOffsetRAII restoreOffset(DeclTypeCursor); BCOffsetRAII restoreOffset(DeclTypeCursor);
DeclTypeCursor.JumpToBit(contextData); DeclTypeCursor.JumpToBit(contextData);

View File

@@ -73,3 +73,14 @@ extern NSNumber * const SomeNumber;
__weak id globalWeakVar; __weak id globalWeakVar;
@protocol Incomplete
- (id)getObject;
- (id)getObjectFromVarArgs:(id)first, ...;
@end
@protocol IncompleteOptional
@optional
- (id)getObject;
- (id)getObjectFromVarArgs:(id)first, ...;
@end

View File

@@ -426,3 +426,7 @@ func testWeakVariable() {
let _: AnyObject = globalWeakVar let _: AnyObject = globalWeakVar
} }
class IncompleteProtocolAdopter : Incomplete, IncompleteOptional { // expected-error {{type 'IncompleteProtocolAdopter' cannot conform to protocol 'Incomplete' because it has requirements that cannot be satisfied}}
func getObject() -> AnyObject { return self }
}