matching_impl_set.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "common/check.h"
  5. #include "common/error.h"
  6. #include "explorer/ast/declaration.h"
  7. #include "explorer/ast/value.h"
  8. #include "explorer/base/error_builders.h"
  9. #include "explorer/base/nonnull.h"
  10. #include "explorer/base/source_location.h"
  11. #include "explorer/interpreter/type_checker.h"
  12. namespace Carbon {
  13. // A visitor for type values that collects leaf 'labels', such as class names,
  14. // and adds them to the signature of a `Match` object.
  15. class MatchingImplSet::LeafCollector {
  16. public:
  17. explicit LeafCollector(Match* match) : match_(match) {}
  18. void Collect(const Value* value) {
  19. value->Visit<void>(
  20. [&](const auto* derived_value) { VisitValue(derived_value); });
  21. }
  22. void Collect(Label label) { ++match_->signature_[label]; }
  23. private:
  24. // Most kinds of value don't contribute to the signature.
  25. void VisitValue(const Value* /*unused*/) {}
  26. void VisitValue(const TypeType* /*unused*/) { Collect(Label::TypeType); }
  27. void VisitValue(const BoolType* /*unused*/) { Collect(Label::BoolType); }
  28. void VisitValue(const IntType* /*unused*/) { Collect(Label::IntType); }
  29. void VisitValue(const StringType* /*unused*/) { Collect(Label::StringType); }
  30. void VisitValue(const StaticArrayType* array) {
  31. Collect(Label::ArrayType);
  32. Collect(&array->element_type());
  33. }
  34. void VisitValue(const PointerType* pointer) {
  35. Collect(Label::PointerType);
  36. Collect(&pointer->pointee_type());
  37. }
  38. void VisitValue(const StructType* struct_type) {
  39. Collect(Label::StructType);
  40. for (const auto& [name, type] : struct_type->fields()) {
  41. Collect(type);
  42. }
  43. }
  44. void VisitValue(const TupleType* tuple_type) {
  45. Collect(Label::TupleType);
  46. for (const auto* elem_type : tuple_type->elements()) {
  47. Collect(elem_type);
  48. }
  49. }
  50. void VisitValue(const NominalClassType* class_type) {
  51. VisitDeclarationAndArgs(class_type->declaration(), class_type->bindings());
  52. }
  53. void VisitValue(const MixinPseudoType* mixin_type) {
  54. VisitDeclarationAndArgs(mixin_type->declaration(), mixin_type->bindings());
  55. }
  56. void VisitValue(const InterfaceType* iface_type) {
  57. VisitDeclarationAndArgs(iface_type->declaration(), iface_type->bindings());
  58. }
  59. void VisitValue(const NamedConstraintType* constraint_type) {
  60. VisitDeclarationAndArgs(constraint_type->declaration(),
  61. constraint_type->bindings());
  62. }
  63. void VisitValue(const ChoiceType* choice_type) {
  64. VisitDeclarationAndArgs(choice_type->declaration(),
  65. choice_type->bindings());
  66. }
  67. void VisitDeclarationAndArgs(const Declaration& declaration,
  68. const Bindings& bindings) {
  69. Collect(match_->parent_->GetLabelForDeclaration(declaration));
  70. for (auto [key, value] : bindings.args()) {
  71. Collect(value);
  72. }
  73. }
  74. Match* match_;
  75. };
  76. auto MatchingImplSet::GetLabelForDeclaration(const Declaration& declaration)
  77. -> Label {
  78. auto [it, added] = declaration_labels_.insert(
  79. {&declaration,
  80. static_cast<Label>(static_cast<int>(Label::FirstDeclarationLabel) +
  81. declaration_labels_.size())});
  82. return it->second;
  83. }
  84. MatchingImplSet::Match::Match(Nonnull<MatchingImplSet*> parent,
  85. Nonnull<const ImplScope::ImplFact*> impl,
  86. Nonnull<const Value*> type,
  87. Nonnull<const Value*> interface)
  88. : parent_(parent), impl_(impl), type_(type), interface_(interface) {
  89. // Build our signature.
  90. LeafCollector collector(this);
  91. collector.Collect(type);
  92. collector.Collect(interface);
  93. parent_->matches_.push_back(this);
  94. }
  95. MatchingImplSet::Match::~Match() {
  96. CARBON_CHECK(parent_->matches_.back() == this, "match stack broken");
  97. parent_->matches_.pop_back();
  98. }
  99. auto MatchingImplSet::Match::DiagnosePotentialCycle(SourceLocation source_loc)
  100. -> ErrorOr<Success> {
  101. // Determine whether any labels in 'a' have a higher count than in 'b'.
  102. auto any_labels_with_higher_count = [](const Signature& a,
  103. const Signature& b) {
  104. if (a.size() > b.size()) {
  105. // Every label in a signature has a count of at least one.
  106. return true;
  107. }
  108. for (auto [key, a_value] : a) {
  109. int b_value = b.lookup(key);
  110. if (a_value > b_value) {
  111. return true;
  112. }
  113. }
  114. return false;
  115. };
  116. for (auto* match : parent_->matches_) {
  117. if (match != this && match->impl_ == impl_ &&
  118. !any_labels_with_higher_count(match->signature_, signature_)) {
  119. // No label in the outer match has a higher count than the same label in
  120. // the inner match. We might have reached a cycle.
  121. if (any_labels_with_higher_count(signature_, match->signature_)) {
  122. // The inner match has a higher count for some label. This query is
  123. // strictly more complex than the outer one, so reject this potential
  124. // cycle.
  125. // TODO: Track which label has a higher count, map it back to a string,
  126. // and include it in this diagnostic.
  127. return ProgramError(source_loc)
  128. << "impl matching recursively performed a more complex match "
  129. "using the same impl\n"
  130. << " outer match: " << *match->type_ << " as "
  131. << *match->interface_ << "\n"
  132. << " inner match: " << *type_ << " as " << *interface_;
  133. }
  134. if (ValueEqual(match->type_, type_, std::nullopt) &&
  135. ValueEqual(match->interface_, interface_, std::nullopt)) {
  136. // We hit the same query twice recursively. This is definitely a cycle.
  137. return ProgramError(source_loc)
  138. << "impl matching for " << *type_ << " as " << *interface_
  139. << " recursively performed a match for the same type and "
  140. "interface";
  141. }
  142. }
  143. }
  144. return Success();
  145. }
  146. } // namespace Carbon