Files
carbon-lang/explorer/interpreter/pattern_match.cpp
T
kshokhin b6d660b4d9 Add nested array size deduction from tuple (#2909)
As a follow up to #2825 this pr implements size deduction for nested
arrays.
E.g. `var x: [[i32;];] = ((1,2), (3,4));`.

Also updated the pattern matching logic for arrays, now it also checks
element types of the tuple size being deduced from. As a result code
like `var x: [i32;] = ("foo", "bar");`(note, that it tries to init an
array of i32 with a tuple of strings) fails to compile with a pattern
match error instead of an implicit cast failed error.

Moved some common type-related logic used in type_checker.cpp and
interpreter.cpp to the separate file.
2023-11-20 20:20:21 +00:00

253 lines
10 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 <algorithm>
#include "explorer/ast/value.h"
#include "explorer/base/arena.h"
#include "explorer/base/trace_stream.h"
#include "explorer/interpreter/action.h"
#include "explorer/interpreter/type_utils.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: {
const auto& p_arr = cast<StaticArrayType>(*p);
switch (v.value()->kind()) {
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
const auto& v_tup = cast<TupleValueBase>(*v.value());
if (v_tup.elements().empty()) {
return !TypeIsDeduceable(&p_arr.element_type());
}
std::vector<Nonnull<const Value*>> deduced_types;
deduced_types.reserve(v_tup.elements().size());
for (const auto& tup_elem : v_tup.elements()) {
if (!PatternMatch(&p_arr.element_type(), make_expr_result(tup_elem),
source_loc, bindings, generic_args, trace_stream,
arena)) {
return false;
}
deduced_types.emplace_back(
DeducePatternType(&p_arr.element_type(), tup_elem, arena));
} // for
return std::adjacent_find(deduced_types.begin(), deduced_types.end(),
[](const auto& lhs, const auto& rhs) {
return !TypeEqual(lhs, rhs, std::nullopt);
}) == deduced_types.end();
}
case Value::Kind::StaticArrayType: {
const auto& v_arr = cast<StaticArrayType>(*v.value());
if (!v_arr.has_size()) {
return false;
}
return PatternMatch(
&p_arr.element_type(), make_expr_result(&v_arr.element_type()),
source_loc, bindings, generic_args, trace_stream, arena);
}
default:
return false;
}
}
default:
return ValueEqual(p, v.value(), std::nullopt);
}
}
} // namespace Carbon