Skip to content

Commit ee5f2c3

Browse files
committed
Redo getConditionalRequirements as a Request
1 parent 5709721 commit ee5f2c3

File tree

8 files changed

+72
-91
lines changed

8 files changed

+72
-91
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -443,19 +443,6 @@ class NormalProtocolConformance : public RootProtocolConformance,
443443
/// requirement signature of the protocol.
444444
ArrayRef<ProtocolConformanceRef> SignatureConformances;
445445

446-
/// Any additional requirements that are required for this conformance to
447-
/// apply, e.g. 'Something: Baz' in 'extension Foo: Bar where Something: Baz'.
448-
mutable ArrayRef<Requirement> ConditionalRequirements;
449-
enum class ConditionalRequirementsState {
450-
Uncomputed,
451-
Computing,
452-
Complete,
453-
};
454-
/// The state of the ConditionalRequirements field: whether it has been
455-
/// computed or not.
456-
mutable ConditionalRequirementsState CRState =
457-
ConditionalRequirementsState::Uncomputed;
458-
459446
/// The lazy member loader provides callbacks for populating imported and
460447
/// deserialized conformances.
461448
///
@@ -467,8 +454,6 @@ class NormalProtocolConformance : public RootProtocolConformance,
467454

468455
void resolveLazyInfo() const;
469456

470-
void differenceAndStoreConditionalRequirements() const;
471-
472457
public:
473458
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
474459
SourceLoc loc, DeclContext *dc,
@@ -493,39 +478,12 @@ class NormalProtocolConformance : public RootProtocolConformance,
493478
return ContextAndInvalid.getPointer();
494479
}
495480

496-
/// Get any additional requirements that are required for this conformance to
497-
/// be satisfied if they can be computed.
498-
///
499-
/// If \c computeIfPossible is false, this will not do the lazy computation of
500-
/// the conditional requirements and will just query the current state. This
501-
/// should almost certainly only be used for debugging purposes, prefer \c
502-
/// getConditionalRequirementsIfAvailable (these are separate because
503-
/// CONFORMANCE_SUBCLASS_DISPATCH does some type checks and a defaulted
504-
/// parameter gets in the way of that).
505-
Optional<ArrayRef<Requirement>>
506-
getConditionalRequirementsIfAvailableOrCached(bool computeIfPossible) const {
507-
if (computeIfPossible)
508-
differenceAndStoreConditionalRequirements();
509-
510-
if (CRState == ConditionalRequirementsState::Complete)
511-
return ConditionalRequirements;
512-
513-
return None;
514-
}
515-
/// Get any additional requirements that are required for this conformance to
516-
/// be satisfied if they can be computed.
517-
Optional<ArrayRef<Requirement>>
518-
getConditionalRequirementsIfAvailable() const {
519-
return getConditionalRequirementsIfAvailableOrCached(
520-
/*computeIfPossible=*/true);
521-
}
522-
523481
/// Get any additional requirements that are required for this conformance to
524482
/// be satisfied, e.g. for Array<T>: Equatable, T: Equatable also needs
525483
/// to be satisfied.
526-
ArrayRef<Requirement> getConditionalRequirements() const {
527-
return *getConditionalRequirementsIfAvailable();
528-
}
484+
ArrayRef<Requirement> getConditionalRequirements() const;
485+
486+
Optional<ArrayRef<Requirement>> getConditionalRequirementsIfAvailable() const;
529487

530488
/// Retrieve the state of this conformance.
531489
ProtocolConformanceState getState() const {

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2917,6 +2917,25 @@ class GetImplicitConcurrentValueRequest :
29172917
bool isCached() const { return true; }
29182918
};
29192919

2920+
class ConditionalRequirementsRequest
2921+
: public SimpleRequest<ConditionalRequirementsRequest,
2922+
llvm::ArrayRef<Requirement>(
2923+
NormalProtocolConformance *),
2924+
RequestFlags::Cached> {
2925+
public:
2926+
using SimpleRequest::SimpleRequest;
2927+
2928+
private:
2929+
friend SimpleRequest;
2930+
2931+
// Evaluation.
2932+
llvm::ArrayRef<Requirement> evaluate(Evaluator &evaluator,
2933+
NormalProtocolConformance *decl) const;
2934+
2935+
public:
2936+
bool isCached() const { return true; }
2937+
};
2938+
29202939
void simple_display(llvm::raw_ostream &out, Type value);
29212940
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);
29222941
void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ SWIFT_REQUEST(TypeChecker, CodeCompletionFileRequest,
4343
SWIFT_REQUEST(TypeChecker, CompareDeclSpecializationRequest,
4444
bool (DeclContext *, ValueDecl *, ValueDecl *, bool), Cached,
4545
NoLocationInfo)
46+
SWIFT_REQUEST(TypeChecker, ConditionalRequirementsRequest,
47+
ArrayRef<Requirement> (NormalProtocolConformance *),
48+
Cached, NoLocationInfo)
4649
SWIFT_REQUEST(TypeChecker, IsDeclRefinementOfRequest,
4750
bool (ValueDecl *, ValueDecl *),
4851
Cached, NoLocationInfo)

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,8 +3279,7 @@ static void dumpProtocolConformanceRec(
32793279
}
32803280
}
32813281

