// 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 #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 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 p, ExpressionResult v, SourceLocation source_loc, std::optional> bindings, BindingMap& generic_args, Nonnull trace_stream, Nonnull 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 v) -> ExpressionResult { if (const auto* expr_v = dyn_cast(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(*p); if (placeholder.value_node().has_value()) { InitializePlaceholderValue(*placeholder.value_node(), v, *bindings); } return true; } case Value::Kind::AddrValue: { const auto& addr = cast(*p); CARBON_CHECK(v.value()->kind() == Value::Kind::LocationValue); const auto& location = cast(*v.value()); return PatternMatch( &addr.pattern(), ExpressionResult::Value(arena->New(location.address())), source_loc, bindings, generic_args, trace_stream, arena); } case Value::Kind::VariableType: { const auto& var_type = cast(*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(*p); const auto& v_tup = cast(*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(*p); for (const auto& ele : p_tup.elements()) { if (!PatternMatch(ele, ExpressionResult::Value( arena->New(ele)), source_loc, bindings, generic_args, trace_stream, arena)) { return false; } } return true; } default: CARBON_FATAL("expected a tuple value in pattern, not {0}", *v.value()); } case Value::Kind::StructValue: { const auto& p_struct = cast(*p); const auto& v_struct = cast(*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(*p); const auto& v_alt = cast(*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 {0}", *v.value()); } case Value::Kind::UninitializedValue: CARBON_FATAL("uninitialized value is not allowed in pattern {0}", *v.value()); case Value::Kind::FunctionType: switch (v.value()->kind()) { case Value::Kind::FunctionType: { const auto& p_fn = cast(*p); const auto& v_fn = cast(*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(*p); switch (v.value()->kind()) { case Value::Kind::TupleType: case Value::Kind::TupleValue: { const auto& v_tup = cast(*v.value()); if (v_tup.elements().empty()) { return !TypeIsDeduceable(&p_arr.element_type()); } std::vector> 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(*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