Skip to content

Commit 0ca361f

Browse files
authored
Merge pull request #17385 from huonw/lazier-conditional-requirements-4.2
[4.2] More errors instead of crashes for conditional conformances that are invalid or involve hard-to-resolve recursion
2 parents 2d41d4a + 8bf2823 commit 0ca361f

14 files changed

+286
-52
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,9 @@ ERROR(ambiguous_module_type,none,
647647
ERROR(use_nonmatching_operator,none,
648648
"%0 is not a %select{binary|prefix unary|postfix unary}1 operator",
649649
(DeclName, unsigned))
650+
ERROR(unsupported_recursion_in_associated_type_reference,none,
651+
"unsupported recursion for reference to associated type %0 of type %1",
652+
(DeclName, Type))
650653
ERROR(broken_associated_type_witness,none,
651654
"reference to invalid associated type %0 of type %1", (DeclName, Type))
652655

@@ -1800,6 +1803,9 @@ NOTE(redundant_conformance_here,none,
18001803
"inferred from type here}0",
18011804
(unsigned, Type, ProtocolDecl *))
18021805

1806+
ERROR(unsupported_recursive_requirements, none,
1807+
"requirement involves recursion that is not currently supported", ())
1808+
18031809
ERROR(same_type_conflict,none,
18041810
"%select{generic parameter |protocol |}0%1 cannot be equal to both "
18051811
"%2 and %3", (unsigned, Type, Type, Type))

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ class GenericSignatureBuilder {
337337
EquivalenceClass *unresolvedEquivClass,
338338
UnresolvedHandlingKind unresolvedHandling);
339339

340+
/// Add any conditional requirements from the given conformance.
341+
///
342+
/// \returns \c true if an error occurred, \c false if not.
343+
bool addConditionalRequirements(ProtocolConformanceRef conformance,
344+
ModuleDecl *inferForModule, SourceLoc loc);
345+
340346
/// Resolve the conformance of the given type to the given protocol when the
341347
/// potential archetype is known to be equivalent to a concrete type.
342348
///

include/swift/AST/ProtocolConformance.h

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
306306
/// Get the property declaration for a behavior conformance, if this is one.
307307
AbstractStorageDecl *getBehaviorDecl() const;
308308

309+
/// Get any additional requirements that are required for this conformance to
310+
/// be satisfied, if it is possible for them to be computed.
311+
Optional<ArrayRef<Requirement>> getConditionalRequirementsIfAvailable() const;
312+
309313
/// Get any additional requirements that are required for this conformance to
310314
/// be satisfied.
311315
ArrayRef<Requirement> getConditionalRequirements() const;
@@ -381,7 +385,16 @@ class NormalProtocolConformance : public ProtocolConformance,
381385

382386
/// Any additional requirements that are required for this conformance to
383387
/// apply, e.g. 'Something: Baz' in 'extension Foo: Bar where Something: Baz'.
384-
ArrayRef<Requirement> ConditionalRequirements;
388+
mutable ArrayRef<Requirement> ConditionalRequirements;
389+
enum class ConditionalRequirementsState {
390+
Uncomputed,
391+
Computing,
392+
Complete,
393+
};
394+
/// The state of the ConditionalRequirements field: whether it has been
395+
/// computed or not.
396+
mutable ConditionalRequirementsState CRState =
397+
ConditionalRequirementsState::Uncomputed;
385398

386399
/// The lazy member loader provides callbacks for populating imported and
387400
/// deserialized conformances.
@@ -418,7 +431,7 @@ class NormalProtocolConformance : public ProtocolConformance,
418431

419432
void resolveLazyInfo() const;
420433

421-
void differenceAndStoreConditionalRequirements();
434+
void differenceAndStoreConditionalRequirements() const;
422435

423436
public:
424437
/// Get the protocol being conformed to.
@@ -438,11 +451,38 @@ class NormalProtocolConformance : public ProtocolConformance,
438451
}
439452
}
440453

