matching_impl_set.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. #ifndef CARBON_EXPLORER_INTERPRETER_MATCHING_IMPL_SET_H_
  5. #define CARBON_EXPLORER_INTERPRETER_MATCHING_IMPL_SET_H_
  6. #include <vector>
  7. #include "common/ostream.h"
  8. #include "explorer/ast/declaration.h"
  9. #include "explorer/ast/value.h"
  10. #include "explorer/base/nonnull.h"
  11. #include "explorer/interpreter/impl_scope.h"
  12. #include "llvm/ADT/DenseMap.h"
  13. namespace Carbon {
  14. // A set of impl matches that we're currently performing. Each `Match`
  15. // represents an attempt to match a `type as interface` query against an `impl`
  16. // declaration. This is used to detect and reject non-termination when impl
  17. // matching recursively triggers further impl matching.
  18. //
  19. // The language rule we use to detect potential non-termination is to count the
  20. // number of times each "label" appears within the type and interface, where a
  21. // label is the name of a declared entity such as a class or interface, or a
  22. // primitive like `type` or `bool`. For example, `Optional(i32*)` contains the
  23. // labels for `Optional`, `i32`, and `*` (built-in pointer type) once each. If
  24. // we ever try matching the same `impl` twice, where the inner match contains
  25. // at least as many appearances of each label as the outer match, we reject the
  26. // program as invalid. We also reject if a query results in the exact same
  27. // query being performed again.
  28. //
  29. // This class is an implementation detail of `TypeChecker::MatchImpl`.
  30. class MatchingImplSet {
  31. private:
  32. class LeafCollector;
  33. enum class Label : int;
  34. using Signature = llvm::DenseMap<Label, int>;
  35. public:
  36. // An RAII type that tracks an impl match that we're currently performing.
  37. // One instance of this class will exist for each in-progress call to
  38. // `MatchImpl`.
  39. class Match {
  40. public:
  41. explicit Match(Nonnull<MatchingImplSet*> parent,
  42. Nonnull<const ImplScope::ImplFact*> impl,
  43. Nonnull<const Value*> type, Nonnull<const Value*> interface);
  44. ~Match();
  45. Match(const Match&) = delete;
  46. auto operator=(const Match&) -> Match& = delete;
  47. // Check to see if this match duplicates any prior one within the same set,
  48. // or if there's a simpler form of this match in the set. If so, returns a
  49. // suitable error. This should be delayed until we know that the impl
  50. // structurally matches the type and interface.
  51. auto DiagnosePotentialCycle(SourceLocation source_loc) -> ErrorOr<Success>;
  52. private:
  53. friend class LeafCollector;
  54. // The set that this match is part of.
  55. Nonnull<MatchingImplSet*> parent_;
  56. // The `impl` that is being matched against.
  57. Nonnull<const ImplScope::ImplFact*> impl_;
  58. // The type that is being matched against the impl.
  59. Nonnull<const Value*> type_;
  60. // The interface that is being matched against the impl.
  61. Nonnull<const Value*> interface_;
  62. // The number of times each label appears in the type or interface.
  63. Signature signature_;
  64. };
  65. private:
  66. friend class llvm::DenseMapInfo<Label>;
  67. // An opaque integer used to identify a particular label appearing in a type,
  68. // such as a class name. The named enumerators represent builtins, and values
  69. // >= `FirstDeclarationLabel` represent declarations from the program.
  70. enum class Label : int {
  71. // Label for `type` type constant.
  72. TypeType,
  73. // Label for `bool` type constant.
  74. BoolType,
  75. // Label for `i32` type constant.
  76. IntType,
  77. // Label for `String` type constant.
  78. StringType,
  79. // Label for `[_;_]` type constructor.
  80. ArrayType,
  81. // Label for `_*` type constructor.
  82. PointerType,
  83. // Label for `{.a: _, .b: _, ...}` struct type constructor. We use the same
  84. // label regardless of the arity of the struct type and any field names.
  85. StructType,
  86. // Label for `(_, _, ..., _)` tuple type constructor. We use the same label
  87. // regardless of the arity of the tuple type.
  88. TupleType,
  89. // First Label value corresponding to a Declaration. Must be kept at the
  90. // end of the enum.
  91. FirstDeclarationLabel
  92. };
  93. // Get the Label that represents a given declaration.
  94. auto GetLabelForDeclaration(const Declaration& declaration) -> Label;
  95. // The known declarations and their labels.
  96. llvm::DenseMap<const Declaration*, Label> declaration_labels_;
  97. // The matches that are currently being performed, in order from outermost to
  98. // innermost.
  99. std::vector<Match*> matches_;
  100. };
  101. } // namespace Carbon
  102. // Support use of Label as a DenseMap key.
  103. template <>
  104. struct llvm::DenseMapInfo<Carbon::MatchingImplSet::Label> {
  105. using Base = llvm::DenseMapInfo<int>;
  106. using Label = Carbon::MatchingImplSet::Label;
  107. static inline auto getEmptyKey() -> Label {
  108. return static_cast<Label>(Base::getEmptyKey());
  109. }
  110. static inline auto getTombstoneKey() -> Label {
  111. return static_cast<Label>(Base::getTombstoneKey());
  112. }
  113. static inline auto getHashValue(Label label) -> unsigned {
  114. return Base::getHashValue(static_cast<int>(label));
  115. }
  116. static auto isEqual(Label a, Label b) -> bool { return a == b; }
  117. };
  118. #endif // CARBON_EXPLORER_INTERPRETER_MATCHING_IMPL_SET_H_