summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mir/from_hir_match.cpp325
1 files changed, 257 insertions, 68 deletions
diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp
index 750df91e..66625753 100644
--- a/src/mir/from_hir_match.cpp
+++ b/src/mir/from_hir_match.cpp
@@ -660,6 +660,7 @@ struct DecisionTreeNode
(Variant, ::std::vector< ::std::pair<unsigned int, Branch> >),
(Unsigned, ::std::vector< ::std::pair< Range<uint64_t>, Branch> >),
(Signed, ::std::vector< ::std::pair< Range<int64_t>, Branch> >),
+ (Float, ::std::vector< ::std::pair< Range<double>, Branch> >),
(String, ::std::vector< ::std::pair< ::std::string, Branch> >)
);
@@ -674,10 +675,6 @@ struct DecisionTreeNode
m_field_path( mv$(field_path) ) // */
{}
- static Branch new_branch_subtree(PatternRule::field_path_t path) {
- return Branch( box$(DecisionTreeNode( mv$(path) )) );
- }
-
static Branch clone(const Branch& b);
static Values clone(const Values& x);
DecisionTreeNode clone() const;
@@ -777,6 +774,13 @@ struct DecisionTreeGen
const ::HIR::TypeRef& ty, ::MIR::LValue val,
::std::function<void(const DecisionTreeNode&)> and_then
);
+ void generate_branches_Float(
+ const Span& sp,
+ const DecisionTreeNode::Branch& default_branch,
+ const DecisionTreeNode::Values::Data_Float& branches,
+ const ::HIR::TypeRef& ty, ::MIR::LValue val,
+ ::std::function<void(const DecisionTreeNode&)> and_then
+ );
void generate_branches_Char(
const Span& sp,
const DecisionTreeNode::Branch& default_branch,
@@ -979,6 +983,21 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa
)
throw "";
}
+ static double get_pattern_value_float(const Span& sp, const ::HIR::Pattern& pat, const ::HIR::Pattern::Value& val) {
+ TU_MATCH_DEF( ::HIR::Pattern::Value, (val), (e),
+ (
+ BUG(sp, "Invalid Value type in " << pat);
+ ),
+ (Float,
+ return e.value;
+ ),
+ (Named,
+ assert(e.binding);
+ return e.binding->m_value_res.as_Float();
+ )
+ )
+ throw "";
+ }
};
TU_MATCHA( (ty.m_data), (e),
@@ -995,7 +1014,9 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa
{
case ::HIR::CoreType::F32:
case ::HIR::CoreType::F64: {
- TODO(sp, "Match over float, is it valid?");
+ double start = H::get_pattern_value_float(sp, pat, pe.start);
+ double end = H::get_pattern_value_float(sp, pat, pe.end );
+ this->push_rule( PatternRule::make_ValueRange( {::MIR::Constant(start), ::MIR::Constant(end)} ) );
} break;
case ::HIR::CoreType::U8:
case ::HIR::CoreType::U16:
@@ -1033,9 +1054,9 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa
{
case ::HIR::CoreType::F32:
case ::HIR::CoreType::F64: {
- TODO(sp, "Match over float, is it valid?");
- //double val = pe.val.as_Float().value;
- //this->push_rule( PatternRule::make_Value( ::MIR::Constant(val) ) );
+ // Yes, this is valid.
+ double val = H::get_pattern_value_float(sp, pat, pe.val);
+ this->push_rule( PatternRule::make_Value( ::MIR::Constant(val) ) );
} break;
case ::HIR::CoreType::U8:
case ::HIR::CoreType::U16:
@@ -1347,6 +1368,13 @@ DecisionTreeNode::Values DecisionTreeNode::clone(const DecisionTreeNode::Values&
rv.push_back( ::std::make_pair(v.first, clone(v.second)) );
return Values( mv$(rv) );
),
+ (Float,
+ Values::Data_Float 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) );
+ ),
(String,
Values::Data_String rv;
rv.reserve(e.size());
@@ -1364,6 +1392,69 @@ DecisionTreeNode DecisionTreeNode::clone() const {
rv.m_default = clone(m_default);
return rv;
}
+
+// Helpers for `populate_tree_from_rule`
+namespace
+{
+ DecisionTreeNode::Branch new_branch_subtree(PatternRule::field_path_t path)
+ {
+ return DecisionTreeNode::Branch( box$(DecisionTreeNode( mv$(path) )) );
+ }
+
+ // Common code for numerics (Int, Uint, and Float)
+ template<typename T>
+ static void from_rule_valuerange(
+ const Span& sp,
+ ::std::vector< ::std::pair< DecisionTreeNode::Range<T>, DecisionTreeNode::Branch> >& be, T ve_start, T ve_end,
+ const char* name, const PatternRule::field_path_t& field_path, ::std::function<void(DecisionTreeNode::Branch&)> and_then
+ )
+ {
+ // Find the first entry that sorts after this range
+ auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first >= ve_start || v.first.contains(ve_end); });
+ // If the end of the list was reached, OR the located entry sorts after the end of this range
+ if( it == be.end() || it->first >= ve_end ) {
+ it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start,ve_end }, new_branch_subtree(field_path) ) );
+ }
+ else if( it->first.start == ve_start && it->first.end == ve_end ) {
+ // Equal, add sub-pattern
+ }
+ else {
+ // Collide or overlap!
+ // - Overlap should split around the collision and merge into it (if this is less-specific).
+ ASSERT_BUG(sp, ve_start < ve_end, "Range pattern with one value");
+ ASSERT_BUG(sp, it->first.start == it->first.end, "Attempting to override a range pattern with another range");
+
+ // TODO: This breaks miserably if the new range overlaps with two other ranges
+
+ // Insert half of ve before, and the other half after? OR Create a sub-pattern specializing.
+ if( ve_start == it->first.start ) {
+ // Add single range after
+ it ++;
+ it = be.insert(it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start + 1, ve_end }, new_branch_subtree(field_path) ) );
+ }
+ else if( ve_end == it->first.start ) {
+ // Add single range before
+ it = be.insert(it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start, ve_end - 1 }, new_branch_subtree(field_path) ) );
+ }
+ else {
+ // Add two ranges
+ auto end1 = it->first.start - 1;
+ auto start2 = it->first.end + 1;
+ it = be.insert(it, ::std::make_pair( DecisionTreeNode::Range<T> { ve_start, end1 }, new_branch_subtree(field_path) ) );
+ auto& branch_1 = it->second;
+ it ++;
+ it ++; // Skip the original entry
+ it = be.insert(it, ::std::make_pair( DecisionTreeNode::Range<T> { start2, ve_end }, new_branch_subtree(field_path) ) );
+ auto& branch_2 = it->second;
+
+ and_then(branch_1);
+ and_then(branch_2);
+ return ;
+ }
+ }
+ and_then(it->second);
+ }
+}
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 );
@@ -1488,13 +1579,15 @@ void DecisionTreeNode::populate_tree_from_rule(const Span& sp, const PatternRule
TODO(sp, "Value patterns - Int");
),
(Uint,
+ // TODO: De-duplicate this code between Uint and Float
if( m_branches.is_Unset() ) {
m_branches = Values::make_Unsigned({});
}
else if( !m_branches.is_Unsigned() ) {
- BUG(sp, "Mismatched rules");
+ BUG(sp, "Mismatched rules - have Unsigned, but have seen " << m_branches.tag_str());
}
auto& be = m_branches.as_Unsigned();
+
auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first.end >= ve; });
if( it == be.end() || it->first.start > ve ) {
it = be.insert( it, ::std::make_pair( Range<uint64_t> { ve,ve }, new_branch_subtree(rule.field_path) ) );
@@ -1519,7 +1612,36 @@ void DecisionTreeNode::populate_tree_from_rule(const Span& sp, const PatternRule
}
),
(Float,
- TODO(sp, "Value patterns - Float");
+ if( m_branches.is_Unset() ) {
+ m_branches = Values::make_Float({});
+ }
+ else if( !m_branches.is_Float() ) {
+ BUG(sp, "Mismatched rules - have Float, but have seen " << m_branches.tag_str());
+ }
+ auto& be = m_branches.as_Float();
+
+ auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first.end >= ve; });
+ if( it == be.end() || it->first.start > ve ) {
+ it = be.insert( it, ::std::make_pair( Range<double> { ve,ve }, new_branch_subtree(rule.field_path) ) );
+ }
+ else if( it->first.start == ve && it->first.end == ve ) {
+ // Equal, continue and add sub-pat
+ }
+ else {
+ // Collide or overlap!
+ TODO(sp, "Value patterns - Float - Overlapping - " << it->first.start << " <= " << ve << " <= " << it->first.end);
+ }
+ auto& branch = it->second;
+ if( rule_count > 1 )
+ {
+ assert( branch.as_Subtree() );
+ auto& subtree = *branch.as_Subtree();
+ subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ }
+ else
+ {
+ and_then(branch);
+ }
),
(Bool,
throw "";
@@ -1561,84 +1683,56 @@ void DecisionTreeNode::populate_tree_from_rule(const Span& sp, const PatternRule
)
),
(ValueRange,
+
ASSERT_BUG(sp, e.first.tag() == e.last.tag(), "");
TU_MATCHA( (e.first, e.last), (ve_start, ve_end),
(Int,
TODO(sp, "ValueRange patterns - Int");
),
(Uint,
+ // TODO: Share code between the three numeric groups
if( m_branches.is_Unset() ) {
m_branches = Values::make_Unsigned({});
}
else if( !m_branches.is_Unsigned() ) {
- BUG(sp, "Mismatched rules");
+ BUG(sp, "Mismatched rules - have Unsigned, seen " << m_branches.tag_str());
}
auto& be = m_branches.as_Unsigned();
- // Find the first entry that sorts after this range
- auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first >= ve_start || v.first.contains(ve_end); });
- // If the end of the list was reached, OR the located entry sorts after the end of this range
- if( it == be.end() || it->first >= ve_end ) {
- it = be.insert( it, ::std::make_pair( Range<uint64_t> { ve_start,ve_end }, new_branch_subtree(rule.field_path) ) );
- }
- else if( it->first.start == ve_start && it->first.end == ve_end ) {
- // Equal, add sub-pattern
- }
- else {
- // Collide or overlap!
- // - Overlap should split around the collision and merge into it (if this is less-specific).
- ASSERT_BUG(sp, ve_start < ve_end, "Range pattern with one value");
- ASSERT_BUG(sp, it->first.start == it->first.end, "Attempting to override a range pattern with another range");
-
- // TODO: This breaks miserably if the new range overlaps with two other ranges
-
- // Insert half of ve before, and the other half after? OR Create a sub-pattern specializing.
- if( ve_start == it->first.start ) {
- // Add single range after
- it ++;
- it = be.insert(it, ::std::make_pair( Range<uint64_t> { ve_start + 1, ve_end }, new_branch_subtree(rule.field_path) ) );
- }
- else if( ve_end == it->first.start ) {
- // Add single range before
- it = be.insert(it, ::std::make_pair( Range<uint64_t> { ve_start, ve_end - 1 }, new_branch_subtree(rule.field_path) ) );
- }
- else {
- // Add two ranges
- auto end1 = it->first.start - 1;
- auto start2 = it->first.end + 1;
- it = be.insert(it, ::std::make_pair( Range<uint64_t> { ve_start, end1 }, new_branch_subtree(rule.field_path) ) );
- auto& branch_1 = it->second;
- it ++;
- it ++; // Skip the original entry
- it = be.insert(it, ::std::make_pair( Range<uint64_t> { start2, ve_end }, new_branch_subtree(rule.field_path) ) );
- auto& branch_2 = it->second;
-
+ from_rule_valuerange(sp, be, ve_start, ve_end, "Unsigned", rule.field_path,
+ [&](auto& branch) {
if( rule_count > 1 )
{
- branch_1.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- branch_2.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ assert( branch.as_Subtree() );
+ auto& subtree = *branch.as_Subtree();
+ subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
}
else
{
- and_then(branch_1);
- and_then(branch_2);
+ and_then(branch);
}
- return ;
- }
- }
- auto& branch = it->second;
- if( rule_count > 1 )
- {
- assert( branch.as_Subtree() );
- auto& subtree = *branch.as_Subtree();
- subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
- }
- else
- {
- and_then(branch);
- }
+ });
),
(Float,
- TODO(sp, "ValueRange patterns - Float");
+ if( m_branches.is_Unset() ) {
+ m_branches = Values::make_Float({});
+ }
+ else if( !m_branches.is_Float() ) {
+ BUG(sp, "Mismatched rules - have Float, seen " << m_branches.tag_str());
+ }
+ auto& be = m_branches.as_Float();
+ from_rule_valuerange(sp, be, ve_start, ve_end, "Float", rule.field_path,
+ [&](auto& branch) {
+ if( rule_count > 1 )
+ {
+ assert( branch.as_Subtree() );
+ auto& subtree = *branch.as_Subtree();
+ subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then);
+ }
+ else
+ {
+ and_then(branch);
+ }
+ });
),
(Bool,
throw "";
@@ -1705,6 +1799,11 @@ void DecisionTreeNode::simplify()
H::simplify_branch(branch.second);
}
),
+ (Float,
+ for(auto& branch : e) {
+ H::simplify_branch(branch.second);
+ }
+ ),
(String,
for(auto& branch : e) {
H::simplify_branch(branch.second);
@@ -1751,6 +1850,11 @@ void DecisionTreeNode::propagate_default()
H::handle_branch(branch.second, m_default);
}
),
+ (Float,
+ for(auto& branch : e) {
+ H::handle_branch(branch.second, m_default);
+ }
+ ),
(String,
for(auto& branch : e) {
H::handle_branch(branch.second, m_default);
@@ -1784,6 +1888,11 @@ void DecisionTreeNode::propagate_default()
be->unify_from(branch.second);
}
),
+ (Float,
+ for(auto& branch : e) {
+ be->unify_from(branch.second);
+ }
+ ),
(String,
for(auto& branch : e) {
be->unify_from(branch.second);
@@ -1863,6 +1972,24 @@ void DecisionTreeNode::unify_from(const Branch& b)
}
}
),
+ (Float,
+ for(const auto& srcv : src)
+ {
+ // Find the first entry with an end greater than or equal to the start of this entry
+ auto it = ::std::find_if( dst.begin(), dst.end(), [&](const auto& x){ return x.first.end >= srcv.first.start; });
+ // Not found? Insert a new branch
+ if( it == dst.end() ) {
+ it = dst.insert(it, ::std::make_pair( srcv.first, clone(srcv.second) ));
+ }
+ // If the found entry doesn't overlap (the start of `*it` is after the end of `srcv`)
+ else if( it->first.start > srcv.first.end ) {
+ it = dst.insert(it, ::std::make_pair( srcv.first, clone(srcv.second) ));
+ }
+ else {
+ // NOTE: Overlapping doesn't get handled here
+ }
+ }
+ ),
(String,
for(const auto& srcv : src)
{
@@ -1941,6 +2068,19 @@ void DecisionTreeNode::unify_from(const Branch& b)
os << " = " << branch.second << ", ";
}
),
+ (Float,
+ os << "F ";
+ for(const auto& branch : e) {
+ const auto& range = branch.first;
+ if( range.start == range.end ) {
+ os << range.start;
+ }
+ else {
+ os << range.start << "..." << range.end;
+ }
+ os << " = " << branch.second << ", ";
+ }
+ ),
(String,
for(const auto& branch : e) {
os << "\"" << branch.first << "\"" << " = " << branch.second << ", ";
@@ -2112,6 +2252,11 @@ void DecisionTreeGen::generate_tree_code(
ASSERT_BUG(sp, node.m_branches.is_String(), "Tree for &str isn't a _String - node="<<node);
this->generate_branches_Borrow_str(sp, node.m_default, node.m_branches.as_String(), ty, mv$(val), mv$(and_then));
break;
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64:
+ ASSERT_BUG(sp, node.m_branches.is_Float(), "Tree for float isn't a _Float - node="<<node);
+ this->generate_branches_Float(sp, node.m_default, node.m_branches.as_Float(), ty, mv$(val), mv$(and_then));
+ break;
default:
TODO(sp, "Primitive - " << ty);
break;
@@ -2244,6 +2389,50 @@ void DecisionTreeGen::generate_branches_Unsigned(
}
}
+void DecisionTreeGen::generate_branches_Float(
+ const Span& sp,
+ const DecisionTreeNode::Branch& default_branch,
+ const DecisionTreeNode::Values::Data_Float& branches,
+ const ::HIR::TypeRef& ty, ::MIR::LValue val,
+ ::std::function<void(const DecisionTreeNode&)> and_then
+ )
+{
+ auto default_block = m_builder.new_bb_unlinked();
+
+ for( const auto& branch : branches )
+ {
+ auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked());
+
+ auto val_start = m_builder.lvalue_or_temp(sp, ty, ::MIR::Constant(branch.first.start));
+ auto val_end = (branch.first.end == branch.first.start ? val_start.clone() : m_builder.lvalue_or_temp(sp, ty, ::MIR::Constant(branch.first.end)));
+
+ auto cmp_gt_block = m_builder.new_bb_unlinked();
+ auto val_cmp_lt = m_builder.lvalue_or_temp(sp, ::HIR::TypeRef(::HIR::CoreType::Bool), ::MIR::RValue::make_BinOp({
+ val.clone(), ::MIR::eBinOp::LT, mv$(val_start)
+ }) );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) );
+ m_builder.set_cur_block( cmp_gt_block );
+ auto success_block = m_builder.new_bb_unlinked();
+ auto val_cmp_gt = m_builder.lvalue_or_temp(sp, ::HIR::TypeRef(::HIR::CoreType::Bool), ::MIR::RValue::make_BinOp({
+ val.clone(), ::MIR::eBinOp::GT, mv$(val_end)
+ }) );
+ m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) );
+
+ m_builder.set_cur_block( success_block );
+ this->generate_branch(branch.second, and_then);
+
+ m_builder.set_cur_block( next_block );
+ }
+ assert( m_builder.block_active() );
+
+ if( default_branch.is_Unset() ) {
+ ERROR(sp, E0000, "Match over floating point with no `_` arm");
+ }
+ else {
+ this->generate_branch(default_branch, and_then);
+ }
+}
+
void DecisionTreeGen::generate_branches_Char(
const Span& sp,
const DecisionTreeNode::Branch& default_branch,