@@ -648,3 +648,152 @@ MutableTerm PropertyMap::computeConstraintTermForTypeWitness(
648648
649649 return constraintType;
650650}
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