forked from carbon-language/carbon-lang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pattern_analysis.h
162 lines (131 loc) · 6.16 KB
/
pattern_analysis.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 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 <vector>
#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<const Pattern*> pattern) { Set(pattern); }
AbstractPattern(Nonnull<const Value*> value, Nonnull<const Value*> 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<AbstractPattern>& 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<const WildcardTag*>(nullptr)), type_(nullptr) {}
void Set(Nonnull<const Pattern*> 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 Pattern*>, Nonnull<const Value*>,
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<AbstractPattern> 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<AbstractPattern> 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<DiscriminatorInfo> 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<AbstractPattern> 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<AbstractPattern> row,
DiscriminatorInfo discriminator)
-> std::optional<std::vector<AbstractPattern>>;
// 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<std::vector<AbstractPattern>> matrix_;
};
} // namespace Carbon
#endif // CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_