Skip to content

Commit 92189c6

Browse files
authored
Merge pull request #17356 from huonw/lazier-conditional-requirements
More errors instead of crashes for conditional conformances that are invalid or involve hard-to-resolve recursion
2 parents ff89fce + a21a779 commit 92189c6

14 files changed

+291
-61
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,9 @@ ERROR(ambiguous_module_type,none,
660660
ERROR(use_nonmatching_operator,none,
661661
"%0 is not a %select{binary|prefix unary|postfix unary}1 operator",
662662
(DeclName, unsigned))
663+
ERROR(unsupported_recursion_in_associated_type_reference,none,
664+
"unsupported recursion for reference to associated type %0 of type %1",
665+
(DeclName, Type))
663666
ERROR(broken_associated_type_witness,none,
664667
"reference to invalid associated type %0 of type %1", (DeclName, Type))
665668

@@ -1806,6 +1809,9 @@ NOTE(redundant_conformance_here,none,
18061809
"inferred from type here}0",
18071810
(unsigned, Type, ProtocolDecl *))
18081811

1812+
ERROR(unsupported_recursive_requirements, none,
1813+
"requirement involves recursion that is not currently supported", ())
1814+
18091815
ERROR(same_type_conflict,none,
18101816
"%select{generic parameter |protocol |}0%1 cannot be equal to both "
18111817
"%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
@@ -305,6 +305,10 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
305305
/// Get the property declaration for a behavior conformance, if this is one.
306306
AbstractStorageDecl *getBehaviorDecl() const;
307307

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

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

385398
/// The lazy member loader provides callbacks for populating imported and
386399
/// deserialized conformances.
@@ -417,7 +430,7 @@ class NormalProtocolConformance : public ProtocolConformance,
417430

418431
void resolveLazyInfo() const;
419432

420-
void differenceAndStoreConditionalRequirements();
433+
void differenceAndStoreConditionalRequirements() const;
421434

422435
public:
423436
/// Get the protocol being conformed to.
@@ -437,11 +450,38 @@ class NormalProtocolConformance : public ProtocolConformance,
437450
}
438451
}
439452

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

447487
/// Retrieve the state of this conformance.
@@ -660,14 +700,16 @@ class SpecializedProtocolConformance : public ProtocolConformance,
660700

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

665705
friend class ASTContext;
666706

667707
SpecializedProtocolConformance(Type conformingType,
668708
ProtocolConformance *genericConformance,
669709
SubstitutionMap substitutions);
670710

711+
void computeConditionalRequirements() const;
712+
671713
public:
672714
/// Get the generic conformance from which this conformance was derived,
673715
/// if there is one.
@@ -680,9 +722,29 @@ class SpecializedProtocolConformance : public ProtocolConformance,
680722
SubstitutionMap getSubstitutionMap() const { return GenericSubstitutions; }
681723