3282-
if (auto condReqs = normal->getConditionalRequirementsIfAvailableOrCached(
3283-
/*computeIfPossible=*/false)) {
3282+
if (auto condReqs = normal->getConditionalRequirementsIfAvailable()) {
32843283
for (auto requirement : *condReqs) {
32853284
out << '\n';
32863285
out.indent(indent + 2);

lib/AST/ProtocolConformance.cpp

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -482,56 +482,47 @@ ProtocolConformanceRef::getConditionalRequirements() const {
482482
return {};
483483
}
484484

485-
void NormalProtocolConformance::differenceAndStoreConditionalRequirements()
486-
const {
487-
switch (CRState) {
488-
case ConditionalRequirementsState::Complete:
489-
// already done!
490-
return;
491-
case ConditionalRequirementsState::Computing:
492-
// recursive
493-
return;
494-
case ConditionalRequirementsState::Uncomputed:
495-
// try to compute it!
496-
break;
497-
};
485+
Optional<ArrayRef<Requirement>>
486+
NormalProtocolConformance::getConditionalRequirementsIfAvailable() const {
487+
const auto &eval = getDeclContext()->getASTContext().evaluator;
488+
if (eval.hasActiveRequest(ConditionalRequirementsRequest{
489+
const_cast<NormalProtocolConformance *>(this)})) {
490+
return None;
491+
}
492+
return getConditionalRequirements();
493+
}
498494

499-
CRState = ConditionalRequirementsState::Computing;
500-
auto success = [this](ArrayRef<Requirement> reqs = {}) {
501-
ConditionalRequirements = reqs;
502-
assert(CRState == ConditionalRequirementsState::Computing);
503-
CRState = ConditionalRequirementsState::Complete;
504-
};
505-
auto failure = [this] {
506-
assert(CRState == ConditionalRequirementsState::Computing);
507-
CRState = ConditionalRequirementsState::Uncomputed;
508-
};
495+
llvm::ArrayRef<Requirement>
496+
NormalProtocolConformance::getConditionalRequirements() const {
497+
const auto ext = dyn_cast<ExtensionDecl>(getDeclContext());
498+
if (ext && ext->isComputingGenericSignature()) {
499+
return {};
500+
}
501+
return evaluateOrDefault(getProtocol()->getASTContext().evaluator,
502+
ConditionalRequirementsRequest{
503+
const_cast<NormalProtocolConformance *>(this)},
504+
{});
505+
}
509506

507+
llvm::ArrayRef<Requirement>
508+
ConditionalRequirementsRequest::evaluate(Evaluator &evaluator,
509+
NormalProtocolConformance *NPC) const {
510510
// A non-extension conformance won't have conditional requirements.
511-
const auto ext = dyn_cast<ExtensionDecl>(getDeclContext());
511+
const auto ext = dyn_cast<ExtensionDecl>(NPC->getDeclContext());
512512
if (!ext) {
513-
return success();
513+
return {};
514514
}
515515

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

522522
// A non-generic type won't have conditional requirements.
523523
const auto typeSig = ext->getExtendedNominal()->getGenericSignature();
524524
if (!typeSig) {
525-
return success();
526-
}
527-
528-
// Recursively validating the signature comes up frequently as expanding
529-
// conformance requirements might re-enter this method. We can at least catch
530-
// this and come back to these requirements later.
531-
//
532-
// FIXME: In the long run, break this cycle in a more principled way.
533-
if (ext->isComputingGenericSignature()) {
534-
return failure();
525+
return {};
535526
}
536527

537528
const auto extensionSig = ext->getGenericSignature();
@@ -548,9 +539,9 @@ void NormalProtocolConformance::differenceAndStoreConditionalRequirements()
548539
// type, these are the ones that make the conformance conditional.
549540
const auto unsatReqs = extensionSig->requirementsNotSatisfiedBy(typeSig);
550541
if (unsatReqs.empty())
551-
return success();
542+
return {};
552543

553-
return success(getProtocol()->getASTContext().AllocateCopy(unsatReqs));
544+
return NPC->getProtocol()->getASTContext().AllocateCopy(unsatReqs);
554545
}
555546

556547
void NormalProtocolConformance::setSignatureConformances(
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
// RUN: %target-typecheck-verify-swift
22

33
protocol P {
4-
associatedtype A: P
4+
associatedtype A: P // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
55
}
66

77
struct Type<Param> {}
8-
extension Type: P where Param: P, Param.A == Type<Param> { // expected-error {{requirement involves recursion that is not currently supported}}
9-
typealias A = Param
8+
extension Type: P where Param: P, Param.A == Type<Param> {
9+
// expected-error@-1 {{circular reference}}
10+
// expected-note@-2 {{through reference here}}
11+
// expected-error@-3 {{circular reference}}
12+
// expected-note@-4 {{through reference here}}
13+
// expected-error@-5 {{circular reference}}
14+
// expected-note@-6 {{through reference here}}
15+
// expected-error@-7 {{type 'Type<Param>' does not conform to protocol 'P'}}
16+
typealias A = Param
17+
// expected-note@-1 {{through reference here}}
18+
// expected-note@-2 {{through reference here}}
1019
}

validation-test/compiler_crashers_2_fixed/0162-sr8019.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
protocol P {}
44
struct A<C> {}
5-
extension A: P where A: P {} // expected-error {{requirement involves recursion that is not currently supported}}
5+
extension A: P where A: P {} // expected-error {{type 'A<C>' in conformance requirement does not refer to a generic parameter or associated type}}

validation-test/compiler_crashers_2_fixed/0163-sr8033.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ struct Foo<T> {}
55
protocol P1 {
66
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
77
}
8-
extension Foo: P1 where A : P1 {} // expected-error {{unsupported recursion for reference to associated type 'A' of type 'Foo<T>'}}
9-
// expected-error@-1 {{type 'Foo<T>' does not conform to protocol 'P1'}}
8+
extension Foo: P1 where A : P1 {} // expected-error {{circular reference}}
9+
// expected-note@-1 {{while resolving type 'A'}}
10+
// expected-note@-2 {{through reference here}}
11+
// expected-error@-3 {{type 'Foo<T>' does not conform to protocol 'P1'}}

0 commit comments

Comments
 (0)