@@ -648,3 +648,152 @@ MutableTerm PropertyMap::computeConstraintTermForTypeWitness(
648
648
649
649
return constraintType;
650
650
}
651
+
652
+ void PropertyMap::recordConcreteConformanceRules (
653
+ SmallVectorImpl<InducedRule> &inducedRules) {
654
+ for (const auto &props : Entries) {
655
+ if (props->getConformsTo ().empty ())
656
+ continue ;
657
+
658
+ if (props->ConcreteType ) {
659
+ unsigned concreteRuleID = *props->ConcreteTypeRule ;
660
+
661
+ // The GSB drops all conformance requirements on a type parameter equated
662
+ // to a concrete type, even if the concrete type doesn't conform. That is,
663
+ //
664
+ // protocol P {}
665
+ // <T where T : P, T == Int>
666
+ //
667
+ // minimizes as
668
+ //
669
+ // <T where T == Int>.
670
+ for (unsigned i : indices (props->ConformsTo )) {
671
+ auto *proto = props->ConformsTo [i];
672
+ auto conformanceRuleID = props->ConformsToRules [i];
673
+ recordConcreteConformanceRule (concreteRuleID, conformanceRuleID, proto,
674
+ inducedRules);
675
+ }
676
+ }
677
+
678
+ if (props->Superclass ) {
679
+ unsigned superclassRuleID = *props->SuperclassRule ;
680
+
681
+ // For superclass rules, we only introduce a concrete conformance if the
682
+ // superclass actually conforms. Otherwise, it is totally fine to have a
683
+ // signature like
684
+ //
685
+ // protocol P {}
686
+ // class C {}
687
+ // <T where T : P, T : C>
688
+ //
689
+ // There is no relation between P and C here.
690
+
691
+ // The conformances in SuperclassConformances should appear in the same
692
+ // order as the protocols in ConformsTo.
693
+ auto conformanceIter = props->SuperclassConformances .begin ();
694
+
695
+ for (unsigned i : indices (props->ConformsTo )) {
696
+ if (conformanceIter == props->SuperclassConformances .end ())
697
+ break ;
698
+
699
+ auto *proto = props->ConformsTo [i];
700
+ if (proto != (*conformanceIter)->getProtocol ())
701
+ continue ;
702
+
703
+ unsigned conformanceRuleID = props->ConformsToRules [i];
704
+ recordConcreteConformanceRule (superclassRuleID, conformanceRuleID, proto,
705
+ inducedRules);
706
+ ++conformanceIter;
707
+ }
708
+
709
+ assert (conformanceIter == props->SuperclassConformances .end ());
710
+ }
711
+ }
712
+ }
713
+
714
+ void PropertyMap::recordConcreteConformanceRule (
715
+ unsigned concreteRuleID,
716
+ unsigned conformanceRuleID,
717
+ const ProtocolDecl *proto,
718
+ SmallVectorImpl<InducedRule> &inducedRules) {
719
+ if (!ConcreteConformanceRules.insert (
720
+ std::make_pair (concreteRuleID, conformanceRuleID)).second ) {
721
+ // We've already emitted this rule.
722
+ return ;
723
+ }
724
+
725
+ const auto &concreteRule = System.getRule (concreteRuleID);
726
+ const auto &conformanceRule = System.getRule (conformanceRuleID);
727
+
728
+ auto conformanceSymbol = *conformanceRule.isPropertyRule ();
729
+ assert (conformanceSymbol.getKind () == Symbol::Kind::Protocol);
730
+ assert (conformanceSymbol.getProtocol () == proto);
731
+
732
+ auto concreteSymbol = *concreteRule.isPropertyRule ();
733
+ assert (concreteSymbol.getKind () == Symbol::Kind::ConcreteType ||
734
+ concreteSymbol.getKind () == Symbol::Kind::Superclass);
735
+
736
+ RewritePath path;
737
+
738
+ // We have a pair of rules T.[P] and T'.[concrete: C].
739
+ // Either T == T', or T is a prefix of T', or T' is a prefix of T.
740
+ //
741
+ // Let T'' be the longest of T and T'.
742
+ MutableTerm rhs (concreteRule.getRHS ().size () > conformanceRule.getRHS ().size ()
743
+ ? concreteRule.getRHS ()
744
+ : conformanceRule.getRHS ());
745
+
746
+ // First, apply the conformance rule in reverse to obtain T''.[P].
747
+ path.add (RewriteStep::forRewriteRule (
748
+ /* startOffset=*/ rhs.size () - conformanceRule.getRHS ().size (),
749
+ /* endOffset=*/ 0 ,
750
+ /* ruleID=*/ conformanceRuleID,
751
+ /* inverse=*/ true ));
752
+
753
+ // Now, apply the concrete type rule in reverse to obtain T''.[concrete: C].[P].
754
+ path.add (RewriteStep::forRewriteRule (
755
+ /* startOffset=*/ rhs.size () - concreteRule.getRHS ().size (),
756
+ /* endOffset=*/ 1 ,
757
+ /* ruleID=*/ concreteRuleID,
758
+ /* inverse=*/ true ));
759
+
760
+ // Apply a concrete type adjustment to the concrete symbol if T' is shorter
761
+ // than T.
762
+ unsigned adjustment = rhs.size () - concreteRule.getRHS ().size ();
763
+ if (adjustment > 0 &&
764
+ !concreteSymbol.getSubstitutions ().empty ()) {
765
+ // FIXME: This needs an endOffset!
766
+ path.add (RewriteStep::forAdjustment (adjustment, /* inverse=*/ false ));
767
+
768
+ concreteSymbol = concreteSymbol.prependPrefixToConcreteSubstitutions (
769
+ MutableTerm (rhs.begin (), rhs.begin () + adjustment),
770
+ Context);
771
+ }
772
+
773
+ // Now, transform T''.[concrete: C].[P] into T''.[concrete: C : P].
774
+ Symbol concreteConformanceSymbol = [&]() {
775
+ if (concreteSymbol.getKind () == Symbol::Kind::ConcreteType) {
776
+ path.add (RewriteStep::forConcreteConformance (/* inverse=*/ false ));
777
+ return Symbol::forConcreteConformance (
778
+ concreteSymbol.getConcreteType (),
779
+ concreteSymbol.getSubstitutions (),
780
+ proto, Context);
781
+ } else {
782
+ assert (concreteSymbol.getKind () == Symbol::Kind::Superclass);
783
+ path.add (RewriteStep::forSuperclassConformance (/* inverse=*/ false ));
784
+ return Symbol::forConcreteConformance (
785
+ concreteSymbol.getSuperclass (),
786
+ concreteSymbol.getSubstitutions (),
787
+ proto, Context);
788
+ }
789
+ }();
790
+
791
+ MutableTerm lhs (rhs);
792
+ lhs.add (concreteConformanceSymbol);
793
+
794
+ // The path turns T'' (RHS) into T''.[concrete: C : P] (LHS), but we need
795
+ // it to go in the other direction.
796
+ path.invert ();
797
+
798
+ inducedRules.emplace_back (std::move (lhs), std::move (rhs), std::move (path));
799
+ }
0 commit comments