| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- // 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
- #include "explorer/interpreter/pattern_analysis.h"
- #include <set>
- using llvm::cast;
- using llvm::dyn_cast;
- using llvm::isa;
- namespace Carbon {
- auto AbstractPattern::kind() const -> Kind {
- if (isa<const Pattern*>(value_)) {
- return Compound;
- }
- if (const auto* value = value_.dyn_cast<const Value*>()) {
- if (isa<TupleValue, AlternativeValue, BoolValue>(value)) {
- return Compound;
- }
- return Primitive;
- }
- CARBON_CHECK(isa<const WildcardTag*>(value_));
- return Wildcard;
- }
- auto AbstractPattern::discriminator() const -> std::string_view {
- CARBON_CHECK(kind() == Compound);
- if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
- if (const auto* alt_pattern = dyn_cast<AlternativePattern>(pattern)) {
- return alt_pattern->alternative_name();
- }
- } else if (const auto* value = value_.dyn_cast<const Value*>()) {
- if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
- return alt->alternative().name();
- } else if (const auto* bool_val = dyn_cast<BoolValue>(value)) {
- return bool_val->value() ? "true" : "false";
- }
- }
- return {};
- }
- auto AbstractPattern::elements_size() const -> int {
- if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
- if (const auto* tuple_pattern = dyn_cast<TuplePattern>(pattern)) {
- return tuple_pattern->fields().size();
- } else if (isa<AlternativePattern>(pattern)) {
- return 1;
- }
- } else if (const auto* value = value_.dyn_cast<const Value*>()) {
- if (const auto* tuple = dyn_cast<TupleValue>(value)) {
- return tuple->elements().size();
- } else if (isa<AlternativeValue>(value)) {
- return 1;
- }
- }
- return 0;
- }
- void AbstractPattern::AppendElementsTo(
- std::vector<AbstractPattern>& out) const {
- if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
- if (const auto* tuple_pattern = dyn_cast<TuplePattern>(pattern)) {
- auto fields = tuple_pattern->fields();
- out.insert(out.end(), fields.begin(), fields.end());
- } else if (const auto* alt_pattern =
- dyn_cast<AlternativePattern>(pattern)) {
- out.push_back(&alt_pattern->arguments());
- }
- } else if (const auto* value = value_.dyn_cast<const Value*>()) {
- if (const auto* tuple = dyn_cast<TupleValue>(value)) {
- const auto* tuple_type = cast<TupleType>(type_);
- CARBON_CHECK(tuple->elements().size() == tuple_type->elements().size());
- for (size_t i = 0; i != tuple->elements().size(); ++i) {
- out.push_back(
- AbstractPattern(tuple->elements()[i], tuple_type->elements()[i]));
- }
- } else if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
- if (auto arg = alt->argument()) {
- out.push_back(AbstractPattern(
- *arg, *alt->alternative().parameters_static_type()));
- } else {
- // There's no value to match for this alternative, so just insert a
- // wildcard.
- out.push_back(AbstractPattern::MakeWildcard());
- }
- }
- }
- }
- auto AbstractPattern::value() const -> const Value& {
- CARBON_CHECK(kind() == Primitive);
- return *cast<const Value*>(value_);
- }
- auto AbstractPattern::type() const -> const Value& {
- CARBON_CHECK(kind() != Wildcard);
- return *type_;
- }
- void AbstractPattern::Set(Nonnull<const Pattern*> pattern) {
- type_ = &pattern->static_type();
- switch (pattern->kind()) {
- case PatternKind::AddrPattern:
- case PatternKind::AutoPattern:
- case PatternKind::BindingPattern:
- case PatternKind::GenericBinding:
- value_ = static_cast<const WildcardTag*>(nullptr);
- break;
- case PatternKind::TuplePattern:
- case PatternKind::AlternativePattern:
- value_ = pattern;
- break;
- case PatternKind::ExpressionPattern:
- value_ = &pattern->value();
- break;
- case PatternKind::VarPattern:
- Set(&cast<VarPattern>(pattern)->pattern());
- break;
- }
- }
- auto PatternMatrix::IsUseful(llvm::ArrayRef<AbstractPattern> pattern,
- int max_exponential_depth) const -> bool {
- if (matrix_.empty()) {
- return true;
- }
- CARBON_CHECK(pattern.size() == matrix_[0].size());
- if (matrix_[0].empty()) {
- return false;
- }
- switch (pattern[0].kind()) {
- case AbstractPattern::Wildcard: {
- auto discrim = FirstColumnDiscriminators();
- // Check if we hit the depth limit. If so, we act as if the
- // constructors present in this position are not exhaustive, that is,
- // as if the type we're matching has some other constructor not
- // corresponding to anything written in the pattern in this position.
- // This can lead us to conclude that a pattern is useful if it is not,
- // and that a set of patterns is not exhaustive when it is.
- int new_depth =
- max_exponential_depth - (discrim.found.size() > 1 ? 1 : 0);
- if (!discrim.any_missing && new_depth >= 0) {
- for (auto found : discrim.found) {
- if (Specialize(found).IsUseful(*SpecializeRow(pattern, found),
- new_depth)) {
- return true;
- }
- }
- return false;
- }
- return Default().IsUseful(pattern.slice(1), max_exponential_depth);
- }
- case AbstractPattern::Compound: {
- DiscriminatorInfo discrim = {.discriminator = pattern[0].discriminator(),
- .size = pattern[0].elements_size()};
- return Specialize(discrim).IsUseful(*SpecializeRow(pattern, discrim),
- max_exponential_depth);
- }
- case AbstractPattern::Primitive: {
- return Specialize(pattern[0].value())
- .IsUseful(pattern.slice(1), max_exponential_depth);
- }
- }
- }
- auto PatternMatrix::FirstColumnDiscriminators() const -> DiscriminatorSet {
- std::set<std::string_view> discrims;
- std::optional<int> num_discrims;
- std::optional<int> elem_size;
- for (const auto& row : matrix_) {
- CARBON_CHECK(!row.empty());
- switch (row[0].kind()) {
- case AbstractPattern::Wildcard:
- continue;
- case AbstractPattern::Compound: {
- const Value& type = row[0].type();
- if (const auto* tuple = dyn_cast<TupleType>(&type)) {
- // If we find a tuple match, we've found all constructors (there's
- // only one!) and none were missing.
- return {
- .found = {{.discriminator = {},
- .size = static_cast<int>(tuple->elements().size())}},
- .any_missing = false};
- } else if (const auto* choice = dyn_cast<ChoiceType>(&type)) {
- num_discrims = choice->declaration().alternatives().size();
- elem_size = 1;
- } else if (isa<BoolType>(type)) {
- // `bool` behaves like a choice type with two alternatives,
- // and with no nested patterns for either of them.
- num_discrims = 2;
- elem_size = 0;
- } else {
- llvm_unreachable("unexpected compound type");
- }
- discrims.insert(row[0].discriminator());
- break;
- }
- case AbstractPattern::Primitive: {
- // We assume that primitive value matches are always incomplete, even
- // for types like `i8` where a covering match might be possible.
- return {.found = {}, .any_missing = true};
- }
- }
- }
- if (!num_discrims || *num_discrims != static_cast<int>(discrims.size())) {
- return {.found = {}, .any_missing = true};
- }
- DiscriminatorSet result = {.found = {}, .any_missing = false};
- result.found.reserve(discrims.size());
- for (auto s : discrims) {
- result.found.push_back({.discriminator = s, .size = *elem_size});
- }
- return result;
- }
- auto PatternMatrix::SpecializeRow(llvm::ArrayRef<AbstractPattern> row,
- DiscriminatorInfo discriminator)
- -> std::optional<std::vector<AbstractPattern>> {
- CARBON_CHECK(!row.empty());
- std::vector<AbstractPattern> new_row;
- switch (row[0].kind()) {
- case AbstractPattern::Wildcard:
- new_row.reserve(discriminator.size + row.size() - 1);
- new_row.insert(new_row.end(), discriminator.size,
- AbstractPattern::MakeWildcard());
- break;
- case AbstractPattern::Compound: {
- if (row[0].discriminator() != discriminator.discriminator) {
- return std::nullopt;
- }
- CARBON_CHECK(static_cast<int>(row[0].elements_size()) ==
- discriminator.size);
- new_row.reserve(discriminator.size + row.size() - 1);
- row[0].AppendElementsTo(new_row);
- break;
- }
- case AbstractPattern::Primitive:
- // These cases should be rejected by the type checker.
- llvm_unreachable("matched primitive against compound");
- }
- new_row.insert(new_row.end(), row.begin() + 1, row.end());
- return std::move(new_row);
- }
- auto PatternMatrix::Specialize(DiscriminatorInfo discriminator) const
- -> PatternMatrix {
- PatternMatrix specialized;
- for (const auto& row : matrix_) {
- // TODO: If we add support for "or" patterns, specialization might
- // produce multiple rows here.
- if (auto new_row = SpecializeRow(row, discriminator)) {
- specialized.Add(std::move(new_row.value()));
- }
- }
- return specialized;
- }
- // Specialize the pattern matrix for the case where the first value is known
- // to be `value`, and is not matched.
- auto PatternMatrix::Specialize(const Value& value) const -> PatternMatrix {
- PatternMatrix specialized;
- for (const auto& row : matrix_) {
- CARBON_CHECK(!row.empty());
- switch (row[0].kind()) {
- case AbstractPattern::Wildcard:
- break;
- case AbstractPattern::Compound:
- llvm_unreachable("matched compound against primitive");
- case AbstractPattern::Primitive:
- // TODO: Use an equality context here?
- if (!ValueEqual(&row[0].value(), &value, std::nullopt)) {
- continue;
- }
- break;
- }
- specialized.Add(std::vector<AbstractPattern>(row.begin() + 1, row.end()));
- }
- return specialized;
- }
- // Specialize the pattern matrix for the case where the first value uses a
- // discriminator matching none of the non-wildcard patterns.
- auto PatternMatrix::Default() const -> PatternMatrix {
- PatternMatrix default_matrix;
- for (const auto& row : matrix_) {
- CARBON_CHECK(!row.empty());
- switch (row[0].kind()) {
- case AbstractPattern::Wildcard:
- default_matrix.Add(
- std::vector<AbstractPattern>(row.begin() + 1, row.end()));
- break;
- case AbstractPattern::Compound:
- case AbstractPattern::Primitive:
- break;
- }
- }
- return default_matrix;
- }
- } // namespace Carbon
|