Teach loadAllMembers() implementations to add the members themselves.

The contract for LazyResolver::loadAllMembers() was that the caller
would handle actually adding the members, since it was an iterable
declaration context and could centralize that (simple) logic. However,
this fails in the Clang importer in rare but amusing ways when some of
the deferred actions (e.g., finishing a protocol conformance) depend
on having the members already set. The deferred action occurs after
the member list is complete in ClangImporter's loadAllMembers(), but
before its caller actual set the member list, leaving incomplete
conformances. Fixes rdar://problem/18884272.

Swift SVN r25630
This commit is contained in:
Doug Gregor
2015-02-28 01:03:41 +00:00
parent 5fed3c4b60
commit bce5c20c25
7 changed files with 41 additions and 23 deletions

View File

@@ -114,13 +114,12 @@ public:
/// Populates the given vector with all member decls for \p D. /// Populates the given vector with all member decls for \p D.
/// ///
/// The implementation should \em not call setMembers on \p D. /// The implementation should add the members to D.
/// ///
/// \param[out] hasMissingRequiredMembers If present, set to true if any /// \param[out] hasMissingRequiredMembers If present, set to true if any
/// members failed to import and were non-optional protocol requirements. /// members failed to import and were non-optional protocol requirements.
virtual void virtual void
loadAllMembers(const Decl *D, uint64_t contextData, loadAllMembers(Decl *D, uint64_t contextData,
SmallVectorImpl<Decl *> &Members,
bool *hasMissingRequiredMembers = nullptr) { bool *hasMissingRequiredMembers = nullptr) {
llvm_unreachable("unimplemented"); llvm_unreachable("unimplemented");
} }

View File

@@ -574,9 +574,8 @@ public:
/// Has no effect in NDEBUG builds. /// Has no effect in NDEBUG builds.
void verify() const; void verify() const;
virtual void loadAllMembers(const Decl *D, virtual void loadAllMembers(Decl *D,
uint64_t contextData, uint64_t contextData,
SmallVectorImpl<Decl *> &Members,
bool *ignored) override; bool *ignored) override;
virtual void virtual void

View File

@@ -683,12 +683,8 @@ void IterableDeclContext::loadAllMembers() const {
} }
bool hasMissingRequiredMembers = false; bool hasMissingRequiredMembers = false;
SmallVector<Decl *, 16> Members; resolver->loadAllMembers(const_cast< Decl *>(container), contextData,
resolver->loadAllMembers(container, contextData, Members,
&hasMissingRequiredMembers); &hasMissingRequiredMembers);
for (auto member : Members) {
const_cast<IterableDeclContext *>(this)->addMember(member);
}
if (hasMissingRequiredMembers) if (hasMissingRequiredMembers)
if (auto proto = dyn_cast<ProtocolDecl>(this)) if (auto proto = dyn_cast<ProtocolDecl>(this))

View File

