pattern_analysis.cpp 10 KB

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