Skip to content

Reworking ClangImporter to not depend on Sema, part 4 #11873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,33 @@ ProtocolConformance *ConformanceLookupTable::getConformance(
conformance = ctx.getConformance(conformingType, protocol, conformanceLoc,
conformingDC,
ProtocolConformanceState::Incomplete);

// If the conformance was synthesized by the ClangImporter, give it a
// lazy loader that will be used to populate the conformance.

// First, if this is a conformance to a base protocol of a derived
// protocol, find the most derived protocol.
auto *impliedEntry = entry;
while (impliedEntry->getKind() == ConformanceEntryKind::Implied)
impliedEntry = impliedEntry->Source.getImpliedSource();

// Check if this was a synthesized conformance.
if (impliedEntry->getKind() == ConformanceEntryKind::Synthesized) {
auto *impliedProto = impliedEntry->getProtocol();

// Find a SynthesizedProtocolAttr corresponding to the protocol.
for (auto attr : conformingNominal->getAttrs()
.getAttributes<SynthesizedProtocolAttr>()) {
auto otherProto = ctx.getProtocol(attr->getProtocolKind());
if (otherProto == impliedProto) {
// Set the conformance loader to the loader stashed inside
// the attribute.
cast<NormalProtocolConformance>(conformance)
->setLazyLoader(attr->getLazyLoader(), /*context=*/0);
break;
}
}
}
}

