Skip to content

Commit

Permalink
Merge pull request swiftlang#24591 from xymus/req-sig-request
Browse files Browse the repository at this point in the history
GSB: Lazy requirement signature
  • Loading branch information
xymus authored May 10, 2019
2 parents c2ecf6d + cfae7bb commit 9c4c304
Show file tree
Hide file tree
Showing 17 changed files with 129 additions and 112 deletions.
17 changes: 9 additions & 8 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4084,6 +4084,7 @@ class ProtocolDecl final : public NominalTypeDecl {

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

public:
Expand Down Expand Up @@ -4305,22 +4306,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

0 comments on commit 9c4c304

Please sign in to comment.