pattern_analysis.cpp 10 KB

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