Files
carbon-lang/explorer/interpreter/pattern_match.cpp
T
kshokhin d34b3e02c2 Add type_utils file (#2926)
Prerequisite for #2909. Moves some type-related functions to the
separate file.
2023-08-07 16:22:19 +00:00

221 lines
8.7 KiB
C++

// 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/common/arena.h"
#include "explorer/common/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 pattern " << *p << "\nfrom "
<< 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