454+
/// Get any additional requirements that are required for this conformance to
455+
/// be satisfied if they can be computed.
456+
///
457+
/// If \c computeIfPossible is false, this will not do the lazy computation of
458+
/// the conditional requirements and will just query the current state. This
459+
/// should almost certainly only be used for debugging purposes, prefer \c
460+
/// getConditionalRequirementsIfAvailable (these are separate because
461+
/// CONFORMANCE_SUBCLASS_DISPATCH does some type checks and a defaulted
462+
/// parameter gets in the way of that).
463+
Optional<ArrayRef<Requirement>>
464+
getConditionalRequirementsIfAvailableOrCached(bool computeIfPossible) const {
465+
if (computeIfPossible)
466+
differenceAndStoreConditionalRequirements();
467+
468+
if (CRState == ConditionalRequirementsState::Complete)
469+
return ConditionalRequirements;
470+
471+
return None;
472+
}
473+
/// Get any additional requirements that are required for this conformance to
474+
/// be satisfied if they can be computed.
475+
Optional<ArrayRef<Requirement>>
476+
getConditionalRequirementsIfAvailable() const {
477+
return getConditionalRequirementsIfAvailableOrCached(
478+
/*computeIfPossible=*/true);
479+
}
480+
441481
/// Get any additional requirements that are required for this conformance to
442482
/// be satisfied, e.g. for Array<T>: Equatable, T: Equatable also needs
443483
/// to be satisfied.
444484
ArrayRef<Requirement> getConditionalRequirements() const {
445-
return ConditionalRequirements;
485+
return *getConditionalRequirementsIfAvailable();
446486
}
447487

448488
/// Retrieve the state of this conformance.
@@ -661,14 +701,16 @@ class SpecializedProtocolConformance : public ProtocolConformance,
661701

662702
/// Any conditional requirements, in substituted form. (E.g. given Foo<T>: Bar
663703
/// where T: Bar, Foo<Baz<U>> will include Baz<U>: Bar.)
664-
ArrayRef<Requirement> ConditionalRequirements;
704+
mutable Optional<ArrayRef<Requirement>> ConditionalRequirements;
665705

666706
friend class ASTContext;
667707

668708
SpecializedProtocolConformance(Type conformingType,
669709
ProtocolConformance *genericConformance,
670710
SubstitutionList substitutions);
671711

712+
void computeConditionalRequirements() const;
713+
672714
public:
673715
/// Get the generic conformance from which this conformance was derived,
674716
/// if there is one.
@@ -687,9 +729,29 @@ class SpecializedProtocolConformance : public ProtocolConformance,
687729
SubstitutionMap getSubstitutionMap() const;
688730

689731
/// Get any requirements that must be satisfied for this conformance to apply.
690-
ArrayRef<Requirement> getConditionalRequirements() const {
732+
///
733+
/// If \c computeIfPossible is false, this will not do the lazy computation of
734+
/// the conditional requirements and will just query the current state. This
735+
/// should almost certainly only be used for debugging purposes, prefer \c
736+
/// getConditionalRequirementsIfAvailable (these are separate because
737+
/// CONFORMANCE_SUBCLASS_DISPATCH does some type checks and a defaulted
738+
/// parameter gets in the way of that).
739+
Optional<ArrayRef<Requirement>>
740+
getConditionalRequirementsIfAvailableOrCached(bool computeIfPossible) const {
741+
if (computeIfPossible)
742+
computeConditionalRequirements();
691743
return ConditionalRequirements;
692744
}
745+
Optional<ArrayRef<Requirement>>
746+
getConditionalRequirementsIfAvailable() const {
747+
return getConditionalRequirementsIfAvailableOrCached(
748+
/*computeIfPossible=*/true);
749+
}
750+
751+
/// Get any requirements that must be satisfied for this conformance to apply.
752+
ArrayRef<Requirement> getConditionalRequirements() const {
753+
return *getConditionalRequirementsIfAvailable();
754+
}
693755

694756
/// Get the protocol being conformed to.
695757
ProtocolDecl *getProtocol() const {
@@ -798,6 +860,12 @@ class InheritedProtocolConformance : public ProtocolConformance,
798860
return InheritedConformance->getProtocol();
799861
}
800862

863+
/// Get any requirements that must be satisfied for this conformance to apply.
864+
Optional<ArrayRef<Requirement>>
865+
getConditionalRequirementsIfAvailable() const {
866+
return InheritedConformance->getConditionalRequirementsIfAvailable();
867+
}
868+
801869
/// Get any requirements that must be satisfied for this conformance to apply.
802870
ArrayRef<Requirement> getConditionalRequirements() const {
803871
return InheritedConformance->getConditionalRequirements();

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ class ProtocolConformanceRef {
130130
/// Create a canonical conformance from the current one.
131131
ProtocolConformanceRef getCanonicalConformanceRef() const;
132132

133+
/// Get any additional requirements that are required for this conformance to
134+
/// be satisfied, if they're possible to compute.
135+
Optional<ArrayRef<Requirement>> getConditionalRequirementsIfAvailable() const;
136+
133137
/// Get any additional requirements that are required for this conformance to
134138
/// be satisfied.
135139
ArrayRef<Requirement> getConditionalRequirements() const;

lib/AST/ASTDumper.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,10 +2885,17 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
28852885
}
28862886
}
28872887

2888-
for (auto requirement : normal->getConditionalRequirements()) {
2888+
if (auto condReqs = normal->getConditionalRequirementsIfAvailableOrCached(
2889+
/*computeIfPossible=*/false)) {
2890+
for (auto requirement : *condReqs) {
2891+
out << '\n';
2892+
out.indent(indent + 2);
2893+
requirement.dump(out);
2894+
}
2895+
} else {
28892896
out << '\n';
28902897
out.indent(indent + 2);
2891-
requirement.dump(out);
2898+
out << "(conditional requirements unable to be computed)";
28922899
}
28932900
break;
28942901
}
@@ -2909,10 +2916,16 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
29092916
sub.dump(out, indent + 2);
29102917
out << '\n';
29112918
}
2912-
for (auto subReq : conf->getConditionalRequirements()) {
2919+
if (auto condReqs = conf->getConditionalRequirementsIfAvailableOrCached(
2920+
/*computeIfPossible=*/false)) {
2921+
for (auto subReq : *condReqs) {
2922+
out.indent(indent + 2);
2923+
subReq.dump(out);
2924+
out << '\n';
2925+
}
2926+
} else {
29132927
out.indent(indent + 2);
2914-
subReq.dump(out);
2915-
out << '\n';
2928+
out << "(conditional requirements unable to be computed)\n";
29162929
}
29172930
conf->getGenericConformance()->dump(out, indent + 2);
29182931
break;

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2593,20 +2593,28 @@ ConstraintResult GenericSignatureBuilder::handleUnresolvedRequirement(
25932593
}
25942594
}
25952595

