summaryrefslogtreecommitdiff
path: root/src/mir/from_hir_match.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir/from_hir_match.cpp')
-rw-r--r--src/mir/from_hir_match.cpp822
1 files changed, 822 insertions, 0 deletions
diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp
new file mode 100644
index 00000000..fa5fc880
--- /dev/null
+++ b/src/mir/from_hir_match.cpp
@@ -0,0 +1,822 @@
+/*
+ * MRustC - Rust Compiler
+ * - By John Hodge (Mutabah/thePowersGang)
+ *
+ * mir/from_hir_match.cpp
+ * - Conversion of `match` blocks into MIR
+ */
+#include "from_hir.hpp"
+#include <hir_typeck/helpers.hpp> // monomorphise_type
+#include <algorithm>
+
+/// Pattern rule
+TAGGED_UNION(PatternRule, Any,
+ (Any, struct {}),
+ (Variant, struct { unsigned int idx; ::std::vector<PatternRule> sub_rules; }),
+ (Value, ::MIR::Constant)
+ );
+
+/// Constructed set of rules from a pattern
+struct PatternRuleset
+{
+ ::std::vector<PatternRule> m_rules;
+
+ static ::Ordering rule_is_before(const PatternRule& l, const PatternRule& r);
+
+ bool is_before(const PatternRuleset& other) const;
+};
+/// Helper to construct rules from a passed pattern
+struct PatternRulesetBuilder
+{
+ ::std::vector<PatternRule> m_rules;
+ void append_from(const Span& sp, const ::HIR::Pattern& pat, const ::HIR::TypeRef& ty);
+
+ PatternRuleset into_ruleset() {
+ return PatternRuleset { mv$(this->m_rules) };
+ }
+};
+
+// ## Create descision tree in-memory based off the ruleset
+// > Tree contains an lvalue and a set of possibilities (PatternRule) connected to another tree or to a branch index
+struct DecisionTreeNode
+{
+ TAGGED_UNION( Branch, Unset,
+ (Unset, struct{}),
+ (Subtree, ::std::unique_ptr<DecisionTreeNode>),
+ (Terminal, unsigned int)
+ );
+
+ TAGGED_UNION( Values, Unset,
+ (Unset, struct {}),
+ (Variant, ::std::vector< ::std::pair<unsigned int, Branch> >)/*,
+ (Unsigned, struct { branchset_t<uint64_t[2]> branches; }),
+ (String, struct { branchset_t< ::std::string> branches; })*/
+ );
+
+ bool is_specialisation;
+ Values m_branches;
+ Branch m_default;
+
+ DecisionTreeNode():
+ is_specialisation(false)
+ {}
+
+ static Branch clone(const Branch& b);
+ static Values clone(const Values& x);
+ DecisionTreeNode clone() const;
+
+ void populate_tree_from_rule(const Span& sp, unsigned int arm_index, const PatternRule* first_rule, unsigned int rule_count) {
+ populate_tree_from_rule(sp, first_rule, rule_count, [arm_index](auto& branch){ branch = Branch::make_Terminal(arm_index); });
+ }
+ // `and_then` - Closure called after processing the final rule
+ void populate_tree_from_rule(const Span& sp, const PatternRule* first_rule, unsigned int rule_count, ::std::function<void(Branch&)> and_then);
+
+ /// Simplifies the tree by eliminating nodes with just a default
+ void simplify();
+ /// Propagate the m_default arm's contents to value arms, and vice-versa
+ void propagate_default();
+ /// HELPER: Unfies the rules from the provided branch with this node
+ void unify_from(const Branch& b);
+
+
+ friend ::std::ostream& operator<<(::std::ostream& os, const Branch& x);
+ friend ::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode& x);
+};
+
+struct DecisionTreeGen
+{
+ MirBuilder& m_builder;
+ const ::std::vector< ::MIR::BasicBlockId>& m_rule_blocks;
+
+ DecisionTreeGen(MirBuilder& builder, const ::std::vector< ::MIR::BasicBlockId >& rule_blocks):
+ m_builder( builder ),
+ m_rule_blocks( rule_blocks )
+ {}
+
+ ::MIR::BasicBlockId get_block_for_rule(unsigned int rule_index) {
+ return m_rule_blocks.at( rule_index );
+ }
+
+ void populate_tree_vals(const Span& sp, const DecisionTreeNode& node, const ::HIR::TypeRef& ty, const ::MIR::LValue& val) {
+ populate_tree_vals(sp, node, ty, 0, val, [](const auto& n){ DEBUG("final node = " << n); });
+ }
+ void populate_tree_vals(
+ const Span& sp,
+ const DecisionTreeNode& node,
+ const ::HIR::TypeRef& ty, unsigned int ty_ofs, const ::MIR::LValue& val,
+ ::std::function<void(const DecisionTreeNode&)> and_then
+ );
+};
+
+// --------------------------------------------------------------------
+// CODE
+// --------------------------------------------------------------------
+
+// Handles lowering non-trivial matches to MIR
+// - Non-trivial means that there's more than one pattern
+void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val )
+{
+ // 1. Build up a sorted vector of MIR pattern rules
+
+ // Map of arm index to ruleset
+ ::std::vector< ::std::pair< ::std::pair<unsigned int,unsigned int>, PatternRuleset > > arm_rules;
+ for(unsigned int arm_idx = 0; arm_idx < node.m_arms.size(); arm_idx ++)
+ {
+ const auto& arm = node.m_arms[arm_idx];
+ unsigned int pat_idx = 0;
+ for( const auto& pat : arm.m_patterns )
+ {
+ auto builder = PatternRulesetBuilder {};
+ builder.append_from(node.span(), pat, node.m_value->m_res_type);
+ arm_rules.push_back( ::std::make_pair( ::std::make_pair(arm_idx, pat_idx), builder.into_ruleset()) );
+ pat_idx += 1;
+ }
+
+ if( arm.m_cond ) {
+ // - TODO: What to do with contidionals?
+ TODO(node.span(), "Handle conditional match arms (ordering matters)");
+ }
+ }
+ // TODO: Detect if a rule is ordering-dependent.
+ // XXX XXX XXX: The current codegen (below) will generate incorrect code if ordering matters.
+ // ```
+ // match ("foo", "bar")
+ // {
+ // (_, "bar") => {}, // Expected
+ // ("foo", _) => {}, // Actual
+ // _ => {},
+ // }
+ // ```
+
+ // Generate arm code
+ DEBUG("- Generating arm code");
+ // - End the current block with a jump to the descision code (TODO: Can this goto be avoided while still being defensive?)
+ auto descision_block = builder.new_bb_unlinked();
+ builder.end_block( ::MIR::Terminator::make_Goto(descision_block) );
+
+ // - Create a result and next block
+ auto result_val = builder.new_temporary(node.m_res_type);
+ auto next_block = builder.new_bb_unlinked();
+
+ // - Generate code for arms.
+ ::std::vector< ::MIR::BasicBlockId> arm_blocks;
+ arm_blocks.reserve( arm_rules.size() );
+ for(auto& arm : node.m_arms)
+ {
+ // TODO: Register introduced bindings to be dropped on return/diverge
+ arm_blocks.push_back( builder.new_bb_unlinked() );
+ builder.set_cur_block(arm_blocks.back());
+
+ conv.visit_node_ptr(arm.m_code);
+
+ if( !builder.block_active() && !builder.has_result() ) {
+ // Nothing need be done, as the block diverged.
+ }
+ else {
+ builder.push_stmt_assign( result_val.clone(), builder.get_result(arm.m_code->span()) );
+ builder.end_block( ::MIR::Terminator::make_Goto(next_block) );
+ }
+ }
+
+ DEBUG("- Generating rule bindings");
+ ::std::vector< ::MIR::BasicBlockId> rule_blocks;
+ for(const auto& rule : arm_rules)
+ {
+ rule_blocks.push_back( builder.new_bb_unlinked() );
+ builder.set_cur_block(arm_blocks.back());
+
+ const auto& arm = node.m_arms[ rule.first.first ];
+ const auto& pat = arm.m_patterns[rule.first.second];
+
+ // Assign bindings (drop registration happens in previous loop) - Allow refutable patterns
+ conv.destructure_from( arm.m_code->span(), pat, match_val.clone(), 1 );
+
+ builder.end_block( ::MIR::Terminator::make_Goto( arm_blocks[rule.first.first] ) );
+ }
+
+
+ // - Build tree by running each arm's pattern across it
+ DEBUG("- Building decision tree");
+ DecisionTreeNode root_node;
+ for( const auto& arm_rule : arm_rules )
+ {
+ auto arm_idx = arm_rule.first.first;
+ root_node.populate_tree_from_rule( node.m_arms[arm_idx].m_code->span(), arm_idx, arm_rule.second.m_rules.data(), arm_rule.second.m_rules.size() );
+ }
+ DEBUG("root_node = " << root_node);
+ root_node.simplify();
+ root_node.propagate_default();
+ DEBUG("root_node = " << root_node);
+
+ // - Convert the above decision tree into MIR
+ DEBUG("- Emitting decision tree");
+ DecisionTreeGen gen { builder, rule_blocks };
+ builder.set_cur_block( descision_block );
+ gen.populate_tree_vals( node.span(), root_node, node.m_value->m_res_type, mv$(match_val) );
+
+ builder.set_cur_block(next_block);
+ builder.set_result( node.span(), mv$(result_val) );
+}
+
+::Ordering PatternRuleset::rule_is_before(const PatternRule& l, const PatternRule& r)
+{
+ if( l.tag() != r.tag() ) {
+ // Any comes last, don't care about rest
+ if( l.tag() < r.tag() )
+ return ::OrdGreater;
+ else
+ return ::OrdLess;
+ }
+
+ TU_MATCHA( (l,r), (le,re),
+ (Any,
+ return ::OrdEqual;
+ ),
+ (Variant,
+ if( le.idx != re.idx )
+ return ::ord(le.idx, re.idx);
+ assert( le.sub_rules.size() == re.sub_rules.size() );
+ for(unsigned int i = 0; i < le.sub_rules.size(); i ++)
+ {
+ auto cmp = rule_is_before(le.sub_rules[i], re.sub_rules[i]);
+ if( cmp != ::OrdEqual )
+ return cmp;
+ }
+ return ::OrdEqual;
+ ),
+ (Value,
+ TODO(Span(), "Order PatternRule::Value");
+ )
+ )
+ throw "";
+}
+
+bool PatternRuleset::is_before(const PatternRuleset& other) const
+{
+ assert( m_rules.size() == other.m_rules.size() );
+ for(unsigned int i = 0; i < m_rules.size(); i ++)
+ {
+ const auto& l = m_rules[i];
+ const auto& r = other.m_rules[i];
+ auto cmp = rule_is_before(l, r);
+ if( cmp != ::OrdEqual )
+ return cmp == ::OrdLess;
+ }
+ return false;
+}
+
+
+void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pat, const ::HIR::TypeRef& ty)
+{
+ TU_MATCHA( (ty.m_data), (e),
+ (Infer, BUG(sp, "Ivar for in match type"); ),
+ (Diverge, BUG(sp, "Diverge in match type"); ),
+ (Primitive,
+ TU_MATCH_DEF(::HIR::Pattern::Data, (pat.m_data), (pe),
+ ( throw ""; ),
+ (Any,
+ m_rules.push_back( PatternRule::make_Any({}) );
+ ),
+ (Value,
+ switch(e)
+ {
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64:
+ TODO(sp, "Match value float");
+ break;
+ case ::HIR::CoreType::U8:
+ case ::HIR::CoreType::U16:
+ case ::HIR::CoreType::U32:
+ case ::HIR::CoreType::U64:
+ case ::HIR::CoreType::Usize:
+ TODO(sp, "Match value unsigned");
+ break;
+ case ::HIR::CoreType::I8:
+ case ::HIR::CoreType::I16:
+ case ::HIR::CoreType::I32:
+ case ::HIR::CoreType::I64:
+ case ::HIR::CoreType::Isize:
+ TODO(sp, "Match value signed");
+ break;
+ case ::HIR::CoreType::Bool:
+ TODO(sp, "Match value bool");
+ break;
+ case ::HIR::CoreType::Char:
+ TODO(sp, "Match value char");
+ break;
+ case ::HIR::CoreType::Str:
+ BUG(sp, "Hit match over `str` - must be `&str`");
+ break;
+ }
+ )
+ )
+ ),
+ (Tuple,
+ TU_MATCH_DEF(::HIR::Pattern::Data, (pat.m_data), (pe),
+ ( throw ""; ),
+ (Any,
+ for(const auto& sty : e)
+ this->append_from(sp, pat, sty);
+ ),
+ (Tuple,
+ assert(e.size() == pe.sub_patterns.size());
+ for(unsigned int i = 0; i < e.size(); i ++)
+ this->append_from(sp, pe.sub_patterns[i], e[i]);
+ )
+ )
+ ),
+ (Path,
+ // This is either a struct destructure or an enum
+ TU_MATCHA( (e.binding), (pbe),
+ (Unbound,
+ BUG(sp, "Encounterd unbound path - " << e.path);
+ ),
+ (Opaque,
+ TU_MATCH_DEF( ::HIR::Pattern::Data, (pat.m_data), (pe),
+ ( throw ""; ),
+ (Any,
+ m_rules.push_back( PatternRule::make_Any({}) );
+ )
+ )
+ ),
+ (Struct,
+ TODO(sp, "Match over struct - " << e.path);
+ ),
+ (Enum,
+ TU_MATCH_DEF( ::HIR::Pattern::Data, (pat.m_data), (pe),
+ ( BUG(sp, "Match not allowed, " << ty << " with " << pat); ),
+ (Any,
+ m_rules.push_back( PatternRule::make_Any({}) );
+ ),
+ (EnumValue,
+ m_rules.push_back( PatternRule::make_Variant( {pe.binding_idx, {} } ) );
+ ),
+ (EnumTuple,
+ const auto& fields_def = pe.binding_ptr->m_variants[pe.binding_idx].second.as_Tuple();
+ PatternRulesetBuilder sub_builder;
+ for( unsigned int i = 0; i < pe.sub_patterns.size(); i ++ )
+ {
+ const auto& subpat = pe.sub_patterns[i];
+ auto subty = monomorphise_type(sp, pe.binding_ptr->m_params, e.path.m_data.as_Generic().m_params, fields_def[i].ent);
+ sub_builder.append_from( sp, subpat, subty );
+ }
+ m_rules.push_back( PatternRule::make_Variant({ pe.binding_idx, mv$(sub_builder.m_rules) }) );
+ ),
+ (EnumTupleWildcard,
+ m_rules.push_back( PatternRule::make_Variant({ pe.binding_idx, {} }) );
+ ),
+ (EnumStruct,
+ PatternRulesetBuilder sub_builder;
+ TODO(sp, "Convert EnumStruct patterns");
+ m_rules.push_back( PatternRule::make_Variant({ pe.binding_idx, mv$(sub_builder.m_rules) }) );
+ )
+ )
+ )
+ )
+ ),
+ (Generic,
+ // Generics don't destructure, so the only valid pattern is `_`
+ TU_MATCH_DEF( ::HIR::Pattern::Data, (pat.m_data), (pe),
+ ( BUG(sp, "Match not allowed, " << ty << " with " << pat); ),
+ (Any,
+ m_rules.push_back( PatternRule::make_Any({}) );
+ )
+ )
+ ),
+ (TraitObject,
+ ERROR(sp, E0000, "Attempting to match over a trait object");
+ ),
+ (Array,
+ // TODO: Slice patterns, sequential comparison/sub-match
+ TODO(sp, "Match over array");
+ ),
+ (Slice,
+ BUG(sp, "Hit match over `[T]` - must be `&[T]`");
+ ),
+ (Borrow,
+ TU_MATCH_DEF( ::HIR::Pattern::Data, (pat.m_data), (pe),
+ ( throw ""; ),
+ (Any,
+ this->append_from( sp, pat, *e.inner );
+ ),
+ (Ref,
+ this->append_from( sp, *pe.sub, *e.inner );
+ )
+ )
+ ),
+ (Pointer,
+ ERROR(sp, E0000, "Attempting to match over a pointer");
+ ),
+ (Function,
+ ERROR(sp, E0000, "Attempting to match over a functon pointer");
+ ),
+ (Closure,
+ ERROR(sp, E0000, "Attempting to match over a closure");
+ )
+ )
+}
+
+
+// ----------------------------
+// DecisionTreeNode
+// ----------------------------
+DecisionTreeNode::Branch DecisionTreeNode::clone(const DecisionTreeNode::Branch& b) {
+ TU_MATCHA( (b), (e),
+ (Unset, return Branch(e); ),
+ (Subtree, return Branch(box$( e->clone() )); ),
+ (Terminal, return Branch(e); )
+ )
+ throw "";
+}
+DecisionTreeNode::Values DecisionTreeNode::clone(const DecisionTreeNode::Values& x) {
+ TU_MATCHA( (x), (e),
+ (Unset, return Values(e); ),
+ (Variant,
+ Values::Data_Variant rv;
+ rv.reserve(e.size());
+ for(const auto& v : e)
+ rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
+ return Values( mv$(rv) );
+ )
+ )
+ throw "";
+}
+DecisionTreeNode DecisionTreeNode::clone() const {
+ DecisionTreeNode rv;
+ rv.is_specialisation = is_specialisation;
+ rv.m_branches = clone(m_branches);
+ rv.m_default = clone(m_default);
+ return rv;
+}
+void DecisionTreeNode::populate_tree_from_rule(const Span& sp, const PatternRule* first_rule, unsigned int rule_count, ::std::function<void(Branch&)> and_then)
+{
+ assert( rule_count > 0 );
+ const auto& rule = *first_rule;
+
+ TU_MATCHA( (rule), (e),
+ (Any, {
+ if( m_default.is_Unset() ) {
+ if( rule_count == 1 ) {
+ and_then(m_default);
+ }
+ else {
+ auto be = box$(DecisionTreeNode());
+ be->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ m_default = Branch(mv$(be));
+ }
+ }
+ else TU_IFLET( Branch, m_default, Subtree, be,
+ assert( be );
+ assert(rule_count > 1);
+ be->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ )
+ else {
+ // NOTE: All lists processed as part of the same tree should be the same length
+ BUG(sp, "Duplicate terminal rule");
+ }
+ }),
+ (Variant, {
+ if( m_branches.is_Unset() ) {
+ m_branches = Values::make_Variant({});
+ }
+ if( !m_branches.is_Variant() ) {
+ BUG(sp, "Mismatched rules");
+ }
+ auto& be = m_branches.as_Variant();
+ auto it = ::std::find_if( be.begin(), be.end(), [&](const auto& x){ return x.first >= e.idx; });
+ // If this variant isn't yet processed, add a new subtree for it
+ if( it == be.end() || it->first != e.idx ) {
+ it = be.insert(it, ::std::make_pair(e.idx, Branch( box$(DecisionTreeNode()) )));
+ assert( it->second.is_Subtree() );
+ }
+ else {
+ if( it->second.is_Terminal() ) {
+ BUG(sp, "Duplicate terminal rule - " << it->second.as_Terminal());
+ }
+ assert( !it->second.is_Unset() );
+ assert( it->second.is_Subtree() );
+ }
+ auto& subtree = *it->second.as_Subtree();
+
+ if( e.sub_rules.size() > 0 && rule_count > 1 )
+ {
+ subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), [&](auto& branch){
+ ASSERT_BUG(sp, branch.is_Unset(), "Duplicate terminator");
+ branch = Branch( box$(DecisionTreeNode()) );
+ branch.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ });
+ }
+ else if( e.sub_rules.size() > 0)
+ {
+ subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), and_then);
+ }
+ else if( rule_count > 1 )
+ {
+ subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ }
+ else
+ {
+ and_then(it->second);
+ }
+ }),
+ (Value,
+ TODO(sp, "Value patterns");
+ )
+ )
+}
+
+void DecisionTreeNode::simplify()
+{
+ struct H {
+ static void simplify_branch(Branch& b)
+ {
+ TU_IFLET(Branch, b, Subtree, be,
+ be->simplify();
+ if( be->m_branches.is_Unset() ) {
+ auto v = mv$( be->m_default );
+ b = mv$(v);
+ }
+ )
+ }
+ };
+
+ TU_MATCHA( (m_branches), (e),
+ (Unset,
+ ),
+ (Variant,
+ for(auto& branch : e) {
+ H::simplify_branch(branch.second);
+ }
+ )
+ )
+
+ H::simplify_branch(m_default);
+}
+
+void DecisionTreeNode::propagate_default()
+{
+ TU_MATCHA( (m_branches), (e),
+ (Unset,
+ ),
+ (Variant,
+ for(auto& branch : e) {
+ TU_IFLET(Branch, branch.second, Subtree, be,
+ be->propagate_default();
+ if( be->m_default.is_Unset() ) {
+ be->unify_from(m_default);
+ }
+ )
+ }
+ )
+ )
+ TU_IFLET(Branch, m_default, Subtree, be,
+ be->propagate_default();
+
+ if( be->m_default.is_Unset() ) {
+ // TODO: Propagate default from value branches
+ TU_MATCHA( (m_branches), (e),
+ (Unset,
+ ),
+ (Variant,
+ for(auto& branch : e) {
+ be->unify_from(branch.second);
+ }
+ )
+ )
+ }
+ )
+}
+void DecisionTreeNode::unify_from(const Branch& b)
+{
+ TU_MATCHA( (b), (be),
+ (Unset,
+ ),
+ (Terminal,
+ if( m_default.is_Unset() ) {
+ m_default = Branch(be);
+ }
+ ),
+ (Subtree,
+ assert( be->m_branches.tag() == m_branches.tag() );
+ TU_MATCHA( (be->m_branches, m_branches), (src, dst),
+ (Unset,
+ ),
+ (Variant,
+ // Insert items not already present
+ for(const auto& srcv : src)
+ {
+ auto it = ::std::find_if( dst.begin(), dst.end(), [&](const auto& x){ return x.first >= srcv.first; });
+ // Not found? Insert a new branch
+ if( it == dst.end() || it->first != srcv.first ) {
+ it = dst.insert(it, ::std::make_pair(srcv.first, clone(srcv.second)));
+ }
+ }
+ )
+ )
+ if( m_default.is_Unset() ) {
+ m_default = clone(be->m_default);
+ }
+ )
+ )
+}
+
+::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode::Branch& x) {
+ TU_MATCHA( (x), (be),
+ (Unset,
+ os << "!";
+ ),
+ (Terminal,
+ os << "ARM " << be;
+ ),
+ (Subtree,
+ os << *be;
+ )
+ )
+ return os;
+}
+::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode& x) {
+ os << "DTN { ";
+ TU_MATCHA( (x.m_branches), (e),
+ (Unset,
+ os << "!, ";
+ ),
+ (Variant,
+ for(const auto& branch : e) {
+ os << branch.first << " = " << branch.second << ", ";
+ }
+ )
+ )
+
+ os << "* = " << x.m_default;
+ os << " }";
+ return os;
+}
+
+
+// ----------------------------
+// DecisionTreeGen
+// ----------------------------
+void DecisionTreeGen::populate_tree_vals(
+ const Span& sp,
+ const DecisionTreeNode& node,
+ const ::HIR::TypeRef& ty, unsigned int ty_ofs, const ::MIR::LValue& val,
+ ::std::function<void(const DecisionTreeNode&)> and_then
+ )
+{
+ TRACE_FUNCTION_F("ty=" << ty << ", ty_ofs=" << ty_ofs << ", node=" << node);
+
+ TU_MATCHA( (ty.m_data), (e),
+ (Infer, BUG(sp, "Ivar for in match type"); ),
+ (Diverge, BUG(sp, "Diverge in match type"); ),
+ (Primitive,
+ TODO(sp, "Primitive");
+ ),
+ (Tuple,
+ // Tuple - Recurse on each sub-type (increasing the index)
+ if( ty_ofs == e.size() ) {
+ and_then(node);
+ }
+ else {
+ populate_tree_vals( sp, node,
+ e[ty_ofs], 0, ::MIR::LValue::make_Field({ box$(val.clone()), ty_ofs}),
+ [&](auto& n){ this->populate_tree_vals(sp, n, ty, ty_ofs+1, val, and_then); }
+ );
+ }
+ ),
+ (Path,
+ // This is either a struct destructure or an enum
+ TU_MATCHA( (e.binding), (pbe),
+ (Unbound,
+ BUG(sp, "Encounterd unbound path - " << e.path);
+ ),
+ (Opaque,
+ and_then(node);
+ ),
+ (Struct,
+ TODO(sp, "Match over struct - " << e.path);
+ ),
+ (Enum,
+ const auto& enum_path = e.path.m_data.as_Generic();
+ ASSERT_BUG(sp, node.m_branches.is_Variant(), "Tree for enum isn't a Variant - node="<<node);
+ const auto& branches = node.m_branches.as_Variant();
+ const auto& variants = pbe->m_variants;
+ auto variant_count = pbe->m_variants.size();
+ bool has_any = ! node.m_default.is_Unset();
+
+ if( branches.size() < variant_count && ! has_any ) {
+ ERROR(sp, E0000, "Non-exhaustive match");
+ }
+ // DISABLED: Some complex matches don't directly use some defaults
+ //if( branches.size() == variant_count && has_any ) {
+ // ERROR(sp, E0000, "Unreachable _ arm - " << branches.size() << " variants in " << enum_path);
+ //}
+
+ auto any_block = (has_any ? m_builder.new_bb_unlinked() : 0);
+
+ // Emit a switch over the variant
+ ::std::vector< ::MIR::BasicBlockId> variant_blocks;
+ variant_blocks.reserve( variant_count );
+ for( const auto& branch : branches )
+ {
+ if( variant_blocks.size() != branch.first ) {
+ assert( variant_blocks.size() < branch.first );
+ assert( has_any );
+ variant_blocks.resize( branch.first, any_block );
+ }
+ variant_blocks.push_back( m_builder.new_bb_unlinked() );
+ }
+ if( variant_blocks.size() != variant_count )
+ {
+ assert( variant_blocks.size() < variant_count );
+ assert( has_any );
+ variant_blocks.resize( variant_count, any_block );
+ }
+
+ m_builder.end_block( ::MIR::Terminator::make_Switch({
+ val.clone(), variant_blocks // NOTE: Copies the list, so it can be used lower down
+ }) );
+
+ // Emit sub-patterns, looping over variants
+ for( const auto& branch : branches )
+ {
+ auto bb = variant_blocks[branch.first];
+ const auto& var = variants[branch.first];
+ DEBUG(branch.first << " " << var.first << " = " << branch);
+
+ m_builder.set_cur_block(bb);
+ if( branch.second.is_Terminal() ) {
+ m_builder.end_block( ::MIR::Terminator::make_Goto( this->get_block_for_rule( branch.second.as_Terminal() ) ) );
+ }
+ else {
+ assert( branch.second.is_Subtree() );
+ const auto& subnode = *branch.second.as_Subtree();
+
+ TU_MATCHA( (var.second), (e),
+ (Unit,
+ and_then( subnode );
+ ),
+ (Value,
+ and_then( subnode );
+ ),
+ (Tuple,
+ // Make a fake tuple
+ ::std::vector< ::HIR::TypeRef> ents;
+ for( const auto& fld : e )
+ {
+ ents.push_back( monomorphise_type(sp, pbe->m_params, enum_path.m_params, fld.ent) );
+ }
+ ::HIR::TypeRef fake_ty { mv$(ents) };
+ populate_tree_vals(sp, subnode, fake_ty, 0, ::MIR::LValue::make_Downcast({ box$(val.clone()), branch.first }), and_then);
+ ),
+ (Struct,
+ TODO(sp, "Enum pattern - struct");
+ )
+ )
+ }
+ }
+
+ DEBUG("_");
+ TU_MATCHA( (node.m_default), (be),
+ (Unset, ),
+ (Terminal,
+ m_builder.set_cur_block(any_block);
+ m_builder.end_block( ::MIR::Terminator::make_Goto( this->get_block_for_rule( be ) ) );
+ ),
+ (Subtree,
+ m_builder.set_cur_block(any_block);
+ and_then( *be );
+ )
+ )
+ )
+ )
+ ),
+ (Generic,
+ and_then(node);
+ ),
+ (TraitObject,
+ ERROR(sp, E0000, "Attempting to match over a trait object");
+ ),
+ (Array,
+ // TODO: Slice patterns, sequential comparison/sub-match
+ TODO(sp, "Match over array");
+ ),
+ (Slice,
+ BUG(sp, "Hit match over `[T]` - must be `&[T]`");
+ ),
+ (Borrow,
+ populate_tree_vals( sp, node,
+ *e.inner, 0, ::MIR::LValue::make_Deref({ box$(val.clone()) }),
+ and_then
+ );
+ ),
+ (Pointer,
+ ERROR(sp, E0000, "Attempting to match over a pointer");
+ ),
+ (Function,
+ ERROR(sp, E0000, "Attempting to match over a functon pointer");
+ ),
+ (Closure,
+ ERROR(sp, E0000, "Attempting to match over a closure");
+ )
+ )
+}