Skip to content

Commit 8e94fff

Browse files
committed
RequirementMachine: Fix up concrete conformance handling in minimal conformances algorithm
1 parent 04f9bce commit 8e94fff

File tree

3 files changed

+93
-12
lines changed

3 files changed

+93
-12
lines changed

lib/AST/RequirementMachine/MinimalConformances.cpp

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,22 @@ class MinimalConformances {
192192
MutableTerm term, unsigned ruleID,
193193
SmallVectorImpl<unsigned> &result) const;
194194

195+
enum class ConcreteConformances : uint8_t {
196+
/// Don't consider paths involving concrete conformances at all.
197+
Disallowed,
198+
199+
/// Consider paths involving a concrete conformance only if it appears
200+
/// at the end of the path.
201+
AllowedAtEnd,
202+
203+
/// Consider paths involving concrete conformances anywhere.
204+
AllowedAnywhere
205+
};
206+
195207
bool isValidConformancePath(
196208
llvm::SmallDenseSet<unsigned, 4> &visited,
197-
const llvm::SmallVectorImpl<unsigned> &path, bool allowConcrete) const;
209+
const llvm::SmallVectorImpl<unsigned> &path,
210+
ConcreteConformances allowConcrete) const;
198211

199212
bool isValidRefinementPath(
200213
const llvm::SmallVectorImpl<unsigned> &path) const;
@@ -619,7 +632,8 @@ void MinimalConformances::computeCandidateConformancePaths() {
619632
/// rules.
620633
bool MinimalConformances::isValidConformancePath(
621634
llvm::SmallDenseSet<unsigned, 4> &visited,
622-
const llvm::SmallVectorImpl<unsigned> &path, bool allowConcrete) const {
635+
const llvm::SmallVectorImpl<unsigned> &path,
636+
ConcreteConformances allowConcrete) const {
623637

624638
unsigned lastIdx = path.size() - 1;
625639

@@ -628,13 +642,27 @@ bool MinimalConformances::isValidConformancePath(
628642
if (visited.count(ruleID) > 0)
629643
return false;
630644

645+
const auto &rule = System.getRule(ruleID);
646+
631647
bool isLastElement = (ruleIdx == lastIdx);
648+
bool isConcreteConformance = rule.getLHS().back().getKind()
649+
== Symbol::Kind::ConcreteConformance;
632650

633651
// Concrete conformances cannot appear in the middle of a conformance path.
634-
if (!allowConcrete || !isLastElement) {
635-
if (System.getRule(ruleID).getLHS().back().getKind()
636-
== Symbol::Kind::ConcreteConformance)
652+
if (isConcreteConformance) {
653+
switch (allowConcrete) {
654+
case ConcreteConformances::Disallowed:
637655
return false;
656+
657+
case ConcreteConformances::AllowedAtEnd:
658+
if (!isLastElement)
659+
return false;
660+
661+
break;
662+
663+
case ConcreteConformances::AllowedAnywhere:
664+
break;
665+
}
638666
}
639667

640668
if (RedundantConformances.count(ruleID)) {
@@ -647,10 +675,29 @@ bool MinimalConformances::isValidConformancePath(
647675
if (found == ConformancePaths.end())
648676
return false;
649677

678+
ConcreteConformances allowConcreteRec;
679+
switch (allowConcrete) {
680+
case ConcreteConformances::Disallowed:
681+
allowConcreteRec = ConcreteConformances::Disallowed;
682+
break;
683+
684+
case ConcreteConformances::AllowedAnywhere:
685+
allowConcreteRec = ConcreteConformances::AllowedAnywhere;
686+
break;
687+
688+
case ConcreteConformances::AllowedAtEnd:
689+
if (isLastElement)
690+
allowConcreteRec = ConcreteConformances::AllowedAtEnd;
691+
else
692+
allowConcreteRec = ConcreteConformances::Disallowed;
693+
694+
break;
695+
}
696+
650697
bool foundValidConformancePath = false;
651698
for (const auto &otherPath : found->second) {
652699
if (isValidConformancePath(visited, otherPath,
653-
allowConcrete && isLastElement)) {
700+
allowConcreteRec)) {
654701
foundValidConformancePath = true;
655702
break;
656703
}
@@ -666,11 +713,17 @@ bool MinimalConformances::isValidConformancePath(
666713
};
667714
visited.insert(ruleID);
668715

716+
ConcreteConformances allowConcreteRec;
717+
if (isConcreteConformance)
718+
allowConcreteRec = ConcreteConformances::AllowedAnywhere;
719+
else
720+
allowConcreteRec = ConcreteConformances::AllowedAtEnd;
721+
669722
// If 'req' is based on some other conformance requirement
670723
// `T.[P.]A : Q', we want to make sure that we have a
671724
// non-redundant derivation for 'T : P'.
672725
if (!isValidConformancePath(visited, found->second,
673-
/*allowConcrete=*/false)) {
726+
allowConcreteRec)) {
674727
return false;
675728
}
676729
}
@@ -868,7 +921,7 @@ void MinimalConformances::computeMinimalConformances(bool firstPass) {
868921
visited.insert(ruleID);
869922

870923
if (isValidConformancePath(visited, path,
871-
/*allowConcrete=*/true)) {
924+
ConcreteConformances::AllowedAtEnd)) {
872925
if (Debug.contains(DebugFlags::MinimalConformances)) {
873926
llvm::dbgs() << "Redundant rule in ";
874927
llvm::dbgs() << (firstPass ? "first" : "second");
@@ -902,8 +955,19 @@ void MinimalConformances::verifyMinimalConformances() const {
902955
llvm::SmallVector<unsigned, 1> path;
903956
path.push_back(ruleID);
904957

905-
if (!isValidConformancePath(visited, path,
906-
/*allowConcrete=*/true)) {
958+
ConcreteConformances allowConcrete;
959+
if (rule.isProtocolConformanceRule()) {
960+
// Protocol conformance rules are recoverable if the path
961+
// has a concrete conformance at the end.
962+
allowConcrete = ConcreteConformances::AllowedAtEnd;
963+
} else {
964+
// Concrete conformance rules are recoverable via paths
965+
// containing other concrete conformances anywhere.
966+
assert(rule.isAnyConformanceRule());
967+
allowConcrete = ConcreteConformances::AllowedAnywhere;
968+
}
969+
970+
if (!isValidConformancePath(visited, path, allowConcrete)) {
907971
llvm::errs() << "Redundant conformance is not recoverable:\n";
908972
llvm::errs() << rule << "\n\n";
909973
dumpMinimalConformanceEquations(llvm::errs());
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
2+
3+
struct MyOptionSet : OptionSet {
4+
let rawValue: UInt
5+
}
6+
7+
struct G1<T : OptionSet> where T.RawValue: FixedWidthInteger & UnsignedInteger {}
8+
9+
// CHECK-LABEL: ExtensionDecl line={{[0-9]+}} base=G1
10+
// CHECK-NEXT: Generic signature: <T where T == MyOptionSet>
11+
extension G1 where T == MyOptionSet {}
12+
13+
struct G2<T : SIMD> {}
14+
15+
// CHECK-LABEL: ExtensionDecl line={{[0-9]+}} base=G2
16+
// CHECK-NEXT: Generic signature: <T where T == SIMD2<Double>>
17+
extension G2 where T == SIMD2<Double> {}

test/Generics/conditional_requirement_inference_2.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-typecheck-verify-swift
2-
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
1+
// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=verify
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-inferred-signatures=verify %s 2>&1 | %FileCheck %s
33

44
// A more complicated example.
55
protocol Equatable {}

0 commit comments

Comments
 (0)