Skip to content

GSB: Lazy requirement signature #24591

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 6 commits into from
May 10, 2019
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
17 changes: 9 additions & 8 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4081,6 +4081,7 @@ class ProtocolDecl final : public NominalTypeDecl {

friend class SuperclassDeclRequest;
friend class SuperclassTypeRequest;
friend class RequirementSignatureRequest;
friend class TypeChecker;

public:
Expand Down Expand Up @@ -4302,22 +4303,22 @@ class ProtocolDecl final : public NominalTypeDecl {
/// protocol. Requirements implied via any other protocol (e.g., inherited
/// protocols of the inherited protocols) are not mentioned. The conformance
/// requirements listed here become entries in the witness table.
ArrayRef<Requirement> getRequirementSignature() const {
assert(isRequirementSignatureComputed() &&
"getting requirement signature before computing it");
return llvm::makeArrayRef(RequirementSignature,
Bits.ProtocolDecl.NumRequirementsInSignature);
}
ArrayRef<Requirement> getRequirementSignature() const;

/// Is the requirement signature currently being computed?
bool isComputingRequirementSignature() const;

/// Has the requirement signature been computed yet?
bool isRequirementSignatureComputed() const {
return RequirementSignature != nullptr;
}

void computeRequirementSignature();

void setRequirementSignature(ArrayRef<Requirement> requirements);

private:
ArrayRef<Requirement> getCachedRequirementSignature() const;

public:
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == DeclKind::Protocol;
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/Evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ class Evaluator {
/// caching.
void clearCache() { cache.clear(); }

/// Is the given request, or an equivalent, currently being evaluated?
template <typename Request>
bool hasActiveRequest(const Request &request) const {
return activeRequests.count(AnyRequest(request));
}

private:
template <typename Request>
const AnyRequest &getCanonicalRequest(const Request &request) {
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/GenericSignatureBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1056,7 +1056,7 @@ class GenericSignatureBuilder::RequirementSource final
WrittenRequirementLoc writtenReqLoc)
: kind(kind), storageKind(StorageKind::StoredType),
hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()),
usesRequirementSignature(protocol->isRequirementSignatureComputed()),
usesRequirementSignature(!protocol->isComputingRequirementSignature()),
parent(parent) {
assert((static_cast<bool>(parent) != isRootKind(kind)) &&
"Root RequirementSource should not have parent (or vice versa)");
Expand Down
26 changes: 26 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,32 @@ class IsDynamicRequest :
void cacheResult(bool value) const;
};

/// Compute the requirements that describe a protocol.
class RequirementSignatureRequest :
public SimpleRequest<RequirementSignatureRequest,
CacheKind::SeparatelyCached,
ArrayRef<Requirement>,
ProtocolDecl *> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
llvm::Expected<ArrayRef<Requirement>> evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;

public:
// Cycle handling
void diagnoseCycle(DiagnosticEngine &diags) const;
void noteCycleStep(DiagnosticEngine &diags) const;

// Separate caching.
bool isCached() const { return true; }
Optional<ArrayRef<Requirement>> getCachedResult() const;
void cacheResult(ArrayRef<Requirement> value) const;
};

/// Describes the owner of a where clause, from which we can extract
/// requirements.
struct WhereClauseOwner {
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ SWIFT_TYPEID(IsObjCRequest)
SWIFT_TYPEID(IsFinalRequest)
SWIFT_TYPEID(IsDynamicRequest)
SWIFT_TYPEID(RequirementRequest)
SWIFT_TYPEID(RequirementSignatureRequest)
SWIFT_TYPEID(USRGenerationRequest)
SWIFT_TYPEID(StructuralTypeRequest)
SWIFT_TYPEID(DefaultTypeRequest)
Expand Down
30 changes: 4 additions & 26 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,6 @@ bestRequirementPrintLocation(ProtocolDecl *proto, const Requirement &req) {

void PrintAST::printInheritedFromRequirementSignature(ProtocolDecl *proto,
Decl *attachingTo) {
assert(proto->isRequirementSignatureComputed());
printGenericSignature(
GenericSignature::get({proto->getProtocolSelfType()} ,
proto->getRequirementSignature()),
Expand All @@ -1282,7 +1281,6 @@ void PrintAST::printInheritedFromRequirementSignature(ProtocolDecl *proto,

void PrintAST::printWhereClauseFromRequirementSignature(ProtocolDecl *proto,
Decl *attachingTo) {
assert(proto->isRequirementSignatureComputed());
unsigned flags = PrintRequirements;
if (isa<AssociatedTypeDecl>(attachingTo))
flags |= SwapSelfAndDependentMemberType;
Expand Down Expand Up @@ -2316,11 +2314,7 @@ void PrintAST::visitAssociatedTypeDecl(AssociatedTypeDecl *decl) {
});

auto proto = decl->getProtocol();
if (proto->isRequirementSignatureComputed()) {
printInheritedFromRequirementSignature(proto, decl);
} else {
printInherited(decl);
}
printInheritedFromRequirementSignature(proto, decl);

if (decl->hasDefaultDefinitionType()) {
Printer << " = ";
Expand All @@ -2329,13 +2323,7 @@ void PrintAST::visitAssociatedTypeDecl(AssociatedTypeDecl *decl) {

// As with protocol's trailing where clauses, use the requirement signature
// when available.
if (proto->isRequirementSignatureComputed()) {
printWhereClauseFromRequirementSignature(proto, decl);
} else {
if (auto trailingWhere = decl->getTrailingWhereClause()) {
printTrailingWhereClause(trailingWhere);
}
}
printWhereClauseFromRequirementSignature(proto, decl);
}

void PrintAST::visitEnumDecl(EnumDecl *decl) {
Expand Down Expand Up @@ -2442,23 +2430,13 @@ void PrintAST::visitProtocolDecl(ProtocolDecl *decl) {
Printer.printName(decl->getName());
});

if (decl->isRequirementSignatureComputed()) {
printInheritedFromRequirementSignature(decl, decl);
} else {
printInherited(decl);
}
printInheritedFromRequirementSignature(decl, decl);

// The trailing where clause is a syntactic thing, which isn't serialized
// (etc.) and thus isn't available for printing things out of
// already-compiled SIL modules. The requirement signature is available in
// such cases, so let's go with that when we can.
if (decl->isRequirementSignatureComputed()) {
printWhereClauseFromRequirementSignature(decl, decl);
} else {
if (auto trailingWhere = decl->getTrailingWhereClause()) {
printTrailingWhereClause(trailingWhere);
}
}
printWhereClauseFromRequirementSignature(decl, decl);
}
if (Options.TypeDefinitions) {
printMembersOfDecl(decl, false, true,
Expand Down
26 changes: 16 additions & 10 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4569,20 +4569,19 @@ void ProtocolDecl::createGenericParamsIfMissing() {
setGenericParams(result);
}

void ProtocolDecl::computeRequirementSignature() {
assert(!RequirementSignature && "already computed requirement signature");
ArrayRef<Requirement> ProtocolDecl::getRequirementSignature() const {
return evaluateOrDefault(getASTContext().evaluator,
RequirementSignatureRequest { const_cast<ProtocolDecl *>(this) },
None);
}

// Compute and record the signature.
auto requirementSig =
GenericSignatureBuilder::computeRequirementSignature(this);
RequirementSignature = requirementSig->getRequirements().data();
assert(RequirementSignature != nullptr);
Bits.ProtocolDecl.NumRequirementsInSignature =
requirementSig->getRequirements().size();
bool ProtocolDecl::isComputingRequirementSignature() const {
return getASTContext().evaluator.hasActiveRequest(
RequirementSignatureRequest{const_cast<ProtocolDecl*>(this)});
}

void ProtocolDecl::setRequirementSignature(ArrayRef<Requirement> requirements) {
assert(!RequirementSignature && "already computed requirement signature");
assert(!RequirementSignature && "requirement signature already set");
if (requirements.empty()) {
RequirementSignature = reinterpret_cast<Requirement *>(this + 1);
Bits.ProtocolDecl.NumRequirementsInSignature = 0;
Expand All @@ -4592,6 +4591,13 @@ void ProtocolDecl::setRequirementSignature(ArrayRef<Requirement> requirements) {
}
}

ArrayRef<Requirement> ProtocolDecl::getCachedRequirementSignature() const {
assert(RequirementSignature &&
"getting requirement signature before computing it");
return llvm::makeArrayRef(RequirementSignature,
Bits.ProtocolDecl.NumRequirementsInSignature);
}

void ProtocolDecl::computeKnownProtocolKind() const {
auto module = getModuleContext();
if (module != module->getASTContext().getStdlibModule() &&
Expand Down
1 change: 1 addition & 0 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ GenericTypeParamType *DeclContext::getProtocolSelfType() const {

GenericParamList *genericParams;
if (auto proto = dyn_cast<ProtocolDecl>(this)) {
const_cast<ProtocolDecl*>(proto)->createGenericParamsIfMissing();
genericParams = proto->getGenericParams();
} else {
genericParams = cast<ExtensionDecl>(this)->getGenericParams();
Expand Down
11 changes: 1 addition & 10 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -771,11 +771,6 @@ static bool hasNonCanonicalSelfProtocolRequirement(
// If we don't already have a requirement signature for this protocol,
// build one now.
auto inProto = source->getProtocolDecl();
if (!inProto->isRequirementSignatureComputed()) {
inProto->computeRequirementSignature();
assert(inProto->isRequirementSignatureComputed() &&
"couldn't compute requirement signature?");
}

// Check whether the given requirement is in the requirement signature.
if (!source->usesRequirementSignature &&
Expand Down Expand Up @@ -856,13 +851,9 @@ void GenericSignature::buildConformanceAccessPath(

// The generic signature builder we're using for this protocol
// wasn't built from its own requirement signature, so we can't
// trust it. Make sure we have a requirement signature, then build
// a new generic signature builder.
// trust it, build a new generic signature builder.
// FIXME: It would be better if we could replace the canonical generic
// signature builder with the rebuilt one.
if (!requirementSignatureProto->isRequirementSignatureComputed())
requirementSignatureProto->computeRequirementSignature();
assert(requirementSignatureProto->isRequirementSignatureComputed());

replacementBuilder.emplace(getASTContext());
replacementBuilder->addGenericSignature(
Expand Down
30 changes: 1 addition & 29 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4107,7 +4107,7 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
// Use the requirement signature to avoid rewalking the entire protocol. This
// cannot compute the requirement signature directly, because that may be
// infinitely recursive: this code is also used to construct it.
if (proto->isRequirementSignatureComputed()) {
if (!proto->isComputingRequirementSignature()) {
auto innerSource =
FloatingRequirementSource::viaProtocolRequirement(source, proto,
/*inferred=*/false);
Expand Down Expand Up @@ -7447,34 +7447,6 @@ GenericSignature *GenericSignatureBuilder::computeGenericSignature(
return sig;
}

GenericSignature *GenericSignatureBuilder::computeRequirementSignature(
ProtocolDecl *proto) {
GenericSignatureBuilder builder(proto->getASTContext());

// Add all of the generic parameters.
proto->createGenericParamsIfMissing();
for (auto gp : *proto->getGenericParams())
builder.addGenericParameter(gp);

// Add the conformance of 'self' to the protocol.
auto selfType =
proto->getSelfInterfaceType()->castTo<GenericTypeParamType>();
auto requirement =
Requirement(RequirementKind::Conformance, selfType,
proto->getDeclaredInterfaceType());

builder.addRequirement(
requirement,
RequirementSource::forRequirementSignature(builder, selfType,
proto),
nullptr);

return std::move(builder).computeGenericSignature(
SourceLoc(),
/*allowConcreteGenericPArams=*/false,
/*allowBuilderToMove=*/false);
}

#pragma mark Generic signature verification

void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context,
Expand Down
27 changes: 27 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,33 @@ void IsDynamicRequest::cacheResult(bool value) const {
decl->setIsDynamic(value);
}

//----------------------------------------------------------------------------//
// RequirementSignatureRequest computation.
//----------------------------------------------------------------------------//

void RequirementSignatureRequest::diagnoseCycle(DiagnosticEngine &diags) const {
auto decl = std::get<0>(getStorage());
diags.diagnose(decl, diag::circular_reference);
}

void RequirementSignatureRequest::noteCycleStep(DiagnosticEngine &diags) const {
auto decl = std::get<0>(getStorage());
diags.diagnose(decl, diag::circular_reference_through);
}

Optional<ArrayRef<Requirement>> RequirementSignatureRequest::getCachedResult() const {
auto proto = std::get<0>(getStorage());
if (proto->isRequirementSignatureComputed())
return proto->getCachedRequirementSignature();

return None;
}

void RequirementSignatureRequest::cacheResult(ArrayRef<Requirement> value) const {
auto proto = std::get<0>(getStorage());
proto->setRequirementSignature(value);
}

//----------------------------------------------------------------------------//
// Requirement computation.
//----------------------------------------------------------------------------//
Expand Down
7 changes: 0 additions & 7 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4521,10 +4521,6 @@ namespace {
inheritedTypes);
result->setInherited(Impl.SwiftContext.AllocateCopy(inheritedTypes));

// Compute the requirement signature.
if (!result->isRequirementSignatureComputed())
result->computeRequirementSignature();

result->setMemberLoader(&Impl, 0);

// Add the protocol decl to ExternalDefinitions so that IRGen can emit
Expand Down Expand Up @@ -7878,9 +7874,6 @@ void ClangImporter::Implementation::finishNormalConformance(
PrettyStackTraceConformance trace(SwiftContext, "completing import of",
conformance);

if (!proto->isRequirementSignatureComputed())
proto->computeRequirementSignature();

finishTypeWitnesses(conformance);
finishInheritedConformances(conformance);
finishSignatureConformances(conformance);
Expand Down
Loading