Skip to content

AST: Fast path for matching can. signatures in requirementsNotSatisfiedBy #31214

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
merged 2 commits into from
Apr 23, 2020
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
2 changes: 1 addition & 1 deletion include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
/// \param otherSig Another generic signature whose generic parameters are
/// equivalent to or a subset of the generic parameters in this signature.
SmallVector<Requirement, 4> requirementsNotSatisfiedBy(
GenericSignature otherSig);
GenericSignature otherSig) const;

/// Return the canonical version of the given type under this generic
/// signature.
Expand Down
12 changes: 8 additions & 4 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,19 +592,23 @@ bool GenericSignatureImpl::isRequirementSatisfied(Requirement requirement) {
}

SmallVector<Requirement, 4> GenericSignatureImpl::requirementsNotSatisfiedBy(
GenericSignature otherSig) {
GenericSignature otherSig) const {
SmallVector<Requirement, 4> result;

// If the signatures are the same, all requirements are satisfied.
// If the signatures match by pointer, all requirements are satisfied.
if (otherSig.getPointer() == this) return result;

// If there is no other signature, no requirements are satisfied.
if (!otherSig){
result.insert(result.end(),
getRequirements().begin(), getRequirements().end());
const auto reqs = getRequirements();
result.append(reqs.begin(), reqs.end());
return result;
}

// If the canonical signatures are equal, all requirements are satisfied.
if (getCanonicalSignature() == otherSig->getCanonicalSignature())
return result;

// Find the requirements that aren't satisfied.
for (const auto &req : getRequirements()) {
if (!otherSig->isRequirementSatisfied(req))
Expand Down
51 changes: 21 additions & 30 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ void NormalProtocolConformance::differenceAndStoreConditionalRequirements()
};

CRState = ConditionalRequirementsState::Computing;
auto success = [this](ArrayRef<Requirement> reqs) {
auto success = [this](ArrayRef<Requirement> reqs = {}) {
ConditionalRequirements = reqs;
assert(CRState == ConditionalRequirementsState::Computing);
CRState = ConditionalRequirementsState::Complete;
Expand All @@ -510,30 +510,23 @@ void NormalProtocolConformance::differenceAndStoreConditionalRequirements()
assert(CRState == ConditionalRequirementsState::Computing);
CRState = ConditionalRequirementsState::Uncomputed;
};

auto &ctxt = getProtocol()->getASTContext();
auto DC = getDeclContext();
// A non-extension conformance won't have conditional requirements.
if (!isa<ExtensionDecl>(DC)) {
success({});
return;
}

auto *ext = cast<ExtensionDecl>(DC);
auto nominal = ext->getExtendedNominal();
auto typeSig = nominal->getGenericSignature();

// A non-generic type won't have conditional requirements.
if (!typeSig) {
success({});
return;
// A non-extension conformance won't have conditional requirements.
const auto ext = dyn_cast<ExtensionDecl>(getDeclContext());
if (!ext) {
return success();
}

// If the extension is invalid, it won't ever get a signature, so we
// "succeed" with an empty result instead.
if (ext->isInvalid()) {
success({});
return;
return success();
}

// A non-generic type won't have conditional requirements.
const auto typeSig = ext->getExtendedNominal()->getGenericSignature();
if (!typeSig) {
return success();
}

// Recursively validating the signature comes up frequently as expanding
Expand All @@ -542,28 +535,26 @@ void NormalProtocolConformance::differenceAndStoreConditionalRequirements()
//
// FIXME: In the long run, break this cycle in a more principled way.
if (ext->isComputingGenericSignature()) {
failure();
return;
return failure();
}

auto extensionSig = ext->getGenericSignature();
auto canExtensionSig = extensionSig.getCanonicalSignature();
auto canTypeSig = typeSig.getCanonicalSignature();
if (canTypeSig == canExtensionSig) {
success({});
return;
}
const auto extensionSig = ext->getGenericSignature();

// The extension signature should be a superset of the type signature, meaning
// every thing in the type signature either is included too or is implied by
// something else. The most important bit is having the same type
// parameters. (NB. if/when Swift gets parameterized extensions, this needs to
// change.)
assert(canTypeSig.getGenericParams() == canExtensionSig.getGenericParams());
assert(typeSig->getCanonicalSignature().getGenericParams() ==
extensionSig->getCanonicalSignature().getGenericParams());

// Find the requirements in the extension that aren't proved by the original
// type, these are the ones that make the conformance conditional.
success(ctxt.AllocateCopy(extensionSig->requirementsNotSatisfiedBy(typeSig)));
const auto unsatReqs = extensionSig->requirementsNotSatisfiedBy(typeSig);
if (unsatReqs.empty())
return success();

return success(getProtocol()->getASTContext().AllocateCopy(unsatReqs));
}

void NormalProtocolConformance::setSignatureConformances(
Expand Down