Skip to content

Never use a protocol's requirement signature as a generic signature #10711

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
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
29 changes: 14 additions & 15 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3501,7 +3501,7 @@ class ProtocolDecl : public NominalTypeDecl {

/// The generic signature representing exactly the new requirements introduced
/// by this protocol.
GenericSignature *RequirementSignature = nullptr;
const Requirement *RequirementSignature = nullptr;

/// True if the protocol has requirements that cannot be satisfied (e.g.
/// because they could not be imported from Objective-C).
Expand All @@ -3511,6 +3511,9 @@ class ProtocolDecl : public NominalTypeDecl {
/// value, plus one. Otherwise, it will be 0.
unsigned KnownProtocol : 6;

/// The number of requirements in the requirement signature.
unsigned NumRequirementsInSignature : 16;

bool requiresClassSlow();

bool existentialConformsToSelfSlow();
Expand Down Expand Up @@ -3687,8 +3690,6 @@ class ProtocolDecl : public NominalTypeDecl {

/// Create the implicit generic parameter list for a protocol or
/// extension thereof.
///
/// FIXME: protocol extensions will introduce a where clause here as well.
GenericParamList *createGenericParams(DeclContext *dc);

/// Create the generic parameters of this protocol if the haven't been
Expand All @@ -3700,17 +3701,17 @@ class ProtocolDecl : public NominalTypeDecl {
return TrailingWhere;
}

/// Retrieve the generic signature representing the requirements introduced by
/// this protocol.
/// Retrieve the requirements that describe this protocol.
///
/// These are the requirements like any inherited protocols and conformances
/// for associated types that are mentioned literally in this
/// decl. Requirements implied via inheritance are not mentioned, nor is the
/// conformance of Self to this protocol.
GenericSignature *getRequirementSignature() const {
assert(RequirementSignature &&
/// These are the requirements including any inherited protocols
/// and conformances for associated types that are introduced in this
/// 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 RequirementSignature;
return llvm::makeArrayRef(RequirementSignature, NumRequirementsInSignature);
}

/// Has the requirement signature been computed yet?
Expand All @@ -3720,9 +3721,7 @@ class ProtocolDecl : public NominalTypeDecl {

void computeRequirementSignature();

void setRequirementSignature(GenericSignature *sig) {
RequirementSignature = sig;
}
void setRequirementSignature(ArrayRef<Requirement> requirements);

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
Expand Down
8 changes: 3 additions & 5 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ class LinkEntity {
CanType associatedType,
ProtocolDecl *requirement) {
unsigned index = 0;
for (auto &reqt : conformance->getProtocol()->getRequirementSignature()
->getRequirements()) {
for (const auto &reqt :
conformance->getProtocol()->getRequirementSignature()) {
if (reqt.getKind() == RequirementKind::Conformance &&
reqt.getFirstType()->getCanonicalType() == associatedType &&
reqt.getSecondType()->castTo<ProtocolType>()->getDecl() ==
Expand All @@ -328,9 +328,7 @@ class LinkEntity {
static std::pair<CanType, ProtocolDecl*>
getAssociatedConformanceByIndex(const ProtocolConformance *conformance,
unsigned index) {
auto &reqt =
conformance->getProtocol()->getRequirementSignature()
->getRequirements()[index];
auto &reqt = conformance->getProtocol()->getRequirementSignature()[index];
assert(reqt.getKind() == RequirementKind::Conformance);
return { reqt.getFirstType()->getCanonicalType(),
reqt.getSecondType()->castTo<ProtocolType>()->getDecl() };
Expand Down
8 changes: 4 additions & 4 deletions include/swift/SIL/SILWitnessVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
}
};

for (auto &reqt : protocol->getRequirementSignature()
->getCanonicalSignature()->getRequirements()) {
for (const auto &reqt : protocol->getRequirementSignature()) {
switch (reqt.getKind()) {
// These requirements don't show up in the witness table.
case RequirementKind::Superclass:
Expand All @@ -74,10 +73,11 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
continue;

case RequirementKind::Conformance: {
auto type = CanType(reqt.getFirstType());
auto type = reqt.getFirstType()->getCanonicalType();
assert(type->isTypeParameter());
auto requirement =
cast<ProtocolType>(CanType(reqt.getSecondType()))->getDecl();
cast<ProtocolType>(reqt.getSecondType()->getCanonicalType())
->getDecl();

// ObjC protocols do not have witnesses.
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(requirement))
Expand Down
4 changes: 3 additions & 1 deletion lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,9 @@ namespace {

OS << " requirement signature=";
if (PD->isRequirementSignatureComputed()) {
OS << PD->getRequirementSignature()->getAsString();
OS << GenericSignature::get({PD->getProtocolSelfType()} ,
PD->getRequirementSignature())
->getAsString();
} else {
OS << "<null>";
}
Expand Down
4 changes: 3 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1367,7 +1367,9 @@ void PrintAST::printWhereClauseFromRequirementSignature(ProtocolDecl *proto,
if (isa<AssociatedTypeDecl>(attachingTo))
flags |= SwapSelfAndDependentMemberType;
printGenericSignature(
proto->getRequirementSignature(), flags,
GenericSignature::get({proto->getProtocolSelfType()} ,
proto->getRequirementSignature()),
flags,
[&](const Requirement &req) {
auto location = bestRequirementPrintLocation(proto, req);
return location.AttachedTo == attachingTo && location.InWhereClause;
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2348,7 +2348,7 @@ class Verifier : public ASTWalker {
if (!normal->isInvalid()){
auto conformances = normal->getSignatureConformances();
unsigned idx = 0;
for (auto req : proto->getRequirementSignature()->getRequirements()) {
for (const auto &req : proto->getRequirementSignature()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

Expand Down
24 changes: 20 additions & 4 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2861,10 +2861,11 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
ProtocolDeclBits.RequiresClass = false;
ProtocolDeclBits.ExistentialConformsToSelfValid = false;
ProtocolDeclBits.ExistentialConformsToSelf = false;
KnownProtocol = 0;
ProtocolDeclBits.Circularity
= static_cast<unsigned>(CircularityCheck::Unchecked);
HasMissingRequirements = false;
KnownProtocol = 0;
NumRequirementsInSignature = 0;
}

llvm::TinyPtrVector<ProtocolDecl *>
Expand Down Expand Up @@ -2895,7 +2896,7 @@ ProtocolDecl::getInheritedProtocols() const {

// Gather inherited protocols from the requirement signature.
auto selfType = getProtocolSelfType();
for (auto req : getRequirementSignature()->getRequirements()) {
for (const auto &req : getRequirementSignature()) {
if (req.getKind() == RequirementKind::Conformance &&
req.getFirstType()->isEqual(selfType))
result.push_back(req.getSecondType()->castTo<ProtocolType>()->getDecl());
Expand Down Expand Up @@ -3299,8 +3300,23 @@ void ProtocolDecl::computeRequirementSignature() {
GenericSignatureBuilder::RequirementSource
::forRequirementSignature(selfPA, this),
nullptr);

RequirementSignature = builder.computeGenericSignature(SourceLoc());

// Compute and record the signature.
auto requirementSig = builder.computeGenericSignature(SourceLoc());
RequirementSignature = requirementSig->getRequirements().data();
assert(RequirementSignature != nullptr);
NumRequirementsInSignature = requirementSig->getRequirements().size();
}

void ProtocolDecl::setRequirementSignature(ArrayRef<Requirement> requirements) {
assert(!RequirementSignature && "already computed requirement signature");
if (requirements.empty()) {
RequirementSignature = reinterpret_cast<Requirement *>(this + 1);
NumRequirementsInSignature = 0;
} else {
RequirementSignature = getASTContext().AllocateCopy(requirements).data();
NumRequirementsInSignature = requirements.size();
}
}

/// Returns the default witness for a requirement, or nullptr if there is
Expand Down
8 changes: 0 additions & 8 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,6 @@ ProtocolDecl *DeclContext::getAsProtocolExtensionContext() const {
GenericTypeParamType *DeclContext::getProtocolSelfType() const {
assert(getAsProtocolOrProtocolExtensionContext() && "not a protocol");

// FIXME: This comes up when the extension didn't resolve,
// and we have a protocol nested inside that extension
// (which is not allowed in the first place).
//
// Handle this more systematically elsewhere.
if (!isInnermostContextGeneric())
return nullptr;

return getGenericParamsOfContext()->getParams().front()
->getDeclaredInterfaceType()
->castTo<GenericTypeParamType>();
Expand Down
54 changes: 33 additions & 21 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,13 +793,13 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(

#ifndef NDEBUG
// Local function to determine whether there is a conformance of the given
// subject type to the given protocol within the given generic signature's
// explicit requirements.
auto hasConformanceInSignature = [&](const GenericSignature *genericSig,
// subject type to the given protocol within the given set of explicit
// requirements.
auto hasConformanceInSignature = [&](ArrayRef<Requirement> requirements,
Type subjectType,
ProtocolDecl *proto) -> bool {
// Make sure this requirement exists in the requirement signature.
for (const auto& req: genericSig->getRequirements()) {
for (const auto& req: requirements) {
if (req.getKind() == RequirementKind::Conformance &&
req.getFirstType()->isEqual(subjectType) &&
req.getSecondType()->castTo<ProtocolType>()->getDecl()
Expand All @@ -814,16 +814,27 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(

// Local function to construct the conformance access path from the
// requirement.
std::function<void(GenericSignature *, const RequirementSource *,
ProtocolDecl *, Type)> buildPath;
buildPath = [&](GenericSignature *sig, const RequirementSource *source,
ProtocolDecl *conformingProto, Type rootType) {
std::function<void(ArrayRef<Requirement>, const RequirementSource *,
ProtocolDecl *, Type, ProtocolDecl *)> buildPath;
buildPath = [&](ArrayRef<Requirement> reqs, const RequirementSource *source,
ProtocolDecl *conformingProto, Type rootType,
ProtocolDecl *requirementSignatureProto) {
// Each protocol requirement is a step along the path.
if (source->isProtocolRequirement()) {
// If we're expanding for a protocol that has no requirement signature
// (yet) and have hit the penultimate step, this is the last step
// that would occur in the requirement signature.
if (!source->parent->parent && requirementSignatureProto) {
Type subjectType = source->getStoredType()->getCanonicalType();
path.path.push_back({subjectType, conformingProto});
return;
}

// Follow the rest of the path to derive the conformance into which
// this particular protocol requirement step would look.
auto inProtocol = source->getProtocolDecl();
buildPath(sig, source->parent, inProtocol, rootType);
buildPath(reqs, source->parent, inProtocol, rootType,
requirementSignatureProto);
assert(path.path.back().second == inProtocol &&
"path produces incorrect conformance");

Expand Down Expand Up @@ -857,11 +868,10 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
"missing signature");
}

// Get a generic signature builder for the requirement signature. This has
// the requirement we need.
auto reqSig = inProtocol->getRequirementSignature();
auto &reqSigBuilder = *reqSig->getGenericSignatureBuilder(
*inProtocol->getModuleContext());
// Get a generic signature for the protocol's signature.
auto inProtoSig = inProtocol->getGenericSignature();
auto &inProtoSigBuilder = *inProtoSig->getGenericSignatureBuilder(
*inProtocol->getModuleContext());

// Retrieve the stored type, but erase all of the specific associated
// type declarations; we don't want any details of the enclosing context
Expand All @@ -870,9 +880,9 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(

// Dig out the potential archetype for this stored type.
auto pa =
reqSigBuilder.resolveArchetype(
inProtoSigBuilder.resolveArchetype(
storedType,
ArchetypeResolutionKind::AlwaysPartial);
ArchetypeResolutionKind::WellFormed);
auto equivClass = pa->getOrCreateEquivalenceClass();

// Find the conformance of this potential archetype to the protocol in
Expand All @@ -881,8 +891,8 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
assert(conforms != equivClass->conformsTo.end());

// Compute the root type, canonicalizing it w.r.t. the protocol context.
auto inProtoSig = inProtocol->getGenericSignature();
auto conformsSource = getBestRequirementSource(conforms->second);
assert(conformsSource != source || !requirementSignatureProto);
Type localRootType = conformsSource->getRootPotentialArchetype()
->getDependentType(inProtoSig->getGenericParams(),
/*allowUnresolved*/true);
Expand All @@ -891,15 +901,17 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
*inProtocol->getModuleContext());

// Build the path according to the requirement signature.
buildPath(reqSig, conformsSource, conformingProto, localRootType);
buildPath(inProtocol->getRequirementSignature(), conformsSource,
conformingProto, localRootType, inProtocol);

// We're done.
return;
}

// If we still have a parent, keep going.
if (source->parent) {
buildPath(sig, source->parent, conformingProto, rootType);
buildPath(reqs, source->parent, conformingProto, rootType,
requirementSignatureProto);
return;
}

Expand All @@ -914,7 +926,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
rootType->isEqual(conformingProto->getSelfInterfaceType()))
return;

assert(hasConformanceInSignature(sig, rootType, conformingProto) &&
assert(hasConformanceInSignature(reqs, rootType, conformingProto) &&
"missing explicit conformance in signature");

// Add the root of the path, which starts at this explicit requirement.
Expand All @@ -929,7 +941,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
/*allowUnresolved=*/false);

// Build the path.
buildPath(this, source, protocol, rootType);
buildPath(getRequirements(), source, protocol, rootType, nullptr);

// Return the path; we're done!
return path;
Expand Down
4 changes: 1 addition & 3 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2628,12 +2628,10 @@ ConstraintResult GenericSignatureBuilder::addConformanceRequirement(
// cannot compute the requirement signature directly, because that may be
// infinitely recursive: this code is also used to construct it.
if (Proto->isRequirementSignatureComputed()) {
auto reqSig = Proto->getRequirementSignature();

auto innerSource =
FloatingRequirementSource::viaProtocolRequirement(Source, Proto,
/*inferred=*/false);
for (auto req : reqSig->getRequirements()) {
for (const auto &req : Proto->getRequirementSignature()) {
auto reqResult = addRequirement(req, innerSource, nullptr,
&protocolSubMap);
if (isErrorResult(reqResult)) return reqResult;
Expand Down
5 changes: 2 additions & 3 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ void NormalProtocolConformance::setSignatureConformances(

#if !NDEBUG
unsigned idx = 0;
for (auto req : getProtocol()->getRequirementSignature()->getRequirements()) {
for (const auto &req : getProtocol()->getRequirementSignature()) {
if (req.getKind() == RequirementKind::Conformance) {
assert(idx < conformances.size());
assert(conformances[idx].getRequirement() ==
Expand Down Expand Up @@ -596,8 +596,7 @@ NormalProtocolConformance::getAssociatedConformance(Type assocType,
"signature conformances not yet computed");

unsigned conformanceIndex = 0;
for (auto &reqt :
getProtocol()->getRequirementSignature()->getRequirements()) {
for (const auto &reqt : getProtocol()->getRequirementSignature()) {
if (reqt.getKind() == RequirementKind::Conformance) {
// Is this the conformance we're looking for?
if (reqt.getFirstType()->isEqual(assocType) &&
Expand Down
8 changes: 4 additions & 4 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4159,13 +4159,13 @@ namespace {
result->setInherited(Impl.SwiftContext.AllocateCopy(inheritedTypes));
result->setCheckedInheritanceClause();

auto *env = Impl.buildGenericEnvironment(result->getGenericParams(), dc);
result->setGenericEnvironment(env);

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

auto *env = Impl.buildGenericEnvironment(result->getGenericParams(), dc);
result->setGenericEnvironment(env);

result->setMemberLoader(&Impl, 0);

// Add the protocol decl to ExternalDefinitions so that IRGen can emit
Expand Down Expand Up @@ -7376,7 +7376,7 @@ void ClangImporter::Implementation::finishProtocolConformance(

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

Expand Down
Loading