pattern_analysis.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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. #include "explorer/interpreter/pattern_analysis.h"
  5. #include <set>
  6. using llvm::cast;
  7. using llvm::dyn_cast;
  8. using llvm::isa;
  9. namespace Carbon {
  10. auto AbstractPattern::kind() const -> Kind {
  11. if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
  12. return Compound;
  13. }
  14. if (const auto* value = value_.dyn_cast<const Value*>()) {
  15. if (isa<TupleValue, AlternativeValue, BoolValue>(value)) {
  16. return Compound;
  17. }
  18. return Primitive;
  19. }
  20. CARBON_CHECK(value_.is<const WildcardTag*>());
  21. return Wildcard;
  22. }
  23. auto AbstractPattern::discriminator() const -> std::string_view {
  24. CARBON_CHECK(kind() == Compound);
  25. if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
  26. if (const auto* alt_pattern = dyn_cast<AlternativePattern>(pattern)) {
  27. return alt_pattern->alternative_name();
  28. }
  29. } else if (const auto* value = value_.dyn_cast<const Value*>()) {
  30. if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
  31. return alt->alternative().name();
  32. } else if (const auto* bool_val = dyn_cast<BoolValue>(value)) {
  33. return bool_val->value() ? "true" : "false";
  34. }
  35. }
  36. return {};
  37. }
  38. auto AbstractPattern::elements_size() const -> int {
  39. if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
  40. if (const auto* tuple_pattern = dyn_cast<TuplePattern>(pattern)) {
  41. return tuple_pattern->fields().size();
  42. } else if (isa<AlternativePattern>(pattern)) {
  43. // Note, AlternativePattern is only used for a pattern with arguments. An
  44. // alternative pattern without arguments is represented as an
  45. // AlternativeValue.
  46. return 1;
  47. }
  48. } else if (const auto* value = value_.dyn_cast<const Value*>()) {
  49. if (const auto* tuple = dyn_cast<TupleValue>(value)) {
  50. return tuple->elements().size();
  51. } else if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
  52. return alt->argument() ? 1 : 0;
  53. }
  54. }
  55. return 0;
  56. }
  57. void AbstractPattern::AppendElementsTo(
  58. std::vector<AbstractPattern>& out) const {
  59. if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
  60. if (const auto* tuple_pattern = dyn_cast<TuplePattern>(pattern)) {
  61. auto fields = tuple_pattern->fields();
  62. out.insert(out.end(), fields.begin(), fields.end());
  63. } else if (const auto* alt_pattern =
  64. dyn_cast<AlternativePattern>(pattern)) {
  65. out.push_back(&alt_pattern->arguments());
  66. }
  67. } else if (const auto* value = value_.dyn_cast<const Value*>()) {
  68. if (const auto* tuple = dyn_cast<TupleValue>(value)) {
  69. const auto* tuple_type = cast<TupleType>(type_);
  70. CARBON_CHECK(tuple->elements().size() == tuple_type->elements().size());
  71. for (size_t i = 0; i != tuple->elements().size(); ++i) {
  72. out.push_back(
  73. AbstractPattern(tuple->elements()[i], tuple_type->elements()[i]));
  74. }
  75. } else if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
  76. if (auto arg = alt->argument()) {
  77. out.push_back(AbstractPattern(
  78. *arg, *alt->alternative().parameters_static_type()));
  79. }
  80. }
  81. }
  82. }
  83. auto AbstractPattern::value() const -> const Value& {
  84. CARBON_CHECK(kind() == Primitive);
  85. return *value_.get<const Value*>();
  86. }
  87. auto AbstractPattern::type() const -> const Value& {
  88. CARBON_CHECK(kind() != Wildcard);
  89. return *type_;
  90. }
  91. void AbstractPattern::Set(Nonnull<const Pattern*> pattern) {
  92. type_ = &pattern->static_type();
  93. switch (pattern->kind()) {
  94. case PatternKind::AddrPattern:
  95. case PatternKind::AutoPattern:
  96. case PatternKind::BindingPattern:
  97. case PatternKind::GenericBinding:
  98. value_ = static_cast<const WildcardTag*>(nullptr);
  99. break;
  100. case PatternKind::TuplePattern:
  101. case PatternKind::AlternativePattern:
  102. value_ = pattern;
  103. break;
  104. case PatternKind::ExpressionPattern:
  105. value_ = &pattern->value();
  106. break;
  107. case PatternKind::VarPattern:
  108. Set(&cast<VarPattern>(pattern)->pattern());
  109. break;
  110. }
  111. }
  112. auto PatternMatrix::IsUseful(llvm::ArrayRef<AbstractPattern> pattern,
  113. int max_exponential_depth) const -> bool {
  114. if (matrix_.empty()) {
  115. return true;
  116. }
  117. CARBON_CHECK(pattern.size() == matrix_[0].size());
  118. if (matrix_[0].empty()) {
  119. return false;
  120. }
  121. switch (pattern[0].kind()) {
  122. case AbstractPattern::Wildcard: {
  123. auto discrim = FirstColumnDiscriminators();
  124. // Check if we hit the depth limit. If so, we act as if the
  125. // constructors present in this position are not exhaustive, that is,
  126. // as if the type we're matching has some other constructor not
  127. // corresponding to anything written in the pattern in this position.
  128. // This can lead us to conclude that a pattern is useful if it is not,
  129. // and that a set of patterns is not exhaustive when it is.
  130. int new_depth =
  131. max_exponential_depth - (discrim.found.size() > 1 ? 1 : 0);
  132. if (!discrim.any_missing && new_depth >= 0) {
  133. for (auto found : discrim.found) {
  134. if (Specialize(found).IsUseful(*SpecializeRow(pattern, found),
  135. new_depth)) {
  136. return true;
  137. }
  138. }
  139. return false;
  140. }
  141. return Default().IsUseful(pattern.slice(1), max_exponential_depth);
  142. }
  143. case AbstractPattern::Compound: {
  144. DiscriminatorInfo discrim = {.discriminator = pattern[0].discriminator(),
  145. .size = pattern[0].elements_size()};
  146. return Specialize(discrim).IsUseful(*SpecializeRow(pattern, discrim),
  147. max_exponential_depth);
  148. }
  149. case AbstractPattern::Primitive: {
  150. return Specialize(pattern[0].value())
  151. .IsUseful(pattern.slice(1), max_exponential_depth);
  152. }
  153. }
  154. }
  155. auto PatternMatrix::FirstColumnDiscriminators() const -> DiscriminatorSet {
  156. std::set<std::string_view> discrims;
  157. std::optional<int> num_discrims;
  158. std::optional<int> elem_size;
  159. for (const auto& row : matrix_) {
  160. CARBON_CHECK(!row.empty());
  161. switch (row[0].kind()) {
  162. case AbstractPattern::Wildcard:
  163. continue;
  164. case AbstractPattern::Compound: {
  165. const Value& type = row[0].type();
  166. if (const auto* tuple = dyn_cast<TupleType>(&type)) {
  167. // If we find a tuple match, we've found all constructors (there's
  168. // only one!) and none were missing.
  169. return {
  170. .found = {{.discriminator = {},
  171. .size = static_cast<int>(tuple->elements().size())}},
  172. .any_missing = false};
  173. } else if (const auto* choice = dyn_cast<ChoiceType>(&type)) {
  174. num_discrims = choice->declaration().alternatives().size();
  175. elem_size = 1;
  176. } else if (isa<BoolType>(type)) {
  177. // `bool` behaves like a choice type with two alternativs,
  178. // and with no nested patterns for either of them.
  179. num_discrims = 2;
  180. elem_size = 0;
  181. } else {
  182. llvm_unreachable("unexpected compound type");
  183. }
  184. discrims.insert(row[0].discriminator());
  185. break;
  186. }
  187. case AbstractPattern::Primitive: {
  188. // We assume that primitive value matches are always incomplete, even
  189. // for types like `i8` where a covering match might be possible.
  190. return {.found = {}, .any_missing = true};
  191. }
  192. }
  193. }
  194. if (!num_discrims || *num_discrims != static_cast<int>(discrims.size())) {
  195. return {.found = {}, .any_missing = true};
  196. }
  197. DiscriminatorSet result = {.found = {}, .any_missing = false};
  198. result.found.reserve(discrims.size());
  199. for (auto s : discrims) {
  200. result.found.push_back({.discriminator = s, .size = *elem_size});
  201. }
  202. return result;
  203. }
  204. auto PatternMatrix::SpecializeRow(llvm::ArrayRef<AbstractPattern> row,
  205. DiscriminatorInfo discriminator)
  206. -> std::optional<std::vector<AbstractPattern>> {
  207. CARBON_CHECK(!row.empty());
  208. std::vector<AbstractPattern> new_row;
  209. switch (row[0].kind()) {
  210. case AbstractPattern::Wildcard:
  211. new_row.reserve(discriminator.size + row.size() - 1);
  212. new_row.insert(new_row.end(), discriminator.size,
  213. AbstractPattern::MakeWildcard());
  214. break;
  215. case AbstractPattern::Compound: {
  216. if (row[0].discriminator() != discriminator.discriminator) {
  217. return std::nullopt;
  218. }
  219. CARBON_CHECK(static_cast<int>(row[0].elements_size()) ==
  220. discriminator.size);
  221. new_row.reserve(discriminator.size + row.size() - 1);
  222. row[0].AppendElementsTo(new_row);
  223. break;
  224. }
  225. case AbstractPattern::Primitive:
  226. // These cases should be rejected by the type checker.
  227. llvm_unreachable("matched primitive against compound");
  228. }
  229. new_row.insert(new_row.end(), row.begin() + 1, row.end());
  230. return std::move(new_row);
  231. }
  232. auto PatternMatrix::Specialize(DiscriminatorInfo discriminator) const
  233. -> PatternMatrix {
  234. PatternMatrix specialized;
  235. for (const auto& row : matrix_) {
  236. // TODO: If we add support for "or" patterns, specialization might
  237. // produce multiple rows here.
  238. if (auto new_row = SpecializeRow(row, discriminator)) {
  239. specialized.Add(std::move(new_row.value()));
  240. }
  241. }
  242. return specialized;
  243. }
  244. // Specialize the pattern matrix for the case where the first value is known
  245. // to be `value`, and is not matched.
  246. auto PatternMatrix::Specialize(const Value& value) const -> PatternMatrix {
  247. PatternMatrix specialized;
  248. for (const auto& row : matrix_) {
  249. CARBON_CHECK(!row.empty());
  250. switch (row[0].kind()) {
  251. case AbstractPattern::Wildcard:
  252. break;
  253. case AbstractPattern::Compound:
  254. llvm_unreachable("matched compound against primitive");
  255. case AbstractPattern::Primitive:
  256. // TODO: Use an equality context here?
  257. if (!ValueEqual(&row[0].value(), &value, std::nullopt)) {
  258. continue;
  259. }
  260. break;
  261. }
  262. specialized.Add(std::vector<AbstractPattern>(row.begin() + 1, row.end()));
  263. }
  264. return specialized;
  265. }
  266. // Specialize the pattern matrix for the case where the first value uses a
  267. // discriminator matching none of the non-wildcard patterns.
  268. auto PatternMatrix::Default() const -> PatternMatrix {
  269. PatternMatrix default_matrix;
  270. for (const auto& row : matrix_) {
  271. CARBON_CHECK(!row.empty());
  272. switch (row[0].kind()) {
  273. case AbstractPattern::Wildcard:
  274. default_matrix.Add(
  275. std::vector<AbstractPattern>(row.begin() + 1, row.end()));
  276. break;
  277. case AbstractPattern::Compound:
  278. case AbstractPattern::Primitive:
  279. break;
  280. }
  281. }
  282. return default_matrix;
  283. }
  284. } // namespace Carbon