pattern_analysis.h 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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_PATTERN_ANALYSIS_H_
  5. #define CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_
  6. #include <vector>
  7. #include "explorer/ast/pattern.h"
  8. #include "explorer/ast/value.h"
  9. #include "explorer/base/nonnull.h"
  10. #include "llvm/ADT/PointerUnion.h"
  11. namespace Carbon {
  12. // An abstracted view of a pattern or constant value (which we view as a
  13. // particular kind of pattern).
  14. class AbstractPattern {
  15. public:
  16. enum Kind {
  17. // A pattern that matches anything.
  18. Wildcard,
  19. // A pattern that matches a compound value with sub-patterns to match
  20. // elements. A compound value is modeled as a discriminator name applied to
  21. // a sequence of nested values: the alternative `Optional.Element(E)` has
  22. // discriminator `Element` and nested value `E`, and the tuple `(A,B,C)`
  23. // has an empty discriminator and nested values `A`, `B`, and `C`.
  24. Compound,
  25. // A pattern that matches a particular primitive value.
  26. Primitive
  27. };
  28. // This is intentionally implicit to allow easy conversion from a container
  29. // of `const Pattern*` to a container of `AbstractPattern`s.
  30. // NOLINTNEXTLINE(google-explicit-constructor)
  31. AbstractPattern(Nonnull<const Pattern*> pattern) { Set(pattern); }
  32. AbstractPattern(Nonnull<const Value*> value, Nonnull<const Value*> type)
  33. : value_(value), type_(type) {}
  34. // Make a match-anything wildcard pattern.
  35. static auto MakeWildcard() -> AbstractPattern {
  36. return AbstractPattern(WildcardTag());
  37. }
  38. // Get the kind for this pattern.
  39. auto kind() const -> Kind;
  40. // Get the type, for a non-wildcard pattern.
  41. auto type() const -> const Value&;
  42. // Get the discriminator used for a compound pattern.
  43. auto discriminator() const -> std::string_view;
  44. // Get the number of nested patterns for a compound pattern.
  45. auto elements_size() const -> int;
  46. // Append the nested patterns in this compound pattern to `out`.
  47. void AppendElementsTo(std::vector<AbstractPattern>& out) const;
  48. // Get the value for a primitive pattern.
  49. auto value() const -> const Value&;
  50. private:
  51. // This is aligned so that we can use it in the `PointerUnion` below.
  52. struct alignas(8) WildcardTag {};
  53. explicit AbstractPattern(WildcardTag /*unused*/)
  54. : value_(static_cast<const WildcardTag*>(nullptr)), type_(nullptr) {}
  55. void Set(Nonnull<const Pattern*> pattern);
  56. // The underlying pattern: either a syntactic pattern, or a constant value,
  57. // or a placeholder indicating that this is a wildcard pattern.
  58. llvm::PointerUnion<Nonnull<const Pattern*>, Nonnull<const Value*>,
  59. const WildcardTag*>
  60. value_;
  61. // Values don't always know their types, so store the type here. We only
  62. // really need it for the `const Value*` case but also store it for the
  63. // `const Pattern*` case for convenience.
  64. const Value* type_;
  65. };
  66. // A matrix of patterns, used for determining exhaustiveness and redundancy of
  67. // patterns in a match statement.
  68. //
  69. // See http://moscova.inria.fr/~maranget/papers/warn/index.html for details on
  70. // the algorithm used here.
  71. class PatternMatrix {
  72. public:
  73. // Add a pattern vector row to this collection of pattern vectors.
  74. void Add(std::vector<AbstractPattern> pattern_vector) {
  75. CARBON_CHECK(matrix_.empty() || matrix_[0].size() == pattern_vector.size());
  76. matrix_.push_back(std::move(pattern_vector));
  77. }
  78. // Returns true if the given pattern vector is redundant if it appears after
  79. // the patterns in this matrix. That is, if it will never match following the
  80. // other patterns because everything it matches is matched by some other
  81. // pattern.
  82. auto IsRedundant(llvm::ArrayRef<AbstractPattern> pattern) const -> bool {
  83. return !IsUseful(pattern, MaxExponentialDepth);
  84. }
  85. private:
  86. // The maximum number of times we will consider all alternatives when
  87. // recursively expanding the pattern. Allowing this to happen an arbitrary
  88. // number of times leads to exponential growth in the runtime of the
  89. // algorithm.
  90. static constexpr int MaxExponentialDepth = 8;
  91. // Information about a constructor for a compound type.
  92. struct DiscriminatorInfo {
  93. // For an alternative, the name. Otherwise, empty.
  94. std::string_view discriminator;
  95. // The number of elements. For a tuple, the size. Always 1 for an
  96. // alternative.
  97. int size;
  98. };
  99. struct DiscriminatorSet {
  100. std::vector<DiscriminatorInfo> found;
  101. bool any_missing;
  102. };
  103. // Determine whether the given pattern vector is useful: that is, whether
  104. // adding it to the matrix would allow any more values to be matched.
  105. auto IsUseful(llvm::ArrayRef<AbstractPattern> pattern,
  106. int max_exponential_depth) const -> bool;
  107. // Find the discriminators used by the first column and check whether we
  108. // found all of them.
  109. auto FirstColumnDiscriminators() const -> DiscriminatorSet;
  110. // Specialize the pattern vector `row` for the case that the first value
  111. // matched uses `discriminator`.
  112. static auto SpecializeRow(llvm::ArrayRef<AbstractPattern> row,
  113. DiscriminatorInfo discriminator)
  114. -> std::optional<std::vector<AbstractPattern>>;
  115. // Specialize the pattern matrix for the case that the first value matched
  116. // uses `discriminator`, and its elements are matched.
  117. auto Specialize(DiscriminatorInfo discriminator) const -> PatternMatrix;
  118. // Specialize the pattern matrix for the case where the first value is known
  119. // to be `value`, and is not matched.
  120. auto Specialize(const Value& value) const -> PatternMatrix;
  121. // Specialize the pattern matrix for the case where the first value uses a
  122. // discriminator matching none of the non-wildcard patterns.
  123. auto Default() const -> PatternMatrix;
  124. // The pattern matrix itself, in row-major order. Each element of `matrix_`
  125. // is a distinct sequence of patterns that can be matched against a
  126. // corresponding sequence of values. Each such row has the same length and
  127. // has elements of the same type.
  128. std::vector<std::vector<AbstractPattern>> matrix_;
  129. };
  130. } // namespace Carbon
  131. #endif // CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_