| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #include "common/check.h"
- #include "common/error.h"
- #include "explorer/ast/declaration.h"
- #include "explorer/ast/value.h"
- #include "explorer/base/error_builders.h"
- #include "explorer/base/nonnull.h"
- #include "explorer/base/source_location.h"
- #include "explorer/interpreter/type_checker.h"
- namespace Carbon {
- // A visitor for type values that collects leaf 'labels', such as class names,
- // and adds them to the signature of a `Match` object.
- class MatchingImplSet::LeafCollector {
- public:
- explicit LeafCollector(Match* match) : match_(match) {}
- void Collect(const Value* value) {
- value->Visit<void>(
- [&](const auto* derived_value) { VisitValue(derived_value); });
- }
- void Collect(Label label) { ++match_->signature_[label]; }
- private:
- // Most kinds of value don't contribute to the signature.
- void VisitValue(const Value* /*unused*/) {}
- void VisitValue(const TypeType* /*unused*/) { Collect(Label::TypeType); }
- void VisitValue(const BoolType* /*unused*/) { Collect(Label::BoolType); }
- void VisitValue(const IntType* /*unused*/) { Collect(Label::IntType); }
- void VisitValue(const StringType* /*unused*/) { Collect(Label::StringType); }
- void VisitValue(const StaticArrayType* array) {
- Collect(Label::ArrayType);
- Collect(&array->element_type());
- }
- void VisitValue(const PointerType* pointer) {
- Collect(Label::PointerType);
- Collect(&pointer->pointee_type());
- }
- void VisitValue(const StructType* struct_type) {
- Collect(Label::StructType);
- for (auto [name, type] : struct_type->fields()) {
- Collect(type);
- }
- }
- void VisitValue(const TupleType* tuple_type) {
- Collect(Label::TupleType);
- for (const auto* elem_type : tuple_type->elements()) {
- Collect(elem_type);
- }
- }
- void VisitValue(const NominalClassType* class_type) {
- VisitDeclarationAndArgs(class_type->declaration(), class_type->bindings());
- }
- void VisitValue(const MixinPseudoType* mixin_type) {
- VisitDeclarationAndArgs(mixin_type->declaration(), mixin_type->bindings());
- }
- void VisitValue(const InterfaceType* iface_type) {
- VisitDeclarationAndArgs(iface_type->declaration(), iface_type->bindings());
- }
- void VisitValue(const NamedConstraintType* constraint_type) {
- VisitDeclarationAndArgs(constraint_type->declaration(),
- constraint_type->bindings());
- }
- void VisitValue(const ChoiceType* choice_type) {
- VisitDeclarationAndArgs(choice_type->declaration(),
- choice_type->bindings());
- }
- void VisitDeclarationAndArgs(const Declaration& declaration,
- const Bindings& bindings) {
- Collect(match_->parent_->GetLabelForDeclaration(declaration));
- for (auto [key, value] : bindings.args()) {
- Collect(value);
- }
- }
- Match* match_;
- };
- auto MatchingImplSet::GetLabelForDeclaration(const Declaration& declaration)
- -> Label {
- auto [it, added] = declaration_labels_.insert(
- {&declaration,
- static_cast<Label>(static_cast<int>(Label::FirstDeclarationLabel) +
- declaration_labels_.size())});
- return it->second;
- }
- MatchingImplSet::Match::Match(Nonnull<MatchingImplSet*> parent,
- Nonnull<const ImplScope::ImplFact*> impl,
- Nonnull<const Value*> type,
- Nonnull<const Value*> interface)
- : parent_(parent), impl_(impl), type_(type), interface_(interface) {
- // Build our signature.
- LeafCollector collector(this);
- collector.Collect(type);
- collector.Collect(interface);
- parent_->matches_.push_back(this);
- }
- MatchingImplSet::Match::~Match() {
- CARBON_CHECK(parent_->matches_.back() == this) << "match stack broken";
- parent_->matches_.pop_back();
- }
- auto MatchingImplSet::Match::DiagnosePotentialCycle(SourceLocation source_loc)
- -> ErrorOr<Success> {
- // Determine whether any labels in 'a' have a higher count than in 'b'.
- auto any_labels_with_higher_count = [](const Signature& a,
- const Signature& b) {
- if (a.size() > b.size()) {
- // Every label in a signature has a count of at least one.
- return true;
- }
- for (auto [key, a_value] : a) {
- int b_value = b.lookup(key);
- if (a_value > b_value) {
- return true;
- }
- }
- return false;
- };
- for (auto* match : parent_->matches_) {
- if (match != this && match->impl_ == impl_ &&
- !any_labels_with_higher_count(match->signature_, signature_)) {
- // No label in the outer match has a higher count than the same label in
- // the inner match. We might have reached a cycle.
- if (any_labels_with_higher_count(signature_, match->signature_)) {
- // The inner match has a higher count for some label. This query is
- // strictly more complex than the outer one, so reject this potential
- // cycle.
- // TODO: Track which label has a higher count, map it back to a string,
- // and include it in this diagnostic.
- return ProgramError(source_loc)
- << "impl matching recursively performed a more complex match "
- "using the same impl\n"
- << " outer match: " << *match->type_ << " as "
- << *match->interface_ << "\n"
- << " inner match: " << *type_ << " as " << *interface_;
- }
- if (ValueEqual(match->type_, type_, std::nullopt) &&
- ValueEqual(match->interface_, interface_, std::nullopt)) {
- // We hit the same query twice recursively. This is definitely a cycle.
- return ProgramError(source_loc)
- << "impl matching for " << *type_ << " as " << *interface_
- << " recursively performed a match for the same type and "
- "interface";
- }
- }
- }
- return Success();
- }
- } // namespace Carbon
|