2596-
// Function for feeding through any other requirements that the conformance
2597-
// requires to be satisfied. These are things we're inferring.
2598-
static void addConditionalRequirements(GenericSignatureBuilder &builder,
2599-
ProtocolConformanceRef conformance,
2600-
ModuleDecl *inferForModule) {
2596+
bool GenericSignatureBuilder::addConditionalRequirements(
2597+
ProtocolConformanceRef conformance, ModuleDecl *inferForModule,
2598+
SourceLoc loc) {
26012599
// Abstract conformances don't have associated decl-contexts/modules, but also
26022600
// don't have conditional requirements.
26032601
if (conformance.isConcrete()) {
2604-
auto source = FloatingRequirementSource::forInferred(nullptr);
2605-
for (auto requirement : conformance.getConditionalRequirements()) {
2606-
builder.addRequirement(requirement, source, inferForModule);
2607-
++NumConditionalRequirementsAdded;
2602+
if (auto condReqs = conformance.getConditionalRequirementsIfAvailable()) {
2603+
auto source = FloatingRequirementSource::forInferred(nullptr);
2604+
for (auto requirement : *condReqs) {
2605+
addRequirement(requirement, source, inferForModule);
2606+
++NumConditionalRequirementsAdded;
2607+
}
2608+
} else {
2609+
if (loc.isValid())
2610+
Diags.diagnose(loc, diag::unsupported_recursive_requirements);
2611+
2612+
Impl->HadAnyError = true;
2613+
return true;
26082614
}
26092615
}
2616+
2617+
return false;
26102618
}
26112619

26122620
const RequirementSource *
@@ -2647,7 +2655,10 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type,
26472655

26482656
concreteSource = concreteSource->viaConcrete(*this, *conformance);
26492657
equivClass->recordConformanceConstraint(*this, type, proto, concreteSource);
2650-
addConditionalRequirements(*this, *conformance, /*inferForModule=*/nullptr);
2658+
if (addConditionalRequirements(*conformance, /*inferForModule=*/nullptr,
2659+
concreteSource->getLoc()))
2660+
return nullptr;
2661+
26512662
return concreteSource;
26522663
}
26532664
const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
@@ -2678,7 +2689,10 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
26782689
superclassSource =
26792690
superclassSource->viaSuperclass(*this, *conformance);
26802691
equivClass->recordConformanceConstraint(*this, type, proto, superclassSource);
2681-
addConditionalRequirements(*this, *conformance, /*inferForModule=*/nullptr);
2692+
if (addConditionalRequirements(*conformance, /*inferForModule=*/nullptr,
2693+
superclassSource->getLoc()))
2694+
return nullptr;
2695+
26822696
return superclassSource;
26832697
}
26842698

@@ -4719,7 +4733,9 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
47194733

47204734
// FIXME: diagnose if there's no conformance.
47214735
if (conformance) {
4722-
addConditionalRequirements(*this, *conformance, inferForModule);
4736+
if (addConditionalRequirements(*conformance, inferForModule,
4737+
source.getLoc()))
4738+
return ConstraintResult::Conflicting;
47234739
}
47244740
}
47254741
}

0 commit comments

Comments
 (0)