Skip to content

Commit e4173ad

Browse files
committed
RequirementMachine: Fix superclass requirement vs concrete completion heuristic
If we have something like this: protocol P { associatedtype A : P } class C<U> : P { typealias A = C<U> } <T where T : P, T : C<U>> Then we would add a rewrite T.A => T because both the concrete type was equal to the type witness. But that is only valid if the original requirement is a concrete type requirement, not a superclass requirement.
1 parent 463b298 commit e4173ad

File tree

3 files changed

+61
-41
lines changed

3 files changed

+61
-41
lines changed

lib/AST/RequirementMachine/EquivalenceClassMap.cpp

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParents(
632632

633633
concretizeNestedTypesFromConcreteParent(
634634
equivClass->getKey(),
635+
RequirementKind::SameType,
635636
equivClass->ConcreteType->getConcreteType(),
636637
equivClass->ConcreteType->getSubstitutions(),
637638
equivClass->getConformsTo(),
@@ -645,6 +646,7 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParents(
645646

646647
concretizeNestedTypesFromConcreteParent(
647648
equivClass->getKey(),
649+
RequirementKind::Superclass,
648650
equivClass->Superclass->getSuperclass(),
649651
equivClass->Superclass->getSubstitutions(),
650652
equivClass->getConformsTo(),
@@ -686,10 +688,13 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParents(
686688
/// T.[P:B] => U.V
687689
///
688690
void EquivalenceClassMap::concretizeNestedTypesFromConcreteParent(
689-
const MutableTerm &key,
691+
const MutableTerm &key, RequirementKind requirementKind,
690692
CanType concreteType, ArrayRef<Term> substitutions,
691693
ArrayRef<const ProtocolDecl *> conformsTo,
692694
SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules) const {
695+
assert(requirementKind == RequirementKind::SameType ||
696+
requirementKind == RequirementKind::Superclass);
697+
693698
for (auto *proto : conformsTo) {
694699
// FIXME: Either remove the ModuleDecl entirely from conformance lookup,
695700
// or pass the correct one down in here.
@@ -745,9 +750,24 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParent(
745750
subjectType.add(Atom::forAssociatedType(proto, assocType->getName(),
746751
Context));
747752

748-
MutableTerm constraintType = computeConstraintTermForTypeWitness(
749-
key, concreteType, typeWitness, subjectType,
750-
substitutions);
753+
MutableTerm constraintType;
754+
755+
if (concreteType == typeWitness &&
756+
requirementKind == RequirementKind::SameType) {
757+
// FIXME: ConcreteTypeInDomainMap should support substitutions so
758+
// that we can remove this.
759+
760+
if (DebugConcretizeNestedTypes) {
761+
llvm::dbgs() << "^^ Type witness is the same as the concrete type\n";
762+
}
763+
764+
// Add a rule T.[P:A] => T.
765+
constraintType = key;
766+
} else {
767+
constraintType = computeConstraintTermForTypeWitness(
768+
key, concreteType, typeWitness, subjectType,
769+
substitutions);
770+
}
751771

752772
inducedRules.emplace_back(subjectType, constraintType);
753773
if (DebugConcretizeNestedTypes) {
@@ -775,25 +795,16 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParent(
775795
///
776796
/// T.[P:A].[concrete: Foo] => T.[P:A]
777797
///
778-
/// However, this also tries to tie off recursion first, using two
779-
/// heuristics:
780-
///
781-
/// 1) If the type witness is the same exact type as the concrete type,
782-
/// we instead produce a rewrite rule:
798+
/// However, this also tries to tie off recursion first using a heuristic.
783799
///
784-
/// T.[P:A] => T
785-
///
786-
/// 2) If the type witness is fully concrete and we've already seen a
787-
/// shorter term V in the same domain with the same concrete type,
788-
/// we produce a rewrite rule:
800+
/// If the type witness is fully concrete and we've already seen some
801+
/// term V in the same domain with the same concrete type, we produce a
802+
/// rewrite rule:
789803
///
790804
/// T.[P:A] => V
791805
MutableTerm EquivalenceClassMap::computeConstraintTermForTypeWitness(
792-
const MutableTerm &key,
793-
CanType concreteType,
794-
CanType typeWitness,
795-
const MutableTerm &subjectType,
796-
ArrayRef<Term> substitutions) const {
806+
const MutableTerm &key, CanType concreteType, CanType typeWitness,
807+
const MutableTerm &subjectType, ArrayRef<Term> substitutions) const {
797808
if (!typeWitness->hasTypeParameter()) {
798809
// Check if we have a shorter representative we can use.
799810
auto domain = key.getRootProtocols();
@@ -811,20 +822,6 @@ MutableTerm EquivalenceClassMap::computeConstraintTermForTypeWitness(
811822
}
812823
}
813824

814-
if (concreteType == typeWitness) {
815-
// FIXME: This is wrong for the superclass case!
816-
//
817-
// FIXME: ConcreteTypeInDomainMap should support substitutions so
818-
// that we can remove this.
819-
820-
if (DebugConcretizeNestedTypes) {
821-
llvm::dbgs() << "^^ Type witness is the same as the concrete type\n";
822-
}
823-
824-
// Add a rule T.[P:A] => T.
825-
return key;
826-
}
827-
828825
if (typeWitness->isTypeParameter()) {
829826
// The type witness is a type parameter of the form τ_0_n.X.Y...Z,
830827
// where 'n' is an index into the substitution array.

lib/AST/RequirementMachine/EquivalenceClassMap.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace llvm {
3636
namespace swift {
3737

3838
class ProtocolDecl;
39+
enum class RequirementKind : unsigned;
3940

4041
namespace rewriting {
4142

@@ -149,18 +150,14 @@ class EquivalenceClassMap {
149150

150151
private:
151152
void concretizeNestedTypesFromConcreteParent(
152-
const MutableTerm &key,
153-
CanType concreteType,
154-
ArrayRef<Term> substitutions,
153+
const MutableTerm &key, RequirementKind requirementKind,
154+
CanType concreteType, ArrayRef<Term> substitutions,
155155
ArrayRef<const ProtocolDecl *> conformsTo,
156156
SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules) const;
157157

158158
MutableTerm computeConstraintTermForTypeWitness(
159-
const MutableTerm &key,
160-
CanType concreteType,
161-
CanType typeWitness,
162-
const MutableTerm &subjectType,
163-
ArrayRef<Term> substitutions) const;
159+
const MutableTerm &key, CanType concreteType, CanType typeWitness,
160+
const MutableTerm &subjectType, ArrayRef<Term> substitutions) const;
164161
};
165162

166163
} // end namespace rewriting
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-typecheck-verify-swift -enable-requirement-machine
2+
3+
protocol P {
4+
associatedtype A : P
5+
}
6+
7+
class C<U> : P {
8+
typealias A = C<U>
9+
}
10+
11+
protocol P1 {
12+
associatedtype T : P
13+
}
14+
15+
protocol P2 {
16+
associatedtype T where T : C<U>
17+
associatedtype U
18+
}
19+
20+
func eq<T>(_: T, _: T) {}
21+
22+
struct G<T : P1 & P2> {
23+
func foo(_ x: T.T.A, _ y: C<T.U>) {
24+
eq(x, y)
25+
}
26+
}

0 commit comments

Comments
 (0)