682724
/// Get any requirements that must be satisfied for this conformance to apply.
683-
ArrayRef<Requirement> getConditionalRequirements() const {
725+
///
726+
/// If \c computeIfPossible is false, this will not do the lazy computation of
727+
/// the conditional requirements and will just query the current state. This
728+
/// should almost certainly only be used for debugging purposes, prefer \c
729+
/// getConditionalRequirementsIfAvailable (these are separate because
730+
/// CONFORMANCE_SUBCLASS_DISPATCH does some type checks and a defaulted
731+
/// parameter gets in the way of that).
732+
Optional<ArrayRef<Requirement>>
733+
getConditionalRequirementsIfAvailableOrCached(bool computeIfPossible) const {
734+
if (computeIfPossible)
735+
computeConditionalRequirements();
684736
return ConditionalRequirements;
685737
}
738+
Optional<ArrayRef<Requirement>>
739+
getConditionalRequirementsIfAvailable() const {
740+
return getConditionalRequirementsIfAvailableOrCached(
741+
/*computeIfPossible=*/true);
742+
}
743+
744+
/// Get any requirements that must be satisfied for this conformance to apply.
745+
ArrayRef<Requirement> getConditionalRequirements() const {
746+
return *getConditionalRequirementsIfAvailable();
747+
}
686748

687749
/// Get the protocol being conformed to.
688750
ProtocolDecl *getProtocol() const {
@@ -790,6 +852,12 @@ class InheritedProtocolConformance : public ProtocolConformance,
790852
return InheritedConformance->getProtocol();
791853
}
792854

855+
/// Get any requirements that must be satisfied for this conformance to apply.
856+
Optional<ArrayRef<Requirement>>
857+
getConditionalRequirementsIfAvailable() const {
858+
return InheritedConformance->getConditionalRequirementsIfAvailable();
859+
}
860+
793861
/// Get any requirements that must be satisfied for this conformance to apply.
794862
ArrayRef<Requirement> getConditionalRequirements() const {
795863
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
@@ -2926,10 +2926,17 @@ static void dumpProtocolConformanceRec(
29262926
}
29272927
}
29282928

2929-
for (auto requirement : normal->getConditionalRequirements()) {
2929+
if (auto condReqs = normal->getConditionalRequirementsIfAvailableOrCached(
2930+
/*computeIfPossible=*/false)) {
2931+
for (auto requirement : *condReqs) {
2932+
out << '\n';
2933+
out.indent(indent + 2);
2934+
requirement.dump(out);
2935+
}
2936+
} else {
29302937
out << '\n';
29312938
out.indent(indent + 2);
2932-
requirement.dump(out);
2939+
out << "(conditional requirements unable to be computed)";
29332940
}
29342941
break;
29352942
}
@@ -2957,10 +2964,16 @@ static void dumpProtocolConformanceRec(
29572964
SubstitutionMap::DumpStyle::Full, indent + 2,
29582965
visited);
29592966
out << '\n';
2960-
for (auto subReq : conf->getConditionalRequirements()) {
2967+
if (auto condReqs = conf->getConditionalRequirementsIfAvailableOrCached(
2968+
/*computeIfPossible=*/false)) {
2969+
for (auto subReq : *condReqs) {
2970+
out.indent(indent + 2);
2971+
subReq.dump(out);
2972+
out << '\n';
2973+
}
2974+
} else {
29612975
out.indent(indent + 2);
2962-
subReq.dump(out);
2963-
out << '\n';
2976+
out << "(conditional requirements unable to be computed)\n";
29642977
}
29652978
dumpProtocolConformanceRec(conf->getGenericConformance(), out, indent + 2,
29662979
visited);

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,20 +2604,28 @@ ConstraintResult GenericSignatureBuilder::handleUnresolvedRequirement(
26042604
}
26052605
}
26062606

2607-
// Function for feeding through any other requirements that the conformance
2608-
// requires to be satisfied. These are things we're inferring.
2609-
static void addConditionalRequirements(GenericSignatureBuilder &builder,
2610-
ProtocolConformanceRef conformance,
2611-
ModuleDecl *inferForModule) {
2607+
bool GenericSignatureBuilder::addConditionalRequirements(
2608+
ProtocolConformanceRef conformance, ModuleDecl *inferForModule,
2609+
SourceLoc loc) {
26122610
// Abstract conformances don't have associated decl-contexts/modules, but also
26132611
// don't have conditional requirements.
26142612
if (conformance.isConcrete()) {
2615-
auto source = FloatingRequirementSource::forInferred(nullptr);
2616-
for (auto requirement : conformance.getConditionalRequirements()) {
2617-
builder.addRequirement(requirement, source, inferForModule);
2618-
++NumConditionalRequirementsAdded;
2613+
if (auto condReqs = conformance.getConditionalRequirementsIfAvailable()) {
2614+
auto source = FloatingRequirementSource::forInferred(nullptr);
2615+
for (auto requirement : *condReqs) {
2616+
addRequirement(requirement, source, inferForModule);
2617+
++NumConditionalRequirementsAdded;
2618+
}
2619+
} else {
2620+
if (loc.isValid())
2621+
Diags.diagnose(loc, diag::unsupported_recursive_requirements);
2622+
2623+
Impl->HadAnyError = true;
2624+
return true;
26192625
}
26202626
}
2627+
2628+
return false;
26212629
}
26222630

26232631
const RequirementSource *
@@ -2656,7 +2664,10 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type,
26562664

26572665
concreteSource = concreteSource->viaConcrete(*this, *conformance);
26582666
equivClass->recordConformanceConstraint(*this, type, proto, concreteSource);
2659-
addConditionalRequirements(*this, *conformance, /*inferForModule=*/nullptr);
2667+
if (addConditionalRequirements(*conformance, /*inferForModule=*/nullptr,
2668+
concreteSource->getLoc()))
2669+
return nullptr;
2670+
26602671
return concreteSource;
26612672
}
26622673
const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
@@ -2685,7 +2696,10 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
26852696
superclassSource =
26862697
superclassSource->viaSuperclass(*this, *conformance);
26872698
equivClass->recordConformanceConstraint(*this, type, proto, superclassSource);
2688-
addConditionalRequirements(*this, *conformance, /*inferForModule=*/nullptr);
2699+
if (addConditionalRequirements(*conformance, /*inferForModule=*/nullptr,
2700+
superclassSource->getLoc()))
2701+
return nullptr;
2702+
26892703
return superclassSource;
26902704
}
26912705

@@ -4741,7 +4755,9 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
47414755

47424756
// FIXME: diagnose if there's no conformance.
47434757
if (conformance) {
4744-
addConditionalRequirements(*this, *conformance, inferForModule);
4758+
if (addConditionalRequirements(*conformance, inferForModule,
4759+
source.getLoc()))
4760+
return ConstraintResult::Conflicting;
47454761
}
47464762
}
47474763
}

0 commit comments

Comments
 (0)