// Record the conformance.
Expand Down
3 changes: 0 additions & 3 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,11 @@ void NormalProtocolConformance::setSignatureConformances(

void NormalProtocolConformance::resolveLazyInfo() const {
assert(Loader);
assert(isComplete());

auto *loader = Loader;
auto *mutableThis = const_cast<NormalProtocolConformance *>(this);
mutableThis->Loader = nullptr;
mutableThis->setState(ProtocolConformanceState::Incomplete);
loader->finishNormalConformance(mutableThis, LoaderContextData);
mutableThis->setState(ProtocolConformanceState::Complete);
}

void NormalProtocolConformance::setLazyLoader(LazyConformanceLoader *loader,
Expand Down
124 changes: 70 additions & 54 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Parse/Lexer.h"
Expand Down Expand Up @@ -7358,17 +7359,56 @@ void ClangImporter::Implementation::finishPendingActions() {
}
}

void ClangImporter::Implementation::finishNormalConformance(
NormalProtocolConformance *conformance,
uint64_t unused) {
(void)unused;
const ProtocolDecl *proto = conformance->getProtocol();
/// Make sure any inherited conformances also get completed, if necessary.
static void finishInheritedConformances(
NormalProtocolConformance *conformance) {
auto *proto = conformance->getProtocol();

PrettyStackTraceType trace(SwiftContext, "completing conformance for",
conformance->getType());
PrettyStackTraceDecl traceTo("... to", proto);
SmallVector<ProtocolDecl *, 2> inheritedProtos;
for (auto *inherited : proto->getInheritedProtocols())
inheritedProtos.push_back(inherited);

// Sort for deterministic import.
ProtocolType::canonicalizeProtocols(inheritedProtos);

// Schedule any that aren't complete.
for (auto *inherited : inheritedProtos) {
ModuleDecl *M = conformance->getDeclContext()->getParentModule();
auto inheritedConformance = M->lookupConformance(conformance->getType(),
inherited);
assert(inheritedConformance && inheritedConformance->isConcrete() &&
"inherited conformance not found");
}
}

/// Collect conformances for the requirement signature.
static void finishSignatureConformances(
NormalProtocolConformance *conformance) {
auto *proto = conformance->getProtocol();

SmallVector<ProtocolConformanceRef, 4> reqConformances;
for (const auto &req : proto->getRequirementSignature()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

assert(req.getFirstType()->isEqual(proto->getSelfInterfaceType()));
auto reqProto = req.getSecondType()->castTo<ProtocolType>()->getDecl();

ModuleDecl *M = conformance->getDeclContext()->getParentModule();
auto reqConformance = M->lookupConformance(conformance->getType(),
reqProto);
assert(reqConformance && reqConformance->isConcrete() &&
"required conformance not found");
reqConformances.push_back(*reqConformance);
}
conformance->setSignatureConformances(reqConformances);
}

/// Create witnesses for requirements not already met.
static void finishMissingOptionalWitnesses(
NormalProtocolConformance *conformance) {
auto *proto = conformance->getProtocol();

// Create witnesses for requirements not already met.
for (auto req : proto->getMembers()) {
auto valueReq = dyn_cast<ValueDecl>(req);
if (!valueReq)
Expand All @@ -7391,60 +7431,36 @@ void ClangImporter::Implementation::finishNormalConformance(
auto witness = conformance->getWitness(valueReq, nullptr).getDecl();
if (auto ctor = dyn_cast_or_null<ConstructorDecl>(witness)) {
if (!ctor->getAttrs().hasAttribute<RequiredAttr>()) {
ctor->getAttrs().add(
new (SwiftContext) RequiredAttr(/*IsImplicit=*/true));
auto &ctx = proto->getASTContext();
ctor->getAttrs().add(new (ctx) RequiredAttr(/*IsImplicit=*/true));
}
}
}
}
}

// And make sure any inherited conformances also get completed, if necessary.
SmallVector<ProtocolDecl *, 8> inheritedProtos;
for (auto *inherited : proto->getInheritedProtocols()) {
inheritedProtos.push_back(inherited);
}
// Sort for deterministic import.
llvm::array_pod_sort(inheritedProtos.begin(),
inheritedProtos.end(),
[](ProtocolDecl * const *left,
ProtocolDecl * const *right) -> int {
// We know all Objective-C protocols in a translation unit have unique
// names, so go by the Objective-C name.
auto getDeclName = [](const ProtocolDecl *proto) -> StringRef {
if (auto *objCAttr = proto->getAttrs().getAttribute<ObjCAttr>())
if (auto name = objCAttr->getName())
return name.getValue().getSelectorPieces().front().str();
return proto->getName().str();
};
return getDeclName(*left).compare(getDeclName(*right));
});
void ClangImporter::Implementation::finishNormalConformance(
NormalProtocolConformance *conformance,
uint64_t unused) {
(void)unused;

// Schedule any that aren't complete.
for (auto *inherited : inheritedProtos) {
ModuleDecl *M = conformance->getDeclContext()->getParentModule();
auto inheritedConformance = M->lookupConformance(conformance->getType(),
inherited);
assert(inheritedConformance && inheritedConformance->isConcrete() &&
"inherited conformance not found");
}
auto *proto = conformance->getProtocol();
PrettyStackTraceType trace(SwiftContext, "completing conformance for",
conformance->getType());
PrettyStackTraceDecl traceTo("... to", proto);

// Collect conformances for the requirement signature.
SmallVector<ProtocolConformanceRef, 4> reqConformances;
for (const auto &req : proto->getRequirementSignature()) {
if (req.getKind() != RequirementKind::Conformance)
continue;
finishInheritedConformances(conformance);
finishSignatureConformances(conformance);

assert(req.getFirstType()->isEqual(proto->getSelfInterfaceType()));
auto reqProto = req.getSecondType()->castTo<ProtocolType>()->getDecl();
// Imported conformances to @objc protocols also require additional
// initialization to complete the requirement to witness mapping.
if (!proto->isObjC())
return;

ModuleDecl *M = conformance->getDeclContext()->getParentModule();
auto reqConformance = M->lookupConformance(conformance->getType(),
reqProto);
assert(reqConformance && reqConformance->isConcrete() &&
"required conformance not found");
reqConformances.push_back(*reqConformance);
}
conformance->setSignatureConformances(reqConformances);
assert(conformance->isComplete());
conformance->setState(ProtocolConformanceState::Incomplete);

finishMissingOptionalWitnesses(conformance);

conformance->setState(ProtocolConformanceState::Complete);
}
Expand Down
5 changes: 5 additions & 0 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4642,6 +4642,11 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
PrettyStackTraceDecl traceTo("... to", conformance->getProtocol());
++NumNormalProtocolConformancesCompleted;

assert(conformance->isComplete());

conformance->setState(ProtocolConformanceState::Incomplete);
SWIFT_DEFER { conformance->setState(ProtocolConformanceState::Complete); };

// Find the conformance record.
BCOffsetRAII restoreOffset(DeclTypeCursor);
DeclTypeCursor.JumpToBit(contextData);
Expand Down