pattern_analysis.cpp 11 KB

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