@@ -565,6 +565,7 @@ EquivalenceClassMap::getOrCreateEquivalenceClass(const MutableTerm &key) {
565
565
566
566
void EquivalenceClassMap::clear () {
567
567
Map.clear ();
568
+ ConcreteTypeInDomainMap.clear ();
568
569
}
569
570
570
571
// / Record a protocol conformance, layout or superclass constraint on the given
@@ -578,6 +579,37 @@ void EquivalenceClassMap::addProperty(
578
579
inducedRules, DebugConcreteUnification);
579
580
}
580
581
582
+ // / For each fully-concrete type, find the shortest term having that concrete type.
583
+ // / This is later used by computeConstraintTermForTypeWitness().
584
+ void EquivalenceClassMap::computeConcreteTypeInDomainMap () {
585
+ for (const auto &equivClass : Map) {
586
+ if (!equivClass->isConcreteType ())
587
+ continue ;
588
+
589
+ auto concreteType = equivClass->ConcreteType ->getConcreteType ();
590
+ if (concreteType->hasTypeParameter ())
591
+ continue ;
592
+
593
+ assert (equivClass->ConcreteType ->getSubstitutions ().empty ());
594
+
595
+ auto domain = equivClass->Key .getRootProtocols ();
596
+ auto concreteTypeKey = std::make_pair (concreteType, domain);
597
+
598
+ auto found = ConcreteTypeInDomainMap.find (concreteTypeKey);
599
+ if (found != ConcreteTypeInDomainMap.end ()) {
600
+ const auto &otherTerm = found->second ;
601
+ assert (equivClass->Key .compare (otherTerm, Protos) > 0 &&
602
+ " Out-of-order keys?" );
603
+ continue ;
604
+ }
605
+
606
+ auto inserted = ConcreteTypeInDomainMap.insert (
607
+ std::make_pair (concreteTypeKey, equivClass->Key ));
608
+ assert (inserted.second );
609
+ (void ) inserted;
610
+ }
611
+ }
612
+
581
613
void EquivalenceClassMap::concretizeNestedTypesFromConcreteParents (
582
614
SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules) const {
583
615
for (const auto &equivClass : Map) {
@@ -709,44 +741,13 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParent(
709
741
<< " of " << concreteType << " is " << typeWitness << " \n " ;
710
742
}
711
743
712
- auto nestedType = Atom::forAssociatedType (proto, assocType->getName (),
713
- Context);
714
-
715
744
MutableTerm subjectType = key;
716
- subjectType.add (nestedType);
745
+ subjectType.add (Atom::forAssociatedType (proto, assocType->getName (),
746
+ Context));
717
747
718
- MutableTerm constraintType;
719
-
720
- if (concreteType == typeWitness) {
721
- if (DebugConcretizeNestedTypes) {
722
- llvm::dbgs () << " ^^ Type witness is the same as the concrete type\n " ;
723
- }
724
-
725
- // Add a rule T.[P:A] => T.
726
- constraintType = key;
727
-
728
- } else if (typeWitness->isTypeParameter ()) {
729
- // The type witness is a type parameter of the form τ_0_n.X.Y...Z,
730
- // where 'n' is an index into the substitution array.
731
- //
732
- // Add a rule T => S.X.Y...Z, where S is the nth substitution term.
733
- constraintType = getRelativeTermForType (typeWitness, substitutions,
734
- Context);
735
-
736
- } else {
737
- // The type witness is a concrete type.
738
- constraintType = subjectType;
739
-
740
- SmallVector<Term, 3 > result;
741
- auto typeWitnessSchema =
742
- remapConcreteSubstitutionSchema (typeWitness, substitutions,
743
- Context, result);
744
-
745
- // Add a rule T.[P:A].[concrete: Foo.A] => T.[P:A].
746
- constraintType.add (
747
- Atom::forConcreteType (
748
- typeWitnessSchema, result, Context));
749
- }
748
+ MutableTerm constraintType = computeConstraintTermForTypeWitness (
749
+ key, concreteType, typeWitness, subjectType,
750
+ substitutions);
750
751
751
752
inducedRules.emplace_back (subjectType, constraintType);
752
753
if (DebugConcretizeNestedTypes) {
@@ -757,6 +758,97 @@ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParent(
757
758
}
758
759
}
759
760
761
+ // / Given the key of an equivalence class known to have \p concreteType,
762
+ // / together with a \p typeWitness from a conformance on that concrete
763
+ // / type, return the right hand side of a rewrite rule to relate
764
+ // / \p subjectType with a term representing the type witness.
765
+ // /
766
+ // / Suppose the key is T and the subject type is T.[P:A].
767
+ // /
768
+ // / If the type witness is an abstract type U, this produces a rewrite
769
+ // / rule
770
+ // /
771
+ // / T.[P:A] => U
772
+ // /
773
+ // / If the type witness is a concrete type Foo, this produces a rewrite
774
+ // / rule
775
+ // /
776
+ // / T.[P:A].[concrete: Foo] => T.[P:A]
777
+ // /
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:
783
+ // /
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:
789
+ // /
790
+ // / T.[P:A] => V
791
+ MutableTerm EquivalenceClassMap::computeConstraintTermForTypeWitness (
792
+ const MutableTerm &key,
793
+ CanType concreteType,
794
+ CanType typeWitness,
795
+ const MutableTerm &subjectType,
796
+ ArrayRef<Term> substitutions) const {
797
+ if (!typeWitness->hasTypeParameter ()) {
798
+ // Check if we have a shorter representative we can use.
799
+ auto domain = key.getRootProtocols ();
800
+ auto concreteTypeKey = std::make_pair (typeWitness, domain);
801
+
802
+ auto found = ConcreteTypeInDomainMap.find (concreteTypeKey);
803
+ if (found != ConcreteTypeInDomainMap.end ()) {
804
+ if (found->second != subjectType) {
805
+ if (DebugConcretizeNestedTypes) {
806
+ llvm::dbgs () << " ^^ Type witness can re-use equivalence class of "
807
+ << found->second << " \n " ;
808
+ }
809
+ return found->second ;
810
+ }
811
+ }
812
+ }
813
+
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
+
828
+ if (typeWitness->isTypeParameter ()) {
829
+ // The type witness is a type parameter of the form τ_0_n.X.Y...Z,
830
+ // where 'n' is an index into the substitution array.
831
+ //
832
+ // Add a rule T => S.X.Y...Z, where S is the nth substitution term.
833
+ return getRelativeTermForType (typeWitness, substitutions, Context);
834
+ }
835
+
836
+ // The type witness is a concrete type.
837
+ MutableTerm constraintType = subjectType;
838
+
839
+ SmallVector<Term, 3 > result;
840
+ auto typeWitnessSchema =
841
+ remapConcreteSubstitutionSchema (typeWitness, substitutions,
842
+ Context, result);
843
+
844
+ // Add a rule T.[P:A].[concrete: Foo.A] => T.[P:A].
845
+ constraintType.add (
846
+ Atom::forConcreteType (
847
+ typeWitnessSchema, result, Context));
848
+
849
+ return constraintType;
850
+ }
851
+
760
852
void EquivalenceClassMap::dump (llvm::raw_ostream &out) const {
761
853
out << " Equivalence class map: {\n " ;
762
854
for (const auto &equivClass : Map) {
@@ -827,8 +919,13 @@ RewriteSystem::buildEquivalenceClassMap(EquivalenceClassMap &map,
827
919
map.addProperty (pair.first , pair.second , inducedRules);
828
920
}
829
921
830
- // We also need to merge concrete type rules with conformance rules, by
831
- // concretizing the associated type witnesses of the concrete type.
922
+ // We collect equivalence classes with fully concrete types so that we can
923
+ // re-use them to tie off recursion in the next step.
924
+ map.computeConcreteTypeInDomainMap ();
925
+
926
+ // Now, we merge concrete type rules with conformance rules, by adding
927
+ // relations between associated type members of type parameters with
928
+ // the concrete type witnesses in the concrete type's conformance.
832
929
map.concretizeNestedTypesFromConcreteParents (inducedRules);
833
930
834
931
// Some of the induced rules might be trivial; only count the induced rules
0 commit comments