Sema: introduce ProtocolInversesRequest

We need special handling for protocols whose requirement
signature exists but is in a serialized state, as we
cannot run the StructuralRequirementsRequest on such
a protocol as there's no work to be done, effectively.
This commit is contained in:
Kavon Farvardin
2025-12-02 15:34:33 -08:00
parent 6f95203dfd
commit c9d2f522c0
7 changed files with 68 additions and 40 deletions

View File

@@ -636,6 +636,16 @@ See also \SecRef{src:declarations}.
\item \texttt{getProtocolDependencies()} evaluates the \texttt{ProtocolDependenciesRequest}.
\end{itemize}
\IndexSource{protocol inverses request}
\apiref{ProtocolInversesRequest}{class}
A request evaluator request which enumerates the inverse requirements written on the given protocol and its associated types.
\apiref{ProtocolDecl}{class}
See also \SecRef{src:declarations}.
\begin{itemize}
\item \texttt{getInverseRequirements()} evaluates the \texttt{ProtocolInversesRequest}.
\end{itemize}
\IndexSource{requirement machine}
\apiref{rewriting::RequirementMachine}{class}
A list of rewrite rules and a property map. See also \SecRef{src:symbols terms rules} and \SecRef{property map sourceref}. Entry points for initializing a requirement machine, called by the rewrite context and various requests:

View File

@@ -5578,6 +5578,7 @@ class ProtocolDecl final : public NominalTypeDecl {
friend class StructuralRequirementsRequest;
friend class TypeAliasRequirementsRequest;
friend class ProtocolDependenciesRequest;
friend class ProtocolInversesRequest;
friend class RequirementSignatureRequest;
friend class ProtocolRequiresClassRequest;
friend class ExistentialConformsToSelfRequest;

View File

@@ -619,6 +619,25 @@ public:
bool isCached() const { return true; }
};
class ProtocolInversesRequest :
public SimpleRequest<ProtocolInversesRequest,
ArrayRef<InverseRequirement>(ProtocolDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
ArrayRef<InverseRequirement>
evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;
public:
// Caching.
bool isCached() const { return true; }
};
/// Compute the requirements that describe a protocol.
class RequirementSignatureRequest :
public SimpleRequest<RequirementSignatureRequest,

View File

@@ -309,6 +309,9 @@ SWIFT_REQUEST(TypeChecker, TypeAliasRequirementsRequest,
SWIFT_REQUEST(TypeChecker, ProtocolDependenciesRequest,
ArrayRef<ProtocolDecl *>(ProtocolDecl *), Cached,
HasNearestLocation)
SWIFT_REQUEST(TypeChecker, ProtocolInversesRequest,
ArrayRef<InverseRequirement>(ProtocolDecl *), Cached,
HasNearestLocation)
SWIFT_REQUEST(TypeChecker, RequirementSignatureRequest,
RequirementSignature(ProtocolDecl *), SeparatelyCached,
NoLocationInfo)

View File

@@ -7478,8 +7478,8 @@ ProtocolDecl::getStructuralRequirements() const {
ArrayRef<InverseRequirement>
ProtocolDecl::getInverseRequirements() const {
return evaluateOrDefault(
getASTContext().evaluator,
StructuralRequirementsRequest{const_cast<ProtocolDecl *>(this)}, {}).second;
getASTContext().evaluator,
ProtocolInversesRequest{const_cast<ProtocolDecl *>(this)}, {});
}
ArrayRef<Requirement>

View File

@@ -1329,3 +1329,26 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator,
return ctx.AllocateCopy(result);
}
ArrayRef<InverseRequirement>
ProtocolInversesRequest::evaluate(Evaluator &evaluator,
ProtocolDecl *proto) const {
auto &ctx = proto->getASTContext();
// If we have a serialized requirement signature, deserialize it and
// query it for the inverses.
if (proto->hasLazyRequirementSignature()) {
SmallVector<Requirement, 2> _ignored;
SmallVector<InverseRequirement, 2> result;
auto reqSig = proto->getRequirementSignature();
reqSig.getRequirementsWithInverses(proto, _ignored, result);
return ctx.AllocateCopy(result);
}
// Otherwise, we must avoid building a RequirementSignature, as this query
// needs to be safe to ask while building the protocol's RequirementSignature.
//
// So, use a StructuralRequirementsRequest to get the inverses.
return evaluateOrDefault(ctx.evaluator,
StructuralRequirementsRequest{proto}, {}).second;
}

View File

@@ -942,45 +942,17 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
auto *extendedNominal = ext->getExtendedNominal();
// Avoid building a generic signature if we have an unconstrained protocol
// extension of a protocol that does not suppress conformance to ~Copyable
// or ~Escapable. This avoids a request cycle when referencing a protocol
// extension type alias via an unqualified name from a `where` clause on
// the protocol.
// Optimization: avoid building a generic signature if we have an
// unconstrained protocol extension, as they have the same signature as the
// protocol itself.
//
// Protocols who suppress conformance to ~Copyable or ~Escapable either on
// Self or its associated types will infer default requirements in
// ordinary extensions of that protocol, so the signature can differ there.
if (auto *proto = dyn_cast<ProtocolDecl>(extendedNominal)) {
if (extraReqs.empty() &&
!ext->getTrailingWhereClause()) {
// Check for inverse requirements on Self or any associated types.
auto reqSig = proto->getRequirementSignature();
SmallVector<Requirement, 2> _ignored;
SmallVector<InverseRequirement, 2> inverses;
reqSig.getRequirementsWithInverses(proto, _ignored, inverses);
if (inverses.empty())
return extendedNominal->getGenericSignatureOfContext();
if (ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes) &&
!ctx.LangOpts.hasFeature(
Feature::SuppressedAssociatedTypesWithDefaults)) {
// NOTE: remove this once SuppressedAssociatedTypes is deprecated.
//
// We don't infer defaults for the associated types in the legacy
// version of the feature, only for Self. If there's no inverse on
// Self, then we need to reuse the generic signature of the context,
// or else we'll crash with some generic signature verifier error.
//
// We can assume the subject is Self if it's a GenericTypeParamType.
bool hasInverseOnSelf = false;
for (auto const &ir : inverses) {
if (ir.subject->getAs<GenericTypeParamType>()) {
hasInverseOnSelf = true;
break;
}
}
if (!hasInverseOnSelf) {
return extendedNominal->getGenericSignatureOfContext();
}
}
if (extraReqs.empty() && !ext->getTrailingWhereClause() &&
proto->getInverseRequirements().empty()) {
return extendedNominal->getGenericSignatureOfContext();
}
}