diff --git a/Notes/MIR-Match_OrPatterns.md b/Notes/MIR-Match_OrPatterns.md new file mode 100644 index 000000000..cbefd89d5 --- /dev/null +++ b/Notes/MIR-Match_OrPatterns.md @@ -0,0 +1,61 @@ +Or patterns, Match guards, and if-let + +Problem: or-patterns mean that there are multiple inputs and outputs of a guarded match arm +It's possible for or-patterns to overlap, and thus the guard run twice + +e.g. +```rust +fn test(a: Option, b: Option) { + match (a, b) + { + (Some(v), _) | (_, Some(v)) => if v == 0 {}, + _ => {}, + } +} +``` +In this case, the guard has to run twice, if, `(Some(1), Some(0))` is passed + + +# Background +Logically, a `match` evaluates as if each arm is tried in sequence (including or patterns). +This would intuitively mean that guards and body code is duplicated for each or-pattern, but +in practice the body can be shared between or-pattern duplications (although the guard needs +to be duplicated in order for the first or to fall through to the second one). + +E.g. +```rust +match foo { + Ok(_) if bar() => 1, + Ok(v) => v, + Err(_) => panic(), +} +``` +expands as if it's the following +```rust +loop { + let _value = foo; + if let Ok(_) = _value { + if bar() { + break { 1 }; + } + } + if let Ok(v) = _value { + if true { + break v; + } + } + if let Err(_) = _value { + if true { + break panic(); + } + } + diverge() +} +``` + +# Scope stacks +To get the right variable state tracking, MIR match lowering needs to follow the logical pattern of the match arms: +- A loop scope wrapping the entire block, early-terminated in each arm. +- split scope with empty arm for each pattern + - Early exited +- same (split with empty arm) for each guard rule \ No newline at end of file diff --git a/samples/test/scoping_rules.rs b/samples/test/scoping_rules.rs index 573fff21c..f434f3389 100644 --- a/samples/test/scoping_rules.rs +++ b/samples/test/scoping_rules.rs @@ -14,9 +14,31 @@ impl<'a> ::std::ops::Drop for DropFlag<'a> fn temporaries_in_yielded_expr() { let drop_count = ::std::cell::Cell::new(0); - let _foo = ({ DropFlag(&drop_count).0 }, assert_eq!(drop_count.get(), 0) ); + // NOTE: This is edition specific! in 2024 this is what happens, but in pre 24 the drop happens the `let` + let _foo = ({ DropFlag(&drop_count).0 }, assert_eq!(drop_count.get(), 1) ); drop(_foo); assert_eq!(drop_count.get(), 1); } +#[test] +fn temp_in_if() +{ + let drop_count = ::std::cell::Cell::new(0); + if (DropFlag(&drop_count), true).1 { + assert_eq!(drop_count.get(), 1); + } + if let true = (DropFlag(&drop_count), true).1 { + // Temporaries in a `let` get dropped after the body + assert_eq!(drop_count.get(), 1); + } + assert_eq!(drop_count.get(), 2); + + if let true = true && (DropFlag(&drop_count), true).1 { + // Expect the non-let to have its temporaries dropped before the body + assert_eq!(drop_count.get(), 3); + } + assert_eq!(drop_count.get(), 3); + + {} +} \ No newline at end of file diff --git a/src/ast/dump.cpp b/src/ast/dump.cpp index 9b1029e2b..a9cb999ef 100644 --- a/src/ast/dump.cpp +++ b/src/ast/dump.cpp @@ -262,7 +262,7 @@ class RustPrinter: WRAPIF( n.m_val , AST::ExprNode_Deref, AST::ExprNode_UniOp , AST::ExprNode_Cast, AST::ExprNode_BinOp, AST::ExprNode_Assign - , AST::ExprNode_Match, AST::ExprNode_If, AST::ExprNode_IfLet, AST::ExprNode_Match + , AST::ExprNode_Match, AST::ExprNode_If, AST::ExprNode_Match ); m_os << "." << n.m_method; m_os << "("; @@ -303,30 +303,35 @@ class RustPrinter: m_os << "'" << n.m_label << ": "; } - switch(n.m_type) - { - case AST::ExprNode_Loop::LOOP: - m_os << "loop"; - break; - case AST::ExprNode_Loop::WHILE: - m_os << "while "; - AST::NodeVisitor::visit(n.m_cond); - break; - case AST::ExprNode_Loop::FOR: - m_os << "for "; - print_pattern(n.m_pattern, true); - m_os << " in "; - AST::NodeVisitor::visit(n.m_cond); - break; + m_os << "loop"; + + if( expr_root ) { + m_os << "\n"; + m_os << indent(); + } + else { + m_os << " "; } - if( expr_root ) - { + AST::NodeVisitor::visit(n.m_code); + } + virtual void visit(AST::ExprNode_For& n) override { + bool expr_root = m_expr_root; + m_expr_root = false; + + if( n.m_label.name != "" ) { + m_os << "'" << n.m_label << ": "; + } + m_os << "for "; + print_pattern(n.m_pattern, true); + m_os << " in "; + AST::NodeVisitor::visit(n.m_value); + + if( expr_root ) { m_os << "\n"; m_os << indent(); } - else - { + else { m_os << " "; } @@ -345,7 +350,7 @@ class RustPrinter: m_os << ")"; } } - void visit(AST::ExprNode_WhileLet& n) override { + void visit(AST::ExprNode_While& n) override { bool expr_root = m_expr_root; m_expr_root = false; @@ -419,54 +424,34 @@ class RustPrinter: virtual void visit(AST::ExprNode_If& n) override { bool expr_root = m_expr_root; m_expr_root = false; - m_os << "if "; - AST::NodeVisitor::visit(n.m_cond); - - visit_if_common(expr_root, n.m_true, n.m_false); - } - virtual void visit(AST::ExprNode_IfLet& n) override { - bool expr_root = m_expr_root; - m_expr_root = false; - m_os << "if "; - visit_iflet_conditions(n.m_conditions); + for(auto& arm : n.m_arms) { + if( &arm != n.m_arms.data() ) { + if( expr_root ) { + m_os << indent(); + } + m_os << "else "; + } - visit_if_common(expr_root, n.m_true, n.m_false); - } - void visit_if_common(bool expr_root, ::AST::ExprNodeP& tv, ::AST::ExprNodeP& fv) - { - if( expr_root ) - { - m_os << "\n"; - m_os << indent(); - } - else - { - m_os << " "; - } + m_os << "if "; + visit_iflet_conditions(arm.m_conditions); - bool is_block = (dynamic_cast(&*tv) != nullptr); - if( !is_block ) m_os << "{ "; - AST::NodeVisitor::visit(tv); - if( !is_block ) m_os << " }"; - if(fv.get()) - { - if( expr_root ) - { + bool is_block = (dynamic_cast(&*arm.m_body) != nullptr); + if( !is_block ) m_os << "{ "; + AST::NodeVisitor::visit(arm.m_body); + if( !is_block ) m_os << " }"; + if( expr_root ) { m_os << "\n"; - m_os << indent() << "else"; - // handle chained if statements nicely - if( IS(*fv, AST::ExprNode_If) || IS(*fv, AST::ExprNode_IfLet) ) { - m_expr_root = true; - m_os << " "; - } - else - m_os << "\n" << indent(); } - else - { - m_os << " else "; + } + if( n.m_else ) { + if( expr_root ) { + m_os << indent(); } - AST::NodeVisitor::visit(fv); + m_os << "else"; + bool is_block = (dynamic_cast(&*n.m_else) != nullptr); + if( !is_block ) m_os << "{ "; + AST::NodeVisitor::visit(n.m_else); + if( !is_block ) m_os << " }"; } } virtual void visit(AST::ExprNode_Closure& n) override { @@ -666,7 +651,7 @@ class RustPrinter: WRAPIF( n.m_obj , AST::ExprNode_Deref, AST::ExprNode_UniOp , AST::ExprNode_Cast, AST::ExprNode_BinOp, AST::ExprNode_Assign - , AST::ExprNode_Match, AST::ExprNode_If, AST::ExprNode_IfLet, AST::ExprNode_Match + , AST::ExprNode_Match, AST::ExprNode_If, AST::ExprNode_Match ); m_os << "." << n.m_name; } @@ -675,7 +660,7 @@ class RustPrinter: WRAPIF( n.m_obj , AST::ExprNode_Deref, AST::ExprNode_UniOp , AST::ExprNode_Cast, AST::ExprNode_BinOp, AST::ExprNode_Assign - , AST::ExprNode_Match, AST::ExprNode_If, AST::ExprNode_IfLet, AST::ExprNode_Match + , AST::ExprNode_Match, AST::ExprNode_If, AST::ExprNode_Match ); m_os << "["; AST::NodeVisitor::visit(n.m_idx); diff --git a/src/ast/expr.cpp b/src/ast/expr.cpp index 929848938..362b4f93d 100644 --- a/src/ast/expr.cpp +++ b/src/ast/expr.cpp @@ -323,12 +323,16 @@ NODE(ExprNode_CallObject, { }) NODE(ExprNode_Loop, { - os << "LOOP [" << m_label << "] " << m_pattern; - if(m_cond) - os << " in/= " << *m_cond; + os << "LOOP [" << m_label << "] " << *m_code; +},{ + return NEWNODE(ExprNode_Loop, m_label, m_code->clone()); +}) + +NODE(ExprNode_For, { + os << "FOR [" << m_label << "] " << m_pattern << "in" << *m_value; os << " " << *m_code; },{ - return NEWNODE(ExprNode_Loop, m_type, m_label, m_pattern.clone(), OPT_CLONE(m_cond), m_code->clone()); + return NEWNODE(ExprNode_For, m_label, m_pattern.clone(), m_value->clone(), m_code->clone()); }) namespace { @@ -338,6 +342,7 @@ namespace { os << " && "; } if( cond.opt_pat ) { + os << "let "; os << *cond.opt_pat << " = "; } os << "(" << *cond.value << ")"; @@ -358,16 +363,16 @@ namespace { } } -NODE(ExprNode_WhileLet, { +NODE(ExprNode_While, { if(m_label != "") { os << "'" << m_label << ": "; } - os << "while let "; + os << "while "; fmt_iflet_conditions(os, m_conditions); os << " { " << *m_code << " }"; },{ auto new_conds = clone_iflet_conditions(m_conditions); - return NEWNODE(ExprNode_WhileLet, m_label, mv$(new_conds), m_code->clone()); + return NEWNODE(ExprNode_While, m_label, mv$(new_conds), m_code->clone()); }) NODE(ExprNode_Match, { @@ -399,20 +404,26 @@ NODE(ExprNode_Match, { }) NODE(ExprNode_If, { - os << "if " << *m_cond << " { " << *m_true << " }"; - if(m_false) - os << " else { " << *m_false << " }"; -},{ - return NEWNODE(ExprNode_If, m_cond->clone(), m_true->clone(), OPT_CLONE(m_false)); -}) -NODE(ExprNode_IfLet, { - os << "if let "; - fmt_iflet_conditions(os, m_conditions); - os << " { " << *m_true << " }"; - if(m_false) os << " else { " << *m_false << " }"; + for(const auto& arm : m_arms) { + if( &arm != m_arms.data() ) { + os << " else "; + } + os << "if "; + fmt_iflet_conditions(os, arm.m_conditions); + os << " { " << *arm.m_body << " }"; + } + if(m_else) + os << " else { " << *m_else << " }"; },{ - auto new_conds = clone_iflet_conditions(m_conditions); - return NEWNODE(ExprNode_IfLet, mv$(new_conds), m_true->clone(), OPT_CLONE(m_false)); + std::vector arms; + arms.reserve(m_arms.size()); + for(const auto& arm : m_arms) { + arms.push_back(ExprNode_If::Arm { + clone_iflet_conditions(arm.m_conditions), + arm.m_body->clone() + }); + } + return NEWNODE(ExprNode_If, std::move(arms), OPT_CLONE(m_else)); }) NODE(ExprNode_WildcardPattern, { @@ -754,11 +765,17 @@ NV(ExprNode_CallObject, NV(ExprNode_Loop, { INDENT(); - visit(node.m_cond); visit(node.m_code); UNINDENT(); }) -NV(ExprNode_WhileLet, +NV(ExprNode_For, +{ + INDENT(); + visit(node.m_value); + visit(node.m_code); + UNINDENT(); +}) +NV(ExprNode_While, { INDENT(); for(auto& c : node.m_conditions) { @@ -783,19 +800,13 @@ NV(ExprNode_Match, NV(ExprNode_If, { INDENT(); - visit(node.m_cond); - visit(node.m_true); - visit(node.m_false); - UNINDENT(); -}) -NV(ExprNode_IfLet, -{ - INDENT(); - for(auto& c : node.m_conditions) { - visit(c.value); + for(auto& a : node.m_arms) { + for(auto& c : a.m_conditions) { + visit(c.value); + } + visit(a.m_body); } - visit(node.m_true); - visit(node.m_false); + visit(node.m_else); UNINDENT(); }) diff --git a/src/ast/expr.hpp b/src/ast/expr.hpp index 6d267d7ef..8786ca275 100644 --- a/src/ast/expr.hpp +++ b/src/ast/expr.hpp @@ -337,62 +337,48 @@ struct ExprNode_CallObject: struct ExprNode_Loop: public ExprNode { - enum Type { - LOOP, - WHILE, - FOR, - } m_type; Ident m_label; - AST::Pattern m_pattern; - ExprNodeP m_cond; // if NULL, loop is a 'loop' ExprNodeP m_code; ExprNode_Loop(): - m_type(LOOP), m_label("") { } ExprNode_Loop(Ident label, ExprNodeP code): - m_type(LOOP), m_label( ::std::move(label) ), m_code( ::std::move(code) ) {} - ExprNode_Loop(Ident label, ExprNodeP cond, ExprNodeP code): - m_type(WHILE), - m_label( ::std::move(label) ), - m_cond( ::std::move(cond) ), - m_code( ::std::move(code) ) - {} - ExprNode_Loop(Ident label, AST::Pattern pattern, ExprNodeP val, ExprNodeP code): - m_type(FOR), + NODE_METHODS(); +}; +struct ExprNode_For: + public ExprNode +{ + Ident m_label; + AST::Pattern m_pattern; + ExprNodeP m_value; + ExprNodeP m_code; + + ExprNode_For(Ident label, AST::Pattern pattern, ExprNodeP val, ExprNodeP code): m_label( ::std::move(label) ), m_pattern( ::std::move(pattern) ), - m_cond( ::std::move(val) ), + m_value( ::std::move(val) ), m_code( ::std::move(code) ) {} NODE_METHODS(); -private: - ExprNode_Loop(Type type, Ident label, AST::Pattern pattern, ExprNodeP val, ExprNodeP code) - : m_type( type ) - , m_label( ::std::move(label) ) - , m_pattern( ::std::move(pattern) ) - , m_cond( ::std::move(val) ) - , m_code( ::std::move(code) ) - {} }; struct IfLet_Condition { ::std::unique_ptr opt_pat; ExprNodeP value; }; -struct ExprNode_WhileLet: +struct ExprNode_While: public ExprNode { Ident m_label; std::vector m_conditions; ExprNodeP m_code; - ExprNode_WhileLet(Ident label, std::vector conditions, ExprNodeP code) + ExprNode_While(Ident label, std::vector conditions, ExprNodeP code) : m_label( ::std::move(label) ) , m_conditions( ::std::move(conditions) ) , m_code( ::std::move(code) ) @@ -435,29 +421,16 @@ struct ExprNode_Match: struct ExprNode_If: public ExprNode { - ExprNodeP m_cond; - ExprNodeP m_true; - ExprNodeP m_false; - - ExprNode_If(ExprNodeP cond, ExprNodeP true_code, ExprNodeP false_code): - m_cond( ::std::move(cond) ), - m_true( ::std::move(true_code) ), - m_false( ::std::move(false_code) ) - { - } - NODE_METHODS(); -}; -struct ExprNode_IfLet: - public ExprNode -{ - std::vector m_conditions; - ExprNodeP m_true; - ExprNodeP m_false; + struct Arm { + std::vector m_conditions; + ExprNodeP m_body; + }; + std::vector m_arms; + ExprNodeP m_else; - ExprNode_IfLet(std::vector conditions, ExprNodeP true_code, ExprNodeP false_code) - : m_conditions( ::std::move(conditions) ) - , m_true( ::std::move(true_code) ) - , m_false( ::std::move(false_code) ) + ExprNode_If(std::vector arms, ExprNodeP else_code): + m_arms( ::std::move(arms) ), + m_else( ::std::move(else_code) ) { } NODE_METHODS(); @@ -828,10 +801,10 @@ class NodeVisitor NT(ExprNode_CallMethod); NT(ExprNode_CallObject); NT(ExprNode_Loop); - NT(ExprNode_WhileLet); + NT(ExprNode_For); + NT(ExprNode_While); NT(ExprNode_Match); NT(ExprNode_If); - NT(ExprNode_IfLet); NT(ExprNode_WildcardPattern); NT(ExprNode_Integer); @@ -883,10 +856,10 @@ class NodeVisitorDef: NT(ExprNode_CallMethod); NT(ExprNode_CallObject); NT(ExprNode_Loop); - NT(ExprNode_WhileLet); + NT(ExprNode_For); + NT(ExprNode_While); NT(ExprNode_Match); NT(ExprNode_If); - NT(ExprNode_IfLet); NT(ExprNode_WildcardPattern); NT(ExprNode_Integer); diff --git a/src/expand/derive.cpp b/src/expand/derive.cpp index f3a244fe0..d37b57b33 100644 --- a/src/expand/derive.cpp +++ b/src/expand/derive.cpp @@ -738,11 +738,12 @@ class Deriver_PartialEq: } AST::ExprNodeP compare_and_ret(Span sp, const RcString& core_name, AST::ExprNodeP v1, AST::ExprNodeP v2) const override { - return NEWNODE(If, - NEWNODE(BinOp, AST::ExprNode_BinOp::CMPNEQU, mv$(v1), mv$(v2)), - NEWNODE(Flow, AST::ExprNode_Flow::RETURN, "", NEWNODE(Bool, false)), - nullptr - ); + std::vector arms; + arms.push_back(AST::ExprNode_If::Arm { + make_vec1(AST::IfLet_Condition { {}, NEWNODE(BinOp, AST::ExprNode_BinOp::CMPNEQU, mv$(v1), mv$(v2)) }), + NEWNODE(Flow, AST::ExprNode_Flow::RETURN, "", NEWNODE(Bool, false)) + }); + return NEWNODE(If, std::move(arms), nullptr ); } AST::ExprNodeP equal_value(Span sp, const RcString& core_name) const override { diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp index 13298c5d4..17d91f2ff 100644 --- a/src/expand/mod.cpp +++ b/src/expand/mod.cpp @@ -1182,10 +1182,10 @@ struct CExpandExpr: void visit(::AST::ExprNode_CallMethod& v) override { invalid(v); } void visit(::AST::ExprNode_CallObject& v) override { invalid(v); } void visit(::AST::ExprNode_Loop& v) override { invalid(v); } - void visit(::AST::ExprNode_WhileLet& v) override { invalid(v); } + void visit(::AST::ExprNode_For& v) override { invalid(v); } + void visit(::AST::ExprNode_While& v) override { invalid(v); } void visit(::AST::ExprNode_Match& v) override { invalid(v); } void visit(::AST::ExprNode_If& v) override { invalid(v); } - void visit(::AST::ExprNode_IfLet& v) override { invalid(v); } void visit(::AST::ExprNode_WildcardPattern& v) override { m_rv_set = true; @@ -1297,104 +1297,73 @@ struct CExpandExpr: this->visit_vector(node.m_args); } void visit(::AST::ExprNode_Loop& node) override { - this->visit_nodelete(node, node.m_cond); this->visit_nodelete(node, node.m_code); - Expand_Pattern(this->expand_state, this->cur_mod(), node.m_pattern, false); - if(node.m_type == ::AST::ExprNode_Loop::FOR) - { - static const RcString rcstring_into_iter = RcString::new_interned("into_iter"); - static const RcString rcstring_next = RcString::new_interned("next"); - static const RcString rcstring_it = RcString::new_interned("it"); - auto core_crate = crate.m_ext_cratename_core; - auto path_Some = get_path(core_crate, "option", "Option", "Some"); - auto path_None = get_path(core_crate, "option", "Option", "None"); - auto path_IntoIterator = get_path(core_crate, "iter", "IntoIterator"); - auto path_Iterator = get_path(core_crate, "iter", "Iterator" ); - // Desugar into: - // { - // match <_ as ::iter::IntoIterator>::into_iter(`m_cond`) { - // mut it => { - // `m_label`: loop { - // match ::iter::Iterator::next(&mut it) { - // Some(`m_pattern`) => `m_code`, - // None => break `m_label`, - // } - // } - // } - // } - ::std::vector< ::AST::ExprNode_Match_Arm> arms; - // - `Some(pattern ) => code` - arms.push_back( ::AST::ExprNode_Match_Arm( - ::make_vec1( ::AST::Pattern(::AST::Pattern::TagNamedTuple(), node.span(), path_Some, ::make_vec1( mv$(node.m_pattern) ) ) ), - {}, - mv$(node.m_code) - ) ); - // - `None => break label` - arms.push_back( ::AST::ExprNode_Match_Arm( - ::make_vec1( ::AST::Pattern(::AST::Pattern::TagValue(), node.span(), ::AST::Pattern::Value::make_Named(path_None)) ), - {}, - ::AST::ExprNodeP(new ::AST::ExprNode_Flow(::AST::ExprNode_Flow::BREAK, node.m_label, nullptr)) - ) ); - - replacement.reset(new ::AST::ExprNode_Match( - ::AST::ExprNodeP(new ::AST::ExprNode_CallPath( - ::AST::Path::new_ufcs_trait( ::TypeRef(node.span()), path_IntoIterator, { ::AST::PathNode(rcstring_into_iter) } ), - ::make_vec1( mv$(node.m_cond) ) - )), - ::make_vec1(::AST::ExprNode_Match_Arm( - ::make_vec1( ::AST::Pattern(::AST::Pattern::TagBind(), node.span(), rcstring_it) ), - {}, - ::AST::ExprNodeP(new ::AST::ExprNode_Loop( - node.m_label, - ::AST::ExprNodeP(new ::AST::ExprNode_Match( - ::AST::ExprNodeP(new ::AST::ExprNode_CallPath( - ::AST::Path::new_ufcs_trait( ::TypeRef(node.span()), path_Iterator, { ::AST::PathNode(rcstring_next) } ), - ::make_vec1( ::AST::ExprNodeP(new ::AST::ExprNode_UniOp( - ::AST::ExprNode_UniOp::REFMUT, - ::AST::ExprNodeP(new ::AST::ExprNode_NamedValue( ::AST::Path(rcstring_it) )) - )) ) - )), - mv$(arms) - )) - )) ) - ) - ) ); - } } + void visit(::AST::ExprNode_For& node) override { + Expand_Pattern(this->expand_state, this->cur_mod(), node.m_pattern, false); + this->visit_nodelete(node, node.m_value); + this->visit_nodelete(node, node.m_code); - AST::ExprNodeP desugar_let_chain(const Span& sp, std::vector& conditions, AST::ExprNodeP true_block, std::function get_false_block) - { - // Iterate the conditions in reverse, because this is being built from the innermost block - for(size_t i = conditions.size(); i--; ) - { - auto& cond = conditions[i]; - auto node_nomatch = get_false_block(); - node_nomatch->set_span(sp); - if( cond.opt_pat ) { - ::std::vector< ::AST::ExprNode_Match_Arm> arms; - // - `pattern => body` - arms.push_back( ::AST::ExprNode_Match_Arm( - ::make_vec1( std::move(*cond.opt_pat) ), - {}, - std::move(true_block) - ) ); - // - `_ => break,` - arms.push_back( ::AST::ExprNode_Match_Arm( - ::make_vec1( ::AST::Pattern() ), - {}, - std::move(node_nomatch) - ) ); - true_block.reset(new ::AST::ExprNode_Match( std::move(cond.value), std::move(arms) )); - } - else { - true_block.reset(new ::AST::ExprNode_If( std::move(cond.value), std::move(true_block), std::move(node_nomatch) )); - } - true_block->set_span(sp); - } - return true_block; + static const RcString rcstring_into_iter = RcString::new_interned("into_iter"); + static const RcString rcstring_next = RcString::new_interned("next"); + static const RcString rcstring_it = RcString::new_interned("it"); + auto core_crate = crate.m_ext_cratename_core; + auto path_Some = get_path(core_crate, "option", "Option", "Some"); + auto path_None = get_path(core_crate, "option", "Option", "None"); + auto path_IntoIterator = get_path(core_crate, "iter", "IntoIterator"); + auto path_Iterator = get_path(core_crate, "iter", "Iterator" ); + // Desugar into: + // { + // match <_ as ::iter::IntoIterator>::into_iter(`m_cond`) { + // mut it => { + // `m_label`: loop { + // match ::iter::Iterator::next(&mut it) { + // Some(`m_pattern`) => `m_code`, + // None => break `m_label`, + // } + // } + // } + // } + ::std::vector< ::AST::ExprNode_Match_Arm> arms; + // - `Some(pattern ) => code` + arms.push_back( ::AST::ExprNode_Match_Arm( + ::make_vec1( ::AST::Pattern(::AST::Pattern::TagNamedTuple(), node.span(), path_Some, ::make_vec1( mv$(node.m_pattern) ) ) ), + {}, + mv$(node.m_code) + ) ); + // - `None => break label` + arms.push_back( ::AST::ExprNode_Match_Arm( + ::make_vec1( ::AST::Pattern(::AST::Pattern::TagValue(), node.span(), ::AST::Pattern::Value::make_Named(path_None)) ), + {}, + ::AST::ExprNodeP(new ::AST::ExprNode_Flow(::AST::ExprNode_Flow::BREAK, node.m_label, nullptr)) + ) ); + + replacement.reset(new ::AST::ExprNode_Match( + ::AST::ExprNodeP(new ::AST::ExprNode_CallPath( + ::AST::Path::new_ufcs_trait( ::TypeRef(node.span()), path_IntoIterator, { ::AST::PathNode(rcstring_into_iter) } ), + ::make_vec1( mv$(node.m_value) ) + )), + ::make_vec1(::AST::ExprNode_Match_Arm( + ::make_vec1( ::AST::Pattern(::AST::Pattern::TagBind(), node.span(), rcstring_it) ), + {}, + ::AST::ExprNodeP(new ::AST::ExprNode_Loop( + node.m_label, + ::AST::ExprNodeP(new ::AST::ExprNode_Match( + ::AST::ExprNodeP(new ::AST::ExprNode_CallPath( + ::AST::Path::new_ufcs_trait( ::TypeRef(node.span()), path_Iterator, { ::AST::PathNode(rcstring_next) } ), + ::make_vec1( ::AST::ExprNodeP(new ::AST::ExprNode_UniOp( + ::AST::ExprNode_UniOp::REFMUT, + ::AST::ExprNodeP(new ::AST::ExprNode_NamedValue( ::AST::Path(rcstring_it) )) + )) ) + )), + mv$(arms) + )) + )) ) + ) + ) ); } - void visit(::AST::ExprNode_WhileLet& node) override { + void visit(::AST::ExprNode_While& node) override { for(auto& cond : node.m_conditions) { if( cond.opt_pat ) { Expand_Pattern(this->expand_state, this->cur_mod(), *cond.opt_pat, true); @@ -1402,25 +1371,6 @@ struct CExpandExpr: this->visit_nodelete(node, cond.value); } this->visit_nodelete(node, node.m_code); - - // Desugar into: - /* - * loop { - * match val_a { - * pat_a => match val_b { - * pat_b => ..., - * _ => break, - * }, - * _ => break, - * } - * } - */ - auto node_body = desugar_let_chain(node.span(), node.m_conditions, - std::move(node.m_code), - [&](){ return AST::ExprNodeP { new AST::ExprNode_Flow(AST::ExprNode_Flow::BREAK, node.m_label, AST::ExprNodeP()) }; } - ); - replacement.reset(new ::AST::ExprNode_Loop(node.m_label, std::move(node_body) )); - replacement->set_span(node.span()); } void visit(::AST::ExprNode_Match& node) override { this->visit_nodelete(node, node.m_val); @@ -1452,53 +1402,16 @@ struct CExpandExpr: } } void visit(::AST::ExprNode_If& node) override { - this->visit_nodelete(node, node.m_cond); - this->visit_nodelete(node, node.m_true); - this->visit_nodelete(node, node.m_false); - } - void visit(::AST::ExprNode_IfLet& node) override { - for(auto& cond : node.m_conditions) { - if( cond.opt_pat ) { - Expand_Pattern(this->expand_state, this->cur_mod(), *cond.opt_pat, true); + for(auto& arm : node.m_arms) { + for(auto& cond : arm.m_conditions) { + if( cond.opt_pat ) { + Expand_Pattern(this->expand_state, this->cur_mod(), *cond.opt_pat, true); + } + this->visit_nodelete(node, cond.value); } - this->visit_nodelete(node, cond.value); + this->visit_nodelete(node, arm.m_body); } - this->visit_nodelete(node, node.m_true); - this->visit_nodelete(node, node.m_false); - - // TODO: Desugar into: - /* - * '#iflet: { - * match val_a { - * pat_a => match val_b { - * pat_b => break '#iflet ({ true body }), - * _ => {}, - * }, - * _ => {}, - * } - * false body - * } - */ -#if 1 - Ident label({}, RcString::new_interned("#iflet")); - auto node_body = desugar_let_chain(node.span(), node.m_conditions, - AST::ExprNodeP { new AST::ExprNode_Flow(AST::ExprNode_Flow::BREAK, label, std::move(node.m_true)) }, - [&](){ return AST::ExprNodeP { new AST::ExprNode_Block() }; } - ); - // - Create the block - auto* replacement = new ::AST::ExprNode_Block(); - replacement->m_label = label; - replacement->m_nodes.push_back({ true, std::move(node_body) }); - if(node.m_false) { - replacement->m_nodes.push_back({ false, std::move(node.m_false) }); - } - else { - replacement->m_nodes.push_back({ false, new AST::ExprNode_Tuple({}) }); - replacement->m_nodes.back().node->set_span(node.span()); - } - replacement->set_span(node.span()); - this->replacement.reset(replacement); -#endif + this->visit_nodelete(node, node.m_else); } void visit(::AST::ExprNode_WildcardPattern& node) override { } void visit(::AST::ExprNode_Integer& node) override { } diff --git a/src/expand/proc_macro.cpp b/src/expand/proc_macro.cpp index 1302ac589..9fc3371bb 100644 --- a/src/expand/proc_macro.cpp +++ b/src/expand/proc_macro.cpp @@ -458,8 +458,7 @@ ProcMacroInv ProcMacro_Invoke_int(const Span& sp, const ::AST::Crate& crate, con namespace { - struct Visitor: - public AST::NodeVisitor + struct Visitor { const Span& sp; ProcMacroInv& m_pmi; @@ -1217,142 +1216,6 @@ namespace { this->visit_node(e.node()); } - // === Expressions ==== - void visit(::AST::ExprNode_Block& node) { - switch(node.m_block_type) { - case AST::ExprNode_Block::Type::Bare: - break; - case AST::ExprNode_Block::Type::Unsafe: - m_pmi.send_rword("unsafe"); - break; - case AST::ExprNode_Block::Type::Const: - m_pmi.send_rword("const"); - break; - } - m_pmi.send_symbol("{"); - for(const auto& n : node.m_nodes) - { - this->visit_node(*n.node); - if( n.has_semicolon ) { - m_pmi.send_symbol(";"); - } - } - m_pmi.send_symbol("}"); - } - void visit(::AST::ExprNode_AsyncBlock& node) { - TODO(sp, "ExprNode_AsyncBlock"); - } - void visit(::AST::ExprNode_GeneratorBlock& node) { - TODO(sp, "ExprNode_GeneratorBlock"); - } - void visit(::AST::ExprNode_Try& node) { - TODO(sp, "ExprNode_Try"); - } - void visit(::AST::ExprNode_Macro& node) { - TODO(sp, "ExprNode_Macro"); - } - void visit(::AST::ExprNode_Asm& node) { - TODO(sp, "ExprNode_Asm"); - } - void visit(::AST::ExprNode_Asm2& node) { - TODO(sp, "ExprNode_Asm"); - } - void visit(::AST::ExprNode_Flow& node) { - TODO(sp, "ExprNode_Flow"); - } - void visit(::AST::ExprNode_LetBinding& node) { - TODO(sp, "ExprNode_LetBinding"); - } - void visit(::AST::ExprNode_Assign& node) { - TODO(sp, "ExprNode_Assign"); - } - void visit(::AST::ExprNode_CallPath& node) { - TODO(sp, "ExprNode_CallPath"); - } - void visit(::AST::ExprNode_CallMethod& node) { - TODO(sp, "ExprNode_CallMethod"); - } - void visit(::AST::ExprNode_CallObject& node) { - TODO(sp, "ExprNode_CallObject"); - } - void visit(::AST::ExprNode_Loop& node) { - TODO(sp, "ExprNode_Loop"); - } - void visit(::AST::ExprNode_WhileLet& node) { - TODO(sp, "ExprNode_Loop"); - } - void visit(::AST::ExprNode_Match& node) { - TODO(sp, "ExprNode_Match"); - } - void visit(::AST::ExprNode_If& node) { - TODO(sp, "ExprNode_If"); - } - void visit(::AST::ExprNode_IfLet& node) { - TODO(sp, "ExprNode_IfLet"); - } - - void visit(::AST::ExprNode_WildcardPattern& node) { - m_pmi.send_rword("_"); - } - void visit(::AST::ExprNode_Integer& node) { - m_pmi.send_int(node.m_datatype, node.m_value); - } - void visit(::AST::ExprNode_Float& node) { - TODO(sp, "ExprNode_Float"); - } - void visit(::AST::ExprNode_Bool& node) { - TODO(sp, "ExprNode_Bool"); - } - void visit(::AST::ExprNode_String& node) { - TODO(sp, "ExprNode_String"); - } - void visit(::AST::ExprNode_ByteString& node) { - TODO(sp, "ExprNode_ByteString"); - } - void visit(::AST::ExprNode_CString& node) { - TODO(sp, "ExprNode_CString"); - } - void visit(::AST::ExprNode_Closure& node) { - TODO(sp, "ExprNode_Closure"); - } - void visit(::AST::ExprNode_StructLiteral& node) { - TODO(sp, "ExprNode_StructLiteral"); - } - void visit(::AST::ExprNode_StructLiteralPattern& node) { - TODO(sp, "ExprNode_StructLiteralPattern"); - } - void visit(::AST::ExprNode_Array& node) { - TODO(sp, "ExprNode_Array"); - } - void visit(::AST::ExprNode_Tuple& node) { - TODO(sp, "ExprNode_Tuple"); - } - void visit(::AST::ExprNode_NamedValue& node) { - this->visit_path(node.m_path, true); - } - - void visit(::AST::ExprNode_Field& node) { - TODO(sp, "ExprNode_Field"); - } - void visit(::AST::ExprNode_Index& node) { - TODO(sp, "ExprNode_Index"); - } - void visit(::AST::ExprNode_Deref& node) { - TODO(sp, "ExprNode_Deref"); - } - void visit(::AST::ExprNode_Cast& node) { - TODO(sp, "ExprNode_Cast"); - } - void visit(::AST::ExprNode_TypeAnnotation& node) { - TODO(sp, "ExprNode_TypeAnnotation"); - } - void visit(::AST::ExprNode_BinOp& node) { - TODO(sp, "ExprNode_BinOp"); - } - void visit(::AST::ExprNode_UniOp& node) { - TODO(sp, "ExprNode_UniOp"); - } - void visit_top_attrs(slice& attrs) { for(const auto& a : attrs) diff --git a/src/hir/deserialise.cpp b/src/hir/deserialise.cpp index 90e15bcb4..0f688bc3e 100644 --- a/src/hir/deserialise.cpp +++ b/src/hir/deserialise.cpp @@ -1618,6 +1618,20 @@ namespace { deserialise_vec< MIR::AsmParam>() }); break; + case 6: + rv = ::MIR::Statement::make_SaveDropFlag({ + deserialise_mir_lvalue(), + static_cast(m_in.read_count()), + static_cast(m_in.read_count()) + }); + break; + case 7: + rv = ::MIR::Statement::make_LoadDropFlag({ + static_cast(m_in.read_count()), + deserialise_mir_lvalue(), + static_cast(m_in.read_count()) + }); + break; default: BUG(Span(), "Bad tag for MIR::Statement - " << tag); } diff --git a/src/hir/dump.cpp b/src/hir/dump.cpp index 7c250946c..aa87b772b 100644 --- a/src/hir/dump.cpp +++ b/src/hir/dump.cpp @@ -476,35 +476,6 @@ namespace { } m_os << indent() << "}"; } - void visit(::HIR::ExprNode_If& node) override - { - m_os << "if "; - this->visit_node_ptr(node.m_cond); - m_os << " "; - if( NODE_IS(node.m_true, _Block) ) { - this->visit_node_ptr(node.m_true); - } - else { - m_os << "{"; - this->visit_node_ptr(node.m_true); - m_os << "}"; - } - if( node.m_false ) - { - m_os << " else "; - if( NODE_IS(node.m_false, _Block) ) { - this->visit_node_ptr(node.m_false); - } - else if( NODE_IS(node.m_false, _If) ) { - this->visit_node_ptr(node.m_false); - } - else { - m_os << "{ "; - this->visit_node_ptr(node.m_false); - m_os << " }"; - } - } - } void visit(::HIR::ExprNode_Assign& node) override { this->visit_node_ptr(node.m_slot); @@ -847,7 +818,12 @@ namespace { m_os << "move "; } m_os << "async {"; - this->visit_node_ptr(node.m_code); + if( !node.m_code ) { + m_os << "/* lowered: " << node.m_obj_path << " */"; + } + else { + this->visit_node_ptr(node.m_code); + } m_os << "}"; } diff --git a/src/hir/expr.cpp b/src/hir/expr.cpp index 96479ad25..871be6119 100644 --- a/src/hir/expr.cpp +++ b/src/hir/expr.cpp @@ -116,13 +116,6 @@ DEF_VISIT_H(ExprNode_Match, node) { visit_node_ptr(arm.m_code); } } -DEF_VISIT_H(ExprNode_If, node) { - TRACE_FUNCTION_F("_If"); - visit_node_ptr(node.m_cond); - visit_node_ptr(node.m_true); - if( node.m_false ) - visit_node_ptr(node.m_false); -} DEF_VISIT(ExprNode_Assign, node, TRACE_FUNCTION_F("_Assign"); diff --git a/src/hir/expr.hpp b/src/hir/expr.hpp index 59daea730..b1bdcc291 100644 --- a/src/hir/expr.hpp +++ b/src/hir/expr.hpp @@ -266,6 +266,8 @@ struct ExprNode_Match: ::HIR::Pattern pat; /// Guard value ::HIR::ExprNodeP val; + /// Indicates that this guard is an `if` (changes scoping rules, and tweaks how typecheck happens) + bool is_if; }; struct Arm { @@ -288,24 +290,6 @@ struct ExprNode_Match: NODE_METHODS(); }; -// TODO: Refactor this such that if-else if-else is represented in a flat manner, instead of being recursive -// - `if let` is a challenge with that form -struct ExprNode_If: - public ExprNode -{ - ::HIR::ExprNodeP m_cond; - ::HIR::ExprNodeP m_true; - ::HIR::ExprNodeP m_false; - - ExprNode_If(Span sp, ::HIR::ExprNodeP cond, ::HIR::ExprNodeP true_code, ::HIR::ExprNodeP false_code): - ExprNode( mv$(sp) ), - m_cond( mv$(cond) ), - m_true( mv$(true_code) ), - m_false( mv$(false_code) ) - {} - - NODE_METHODS(); -}; struct ExprNode_Assign: public ExprNode @@ -1037,7 +1021,6 @@ class ExprVisitor NV(ExprNode_Loop) NV(ExprNode_LoopControl) NV(ExprNode_Match) - NV(ExprNode_If) NV(ExprNode_Assign) NV(ExprNode_BinOp) @@ -1094,7 +1077,6 @@ class ExprVisitorDef: NV(ExprNode_Loop) NV(ExprNode_LoopControl) NV(ExprNode_Match) - NV(ExprNode_If) NV(ExprNode_Assign) NV(ExprNode_BinOp) diff --git a/src/hir/from_ast_expr.cpp b/src/hir/from_ast_expr.cpp index d9d23a967..80c271bf8 100644 --- a/src/hir/from_ast_expr.cpp +++ b/src/hir/from_ast_expr.cpp @@ -483,106 +483,58 @@ struct LowerHIR_ExprNode_Visitor: ) ); } virtual void visit(::AST::ExprNode_Loop& v) override { - switch( v.m_type ) - { - case ::AST::ExprNode_Loop::LOOP: - m_rv.reset( new ::HIR::ExprNode_Loop( v.span(), - v.m_label.name, - lower(v.m_code) - ) ); - break; - case ::AST::ExprNode_Loop::WHILE: { - ::std::vector< ::HIR::ExprNodeP> code; - // - if `m_cond` { () } else { break `m_label` } - code.push_back( ::HIR::ExprNodeP(new ::HIR::ExprNode_If( v.span(), - lower(v.m_cond), - ::HIR::ExprNodeP( new ::HIR::ExprNode_Tuple(v.span(), {}) ), - ::HIR::ExprNodeP( new ::HIR::ExprNode_LoopControl(v.span(), v.m_label.name, false) ) - )) ); - code.push_back( lower(v.m_code) ); - - m_rv.reset( new ::HIR::ExprNode_Loop( v.span(), - v.m_label.name, - ::HIR::ExprNodeP(new ::HIR::ExprNode_Block( v.span(), false, mv$(code), {} )) - ) ); - break; } - case ::AST::ExprNode_Loop::FOR: - // NOTE: This should already be desugared (as a pass before resolve) - BUG(v.span(), "Encountered still-sugared for loop"); - break; - } - - // Iterate the constructed loop and determine if there are any `break` statements pointing to it - { - struct LoopVisitor: - public ::HIR::ExprVisitorDef - { - const RcString& top_label; - bool top_is_broken; - ::std::vector< const RcString*> name_stack; - - LoopVisitor(const RcString& top_label): - top_label(top_label), - top_is_broken(false), - name_stack() - {} - - void visit(::HIR::ExprNode_Loop& node) override { - bool push = !node.m_require_label; // Ignore any loops that require a targeted break - if( push ) { - this->name_stack.push_back( &node.m_label ); - } - ::HIR::ExprVisitorDef::visit(node); - if( push ) { - this->name_stack.pop_back( ); - } - } - void visit(::HIR::ExprNode_LoopControl& node) override { - ::HIR::ExprVisitorDef::visit(node); - - if( node.m_continue ) { - } - else { - for( auto it = this->name_stack.rbegin(); it != this->name_stack.rend(); ++ it ) - { - if( node.m_label == "" || node.m_label == **it ) - return ; - } - if( node.m_label == "" || node.m_label == this->top_label ) { - this->top_is_broken = true; - } - else { - // break is for a higher loop - } - } - } - }; + m_rv.reset( new ::HIR::ExprNode_Loop( v.span(), + v.m_label.name, + lower(v.m_code) + ) ); + } + void visit(::AST::ExprNode_For& v) override { + // NOTE: This should already be desugared (as a pass before resolve) + BUG(v.span(), "Encountered still-sugared for loop"); + } + ::std::vector< ::HIR::ExprNode_Match::Guard> iflet_to_guards(std::vector& guards) { + ::std::vector< ::HIR::ExprNode_Match::Guard> rv; + rv.reserve(guards.size()); + for(auto& c : guards) { + auto cond_pat = c.opt_pat + ? LowerHIR_Pattern(*c.opt_pat) + : HIR::Pattern { HIR::PatternBinding(), HIR::Pattern::Data::make_Value({ ::HIR::Pattern::Value::make_Integer({ + HIR::CoreType::Bool, + U128(1) + }) }) } + ; + auto cond_val = lower_opt(c.value); + rv.push_back(::HIR::ExprNode_Match::Guard { std::move(cond_pat), std::move(cond_val), c.opt_pat ? false : true }); } + return rv; } - virtual void visit(::AST::ExprNode_WhileLet& v) override { - TODO(v.span(), "`while let` left sugared"); + virtual void visit(::AST::ExprNode_While& v) override { + // Desugar to `loop { match () { _ if ... => { body }, _ => break, } }` + ::std::vector< ::HIR::ExprNode_Match::Arm> arms; + arms.push_back( ::HIR::ExprNode_Match::Arm { + make_vec1(::HIR::Pattern()), + iflet_to_guards(v.m_conditions), + lower(v.m_code) + }); + arms.push_back(::HIR::ExprNode_Match::Arm { + make_vec1(::HIR::Pattern()), + {}, + ::std::make_unique(v.span(), "", false, nullptr) + }); + m_rv.reset(new HIR::ExprNode_Loop(v.span(), v.m_label.name, std::make_unique( + v.span(), + std::make_unique(v.span(), ::std::vector()), + std::move(arms) + ))); } virtual void visit(::AST::ExprNode_Match& v) override { ::std::vector< ::HIR::ExprNode_Match::Arm> arms; for(auto& arm : v.m_arms) { - ::std::vector< ::HIR::ExprNode_Match::Guard> guards; - guards.reserve(arm.m_guard.size()); - for(auto& c : arm.m_guard) { - auto cond_pat = c.opt_pat - ? LowerHIR_Pattern(*c.opt_pat) - : HIR::Pattern { HIR::PatternBinding(), HIR::Pattern::Data::make_Value({ ::HIR::Pattern::Value::make_Integer({ - HIR::CoreType::Bool, - U128(1) - }) }) } - ; - auto cond_val = lower_opt(c.value); - guards.push_back(::HIR::ExprNode_Match::Guard { std::move(cond_pat), std::move(cond_val) }); - } ::HIR::ExprNode_Match::Arm new_arm { {}, - mv$(guards), + iflet_to_guards(arm.m_guard), lower(arm.m_code) }; @@ -598,16 +550,29 @@ struct LowerHIR_ExprNode_Visitor: )); } virtual void visit(::AST::ExprNode_If& v) override { - m_rv.reset( new ::HIR::ExprNode_If( v.span(), - lower(v.m_cond), - lower(v.m_true), - lower_opt(v.m_false) + ::std::vector< ::HIR::ExprNode_Match::Arm> arms; + // Desugar to a `match` + for(auto& arm : v.m_arms) + { + arms.push_back( ::HIR::ExprNode_Match::Arm { + make_vec1(::HIR::Pattern()), + iflet_to_guards(arm.m_conditions), + lower(arm.m_body) + } ); + } + arms.push_back(::HIR::ExprNode_Match::Arm { + make_vec1(::HIR::Pattern()), + {}, + v.m_else + ? lower(v.m_else) + : std::make_unique(v.span(), ::std::vector()) + }); + + m_rv.reset( new ::HIR::ExprNode_Match( v.span(), + std::make_unique(v.span(), ::std::vector()), + std::move(arms) )); } - virtual void visit(::AST::ExprNode_IfLet& v) override { - // TODO: Do the desugar here, so irrefutable patterns can be handled nicely - TODO(v.span(), "`if let` left sugared"); - } virtual void visit(::AST::ExprNode_WildcardPattern& v) override { ERROR(v.span(), E0000, "`_` is only valid in expressions on the left-hand side of an assignment"); diff --git a/src/hir/serialise.cpp b/src/hir/serialise.cpp index 12f703e30..93afb6757 100644 --- a/src/hir/serialise.cpp +++ b/src/hir/serialise.cpp @@ -877,9 +877,9 @@ } TU_ARMA(SaveDropFlag, e) { m_out.write_tag(6); - m_out.write_count(e.idx); serialise(e.slot); m_out.write_count(e.bit_index); + m_out.write_count(e.idx); } TU_ARMA(LoadDropFlag, e) { m_out.write_tag(7); diff --git a/src/hir_expand/annotate_value_usage.cpp b/src/hir_expand/annotate_value_usage.cpp index 52ea15389..11613a3ef 100644 --- a/src/hir_expand/annotate_value_usage.cpp +++ b/src/hir_expand/annotate_value_usage.cpp @@ -360,15 +360,6 @@ namespace { this->visit_node_ptr( arm.m_code ); } } - void visit(::HIR::ExprNode_If& node) override - { - auto _ = this->push_usage( ::HIR::ValueUsage::Move ); - this->visit_node_ptr( node.m_cond ); - this->visit_node_ptr( node.m_true ); - if( node.m_false ) { - this->visit_node_ptr( node.m_false ); - } - } void visit(::HIR::ExprNode_Assign& node) override { diff --git a/src/hir_expand/lifetime_infer.cpp b/src/hir_expand/lifetime_infer.cpp index 0555ec39f..36c7bfea9 100644 --- a/src/hir_expand/lifetime_infer.cpp +++ b/src/hir_expand/lifetime_infer.cpp @@ -999,7 +999,6 @@ namespace { NV(ExprNode_Loop) NV(ExprNode_LoopControl) NV(ExprNode_Match) - NV(ExprNode_If) NV(ExprNode_Assign) NV(ExprNode_BinOp) @@ -1152,14 +1151,6 @@ namespace { equate_node_types(node, arm.m_code); } } - void visit(::HIR::ExprNode_If& node) override { - HIR::ExprVisitorDef::visit(node); - // Equate both arms to the output - if( node.m_false ) { - equate_node_types(node, node.m_true ); - equate_node_types(node, node.m_false); - } - } void visit(::HIR::ExprNode_Assign& node) override { HIR::ExprVisitorDef::visit(node); diff --git a/src/hir_typeck/expr_check.cpp b/src/hir_typeck/expr_check.cpp index 5ea265f5c..65cad7ade 100644 --- a/src/hir_typeck/expr_check.cpp +++ b/src/hir_typeck/expr_check.cpp @@ -203,18 +203,6 @@ namespace { arm.m_code->visit( *this ); } } - void visit(::HIR::ExprNode_If& node) override - { - TRACE_FUNCTION_F(&node << " if ... { ... } else { ... }"); - node.m_cond->visit( *this ); - node.m_true->visit( *this ); - check_types_equal(node.span(), node.m_res_type, node.m_true->m_res_type); - if( node.m_false ) - { - node.m_false->visit( *this ); - check_types_equal(node.span(), node.m_res_type, node.m_false->m_res_type); - } - } void visit(::HIR::ExprNode_Assign& node) override { TRACE_FUNCTION_F(&node << "... ?= ..."); diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index c9af3bc12..59bbe3056 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -185,9 +185,6 @@ namespace { void visit(::HIR::ExprNode_Match& node) override { no_revisit(node); } - void visit(::HIR::ExprNode_If& node) override { - no_revisit(node); - } void visit(::HIR::ExprNode_Assign& node) override { no_revisit(node); @@ -1850,9 +1847,6 @@ namespace { void visit(::HIR::ExprNode_Match& node) override { no_revisit(node); } - void visit(::HIR::ExprNode_If& node) override { - no_revisit(node); - } void visit(::HIR::ExprNode_Assign& node) override { no_revisit(node); } diff --git a/src/hir_typeck/expr_cs__enum.cpp b/src/hir_typeck/expr_cs__enum.cpp index ffc45583d..474a984ba 100644 --- a/src/hir_typeck/expr_cs__enum.cpp +++ b/src/hir_typeck/expr_cs__enum.cpp @@ -948,7 +948,13 @@ namespace typecheck this->context.add_ivars( c.val->m_res_type ); c.val->visit( *this ); - this->context.handle_pattern(node.span(), c.pat, c.val->m_res_type); + // Shortcut `if` to avoid the pattern matching complexity + if( c.is_if ) { + this->context.equate_types(c.val->span(), ::HIR::TypeRef(::HIR::CoreType::Bool), c.val->m_res_type); + } + else { + this->context.handle_pattern(node.span(), c.pat, c.val->m_res_type); + } } this->context.add_ivars( arm.m_code->m_res_type ); @@ -962,37 +968,6 @@ namespace typecheck } } - void visit(::HIR::ExprNode_If& node) override - { - TRACE_FUNCTION_F(&node << " if ..."); - - this->context.add_ivars( node.m_cond->m_res_type ); - - { - auto _ = this->push_inner_coerce_scoped(false); - this->context.equate_types(node.m_cond->span(), ::HIR::TypeRef(::HIR::CoreType::Bool), node.m_cond->m_res_type); - node.m_cond->visit( *this ); - } - - this->context.add_ivars( node.m_true->m_res_type ); - if( node.m_false ) { - this->context.equate_types_coerce(node.span(), node.m_res_type, node.m_true); - } - else { - this->context.equate_types(node.span(), node.m_true->m_res_type, ::HIR::TypeRef::new_unit()); - this->context.equate_types(node.span(), node.m_res_type, ::HIR::TypeRef::new_unit()); - } - node.m_true->visit( *this ); - - if( node.m_false ) { - this->context.add_ivars( node.m_false->m_res_type ); - this->context.equate_types_coerce(node.span(), node.m_res_type, node.m_false); - node.m_false->visit( *this ); - } - else { - } - } - void visit(::HIR::ExprNode_Assign& node) override { diff --git a/src/mir/check.cpp b/src/mir/check.cpp index 2215b19e2..4039d0bfa 100644 --- a/src/mir/check.cpp +++ b/src/mir/check.cpp @@ -432,19 +432,27 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn case ::MIR::Statement::TAGDEAD: throw ""; case ::MIR::Statement::TAG_SetDropFlag: + MIR_ASSERT(state, stmt.as_SetDropFlag().idx < fcn.drop_flags.size(), ""); + if( stmt.as_SetDropFlag().other != ~0u ) { + MIR_ASSERT(state, stmt.as_SetDropFlag().other < fcn.drop_flags.size(), ""); + } break; case ::MIR::Statement::TAG_LoadDropFlag: + MIR_ASSERT(state, stmt.as_LoadDropFlag().idx < fcn.drop_flags.size(), ""); val_state.ensure_valid(state, stmt.as_LoadDropFlag().slot); break; case ::MIR::Statement::TAG_SaveDropFlag: + MIR_ASSERT(state, stmt.as_SaveDropFlag().idx < fcn.drop_flags.size(), ""); val_state.ensure_valid(state, stmt.as_SaveDropFlag().slot); break; case ::MIR::Statement::TAG_Drop: // Invalidate the slot - if( stmt.as_Drop().flag_idx == ~0u ) - { + if( stmt.as_Drop().flag_idx == ~0u ) { val_state.ensure_valid(state, stmt.as_Drop().slot); } + else { + MIR_ASSERT(state, stmt.as_Drop().flag_idx < fcn.drop_flags.size(), ""); + } val_state.mark_validity( state, stmt.as_Drop().slot, false ); break; case ::MIR::Statement::TAG_Asm: @@ -751,6 +759,8 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path case ::MIR::Statement::TAG_SaveDropFlag: case ::MIR::Statement::TAG_LoadDropFlag: { + const auto idx = stmt.is_SaveDropFlag() ? stmt.as_SaveDropFlag().idx : stmt.as_LoadDropFlag().idx; + MIR_ASSERT(state, idx < fcn.drop_flags.size(), "df" << idx << " out of range (nflags " << fcn.drop_flags.size() << ")"); const auto& slot = stmt.is_SaveDropFlag() ? stmt.as_SaveDropFlag().slot : stmt.as_LoadDropFlag().slot; const auto bit = stmt.is_SaveDropFlag() ? stmt.as_SaveDropFlag().bit_index : stmt.as_LoadDropFlag().bit_index; ::HIR::TypeRef slot_tmp; diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 06235a6cf..3d27ae3a6 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -310,9 +310,8 @@ namespace { MIR::LValue get_value_for_binding_path(const Span& sp, const ::HIR::TypeRef& outer_ty, const ::MIR::LValue& outer_lval, const PatternBinding& b) { + HIR::TypeRef ty; MIR::LValue lval; - HIR::TypeRef ty; - MIR_LowerHIR_GetTypeValueForPath(sp, m_builder, outer_ty, outer_lval, b.field, ty, lval); if(b.is_split_slice()) @@ -395,7 +394,7 @@ namespace { return lval; } - void destructure_from_list(const Span& sp, const ::HIR::TypeRef& outer_ty, ::MIR::LValue outer_lval, const ::std::vector& bindings) override + void destructure_from_list(const Span& sp, const ::HIR::TypeRef& outer_ty, ::MIR::LValue outer_lval, const ::std::vector& bindings, bool update_states/*=true*/) override { TRACE_FUNCTION_F(outer_lval << ": " << outer_ty << " [" << bindings << "]"); // Reverse order to avoid potential use-after-move for `foo @ Bar(baz, ..)` @@ -428,16 +427,13 @@ namespace { rv = ::MIR::RValue::make_Borrow({ ::HIR::BorrowType::Unique, false, mv$(lval) }); break; } - m_builder.push_stmt_assign( sp, m_builder.get_variable(sp, b.binding->m_slot), mv$(rv) ); + // NOTE: Don't drop the destination, as `match` does some tricky things with calling destructure multiple times (to handle or-patterns) + m_builder.push_stmt_assign( sp, m_builder.get_variable(sp, b.binding->m_slot), mv$(rv), update_states); } } - void destructure_aliases_from_list(const Span& sp, const ::HIR::TypeRef& outer_ty, ::MIR::LValue outer_lval, const ::std::vector& bindings) override + const HIR::TypeRef& get_binding_type(const Span& sp, unsigned index) const { - for(const auto& b : bindings) - { - auto val = get_value_for_binding_path(sp, outer_ty, outer_lval, b); - m_builder.add_variable_alias(sp, b.binding->m_slot, b.binding->m_type, mv$(val)); - } + return m_variable_types.at(index); } void emit_unwind(const Span& sp) @@ -470,7 +466,8 @@ namespace { const Span& sp = subnode->span(); auto stmt_scope = m_builder.new_scope_temp(sp); - auto _stmt_scope_push = save_and_edit(m_stmt_scope, &stmt_scope); + // NOTE: Only set the statement scope if processing a block + auto _stmt_scope_push = save_and_edit(m_stmt_scope, dynamic_cast<::HIR::ExprNode_Block*>(subnode.get()) ? &stmt_scope : nullptr); this->visit_node_ptr(subnode); if( m_builder.block_active() || m_builder.has_result() ) { @@ -1165,68 +1162,6 @@ namespace { m_builder.end_block( ::MIR::Terminator::make_If({ mv$(decision_val), true_branch, false_branch }) ); } - void visit(::HIR::ExprNode_If& node) override - { - TRACE_FUNCTION_FR("_If", "_If"); - - auto true_branch = m_builder.new_bb_unlinked(); - auto false_branch = m_builder.new_bb_unlinked(); - emit_if(node.m_cond, true_branch, false_branch); - - auto next_block = m_builder.new_bb_unlinked(); - auto result_val = m_builder.new_temporary(node.m_res_type); - - // Scope handles cases where one arm moves a value but the other doesn't - auto scope = m_builder.new_scope_split( node.m_true->span() ); - - // 'true' branch - { - auto stmt_scope = m_builder.new_scope_temp(node.m_true->span()); - m_builder.set_cur_block(true_branch); - this->visit_node_ptr(node.m_true); - if( m_builder.block_active() || m_builder.has_result() ) { - m_builder.push_stmt_assign( node.span(), result_val.clone(), m_builder.get_result(node.m_true->span()) ); - m_builder.terminate_scope(node.span(), mv$(stmt_scope)); - m_builder.end_split_arm(node.span(), scope, true); - m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); - } - else { - m_builder.terminate_scope(node.span(), mv$(stmt_scope), false); - m_builder.end_split_arm(node.span(), scope, false); - } - } - - // 'false' branch - m_builder.set_cur_block(false_branch); - if( node.m_false ) - { - auto stmt_scope = m_builder.new_scope_temp(node.m_false->span()); - this->visit_node_ptr(node.m_false); - if( m_builder.block_active() ) - { - m_builder.push_stmt_assign( node.span(), result_val.clone(), m_builder.get_result(node.m_false->span()) ); - m_builder.terminate_scope(node.span(), mv$(stmt_scope)); - m_builder.end_split_arm(node.span(), scope, true); - m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); - } - else { - m_builder.terminate_scope(node.span(), mv$(stmt_scope), false); - m_builder.end_split_arm(node.span(), scope, false); - } - } - else - { - // Assign `()` to the result - m_builder.push_stmt_assign(node.span(), result_val.clone(), ::MIR::RValue::make_Tuple({}) ); - m_builder.end_split_arm(node.span(), scope, true); - m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); - } - m_builder.set_cur_block(next_block); - m_builder.terminate_scope( node.span(), mv$(scope) ); - - m_builder.set_result( node.span(), mv$(result_val) ); - } - void generate_checked_binop(const Span& sp, ::MIR::LValue res_slot, ::MIR::eBinOp op, ::MIR::Param val_l, const ::HIR::TypeRef& ty_l, ::MIR::Param val_r, const ::HIR::TypeRef& ty_r) { switch(op) @@ -3326,28 +3261,44 @@ ::MIR::FunctionPointer LowerMIR(const StaticTraitResolve& resolve, const ::HIR:: bool visit_stmt(::MIR::Statement& stmt) override { + auto get_drop_flags_slot = [this]()->MIR::LValue { + ::MIR::LValue slot = ::MIR::LValue::new_Argument(0); + slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(0)); // Pin.ptr + slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Deref()); // * + slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(0)); // .0 + slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Downcast(1)); // .value (From MaybeUninit) + slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(0)); // .value (From ManuallyDrop) + slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(m_drop_flags_field)); // .drop_flags + return slot; + }; if( auto* s = stmt.opt_Drop() ) { if( m_drop_flag_mapping.count(s->flag_idx) != 0 ) { - // TODO: Need to emit a different `SetDropFlag` to load from bitset // `LoadDropFlag(df$N, src_lv, bit_num)`, where `src_lv` is an array of `u8` - TODO(Span(), "Rewrite drop flag usage df$" << s->flag_idx); + auto slot = get_drop_flags_slot(); + unsigned bit_num = m_drop_flag_mapping.at(s->flag_idx); + m_new_statements.push_back(::MIR::Statement::make_LoadDropFlag({ + s->flag_idx, + std::move(slot), + bit_num, + })); } } else if(auto* s = stmt.opt_SetDropFlag() ) { if( m_drop_flag_mapping.count(s->other) != 0 ) { - TODO(Span(), "Rewrite drop flag usage df$" << s->other); + auto slot = get_drop_flags_slot(); + unsigned bit_num = m_drop_flag_mapping.at(s->other); + // `LoadDropFlag(df$N, src_lv, bit_num)`, where `src_lv` is an array of `u8` + m_new_statements.push_back(::MIR::Statement::make_LoadDropFlag({ + s->other, + std::move(slot), + bit_num, + })); } if( m_drop_flag_mapping.count(s->idx) != 0 ) { // Copy this statement to the output queue, and then rewrite to be: m_new_statements.push_back(*s); // `SaveDropFlag(dst_lv, bit_num, df$N)` - ::MIR::LValue slot = ::MIR::LValue::new_Argument(0); - slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(0)); // Pin.ptr - slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Deref()); // * - slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(0)); // .0 - slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Downcast(1)); // .value (From MaybeUninit) - slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(0)); // .value (From ManuallyDrop) - slot.m_wrappers.push_back(::MIR::LValue::Wrapper::new_Field(m_drop_flags_field)); // .drop_flags + auto slot = get_drop_flags_slot(); unsigned bit_num = m_drop_flag_mapping.at(s->idx); stmt = ::MIR::Statement::make_SaveDropFlag({ std::move(slot), @@ -3411,8 +3362,12 @@ ::MIR::FunctionPointer LowerMIR(const StaticTraitResolve& resolve, const ::HIR:: d->flag_idx = drop_flag_mapping.at(d->flag_idx); } } + if( auto* d = stmt.opt_LoadDropFlag() ) { + d->idx = drop_flag_mapping.at(d->idx); + } } } + MIR_Validate(resolve, path, *drop_impl_body, gen_node->m_drop_fcn_ptr->m_args, ::HIR::TypeRef::new_unit()); gen_node->m_drop_fcn_ptr->m_code.m_mir = std::move(drop_impl_body); } else diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp index 2ba7b5fa5..0ed1c6a52 100644 --- a/src/mir/from_hir.hpp +++ b/src/mir/from_hir.hpp @@ -111,12 +111,10 @@ TAGGED_UNION(ScopeType, Owning, ::MIR::BasicBlockId entry_bb; ::std::vector drop_flags; }), - // State which should end up with no mutation of variable states (Freeze, struct { - ::std::map changed_slots; - //::std::map changed_args; - std::vector original_aliases; - }) + /// Has `unfreeze_scope` been called on this entry? + bool unfrozen = false; + }) ); #define FIELD_DEREF 0xFFFF @@ -253,6 +251,29 @@ class MirBuilder /// Check if the passed type is Box and returns a pointer to the T type if so, otherwise nullptr const ::HIR::TypeRef* is_type_owned_box(const ::HIR::TypeRef& ty) const; + class SavedAliases { + friend class MirBuilder; + // Just remember which variables had aliases on them, as we want to clear anything added while saving. + ::std::vector set_aliases; + }; + /// Save the current state of aliases (see add_variable_alias) + SavedAliases save_aliases() const { + SavedAliases rv; + rv.set_aliases.reserve(m_variable_aliases.size()); + for(const auto& v : m_variable_aliases) { + rv.set_aliases.push_back(v.second != MIR::LValue()); + } + return rv; + } + void restore_aliases(SavedAliases a) { + assert(a.set_aliases.size() == m_variable_aliases.size()); + for(size_t i = 0; i < a.set_aliases.size(); i ++) { + if( !a.set_aliases[i] ) { + m_variable_aliases.at(i).second = MIR::LValue(); + } + } + } + // Variable aliases (used for match guards) void add_variable_alias(const Span& sp, unsigned idx, HIR::PatternBinding::Type ty, MIR::LValue lv) { DEBUG("#" << idx << " = " << int(ty) << " " << lv); @@ -305,7 +326,7 @@ class MirBuilder // - Statements // Push an assignment. NOTE: This also marks the rvalue as moved - void push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val, bool drop_destination=true); + void push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val, bool update_dest_state=true); // Push a drop (likely only used by scope cleanup) void push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int drop_flag=~0u); // Push a shallow drop (for Box) @@ -331,6 +352,36 @@ class MirBuilder void raise_temporaries(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above=false); void raise_temporaries(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above=false); + + class SaveCodeProto { + friend class MirBuilder; + size_t index; + }; + /// @brief Start saving code for later duplication (match guards) + /// @return Handle to the current save stack entry + SaveCodeProto code_save_start(); + class SavedCode { + friend class MirBuilder; + std::vector blocks; + }; + /// @brief Complete and finalise saved code + SavedCode code_save_end(SaveCodeProto h); + class CloneMapper { + public: + virtual MIR::BasicBlockId update_bb_ref(MIR::BasicBlockId bb_idx) = 0; + }; + /// @brief Insert saved code, applying the supplied mapper + void insert_cloned(const Span& sp, const SavedCode& c, CloneMapper& mapper); +private: + struct CodeSaveStackEnt { + /// Unique index to catch stack violations + size_t index; + /// Basic blocks in the copied region + std::vector blocks; + }; + std::vector m_code_save_stack; +public: + void set_cur_block(unsigned int new_block); ::MIR::BasicBlockId pause_cur_block(); @@ -346,10 +397,15 @@ class MirBuilder void drop_flag_alias(unsigned int old_idx, unsigned int new_idx); // --- Scopes --- + /// Scope controlling the state of defined variables ScopeHandle new_scope_var(const Span& sp); + /// Scope controlling the state of temporaries created within it ScopeHandle new_scope_temp(const Span& sp); + /// Scope for split code paths (e.g. `if`) ScopeHandle new_scope_split(const Span& sp); + /// Scope for escapable code paths (e.g. `loop`) ScopeHandle new_scope_loop(const Span& sp); + /// Prevent any mutation of states above this scope until `unfreeze_scope` is called ScopeHandle new_scope_freeze(const Span& sp); /// Raises every variable defined in the source scope into the target scope @@ -364,12 +420,8 @@ class MirBuilder void end_split_arm_early(const Span& sp); /// Terminates the current split condition clause (used for the conditional portion of a match arm) void end_split_condition(const Span& sp, const ScopeHandle&); - - void uncomplete_scope(const ScopeHandle& scope) { - auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx ); - auto& s = m_scopes.at(*it); - s.complete = false; - } + /// Allows mutation through a freeze scope (see `new_scope_freeze`) + void unfreeze_scope(const Span& sp, const ScopeHandle& ); const ScopeHandle& fcn_scope() const { return m_fcn_scope; @@ -381,18 +433,25 @@ class MirBuilder void moved_lvalue(const Span& sp, const ::MIR::LValue& lv); private: enum class SlotType { + /// @brief Local variable (either a binding or a temporary, it matters not). Maps to `LValue::Local` Local, // Local ~0u is return + /// Function argument. Maps to `LValue::Argument` Argument }; - const VarState& get_slot_state(const Span& sp, unsigned int idx, SlotType type, unsigned int skip_count=0) const; + const VarState& get_slot_state(const Span& sp, unsigned int idx, SlotType type, const ScopeHandle* above_scope=nullptr) const; VarState& get_slot_state_mut(const Span& sp, unsigned int idx, SlotType type); VarState* get_val_state_mut_p(const Span& sp, const ::MIR::LValue& lv, bool expect_valid=false); + void merge_split_lists(const Span& sp, const ScopeHandle& handle, + const ::std::map& states, ::std::map& end_states, MirBuilder::SlotType type + ); + void terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_loop); void drop_value_from_state(const Span& sp, const VarState& vs, ::MIR::LValue lv); void drop_scope_values(const ScopeDef& sd); + /// Finalise a scope before it's fully destroyed. Doesn't emit destructors (already done by `drop_scope_values`) void complete_scope(ScopeDef& sd); public: @@ -445,8 +504,9 @@ class MirConverter: //virtual void destructure_from(const Span& sp, const ::HIR::Pattern& pat, ::MIR::LValue lval, bool allow_refutable=false) = 0; virtual void define_vars_from(const Span& sp, const ::HIR::Pattern& pat) = 0; - virtual void destructure_from_list(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::LValue lval, const ::std::vector& bindings) = 0; - virtual void destructure_aliases_from_list(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::LValue lval, const ::std::vector& bindings) = 0; + virtual void destructure_from_list(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::LValue lval, const ::std::vector& bindings, bool update_states=true) = 0; + virtual MIR::LValue get_value_for_binding_path(const Span& sp, const ::HIR::TypeRef& outer_ty, const ::MIR::LValue& outer_lval, const PatternBinding& b) = 0; + virtual const HIR::TypeRef& get_binding_type(const Span& sp, unsigned index) const = 0; virtual SaveAndEditVal disable_borrow_extension() = 0; }; diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index f8e677c43..ccba5360d 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -306,53 +306,71 @@ void MIR_LowerHIR_Let(MirBuilder& builder, MirConverter& conv, const Span& sp, c // Handles lowering non-trivial matches to MIR // - Non-trivial means that there's more than one pattern +// - Trivial matches are handled using `MIR_LowerHIR_Let` void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val ) { - // TODO: If any arm moves a non-Copy value, then mark `match_val` as moved TRACE_FUNCTION; - + // NOTE: Lowers to the following pattern: + // ``` + // loop { // `match_scope` + // let _value = foo; + // if let Ok(_) = _value { + // if bar() { + // break { 1 }; + // } + // } + // if let Ok(v) = _value { + // if true { + // break v; + // } + // } + // if let Err(_) = _value { + // if true { + // break panic(); + // } + // } + // diverge() + // } + // ``` + + // Indicates that an arm has a guard (which prevents most of the match optimisations from working) bool fall_back_on_simple = false; const auto& match_ty = node.m_value->m_res_type; auto result_val = builder.new_temporary( node.m_res_type ); auto next_block = builder.new_bb_unlinked(); - // 1. Stop the current block so we can generate code - auto first_cmp_block = builder.pause_cur_block(); + /// Top level scope for the match + auto match_scope = builder.new_scope_loop(node.span()); - auto match_scope = builder.new_scope_split(node.span()); + // 1. Stop the current block so we can generate code before generating the pattern matching code + auto first_cmp_block = builder.pause_cur_block(); - // Map of arm index to ruleset + /// Entries for each arm, containing the code to run for each ::std::vector< ArmCode> arm_code; + /// Final list of rules (flattened patterns), for all patterns t_arm_rules arm_rules; + + // For each arm, generate the contents of the logical `if pattern_matches { if guard { break body; } }` for(unsigned int arm_idx = 0; arm_idx < node.m_arms.size(); arm_idx ++) { TRACE_FUNCTION_FR("ARM " << arm_idx, "ARM " << arm_idx); /*const*/ auto& arm = node.m_arms[arm_idx]; const Span& sp = arm.m_code->span(); - ArmCode ac; - // Register introduced bindings to be dropped on return/diverge within this scope - auto drop_scope = builder.new_scope_var( arm.m_code->span() ); - // - Define variables from the first pattern - conv.define_vars_from(node.span(), arm.m_patterns.front()); - - auto arm_body_block = builder.new_bb_unlinked(); - - auto pat_scope = builder.new_scope_split(node.span()); + // --- + // Convert all patterns on this arm into flattened "rules" + // --- auto first_arm_rule_idx = arm_rules.size(); for( unsigned int pat_idx = 0; pat_idx < arm.m_patterns.size(); pat_idx ++ ) { const auto& pat = arm.m_patterns[pat_idx]; - // - Convert HIR pattern into ruleset auto pat_builder = PatternRulesetBuilder { builder.resolve() }; pat_builder.append_from(node.span(), pat, match_ty); size_t first_rule = arm_rules.size(); for(auto& sr : pat_builder.m_rulesets) { - ::std::sort(sr.m_bindings.begin(), sr.m_bindings.end(), - [](const PatternBinding& a, const PatternBinding& b){ return a.binding->m_slot < b.binding->m_slot; }); size_t i = &sr - &pat_builder.m_rulesets.front(); if( sr.m_is_impossible ) { @@ -361,6 +379,9 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod else { DEBUG("ARM PAT (" << arm_idx << "," << pat_idx << " #" << i << ") " << pat << " ==> [" << sr.m_rules << "]"); + // Sort the binding lists, so we can check that the lists are compatible + ::std::sort(sr.m_bindings.begin(), sr.m_bindings.end(), + [](const PatternBinding& a, const PatternBinding& b){ return a.binding->m_slot < b.binding->m_slot; }); // Ensure that all patterns binding to the same set of variables (only check the variables) if( first_rule < arm_rules.size() ) { const auto& fr = arm_rules[first_rule]; @@ -375,157 +396,333 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod } } - // Generate `if` guard and destructuring code - // - Prefers to use one copy (if all rules have the same binding set) - { - auto _dbe = conv.disable_borrow_extension(); - // Do all rules in this `match` arm have the same set of bindings? - // - Checks in `arm_rules`, which has all arms in it - bool same_bindings = std::all_of(arm_rules.begin() + first_arm_rule_idx+1, arm_rules.end(), - [&](const PatternRuleset& r)->bool{ return r.m_bindings == arm_rules[first_arm_rule_idx].m_bindings; } - ); + ArmCode ac; - auto emit_condition = [&](MIR::BasicBlockId& cond_false, const std::vector& bindings) { - if( !arm.m_guards.empty() ) - { - TRACE_FUNCTION_FR("CONDITIONAL", "CONDITIONAL"); + /// Block allocated for the body code of this arm (jumped to after bindings are set) + auto arm_body_block = builder.new_bb_unlinked(); - const Span& top_span = arm.m_guards.front().val->span(); + /// Block for when the first rule matches (contains the guard and binding setup for this rule) + auto entry_block_pat0 = builder.new_bb_unlinked(); + builder.set_cur_block( entry_block_pat0 ); - // Set up a scope that doesn't allow modification of variable states outside it - auto freeze_scope = builder.new_scope_freeze(top_span); - conv.destructure_aliases_from_list(arm.m_code->span(), match_ty, match_val.clone(), bindings); + // Split scope for the `if pattern_matches { }` outer arm, + auto pat_scope = builder.new_scope_split(node.span()); + builder.end_split_arm(sp, pat_scope, /*reachable=*/true); // Inject the `else` case first, this should not push any statements + + // Generate code for this arm (guard, destructuring, and body) + { + // Scopes present for the body (generated during guard processing) + // - Temporary/variable scopes, and split scopes + struct MatchScope { + ScopeHandle handle; + bool is_split; + }; + std::vector scopes; + + const auto& bindings0 = arm_rules[first_arm_rule_idx].m_bindings; + // Create aliases for every binding that only allows shared/immutable access (for use in the guard) + auto aliases = builder.save_aliases(); + std::vector binding_temps; + std::vector binding_temps_alt(bindings0.size(), ~0u); + for(const auto& b : bindings0) + { + HIR::TypeRef final_ty = conv.get_binding_type(sp, b.binding->m_slot).clone(); + const Span& sp = arm.m_code->span(); + auto val = conv.get_value_for_binding_path(sp, match_ty, match_val, b); + DEBUG("Set alias for: " << *b.binding << " := " << val); + if( b.binding->m_type != ::HIR::PatternBinding::Type::Move ) { + final_ty.get_unique().as_Borrow().type = ::HIR::BorrowType::Shared; + // Not a move binding, still need to borrow but no deref + // - Or, make another temporary for the borrow (no scope needed) + auto tmp2 = builder.new_temporary(final_ty); + binding_temps_alt[binding_temps.size()] = tmp2.as_Local(); + builder.push_stmt_assign(sp, tmp2.clone(), ::MIR::RValue::make_Borrow({ ::HIR::BorrowType::Shared, false, std::move(val) })); + val = std::move(tmp2); + } + // Allocate a temporary to hold a borrow of that type + auto tmp = builder.new_temporary(::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, std::move(final_ty))); + // - Store the temporary index so later copies can write to it + binding_temps.push_back(tmp.as_Local()); + // Assign the temporary with a borrow of the other slot + builder.push_stmt_assign(sp, tmp.clone(), ::MIR::RValue::make_Borrow({ ::HIR::BorrowType::Shared, false, std::move(val) })); + // And set an alias to point to `*temp` + builder.add_variable_alias(sp, b.binding->m_slot, ::HIR::PatternBinding::Type::Move, ::MIR::LValue::new_Deref(std::move(tmp))); + } + + // Require that either there's no guards, or that there's only one rule + // - Otherwise, we can't (currently) prevent use-after-free + // This is expected to fail at some point, but more testing needed elsewhere + bool should_freeze = (!arm.m_guards.empty() && first_arm_rule_idx+1 < arm_rules.size()); + scopes.push_back({ builder.new_scope_freeze(sp), false }); + if( !should_freeze ) { + builder.unfreeze_scope(sp, scopes.front().handle); + } + + // Block at the start of the saved guard data + auto block0 = builder.pause_cur_block(); + builder.set_cur_block(block0); + // Start saving code (the copyable part of the guard, after the assignment of the binding temporaries) + auto cs_h = builder.code_save_start(); + MIR::BasicBlockId cond_false_block_pat0 = ~0u; + // Emit the condtion using the first set of bindings + if( !arm.m_guards.empty() ) + { + auto _dbe = conv.disable_borrow_extension(); + // Emit the guard code + TRACE_FUNCTION_FR("CONDITIONAL", "CONDITIONAL"); - // A scope for temporary variables defined within these expressions - auto tmp_scope = builder.new_scope_temp(top_span); - //auto split_scope = builder.new_scope_split(top_span); + // The guards are chanined, and all must match for the arm to be taken + // I.e. These are ANDs + for( auto& c : arm.m_guards ) + { + const Span& sp = c.val->span(); + // Emit the logical `if !guard { } else { ... }` + + /// Block for when this guard successfully matches + auto destructure = builder.new_bb_unlinked(); + + // Make a temp scope and push + scopes.push_back({ builder.new_scope_temp(c.val->span()), false }); + conv.visit_node_ptr( c.val ); + MIR::LValue match_cond_val = builder.get_result_in_lvalue(c.val->span(), c.val->m_res_type); + DEBUG("GUARD " << c.pat << " = " << match_cond_val); + + // If this is not a pattern-match, terminate the temporary scope here + if( c.is_if ) { + auto t = builder.new_temporary(c.val->m_res_type); + builder.push_stmt_assign(c.val->span(), t.clone(), std::move(match_cond_val)); + match_cond_val = std::move(t); + builder.terminate_scope(sp, std::move(scopes.back().handle)); + scopes.pop_back(); + } - bool is_cond_bb_set = false; + // Generate simplified rules from patterns + auto pat_builder = PatternRulesetBuilder { builder.resolve() }; + pat_builder.append_from(node.span(), c.pat, c.val->m_res_type); - // The guards are chanined, and all must match for the arm to be taken - // I.e. These are ANDs - for( auto& c : arm.m_guards ) + /// Block for when a pattern fails to match + auto local_false = builder.new_bb_unlinked(); + bool local_false_used = false; + // OR'd patterns + ::std::vector> ends; + for(auto& sr : pat_builder.m_rulesets) { - // TODO: Define variables from all patterns so they don't get dropped by the tmp/freeze? - conv.visit_node_ptr( c.val ); - MIR::LValue match_cond_val = builder.get_result_in_lvalue(c.val->span(), c.val->m_res_type); - DEBUG("GUARD " << c.pat << " = " << match_cond_val); - - /// Block for when this guard successfully matches - auto destructure = builder.new_bb_unlinked(); - - // Generate simplified rules from patterns - auto pat_builder = PatternRulesetBuilder { builder.resolve() }; - pat_builder.append_from(node.span(), c.pat, c.val->m_res_type); - - /// Block for when a pattern fails to match - auto local_false = builder.new_bb_unlinked(); - /// Was an arm seen that was possible to match? (indicates that `local_false` has been set as the current block) - bool had_possible = false; - // These are ORs - multiple options for this guard pattern to match - for(auto& sr : pat_builder.m_rulesets) - { - DEBUG("sr.m_rules = " << sr.m_rules); - if( sr.m_is_impossible ) { - // The rule is impossible, so don't visit - } - else { - - if( had_possible ) { - local_false = builder.new_bb_unlinked(); - } - - ASSERT_BUG(c.val->span(), builder.block_active(), "Block not active"); - MIR_LowerHIR_Match_Simple__GeneratePattern(builder, c.val->span(), sr.m_rules.data(), sr.m_rules.size(), - c.val->m_res_type, match_cond_val, 0, local_false); - conv.destructure_from_list(arm.m_code->span(), c.val->m_res_type, match_cond_val.clone(), sr.m_bindings); - builder.end_block(::MIR::Terminator::make_Goto(destructure)); - builder.set_cur_block(local_false); - had_possible = true; - } - } - if(!is_cond_bb_set) { - cond_false = builder.new_bb_unlinked(); - is_cond_bb_set = true; - // No patterns as output, so `false` is unreachable? - } - if( had_possible ) { - // Currently in `local_false` - DEBUG("GUARD: Clean up and jump to `cond_false`"); - builder.terminate_scope_early( arm.m_code->span(), tmp_scope ); - builder.uncomplete_scope(tmp_scope); // Remove the `complete` flag - //builder.end_split_arm(arm.m_code->span(), split_scope, true); - builder.end_block(::MIR::Terminator::make_Goto(cond_false)); + if( sr.m_is_impossible ) { + // The rule is impossible, so don't visit } else { - // TODO: What does it mean if there's no possible arms? + + if( local_false_used ) { + local_false = builder.new_bb_unlinked(); + } + + ASSERT_BUG(c.val->span(), builder.block_active(), "Block not active"); + MIR_LowerHIR_Match_Simple__GeneratePattern(builder, c.val->span(), sr.m_rules.data(), sr.m_rules.size(), + c.val->m_res_type, match_cond_val, 0, local_false); + ends.push_back(std::make_pair(builder.pause_cur_block(), &sr)); + builder.set_cur_block(local_false); + local_false_used = true; } + } + if( !local_false_used ) { + // None of the patterns were possible? + TODO(sp, "No possible arms in a `if-let` guard?"); + } + if( cond_false_block_pat0 == ~0u ) { + cond_false_block_pat0 = builder.new_bb_unlinked(); + } + // Split scope for the body of this logical `if` + scopes.push_back({ builder.new_scope_split(sp), true }); + builder.end_split_arm(sp, scopes.back().handle, true); + // Currently in `local_false` + DEBUG("GUARD: Clean up and jump to `cond_false`"); + // End the top scope early, which also handles ending all intervening scopes + builder.terminate_scope_early(sp, scopes.front().handle); + // Indicate an exit point to the split + builder.end_split_arm(arm.m_code->span(), pat_scope, /*reachable*/true, /*early*/true); + builder.end_block(::MIR::Terminator::make_Goto(cond_false_block_pat0)); + + // Introduce a local variable scope for the new bindings + scopes.push_back({ builder.new_scope_var(c.val->span()), false }); + for(const auto& b : ends.front().second->m_bindings ) { + builder.define_variable(b.binding->m_slot); + } - ASSERT_BUG(node.span(), !builder.block_active(), "Block still active?"); - builder.set_cur_block(destructure); + // Only introduce the new bindings (with `destructure_from_list`) after handling the early-exit case + // - This stops the `terminate_scope_early` from dropping too eagerly + for(const auto& e : ends) { + builder.set_cur_block(e.first); + conv.destructure_from_list(arm.m_code->span(), c.val->m_res_type, match_cond_val.clone(), e.second->m_bindings, /*update_states=*/&e == ends.data()); + builder.end_block(::MIR::Terminator::make_Goto(destructure)); } - // Now we're in BB`destructure` from the last loop - // End scopes, releasing temporaries - //builder.terminate_scope( arm.m_code->span(), std::move(split_scope) ); - builder.terminate_scope( arm.m_code->span(), std::move(tmp_scope) ); - builder.terminate_scope( arm.m_code->span(), std::move(freeze_scope) ); + ASSERT_BUG(node.span(), !builder.block_active(), "Block still active?"); + builder.set_cur_block(destructure); + } + } + // Release the freezing of outer states + if( should_freeze ) { + // NOTE: The first scope should be the freeze + builder.unfreeze_scope(sp, scopes.front().handle); + } + // And undo aliases + builder.restore_aliases(std::move(aliases)); + auto guard_end_block = builder.new_bb_unlinked(); + builder.end_block( ::MIR::Terminator::make_Goto(guard_end_block) ); + auto guard_code = builder.code_save_end(std::move(cs_h)); + builder.set_cur_block(guard_end_block); + // Emit actual bindings + DEBUG("Arm " << arm_idx << " rule " << 0 << ": Destructure"); + scopes.push_back({ builder.new_scope_var(arm.m_code->span()), false }); + conv.define_vars_from(node.span(), arm.m_patterns.front()); + conv.destructure_from_list(arm.m_code->span(), match_ty, match_val.clone(), bindings0); + builder.end_block(::MIR::Terminator::make_Goto(arm_body_block)); + // Emit body code + DEBUG("-- Body Code"); + + scopes.push_back({ builder.new_scope_temp(arm.m_code->span()), false }); + builder.set_cur_block( arm_body_block ); + + // Push the MovedOut state up into the split's m_cond_state, so that values moved + // in the pattern are recognized as moved in following branches. + //builder.end_split_condition( arm.m_code->span(), match_scope ); + + conv.visit_node_ptr( arm.m_code ); + + if( builder.block_active() ) { + // - Set result + auto res = builder.get_result(arm.m_code->span()); + builder.push_stmt_assign( arm.m_code->span(), result_val.clone(), mv$(res) ); + } + else { + assert(!builder.has_result()); + } + // Pop/end scopes + while(!scopes.empty()) { + if( scopes.back().is_split ) { + builder.end_split_arm(arm.m_code->span(), scopes.back().handle, /*reachable*/builder.block_active()); } + builder.terminate_scope( arm.m_code->span(), std::move(scopes.back().handle), builder.block_active() ); + scopes.pop_back(); + } + builder.end_split_arm(arm.m_code->span(), pat_scope, /*reachable*/builder.block_active()); + builder.terminate_scope( sp, std::move(pat_scope), builder.block_active() ); + builder.terminate_scope_early(sp, match_scope); + + // Go to the next block (out of the match) (if the body didn't diverge) + if( builder.block_active() ) { + builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); + } - conv.destructure_from_list(arm.m_code->span(), match_ty, match_val.clone(), bindings); - // TODO: Previous versions had reachable=false here (causing a use-after-free), would having `true` lead to leaks? - builder.end_split_arm( arm.m_code->span(), pat_scope, /*reachable=*/true ); - builder.end_block(::MIR::Terminator::make_Goto(arm_body_block)); - }; - if( same_bindings ) + // The first rule just uses the code generated above { - // If the patterns share the same set of bindings (same paths), the `condition` code can be shared - TRACE_FUNCTION_FR("Bindings (common)", "Bindings (common)"); - MIR::BasicBlockId cond_false_block = ~0u; - auto entry_block = builder.new_bb_unlinked(); - builder.set_cur_block( entry_block ); + ArmCode::Pattern acp; + acp.entry = entry_block_pat0; + acp.cond_false = cond_false_block_pat0; + ac.rules.push_back(acp); + } + // Subsequent rules clone the guard with different values for the bindings, and (importantly) a different failure exit point + for(size_t i = first_arm_rule_idx+1; i < arm_rules.size(); i ++) + { + TRACE_FUNCTION_FR( + "Bindings (AR" << i << ")", + "Bindings (AR" << i << ")" + ); - emit_condition(cond_false_block, arm_rules[first_arm_rule_idx].m_bindings); + // Clone guard code, with the two exit blocks updated, and references updated + struct Mapper + : public MirBuilder::CloneMapper + { + MIR::BasicBlockId block0; + MIR::BasicBlockId cond_false; + MIR::BasicBlockId cond_true; + MIR::BasicBlockId new_cond_false; + MIR::BasicBlockId new_cond_true; + + Mapper(MirBuilder& builder, MIR::BasicBlockId block0, MIR::BasicBlockId cond_false, MIR::BasicBlockId cond_true) + : block0(block0) + , cond_false(cond_false) + , cond_true (cond_true ) + , new_cond_false(builder.new_bb_unlinked()) + , new_cond_true (builder.new_bb_unlinked()) + { + DEBUG("new_cond_false=" << new_cond_false << ", new_cond_true=" << new_cond_true); + } + MIR::BasicBlockId update_bb_ref(MIR::BasicBlockId bb_idx) { + // Any block defined before the save just propagates through + // E.g. if the guard contains a `break` + if( bb_idx < block0 ) { + return bb_idx; + } + if( bb_idx == cond_false ) { + return new_cond_false; + } + if( bb_idx == cond_true ) { + return new_cond_true; + } + BUG(Span(), "update_bb_ref: Unknown BB " << bb_idx << " " + << ": block0=" << block0 + << ", cond_false=" << cond_false + << ", cond_true=" << cond_true + ); + } + } mapper(builder, block0, cond_false_block_pat0, guard_end_block); - for(size_t i = first_arm_rule_idx; i < arm_rules.size(); i ++) + auto entry_block = builder.new_bb_unlinked(); + builder.set_cur_block( entry_block ); + // Set the binding temporaries with the correct borrows + assert(binding_temps.size() == arm_rules[i].m_bindings.size()); + for(size_t j = 0; j < binding_temps.size(); j ++) { - ArmCode::Pattern acp; - acp.entry = entry_block; - acp.cond_false = cond_false_block; - ac.rules.push_back(acp); + const auto& b = arm_rules[i].m_bindings[j]; + auto val = conv.get_value_for_binding_path(sp, match_ty, match_val, b); + DEBUG("Set alias for: " << *b.binding << " := " << val); + if( b.binding->m_type != ::HIR::PatternBinding::Type::Move ) { + MIR::LValue tmp2; + if( binding_temps_alt[j] == ~0u ) { + // Not a move binding, still need to borrow but no deref + // - Or, make another temporary for the borrow (no scope needed) + auto final_ty = conv.get_binding_type(sp, b.binding->m_slot).clone(); + final_ty.get_unique().as_Borrow().type = ::HIR::BorrowType::Shared; + tmp2 = builder.new_temporary(final_ty); + binding_temps_alt[j] = tmp2.as_Local(); + } + else { + tmp2 = ::MIR::LValue::new_Local(binding_temps_alt[j]); + } + builder.push_stmt_assign(sp, tmp2.clone(), ::MIR::RValue::make_Borrow({ ::HIR::BorrowType::Shared, false, std::move(val) })); + val = std::move(tmp2); + } + builder.push_stmt_assign( + sp, + ::MIR::LValue::new_Local(binding_temps[j]), + ::MIR::RValue::make_Borrow({ ::HIR::BorrowType::Shared, false, std::move(val) }) + ); } - } - else - { - // Different paths to the bound varibles, the condition code needs to be specialised for each pattern - for(size_t i = first_arm_rule_idx; i < arm_rules.size(); i ++) - { - TRACE_FUNCTION_FR("Bindings (AR" << i << ")", "Bindings (AR" << i << ")"); - MIR::BasicBlockId cond_false_block = ~0u; - auto entry_block = builder.new_bb_unlinked(); - builder.set_cur_block( entry_block ); + // Clone the guard contents with updated block references + builder.insert_cloned(sp, guard_code, mapper); - emit_condition(cond_false_block, arm_rules[i].m_bindings); + // Add the final bindings and jump to the body + builder.set_cur_block(mapper.new_cond_true); + DEBUG("Arm " << arm_idx << " rule " << i-first_arm_rule_idx << ": Destructure"); + conv.destructure_from_list(arm.m_code->span(), match_ty, match_val.clone(), arm_rules[i].m_bindings, /*update_dst_state*/false); + builder.end_block(::MIR::Terminator::make_Goto(arm_body_block)); - ArmCode::Pattern acp; - acp.entry = entry_block; - acp.cond_false = cond_false_block; - ac.rules.push_back(acp); - } + ArmCode::Pattern acp; + acp.entry = entry_block; + acp.cond_false = mapper.new_cond_false; + ac.rules.push_back(acp); } } - builder.terminate_scope( sp, mv$(pat_scope) ); - - // Condition - if(arm.m_guards.size() > 0) + // If there is a guard, then flag + if( !arm.m_guards.empty() ) { ac.has_condition = true; - // NOTE: Paused so that later code (which knows what the false branch will be) can end it correctly - // TODO: What to do with conditionals in the fast model? // > Could split the match on each conditional - separating such that if a conditional fails it can fall into the other compatible branches. + // For now: Disable the complex logic, and fall back to a sequence of checks. fall_back_on_simple = true; } else @@ -533,41 +730,7 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod ac.has_condition = false; } - // Code - DEBUG("-- Body Code"); - - auto tmp_scope = builder.new_scope_temp(arm.m_code->span()); - builder.set_cur_block( arm_body_block ); - - // Push the MovedOut state up into the split's m_cond_state, so that values moved - // in the pattern are recognized as moved in following branches. - builder.end_split_condition( arm.m_code->span(), match_scope ); - - conv.visit_node_ptr( arm.m_code ); - - if( !builder.block_active() && !builder.has_result() ) { - DEBUG("Arm diverged"); - // Nothing need be done, as the block diverged. - // - Drops were handled by the diverging block (if not, the below will panic) - builder.terminate_scope( arm.m_code->span(), mv$(tmp_scope), false ); - builder.terminate_scope( arm.m_code->span(), mv$(drop_scope), false ); - builder.end_split_arm( arm.m_code->span(), match_scope, false ); - } - else { - DEBUG("Arm result"); - // - Set result - auto res = builder.get_result(arm.m_code->span()); - builder.push_stmt_assign( arm.m_code->span(), result_val.clone(), mv$(res) ); - // - Drop all non-moved values from this scope - builder.terminate_scope( arm.m_code->span(), mv$(tmp_scope) ); - builder.terminate_scope( arm.m_code->span(), mv$(drop_scope) ); - // - Split end match scope - builder.end_split_arm( arm.m_code->span(), match_scope, true ); - // - Go to the next block - builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); - } - - arm_code.push_back( mv$(ac) ); + arm_code.push_back( std::move(ac) ); } // Sort columns of `arm_rules` to maximise effectiveness diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index b4c8f8967..322cb35bb 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -7,6 +7,9 @@ */ #include #include // std::min +#include +#include +#include // Target_GetPointerBits namespace MIR { ::std::ostream& operator<<(::std::ostream& os, const Constant& v) { @@ -873,3 +876,390 @@ bool MIR::SwitchValues::operator==(const SwitchValues& x) const return true; } +const HIR::TypeRef& MIR::Cloner::value_generic_type(HIR::GenericRef ce) const +{ + TODO(sp, "`value_generic_type` not implemented, shouldn't be called unless `monomorpiser` has been overridden"); +} +const Monomorphiser& MIR::Cloner::monomorphiser() const +{ + static MonomorphiserNop nop; + return nop; +} + +::HIR::TypeRef MIR::Cloner::monomorph(const ::HIR::TypeRef& ty) const { + TRACE_FUNCTION_F(ty); + auto rv = monomorphiser().monomorph_type(sp, ty); + if(auto* r = resolve()) { + r->expand_associated_types(sp, rv); + } + return rv; +} +::HIR::GenericPath MIR::Cloner::monomorph(const ::HIR::GenericPath& ty) const { + TRACE_FUNCTION_F(ty); + auto rv = monomorphiser().monomorph_genericpath(sp, ty, false); + if(const auto* r = resolve()) { + for(auto& arg : rv.m_params.m_types) + r->expand_associated_types(sp, arg); + } + return rv; +} +::HIR::Path MIR::Cloner::monomorph(const ::HIR::Path& ty) const { + TRACE_FUNCTION_F(ty); + auto rv = monomorphiser().monomorph_path(sp, ty, false); + if(const auto* r = resolve()) { + TU_MATCH(::HIR::Path::Data, (rv.m_data), (e2), + (Generic, + for(auto& arg : e2.m_params.m_types) + r->expand_associated_types(sp, arg); + ), + (UfcsInherent, + r->expand_associated_types(sp, e2.type); + for(auto& arg : e2.params.m_types) + r->expand_associated_types(sp, arg); + // TODO: impl params too? + for(auto& arg : e2.impl_params.m_types) + r->expand_associated_types(sp, arg); + ), + (UfcsKnown, + r->expand_associated_types(sp, e2.type); + for(auto& arg : e2.trait.m_params.m_types) + r->expand_associated_types(sp, arg); + for(auto& arg : e2.params.m_types) + r->expand_associated_types(sp, arg); + ), + (UfcsUnknown, + BUG(sp, "Encountered UfcsUnknown"); + ) + ) + } + return rv; +} +::HIR::PathParams MIR::Cloner::monomorph(const ::HIR::PathParams& ty) const { + TRACE_FUNCTION_F(ty); + auto rv = monomorphiser().monomorph_path_params(sp, ty, false); + if(const auto* r = resolve()) { + for(auto& arg : rv.m_types) + r->expand_associated_types(sp, arg); + } + return rv; +} + +::std::vector MIR::Cloner::clone_asm_params(const ::std::vector& params) const +{ + ::std::vector rv; + for(const auto& p : params) + { + TU_MATCH_HDRA((p), {) + TU_ARMA(Const, v) + rv.push_back( this->clone_constant(v) ); + TU_ARMA(Sym, v) + rv.push_back( this->monomorph(v) ); + TU_ARMA(Reg, v) + rv.push_back(::MIR::AsmParam::make_Reg({ + v.dir, + v.spec.clone(), + v.input ? box$(this->clone_param(*v.input)) : std::unique_ptr(), + v.output ? box$(this->clone_lval(*v.output)) : std::unique_ptr() + })); + } + } + return rv; +} +::MIR::Statement MIR::Cloner::clone_stmt(const ::MIR::Statement& src) const +{ + TU_MATCH_HDRA( (src), { ) + TU_ARMA(Assign, se) { + return ::MIR::Statement::make_Assign({ + this->clone_lval(se.dst), + this->clone_rval(se.src) + }); + } + TU_ARMA(Asm, se) { + return ::MIR::Statement::make_Asm({ + se.tpl, + this->clone_name_lval_vec(se.outputs), + this->clone_name_lval_vec(se.inputs), + se.clobbers, + se.flags + }); + } + TU_ARMA(Asm2, se) { + return ::MIR::Statement::make_Asm2({ + se.options, + se.lines, + this->clone_asm_params(se.params) + }); + } + TU_ARMA(SetDropFlag, se) { + return ::MIR::Statement::make_SetDropFlag({ + map_drop_flag(se.idx), + se.new_val, + se.other == ~0u ? ~0u : map_drop_flag(se.other) + }); + } + TU_ARMA(SaveDropFlag, se) { + TODO(Span(), "clone_bb SaveDropFlag"); + } + TU_ARMA(LoadDropFlag, se) { + TODO(Span(), "clone_bb LoadDropFlag"); + } + TU_ARMA(Drop, se) { + return ::MIR::Statement::make_Drop({ + se.kind, + this->clone_lval(se.slot), + se.flag_idx == ~0u ? ~0u : map_drop_flag(se.flag_idx) + }); + } + TU_ARMA(ScopeEnd, se) { + ::MIR::Statement::Data_ScopeEnd new_se; + new_se.slots.reserve(se.slots.size()); + for(auto idx : se.slots) + new_se.slots.push_back(map_local(idx)); + return ::MIR::Statement( mv$(new_se) ); + } + } + throw ""; +} +::MIR::Terminator MIR::Cloner::clone_term(const ::MIR::Terminator& src) const +{ + TU_MATCH_HDRA( (src), { ) + TU_ARMA(Incomplete, se) { + return ::MIR::Terminator::make_Incomplete({}); + } + TU_ARMA(Return, se) { + return ::MIR::Terminator::make_Return({}); + } + TU_ARMA(Diverge, se) { + return ::MIR::Terminator::make_Diverge({}); + } + TU_ARMA(Panic, se) { + return ::MIR::Terminator::make_Panic({}); + } + TU_ARMA(Goto, se) { + return ::MIR::Terminator::make_Goto(map_bb_idx(se)); + } + TU_ARMA(If, se) { + return ::MIR::Terminator::make_If({ + this->clone_lval(se.cond), + map_bb_idx(se.bb_true ), + map_bb_idx(se.bb_false) + }); + } + TU_ARMA(Switch, se) { + ::std::vector<::MIR::BasicBlockId> arms; + arms.reserve(se.targets.size()); + for(const auto& bbi : se.targets) + arms.push_back( map_bb_idx(bbi) ); + return ::MIR::Terminator::make_Switch({ this->clone_lval(se.val), mv$(arms) }); + } + TU_ARMA(SwitchValue, se) { + ::std::vector<::MIR::BasicBlockId> arms; + arms.reserve(se.targets.size()); + for(const auto& bbi : se.targets) + arms.push_back( map_bb_idx(bbi) ); + return ::MIR::Terminator::make_SwitchValue({ this->clone_lval(se.val), map_bb_idx(se.def_target), mv$(arms), se.values.clone() }); + } + TU_ARMA(Call, se) { + ::MIR::CallTarget tgt; + TU_MATCHA( (se.fcn), (ste), + (Value, + tgt = ::MIR::CallTarget::make_Value( this->clone_lval(ste) ); + ), + (Path, + tgt = ::MIR::CallTarget::make_Path( this->monomorph(ste) ); + ), + (Intrinsic, + tgt = ::MIR::CallTarget::make_Intrinsic({ ste.name, this->monomorph(ste.params) }); + ) + ) + return ::MIR::Terminator::make_Call({ + map_bb_idx(se.ret_block), + map_bb_idx(se.panic_block), + this->clone_lval(se.ret_val), + mv$(tgt), + this->clone_param_vec(se.args) + }); + } + } + throw ""; +} +::std::vector< ::std::pair<::std::string,::MIR::LValue> > MIR::Cloner::clone_name_lval_vec(const ::std::vector< ::std::pair<::std::string,::MIR::LValue> >& src) const +{ + ::std::vector< ::std::pair<::std::string,::MIR::LValue> > rv; + rv.reserve(src.size()); + for(const auto& e : src) + rv.push_back(::std::make_pair(e.first, this->clone_lval(e.second))); + return rv; +} +::std::vector<::MIR::LValue> MIR::Cloner::clone_lval_vec(const ::std::vector<::MIR::LValue>& src) const +{ + ::std::vector<::MIR::LValue> rv; + rv.reserve(src.size()); + for(const auto& lv : src) + rv.push_back( this->clone_lval(lv) ); + return rv; +} +::std::vector<::MIR::Param> MIR::Cloner::clone_param_vec(const ::std::vector<::MIR::Param>& src) const +{ + ::std::vector<::MIR::Param> rv; + rv.reserve(src.size()); + for(const auto& lv : src) + rv.push_back( this->clone_param(lv) ); + return rv; +} + +::MIR::LValue MIR::Cloner::clone_lval(const ::MIR::LValue& src) const +{ + auto wrappers = src.m_wrappers; + for(auto& w : wrappers) + { + if( w.is_Index() ) { + w = ::MIR::LValue::Wrapper::new_Index( map_local(w.as_Index()) ); + } + } + TU_MATCH_HDRA( (src.m_root), {) + TU_ARMA(Return, se) { + return ::MIR::LValue( ::MIR::LValue::Storage::new_Return(), mv$(wrappers) ); + } + TU_ARMA(Argument, se) { + return ::MIR::LValue( ::MIR::LValue::Storage::new_Argument(se), mv$(wrappers) ); + } + TU_ARMA(Local, se) { + return ::MIR::LValue( ::MIR::LValue::Storage::new_Local(this->map_local(se)), mv$(wrappers) ); + } + TU_ARMA(Static, se) { + return ::MIR::LValue( ::MIR::LValue::Storage::new_Static(this->monomorph(se)), mv$(wrappers) ); + } + } + throw ""; +} +::MIR::Constant MIR::Cloner::clone_constant(const ::MIR::Constant& src) const +{ + TU_MATCH_HDRA( (src), {) + TU_ARMA(Int , ce) return ::MIR::Constant(ce); + TU_ARMA(Uint , ce) return ::MIR::Constant(ce); + TU_ARMA(Float, ce) return ::MIR::Constant(ce); + TU_ARMA(Bool , ce) return ::MIR::Constant(ce); + TU_ARMA(Bytes, ce) return ::MIR::Constant(ce); + TU_ARMA(StaticString, ce) return ::MIR::Constant(ce); + TU_ARMA(Const, ce) { + return ::MIR::Constant::make_Const({ box$(this->monomorph(*ce.p)) }); + } + TU_ARMA(Generic, ce) { + auto val = monomorphiser().get_value(sp, ce); + TU_MATCH_HDRA( (val), {) + default: + TODO(sp, "Monomorphise MIR generic constant " << ce << " = " << val); + TU_ARMA(Generic, ve) { + return ve; + } + TU_ARMA(Evaluated, ve) { + const auto& ty = this->value_generic_type(ce); + auto v = EncodedLiteralSlice(*ve); + ASSERT_BUG(sp, ty.data().is_Primitive(), "Handle non-primitive const generic: " << ty); + // TODO: This is duplicated in `mir/from_hir_match.cpp` - De-duplicate? + switch(ty.data().as_Primitive()) + { + case ::HIR::CoreType::Bool: return ::MIR::Constant::make_Bool({ v.read_uint(1) != 0 }); + case ::HIR::CoreType::U8: + case ::HIR::CoreType::U16: + case ::HIR::CoreType::U32: + case ::HIR::CoreType::U64: + case ::HIR::CoreType::U128: return ::MIR::Constant::make_Uint({ v.read_uint(ve->bytes.size()), ty.data().as_Primitive() }); + case ::HIR::CoreType::Usize: return ::MIR::Constant::make_Uint({ v.read_uint(Target_GetPointerBits() / 8), ty.data().as_Primitive() }); + case ::HIR::CoreType::I8: + case ::HIR::CoreType::I16: + case ::HIR::CoreType::I32: + case ::HIR::CoreType::I64: + case ::HIR::CoreType::I128: return ::MIR::Constant::make_Int({ v.read_sint(ve->bytes.size()), ty.data().as_Primitive() }); + case ::HIR::CoreType::Isize: return ::MIR::Constant::make_Int({ v.read_sint(Target_GetPointerBits() / 8), ty.data().as_Primitive() }); + case ::HIR::CoreType::F16: + case ::HIR::CoreType::F32: + case ::HIR::CoreType::F64: + case ::HIR::CoreType::F128: return ::MIR::Constant::make_Float({ v.read_float(ve->bytes.size()), ty.data().as_Primitive() }); + case ::HIR::CoreType::Char: return ::MIR::Constant::make_Uint({ v.read_uint(4), ty.data().as_Primitive() }); + case ::HIR::CoreType::Str: BUG(sp, "`str` const generic"); + } + } + } + } + TU_ARMA(Function, ce) { + return ::MIR::Constant::make_Function({ box$(this->monomorph(*ce.p)) }); + } + TU_ARMA(ItemAddr, ce) { + if(!ce) + return ::MIR::Constant::make_ItemAddr({}); + return ::MIR::Constant::make_ItemAddr(box$(this->monomorph(*ce))); + } + } + throw ""; +} +::MIR::Param MIR::Cloner::clone_param(const ::MIR::Param& src) const +{ + TU_MATCHA( (src), (se), + (LValue, + return clone_lval(se); + ), + (Borrow, + return ::MIR::Param::make_Borrow({ se.type, this->clone_lval(se.val) }); + ), + (Constant, + return clone_constant(se); + ) + ) + throw ""; +} +::MIR::RValue MIR::Cloner::clone_rval(const ::MIR::RValue& src) const +{ + TU_MATCH_HDRA( (src), {) + TU_ARMA(Use, se) { + //if( const auto* ae = se.opt_Argument() ) + // if( const auto* e = this->te.args.at(ae->idx).opt_Constant() ) + // return e->clone(); + return ::MIR::RValue( this->clone_lval(se) ); + } + TU_ARMA(Constant, se) { + return this->clone_constant(se); + } + TU_ARMA(SizedArray, se) { + return ::MIR::RValue::make_SizedArray({ this->clone_param(se.val), monomorphiser().monomorph_arraysize(sp, se.count) }); + } + TU_ARMA(Borrow, se) { + return ::MIR::RValue::make_Borrow({ se.type, se.is_raw, this->clone_lval(se.val) }); + } + TU_ARMA(Cast, se) { + return ::MIR::RValue::make_Cast({ this->clone_lval(se.val), this->monomorph(se.type) }); + } + TU_ARMA(BinOp, se) { + return ::MIR::RValue::make_BinOp({ this->clone_param(se.val_l), se.op, this->clone_param(se.val_r) }); + } + TU_ARMA(UniOp, se) { + return ::MIR::RValue::make_UniOp({ this->clone_lval(se.val), se.op }); + } + TU_ARMA(DstMeta, se) { + return ::MIR::RValue::make_DstMeta({ this->clone_lval(se.val) }); + } + TU_ARMA(DstPtr, se) { + return ::MIR::RValue::make_DstPtr({ this->clone_lval(se.val) }); + } + TU_ARMA(MakeDst, se) { + return ::MIR::RValue::make_MakeDst({ this->clone_param(se.ptr_val), this->clone_param(se.meta_val) }); + } + TU_ARMA(Tuple, se) { + return ::MIR::RValue::make_Tuple({ this->clone_param_vec(se.vals) }); + } + TU_ARMA(Array, se) { + return ::MIR::RValue::make_Array({ this->clone_param_vec(se.vals) }); + } + TU_ARMA(UnionVariant, se) { + return ::MIR::RValue::make_UnionVariant({ this->monomorph(se.path), se.index, this->clone_param(se.val) }); + } + TU_ARMA(EnumVariant, se) { + return ::MIR::RValue::make_EnumVariant({ this->monomorph(se.path), se.index, this->clone_param_vec(se.vals) }); + } + TU_ARMA(Struct, se) { + return ::MIR::RValue::make_Struct({ this->monomorph(se.path), this->clone_param_vec(se.vals) }); + } + } + throw ""; +} diff --git a/src/mir/mir.hpp b/src/mir/mir.hpp index 3f685c8c2..784936940 100644 --- a/src/mir/mir.hpp +++ b/src/mir/mir.hpp @@ -16,6 +16,7 @@ #include struct MonomorphState; +class StaticTraitResolve; namespace MIR { @@ -755,5 +756,45 @@ class Function mutable EnumCachePtr trans_enum_state; }; +class Cloner +{ +public: + const Span& sp; + Cloner(const Span& sp): sp(sp) {} + + virtual ::MIR::BasicBlockId map_bb_idx(::MIR::BasicBlockId idx) const { + return idx; + } + virtual unsigned map_local(unsigned f) const { + return f; + } + virtual unsigned map_drop_flag(unsigned f) const { + return f; + } + + virtual const HIR::TypeRef& value_generic_type(HIR::GenericRef ce) const; + virtual const Monomorphiser& monomorphiser() const; + virtual const StaticTraitResolve* resolve() const { return nullptr; } + + virtual ::MIR::Statement clone_stmt(const ::MIR::Statement& src) const; + virtual ::MIR::Terminator clone_term(const ::MIR::Terminator& src) const; + + virtual ::MIR::LValue clone_lval(const ::MIR::LValue& src) const; + virtual ::MIR::RValue clone_rval(const ::MIR::RValue& src) const; + virtual ::MIR::Param clone_param(const ::MIR::Param& src) const; + virtual ::MIR::Constant clone_constant(const ::MIR::Constant& src) const; + + ::std::vector clone_asm_params(const ::std::vector& params) const; + ::std::vector< ::std::pair<::std::string,::MIR::LValue> > clone_name_lval_vec(const ::std::vector< ::std::pair<::std::string,::MIR::LValue> >& src) const; + ::std::vector<::MIR::Param> clone_param_vec(const ::std::vector<::MIR::Param>& src) const; + ::std::vector<::MIR::LValue> clone_lval_vec(const ::std::vector<::MIR::LValue>& src) const; + + // -- Monomorphise various types + ::HIR::TypeRef monomorph(const ::HIR::TypeRef& x) const; + ::HIR::GenericPath monomorph(const ::HIR::GenericPath& x) const; + ::HIR::Path monomorph(const ::HIR::Path& x) const; + ::HIR::PathParams monomorph(const ::HIR::PathParams& x) const; }; +} // namespace MIR + diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 0deb82fd0..97ac235cf 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -315,7 +315,7 @@ void MirBuilder::set_result(const Span& sp, ::MIR::RValue val) DEBUG(m_result); } -void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val, bool drop_destination/*=true*/) +void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val, bool update_dest_state/*=true*/) { DEBUG(dst << " = " << val); ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); @@ -399,7 +399,7 @@ void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RVal ) // Drop target if populated - if( drop_destination ) + if( update_dest_state ) { mark_value_assigned(sp, dst); } @@ -583,38 +583,37 @@ void MirBuilder::raise_temporaries(const Span& sp, const ::MIR::LValue& val, con target_seen = true; } - TU_IFLET( ScopeType, scope_def.data, Owning, e, + TU_MATCH_HDRA((scope_def.data), {) + TU_ARMA(Owning, e) { if( target_seen && e.is_temporary == is_temp ) { e.slots.push_back( idx ); DEBUG("- to " << *scope_it); return ; } - ) - else if( auto* sd_loop = scope_def.data.opt_Loop() ) - { + } + TU_ARMA(Loop, sd_loop) { // If there is an exit state present, ensure that this variable is // present in that state (as invalid, as it can't have been valid // externally) - if( sd_loop->exit_state_valid ) + if( sd_loop.exit_state_valid ) { DEBUG("Adding " << val << " as unset to loop exit state"); - auto v = sd_loop->exit_state.states.insert( ::std::make_pair(idx, VarState(InvalidType::Uninit)) ); + auto v = sd_loop.exit_state.states.insert( ::std::make_pair(idx, VarState(InvalidType::Uninit)) ); ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); } else { DEBUG("Crossing loop with no existing exit state"); } - } - else if( auto* sd_split = scope_def.data.opt_Split() ) - { + } + TU_ARMA(Split, sd_split) { // If the split has already registered an exit state, ensure that // this variable is present in it. (as invalid) - if( sd_split->end_state_valid ) + if( sd_split.end_state_valid ) { DEBUG("Adding " << val << " as unset to loop exit state"); - auto v = sd_split->end_state.states.insert( ::std::make_pair(idx, VarState(InvalidType::Uninit)) ); + auto v = sd_split.end_state.states.insert( ::std::make_pair(idx, VarState(InvalidType::Uninit)) ); ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); } else @@ -623,13 +622,17 @@ void MirBuilder::raise_temporaries(const Span& sp, const ::MIR::LValue& val, con } // TODO: This should update the outer state to unset. - auto& arm = sd_split->arms.back(); + auto& arm = sd_split.arms.back(); arm.states.insert(::std::make_pair( idx, get_slot_state(sp, idx, SlotType::Local).clone() )); m_slot_states.at(idx) = VarState(InvalidType::Uninit); - } - else - { - BUG(sp, "Crossing unknown scope type - " << scope_def.data.tag_str()); + } + TU_ARMA(Freeze, sde) { + // Can we raise across a freeze state? + if( !sde.unfrozen ) + { + TODO(sp, "Raising temporary across a freeze?"); + } + } } } BUG(sp, "Couldn't find a scope to raise " << val << " into"); @@ -695,11 +698,96 @@ void MirBuilder::raise_temporaries(const Span& sp, const ::MIR::RValue& rval, co ) } + +MirBuilder::SaveCodeProto MirBuilder::code_save_start() +{ + TRACE_FUNCTION; + // Push to the stack + // Create a new block and link in + static size_t s_next_index; + SaveCodeProto rv; + rv.index = s_next_index ++; + m_code_save_stack.push_back(CodeSaveStackEnt { rv.index, {} }); + // If currently in a block, then go into a new one + if(block_active()) + { + new_bb_linked(); + } + return rv; +} +MirBuilder::SavedCode MirBuilder::code_save_end(SaveCodeProto h) +{ + // Check stack + assert(!block_active()); // Can't be a block active + assert(!m_code_save_stack.empty()); + assert(h.index == m_code_save_stack.back().index); + SavedCode rv; + rv.blocks = std::move(m_code_save_stack.back().blocks); + m_code_save_stack.pop_back(); + DEBUG("rv.blocks = { " << rv.blocks << " }"); + return rv; +} +void MirBuilder::insert_cloned(const Span& sp, const SavedCode& c, CloneMapper& mapper) +{ + TRACE_FUNCTION; + assert(block_active()); // Need an active block to start inserting + if( !c.blocks.empty() ) + { + struct Cloner: ::MIR::Cloner { + CloneMapper& mapper; + std::map new_block_map; + + Cloner(const Span& sp, CloneMapper& mapper) + : ::MIR::Cloner(sp) + , mapper(mapper) + {} + + ::MIR::BasicBlockId map_bb_idx(::MIR::BasicBlockId idx) const override { + auto it = new_block_map.find(idx); + if(it != new_block_map.end()) { + return it->second; + } + return mapper.update_bb_ref(idx); + } + } cloner { sp, mapper }; + // Allocate new block IDs for all referenced blocks + for(auto bb_idx : c.blocks) + { + cloner.new_block_map.insert(std::make_pair(bb_idx, new_bb_unlinked())); + } + // End the current block with a goto to the first block + end_block(::MIR::Terminator::make_Goto({ cloner.new_block_map[c.blocks.front()] })); + + DEBUG("c.blocks = [" << c.blocks << "]"); + DEBUG("new_block_map = {" << cloner.new_block_map << "}"); + // Start inserting (and remapping) + for(auto src_idx : c.blocks) + { + auto new_idx = cloner.new_block_map.at(src_idx); + DEBUG("BB" << new_idx << " <= BB" << src_idx); + const auto& src = m_output.blocks[src_idx]; + set_cur_block(new_idx); + for(const auto& v : src.statements) { + push_stmt(sp, cloner.clone_stmt(v)); + } + end_block(cloner.clone_term(src.terminator)); + } + // Leave no active block + } +} + void MirBuilder::set_cur_block(unsigned int new_block) { ASSERT_BUG(Span(), !m_block_active, "Updating block when previous is active"); ASSERT_BUG(Span(), new_block < m_output.blocks.size(), "Invalid block ID being started - " << new_block); ASSERT_BUG(Span(), m_output.blocks[new_block].terminator.is_Incomplete(), "Attempting to resume a completed block - BB" << new_block); + // Record this new block in the save stack entries + for(auto& v : m_code_save_stack) { + // Just in case a block is saved+resumed + if( std::find(v.blocks.begin(), v.blocks.end(), new_block) == v.blocks.end() ) { + v.blocks.push_back(new_block); + } + } DEBUG("BB" << new_block << " START"); m_current_block = new_block; m_block_active = true; @@ -754,7 +842,7 @@ unsigned int MirBuilder::new_drop_flag(bool default_state) break; } } - DEBUG("(" << default_state << ") = " << rv); + DEBUG("df$" << rv << " := " << default_state); return rv; } unsigned int MirBuilder::new_drop_flag_and_set(const Span& sp, bool set_state) @@ -811,15 +899,11 @@ ScopeHandle MirBuilder::new_scope_freeze(const Span& sp) { unsigned int idx = m_scopes.size(); m_scopes.push_back( ScopeDef {sp, ScopeType::make_Freeze({})} ); - m_scopes.back().data.as_Freeze().original_aliases.resize( m_variable_aliases.size() ); - for(size_t i = 0; i < m_variable_aliases.size(); i ++) - { - m_scopes.back().data.as_Freeze().original_aliases[i] = (m_variable_aliases[i].second != MIR::LValue()); - } m_scope_stack.push_back( idx ); DEBUG("START (freeze) scope " << idx); return ScopeHandle { *this, idx }; } + void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope, bool emit_cleanup/*=true*/) { TRACE_FUNCTION_F("DONE scope " << scope.idx << " - " << (emit_cleanup ? "CLEANUP" : "NO CLEANUP")); @@ -1468,6 +1552,34 @@ void MirBuilder::terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_l } } +void MirBuilder::merge_split_lists(const Span& sp, const ScopeHandle& handle, + const ::std::map& states, ::std::map& end_states, MirBuilder::SlotType type +) +{ + // Insert copies of the parent state + for(const auto& ent : states) + { + if( end_states.count(ent.first) == 0 ) { + auto s = this->get_slot_state(sp, ent.first, type, &handle).clone(); + DEBUG("Add from parent: " << (type == SlotType::Local ? ::MIR::LValue::new_Local(ent.first) : ::MIR::LValue::new_Argument(ent.first)) << " = " << s); + end_states.insert(::std::make_pair( ent.first, std::move(s) )); + } + } + // Merge state + for(auto& ent : end_states) + { + auto idx = ent.first; + auto& out_state = ent.second; + + // Merge the states + auto it = states.find(idx); + const auto& src_state = (it != states.end() ? it->second : this->get_slot_state(sp, idx, type, &handle)); + + auto lv = (type == SlotType::Local ? ::MIR::LValue::new_Local(idx) : ::MIR::LValue::new_Argument(idx)); + merge_state(sp, *this, mv$(lv), out_state, src_state); + } +} + void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable, bool early/*=false*/) { ASSERT_BUG(sp, handle.idx < m_scopes.size(), "Handle passed to end_split_arm is invalid"); @@ -1476,6 +1588,25 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r auto& sd_split = sd.data.as_Split(); ASSERT_BUG(sp, !sd_split.arms.empty(), "Split arm list is empty (impossible)"); + // If this is not at the top of the stack (if there are other splits in the way), then get state from them + for(auto v : ::reverse(m_scope_stack)) { + if( v == handle.idx ) { + break; + } + + // If this stack entry is a Split, get the current values and add them to `sd_split` + if( const auto* other_split = m_scopes.at(v).data.opt_Split() ) { + for(auto& s : other_split->arms.back().states) { + DEBUG("In scope " << handle.idx << " _" << s.first << " = " << s.second << " (from scope " << v << ")"); + sd_split.arms.back().states[s.first] = s.second.clone(); + } + for(auto& s : other_split->arms.back().arg_states) { + DEBUG("In scope " << handle.idx << " a" << s.first << " = " << s.second << " (from scope " << v << ")"); + sd_split.arms.back().arg_states[s.first] = s.second.clone(); + } + } + } + TRACE_FUNCTION_F("end split scope " << handle.idx << " arm " << (sd_split.arms.size()-1) << (reachable ? " reachable" : "") << (early ? " early" : "")); if( reachable ) ASSERT_BUG(sp, m_block_active, "Block must be active when ending a reachable split arm"); @@ -1489,29 +1620,8 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r { DEBUG("Reachable w/ end state, merging"); - auto merge_list = [sp,this](const auto& states, auto& end_states, auto type) { - // Insert copies of the parent state - for(const auto& ent : states) { - if( end_states.count(ent.first) == 0 ) { - end_states.insert(::std::make_pair( ent.first, get_slot_state(sp, ent.first, type, 1).clone() )); - } - } - // Merge state - for(auto& ent : end_states) - { - auto idx = ent.first; - auto& out_state = ent.second; - - // Merge the states - auto it = states.find(idx); - const auto& src_state = (it != states.end() ? it->second : get_slot_state(sp, idx, type, 1)); - - auto lv = (type == SlotType::Local ? ::MIR::LValue::new_Local(idx) : ::MIR::LValue::new_Argument(idx)); - merge_state(sp, *this, mv$(lv), out_state, src_state); - } - }; - merge_list(this_arm_state.states, sd_split.end_state.states, SlotType::Local); - merge_list(this_arm_state.arg_states, sd_split.end_state.arg_states, SlotType::Argument); + merge_split_lists(sp, handle, this_arm_state.states, sd_split.end_state.states, SlotType::Local); + merge_split_lists(sp, handle, this_arm_state.arg_states, sd_split.end_state.arg_states, SlotType::Argument); } else { @@ -1531,7 +1641,7 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r } for(auto& ent : this_arm_state.arg_states) { - DEBUG("Argument(" << ent.first << ") = " << ent.second); + DEBUG("State a" << ent.first << " = " << ent.second); sd_split.end_state.arg_states.insert(::std::make_pair( ent.first, ent.second.clone() )); } sd_split.end_state_valid = true; @@ -1552,12 +1662,12 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r DEBUG("New Arm"); for(auto& ent : sd_split.cond_state.states) { - DEBUG(" Condition State _" << ent.first << " = " << ent.second); + DEBUG("Condition State _" << ent.first << " = " << ent.second); arm.states.insert(::std::make_pair( ent.first, ent.second.clone() )); } for(auto& ent : sd_split.cond_state.arg_states) { - DEBUG(" Condition Argument(" << ent.first << ") = " << ent.second); + DEBUG("Condition State a" << ent.first << " = " << ent.second); arm.arg_states.insert(::std::make_pair( ent.first, ent.second.clone() )); } sd_split.arms.push_back(mv$(arm)); @@ -1601,49 +1711,24 @@ void MirBuilder::end_split_condition(const Span& sp, const ScopeHandle& handle) const auto& this_arm_state = sd_split.arms.back(); - DEBUG("Split condition clause end: merging"); - - auto merge_list = [sp,this](const auto& states, auto& end_states, auto type) { - // Insert copies of the parent state - for(const auto& ent : states) { - if( end_states.count(ent.first) == 0 ) { - end_states.insert(::std::make_pair( ent.first, get_slot_state(sp, ent.first, type, 1).clone() )); - } - } - // Merge state - for(auto& ent : end_states) - { - auto idx = ent.first; - auto& out_state = ent.second; - - // Merge the states - auto it = states.find(idx); - const auto& src_state = (it != states.end() ? it->second : get_slot_state(sp, idx, type, 1)); + DEBUG("Split condition clause end (scope " << handle.idx << "): merging"); - auto lv = (type == SlotType::Local ? ::MIR::LValue::new_Local(idx) : ::MIR::LValue::new_Argument(idx)); - merge_state(sp, *this, mv$(lv), out_state, src_state); - } - }; - merge_list(this_arm_state.states, sd_split.cond_state.states, SlotType::Local); - merge_list(this_arm_state.arg_states, sd_split.cond_state.arg_states, SlotType::Argument); + merge_split_lists(sp, handle, this_arm_state.states , sd_split.cond_state.states , SlotType::Local ); + merge_split_lists(sp, handle, this_arm_state.arg_states, sd_split.cond_state.arg_states, SlotType::Argument); } -void MirBuilder::complete_scope(ScopeDef& sd) +void MirBuilder::unfreeze_scope(const Span& sp, const ScopeHandle& handle) { - sd.complete = true; + ASSERT_BUG(sp, handle.idx < m_scopes.size(), "Handle passed to `unfreeze_scope` is invalid"); + auto& sd = m_scopes.at( handle.idx ); + ASSERT_BUG(sp, sd.data.is_Freeze(), "Handle passed to `unfreeze_scope` was not a freeze, - " << sd.data.tag_str()); + auto& sd_e = sd.data.as_Freeze(); - TU_MATCHA( (sd.data), (e), - (Owning, - DEBUG("Owning (" << (e.is_temporary ? "temps" : "vars") << ") - " << e.slots); - ), - (Loop, - DEBUG("Loop"); - ), - (Split, - ), - (Freeze, - ) - ) + DEBUG("Unfreeze scope " << handle.idx); + sd_e.unfrozen = true; +} +void MirBuilder::complete_scope(ScopeDef& sd) +{ struct H { static void apply_end_state(const Span& sp, MirBuilder& builder, SplitEnd& end_state) { @@ -1668,10 +1753,13 @@ void MirBuilder::complete_scope(ScopeDef& sd) } }; - // No macro for better debug output. + sd.complete = true; + TU_MATCH_HDRA( (sd.data), { ) TU_ARMA(Owning, e) { } + TU_ARMA(Freeze, e) { + } TU_ARMA(Loop, e) { TRACE_FUNCTION_F("Loop"); if( e.exit_state_valid ) @@ -1697,36 +1785,6 @@ void MirBuilder::complete_scope(ScopeDef& sd) H::apply_end_state(sd.span, *this, e.end_state); } } - TU_ARMA(Freeze, e) { - TRACE_FUNCTION_F("Freeze"); - for(auto& ent : e.changed_slots) - { - auto& vs = this->get_slot_state_mut(sd.span, ent.first, SlotType::Local); - auto lv = ::MIR::LValue::new_Local(ent.first); - DEBUG(lv << " " << vs << " => " << ent.second); - if( vs != ent.second ) - { - if( vs.is_Valid() ) { - ERROR(sd.span, E0000, "Value went from " << vs << " => " << ent.second << " over freeze"); - } - else if( !this->lvalue_is_copy(sd.span, lv) ) { - ERROR(sd.span, E0000, "Non-Copy value went from " << vs << " => " << ent.second << " over freeze"); - } - else { - // It's a Copy value, and it wasn't originally fully Valid - allowable - } - } - } - - for(size_t i = 0; i < e.original_aliases.size(); i ++) - { - if( !e.original_aliases[i] && this->m_variable_aliases[i].second != MIR::LValue() ) - { - DEBUG("Reset alias on #" << i); - this->m_variable_aliases[i].second = MIR::LValue(); - } - } - } } } @@ -1914,16 +1972,24 @@ bool MirBuilder::lvalue_is_copy(const Span& sp, const ::MIR::LValue& val) const return rv == 2; } -const VarState& MirBuilder::get_slot_state(const Span& sp, unsigned int idx, SlotType type, unsigned int skip_count/*=0*/) const +const VarState& MirBuilder::get_slot_state(const Span& sp, unsigned int idx, SlotType type, const ScopeHandle* above_scope/*=nullptr*/) const { // 1. Find an applicable Split scope for( auto scope_idx : ::reverse(m_scope_stack) ) { + // Is this supposed to only consider above a specified (likely split) scope? + if( above_scope ) { + // Once the scope is found, clear `above_scope` so subsequent iterations skip this check + if( scope_idx == above_scope->idx ) { + above_scope = nullptr; + } + continue ; + } const auto& scope_def = m_scopes.at(scope_idx); - TU_MATCH_DEF( ScopeType, (scope_def.data), (e), - ( - ), - (Owning, + TU_MATCH_HDRA( (scope_def.data), {) + default: + break; + TU_ARMA(Owning, e) { if( type == SlotType::Local ) { auto it = ::std::find(e.slots.begin(), e.slots.end(), idx); @@ -1931,20 +1997,22 @@ const VarState& MirBuilder::get_slot_state(const Span& sp, unsigned int idx, Slo break ; } } - ), - (Split, + } + TU_ARMA(Split, e) { const auto& cur_arm = e.arms.back(); const auto& list = (type == SlotType::Local ? cur_arm.states : cur_arm.arg_states); auto it = list.find(idx); if( it != list.end() ) { - if( ! skip_count -- ) - { - return it->second; - } + DEBUG("From scope " << scope_idx); + return it->second; } - ) - ) + } + } + } + + if( above_scope ) { + BUG(sp, "Scope " << *above_scope << " not found on stack"); } switch(type) { @@ -1971,19 +2039,18 @@ VarState& MirBuilder::get_slot_state_mut(const Span& sp, unsigned int idx, SlotT for( auto scope_idx : ::reverse(m_scope_stack) ) { auto& scope_def = m_scopes.at(scope_idx); - if( const auto* e = scope_def.data.opt_Owning() ) - { - if( type == SlotType::Local ) + TU_MATCH_HDRA( (scope_def.data), {) + TU_ARMA(Owning, e) { + if( type == SlotType::Local ) // `Local` counts both variables and temporaries { - auto it = ::std::find(e->slots.begin(), e->slots.end(), idx); - if( it != e->slots.end() ) { - break ; + auto it = ::std::find(e.slots.begin(), e.slots.end(), idx); + if( it != e.slots.end() ) { + goto out_of_loop; // `goto` to avoid issues with the loops in `TU_ARMA` } } - } - else if( auto* e = scope_def.data.opt_Split() ) - { - auto& cur_arm = e->arms.back(); + } + TU_ARMA(Split, e) { + auto& cur_arm = e.arms.back(); if( ! ret ) { if( idx == ~0u ) { @@ -2003,81 +2070,49 @@ VarState& MirBuilder::get_slot_state_mut(const Span& sp, unsigned int idx, SlotT ret = &it->second; } } - } - else if( auto* e = scope_def.data.opt_Loop() ) - { + } + TU_ARMA(Loop, e) { if( idx == ~0u ) { } else { - auto& states = (type == SlotType::Local ? e->changed_slots : e->changed_args); + auto& states = (type == SlotType::Local ? e.changed_slots : e.changed_args); if( states.count(idx) == 0 ) { - auto state = e->exit_state_valid ? get_slot_state(sp, idx, type).clone() : VarState::make_Valid({}); + auto state = e.exit_state_valid ? get_slot_state(sp, idx, type).clone() : VarState::make_Valid({}); states.insert(::std::make_pair( idx, mv$(state) )); } } - } - // DISABLED: Long-ish experiment for allowing moves within match conditions (hopefully won't break codegen) -#if 0 - // Freeze is used for `match` guards - // - These are only allowed to modify the (known `bool`) condition variable - // TODO: Some guards have more complex pieces of code, with self-contained scopes, allowable? - // - Those should already have defined their own scope? - // - OR, allow mutations here but ONLY if it's of a Copy type, and force it uninit at the end of the scope - else if( auto* e = scope_def.data.opt_Freeze() ) - { - // If modified variable is the guard's result variable, allow it. - if( type != SlotType::Local ) { - DEBUG("Mutating state of arg" << idx); - ERROR(sp, E0000, "Attempting to move/initialise a value where not allowed"); } - if( type == SlotType::Local && idx == m_if_cond_lval.as_Local() ) - { - // The guard condition variable is allowed to be mutated, and falls through to the upper scope + TU_ARMA(Freeze, e) { + if( !e.unfrozen ) { + // Prevent any mutation + ERROR(sp, E0000, "Attempting to move/initialise a value where not allowed (across scope " << scope_idx << ")"); } - else - { - DEBUG("Mutating state of local" << idx); - auto& states = e->changed_slots; - if( states.count(idx) == 0 ) - { - auto state = get_slot_state(sp, idx, type).clone(); - states.insert(::std::make_pair( idx, mv$(state) )); - } - ret = &states[idx]; - break; // Stop searching } } -#endif - else - { - // Unknown scope type? - } - } - if( ret ) - { - return *ret; } - else + // Label used because we need to break out of the loop and the `TU_ARMA`/`TU_MATCH_HDRA` +out_of_loop: + if( !ret ) { + // Not set by a split/loop scope switch(type) { case SlotType::Local: - if( idx == ~0u ) - { - return m_return_state; - } - else - { - return m_slot_states.at(idx); - } + ret = (idx == ~0u) + ? &m_return_state + : &m_slot_states.at(idx) + ; + break; case SlotType::Argument: - return m_arg_states.at(idx); + ret = &m_arg_states.at(idx); + break; } - throw ""; } + assert(ret); + return *ret; } VarState* MirBuilder::get_val_state_mut_p(const Span& sp, const ::MIR::LValue& lv, bool expect_valid/*=false*/) @@ -2319,6 +2354,7 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) // No values ), (Freeze, + // No values ) ) } @@ -2355,16 +2391,16 @@ ::MIR::LValue MirBuilder::get_ptr_to_dst(const Span& sp, const ::MIR::LValue& lv std::map MirBuilder::get_active_locals(const Span& sp, std::set& saved_drop_flags) const { + TRACE_FUNCTION; std::map rv; for(size_t i = 0; i < m_slot_states.size(); i ++) { - TU_MATCH_HDRA( (m_slot_states[i]), { ) + const auto& s = get_slot_state(sp, i, SlotType::Local); + TU_MATCH_HDRA( (s), {) default: - // TODO: Handle optionals, requires some way to get the value of a drop flag - // - OR: Rewriting of drop flags into a bitset down the line - m_slot_states[i].get_used_drop_flags(&saved_drop_flags); - //ASSERT_BUG(Span(), !m_slot_states[i].contains_optional(), "Save state with optional (save drop flag): " << m_slot_states[i]); - rv.insert( std::make_pair( static_cast(i), SavedActiveLocal(m_slot_states[i].clone()) )); + DEBUG("_" << i << " : " << s); + s.get_used_drop_flags(&saved_drop_flags); + rv.insert( std::make_pair( static_cast(i), SavedActiveLocal(s.clone()) )); break; TU_ARMA(Invalid, e) {} TU_ARMA(MovedOut, e) {} diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 14f873b46..66c462ba3 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -1291,15 +1291,13 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool } }; // TODO: Can this use the code in `monomorphise.cpp`? - struct Cloner + struct Cloner: public ::MIR::Cloner { - const Span& sp; - const ::StaticTraitResolve& resolve; + const ::StaticTraitResolve& m_resolve; const ::MIR::Terminator::Data_Call& te; ::std::vector copy_args; // Local indexes containing copies of Copy args ParamsSet params; unsigned int bb_base = ~0u; - unsigned int tmp_base = ~0u; unsigned int var_base = ~0u; unsigned int df_base = ~0u; @@ -1309,84 +1307,47 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool ::MIR::LValue retval; Cloner(const Span& sp, const ::StaticTraitResolve& resolve, ::MIR::Terminator::Data_Call& te): - sp(sp), - resolve(resolve), + ::MIR::Cloner(sp), + m_resolve(resolve), te(te), copy_args(te.args.size(), ~0u) { } - ::HIR::TypeRef monomorph(const ::HIR::TypeRef& ty) const { - TRACE_FUNCTION_F(ty); - auto rv = params.monomorph_type(sp, ty); - resolve.expand_associated_types(sp, rv); - return rv; - } - ::HIR::GenericPath monomorph(const ::HIR::GenericPath& ty) const { - TRACE_FUNCTION_F(ty); - auto rv = params.monomorph_genericpath(sp, ty, false); - for(auto& arg : rv.m_params.m_types) - resolve.expand_associated_types(sp, arg); - return rv; + ::MIR::BasicBlockId map_bb_idx(::MIR::BasicBlockId idx) const override { + return this->bb_base + idx; } - ::HIR::Path monomorph(const ::HIR::Path& ty) const { - TRACE_FUNCTION_F(ty); - auto rv = params.monomorph_path(sp, ty, false); - TU_MATCH(::HIR::Path::Data, (rv.m_data), (e2), - (Generic, - for(auto& arg : e2.m_params.m_types) - resolve.expand_associated_types(sp, arg); - ), - (UfcsInherent, - resolve.expand_associated_types(sp, e2.type); - for(auto& arg : e2.params.m_types) - resolve.expand_associated_types(sp, arg); - // TODO: impl params too? - for(auto& arg : e2.impl_params.m_types) - resolve.expand_associated_types(sp, arg); - ), - (UfcsKnown, - resolve.expand_associated_types(sp, e2.type); - for(auto& arg : e2.trait.m_params.m_types) - resolve.expand_associated_types(sp, arg); - for(auto& arg : e2.params.m_types) - resolve.expand_associated_types(sp, arg); - ), - (UfcsUnknown, - BUG(sp, "Encountered UfcsUnknown"); - ) - ) - return rv; + virtual unsigned map_local(unsigned f) const { + return this->var_base + f; } - ::HIR::PathParams monomorph(const ::HIR::PathParams& ty) const { - TRACE_FUNCTION_F(ty); - auto rv = params.monomorph_path_params(sp, ty, false); - for(auto& arg : rv.m_types) - resolve.expand_associated_types(sp, arg); - return rv; + virtual unsigned map_drop_flag(unsigned f) const { + return this->df_base + f; } - ::std::vector clone_asm_params(const ::std::vector& params) const - { - ::std::vector rv; - for(const auto& p : params) + const HIR::TypeRef& value_generic_type(HIR::GenericRef ce) const override { + const HIR::GenericParams* p; + switch(ce.group()) { - TU_MATCH_HDRA((p), {) - TU_ARMA(Const, v) - rv.push_back( this->clone_constant(v) ); - TU_ARMA(Sym, v) - rv.push_back( this->monomorph(v) ); - TU_ARMA(Reg, v) - rv.push_back(::MIR::AsmParam::make_Reg({ - v.dir, - v.spec.clone(), - v.input ? box$(this->clone_param(*v.input)) : std::unique_ptr(), - v.output ? box$(this->clone_lval(*v.output)) : std::unique_ptr() - })); - } + case 0: // impl level + p = params.impl_params_def; + break; + case 1: // method level + p = params.fcn_params_def; + break; + default: + TODO(sp, "Typecheck const generics - look up the type"); } - return rv; + ASSERT_BUG(sp, p, "No generic list for " << ce); + ASSERT_BUG(sp, ce.idx() < p->m_values.size(), "Generic param index out of range"); + return p->m_values.at(ce.idx()).m_type; } + const Monomorphiser& monomorphiser() const override { + return params; + } + const StaticTraitResolve* resolve() const override { + return &this->m_resolve; + } + ::MIR::BasicBlock clone_bb(const ::MIR::BasicBlock& src, unsigned src_idx, unsigned new_idx) const { ::MIR::BasicBlock rv; @@ -1394,57 +1355,7 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool for(const auto& stmt : src.statements) { DEBUG("BB" << src_idx << "->BB" << new_idx << "/" << rv.statements.size() << ": " << stmt); - TU_MATCHA( (stmt), (se), - (Assign, - rv.statements.push_back( ::MIR::Statement::make_Assign({ - this->clone_lval(se.dst), - this->clone_rval(se.src) - }) ); - ), - (Asm, - rv.statements.push_back( ::MIR::Statement::make_Asm({ - se.tpl, - this->clone_name_lval_vec(se.outputs), - this->clone_name_lval_vec(se.inputs), - se.clobbers, - se.flags - }) ); - ), - (Asm2, - rv.statements.push_back( ::MIR::Statement::make_Asm2({ - se.options, - se.lines, - this->clone_asm_params(se.params) - }) ); - ), - (SetDropFlag, - rv.statements.push_back( ::MIR::Statement::make_SetDropFlag({ - this->df_base + se.idx, - se.new_val, - se.other == ~0u ? ~0u : this->df_base + se.other - }) ); - ), - (SaveDropFlag, - TODO(Span(), "clone_bb SaveDropFlag"); - ), - (LoadDropFlag, - TODO(Span(), "clone_bb LoadDropFlag"); - ), - (Drop, - rv.statements.push_back( ::MIR::Statement::make_Drop({ - se.kind, - this->clone_lval(se.slot), - se.flag_idx == ~0u ? ~0u : this->df_base + se.flag_idx - }) ); - ), - (ScopeEnd, - ::MIR::Statement::Data_ScopeEnd new_se; - new_se.slots.reserve(se.slots.size()); - for(auto idx : se.slots) - new_se.slots.push_back(this->var_base + idx); - rv.statements.push_back(::MIR::Statement( mv$(new_se) )); - ) - ) + rv.statements.push_back(this->clone_stmt(stmt)); DEBUG("-> " << rv.statements.back()); } DEBUG("BB" << src_idx << "->BB" << new_idx << "/" << rv.statements.size() << ": " << src.terminator); @@ -1457,276 +1368,37 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool DEBUG("-> " << rv.terminator); return rv; } - ::MIR::Terminator clone_term(const ::MIR::Terminator& src) const + ::MIR::Terminator clone_term(const ::MIR::Terminator& src) const override { - TU_MATCHA( (src), (se), - (Incomplete, - return ::MIR::Terminator::make_Incomplete({}); - ), - (Return, + if( src.is_Return() ) { return ::MIR::Terminator::make_Goto(this->te.ret_block); - ), - (Diverge, + } + else if( src.is_Diverge() ) { return ::MIR::Terminator::make_Goto(this->te.panic_block); - ), - (Panic, - return ::MIR::Terminator::make_Panic({}); - ), - (Goto, - return ::MIR::Terminator::make_Goto(se + this->bb_base); - ), - (If, - return ::MIR::Terminator::make_If({ - this->clone_lval(se.cond), - se.bb_true + this->bb_base, - se.bb_false + this->bb_base - }); - ), - (Switch, - ::std::vector<::MIR::BasicBlockId> arms; - arms.reserve(se.targets.size()); - for(const auto& bbi : se.targets) - arms.push_back( bbi + this->bb_base ); - return ::MIR::Terminator::make_Switch({ this->clone_lval(se.val), mv$(arms) }); - ), - (SwitchValue, - ::std::vector<::MIR::BasicBlockId> arms; - arms.reserve(se.targets.size()); - for(const auto& bbi : se.targets) - arms.push_back( bbi + this->bb_base ); - return ::MIR::Terminator::make_SwitchValue({ this->clone_lval(se.val), se.def_target + this->bb_base, mv$(arms), se.values.clone() }); - ), - (Call, - ::MIR::CallTarget tgt; - TU_MATCHA( (se.fcn), (ste), - (Value, - tgt = ::MIR::CallTarget::make_Value( this->clone_lval(ste) ); - ), - (Path, - tgt = ::MIR::CallTarget::make_Path( this->monomorph(ste) ); - ), - (Intrinsic, - tgt = ::MIR::CallTarget::make_Intrinsic({ ste.name, this->monomorph(ste.params) }); - ) - ) - return ::MIR::Terminator::make_Call({ - this->bb_base + se.ret_block, - this->bb_base + se.panic_block, - this->clone_lval(se.ret_val), - mv$(tgt), - this->clone_param_vec(se.args) - }); - ) - ) - throw ""; - } - ::std::vector< ::std::pair<::std::string,::MIR::LValue> > clone_name_lval_vec(const ::std::vector< ::std::pair<::std::string,::MIR::LValue> >& src) const - { - ::std::vector< ::std::pair<::std::string,::MIR::LValue> > rv; - rv.reserve(src.size()); - for(const auto& e : src) - rv.push_back(::std::make_pair(e.first, this->clone_lval(e.second))); - return rv; - } - ::std::vector<::MIR::LValue> clone_lval_vec(const ::std::vector<::MIR::LValue>& src) const - { - ::std::vector<::MIR::LValue> rv; - rv.reserve(src.size()); - for(const auto& lv : src) - rv.push_back( this->clone_lval(lv) ); - return rv; - } - ::std::vector<::MIR::Param> clone_param_vec(const ::std::vector<::MIR::Param>& src) const - { - ::std::vector<::MIR::Param> rv; - rv.reserve(src.size()); - for(const auto& lv : src) - rv.push_back( this->clone_param(lv) ); - return rv; + } + else { + return ::MIR::Cloner::clone_term(src); + } } - ::MIR::LValue clone_lval(const ::MIR::LValue& src) const + ::MIR::LValue clone_lval(const ::MIR::LValue& src) const override { - auto wrappers = src.m_wrappers; - for(auto& w : wrappers) - { - if( w.is_Index() ) { - w = ::MIR::LValue::Wrapper::new_Index( this->var_base + w.as_Index() ); - } + auto rv = ::MIR::Cloner::clone_lval(src); + if( rv.m_root.is_Return() ) { + return this->retval.clone_wrapped( std::move(rv.m_wrappers) ); } - TU_MATCHA( (src.m_root), (se), - (Return, - return this->retval.clone_wrapped( mv$(wrappers) ); - ), - (Argument, - const auto& arg = this->te.args.at(se); - if( this->copy_args[se] != ~0u ) - { - return ::MIR::LValue( ::MIR::LValue::Storage::new_Local(this->copy_args[se]), mv$(wrappers) ); + if( rv.m_root.is_Argument() ) { + auto se = rv.m_root.as_Argument(); + const auto& arg = this->te.args.at( se ); + if( this->copy_args[se] != ~0u ) { + return ::MIR::LValue( ::MIR::LValue::Storage::new_Local(this->copy_args[se]), std::move(rv.m_wrappers) ); } - else - { + else { assert( !arg.is_Constant() ); // Should have been handled in the above - return arg.as_LValue().clone_wrapped( mv$(wrappers) ); - } - ), - (Local, - return ::MIR::LValue( ::MIR::LValue::Storage::new_Local(this->var_base + se), mv$(wrappers) ); - ), - (Static, - return ::MIR::LValue( ::MIR::LValue::Storage::new_Static(this->monomorph(se)), mv$(wrappers) ); - ) - ) - throw ""; - } - ::MIR::Constant clone_constant(const ::MIR::Constant& src) const - { - TU_MATCH_HDRA( (src), {) - TU_ARMA(Int , ce) return ::MIR::Constant(ce); - TU_ARMA(Uint , ce) return ::MIR::Constant(ce); - TU_ARMA(Float, ce) return ::MIR::Constant(ce); - TU_ARMA(Bool , ce) return ::MIR::Constant(ce); - TU_ARMA(Bytes, ce) return ::MIR::Constant(ce); - TU_ARMA(StaticString, ce) return ::MIR::Constant(ce); - TU_ARMA(Const, ce) { - return ::MIR::Constant::make_Const({ box$(this->monomorph(*ce.p)) }); - } - TU_ARMA(Generic, ce) { - const HIR::GenericParams* p; - switch(ce.group()) - { - case 0: // impl level - p = params.impl_params_def; - break; - case 1: // method level - p = params.fcn_params_def; - break; - default: - TODO(sp, "Typecheck const generics - look up the type"); - } - ASSERT_BUG(sp, p, "No generic list for " << ce); - ASSERT_BUG(sp, ce.idx() < p->m_values.size(), "Generic param index out of range"); - const auto& ty = p->m_values.at(ce.idx()).m_type; - - auto val = params.get_value(sp, ce); - TU_MATCH_HDRA( (val), {) - default: - TODO(sp, "Monomorphise MIR generic constant " << ce << " = " << val); - TU_ARMA(Generic, ve) { - return ve; - } - TU_ARMA(Evaluated, ve) { - auto v = EncodedLiteralSlice(*ve); - ASSERT_BUG(sp, ty.data().is_Primitive(), "Handle non-primitive const generic: " << ty); - // TODO: This is duplicated in `mir/from_hir_match.cpp` - De-duplicate? - switch(ty.data().as_Primitive()) - { - case ::HIR::CoreType::Bool: return ::MIR::Constant::make_Bool({ v.read_uint(1) == 0 }); - case ::HIR::CoreType::U8: return ::MIR::Constant::make_Uint({ v.read_uint(1), ty.data().as_Primitive() }); - case ::HIR::CoreType::U16: return ::MIR::Constant::make_Uint({ v.read_uint(2), ty.data().as_Primitive() }); - case ::HIR::CoreType::U32: return ::MIR::Constant::make_Uint({ v.read_uint(4), ty.data().as_Primitive() }); - case ::HIR::CoreType::U64: return ::MIR::Constant::make_Uint({ v.read_uint(8), ty.data().as_Primitive() }); - case ::HIR::CoreType::U128: return ::MIR::Constant::make_Uint({ v.read_uint(16), ty.data().as_Primitive() }); - case ::HIR::CoreType::Usize: return ::MIR::Constant::make_Uint({ v.read_uint(Target_GetPointerBits() / 8), ty.data().as_Primitive() }); - case ::HIR::CoreType::I8: return ::MIR::Constant::make_Int({ v.read_sint(1), ty.data().as_Primitive() }); - case ::HIR::CoreType::I16: return ::MIR::Constant::make_Int({ v.read_sint(2), ty.data().as_Primitive() }); - case ::HIR::CoreType::I32: return ::MIR::Constant::make_Int({ v.read_sint(4), ty.data().as_Primitive() }); - case ::HIR::CoreType::I64: return ::MIR::Constant::make_Int({ v.read_sint(8), ty.data().as_Primitive() }); - case ::HIR::CoreType::I128: return ::MIR::Constant::make_Int({ v.read_sint(16), ty.data().as_Primitive() }); - case ::HIR::CoreType::Isize: return ::MIR::Constant::make_Int({ v.read_sint(Target_GetPointerBits() / 8), ty.data().as_Primitive() }); - case ::HIR::CoreType::F16: return ::MIR::Constant::make_Float({ v.read_float(2), ty.data().as_Primitive() }); - case ::HIR::CoreType::F32: return ::MIR::Constant::make_Float({ v.read_float(4), ty.data().as_Primitive() }); - case ::HIR::CoreType::F64: return ::MIR::Constant::make_Float({ v.read_float(8), ty.data().as_Primitive() }); - case ::HIR::CoreType::F128: return ::MIR::Constant::make_Float({ v.read_float(16), ty.data().as_Primitive() }); - case ::HIR::CoreType::Char: return ::MIR::Constant::make_Uint({ v.read_uint(4), ty.data().as_Primitive() }); - case ::HIR::CoreType::Str: BUG(sp, "`str` const generic"); - } - } - } - } - TU_ARMA(Function, ce) { - return ::MIR::Constant::make_Function({ box$(this->monomorph(*ce.p)) }); - } - TU_ARMA(ItemAddr, ce) { - if(!ce) - return ::MIR::Constant::make_ItemAddr({}); - return ::MIR::Constant::make_ItemAddr(box$(this->monomorph(*ce))); + return arg.as_LValue().clone_wrapped( std::move(rv.m_wrappers) ); } } - throw ""; - } - ::MIR::Param clone_param(const ::MIR::Param& src) const - { - TU_MATCHA( (src), (se), - (LValue, - // NOTE: No need to use `copy_args` here as all uses of Param are copies/moves - //if( const auto* ae = se.opt_Argument() ) - // return this->te.args.at(ae->idx).clone(); - return clone_lval(se); - ), - (Borrow, - return ::MIR::Param::make_Borrow({ se.type, this->clone_lval(se.val) }); - ), - (Constant, - return clone_constant(se); - ) - ) - throw ""; - } - ::MIR::RValue clone_rval(const ::MIR::RValue& src) const - { - TU_MATCHA( (src), (se), - (Use, - //if( const auto* ae = se.opt_Argument() ) - // if( const auto* e = this->te.args.at(ae->idx).opt_Constant() ) - // return e->clone(); - return ::MIR::RValue( this->clone_lval(se) ); - ), - (Constant, - return this->clone_constant(se); - ), - (SizedArray, - return ::MIR::RValue::make_SizedArray({ this->clone_param(se.val), params.monomorph_arraysize(sp, se.count) }); - ), - (Borrow, - // TODO: Region IDs - return ::MIR::RValue::make_Borrow({ se.type, se.is_raw, this->clone_lval(se.val) }); - ), - (Cast, - return ::MIR::RValue::make_Cast({ this->clone_lval(se.val), this->monomorph(se.type) }); - ), - (BinOp, - return ::MIR::RValue::make_BinOp({ this->clone_param(se.val_l), se.op, this->clone_param(se.val_r) }); - ), - (UniOp, - return ::MIR::RValue::make_UniOp({ this->clone_lval(se.val), se.op }); - ), - (DstMeta, - return ::MIR::RValue::make_DstMeta({ this->clone_lval(se.val) }); - ), - (DstPtr, - return ::MIR::RValue::make_DstPtr({ this->clone_lval(se.val) }); - ), - (MakeDst, - return ::MIR::RValue::make_MakeDst({ this->clone_param(se.ptr_val), this->clone_param(se.meta_val) }); - ), - (Tuple, - return ::MIR::RValue::make_Tuple({ this->clone_param_vec(se.vals) }); - ), - (Array, - return ::MIR::RValue::make_Array({ this->clone_param_vec(se.vals) }); - ), - (UnionVariant, - return ::MIR::RValue::make_UnionVariant({ this->monomorph(se.path), se.index, this->clone_param(se.val) }); - ), - (EnumVariant, - return ::MIR::RValue::make_EnumVariant({ this->monomorph(se.path), se.index, this->clone_param_vec(se.vals) }); - ), - (Struct, - return ::MIR::RValue::make_Struct({ this->monomorph(se.path), this->clone_param_vec(se.vals) }); - ) - ) - throw ""; + return rv; } }; @@ -6213,6 +5885,10 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn used_dfs.at(e->other) = true; used_dfs.at(e->idx) = true; } + else if( const auto* e = stmt.opt_LoadDropFlag() ) + { + used_dfs.at(e->idx) = true; + } } if( const auto* te = block.terminator.opt_Call() ) @@ -6339,6 +6015,14 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn if( se->other != ~0u ) se->other = df_rewrite_table[se->other]; } + else if( auto* se = stmt.opt_LoadDropFlag() ) + { + se->idx = df_rewrite_table[se->idx]; + } + else if( auto* se = stmt.opt_SaveDropFlag() ) + { + se->idx = df_rewrite_table[se->idx]; + } else if( auto* se = stmt.opt_ScopeEnd() ) { for(auto it = se->slots.begin(); it != se->slots.end(); ) diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index f44237e7d..e0d4dff55 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -486,19 +486,8 @@ std::vector Parse_IfLetChain(TokenStream& lex) /// While loop (either as a statement, or as part of an expression) ExprNodeP Parse_WhileStmt(TokenStream& lex, Ident lifetime) { - if( TARGETVER_LEAST_1_74 || lex.lookahead(0) == TOK_RWORD_LET ) { - // TODO: Pattern list (same as match)? - auto conditions = Parse_IfLetChain(lex); - return NEWNODE( AST::ExprNode_WhileLet, lifetime, ::std::move(conditions), Parse_ExprBlockNode(lex) ); - } - else { - ExprNodeP cnd; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - cnd = Parse_Expr1(lex); - } - return NEWNODE( AST::ExprNode_Loop, lifetime, ::std::move(cnd), Parse_ExprBlockNode(lex) ); - } + auto conditions = Parse_IfLetChain(lex); + return NEWNODE( AST::ExprNode_While, lifetime, ::std::move(conditions), Parse_ExprBlockNode(lex) ); } /// For loop (either as a statement, or as part of an expression) ExprNodeP Parse_ForStmt(TokenStream& lex, Ident lifetime) @@ -514,7 +503,7 @@ ExprNodeP Parse_ForStmt(TokenStream& lex, Ident lifetime) SET_PARSE_FLAG(lex, disallow_struct_literal); val = Parse_Expr0(lex); } - return NEWNODE( AST::ExprNode_Loop, lifetime, ::std::move(pat), ::std::move(val), Parse_ExprBlockNode(lex) ); + return NEWNODE( AST::ExprNode_For, lifetime, ::std::move(pat), ::std::move(val), Parse_ExprBlockNode(lex) ); } /// Parse an 'if' statement // Note: TOK_RWORD_IF has already been eaten @@ -523,49 +512,38 @@ ExprNodeP Parse_IfStmt(TokenStream& lex) TRACE_FUNCTION; Token tok; - ExprNodeP cond; - std::vector conditions; + std::vector arms; + ExprNodeP else_block; + do { + std::vector conditions; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - if( TARGETVER_LEAST_1_74 || lex.lookahead(0) == TOK_RWORD_LET ) { + { + SET_PARSE_FLAG(lex, disallow_struct_literal); conditions = Parse_IfLetChain(lex); - // If the conditions are just booleans, emit as `if` - if( conditions.size() == 1 && !conditions[0].opt_pat ) { - cond = std::move(conditions[0].value); - conditions.clear(); - } } - else { - // TODO: `if foo && let bar` is valid - cond = Parse_Expr0(lex); - } - } - // Contents - ExprNodeP code = Parse_ExprBlockNode(lex); + // Contents + ExprNodeP code = Parse_ExprBlockNode(lex); - // Handle else: - ExprNodeP altcode; - if( lex.getTokenIf(TOK_RWORD_ELSE) ) - { - // Recurse for 'else if' - if( lex.getTokenIf(TOK_RWORD_IF) ) { - altcode = Parse_IfStmt(lex); + arms.push_back(AST::ExprNode_If::Arm { + std::move(conditions), + std::move(code) + }); + + // Handle else: + if( !lex.getTokenIf(TOK_RWORD_ELSE) ) { + // No `else`, leave `else_block` as `nullptr` + break; } - // - or get block - else { - altcode = Parse_ExprBlockNode(lex); + // Recurse for 'else if' + if( !lex.getTokenIf(TOK_RWORD_IF) ) { + else_block= Parse_ExprBlockNode(lex); + break; } - } - // - or nothing - else { - } + // Keep looping + } while(true); - if( !cond ) - return NEWNODE( AST::ExprNode_IfLet, ::std::move(conditions), ::std::move(code), ::std::move(altcode) ); - else - return NEWNODE( AST::ExprNode_If, ::std::move(cond), ::std::move(code), ::std::move(altcode) ); + return NEWNODE( AST::ExprNode_If, ::std::move(arms), ::std::move(else_block) ); } /// "match" block ExprNodeP Parse_Expr_Match(TokenStream& lex) diff --git a/src/resolve/absolute.cpp b/src/resolve/absolute.cpp index 192607030..afc9950d0 100644 --- a/src/resolve/absolute.cpp +++ b/src/resolve/absolute.cpp @@ -267,7 +267,8 @@ namespace BUG(sp, "resolve/absolute.cpp - Context::push_var - No block"); } auto& vb = m_name_context.back().as_VarBlock(); - for( const auto& v : vb.variables ) + // Work backwards, in case there are multiple bindings in the same scope. + for( const auto& v : ::reverse(vb.variables) ) { if( v.first == name ) { DEBUG("Arm defined var @ " << m_block_level << ": #" << v.second << " " << name); @@ -2331,22 +2332,18 @@ void Resolve_Absolute_ExprNode(Context& context, ::AST::ExprNode& node) } } void visit(AST::ExprNode_Loop& node) override { - AST::NodeVisitorDef::visit(node.m_cond); this->context.push_block(); - switch( node.m_type ) - { - case ::AST::ExprNode_Loop::LOOP: - break; - case ::AST::ExprNode_Loop::WHILE: - break; - case ::AST::ExprNode_Loop::FOR: - BUG(node.span(), "`for` should be desugared"); - } node.m_code->visit( *this ); this->context.pop_block(); } - void visit(AST::ExprNode_WhileLet& node) override { - BUG(node.span(), "`while let` should be desugared"); + void visit(AST::ExprNode_For& node) override { + BUG(node.span(), "`for` should be desugared"); + } + void visit(AST::ExprNode_While& node) override { + this->context.push_block(); + visit_if_let_conditions(node.m_conditions); + node.m_code->visit(*this); + this->context.pop_block(); } void visit(AST::ExprNode_LetBinding& node) override { @@ -2364,29 +2361,30 @@ void Resolve_Absolute_ExprNode(Context& context, ::AST::ExprNode& node) this->context.m_var_count += n_vars; } } - void visit(AST::ExprNode_IfLet& node) override { - BUG(node.span(), "`if let` should be desugared"); -#if 0 - DEBUG("ExprNode_IfLet"); - node.m_value->visit( *this ); - - this->context.push_block(); - - this->context.start_patbind(); - for(auto& pat : node.m_patterns) - { - Resolve_Absolute_Pattern(this->context, true, pat); - this->context.end_patbind_arm(pat.span()); + void visit_if_let_conditions(std::vector& conds) { + for(auto& cond : conds) { + // Visit the value first, so it doesn't bind to the newly created variables in the pattern + cond.value->visit(*this); + + if( cond.opt_pat ) + { + this->context.start_patbind(); + Resolve_Absolute_Pattern(this->context, true, *cond.opt_pat); + this->context.end_patbind_arm(cond.opt_pat->span()); + this->context.end_patbind(); + } + } + } + void visit(AST::ExprNode_If& node) override { + for(auto& arm : node.m_arms) { + this->context.push_block(); + visit_if_let_conditions(arm.m_conditions); + arm.m_body->visit(*this); + this->context.pop_block(); + } + if( node.m_else ) { + node.m_else->visit(*this); } - this->context.end_patbind(); - - assert( node.m_true ); - node.m_true->visit( *this ); - this->context.pop_block(); - - if(node.m_false) - node.m_false->visit(*this); -#endif } void visit(AST::ExprNode_StructLiteral& node) override { DEBUG("ExprNode_StructLiteral"); diff --git a/src/trans/monomorphise.cpp b/src/trans/monomorphise.cpp index 3334f7013..ebb0fc091 100644 --- a/src/trans/monomorphise.cpp +++ b/src/trans/monomorphise.cpp @@ -13,142 +13,39 @@ #include namespace { - ::MIR::LValue monomorph_LValue(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::LValue& tpl) + class Cloner: public ::MIR::Cloner { - if( tpl.m_root.is_Static() ) - { - return ::MIR::LValue( ::MIR::LValue::Storage::new_Static(params.monomorph(resolve, tpl.m_root.as_Static())), tpl.m_wrappers ); - } - else - { - return tpl.clone(); - } - } - const ::HIR::ValueParamDef& get_value_param_def(const ::StaticTraitResolve& resolve, const ::HIR::GenericRef& g) { - switch(g.group()) - { - case 0: - ASSERT_BUG(Span(), g.idx() < resolve.impl_generics().m_values.size(), "Value generic " << g << " out of bounds in impl: " << resolve.impl_generics().m_values.size()); - return resolve.impl_generics().m_values.at(g.idx()); - case 1: - ASSERT_BUG(Span(), g.idx() < resolve.item_generics().m_values.size(), "Value generic " << g << " out of bounds in fcn: " << resolve.item_generics().m_values.size()); - return resolve.item_generics().m_values.at(g.idx()); - default: - BUG(Span(), ""); - } - } - ::MIR::Constant monomorph_Constant(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::Constant& tpl) - { - TU_MATCH_HDRA( (tpl), {) - TU_ARMA(Int, ce) { - return ::MIR::Constant::make_Int(ce); - } - TU_ARMA(Uint, ce) { - return ::MIR::Constant::make_Uint(ce); - } - TU_ARMA(Float, ce) { - return ::MIR::Constant::make_Float(ce); - } - TU_ARMA(Bool, ce) { - return ::MIR::Constant::make_Bool(ce); - } - TU_ARMA(Bytes, ce) { - return ::MIR::Constant(ce); - } - TU_ARMA(StaticString, ce) { - return ::MIR::Constant(ce); - } - TU_ARMA(Const, ce) { - return ::MIR::Constant::make_Const({ - box$(params.monomorph(resolve, *ce.p)) - }); - } - TU_ARMA(Generic, ce) { - auto val = params.get_value(params.sp, ce); - TU_MATCH_HDRA( (val), {) + const ::StaticTraitResolve& m_resolve; + const Trans_Params& params; + public: + Cloner(const Span& sp, const ::StaticTraitResolve& resolve, const Trans_Params& params) + : ::MIR::Cloner(sp) + , m_resolve(resolve) + , params(params) + {} + + const HIR::TypeRef& value_generic_type(HIR::GenericRef g) const override { + switch(g.group()) + { + case 0: + ASSERT_BUG(sp, g.idx() < m_resolve.impl_generics().m_values.size(), + "Value generic " << g << " out of bounds in impl: " << m_resolve.impl_generics().m_values.size()); + return m_resolve.impl_generics().m_values.at(g.idx()).m_type; + case 1: + ASSERT_BUG(sp, g.idx() < m_resolve.item_generics().m_values.size(), + "Value generic " << g << " out of bounds in fcn: " << m_resolve.item_generics().m_values.size()); + return m_resolve.item_generics().m_values.at(g.idx()).m_type; default: - TODO(params.sp, "Monomorphise MIR generic constant " << ce << " = " << val); - TU_ARMA(Evaluated, ve) { - const auto& def = get_value_param_def(resolve, ce); - auto ty = def.m_type.data().as_Primitive(); - switch(ty) - { - case HIR::CoreType::Char: - case HIR::CoreType::Usize: - case HIR::CoreType::U128: - case HIR::CoreType::U64: - case HIR::CoreType::U32: - case HIR::CoreType::U16: - case HIR::CoreType::U8: - return ::MIR::Constant::make_Uint({EncodedLiteralSlice(*ve).read_uint(ve->bytes.size()), ty}); - case HIR::CoreType::Bool: - return ::MIR::Constant::make_Bool({EncodedLiteralSlice(*ve).read_uint(ve->bytes.size()) != 0}); - case HIR::CoreType::Isize: - case HIR::CoreType::I128: - case HIR::CoreType::I64: - case HIR::CoreType::I32: - case HIR::CoreType::I16: - case HIR::CoreType::I8: - return ::MIR::Constant::make_Int({EncodedLiteralSlice(*ve).read_sint(ve->bytes.size()), ty}); - case HIR::CoreType::F16: - case HIR::CoreType::F32: - case HIR::CoreType::F64: - case HIR::CoreType::F128: - return ::MIR::Constant::make_Float({EncodedLiteralSlice(*ve).read_float(ve->bytes.size()), ty}); - case HIR::CoreType::Str: - BUG(params.sp, "Constant of type `str`?"); - } - } - } - } - TU_ARMA(Function, ce) { - return ::MIR::Constant::make_Function({ - box$(params.monomorph(resolve, *ce.p)) - }); - } - TU_ARMA(ItemAddr, ce) { - if(!ce) - return ::MIR::Constant::make_ItemAddr({}); - auto p = params.monomorph(resolve, *ce); - // TODO: If this is a pointer to a function on a trait object, replace with the address loaded from the vtable. - // - Requires creating a new temporary for the vtable pointer. - // - Also requires knowing what the receiver is. - return ::MIR::Constant( box$(p) ); + BUG(Span(), ""); } } - throw ""; - } - ::MIR::Param monomorph_Param(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::Param& tpl) - { - TU_MATCHA( (tpl), (e), - (LValue, - return monomorph_LValue(resolve, params, e); - ), - (Borrow, - return ::MIR::Param::make_Borrow({ e.type, monomorph_LValue(resolve, params, e.val) }); - ), - (Constant, - return monomorph_Constant(resolve, params, e); - ) - ) - throw ""; - } - //::std::vector<::MIR::LValue> monomorph_LValue_list(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::std::vector<::MIR::LValue>& tpl) - //{ - // ::std::vector<::MIR::LValue> rv; - // rv.reserve( tpl.size() ); - // for(const auto& v : tpl) - // rv.push_back( monomorph_LValue(resolve, params, v) ); - // return rv; - //} - ::std::vector<::MIR::Param> monomorph_Param_list(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::std::vector<::MIR::Param>& tpl) - { - ::std::vector<::MIR::Param> rv; - rv.reserve( tpl.size() ); - for(const auto& v : tpl) - rv.push_back( monomorph_Param(resolve, params, v) ); - return rv; - } + const Monomorphiser& monomorphiser() const override { + return params; + } + const StaticTraitResolve* resolve() const override { + return &m_resolve; + } + }; } ::MIR::FunctionPointer Trans_Monomorphise(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::FunctionPointer& tpl) @@ -169,6 +66,7 @@ ::MIR::FunctionPointer Trans_Monomorphise(const ::StaticTraitResolve& resolve, c } output.drop_flags = tpl->drop_flags; + Cloner c { sp, resolve, params }; // 2. Monomorphise all paths output.blocks.reserve( tpl->blocks.size() ); for(const auto& block : tpl->blocks) @@ -181,231 +79,20 @@ ::MIR::FunctionPointer Trans_Monomorphise(const ::StaticTraitResolve& resolve, c { switch( stmt.tag() ) { - case ::MIR::Statement::TAGDEAD: throw ""; - case ::MIR::Statement::TAG_SetDropFlag: - statements.push_back( ::MIR::Statement( stmt.as_SetDropFlag() ) ); - break; + // LAZY: These _should_ be in `clone_stmt`, but they're not needed in optimising and MIR cloning TU_ARM(stmt, SaveDropFlag, e) { statements.push_back(::MIR::Statement::make_SaveDropFlag({ e.slot.clone(), e.bit_index, e.idx })); } break; TU_ARM(stmt, LoadDropFlag, e) { statements.push_back(::MIR::Statement::make_LoadDropFlag({ e.idx, e.slot.clone(), e.bit_index})); } break; - case ::MIR::Statement::TAG_ScopeEnd: - statements.push_back( ::MIR::Statement( stmt.as_ScopeEnd() ) ); + default: + statements.push_back(c.clone_stmt(stmt)); break; - case ::MIR::Statement::TAG_Drop: { - const auto& e = stmt.as_Drop(); - DEBUG("- DROP " << e.slot); - statements.push_back( ::MIR::Statement::make_Drop({ - e.kind, - monomorph_LValue(resolve, params, e.slot), - e.flag_idx - }) ); - } break; - case ::MIR::Statement::TAG_Assign: { - const auto& e = stmt.as_Assign(); - DEBUG("- " << e.dst << " = " << e.src); - - ::MIR::RValue rval; - TU_MATCHA( (e.src), (se), - (Use, - rval = ::MIR::RValue( monomorph_LValue(resolve, params, se) ); - ), - (Constant, - rval = monomorph_Constant(resolve, params, se); - ), - (SizedArray, - rval = ::MIR::RValue::make_SizedArray({ - monomorph_Param(resolve, params, se.val), - params.monomorph_arraysize(sp, se.count) - }); - ), - (Borrow, - rval = ::MIR::RValue::make_Borrow({ - se.type, - se.is_raw, - monomorph_LValue(resolve, params, se.val) - }); - ), - (Cast, - rval = ::MIR::RValue::make_Cast({ - monomorph_LValue(resolve, params, se.val), - params.monomorph(resolve, se.type) - }); - ), - (BinOp, - rval = ::MIR::RValue::make_BinOp({ - monomorph_Param(resolve, params, se.val_l), - se.op, - monomorph_Param(resolve, params, se.val_r) - }); - ), - (UniOp, - rval = ::MIR::RValue::make_UniOp({ - monomorph_LValue(resolve, params, se.val), - se.op - }); - ), - (DstMeta, - auto lv = monomorph_LValue(resolve, params, se.val); - // TODO: Get the type of this, and if it's an array - replace with the size - rval = ::MIR::RValue::make_DstMeta({ mv$(lv) }); - ), - (DstPtr, - rval = ::MIR::RValue::make_DstPtr({ monomorph_LValue(resolve, params, se.val) }); - ), - (MakeDst, - rval = ::MIR::RValue::make_MakeDst({ - monomorph_Param(resolve, params, se.ptr_val), - monomorph_Param(resolve, params, se.meta_val) - }); - ), - (Tuple, - rval = ::MIR::RValue::make_Tuple({ - monomorph_Param_list(resolve, params, se.vals) - }); - ), - (Array, - rval = ::MIR::RValue::make_Array({ - monomorph_Param_list(resolve, params, se.vals) - }); - ), - (UnionVariant, - rval = ::MIR::RValue::make_UnionVariant({ - params.monomorph(resolve, se.path), - se.index, - monomorph_Param(resolve, params, se.val) - }); - ), - (EnumVariant, - rval = ::MIR::RValue::make_EnumVariant({ - params.monomorph(resolve, se.path), - se.index, - monomorph_Param_list(resolve, params, se.vals) - }); - ), - (Struct, - rval = ::MIR::RValue::make_Struct({ - params.monomorph(resolve, se.path), - monomorph_Param_list(resolve, params, se.vals) - }); - ) - ) - - statements.push_back( ::MIR::Statement::make_Assign({ - monomorph_LValue(resolve, params, e.dst), - mv$(rval) - }) ); - } break; - case ::MIR::Statement::TAG_Asm: { - const auto& e = stmt.as_Asm(); - DEBUG("- llvm_asm! \"" << e.tpl << "\""); - ::std::vector< ::std::pair<::std::string, ::MIR::LValue>> new_out, new_in; - new_out.reserve( e.outputs.size() ); - for(auto& ent : e.outputs) - new_out.push_back(::std::make_pair( ent.first, monomorph_LValue(resolve, params, ent.second) )); - new_in.reserve( e.inputs.size() ); - for(auto& ent : e.inputs) - new_in.push_back(::std::make_pair( ent.first, monomorph_LValue(resolve, params, ent.second) )); - - statements.push_back( ::MIR::Statement::make_Asm({ - e.tpl, mv$(new_out), mv$(new_in), e.clobbers, e.flags - }) ); - } break; - case ::MIR::Statement::TAG_Asm2: { - const auto& e = stmt.as_Asm2(); - DEBUG("- asm!"); - std::vector new_params; - for(const auto& p : e.params) - { - TU_MATCH_HDRA( (p), {) - TU_ARMA(Const, v) - new_params.push_back(monomorph_Constant(resolve, params, v)); - TU_ARMA(Sym, v) - new_params.push_back(params.monomorph(resolve, v)); - TU_ARMA(Reg, v) - new_params.push_back(MIR::AsmParam::make_Reg({ - v.dir, - v.spec.clone(), - v.input ? box$( monomorph_Param(resolve, params, *v.input) ) : nullptr, - v.output ? box$( monomorph_LValue(resolve, params, *v.output) ) : nullptr, - })); - } - } - statements.push_back(::MIR::Statement::make_Asm2({ - e.options, e.lines, std::move(new_params) - })); - } break; } } - ::MIR::Terminator terminator; - - DEBUG("> " << block.terminator); - TU_MATCHA( (block.terminator), (e), - (Incomplete, - //BUG(sp, "Incomplete block"); - terminator = e; - ), - (Return, - terminator = e; - ), - (Diverge, - terminator = e; - ), - (Goto, - terminator = e; - ), - (Panic, - terminator = e; - ), - (If, - terminator = ::MIR::Terminator::make_If({ - monomorph_LValue(resolve, params, e.cond), - e.bb_true, e.bb_false - }); - ), - (Switch, - terminator = ::MIR::Terminator::make_Switch({ - monomorph_LValue(resolve, params, e.val), - e.targets - }); - ), - (SwitchValue, - terminator = ::MIR::Terminator::make_SwitchValue({ - monomorph_LValue(resolve, params, e.val), - e.def_target, - e.targets, - e.values.clone() - }); - ), - (Call, - struct H { - static ::MIR::CallTarget monomorph_calltarget(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::CallTarget& ct) { - TU_MATCHA( (ct), (e), - (Value, - return monomorph_LValue(resolve, params, e); - ), - (Path, - return params.monomorph(resolve, e); - ), - (Intrinsic, - return ::MIR::CallTarget::make_Intrinsic({ e.name, params.monomorph(resolve, e.params) }); - ) - ) - throw ""; - } - }; - terminator = ::MIR::Terminator::make_Call({ - e.ret_block, e.panic_block, - monomorph_LValue(resolve, params, e.ret_val), - H::monomorph_calltarget(resolve, params, e.fcn), - monomorph_Param_list(resolve, params, e.args) - }); - ) - ) - + ::MIR::Terminator terminator = c.clone_term(block.terminator); output.blocks.push_back( ::MIR::BasicBlock { mv$(statements), mv$(terminator) } ); }