|
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"
|
@@ -511,6 +513,157 @@ void EquivalenceClassMap::addProperty(
|
511 | 513 | inducedRules, DebugConcreteUnification);
|
512 | 514 | }
|
513 | 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 | + |
514 | 667 | void EquivalenceClassMap::dump(llvm::raw_ostream &out) const {
|
515 | 668 | out << "Equivalence class map: {\n";
|
516 | 669 | for (const auto &equivClass : Map) {
|
@@ -581,6 +734,10 @@ RewriteSystem::buildEquivalenceClassMap(EquivalenceClassMap &map,
|
581 | 734 | map.addProperty(pair.first, pair.second, inducedRules);
|
582 | 735 | }
|
583 | 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 | + |
584 | 741 | // Some of the induced rules might be trivial; only count the induced rules
|
585 | 742 | // where the left hand side is not already equivalent to the right hand side.
|
586 | 743 | unsigned addedNewRules = 0;
|
|
0 commit comments