@@ -5717,31 +5717,35 @@ createUnavailableDecl(Identifier name, DeclContext *dc, Type type,
void void
ClangImporter::Implementation::loadAllMembers(const Decl *D, uint64_t unused, ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t unused,
SmallVectorImpl<Decl *> &members,
bool *hasMissingRequiredMembers) { bool *hasMissingRequiredMembers) {
assert(D->hasClangNode()); assert(D->hasClangNode());
auto clangDecl = cast<clang::ObjCContainerDecl>(D->getClangDecl()); auto clangDecl = cast<clang::ObjCContainerDecl>(D->getClangDecl());
SwiftDeclConverter converter(*this); SwiftDeclConverter converter(*this);
const DeclContext *DC; DeclContext *DC;
IterableDeclContext *IDC;
ArrayRef<ProtocolDecl *> protos; ArrayRef<ProtocolDecl *> protos;
// Figure out the declaration context we're importing into. // Figure out the declaration context we're importing into.
if (auto nominal = dyn_cast<NominalTypeDecl>(D)) { if (auto nominal = dyn_cast<NominalTypeDecl>(D)) {
DC = nominal; DC = nominal;
IDC = nominal;
} else { } else {
DC = cast<ExtensionDecl>(D); auto ext = cast<ExtensionDecl>(D);
DC = ext;
IDC = ext;
} }
ImportingEntityRAII Importing(*this); ImportingEntityRAII Importing(*this);
SmallVector<Decl *, 16> members;
bool scratch; bool scratch;
if (!hasMissingRequiredMembers) if (!hasMissingRequiredMembers)
hasMissingRequiredMembers = &scratch; hasMissingRequiredMembers = &scratch;
*hasMissingRequiredMembers = false; *hasMissingRequiredMembers = false;
converter.importObjCMembers(clangDecl, const_cast<DeclContext *>(DC), converter.importObjCMembers(clangDecl, DC,
members, *hasMissingRequiredMembers); members, *hasMissingRequiredMembers);
if (auto clangClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) { if (auto clangClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) {
@@ -5766,9 +5770,15 @@ ClangImporter::Implementation::loadAllMembers(const Decl *D, uint64_t unused,
// Import mirrored declarations for protocols to which this category // Import mirrored declarations for protocols to which this category
// or extension conforms. // or extension conforms.
// FIXME: This is supposed to be a short-term hack. // FIXME: This is supposed to be a short-term hack.
converter.importMirroredProtocolMembers(clangDecl, converter.importMirroredProtocolMembers(clangDecl, DC,
const_cast<DeclContext *>(DC),
protos, members, SwiftContext); protos, members, SwiftContext);
// Add the members now, before ~ImportingEntityRAII does work that might
// involve them.
for (auto member : members) {
IDC->addMember(member);
}
} }
Optional<MappedTypeNameKind> Optional<MappedTypeNameKind>

View File

@@ -955,8 +955,7 @@ public:
} }
virtual void virtual void
loadAllMembers(const Decl *D, uint64_t unused, loadAllMembers(Decl *D, uint64_t unused,
SmallVectorImpl<Decl *> &Members,
bool *hasMissingRequiredMembers) override; bool *hasMissingRequiredMembers) override;
template <typename DeclTy, typename ...Targs> template <typename DeclTy, typename ...Targs>

View File

@@ -595,7 +595,7 @@ ModuleFile::maybeReadConformance(Type conformingType,
witness = ConcreteDeclRef(ctx, second, substitutions); witness = ConcreteDeclRef(ctx, second, substitutions);
witnesses.insert(std::make_pair(first, witness)); witnesses.insert(std::make_pair(first, witness));
if (second) if (second && second != first)
ctx.recordConformingDecl(second, first); ctx.recordConformingDecl(second, first);
} }
assert(rawIDIter <= rawIDs.end() && "read too much"); assert(rawIDIter <= rawIDs.end() && "read too much");
@@ -3782,16 +3782,25 @@ Type ModuleFile::getType(TypeID TID) {
return typeOrOffset; return typeOrOffset;
} }
void ModuleFile::loadAllMembers(const Decl *D, void ModuleFile::loadAllMembers(Decl *D,
uint64_t contextData, uint64_t contextData,
SmallVectorImpl<Decl *> &Members,
bool *) { bool *) {
// FIXME: Add PrettyStackTrace. // FIXME: Add PrettyStackTrace.
BCOffsetRAII restoreOffset(DeclTypeCursor); BCOffsetRAII restoreOffset(DeclTypeCursor);
DeclTypeCursor.JumpToBit(contextData); DeclTypeCursor.JumpToBit(contextData);
bool Err = readMembers(Members); SmallVector<Decl *, 16> members;
bool Err = readMembers(members);
assert(!Err && "unable to read members"); assert(!Err && "unable to read members");
(void)Err; (void)Err;
IterableDeclContext *IDC;
if (auto nominal = dyn_cast<NominalTypeDecl>(D))
IDC = nominal;
else
IDC = cast<ExtensionDecl>(D);
for (auto member : members)
IDC->addMember(member);
} }
void void

View File

@@ -6,6 +6,12 @@
import Foundation import Foundation
import StdlibUnittest import StdlibUnittest
// rdar://problem/18884272
// Make sure that NSObject conforms to NSObjectProtocol. This
// particular bug is ridiculously hard to trigger without a complete
// SDK, so it sits here.
let objcProtocol: NSObjectProtocol = NSObject()
var FoundationTestSuite = TestSuite("Foundation") var FoundationTestSuite = TestSuite("Foundation")
func asNSString(s: String) -> NSString { return s as NSString } func asNSString(s: String) -> NSString { return s as NSString }