// 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 #ifndef CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_ #define CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_ #include #include "explorer/ast/pattern.h" #include "explorer/ast/value.h" #include "explorer/base/nonnull.h" #include "llvm/ADT/PointerUnion.h" namespace Carbon { // An abstracted view of a pattern or constant value (which we view as a // particular kind of pattern). class AbstractPattern { public: enum Kind { // A pattern that matches anything. Wildcard, // A pattern that matches a compound value with sub-patterns to match // elements. A compound value is modeled as a discriminator name applied to // a sequence of nested values: the alternative `Optional.Element(E)` has // discriminator `Element` and nested value `E`, and the tuple `(A,B,C)` // has an empty discriminator and nested values `A`, `B`, and `C`. Compound, // A pattern that matches a particular primitive value. Primitive }; // This is intentionally implicit to allow easy conversion from a container // of `const Pattern*` to a container of `AbstractPattern`s. // NOLINTNEXTLINE(google-explicit-constructor) AbstractPattern(Nonnull pattern) { Set(pattern); } AbstractPattern(Nonnull value, Nonnull type) : value_(value), type_(type) {} // Make a match-anything wildcard pattern. static auto MakeWildcard() -> AbstractPattern { return AbstractPattern(WildcardTag()); } // Get the kind for this pattern. auto kind() const -> Kind; // Get the type, for a non-wildcard pattern. auto type() const -> const Value&; // Get the discriminator used for a compound pattern. auto discriminator() const -> std::string_view; // Get the number of nested patterns for a compound pattern. auto elements_size() const -> int; // Append the nested patterns in this compound pattern to `out`. void AppendElementsTo(std::vector& out) const; // Get the value for a primitive pattern. auto value() const -> const Value&; private: // This is aligned so that we can use it in the `PointerUnion` below. struct alignas(8) WildcardTag {}; explicit AbstractPattern(WildcardTag /*unused*/) : value_(static_cast(nullptr)), type_(nullptr) {} void Set(Nonnull pattern); // The underlying pattern: either a syntactic pattern, or a constant value, // or a placeholder indicating that this is a wildcard pattern. llvm::PointerUnion, Nonnull, const WildcardTag*> value_; // Values don't always know their types, so store the type here. We only // really need it for the `const Value*` case but also store it for the // `const Pattern*` case for convenience. const Value* type_; }; // A matrix of patterns, used for determining exhaustiveness and redundancy of // patterns in a match statement. // // See http://moscova.inria.fr/~maranget/papers/warn/index.html for details on // the algorithm used here. class PatternMatrix { public: // Add a pattern vector row to this collection of pattern vectors. void Add(std::vector pattern_vector) { CARBON_CHECK(matrix_.empty() || matrix_[0].size() == pattern_vector.size()); matrix_.push_back(std::move(pattern_vector)); } // Returns true if the given pattern vector is redundant if it appears after // the patterns in this matrix. That is, if it will never match following the // other patterns because everything it matches is matched by some other // pattern. auto IsRedundant(llvm::ArrayRef pattern) const -> bool { return !IsUseful(pattern, MaxExponentialDepth); } private: // The maximum number of times we will consider all alternatives when // recursively expanding the pattern. Allowing this to happen an arbitrary // number of times leads to exponential growth in the runtime of the // algorithm. static constexpr int MaxExponentialDepth = 8; // Information about a constructor for a compound type. struct DiscriminatorInfo { // For an alternative, the name. Otherwise, empty. std::string_view discriminator; // The number of elements. For a tuple, the size. Always 1 for an // alternative. int size; }; struct DiscriminatorSet { std::vector found; bool any_missing; }; // Determine whether the given pattern vector is useful: that is, whether // adding it to the matrix would allow any more values to be matched. auto IsUseful(llvm::ArrayRef pattern, int max_exponential_depth) const -> bool; // Find the discriminators used by the first column and check whether we // found all of them. auto FirstColumnDiscriminators() const -> DiscriminatorSet; // Specialize the pattern vector `row` for the case that the first value // matched uses `discriminator`. static auto SpecializeRow(llvm::ArrayRef row, DiscriminatorInfo discriminator) -> std::optional>; // Specialize the pattern matrix for the case that the first value matched // uses `discriminator`, and its elements are matched. auto Specialize(DiscriminatorInfo discriminator) const -> PatternMatrix; // Specialize the pattern matrix for the case where the first value is known // to be `value`, and is not matched. auto Specialize(const Value& value) const -> PatternMatrix; // Specialize the pattern matrix for the case where the first value uses a // discriminator matching none of the non-wildcard patterns. auto Default() const -> PatternMatrix; // The pattern matrix itself, in row-major order. Each element of `matrix_` // is a distinct sequence of patterns that can be matched against a // corresponding sequence of values. Each such row has the same length and // has elements of the same type. std::vector> matrix_; }; } // namespace Carbon #endif // CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_