| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // 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_match.h"
- #include "explorer/ast/value.h"
- #include "explorer/base/arena.h"
- #include "explorer/base/trace_stream.h"
- #include "explorer/interpreter/action.h"
- #include "llvm/Support/Casting.h"
- using llvm::cast;
- using llvm::dyn_cast;
- namespace Carbon {
- static auto InitializePlaceholderValue(const ValueNodeView& value_node,
- ExpressionResult v,
- Nonnull<RuntimeScope*> bindings) {
- switch (value_node.expression_category()) {
- case ExpressionCategory::Reference:
- if (v.expression_category() == ExpressionCategory::Value ||
- v.expression_category() == ExpressionCategory::Reference) {
- // Build by copying from value or reference expression.
- bindings->Initialize(value_node, v.value());
- } else {
- // Location initialized by initializing expression, bind node to
- // address.
- CARBON_CHECK(v.address())
- << "Missing location from initializing expression";
- bindings->Bind(value_node, *v.address());
- }
- break;
- case ExpressionCategory::Value:
- if (v.expression_category() == ExpressionCategory::Value) {
- // We assume values are strictly nested for now.
- bindings->BindValue(value_node, v.value());
- } else if (v.expression_category() == ExpressionCategory::Reference) {
- // Bind the reference expression value directly.
- CARBON_CHECK(v.address())
- << "Missing location from reference expression";
- bindings->BindAndPin(value_node, *v.address());
- } else {
- // Location initialized by initializing expression, bind node to
- // address.
- CARBON_CHECK(v.address())
- << "Missing location from initializing expression";
- bindings->Bind(value_node, *v.address());
- }
- break;
- case ExpressionCategory::Initializing:
- CARBON_FATAL() << "Cannot pattern match an initializing expression";
- break;
- }
- }
- auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
- SourceLocation source_loc,
- std::optional<Nonnull<RuntimeScope*>> bindings,
- BindingMap& generic_args, Nonnull<TraceStream*> trace_stream,
- Nonnull<Arena*> arena) -> bool {
- if (trace_stream->is_enabled()) {
- trace_stream->Match() << "match pattern `" << *p << "`\n";
- trace_stream->Indent() << "from "
- << ExpressionCategoryToString(
- v.expression_category())
- << " expression with value `" << *v.value() << "`\n";
- }
- const auto make_expr_result =
- [](Nonnull<const Value*> v) -> ExpressionResult {
- if (const auto* expr_v = dyn_cast<ReferenceExpressionValue>(v)) {
- return ExpressionResult::Reference(expr_v->value(), expr_v->address());
- }
- return ExpressionResult::Value(v);
- };
- if (v.value()->kind() == Value::Kind::ReferenceExpressionValue) {
- return PatternMatch(p, make_expr_result(v.value()), source_loc, bindings,
- generic_args, trace_stream, arena);
- }
- switch (p->kind()) {
- case Value::Kind::BindingPlaceholderValue: {
- CARBON_CHECK(bindings.has_value());
- const auto& placeholder = cast<BindingPlaceholderValue>(*p);
- if (placeholder.value_node().has_value()) {
- InitializePlaceholderValue(*placeholder.value_node(), v, *bindings);
- }
- return true;
- }
- case Value::Kind::AddrValue: {
- const auto& addr = cast<AddrValue>(*p);
- CARBON_CHECK(v.value()->kind() == Value::Kind::LocationValue);
- const auto& location = cast<LocationValue>(*v.value());
- return PatternMatch(
- &addr.pattern(),
- ExpressionResult::Value(arena->New<PointerValue>(location.address())),
- source_loc, bindings, generic_args, trace_stream, arena);
- }
- case Value::Kind::VariableType: {
- const auto& var_type = cast<VariableType>(*p);
- generic_args[&var_type.binding()] = v.value();
- return true;
- }
- case Value::Kind::TupleType:
- case Value::Kind::TupleValue:
- switch (v.value()->kind()) {
- case Value::Kind::TupleType:
- case Value::Kind::TupleValue: {
- const auto& p_tup = cast<TupleValueBase>(*p);
- const auto& v_tup = cast<TupleValueBase>(*v.value());
- CARBON_CHECK(p_tup.elements().size() == v_tup.elements().size());
- for (size_t i = 0; i < p_tup.elements().size(); ++i) {
- if (!PatternMatch(p_tup.elements()[i],
- make_expr_result(v_tup.elements()[i]), source_loc,
- bindings, generic_args, trace_stream, arena)) {
- return false;
- }
- } // for
- return true;
- }
- case Value::Kind::UninitializedValue: {
- const auto& p_tup = cast<TupleValueBase>(*p);
- for (const auto& ele : p_tup.elements()) {
- if (!PatternMatch(ele,
- ExpressionResult::Value(
- arena->New<UninitializedValue>(ele)),
- source_loc, bindings, generic_args, trace_stream,
- arena)) {
- return false;
- }
- }
- return true;
- }
- default:
- CARBON_FATAL() << "expected a tuple value in pattern, not "
- << *v.value();
- }
- case Value::Kind::StructValue: {
- const auto& p_struct = cast<StructValue>(*p);
- const auto& v_struct = cast<StructValue>(*v.value());
- CARBON_CHECK(p_struct.elements().size() == v_struct.elements().size());
- for (size_t i = 0; i < p_struct.elements().size(); ++i) {
- CARBON_CHECK(p_struct.elements()[i].name ==
- v_struct.elements()[i].name);
- if (!PatternMatch(p_struct.elements()[i].value,
- ExpressionResult::Value(v_struct.elements()[i].value),
- source_loc, bindings, generic_args, trace_stream,
- arena)) {
- return false;
- }
- }
- return true;
- }
- case Value::Kind::AlternativeValue:
- switch (v.value()->kind()) {
- case Value::Kind::AlternativeValue: {
- const auto& p_alt = cast<AlternativeValue>(*p);
- const auto& v_alt = cast<AlternativeValue>(*v.value());
- if (&p_alt.alternative() != &v_alt.alternative()) {
- return false;
- }
- CARBON_CHECK(p_alt.argument().has_value() ==
- v_alt.argument().has_value());
- if (!p_alt.argument().has_value()) {
- return true;
- }
- return PatternMatch(
- *p_alt.argument(), ExpressionResult::Value(*v_alt.argument()),
- source_loc, bindings, generic_args, trace_stream, arena);
- }
- default:
- CARBON_FATAL() << "expected a choice alternative in pattern, not "
- << *v.value();
- }
- case Value::Kind::UninitializedValue:
- CARBON_FATAL() << "uninitialized value is not allowed in pattern "
- << *v.value();
- case Value::Kind::FunctionType:
- switch (v.value()->kind()) {
- case Value::Kind::FunctionType: {
- const auto& p_fn = cast<FunctionType>(*p);
- const auto& v_fn = cast<FunctionType>(*v.value());
- if (!PatternMatch(&p_fn.parameters(),
- ExpressionResult::Value(&v_fn.parameters()),
- source_loc, bindings, generic_args, trace_stream,
- arena)) {
- return false;
- }
- if (!PatternMatch(&p_fn.return_type(),
- ExpressionResult::Value(&v_fn.return_type()),
- source_loc, bindings, generic_args, trace_stream,
- arena)) {
- return false;
- }
- return true;
- }
- default:
- return false;
- }
- case Value::Kind::AutoType:
- // `auto` matches any type, without binding any new names. We rely
- // on the typechecker to ensure that `v.value()` is a type.
- return true;
- case Value::Kind::StaticArrayType: {
- switch (v.value()->kind()) {
- case Value::Kind::TupleType:
- case Value::Kind::TupleValue: {
- return true;
- }
- case Value::Kind::StaticArrayType: {
- const auto& v_arr = cast<StaticArrayType>(*v.value());
- return v_arr.has_size();
- }
- default:
- return false;
- }
- }
- default:
- return ValueEqual(p, v.value(), std::nullopt);
- }
- }
- } // namespace Carbon
|