50
50
51
51
#include " swift/AST/Decl.h"
52
52
#include " swift/AST/LayoutConstraint.h"
53
+ #include " swift/AST/Module.h"
54
+ #include " swift/AST/ProtocolConformance.h"
53
55
#include " swift/AST/TypeMatcher.h"
54
56
#include " swift/AST/Types.h"
55
57
#include " llvm/Support/raw_ostream.h"
@@ -102,6 +104,45 @@ static unsigned getGenericParamIndex(Type type) {
102
104
return paramTy->getIndex ();
103
105
}
104
106
107
+ // / Reverses the transformation performed by
108
+ // / RewriteSystemBuilder::getConcreteSubstitutionSchema().
109
+ static Type getTypeFromSubstitutionSchema (Type schema,
110
+ ArrayRef<Term> substitutions,
111
+ TypeArrayView<GenericTypeParamType> genericParams,
112
+ const ProtocolGraph &protos,
113
+ RewriteContext &ctx) {
114
+ assert (!schema->isTypeParameter () && " Must have a concrete type here" );
115
+
116
+ if (!schema->hasTypeParameter ())
117
+ return schema;
118
+
119
+ return schema.transformRec ([&](Type t) -> Optional<Type> {
120
+ if (t->is <GenericTypeParamType>()) {
121
+ auto index = getGenericParamIndex (t);
122
+
123
+ return ctx.getTypeForTerm (substitutions[index],
124
+ genericParams, protos);
125
+ }
126
+
127
+ assert (!t->isTypeParameter ());
128
+ return None;
129
+ });
130
+ }
131
+
132
+ // / Get the concrete type of this equivalence class.
133
+ // /
134
+ // / Asserts if this equivalence class is not concrete.
135
+ Type EquivalenceClass::getConcreteType (
136
+ TypeArrayView<GenericTypeParamType> genericParams,
137
+ const ProtocolGraph &protos,
138
+ RewriteContext &ctx) const {
139
+ return getTypeFromSubstitutionSchema (ConcreteType->getConcreteType (),
140
+ ConcreteType->getSubstitutions (),
141
+ genericParams,
142
+ protos,
143
+ ctx);
144
+ }
145
+
105
146
// / Given a concrete type that is a structural sub-component of a concrete
106
147
// / type produced by RewriteSystemBuilder::getConcreteSubstitutionSchema(),
107
148
// / collect the subset of referenced substitutions and renumber the generic
@@ -472,6 +513,157 @@ void EquivalenceClassMap::addProperty(
472
513
inducedRules, DebugConcreteUnification);
473
514
}
474
515
516
+ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParents (
517
+ SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules) const {
518
+ for (const auto &equivClass : Map) {
519
+ if (equivClass->isConcreteType () &&
520
+ !equivClass->getConformsTo ().empty ()) {
521
+ if (DebugConcretizeNestedTypes) {
522
+ llvm::dbgs () << " ^ Concretizing nested types of " ;
523
+ equivClass->dump (llvm::dbgs ());
524
+ llvm::dbgs () << " \n " ;
525
+ }
526
+
527
+ concretizeNestedTypesFromConcreteParent (
528
+ equivClass->getKey (),
529
+ equivClass->ConcreteType ->getConcreteType (),
530
+ equivClass->ConcreteType ->getSubstitutions (),
531
+ equivClass->getConformsTo (),
532
+ inducedRules);
533
+ }
534
+ }
535
+ }
536
+
537
+ // / If we have an equivalence class T => { conforms_to: [ P ], concrete: Foo },
538
+ // / then for each associated type A of P, we generate a new rule:
539
+ // /
540
+ // / T.[P:A].[concrete: Foo.A] => T.[P:A] (if Foo.A is concrete)
541
+ // / T.[P:A] => T.(Foo.A) (if Foo.A is abstract)
542
+ // /
543
+ void EquivalenceClassMap::concretizeNestedTypesFromConcreteParent (
544
+ const MutableTerm &key,
545
+ CanType concreteType, ArrayRef<Term> substitutions,
546
+ ArrayRef<const ProtocolDecl *> conformsTo,
547
+ SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules) const {
548
+ for (auto *proto : conformsTo) {
549
+ // FIXME: Either remove the ModuleDecl entirely from conformance lookup,
550
+ // or pass the correct one down in here.
551
+ auto *module = proto->getParentModule ();
552
+
553
+ auto conformance = module ->lookupConformance (concreteType,
554
+ const_cast <ProtocolDecl *>(proto));
555
+ if (conformance.isInvalid ()) {
556
+ // FIXME: Diagnose conflict
557
+ if (DebugConcretizeNestedTypes) {
558
+ llvm::dbgs () << " ^^ " << concreteType << " does not conform to "
559
+ << proto->getName () << " \n " ;
560
+ }
561
+
562
+ continue ;
563
+ }
564
+
565
+ // FIXME: Maybe this can happen if the concrete type is an
566
+ // opaque result type?
567
+ assert (!conformance.isAbstract ());
568
+
569
+ auto assocTypes = Protos.getProtocolInfo (proto).AssociatedTypes ;
570
+ if (assocTypes.empty ())
571
+ continue ;
572
+
573
+ auto *concrete = conformance.getConcrete ();
574
+
575
+ // We might have duplicates in the list due to diamond inheritance.
576
+ // FIXME: Filter those out further upstream?
577
+ // FIXME: This should actually be outside of the loop over the conforming protos...
578
+ llvm::SmallDenseSet<AssociatedTypeDecl *, 4 > visited;
579
+ for (auto *assocType : assocTypes) {
580
+ if (!visited.insert (assocType).second )
581
+ continue ;
582
+
583
+ // Get the actual protocol in case we inherited this associated type.
584
+ auto *actualProto = assocType->getProtocol ();
585
+ if (actualProto != proto)
586
+ continue ;
587
+
588
+ if (DebugConcretizeNestedTypes) {
589
+ llvm::dbgs () << " ^^ " << " Looking up type witness for "
590
+ << proto->getName () << " :" << assocType->getName ()
591
+ << " on " << concreteType << " \n " ;
592
+ }
593
+
594
+ auto typeWitness = concrete->getTypeWitness (assocType)
595
+ ->getCanonicalType ();
596
+
597
+ if (DebugConcretizeNestedTypes) {
598
+ llvm::dbgs () << " ^^ " << " Type witness for " << assocType->getName ()
599
+ << " of " << concreteType << " is " << typeWitness << " \n " ;
600
+ }
601
+
602
+ auto nestedType = Atom::forAssociatedType (proto, assocType->getName (),
603
+ Context);
604
+
605
+ MutableTerm subjectType = key;
606
+ subjectType.add (nestedType);
607
+
608
+ MutableTerm constraintType;
609
+
610
+ if (concreteType == typeWitness) {
611
+ if (DebugConcretizeNestedTypes) {
612
+ llvm::dbgs () << " ^^ Type witness is the same as the concrete type\n " ;
613
+ }
614
+
615
+ // Add a rule T.[P:A] => T.
616
+ constraintType = key;
617
+
618
+ } else if (typeWitness->isTypeParameter ()) {
619
+ // The type witness is a type parameter of the form τ_0_n.X.Y...Z,
620
+ // where 'n' is an index into the substitution array.
621
+ //
622
+ // Collect zero or more member type names in reverse order.
623
+ SmallVector<Atom, 3 > atoms;
624
+ while (auto memberType = dyn_cast<DependentMemberType>(typeWitness)) {
625
+ atoms.push_back (Atom::forName (memberType->getName (), Context));
626
+ typeWitness = memberType.getBase ();
627
+ }
628
+
629
+ // Get the substitution S corresponding to τ_0_n.
630
+ unsigned index = getGenericParamIndex (typeWitness);
631
+ constraintType = MutableTerm (substitutions[index]);
632
+
633
+ // Add the member type names.
634
+ std::reverse (atoms.begin (), atoms.end ());
635
+ for (auto atom : atoms)
636
+ constraintType.add (atom);
637
+
638
+ // Add a rule T => S.X.Y...Z.
639
+
640
+ } else {
641
+ // The type witness is a concrete type.
642
+ constraintType = subjectType;
643
+
644
+ // FIXME: Handle dependent member types here
645
+ SmallVector<Term, 3 > result;
646
+ auto typeWitnessSchema =
647
+ remapConcreteSubstitutionSchema (typeWitness, substitutions,
648
+ Context.getASTContext (),
649
+ result);
650
+ constraintType.add (
651
+ Atom::forConcreteType (
652
+ typeWitnessSchema, result, Context));
653
+
654
+ // Add a rule T.[P:A].[concrete: Foo.A] => T.[P:A].
655
+
656
+ }
657
+
658
+ inducedRules.emplace_back (subjectType, constraintType);
659
+ if (DebugConcretizeNestedTypes) {
660
+ llvm::dbgs () << " ^^ Induced rule " << constraintType
661
+ << " => " << subjectType << " \n " ;
662
+ }
663
+ }
664
+ }
665
+ }
666
+
475
667
void EquivalenceClassMap::dump (llvm::raw_ostream &out) const {
476
668
out << " Equivalence class map: {\n " ;
477
669
for (const auto &equivClass : Map) {
@@ -485,10 +677,19 @@ void EquivalenceClassMap::dump(llvm::raw_ostream &out) const {
485
677
// / Build the equivalence class map from all rules of the form T.[p] => T, where
486
678
// / [p] is a property atom.
487
679
// /
488
- // / Returns true if concrete term unification performed while building the map
489
- // / introduced new rewrite rules; in this case, the completion procedure must
490
- // / run again.
491
- bool RewriteSystem::buildEquivalenceClassMap (EquivalenceClassMap &map) {
680
+ // / Returns a pair consisting of a status and number of iterations executed.
681
+ // /
682
+ // / The status is CompletionResult::MaxIterations if we exceed \p maxIterations
683
+ // / iterations.
684
+ // /
685
+ // / The status is CompletionResult::MaxDepth if we produce a rewrite rule whose
686
+ // / left hand side has a length exceeding \p maxDepth.
687
+ // /
688
+ // / Otherwise, the status is CompletionResult::Success.
689
+ std::pair<RewriteSystem::CompletionResult, unsigned >
690
+ RewriteSystem::buildEquivalenceClassMap (EquivalenceClassMap &map,
691
+ unsigned maxIterations,
692
+ unsigned maxDepth) {
492
693
map.clear ();
493
694
494
695
std::vector<std::pair<MutableTerm, Atom>> properties;
@@ -533,18 +734,25 @@ bool RewriteSystem::buildEquivalenceClassMap(EquivalenceClassMap &map) {
533
734
map.addProperty (pair.first , pair.second , inducedRules);
534
735
}
535
736
737
+ // We also need to merge concrete type rules with conformance rules, by
738
+ // concretizing the associated type witnesses of the concrete type.
739
+ map.concretizeNestedTypesFromConcreteParents (inducedRules);
740
+
536
741
// Some of the induced rules might be trivial; only count the induced rules
537
742
// where the left hand side is not already equivalent to the right hand side.
538
743
unsigned addedNewRules = 0 ;
539
744
for (auto pair : inducedRules) {
540
- if (addRule (pair.first , pair.second ))
745
+ if (addRule (pair.first , pair.second )) {
541
746
++addedNewRules;
542
- }
543
747
544
- if (auto *stats = Context.getASTContext ().Stats ) {
545
- stats->getFrontendCounters ()
546
- .NumRequirementMachineUnifiedConcreteTerms += addedNewRules;
748
+ const auto &newRule = Rules.back ();
749
+ if (newRule.getLHS ().size () > maxDepth)
750
+ return std::make_pair (CompletionResult::MaxDepth, addedNewRules);
751
+ }
547
752
}
548
753
549
- return addedNewRules > 0 ;
754
+ if (Rules.size () > maxIterations)
755
+ return std::make_pair (CompletionResult::MaxIterations, addedNewRules);
756
+
757
+ return std::make_pair (CompletionResult::Success, addedNewRules);
550
758
}
0 commit comments