From 177040dd129bccdc275831e3423af2efab6e948e Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Feb 2017 17:04:01 +0800 Subject: Codegen C - Fix transmute on constants --- src/trans/codegen_c.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 5e365ceb..f44c9980 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -2046,7 +2046,16 @@ namespace { emit_lvalue(e.ret_val); m_of << ".META = " << s.size() << ""; } else if( name == "transmute" ) { - m_of << "memcpy( &"; emit_lvalue(e.ret_val); m_of << ", &"; emit_param(e.args.at(0)); m_of << ", sizeof("; emit_ctype(params.m_types.at(0)); m_of << "))"; + if( e.args.at(0).is_Constant() ) + { + m_of << "{ "; emit_ctype(params.m_types.at(1), FMT_CB(s, s << "v";)); m_of << " = "; emit_param(e.args.at(0)); m_of << ";"; + m_of << "memcpy( &"; emit_lvalue(e.ret_val); m_of << ", &v, sizeof("; emit_ctype(params.m_types.at(0)); m_of << ")); "; + m_of << "}"; + } + else + { + m_of << "memcpy( &"; emit_lvalue(e.ret_val); m_of << ", &"; emit_param(e.args.at(0)); m_of << ", sizeof("; emit_ctype(params.m_types.at(0)); m_of << "))"; + } } else if( name == "copy_nonoverlapping" || name == "copy" ) { if( name == "copy" ) { -- cgit v1.2.3 From aa99de423034435cf765b9dc104f69fc20f84bd6 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Feb 2017 18:10:38 +0800 Subject: Expand derive - Debug for enums --- src/expand/derive.cpp | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/expand/derive.cpp b/src/expand/derive.cpp index 58eb1f96..c363b689 100644 --- a/src/expand/derive.cpp +++ b/src/expand/derive.cpp @@ -369,43 +369,56 @@ public: pat_a = AST::Pattern(AST::Pattern::TagValue(), AST::Pattern::Value::make_Named(base_path + v.m_name)); ), (Tuple, - // TODO: Complete this. ::std::vector pats_a; - //::std::vector nodes; + AST::ExprNodeP node; + + node = NEWNODE(NamedValue, AST::Path("f")); + node = NEWNODE(CallMethod, + mv$(node), AST::PathNode("debug_tuple",{}), + vec$( NEWNODE(String, v.m_name) ) + ); for( unsigned int idx = 0; idx < e.m_sub_types.size(); idx ++ ) { auto name_a = FMT("a" << idx); pats_a.push_back( ::AST::Pattern(::AST::Pattern::TagBind(), name_a, ::AST::PatternBinding::Type::REF) ); - //nodes.push_back( this->assert_is_eq(assert_method_path, NEWNODE(NamedValue, AST::Path(name_a))) ); - } - //code = NEWNODE(Block, mv$(nodes)); - code = NEWNODE(CallMethod, - NEWNODE(NamedValue, AST::Path("f")), - AST::PathNode("write_str",{}), - vec$( NEWNODE(String, v.m_name + "(...)") ) - ); + node = NEWNODE(CallMethod, + mv$(node), AST::PathNode("field",{}), + vec$( + NEWNODE(NamedValue, AST::Path(name_a)) + ) + ); + } + code = NEWNODE(CallMethod, mv$(node), AST::PathNode("finish",{}), {}); pat_a = AST::Pattern(AST::Pattern::TagNamedTuple(), base_path + v.m_name, mv$(pats_a)); ), (Struct, ::std::vector< ::std::pair > pats_a; - //::std::vector nodes; + AST::ExprNodeP node; + + node = NEWNODE(NamedValue, AST::Path("f")); + node = NEWNODE(CallMethod, + mv$(node), AST::PathNode("debug_struct",{}), + vec$( NEWNODE(String, v.m_name) ) + ); for( const auto& fld : e.m_fields ) { auto name_a = FMT("a" << fld.m_name); pats_a.push_back( ::std::make_pair(fld.m_name, ::AST::Pattern(::AST::Pattern::TagBind(), name_a, ::AST::PatternBinding::Type::REF)) ); - //nodes.push_back( this->assert_is_eq(assert_method_path, NEWNODE(NamedValue, AST::Path(name_a))) ); + + node = NEWNODE(CallMethod, + mv$(node), AST::PathNode("field",{}), + vec$( + NEWNODE(String, fld.m_name), + NEWNODE(NamedValue, AST::Path(name_a)) + ) + ); } - //code = NEWNODE(Block, mv$(nodes) ); - code = NEWNODE(CallMethod, - NEWNODE(NamedValue, AST::Path("f")), - AST::PathNode("write_str",{}), - vec$( NEWNODE(String, v.m_name + "{...}") ) - ); + code = NEWNODE(CallMethod, mv$(node), AST::PathNode("finish",{}), {}); pat_a = AST::Pattern(AST::Pattern::TagStruct(), base_path + v.m_name, mv$(pats_a), true); ) ) -- cgit v1.2.3 From ee773edacfeba650931c604e266cf0862dbc2bfe Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Feb 2017 18:15:22 +0800 Subject: Expand stringify! - Fix trailing space --- src/expand/stringify.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/expand/stringify.cpp b/src/expand/stringify.cpp index f43d896f..cbb5c65c 100644 --- a/src/expand/stringify.cpp +++ b/src/expand/stringify.cpp @@ -20,8 +20,9 @@ class CExpander: auto lex = TTStream(tt); while( GET_TOK(tok, lex) != TOK_EOF ) { + if(!rv.empty()) + rv += " "; rv += tok.to_str(); - rv += " "; } return box$( TTStreamO(TokenTree(Token(TOK_STRING, mv$(rv)))) ); -- cgit v1.2.3 From 0c7faa5fd118366c4b272e036e95625ebccf67ea Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Feb 2017 18:15:35 +0800 Subject: Main - Add support for -g option --- Makefile | 21 +++++++++++++-------- src/main.cpp | 10 ++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index a5c3edf4..d5ff2b53 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,11 @@ CXXFLAGS += -Wno-pessimizing-move CXXFLAGS += -Wno-misleading-indentation #CXXFLAGS += -Wno-unused-private-field + +# - Flags to pass to all mrustc invocations +RUST_FLAGS := --cfg debug_assertions +#RUST_FLAGS += -g + SHELL = bash ifeq ($(DBGTPL),) @@ -127,14 +132,14 @@ output/lib%.hir: $(RUSTCSRC)src/lib%/lib.rs $(RUSTCSRC) $(BIN) @echo "--- [MRUSTC] $@" @mkdir -p output/ @rm -f $@ - $(DBG) $(ENV_$@) $(BIN) $< -o $@ $(ARGS_$@) $(PIPECMD) + $(DBG) $(ENV_$@) $(BIN) $< -o $@ $(RUST_FLAGS) $(ARGS_$@) $(PIPECMD) # # HACK: Work around gdb returning success even if the program crashed @test -e $@ output/lib%.hir: $(RUSTCSRC)src/lib%/src/lib.rs $(RUSTCSRC) $(BIN) @echo "--- [MRUSTC] $@" @mkdir -p output/ @rm -f $@ - $(DBG) $(BIN) $< -o $@ $(PIPECMD) + $(DBG) $(ENV_$@) $(BIN) $< -o $@ $(RUST_FLAGS) $(ARGS_$@) $(PIPECMD) # # HACK: Work around gdb returning success even if the program crashed @test -e $@ @@ -163,13 +168,13 @@ output/librustc_llvm.hir: $(LLVM_LINKAGE_FILE) RUSTC_LLVM_LINKAGE: $(LLVM_LINKAGE_FILE) output/librustc_llvm_build: rustc-nightly/src/librustc_llvm/build.rs $(call fcn_extcrate, std gcc build_helper alloc_system panic_abort) @echo "--- [MRUSTC] $@" - $(BIN) $< -o $@ -L output/libs $(PIPECMD) + $(BIN) $< -o $@ -L output/libs $(RUST_FLAGS) $(PIPECMD) output/libgcc.hir: crates.io/gcc-0.3.28/src/lib.rs $(BIN) output/libstd.hir @echo "--- [MRUSTC] $@" - $(BIN) $< -o $@ --crate-type rlib --crate-name gcc $(PIPECMD) + $(BIN) $< -o $@ --crate-type rlib --crate-name gcc $(RUST_FLAGS) $(PIPECMD) output/libbuild_helper.hir: rustc-nightly/src/build_helper/lib.rs $(BIN) output/libstd.hir @echo "--- [MRUSTC] $@" - $(BIN) $< -o $@ --crate-type rlib --crate-name build_helper $(PIPECMD) + $(BIN) $< -o $@ --crate-type rlib --crate-name build_helper $(RUST_FLAGS) $(PIPECMD) crates.io/%/src/lib.rs: crates.io/%.tar.gz tar -xf $< -C crates.io/ @@ -199,7 +204,7 @@ output/cargo_libflate/libminiz.a: output/libflate_build output/libflate_build: rustc-nightly/src/libflate/build.rs $(call fcn_extcrate, std gcc alloc_system panic_abort) @echo "--- [MRUSTC] $@" - $(BIN) $< -o $@ -L output/libs $(PIPECMD) + $(BIN) $< -o $@ -L output/libs $(RUST_FLAGS) $(PIPECMD) ARGS_output/librustc_llvm.hir := --cfg llvm_component=x86 --cfg cargobuild ENV_output/librustc_llvm.hir := CFG_LLVM_LINKAGE_FILE=$(LLVM_LINKAGE_FILE) @@ -268,7 +273,7 @@ output/rustc: $(RUSTCSRC)src/rustc/rustc.rs output/librustc_driver.hir output/ru @echo "--- [MRUSTC] $@" @mkdir -p output/ @rm -f $@ - $V$(DBG) $(BIN) $< -o $@ -L output/libs $$(cat output/rustc_link_opts.txt output/rustc_link_opts-libflate.txt) -l stdc++ $(PIPECMD) + $V$(DBG) $(BIN) $< -o $@ -L output/libs $$(cat output/rustc_link_opts.txt output/rustc_link_opts-libflate.txt) -l stdc++ $(RUST_FLAGS) $(PIPECMD) # # HACK: Work around gdb returning success even if the program crashed @test -e $@ @@ -346,7 +351,7 @@ rust_tests-run-fail: $(call DEF_RUST_TESTS,run-fail) output/rust/test_run-pass_hello: $(RUST_TESTS_DIR)run-pass/hello.rs output/libstd.hir $(BIN) output/liballoc_system.hir output/libpanic_abort.hir @mkdir -p $(dir $@) @echo "--- [MRUSTC] -o $@" - $(DBG) $(BIN) $< -L output/libs -o $@ $(PIPECMD) + $(DBG) $(BIN) $< -L output/libs -o $@ $(RUST_FLAGS) $(PIPECMD) @echo "--- [$@]" @./$@ diff --git a/src/main.cpp b/src/main.cpp index 3eb385f7..28889159 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -131,6 +131,9 @@ struct ProgramParams ::AST::Crate::Type crate_type = ::AST::Crate::Type::Unknown; ::std::string crate_name; + unsigned opt_level = 0; + bool emit_debug_info = false; + ::std::vector lib_search_dirs; ::std::vector libraries; @@ -450,6 +453,7 @@ int main(int argc, char *argv[]) for(const char* libdir : params.libraries ) { trans_opt.libraries.push_back( libdir ); } + trans_opt.emit_debug_info = params.emit_debug_info; // Generate code for non-generic public items (if requested) switch( crate_type ) @@ -579,6 +583,12 @@ ProgramParams::ProgramParams(int argc, char *argv[]) } this->outfile = argv[++i]; break; + case 'O': + this->opt_level = 2; + break; + case 'g': + this->emit_debug_info = true; + break; default: exit(1); } -- cgit v1.2.3 From e00565479fd031681f047225cac5a26cfa94ad5e Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 2 Mar 2017 18:20:14 +0800 Subject: MIR Gen - More Param usage --- src/hir_typeck/static.cpp | 12 ++++++++---- src/mir/from_hir.cpp | 23 ++++++++++++++++------- src/trans/codegen_c.cpp | 3 +-- src/trans/enumerate.cpp | 4 +++- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/hir_typeck/static.cpp b/src/hir_typeck/static.cpp index 5fec2477..135c778c 100644 --- a/src/hir_typeck/static.cpp +++ b/src/hir_typeck/static.cpp @@ -548,7 +548,7 @@ bool StaticTraitResolve::find_impl__check_crate_raw( ( ), (TraitBound, - DEBUG("[find_impl] Trait bound " << e.type << " : " << e.trait); + DEBUG("Trait bound " << e.type << " : " << e.trait); auto b_ty_mono = monomorphise_type_with(sp, e.type, cb_monomorph); this->expand_associated_types(sp, b_ty_mono); auto b_tp_mono = monomorphise_traitpath_with(sp, e.trait, cb_monomorph, false); @@ -559,14 +559,15 @@ bool StaticTraitResolve::find_impl__check_crate_raw( // TODO: These should be tagged with the source trait and that source trait used for expansion. this->expand_associated_types(sp, assoc_bound.second); } - DEBUG("[find_impl] - b_ty_mono = " << b_ty_mono << ", b_tp_mono = " << b_tp_mono); + DEBUG("- b_ty_mono = " << b_ty_mono << ", b_tp_mono = " << b_tp_mono); // HACK: If the type is '_', assume the bound passes if( b_ty_mono.m_data.is_Infer() ) { continue ; } // TODO: This is extrememly inefficient (looks up the trait impl 1+N times) - if( b_tp_mono.m_type_bounds.size() > 0 ) { + if( b_tp_mono.m_type_bounds.size() > 0 ) + { // for(const auto& assoc_bound : b_tp_mono.m_type_bounds) { const auto& aty_name = assoc_bound.first; @@ -597,10 +598,13 @@ bool StaticTraitResolve::find_impl__check_crate_raw( } } } - else + + // TODO: Detect if the associated type bound above is from directly the bounded trait, and skip this if it's the case + //else { bool rv = false; if( b_ty_mono.m_data.is_Generic() && (b_ty_mono.m_data.as_Generic().binding >> 8) == 2 ) { + DEBUG("- Placeholder param " << b_ty_mono << ", magic success"); rv = true; } else { diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 722e3aa1..4b9b6f5e 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -1524,20 +1524,29 @@ namespace { for(auto& arg : args) { this->visit_node_ptr(arg); - - if( args.size() == 1 ) + if( !m_builder.block_active() ) + { + auto tmp = m_builder.new_temporary(arg->m_res_type); + values.push_back( mv$(tmp) ); + } + else if( args.size() == 1 ) { values.push_back( m_builder.get_result_in_param(arg->span(), arg->m_res_type, /*allow_missing_value=*/true) ); } else { - // NOTE: Have to allocate a new temporary because ordering matters - auto tmp = m_builder.new_temporary(arg->m_res_type); - if( m_builder.block_active() ) + auto res = m_builder.get_result(arg->span()); + if( auto* e = res.opt_Constant() ) { - m_builder.push_stmt_assign( arg->span(), tmp.clone(), m_builder.get_result(arg->span()) ); + values.push_back( mv$(*e) ); + } + else + { + // NOTE: Have to allocate a new temporary because ordering matters + auto tmp = m_builder.new_temporary(arg->m_res_type); + m_builder.push_stmt_assign( arg->span(), tmp.clone(), mv$(res) ); + values.push_back( mv$(tmp) ); } - values.push_back( mv$(tmp) ); } if(const auto* e = values.back().opt_LValue() ) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index f44c9980..5c56e4d1 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -1498,9 +1498,8 @@ namespace { (BinOp, emit_lvalue(e.dst); m_of << " = "; - MIR_ASSERT(mir_res, ve.val_l.is_LValue() || ve.val_r.is_LValue(), ""); ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, ve.val_l.is_LValue() ? ve.val_l.as_LValue() : ve.val_r.as_LValue()); + const auto& ty = ve.val_l.is_LValue() ? mir_res.get_lvalue_type(tmp, ve.val_l.as_LValue()) : tmp = mir_res.get_const_type(ve.val_l.as_Constant()); if( ty.m_data.is_Borrow() ) { m_of << "(slice_cmp("; emit_param(ve.val_l); m_of << ", "; emit_param(ve.val_r); m_of << ")"; switch(ve.op) diff --git a/src/trans/enumerate.cpp b/src/trans/enumerate.cpp index 89846344..e4560ffa 100644 --- a/src/trans/enumerate.cpp +++ b/src/trans/enumerate.cpp @@ -1150,7 +1150,7 @@ namespace { ::std::vector<::HIR::TypeRef> best_impl_params; const ::HIR::TraitImpl* best_impl = nullptr; resolve.find_impl(sp, e.trait.m_path, e.trait.m_params, *e.type, [&](auto impl_ref, auto is_fuzz) { - DEBUG("Found " << impl_ref); + DEBUG("[get_ent_fullpath] Found " << impl_ref); //ASSERT_BUG(sp, !is_fuzz, "Fuzzy match not allowed here"); if( ! impl_ref.m_data.is_TraitImpl() ) { DEBUG("Trans impl search found an invalid impl type"); @@ -1201,6 +1201,8 @@ namespace { else BUG(sp, "Parameter " << i << " unset"); } + if( is_spec ) + DEBUG("- Specialisable"); return !is_spec; } return false; -- cgit v1.2.3 From 0fadf8d1cebd6dfb1195245d46a0bde51bfbdb85 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 2 Mar 2017 23:15:04 +0800 Subject: MIR Gen - Reset drop flags to defaults after use --- src/mir/mir_builder.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index aa42cbc1..e3c0096d 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -302,6 +302,12 @@ void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int auto stmt = ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val), flag }); m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); + + if( flag != ~0u ) + { + // Reset flag value back to default. + push_stmt_set_dropflag_val(sp, flag, m_output.drop_flags.at(flag)); + } } void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsigned int flag/*=~0u*/) { @@ -312,6 +318,12 @@ void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsig DEBUG("DROP shallow " << val); m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Drop({ ::MIR::eDropKind::SHALLOW, mv$(val), flag }) ); + + if( flag != ~0u ) + { + // Reset flag value back to default. + push_stmt_set_dropflag_val(sp, flag, m_output.drop_flags.at(flag)); + } } void MirBuilder::push_stmt_asm(const Span& sp, ::MIR::Statement::Data_Asm data) { -- cgit v1.2.3 From 6dbd3cb0dad2bc180cfca152d14508526fdeb1ca Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 3 Mar 2017 21:15:39 +0800 Subject: format_args! - Support complex formatting types --- src/expand/format_args.cpp | 115 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/src/expand/format_args.cpp b/src/expand/format_args.cpp index 60c92952..3d0eb9af 100644 --- a/src/expand/format_args.cpp +++ b/src/expand/format_args.cpp @@ -406,6 +406,24 @@ namespace { toks.push_back( Token(TOK_IDENT, ent) ); } } + void push_toks(::std::vector& toks, Token t1) { + toks.push_back( mv$(t1) ); + } + void push_toks(::std::vector& toks, Token t1, Token t2) { + toks.push_back( mv$(t1) ); + toks.push_back( mv$(t2) ); + } + //void push_toks(::std::vector& toks, Token t1, Token t2, Token t3) { + // toks.push_back( mv$(t1) ); + // toks.push_back( mv$(t2) ); + // toks.push_back( mv$(t3) ); + //} + void push_toks(::std::vector& toks, Token t1, Token t2, Token t3, Token t4) { + toks.push_back( mv$(t1) ); + toks.push_back( mv$(t2) ); + toks.push_back( mv$(t3) ); + toks.push_back( mv$(t4) ); + } } class CFormatArgsExpander: @@ -575,10 +593,12 @@ class CFormatArgsExpander: } else // if(is_simple) { + // 1. Generate a set of arguments+formatters + // > Each combination of argument index and fragment type needs a unique entry in the `args` array + // Use new_v1_formatted // - requires creating more entries in the `args` list to cover multiple formatters for one value - //push_path(toks, crate, {"fmt", "Arguments", "new_v1_formatted"}); - push_path(toks, crate, {"fmt", "Arguments", "new_v1"}); + push_path(toks, crate, {"fmt", "Arguments", "new_v1_formatted"}); // ( toks.push_back( TokenTree(TOK_PAREN_OPEN) ); { @@ -586,14 +606,97 @@ class CFormatArgsExpander: toks.push_back( Token(TOK_IDENT, "FRAGMENTS") ); toks.push_back( TokenTree(TOK_COMMA) ); - // 1. Generate a set of arguments+formatters - // TODO: Fragments to format // - The format stored by mrustc doesn't quite work with how rustc (and fmt::rt::v1) works toks.push_back( TokenTree(TOK_AMP) ); toks.push_back( TokenTree(TOK_SQUARE_OPEN) ); - //for(const auto& frag : fragments ) { - //} + for(const auto& frag : fragments ) + { + push_path(toks, crate, {"fmt", "ArgumentV1", "new"}); + toks.push_back( Token(TOK_PAREN_OPEN) ); + toks.push_back( Token(TOK_IDENT, FMT("a" << frag.arg_index)) ); + + toks.push_back( TokenTree(TOK_COMMA) ); + + push_path(toks, crate, {"fmt", frag.trait_name, "fmt"}); + toks.push_back( TokenTree(TOK_PAREN_CLOSE) ); + toks.push_back( TokenTree(TOK_COMMA) ); + } + toks.push_back( TokenTree(TOK_SQUARE_CLOSE) ); + toks.push_back( TokenTree(TOK_COMMA) ); + + toks.push_back( TokenTree(TOK_AMP) ); + toks.push_back( TokenTree(TOK_SQUARE_OPEN) ); + for(const auto& frag : fragments) + { + push_path(toks, crate, {"fmt", "rt", "v1", "Argument"}); + toks.push_back( TokenTree(TOK_BRACE_OPEN) ); + + push_toks(toks, Token(TOK_IDENT, "position"), TOK_COLON ); + push_path(toks, crate, {"fmt", "rt", "v1", "Position", "Next"}); + push_toks(toks, TOK_COMMA); + + push_toks(toks, Token(TOK_IDENT, "format"), TOK_COLON ); + push_path(toks, crate, {"fmt", "rt", "v1", "FormatSpec"}); + toks.push_back( TokenTree(TOK_BRACE_OPEN) ); + { + push_toks(toks, Token(TOK_IDENT, "fill"), TOK_COLON, Token(uint64_t(frag.args.align_char), CORETYPE_CHAR), TOK_COMMA ); + + push_toks(toks, Token(TOK_IDENT, "align"), TOK_COLON); + const char* align_var_name = nullptr; + switch( frag.args.align ) + { + case FmtArgs::Align::Unspec: align_var_name = "Unknown"; break; + case FmtArgs::Align::Left: align_var_name = "Left"; break; + case FmtArgs::Align::Center: align_var_name = "Center"; break; + case FmtArgs::Align::Right: align_var_name = "Right"; break; + } + push_path(toks, crate, {"fmt", "rt", "v1", "Alignment", align_var_name}); + push_toks(toks, TOK_COMMA); + + push_toks(toks, Token(TOK_IDENT, "flags"), TOK_COLON); + push_toks(toks, Token(uint64_t(0), CORETYPE_U32)); + push_toks(toks, TOK_COMMA); + + push_toks(toks, Token(TOK_IDENT, "precision"), TOK_COLON ); + if( frag.args.prec_is_arg || frag.args.prec != 0 ) { + push_path(toks, crate, {"fmt", "rt", "v1", "Count", "Is"}); + push_toks(toks, TOK_PAREN_OPEN); + if( frag.args.prec_is_arg ) { + push_toks(toks, TOK_STAR, Token(TOK_IDENT, FMT("a" << frag.args.prec)) ); + } + else { + push_toks(toks, Token(uint64_t(frag.args.prec), CORETYPE_UINT) ); + } + toks.push_back( TokenTree(TOK_PAREN_CLOSE) ); + } + else { + push_path(toks, crate, {"fmt", "rt", "v1", "Count", "Implied"}); + } + toks.push_back( TokenTree(TOK_COMMA) ); + + push_toks(toks, Token(TOK_IDENT, "width"), TOK_COLON ); + if( frag.args.width_is_arg || frag.args.width != 0 ) { + push_path(toks, crate, {"fmt", "rt", "v1", "Count", "Is"}); + push_toks(toks, TOK_PAREN_OPEN); + if( frag.args.width_is_arg ) { + push_toks(toks, TOK_STAR, Token(TOK_IDENT, FMT("a" << frag.args.width)) ); + } + else { + push_toks(toks, Token(uint64_t(frag.args.width), CORETYPE_UINT) ); + } + toks.push_back( TokenTree(TOK_PAREN_CLOSE) ); + } + else { + push_path(toks, crate, {"fmt", "rt", "v1", "Count", "Implied"}); + } + toks.push_back( TokenTree(TOK_COMMA) ); + } + toks.push_back( TokenTree(TOK_BRACE_CLOSE) ); + + toks.push_back( TokenTree(TOK_BRACE_CLOSE) ); + toks.push_back( TokenTree(TOK_COMMA) ); + } toks.push_back( TokenTree(TOK_SQUARE_CLOSE) ); } // ) -- cgit v1.2.3 From 0b18b46009d68029a933de79914d93462f450342 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 3 Mar 2017 21:16:00 +0800 Subject: HIR Typecheck Expr - Better bug message (can be triggered by bad code) --- src/hir_typeck/expr_cs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index 57bad017..3d4826ff 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -1199,7 +1199,7 @@ namespace { { const auto& name = val.first; auto it = ::std::find_if(fields.begin(), fields.end(), [&](const auto& v)->bool{ return v.first == name; }); - assert(it != fields.end()); + ASSERT_BUG(node.span(), it != fields.end(), "Field '" << name << "' not found in struct " << node.m_path); const auto& des_ty_r = it->second.ent; auto& des_ty_cache = node.m_value_types[it - fields.begin()]; const auto* des_ty = &des_ty_r; -- cgit v1.2.3 From 56ed7c849d74e2ad06522db5b96a2938b173a10c Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 3 Mar 2017 21:37:35 +0800 Subject: HIR - Fix Div and Mul mixup in HIR lower --- src/hir/dump.cpp | 2 +- src/hir/from_ast_expr.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hir/dump.cpp b/src/hir/dump.cpp index f00efcd5..7e877a4f 100644 --- a/src/hir/dump.cpp +++ b/src/hir/dump.cpp @@ -418,7 +418,7 @@ namespace { void visit(::HIR::ExprNode_Assign& node) override { this->visit_node_ptr(node.m_slot); - m_os << " = "; + m_os << " " << ::HIR::ExprNode_Assign::opname(node.m_op) << "= "; this->visit_node_ptr(node.m_value); } void visit(::HIR::ExprNode_BinOp& node) override diff --git a/src/hir/from_ast_expr.cpp b/src/hir/from_ast_expr.cpp index 25f6eade..d1d24f28 100644 --- a/src/hir/from_ast_expr.cpp +++ b/src/hir/from_ast_expr.cpp @@ -89,8 +89,8 @@ struct LowerHIR_ExprNode_Visitor: case ::AST::ExprNode_Assign::ADD: return ::HIR::ExprNode_Assign::Op::Add; case ::AST::ExprNode_Assign::SUB: return ::HIR::ExprNode_Assign::Op::Sub; - case ::AST::ExprNode_Assign::DIV: return ::HIR::ExprNode_Assign::Op::Mul; - case ::AST::ExprNode_Assign::MUL: return ::HIR::ExprNode_Assign::Op::Div; + case ::AST::ExprNode_Assign::MUL: return ::HIR::ExprNode_Assign::Op::Mul; + case ::AST::ExprNode_Assign::DIV: return ::HIR::ExprNode_Assign::Op::Div; case ::AST::ExprNode_Assign::MOD: return ::HIR::ExprNode_Assign::Op::Mod; case ::AST::ExprNode_Assign::AND: return ::HIR::ExprNode_Assign::Op::And; -- cgit v1.2.3 From ab60a92b6e442406abf90d78b3d6c2a970f7e744 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 10:16:35 +0800 Subject: MIR - Add a (disabled) full value state validator --- Makefile | 3 +- src/mir/check_full.cpp | 499 ++++++++++++++++++++++++++++++++++++++++++++++ src/mir/main_bindings.hpp | 1 + 3 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 src/mir/check_full.cpp diff --git a/Makefile b/Makefile index d5ff2b53..5689863d 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ CXXFLAGS += -Wno-misleading-indentation # - Flags to pass to all mrustc invocations RUST_FLAGS := --cfg debug_assertions -#RUST_FLAGS += -g +RUST_FLAGS += -g SHELL = bash @@ -104,6 +104,7 @@ OBJ += mir/mir.o mir/mir_ptr.o OBJ += mir/dump.o mir/helpers.o mir/visit_crate_mir.o OBJ += mir/from_hir.o mir/from_hir_match.o mir/mir_builder.o OBJ += mir/check.o mir/cleanup.o mir/optimise.o +OBJ += mir/check_full.o OBJ += hir/serialise.o hir/deserialise.o hir/serialise_lowlevel.o OBJ += trans/trans_list.o trans/mangling.o OBJ += trans/enumerate.o trans/monomorphise.o trans/codegen.o trans/codegen_c.o diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp new file mode 100644 index 00000000..d56d4a76 --- /dev/null +++ b/src/mir/check_full.cpp @@ -0,0 +1,499 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * mir/check_full.cpp + * - Full MIR correctness checks (expensive value state checks) + */ +#include "main_bindings.hpp" +#include "mir.hpp" +#include +#include +#include +#include + +namespace +{ + struct State + { + // 0 = invalid + // -1 = valid + // other = 1-based index into `inner_states` + unsigned int index; + + State(): index(0) {} + State(bool valid): index(valid ? ~0u : 0) {} + State(size_t idx): + index(idx+1) + { + } + + bool is_composite() const { + return index != 0 && index != ~0u; + } + bool is_valid() const { + return index != 0; + } + + bool operator==(const State& x) const { + return index == x.index; + } + bool operator!=(const State& x) const { + return !(*this == x); + } + }; + struct ValueStates + { + ::std::vector vars; + ::std::vector temporaries; + ::std::vector arguments; + State return_value; + ::std::vector drop_flags; + + ::std::vector< ::std::vector > inner_states; + + ::std::vector bb_path; + + ValueStates clone() const + { + return *this; + } + bool is_equivalent_to(const ValueStates& x) const + { + return true; + } + + void ensure_param_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::Param& lv) const + { + if(const auto* e = lv.opt_LValue()) + { + this->ensure_lvalue_valid(mir_res, *e); + } + } + void ensure_lvalue_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const + { + auto vs = get_lvalue_state(mir_res, lv); + ::std::vector path; + ensure_valid(mir_res, lv, vs, path); + } + private: + void ensure_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& root_lv, const State& vs, ::std::vector& path) const + { + if( vs.is_composite() ) + { + MIR_ASSERT(mir_res, vs.index-1 < this->inner_states.size(), ""); + const auto& states = this->inner_states.at( vs.index - 1 ); + + path.push_back(0); + for(const auto& inner_vs : states) + { + ensure_valid(mir_res,root_lv, inner_vs, path); + path.back() ++; + } + path.pop_back(); + } + else if( !vs.is_valid() ) + { + MIR_BUG(mir_res, "Accessing invalidated lvalue - " << root_lv << " - field path=[" << path << "], BBs=[" << this->bb_path << "]"); + } + else + { + } + } + + public: + void move_lvalue(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) + { + this->ensure_lvalue_valid(mir_res, lv); + + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, lv); + if( mir_res.m_resolve.type_is_copy(mir_res.sp, ty) ) + { + } + else + { + this->set_lvalue_state(mir_res, lv, State(false)); + } + } + void mark_lvalue_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) + { + this->set_lvalue_state(mir_res, lv, State(true)); + } + private: + State allocate_composite(unsigned int n_fields, State basis) + { + auto idx = inner_states.size(); + inner_states.push_back( ::std::vector(n_fields, basis) ); + return State(idx); + } + + State get_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const + { + TU_MATCHA( (lv), (e), + (Variable, + return vars.at(e); + ), + (Temporary, + return temporaries.at(e.idx); + ), + (Argument, + return arguments.at(e.idx); + ), + (Static, + return State(true); + ), + (Return, + return return_value; + ), + (Field, + auto vs = get_lvalue_state(mir_res, *e.val); + if( vs.is_composite() ) + { + MIR_ASSERT(mir_res, vs.index-1 < this->inner_states.size(), ""); + const auto& states = this->inner_states.at( vs.index - 1 ); + MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); + return states[e.field_index]; + } + else + { + return vs; + } + ), + (Deref, + auto vs = get_lvalue_state(mir_res, *e.val); + if( vs.is_composite() ) + { + MIR_TODO(mir_res, "Deref with composite state"); + } + else + { + return vs; + } + ), + (Index, + auto vs_v = get_lvalue_state(mir_res, *e.val); + auto vs_i = get_lvalue_state(mir_res, *e.idx); + MIR_ASSERT(mir_res, !vs_v.is_composite(), ""); + MIR_ASSERT(mir_res, !vs_i.is_composite(), ""); + return State(vs_v.is_valid() && vs_i.is_valid()); + ), + (Downcast, + auto vs_v = get_lvalue_state(mir_res, *e.val); + MIR_ASSERT(mir_res, !vs_v.is_composite(), ""); + return vs_v; + ) + ) + throw ""; + } + void set_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv, State new_vs) + { + TU_MATCHA( (lv), (e), + (Variable, + vars.at(e) = new_vs; + ), + (Temporary, + temporaries.at(e.idx) = new_vs; + ), + (Argument, + arguments.at(e.idx) = new_vs; + ), + (Static, + // Ignore. + ), + (Return, + return_value = new_vs; + ), + (Field, + auto cur_vs = get_lvalue_state(mir_res, *e.val); + if( !cur_vs.is_composite() && cur_vs == new_vs ) + { + // Not a composite, and no state change + } + else + { + if( !cur_vs.is_composite() ) + { + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, *e.val); + unsigned int n_fields = 0; + if( const auto* e = ty.m_data.opt_Tuple() ) + { + n_fields = e->size(); + } + else if( ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Struct() ) + { + const auto& e = ty.m_data.as_Path().binding.as_Struct(); + TU_MATCHA( (e->m_data), (se), + (Unit, + n_fields = 0; + ), + (Tuple, + n_fields = se.size(); + ), + (Named, + n_fields = se.size(); + ) + ) + } + else { + MIR_BUG(mir_res, "Unknown type being accessed with Field - " << ty); + } + cur_vs = this->allocate_composite(n_fields, cur_vs); + set_lvalue_state(mir_res, *e.val, cur_vs); + } + // Get composite state and assign into it + auto& states = this->inner_states.at( cur_vs.index - 1 ); + MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); + states[e.field_index] = new_vs; + } + ), + (Deref, + auto cur_vs = get_lvalue_state(mir_res, *e.val); + if( cur_vs.is_composite() ) + { + MIR_TODO(mir_res, "Deref with composite state"); + } + else if( new_vs.is_composite() ) + { + MIR_TODO(mir_res, "Deref with composite state (store a composite)"); + } + else if( cur_vs != new_vs ) + { + MIR_TODO(mir_res, "Deref with composite state, store mismatched"); + } + else + { + } + ), + (Index, + auto vs_v = get_lvalue_state(mir_res, *e.val); + auto vs_i = get_lvalue_state(mir_res, *e.idx); + MIR_ASSERT(mir_res, !vs_v.is_composite(), ""); + MIR_ASSERT(mir_res, !vs_i.is_composite(), ""); + + MIR_ASSERT(mir_res, vs_v.is_valid(), "Indexing an invalid value"); + MIR_ASSERT(mir_res, vs_i.is_valid(), "Indexing with an invalid index"); + + // NOTE: Ignore + ), + (Downcast, + // NOTE: Ignore + ) + ) + } + }; + + + struct StateSet + { + ::std::vector known_state_sets; + + bool add_state(const ValueStates& state_set) + { + for(const auto& s : this->known_state_sets) + { + if( s.is_equivalent_to(state_set) ) + { + return false; + } + } + this->known_state_sets.push_back( state_set.clone() ); + return true; + } + }; +} + + +// "Executes" the function, keeping track of drop flags and variable validities +void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Function& fcn) +{ + ::std::vector block_entry_states( fcn.blocks.size() ); + + ValueStates state; + state.arguments.resize( mir_res.m_args.size(), State(true) ); + state.vars.resize( fcn.named_variables.size() ); + state.temporaries.resize( fcn.temporaries.size() ); + state.drop_flags = fcn.drop_flags; + + ::std::vector< ::std::pair > todo_queue; + todo_queue.push_back( ::std::make_pair(0, mv$(state)) ); + while( ! todo_queue.empty() ) + { + auto cur_block = todo_queue.back().first; + auto state = mv$(todo_queue.back().second); + todo_queue.pop_back(); + + if( ! block_entry_states[cur_block].add_state(state) ) + { + continue ; + } + state.bb_path.push_back( cur_block ); + + const auto& blk = fcn.blocks.at(cur_block); + for(size_t i = 0; i < blk.statements.size(); i++) + { + mir_res.set_cur_stmt(cur_block, i); + + TU_MATCHA( (blk.statements[i]), (se), + (Assign, + DEBUG(mir_res << " " << se.dst << " = " << se.src); + TU_MATCHA( (se.src), (ve), + (Use, + state.move_lvalue(mir_res, ve); + ), + (Constant, + ), + (SizedArray, + state.ensure_param_valid(mir_res, ve.val); + ), + (Borrow, + state.ensure_lvalue_valid(mir_res, ve.val); + ), + // Cast on primitives + (Cast, + state.ensure_lvalue_valid(mir_res, ve.val); + ), + // Binary operation on primitives + (BinOp, + state.ensure_param_valid(mir_res, ve.val_l); + state.ensure_param_valid(mir_res, ve.val_r); + ), + // Unary operation on primitives + (UniOp, + state.ensure_lvalue_valid(mir_res, ve.val); + ), + // Extract the metadata from a DST pointer + // NOTE: If used on an array, this yields the array size (for generics) + (DstMeta, + state.ensure_lvalue_valid(mir_res, ve.val); + ), + // Extract the pointer from a DST pointer (as *const ()) + (DstPtr, + state.ensure_lvalue_valid(mir_res, ve.val); + ), + // Construct a DST pointer from a thin pointer and metadata + (MakeDst, + state.ensure_param_valid(mir_res, ve.ptr_val); + state.ensure_param_valid(mir_res, ve.meta_val); + ), + (Tuple, + for(const auto& v : ve.vals) + if(const auto* e = v.opt_LValue()) + state.move_lvalue(mir_res, *e); + ), + // Array literal + (Array, + for(const auto& v : ve.vals) + if(const auto* e = v.opt_LValue()) + state.move_lvalue(mir_res, *e); + ), + // Create a new instance of a union (and eventually enum) + (Variant, + if(const auto* e = ve.val.opt_LValue()) + state.move_lvalue(mir_res, *e); + ), + // Create a new instance of a struct (or enum) + (Struct, + for(const auto& v : ve.vals) + if(const auto* e = v.opt_LValue()) + state.move_lvalue(mir_res, *e); + ) + ) + state.mark_lvalue_valid(mir_res, se.dst); + ), + (Asm, + for(const auto& v : se.inputs) + state.ensure_lvalue_valid(mir_res, v.second); + for(const auto& v : se.outputs) + state.mark_lvalue_valid(mir_res, v.second); + ), + (SetDropFlag, + if( se.other == ~0u ) + { + state.drop_flags[se.idx] = se.new_val; + } + else + { + state.drop_flags[se.idx] = (se.new_val != state.drop_flags[se.other]); + } + ), + (Drop, + if( se.flag_idx == ~0u || state.drop_flags.at(se.flag_idx) ) + { + // TODO: Treat shallow drops differently + state.move_lvalue(mir_res, se.slot); + } + ) + ) + } + + mir_res.set_cur_stmt_term(cur_block); + DEBUG(mir_res << " " << blk.terminator); + TU_MATCHA( (blk.terminator), (te), + (Incomplete, + ), + (Return, + ), + (Diverge, + ), + (Goto, // Jump to another block + todo_queue.push_back( ::std::make_pair(te, mv$(state)) ); + ), + (Panic, + todo_queue.push_back( ::std::make_pair(te.dst, mv$(state)) ); + ), + (If, + state.ensure_lvalue_valid(mir_res, te.cond); + todo_queue.push_back( ::std::make_pair(te.bb0, state.clone()) ); + todo_queue.push_back( ::std::make_pair(te.bb1, mv$(state)) ); + ), + (Switch, + state.ensure_lvalue_valid(mir_res, te.val); + for(size_t i = 0; i < te.targets.size(); i ++) + { + todo_queue.push_back( ::std::make_pair(te.targets[i], i == te.targets.size()-1 ? mv$(state) : state.clone()) ); + } + ), + (Call, + if(const auto* e = te.fcn.opt_Value()) + { + state.ensure_lvalue_valid(mir_res, *e); + } + for(auto& arg : te.args) + { + if(const auto* e = arg.opt_LValue()) + { + state.move_lvalue(mir_res, *e); + } + } + todo_queue.push_back( ::std::make_pair(te.panic_block, state.clone()) ); + state.mark_lvalue_valid(mir_res, te.ret_val); + todo_queue.push_back( ::std::make_pair(te.ret_block, mv$(state)) ); + ) + ) + } +} + +void MIR_Validate_Full(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, const ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) +{ + TRACE_FUNCTION_F(path); + Span sp; + ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; + // Validation rules: + + MIR_Validate_FullValState(state, fcn); +} + +// -------------------------------------------------------------------- + +void MIR_CheckCrate_Full(/*const*/ ::HIR::Crate& crate) +{ + ::MIR::OuterVisitor ov(crate, [](const auto& res, const auto& p, auto& expr, const auto& args, const auto& ty) + { + MIR_Validate_Full(res, p, *expr.m_mir, args, ty); + } + ); + ov.visit_crate( crate ); +} + diff --git a/src/mir/main_bindings.hpp b/src/mir/main_bindings.hpp index dc9a61a9..0d6074cb 100644 --- a/src/mir/main_bindings.hpp +++ b/src/mir/main_bindings.hpp @@ -15,6 +15,7 @@ class Crate; extern void HIR_GenerateMIR(::HIR::Crate& crate); extern void MIR_Dump(::std::ostream& sink, const ::HIR::Crate& crate); extern void MIR_CheckCrate(/*const*/ ::HIR::Crate& crate); +extern void MIR_CheckCrate_Full(/*const*/ ::HIR::Crate& crate); extern void MIR_CleanupCrate(::HIR::Crate& crate); extern void MIR_OptimiseCrate(::HIR::Crate& crate); -- cgit v1.2.3 From 89100b4a1e73c7abfea228b8512bd0f79952f911 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 13:02:23 +0800 Subject: MIR Check Full - Handling of shallow drops --- src/mir/check_full.cpp | 180 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 165 insertions(+), 15 deletions(-) diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index d56d4a76..9affd0b8 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -12,6 +12,8 @@ #include #include +#define ENABLE_LEAK_DETECTOR 0 + namespace { struct State @@ -42,6 +44,20 @@ namespace return !(*this == x); } }; + + struct ValueStates; +} + +struct StateFmt { + const ValueStates& vss; + State s; + StateFmt( const ValueStates& vss, State s ): + vss(vss), s(s) + {} +}; + +namespace +{ struct ValueStates { ::std::vector vars; @@ -63,6 +79,10 @@ namespace return true; } + StateFmt fmt_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const { + return StateFmt(*this, get_lvalue_state(mir_res, lv)); + } + void ensure_param_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::Param& lv) const { if(const auto* e = lv.opt_LValue()) @@ -120,14 +140,60 @@ namespace { this->set_lvalue_state(mir_res, lv, State(true)); } + + // Scan states and clear unused composite slots + void garbage_collect() + { + struct Marker { + ::std::vector used; + + void mark_from_state(const ValueStates& vss, const State& s) { + if(s.is_composite()) { + used.at(s.index-1) = true; + for(const auto& s : vss.inner_states.at(s.index-1)) + mark_from_state(vss, s); + } + } + }; + Marker m; + m.used.resize(this->inner_states.size(), false); + + for(const auto& s : this->vars) + m.mark_from_state(*this, s); + for(const auto& s : this->temporaries) + m.mark_from_state(*this, s); + for(const auto& s : this->arguments) + m.mark_from_state(*this, s); + m.mark_from_state(*this, this->return_value); + } private: State allocate_composite(unsigned int n_fields, State basis) { + assert(n_fields > 0); + for(size_t i = 0; i < this->inner_states.size(); i ++) + { + if( this->inner_states[i].size() == 0 ) + { + inner_states[i] = ::std::vector(n_fields, basis); + return State(i); + } + } auto idx = inner_states.size(); inner_states.push_back( ::std::vector(n_fields, basis) ); return State(idx); } + public: + ::std::vector& get_composite(const ::MIR::TypeResolve& mir_res, const State& vs) + { + MIR_ASSERT(mir_res, vs.index-1 < this->inner_states.size(), ""); + return this->inner_states.at( vs.index - 1 ); + } + const ::std::vector& get_composite(const ::MIR::TypeResolve& mir_res, const State& vs) const + { + MIR_ASSERT(mir_res, vs.index-1 < this->inner_states.size(), ""); + return this->inner_states.at( vs.index - 1 ); + } State get_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const { TU_MATCHA( (lv), (e), @@ -150,8 +216,7 @@ namespace auto vs = get_lvalue_state(mir_res, *e.val); if( vs.is_composite() ) { - MIR_ASSERT(mir_res, vs.index-1 < this->inner_states.size(), ""); - const auto& states = this->inner_states.at( vs.index - 1 ); + const auto& states = this->get_composite(mir_res, vs); MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); return states[e.field_index]; } @@ -186,6 +251,7 @@ namespace ) throw ""; } + void set_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv, State new_vs) { TU_MATCHA( (lv), (e), @@ -243,27 +309,32 @@ namespace set_lvalue_state(mir_res, *e.val, cur_vs); } // Get composite state and assign into it - auto& states = this->inner_states.at( cur_vs.index - 1 ); + auto& states = this->get_composite(mir_res, cur_vs); MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); states[e.field_index] = new_vs; } ), (Deref, auto cur_vs = get_lvalue_state(mir_res, *e.val); - if( cur_vs.is_composite() ) - { - MIR_TODO(mir_res, "Deref with composite state"); - } - else if( new_vs.is_composite() ) - { - MIR_TODO(mir_res, "Deref with composite state (store a composite)"); - } - else if( cur_vs != new_vs ) + if( !cur_vs.is_composite() && cur_vs == new_vs ) { - MIR_TODO(mir_res, "Deref with composite state, store mismatched"); + // Not a composite, and no state change } else { + if( !cur_vs.is_composite() ) + { + //::HIR::TypeRef tmp; + //const auto& ty = mir_res.get_lvalue_type(tmp, *e.val); + // TODO: Should this check if the type is Box? + + cur_vs = this->allocate_composite(2, cur_vs); + set_lvalue_state(mir_res, *e.val, cur_vs); + } + // Get composite state and assign into it + auto& states = this->get_composite(mir_res, cur_vs); + MIR_ASSERT(mir_res, states.size() == 2, "Deref with invalid state list size"); + states[1] = new_vs; } ), (Index, @@ -304,6 +375,25 @@ namespace }; } +::std::ostream& operator<<(::std::ostream& os, const StateFmt& x) +{ + if(x.s.index == 0) { + os << "_"; + } + else if( x.s.index == ~0u ) { + os << "X"; + } + else { + assert(x.s.index-1 < x.vss.inner_states.size()); + const auto& is = x.vss.inner_states[x.s.index-1]; + os << "["; + for(const auto& s : is) + os << StateFmt(x.vss, s); + os << "]"; + } + return os; +} + // "Executes" the function, keeping track of drop flags and variable validities void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Function& fcn) @@ -421,19 +511,79 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio (Drop, if( se.flag_idx == ~0u || state.drop_flags.at(se.flag_idx) ) { - // TODO: Treat shallow drops differently - state.move_lvalue(mir_res, se.slot); + if( se.kind == ::MIR::eDropKind::SHALLOW ) + { + // HACK: A move out of a Box generates the following pattern: `[[[[X_]]X]]` + // - Ensure that that is the pattern we're seeing here. + auto vs = state.get_lvalue_state(mir_res, se.slot); + + MIR_ASSERT(mir_res, !vs.is_valid(), "Shallow drop on fully-valid value - " << se.slot); + + // Box - Wrapper around Unique + MIR_ASSERT(mir_res, vs.is_composite(), "Shallow drop on non-composite state - " << se.slot << " (state=" << StateFmt(state,vs) << ")"); + const auto& sub_states = state.get_composite(mir_res, vs); + MIR_ASSERT(mir_res, sub_states.size() == 1, ""); + // Unique - NonZero<*const T>, PhantomData + MIR_ASSERT(mir_res, sub_states[0].is_composite(), ""); + const auto& sub_states2 = state.get_composite(mir_res, sub_states[0]); + MIR_ASSERT(mir_res, sub_states2.size() == 2, "- " << StateFmt(state, sub_states[0])); + MIR_ASSERT(mir_res, sub_states2[0].is_composite(), ""); + MIR_ASSERT(mir_res, sub_states2[1].is_valid(), ""); + // `NonZero<*const T>` - *const T + const auto& sub_states3 = state.get_composite(mir_res, sub_states2[0]); + MIR_ASSERT(mir_res, sub_states3.size() == 1, "- " << StateFmt(state, sub_states2[0])); + MIR_ASSERT(mir_res, sub_states3[0].is_composite(), ""); + // `*const T` - Moved out of, so has a composite state + const auto& sub_states4 = state.get_composite(mir_res, sub_states3[0]); + MIR_ASSERT(mir_res, sub_states4.size() == 2, "- " << StateFmt(state, sub_states3[0])); + MIR_ASSERT(mir_res, sub_states4[0].is_valid(), "Shallow drop on deallocated Box - " << se.slot << " (state=" << StateFmt(state,vs) << ")"); + // TODO: This is leak protection, enable it once the rest works + if( ENABLE_LEAK_DETECTOR ) + { + MIR_ASSERT(mir_res, !sub_states4[1].is_valid(), "Shallow drop on populated Box - " << se.slot << " (state=" << StateFmt(state,vs) << ")"); + } + + state.set_lvalue_state(mir_res, se.slot, State(false)); + } + else + { + state.move_lvalue(mir_res, se.slot); + } } ) ) } + state.garbage_collect(); + mir_res.set_cur_stmt_term(cur_block); DEBUG(mir_res << " " << blk.terminator); TU_MATCHA( (blk.terminator), (te), (Incomplete, ), (Return, + state.ensure_lvalue_valid(mir_res, ::MIR::LValue::make_Return({})); + if( ENABLE_LEAK_DETECTOR ) + { + auto ensure_dropped = [&](const State& s, const ::MIR::LValue& lv) { + if( s.is_valid() ) { + // Check if !Copy + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, lv); + if( mir_res.m_resolve.type_is_copy(mir_res.sp, ty) ) { + } + else { + MIR_BUG(mir_res, "Value " << lv << " was not dropped at end of function"); + } + } + }; + for(unsigned i = 0; i < state.arguments.size(); i ++ ) { + ensure_dropped(state.arguments[i], ::MIR::LValue::make_Argument({i})); + } + for(unsigned i = 0; i < state.vars.size(); i ++ ) { + ensure_dropped(state.vars[i], ::MIR::LValue::make_Variable(i)); + } + } ), (Diverge, ), -- cgit v1.2.3 From 40d192c4e2ee26add4439889461fa314a195e90a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 13:36:34 +0800 Subject: MIR Check Full - Variants --- src/mir/check_full.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index 9affd0b8..34a14d0c 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -245,8 +245,16 @@ namespace ), (Downcast, auto vs_v = get_lvalue_state(mir_res, *e.val); - MIR_ASSERT(mir_res, !vs_v.is_composite(), ""); - return vs_v; + if( vs_v.is_composite() ) + { + const auto& states = this->get_composite(mir_res, vs_v); + MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size"); + return states[0]; + } + else + { + return vs_v; + } ) ) throw ""; @@ -349,7 +357,23 @@ namespace // NOTE: Ignore ), (Downcast, - // NOTE: Ignore + auto cur_vs = get_lvalue_state(mir_res, *e.val); + if( !cur_vs.is_composite() && cur_vs == new_vs ) + { + // Not a composite, and no state change + } + else + { + if( !cur_vs.is_composite() ) + { + cur_vs = this->allocate_composite(1, cur_vs); + set_lvalue_state(mir_res, *e.val, cur_vs); + } + // Get composite state and assign into it + auto& states = this->get_composite(mir_res, cur_vs); + MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size"); + states[0] = new_vs; + } ) ) } @@ -517,7 +541,7 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio // - Ensure that that is the pattern we're seeing here. auto vs = state.get_lvalue_state(mir_res, se.slot); - MIR_ASSERT(mir_res, !vs.is_valid(), "Shallow drop on fully-valid value - " << se.slot); + MIR_ASSERT(mir_res, vs.index != ~0u, "Shallow drop on fully-valid value - " << se.slot); // Box - Wrapper around Unique MIR_ASSERT(mir_res, vs.is_composite(), "Shallow drop on non-composite state - " << se.slot << " (state=" << StateFmt(state,vs) << ")"); -- cgit v1.2.3 From 9cf7dc21aaf3ccd6967960f31443f6e6d594c362 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 13:36:42 +0800 Subject: MIR Gen - Patch around edge case where an arm can end up with the drop flag incorrectly set --- src/mir/mir_builder.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index e3c0096d..1733df2f 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -747,7 +747,7 @@ namespace case VarState::TAG_Optional: { // Was invalid, now optional. auto flag_idx = new_state.as_Optional(); - if( builder.get_drop_flag_default(sp, flag_idx) != false ) { + if( true || builder.get_drop_flag_default(sp, flag_idx) != false ) { #if 1 auto new_flag = builder.new_drop_flag(false); builder.push_stmt_set_dropflag_other(sp, new_flag, flag_idx); @@ -757,7 +757,9 @@ namespace TODO(sp, "Drop flag default not false when going Invalid->Optional"); #endif } - old_state = VarState::make_Optional( flag_idx ); + else { + old_state = VarState::make_Optional( flag_idx ); + } return ; } case VarState::TAG_Partial: { @@ -1594,7 +1596,7 @@ void MirBuilder::moved_lvalue(const Span& sp, const ::MIR::LValue& lv) if( lvalue_is_copy(sp, lv) ) { } else { - // TODO: Partial moves. + // TODO: Partial moves of fields. moved_lvalue(sp, *e.val); } ), -- cgit v1.2.3 From b3362d47a704f2d474f9a2f1e78ba9bd386e7c01 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 14:02:07 +0800 Subject: MIR Check Full - State comparison, printing of state --- src/mir/check_full.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index 34a14d0c..1703dfd4 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -76,6 +76,58 @@ namespace } bool is_equivalent_to(const ValueStates& x) const { + struct H { + static bool equal(const ValueStates& vss_a, const State& a, const ValueStates& vss_b, const State& b) + { + if( a.index == 0 ) + { + return b.index == 0; + } + if( a.index == ~0u ) + { + return b.index == ~0u; + } + if( b.index == 0 || b.index == ~0u ) + { + return false; + } + + const auto& states_a = vss_a.inner_states.at( a.index - 1 ); + const auto& states_b = vss_b.inner_states.at( b.index - 1 ); + // NOTE: If there's two differen variants, this can happen. + if( states_a.size() != states_b.size() ) + return false; + + for(size_t i = 0; i < states_a.size(); i ++) + { + if( ! H::equal(vss_a, states_a[i], vss_b, states_b[i]) ) + return false; + } + // If the above loop didn't early exit, the two states are equal + return true; + } + }; + + if( ! H::equal(*this, return_value, x, x.return_value) ) + return false; + assert(vars.size() == x.vars.size()); + for(size_t i = 0; i < vars.size(); i ++) + { + if( ! H::equal(*this, vars[i], x, x.vars[i]) ) + return false; + } + assert(temporaries.size() == x.temporaries.size()); + for(size_t i = 0; i < temporaries.size(); i ++) + { + if( ! H::equal(*this, temporaries[i], x, x.temporaries[i]) ) + return false; + } + assert(arguments.size() == x.arguments.size()); + for(size_t i = 0; i < arguments.size(); i ++) + { + if( ! H::equal(*this, arguments[i], x, x.arguments[i]) ) + return false; + } return true; } @@ -152,6 +204,8 @@ namespace used.at(s.index-1) = true; for(const auto& s : vss.inner_states.at(s.index-1)) mark_from_state(vss, s); + + // TODO: Should this compact composites with all-equal inner states? } } }; @@ -418,6 +472,27 @@ namespace return os; } +namespace std { + ostream& operator<<(ostream& os, const ValueStates& x) + { + auto print_val = [&](auto tag, const State& s) { + if(s.is_composite()) { + os << tag << "=" << StateFmt(x,s); + } + else if( s.is_valid() ) { + os << tag; + } + else { + } + }; + + os << "ValueStates(path=[" << x.bb_path << "]"; + print_val(",rv", x.return_value); + os << ")"; + return os; + } +} + // "Executes" the function, keeping track of drop flags and variable validities void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Function& fcn) @@ -442,6 +517,7 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio { continue ; } + DEBUG("BB" << cur_block << " - " << state); state.bb_path.push_back( cur_block ); const auto& blk = fcn.blocks.at(cur_block); -- cgit v1.2.3 From b00e904995edd4415a2dbae0a5407269772e818a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 14:29:51 +0800 Subject: MIR Gen - Use MIR::Param for binops --- src/mir/from_hir.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 4b9b6f5e..60ac0275 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -331,7 +331,7 @@ namespace { if( e.extra_bind.is_valid() ) { // 1. Obtain remaining length - auto sub_val = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::Constant::make_Uint({ e.leading.size() + e.trailing.size(), ::HIR::CoreType::Usize })); + auto sub_val = ::MIR::Param(::MIR::Constant::make_Uint({ e.leading.size() + e.trailing.size(), ::HIR::CoreType::Usize })); ::MIR::LValue len_val = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_BinOp({ len_lval.clone(), ::MIR::eBinOp::SUB, mv$(sub_val) }) ); // 2. Obtain pointer to element @@ -760,7 +760,7 @@ namespace { m_builder.set_result( node.span(), mv$(result_val) ); } - void generate_checked_binop(const Span& sp, ::MIR::LValue res_slot, ::MIR::eBinOp op, ::MIR::LValue val_l, const ::HIR::TypeRef& ty_l, ::MIR::LValue val_r, const ::HIR::TypeRef& ty_r) + 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) { @@ -870,7 +870,16 @@ namespace { if( node.m_op != ::HIR::ExprNode_Assign::Op::None ) { auto dst_clone = dst.clone(); - auto val_lv = m_builder.lvalue_or_temp( node.span(), ty_val, mv$(val) ); + ::MIR::Param val_p; + if( auto* e = val.opt_Use() ) { + val_p = mv$(*e); + } + else if( auto* e = val.opt_Constant() ) { + val_p = mv$(*e); + } + else { + val_p = m_builder.lvalue_or_temp( node.span(), ty_val, mv$(val) ); + } ASSERT_BUG(sp, ty_slot.m_data.is_Primitive(), "Assignment operator overloads are only valid on primitives - ty_slot="< NOTE: No need to locally stitch blocks, next pass will do that // TODO: Use ValState to do full constant propagation across blocks - // 1. Remove based on known booleans within a single block - // - Eliminates `if false`/`if true` branches + // Remove redundant temporaries and evaluate known binops + for(auto& bb : fcn.blocks) + { + auto bbidx = &bb - &fcn.blocks.front(); + + ::std::map< ::MIR::LValue, ::MIR::Constant > known_values; + + auto check_param = [&](::MIR::Param& p) { + if(const auto* pe = p.opt_LValue()) { + auto it = known_values.find(*pe); + if( it != known_values.end() ) + { + p = it->second.clone(); + } + } + }; + + for(auto& stmt : bb.statements) + { + auto stmtidx = &stmt - &bb.statements.front(); + state.set_cur_stmt(bbidx, stmtidx); + // Scan statements forwards: + // - If a known temporary is used as Param::LValue, replace LValue with the value + // - If a UniOp has its input known, evaluate + // - If a BinOp has both values known, evaluate + if( auto* e = stmt.opt_Assign() ) + { + TU_MATCHA( (e->src), (se), + (Use, + auto it = known_values.find(se); + if( it != known_values.end() ) + { + e->src = it->second.clone(); + } + ), + (Constant, + // Ignore (knowledge done below) + ), + (SizedArray, + check_param(se.val); + ), + (Borrow, + ), + (Cast, + ), + (BinOp, + check_param(se.val_l); + check_param(se.val_r); + + if( se.val_l.is_Constant() && se.val_r.is_Constant() ) + { + // TODO: Evaluate BinOp + } + ), + (UniOp, + auto it = known_values.find(se.val); + if( it != known_values.end() ) + { + const auto& val = it->second; + // TODO: Evaluate UniOp + switch( se.op ) + { + case ::MIR::eUniOp::INV: + TU_MATCHA( (val), (ve), + (Uint, + auto val = ve.v; + switch(ve.t) + { + case ::HIR::CoreType::U8: val = (~val) & 0xFF; break; + case ::HIR::CoreType::U16: val = (~val) & 0xFFFF; break; + case ::HIR::CoreType::U32: val = (~val) & 0xFFFFFFFF; break; + case ::HIR::CoreType::Usize: + case ::HIR::CoreType::U64: + val = ~val; + break; + case ::HIR::CoreType::U128: + val = ~val; + break; + case ::HIR::CoreType::Char: + MIR_BUG(state, "Invalid use of ! on char"); + break; + default: + // Invalid type for Uint literal + break; + } + e->src = ::MIR::Constant::make_Uint({ val, ve.t }); + ), + (Int, + // Is ! valid on Int? + ), + (Float, + // Not valid? + ), + (Bool, + e->src = ::MIR::Constant::make_Bool({ !ve.v }); + ), + (Bytes, ), + (StaticString, ), + (Const, + // TODO: + ), + (ItemAddr, + ) + ) + break; + case ::MIR::eUniOp::NEG: + TU_MATCHA( (val), (ve), + (Uint, + // Not valid? + ), + (Int, + e->src = ::MIR::Constant::make_Int({ -ve.v, ve.t }); + ), + (Float, + e->src = ::MIR::Constant::make_Float({ -ve.v, ve.t }); + ), + (Bool, + // Not valid? + ), + (Bytes, ), + (StaticString, ), + (Const, + // TODO: + ), + (ItemAddr, + ) + ) + break; + } + } + ), + (DstMeta, + ), + (DstPtr, + ), + (MakeDst, + check_param(se.ptr_val); + check_param(se.meta_val); + ), + (Tuple, + for(auto& p : se.vals) + check_param(p); + ), + (Array, + for(auto& p : se.vals) + check_param(p); + ), + (Variant, + check_param(se.val); + ), + (Struct, + for(auto& p : se.vals) + check_param(p); + ) + ) + } + // - If a known temporary is borrowed mutably or mutated somehow, clear its knowledge + visit_mir_lvalues(stmt, [&known_values](const ::MIR::LValue& lv, ValUsage vu)->bool { + if( vu == ValUsage::Write ) { + auto it = known_values.find(lv); + if(it != known_values.end()) + known_values.erase(it); + } + return false; + }); + // - Locate `temp = SOME_CONST` and record value + if( const auto* e = stmt.opt_Assign() ) + { + if( e->dst.is_Temporary() && e->src.is_Constant() ) + { + known_values.insert(::std::make_pair( e->dst.clone(), e->src.as_Constant().clone() )); + } + } + } + + state.set_cur_stmt_term(bbidx); + } + + // - Remove based on known booleans within a single block + // > Eliminates `if false`/`if true` branches for(auto& bb : fcn.blocks) { auto bbidx = &bb - &fcn.blocks.front(); -- cgit v1.2.3 From 3245f70f41d309a9846b57c0f79812f65d5cdf61 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 15:50:15 +0800 Subject: HIR Typecheck Static - Look up trait impls for associated constants --- src/hir_typeck/static.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/hir_typeck/static.cpp b/src/hir_typeck/static.cpp index 135c778c..5aa02a9a 100644 --- a/src/hir_typeck/static.cpp +++ b/src/hir_typeck/static.cpp @@ -1459,7 +1459,44 @@ StaticTraitResolve::ValuePtr StaticTraitResolve::get_value(const Span& sp, const } else { - TODO(sp, "Search for trait impl"); + ImplRef best_impl; + this->find_impl(sp, pe.trait.m_path, &pe.trait.m_params, *pe.type, [&](auto impl, bool is_fuzz)->bool{ + if( ! impl.m_data.is_TraitImpl() ) + return false; + const auto& ti = *impl.m_data.as_TraitImpl().impl; + auto it = ti.m_constants.find(pe.item); + if(it == ti.m_constants.end()) + return false; + + if( impl.more_specific_than(best_impl) ) + { + best_impl = mv$(impl); + // If this value is specialisable, keep searching (return false) + return !it->second.is_specialisable; + } + // Keep searching + return false; + }); + if( !best_impl.is_valid() ) + { + TODO(sp, "What should be done when an impl can't be found? " << p); + } + + if( ! best_impl.m_data.is_TraitImpl() ) + TODO(sp, "Use bounded constant values for " << p); + auto& ie = best_impl.m_data.as_TraitImpl(); + out_params.pp_impl = &out_params.pp_impl_data; + for(auto ptr : ie.params) + { + // TODO: Avoid cloning when the params are in the placeholder array + out_params.pp_impl_data.m_types.push_back( ptr->clone() ); + } + + const auto& ti = *ie.impl; + const auto& c = ti.m_constants.at(pe.item); + + // TODO: What if the type requires monomorphisation? Leave it up to the caller + return &c.data; } throw ""; ), @@ -1471,6 +1508,7 @@ StaticTraitResolve::ValuePtr StaticTraitResolve::get_value(const Span& sp, const m_crate.find_type_impls(*pe.type, [](const auto&x)->const auto& { return x; }, [&](const auto& impl) { DEBUG("Found impl" << impl.m_params.fmt_args() << " " << impl.m_type); // TODO: Populate pp_impl + // TODO: Specialisation { auto fit = impl.m_methods.find(pe.item); if( fit != impl.m_methods.end() ) -- cgit v1.2.3 From 9da93885a0237162a4fd6f8d8e408e0aa56ccc2b Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 20:45:51 +0800 Subject: MIR - Debug printing for Statement (incomplete) --- src/mir/mir.cpp | 26 ++++++++++++++++++++++++++ src/mir/mir.hpp | 1 + 2 files changed, 27 insertions(+) diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index 6ae5808b..30d94a3d 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -444,6 +444,32 @@ namespace MIR { return os; } + ::std::ostream& operator<<(::std::ostream& os, const Statement& x) + { + TU_MATCHA( (x), (e), + (Assign, + os << e.dst << " = " << e.src; + ), + (Asm, + os << "("; + for(const auto& spec : e.outputs) + os << "\"" << spec.first << "\" : " << spec.second << ", "; + os << ") = asm!(\"\", input=( "; + for(const auto& spec : e.inputs) + os << "\"" << spec.first << "\" : " << spec.second << ", "; + os << "), clobbers=[" << e.clobbers << "], flags=[" << e.flags << "])"; + ), + (SetDropFlag, + ), + (Drop, + os << "drop(" << e.slot; + if(e.kind == ::MIR::eDropKind::SHALLOW) + os << " SHALLOW"; + os << ")"; + ) + ) + return os; + } } ::MIR::LValue MIR::LValue::clone() const diff --git a/src/mir/mir.hpp b/src/mir/mir.hpp index aed7e2fc..f31ede8d 100644 --- a/src/mir/mir.hpp +++ b/src/mir/mir.hpp @@ -271,6 +271,7 @@ TAGGED_UNION(Statement, Assign, unsigned int flag_idx; // Valid if != ~0u }) ); +extern ::std::ostream& operator<<(::std::ostream& os, const Statement& x); struct BasicBlock { -- cgit v1.2.3 From fe0a10f80ec70407f4710d33e4c1323954d97528 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 4 Mar 2017 20:46:17 +0800 Subject: MIR Optimise - Working (but unused) temporary value lifetime tracking --- src/mir/optimise.cpp | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 65896ded..fd9b8472 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include namespace { ::MIR::BasicBlockId get_new_target(const ::MIR::TypeResolve& state, ::MIR::BasicBlockId bb) @@ -1065,9 +1067,268 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f //::std::vector var_lifetimes; ::std::vector tmp_lifetimes( fcn.temporaries.size(), VarLifetime(fcn) ); + + // New algorithm notes: + // --- + // The lifetime of a value starts when it is written, and ends the last time it is read + // - When a variable is read, end any existing lifetime and start a new one. + // - When the value is read, update the end of its lifetime. + // --- + // A lifetime is a range in the call graph (with a start and end, including list of blocks) + // - Representation: Bitmap with a bit per statement. + // - Record the current block path in general state, along with known active lifetimes + { + // Scan through all possible paths in the graph (with loopback detection using a memory of the path) + // - If a loop is detected, determine if there were changes to the lifetime set during that pass + // > Changes are noticed by recording in the state structure when it triggers a change in the lifetime + // map. + struct Position + { + size_t path_index = 0; // index into the block path. + unsigned int stmt_idx = 0; + }; + struct ProtoLifetime + { + Position start; + Position end; + }; + struct State + { + ::std::vector block_path; + ::std::vector block_change_idx; + unsigned int cur_change_idx = 0; + + // if read, update. If set, save and update + ::std::vector tmp_ends; + //::std::vector var_ends; + + State clone() const { + return *this; + } + }; + + struct ValueLifetime + { + ::std::vector stmt_bitmap; + ValueLifetime(size_t stmt_count): + stmt_bitmap(stmt_count) + {} + }; + + size_t statement_count = 0; + ::std::vector block_offsets; + block_offsets.reserve( fcn.blocks.size() ); + for(const auto& bb : fcn.blocks) + { + block_offsets.push_back(statement_count); + statement_count += bb.statements.size() + 1; // +1 for the terminator + } + + ::std::vector temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) ); + + ::std::vector<::std::pair> todo_queue; + + State init_state; + init_state.tmp_ends.resize( fcn.temporaries.size(), ProtoLifetime() ); + todo_queue.push_back(::std::make_pair( 0, mv$(init_state) )); + + while(!todo_queue.empty()) + { + auto bb_idx = todo_queue.back().first; + auto val_state = mv$(todo_queue.back().second); + todo_queue.pop_back(); + state.set_cur_stmt(bb_idx, 0); + + // Check if this state has visited this block before, and if anything changed since last time + { + auto it = ::std::find(val_state.block_path.rbegin(), val_state.block_path.rend(), bb_idx); + if( it != val_state.block_path.rend() ) + { + auto idx = &*it - &val_state.block_path.front(); + if( val_state.block_change_idx[idx] == val_state.cur_change_idx ) + { + DEBUG(state << "Loop and no change"); + continue ; + } + } + val_state.block_path.push_back(bb_idx); + val_state.block_change_idx.push_back( val_state.cur_change_idx ); + } + + // Fill alive time in the bitmap + auto add_lifetime = [&](const ::MIR::LValue& lv, const Position& start, const Position& end) { + assert(start.path_index <= end.path_index); + assert(start.path_index < end.path_index || start.stmt_idx <= end.stmt_idx); + if(start.path_index == end.path_index && start.stmt_idx == end.stmt_idx) + return; + DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ")"); + ValueLifetime* lft; + if(const auto* e = lv.opt_Temporary()) + { + lft = &temporary_lifetimes[e->idx]; + } + else + { + MIR_TODO(state, "[add_lifetime] " << lv); + return; + } + + // Fill lifetime map for this temporary in the indicated range + bool did_set = false; + unsigned int j = start.stmt_idx; + unsigned int i = start.path_index; + while( i <= end.path_index ) + { + auto bb_idx = val_state.block_path.at(i); + const auto& bb = fcn.blocks[bb_idx]; + MIR_ASSERT(state, j <= bb.statements.size(), ""); + + auto block_base = block_offsets.at(bb_idx); + auto idx = block_base + j; + if( !lft->stmt_bitmap.at(idx) ) + { + lft->stmt_bitmap[idx] = true; + did_set = true; + } + + if( i == end.path_index && j == end.stmt_idx ) + break; + + // If the current index is the terminator (one after the size) + if(j == bb.statements.size()) + { + j = 0; + i++; + } + else + { + j ++; + } + } + + // - If the above set a new bit, increment `val_state.cur_change_idx` + if( did_set ) + { + val_state.cur_change_idx += 1; + } + }; + + Position cur_pos; + cur_pos.path_index = val_state.block_path.size() - 1; + cur_pos.stmt_idx = 0; + auto lvalue_read = [&](const ::MIR::LValue& lv) { + if(const auto* e = lv.opt_Temporary()) + { + // Update the last read location + val_state.tmp_ends.at(e->idx).end = cur_pos; + } + }; + auto lvalue_set = [&](const ::MIR::LValue& lv) { + if(const auto* e = lv.opt_Temporary()) + { + // End whatever value was originally there, and insert this new one + add_lifetime(lv, val_state.tmp_ends.at(e->idx).start, val_state.tmp_ends.at(e->idx).end); + val_state.tmp_ends.at(e->idx).start = cur_pos; + val_state.tmp_ends.at(e->idx).end = cur_pos; + } + }; + + // Run statements + for(const auto& stmt : fcn.blocks[bb_idx].statements) + { + auto stmt_idx = &stmt - &fcn.blocks[bb_idx].statements.front(); + cur_pos.stmt_idx = stmt_idx; + state.set_cur_stmt(bb_idx, stmt_idx); + DEBUG(state << " " << stmt); + + visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{ + if(vu == ValUsage::Read) + lvalue_read(lv); + if(vu == ValUsage::Write) + lvalue_set(lv); + return false; + }); + } + cur_pos.stmt_idx = fcn.blocks[bb_idx].statements.size(); + + state.set_cur_stmt_term(bb_idx); + DEBUG(state << "TERM " << fcn.blocks[bb_idx].terminator); + TU_MATCH(::MIR::Terminator, (fcn.blocks[bb_idx].terminator), (e), + (Incomplete, + // Should be impossible here. + ), + (Return, + // End all active lifetimes at their previous location. + for(unsigned i = 0; i < fcn.temporaries.size(); i++) + add_lifetime(::MIR::LValue::make_Temporary({i}), val_state.tmp_ends[i].start, val_state.tmp_ends[i].end ); + ), + (Diverge, + for(unsigned i = 0; i < fcn.temporaries.size(); i++) + add_lifetime(::MIR::LValue::make_Temporary({i}), val_state.tmp_ends[i].start, val_state.tmp_ends[i].end ); + ), + (Goto, + todo_queue.push_back(::std::make_pair( e, mv$(val_state) )); + ), + (Panic, + // What should be done here? + ), + (If, + lvalue_read(e.cond); + + // Push blocks + todo_queue.push_back(::std::make_pair( e.bb0, val_state.clone() )); + todo_queue.push_back(::std::make_pair( e.bb1, mv$(val_state) )); + ), + (Switch, + lvalue_read(e.val); + for(const auto& tgt : e.targets) + { + if( &tgt != &e.targets.back() ) + todo_queue.push_back(::std::make_pair( tgt, val_state.clone() )); + else + todo_queue.push_back(::std::make_pair( tgt, mv$(val_state) )); + } + ), + (Call, + if( const auto* f = e.fcn.opt_Value() ) + lvalue_read(*f); + for(const auto& arg : e.args) + if( const auto* e = arg.opt_LValue() ) + lvalue_read(*e); + + // Push blocks (with return valid only in one) + todo_queue.push_back(::std::make_pair( e.panic_block, val_state.clone() )); + + // TODO: If the function returns !, don't follow the ret_block + lvalue_set(e.ret_val); + todo_queue.push_back(::std::make_pair( e.ret_block, mv$(val_state) )); + ) + ) + } + + // Dump out variable lifetimes. + for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++) + { + const auto& lft = temporary_lifetimes[i]; + auto name = FMT("tmp$" << i); + while(name.size() < 3+1+3) + name += " "; + DEBUG(name << " : " << FMT_CB(os, + for(unsigned int j = 0; j < lft.stmt_bitmap.size(); j++) + { + if(j != 0 && ::std::find(block_offsets.begin(), block_offsets.end(), j) != block_offsets.end()) + os << "|"; + os << (lft.stmt_bitmap[j] ? "X" : " "); + } + )); + } + } + + // 1. Calculate lifetimes of all variables/temporaries that are eligable to be merged // - Lifetime is from first write to last read. Borrows lead to the value being assumed to live forever // - > BUT: Since this is lazy, it's taken as only being the lifetime of non-Copy items (as determined by the drop call or a move) + // TODO: Support extended lifetime inferrence that extends from first write to last read (before a write) { auto mark_borrowed = [&](const ::MIR::LValue& lv) { if( const auto* ve = lv.opt_Temporary() ) { -- cgit v1.2.3 From ed86bf6b3c6e0b0772d2677279d16c644a157ac6 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 5 Mar 2017 08:48:09 +0800 Subject: MIR Optimise - Use new value lifetime information in optimisation, fix unbounded runtime. --- src/mir/optimise.cpp | 406 +++++++++++++++++++-------------------------------- 1 file changed, 147 insertions(+), 259 deletions(-) diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index fd9b8472..464a40d7 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -426,6 +426,8 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path while( MIR_Optimise_PropagateSingleAssignments(state, fcn) ) change_happened = true; + change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); + // >> Unify duplicate temporaries // If two temporaries don't overlap in lifetime (blocks in which they're valid), unify the two change_happened |= MIR_Optimise_UnifyTemporaries(state, fcn); @@ -1032,40 +1034,42 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f return false; } - struct VarLifetime { - ::std::vector blocks; + class VarLifetime + { + ::std::vector statements; - VarLifetime(const ::MIR::Function& fcn): - blocks(fcn.blocks.size()) - { - } + public: + VarLifetime(::std::vector stmts): + statements( mv$(stmts) ) + {} - bool is_valid() const { - for(auto v : blocks) + // true if this value is used at any point + bool is_used() const { + for(auto v : statements) if( v ) return true; return false; } bool overlaps(const VarLifetime& x) const { - assert(blocks.size() == x.blocks.size()); - for(unsigned int i = 0; i < blocks.size(); i ++) + assert(statements.size() == x.statements.size()); + for(unsigned int i = 0; i < statements.size(); i ++) { - if( blocks[i] && x.blocks[i] ) + if( statements[i] && x.statements[i] ) return true; } return false; } void unify(const VarLifetime& x) { - assert(blocks.size() == x.blocks.size()); - for(unsigned int i = 0; i < blocks.size(); i ++) + assert(statements.size() == x.statements.size()); + for(unsigned int i = 0; i < statements.size(); i ++) { - if( x.blocks[i] ) - blocks[i] = true; + if( x.statements[i] ) + statements[i] = true; } } }; //::std::vector var_lifetimes; - ::std::vector tmp_lifetimes( fcn.temporaries.size(), VarLifetime(fcn) ); + ::std::vector tmp_lifetimes; // New algorithm notes: @@ -1077,6 +1081,8 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f // A lifetime is a range in the call graph (with a start and end, including list of blocks) // - Representation: Bitmap with a bit per statement. // - Record the current block path in general state, along with known active lifetimes + + // TODO: Move all this code to a helper to other passes can use it. { // Scan through all possible paths in the graph (with loopback detection using a memory of the path) // - If a loop is detected, determine if there were changes to the lifetime set during that pass @@ -1086,14 +1092,24 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f { size_t path_index = 0; // index into the block path. unsigned int stmt_idx = 0; + + bool operator==(const Position& x) const { + return path_index == x.path_index && stmt_idx == x.stmt_idx; + } }; struct ProtoLifetime { Position start; Position end; + + bool is_empty() const { + return start == end; + } }; + static unsigned NEXT_INDEX = 0; struct State { + unsigned int index = 0; ::std::vector block_path; ::std::vector block_change_idx; unsigned int cur_change_idx = 0; @@ -1103,9 +1119,12 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f //::std::vector var_ends; State clone() const { - return *this; + auto rv = *this; + rv.index = ++NEXT_INDEX; + return rv; } }; + NEXT_INDEX = 0; struct ValueLifetime { @@ -1126,10 +1145,24 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f ::std::vector temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) ); - ::std::vector<::std::pair> todo_queue; + struct BlockSeenLifetimes { + ::std::vector< ::std::vector > tmp; + + BlockSeenLifetimes(const ::MIR::Function& fcn): + tmp( fcn.temporaries.size() ) + {} + + bool has_state() const + { + return ::std::any_of(tmp.begin(), tmp.end(), [&](const auto& x){ return !x.empty(); }); + } + }; + ::std::vector block_seen_lifetimes( fcn.blocks.size(), BlockSeenLifetimes(fcn) ); State init_state; init_state.tmp_ends.resize( fcn.temporaries.size(), ProtoLifetime() ); + + ::std::vector<::std::pair> todo_queue; todo_queue.push_back(::std::make_pair( 0, mv$(init_state) )); while(!todo_queue.empty()) @@ -1139,29 +1172,14 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f todo_queue.pop_back(); state.set_cur_stmt(bb_idx, 0); - // Check if this state has visited this block before, and if anything changed since last time - { - auto it = ::std::find(val_state.block_path.rbegin(), val_state.block_path.rend(), bb_idx); - if( it != val_state.block_path.rend() ) - { - auto idx = &*it - &val_state.block_path.front(); - if( val_state.block_change_idx[idx] == val_state.cur_change_idx ) - { - DEBUG(state << "Loop and no change"); - continue ; - } - } - val_state.block_path.push_back(bb_idx); - val_state.block_change_idx.push_back( val_state.cur_change_idx ); - } - // Fill alive time in the bitmap + // TODO: Maybe also store the range (as a sequence of {block,start,end}) auto add_lifetime = [&](const ::MIR::LValue& lv, const Position& start, const Position& end) { assert(start.path_index <= end.path_index); assert(start.path_index < end.path_index || start.stmt_idx <= end.stmt_idx); if(start.path_index == end.path_index && start.stmt_idx == end.stmt_idx) return; - DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ")"); + //DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ")"); ValueLifetime* lft; if(const auto* e = lv.opt_Temporary()) { @@ -1209,10 +1227,67 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f // - If the above set a new bit, increment `val_state.cur_change_idx` if( did_set ) { + DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ") - New information"); val_state.cur_change_idx += 1; } }; + // Compare this state to a composite list of lifetimes seen in this block + // - Just compares the end of each proto lifetime + { + auto& bb_memory_ent = block_seen_lifetimes[bb_idx]; + bool has_new = false; + for(size_t i = 0; i < val_state.tmp_ends.size(); i++) + { + const auto& lft = val_state.tmp_ends[i]; + if(lft.is_empty()) continue ; + auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx; + + auto it = ::std::find(bb_memory_ent.tmp[i].begin(), bb_memory_ent.tmp[i].end(), end_idx); + if( it == bb_memory_ent.tmp[i].end() ) + { + has_new = true; + bb_memory_ent.tmp[i].push_back( end_idx ); + } + } + + if( !has_new && bb_memory_ent.has_state() ) + { + DEBUG(state << " " << val_state.index << " No new entry state"); + + // Apply all changes in this state, just in case there was new information + for(unsigned i = 0; i < fcn.temporaries.size(); i++) + add_lifetime( ::MIR::LValue::make_Temporary({i}), val_state.tmp_ends[i].start, val_state.tmp_ends[i].end ); + + continue ; + } + } + + // Check if this state has visited this block before, and if anything changed since last time + { + auto it = ::std::find(val_state.block_path.rbegin(), val_state.block_path.rend(), bb_idx); + if( it != val_state.block_path.rend() ) + { + auto idx = &*it - &val_state.block_path.front(); + if( val_state.block_change_idx[idx] == val_state.cur_change_idx ) + { + DEBUG(state << " " << val_state.index << " Loop and no change"); + continue ; + } + else + { + assert( val_state.block_change_idx[idx] < val_state.cur_change_idx ); + DEBUG(state << " " << val_state.index << " --- Loop, " << val_state.cur_change_idx - val_state.block_change_idx[idx] << " changes"); + } + } + else + { + DEBUG(state << " " << val_state.index << " ---"); + } + val_state.block_path.push_back(bb_idx); + val_state.block_change_idx.push_back( val_state.cur_change_idx ); + } + Position cur_pos; cur_pos.path_index = val_state.block_path.size() - 1; cur_pos.stmt_idx = 0; @@ -1241,13 +1316,26 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f state.set_cur_stmt(bb_idx, stmt_idx); DEBUG(state << " " << stmt); - visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{ - if(vu == ValUsage::Read) - lvalue_read(lv); - if(vu == ValUsage::Write) - lvalue_set(lv); - return false; - }); + if( const auto* e = stmt.opt_Drop() ) + { + visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{ + if(vu == ValUsage::Read) + lvalue_read(lv); + return false; + }); + lvalue_read(e->slot); + lvalue_set(e->slot); + } + else + { + visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{ + if(vu == ValUsage::Read) + lvalue_read(lv); + if(vu == ValUsage::Write) + lvalue_set(lv); + return false; + }); + } } cur_pos.stmt_idx = fcn.blocks[bb_idx].statements.size(); @@ -1281,12 +1369,15 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f ), (Switch, lvalue_read(e.val); + ::std::set tgts; for(const auto& tgt : e.targets) + tgts.insert(tgt); + + for(const auto& tgt : tgts) { - if( &tgt != &e.targets.back() ) - todo_queue.push_back(::std::make_pair( tgt, val_state.clone() )); - else - todo_queue.push_back(::std::make_pair( tgt, mv$(val_state) )); + //auto vs = (tgt == *tgts.rbegin() ? mv$(val_state) : val_state.clone()); + auto vs = val_state.clone(); + todo_queue.push_back(::std::make_pair( tgt, mv$(vs) )); } ), (Call, @@ -1307,6 +1398,7 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f } // Dump out variable lifetimes. +#if 1 for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++) { const auto& lft = temporary_lifetimes[i]; @@ -1322,217 +1414,12 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f } )); } - } - - - // 1. Calculate lifetimes of all variables/temporaries that are eligable to be merged - // - Lifetime is from first write to last read. Borrows lead to the value being assumed to live forever - // - > BUT: Since this is lazy, it's taken as only being the lifetime of non-Copy items (as determined by the drop call or a move) - // TODO: Support extended lifetime inferrence that extends from first write to last read (before a write) - { - auto mark_borrowed = [&](const ::MIR::LValue& lv) { - if( const auto* ve = lv.opt_Temporary() ) { - replacable[ve->idx] = false; - } - // TODO: Recurse! - }; - - struct State { - //::std::vector vars; - ::std::vector tmps; - - State() {} - State(const ::MIR::Function& fcn): - tmps(fcn.temporaries.size()) - { - } +#endif - bool merge(const State& other) { - if( tmps.size() == 0 ) - { - assert(other.tmps.size() != 0); - tmps = other.tmps; - return true; - } - else - { - assert(tmps.size() == other.tmps.size()); - bool rv = false; - for(unsigned int i = 0; i < tmps.size(); i ++) - { - if( tmps[i] != other.tmps[i] && other.tmps[i] ) { - tmps[i] = true; - rv = true; - } - } - return rv; - } - } - - void mark_validity(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv, bool val) { - if( const auto& ve = lv.opt_Temporary() ) { - tmps[ve->idx] = val; - } - else { - } - } - void move_val(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) { - ::HIR::TypeRef tmp; - if( mir_res.m_resolve.type_is_copy( mir_res.sp, mir_res.get_lvalue_type(tmp, lv) ) ) { - } - else { - mark_validity(mir_res, lv, false); - } - } - void move_val(const ::MIR::TypeResolve& mir_res, const ::MIR::Param& p) { - if(const auto* e = p.opt_LValue()) - { - move_val(mir_res, *e); - } - } - }; - ::std::vector block_states( fcn.blocks.size() ); - ::std::vector< ::std::pair > to_visit; - auto add_to_visit = [&to_visit](unsigned int bb, State state) { - to_visit.push_back( ::std::make_pair(bb, mv$(state)) ); - }; - to_visit.push_back( ::std::make_pair(0, State(fcn)) ); - while( !to_visit.empty() ) - { - auto bb_idx = to_visit.back().first; - auto val_state = mv$(to_visit.back().second); - to_visit.pop_back(); - - // 1. Merge with block state - if( ! block_states[bb_idx].merge(val_state) ) - continue ; - //DEBUG("BB" << bb_idx); - - // 2. Run block - const auto& bb = fcn.blocks[bb_idx]; - for(unsigned int stmt_idx = 0; stmt_idx < bb.statements.size(); stmt_idx ++) - { - const auto& stmt = bb.statements[stmt_idx]; - state.set_cur_stmt(bb_idx, stmt_idx); - - switch( stmt.tag() ) - { - case ::MIR::Statement::TAGDEAD: - throw ""; - case ::MIR::Statement::TAG_SetDropFlag: - break; - case ::MIR::Statement::TAG_Drop: - val_state.mark_validity( state, stmt.as_Drop().slot, false ); - break; - case ::MIR::Statement::TAG_Asm: - for(const auto& v : stmt.as_Asm().outputs) - val_state.mark_validity( state, v.second, true ); - break; - case ::MIR::Statement::TAG_Assign: - // Check source (and invalidate sources) - TU_MATCH( ::MIR::RValue, (stmt.as_Assign().src), (se), - (Use, - val_state.move_val(state, se); - ), - (Constant, - ), - (SizedArray, - val_state.move_val(state, se.val); - ), - (Borrow, - mark_borrowed(se.val); - ), - (Cast, - ), - (BinOp, - ), - (UniOp, - ), - (DstMeta, - ), - (DstPtr, - ), - (MakeDst, - val_state.move_val(state, se.meta_val); - ), - (Tuple, - for(const auto& v : se.vals) - val_state.move_val(state, v); - ), - (Array, - for(const auto& v : se.vals) - val_state.move_val(state, v); - ), - (Variant, - val_state.move_val(state, se.val); - ), - (Struct, - for(const auto& v : se.vals) - val_state.move_val(state, v); - ) - ) - // Mark destination as valid - val_state.mark_validity( state, stmt.as_Assign().dst, true ); - break; - } - block_states[bb_idx].merge(val_state); - } - - // 3. During terminator, merge again - state.set_cur_stmt_term(bb_idx); - //DEBUG("- " << bb.terminator); - TU_MATCH(::MIR::Terminator, (bb.terminator), (e), - (Incomplete, - // Should be impossible here. - ), - (Return, - block_states[bb_idx].merge(val_state); - ), - (Diverge, - ), - (Goto, - block_states[bb_idx].merge(val_state); - // Push block with the new state - add_to_visit( e, mv$(val_state) ); - ), - (Panic, - // What should be done here? - ), - (If, - // Push blocks - block_states[bb_idx].merge(val_state); - add_to_visit( e.bb0, val_state ); - add_to_visit( e.bb1, mv$(val_state) ); - ), - (Switch, - block_states[bb_idx].merge(val_state); - for(const auto& tgt : e.targets) - { - add_to_visit( tgt, val_state ); - } - ), - (Call, - for(const auto& arg : e.args) - val_state.move_val( state, arg ); - block_states[bb_idx].merge(val_state); - // Push blocks (with return valid only in one) - add_to_visit(e.panic_block, val_state); - - // TODO: If the function returns !, don't follow the ret_block - val_state.mark_validity( state, e.ret_val, true ); - add_to_visit(e.ret_block, mv$(val_state)); - ) - ) - } - - // Convert block states into temp states - for(unsigned int bb_idx = 0; bb_idx < block_states.size(); bb_idx ++) - { - for(unsigned int tmp_idx = 0; tmp_idx < block_states[bb_idx].tmps.size(); tmp_idx ++) - { - tmp_lifetimes[tmp_idx].blocks[bb_idx] = block_states[bb_idx].tmps[tmp_idx]; - } - } + // Move lifetime bitmaps into the variable for the below code + tmp_lifetimes.reserve( temporary_lifetimes.size() ); + for(auto& lft : temporary_lifetimes) + tmp_lifetimes.push_back( VarLifetime(mv$(lft.stmt_bitmap)) ); } // 2. Unify variables of the same type with distinct non-overlapping lifetimes @@ -1543,7 +1430,7 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f { if( ! replacable[tmpidx] ) continue ; if( visited[tmpidx] ) continue ; - if( ! tmp_lifetimes[tmpidx].is_valid() ) continue ; + if( ! tmp_lifetimes[tmpidx].is_used() ) continue ; visited[tmpidx] = true; for(unsigned int i = tmpidx+1; i < fcn.temporaries.size(); i ++) @@ -1552,11 +1439,12 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f continue ; if( fcn.temporaries[i] != fcn.temporaries[tmpidx] ) continue ; - if( ! tmp_lifetimes[i].is_valid() ) continue ; + if( ! tmp_lifetimes[i].is_used() ) + continue ; // Variables are of the same type, check if they overlap if( tmp_lifetimes[tmpidx].overlaps( tmp_lifetimes[i] ) ) continue ; - // They overlap, unify + // They don't overlap, unify tmp_lifetimes[tmpidx].unify( tmp_lifetimes[i] ); replacements[i] = tmpidx; replacement_needed = true; -- cgit v1.2.3 From 41050e3bbdcf0f3dcb73d86f78fdbad0f4984cb5 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 6 Mar 2017 09:57:51 +0800 Subject: MIR Optimise - Fix lifetime tracking --- src/mir/optimise.cpp | 121 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 464a40d7..637e78ec 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -434,7 +434,7 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path // >> Combine Duplicate Blocks change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); - #if 0 + #if 1 if( change_happened ) { //MIR_Dump_Fcn(::std::cout, fcn); @@ -1146,9 +1146,11 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f ::std::vector temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) ); struct BlockSeenLifetimes { + const ::std::vector& block_offsets; ::std::vector< ::std::vector > tmp; - BlockSeenLifetimes(const ::MIR::Function& fcn): + BlockSeenLifetimes(const ::std::vector& block_offsets, const ::MIR::Function& fcn): + block_offsets( block_offsets ), tmp( fcn.temporaries.size() ) {} @@ -1156,8 +1158,45 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f { return ::std::any_of(tmp.begin(), tmp.end(), [&](const auto& x){ return !x.empty(); }); } + + bool try_merge(const State& val_state) const + { + for(size_t i = 0; i < val_state.tmp_ends.size(); i++) + { + const auto& lft = val_state.tmp_ends[i]; + const auto& seen = this->tmp[i]; + if(lft.is_empty()) continue ; + auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx; + + auto it = ::std::find(seen.begin(), seen.end(), end_idx); + if( it == seen.end() ) + { + return true; + } + } + return false; + } + bool merge(const State& val_state) + { + bool rv = false; + for(size_t i = 0; i < val_state.tmp_ends.size(); i++) + { + const auto& lft = val_state.tmp_ends[i]; + auto& seen = this->tmp[i]; + if(lft.is_empty()) continue ; + auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx; + + auto it = ::std::find(seen.begin(), seen.end(), end_idx); + if( it == seen.end() ) + { + rv = true; + seen.push_back( end_idx ); + } + } + return rv; + } }; - ::std::vector block_seen_lifetimes( fcn.blocks.size(), BlockSeenLifetimes(fcn) ); + ::std::vector block_seen_lifetimes( fcn.blocks.size(), BlockSeenLifetimes(block_offsets, fcn) ); State init_state; init_state.tmp_ends.resize( fcn.temporaries.size(), ProtoLifetime() ); @@ -1174,7 +1213,7 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f // Fill alive time in the bitmap // TODO: Maybe also store the range (as a sequence of {block,start,end}) - auto add_lifetime = [&](const ::MIR::LValue& lv, const Position& start, const Position& end) { + auto add_lifetime_s = [&](State& val_state, const ::MIR::LValue& lv, const Position& start, const Position& end) { assert(start.path_index <= end.path_index); assert(start.path_index < end.path_index || start.stmt_idx <= end.stmt_idx); if(start.path_index == end.path_index && start.stmt_idx == end.stmt_idx) @@ -1200,6 +1239,7 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f auto bb_idx = val_state.block_path.at(i); const auto& bb = fcn.blocks[bb_idx]; MIR_ASSERT(state, j <= bb.statements.size(), ""); + MIR_ASSERT(state, bb_idx < block_offsets.size(), ""); auto block_base = block_offsets.at(bb_idx); auto idx = block_base + j; @@ -1231,33 +1271,47 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f val_state.cur_change_idx += 1; } }; + auto add_lifetime = [&](const ::MIR::LValue& lv, const Position& start, const Position& end) { + add_lifetime_s(val_state, lv, start, end); + }; + + auto apply_state = [&](State& state) { + // Apply all changes in this state, just in case there was new information + for(unsigned i = 0; i < fcn.temporaries.size(); i++) + add_lifetime_s( state, ::MIR::LValue::make_Temporary({i}), state.tmp_ends[i].start, state.tmp_ends[i].end ); + }; + auto add_to_visit = [&](unsigned int new_bb_idx, State new_state) { + auto& bb_memory_ent = block_seen_lifetimes[new_bb_idx]; + /*if( !bb_memory_ent.has_state() ) + { + // No recorded state, needs to be visited + DEBUG(state << " " << new_state.index << " -> bb" << new_bb_idx << " (no existing state)"); + } + else*/ if( bb_memory_ent.try_merge(new_state) ) + { + // This state has new information, needs to be visited + DEBUG(state << " " << new_state.index << " -> bb" << new_bb_idx << " (new info)"); + } + else + { + // Skip + DEBUG(state << " " << new_state.index << " No new state before push (to bb" << new_bb_idx << "), applying"); + apply_state(new_state); + return ; + } + todo_queue.push_back(::std::make_pair( new_bb_idx, mv$(new_state) )); + }; // Compare this state to a composite list of lifetimes seen in this block // - Just compares the end of each proto lifetime { auto& bb_memory_ent = block_seen_lifetimes[bb_idx]; - bool has_new = false; - for(size_t i = 0; i < val_state.tmp_ends.size(); i++) - { - const auto& lft = val_state.tmp_ends[i]; - if(lft.is_empty()) continue ; - auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx; - - auto it = ::std::find(bb_memory_ent.tmp[i].begin(), bb_memory_ent.tmp[i].end(), end_idx); - if( it == bb_memory_ent.tmp[i].end() ) - { - has_new = true; - bb_memory_ent.tmp[i].push_back( end_idx ); - } - } + bool has_new = bb_memory_ent.merge(val_state); if( !has_new && bb_memory_ent.has_state() ) { DEBUG(state << " " << val_state.index << " No new entry state"); - - // Apply all changes in this state, just in case there was new information - for(unsigned i = 0; i < fcn.temporaries.size(); i++) - add_lifetime( ::MIR::LValue::make_Temporary({i}), val_state.tmp_ends[i].start, val_state.tmp_ends[i].end ); + apply_state(val_state); continue ; } @@ -1302,9 +1356,9 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f if(const auto* e = lv.opt_Temporary()) { // End whatever value was originally there, and insert this new one + val_state.tmp_ends.at(e->idx).end = cur_pos; add_lifetime(lv, val_state.tmp_ends.at(e->idx).start, val_state.tmp_ends.at(e->idx).end); val_state.tmp_ends.at(e->idx).start = cur_pos; - val_state.tmp_ends.at(e->idx).end = cur_pos; } }; @@ -1347,15 +1401,13 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f ), (Return, // End all active lifetimes at their previous location. - for(unsigned i = 0; i < fcn.temporaries.size(); i++) - add_lifetime(::MIR::LValue::make_Temporary({i}), val_state.tmp_ends[i].start, val_state.tmp_ends[i].end ); + apply_state(val_state); ), (Diverge, - for(unsigned i = 0; i < fcn.temporaries.size(); i++) - add_lifetime(::MIR::LValue::make_Temporary({i}), val_state.tmp_ends[i].start, val_state.tmp_ends[i].end ); + apply_state(val_state); ), (Goto, - todo_queue.push_back(::std::make_pair( e, mv$(val_state) )); + add_to_visit(e, mv$(val_state)); ), (Panic, // What should be done here? @@ -1364,8 +1416,8 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f lvalue_read(e.cond); // Push blocks - todo_queue.push_back(::std::make_pair( e.bb0, val_state.clone() )); - todo_queue.push_back(::std::make_pair( e.bb1, mv$(val_state) )); + add_to_visit(e.bb0, val_state.clone()); + add_to_visit(e.bb1, mv$(val_state)); ), (Switch, lvalue_read(e.val); @@ -1375,9 +1427,8 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f for(const auto& tgt : tgts) { - //auto vs = (tgt == *tgts.rbegin() ? mv$(val_state) : val_state.clone()); - auto vs = val_state.clone(); - todo_queue.push_back(::std::make_pair( tgt, mv$(vs) )); + auto vs = (tgt == *tgts.rbegin() ? mv$(val_state) : val_state.clone()); + add_to_visit(tgt, mv$(vs)); } ), (Call, @@ -1388,11 +1439,11 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f lvalue_read(*e); // Push blocks (with return valid only in one) - todo_queue.push_back(::std::make_pair( e.panic_block, val_state.clone() )); + add_to_visit(e.panic_block, val_state.clone()); // TODO: If the function returns !, don't follow the ret_block lvalue_set(e.ret_val); - todo_queue.push_back(::std::make_pair( e.ret_block, mv$(val_state) )); + add_to_visit(e.ret_block, mv$(val_state)); ) ) } -- cgit v1.2.3 From 15b32eeb92904d3172eefed614d3a512fc341dbe Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 6 Mar 2017 10:01:29 +0800 Subject: MIR Optimise - Reduce debug and pedantic validation --- src/mir/optimise.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 637e78ec..5062fcf7 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -434,7 +434,7 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path // >> Combine Duplicate Blocks change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); - #if 1 + #if 0 if( change_happened ) { //MIR_Dump_Fcn(::std::cout, fcn); @@ -1449,7 +1449,7 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f } // Dump out variable lifetimes. -#if 1 +#if 0 for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++) { const auto& lft = temporary_lifetimes[i]; -- cgit v1.2.3 From efa36493b124d5819d2f9687de979af3dc637817 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 6 Mar 2017 10:44:04 +0800 Subject: Codegen C - Minor fixes --- src/trans/codegen_c.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 5c56e4d1..d147e5f8 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -2370,9 +2370,20 @@ namespace { auto fail_ordering = H::get_atomic_ordering(mir_res, name, 7+10+4); emit_atomic_cxchg(e, "memory_order_seq_cst", fail_ordering, true); } - else if( name == "atomic_cxchgweak" || name.compare(0, 7+9+1, "atomic_cxchgweak_") == 0 ) { - auto ordering = H::get_atomic_ordering(mir_res, name, 7+9+1); - emit_atomic_cxchg(e, ordering, ordering, true); + else if( name == "atomic_cxchgweak" ) { + emit_atomic_cxchg(e, "memory_order_seq_cst", "memory_order_seq_cst", true); + } + else if( name == "atomic_cxchgweak_acq" ) { + emit_atomic_cxchg(e, "memory_order_acquire", "memory_order_acquire", true); + } + else if( name == "atomic_cxchgweak_rel" ) { + emit_atomic_cxchg(e, "memory_order_release", "memory_order_relaxed", true); + } + else if( name == "atomic_cxchgweak_acqrel" ) { + emit_atomic_cxchg(e, "memory_order_acq_rel", "memory_order_acquire", true); + } + else if( name == "atomic_cxchgweak_relaxed" ) { + emit_atomic_cxchg(e, "memory_order_relaxed", "memory_order_relaxed", true); } else if( name == "atomic_xchg" || name.compare(0, 7+5, "atomic_xchg_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+5); @@ -2792,6 +2803,15 @@ namespace { m_of << "INT64_MIN"; else m_of << c.v; + switch(c.t) + { + case ::HIR::CoreType::I64: + case ::HIR::CoreType::I128: + case ::HIR::CoreType::Isize: + m_of << "ull"; + default: + break; + } ), (Uint, switch(c.t) @@ -2808,7 +2828,7 @@ namespace { case ::HIR::CoreType::U64: case ::HIR::CoreType::U128: case ::HIR::CoreType::Usize: - m_of << ::std::hex << "0x" << c.v << ::std::dec; + m_of << ::std::hex << "0x" << c.v << "ull" << ::std::dec; break; case ::HIR::CoreType::Char: assert(0 <= c.v && c.v <= 0x10FFFF); -- cgit v1.2.3 From 33ec90c927fef40bdfc86554285ddaf5ff772d15 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 10 Mar 2017 22:45:32 +0800 Subject: Codegen C - Fix edge case with INT64_MIN --- src/trans/codegen_c.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index d147e5f8..08cf3ac7 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -134,6 +134,7 @@ namespace { ::std::vector<::std::string> tmp; ::std::vector args; args.push_back( getenv("CC") ? getenv("CC") : "gcc" ); + args.push_back("-ffunction-sections"); args.push_back("-pthread"); switch(opt.opt_level) { @@ -2802,15 +2803,17 @@ namespace { if( c.v == INT64_MIN ) m_of << "INT64_MIN"; else - m_of << c.v; - switch(c.t) { - case ::HIR::CoreType::I64: - case ::HIR::CoreType::I128: - case ::HIR::CoreType::Isize: - m_of << "ull"; - default: - break; + m_of << c.v; + switch(c.t) + { + case ::HIR::CoreType::I64: + case ::HIR::CoreType::I128: + case ::HIR::CoreType::Isize: + m_of << "ull"; + default: + break; + } } ), (Uint, -- cgit v1.2.3 From 13e2409860c35c8693675d7c2c9b202741f7ddde Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 10 Mar 2017 22:46:59 +0800 Subject: MIR Optimise - basic size_of replacemnt --- src/mir/optimise.cpp | 31 ++++++------ src/trans/target.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/trans/target.hpp | 15 ++++++ 3 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 src/trans/target.cpp create mode 100644 src/trans/target.hpp diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 5062fcf7..173b5d7e 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace { ::MIR::BasicBlockId get_new_target(const ::MIR::TypeResolve& state, ::MIR::BasicBlockId bb) @@ -1738,23 +1739,25 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn) const auto& tef = te.fcn.as_Intrinsic(); if( tef.name == "size_of" ) { - //size_t size_val = 0; - //if( Target_GetSizeOf(tef.params.m_types.at(0), size_val) ) - //{ - // bb.statements.push_back(::MIR::Statement::make_Assign({ mv$(te.ret_val), ::MIR::Constant::make_Uint(size_val) })); - // bb.terminator = ::MIR::Terminator::make_Goto(te.ret_block); - // changed = true; - //} + size_t size_val = 0; + if( Target_GetSizeOf(state.sp, tef.params.m_types.at(0), size_val) ) + { + auto val = ::MIR::Constant::make_Uint({ size_val, ::HIR::CoreType::Usize }); + bb.statements.push_back(::MIR::Statement::make_Assign({ mv$(te.ret_val), mv$(val) })); + bb.terminator = ::MIR::Terminator::make_Goto(te.ret_block); + changed = true; + } } else if( tef.name == "align_of" ) { - //size_t size_val = 0; - //if( Target_GetAlignOf(tef.params.m_types.at(0), size_val) ) - //{ - // bb.statements.push_back(::MIR::Statement::make_Assign({ mv$(te.ret_val), ::MIR::Constant::make_Uint(size_val) })); - // bb.terminator = ::MIR::Terminator::make_Goto(te.ret_block); - // changed = true; - //} + size_t align_val = 0; + if( Target_GetAlignOf(state.sp, tef.params.m_types.at(0), align_val) ) + { + auto val = ::MIR::Constant::make_Uint({ align_val, ::HIR::CoreType::Usize }); + bb.statements.push_back(::MIR::Statement::make_Assign({ mv$(te.ret_val), mv$(val) })); + bb.terminator = ::MIR::Terminator::make_Goto(te.ret_block); + changed = true; + } } // NOTE: Quick special-case for bswap (a no-op) else if( tef.name == "bswap" && tef.params.m_types.at(0) == ::HIR::CoreType::U8 ) diff --git a/src/trans/target.cpp b/src/trans/target.cpp new file mode 100644 index 00000000..21555101 --- /dev/null +++ b/src/trans/target.cpp @@ -0,0 +1,134 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * trans/target.cpp + * - Target-specific information + */ +#include "target.hpp" + +// TODO: Replace with target selection +#define POINTER_SIZE_BYTES 8 + +bool Target_GetSizeAndAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_size, size_t& out_align) +{ + TU_MATCHA( (ty.m_data), (te), + (Infer, + BUG(sp, "sizeof on _ type"); + ), + (Diverge, + out_size = 0; + out_align = 0; + return true; + ), + (Primitive, + switch(te) + { + case ::HIR::CoreType::Bool: + case ::HIR::CoreType::U8: + case ::HIR::CoreType::I8: + out_size = 1; + out_align = 1; + return true; + case ::HIR::CoreType::U16: + case ::HIR::CoreType::I16: + out_size = 2; + out_align = 2; + return true; + case ::HIR::CoreType::U32: + case ::HIR::CoreType::I32: + case ::HIR::CoreType::Char: + out_size = 4; + out_align = 4; + return true; + case ::HIR::CoreType::U64: + case ::HIR::CoreType::I64: + out_size = 8; + out_align = 8; + return true; + case ::HIR::CoreType::U128: + case ::HIR::CoreType::I128: + out_size = 16; + // TODO: If i128 is emulated, this can be 8 + out_align = 16; + return true; + case ::HIR::CoreType::Usize: + case ::HIR::CoreType::Isize: + out_size = POINTER_SIZE_BYTES; + out_align = POINTER_SIZE_BYTES; + return true; + case ::HIR::CoreType::F32: + out_size = 4; + out_align = 4; + return true; + case ::HIR::CoreType::F64: + out_size = 8; + out_align = 8; + return true; + case ::HIR::CoreType::Str: + BUG(sp, "sizeof on a `str` - unsized"); + } + ), + (Path, + // TODO: + return false; + ), + (Generic, + // Unknown - return false + return false; + ), + (TraitObject, + BUG(sp, "sizeof on a trait object - unsized"); + ), + (ErasedType, + BUG(sp, "sizeof on an erased type - shouldn't exist"); + ), + (Array, + // TODO: + size_t size; + if( !Target_GetSizeAndAlignOf(sp, *te.inner, size,out_align) ) + return false; + size *= te.size_val; + ), + (Slice, + BUG(sp, "sizeof on a slice - unsized"); + ), + (Tuple, + out_size = 0; + out_align = 0; + + // TODO: Struct reordering + for(const auto& t : te) + { + size_t size, align; + if( !Target_GetSizeAndAlignOf(sp, t, size,align) ) + return false; + out_size += size; + out_align = ::std::max(out_align, align); + } + ), + (Borrow, + // TODO + ), + (Pointer, + // TODO + ), + (Function, + // Pointer size + ), + (Closure, + // TODO. + ) + ) + return false; +} +bool Target_GetSizeOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_size) +{ + size_t ignore_align; + return Target_GetSizeAndAlignOf(sp, ty, out_size, ignore_align); +} +bool Target_GetAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_align) +{ + size_t ignore_size; + return Target_GetSizeAndAlignOf(sp, ty, ignore_size, out_align); +} diff --git a/src/trans/target.hpp b/src/trans/target.hpp new file mode 100644 index 00000000..1c081b54 --- /dev/null +++ b/src/trans/target.hpp @@ -0,0 +1,15 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * trans/target.hpp + * - Target-specific information + */ +#pragma once + +#include +#include + +extern bool Target_GetSizeOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_size); +extern bool Target_GetAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_align); + -- cgit v1.2.3 From 993fc7a94a3aa2d8f59b59b0f0e30a282c5fa433 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 10 Mar 2017 22:47:25 +0800 Subject: MIR Helpers - get_const_type defer TODO --- src/mir/helpers.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mir/helpers.cpp b/src/mir/helpers.cpp index 9c2107ab..9dac1567 100644 --- a/src/mir/helpers.cpp +++ b/src/mir/helpers.cpp @@ -252,14 +252,17 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c auto v = m_resolve.get_value(this->sp, e.p, p, /*signature_only=*/true); if( const auto* ve = v.opt_Constant() ) { const auto& ty = (*ve)->m_type; - MIR_TODO(*this, "Monomorphise type " << ty); + if( monomorphise_type_needed(ty) ) + MIR_TODO(*this, "get_const_type - Monomorphise type " << ty); + else + return ty.clone(); } else { - MIR_BUG(*this, ""); + MIR_BUG(*this, "get_const_type - Not a constant"); } ), (ItemAddr, - MIR_TODO(*this, "Get type for constant `" << c << "`"); + MIR_TODO(*this, "get_const_type - Get type for constant `" << c << "`"); ) ) throw ""; -- cgit v1.2.3 From 499fc003610febe43c546db508b44ca6ec00beed Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 15:58:33 +0800 Subject: Makefile - Add target --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 5689863d..a0dfb46c 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,7 @@ OBJ += mir/check_full.o OBJ += hir/serialise.o hir/deserialise.o hir/serialise_lowlevel.o OBJ += trans/trans_list.o trans/mangling.o OBJ += trans/enumerate.o trans/monomorphise.o trans/codegen.o trans/codegen_c.o +OBJ += trans/target.o PCHS := ast/ast.hpp -- cgit v1.2.3 From e96ea8fdc617fa699ade1bd950a265be519ec731 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 16:48:58 +0800 Subject: MIR Gen Match - Fix bug in range handling --- src/mir/from_hir_match.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index e08cd5f7..8236221b 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -2365,8 +2365,7 @@ namespace //assert(it->first.start == ve_start); assert((it->first.end) < ve_end); - if( it->first.start != it->first.end ) - and_then(it->second); + and_then(it->second); ve_start = it->first.end + 1; ++ it; } -- cgit v1.2.3 From 8314328ea51fa217abc7dfec44acf5b04c85f58e Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 17:14:30 +0800 Subject: MIR Check full - Fiddling (still unbouned memory usage) --- src/mir/check_full.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index 1703dfd4..8dcd359c 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -12,7 +12,7 @@ #include #include -#define ENABLE_LEAK_DETECTOR 0 +#define ENABLE_LEAK_DETECTOR 1 namespace { @@ -108,6 +108,8 @@ namespace } }; + if( this->drop_flags != x.drop_flags ) + return false; if( ! H::equal(*this, return_value, x, x.return_value) ) return false; assert(vars.size() == x.vars.size()); @@ -182,6 +184,7 @@ namespace const auto& ty = mir_res.get_lvalue_type(tmp, lv); if( mir_res.m_resolve.type_is_copy(mir_res.sp, ty) ) { + // NOTE: Copy types aren't moved. } else { @@ -448,6 +451,7 @@ namespace } } this->known_state_sets.push_back( state_set.clone() ); + this->known_state_sets.back().bb_path = ::std::vector(); return true; } }; @@ -488,6 +492,12 @@ namespace std { os << "ValueStates(path=[" << x.bb_path << "]"; print_val(",rv", x.return_value); + for(unsigned int i = 0; i < x.arguments.size(); i ++) + print_val(FMT_CB(ss, ss << ",a" << i;), x.arguments[i]); + for(unsigned int i = 0; i < x.vars.size(); i ++) + print_val(FMT_CB(ss, ss << ",_" << i;), x.vars[i]); + for(unsigned int i = 0; i < x.temporaries.size(); i ++) + print_val(FMT_CB(ss, ss << ",t" << i;), x.temporaries[i]); os << ")"; return os; } -- cgit v1.2.3 From 0060a3b582b8c1e549b29e9abcb6148cb32a6427 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 17:43:40 +0800 Subject: MIR Gen - Support integer ranges in simple match generation --- src/mir/from_hir_match.cpp | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index 8236221b..b098f900 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -1554,7 +1554,23 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& builder.set_cur_block(succ_bb); ), (ValueRange, - TODO(sp, "Simple match over primitive - " << ty << " - ValueRange"); + auto succ_bb = builder.new_bb_unlinked(); + auto test_bb_2 = builder.new_bb_unlinked(); + + auto test_lt_val = ::MIR::Param(::MIR::Constant::make_Uint({ re.first.as_Uint().v, te })); + auto test_gt_val = ::MIR::Param(::MIR::Constant::make_Uint({ re.last.as_Uint().v, te })); + + // IF `val` < `first` : fail_bb + auto cmp_lt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::LT, mv$(test_lt_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lt_lval), fail_bb, test_bb_2 }) ); + + builder.set_cur_block(test_bb_2); + + // IF `val` > `last` : fail_bb + auto cmp_gt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::GT, mv$(test_gt_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_gt_lval), fail_bb, succ_bb }) ); + + builder.set_cur_block(succ_bb); ) ) break; @@ -1577,7 +1593,23 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& builder.set_cur_block(succ_bb); ), (ValueRange, - TODO(sp, "Simple match over primitive - " << ty << " - ValueRange"); + auto succ_bb = builder.new_bb_unlinked(); + auto test_bb_2 = builder.new_bb_unlinked(); + + auto test_lt_val = ::MIR::Param(::MIR::Constant::make_Int({ re.first.as_Int().v, te })); + auto test_gt_val = ::MIR::Param(::MIR::Constant::make_Int({ re.last.as_Int().v, te })); + + // IF `val` < `first` : fail_bb + auto cmp_lt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::LT, mv$(test_lt_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lt_lval), fail_bb, test_bb_2 }) ); + + builder.set_cur_block(test_bb_2); + + // IF `val` > `last` : fail_bb + auto cmp_gt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::GT, mv$(test_gt_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_gt_lval), fail_bb, succ_bb }) ); + + builder.set_cur_block(succ_bb); ) ) break; -- cgit v1.2.3 From 89b40c78c9e96a32043cb12380777a5ccd13a4a1 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 22:54:41 +0800 Subject: Parse - Fix incorrect handing of $crate --- src/parse/root.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parse/root.cpp b/src/parse/root.cpp index aefd5a13..970e2cfc 100644 --- a/src/parse/root.cpp +++ b/src/parse/root.cpp @@ -1336,7 +1336,6 @@ void Parse_Use(TokenStream& lex, ::std::function 0); + ASSERT_BUG(lex.getPosition(), path.nodes().size() > 0, "`use` with no path"); name = path.nodes().back().name(); } -- cgit v1.2.3 From 9540cf3642abaf41c8d4c89eaee8a0d72f2dcf1d Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 22:54:55 +0800 Subject: MIR Dump - Fix bad printing of byte string literals --- src/mir/dump.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mir/dump.cpp b/src/mir/dump.cpp index f3dee478..eb74dc9e 100644 --- a/src/mir/dump.cpp +++ b/src/mir/dump.cpp @@ -9,6 +9,7 @@ #include #include "mir.hpp" #include "operations.hpp" +#include namespace { @@ -208,7 +209,19 @@ namespace { os << (ce.v ? "true" : "false"); ), (Bytes, - os << "b\"" << ce << "\""; + os << ::std::hex << "b\""; + for(auto b : ce) + { + if( b == '\\' ) + os << "\\\\"; + else if( b == '"' ) + os << "\\\""; + else if( ' ' <= b && b < 0x7F ) + os << b; + else + os << "\\x" << ::std::setw(2) << ::std::setfill('0') << (int)b; + } + os << ::std::dec << "\""; ), (StaticString, os << "\"" << ce << "\""; -- cgit v1.2.3 From 7249b41f12e690cea8f3ae44223689934dbb1fd4 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 11 Mar 2017 23:46:07 +0800 Subject: Lex - Fix handling of escaped newlines --- src/parse/lex.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/parse/lex.cpp b/src/parse/lex.cpp index 4e5b4c2d..7b99c433 100644 --- a/src/parse/lex.cpp +++ b/src/parse/lex.cpp @@ -857,9 +857,13 @@ uint32_t Lexer::parseEscape(char enclosing) case '\n': while( ch.isspace() ) ch = this->getc(); - this->ungetc(); - if( ch == enclosing ) + if(ch == '\\' ) + return parseEscape(enclosing); + else if( ch == enclosing ) + { + this->ungetc(); return ~0; + } else return ch.v; default: -- cgit v1.2.3 From 0b5ed4f74da314b30df39de1ee5f88acd81353e7 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 12 Mar 2017 10:03:32 +0800 Subject: Codegen C - Fix integer literal types --- src/trans/codegen_c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 08cf3ac7..a1440422 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -2810,7 +2810,7 @@ namespace { case ::HIR::CoreType::I64: case ::HIR::CoreType::I128: case ::HIR::CoreType::Isize: - m_of << "ull"; + m_of << "ll"; default: break; } -- cgit v1.2.3 From 1a3d6fc97220979067a8b825a399f735af2f0e88 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 12 Mar 2017 10:07:39 +0800 Subject: Trans Enumerate - Avoid segfault on auto-trait only vtables --- src/trans/enumerate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/trans/enumerate.cpp b/src/trans/enumerate.cpp index e4560ffa..950d7910 100644 --- a/src/trans/enumerate.cpp +++ b/src/trans/enumerate.cpp @@ -515,6 +515,7 @@ namespace { // Ensure that the data trait's vtable is present const auto& trait = *te.m_trait.m_trait_ptr; + ASSERT_BUG(Span(), ! te.m_trait.m_path.m_path.m_components.empty(), "TODO: Data trait is empty, what can be done?"); auto vtable_ty_spath = te.m_trait.m_path.m_path; vtable_ty_spath.m_components.back() += "#vtable"; const auto& vtable_ref = m_crate.get_struct_by_path(sp, vtable_ty_spath); -- cgit v1.2.3 From 40fca6696bd7c201010e4533a2435ae32f59eecd Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 12 Mar 2017 10:15:14 +0800 Subject: Makefile - Curated list of tests (disabling known-failing ones) --- Makefile | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a0dfb46c..6c103d47 100644 --- a/Makefile +++ b/Makefile @@ -332,16 +332,169 @@ DISABLED_TESTS = run-pass/abi-sysv64-arg-passing run-pass/abi-sysv64-register-us DISABLED_TESTS += run-pass/backtrace-debuginfo-aux # - asm! is hard to trnaslate DISABLED_TESTS += run-pass/asm-in-out-operand run-pass/asm-indirect-memory run-pass/asm-out-assign +DISABLED_TESTS += run-pass/i128 +DISABLED_TESTS += run-pass/issue-14936 # - Requires jemalloc DISABLED_TESTS += run-pass/allocator-default run-pass/allocator-override # - Bug in inferrence order. DISABLED_TESTS += run-pass/associated-types-conditional-dispatch # - Lazy. DISABLED_TESTS += run-pass/associated-types-projection-in-where-clause run-pass/autoderef-privacy +DISABLED_TESTS += run-pass/builtin-superkinds-self-type +DISABLED_TESTS += run-pass/byte-literals +DISABLED_TESTS += run-pass/c-stack-as-value run-pass/cabi-int-widening +DISABLED_TESTS += run-pass/cast-rfc0401-vtable-kinds run-pass/cast-rfc0401 +DISABLED_TESTS += run-pass/cast-in-array-size +DISABLED_TESTS += run-pass/cast +DISABLED_TESTS += run-pass/cfg-in-crate-1 +DISABLED_TESTS += run-pass/coerce-expect-unsized +DISABLED_TESTS += run-pass/coerce-overloaded-autoderef +DISABLED_TESTS += run-pass/coerce-unify-return +DISABLED_TESTS += run-pass/concat +DISABLED_TESTS += run-pass/const-autoderef +DISABLED_TESTS += run-pass/const-block-cross-crate-fn +DISABLED_TESTS += run-pass/const-block-item +DISABLED_TESTS += run-pass/const-block +DISABLED_TESTS += run-pass/const-bound +DISABLED_TESTS += run-pass/const-cast +DISABLED_TESTS += run-pass/discrim-explicit-23030 +DISABLED_TESTS += run-pass/dst-coerce-rc +DISABLED_TESTS += run-pass/dst-coercions +DISABLED_TESTS += run-pass/dst-field-align +DISABLED_TESTS += run-pass/dst-irrefutable-bind +DISABLED_TESTS += run-pass/dst-raw +DISABLED_TESTS += run-pass/dst-struct-sole +DISABLED_TESTS += run-pass/dst-struct +DISABLED_TESTS += run-pass/dst-trait +DISABLED_TESTS += run-pass/empty-struct-braces +DISABLED_TESTS += run-pass/explicit-self-generic +DISABLED_TESTS += run-pass/extern-compare-with-return-type +DISABLED_TESTS += run-pass/fat-ptr-cast +DISABLED_TESTS += run-pass/intrinsic-move-val +DISABLED_TESTS += run-pass/issue-11205 +DISABLED_TESTS += run-pass/issue-13902 +DISABLED_TESTS += run-pass/issue-14399 +DISABLED_TESTS += run-pass/issue-15221 +DISABLED_TESTS += run-pass/issue-20575 +DISABLED_TESTS += run-pass/issue-20797 +# - Lazy (MIR) +DISABLED_TESTS += run-pass/if-ret +DISABLED_TESTS += run-pass/intrinsics-integer +DISABLED_TESTS += run-pass/issue-11940 +DISABLED_TESTS += run-pass/issue-13620 +DISABLED_TESTS += run-pass/issue-13867 +DISABLED_TESTS += run-pass/issue-15080 +DISABLED_TESTS += run-pass/issue-15104 +DISABLED_TESTS += run-pass/issue-15763 +DISABLED_TESTS += run-pass/issue-17877 # - SplitSlice + array +DISABLED_TESTS += run-pass/issue-18060 # - Overlapping value ranges +DISABLED_TESTS += run-pass/issue-18110 # - Missing value +DISABLED_TESTS += run-pass/issue-18352 # - Match+const +# - Lazy (misg) +DISABLED_TESTS += run-pass/issue-13494 +# - Overly-restrictive consteval +DISABLED_TESTS += run-pass/check-static-mut-slices run-pass/check-static-slice +DISABLED_TESTS += run-pass/const-binops +DISABLED_TESTS += run-pass/const-contents +DISABLED_TESTS += run-pass/const-deref +DISABLED_TESTS += run-pass/const-enum-cast +DISABLED_TESTS += run-pass/const-err +DISABLED_TESTS += run-pass/const-fields-and-indexing +DISABLED_TESTS += run-pass/const-fn-method +DISABLED_TESTS += run-pass/const-fn +DISABLED_TESTS += run-pass/const-str-ptr +DISABLED_TESTS += run-pass/const-vec-of-fns +DISABLED_TESTS += run-pass/diverging-fn-tail-35849 +DISABLED_TESTS += run-pass/enum-vec-initializer +DISABLED_TESTS += run-pass/huge-largest-array +DISABLED_TESTS += run-pass/issue-17233 +DISABLED_TESTS += run-pass/issue-19244 # Missing type info +# - Type defaults not supported +DISABLED_TESTS += run-pass/default-associated-types +DISABLED_TESTS += run-pass/default_ty_param_default_dependent_associated_type +DISABLED_TESTS += run-pass/default_ty_param_dependent_defaults +DISABLED_TESTS += run-pass/default_ty_param_method_call_test +DISABLED_TESTS += run-pass/default_ty_param_struct_and_type_alias +DISABLED_TESTS += run-pass/default_ty_param_struct +DISABLED_TESTS += run-pass/default_ty_param_trait_impl +DISABLED_TESTS += run-pass/default_ty_param_trait_impl_simple +DISABLED_TESTS += run-pass/default_ty_param_type_alias +DISABLED_TESTS += run-pass/generic-default-type-params-cross-crate +DISABLED_TESTS += run-pass/generic-default-type-params +# - ERROR: Function pointers in consants/statics don't trigger calls +DISABLED_TESTS += run-pass/issue-17718 +# - Quirks +DISABLED_TESTS += run-pass/fn-item-type-zero-sized # fn() items are not ZSTs +DISABLED_TESTS += run-pass/int-abs-overflow # No overflow checks +DISABLED_TESTS += run-pass/issue-18859 # module_path output is differend +# - BUG-USE AFTER FREE (scoping) +DISABLED_TESTS += run-pass/cleanup-rvalue-scopes +# - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying +DISABLED_TESTS += run-pass/deriving-copyclone +# - BUG-Expand: format_args! +DISABLED_TESTS += run-pass/fmt-pointer-trait +DISABLED_TESTS += run-pass/format-ref-cell +DISABLED_TESTS += run-pass/ifmt +# - BUG-Expand: #[main] and cfg +DISABLED_TESTS += run-pass/intrinsic-alignment +# - BUG-Expand: No cfg on enum vars +DISABLED_TESTS += run-pass/issue-11085 +# - BUG-Parse: `use *` +DISABLED_TESTS += run-pass/import-glob-crate +DISABLED_TESTS += run-pass/import-prefix-macro +# - BUG-CODEGEN: Missing symbol +DISABLED_TESTS += run-pass/const-enum-ptr +DISABLED_TESTS += run-pass/const-enum-vec-ptr +DISABLED_TESTS += run-pass/const-vecs-and-slices +# - BUG: Codegen drops +DISABLED_TESTS += run-pass/extern_fat_drop +# - BUG: Enum variants not getting correct integer values +DISABLED_TESTS += run-pass/discriminant_value +DISABLED_TESTS += run-pass/const-nullary-univariant-enum +DISABLED_TESTS += run-pass/enum-discr +DISABLED_TESTS += run-pass/enum-disr-val-pretty +DISABLED_TESTS += run-pass/enum-univariant-repr +DISABLED_TESTS += run-pass/issue-15523-big +DISABLED_TESTS += run-pass/issue-15523 +# - BUG: Null pointer opt not fully correct +DISABLED_TESTS += run-pass/enum-null-pointer-opt +# - BUG: Incorrect enum sizing +DISABLED_TESTS += run-pass/enum-discrim-autosizing +DISABLED_TESTS += run-pass/enum-discrim-manual-sizing +DISABLED_TESTS += run-pass/enum-discrim-width-stuff +# - BUG: Leaking contents of boxed trait objects +DISABLED_TESTS += run-pass/drop-struct-as-object +DISABLED_TESTS += run-pass/dynamic-drop +DISABLED_TESTS += run-pass/issue-10802 +# - BUG: Bad floats +DISABLED_TESTS += run-pass/float-nan +DISABLED_TESTS += run-pass/float_math +DISABLED_TESTS += run-pass/floatlits +DISABLED_TESTS += run-pass/intrinsics-math +# - BUG: Hygine +DISABLED_TESTS += run-pass/hygiene +DISABLED_TESTS += run-pass/hygienic-labels-in-let +DISABLED_TESTS += run-pass/hygienic-labels +# - ?? Is this valid +DISABLED_TESTS += run-pass/const-enum-vec-index # - Line information that isn't avaliable due to codegen DISABLED_TESTS += run-pass/backtrace-debuginfo run-pass/backtrace # - No unwind catching support DISABLED_TESTS += run-pass/binary-heap-panic-safe run-pass/box-of-array-of-drop-1 run-pass/box-of-array-of-drop-2 +DISABLED_TESTS += run-pass/cleanup-rvalue-temp-during-incomplete-alloc +DISABLED_TESTS += run-pass/drop-trait-enum +DISABLED_TESTS += run-pass/intrinsic-move-val-cleanups +DISABLED_TESTS += run-pass/issue-14875 +# - Test framework required +DISABLED_TESTS += run-pass/core-run-destroy +DISABLED_TESTS += run-pass/exec-env +DISABLED_TESTS += run-pass/issue-16597-empty +DISABLED_TESTS += run-pass/issue-16597 # NOTE: Crashes in resolve +DISABLED_TESTS += run-pass/issue-20823 +# - Makefile test framework quirks +DISABLED_TESTS += run-pass/issue-18913 +# - Target Features +DISABLED_TESTS += run-pass/crt-static-on-works # - Infinite loops DISABLED_TESTS += run-pass/issue-16671 @@ -358,15 +511,24 @@ output/rust/test_run-pass_hello: $(RUST_TESTS_DIR)run-pass/hello.rs output/libst @./$@ TEST_ARGS_run-pass/cfgs-on-items := --cfg fooA --cfg fooB +TEST_ARGS_run-pass/cfg-macros-foo := --cfg foo +TEST_ARGS_run-pass/cfg_attr := --cfg set1 --cfg set2 +TEST_ARGS_run-pass/issue-11085 := --cfg foo +TEST_ARGS_run-pass/issue-15881-model-lexer-dotdotdot := -g -output/rust/%: $(RUST_TESTS_DIR)%.rs $(RUSTCSRC) $(BIN) output/libstd.hir output/libtest.hir +output/rust/%: $(RUST_TESTS_DIR)%.rs $(RUSTCSRC) $(BIN) output/libstd.hir output/libtest.hir output/test_deps/librust_test_helpers.a @mkdir -p $(dir $@) @echo "=== TEST $(patsubst output/rust/%,%,$@)" @echo "--- [MRUSTC] -o $@" - $V$(BIN) $< -o $@ -L output/libs --stop-after $(RUST_TESTS_FINAL_STAGE) $(TEST_ARGS_$*) > $@.txt 2>&1 || (tail -n 1 $@.txt; false) + $V$(BIN) $< -o $@ -L output/libs -L output/test_deps --stop-after $(RUST_TESTS_FINAL_STAGE) $(TEST_ARGS_$*) > $@.txt 2>&1 || (tail -n 1 $@.txt; false) output/rust/%_out.txt: output/rust/% @echo "--- [$<]" - @./$< > $@ || (tail -n 1 $@; false) + @./$< > $@ || (tail -n 1 $@; mv $@ $@_fail; false) + +output/test_deps/librust_test_helpers.a: output/test_deps/rust_test_helpers.o + ar cur $@ $< +output/test_deps/rust_test_helpers.o: $(RUSTCSRC)src/rt/rust_test_helpers.c + $(CC) -c $< -o $@ output/rust/run-pass/allocator-default.o: output/libstd.hir output/liballoc_jemalloc.hir output/rust/run-pass/allocator-system.o: output/liballoc_system.hir @@ -420,6 +582,9 @@ $(BIN): $(OBJ) @mkdir -p $(dir $@) @echo [CXX] -o $@ $V$(CXX) -o $@ $(LINKFLAGS) $(OBJ) $(LIBS) + objcopy --only-keep-debug $(BIN) $(BIN).debug + objcopy --add-gnu-debuglink=$(BIN).debug $(BIN) + strip $(BIN) $(OBJDIR)%.o: src/%.cpp @mkdir -p $(dir $@) -- cgit v1.2.3 From 5a907d042ea463e6f86985aaa66a1f2a46e3406a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 12 Mar 2017 12:43:43 +0800 Subject: Parse - Hackily handle `struct Foo(pub ())` --- src/parse/root.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parse/root.cpp b/src/parse/root.cpp index 970e2cfc..d40e1f95 100644 --- a/src/parse/root.cpp +++ b/src/parse/root.cpp @@ -44,13 +44,13 @@ AST::MetaItem Parse_MetaItem(TokenStream& lex); void Parse_ModRoot(TokenStream& lex, AST::Module& mod, AST::MetaItems& mod_attrs); //::AST::Path Parse_Publicity(TokenStream& lex) -bool Parse_Publicity(TokenStream& lex) +bool Parse_Publicity(TokenStream& lex, bool allow_restricted=true) { Token tok; if( LOOK_AHEAD(lex) == TOK_RWORD_PUB ) { GET_TOK(tok, lex); - if( LOOK_AHEAD(lex) == TOK_PAREN_OPEN ) + if( allow_restricted && LOOK_AHEAD(lex) == TOK_PAREN_OPEN ) { auto path = AST::Path("", {}); // Restricted publicity. @@ -541,7 +541,7 @@ AST::Struct Parse_Struct(TokenStream& lex, const AST::MetaItems& meta_items) SET_ATTRS(lex, item_attrs); PUTBACK(tok, lex); - bool is_pub = Parse_Publicity(lex); + bool is_pub = Parse_Publicity(lex, /*allow_restricted=*/false); // HACK: Disable `pub(restricted)` syntax in tuple structs, due to ambiguity refs.push_back( AST::TupleItem( mv$(item_attrs), is_pub, Parse_Type(lex) ) ); if( GET_TOK(tok, lex) != TOK_COMMA ) -- cgit v1.2.3 From 7a30e6e65bda212ffc09807bfacb8db3b815bc5d Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 12 Mar 2017 12:44:12 +0800 Subject: Trans Codegen - Enable optimisation after monomorph --- src/trans/codegen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/trans/codegen.cpp b/src/trans/codegen.cpp index 6a581d93..9d536181 100644 --- a/src/trans/codegen.cpp +++ b/src/trans/codegen.cpp @@ -160,8 +160,7 @@ void Trans_Codegen(const ::std::string& outfile, const TransOptions& opt, const auto mir = Trans_Monomorphise(resolve, pp, fcn.m_code.m_mir); MIR_Validate(resolve, ::HIR::ItemPath(""), *mir, args, ret_type); MIR_Cleanup(resolve, ::HIR::ItemPath(""), *mir, args, ret_type); - // TODO: MIR Optimisation - //MIR_Optimise(resolve, ::HIR::ItemPath(""), *mir, args, ret_type); + MIR_Optimise(resolve, ::HIR::ItemPath(""), *mir, args, ret_type); MIR_Validate(resolve, ::HIR::ItemPath(""), *mir, args, ret_type); codegen->emit_function_code(path, fcn, ent.second->pp, mir); } -- cgit v1.2.3 From bf689be1b4c40c90c257595a0d884b4a4674a8f3 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 12 Mar 2017 12:57:43 +0800 Subject: MIR Gen - Fix Use-after-free when let borrows a temporary --- src/mir/from_hir.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 60ac0275..a9a40b58 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -381,6 +381,19 @@ namespace { auto& subnode = node.m_nodes[i]; const Span& sp = subnode->span(); + // Let statements don't generate a new temporary scope + if( dynamic_cast<::HIR::ExprNode_Let*>(subnode.get()) ) { + this->visit_node_ptr(subnode); + if( ! m_builder.block_active() ) { + m_builder.set_cur_block( m_builder.new_bb_unlinked() ); + diverged = true; + } + else { + m_builder.get_result(sp); + } + continue ; + } + auto stmt_scope = m_builder.new_scope_temp(sp); this->visit_node_ptr(subnode); -- cgit v1.2.3 From e2aa769e7604beba0606f3657309528845afe279 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 16 Mar 2017 10:54:28 +0800 Subject: MIR Gen - Fix incorrect scoping of temporaries --- src/mir/from_hir.cpp | 25 ++++++++- src/mir/mir_builder.cpp | 146 ++++++++++++++++++++++++++++-------------------- 2 files changed, 106 insertions(+), 65 deletions(-) diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index a9a40b58..0f200d31 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -374,6 +374,8 @@ namespace { { bool diverged = false; + auto res_val = (node.m_yields_final ? m_builder.new_temporary(node.m_res_type) : ::MIR::LValue()); + auto tmp_scope = m_builder.new_scope_temp(node.span()); auto scope = m_builder.new_scope_var(node.span()); for(unsigned int i = 0; i < node.m_nodes.size() - (node.m_yields_final ? 1 : 0); i ++) @@ -416,7 +418,6 @@ namespace { auto& subnode = node.m_nodes.back(); const Span& sp = subnode->span(); - auto res_val = m_builder.new_temporary(node.m_res_type); auto stmt_scope = m_builder.new_scope_temp(sp); this->visit_node_ptr(subnode); if( m_builder.has_result() || m_builder.block_active() ) @@ -428,13 +429,15 @@ namespace { m_builder.push_stmt_assign(sp, res_val.clone(), m_builder.get_result(sp)); m_builder.terminate_scope(sp, mv$(stmt_scope)); - m_builder.terminate_scope( node.span(), mv$(scope) ); + m_builder.terminate_scope(node.span(), mv$(scope) ); + m_builder.terminate_scope(node.span(), mv$(tmp_scope) ); m_builder.set_result( node.span(), mv$(res_val) ); } else { - m_builder.terminate_scope( node.span(), mv$(stmt_scope), false ); + m_builder.terminate_scope( sp, mv$(stmt_scope), false ); m_builder.terminate_scope( node.span(), mv$(scope), false ); + m_builder.terminate_scope( node.span(), mv$(tmp_scope), false ); // Block diverged in final node. } } @@ -443,12 +446,14 @@ namespace { if( diverged ) { m_builder.terminate_scope( node.span(), mv$(scope), false ); + m_builder.terminate_scope( node.span(), mv$(tmp_scope), false ); m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); // Don't set a result if there's no block. } else { m_builder.terminate_scope( node.span(), mv$(scope) ); + m_builder.terminate_scope( node.span(), mv$(tmp_scope) ); m_builder.set_result(node.span(), ::MIR::RValue::make_Tuple({})); } } @@ -593,6 +598,7 @@ namespace { { TRACE_FUNCTION_FR("_Match", "_Match"); this->visit_node_ptr(node.m_value); + auto stmt_scope = m_builder.new_scope_temp(node.span()); auto match_val = m_builder.get_result_in_lvalue(node.m_value->span(), node.m_value->m_res_type); if( node.m_arms.size() == 0 ) { @@ -635,6 +641,19 @@ namespace { else { MIR_LowerHIR_Match(m_builder, *this, node, mv$(match_val)); } + + if( m_builder.block_active() ) { + const auto& sp = node.span(); + + auto res = m_builder.get_result(sp); + m_builder.raise_variables(sp, res, stmt_scope); + m_builder.set_result(sp, mv$(res)); + + m_builder.terminate_scope( node.span(), mv$(stmt_scope) ); + } + else { + m_builder.terminate_scope( node.span(), mv$(stmt_scope), false ); + } } // ExprNode_Match void emit_if(/*const*/ ::HIR::ExprNodeP& cond, ::MIR::BasicBlockId true_branch, ::MIR::BasicBlockId false_branch) diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 1733df2f..987eb267 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -116,6 +116,22 @@ void MirBuilder::define_variable(unsigned int idx) top_scope = &m_scopes.at(idx); break ; } + else if( m_scopes.at(idx).data.is_Loop() ) + { + // Newly created temporary within a loop, if there is a saved + // state this temp needs a drop flag. + // TODO: ^ + } + else if( m_scopes.at(idx).data.is_Split() ) + { + // Newly created temporary within a split, if there is a saved + // state this temp needs a drop flag. + // TODO: ^ + } + else + { + // Nothign. + } } assert( top_scope ); auto& tmp_scope = top_scope->data.as_Temporaries(); @@ -396,22 +412,33 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const // HACK: Working around cases where values are dropped while the result is not yet used. (Deref, raise_variables(sp, *e.val, scope); + return ; ), (Field, raise_variables(sp, *e.val, scope); + return ; ), (Downcast, raise_variables(sp, *e.val, scope); + return ; ), // Actual value types (Variable, - auto idx = e; - auto scope_it = m_scope_stack.rbegin(); - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); + ), + (Temporary, + ) + ) + ASSERT_BUG(sp, val.is_Variable() || val.is_Temporary(), ""); - TU_IFLET( ScopeType, scope_def.data, Variables, e, + auto scope_it = m_scope_stack.rbegin(); + while( scope_it != m_scope_stack.rend() ) + { + auto& scope_def = m_scopes.at(*scope_it); + + TU_IFLET( ScopeType, scope_def.data, Variables, e, + if( const auto* ve = val.opt_Variable() ) + { + auto idx = *ve; auto tmp_it = ::std::find( e.vars.begin(), e.vars.end(), idx ); if( tmp_it != e.vars.end() ) { @@ -419,41 +446,12 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const DEBUG("Raise variable " << idx << " from " << *scope_it); break ; } - ) - // If the variable was defined above the desired scope (i.e. this didn't find it), return - if( *scope_it == scope.idx ) - return ; - ++scope_it; - } - if( scope_it == m_scope_stack.rend() ) - { - // Temporary wasn't defined in a visible scope? - return ; - } - ++scope_it; - - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); - - TU_IFLET( ScopeType, scope_def.data, Variables, e, - e.vars.push_back( idx ); - DEBUG("- to " << *scope_it); - return ; - ) - ++scope_it; - } - - DEBUG("- top"); - ), - (Temporary, - auto idx = e.idx; - auto scope_it = m_scope_stack.rbegin(); - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); - - TU_IFLET( ScopeType, scope_def.data, Temporaries, e, + } + ) + else TU_IFLET( ScopeType, scope_def.data, Temporaries, e, + if( const auto* ve = val.opt_Temporary() ) + { + auto idx = ve->idx; auto tmp_it = ::std::find( e.temporaries.begin(), e.temporaries.end(), idx ); if( tmp_it != e.temporaries.end() ) { @@ -461,35 +459,59 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const DEBUG("Raise temporary " << idx << " from " << *scope_it); break ; } - ) - - // If the temporary was defined above the desired scope (i.e. this didn't find it), return - if( *scope_it == scope.idx ) - return ; - ++scope_it; - } - if( scope_it == m_scope_stack.rend() ) + } + ) + else { - // Temporary wasn't defined in a visible scope? - return ; + // TODO: Does this need to handle this value being set in the + // split scopes? } + // If the variable was defined above the desired scope (i.e. this didn't find it), return + if( *scope_it == scope.idx ) + return ; ++scope_it; + } + if( scope_it == m_scope_stack.rend() ) + { + // Temporary wasn't defined in a visible scope? + BUG(sp, val << " wasn't defined in a visible scope"); + return ; + } + ++scope_it; - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); + while( scope_it != m_scope_stack.rend() ) + { + auto& scope_def = m_scopes.at(*scope_it); - TU_IFLET( ScopeType, scope_def.data, Temporaries, e, - e.temporaries.push_back( idx ); + TU_IFLET( ScopeType, scope_def.data, Variables, e, + if( const auto* ve = val.opt_Variable() ) + { + e.vars.push_back( *ve ); DEBUG("- to " << *scope_it); return ; - ) - ++scope_it; - } - - DEBUG("- top"); + } ) - ) + else TU_IFLET( ScopeType, scope_def.data, Temporaries, e, + if( const auto* ve = val.opt_Temporary() ) + { + e.temporaries.push_back( ve->idx ); + DEBUG("- to " << *scope_it); + return ; + } + ) + else if( scope_def.data.is_Loop() ) + { + //TODO(sp, "Raising " << val << " to outside of a loop"); + } + else if( scope_def.data.is_Split() ) + { + //TODO(sp, "Raising " << val << " to outside of a split"); + } + else + { + } + ++scope_it; + } } void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope) { -- cgit v1.2.3 From a3c1bd5942fc5a0e8dc8196bb7ae068429719d31 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 16 Mar 2017 11:53:20 +0800 Subject: MIR Validate - Better debug --- src/mir/check.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/mir/check.cpp b/src/mir/check.cpp index 638a540c..7c8fbd13 100644 --- a/src/mir/check.cpp +++ b/src/mir/check.cpp @@ -209,9 +209,9 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn return arguments.empty() && temporaries.empty() && variables.empty(); } - bool merge(ValStates& other) + bool merge(unsigned bb_idx, ValStates& other) { - DEBUG("this=" << FMT_CB(ss,this->fmt(ss);) << ", other=" << FMT_CB(ss,other.fmt(ss);)); + DEBUG("bb" << bb_idx << " this=" << FMT_CB(ss,this->fmt(ss);) << ", other=" << FMT_CB(ss,other.fmt(ss);)); if( this->empty() ) { *this = other; @@ -242,14 +242,17 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn ), (Argument, MIR_ASSERT(state, e.idx < this->arguments.size(), ""); + DEBUG("arg" << e.idx << " = " << (is_valid ? "Valid" : "Invalid")); this->arguments[e.idx] = is_valid ? State::Valid : State::Invalid; ), (Variable, MIR_ASSERT(state, e < this->variables.size(), ""); + DEBUG("var" << e << " = " << (is_valid ? "Valid" : "Invalid")); this->variables[e] = is_valid ? State::Valid : State::Invalid; ), (Temporary, MIR_ASSERT(state, e.idx < this->temporaries.size(), ""); + DEBUG("tmp" << e.idx << " = " << (is_valid ? "Valid" : "Invalid")); this->temporaries[e.idx] = is_valid ? State::Valid : State::Invalid; ) ) @@ -364,7 +367,7 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn // 1. Apply current state to `block_start_states` (merging if needed) // - If no change happened, skip. - if( ! block_start_states.at(block).merge( val_state ) ) { + if( ! block_start_states.at(block).merge(block, val_state) ) { continue ; } DEBUG("BB" << block << " via [" << path << "]"); @@ -376,6 +379,7 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn const auto& stmt = bb.statements[stmt_idx]; state.set_cur_stmt(block, stmt_idx); + DEBUG(state << stmt); switch( stmt.tag() ) { case ::MIR::Statement::TAGDEAD: @@ -458,6 +462,7 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn // 3. Pass new state on to destination blocks state.set_cur_stmt_term(block); + DEBUG(state << bb.terminator); TU_MATCH(::MIR::Terminator, (bb.terminator), (e), (Incomplete, // Should be impossible here. -- cgit v1.2.3 From 19c2c6a9d2f27cf9da02fa766439274d1a4b4f5a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 16 Mar 2017 11:53:50 +0800 Subject: MIR Optimise - Fix mis-optimise with assignment propagation --- src/mir/optimise.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 173b5d7e..2e823821 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -272,7 +272,7 @@ namespace { ), (UfcsKnown, TRACE_FUNCTION_F(path); - + // Obtain trait pointer (for default impl and to know what the item type is) const auto& trait_ref = state.m_resolve.m_crate.get_trait_by_path(state.sp, pe.trait.m_path); auto trait_vi_it = trait_ref.m_values.find(pe.item); @@ -319,7 +319,7 @@ namespace { } return false; }); - + if( bound_found ) { return nullptr; } @@ -333,6 +333,7 @@ namespace { params.self_ty = &*pe.type; params.fcn_params = &pe.params; + // Search for the method in the impl auto fit = impl.m_methods.find(pe.item); if( fit != impl.m_methods.end() ) { @@ -2146,6 +2147,12 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F srcp = &*srcp->as_Field().val; if( !( srcp->is_Temporary() || srcp->is_Variable() || srcp->is_Argument() ) ) continue ; + + if( replacements.find(*srcp) != replacements.end() ) + { + DEBUG("> Can't replace, source has pending replacement"); + continue; + } } // TODO: Allow any rvalue, but that currently breaks due to chaining //else if( e.src.is_Borrow() ) @@ -2172,6 +2179,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F for(unsigned int si2 = stmt_idx+1; si2 < block.statements.size(); si2 ++) { const auto& stmt2 = block.statements[si2]; + DEBUG("[find usage] " << stmt2); // Usage found. if( visit_mir_lvalues(stmt2, is_lvalue_usage) ) @@ -2195,7 +2203,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F // Determine if source is mutated. // > Assume that any mutating access of the root value counts (over-cautious) - if( visit_mir_lvalues(block.statements[si2], [&](const auto& lv, auto vu){ return /*vu == ValUsage::Write &&*/ is_lvalue_in_val(lv); }) ) + if( visit_mir_lvalues(stmt2, [&](const auto& lv, auto vu){ return /*vu == ValUsage::Write &&*/ is_lvalue_in_val(lv); }) ) { stop = true; break; @@ -2203,7 +2211,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F } if( !stop ) { - DEBUG(block.terminator); + DEBUG("[find usage] " << block.terminator); TU_MATCHA( (block.terminator), (e), (Incomplete, ), -- cgit v1.2.3 From 95ca7ba9049d378d602c0b44b7569c3276a013d5 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 16 Mar 2017 11:54:47 +0800 Subject: Main - Set target_family --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 28889159..c534ff15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -174,6 +174,7 @@ int main(int argc, char *argv[]) Cfg_SetFlag("unix"); Cfg_SetFlag("linux"); Cfg_SetValue("target_os", "linux"); + Cfg_SetValue("target_family", "unix"); Cfg_SetValue("target_pointer_width", "64"); Cfg_SetValue("target_endian", "little"); Cfg_SetValue("target_arch", "x86_64"); @@ -437,6 +438,9 @@ int main(int argc, char *argv[]) CompilePhaseV("MIR Validate PO", [&]() { MIR_CheckCrate(*hir_crate); }); + CompilePhaseV("MIR Validate Full", [&]() { + //MIR_CheckCrate_Full(*hir_crate); + }); if( params.last_stage == ProgramParams::STAGE_MIR ) { return 0; -- cgit v1.2.3 From d889bd6c089f76c13b74ca231bd008869f64b6d8 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 16 Mar 2017 12:11:18 +0800 Subject: Travis CI - Add cmake upgrade --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c9fd9118..cb6b93b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,11 @@ addons: apt: sources: - ubuntu-toolchain-r-test + - george-edison55-precise-backports packages: - g++-6 - zlib1g-dev + - cmake cmake-data install: # Build mrustc -- cgit v1.2.3 From b3a331ea432e19ffbbbd81c2dc64d5b1caaa2794 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 16 Mar 2017 13:31:41 +0800 Subject: Travis CI - (minor) comments --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb6b93b1..09285402 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,11 +17,10 @@ install: - make RUSTCSRC - CC=gcc-6 CXX=g++-6 make -# Tests! (check that they parse, and keep going) script: -# libstd +# libstd and hello_world - CC=gcc-6 make test TAIL_COUNT=2 -# rustc (not the actual binary, because that doesn't emit a file yet) - - CC=gcc-6 make output/librustc_driver.hir TAIL_COUNT=15 +# rustc + - CC=gcc-6 make output/rustc TAIL_COUNT=15 # Tests # - CC=gcc-6 make rust_tests-run-pass RUST_TESTS_FINAL_STAGE=expand -k -- cgit v1.2.3 From 6d29b4448479ac1f6299e9c4e73625e74cdb4581 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 13:36:19 +0800 Subject: AST - Fixed printing of interpolated fragments --- src/ast/expr.cpp | 22 ++++++++++++++++++++-- src/parse/token.cpp | 10 +++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/ast/expr.cpp b/src/ast/expr.cpp index 65048183..c35700e7 100644 --- a/src/ast/expr.cpp +++ b/src/ast/expr.cpp @@ -266,7 +266,16 @@ NODE(ExprNode_ByteString, { }) NODE(ExprNode_Closure, { - os << "/* todo: closure */"; + if( m_is_move ) + os << "move "; + os << "|"; + for(const auto& a : m_args) + { + os << a.first << ": " << a.second << ","; + } + os << "|"; + os << "->" << m_return; + os << " " << *m_code; },{ ExprNode_Closure::args_t args; for(const auto& a : m_args) { @@ -276,7 +285,16 @@ NODE(ExprNode_Closure, { }); NODE(ExprNode_StructLiteral, { - os << "/* todo: sl */"; + os << m_path << " { "; + for(const auto& v : m_values) + { + os << v.first << ": " << *v.second << ", "; + } + if(m_base_value) + { + os << ".." << *m_base_value; + } + os << "}"; },{ ExprNode_StructLiteral::t_values vals; diff --git a/src/parse/token.cpp b/src/parse/token.cpp index 69b952cc..84838207 100644 --- a/src/parse/token.cpp +++ b/src/parse/token.cpp @@ -290,16 +290,16 @@ struct EscapedString { case TOK_NEWLINE: return "\n"; case TOK_WHITESPACE: return " "; case TOK_COMMENT: return "/*" + m_data.as_String() + "*/"; - case TOK_INTERPOLATED_TYPE: return "/*:ty*/"; - case TOK_INTERPOLATED_PATH: return "/*:path*/"; - case TOK_INTERPOLATED_PATTERN: return "/*:pat*/"; + case TOK_INTERPOLATED_TYPE: return FMT( *reinterpret_cast(m_data.as_Fragment()) ); + case TOK_INTERPOLATED_PATH: return FMT( *reinterpret_cast(m_data.as_Fragment()) ); + case TOK_INTERPOLATED_PATTERN: return FMT( *reinterpret_cast(m_data.as_Fragment()) ); + case TOK_INTERPOLATED_STMT: + case TOK_INTERPOLATED_BLOCK: case TOK_INTERPOLATED_EXPR: { ::std::stringstream ss; reinterpret_cast(m_data.as_Fragment())->print(ss); return ss.str(); } - case TOK_INTERPOLATED_STMT: return "/*:stmt*/"; - case TOK_INTERPOLATED_BLOCK: return "/*:block*/"; case TOK_INTERPOLATED_META: return "/*:meta*/"; case TOK_INTERPOLATED_ITEM: return "/*:item*/"; case TOK_INTERPOLATED_IDENT: return "/*:ident*/"; -- cgit v1.2.3 From aa50d1f30eac5dd9264e99c9588b2ca3871a9de0 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 13:56:56 +0800 Subject: MIR Gen - Extend lifetimes of variables borrowed in let bindings --- src/mir/from_hir.cpp | 80 +++++++++++++++++++++++++++++++++++++------------ src/mir/from_hir.hpp | 5 ++++ src/mir/mir_builder.cpp | 4 ++- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 0f200d31..fe876686 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -21,6 +21,26 @@ namespace { + template + struct SaveAndEditVal { + T& m_dst; + T m_saved; + SaveAndEditVal(T& dst, T newval): + m_dst(dst), + m_saved(dst) + { + m_dst = mv$(newval); + } + ~SaveAndEditVal() + { + this->m_dst = this->m_saved; + } + }; + template + SaveAndEditVal save_and_edit(T& dst, typename ::std::remove_reference::type newval) { + return SaveAndEditVal { dst, mv$(newval) }; + } + class ExprVisitor_Conv: public MirConverter { @@ -36,6 +56,9 @@ namespace { }; ::std::vector m_loop_stack; + const ScopeHandle* m_block_tmp_scope = nullptr; + const ScopeHandle* m_borrow_raise_target = nullptr; + public: ExprVisitor_Conv(MirBuilder& builder, const ::std::vector< ::HIR::TypeRef>& var_types): m_builder(builder), @@ -153,11 +176,22 @@ namespace { m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(pat.m_binding.m_slot), mv$(lval) ); break; case ::HIR::PatternBinding::Type::Ref: + if(m_borrow_raise_target) + { + DEBUG("- Raising destructure borrow of " << lval << " to scope " << *m_borrow_raise_target); + m_builder.raise_variables(sp, lval, *m_borrow_raise_target); + } + m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(pat.m_binding.m_slot), ::MIR::RValue::make_Borrow({ 0, ::HIR::BorrowType::Shared, mv$(lval) }) ); break; case ::HIR::PatternBinding::Type::MutRef: + if(m_borrow_raise_target) + { + DEBUG("- Raising destructure borrow of " << lval << " to scope " << *m_borrow_raise_target); + m_builder.raise_variables(sp, lval, *m_borrow_raise_target); + } m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(pat.m_binding.m_slot), ::MIR::RValue::make_Borrow({ 0, ::HIR::BorrowType::Unique, mv$(lval) }) ); @@ -376,26 +410,15 @@ namespace { auto res_val = (node.m_yields_final ? m_builder.new_temporary(node.m_res_type) : ::MIR::LValue()); auto tmp_scope = m_builder.new_scope_temp(node.span()); + auto _block_tmp_scope = save_and_edit(m_block_tmp_scope, &tmp_scope); auto scope = m_builder.new_scope_var(node.span()); for(unsigned int i = 0; i < node.m_nodes.size() - (node.m_yields_final ? 1 : 0); i ++) { + auto _ = save_and_edit(m_borrow_raise_target, nullptr); auto& subnode = node.m_nodes[i]; const Span& sp = subnode->span(); - // Let statements don't generate a new temporary scope - if( dynamic_cast<::HIR::ExprNode_Let*>(subnode.get()) ) { - this->visit_node_ptr(subnode); - if( ! m_builder.block_active() ) { - m_builder.set_cur_block( m_builder.new_bb_unlinked() ); - diverged = true; - } - else { - m_builder.get_result(sp); - } - continue ; - } - auto stmt_scope = m_builder.new_scope_temp(sp); this->visit_node_ptr(subnode); @@ -495,23 +518,35 @@ namespace { } void visit(::HIR::ExprNode_Let& node) override { - TRACE_FUNCTION_F("_Let"); + TRACE_FUNCTION_F("_Let " << node.m_pattern); this->define_vars_from(node.span(), node.m_pattern); if( node.m_value ) { + auto _ = save_and_edit(m_borrow_raise_target, m_block_tmp_scope); this->visit_node_ptr(node.m_value); if( ! m_builder.block_active() ) { return ; } + auto res = m_builder.get_result(node.span()); + + // NOTE: `let foo = &bar();` is valid! + // - So is `let (ref foo, _) = (bar(), 1);` + // - Raising the variables if there's a borrow works for most + // cases, but not cases where the let causes an unsizing. + if( res.is_Borrow() ) + { + assert(m_block_tmp_scope); + m_builder.raise_variables(node.span(), res, *m_block_tmp_scope); + } if( node.m_pattern.m_binding.is_valid() && node.m_pattern.m_data.is_Any() && node.m_pattern.m_binding.m_type == ::HIR::PatternBinding::Type::Move ) { - m_builder.push_stmt_assign( node.span(), ::MIR::LValue::make_Variable(node.m_pattern.m_binding.m_slot), m_builder.get_result(node.span()) ); + m_builder.push_stmt_assign( node.span(), ::MIR::LValue::make_Variable(node.m_pattern.m_binding.m_slot), mv$(res) ); } else { - this->destructure_from(node.span(), node.m_pattern, m_builder.get_result_in_lvalue(node.m_value->span(), node.m_type)); + this->destructure_from(node.span(), node.m_pattern, m_builder.lvalue_or_temp(node.m_value->span(), node.m_type, mv$(res))); } } m_builder.set_result(node.span(), ::MIR::RValue::make_Tuple({})); @@ -597,6 +632,7 @@ namespace { void visit(::HIR::ExprNode_Match& node) override { TRACE_FUNCTION_FR("_Match", "_Match"); + auto _ = save_and_edit(m_borrow_raise_target, nullptr); this->visit_node_ptr(node.m_value); auto stmt_scope = m_builder.new_scope_temp(node.span()); auto match_val = m_builder.get_result_in_lvalue(node.m_value->span(), node.m_value->m_res_type); @@ -1119,9 +1155,13 @@ namespace { this->visit_node_ptr(node.m_value); auto val = m_builder.get_result_in_lvalue(node.m_value->span(), ty_val); - auto res = m_builder.new_temporary(node.m_res_type); - m_builder.push_stmt_assign( node.span(), res.as_Temporary(), ::MIR::RValue::make_Borrow({ 0, node.m_type, mv$(val) })); - m_builder.set_result( node.span(), mv$(res) ); + if( m_borrow_raise_target ) + { + DEBUG("- Raising borrow to scope " << *m_borrow_raise_target); + m_builder.raise_variables(node.span(), val, *m_borrow_raise_target); + } + + m_builder.set_result( node.span(), ::MIR::RValue::make_Borrow({ 0, node.m_type, mv$(val) }) ); } void visit(::HIR::ExprNode_Cast& node) override { @@ -1603,6 +1643,7 @@ namespace { void visit(::HIR::ExprNode_CallPath& node) override { TRACE_FUNCTION_F("_CallPath " << node.m_path); + auto _ = save_and_edit(m_borrow_raise_target, nullptr); auto values = get_args(node.m_args); auto panic_block = m_builder.new_bb_unlinked(); @@ -1670,6 +1711,7 @@ namespace { void visit(::HIR::ExprNode_CallValue& node) override { TRACE_FUNCTION_F("_CallValue " << node.m_value->m_res_type); + auto _ = save_and_edit(m_borrow_raise_target, nullptr); // _CallValue is ONLY valid on function pointers (all others must be desugared) ASSERT_BUG(node.span(), node.m_value->m_res_type.m_data.is_Function(), "Leftover _CallValue on a non-fn()"); diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp index c8d34151..9ee7b733 100644 --- a/src/mir/from_hir.hpp +++ b/src/mir/from_hir.hpp @@ -36,6 +36,11 @@ public: ScopeHandle& operator=(const ScopeHandle& x) = delete; ScopeHandle& operator=(ScopeHandle&& x) = delete; ~ScopeHandle(); + + friend ::std::ostream& operator<<(::std::ostream& os, const ScopeHandle& x) { + os << x.idx; + return os; + } }; // - Needs to handle future DerefMove (which can't use the Box hack) diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 987eb267..54dbc5e7 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -407,6 +407,8 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const TRACE_FUNCTION_F(val); TU_MATCH_DEF(::MIR::LValue, (val), (e), ( + // No raising of these source values + return ; ), // TODO: This may not be correct, because it can change the drop points and ordering // HACK: Working around cases where values are dropped while the result is not yet used. @@ -428,7 +430,7 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const (Temporary, ) ) - ASSERT_BUG(sp, val.is_Variable() || val.is_Temporary(), ""); + ASSERT_BUG(sp, val.is_Variable() || val.is_Temporary(), "Hit value raising code with non-variable value - " << val); auto scope_it = m_scope_stack.rbegin(); while( scope_it != m_scope_stack.rend() ) -- cgit v1.2.3 From 3aaee59ee3ceab85e88e046336b2fcc72817e89f Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 15:29:32 +0800 Subject: MIR Gen - Fix scoping of values in let bindings --- src/mir/from_hir.cpp | 20 +++++------- src/mir/from_hir.hpp | 6 ++-- src/mir/mir_builder.cpp | 82 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index fe876686..a15ea8ef 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -530,16 +530,6 @@ namespace { } auto res = m_builder.get_result(node.span()); - // NOTE: `let foo = &bar();` is valid! - // - So is `let (ref foo, _) = (bar(), 1);` - // - Raising the variables if there's a borrow works for most - // cases, but not cases where the let causes an unsizing. - if( res.is_Borrow() ) - { - assert(m_block_tmp_scope); - m_builder.raise_variables(node.span(), res, *m_block_tmp_scope); - } - if( node.m_pattern.m_binding.is_valid() && node.m_pattern.m_data.is_Any() && node.m_pattern.m_binding.m_type == ::HIR::PatternBinding::Type::Move ) { m_builder.push_stmt_assign( node.span(), ::MIR::LValue::make_Variable(node.m_pattern.m_binding.m_slot), mv$(res) ); @@ -663,7 +653,7 @@ namespace { if( m_builder.block_active() ) { auto res = m_builder.get_result(arm.m_code->span()); - m_builder.raise_variables( arm.m_code->span(), res, scope ); + m_builder.raise_variables( arm.m_code->span(), res, scope, /*to_above=*/true); m_builder.set_result(arm.m_code->span(), mv$(res)); m_builder.terminate_scope( node.span(), mv$(tmp_scope) ); @@ -682,7 +672,7 @@ namespace { const auto& sp = node.span(); auto res = m_builder.get_result(sp); - m_builder.raise_variables(sp, res, stmt_scope); + m_builder.raise_variables(sp, res, stmt_scope, /*to_above=*/true); m_builder.set_result(sp, mv$(res)); m_builder.terminate_scope( node.span(), mv$(stmt_scope) ); @@ -1499,6 +1489,7 @@ namespace { // TODO: Proper panic handling, including scope destruction m_builder.set_cur_block(place__panic); + //m_builder.terminate_scope_early( node.span(), m_builder.fcn_scope() ); // TODO: Drop `place` m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); m_builder.set_cur_block(place__ok); @@ -1523,6 +1514,7 @@ namespace { // TODO: Proper panic handling, including scope destruction m_builder.set_cur_block(place_raw__panic); + //m_builder.terminate_scope_early( node.span(), m_builder.fcn_scope() ); // TODO: Drop `place` m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); m_builder.set_cur_block(place_raw__ok); @@ -1559,11 +1551,13 @@ namespace { // TODO: Proper panic handling, including scope destruction m_builder.set_cur_block(res__panic); + //m_builder.terminate_scope_early( node.span(), m_builder.fcn_scope() ); // TODO: Should this drop the value written to the rawptr? // - No, becuase it's likely invalid now. Goodbye! m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); m_builder.set_cur_block(res__ok); + m_builder.mark_value_assigned(node.span(), res); m_builder.set_result( node.span(), mv$(res) ); } @@ -1735,6 +1729,8 @@ namespace { m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); m_builder.set_cur_block( next_block ); + // TODO: Support diverging value calls + m_builder.mark_value_assigned(node.span(), res); m_builder.set_result( node.span(), mv$(res) ); } void visit(::HIR::ExprNode_CallMethod& node) override diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp index 9ee7b733..2a5da9fe 100644 --- a/src/mir/from_hir.hpp +++ b/src/mir/from_hir.hpp @@ -195,9 +195,9 @@ public: // Mark a value as initialised (used for Call, because it has to be done after the panic block is populated) void mark_value_assigned(const Span& sp, const ::MIR::LValue& val); - // Moves control of temporaries up to the next scope - void raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope); - void raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope); + // Moves control of temporaries up to the specified scope (or to above it) + void raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above=false); + void raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above=false); void set_cur_block(unsigned int new_block); ::MIR::BasicBlockId pause_cur_block(); diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 54dbc5e7..e8392ef2 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -402,26 +402,31 @@ void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) } } -void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope) +void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above/*=false*/) { TRACE_FUNCTION_F(val); TU_MATCH_DEF(::MIR::LValue, (val), (e), ( - // No raising of these source values + // No raising of these source values? return ; ), // TODO: This may not be correct, because it can change the drop points and ordering // HACK: Working around cases where values are dropped while the result is not yet used. + (Index, + raise_variables(sp, *e.val, scope, to_above); + raise_variables(sp, *e.idx, scope, to_above); + return ; + ), (Deref, - raise_variables(sp, *e.val, scope); + raise_variables(sp, *e.val, scope, to_above); return ; ), (Field, - raise_variables(sp, *e.val, scope); + raise_variables(sp, *e.val, scope, to_above); return ; ), (Downcast, - raise_variables(sp, *e.val, scope); + raise_variables(sp, *e.val, scope, to_above); return ; ), // Actual value types @@ -437,6 +442,11 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const { auto& scope_def = m_scopes.at(*scope_it); + if( *scope_it == scope.idx && !to_above ) + { + DEBUG(val << " defined in or above target (scope " << scope << ")"); + } + TU_IFLET( ScopeType, scope_def.data, Variables, e, if( const auto* ve = val.opt_Variable() ) { @@ -470,7 +480,10 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const } // If the variable was defined above the desired scope (i.e. this didn't find it), return if( *scope_it == scope.idx ) + { + DEBUG("Value " << val << " is defined above the target (scope " << scope << ")"); return ; + } ++scope_it; } if( scope_it == m_scope_stack.rend() ) @@ -479,7 +492,37 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const BUG(sp, val << " wasn't defined in a visible scope"); return ; } - ++scope_it; + + if( *scope_it == scope.idx ) + { + // Already hit the specified scope + if( to_above ) { + // Want to shift to any above (but not including) it + ++ scope_it; + } + else { + // Want to shift to it or above. + } + } + else + { + ++scope_it; + + while( scope_it != m_scope_stack.rend() ) + { + if( *scope_it == scope.idx ) + { + break ; + } + ++ scope_it; + } + } + if( scope_it == m_scope_stack.rend() ) + { + // Temporary wasn't defined in a visible scope? + BUG(sp, "Scope " << scope << " isn't on the stack"); + return ; + } while( scope_it != m_scope_stack.rend() ) { @@ -514,16 +557,17 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const } ++scope_it; } + BUG(sp, "Couldn't find a scope to raise " << val << " into"); } -void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope) +void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above/*=false*/) { auto raise_vars = [&](const ::MIR::Param& p) { if( const auto* e = p.opt_LValue() ) - this->raise_variables(sp, *e, scope); + this->raise_variables(sp, *e, scope, to_above); }; TU_MATCHA( (rval), (e), (Use, - this->raise_variables(sp, e, scope); + this->raise_variables(sp, e, scope, to_above); ), (Constant, ), @@ -532,23 +576,23 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, cons ), (Borrow, // TODO: Wait, is this valid? - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (Cast, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (BinOp, raise_vars(e.val_l); raise_vars(e.val_r); ), (UniOp, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (DstMeta, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (DstPtr, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (MakeDst, raise_vars(e.ptr_val); @@ -1130,10 +1174,10 @@ void MirBuilder::complete_scope(ScopeDef& sd) TU_MATCHA( (sd.data), (e), (Temporaries, - DEBUG("Temporaries " << e.temporaries); + DEBUG("Temporaries - " << e.temporaries); ), (Variables, - DEBUG("Variables " << e.vars); + DEBUG("Variables - " << e.vars); ), (Loop, DEBUG("Loop"); @@ -1574,6 +1618,7 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) for(auto tmp_idx : ::reverse(e.temporaries)) { const auto& vs = get_temp_state(sd.span, tmp_idx); + DEBUG("tmp" << tmp_idx << " - " << vs); drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Temporary({ tmp_idx }) ); } ), @@ -1581,6 +1626,7 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) for(auto var_idx : ::reverse(e.vars)) { const auto& vs = get_variable_state(sd.span, var_idx); + DEBUG("var" << var_idx << " - " << vs); drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Variable(var_idx) ); } ), @@ -1599,11 +1645,13 @@ void MirBuilder::moved_lvalue(const Span& sp, const ::MIR::LValue& lv) TU_MATCHA( (lv), (e), (Variable, if( !lvalue_is_copy(sp, lv) ) { + DEBUG("var" << e << " = MOVED"); get_variable_state_mut(sp, e) = VarState::make_Invalid(InvalidType::Moved); } ), (Temporary, if( !lvalue_is_copy(sp, lv) ) { + DEBUG("tmp" << e.idx << " = MOVED"); get_temp_state_mut(sp, e.idx) = VarState::make_Invalid(InvalidType::Moved); } ), @@ -1665,9 +1713,11 @@ void MirBuilder::moved_lvalue(const Span& sp, const ::MIR::LValue& lv) BUG(sp, "Box move out of invalid LValue " << inner_lv << " - should have been moved"); ), (Variable, + DEBUG("var" << ei << " = PARTIAL"); get_variable_state_mut(sp, ei) = VarState::make_Partial({ mv$(ivs) }); ), (Temporary, + DEBUG("tmp" << ei.idx << " = PARTIAL"); get_temp_state_mut(sp, ei.idx) = VarState::make_Partial({ mv$(ivs) }); ), (Argument, -- cgit v1.2.3 From 91bf537331813d7cddd9dd474d7d3bb2984c9d4a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 19:01:01 +0800 Subject: HIR Annotate - (minor) Better checking --- src/hir_expand/annotate_value_usage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hir_expand/annotate_value_usage.cpp b/src/hir_expand/annotate_value_usage.cpp index da4c897f..f21c68ab 100644 --- a/src/hir_expand/annotate_value_usage.cpp +++ b/src/hir_expand/annotate_value_usage.cpp @@ -463,6 +463,7 @@ namespace { return get_usage_for_pattern(sp, *pe.sub, *ty.m_data.as_Borrow().inner); ), (Tuple, + ASSERT_BUG(sp, ty.m_data.is_Tuple(), "Tuple pattern with non-tuple type - " << ty); const auto& subtys = ty.m_data.as_Tuple(); assert(pe.sub_patterns.size() == subtys.size()); auto rv = ::HIR::ValueUsage::Borrow; @@ -471,6 +472,7 @@ namespace { return rv; ), (SplitTuple, + ASSERT_BUG(sp, ty.m_data.is_Tuple(), "SplitTuple pattern with non-tuple type - " << ty); const auto& subtys = ty.m_data.as_Tuple(); assert(pe.leading.size() + pe.trailing.size() < subtys.size()); auto rv = ::HIR::ValueUsage::Borrow; @@ -486,6 +488,7 @@ namespace { (StructTuple, // TODO: Avoid monomorphising all the time. const auto& str = *pe.binding; + ASSERT_BUG(sp, str.m_data.is_Tuple(), "StructTuple pattern with non-tuple struct - " << str.m_data.tag_str()); const auto& flds = str.m_data.as_Tuple(); assert(pe.sub_patterns.size() == flds.size()); auto monomorph_cb = monomorphise_type_get_cb(sp, nullptr, &pe.path.m_params, nullptr); -- cgit v1.2.3 From 3332a8379b7f20173ba5d63fe5ee40e47d57902a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 19:01:27 +0800 Subject: HIR Typecheck - Fix incorrect error message --- src/hir_typeck/expr_cs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index 3d4826ff..4a8f381a 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -2991,11 +2991,11 @@ namespace { void visit(::HIR::ExprNode_Literal& node) override { TU_MATCH(::HIR::ExprNode_Literal::Data, (node.m_data), (e), (Integer, - ASSERT_BUG(node.span(), node.m_res_type.m_data.is_Primitive(), "Float Literal didn't return primitive"); + ASSERT_BUG(node.span(), node.m_res_type.m_data.is_Primitive(), "Integer _Literal didn't return primitive - " << node.m_res_type); e.m_type = node.m_res_type.m_data.as_Primitive(); ), (Float, - ASSERT_BUG(node.span(), node.m_res_type.m_data.is_Primitive(), "Float Literal didn't return primitive"); + ASSERT_BUG(node.span(), node.m_res_type.m_data.is_Primitive(), "Float Literal didn't return primitive - " << node.m_res_type); e.m_type = node.m_res_type.m_data.as_Primitive(); ), (Boolean, -- cgit v1.2.3 From fd6e4e7ff613804d7fee5ce0a59cfa55a5ca6176 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 19:01:40 +0800 Subject: Expand - add column! macro --- src/expand/file_line.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/expand/file_line.cpp b/src/expand/file_line.cpp index a485fe61..8dfb7e6d 100644 --- a/src/expand/file_line.cpp +++ b/src/expand/file_line.cpp @@ -27,6 +27,15 @@ class CExpanderLine: } }; +class CExpanderColumn: + public ExpandProcMacro +{ + ::std::unique_ptr expand(const Span& sp, const AST::Crate& crate, const ::std::string& ident, const TokenTree& tt, AST::Module& mod) override + { + return box$( TTStreamO(TokenTree(Token((uint64_t)sp.start_ofs, CORETYPE_U32))) ); + } +}; + class CExpanderModulePath: public ExpandProcMacro { @@ -44,5 +53,6 @@ class CExpanderModulePath: STATIC_MACRO("file", CExpanderFile); STATIC_MACRO("line", CExpanderLine); +STATIC_MACRO("column", CExpanderColumn); STATIC_MACRO("module_path", CExpanderModulePath); -- cgit v1.2.3 From 6817b6ab8d8762316e2b4bf541d405447c07f8f3 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 19:08:43 +0800 Subject: format_args! - Handle }} --- src/expand/format_args.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/expand/format_args.cpp b/src/expand/format_args.cpp index 3d0eb9af..775326cd 100644 --- a/src/expand/format_args.cpp +++ b/src/expand/format_args.cpp @@ -139,6 +139,11 @@ namespace { if( *s != '{' ) { if( *s == '}' ) { + s ++; + if( *s != '}' ) { + // TODO: Error? Warning? + s --; // Step backwards, just in case + } // Doesn't need escaping cur_literal += '}'; } -- cgit v1.2.3 From 55f1d621f1b2615294a8d50bf74a5aec62df21b4 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 18 Mar 2017 20:34:02 +0800 Subject: Makefile - More triaged tests --- Makefile | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 6c103d47..624a8a9b 100644 --- a/Makefile +++ b/Makefile @@ -334,6 +334,7 @@ DISABLED_TESTS += run-pass/backtrace-debuginfo-aux DISABLED_TESTS += run-pass/asm-in-out-operand run-pass/asm-indirect-memory run-pass/asm-out-assign DISABLED_TESTS += run-pass/i128 DISABLED_TESTS += run-pass/issue-14936 +DISABLED_TESTS += run-pass/issue-32947 # - Requires jemalloc DISABLED_TESTS += run-pass/allocator-default run-pass/allocator-override # - Bug in inferrence order. @@ -377,6 +378,34 @@ DISABLED_TESTS += run-pass/issue-14399 DISABLED_TESTS += run-pass/issue-15221 DISABLED_TESTS += run-pass/issue-20575 DISABLED_TESTS += run-pass/issue-20797 +DISABLED_TESTS += run-pass/issue-21306 +DISABLED_TESTS += run-pass/issue-21245 +DISABLED_TESTS += run-pass/issue-21486 # Type mismatch +DISABLED_TESTS += run-pass/issue-21410 # Infinite recursion +DISABLED_TESTS += run-pass/issue-25439 # ^ +DISABLED_TESTS += run-pass/issue-22629 # Auto trait + UFCS todo +DISABLED_TESTS += run-pass/issue-22828 # ^ +DISABLED_TESTS += run-pass/issue-23485 # _ type with no ivar num +DISABLED_TESTS += run-pass/issue-26805 # ^ +DISABLED_TESTS += run-pass/issue-23699 # fn() inferrence +DISABLED_TESTS += run-pass/issue-25549-multiple-drop +DISABLED_TESTS += run-pass/issue-26709 +DISABLED_TESTS += run-pass/issue-30371 # destructuring pattern on ! +DISABLED_TESTS += run-pass/issue-33687 # Unit struct implementing FnOnce call +DISABLED_TESTS += run-pass/issue-38033 +DISABLED_TESTS += run-pass/issue-7784 # Comparison +DISABLED_TESTS += run-pass/issue-9951 # Trait impled for i32 +# - Lazy (Typecheck - Leftover rules) +DISABLED_TESTS += run-pass/issue-33387 +DISABLED_TESTS += run-pass/issue-35815 +# - Lazy (Typecheck - Array unsize) +DISABLED_TESTS += run-pass/issue-21562 +DISABLED_TESTS += run-pass/issue-23261 +DISABLED_TESTS += run-pass/issue-23491 +DISABLED_TESTS += run-pass/issue-36278-prefix-nesting +DISABLED_TESTS += run-pass/issue-9382 +# - Lazy (Typecheck + Trait unsize) +DISABLED_TESTS += run-pass/issue-27105 # - Lazy (MIR) DISABLED_TESTS += run-pass/if-ret DISABLED_TESTS += run-pass/intrinsics-integer @@ -387,11 +416,29 @@ DISABLED_TESTS += run-pass/issue-15080 DISABLED_TESTS += run-pass/issue-15104 DISABLED_TESTS += run-pass/issue-15763 DISABLED_TESTS += run-pass/issue-17877 # - SplitSlice + array +DISABLED_TESTS += run-pass/issue-37598 # - SplitSlice in DTN DISABLED_TESTS += run-pass/issue-18060 # - Overlapping value ranges DISABLED_TESTS += run-pass/issue-18110 # - Missing value DISABLED_TESTS += run-pass/issue-18352 # - Match+const -# - Lazy (misg) +DISABLED_TESTS += run-pass/issue-28839 # - Move &mut ? +DISABLED_TESTS += run-pass/issue-28950 # - Stack overflow in vec! +DISABLED_TESTS += run-pass/issue-29227 # - Excessive time in MIR lowering +DISABLED_TESTS += run-pass/issue-30018-nopanic # Missing value +DISABLED_TESTS += run-pass/issue-36936 # - Cast removed +DISABLED_TESTS += run-pass/issue-4734 # Destructor on unused rvalue +DISABLED_TESTS += run-pass/issue-8860 # No argument drop +# - Lazy (trans) +DISABLED_TESTS += run-pass/issue-21058 # Empty trait object vtable +DISABLED_TESTS += run-pass/issue-25515 # ^ +DISABLED_TESTS += run-pass/issue-29663 # Missing volatile_store intrinsic +# - Lazy (misc) DISABLED_TESTS += run-pass/issue-13494 +DISABLED_TESTS += run-pass/issue-6919 # Literal function pointer +DISABLED_TESTS += run-pass/item-attributes # Attributed function after last statement leads to last statement yielded +# - Resolve +DISABLED_TESTS += run-pass/issue-22546 # None:: handling in patterns +DISABLED_TESTS += run-pass/issue-29540 # Infinite recursion +DISABLED_TESTS += run-pass/issue-38002 # Enum::StructVariant # - Overly-restrictive consteval DISABLED_TESTS += run-pass/check-static-mut-slices run-pass/check-static-slice DISABLED_TESTS += run-pass/const-binops @@ -409,6 +456,11 @@ DISABLED_TESTS += run-pass/enum-vec-initializer DISABLED_TESTS += run-pass/huge-largest-array DISABLED_TESTS += run-pass/issue-17233 DISABLED_TESTS += run-pass/issue-19244 # Missing type info +DISABLED_TESTS += run-pass/issue-22894 # TODO: Deref +DISABLED_TESTS += run-pass/issue-25180 # Closure in const +DISABLED_TESTS += run-pass/issue-27268 # ^ +DISABLED_TESTS += run-pass/issue-28189 # ^ +DISABLED_TESTS += run-pass/issue-25757 # UFCS function pointer # - Type defaults not supported DISABLED_TESTS += run-pass/default-associated-types DISABLED_TESTS += run-pass/default_ty_param_default_dependent_associated_type @@ -427,8 +479,7 @@ DISABLED_TESTS += run-pass/issue-17718 DISABLED_TESTS += run-pass/fn-item-type-zero-sized # fn() items are not ZSTs DISABLED_TESTS += run-pass/int-abs-overflow # No overflow checks DISABLED_TESTS += run-pass/issue-18859 # module_path output is differend -# - BUG-USE AFTER FREE (scoping) -DISABLED_TESTS += run-pass/cleanup-rvalue-scopes +DISABLED_TESTS += run-pass/issue-8709 # stringify! output # - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying DISABLED_TESTS += run-pass/deriving-copyclone # - BUG-Expand: format_args! @@ -439,13 +490,30 @@ DISABLED_TESTS += run-pass/ifmt DISABLED_TESTS += run-pass/intrinsic-alignment # - BUG-Expand: No cfg on enum vars DISABLED_TESTS += run-pass/issue-11085 +# - BUG-Expand: line/column macros don't work properly +DISABLED_TESTS += run-pass/issue-26322 +# - Expand +DISABLED_TESTS += run-pass/lexer-crlf-line-endings-string-literal-doc-comment # Missing include_str! +DISABLED_TESTS += run-pass/link-cfg-works # cfg in #[link] +DISABLED_TESTS += run-pass/linkage1 # #[linkage] +DISABLED_TESTS += run-pass/log_syntax-trace_macros-macro-locations # no trace_macros! # - BUG-Parse: `use *` DISABLED_TESTS += run-pass/import-glob-crate DISABLED_TESTS += run-pass/import-prefix-macro +# - BUG-Parse +DISABLED_TESTS += run-pass/issue-37733 # for<'a,> +DISABLED_TESTS += run-pass/loop-break-value # `break value` # - BUG-CODEGEN: Missing symbol DISABLED_TESTS += run-pass/const-enum-ptr DISABLED_TESTS += run-pass/const-enum-vec-ptr DISABLED_TESTS += run-pass/const-vecs-and-slices +DISABLED_TESTS += run-pass/issue-5688 +DISABLED_TESTS += run-pass/issue-5917 +DISABLED_TESTS += run-pass/issue-7012 +DISABLED_TESTS += run-pass/issue-29147 # Missing type +DISABLED_TESTS += run-pass/issue-30081 # ^ +DISABLED_TESTS += run-pass/issue-3447 # ^ +DISABLED_TESTS += run-pass/issue-34796 # Missing vtable type (in dep) # - BUG: Codegen drops DISABLED_TESTS += run-pass/extern_fat_drop # - BUG: Enum variants not getting correct integer values @@ -456,6 +524,10 @@ DISABLED_TESTS += run-pass/enum-disr-val-pretty DISABLED_TESTS += run-pass/enum-univariant-repr DISABLED_TESTS += run-pass/issue-15523-big DISABLED_TESTS += run-pass/issue-15523 +DISABLED_TESTS += run-pass/issue-23304-1 +DISABLED_TESTS += run-pass/issue-23304-2 +DISABLED_TESTS += run-pass/issue-2428 +DISABLED_TESTS += run-pass/issue-9837 # - BUG: Null pointer opt not fully correct DISABLED_TESTS += run-pass/enum-null-pointer-opt # - BUG: Incorrect enum sizing @@ -475,6 +547,12 @@ DISABLED_TESTS += run-pass/intrinsics-math DISABLED_TESTS += run-pass/hygiene DISABLED_TESTS += run-pass/hygienic-labels-in-let DISABLED_TESTS += run-pass/hygienic-labels +# - BUG: Incorrect drop order of ? +DISABLED_TESTS += run-pass/issue-23338-ensure-param-drop-order +# - BUG: Incorrect consteval +DISABLED_TESTS += run-pass/issue-23968-const-not-overflow # !0 / 2 incorrect value +# - BUG: Incorrect ordering of read in binops +DISABLED_TESTS += run-pass/issue-27054-primitive-binary-ops # - ?? Is this valid DISABLED_TESTS += run-pass/const-enum-vec-index # - Line information that isn't avaliable due to codegen @@ -485,18 +563,31 @@ DISABLED_TESTS += run-pass/cleanup-rvalue-temp-during-incomplete-alloc DISABLED_TESTS += run-pass/drop-trait-enum DISABLED_TESTS += run-pass/intrinsic-move-val-cleanups DISABLED_TESTS += run-pass/issue-14875 +DISABLED_TESTS += run-pass/issue-25089 +DISABLED_TESTS += run-pass/issue-26655 +DISABLED_TESTS += run-pass/issue-30018-panic +DISABLED_TESTS += run-pass/issue-8460 +DISABLED_TESTS += run-pass/iter-step-overflow-debug +DISABLED_TESTS += run-pass/iter-sum-overflow-debug # - Test framework required DISABLED_TESTS += run-pass/core-run-destroy DISABLED_TESTS += run-pass/exec-env DISABLED_TESTS += run-pass/issue-16597-empty DISABLED_TESTS += run-pass/issue-16597 # NOTE: Crashes in resolve DISABLED_TESTS += run-pass/issue-20823 +DISABLED_TESTS += run-pass/issue-34932 +DISABLED_TESTS += run-pass/issue-36768 # - Makefile test framework quirks DISABLED_TESTS += run-pass/issue-18913 +DISABLED_TESTS += run-pass/issue-2380-b +DISABLED_TESTS += run-pass/issue-29485 # - Target Features DISABLED_TESTS += run-pass/crt-static-on-works # - Infinite loops DISABLED_TESTS += run-pass/issue-16671 +DISABLED_TESTS += run-pass/issue-27890 # - Stack exhausted : Resolve +# - Impl selection +DISABLED_TESTS += run-pass/issue-23208 DEF_RUST_TESTS = $(sort $(patsubst $(RUST_TESTS_DIR)%.rs,output/rust/%_out.txt,$(wildcard $(RUST_TESTS_DIR)$1/*.rs))) rust_tests-run-pass: $(filter-out $(patsubst %,output/rust/%_out.txt,$(DISABLED_TESTS)), $(call DEF_RUST_TESTS,run-pass)) @@ -514,7 +605,7 @@ TEST_ARGS_run-pass/cfgs-on-items := --cfg fooA --cfg fooB TEST_ARGS_run-pass/cfg-macros-foo := --cfg foo TEST_ARGS_run-pass/cfg_attr := --cfg set1 --cfg set2 TEST_ARGS_run-pass/issue-11085 := --cfg foo -TEST_ARGS_run-pass/issue-15881-model-lexer-dotdotdot := -g +TEST_ARGS_run-pass/issue-21361 := -g output/rust/%: $(RUST_TESTS_DIR)%.rs $(RUSTCSRC) $(BIN) output/libstd.hir output/libtest.hir output/test_deps/librust_test_helpers.a @mkdir -p $(dir $@) -- cgit v1.2.3 From 6481b81ddfa5a22eded15df81fa7dfb3c108ba09 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 19 Mar 2017 16:43:27 +0800 Subject: Makefile - run-pass test triage done --- Makefile | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 624a8a9b..f369d634 100644 --- a/Makefile +++ b/Makefile @@ -323,18 +323,22 @@ $(RUSTCSRC)build/Makefile: $(RUSTCSRC)src/llvm/CMakeLists.txt # .PHONY: rust_tests RUST_TESTS_DIR := $(RUSTCSRC)src/test/ -rust_tests: rust_tests-run-pass rust_tests-run-fail +rust_tests: rust_tests-run-pass +# rust_tests-run-fail # rust_tests-compile-fail # - Require external symbols that aren't generated. DISABLED_TESTS = run-pass/abi-sysv64-arg-passing run-pass/abi-sysv64-register-usage run-pass/anon-extern-mod run-pass/anon-extern-mod-cross-crate-2 # - NOT A TEST DISABLED_TESTS += run-pass/backtrace-debuginfo-aux +DISABLED_TESTS += run-pass/mod_file_aux # - asm! is hard to trnaslate DISABLED_TESTS += run-pass/asm-in-out-operand run-pass/asm-indirect-memory run-pass/asm-out-assign DISABLED_TESTS += run-pass/i128 DISABLED_TESTS += run-pass/issue-14936 DISABLED_TESTS += run-pass/issue-32947 +DISABLED_TESTS += run-pass/num-wrapping +DISABLED_TESTS += run-pass/out-of-stack # - Requires jemalloc DISABLED_TESTS += run-pass/allocator-default run-pass/allocator-override # - Bug in inferrence order. @@ -384,26 +388,55 @@ DISABLED_TESTS += run-pass/issue-21486 # Type mismatch DISABLED_TESTS += run-pass/issue-21410 # Infinite recursion DISABLED_TESTS += run-pass/issue-25439 # ^ DISABLED_TESTS += run-pass/issue-22629 # Auto trait + UFCS todo +DISABLED_TESTS += run-pass/send-is-not-static-par-for # ^ DISABLED_TESTS += run-pass/issue-22828 # ^ DISABLED_TESTS += run-pass/issue-23485 # _ type with no ivar num DISABLED_TESTS += run-pass/issue-26805 # ^ +DISABLED_TESTS += run-pass/match-vec-alternatives # ^ +DISABLED_TESTS += run-pass/pure-sum # ^ +DISABLED_TESTS += run-pass/struct-aliases # ^ DISABLED_TESTS += run-pass/issue-23699 # fn() inferrence DISABLED_TESTS += run-pass/issue-25549-multiple-drop DISABLED_TESTS += run-pass/issue-26709 DISABLED_TESTS += run-pass/issue-30371 # destructuring pattern on ! DISABLED_TESTS += run-pass/issue-33687 # Unit struct implementing FnOnce call DISABLED_TESTS += run-pass/issue-38033 -DISABLED_TESTS += run-pass/issue-7784 # Comparison +DISABLED_TESTS += run-pass/issue-7784 # PartialEq impl +DISABLED_TESTS += run-pass/traits-issue-26339 # ^ DISABLED_TESTS += run-pass/issue-9951 # Trait impled for i32 +DISABLED_TESTS += run-pass/trait-default-method-xc # ^ +DISABLED_TESTS += run-pass/trait-impl # ^ +DISABLED_TESTS += run-pass/mir_coercions # Coercion to unsafe fn +DISABLED_TESTS += run-pass/typeck-fn-to-unsafe-fn-ptr # ^ +DISABLED_TESTS += run-pass/unsafe-coercion # ^ +DISABLED_TESTS += run-pass/mir_misc_casts # Cast fn to *const isize +DISABLED_TESTS += run-pass/never-result # ! not correctly unifiying +DISABLED_TESTS += run-pass/reachable-unnameable-items # assert Struct::is_Named() +DISABLED_TESTS += run-pass/self-impl # Unable to infer +DISABLED_TESTS += run-pass/trait-copy-guessing # ^ +DISABLED_TESTS += run-pass/sync-send-iterators-in-libcore # Send for Range +DISABLED_TESTS += run-pass/traits-repeated-supertrait # Type mismatch, i64 and u64 +DISABLED_TESTS += run-pass/trans-object-shim # fn cast to other fn as type annotation +DISABLED_TESTS += run-pass/variadic-ffi # variadics not supported +DISABLED_TESTS += run-pass/weird-exprs # Line 17, let _ = return; result type +DISABLED_TESTS += run-pass/where-for-self # Failed deref coercion? # - Lazy (Typecheck - Leftover rules) DISABLED_TESTS += run-pass/issue-33387 DISABLED_TESTS += run-pass/issue-35815 +DISABLED_TESTS += run-pass/mir_fat_ptr +DISABLED_TESTS += run-pass/regions-infer-borrow-scope-addr-of +DISABLED_TESTS += run-pass/slice_binary_search +DISABLED_TESTS += run-pass/swap-2 # - Lazy (Typecheck - Array unsize) DISABLED_TESTS += run-pass/issue-21562 DISABLED_TESTS += run-pass/issue-23261 DISABLED_TESTS += run-pass/issue-23491 DISABLED_TESTS += run-pass/issue-36278-prefix-nesting DISABLED_TESTS += run-pass/issue-9382 +DISABLED_TESTS += run-pass/match-byte-array-patterns +DISABLED_TESTS += run-pass/mir_raw_fat_ptr +DISABLED_TESTS += run-pass/overloaded-autoderef-indexing +DISABLED_TESTS += run-pass/raw-fat-ptr # - Lazy (Typecheck + Trait unsize) DISABLED_TESTS += run-pass/issue-27105 # - Lazy (MIR) @@ -416,7 +449,12 @@ DISABLED_TESTS += run-pass/issue-15080 DISABLED_TESTS += run-pass/issue-15104 DISABLED_TESTS += run-pass/issue-15763 DISABLED_TESTS += run-pass/issue-17877 # - SplitSlice + array +DISABLED_TESTS += run-pass/vec-matching-fixed # ^ DISABLED_TESTS += run-pass/issue-37598 # - SplitSlice in DTN +DISABLED_TESTS += run-pass/vec-matching-fold # ^ +DISABLED_TESTS += run-pass/vec-matching-legal-tail-element-borrow # ^ +DISABLED_TESTS += run-pass/vec-tail-matching # SplitSlice destructure array +DISABLED_TESTS += run-pass/zero_sized_subslice_match # ^ DISABLED_TESTS += run-pass/issue-18060 # - Overlapping value ranges DISABLED_TESTS += run-pass/issue-18110 # - Missing value DISABLED_TESTS += run-pass/issue-18352 # - Match+const @@ -424,21 +462,54 @@ DISABLED_TESTS += run-pass/issue-28839 # - Move &mut ? DISABLED_TESTS += run-pass/issue-28950 # - Stack overflow in vec! DISABLED_TESTS += run-pass/issue-29227 # - Excessive time in MIR lowering DISABLED_TESTS += run-pass/issue-30018-nopanic # Missing value +DISABLED_TESTS += run-pass/match-bot-2 # ^ +DISABLED_TESTS += run-pass/unreachable-code # ^ DISABLED_TESTS += run-pass/issue-36936 # - Cast removed DISABLED_TESTS += run-pass/issue-4734 # Destructor on unused rvalue DISABLED_TESTS += run-pass/issue-8860 # No argument drop +DISABLED_TESTS += run-pass/mir_build_match_comparisons # todo in match +DISABLED_TESTS += run-pass/struct-order-of-eval-1 # Struct init order +DISABLED_TESTS += run-pass/struct-order-of-eval-3 # ^ # - Lazy (trans) DISABLED_TESTS += run-pass/issue-21058 # Empty trait object vtable DISABLED_TESTS += run-pass/issue-25515 # ^ -DISABLED_TESTS += run-pass/issue-29663 # Missing volatile_store intrinsic +DISABLED_TESTS += run-pass/issue-29663 # Missing volatile_(load|store) intrinsic +DISABLED_TESTS += run-pass/volatile-fat-ptr # ^ +DISABLED_TESTS += run-pass/newtype # Can't handle mutally recursive definitions +DISABLED_TESTS += run-pass/transmute-specialization # Opaque type hit? +DISABLED_TESTS += run-pass/unit-fallback # ! didn't default to () +# - Trans: No handling of repr() +DISABLED_TESTS += run-pass/packed-struct-generic-layout +DISABLED_TESTS += run-pass/packed-struct-generic-size +DISABLED_TESTS += run-pass/packed-struct-layout +DISABLED_TESTS += run-pass/packed-struct-size-xc +DISABLED_TESTS += run-pass/packed-struct-size +DISABLED_TESTS += run-pass/packed-struct-vec +DISABLED_TESTS += run-pass/packed-tuple-struct-layout +DISABLED_TESTS += run-pass/packed-tuple-struct-size # - Lazy (misc) DISABLED_TESTS += run-pass/issue-13494 DISABLED_TESTS += run-pass/issue-6919 # Literal function pointer DISABLED_TESTS += run-pass/item-attributes # Attributed function after last statement leads to last statement yielded +DISABLED_TESTS += run-pass/new-box-syntax # todo - placement syntax +DISABLED_TESTS += run-pass/placement-in-syntax # ^ +DISABLED_TESTS += run-pass/pat-tuple-1 # assertion in AVU +DISABLED_TESTS += run-pass/pat-tuple-2 # ^ +DISABLED_TESTS += run-pass/pat-tuple-3 # ^ +DISABLED_TESTS += run-pass/pat-tuple-4 # ^ +DISABLED_TESTS += run-pass/paths-in-macro-invocations # MISSING: qualified macro paths +DISABLED_TESTS += run-pass/process-spawn-with-unicode-params +DISABLED_TESTS += run-pass/struct-path-associated-type # non-absolute path for HIR::GenericPath +DISABLED_TESTS += run-pass/struct-path-self # ^ +DISABLED_TESTS += run-pass/ufcs-polymorphic-paths # ^ +DISABLED_TESTS += run-pass/u128 # u128 not very good, unknown where error is # - Resolve DISABLED_TESTS += run-pass/issue-22546 # None:: handling in patterns DISABLED_TESTS += run-pass/issue-29540 # Infinite recursion DISABLED_TESTS += run-pass/issue-38002 # Enum::StructVariant +DISABLED_TESTS += run-pass/match-arm-statics # ^ +DISABLED_TESTS += run-pass/mir_ascription_coercion # Missed item +DISABLED_TESTS += run-pass/type-ascription # Relative path in lowering # - Overly-restrictive consteval DISABLED_TESTS += run-pass/check-static-mut-slices run-pass/check-static-slice DISABLED_TESTS += run-pass/const-binops @@ -461,6 +532,8 @@ DISABLED_TESTS += run-pass/issue-25180 # Closure in const DISABLED_TESTS += run-pass/issue-27268 # ^ DISABLED_TESTS += run-pass/issue-28189 # ^ DISABLED_TESTS += run-pass/issue-25757 # UFCS function pointer +DISABLED_TESTS += run-pass/mir_refs_correct +DISABLED_TESTS += run-pass/vec-fixed-length # Overflow in costeval # - Type defaults not supported DISABLED_TESTS += run-pass/default-associated-types DISABLED_TESTS += run-pass/default_ty_param_default_dependent_associated_type @@ -475,13 +548,29 @@ DISABLED_TESTS += run-pass/generic-default-type-params-cross-crate DISABLED_TESTS += run-pass/generic-default-type-params # - ERROR: Function pointers in consants/statics don't trigger calls DISABLED_TESTS += run-pass/issue-17718 +DISABLED_TESTS += run-pass/rfc1623 +DISABLED_TESTS += run-pass/static-function-pointer-xc +DISABLED_TESTS += run-pass/static-function-pointer # - Quirks DISABLED_TESTS += run-pass/fn-item-type-zero-sized # fn() items are not ZSTs DISABLED_TESTS += run-pass/int-abs-overflow # No overflow checks DISABLED_TESTS += run-pass/issue-18859 # module_path output is differend DISABLED_TESTS += run-pass/issue-8709 # stringify! output +DISABLED_TESTS += run-pass/tydesc-name # ^ +DISABLED_TESTS += run-pass/type-id-higher-rank-2 # lifetimes don't apply in type_id +DISABLED_TESTS += run-pass/type-id-higher-rank # ^ # - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying DISABLED_TESTS += run-pass/deriving-copyclone +# - BUG-Expand: macro_rules +DISABLED_TESTS += run-pass/macro-of-higher-order +DISABLED_TESTS += run-pass/macro-pat # :pat doesn't allow MACRO +DISABLED_TESTS += run-pass/macro-reexport-no-intermediate-use # macro_reexport failed +DISABLED_TESTS += run-pass/macro-with-attrs1 # cfg on macro_rules +DISABLED_TESTS += run-pass/shift-near-oflo # Scoping rules +DISABLED_TESTS += run-pass/type-macros-simple # ^ +DISABLED_TESTS += run-pass/stmt_expr_attr_macro_parse # Orderign with :expr and #[] +DISABLED_TESTS += run-pass/sync-send-iterators-in-libcollections # .. should match :expr +DISABLED_TESTS += run-pass/type-macros-hlist # Mismatched arms # - BUG-Expand: format_args! DISABLED_TESTS += run-pass/fmt-pointer-trait DISABLED_TESTS += run-pass/format-ref-cell @@ -494,15 +583,34 @@ DISABLED_TESTS += run-pass/issue-11085 DISABLED_TESTS += run-pass/issue-26322 # - Expand DISABLED_TESTS += run-pass/lexer-crlf-line-endings-string-literal-doc-comment # Missing include_str! +DISABLED_TESTS += run-pass/syntax-extension-source-utils # ^ DISABLED_TESTS += run-pass/link-cfg-works # cfg in #[link] DISABLED_TESTS += run-pass/linkage1 # #[linkage] DISABLED_TESTS += run-pass/log_syntax-trace_macros-macro-locations # no trace_macros! +DISABLED_TESTS += run-pass/macro-use-all-and-none # missing macro_use feature +DISABLED_TESTS += run-pass/macro-use-both # ^ +DISABLED_TESTS += run-pass/macro-use-one # ^ +DISABLED_TESTS += run-pass/two-macro-use # ^ +DISABLED_TESTS += run-pass/simd-intrinsic-generic-cast # Missing concat_idents! +DISABLED_TESTS += run-pass/simd-intrinsic-generic-comparison # ^ +DISABLED_TESTS += run-pass/smallest-hello-world # missing lang item +DISABLED_TESTS += run-pass/trait-item-inside-macro # macro invocations in traits +DISABLED_TESTS += run-pass/try-operator-custom # `?` carrier +DISABLED_TESTS += run-pass/wrapping-int-api # cfg on match arms +# - Parse +DISABLED_TESTS += run-pass/issue-37733 # for<'a,> +DISABLED_TESTS += run-pass/loop-break-value # `break value` +DISABLED_TESTS += run-pass/macro-attribute-expansion # No handling of $expr in attributes +DISABLED_TESTS += run-pass/macro-doc-escapes # Doc comments aren't attributes +DISABLED_TESTS += run-pass/macro-doc-raw-str-hashes # ^ +DISABLED_TESTS += run-pass/macro-interpolation # $block not allowed in place of function body +DISABLED_TESTS += run-pass/macro-stmt # ^ +DISABLED_TESTS += run-pass/macro-tt-followed-by-seq # Mismatched arms? +DISABLED_TESTS += run-pass/struct-field-shorthand # Struct field shorthand +DISABLED_TESTS += run-pass/vec-matching # [a, [b,..].., c] # - BUG-Parse: `use *` DISABLED_TESTS += run-pass/import-glob-crate DISABLED_TESTS += run-pass/import-prefix-macro -# - BUG-Parse -DISABLED_TESTS += run-pass/issue-37733 # for<'a,> -DISABLED_TESTS += run-pass/loop-break-value # `break value` # - BUG-CODEGEN: Missing symbol DISABLED_TESTS += run-pass/const-enum-ptr DISABLED_TESTS += run-pass/const-enum-vec-ptr @@ -514,8 +622,17 @@ DISABLED_TESTS += run-pass/issue-29147 # Missing type DISABLED_TESTS += run-pass/issue-30081 # ^ DISABLED_TESTS += run-pass/issue-3447 # ^ DISABLED_TESTS += run-pass/issue-34796 # Missing vtable type (in dep) +DISABLED_TESTS += run-pass/simd-generics # "platform-intrinsics" +DISABLED_TESTS += run-pass/simd-intrinsic-generic-arithmetic # ^ +DISABLED_TESTS += run-pass/simd-intrinsic-generic-elements # ^ +DISABLED_TESTS += run-pass/thread-local-extern-static # Extern static not generated? # - BUG: Codegen drops DISABLED_TESTS += run-pass/extern_fat_drop +DISABLED_TESTS += run-pass/mir_fat_ptr_drop # fat Box doesn't run inner destructor +# - BUG: Codegen +DISABLED_TESTS += run-pass/mir_overflow_off # out-of-range shift behavior +DISABLED_TESTS += run-pass/unsized3 # Pointer instead of fat pointer +DISABLED_TESTS += run-pass/utf8_idents # C backend doesn't support utf8 idents # - BUG: Enum variants not getting correct integer values DISABLED_TESTS += run-pass/discriminant_value DISABLED_TESTS += run-pass/const-nullary-univariant-enum @@ -528,12 +645,22 @@ DISABLED_TESTS += run-pass/issue-23304-1 DISABLED_TESTS += run-pass/issue-23304-2 DISABLED_TESTS += run-pass/issue-2428 DISABLED_TESTS += run-pass/issue-9837 +DISABLED_TESTS += run-pass/resolve-issue-2428 +DISABLED_TESTS += run-pass/signed-shift-const-eval +DISABLED_TESTS += run-pass/small-enum-range-edge +DISABLED_TESTS += run-pass/tag-variant-disr-val # - BUG: Null pointer opt not fully correct DISABLED_TESTS += run-pass/enum-null-pointer-opt +DISABLED_TESTS += run-pass/nonzero-enum +DISABLED_TESTS += run-pass/nullable-pointer-opt-closures +DISABLED_TESTS += run-pass/nullable-pointer-size # - BUG: Incorrect enum sizing DISABLED_TESTS += run-pass/enum-discrim-autosizing DISABLED_TESTS += run-pass/enum-discrim-manual-sizing DISABLED_TESTS += run-pass/enum-discrim-width-stuff +DISABLED_TESTS += run-pass/multiple-reprs # no repr handling +DISABLED_TESTS += run-pass/small-enums-with-fields +DISABLED_TESTS += run-pass/type-sizes # - BUG: Leaking contents of boxed trait objects DISABLED_TESTS += run-pass/drop-struct-as-object DISABLED_TESTS += run-pass/dynamic-drop @@ -547,6 +674,7 @@ DISABLED_TESTS += run-pass/intrinsics-math DISABLED_TESTS += run-pass/hygiene DISABLED_TESTS += run-pass/hygienic-labels-in-let DISABLED_TESTS += run-pass/hygienic-labels +DISABLED_TESTS += run-pass/macro-nested_stmt_macros # hygine fires when it shouldn't # - BUG: Incorrect drop order of ? DISABLED_TESTS += run-pass/issue-23338-ensure-param-drop-order # - BUG: Incorrect consteval @@ -569,6 +697,23 @@ DISABLED_TESTS += run-pass/issue-30018-panic DISABLED_TESTS += run-pass/issue-8460 DISABLED_TESTS += run-pass/iter-step-overflow-debug DISABLED_TESTS += run-pass/iter-sum-overflow-debug +DISABLED_TESTS += run-pass/multi-panic +DISABLED_TESTS += run-pass/nested-vec-3 +DISABLED_TESTS += run-pass/no-landing-pads +DISABLED_TESTS += run-pass/panic-handler-chain +DISABLED_TESTS += run-pass/panic-handler-flail-wildly +DISABLED_TESTS += run-pass/panic-handler-set-twice +DISABLED_TESTS += run-pass/panic-in-dtor-drops-fields +DISABLED_TESTS += run-pass/panic-recover-propagate +DISABLED_TESTS += run-pass/sepcomp-unwind +DISABLED_TESTS += run-pass/slice-panic-1 +DISABLED_TESTS += run-pass/slice-panic-2 +DISABLED_TESTS += run-pass/task-stderr +DISABLED_TESTS += run-pass/terminate-in-initializer +DISABLED_TESTS += run-pass/unit-like-struct-drop-run +DISABLED_TESTS += run-pass/unwind-resource +DISABLED_TESTS += run-pass/unwind-unique +DISABLED_TESTS += run-pass/vector-sort-panic-safe # - Test framework required DISABLED_TESTS += run-pass/core-run-destroy DISABLED_TESTS += run-pass/exec-env @@ -577,17 +722,35 @@ DISABLED_TESTS += run-pass/issue-16597 # NOTE: Crashes in resolve DISABLED_TESTS += run-pass/issue-20823 DISABLED_TESTS += run-pass/issue-34932 DISABLED_TESTS += run-pass/issue-36768 +DISABLED_TESTS += run-pass/reexport-test-harness-main +DISABLED_TESTS += run-pass/test-fn-signature-verification-for-explicit-return-type +DISABLED_TESTS += run-pass/test-main-not-dead-attr +DISABLED_TESTS += run-pass/test-main-not-dead +DISABLED_TESTS += run-pass/test-runner-hides-buried-main +DISABLED_TESTS += run-pass/test-runner-hides-main +DISABLED_TESTS += run-pass/test-runner-hides-start +DISABLED_TESTS += run-pass/test-should-fail-good-message +DISABLED_TESTS += run-pass/test-should-panic-attr # - Makefile test framework quirks DISABLED_TESTS += run-pass/issue-18913 DISABLED_TESTS += run-pass/issue-2380-b DISABLED_TESTS += run-pass/issue-29485 +DISABLED_TESTS += run-pass/svh-add-comment +DISABLED_TESTS += run-pass/svh-add-doc +DISABLED_TESTS += run-pass/svh-add-macro +DISABLED_TESTS += run-pass/svh-add-nothing +DISABLED_TESTS += run-pass/svh-add-redundant-cfg +DISABLED_TESTS += run-pass/svh-add-whitespace # - Target Features DISABLED_TESTS += run-pass/crt-static-on-works +DISABLED_TESTS += run-pass/sse2 # - Infinite loops DISABLED_TESTS += run-pass/issue-16671 DISABLED_TESTS += run-pass/issue-27890 # - Stack exhausted : Resolve +DISABLED_TESTS += run-pass/project-cache-issue-31849 # - Impl selection DISABLED_TESTS += run-pass/issue-23208 +DISABLED_TESTS += run-pass/xcrate-associated-type-defaults # Failed to find an impl DEF_RUST_TESTS = $(sort $(patsubst $(RUST_TESTS_DIR)%.rs,output/rust/%_out.txt,$(wildcard $(RUST_TESTS_DIR)$1/*.rs))) rust_tests-run-pass: $(filter-out $(patsubst %,output/rust/%_out.txt,$(DISABLED_TESTS)), $(call DEF_RUST_TESTS,run-pass)) @@ -605,7 +768,9 @@ TEST_ARGS_run-pass/cfgs-on-items := --cfg fooA --cfg fooB TEST_ARGS_run-pass/cfg-macros-foo := --cfg foo TEST_ARGS_run-pass/cfg_attr := --cfg set1 --cfg set2 TEST_ARGS_run-pass/issue-11085 := --cfg foo +TEST_ARGS_run-pass/macro-meta-items := --cfg foo TEST_ARGS_run-pass/issue-21361 := -g +TEST_ARGS_run-pass/syntax-extension-cfg := --cfg foo --cfg 'qux=foo' output/rust/%: $(RUST_TESTS_DIR)%.rs $(RUSTCSRC) $(BIN) output/libstd.hir output/libtest.hir output/test_deps/librust_test_helpers.a @mkdir -p $(dir $@) -- cgit v1.2.3 From 071dfa6990aefd97c019f6f07e98a58717f7b3c0 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 19 Mar 2017 17:09:18 +0800 Subject: Makefile - Add union tests (most of which fail) --- Makefile | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f369d634..96e5028d 100644 --- a/Makefile +++ b/Makefile @@ -420,6 +420,7 @@ DISABLED_TESTS += run-pass/trans-object-shim # fn cast to other fn as type annot DISABLED_TESTS += run-pass/variadic-ffi # variadics not supported DISABLED_TESTS += run-pass/weird-exprs # Line 17, let _ = return; result type DISABLED_TESTS += run-pass/where-for-self # Failed deref coercion? +DISABLED_TESTS += run-pass/union/union-backcomp # ? discarded value? # - Lazy (Typecheck - Leftover rules) DISABLED_TESTS += run-pass/issue-33387 DISABLED_TESTS += run-pass/issue-35815 @@ -459,6 +460,7 @@ DISABLED_TESTS += run-pass/issue-18060 # - Overlapping value ranges DISABLED_TESTS += run-pass/issue-18110 # - Missing value DISABLED_TESTS += run-pass/issue-18352 # - Match+const DISABLED_TESTS += run-pass/issue-28839 # - Move &mut ? +DISABLED_TESTS += run-pass/union/union-inherent-method # ^ ? DISABLED_TESTS += run-pass/issue-28950 # - Stack overflow in vec! DISABLED_TESTS += run-pass/issue-29227 # - Excessive time in MIR lowering DISABLED_TESTS += run-pass/issue-30018-nopanic # Missing value @@ -470,6 +472,7 @@ DISABLED_TESTS += run-pass/issue-8860 # No argument drop DISABLED_TESTS += run-pass/mir_build_match_comparisons # todo in match DISABLED_TESTS += run-pass/struct-order-of-eval-1 # Struct init order DISABLED_TESTS += run-pass/struct-order-of-eval-3 # ^ +DISABLED_TESTS += run-pass/union/union-drop-assign # No drop when assiging to union field # - Lazy (trans) DISABLED_TESTS += run-pass/issue-21058 # Empty trait object vtable DISABLED_TESTS += run-pass/issue-25515 # ^ @@ -478,6 +481,8 @@ DISABLED_TESTS += run-pass/volatile-fat-ptr # ^ DISABLED_TESTS += run-pass/newtype # Can't handle mutally recursive definitions DISABLED_TESTS += run-pass/transmute-specialization # Opaque type hit? DISABLED_TESTS += run-pass/unit-fallback # ! didn't default to () +# - HIR resolve +DISABLED_TESTS += run-pass/union/union-generic # Can't find associated type on type param # - Trans: No handling of repr() DISABLED_TESTS += run-pass/packed-struct-generic-layout DISABLED_TESTS += run-pass/packed-struct-generic-size @@ -534,6 +539,7 @@ DISABLED_TESTS += run-pass/issue-28189 # ^ DISABLED_TESTS += run-pass/issue-25757 # UFCS function pointer DISABLED_TESTS += run-pass/mir_refs_correct DISABLED_TESTS += run-pass/vec-fixed-length # Overflow in costeval +DISABLED_TESTS += run-pass/union/union-const-trans # Union literal # - Type defaults not supported DISABLED_TESTS += run-pass/default-associated-types DISABLED_TESTS += run-pass/default_ty_param_default_dependent_associated_type @@ -597,6 +603,11 @@ DISABLED_TESTS += run-pass/smallest-hello-world # missing lang item DISABLED_TESTS += run-pass/trait-item-inside-macro # macro invocations in traits DISABLED_TESTS += run-pass/try-operator-custom # `?` carrier DISABLED_TESTS += run-pass/wrapping-int-api # cfg on match arms +DISABLED_TESTS += run-pass/union/union-c-interop # union derive +DISABLED_TESTS += run-pass/union/union-derive # ^ +DISABLED_TESTS += run-pass/union/union-overwrite # ? MetaItem::as_String() +DISABLED_TESTS += run-pass/union/union-packed # ^ +DISABLED_TESTS += run-pass/union/union-pat-refutability # ^ # - Parse DISABLED_TESTS += run-pass/issue-37733 # for<'a,> DISABLED_TESTS += run-pass/loop-break-value # `break value` @@ -608,6 +619,8 @@ DISABLED_TESTS += run-pass/macro-stmt # ^ DISABLED_TESTS += run-pass/macro-tt-followed-by-seq # Mismatched arms? DISABLED_TESTS += run-pass/struct-field-shorthand # Struct field shorthand DISABLED_TESTS += run-pass/vec-matching # [a, [b,..].., c] +# HIR Lowering +DISABLED_TESTS += run-pass/union/union-basic # Union struct pattern # - BUG-Parse: `use *` DISABLED_TESTS += run-pass/import-glob-crate DISABLED_TESTS += run-pass/import-prefix-macro @@ -633,6 +646,7 @@ DISABLED_TESTS += run-pass/mir_fat_ptr_drop # fat Box doesn't run inner destruct DISABLED_TESTS += run-pass/mir_overflow_off # out-of-range shift behavior DISABLED_TESTS += run-pass/unsized3 # Pointer instead of fat pointer DISABLED_TESTS += run-pass/utf8_idents # C backend doesn't support utf8 idents +DISABLED_TESTS += run-pass/union/union-transmute # Incorrect union behavior, likey backend UB # - BUG: Enum variants not getting correct integer values DISABLED_TESTS += run-pass/discriminant_value DISABLED_TESTS += run-pass/const-nullary-univariant-enum @@ -753,7 +767,7 @@ DISABLED_TESTS += run-pass/issue-23208 DISABLED_TESTS += run-pass/xcrate-associated-type-defaults # Failed to find an impl DEF_RUST_TESTS = $(sort $(patsubst $(RUST_TESTS_DIR)%.rs,output/rust/%_out.txt,$(wildcard $(RUST_TESTS_DIR)$1/*.rs))) -rust_tests-run-pass: $(filter-out $(patsubst %,output/rust/%_out.txt,$(DISABLED_TESTS)), $(call DEF_RUST_TESTS,run-pass)) +rust_tests-run-pass: $(filter-out $(patsubst %,output/rust/%_out.txt,$(DISABLED_TESTS)), $(call DEF_RUST_TESTS,run-pass) $(call DEF_RUST_TESTS,run-pass/union)) rust_tests-run-fail: $(call DEF_RUST_TESTS,run-fail) #rust_tests-compile-fail: $(call DEF_RUST_TESTS,compile-fail) -- cgit v1.2.3 From 33ef03560680b32734f713fe7810e1f4ce5affa8 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 19 Mar 2017 21:10:43 +0800 Subject: Expand - Handle #[main] on deleted object --- src/expand/lang_item.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/expand/lang_item.cpp b/src/expand/lang_item.cpp index 0a5c87f5..988dc0e0 100644 --- a/src/expand/lang_item.cpp +++ b/src/expand/lang_item.cpp @@ -187,7 +187,10 @@ public: AttrStage stage() const override { return AttrStage::Post; } void handle(const Span& sp, const AST::MetaItem& attr, AST::Crate& crate, const AST::Path& path, AST::Module& mod, AST::Item& i) const override { - TU_IFLET(::AST::Item, i, Function, e, + if( i.is_None() ) { + // Ignore. + } + else TU_IFLET(::AST::Item, i, Function, e, auto rv = crate.m_lang_items.insert(::std::make_pair( ::std::string("mrustc-main"), ::AST::Path(path) )); if( !rv.second ) { -- cgit v1.2.3 From a1cde92927770193fe5217966521624a4f2872e5 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 19 Mar 2017 21:32:37 +0800 Subject: Makefile - Cleanup of disabled test list --- Makefile | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 96e5028d..cb26246d 100644 --- a/Makefile +++ b/Makefile @@ -328,27 +328,37 @@ rust_tests: rust_tests-run-pass # rust_tests-compile-fail # - Require external symbols that aren't generated. -DISABLED_TESTS = run-pass/abi-sysv64-arg-passing run-pass/abi-sysv64-register-usage run-pass/anon-extern-mod run-pass/anon-extern-mod-cross-crate-2 +DISABLED_TESTS := +DISABLED_TESTS += run-pass/abi-sysv64-arg-passing +DISABLED_TESTS += run-pass/abi-sysv64-register-usage +DISABLED_TESTS += run-pass/anon-extern-mod +DISABLED_TESTS += run-pass/anon-extern-mod-cross-crate-2 # - NOT A TEST DISABLED_TESTS += run-pass/backtrace-debuginfo-aux DISABLED_TESTS += run-pass/mod_file_aux # - asm! is hard to trnaslate -DISABLED_TESTS += run-pass/asm-in-out-operand run-pass/asm-indirect-memory run-pass/asm-out-assign +DISABLED_TESTS += run-pass/asm-in-out-operand +DISABLED_TESTS += run-pass/asm-indirect-memory +DISABLED_TESTS += run-pass/asm-out-assign DISABLED_TESTS += run-pass/i128 DISABLED_TESTS += run-pass/issue-14936 DISABLED_TESTS += run-pass/issue-32947 DISABLED_TESTS += run-pass/num-wrapping DISABLED_TESTS += run-pass/out-of-stack # - Requires jemalloc -DISABLED_TESTS += run-pass/allocator-default run-pass/allocator-override +DISABLED_TESTS += run-pass/allocator-default +DISABLED_TESTS += run-pass/allocator-override # - Bug in inferrence order. DISABLED_TESTS += run-pass/associated-types-conditional-dispatch # - Lazy. -DISABLED_TESTS += run-pass/associated-types-projection-in-where-clause run-pass/autoderef-privacy +DISABLED_TESTS += run-pass/associated-types-projection-in-where-clause +DISABLED_TESTS += run-pass/autoderef-privacy DISABLED_TESTS += run-pass/builtin-superkinds-self-type DISABLED_TESTS += run-pass/byte-literals -DISABLED_TESTS += run-pass/c-stack-as-value run-pass/cabi-int-widening -DISABLED_TESTS += run-pass/cast-rfc0401-vtable-kinds run-pass/cast-rfc0401 +DISABLED_TESTS += run-pass/c-stack-as-value +DISABLED_TESTS += run-pass/cabi-int-widening +DISABLED_TESTS += run-pass/cast-rfc0401-vtable-kinds +DISABLED_TESTS += run-pass/cast-rfc0401 DISABLED_TESTS += run-pass/cast-in-array-size DISABLED_TESTS += run-pass/cast DISABLED_TESTS += run-pass/cfg-in-crate-1 @@ -477,6 +487,7 @@ DISABLED_TESTS += run-pass/union/union-drop-assign # No drop when assiging to un DISABLED_TESTS += run-pass/issue-21058 # Empty trait object vtable DISABLED_TESTS += run-pass/issue-25515 # ^ DISABLED_TESTS += run-pass/issue-29663 # Missing volatile_(load|store) intrinsic +DISABLED_TESTS += run-pass/intrinsic-alignment # Missing pref_align_of intrinsic DISABLED_TESTS += run-pass/volatile-fat-ptr # ^ DISABLED_TESTS += run-pass/newtype # Can't handle mutally recursive definitions DISABLED_TESTS += run-pass/transmute-specialization # Opaque type hit? @@ -516,7 +527,8 @@ DISABLED_TESTS += run-pass/match-arm-statics # ^ DISABLED_TESTS += run-pass/mir_ascription_coercion # Missed item DISABLED_TESTS += run-pass/type-ascription # Relative path in lowering # - Overly-restrictive consteval -DISABLED_TESTS += run-pass/check-static-mut-slices run-pass/check-static-slice +DISABLED_TESTS += run-pass/check-static-mut-slices +DISABLED_TESTS += run-pass/check-static-slice DISABLED_TESTS += run-pass/const-binops DISABLED_TESTS += run-pass/const-contents DISABLED_TESTS += run-pass/const-deref @@ -581,13 +593,10 @@ DISABLED_TESTS += run-pass/type-macros-hlist # Mismatched arms DISABLED_TESTS += run-pass/fmt-pointer-trait DISABLED_TESTS += run-pass/format-ref-cell DISABLED_TESTS += run-pass/ifmt -# - BUG-Expand: #[main] and cfg -DISABLED_TESTS += run-pass/intrinsic-alignment -# - BUG-Expand: No cfg on enum vars -DISABLED_TESTS += run-pass/issue-11085 # - BUG-Expand: line/column macros don't work properly DISABLED_TESTS += run-pass/issue-26322 # - Expand +DISABLED_TESTS += run-pass/issue-11085 # No support for cfg() on enum variants DISABLED_TESTS += run-pass/lexer-crlf-line-endings-string-literal-doc-comment # Missing include_str! DISABLED_TESTS += run-pass/syntax-extension-source-utils # ^ DISABLED_TESTS += run-pass/link-cfg-works # cfg in #[link] @@ -698,9 +707,12 @@ DISABLED_TESTS += run-pass/issue-27054-primitive-binary-ops # - ?? Is this valid DISABLED_TESTS += run-pass/const-enum-vec-index # - Line information that isn't avaliable due to codegen -DISABLED_TESTS += run-pass/backtrace-debuginfo run-pass/backtrace +DISABLED_TESTS += run-pass/backtrace-debuginfo +DISABLED_TESTS += run-pass/backtrace # - No unwind catching support -DISABLED_TESTS += run-pass/binary-heap-panic-safe run-pass/box-of-array-of-drop-1 run-pass/box-of-array-of-drop-2 +DISABLED_TESTS += run-pass/binary-heap-panic-safe +DISABLED_TESTS += run-pass/box-of-array-of-drop-1 +DISABLED_TESTS += run-pass/box-of-array-of-drop-2 DISABLED_TESTS += run-pass/cleanup-rvalue-temp-during-incomplete-alloc DISABLED_TESTS += run-pass/drop-trait-enum DISABLED_TESTS += run-pass/intrinsic-move-val-cleanups -- cgit v1.2.3 From d5ccc4cb403cfd05b3491ee796eef761b2832ae7 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 19 Mar 2017 22:03:32 +0800 Subject: Makefile - Enable some now-passing tests --- Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index cb26246d..2e8092b5 100644 --- a/Makefile +++ b/Makefile @@ -327,16 +327,12 @@ rust_tests: rust_tests-run-pass # rust_tests-run-fail # rust_tests-compile-fail -# - Require external symbols that aren't generated. DISABLED_TESTS := -DISABLED_TESTS += run-pass/abi-sysv64-arg-passing -DISABLED_TESTS += run-pass/abi-sysv64-register-usage -DISABLED_TESTS += run-pass/anon-extern-mod -DISABLED_TESTS += run-pass/anon-extern-mod-cross-crate-2 # - NOT A TEST DISABLED_TESTS += run-pass/backtrace-debuginfo-aux DISABLED_TESTS += run-pass/mod_file_aux -# - asm! is hard to trnaslate +# - asm! is hard to translate +DISABLED_TESTS += run-pass/abi-sysv64-register-usage DISABLED_TESTS += run-pass/asm-in-out-operand DISABLED_TESTS += run-pass/asm-indirect-memory DISABLED_TESTS += run-pass/asm-out-assign @@ -472,6 +468,7 @@ DISABLED_TESTS += run-pass/issue-18352 # - Match+const DISABLED_TESTS += run-pass/issue-28839 # - Move &mut ? DISABLED_TESTS += run-pass/union/union-inherent-method # ^ ? DISABLED_TESTS += run-pass/issue-28950 # - Stack overflow in vec! +DISABLED_TESTS += run-pass/mir_heavy_promoted # Stack overflow in array constant DISABLED_TESTS += run-pass/issue-29227 # - Excessive time in MIR lowering DISABLED_TESTS += run-pass/issue-30018-nopanic # Missing value DISABLED_TESTS += run-pass/match-bot-2 # ^ -- cgit v1.2.3 From eb21c4268d54c3c71e54b273bd364006d2f82f45 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 20 Mar 2017 14:17:03 +0800 Subject: Makefile - Annotations on more failing tests --- Makefile | 102 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index 2e8092b5..b1e81469 100644 --- a/Makefile +++ b/Makefile @@ -347,71 +347,33 @@ DISABLED_TESTS += run-pass/allocator-override # - Bug in inferrence order. DISABLED_TESTS += run-pass/associated-types-conditional-dispatch # - Lazy. -DISABLED_TESTS += run-pass/associated-types-projection-in-where-clause -DISABLED_TESTS += run-pass/autoderef-privacy -DISABLED_TESTS += run-pass/builtin-superkinds-self-type -DISABLED_TESTS += run-pass/byte-literals -DISABLED_TESTS += run-pass/c-stack-as-value -DISABLED_TESTS += run-pass/cabi-int-widening -DISABLED_TESTS += run-pass/cast-rfc0401-vtable-kinds -DISABLED_TESTS += run-pass/cast-rfc0401 -DISABLED_TESTS += run-pass/cast-in-array-size -DISABLED_TESTS += run-pass/cast -DISABLED_TESTS += run-pass/cfg-in-crate-1 -DISABLED_TESTS += run-pass/coerce-expect-unsized -DISABLED_TESTS += run-pass/coerce-overloaded-autoderef -DISABLED_TESTS += run-pass/coerce-unify-return -DISABLED_TESTS += run-pass/concat -DISABLED_TESTS += run-pass/const-autoderef -DISABLED_TESTS += run-pass/const-block-cross-crate-fn -DISABLED_TESTS += run-pass/const-block-item -DISABLED_TESTS += run-pass/const-block -DISABLED_TESTS += run-pass/const-bound -DISABLED_TESTS += run-pass/const-cast -DISABLED_TESTS += run-pass/discrim-explicit-23030 -DISABLED_TESTS += run-pass/dst-coerce-rc -DISABLED_TESTS += run-pass/dst-coercions -DISABLED_TESTS += run-pass/dst-field-align -DISABLED_TESTS += run-pass/dst-irrefutable-bind -DISABLED_TESTS += run-pass/dst-raw -DISABLED_TESTS += run-pass/dst-struct-sole -DISABLED_TESTS += run-pass/dst-struct -DISABLED_TESTS += run-pass/dst-trait -DISABLED_TESTS += run-pass/empty-struct-braces -DISABLED_TESTS += run-pass/explicit-self-generic -DISABLED_TESTS += run-pass/extern-compare-with-return-type -DISABLED_TESTS += run-pass/fat-ptr-cast -DISABLED_TESTS += run-pass/intrinsic-move-val -DISABLED_TESTS += run-pass/issue-11205 -DISABLED_TESTS += run-pass/issue-13902 -DISABLED_TESTS += run-pass/issue-14399 -DISABLED_TESTS += run-pass/issue-15221 -DISABLED_TESTS += run-pass/issue-20575 -DISABLED_TESTS += run-pass/issue-20797 -DISABLED_TESTS += run-pass/issue-21306 -DISABLED_TESTS += run-pass/issue-21245 +DISABLED_TESTS += run-pass/associated-types-projection-in-where-clause # Not normalizing bounds +DISABLED_TESTS += run-pass/cast # Disallows cast from char to i32 +DISABLED_TESTS += run-pass/empty-struct-braces # Empty struct support +DISABLED_TESTS += run-pass/explicit-self-generic # Tries to use HashMap as an iterator +DISABLED_TESTS += run-pass/extern-compare-with-return-type # Specialisation with function pointers +DISABLED_TESTS += run-pass/issue-14399 # Inferrence ran though a coercion point. +DISABLED_TESTS += run-pass/issue-26709 # ^ +DISABLED_TESTS += run-pass/issue-20797 # Failed to find impl with associated type, possible incorrect coerce? +DISABLED_TESTS += run-pass/issue-21245 # IntoIterator on core::slice::Iterator ? DISABLED_TESTS += run-pass/issue-21486 # Type mismatch DISABLED_TESTS += run-pass/issue-21410 # Infinite recursion DISABLED_TESTS += run-pass/issue-25439 # ^ DISABLED_TESTS += run-pass/issue-22629 # Auto trait + UFCS todo DISABLED_TESTS += run-pass/send-is-not-static-par-for # ^ DISABLED_TESTS += run-pass/issue-22828 # ^ -DISABLED_TESTS += run-pass/issue-23485 # _ type with no ivar num -DISABLED_TESTS += run-pass/issue-26805 # ^ -DISABLED_TESTS += run-pass/match-vec-alternatives # ^ -DISABLED_TESTS += run-pass/pure-sum # ^ -DISABLED_TESTS += run-pass/struct-aliases # ^ DISABLED_TESTS += run-pass/issue-23699 # fn() inferrence -DISABLED_TESTS += run-pass/issue-25549-multiple-drop -DISABLED_TESTS += run-pass/issue-26709 DISABLED_TESTS += run-pass/issue-30371 # destructuring pattern on ! DISABLED_TESTS += run-pass/issue-33687 # Unit struct implementing FnOnce call -DISABLED_TESTS += run-pass/issue-38033 +DISABLED_TESTS += run-pass/issue-38033 # Not equating associated type of type param. DISABLED_TESTS += run-pass/issue-7784 # PartialEq impl DISABLED_TESTS += run-pass/traits-issue-26339 # ^ +DISABLED_TESTS += run-pass/builtin-superkinds-self-type # ^ +DISABLED_TESTS += run-pass/intrinsic-move-val # ^ DISABLED_TESTS += run-pass/issue-9951 # Trait impled for i32 DISABLED_TESTS += run-pass/trait-default-method-xc # ^ DISABLED_TESTS += run-pass/trait-impl # ^ +DISABLED_TESTS += run-pass/issue-11205 # ^ DISABLED_TESTS += run-pass/mir_coercions # Coercion to unsafe fn DISABLED_TESTS += run-pass/typeck-fn-to-unsafe-fn-ptr # ^ DISABLED_TESTS += run-pass/unsafe-coercion # ^ @@ -420,14 +382,28 @@ DISABLED_TESTS += run-pass/never-result # ! not correctly unifiying DISABLED_TESTS += run-pass/reachable-unnameable-items # assert Struct::is_Named() DISABLED_TESTS += run-pass/self-impl # Unable to infer DISABLED_TESTS += run-pass/trait-copy-guessing # ^ -DISABLED_TESTS += run-pass/sync-send-iterators-in-libcore # Send for Range +DISABLED_TESTS += run-pass/issue-20575 # ^ +DISABLED_TESTS += run-pass/sync-send-iterators-in-libcore # Send for Range<_> +DISABLED_TESTS += run-pass/const-bound # Sync for Box<_> DISABLED_TESTS += run-pass/traits-repeated-supertrait # Type mismatch, i64 and u64 DISABLED_TESTS += run-pass/trans-object-shim # fn cast to other fn as type annotation DISABLED_TESTS += run-pass/variadic-ffi # variadics not supported DISABLED_TESTS += run-pass/weird-exprs # Line 17, let _ = return; result type DISABLED_TESTS += run-pass/where-for-self # Failed deref coercion? DISABLED_TESTS += run-pass/union/union-backcomp # ? discarded value? +# - Typecheck - `_` type with no ivar index assigned +DISABLED_TESTS += run-pass/coerce-expect-unsized +DISABLED_TESTS += run-pass/coerce-unify-return +DISABLED_TESTS += run-pass/issue-23485 +DISABLED_TESTS += run-pass/issue-26805 +DISABLED_TESTS += run-pass/match-vec-alternatives +DISABLED_TESTS += run-pass/pure-sum +DISABLED_TESTS += run-pass/struct-aliases # - Lazy (Typecheck - Leftover rules) +DISABLED_TESTS += run-pass/coerce-overloaded-autoderef +DISABLED_TESTS += run-pass/dst-field-align +DISABLED_TESTS += run-pass/dst-irrefutable-bind +DISABLED_TESTS += run-pass/issue-25549-multiple-drop DISABLED_TESTS += run-pass/issue-33387 DISABLED_TESTS += run-pass/issue-35815 DISABLED_TESTS += run-pass/mir_fat_ptr @@ -435,6 +411,11 @@ DISABLED_TESTS += run-pass/regions-infer-borrow-scope-addr-of DISABLED_TESTS += run-pass/slice_binary_search DISABLED_TESTS += run-pass/swap-2 # - Lazy (Typecheck - Array unsize) +DISABLED_TESTS += run-pass/byte-literals +DISABLED_TESTS += run-pass/cast-rfc0401-vtable-kinds +DISABLED_TESTS += run-pass/cast-rfc0401 +DISABLED_TESTS += run-pass/dst-struct-sole +DISABLED_TESTS += run-pass/dst-struct DISABLED_TESTS += run-pass/issue-21562 DISABLED_TESTS += run-pass/issue-23261 DISABLED_TESTS += run-pass/issue-23491 @@ -444,8 +425,13 @@ DISABLED_TESTS += run-pass/match-byte-array-patterns DISABLED_TESTS += run-pass/mir_raw_fat_ptr DISABLED_TESTS += run-pass/overloaded-autoderef-indexing DISABLED_TESTS += run-pass/raw-fat-ptr +DISABLED_TESTS += run-pass/fat-ptr-cast # - Lazy (Typecheck + Trait unsize) DISABLED_TESTS += run-pass/issue-27105 +DISABLED_TESTS += run-pass/dst-coerce-rc +DISABLED_TESTS += run-pass/dst-coercions +DISABLED_TESTS += run-pass/dst-raw +DISABLED_TESTS += run-pass/dst-trait # - Lazy (MIR) DISABLED_TESTS += run-pass/if-ret DISABLED_TESTS += run-pass/intrinsics-integer @@ -467,6 +453,7 @@ DISABLED_TESTS += run-pass/issue-18110 # - Missing value DISABLED_TESTS += run-pass/issue-18352 # - Match+const DISABLED_TESTS += run-pass/issue-28839 # - Move &mut ? DISABLED_TESTS += run-pass/union/union-inherent-method # ^ ? +DISABLED_TESTS += run-pass/issue-21306 # ^ DISABLED_TESTS += run-pass/issue-28950 # - Stack overflow in vec! DISABLED_TESTS += run-pass/mir_heavy_promoted # Stack overflow in array constant DISABLED_TESTS += run-pass/issue-29227 # - Excessive time in MIR lowering @@ -523,7 +510,10 @@ DISABLED_TESTS += run-pass/issue-38002 # Enum::StructVariant DISABLED_TESTS += run-pass/match-arm-statics # ^ DISABLED_TESTS += run-pass/mir_ascription_coercion # Missed item DISABLED_TESTS += run-pass/type-ascription # Relative path in lowering +DISABLED_TESTS += run-pass/issue-15221 # Macros in patterns # - Overly-restrictive consteval +DISABLED_TESTS += run-pass/const-cast # Cast from fn() to pointer +DISABLED_TESTS += run-pass/const-autoderef # Expected [u8] DISABLED_TESTS += run-pass/check-static-mut-slices DISABLED_TESTS += run-pass/check-static-slice DISABLED_TESTS += run-pass/const-binops @@ -566,12 +556,17 @@ DISABLED_TESTS += run-pass/issue-17718 DISABLED_TESTS += run-pass/rfc1623 DISABLED_TESTS += run-pass/static-function-pointer-xc DISABLED_TESTS += run-pass/static-function-pointer +DISABLED_TESTS += run-pass/const-block-cross-crate-fn +DISABLED_TESTS += run-pass/const-block-item +DISABLED_TESTS += run-pass/const-block # - Quirks +DISABLED_TESTS += run-pass/autoderef-privacy # No privacy with autoderef DISABLED_TESTS += run-pass/fn-item-type-zero-sized # fn() items are not ZSTs DISABLED_TESTS += run-pass/int-abs-overflow # No overflow checks DISABLED_TESTS += run-pass/issue-18859 # module_path output is differend DISABLED_TESTS += run-pass/issue-8709 # stringify! output DISABLED_TESTS += run-pass/tydesc-name # ^ +DISABLED_TESTS += run-pass/concat # ^ DISABLED_TESTS += run-pass/type-id-higher-rank-2 # lifetimes don't apply in type_id DISABLED_TESTS += run-pass/type-id-higher-rank # ^ # - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying @@ -681,6 +676,8 @@ DISABLED_TESTS += run-pass/enum-discrim-width-stuff DISABLED_TESTS += run-pass/multiple-reprs # no repr handling DISABLED_TESTS += run-pass/small-enums-with-fields DISABLED_TESTS += run-pass/type-sizes +DISABLED_TESTS += run-pass/discrim-explicit-23030 +DISABLED_TESTS += run-pass/issue-13902 # - BUG: Leaking contents of boxed trait objects DISABLED_TESTS += run-pass/drop-struct-as-object DISABLED_TESTS += run-pass/dynamic-drop @@ -787,6 +784,7 @@ output/rust/test_run-pass_hello: $(RUST_TESTS_DIR)run-pass/hello.rs output/libst @echo "--- [$@]" @./$@ +TEST_ARGS_run-pass/cfg-in-crate-1 := --cfg bar TEST_ARGS_run-pass/cfgs-on-items := --cfg fooA --cfg fooB TEST_ARGS_run-pass/cfg-macros-foo := --cfg foo TEST_ARGS_run-pass/cfg_attr := --cfg set1 --cfg set2 -- cgit v1.2.3 From c8b1ef8df0cd0a30d8e48f786286c70fbb77f4c1 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Tue, 21 Mar 2017 21:35:50 +0800 Subject: format_args! - Add # support --- src/expand/format_args.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/expand/format_args.cpp b/src/expand/format_args.cpp index 775326cd..0f881a6e 100644 --- a/src/expand/format_args.cpp +++ b/src/expand/format_args.cpp @@ -660,7 +660,10 @@ class CFormatArgsExpander: push_toks(toks, TOK_COMMA); push_toks(toks, Token(TOK_IDENT, "flags"), TOK_COLON); - push_toks(toks, Token(uint64_t(0), CORETYPE_U32)); + uint64_t flags = 0; + if(frag.args.alternate) + flags |= 1 << 2; + push_toks(toks, Token(uint64_t(flags), CORETYPE_U32)); push_toks(toks, TOK_COMMA); push_toks(toks, Token(TOK_IDENT, "precision"), TOK_COLON ); -- cgit v1.2.3 From 2c6683a9830f5f3fe5e65937f6f1d46ecffb6835 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 25 Mar 2017 14:02:59 +0800 Subject: MIR Gen - Fix a slight bug in borrow promotion, validate during `Lower MIR` --- src/mir/from_hir.cpp | 15 ++++++++++----- src/mir/mir_builder.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index a15ea8ef..df48627f 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -409,9 +409,9 @@ namespace { bool diverged = false; auto res_val = (node.m_yields_final ? m_builder.new_temporary(node.m_res_type) : ::MIR::LValue()); + auto scope = m_builder.new_scope_var(node.span()); auto tmp_scope = m_builder.new_scope_temp(node.span()); auto _block_tmp_scope = save_and_edit(m_block_tmp_scope, &tmp_scope); - auto scope = m_builder.new_scope_var(node.span()); for(unsigned int i = 0; i < node.m_nodes.size() - (node.m_yields_final ? 1 : 0); i ++) { @@ -452,15 +452,15 @@ namespace { m_builder.push_stmt_assign(sp, res_val.clone(), m_builder.get_result(sp)); m_builder.terminate_scope(sp, mv$(stmt_scope)); - m_builder.terminate_scope(node.span(), mv$(scope) ); m_builder.terminate_scope(node.span(), mv$(tmp_scope) ); + m_builder.terminate_scope(node.span(), mv$(scope) ); m_builder.set_result( node.span(), mv$(res_val) ); } else { m_builder.terminate_scope( sp, mv$(stmt_scope), false ); - m_builder.terminate_scope( node.span(), mv$(scope), false ); m_builder.terminate_scope( node.span(), mv$(tmp_scope), false ); + m_builder.terminate_scope( node.span(), mv$(scope), false ); // Block diverged in final node. } } @@ -468,15 +468,15 @@ namespace { { if( diverged ) { - m_builder.terminate_scope( node.span(), mv$(scope), false ); m_builder.terminate_scope( node.span(), mv$(tmp_scope), false ); + m_builder.terminate_scope( node.span(), mv$(scope), false ); m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); // Don't set a result if there's no block. } else { - m_builder.terminate_scope( node.span(), mv$(scope) ); m_builder.terminate_scope( node.span(), mv$(tmp_scope) ); + m_builder.terminate_scope( node.span(), mv$(scope) ); m_builder.set_result(node.span(), ::MIR::RValue::make_Tuple({})); } } @@ -1990,6 +1990,7 @@ namespace { ::MIR::LValue base_val; if( node.m_base_value ) { + DEBUG("_StructLiteral - base"); this->visit_node_ptr(node.m_base_value); base_val = m_builder.get_result_in_lvalue(node.m_base_value->span(), node.m_base_value->m_res_type); } @@ -2028,6 +2029,7 @@ namespace { auto idx = ::std::find_if(fields.begin(), fields.end(), [&](const auto&x){ return x.first == ent.first; }) - fields.begin(); assert( !values_set[idx] ); values_set[idx] = true; + DEBUG("_StructLiteral - fld '" << ent.first << "' (idx " << idx << ")"); this->visit_node_ptr(valnode); auto res = m_builder.get_result(valnode->span()); @@ -2128,6 +2130,7 @@ namespace { void visit(::HIR::ExprNode_Closure& node) override { TRACE_FUNCTION_F("_Closure - " << node.m_obj_path); + auto _ = save_and_edit(m_borrow_raise_target, nullptr); ::std::vector< ::MIR::Param> vals; vals.reserve( node.m_captures.size() ); @@ -2175,6 +2178,8 @@ namespace { root_node.visit( ev ); } + MIR_Validate(resolve, path, fcn, args, ptr->m_res_type); + return ::MIR::FunctionPointer(new ::MIR::Function(mv$(fcn))); } diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index e8392ef2..96bb28b3 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -544,13 +544,46 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const return ; } ) - else if( scope_def.data.is_Loop() ) + else if( auto* sd_loop = scope_def.data.opt_Loop() ) { - //TODO(sp, "Raising " << val << " to outside of a 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 ) + { + DEBUG("Adding " << val << " as unset to loop exit state"); + if( const auto* ve = val.opt_Variable() ) + { + auto v = sd_loop->exit_state.var_states.insert( ::std::make_pair(*ve, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); + } + else if( const auto* ve = val.opt_Temporary() ) + { + auto v = sd_loop->exit_state.tmp_states.insert( ::std::make_pair(ve->idx, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); + } + else { + BUG(sp, "Impossible raise value"); + } + } + else + { + DEBUG("Crossing loop with no existing exit state"); + } } else if( scope_def.data.is_Split() ) { - //TODO(sp, "Raising " << val << " to outside of a split"); + auto& sd_split = scope_def.data.as_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 ) + { + TODO(sp, "Raising " << val << " to outside of a split"); + } + else + { + DEBUG("Crossing split with no existing end state"); + } } else { @@ -1065,11 +1098,11 @@ void MirBuilder::terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_l void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable) { - ASSERT_BUG(sp, handle.idx < m_scopes.size(), ""); + ASSERT_BUG(sp, handle.idx < m_scopes.size(), "Handle passed to end_split_arm is invalid"); auto& sd = m_scopes.at( handle.idx ); - ASSERT_BUG(sp, sd.data.is_Split(), ""); + ASSERT_BUG(sp, sd.data.is_Split(), "Ending split arm on non-Split arm - " << sd.data.tag_str()); auto& sd_split = sd.data.as_Split(); - ASSERT_BUG(sp, !sd_split.arms.empty(), ""); + ASSERT_BUG(sp, !sd_split.arms.empty(), "Split arm list is empty (impossible)"); TRACE_FUNCTION_F("end split scope " << handle.idx << " arm " << (sd_split.arms.size()-1)); if( reachable ) -- cgit v1.2.3 From a21f8261532231dd1b0da6788f0ea8fb3e143e00 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 25 Mar 2017 14:03:35 +0800 Subject: Makefile - Partial re-audit of tests and reorganisation --- Makefile | 180 ++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 91 insertions(+), 89 deletions(-) diff --git a/Makefile b/Makefile index b1e81469..6d837203 100644 --- a/Makefile +++ b/Makefile @@ -569,8 +569,6 @@ DISABLED_TESTS += run-pass/tydesc-name # ^ DISABLED_TESTS += run-pass/concat # ^ DISABLED_TESTS += run-pass/type-id-higher-rank-2 # lifetimes don't apply in type_id DISABLED_TESTS += run-pass/type-id-higher-rank # ^ -# - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying -DISABLED_TESTS += run-pass/deriving-copyclone # - BUG-Expand: macro_rules DISABLED_TESTS += run-pass/macro-of-higher-order DISABLED_TESTS += run-pass/macro-pat # :pat doesn't allow MACRO @@ -582,8 +580,6 @@ DISABLED_TESTS += run-pass/stmt_expr_attr_macro_parse # Orderign with :expr and DISABLED_TESTS += run-pass/sync-send-iterators-in-libcollections # .. should match :expr DISABLED_TESTS += run-pass/type-macros-hlist # Mismatched arms # - BUG-Expand: format_args! -DISABLED_TESTS += run-pass/fmt-pointer-trait -DISABLED_TESTS += run-pass/format-ref-cell DISABLED_TESTS += run-pass/ifmt # - BUG-Expand: line/column macros don't work properly DISABLED_TESTS += run-pass/issue-26322 @@ -640,100 +636,16 @@ DISABLED_TESTS += run-pass/simd-generics # "platform-intrinsics" DISABLED_TESTS += run-pass/simd-intrinsic-generic-arithmetic # ^ DISABLED_TESTS += run-pass/simd-intrinsic-generic-elements # ^ DISABLED_TESTS += run-pass/thread-local-extern-static # Extern static not generated? -# - BUG: Codegen drops -DISABLED_TESTS += run-pass/extern_fat_drop -DISABLED_TESTS += run-pass/mir_fat_ptr_drop # fat Box doesn't run inner destructor # - BUG: Codegen -DISABLED_TESTS += run-pass/mir_overflow_off # out-of-range shift behavior DISABLED_TESTS += run-pass/unsized3 # Pointer instead of fat pointer DISABLED_TESTS += run-pass/utf8_idents # C backend doesn't support utf8 idents -DISABLED_TESTS += run-pass/union/union-transmute # Incorrect union behavior, likey backend UB -# - BUG: Enum variants not getting correct integer values -DISABLED_TESTS += run-pass/discriminant_value -DISABLED_TESTS += run-pass/const-nullary-univariant-enum -DISABLED_TESTS += run-pass/enum-discr -DISABLED_TESTS += run-pass/enum-disr-val-pretty -DISABLED_TESTS += run-pass/enum-univariant-repr -DISABLED_TESTS += run-pass/issue-15523-big -DISABLED_TESTS += run-pass/issue-15523 -DISABLED_TESTS += run-pass/issue-23304-1 -DISABLED_TESTS += run-pass/issue-23304-2 -DISABLED_TESTS += run-pass/issue-2428 -DISABLED_TESTS += run-pass/issue-9837 -DISABLED_TESTS += run-pass/resolve-issue-2428 -DISABLED_TESTS += run-pass/signed-shift-const-eval -DISABLED_TESTS += run-pass/small-enum-range-edge -DISABLED_TESTS += run-pass/tag-variant-disr-val -# - BUG: Null pointer opt not fully correct -DISABLED_TESTS += run-pass/enum-null-pointer-opt -DISABLED_TESTS += run-pass/nonzero-enum -DISABLED_TESTS += run-pass/nullable-pointer-opt-closures -DISABLED_TESTS += run-pass/nullable-pointer-size -# - BUG: Incorrect enum sizing -DISABLED_TESTS += run-pass/enum-discrim-autosizing -DISABLED_TESTS += run-pass/enum-discrim-manual-sizing -DISABLED_TESTS += run-pass/enum-discrim-width-stuff -DISABLED_TESTS += run-pass/multiple-reprs # no repr handling -DISABLED_TESTS += run-pass/small-enums-with-fields -DISABLED_TESTS += run-pass/type-sizes -DISABLED_TESTS += run-pass/discrim-explicit-23030 -DISABLED_TESTS += run-pass/issue-13902 -# - BUG: Leaking contents of boxed trait objects -DISABLED_TESTS += run-pass/drop-struct-as-object -DISABLED_TESTS += run-pass/dynamic-drop -DISABLED_TESTS += run-pass/issue-10802 -# - BUG: Bad floats -DISABLED_TESTS += run-pass/float-nan -DISABLED_TESTS += run-pass/float_math -DISABLED_TESTS += run-pass/floatlits -DISABLED_TESTS += run-pass/intrinsics-math # - BUG: Hygine DISABLED_TESTS += run-pass/hygiene DISABLED_TESTS += run-pass/hygienic-labels-in-let DISABLED_TESTS += run-pass/hygienic-labels DISABLED_TESTS += run-pass/macro-nested_stmt_macros # hygine fires when it shouldn't -# - BUG: Incorrect drop order of ? -DISABLED_TESTS += run-pass/issue-23338-ensure-param-drop-order -# - BUG: Incorrect consteval -DISABLED_TESTS += run-pass/issue-23968-const-not-overflow # !0 / 2 incorrect value -# - BUG: Incorrect ordering of read in binops -DISABLED_TESTS += run-pass/issue-27054-primitive-binary-ops # - ?? Is this valid DISABLED_TESTS += run-pass/const-enum-vec-index -# - Line information that isn't avaliable due to codegen -DISABLED_TESTS += run-pass/backtrace-debuginfo -DISABLED_TESTS += run-pass/backtrace -# - No unwind catching support -DISABLED_TESTS += run-pass/binary-heap-panic-safe -DISABLED_TESTS += run-pass/box-of-array-of-drop-1 -DISABLED_TESTS += run-pass/box-of-array-of-drop-2 -DISABLED_TESTS += run-pass/cleanup-rvalue-temp-during-incomplete-alloc -DISABLED_TESTS += run-pass/drop-trait-enum -DISABLED_TESTS += run-pass/intrinsic-move-val-cleanups -DISABLED_TESTS += run-pass/issue-14875 -DISABLED_TESTS += run-pass/issue-25089 -DISABLED_TESTS += run-pass/issue-26655 -DISABLED_TESTS += run-pass/issue-30018-panic -DISABLED_TESTS += run-pass/issue-8460 -DISABLED_TESTS += run-pass/iter-step-overflow-debug -DISABLED_TESTS += run-pass/iter-sum-overflow-debug -DISABLED_TESTS += run-pass/multi-panic -DISABLED_TESTS += run-pass/nested-vec-3 -DISABLED_TESTS += run-pass/no-landing-pads -DISABLED_TESTS += run-pass/panic-handler-chain -DISABLED_TESTS += run-pass/panic-handler-flail-wildly -DISABLED_TESTS += run-pass/panic-handler-set-twice -DISABLED_TESTS += run-pass/panic-in-dtor-drops-fields -DISABLED_TESTS += run-pass/panic-recover-propagate -DISABLED_TESTS += run-pass/sepcomp-unwind -DISABLED_TESTS += run-pass/slice-panic-1 -DISABLED_TESTS += run-pass/slice-panic-2 -DISABLED_TESTS += run-pass/task-stderr -DISABLED_TESTS += run-pass/terminate-in-initializer -DISABLED_TESTS += run-pass/unit-like-struct-drop-run -DISABLED_TESTS += run-pass/unwind-resource -DISABLED_TESTS += run-pass/unwind-unique -DISABLED_TESTS += run-pass/vector-sort-panic-safe # - Test framework required DISABLED_TESTS += run-pass/core-run-destroy DISABLED_TESTS += run-pass/exec-env @@ -765,12 +677,102 @@ DISABLED_TESTS += run-pass/svh-add-whitespace DISABLED_TESTS += run-pass/crt-static-on-works DISABLED_TESTS += run-pass/sse2 # - Infinite loops -DISABLED_TESTS += run-pass/issue-16671 DISABLED_TESTS += run-pass/issue-27890 # - Stack exhausted : Resolve DISABLED_TESTS += run-pass/project-cache-issue-31849 # - Impl selection DISABLED_TESTS += run-pass/issue-23208 DISABLED_TESTS += run-pass/xcrate-associated-type-defaults # Failed to find an impl +# --- Runtime Errors --- +# - Line information that isn't avaliable due to codegen +DISABLED_TESTS += run-pass/backtrace-debuginfo +DISABLED_TESTS += run-pass/backtrace +# - No unwind catching support +DISABLED_TESTS += run-pass/binary-heap-panic-safe +DISABLED_TESTS += run-pass/box-of-array-of-drop-1 +DISABLED_TESTS += run-pass/box-of-array-of-drop-2 +DISABLED_TESTS += run-pass/cleanup-rvalue-temp-during-incomplete-alloc +DISABLED_TESTS += run-pass/drop-trait-enum +DISABLED_TESTS += run-pass/intrinsic-move-val-cleanups +DISABLED_TESTS += run-pass/issue-14875 +DISABLED_TESTS += run-pass/issue-25089 +DISABLED_TESTS += run-pass/issue-26655 +DISABLED_TESTS += run-pass/issue-30018-panic +DISABLED_TESTS += run-pass/issue-8460 +DISABLED_TESTS += run-pass/iter-step-overflow-debug +DISABLED_TESTS += run-pass/iter-sum-overflow-debug +DISABLED_TESTS += run-pass/multi-panic +DISABLED_TESTS += run-pass/nested-vec-3 +DISABLED_TESTS += run-pass/no-landing-pads +DISABLED_TESTS += run-pass/panic-handler-chain +DISABLED_TESTS += run-pass/panic-handler-flail-wildly +DISABLED_TESTS += run-pass/panic-handler-set-twice +DISABLED_TESTS += run-pass/panic-in-dtor-drops-fields +DISABLED_TESTS += run-pass/panic-recover-propagate +DISABLED_TESTS += run-pass/sepcomp-unwind +DISABLED_TESTS += run-pass/slice-panic-1 +DISABLED_TESTS += run-pass/slice-panic-2 +DISABLED_TESTS += run-pass/task-stderr +DISABLED_TESTS += run-pass/terminate-in-initializer +DISABLED_TESTS += run-pass/unit-like-struct-drop-run +DISABLED_TESTS += run-pass/unwind-resource +DISABLED_TESTS += run-pass/unwind-unique +DISABLED_TESTS += run-pass/vector-sort-panic-safe +# - Misc +DISABLED_TESTS += run-pass/issue-16671 # Blocks forever +# - BUG: Incorrect drop order of ? +DISABLED_TESTS += run-pass/issue-23338-ensure-param-drop-order +# - BUG: Incorrect consteval +DISABLED_TESTS += run-pass/issue-23968-const-not-overflow # !0 / 2 incorrect value +# - BUG: Incorrect ordering of read in binops +DISABLED_TESTS += run-pass/issue-27054-primitive-binary-ops +# - BUG: Enum variants not getting correct integer values +DISABLED_TESTS += run-pass/discriminant_value +DISABLED_TESTS += run-pass/const-nullary-univariant-enum +DISABLED_TESTS += run-pass/enum-discr +DISABLED_TESTS += run-pass/enum-disr-val-pretty +DISABLED_TESTS += run-pass/enum-univariant-repr +DISABLED_TESTS += run-pass/issue-15523-big +DISABLED_TESTS += run-pass/issue-15523 +DISABLED_TESTS += run-pass/issue-23304-1 +DISABLED_TESTS += run-pass/issue-23304-2 +DISABLED_TESTS += run-pass/issue-2428 +DISABLED_TESTS += run-pass/issue-9837 +DISABLED_TESTS += run-pass/resolve-issue-2428 +DISABLED_TESTS += run-pass/signed-shift-const-eval +DISABLED_TESTS += run-pass/small-enum-range-edge +DISABLED_TESTS += run-pass/tag-variant-disr-val +# - BUG: Null pointer opt not fully correct +DISABLED_TESTS += run-pass/enum-null-pointer-opt +DISABLED_TESTS += run-pass/nonzero-enum +DISABLED_TESTS += run-pass/nullable-pointer-opt-closures +DISABLED_TESTS += run-pass/nullable-pointer-size +# - BUG: Incorrect enum sizing +DISABLED_TESTS += run-pass/enum-discrim-autosizing +DISABLED_TESTS += run-pass/enum-discrim-manual-sizing +DISABLED_TESTS += run-pass/enum-discrim-width-stuff +DISABLED_TESTS += run-pass/multiple-reprs # no repr handling +DISABLED_TESTS += run-pass/small-enums-with-fields +DISABLED_TESTS += run-pass/type-sizes +DISABLED_TESTS += run-pass/discrim-explicit-23030 +DISABLED_TESTS += run-pass/issue-13902 +# - BUG: Leaking contents of boxed trait objects +DISABLED_TESTS += run-pass/drop-struct-as-object +DISABLED_TESTS += run-pass/dynamic-drop +DISABLED_TESTS += run-pass/issue-10802 +DISABLED_TESTS += run-pass/extern_fat_drop +DISABLED_TESTS += run-pass/mir_fat_ptr_drop # fat Box doesn't run inner destructor +# - BUG: Bad floats +DISABLED_TESTS += run-pass/float-nan +DISABLED_TESTS += run-pass/float_math +DISABLED_TESTS += run-pass/floatlits +DISABLED_TESTS += run-pass/intrinsics-math +# - BUG: Codegen +DISABLED_TESTS += run-pass/union/union-transmute # Incorrect union behavior, likey backend UB +DISABLED_TESTS += run-pass/mir_overflow_off # out-of-range shift behavior +# - BUG-Expand: format_args! +DISABLED_TESTS += run-pass/format-ref-cell +# - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying +DISABLED_TESTS += run-pass/deriving-copyclone DEF_RUST_TESTS = $(sort $(patsubst $(RUST_TESTS_DIR)%.rs,output/rust/%_out.txt,$(wildcard $(RUST_TESTS_DIR)$1/*.rs))) rust_tests-run-pass: $(filter-out $(patsubst %,output/rust/%_out.txt,$(DISABLED_TESTS)), $(call DEF_RUST_TESTS,run-pass) $(call DEF_RUST_TESTS,run-pass/union)) -- cgit v1.2.3 From dc7f08ff16eb667ef3feb1d6d54573db2c72939a Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Mar 2017 18:02:26 +0800 Subject: Trans - Handle drop of unsized objects --- src/trans/codegen_c.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++------ src/trans/enumerate.cpp | 1 + 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index a1440422..e74231e8 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -65,7 +65,7 @@ namespace { << "typedef struct { } tTYPEID;\n" << "typedef struct { void* PTR; size_t META; } SLICE_PTR;\n" << "typedef struct { void* PTR; void* META; } TRAITOBJ_PTR;\n" - << "typedef struct { size_t size; size_t align; } VTABLE_HDR;\n" + << "typedef struct { size_t size; size_t align; void (*drop)(void*); } VTABLE_HDR;\n" << "\n" << "extern void _Unwind_Resume(void) __attribute__((noreturn));\n" << "\n" @@ -98,6 +98,8 @@ namespace { << "\treturn (v&0xFFFFFFFFFFFFFFFF == 0 ? __builtin_ctz64(v>>64) + 64 : __builtin_ctz64(v));\n" << "}\n" << "\n" + << "static inline void noop_drop(void *p) {}\n" + << "\n" ; } @@ -311,6 +313,21 @@ namespace { } m_of << "} "; emit_ctype(ty); m_of << ";\n"; } + + auto drop_glue_path = ::HIR::Path(ty.clone(), "#drop_glue"); + auto args = ::std::vector< ::std::pair<::HIR::Pattern,::HIR::TypeRef> >(); + auto ty_ptr = ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Owned, ty.clone()); + ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), ty_ptr, args, *(::MIR::Function*)nullptr }; + m_mir_res = &mir_res; + m_of << "void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(ty); m_of << "* rv) {"; + auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); + auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); + for(const auto& ity : te) + { + emit_destructor_call(fld_lv, ity, /*unsized_valid=*/false); + fld_lv.as_Field().field_index ++; + } + m_of << "}\n"; ) else TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Function, te, emit_type_fn(ty); @@ -407,7 +424,7 @@ namespace { ::std::vector< ::std::pair<::HIR::Pattern,::HIR::TypeRef> > args; if( item.m_markings.has_drop_impl ) { - m_of << "tUNIT " << Trans_Mangle( ::HIR::Path(struct_ty.clone(), m_resolve.m_lang_Drop, "drop") ) << "(struct s_" << Trans_Mangle(p) << "*rv);\n"; + m_of << "tUNIT " << Trans_Mangle( ::HIR::Path(struct_ty.clone(), m_resolve.m_lang_Drop, "drop") ) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ");\n"; } else if( m_resolve.is_type_owned_box(struct_ty) ) { @@ -1179,7 +1196,14 @@ namespace { m_of << "\t{ "; m_of << "sizeof("; emit_ctype(type); m_of << "),"; m_of << "__alignof__("; emit_ctype(type); m_of << "),"; - // TODO: Drop glue pointer + if( type.m_data.is_Borrow() || m_resolve.type_is_copy(sp, type) ) + { + m_of << "noop_drop,"; + } + else + { + m_of << "(void*)" << Trans_Mangle(::HIR::Path(type.clone(), "#drop_glue")) << ","; + } m_of << "}"; // No newline, added below for(unsigned int i = 0; i < trait.m_value_indexes.size(); i ++ ) @@ -2439,7 +2463,17 @@ namespace { make_fcn = "make_sliceptr"; if(0) case MetadataType::TraitObject: make_fcn = "make_traitobjptr"; - m_of << "\t" << Trans_Mangle(p) << "( " << make_fcn << "(&"; emit_lvalue(slot); m_of << ", "; + m_of << "\t" << Trans_Mangle(p) << "( " << make_fcn << "("; + if( slot.is_Deref() ) + { + emit_lvalue(*slot.as_Deref().val); + m_of << ".PTR"; + } + else + { + m_of << "&"; emit_lvalue(slot); + } + m_of << ", "; const auto* lvp = &slot; while(const auto* le = lvp->opt_Field()) lvp = &*le->val; MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); @@ -2474,13 +2508,31 @@ namespace { ), (TraitObject, MIR_ASSERT(*m_mir_res, unsized_valid, "Dropping TraitObject without a pointer"); - //MIR_ASSERT(*m_mir_res, slot.is_Deref(), "Dropping a TraitObject through a non-Deref"); // Call destructor in vtable + const auto* lvp = &slot; + while(const auto* le = lvp->opt_Field()) lvp = &*le->val; + MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); + m_of << "((VTABLE_HDR*)"; emit_lvalue(*lvp->as_Deref().val); m_of << ".META)->drop("; + if( const auto* ve = slot.opt_Deref() ) + { + emit_lvalue(*ve->val); m_of << ".PTR"; + } + else + { + m_of << "&"; emit_lvalue(slot); + } + m_of << ");"; ), (Slice, MIR_ASSERT(*m_mir_res, unsized_valid, "Dropping Slice without a pointer"); - //MIR_ASSERT(*m_mir_res, slot.is_Deref(), "Dropping a slice through a non-Deref"); + const auto* lvp = &slot; + while(const auto* le = lvp->opt_Field()) lvp = &*le->val; + MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); // Call destructor on all entries + m_of << "for(unsigned i = 0; i < "; emit_lvalue(*lvp->as_Deref().val); m_of << ".META; i++) {"; + m_of << "\t\t"; + emit_destructor_call(::MIR::LValue::make_Index({ box$(slot.clone()), box$(::MIR::LValue::make_Temporary({~0u})) }), *te.inner, false); + m_of << "\n\t}"; ) ) } @@ -2680,7 +2732,10 @@ namespace { m_of << "var" << e; ), (Temporary, - m_of << "tmp" << e.idx; + if( e.idx == ~0u ) + m_of << "i"; + else + m_of << "tmp" << e.idx; ), (Argument, m_of << "arg" << e.idx; @@ -2733,11 +2788,11 @@ namespace { ::HIR::TypeRef tmp; const auto& ty = m_mir_res->get_lvalue_type(tmp, val); auto dst_type = metadata_type(ty); - if( dst_type != MetadataType:: None ) + if( dst_type != MetadataType::None ) { m_of << "(*("; emit_ctype(ty); m_of << "*)"; emit_lvalue(*e.val); - m_of << ")"; + m_of << ".PTR)"; } else { diff --git a/src/trans/enumerate.cpp b/src/trans/enumerate.cpp index 950d7910..481b275b 100644 --- a/src/trans/enumerate.cpp +++ b/src/trans/enumerate.cpp @@ -990,6 +990,7 @@ void Trans_Enumerate_Types(EnumState& state) tv.m_resolve.expand_associated_types( sp, vtable_params.m_types[idx] ); } + tv.visit_type( *ent.first.m_data.as_UfcsKnown().type ); tv.visit_type( ::HIR::TypeRef( ::HIR::GenericPath(vtable_ty_spath, mv$(vtable_params)), &vtable_ref ) ); } -- cgit v1.2.3 From d6d0092548f27df7030445076d35f742f29f99c6 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Mar 2017 18:20:02 +0800 Subject: Makefile - Triage pass on tests again --- Makefile | 72 ++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/Makefile b/Makefile index 6d837203..f18b0778 100644 --- a/Makefile +++ b/Makefile @@ -336,7 +336,7 @@ DISABLED_TESTS += run-pass/abi-sysv64-register-usage DISABLED_TESTS += run-pass/asm-in-out-operand DISABLED_TESTS += run-pass/asm-indirect-memory DISABLED_TESTS += run-pass/asm-out-assign -DISABLED_TESTS += run-pass/i128 +DISABLED_TESTS += run-pass/i128 # Unknown leader 'r' DISABLED_TESTS += run-pass/issue-14936 DISABLED_TESTS += run-pass/issue-32947 DISABLED_TESTS += run-pass/num-wrapping @@ -433,23 +433,21 @@ DISABLED_TESTS += run-pass/dst-coercions DISABLED_TESTS += run-pass/dst-raw DISABLED_TESTS += run-pass/dst-trait # - Lazy (MIR) -DISABLED_TESTS += run-pass/if-ret -DISABLED_TESTS += run-pass/intrinsics-integer -DISABLED_TESTS += run-pass/issue-11940 -DISABLED_TESTS += run-pass/issue-13620 -DISABLED_TESTS += run-pass/issue-13867 -DISABLED_TESTS += run-pass/issue-15080 -DISABLED_TESTS += run-pass/issue-15104 -DISABLED_TESTS += run-pass/issue-15763 +DISABLED_TESTS += run-pass/if-ret # If condition wasn't a bool +DISABLED_TESTS += run-pass/intrinsics-integer # todo - bswap +DISABLED_TESTS += run-pass/issue-11940 # todo: Match literal Borrow +DISABLED_TESTS += run-pass/issue-13620 # - Todo in cleanup DISABLED_TESTS += run-pass/issue-17877 # - SplitSlice + array DISABLED_TESTS += run-pass/vec-matching-fixed # ^ -DISABLED_TESTS += run-pass/issue-37598 # - SplitSlice in DTN +DISABLED_TESTS += run-pass/issue-15104 # - SplitSlice in DTN +DISABLED_TESTS += run-pass/issue-15080 # ^ +DISABLED_TESTS += run-pass/issue-37598 # ^ DISABLED_TESTS += run-pass/vec-matching-fold # ^ DISABLED_TESTS += run-pass/vec-matching-legal-tail-element-borrow # ^ DISABLED_TESTS += run-pass/vec-tail-matching # SplitSlice destructure array DISABLED_TESTS += run-pass/zero_sized_subslice_match # ^ DISABLED_TESTS += run-pass/issue-18060 # - Overlapping value ranges -DISABLED_TESTS += run-pass/issue-18110 # - Missing value +DISABLED_TESTS += run-pass/issue-13867 # ^ DISABLED_TESTS += run-pass/issue-18352 # - Match+const DISABLED_TESTS += run-pass/issue-28839 # - Move &mut ? DISABLED_TESTS += run-pass/union/union-inherent-method # ^ ? @@ -457,16 +455,16 @@ DISABLED_TESTS += run-pass/issue-21306 # ^ DISABLED_TESTS += run-pass/issue-28950 # - Stack overflow in vec! DISABLED_TESTS += run-pass/mir_heavy_promoted # Stack overflow in array constant DISABLED_TESTS += run-pass/issue-29227 # - Excessive time in MIR lowering -DISABLED_TESTS += run-pass/issue-30018-nopanic # Missing value +DISABLED_TESTS += run-pass/issue-15763 # No value avaliable +DISABLED_TESTS += run-pass/issue-18110 # ^ +DISABLED_TESTS += run-pass/issue-30018-nopanic # ^ DISABLED_TESTS += run-pass/match-bot-2 # ^ DISABLED_TESTS += run-pass/unreachable-code # ^ DISABLED_TESTS += run-pass/issue-36936 # - Cast removed -DISABLED_TESTS += run-pass/issue-4734 # Destructor on unused rvalue -DISABLED_TESTS += run-pass/issue-8860 # No argument drop DISABLED_TESTS += run-pass/mir_build_match_comparisons # todo in match -DISABLED_TESTS += run-pass/struct-order-of-eval-1 # Struct init order +DISABLED_TESTS += run-pass/struct-order-of-eval-1 # Struct init order (fails validation) DISABLED_TESTS += run-pass/struct-order-of-eval-3 # ^ -DISABLED_TESTS += run-pass/union/union-drop-assign # No drop when assiging to union field +DISABLED_TESTS += run-pass/const-enum-vec-index # This is valid code? # - Lazy (trans) DISABLED_TESTS += run-pass/issue-21058 # Empty trait object vtable DISABLED_TESTS += run-pass/issue-25515 # ^ @@ -478,31 +476,20 @@ DISABLED_TESTS += run-pass/transmute-specialization # Opaque type hit? DISABLED_TESTS += run-pass/unit-fallback # ! didn't default to () # - HIR resolve DISABLED_TESTS += run-pass/union/union-generic # Can't find associated type on type param -# - Trans: No handling of repr() -DISABLED_TESTS += run-pass/packed-struct-generic-layout -DISABLED_TESTS += run-pass/packed-struct-generic-size -DISABLED_TESTS += run-pass/packed-struct-layout -DISABLED_TESTS += run-pass/packed-struct-size-xc -DISABLED_TESTS += run-pass/packed-struct-size -DISABLED_TESTS += run-pass/packed-struct-vec -DISABLED_TESTS += run-pass/packed-tuple-struct-layout -DISABLED_TESTS += run-pass/packed-tuple-struct-size # - Lazy (misc) DISABLED_TESTS += run-pass/issue-13494 DISABLED_TESTS += run-pass/issue-6919 # Literal function pointer DISABLED_TESTS += run-pass/item-attributes # Attributed function after last statement leads to last statement yielded DISABLED_TESTS += run-pass/new-box-syntax # todo - placement syntax DISABLED_TESTS += run-pass/placement-in-syntax # ^ -DISABLED_TESTS += run-pass/pat-tuple-1 # assertion in AVU +DISABLED_TESTS += run-pass/pat-tuple-1 # assertion in "Annotate Value Usage" DISABLED_TESTS += run-pass/pat-tuple-2 # ^ DISABLED_TESTS += run-pass/pat-tuple-3 # ^ DISABLED_TESTS += run-pass/pat-tuple-4 # ^ DISABLED_TESTS += run-pass/paths-in-macro-invocations # MISSING: qualified macro paths -DISABLED_TESTS += run-pass/process-spawn-with-unicode-params DISABLED_TESTS += run-pass/struct-path-associated-type # non-absolute path for HIR::GenericPath DISABLED_TESTS += run-pass/struct-path-self # ^ DISABLED_TESTS += run-pass/ufcs-polymorphic-paths # ^ -DISABLED_TESTS += run-pass/u128 # u128 not very good, unknown where error is # - Resolve DISABLED_TESTS += run-pass/issue-22546 # None:: handling in patterns DISABLED_TESTS += run-pass/issue-29540 # Infinite recursion @@ -580,7 +567,7 @@ DISABLED_TESTS += run-pass/stmt_expr_attr_macro_parse # Orderign with :expr and DISABLED_TESTS += run-pass/sync-send-iterators-in-libcollections # .. should match :expr DISABLED_TESTS += run-pass/type-macros-hlist # Mismatched arms # - BUG-Expand: format_args! -DISABLED_TESTS += run-pass/ifmt +DISABLED_TESTS += run-pass/ifmt # Unknown formatting type specifier '*' # - BUG-Expand: line/column macros don't work properly DISABLED_TESTS += run-pass/issue-26322 # - Expand @@ -644,8 +631,6 @@ DISABLED_TESTS += run-pass/hygiene DISABLED_TESTS += run-pass/hygienic-labels-in-let DISABLED_TESTS += run-pass/hygienic-labels DISABLED_TESTS += run-pass/macro-nested_stmt_macros # hygine fires when it shouldn't -# - ?? Is this valid -DISABLED_TESTS += run-pass/const-enum-vec-index # - Test framework required DISABLED_TESTS += run-pass/core-run-destroy DISABLED_TESTS += run-pass/exec-env @@ -680,7 +665,7 @@ DISABLED_TESTS += run-pass/sse2 DISABLED_TESTS += run-pass/issue-27890 # - Stack exhausted : Resolve DISABLED_TESTS += run-pass/project-cache-issue-31849 # - Impl selection -DISABLED_TESTS += run-pass/issue-23208 +DISABLED_TESTS += run-pass/issue-23208 # Couldn't find an impl for >::get DISABLED_TESTS += run-pass/xcrate-associated-type-defaults # Failed to find an impl # --- Runtime Errors --- # - Line information that isn't avaliable due to codegen @@ -717,6 +702,7 @@ DISABLED_TESTS += run-pass/unit-like-struct-drop-run DISABLED_TESTS += run-pass/unwind-resource DISABLED_TESTS += run-pass/unwind-unique DISABLED_TESTS += run-pass/vector-sort-panic-safe +DISABLED_TESTS += run-pass/dynamic-drop # - Misc DISABLED_TESTS += run-pass/issue-16671 # Blocks forever # - BUG: Incorrect drop order of ? @@ -755,24 +741,34 @@ DISABLED_TESTS += run-pass/small-enums-with-fields DISABLED_TESTS += run-pass/type-sizes DISABLED_TESTS += run-pass/discrim-explicit-23030 DISABLED_TESTS += run-pass/issue-13902 -# - BUG: Leaking contents of boxed trait objects -DISABLED_TESTS += run-pass/drop-struct-as-object -DISABLED_TESTS += run-pass/dynamic-drop -DISABLED_TESTS += run-pass/issue-10802 -DISABLED_TESTS += run-pass/extern_fat_drop -DISABLED_TESTS += run-pass/mir_fat_ptr_drop # fat Box doesn't run inner destructor # - BUG: Bad floats DISABLED_TESTS += run-pass/float-nan DISABLED_TESTS += run-pass/float_math DISABLED_TESTS += run-pass/floatlits DISABLED_TESTS += run-pass/intrinsics-math +# - BUG: MIR Generation +DISABLED_TESTS += run-pass/union/union-drop-assign # No drop when assiging to union field +DISABLED_TESTS += run-pass/issue-4734 # Destructor on unused rvalue +DISABLED_TESTS += run-pass/issue-8860 # No drop of un-moved arguments # - BUG: Codegen DISABLED_TESTS += run-pass/union/union-transmute # Incorrect union behavior, likey backend UB DISABLED_TESTS += run-pass/mir_overflow_off # out-of-range shift behavior +# - BUG: Codegen - No handling of repr() +DISABLED_TESTS += run-pass/packed-struct-generic-layout +DISABLED_TESTS += run-pass/packed-struct-generic-size +DISABLED_TESTS += run-pass/packed-struct-layout +DISABLED_TESTS += run-pass/packed-struct-size-xc +DISABLED_TESTS += run-pass/packed-struct-size +DISABLED_TESTS += run-pass/packed-struct-vec +DISABLED_TESTS += run-pass/packed-tuple-struct-layout +DISABLED_TESTS += run-pass/packed-tuple-struct-size # - BUG-Expand: format_args! DISABLED_TESTS += run-pass/format-ref-cell # - BUG-Expand: Copy,Clone calls Clone for inner values instead of copying DISABLED_TESTS += run-pass/deriving-copyclone +# - BUG: Unknown +DISABLED_TESTS += run-pass/process-spawn-with-unicode-params # Bad path for process spawn +DISABLED_TESTS += run-pass/u128 # u128 not very good, unknown where error is DEF_RUST_TESTS = $(sort $(patsubst $(RUST_TESTS_DIR)%.rs,output/rust/%_out.txt,$(wildcard $(RUST_TESTS_DIR)$1/*.rs))) rust_tests-run-pass: $(filter-out $(patsubst %,output/rust/%_out.txt,$(DISABLED_TESTS)), $(call DEF_RUST_TESTS,run-pass) $(call DEF_RUST_TESTS,run-pass/union)) -- cgit v1.2.3 From 8768dd08c6afaee82eb8bd7f90ebb5eb1a8a9833 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 29 Mar 2017 14:47:15 +0200 Subject: Correctly indent lists for github to pick it up --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b4704238..bcb93b8b 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ Current Features - Resolves all paths to absolute forms - Converts name-resolved AST into a more compact "HIR" (simplified module and expression tree) - Hackily evaluates constants - - Constant evaluation is done by using duck-typing, which is then validated by the Type Check pass - - This is how rustc did (or still does?) const eval before MIR + - Constant evaluation is done by using duck-typing, which is then validated by the Type Check pass + - This is how rustc did (or still does?) const eval before MIR - Type inference and checking - Closure and operator desugaring - MIR generation (with partial validation pass) @@ -39,5 +39,5 @@ Progress - Compiles the standard library into loadable MIR - Compiles the "hello, world" test into compilable and running C code - Compiles `rustc` - - Generated code is likely not correct + - Generated code is likely not correct -- cgit v1.2.3 From 13a23184046cbd2d782e492f3d349c930d4264ea Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 30 Mar 2017 08:48:04 +0800 Subject: MIR Gen Match - Disable DTN algorithm due to minor bug --- src/mir/from_hir_match.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index b098f900..a0747498 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -375,7 +375,7 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod // - A way would be to search for `_` rules with non _ rules following. Would false-positive in some cases, but shouldn't false negative // TODO: Merge equal rulesets if there's one with no condition. - if( fall_back_on_simple ) { + if( true || fall_back_on_simple ) { MIR_LowerHIR_Match_Simple( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); } else { @@ -1649,7 +1649,38 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& break; case ::HIR::CoreType::F32: case ::HIR::CoreType::F64: - TODO(sp, "Simple match over float - " << ty); + TU_MATCH_DEF( PatternRule, (rule), (re), + ( + BUG(sp, "PatternRule for float is not Value or ValueRange"); + ), + (Value, + auto succ_bb = builder.new_bb_unlinked(); + + auto test_val = ::MIR::Param(::MIR::Constant::make_Float({ re.as_Float().v, te })); + auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, mv$(test_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval), succ_bb, fail_bb }) ); + builder.set_cur_block(succ_bb); + ), + (ValueRange, + auto succ_bb = builder.new_bb_unlinked(); + auto test_bb_2 = builder.new_bb_unlinked(); + + auto test_lt_val = ::MIR::Param(::MIR::Constant::make_Float({ re.first.as_Float().v, te })); + auto test_gt_val = ::MIR::Param(::MIR::Constant::make_Float({ re.last.as_Float().v, te })); + + // IF `val` < `first` : fail_bb + auto cmp_lt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::LT, mv$(test_lt_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lt_lval), fail_bb, test_bb_2 }) ); + + builder.set_cur_block(test_bb_2); + + // IF `val` > `last` : fail_bb + auto cmp_gt_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::GT, mv$(test_gt_val) })); + builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_gt_lval), fail_bb, succ_bb }) ); + + builder.set_cur_block(succ_bb); + ) + ) break; case ::HIR::CoreType::Str: { ASSERT_BUG(sp, rule.is_Value() && rule.as_Value().is_StaticString(), ""); @@ -1690,7 +1721,11 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& TODO(sp, "Match over Union"); ), (Enum, - auto monomorph = [&](const auto& ty) { return monomorphise_type(sp, pbe->m_params, te.path.m_data.as_Generic().m_params, ty); }; + auto monomorph = [&](const auto& ty) { + auto rv = monomorphise_type(sp, pbe->m_params, te.path.m_data.as_Generic().m_params, ty); + builder.resolve().expand_associated_types(sp, rv); + return rv; + }; ASSERT_BUG(sp, rule.is_Variant(), "Rule for enum isn't Any or Variant"); const auto& re = rule.as_Variant(); unsigned int var_idx = re.idx; -- cgit v1.2.3 From a3a29169ca415fb6a9a9f6c294572e8d235d78b5 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 7 Apr 2017 11:52:53 +0800 Subject: Match - Replace DecisionTree with a sort+group algorithm --- Notes/MIR-Match.md | 19 + src/common.hpp | 58 +- src/mir/from_hir_match.cpp | 1390 +++++++++++++++++++++++++++++++++++++++++++- src/mir/mir.cpp | 39 +- src/mir/mir.hpp | 11 +- 5 files changed, 1474 insertions(+), 43 deletions(-) diff --git a/Notes/MIR-Match.md b/Notes/MIR-Match.md index f47d2c88..cc95522c 100644 --- a/Notes/MIR-Match.md +++ b/Notes/MIR-Match.md @@ -20,3 +20,22 @@ For each index in the rule (all rules must be the same length) - Ranges sort after? + +Alternative Generator 2 +======================= + +Maintains match ordering + +1. Calculate branch rulesets (as existing) +1. While rules to process: + 1. Group based on shared values. + 1. Generate dispatch arm for each group + 1. Recurse into group, passing local _ as fallback (or parent _ if none) + +``` + +for + +``` + + diff --git a/src/common.hpp b/src/common.hpp index 0b0fad14..63125b0e 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -68,24 +68,56 @@ static inline Ordering ord(bool l, bool r) else return OrdLess; } +static inline Ordering ord(char l, char r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} + +static inline Ordering ord(unsigned char l, unsigned char r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(unsigned short l, unsigned short r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} static inline Ordering ord(unsigned l, unsigned r) { - if(l == r) - return OrdEqual; - else if( l > r ) - return OrdGreater; - else - return OrdLess; + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); } -static inline Ordering ord(::std::uintptr_t l, ::std::uintptr_t r) +static inline Ordering ord(unsigned long l, unsigned long r) { - if(l == r) - return OrdEqual; - else if( l > r ) - return OrdGreater; - else - return OrdLess; + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); } +static inline Ordering ord(unsigned long long l, unsigned long long r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(signed char l, signed char r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(short l, short r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(long l, long r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(long long l, long long r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(float l, float r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} +static inline Ordering ord(double l, double r) +{ + return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess)); +} + static inline Ordering ord(const ::std::string& l, const ::std::string& r) { if(l == r) diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index a0747498..584e7dc0 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -33,8 +33,6 @@ struct field_path_t }; TAGGED_UNION_EX(PatternRule, (), Any,( - // _ pattern - (Any, struct {}), // Enum variant (Variant, struct { unsigned int idx; ::std::vector sub_rules; }), // Slice (includes desired length) @@ -46,11 +44,24 @@ TAGGED_UNION_EX(PatternRule, (), Any,( (Bool, bool), // General value (Value, ::MIR::Constant), - (ValueRange, struct { ::MIR::Constant first, last; }) + (ValueRange, struct { ::MIR::Constant first, last; }), + // _ pattern + (Any, struct {}) ), ( , field_path(mv$(x.field_path)) ), (field_path = mv$(x.field_path);), ( field_path_t field_path; + + bool operator<(const PatternRule& x) const { + return this->ord(x) == OrdLess; + } + bool operator==(const PatternRule& x) const { + return this->ord(x) == OrdEqual; + } + bool operator!=(const PatternRule& x) const { + return this->ord(x) != OrdEqual; + } + Ordering ord(const PatternRule& x) const; ) ); ::std::ostream& operator<<(::std::ostream& os, const PatternRule& x); @@ -74,11 +85,14 @@ struct ArmCode { ::MIR::BasicBlockId cond_end; ::MIR::LValue cond_lval; ::std::vector< ::MIR::BasicBlockId> destructures; // NOTE: Incomplete + + mutable ::MIR::BasicBlockId cond_fail_tgt = 0; }; typedef ::std::vector t_arm_rules; void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector arm_code, ::MIR::BasicBlockId first_cmp_block); +void MIR_LowerHIR_Match_Grouped( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector arms_code, ::MIR::BasicBlockId first_cmp_block ); void MIR_LowerHIR_Match_DecisionTree( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector arm_code , ::MIR::BasicBlockId first_cmp_block); /// Helper to construct rules from a passed pattern struct PatternRulesetBuilder @@ -103,6 +117,82 @@ struct PatternRulesetBuilder void push_rule(PatternRule r); }; +class RulesetRef +{ + ::std::vector* m_rules_vec = nullptr; + RulesetRef* m_parent = nullptr; + size_t m_parent_ofs=0; // If len == 0, this is the innner index, else it's the base + size_t m_parent_len=0; +public: + RulesetRef(::std::vector& rules): + m_rules_vec(&rules) + { + } + RulesetRef(RulesetRef& parent, size_t start, size_t n): + m_parent(&parent), + m_parent_ofs(start), + m_parent_len(n) + { + } + RulesetRef(RulesetRef& parent, size_t idx): + m_parent(&parent), + m_parent_ofs(idx) + { + } + + size_t size() const { + if( m_rules_vec ) { + return m_rules_vec->size(); + } + else if( m_parent_len ) { + return m_parent_len; + } + else { + return m_parent->size(); + } + } + RulesetRef slice(size_t s, size_t n) { + return RulesetRef(*this, s, n); + } + + const ::std::vector& operator[](size_t i) const { + if( m_rules_vec ) { + return (*m_rules_vec)[i].m_rules; + } + else if( m_parent_len ) { + return (*m_parent)[m_parent_ofs + i]; + } + else { + // Fun part - Indexes into inner patterns + const auto& parent_rule = (*m_parent)[i][m_parent_ofs]; + if(const auto* re = parent_rule.opt_Variant()) { + return re->sub_rules; + } + else { + throw "TODO"; + } + } + } + void swap(size_t a, size_t b) { + TRACE_FUNCTION_F(a << ", " << b); + if( m_rules_vec ) { + ::std::swap( (*m_rules_vec)[a], (*m_rules_vec)[b] ); + } + else { + assert(m_parent); + if( m_parent_len ) { + m_parent->swap(m_parent_ofs + a, m_parent_ofs + b); + } + else { + m_parent->swap(a, b); + } + } + } +}; + +void sort_rulesets(RulesetRef rulesets, size_t idx=0); +void sort_rulesets_inner(RulesetRef rulesets, size_t idx); + // -------------------------------------------------------------------- // CODE // -------------------------------------------------------------------- @@ -366,20 +456,54 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod for(const auto& arm_rule : arm_rules) { - DEBUG("> (" << arm_rule.arm_idx << ", " << arm_rule.pat_idx << ") - " << arm_rule.m_rules); + DEBUG("> (" << arm_rule.arm_idx << ", " << arm_rule.pat_idx << ") - " << arm_rule.m_rules + << (arm_code[arm_rule.arm_idx].has_condition ? " (cond)" : "")); } - // TODO: Don't generate inner code until decisions are generated (keeps MIR flow nice) + // TODO: Remove columns that are all `_`? + // - Ideally, only accessible structures would be fully destructured like this. + + // Sort rules using the following restrictions: + // - A rule cannot be reordered across an item that has an overlapping match set + // > e.g. nothing can cross _ + // > equal rules cannot be reordered + // > Values cannot cross ranges that contain the value + // > This will have to be a bubble sort to ensure that it's correctly stable. + sort_rulesets(arm_rules); + DEBUG("Post-sort"); + for(const auto& arm_rule : arm_rules) + { + DEBUG("> (" << arm_rule.arm_idx << ", " << arm_rule.pat_idx << ") - " << arm_rule.m_rules + << (arm_code[arm_rule.arm_idx].has_condition ? " (cond)" : "")); + } + // De-duplicate arms (emitting a warning when it happens) + // - This allows later code to assume that duplicate arms are a codegen bug. + if( ! arm_rules.empty() ) + { + for(auto it = arm_rules.begin()+1; it != arm_rules.end(); ) + { + // If duplicate rule, (and neither is conditional) + if( (it-1)->m_rules == it->m_rules && !arm_code[it->arm_idx].has_condition && !arm_code[(it-1)->arm_idx].has_condition ) + { + // Remove + it = arm_rules.erase(it); + WARNING(node.m_arms[it->arm_idx].m_code->span(), W0000, "Duplicate match pattern, unreachable code"); + } + else + { + ++ it; + } + } + } - // TODO: Detect if a rule is ordering-dependent. In this case we currently have to fall back on the simple match code - // - A way would be to search for `_` rules with non _ rules following. Would false-positive in some cases, but shouldn't false negative - // TODO: Merge equal rulesets if there's one with no condition. + // TODO: Don't generate inner code until decisions are generated (keeps MIR flow nice) - if( true || fall_back_on_simple ) { + if( fall_back_on_simple ) { MIR_LowerHIR_Match_Simple( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); } else { - MIR_LowerHIR_Match_DecisionTree( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); + MIR_LowerHIR_Match_Grouped( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); + //MIR_LowerHIR_Match_DecisionTree( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); } builder.set_cur_block( next_block ); @@ -424,6 +548,56 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod return os; } +::Ordering PatternRule::ord(const PatternRule& x) const +{ + if(tag() != x.tag()) + { + return tag() < x.tag() ? ::OrdLess : ::OrdGreater; + } + TU_MATCHA( (*this, x), (te, xe), + (Any, return OrdEqual;), + (Variant, + if(te.idx != xe.idx) return ::ord(te.idx, xe.idx); + assert( te.sub_rules.size() == xe.sub_rules.size() ); + for(unsigned int i = 0; i < te.sub_rules.size(); i ++) + { + auto cmp = te.sub_rules[i].ord( xe.sub_rules[i] ); + if( cmp != ::OrdEqual ) + return cmp; + } + return ::OrdEqual; + ), + (Slice, + if(te.len != xe.len) return ::ord(te.len, xe.len); + // Wait? Why would the rule count be the same? + assert( te.sub_rules.size() == xe.sub_rules.size() ); + for(unsigned int i = 0; i < te.sub_rules.size(); i ++) + { + auto cmp = te.sub_rules[i].ord( xe.sub_rules[i] ); + if( cmp != ::OrdEqual ) + return cmp; + } + return ::OrdEqual; + ), + (SplitSlice, + auto rv = ::ord( te.leading, xe.leading ); + if(rv != OrdEqual) return rv; + return ::ord(te.trailing, xe.trailing); + ), + (Bool, + return ::ord( te, xe ); + ), + (Value, + return ::ord( te, xe ); + ), + (ValueRange, + if( te.first != xe.first ) + return ::ord(te.first, xe.first); + return ::ord(te.last, xe.last); + ) + ) + throw ""; +} ::Ordering PatternRuleset::rule_is_before(const PatternRule& l, const PatternRule& r) { if( l.tag() != r.tag() ) { @@ -1280,6 +1454,227 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa ) } +namespace { + // Order rules ignoring inner rules + Ordering ord_rule_compatible(const PatternRule& a, const PatternRule& b) + { + if(a.tag() != b.tag()) + return ::ord( (unsigned)a.tag(), b.tag() ); + + TU_MATCHA( (a, b), (ae, be), + (Any, + return OrdEqual; + ), + (Variant, + return ::ord(ae.idx, be.idx); + ), + (Slice, + return ::ord(ae.len, be.len); + ), + (SplitSlice, + auto v = ::ord(ae.leading.size(), be.leading.size()); + if(v != OrdEqual) return v; + v = ::ord(ae.trailing.size(), be.trailing.size()); + if(v != OrdEqual) return v; + return OrdEqual; + ), + (Bool, + return ::ord(ae, be); + ), + (Value, + return ::ord(ae, be); + ), + (ValueRange, + auto v = ::ord(ae.first, be.first); + if(v != OrdEqual) return v; + return ::ord(ae.last, be.last); + ) + ) + throw ""; + } + bool rule_compatible(const PatternRule& a, const PatternRule& b) + { + return ord_rule_compatible(a,b) == OrdEqual; + } + + bool rules_overlap(const PatternRule& a, const PatternRule& b) + { + if( a.is_Any() || b.is_Any() ) + return true; + + // Defensive: If a constant is encountered, assume it overlaps with anything + if(const auto* ae = a.opt_Value()) { + if(ae->is_Const()) + return true; + } + if(const auto* be = b.opt_Value()) { + if(be->is_Const()) + return true; + } + + // Value Range: Overlaps with contained values. + if(const auto* ae = a.opt_ValueRange() ) + { + if(const auto* be = b.opt_Value() ) + { + return ( ae->first <= *be && *be <= ae->last ); + } + else if( const auto* be = b.opt_ValueRange() ) + { + // Start of B within A + if( ae->first <= be->first && be->first <= ae->last ) + return true; + // End of B within A + if( ae->first <= be->last && be->last <= ae->last ) + return true; + // Start of A within B + if( be->first <= ae->first && ae->first <= be->last ) + return true; + // End of A within B + if( be->first <= ae->last && ae->last <= be->last ) + return true; + + // Disjoint + return false; + } + else + { + TODO(Span(), "Check overlap of " << a << " and " << b); + } + } + if(const auto* be = b.opt_ValueRange()) + { + if(const auto* ae = a.opt_Value() ) + { + return (be->first <= *ae && *ae <= be->last); + } + // Note: A can't be ValueRange + else + { + TODO(Span(), "Check overlap of " << a << " and " << b); + } + } + + // SplitSlice patterns overlap with other SplitSlice patterns and larger slices + if(const auto* ae = a.opt_SplitSlice()) + { + if( b.is_SplitSlice() ) + { + return true; + } + else if( const auto* be = b.opt_Slice() ) + { + return be->len >= ae->min_len; + } + else + { + TODO(Span(), "Check overlap of " << a << " and " << b); + } + } + if(const auto* be = b.opt_SplitSlice()) + { + if( const auto* ae = a.opt_Slice() ) + { + return ae->len >= be->min_len; + } + else + { + TODO(Span(), "Check overlap of " << a << " and " << b); + } + } + + // Otherwise, If rules are approximately equal, they overlap + return ( ord_rule_compatible(a, b) == OrdEqual ); + } +} +void sort_rulesets(RulesetRef rulesets, size_t idx) +{ + if(rulesets.size() < 2) + return ; + + bool found_non_any = false; + for(size_t i = 0; i < rulesets.size(); i ++) + if( !rulesets[i][idx].is_Any() ) + found_non_any = true; + if( found_non_any ) + { + TRACE_FUNCTION_F(idx); + for(size_t i = 0; i < rulesets.size(); i ++) + DEBUG("- " << i << ": " << rulesets[i]); + + bool action_taken; + do + { + action_taken = false; + for(size_t i = 0; i < rulesets.size()-1; i ++) + { + if( rules_overlap(rulesets[i][idx], rulesets[i+1][idx]) ) + { + // Don't move + } + else if( ord_rule_compatible(rulesets[i][idx], rulesets[i+1][idx]) == OrdGreater ) + { + rulesets.swap(i, i+1); + action_taken = true; + } + else + { + } + } + } while(action_taken); + for(size_t i = 0; i < rulesets.size(); i ++) + DEBUG("- " << i << ": " << rulesets[i]); + + // TODO: Print sorted ruleset + + // Where compatible, sort insides + size_t start = 0; + for(size_t i = 1; i < rulesets.size(); i++) + { + if( ord_rule_compatible(rulesets[i][idx], rulesets[start][idx]) != OrdEqual ) + { + sort_rulesets_inner(rulesets.slice(start, i-start), idx); + start = i; + } + } + sort_rulesets_inner(rulesets.slice(start, rulesets.size()-start), idx); + + // Iterate onwards where rules are equal + if( idx + 1 < rulesets[0].size() ) + { + size_t start = 0; + for(size_t i = 1; i < rulesets.size(); i++) + { + if( rulesets[i][idx] != rulesets[start][idx] ) + { + sort_rulesets(rulesets.slice(start, i-start), idx+1); + start = i; + } + } + sort_rulesets(rulesets.slice(start, rulesets.size()-start), idx+1); + } + } + else + { + if( idx + 1 < rulesets[0].size() ) + { + sort_rulesets(rulesets, idx + 1); + } + } +} +void sort_rulesets_inner(RulesetRef rulesets, size_t idx) +{ + TRACE_FUNCTION_F(idx << " - " << rulesets[0][idx].tag_str()); + if( const auto* re = rulesets[0][idx].opt_Variant() ) + { + // Sort rules based on contents of enum + if( re->sub_rules.size() > 0 ) + { + sort_rulesets(RulesetRef(rulesets, idx), 0); + } + } +} + namespace { void get_ty_and_val( const Span& sp, const StaticTraitResolve& resolve, @@ -1437,7 +1832,6 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR:: TRACE_FUNCTION; // 1. Generate pattern matches - unsigned int rule_idx = 0; builder.set_cur_block( first_cmp_block ); for( unsigned int arm_idx = 0; arm_idx < node.m_arms.size(); arm_idx ++ ) { @@ -1451,6 +1845,10 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR:: if( arm_code.destructures[i] == 0 ) continue ; + size_t rule_idx = 0; + for(; rule_idx < arm_rules.size(); rule_idx++) + if( arm_rules[rule_idx].arm_idx == arm_idx && arm_rules[rule_idx].pat_idx == i ) + break; const auto& pat_rule = arm_rules[rule_idx]; bool is_last_pat = (i+1 == arm.m_patterns.size()); auto next_pattern_bb = (!is_last_pat ? builder.new_bb_unlinked() : next_arm_bb); @@ -1478,8 +1876,6 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR:: { builder.set_cur_block( next_pattern_bb ); } - - rule_idx ++; } if( arm_code.has_condition ) { @@ -1900,6 +2296,974 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& return 0; } +// -- +// Match v2 Algo - Grouped rules +// -- + + +class t_rules_subset +{ + ::std::vector*> rule_sets; + bool is_arm_indexes; + ::std::vector arm_idxes; +public: + t_rules_subset(size_t exp, bool is_arm_indexes): + is_arm_indexes(is_arm_indexes) + { + rule_sets.reserve(exp); + arm_idxes.reserve(exp); + } + + size_t size() const { + return rule_sets.size(); + } + const ::std::vector& operator[](size_t n) const { + return *rule_sets[n]; + } + bool is_arm() const { return is_arm_indexes; } + ::std::pair arm_idx(size_t n) const { + assert(is_arm_indexes); + auto v = arm_idxes.at(n); + return ::std::make_pair(v & 0xFFF, v >> 12); + } + ::MIR::BasicBlockId bb_idx(size_t n) const { + assert(!is_arm_indexes); + return arm_idxes.at(n); + } + + void sub_sort(size_t ofs, size_t start, size_t n) + { + ::std::vector v; + for(size_t i = 0; i < n; i++) + v.push_back(start + i); + // Sort rules based on just the value (ignore inner rules) + ::std::stable_sort( v.begin(), v.end(), [&](auto a, auto b){ return ord_rule_compatible( (*rule_sets[a])[ofs], (*rule_sets[b])[ofs]) == OrdLess; } ); + + // Reorder contents to above sorting + { + decltype(this->rule_sets) tmp; + for(auto i : v) + tmp.push_back(rule_sets[i]); + ::std::copy( tmp.begin(), tmp.end(), rule_sets.begin() + start ); + } + { + decltype(this->arm_idxes) tmp; + for(auto i : v) + tmp.push_back(arm_idxes[i]); + ::std::copy( tmp.begin(), tmp.end(), arm_idxes.begin() + start ); + } + } + + t_rules_subset sub_slice(size_t ofs, size_t n) + { + t_rules_subset rv { n, this->is_arm_indexes }; + rv.rule_sets.reserve(n); + for(size_t i = 0; i < n; i++) + { + rv.rule_sets.push_back( this->rule_sets[ofs+i] ); + rv.arm_idxes.push_back( this->arm_idxes[ofs+i] ); + } + return rv; + } + void push_arm(const ::std::vector& x, size_t arm_idx, size_t pat_idx) + { + assert(is_arm_indexes); + rule_sets.push_back(&x); + assert(arm_idx <= 0xFFF); + assert(pat_idx <= 0xFFF); + arm_idxes.push_back(arm_idx | (pat_idx << 12)); + } + void push_bb(const ::std::vector& x, ::MIR::BasicBlockId bb) + { + assert(!is_arm_indexes); + rule_sets.push_back(&x); + arm_idxes.push_back(bb); + } + + friend ::std::ostream& operator<<(::std::ostream& os, const t_rules_subset& x) { + os << "t_rules_subset{"; + for(size_t i = 0; i < x.rule_sets.size(); i ++) + { + if(i != 0) + os << ", "; + os << "["; + if(x.is_arm_indexes) + { + os << (x.arm_idxes[i] & 0xFFF) << "," << (x.arm_idxes[i] >> 12); + } + else + { + os << "bb" << x.arm_idxes[i]; + } + os << "]"; + os << ": " << *x.rule_sets[i]; + } + os << "}"; + return os; + } +}; + +class MatchGenGrouped +{ + const Span& sp; + MirBuilder& m_builder; + const ::HIR::TypeRef& m_top_ty; + const ::MIR::LValue& m_top_val; + const ::std::vector& m_arms_code; + + size_t m_field_path_ofs; +public: + MatchGenGrouped(MirBuilder& builder, const Span& sp, const ::HIR::TypeRef& top_ty, const ::MIR::LValue& top_val, const ::std::vector& arms_code, size_t field_path_ofs): + sp(sp), + m_builder(builder), + m_top_ty(top_ty), + m_top_val(top_val), + m_arms_code(arms_code), + m_field_path_ofs(field_path_ofs) + { + } + + void gen_for_slice(t_rules_subset rules, size_t ofs, ::MIR::BasicBlockId default_arm); + void gen_dispatch(const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk); + void gen_dispatch__primitive(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk); + void gen_dispatch__enum(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk); + void gen_dispatch__slice(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk); + + void gen_dispatch_range(const field_path_t& field_path, const ::MIR::Constant& first, const ::MIR::Constant& last, ::MIR::BasicBlockId def_blk); + void gen_dispatch_splitslice(const field_path_t& field_path, const PatternRule::Data_SplitSlice& e, ::MIR::BasicBlockId def_blk); + + ::MIR::LValue push_compare(::MIR::LValue left, ::MIR::eBinOp op, ::MIR::Param right) + { + return m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, + ::MIR::RValue::make_BinOp({ mv$(left), op, mv$(right) }) + ); + } +}; + +void MIR_LowerHIR_Match_Grouped( + MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, + t_arm_rules arm_rules, ::std::vector arms_code, ::MIR::BasicBlockId first_cmp_block + ) +{ + TRACE_FUNCTION_F(""); + + // - Create a "slice" of the passed rules, suitable for passing to the recursive part of the algo + t_rules_subset rules { arm_rules.size(), /*is_arm_indexes=*/true }; + for(const auto& r : arm_rules) + { + rules.push_arm( r.m_rules, r.arm_idx, r.pat_idx ); + } + + auto inst = MatchGenGrouped { builder, node.span(), node.m_value->m_res_type, match_val, arms_code, 0 }; + + // NOTE: This block should never be used + auto default_arm = builder.new_bb_unlinked(); + + builder.set_cur_block( first_cmp_block ); + inst.gen_for_slice( mv$(rules), 0, default_arm ); + + // Make the default infinite loop. + // - Preferably, it'd abort. + builder.set_cur_block(default_arm); + builder.end_block( ::MIR::Terminator::make_Goto(default_arm) ); +} +void MatchGenGrouped::gen_for_slice(t_rules_subset arm_rules, size_t ofs, ::MIR::BasicBlockId default_arm) +{ + TRACE_FUNCTION_F("arm_rules=" << arm_rules << ", ofs="< 0, ""); + + // Quick hack: Skip any layers entirely made up of PatternRule::Any + for(;;) + { + bool is_all_any = true; + for(size_t i = 0; i < arm_rules.size() && is_all_any; i ++) + { + if( arm_rules[i].size() <= ofs ) + is_all_any = false; + else if( ! arm_rules[i][ofs].is_Any() ) + is_all_any = false; + } + if( ! is_all_any ) + { + break ; + } + ofs ++; + DEBUG("Skip to ofs=" << ofs); + } + + // Split current set of rules into groups based on _ patterns + for(size_t idx = 0; idx < arm_rules.size(); ) + { + // Completed arms + while( idx < arm_rules.size() && arm_rules[idx].size() <= ofs ) + { + auto next = idx+1 == arm_rules.size() ? default_arm : m_builder.new_bb_unlinked(); + ASSERT_BUG(sp, arm_rules[idx].size() == ofs, "Offset too large for rule - ofs=" << ofs << ", rules=" << arm_rules[idx]); + DEBUG(idx << ": Complete"); + // Emit jump to either arm code, or arm condition + if( arm_rules.is_arm() ) + { + auto ai = arm_rules.arm_idx(idx); + ASSERT_BUG(sp, m_arms_code.size() > 0, "Bottom-level ruleset with no arm code information"); + const auto& ac = m_arms_code[ai.first]; + + m_builder.end_block( ::MIR::Terminator::make_Goto(ac.destructures[ai.second]) ); + m_builder.set_cur_block( ac.destructures[ai.second] ); + + if( ac.has_condition ) + { + TODO(sp, "Handle conditionals in Grouped"); + // TODO: If the condition fails, this should re-try the match on other rules that could have worked. + // - For now, conditionals are disabled. + + // TODO: What if there's multiple patterns on this condition? + // - For now, only the first pattern gets edited. + // - Maybe clone the blocks used for the condition? + m_builder.end_block( ::MIR::Terminator::make_Goto(ac.cond_start) ); + + // Check for marking in `ac` that the block has already been terminated, assert that target is `next` + if( ai.second == 0 ) + { + if( ac.cond_fail_tgt != 0) + { + ASSERT_BUG(sp, ac.cond_fail_tgt == next, "Condition fail target already set with mismatching arm, set to bb" << ac.cond_fail_tgt << " cur is bb" << next); + } + else + { + ac.cond_fail_tgt = next; + + m_builder.set_cur_block( ac.cond_end ); + m_builder.end_block( ::MIR::Terminator::make_If({ ac.cond_lval.clone(), ac.code, next }) ); + } + } + + if( next != default_arm ) + m_builder.set_cur_block(next); + } + else + { + m_builder.end_block( ::MIR::Terminator::make_Goto(ac.code) ); + ASSERT_BUG(sp, idx+1 == arm_rules.size(), "Ended arm with other arms present"); + } + } + else + { + auto bb = arm_rules.bb_idx(idx); + m_builder.end_block( ::MIR::Terminator::make_Goto(bb) ); + while( idx+1 < arm_rules.size() && bb == arm_rules.bb_idx(idx) && arm_rules[idx].size() == ofs ) + idx ++; + ASSERT_BUG(sp, idx+1 == arm_rules.size(), "Ended arm (inner) with other arms present"); + } + idx ++; + } + + // - Value arms + auto start = idx; + for(; idx < arm_rules.size() ; idx ++) + { + if( arm_rules[idx].size() <= ofs ) + break; + if( arm_rules[idx][ofs].is_Any() ) + break; + if( arm_rules[idx][ofs].is_SplitSlice() ) + break; + // TODO: It would be nice if ValueRange could be combined with Value (if there's no overlap) + if( arm_rules[idx][ofs].is_ValueRange() ) + break; + } + auto first_any = idx; + + // Generate dispatch based on the above list + // - If there's value ranges they need special handling + // - Can sort arms within this group (ordering doesn't matter, as long as ranges are handled) + // - Sort must be stable. + + if( start < first_any ) + { + DEBUG(start << "+" << (first_any-start) << ": Values"); + bool has_default = (first_any < arm_rules.size()); + auto next = (has_default ? m_builder.new_bb_unlinked() : default_arm); + + // Sort rules before getting compatible runs + // TODO: Is this a valid operation? + arm_rules.sub_sort(ofs, start, first_any - start); + + // Create list of compatible arm slices (runs with the same selector value) + ::std::vector slices; + auto cur_test = start; + for(auto i = start; i < first_any; i ++) + { + // Just check if the decision value differs (don't check nested rules) + if( ! rule_compatible(arm_rules[i][ofs], arm_rules[cur_test][ofs]) ) + { + slices.push_back( arm_rules.sub_slice(cur_test, i - cur_test) ); + cur_test = i; + } + } + slices.push_back( arm_rules.sub_slice(cur_test, first_any - cur_test) ); + DEBUG("- " << slices.size() << " groupings"); + ::std::vector<::MIR::BasicBlockId> arm_blocks; + arm_blocks.reserve( slices.size() ); + + auto cur_blk = m_builder.pause_cur_block(); + // > Stable sort list + ::std::sort( slices.begin(), slices.end(), [&](const auto& a, const auto& b){ return a[0][ofs] < b[0][ofs]; } ); + // TODO: Should this do a stable sort of inner patterns too? + // - A sort of inner patterns such that `_` (and range?) patterns don't change position. + + // > Get type of match, generate dispatch list. + for(size_t i = 0; i < slices.size(); i ++) + { + // If rules are actually different, split here. (handles Enum and Slice) + auto cur_block = m_builder.new_bb_unlinked(); + m_builder.set_cur_block(cur_block); + auto cur_start = 0; + for(size_t j = 0; j < slices[i].size(); j ++) + { + if(slices[i][j][ofs] != slices[i][cur_start][ofs]) + { + DEBUG("- Equal range : " << cur_start << "+" << (j - cur_start)); + // Package up previous rules and generate dispatch code + auto ns = slices[i].sub_slice(cur_start, j - cur_start); + this->gen_for_slice(mv$(ns), ofs+1, next); + + cur_block = m_builder.new_bb_unlinked(); + m_builder.set_cur_block(cur_block); + + cur_start = j; + } + arm_blocks.push_back(cur_block); + } + + if( cur_start != 0 ) + { + DEBUG("- Equal range : " << cur_start << "+" << (slices[i].size() - cur_start)); + auto ns = slices[i].sub_slice(cur_start, slices[i].size() - cur_start); + this->gen_for_slice( mv$(ns), ofs+1, next); + } + else + { + this->gen_for_slice(slices[i], ofs+1, next); + } + } + + m_builder.set_cur_block(cur_blk); + + // Generate decision code + this->gen_dispatch(slices, ofs, arm_blocks, next); + + if(has_default) + { + m_builder.set_cur_block(next); + } + } + + // Collate matching blocks at `first_any` + assert(first_any == idx); + if( first_any < arm_rules.size() && arm_rules[idx].size() > ofs ) + { + // Collate all equal rules + while(idx < arm_rules.size() && arm_rules[idx][ofs] == arm_rules[first_any][ofs]) + idx ++; + DEBUG(first_any << "-" << idx << ": Multi-match"); + + bool has_next = idx < arm_rules.size(); + auto next = (has_next ? m_builder.new_bb_unlinked() : default_arm); + + const auto& rule = arm_rules[first_any][ofs]; + if(const auto* e = rule.opt_ValueRange()) + { + // Generate branch based on range + this->gen_dispatch_range(arm_rules[first_any][ofs].field_path, e->first, e->last, next); + } + else if(const auto* e = rule.opt_SplitSlice()) + { + // Generate branch based on slice length being at least required. + this->gen_dispatch_splitslice(rule.field_path, *e, next); + } + else + { + ASSERT_BUG(sp, rule.is_Any(), "Didn't expect non-Any rule here, got " << rule.tag_str() << " " << rule); + } + + // Step deeper into these arms + auto slice = arm_rules.sub_slice(first_any, idx - first_any); + this->gen_for_slice(mv$(slice), ofs+1, next); + + if(has_next) + { + m_builder.set_cur_block(next); + } + } + } + + ASSERT_BUG(sp, ! m_builder.block_active(), "Block left active after match group"); +} + +void MatchGenGrouped::gen_dispatch(const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk) +{ + const auto& field_path = rules[0][0][ofs].field_path; + TRACE_FUNCTION_F("rules=["<gen_dispatch__primitive(mv$(ty), mv$(val), rules, ofs, arm_targets, def_blk); + ), + (Path, + // Matching over a path can only happen with an enum. + // TODO: What about `box` destructures? + // - They're handled via hidden derefs + if( !te.binding.is_Enum() ) { + TU_MATCHA( (te.binding), (pbe), + (Unbound, + BUG(sp, "Encounterd unbound path - " << te.path); + ), + (Opaque, + BUG(sp, "Attempting to match over opaque type - " << ty); + ), + (Struct, + const auto& str_data = pbe->m_data; + TU_MATCHA( (str_data), (sd), + (Unit, + BUG(sp, "Attempting to match over unit type - " << ty); + ), + (Tuple, + TODO(sp, "Matching on tuple-like struct?"); + ), + (Named, + TODO(sp, "Matching on struct?"); + ) + ) + ), + (Union, + TODO(sp, "Match over Union"); + ), + (Enum, + ) + ) + } + + this->gen_dispatch__enum(mv$(ty), mv$(val), rules, ofs, arm_targets, def_blk); + ), + (Generic, + BUG(sp, "Attempting to match a generic"); + ), + (TraitObject, + BUG(sp, "Attempting to match a trait object"); + ), + (ErasedType, + BUG(sp, "Attempting to match an erased type"); + ), + (Array, + BUG(sp, "Attempting to match on an Array (should have been destructured)"); + ), + (Slice, + // TODO: Slice size matches! + this->gen_dispatch__slice(mv$(ty), mv$(val), rules, ofs, arm_targets, def_blk); + ), + (Tuple, + BUG(sp, "Match directly on tuple"); + ), + (Borrow, + BUG(sp, "Match directly on borrow"); + ), + (Pointer, + // TODO: Could this actually be valid? + BUG(sp, "Attempting to match a pointer - " << ty); + ), + (Function, + // TODO: Could this actually be valid? + BUG(sp, "Attempting to match a function pointer - " << ty); + ), + (Closure, + BUG(sp, "Attempting to match a closure"); + ) + ) +} + +void MatchGenGrouped::gen_dispatch__primitive(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk) +{ + auto te = ty.m_data.as_Primitive(); + switch(te) + { + case ::HIR::CoreType::Bool: { + ASSERT_BUG(sp, rules.size() <= 2, "More than 2 rules for boolean"); + for(size_t i = 0; i < rules.size(); i++) + { + ASSERT_BUG(sp, rules[i][0][ofs].is_Bool(), "PatternRule for bool isn't _Bool"); + } + + // False sorts before true. + auto fail_bb = rules.size() == 2 ? arm_targets[ 0] : (rules[0][0][ofs].as_Bool() ? def_blk : arm_targets[0]); + auto succ_bb = rules.size() == 2 ? arm_targets[rules[0].size()] : (rules[0][0][ofs].as_Bool() ? arm_targets[0] : def_blk); + + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val), succ_bb, fail_bb }) ); + } break; + case ::HIR::CoreType::U8: + case ::HIR::CoreType::U16: + case ::HIR::CoreType::U32: + case ::HIR::CoreType::U64: + case ::HIR::CoreType::U128: + case ::HIR::CoreType::Usize: + + case ::HIR::CoreType::I8: + case ::HIR::CoreType::I16: + case ::HIR::CoreType::I32: + case ::HIR::CoreType::I64: + case ::HIR::CoreType::I128: + case ::HIR::CoreType::Isize: + + case ::HIR::CoreType::Char: + if( rules.size() == 1 ) + { + // Special case, single option, equality only + const auto& r = rules[0][0][ofs]; + ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str()); + const auto& re = r.as_Value(); + auto test_val = ::MIR::Param(re.clone()); + auto cmp_lval = this->push_compare( val.clone(), ::MIR::eBinOp::EQ, mv$(test_val) ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval), arm_targets[0], def_blk }) ); + } + else + { + // TODO: Add a SwitchInt terminator for use with this. (Or just a SwitchVal terminator?) + + // NOTE: Rules are currently sorted + // TODO: If there are Constant::Const values in the list, they need to come first! (with equality checks) + + // Does a sorted linear search. Binary search would be nicer but is harder to implement. + size_t tgt_ofs = 0; + for(size_t i = 0; i < rules.size(); i++) + { + for(size_t j = 1; j < rules[i].size(); j ++) + ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Value match"); + + const auto& r = rules[i][0][ofs]; + ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str()); + const auto& re = r.as_Value(); + if(re.is_Const()) + TODO(sp, "Handle Constant::Const in match"); + + // IF v < tst : def_blk + // Skip if the previous value was the imediat predecesor + bool is_succ = i != 0 && (re.is_Uint() + ? re.as_Uint().v == rules[i-1][0][ofs].as_Value().as_Uint().v + 1 + : re.as_Int().v == rules[i-1][0][ofs].as_Value().as_Int().v + 1 + ); + if( !is_succ ) + { + auto cmp_eq_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_lt = this->push_compare(val.clone(), ::MIR::eBinOp::LT, ::MIR::Param(re.clone())); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) ); + m_builder.set_cur_block(cmp_eq_blk); + } + + // IF v == tst : target + { + auto next_cmp_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_eq = this->push_compare( val.clone(), ::MIR::eBinOp::EQ, ::MIR::Param(re.clone()) ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], next_cmp_blk }) ); + m_builder.set_cur_block(next_cmp_blk); + } + + tgt_ofs += rules[i].size(); + } + m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) ); + } + break; + case ::HIR::CoreType::F32: + case ::HIR::CoreType::F64: { + // NOTE: Rules are currently sorted + // TODO: If there are Constant::Const values in the list, they need to come first! + size_t tgt_ofs = 0; + for(size_t i = 0; i < rules.size(); i++) + { + for(size_t j = 1; j < rules[i].size(); j ++) + ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Value match"); + + const auto& r = rules[i][0][ofs]; + ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str()); + const auto& re = r.as_Value(); + if(re.is_Const()) + TODO(sp, "Handle Constant::Const in match"); + + // IF v < tst : def_blk + { + auto cmp_eq_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_lt = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::LT, ::MIR::Param(re.clone()) })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) ); + m_builder.set_cur_block(cmp_eq_blk); + } + + // IF v == tst : target + { + auto next_cmp_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_eq = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, ::MIR::Param(re.clone()) })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], next_cmp_blk }) ); + m_builder.set_cur_block(next_cmp_blk); + } + + tgt_ofs += rules[i].size(); + } + m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) ); + } break; + case ::HIR::CoreType::Str: + // Remove the deref on the &str + auto oval = mv$(val); + auto val = mv$(*oval.as_Deref().val); + // NOTE: Rules are currently sorted + // TODO: If there are Constant::Const values in the list, they need to come first! + size_t tgt_ofs = 0; + for(size_t i = 0; i < rules.size(); i++) + { + for(size_t j = 1; j < rules[i].size(); j ++) + ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Value match"); + + const auto& r = rules[i][0][ofs]; + ASSERT_BUG(sp, r.is_Value(), "Matching without _Value pattern - " << r.tag_str()); + const auto& re = r.as_Value(); + if(re.is_Const()) + TODO(sp, "Handle Constant::Const in match"); + + // IF v < tst : def_blk + { + auto cmp_eq_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_lt = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::LT, ::MIR::Param(re.clone()) })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) ); + m_builder.set_cur_block(cmp_eq_blk); + } + + // IF v == tst : target + { + auto next_cmp_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_eq = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ val.clone(), ::MIR::eBinOp::EQ, ::MIR::Param(re.clone()) })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], next_cmp_blk }) ); + m_builder.set_cur_block(next_cmp_blk); + } + + tgt_ofs += rules[i].size(); + } + m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) ); + break; + } +} + +void MatchGenGrouped::gen_dispatch__enum(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk) +{ + TRACE_FUNCTION; + auto& te = ty.m_data.as_Path(); + const auto& pbe = te.binding.as_Enum(); + + auto decison_arm = m_builder.pause_cur_block(); + + auto monomorph = [&](const auto& ty) { + auto rv = monomorphise_type(sp, pbe->m_params, te.path.m_data.as_Generic().m_params, ty); + m_builder.resolve().expand_associated_types(sp, rv); + return rv; + }; + auto var_count = pbe->m_variants.size(); + size_t arm_idx = 0; + ::std::vector< ::MIR::BasicBlockId> arms(var_count, def_blk); + for(size_t i = 0; i < rules.size(); i ++) + { + ASSERT_BUG(sp, rules[i][0][ofs].is_Variant(), "Rule for enum isn't Any or Variant - " << rules[i][0][ofs].tag_str()); + const auto& re = rules[i][0][ofs].as_Variant(); + unsigned int var_idx = re.idx; + arms[var_idx] = m_builder.new_bb_unlinked(); + DEBUG("Variant " << var_idx); + + // Create new rule collection based on the variants + t_rules_subset inner_set { rules[i].size(), /*is_arm_indexes=*/false }; + for(size_t j = 0; j < rules[i].size(); j ++) + { + const auto& r = rules[i][j]; + inner_set.push_bb(r[ofs].as_Variant().sub_rules, arm_targets[arm_idx]); + arm_idx ++; + } + ::HIR::TypeRef fake_tup; + + const auto& var_data = pbe->m_variants.at(re.idx).second; + TU_MATCHA( (var_data), (ve), + (Unit, + // Nothing to recurse + ), + (Value, + // Nothing to recurse + ), + (Tuple, + // Create a dummy tuple to contain the inner types. + ::std::vector< ::HIR::TypeRef> fake_ty_ents; + fake_ty_ents.reserve( ve.size() ); + for(unsigned int i = 0; i < ve.size(); i ++) + { + fake_ty_ents.push_back( monomorph(ve[i].ent) ); + } + fake_tup = ::HIR::TypeRef( mv$(fake_ty_ents) ); + ), + (Struct, + // Create a dummy tuple to contain the inner types. + ::std::vector< ::HIR::TypeRef> fake_ty_ents; + fake_ty_ents.reserve( ve.size() ); + for(unsigned int i = 0; i < ve.size(); i ++) + { + fake_ty_ents.push_back( monomorph(ve[i].second.ent) ); + } + fake_tup = ::HIR::TypeRef( mv$(fake_ty_ents) ); + ) + ) + + m_builder.set_cur_block(arms[var_idx]); + // Recurse with the new ruleset + // - On success, these should jump to targets[i] + + auto new_lval = ::MIR::LValue::make_Downcast({ box$(val.clone()), var_idx }); + auto inst = MatchGenGrouped { m_builder, sp, fake_tup, new_lval, {}, rules[0][0][ofs].field_path.size() }; + inst.gen_for_slice(inner_set, 0, def_blk); + ASSERT_BUG(sp, ! m_builder.block_active(), "Block still active after enum match generation"); + } + + m_builder.set_cur_block(decison_arm); + m_builder.end_block( ::MIR::Terminator::make_Switch({ mv$(val), mv$(arms) }) ); +} + +void MatchGenGrouped::gen_dispatch__slice(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk) +{ + auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val).clone() })); + + // TODO: Re-sort the rules list to interleve Constant::Bytes and Slice + + // Just needs to check the lengths, then dispatch. + size_t tgt_ofs = 0; + for(size_t i = 0; i < rules.size(); i++) + { + //for(size_t j = 1; j < rules[i].size(); j ++) + // ASSERT_BUG(sp, arm_targets[tgt_ofs] == arm_targets[tgt_ofs+j], "Mismatched target blocks for Slice match"); + + const auto& r = rules[i][0][ofs]; + if(const auto* re = r.opt_Slice()) + { + auto val_tst = ::MIR::Constant::make_Uint({ re->len, ::HIR::CoreType::Usize }); + + // IF v < tst : target + if( re->len > 0 ) + { + auto cmp_eq_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_lt = this->push_compare( val_len.clone(), ::MIR::eBinOp::LT, val_tst.clone() ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) ); + m_builder.set_cur_block(cmp_eq_blk); + } + + // IF v == tst : target + { + auto succ_blk = m_builder.new_bb_unlinked(); + auto next_cmp_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_eq = this->push_compare( val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_tst) ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), succ_blk, next_cmp_blk }) ); + m_builder.set_cur_block(succ_blk); + + // + t_rules_subset inner_set { rules[i].size(), /*is_arm_indexes=*/false }; + for(size_t j = 0; j < rules[i].size(); j ++) + { + const auto& r = rules[i][j]; + inner_set.push_bb(r[ofs].as_Slice().sub_rules, arm_targets[tgt_ofs + j]); + } + auto inst = MatchGenGrouped { m_builder, sp, ty, val, {}, rules[0][0][ofs].field_path.size() }; + inst.gen_for_slice(inner_set, 0, def_blk); + + m_builder.set_cur_block(next_cmp_blk); + } + } + else if(const auto* re = r.opt_Value()) + { + ASSERT_BUG(sp, re->is_Bytes(), "Slice with non-Bytes value - " << *re); + const auto& b = re->as_Bytes(); + + auto val_tst = ::MIR::Constant::make_Uint({ b.size(), ::HIR::CoreType::Usize }); + auto cmp_slice_val = m_builder.lvalue_or_temp(sp, + ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ::HIR::TypeRef::new_slice(::HIR::CoreType::U8) ), + ::MIR::RValue::make_MakeDst({ ::MIR::Param(re->clone()), val_tst.clone() }) + ); + + if( b.size() > 0 ) + { + auto cmp_eq_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_lt = this->push_compare( val_len.clone(), ::MIR::eBinOp::LT, val_tst.clone() ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_lt), def_blk, cmp_eq_blk }) ); + m_builder.set_cur_block(cmp_eq_blk); + } + + // IF v == tst : target + { + auto succ_blk = m_builder.new_bb_unlinked(); + auto next_cmp_blk = m_builder.new_bb_unlinked(); + auto cmp_lval_eq = this->push_compare( val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_tst) ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), succ_blk, next_cmp_blk }) ); + m_builder.set_cur_block(succ_blk); + + // TODO: What if `val` isn't a Deref? + ASSERT_BUG(sp, val.is_Deref(), "TODO: Handle non-Deref matches of byte strings"); + cmp_lval_eq = this->push_compare( val.as_Deref().val->clone(), ::MIR::eBinOp::EQ, mv$(cmp_slice_val) ); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], def_blk }) ); + + m_builder.set_cur_block(next_cmp_blk); + } + } + else + { + BUG(sp, "Matching without _Slice pattern - " << r.tag_str() << " - " << r); + } + + tgt_ofs += rules[i].size(); + } + m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) ); +} + + +void MatchGenGrouped::gen_dispatch_range(const field_path_t& field_path, const ::MIR::Constant& first, const ::MIR::Constant& last, ::MIR::BasicBlockId def_blk) +{ + TRACE_FUNCTION_F("field_path="< 0); + // TODO: Should this also check for the end being the max value of the type? + // - Can just leave that to the optimiser + upper_possible = true; + break; + case ::HIR::CoreType::I8: + case ::HIR::CoreType::I16: + case ::HIR::CoreType::I32: + case ::HIR::CoreType::I64: + case ::HIR::CoreType::I128: + case ::HIR::CoreType::Isize: + lower_possible = true; + upper_possible = true; + break; + case ::HIR::CoreType::Char: + lower_possible = (first.as_Uint().v > 0); + upper_possible = (first.as_Uint().v <= 0x10FFFF); + break; + case ::HIR::CoreType::F32: + case ::HIR::CoreType::F64: + // NOTE: No upper or lower limits + break; + } + + if( lower_possible ) + { + auto test_bb_2 = m_builder.new_bb_unlinked(); + // IF `val` < `first` : fail_bb + auto cmp_lt_lval = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::LT, ::MIR::Param(first.clone()) })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lt_lval), def_blk, test_bb_2 }) ); + + m_builder.set_cur_block(test_bb_2); + } + + + if( upper_possible ) + { + auto succ_bb = m_builder.new_bb_unlinked(); + + // IF `val` > `last` : fail_bb + auto cmp_gt_lval = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ ::MIR::Param(val.clone()), ::MIR::eBinOp::GT, ::MIR::Param(last.clone()) })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_gt_lval), def_blk, succ_bb }) ); + + m_builder.set_cur_block(succ_bb); + } + } + else + { + TODO(sp, "ValueRange on " << ty); + } +} +void MatchGenGrouped::gen_dispatch_splitslice(const field_path_t& field_path, const PatternRule::Data_SplitSlice& e, ::MIR::BasicBlockId def_blk) +{ + TRACE_FUNCTION_F("field_path="<push_compare(val_len.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Uint({ e.min_len, ::HIR::CoreType::Usize })); + m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_val), def_blk, next }) ); + m_builder.set_cur_block(next); + } + + // 2. Recurse into leading patterns. + if( !e.leading.empty() ) + { + auto next = m_builder.new_bb_unlinked(); + auto inner_set = t_rules_subset { 1, /*is_arm_indexes=*/false }; + inner_set.push_bb( e.leading, next ); + auto inst = MatchGenGrouped { m_builder, sp, ty, val, {}, field_path.size() }; + inst.gen_for_slice(inner_set, 0, def_blk); + + m_builder.set_cur_block(next); + } + + if( !e.trailing.empty() ) + { + TODO(sp, "Handle trailing rules in SplitSlice - " << e.trailing); + } +} + // -------------------------------------------------------------------- // Decision Tree // -------------------------------------------------------------------- diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index 30d94a3d..da07ec5a 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -24,12 +24,19 @@ namespace MIR { os << (e.v ? "true" : "false"); ), (Bytes, - os << "["; + os << "b\""; os << ::std::hex; for(auto v : e) - os << static_cast(v) << " "; + { + if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) + os << v; + else if( v < 16 ) + os << "\\x0" << (unsigned int)v; + else + os << "\\x" << (unsigned int)v; + } + os << "\""; os << ::std::dec; - os << "]"; ), (StaticString, os << "\""; @@ -50,34 +57,40 @@ namespace MIR { ) return os; } - bool Constant::operator==(const Constant& b) const + ::Ordering Constant::ord(const Constant& b) const { if( this->tag() != b.tag() ) - return false; + return ::ord( static_cast(this->tag()), b.tag() ); TU_MATCHA( (*this,b), (ae,be), (Int, - return ae.v == be.v && ae.t == be.t; + if( ae.v != be.v ) + return ::ord(ae.v, be.v); + return ::ord((unsigned)ae.t, (unsigned)be.t); ), (Uint, - return ae.v == be.v && ae.t == be.t; + if( ae.v != be.v ) + return ::ord(ae.v, be.v); + return ::ord((unsigned)ae.t, (unsigned)be.t); ), (Float, - return ae.v == be.v && ae.t == be.t; + if( ae.v != be.v ) + return ::ord(ae.v, be.v); + return ::ord((unsigned)ae.t, (unsigned)be.t); ), (Bool, - return ae.v == be.v; + return ::ord(ae.v, be.v); ), (Bytes, - return ae == be; + return ::ord(ae, be); ), (StaticString, - return ae == be; + return ::ord(ae, be); ), (Const, - return ae.p == be.p; + return ::ord(ae.p, be.p); ), (ItemAddr, - return ae == be; + return ::ord(ae, be); ) ) throw ""; diff --git a/src/mir/mir.hpp b/src/mir/mir.hpp index f31ede8d..93a430ff 100644 --- a/src/mir/mir.hpp +++ b/src/mir/mir.hpp @@ -112,10 +112,13 @@ TAGGED_UNION_EX(Constant, (), Int, ( (ItemAddr, ::HIR::Path) // address of a value ), (), (), ( friend ::std::ostream& operator<<(::std::ostream& os, const Constant& v); - bool operator==(const Constant& b) const; - inline bool operator!=(const Constant& b) const { - return !(*this == b); - } + ::Ordering ord(const Constant& b) const; + inline bool operator==(const Constant& b) const { return ord(b) == ::OrdEqual; } + inline bool operator!=(const Constant& b) const { return ord(b) != ::OrdEqual; } + inline bool operator<(const Constant& b) const { return ord(b) == ::OrdLess; } + inline bool operator<=(const Constant& b) const { return ord(b) != ::OrdGreater; } + inline bool operator>(const Constant& b) const { return ord(b) == ::OrdGreater; } + inline bool operator>=(const Constant& b) const { return ord(b) != ::OrdLess; } Constant clone() const; ) ); -- cgit v1.2.3 From 66bce935d59c969ef59db2ab5b7d0c9039fbb02e Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 7 Apr 2017 11:59:22 +0800 Subject: MIR Gen - Remove old DT match code --- src/mir/from_hir_match.cpp | 1960 +------------------------------------------- 1 file changed, 3 insertions(+), 1957 deletions(-) diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index 584e7dc0..ecc8b6e6 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -461,7 +461,7 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod } // TODO: Remove columns that are all `_`? - // - Ideally, only accessible structures would be fully destructured like this. + // - Ideally, only accessible structures would be fully destructured like this, making this check redundant // Sort rules using the following restrictions: // - A rule cannot be reordered across an item that has an overlapping match set @@ -497,13 +497,14 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod } // TODO: Don't generate inner code until decisions are generated (keeps MIR flow nice) + // - Challenging, as the decision code needs somewhere to jump to. + // - Allocating a BB and then rewriting references to it is a possibility. if( fall_back_on_simple ) { MIR_LowerHIR_Match_Simple( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); } else { MIR_LowerHIR_Match_Grouped( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); - //MIR_LowerHIR_Match_DecisionTree( builder, conv, node, mv$(match_val), mv$(arm_rules), mv$(arm_code), first_cmp_block ); } builder.set_cur_block( next_block ); @@ -3264,1958 +3265,3 @@ void MatchGenGrouped::gen_dispatch_splitslice(const field_path_t& field_path, co } } -// -------------------------------------------------------------------- -// Decision Tree -// -------------------------------------------------------------------- - -// ## Create descision tree in-memory based off the ruleset -// > Tree contains an lvalue and a set of possibilities (PatternRule) connected to another tree or to a branch index -struct DecisionTreeNode -{ - TAGGED_UNION( Branch, Unset, - (Unset, struct{}), - (Subtree, ::std::unique_ptr), - (Terminal, unsigned int) - ); - - template - struct Range - { - T start; - T end; - - // `x` starts after this range ends - bool operator<(const Range& x) const { - return (end < x.start); - } - // `x` falls above the end of this range - bool operator<(const T& x) const { - return (end < x); - } - - // `x` ends before this starts, or overlaps - bool operator>=(const Range& x) const { - return start > x.end || ovelaps(x); - } - // `x` is before or within this range - bool operator>=(const T& x) const { - return start > x || contains(x); - } - - bool operator>(const Range& x) const { - return (start > x.end); - } - bool operator>(const T& x) const { - return (start > x); - } - - bool operator==(const Range& x) const { - return start == x.start && end == x.end; - } - bool operator!=(const Range& x) const { - return start != x.start || end != x.end; - } - - bool contains(const T& x) const { - return (start <= x && x <= end); - } - bool overlaps(const Range& x) const { - return (x.start <= start && start <= x.end) || (x.start <= end && end <= x.end); - } - - friend ::std::ostream& operator<<(::std::ostream& os, const Range& x) { - if( x.start == x.end ) { - return os << x.start; - } - else { - return os << x.start << " ... " << x.end; - } - } - }; - - TAGGED_UNION( Values, Unset, - (Unset, struct {}), - (Bool, struct { Branch false_branch, true_branch; }), - (Variant, ::std::vector< ::std::pair >), - (Unsigned, ::std::vector< ::std::pair< Range, Branch> >), - (Signed, ::std::vector< ::std::pair< Range, Branch> >), - (Float, ::std::vector< ::std::pair< Range, Branch> >), - (String, ::std::vector< ::std::pair< ::std::string, Branch> >), - (Slice, struct { - ::std::vector< ::std::pair< unsigned int, Branch> > fixed_arms; - //::std::vector< ::std::pair< unsigned int, Branch> > variable_arms; - }) - ); - - // TODO: Arm specialisation? - field_path_t m_field_path; - Values m_branches; - Branch m_default; - - DecisionTreeNode( field_path_t field_path ): - // TODO: This is commented out fo a reason, but I don't know why. - //m_field_path( mv$(field_path) ), - m_branches(), - m_default() - {} - - static Branch clone(const Branch& b); - static Values clone(const Values& x); - DecisionTreeNode clone() const; - - void populate_tree_from_rule(const Span& sp, unsigned int arm_index, const PatternRule* first_rule, unsigned int rule_count) { - populate_tree_from_rule(sp, first_rule, rule_count, [sp,arm_index](auto& branch){ - TU_MATCHA( (branch), (e), - (Unset, - // Good - ), - (Subtree, - if( e->m_branches.is_Unset() && e->m_default.is_Unset() ) { - // Good. - } - else { - BUG(sp, "Duplicate terminal - branch="< and_then); - - /// Simplifies the tree by eliminating nodes that don't make a decision - void simplify(); - /// Propagate the m_default arm's contents to value arms, and vice-versa - void propagate_default(); - /// HELPER: Unfies the rules from the provided branch with this node - void unify_from(const Branch& b); - - ::MIR::LValue get_field(const ::MIR::LValue& base, unsigned int base_depth) const { - ::MIR::LValue cur = base.clone(); - for(unsigned int i = base_depth; i < m_field_path.size(); i ++ ) { - const auto idx = m_field_path.data[i]; - if( idx == FIELD_DEREF ) { - cur = ::MIR::LValue::make_Deref({ box$(cur) }); - } - else { - cur = ::MIR::LValue::make_Field({ box$(cur), idx }); - } - } - return cur; - } - - friend ::std::ostream& operator<<(::std::ostream& os, const Branch& x); - friend ::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode& x); -}; - -struct DecisionTreeGen -{ - MirBuilder& m_builder; - const ::std::vector< ::MIR::BasicBlockId>& m_rule_blocks; - - DecisionTreeGen(MirBuilder& builder, const ::std::vector< ::MIR::BasicBlockId >& rule_blocks): - m_builder( builder ), - m_rule_blocks( rule_blocks ) - {} - - ::MIR::BasicBlockId get_block_for_rule(unsigned int rule_index) { - return m_rule_blocks.at( rule_index ); - } - - void generate_tree_code(const Span& sp, const DecisionTreeNode& node, const ::HIR::TypeRef& ty, const ::MIR::LValue& val) { - generate_tree_code(sp, node, ty, 0, val, [&](const auto& n){ - DEBUG("node = " << n); - // - Recurse on this method - this->generate_tree_code(sp, n, ty, val); - }); - } - void generate_tree_code( - const Span& sp, - const DecisionTreeNode& node, - const ::HIR::TypeRef& ty, unsigned int path_ofs, const ::MIR::LValue& base_val, - ::std::function and_then - ); - - void generate_branch(const DecisionTreeNode::Branch& branch, ::std::function cb); - - // HELPER - ::MIR::LValue push_compare(const Span& sp, ::MIR::LValue left, ::MIR::eBinOp op, ::MIR::Param right); - - void generate_branches_Signed( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Signed& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_branches_Unsigned( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Unsigned& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_branches_Float( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Float& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_branches_Char( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Unsigned& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_branches_Bool( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Bool& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_branches_Borrow_str( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_String& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - - void generate_branches_Enum( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Variant& branches, - const field_path_t& field_path, // used to know when to stop handling sub-nodes - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_branches_Slice( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Slice& branches, - const field_path_t& field_path, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ); - void generate_tree_code__enum( - const Span& sp, - const DecisionTreeNode& node, const ::HIR::TypeRef& fake_ty, const ::MIR::LValue& val, - const field_path_t& path_prefix, - ::std::function and_then - ); -}; - -void MIR_LowerHIR_Match_DecisionTree( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val, t_arm_rules arm_rules, ::std::vector arms_code, ::MIR::BasicBlockId first_cmp_block ) -{ - TRACE_FUNCTION; - - // XXX XXX XXX: The current codegen (below) will generate incorrect code if ordering matters. - // ``` - // match ("foo", "bar") - // { - // (_, "bar") => {}, // Expected - // ("foo", _) => {}, // Actual - // _ => {}, - // } - // ``` - - // TODO: Sort the columns in `arm_rules` to ensure that the most specific rule is parsed first. - // - Ordering within a pattern doesn't matter, only the order of arms matters. - // - This sort could be designed such that the above case would match correctly? - - DEBUG("- Generating rule bindings"); - ::std::vector< ::MIR::BasicBlockId> rule_blocks; - for(const auto& rule : arm_rules) - { - const auto& arm_code = arms_code[rule.arm_idx]; - ASSERT_BUG(node.span(), !arm_code.has_condition, "Decision tree doesn't (yet) support conditionals"); - - assert( rule.pat_idx < arm_code.destructures.size() ); - // Set the target for when a rule succeeds to the destructuring code for this rule - rule_blocks.push_back( arm_code.destructures[rule.pat_idx] ); - // - Tie the end of that block to the code block for this arm - builder.set_cur_block( rule_blocks.back() ); - builder.end_block( ::MIR::Terminator::make_Goto(arm_code.code) ); - } - - - // - Build tree by running each arm's pattern across it - DEBUG("- Building decision tree"); - DecisionTreeNode root_node({}); - unsigned int rule_idx = 0; - for( const auto& arm_rule : arm_rules ) - { - auto arm_idx = arm_rule.arm_idx; - DEBUG("(" << arm_idx << ", " << arm_rule.pat_idx << "): " << arm_rule.m_rules); - root_node.populate_tree_from_rule( node.m_arms[arm_idx].m_code->span(), rule_idx, arm_rule.m_rules.data(), arm_rule.m_rules.size() ); - rule_idx += 1; - } - DEBUG("root_node = " << root_node); - root_node.simplify(); - DEBUG("root_node = " << root_node); - root_node.propagate_default(); - DEBUG("root_node = " << root_node); - // TODO: Pretty print `root_node` - - // - Convert the above decision tree into MIR - DEBUG("- Emitting decision tree"); - DecisionTreeGen gen { builder, rule_blocks }; - builder.set_cur_block( first_cmp_block ); - gen.generate_tree_code( node.span(), root_node, node.m_value->m_res_type, mv$(match_val) ); - ASSERT_BUG(node.span(), !builder.block_active(), "Decision tree didn't terminate the final block"); -} - -#if 0 -DecisionTreeNode MIR_LowerHIR_Match_DecisionTree__MakeTree(const Span& sp, t_arm_rules& arm_rules) -{ - ::std::vector indexes; - ::std::vector< slice > rules; - for(unsigned i = 0; i < arm_rules.size(); i ++) - { - rules.push_back( arm_rules[i].m_rules ); - indexes.push_back(i); - } - - return MIR_LowerHIR_Match_DecisionTree__MakeTree_Node(sp, indexes, rules); -} -DecisionTreeNode MIR_LowerHIR_Match_DecisionTree__MakeTree_Node(const Span& sp, slice arm_indexes, slice< slice> arm_rules) -{ - assert( arm_indexes.size() == arm_rules.size() ); - assert( arm_rules.size() > 1 ); - assert( arm_rules[0].size() > 0 ); - - // 1. Sort list (should it already be sorted?) - for(const auto& rules : arm_rules) - { - ASSERT_BUG(sp, rules.size() != arm_rules[0].size(), ""); - } - - // 2. Detect all arms being `_` and move on to the next condition - while( ::std::all_of(arm_rules.begin(), arm_rules.end(), [](const auto& r){ return r.m_rules[0].is_Any(); }) ) - { - // Delete first rule from all and continue. - if( arm_rules[0].size() == 1 ) { - // No rules left? - BUG(sp, "Duplicate match arms"); - } - - for(auto& rules : arm_rules) - { - rules = rules.subslice_from(1); - } - } - - // We have a codition. - for(const auto& rules : arm_rules) - { - ASSERT_BUG(sp, rules[0].is_Any() || rules[0].tag() == arm_rules[0][0].tag(), "Mismatched rules in match"); - } - - bool has_any = arm_rules.back()[0].is_Any(); - - // All rules must either be _ or the same type, and can't all be _ - switch( arm_rules[0][0].tag() ) - { - case PatternRule::TAGDEAD: throw ""; - case PatternRule::TAG_Any: throw ""; - - case PatternRule::TAG_Variant: - break; - // TODO: Value and ValueRange can appear together. - // - They also overlap in non-trivial ways. - } -} -#endif - -// ---------------------------- -// DecisionTreeNode -// ---------------------------- -DecisionTreeNode::Branch DecisionTreeNode::clone(const DecisionTreeNode::Branch& b) { - TU_MATCHA( (b), (e), - (Unset, return Branch(e); ), - (Subtree, return Branch(box$( e->clone() )); ), - (Terminal, return Branch(e); ) - ) - throw ""; -} -DecisionTreeNode::Values DecisionTreeNode::clone(const DecisionTreeNode::Values& x) { - TU_MATCHA( (x), (e), - (Unset, return Values(e); ), - (Bool, - return Values::make_Bool({ clone(e.false_branch), clone(e.true_branch) }); - ), - (Variant, - Values::Data_Variant rv; - rv.reserve(e.size()); - for(const auto& v : e) - rv.push_back( ::std::make_pair(v.first, clone(v.second)) ); - return Values( mv$(rv) ); - ), - (Unsigned, - Values::Data_Unsigned rv; - rv.reserve(e.size()); - for(const auto& v : e) - rv.push_back( ::std::make_pair(v.first, clone(v.second)) ); - return Values( mv$(rv) ); - ), - (Signed, - Values::Data_Signed rv; - rv.reserve(e.size()); - for(const auto& v : e) - rv.push_back( ::std::make_pair(v.first, clone(v.second)) ); - return Values( mv$(rv) ); - ), - (Float, - Values::Data_Float rv; - rv.reserve(e.size()); - for(const auto& v : e) - rv.push_back( ::std::make_pair(v.first, clone(v.second)) ); - return Values( mv$(rv) ); - ), - (String, - Values::Data_String rv; - rv.reserve(e.size()); - for(const auto& v : e) - rv.push_back( ::std::make_pair(v.first, clone(v.second)) ); - return Values( mv$(rv) ); - ), - (Slice, - Values::Data_Slice rv; - rv.fixed_arms.reserve(e.fixed_arms.size()); - for(const auto& v : e.fixed_arms) - rv.fixed_arms.push_back( ::std::make_pair(v.first, clone(v.second)) ); - return Values( mv$(rv) ); - ) - ) - throw ""; -} -DecisionTreeNode DecisionTreeNode::clone() const { - DecisionTreeNode rv(m_field_path); - rv.m_field_path = m_field_path; - rv.m_branches = clone(m_branches); - rv.m_default = clone(m_default); - return rv; -} - -// Helpers for `populate_tree_from_rule` -namespace -{ - DecisionTreeNode::Branch new_branch_subtree(field_path_t path) - { - return DecisionTreeNode::Branch( box$(DecisionTreeNode( mv$(path) )) ); - } - - // Common code for numerics (Int, Uint, and Float) - template - static void from_rule_value( - const Span& sp, - ::std::vector< ::std::pair< DecisionTreeNode::Range, DecisionTreeNode::Branch> >& be, T ve, - const char* name, const field_path_t& field_path, ::std::function and_then - ) - { - auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first.end >= ve; }); - if( it == be.end() || it->first.start > ve ) { - it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range { ve,ve }, new_branch_subtree(field_path) ) ); - } - else if( it->first.start == ve && it->first.end == ve ) { - // Equal, continue and add sub-pat - } - else { - // Collide or overlap! - TODO(sp, "Value patterns - " << name << " - Overlapping - " << it->first.start << " <= " << ve << " <= " << it->first.end); - } - and_then( it->second ); - } - - template - static void from_rule_valuerange( - const Span& sp, - ::std::vector< ::std::pair< DecisionTreeNode::Range, DecisionTreeNode::Branch> >& be, T ve_start, T ve_end, - const char* name, const field_path_t& field_path, ::std::function and_then - ) - { - TRACE_FUNCTION_F("be=[" << FMT_CB(os, for(const auto& i:be) os << i.first <<" , ";) << "], new=" << ve_start << "..." << ve_end); - ASSERT_BUG(sp, ve_start <= ve_end, "Range pattern with a start after the end - " << ve_start << "..." << ve_end); - - if( ve_start == ve_end ) { - from_rule_value(sp, be, ve_start, name, field_path, and_then); - return ; - } - // - Find the first entry that ends after the new one starts. - auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first.end >= ve_start; }); - while(ve_start < ve_end) - { - if( it == be.end() ) { - DEBUG("new = (" << ve_start << "..." << ve_end << "), exist=END"); - it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range { ve_start,ve_end }, new_branch_subtree(field_path) ) ); - and_then(it->second); - return ; - } - DEBUG("new = (" << ve_start << "..." << ve_end << "), exist=" << it->first); - // If the located entry starts after the end of this range - if( it->first.start >= ve_end ) { - DEBUG("- New free"); - it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range { ve_start,ve_end }, new_branch_subtree(field_path) ) ); - and_then(it->second); - return ; - } - // If this range is equal to the existing, just recurse into it - else if( it->first.start == ve_start && it->first.end == ve_end ) { - DEBUG("- Equal"); - and_then(it->second); - return ; - } - // If the new range starts before the start of this range, add a new entry before the existing one - else if( it->first.start > ve_start ) { - DEBUG("- New head, continue"); - it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range { ve_start,it->first.start-1 }, new_branch_subtree(field_path) ) ); - and_then(it->second); - ++ it; - ve_start = it->first.start; - } - // If the new range ends before the end of this range, split the existing range and recurse into the first - else if( it->first.end > ve_end ) { - DEBUG("- Inner"); - assert(ve_start == it->first.start); - it = be.insert( it, ::std::make_pair( DecisionTreeNode::Range { ve_start, ve_end }, DecisionTreeNode::clone(it->second) ) ); - and_then(it->second); - (it+1)->first.start = ve_end+1; - return ; - } - // (else) if the new range ends after the end of this range, apply to the rest of this range and advance - else { - DEBUG("- Shared head, continue"); - //assert(it->first.start == ve_start); - assert((it->first.end) < ve_end); - - and_then(it->second); - ve_start = it->first.end + 1; - ++ it; - } - } - } -} -void DecisionTreeNode::populate_tree_from_rule(const Span& sp, const PatternRule* first_rule, unsigned int rule_count, ::std::function and_then) -{ - assert( rule_count > 0 ); - const auto& rule = *first_rule; - - if( m_field_path.size() == 0 ) { - m_field_path = rule.field_path; - } - else { - ASSERT_BUG(sp, m_field_path == rule.field_path, "Patterns with mismatched field paths - " << m_field_path << " != " << rule.field_path); - } - - #define GET_BRANCHES(fld, var) (({if( fld.is_Unset() ) {\ - fld = Values::make_##var({}); \ - } \ - else if( !fld.is_##var() ) { \ - BUG(sp, "Mismatched rules - have " #var ", but have seen " << fld.tag_str()); \ - }}), \ - fld.as_##var()) - - - TU_MATCHA( (rule), (e), - (Any, { - if( rule_count == 1 ) - { - ASSERT_BUG(sp, !m_default.is_Terminal(), "Duplicate terminal rule"); - and_then(m_default); - } - else - { - if( m_default.is_Unset() ) { - m_default = new_branch_subtree(rule.field_path); - m_default.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else TU_IFLET( Branch, m_default, Subtree, be, - be->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - ) - else { - // NOTE: All lists processed as part of the same tree should be the same length - BUG(sp, "Duplicate terminal rule"); - } - } - // TODO: Should this also recurse into branches? - }), - (Variant, { - auto& be = GET_BRANCHES(m_branches, Variant); - - auto it = ::std::find_if( be.begin(), be.end(), [&](const auto& x){ return x.first >= e.idx; }); - // If this variant isn't yet processed, add a new subtree for it - if( it == be.end() || it->first != e.idx ) { - it = be.insert(it, ::std::make_pair(e.idx, new_branch_subtree(rule.field_path))); - assert( it->second.is_Subtree() ); - } - else { - if( it->second.is_Terminal() ) { - BUG(sp, "Duplicate terminal rule - " << it->second.as_Terminal()); - } - assert( !it->second.is_Unset() ); - assert( it->second.is_Subtree() ); - } - auto& subtree = *it->second.as_Subtree(); - - if( e.sub_rules.size() > 0 && rule_count > 1 ) - { - subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), [&](auto& branch){ - TU_MATCH_DEF(Branch, (branch), (be), - ( - BUG(sp, "Duplicate terminator"); - ), - (Unset, - branch = new_branch_subtree(rule.field_path); - ), - (Subtree, - ) - ) - branch.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - }); - } - else if( e.sub_rules.size() > 0) - { - subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), and_then); - } - else if( rule_count > 1 ) - { - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(it->second); - } - }), - (Slice, - auto& be = GET_BRANCHES(m_branches, Slice); - - auto it = ::std::find_if( be.fixed_arms.begin(), be.fixed_arms.end(), [&](const auto& x){ return x.first >= e.len; } ); - if( it == be.fixed_arms.end() || it->first != e.len ) { - it = be.fixed_arms.insert(it, ::std::make_pair(e.len, new_branch_subtree(rule.field_path))); - } - else { - if( it->second.is_Terminal() ) { - BUG(sp, "Duplicate terminal rule - " << it->second.as_Terminal()); - } - assert( !it->second.is_Unset() ); - } - assert( it->second.is_Subtree() ); - auto& subtree = *it->second.as_Subtree(); - - if( e.sub_rules.size() > 0 && rule_count > 1 ) - { - subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), [&](auto& branch){ - TU_MATCH_DEF(Branch, (branch), (be), - ( - BUG(sp, "Duplicate terminator"); - ), - (Unset, - branch = new_branch_subtree(rule.field_path); - ), - (Subtree, - ) - ) - branch.as_Subtree()->populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - }); - } - else if( e.sub_rules.size() > 0) - { - subtree.populate_tree_from_rule(sp, e.sub_rules.data(), e.sub_rules.size(), and_then); - } - else if( rule_count > 1 ) - { - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(it->second); - } - ), - (SplitSlice, - //auto& be = GET_BRANCHES(m_branches, Slice); - TODO(sp, "SplitSlice in DTN - " << rule); - ), - (Bool, - auto& be = GET_BRANCHES(m_branches, Bool); - - auto& branch = (e ? be.true_branch : be.false_branch); - if( branch.is_Unset() ) { - branch = new_branch_subtree( rule.field_path ); - } - else if( branch.is_Terminal() ) { - BUG(sp, "Duplicate terminal rule - " << branch.as_Terminal()); - } - else { - // Good. - } - if( rule_count > 1 ) - { - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - ), - (Value, - TU_MATCHA( (e), (ve), - (Int, - auto& be = GET_BRANCHES(m_branches, Signed); - - from_rule_value(sp, be, ve.v, "Signed", rule.field_path, - [&](auto& branch) { - if( rule_count > 1 ) { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - }); - ), - (Uint, - auto& be = GET_BRANCHES(m_branches, Unsigned); - - from_rule_value(sp, be, ve.v, "Unsigned", rule.field_path, - [&](auto& branch) { - if( rule_count > 1 ) { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - }); - ), - (Float, - auto& be = GET_BRANCHES(m_branches, Float); - - from_rule_value(sp, be, ve.v, "Float", rule.field_path, - [&](auto& branch) { - if( rule_count > 1 ) { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else { - and_then(branch); - } - }); - ), - (Bool, - BUG(sp, "Hit Bool in PatternRule::Value - " << e); - ), - (Bytes, - TODO(sp, "Value patterns - Bytes"); - ), - (StaticString, - auto& be = GET_BRANCHES(m_branches, String); - - auto it = ::std::find_if(be.begin(), be.end(), [&](const auto& v){ return v.first >= ve; }); - if( it == be.end() || it->first != ve ) { - it = be.insert( it, ::std::make_pair(ve, new_branch_subtree(rule.field_path) ) ); - } - auto& branch = it->second; - if( rule_count > 1 ) - { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - ), - (Const, - BUG(sp, "Hit Const in PatternRule::Value - " << e); - ), - (ItemAddr, - BUG(sp, "Hit ItemAddr in PatternRule::Value - " << e); - ) - ) - ), - (ValueRange, - - ASSERT_BUG(sp, e.first.tag() == e.last.tag(), "Constant type mismatch in ValueRange - " << e.first << " and " << e.last); - TU_MATCHA( (e.first, e.last), (ve_start, ve_end), - (Int, - auto& be = GET_BRANCHES(m_branches, Signed); - from_rule_valuerange(sp, be, ve_start.v, ve_end.v, "Signed", rule.field_path, - [&](auto& branch) { - if( rule_count > 1 ) - { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - }); - ), - (Uint, - // TODO: Share code between the three numeric groups - auto& be = GET_BRANCHES(m_branches, Unsigned); - from_rule_valuerange(sp, be, ve_start.v, ve_end.v, "Unsigned", rule.field_path, - [&](auto& branch) { - if( rule_count > 1 ) - { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - }); - ), - (Float, - auto& be = GET_BRANCHES(m_branches, Float); - from_rule_valuerange(sp, be, ve_start.v, ve_end.v, "Float", rule.field_path, - [&](auto& branch) { - if( rule_count > 1 ) - { - assert( branch.as_Subtree() ); - auto& subtree = *branch.as_Subtree(); - subtree.populate_tree_from_rule(sp, first_rule+1, rule_count-1, and_then); - } - else - { - and_then(branch); - } - }); - ), - (Bool, - BUG(sp, "Hit Bool in PatternRule::ValueRange - " << e.first); - ), - (Bytes, - TODO(sp, "ValueRange patterns - Bytes"); - ), - (StaticString, - ERROR(sp, E0000, "Use of string in value range patter"); - ), - (Const, - BUG(sp, "Hit Const in PatternRule::ValueRange - " << e.first); - ), - (ItemAddr, - BUG(sp, "Hit ItemAddr in PatternRule::ValueRange - " << e.first); - ) - ) - ) - ) -} - -void DecisionTreeNode::simplify() -{ - struct H { - static void simplify_branch(Branch& b) - { - TU_IFLET(Branch, b, Subtree, be, - be->simplify(); - if( be->m_branches.is_Unset() ) { - auto v = mv$( be->m_default ); - b = mv$(v); - } - ) - } - }; - - TU_MATCHA( (m_branches), (e), - (Unset, - H::simplify_branch(m_default); - // Replace `this` with `m_default` if `m_default` is a subtree - // - Fixes the edge case for the top of the tree - if( m_default.is_Subtree() ) - { - *this = mv$(*m_default.as_Subtree()); - } - return ; - ), - (Bool, - H::simplify_branch(e.false_branch); - H::simplify_branch(e.true_branch); - ), - (Variant, - for(auto& branch : e) { - H::simplify_branch(branch.second); - } - ), - (Unsigned, - for(auto& branch : e) { - H::simplify_branch(branch.second); - } - ), - (Signed, - for(auto& branch : e) { - H::simplify_branch(branch.second); - } - ), - (Float, - for(auto& branch : e) { - H::simplify_branch(branch.second); - } - ), - (String, - for(auto& branch : e) { - H::simplify_branch(branch.second); - } - ), - (Slice, - for(auto& branch : e.fixed_arms) { - H::simplify_branch(branch.second); - } - ) - ) - - H::simplify_branch(m_default); -} - -void DecisionTreeNode::propagate_default() -{ - TRACE_FUNCTION_FR(*this, *this); - struct H { - static void handle_branch(Branch& b, const Branch& def) { - TU_IFLET(Branch, b, Subtree, be, - be->propagate_default(); - if( !def.is_Unset() ) - { - DEBUG("Unify " << *be << " with " << def); - be->unify_from(def); - be->propagate_default(); - } - ) - } - }; - - TU_MATCHA( (m_branches), (e), - (Unset, - ), - (Bool, - DEBUG("- false"); - H::handle_branch(e.false_branch, m_default); - DEBUG("- true"); - H::handle_branch(e.true_branch, m_default); - ), - (Variant, - for(auto& branch : e) { - DEBUG("- V " << branch.first); - H::handle_branch(branch.second, m_default); - } - ), - (Unsigned, - for(auto& branch : e) { - DEBUG("- U " << branch.first); - H::handle_branch(branch.second, m_default); - } - ), - (Signed, - for(auto& branch : e) { - DEBUG("- S " << branch.first); - H::handle_branch(branch.second, m_default); - } - ), - (Float, - for(auto& branch : e) { - DEBUG("- " << branch.first); - H::handle_branch(branch.second, m_default); - } - ), - (String, - for(auto& branch : e) { - DEBUG("- '" << branch.first << "'"); - H::handle_branch(branch.second, m_default); - } - ), - (Slice, - for(auto& branch : e.fixed_arms) { - DEBUG("- [_;" << branch.first << "]"); - H::handle_branch(branch.second, m_default); - } - ) - ) - DEBUG("- default"); - TU_IFLET(Branch, m_default, Subtree, be, - be->propagate_default(); - - if( be->m_default.is_Unset() ) { - // Propagate default from value branches - TU_MATCHA( (m_branches), (e), - (Unset, - ), - (Bool, - be->unify_from(e.false_branch); - be->unify_from(e.true_branch); - ), - (Variant, - for(auto& branch : e) { - be->unify_from(branch.second); - } - ), - (Unsigned, - for(auto& branch : e) { - be->unify_from(branch.second); - } - ), - (Signed, - for(auto& branch : e) { - be->unify_from(branch.second); - } - ), - (Float, - for(auto& branch : e) { - be->unify_from(branch.second); - } - ), - (String, - for(auto& branch : e) { - be->unify_from(branch.second); - } - ), - (Slice, - for(auto& branch : e.fixed_arms) { - be->unify_from(branch.second); - } - ) - ) - } - ) -} - -namespace { - static void unify_branch(DecisionTreeNode::Branch& dst, const DecisionTreeNode::Branch& src) { - if( dst.is_Unset() ) { - dst = DecisionTreeNode::clone(src); - } - else if( dst.is_Subtree() ) { - dst.as_Subtree()->unify_from(src); - } - else { - // Terminal, no unify - } - } - - template - void unify_from_vals_range(::std::vector< ::std::pair>& dst, const ::std::vector< ::std::pair>& src) - { - for(const auto& srcv : src) - { - // Find the first entry with an end greater than or equal to the start of this entry - auto it = ::std::find_if( dst.begin(), dst.end(), [&](const auto& x){ return x.first.end >= srcv.first.start; }); - // Not found? Insert a new branch - if( it == dst.end() ) { - it = dst.insert(it, ::std::make_pair(srcv.first, DecisionTreeNode::clone(srcv.second))); - } - // If the found entry doesn't overlap (the start of `*it` is after the end of `srcv`) - else if( it->first.start > srcv.first.end ) { - it = dst.insert(it, ::std::make_pair(srcv.first, DecisionTreeNode::clone(srcv.second))); - } - else if( it->first == srcv.first ) { - unify_branch( it->second, srcv.second ); - } - else { - // NOTE: Overlapping doesn't get handled here - } - } - } - - template - void unify_from_vals_pt(::std::vector< ::std::pair>& dst, const ::std::vector< ::std::pair>& src) - { - // Insert items not already present, merge present items - for(const auto& srcv : src) - { - auto it = ::std::find_if( dst.begin(), dst.end(), [&](const auto& x){ return x.first >= srcv.first; }); - // Not found? Insert a new branch - if( it == dst.end() || it->first != srcv.first ) { - it = dst.insert(it, ::std::make_pair(srcv.first, DecisionTreeNode::clone(srcv.second))); - } - else { - unify_branch( it->second, srcv.second ); - } - } - } -} - -void DecisionTreeNode::unify_from(const Branch& b) -{ - TRACE_FUNCTION_FR(*this << " with " << b, *this); - - assert( b.is_Terminal() || b.is_Subtree() ); - - if( m_default.is_Unset() ) { - if( b.is_Terminal() ) { - m_default = clone(b); - } - else { - m_default = clone(b.as_Subtree()->m_default); - } - } - - if( b.is_Subtree() && b.as_Subtree()->m_branches.tag() != m_branches.tag() ) { - // Is this a bug, or expected (and don't unify in?) - DEBUG("TODO - Unify mismatched arms? - " << b.as_Subtree()->m_branches.tag_str() << " and " << m_branches.tag_str()); - return ; - } - bool should_unify_subtree = b.is_Subtree() && this->m_field_path == b.as_Subtree()->m_field_path; - //if( b.is_Subtree() ) { - // ASSERT_BUG(Span(), this->m_field_path == b.as_Subtree()->m_field_path, "Unifiying DTNs with mismatched paths - " << this->m_field_path << " != " << b.as_Subtree()->m_field_path); - //} - - TU_MATCHA( (m_branches), (dst), - (Unset, - if( b.is_Subtree() ) { - assert( b.as_Subtree()->m_branches.is_Unset() ); - } - else { - // Huh? Terminal matching against an unset branch? - } - ), - (Bool, - auto* src = (b.is_Subtree() ? &b.as_Subtree()->m_branches.as_Bool() : nullptr); - - unify_branch( dst.false_branch, (src ? src->false_branch : b) ); - unify_branch( dst.true_branch , (src ? src->true_branch : b) ); - ), - (Variant, - if( should_unify_subtree ) { - auto& sb = b.as_Subtree()->m_branches; - ASSERT_BUG(Span(), sb.is_Variant(), "Unifying Variant with " << sb.tag_str()); - unify_from_vals_pt(dst, sb.as_Variant()); - } - else { - // Unify all with terminal branch - for(auto& dstv : dst) - { - unify_branch(dstv.second, b); - } - } - ), - (Unsigned, - if( should_unify_subtree ) { - auto& sb = b.as_Subtree()->m_branches; - ASSERT_BUG(Span(), sb.is_Unsigned(), "Unifying Unsigned with " << sb.tag_str()); - unify_from_vals_range(dst, sb.as_Unsigned()); - } - else { - for(auto& dstv : dst) - { - unify_branch(dstv.second, b); - } - } - ), - (Signed, - if( should_unify_subtree ) { - auto& sb = b.as_Subtree()->m_branches; - ASSERT_BUG(Span(), sb.is_Signed(), "Unifying Signed with " << sb.tag_str()); - unify_from_vals_range(dst, sb.as_Signed()); - } - else { - for(auto& dstv : dst) - { - unify_branch(dstv.second, b); - } - } - ), - (Float, - if( should_unify_subtree ) { - auto& sb = b.as_Subtree()->m_branches; - ASSERT_BUG(Span(), sb.is_Float(), "Unifying Float with " << sb.tag_str()); - unify_from_vals_range(dst, sb.as_Float()); - } - else { - for(auto& dstv : dst) { - unify_branch(dstv.second, b); - } - } - ), - (String, - if( should_unify_subtree ) { - auto& sb = b.as_Subtree()->m_branches; - ASSERT_BUG(Span(), sb.is_String(), "Unifying String with " << sb.tag_str()); - unify_from_vals_pt(dst, sb.as_String()); - } - else { - for(auto& dstv : dst) { - unify_branch( dstv.second, b ); - } - } - ), - (Slice, - if( should_unify_subtree ) { - auto& sb = b.as_Subtree()->m_branches; - ASSERT_BUG(Span(), sb.is_Slice(), "Unifying Slice with " << sb.tag_str()); - - const auto& src = sb.as_Slice(); - unify_from_vals_pt(dst.fixed_arms, src.fixed_arms); - } - else { - for(auto& dstv : dst.fixed_arms) { - unify_branch( dstv.second, b ); - } - } - ) - ) -} - -::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode::Branch& x) { - TU_MATCHA( (x), (be), - (Unset, - os << "!"; - ), - (Terminal, - os << "ARM " << be; - ), - (Subtree, - os << *be; - ) - ) - return os; -} -::std::ostream& operator<<(::std::ostream& os, const DecisionTreeNode& x) { - os << "DTN [" << x.m_field_path << "] { "; - TU_MATCHA( (x.m_branches), (e), - (Unset, - os << "!, "; - ), - (Bool, - os << "false = " << e.false_branch << ", true = " << e.true_branch << ", "; - ), - (Variant, - os << "V "; - for(const auto& branch : e) { - os << branch.first << " = " << branch.second << ", "; - } - ), - (Unsigned, - os << "U "; - for(const auto& branch : e) { - const auto& range = branch.first; - if( range.start == range.end ) { - os << range.start; - } - else { - os << range.start << "..." << range.end; - } - os << " = " << branch.second << ", "; - } - ), - (Signed, - os << "S "; - for(const auto& branch : e) { - const auto& range = branch.first; - if( range.start == range.end ) { - os << range.start; - } - else { - os << range.start << "..." << range.end; - } - os << " = " << branch.second << ", "; - } - ), - (Float, - os << "F "; - for(const auto& branch : e) { - const auto& range = branch.first; - if( range.start == range.end ) { - os << range.start; - } - else { - os << range.start << "..." << range.end; - } - os << " = " << branch.second << ", "; - } - ), - (String, - for(const auto& branch : e) { - os << "\"" << branch.first << "\"" << " = " << branch.second << ", "; - } - ), - (Slice, - os << "len "; - for(const auto& branch : e.fixed_arms) { - os << "=" << branch.first << " = " << branch.second << ", "; - } - ) - ) - - os << "* = " << x.m_default; - os << " }"; - return os; -} - - -// ---------------------------- -// DecisionTreeGen -// ---------------------------- - -void DecisionTreeGen::generate_tree_code( - const Span& sp, - const DecisionTreeNode& node, - const ::HIR::TypeRef& top_ty, unsigned int field_path_ofs, const ::MIR::LValue& top_val, - ::std::function and_then - ) -{ - TRACE_FUNCTION_F("top_ty=" << top_ty << ", field_path_ofs=" << field_path_ofs << ", top_val=" << top_val << ", node=" << node); - - ::MIR::LValue val; - ::HIR::TypeRef ty; - - get_ty_and_val(sp, m_builder.resolve(), top_ty, top_val, node.m_field_path, field_path_ofs, ty, val); - DEBUG("ty = " << ty << ", val = " << val); - - TU_MATCHA( (ty.m_data), (e), - (Infer, BUG(sp, "Ivar for in match type"); ), - (Diverge, BUG(sp, "Diverge in match type"); ), - (Primitive, - switch(e) - { - case ::HIR::CoreType::Bool: - ASSERT_BUG(sp, node.m_branches.is_Bool(), "Tree for bool isn't a _Bool - node="<generate_branches_Bool(sp, node.m_default, node.m_branches.as_Bool(), ty, mv$(val), mv$(and_then)); - break; - case ::HIR::CoreType::U8: - case ::HIR::CoreType::U16: - case ::HIR::CoreType::U32: - case ::HIR::CoreType::U64: - case ::HIR::CoreType::U128: - case ::HIR::CoreType::Usize: - ASSERT_BUG(sp, node.m_branches.is_Unsigned(), "Tree for unsigned isn't a _Unsigned - node="<generate_branches_Unsigned(sp, node.m_default, node.m_branches.as_Unsigned(), ty, mv$(val), mv$(and_then)); - break; - case ::HIR::CoreType::I8: - case ::HIR::CoreType::I16: - case ::HIR::CoreType::I32: - case ::HIR::CoreType::I64: - case ::HIR::CoreType::I128: - case ::HIR::CoreType::Isize: - ASSERT_BUG(sp, node.m_branches.is_Signed(), "Tree for unsigned isn't a _Signed - node="<generate_branches_Signed(sp, node.m_default, node.m_branches.as_Signed(), ty, mv$(val), mv$(and_then)); - break; - case ::HIR::CoreType::Char: - ASSERT_BUG(sp, node.m_branches.is_Unsigned(), "Tree for char isn't a _Unsigned - node="<generate_branches_Char(sp, node.m_default, node.m_branches.as_Unsigned(), ty, mv$(val), mv$(and_then)); - break; - case ::HIR::CoreType::Str: - ASSERT_BUG(sp, node.m_branches.is_String(), "Tree for &str isn't a _String - node="<generate_branches_Borrow_str(sp, node.m_default, node.m_branches.as_String(), ty, mv$(val), mv$(and_then)); - break; - case ::HIR::CoreType::F32: - case ::HIR::CoreType::F64: - ASSERT_BUG(sp, node.m_branches.is_Float(), "Tree for float isn't a _Float - node="<generate_branches_Float(sp, node.m_default, node.m_branches.as_Float(), ty, mv$(val), mv$(and_then)); - break; - default: - TODO(sp, "Primitive - " << ty); - break; - } - ), - (Tuple, - BUG(sp, "Decision node on tuple - node=" << node); - ), - (Path, - // This is either a struct destructure or an enum - TU_MATCHA( (e.binding), (pbe), - (Unbound, - BUG(sp, "Encounterd unbound path - " << e.path); - ), - (Opaque, - and_then(node); - ), - (Struct, - assert(pbe); - TU_MATCHA( (pbe->m_data), (fields), - (Unit, - and_then(node); - ), - (Tuple, - BUG(sp, "Decision node on tuple struct"); - ), - (Named, - BUG(sp, "Decision node on struct"); - ) - ) - ), - (Union, - TODO(sp, "Decision node on Union"); - ), - (Enum, - ASSERT_BUG(sp, node.m_branches.is_Variant(), "Tree for enum isn't a Variant - node="<generate_branches_Enum(sp, node.m_default, node.m_branches.as_Variant(), node.m_field_path, ty, mv$(val), mv$(and_then)); - ) - ) - ), - (Generic, - and_then(node); - ), - (TraitObject, - ERROR(sp, E0000, "Attempting to match over a trait object"); - ), - (ErasedType, - ERROR(sp, E0000, "Attempting to match over an erased type"); - ), - (Array, - // TODO: Slice patterns, sequential comparison/sub-match - TODO(sp, "Match over array"); - ), - (Slice, - ASSERT_BUG(sp, node.m_branches.is_Slice(), "Tree for [T] isn't a _Slice - node="<generate_branches_Slice(sp, node.m_default, node.m_branches.as_Slice(), node.m_field_path, ty, mv$(val), mv$(and_then)); - ), - (Borrow, - if( *e.inner == ::HIR::CoreType::Str ) { - TODO(sp, "Match over &str"); - } - else { - BUG(sp, "Decision node on non-str/[T] borrow - " << ty); - } - ), - (Pointer, - ERROR(sp, E0000, "Attempting to match over a pointer"); - ), - (Function, - ERROR(sp, E0000, "Attempting to match over a functon pointer"); - ), - (Closure, - ERROR(sp, E0000, "Attempting to match over a closure"); - ) - ) -} - -void DecisionTreeGen::generate_branch(const DecisionTreeNode::Branch& branch, ::std::function cb) -{ - assert( !branch.is_Unset() ); - if( branch.is_Terminal() ) { - this->m_builder.end_block( ::MIR::Terminator::make_Goto( this->get_block_for_rule( branch.as_Terminal() ) ) ); - } - else { - assert( branch.is_Subtree() ); - const auto& subnode = *branch.as_Subtree(); - - cb(subnode); - } -} - -::MIR::LValue DecisionTreeGen::push_compare(const Span& sp, ::MIR::LValue left, ::MIR::eBinOp op, ::MIR::Param right) -{ - return m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, - ::MIR::RValue::make_BinOp({ mv$(left), op, mv$(right) }) - ); -} - -// TODO: Unify logic for these two, and support simpler checks for sequential values -void DecisionTreeGen::generate_branches_Signed( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Signed& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - auto ity = ty.m_data.as_Primitive(); - auto default_block = m_builder.new_bb_unlinked(); - - // TODO: Convert into an integer switch w/ offset instead of chained comparisons - - for( const auto& branch : branches ) - { - auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked()); - - auto cmp_gt_block = m_builder.new_bb_unlinked(); - auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Int({ branch.first.start, ity })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) ); - m_builder.set_cur_block( cmp_gt_block ); - - auto success_block = m_builder.new_bb_unlinked(); - auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Int({ branch.first.end, ity })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( next_block ); - } - assert( m_builder.block_active() ); - - if( default_branch.is_Unset() ) { - // TODO: Emit error if non-exhaustive - m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); - } - else { - this->generate_branch(default_branch, and_then); - } -} - -void DecisionTreeGen::generate_branches_Unsigned( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Unsigned& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - auto ity = ty.m_data.as_Primitive(); - auto default_block = m_builder.new_bb_unlinked(); - - // TODO: Convert into an integer switch w/ offset instead of chained comparisons - - for( const auto& branch : branches ) - { - auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked()); - - auto cmp_gt_block = m_builder.new_bb_unlinked(); - auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Uint({ branch.first.start, ity })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) ); - m_builder.set_cur_block( cmp_gt_block ); - - auto success_block = m_builder.new_bb_unlinked(); - auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Uint({ branch.first.end, ity })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( next_block ); - } - assert( m_builder.block_active() ); - - if( default_branch.is_Unset() ) { - // TODO: Emit error if non-exhaustive - m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); - } - else { - this->generate_branch(default_branch, and_then); - } -} - -void DecisionTreeGen::generate_branches_Float( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Float& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - auto ity = ty.m_data.as_Primitive(); - auto default_block = m_builder.new_bb_unlinked(); - - for( const auto& branch : branches ) - { - auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked()); - - auto cmp_gt_block = m_builder.new_bb_unlinked(); - auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Float({ branch.first.start, ity })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) ); - m_builder.set_cur_block( cmp_gt_block ); - - auto success_block = m_builder.new_bb_unlinked(); - auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Float({ branch.first.end, ity })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( next_block ); - } - assert( m_builder.block_active() ); - - if( default_branch.is_Unset() ) { - ERROR(sp, E0000, "Match over floating point with no `_` arm"); - } - else { - this->generate_branch(default_branch, and_then); - } -} - -void DecisionTreeGen::generate_branches_Char( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Unsigned& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - auto default_block = m_builder.new_bb_unlinked(); - - // TODO: Convert into an integer switch w/ offset instead of chained comparisons - - for( const auto& branch : branches ) - { - auto next_block = (&branch == &branches.back() ? default_block : m_builder.new_bb_unlinked()); - - auto cmp_gt_block = m_builder.new_bb_unlinked(); - auto val_cmp_lt = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant::make_Uint({ branch.first.start, ::HIR::CoreType::Char })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), default_block, cmp_gt_block }) ); - m_builder.set_cur_block( cmp_gt_block ); - - auto success_block = m_builder.new_bb_unlinked(); - auto val_cmp_gt = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant::make_Uint({ branch.first.end, ::HIR::CoreType::Char })); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( next_block ); - } - assert( m_builder.block_active() ); - - if( default_branch.is_Unset() ) { - // TODO: Error if not exhaustive. - m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); - } - else { - this->generate_branch(default_branch, and_then); - } -} -void DecisionTreeGen::generate_branches_Bool( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Bool& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - //assert( ty.m_data.is_Boolean() ); - - if( default_branch.is_Unset() ) - { - if( branches.false_branch.is_Unset() || branches.true_branch.is_Unset() ) { - // Non-exhaustive match - ERROR - } - } - else - { - if( branches.false_branch.is_Unset() && branches.true_branch.is_Unset() ) { - // Unreachable default (NOTE: Not an error here) - } - } - - // Emit an if based on the route taken - auto bb_false = m_builder.new_bb_unlinked(); - auto bb_true = m_builder.new_bb_unlinked(); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val), bb_true, bb_false }) ); - - // Recurse into sub-patterns - const auto& branch_false = ( !branches.false_branch.is_Unset() ? branches.false_branch : default_branch ); - const auto& branch_true = ( !branches. true_branch.is_Unset() ? branches. true_branch : default_branch ); - - m_builder.set_cur_block(bb_true ); - this->generate_branch(branch_true , and_then); - m_builder.set_cur_block(bb_false); - this->generate_branch(branch_false, and_then); -} - -void DecisionTreeGen::generate_branches_Borrow_str( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_String& branches, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - // TODO: Chained comparisons with ordering. - // - Would this just emit a eBinOp? That implies deep codegen support for strings. - // - rustc emits calls to PartialEq::eq for this and for slices. mrustc could use PartialOrd and fall back to PartialEq if unavaliable? - // > Requires crate access here! - A memcmp call is probably better, probably via a binop - // NOTE: The below implementation gets the final codegen to call memcmp on the strings by emitting eBinOp::{LT,GT} - - // - Remove the wrapping Deref (which must be there) - ASSERT_BUG(sp, val.is_Deref(), "Match over str without a deref - " << val); - auto tmp = mv$( *val.as_Deref().val ); - val = mv$(tmp); - - auto default_bb = m_builder.new_bb_unlinked(); - - // TODO: Binary search? Perfect-Hash-Function? - assert( !branches.empty() ); - for(const auto& branch : branches) - { - auto next_bb = (&branch == &branches.back() ? default_bb : m_builder.new_bb_unlinked()); - - auto cmp_gt_bb = m_builder.new_bb_unlinked(); - - auto lt_val = push_compare(sp, val.clone(), ::MIR::eBinOp::LT, ::MIR::Constant(branch.first) ); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(lt_val), default_bb, cmp_gt_bb }) ); - m_builder.set_cur_block(cmp_gt_bb); - - auto eq_bb = m_builder.new_bb_unlinked(); - auto gt_val = push_compare(sp, val.clone(), ::MIR::eBinOp::GT, ::MIR::Constant(branch.first) ); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(gt_val), next_bb, eq_bb }) ); - m_builder.set_cur_block(eq_bb); - - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block(next_bb); - } - this->generate_branch(default_branch, and_then); -} - -void DecisionTreeGen::generate_branches_Enum( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Variant& branches, - const field_path_t& field_path, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - const auto& enum_ref = *ty.m_data.as_Path().binding.as_Enum(); - const auto& enum_path = ty.m_data.as_Path().path.m_data.as_Generic(); - const auto& variants = enum_ref.m_variants; - auto variant_count = variants.size(); - bool has_any = ! default_branch.is_Unset(); - - if( branches.size() < variant_count && ! has_any ) { - ERROR(sp, E0000, "Non-exhaustive match over " << ty << " - " << branches.size() << " out of " << variant_count << " present"); - } - // DISABLED: Some complex matches don't directly use some defaults - //if( branches.size() == variant_count && has_any ) { - // ERROR(sp, E0000, "Unreachable _ arm - " << branches.size() << " variants in " << enum_path); - //} - - auto any_block = (has_any ? m_builder.new_bb_unlinked() : 0); - - // Emit a switch over the variant - ::std::vector< ::MIR::BasicBlockId> variant_blocks; - variant_blocks.reserve( variant_count ); - for( const auto& branch : branches ) - { - if( variant_blocks.size() != branch.first ) { - assert( variant_blocks.size() < branch.first ); - assert( has_any ); - variant_blocks.resize( branch.first, any_block ); - } - variant_blocks.push_back( m_builder.new_bb_unlinked() ); - } - if( variant_blocks.size() != variant_count ) - { - ASSERT_BUG(sp, variant_blocks.size() < variant_count, "Branch count (" << variant_blocks.size() << ") > variant count (" << variant_count << ") in match of " << ty); - ASSERT_BUG(sp, has_any, "Non-exhaustive match and no any arm"); - variant_blocks.resize( variant_count, any_block ); - } - bool any_arm_used = ::std::any_of( variant_blocks.begin(), variant_blocks.end(), [any_block](const auto& blk){ return blk == any_block; } ); - - m_builder.end_block( ::MIR::Terminator::make_Switch({ - val.clone(), variant_blocks // NOTE: Copies the list, so it can be used lower down - }) ); - - // Emit sub-patterns, looping over variants - for( const auto& branch : branches ) - { - auto bb = variant_blocks[branch.first]; - const auto& var = variants[branch.first]; - DEBUG(branch.first << " " << var.first << " = " << branch.second); - - auto var_lval = ::MIR::LValue::make_Downcast({ box$(val.clone()), branch.first }); - - ::HIR::TypeRef fake_ty; - - TU_MATCHA( (var.second), (e), - (Unit, - DEBUG("- Unit"); - ), - (Value, - DEBUG("- Value"); - ), - (Tuple, - // Make a fake tuple - ::std::vector< ::HIR::TypeRef> ents; - for( const auto& fld : e ) - { - ents.push_back( monomorphise_type(sp, enum_ref.m_params, enum_path.m_params, fld.ent) ); - } - fake_ty = ::HIR::TypeRef( mv$(ents) ); - m_builder.resolve().expand_associated_types(sp, fake_ty); - DEBUG("- Tuple - " << fake_ty); - ), - (Struct, - ::std::vector< ::HIR::TypeRef> ents; - for( const auto& fld : e ) - { - ents.push_back( monomorphise_type(sp, enum_ref.m_params, enum_path.m_params, fld.second.ent) ); - } - fake_ty = ::HIR::TypeRef( mv$(ents) ); - m_builder.resolve().expand_associated_types(sp, fake_ty); - DEBUG("- Struct - " << fake_ty); - ) - ) - - m_builder.set_cur_block( bb ); - if( fake_ty == ::HIR::TypeRef() || fake_ty.m_data.as_Tuple().size() == 0 ) { - this->generate_branch(branch.second, and_then); - } - else { - this->generate_branch(branch.second, [&](auto& subnode) { - // Call special handler to determine when the enum is over - this->generate_tree_code__enum(sp, subnode, fake_ty, var_lval, field_path, and_then); - }); - } - } - - if( any_arm_used ) - { - DEBUG("_ = " << default_branch); - if( !default_branch.is_Unset() ) - { - m_builder.set_cur_block(any_block); - this->generate_branch(default_branch, and_then); - } - } - else - { - DEBUG("_ = UNUSED - " << default_branch); - } -} - -void DecisionTreeGen::generate_branches_Slice( - const Span& sp, - const DecisionTreeNode::Branch& default_branch, - const DecisionTreeNode::Values::Data_Slice& branches, - const field_path_t& field_path, - const ::HIR::TypeRef& ty, ::MIR::LValue val, - ::std::function and_then - ) -{ - if( default_branch.is_Unset() ) { - ERROR(sp, E0000, "Non-exhaustive match over " << ty); - } - - auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val).clone() })); - - // NOTE: Un-deref the slice - ASSERT_BUG(sp, val.is_Deref(), "slice matches must be passed a deref"); - auto tmp = mv$( *val.as_Deref().val ); - val = mv$(tmp); - - auto any_block = m_builder.new_bb_unlinked(); - - // TODO: Select one of three ways of picking the arm: - // - Integer switch (unimplemented) - // - Binary search - // - Sequential comparisons - - // TODO: Binary search instead? - for( const auto& branch : branches.fixed_arms ) - { - auto val_des = ::MIR::Constant::make_Uint({ static_cast(branch.first), ::HIR::CoreType::Usize }); - - // Special case - final just does equality - if( &branch == &branches.fixed_arms.back() ) - { - auto val_cmp_eq = push_compare(sp, val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_des)); - - auto success_block = m_builder.new_bb_unlinked(); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_eq), success_block, any_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( any_block ); - } - // Special case for zero (which can't have a LT) - else if( branch.first == 0 ) - { - auto next_block = m_builder.new_bb_unlinked(); - auto val_cmp_eq = push_compare(sp, val_len.clone(), ::MIR::eBinOp::EQ, mv$(val_des)); - - auto success_block = m_builder.new_bb_unlinked(); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_eq), success_block, next_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( next_block ); - } - // General case, with two comparisons - else - { - auto next_block = m_builder.new_bb_unlinked(); - - auto cmp_gt_block = m_builder.new_bb_unlinked(); - auto val_cmp_lt = push_compare(sp, val_len.clone(), ::MIR::eBinOp::LT, val_des.clone()); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_lt), any_block, cmp_gt_block }) ); - m_builder.set_cur_block( cmp_gt_block ); - - auto success_block = m_builder.new_bb_unlinked(); - auto val_cmp_gt = push_compare(sp, val_len.clone(), ::MIR::eBinOp::GT, mv$(val_des)); - m_builder.end_block( ::MIR::Terminator::make_If({ mv$(val_cmp_gt), next_block, success_block }) ); - - m_builder.set_cur_block( success_block ); - this->generate_branch(branch.second, and_then); - - m_builder.set_cur_block( next_block ); - } - } - assert( m_builder.block_active() ); - - if( default_branch.is_Unset() ) { - // TODO: Emit error if non-exhaustive - m_builder.end_block( ::MIR::Terminator::make_Diverge({}) ); - } - else { - this->generate_branch(default_branch, and_then); - } -} - -namespace { - bool path_starts_with(const field_path_t& test, const field_path_t& prefix) - { - //DEBUG("test="< Date: Thu, 13 Apr 2017 08:51:31 +0800 Subject: MIR Gen - Do value raising for deref operator overloads --- Makefile | 3 +- src/hir_expand/ufcs_everything.cpp | 2 ++ src/mir/from_hir.cpp | 62 ++++++++++++++++++++++++++++++++++++-- src/mir/from_hir_match.cpp | 1 + 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f3db4dba..17fbf665 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,8 @@ OBJ += hir_typeck/outer.o hir_typeck/common.o hir_typeck/helpers.o hir_typeck/st OBJ += hir_typeck/expr_visit.o OBJ += hir_typeck/expr_cs.o OBJ += hir_typeck/expr_check.o -OBJ += hir_expand/annotate_value_usage.o hir_expand/closures.o hir_expand/ufcs_everything.o +OBJ += hir_expand/annotate_value_usage.o hir_expand/closures.o +OBJ += hir_expand/ufcs_everything.o OBJ += hir_expand/reborrow.o hir_expand/erased_types.o hir_expand/vtable.o OBJ += hir_expand/const_eval_full.o OBJ += mir/mir.o mir/mir_ptr.o diff --git a/src/hir_expand/ufcs_everything.cpp b/src/hir_expand/ufcs_everything.cpp index 0562c418..00e7eee1 100644 --- a/src/hir_expand/ufcs_everything.cpp +++ b/src/hir_expand/ufcs_everything.cpp @@ -615,6 +615,7 @@ namespace { m_replacement = NEWNODE( mv$(node.m_res_type), Deref, sp, mv$(m_replacement) ); } +#if 0 void visit(::HIR::ExprNode_Deref& node) override { const auto& sp = node.span(); @@ -693,6 +694,7 @@ namespace { // - Dereference the result (which is an &-ptr) m_replacement = NEWNODE( mv$(node.m_res_type), Deref, sp, mv$(m_replacement) ); } +#endif diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index df48627f..f3f8f745 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -58,6 +58,7 @@ namespace { const ScopeHandle* m_block_tmp_scope = nullptr; const ScopeHandle* m_borrow_raise_target = nullptr; + bool m_in_borrow = false; public: ExprVisitor_Conv(MirBuilder& builder, const ::std::vector< ::HIR::TypeRef>& var_types): @@ -1141,6 +1142,8 @@ namespace { { TRACE_FUNCTION_F("_Borrow"); + auto _ = save_and_edit(m_in_borrow, true); + const auto& ty_val = node.m_value->m_res_type; this->visit_node_ptr(node.m_value); auto val = m_builder.get_result_in_lvalue(node.m_value->span(), ty_val); @@ -1421,10 +1424,63 @@ namespace { if( m_builder.is_type_owned_box( ty_val ) ) { // Box magically derefs. - // HACK: Break out of the switch used for TU_MATCH_DEF - break; } - BUG(sp, "Deref on unsupported type - " << ty_val); + else + { + // TODO: Do operator replacement here after handling scope-raising for _Borrow + if( m_borrow_raise_target && m_in_borrow ) + { + DEBUG("- Raising deref in borrow to scope " << *m_borrow_raise_target); + m_builder.raise_variables(node.span(), val, *m_borrow_raise_target); + } + + + const char* langitem = nullptr; + const char* method = nullptr; + ::HIR::BorrowType bt; + // - Uses the value's usage beacuse for T: Copy node.m_value->m_usage is Borrow, but node.m_usage is Move + switch( node.m_value->m_usage ) + { + case ::HIR::ValueUsage::Unknown: + BUG(sp, "Unknown usage type of deref value"); + break; + case ::HIR::ValueUsage::Borrow: + bt = ::HIR::BorrowType::Shared; + langitem = method = "deref"; + break; + case ::HIR::ValueUsage::Mutate: + bt = ::HIR::BorrowType::Unique; + langitem = method = "deref_mut"; + break; + case ::HIR::ValueUsage::Move: + TODO(sp, "ValueUsage::Move for desugared Deref of " << node.m_value->m_res_type); + break; + } + // Needs replacement, continue + assert(langitem); + assert(method); + + // - Construct trait path - Index* + auto method_path = ::HIR::Path(ty_val.clone(), ::HIR::GenericPath(m_builder.resolve().m_crate.get_lang_item_path(node.span(), langitem), {}), method); + + // Store a borrow of the input value + ::std::vector<::MIR::Param> args; + args.push_back( m_builder.lvalue_or_temp(sp, + ::HIR::TypeRef::new_borrow(bt, node.m_value->m_res_type.clone()), + ::MIR::RValue::make_Borrow({0, bt, mv$(val)}) + ) ); + m_builder.moved_lvalue(node.span(), args[0].as_LValue()); + val = m_builder.new_temporary(::HIR::TypeRef::new_borrow(bt, node.m_res_type.clone())); + // Call the above trait method + // Store result of that call in `val` (which will be derefed below) + auto ok_block = m_builder.new_bb_unlinked(); + auto panic_block = m_builder.new_bb_unlinked(); + m_builder.end_block(::MIR::Terminator::make_Call({ ok_block, panic_block, val.clone(), mv$(method_path), mv$(args) })); + m_builder.set_cur_block(panic_block); + m_builder.end_block(::MIR::Terminator::make_Diverge({})); + + m_builder.set_cur_block(ok_block); + } ), (Pointer, // Deref on a pointer - TODO: Requires unsafe diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index ecc8b6e6..2f248f10 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -1387,6 +1387,7 @@ void PatternRulesetBuilder::append_from(const Span& sp, const ::HIR::Pattern& pa sub_builder.m_field_path.back() = 0; if( pe.trailing.size() ) { + // Needs a way of encoding the negative offset in the field path TODO(sp, "SplitSlice on [T] with trailing - " << pat); } auto trailing = mv$(sub_builder.m_rules); -- cgit v1.2.3 From a223ffb9d0629e8b498f07dc67f056a7fb95385e Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 13 Apr 2017 12:46:01 +0800 Subject: All - Add rough support for #[test] (runs basic tests) --- Makefile | 1 + src/ast/crate.hpp | 21 ++++++++ src/expand/test.cpp | 14 ++++- src/expand/test_harness.cpp | 116 ++++++++++++++++++++++++++++++++++++++++++ src/include/main_bindings.hpp | 1 + src/main.cpp | 24 ++++++++- src/trans/codegen_c.cpp | 1 + src/trans/main_bindings.hpp | 1 + 8 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/expand/test_harness.cpp diff --git a/Makefile b/Makefile index 17fbf665..2e907516 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,7 @@ OBJ += expand/include.o OBJ += expand/env.o OBJ += expand/test.o OBJ += expand/rustc_diagnostics.o +OBJ += expand/test_harness.o OBJ += macro_rules/mod.o macro_rules/eval.o macro_rules/parse.o OBJ += resolve/use.o resolve/index.o resolve/absolute.o OBJ += hir/from_ast.o hir/from_ast_expr.o diff --git a/src/ast/crate.hpp b/src/ast/crate.hpp index b74012a3..f9594a83 100644 --- a/src/ast/crate.hpp +++ b/src/ast/crate.hpp @@ -10,6 +10,23 @@ namespace AST { class ExternCrate; +class TestDesc +{ +public: + ::AST::Path path; + ::std::string name; + bool ignore = false; + bool is_benchmark = false; + + enum class ShouldPanic { + No, + Yes, + YesWithMessage, + } panic_type = ShouldPanic::No; + + ::std::string expected_panic_message; +}; + class Crate { public: @@ -22,6 +39,10 @@ public: // Mapping filled by searching for (?visible) macros with is_pub=true ::std::map< ::std::string, const MacroRules*> m_exported_macros; + // List of tests (populated in expand if --test is passed) + bool m_test_harness = false; + ::std::vector m_tests; + enum class Type { Unknown, RustLib, diff --git a/src/expand/test.cpp b/src/expand/test.cpp index fba6556f..26639203 100644 --- a/src/expand/test.cpp +++ b/src/expand/test.cpp @@ -7,6 +7,7 @@ */ #include #include +#include class CTestHandler: public ExpandDecorator @@ -18,8 +19,17 @@ class CTestHandler: ERROR(sp, E0000, "#[test] can only be put on functions - found on " << i.tag_str()); } - // TODO: Proper #[test] support, for now just remove them - i = AST::Item::make_None({}); + if( crate.m_test_harness ) + { + ::AST::TestDesc td; + td.name = path.nodes().back().name(); + td.path = ::AST::Path(path); + crate.m_tests.push_back( mv$(td) ); + } + else + { + i = AST::Item::make_None({}); + } } }; diff --git a/src/expand/test_harness.cpp b/src/expand/test_harness.cpp new file mode 100644 index 00000000..883bd2aa --- /dev/null +++ b/src/expand/test_harness.cpp @@ -0,0 +1,116 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * expand/mod.cpp + * - Expand pass core code + */ +#include +#include +#include +#include +#include // ABI_RUST + +#define NEWNODE(_ty, ...) ::AST::ExprNodeP(new ::AST::ExprNode##_ty(__VA_ARGS__)) + +void Expand_TestHarness(::AST::Crate& crate) +{ + // Create the following module: + // ``` + // mod `#test` { + // extern crate std; + // extern crate test; + // fn main() { + // self::test::test_main_static(&::`#test`::TESTS); + // } + // static TESTS: [test::TestDescAndFn; _] = [ + // test::TestDescAndFn { desc: test::TestDesc { name: "foo", ignore: false, should_panic: test::ShouldPanic::No }, testfn: ::path::to::foo }, + // ]; + // } + // ``` + + // ---- main function ---- + auto main_fn = ::AST::Function { Span(), {}, ABI_RUST, false, false, false, TypeRef(TypeRef::TagUnit(), Span()), {} }; + { + auto call_node = NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode("test_main_static") }), + ::make_vec1( + NEWNODE(_UniOp, ::AST::ExprNode_UniOp::REF, + NEWNODE(_NamedValue, ::AST::Path("", { ::AST::PathNode("test#"), ::AST::PathNode("TESTS") })) + ) + ) + ); + main_fn.set_code( mv$(call_node) ); + } + + + // ---- test list ---- + ::std::vector< ::AST::ExprNodeP> test_nodes; + + for(const auto& test : crate.m_tests) + { + ::AST::ExprNode_StructLiteral::t_values desc_vals; + // `name: "foo",` + desc_vals.push_back( ::std::make_pair("name", NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode("StaticTestName") }), + ::make_vec1( NEWNODE(_String, test.name) ) + ) )); + // `ignore: false,` + desc_vals.push_back( ::std::make_pair("ignore", NEWNODE(_Bool, test.ignore)) ); + // `should_panic: ShouldPanic::No,` + { + ::AST::ExprNodeP should_panic_val; + switch(test.panic_type) + { + case ::AST::TestDesc::ShouldPanic::No: + should_panic_val = NEWNODE(_NamedValue, ::AST::Path("test", { ::AST::PathNode("ShouldPanic"), ::AST::PathNode("No") })); + break; + case ::AST::TestDesc::ShouldPanic::Yes: + should_panic_val = NEWNODE(_NamedValue, ::AST::Path("test", { ::AST::PathNode("ShouldPanic"), ::AST::PathNode("Yes") })); + break; + case ::AST::TestDesc::ShouldPanic::YesWithMessage: + should_panic_val = NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode("ShouldPanic"), ::AST::PathNode("YesWithMessage") }), + make_vec1( NEWNODE(_String, test.expected_panic_message) ) + ); + break; + } + desc_vals.push_back( ::std::make_pair("should_panic", mv$(should_panic_val)) ); + } + auto desc_expr = NEWNODE(_StructLiteral, ::AST::Path("test", { ::AST::PathNode("TestDesc")}), nullptr, mv$(desc_vals)); + + ::AST::ExprNode_StructLiteral::t_values descandfn_vals; + descandfn_vals.push_back( ::std::make_pair(::std::string("desc"), mv$(desc_expr)) ); + + auto test_type_var_name = test.is_benchmark ? "StaticBenchFn" : "StaticTestFn"; + descandfn_vals.push_back( ::std::make_pair(::std::string("testfn"), NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode(test_type_var_name) }), + ::make_vec1( NEWNODE(_NamedValue, AST::Path(test.path)) ) + ) ) ); + + test_nodes.push_back( NEWNODE(_StructLiteral, ::AST::Path("test", { ::AST::PathNode("TestDescAndFn")}), nullptr, mv$(descandfn_vals) ) ); + } + auto* tests_array = new ::AST::ExprNode_Array(mv$(test_nodes)); + + size_t test_count = tests_array->m_values.size(); + auto tests_list = ::AST::Static { ::AST::Static::Class::STATIC, + TypeRef(TypeRef::TagSizedArray(), Span(), + TypeRef(Span(), ::AST::Path("test", { ::AST::PathNode("TestDescAndFn") })), + ::std::shared_ptr<::AST::ExprNode>( new ::AST::ExprNode_Integer(test_count, CORETYPE_UINT) ) + ), + ::AST::Expr( mv$(tests_array) ) + }; + + // ---- module ---- + auto newmod = ::AST::Module { ::AST::Path("", { ::AST::PathNode("test#") }) }; + // - TODO: These need to be loaded too. + // > They don't actually need to exist here, just be loaded (and use absolute paths) + newmod.add_ext_crate(false, "std", "std", {}); + newmod.add_ext_crate(false, "test", "test", {}); + + newmod.add_item(false, "main", mv$(main_fn), {}); + newmod.add_item(false, "TESTS", mv$(tests_list), {}); + + crate.m_root_module.add_item(false, "test#", mv$(newmod), {}); + crate.m_lang_items["mrustc-main"] = ::AST::Path("", { AST::PathNode("test#"), AST::PathNode("main") }); +} diff --git a/src/include/main_bindings.hpp b/src/include/main_bindings.hpp index c01ff86d..184f266f 100644 --- a/src/include/main_bindings.hpp +++ b/src/include/main_bindings.hpp @@ -16,6 +16,7 @@ extern AST::Crate Parse_Crate(::std::string mainfile); extern void Expand(::AST::Crate& crate); +extern void Expand_TestHarness(::AST::Crate& crate); /// Process #[] decorators extern void Process_Decorators(AST::Crate& crate); diff --git a/src/main.cpp b/src/main.cpp index c534ff15..0c181c29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -134,6 +134,8 @@ struct ProgramParams unsigned opt_level = 0; bool emit_debug_info = false; + bool test_harness = false; + ::std::vector lib_search_dirs; ::std::vector libraries; @@ -192,6 +194,11 @@ int main(int argc, char *argv[]) }); + if( params.test_harness ) + { + Cfg_SetFlag("test"); + } + try { @@ -199,6 +206,7 @@ int main(int argc, char *argv[]) AST::Crate crate = CompilePhase("Parse", [&]() { return Parse_Crate(params.infile); }); + crate.m_test_harness = params.test_harness; if( params.last_stage == ProgramParams::STAGE_PARSE ) { return 0; @@ -214,6 +222,12 @@ int main(int argc, char *argv[]) Expand(crate); }); + if( params.test_harness ) + { + // TODO: Generate harness main (and override the mrustc-main lang item) + Expand_TestHarness(crate); + } + // Extract the crate type and name from the crate attributes auto crate_type = params.crate_type; if( crate_type == ::AST::Crate::Type::Unknown ) { @@ -286,7 +300,7 @@ int main(int argc, char *argv[]) } // Allocator and panic strategies - if( crate.m_crate_type == ::AST::Crate::Type::Executable ) + if( crate.m_crate_type == ::AST::Crate::Type::Executable || params.test_harness ) { // TODO: Detect if an allocator crate is already present. crate.load_extern_crate(Span(), "alloc_system"); @@ -460,6 +474,11 @@ int main(int argc, char *argv[]) trans_opt.emit_debug_info = params.emit_debug_info; // Generate code for non-generic public items (if requested) + if( params.test_harness ) + { + // If the test harness is enabled, override crate type to "Executable" + crate_type = ::AST::Crate::Type::Executable; + } switch( crate_type ) { case ::AST::Crate::Type::Unknown: @@ -684,6 +703,9 @@ ProgramParams::ProgramParams(int argc, char *argv[]) exit(1); } } + else if( strcmp(arg, "--test") == 0 ) { + this->test_harness = true; + } else { ::std::cerr << "Unknown option '" << arg << "'" << ::std::endl; exit(1); diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index e74231e8..5d599dd5 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -123,6 +123,7 @@ namespace { << "(uint8_t*)" << Trans_Mangle( ::HIR::GenericPath(m_resolve.m_crate.get_lang_item_path(Span(), "mrustc-main")) ) << ", argc, (uint8_t**)argv" << ");\n"; } + // TODO: Test framework? else { m_of << "\t" << Trans_Mangle(::HIR::GenericPath(c_start_path)) << "(argc, argv);\n"; diff --git a/src/trans/main_bindings.hpp b/src/trans/main_bindings.hpp index 2878cc66..59933863 100644 --- a/src/trans/main_bindings.hpp +++ b/src/trans/main_bindings.hpp @@ -23,6 +23,7 @@ struct TransOptions }; extern TransList Trans_Enumerate_Main(const ::HIR::Crate& crate); +extern TransList Trans_Enumerate_Test(const ::HIR::Crate& crate); // NOTE: This also sets the saveout flags extern TransList Trans_Enumerate_Public(::HIR::Crate& crate); -- cgit v1.2.3 From f162dc9a43b209a000d90533176f6321e9192b8d Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 13 Apr 2017 13:10:26 +0800 Subject: Codegen C - Fix precedence error in 64/128 bit ctz --- src/trans/codegen_c.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 5d599dd5..165486ef 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -84,7 +84,7 @@ namespace { << "\treturn (v >> 32 != 0 ? __builtin_clz(v>>32) : 32 + __builtin_clz(v));\n" << "}\n" << "static inline uint64_t __builtin_ctz64(uint64_t v) {\n" - << "\treturn (v&0xFFFFFFFF == 0 ? __builtin_ctz(v>>32) + 32 : __builtin_ctz(v));\n" + << "\treturn ((v&0xFFFFFFFF) == 0 ? __builtin_ctz(v>>32) + 32 : __builtin_ctz(v));\n" << "}\n" << "static inline unsigned __int128 __builtin_bswap128(unsigned __int128 v) {\n" << "\tuint64_t lo = __builtin_bswap64((uint64_t)v);\n" @@ -95,7 +95,7 @@ namespace { << "\treturn (v >> 64 != 0 ? __builtin_clz64(v>>64) : 64 + __builtin_clz64(v));\n" << "}\n" << "static inline unsigned __int128 __builtin_ctz128(unsigned __int128 v) {\n" - << "\treturn (v&0xFFFFFFFFFFFFFFFF == 0 ? __builtin_ctz64(v>>64) + 64 : __builtin_ctz64(v));\n" + << "\treturn ((v&0xFFFFFFFFFFFFFFFF) == 0 ? __builtin_ctz64(v>>64) + 64 : __builtin_ctz64(v));\n" << "}\n" << "\n" << "static inline void noop_drop(void *p) {}\n" -- cgit v1.2.3 From 285305b6fb1cd94a47332811fb34f599af8ad795 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 13 Apr 2017 14:05:19 +0800 Subject: Expand - Handle #[should_panic] and #[ignore] attributes for tests - Also doesn't emit tests that should_panic (because of lack of unwind support) --- src/expand/test.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++ src/expand/test_harness.cpp | 4 +++ 2 files changed, 64 insertions(+) diff --git a/src/expand/test.cpp b/src/expand/test.cpp index 26639203..707d0795 100644 --- a/src/expand/test.cpp +++ b/src/expand/test.cpp @@ -24,6 +24,7 @@ class CTestHandler: ::AST::TestDesc td; td.name = path.nodes().back().name(); td.path = ::AST::Path(path); + crate.m_tests.push_back( mv$(td) ); } else @@ -32,6 +33,65 @@ class CTestHandler: } } }; +class CTestHandler_SP: + public ExpandDecorator +{ + AttrStage stage() const override { return AttrStage::Pre; } + + void handle(const Span& sp, const AST::MetaItem& mi, ::AST::Crate& crate, const AST::Path& path, AST::Module& mod, AST::Item&i) const override { + if( ! i.is_Function() ) { + ERROR(sp, E0000, "#[should_panic] can only be put on functions - found on " << i.tag_str()); + } + + if( crate.m_test_harness ) + { + for(auto& td : crate.m_tests) + { + if( td.path != path ) + continue ; + + if( mi.has_sub_items() ) + { + td.panic_type = ::AST::TestDesc::ShouldPanic::YesWithMessage; + // TODO: Check that name is correct and that it is a string + td.expected_panic_message = mi.items().at(0).string(); + } + else + { + td.panic_type = ::AST::TestDesc::ShouldPanic::Yes; + } + return ; + } + //ERROR() + } + } +}; +class CTestHandler_Ignore: + public ExpandDecorator +{ + AttrStage stage() const override { return AttrStage::Pre; } + + void handle(const Span& sp, const AST::MetaItem& mi, ::AST::Crate& crate, const AST::Path& path, AST::Module& mod, AST::Item&i) const override { + if( ! i.is_Function() ) { + ERROR(sp, E0000, "#[should_panic] can only be put on functions - found on " << i.tag_str()); + } + + if( crate.m_test_harness ) + { + for(auto& td : crate.m_tests) + { + if( td.path != path ) + continue ; + + td.ignore = true; + return ; + } + //ERROR() + } + } +}; STATIC_DECORATOR("test", CTestHandler); +STATIC_DECORATOR("should_panic", CTestHandler_SP); +STATIC_DECORATOR("ignore", CTestHandler_Ignore); diff --git a/src/expand/test_harness.cpp b/src/expand/test_harness.cpp index 883bd2aa..12d32121 100644 --- a/src/expand/test_harness.cpp +++ b/src/expand/test_harness.cpp @@ -49,6 +49,10 @@ void Expand_TestHarness(::AST::Crate& crate) for(const auto& test : crate.m_tests) { + // HACK: Don't emit should_panic tests + if( test.panic_type != ::AST::TestDesc::ShouldPanic::No ) + continue ; + ::AST::ExprNode_StructLiteral::t_values desc_vals; // `name: "foo",` desc_vals.push_back( ::std::make_pair("name", NEWNODE(_CallPath, -- cgit v1.2.3 From ffaaf8047e615206e5eb27097a0b6e669f521b96 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 13 Apr 2017 15:35:06 +0800 Subject: Expand - Tweaks to test harness --- Makefile | 15 ++++++++++++++- src/expand/format_args.cpp | 1 + src/expand/mod.cpp | 2 +- src/main.cpp | 5 ++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2e907516..9ae61cd0 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,20 @@ output/lib%.hir: $(RUSTCSRC)src/lib%/src/lib.rs $(RUSTCSRC) $(BIN) $(DBG) $(ENV_$@) $(BIN) $< -o $@ $(RUST_FLAGS) $(ARGS_$@) $(PIPECMD) # # HACK: Work around gdb returning success even if the program crashed @test -e $@ - +output/lib%-test: $(RUSTCSRC)src/lib%/lib.rs $(RUSTCSRC) $(BIN) + @echo "--- [MRUSTC] --test -o $@" + @mkdir -p output/ + @rm -f $@ + $(DBG) $(ENV_$@) $(BIN) --test $< -o $@ -L output/libs $(RUST_FLAGS) $(ARGS_$@) $(PIPECMD) +# # HACK: Work around gdb returning success even if the program crashed + @test -e $@ +output/lib%-test: $(RUSTCSRC)src/lib%/src/lib.rs $(RUSTCSRC) $(BIN) + @echo "--- [MRUSTC] $@" + @mkdir -p output/ + @rm -f $@ + $(DBG) $(ENV_$@) $(BIN) --test $< -o $@ -L output/libs $(RUST_FLAGS) $(ARGS_$@) $(PIPECMD) +# # HACK: Work around gdb returning success even if the program crashed + @test -e $@ fcn_extcrate = $(patsubst %,output/lib%.hir,$(1)) fn_getdeps = \ diff --git a/src/expand/format_args.cpp b/src/expand/format_args.cpp index 0f881a6e..89503e0c 100644 --- a/src/expand/format_args.cpp +++ b/src/expand/format_args.cpp @@ -251,6 +251,7 @@ namespace { if( *s == '0' ) { args.zero_pad = true; + args.align_char = '0'; s ++; } else { diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp index f152b1ab..df0a26f2 100644 --- a/src/expand/mod.cpp +++ b/src/expand/mod.cpp @@ -67,7 +67,7 @@ void Expand_Attrs(/*const */::AST::MetaItems& attrs, AttrStage stage, ::std::fu } void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate, const ::AST::Path& path, ::AST::Module& mod, ::AST::Item& item) { - Expand_Attrs(attrs, stage, [&](const auto& sp, const auto& d, const auto& a){ d.handle(sp, a, crate, path, mod, item); }); + Expand_Attrs(attrs, stage, [&](const auto& sp, const auto& d, const auto& a){ if(!item.is_None()) d.handle(sp, a, crate, path, mod, item); }); } void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate, ::AST::Module& mod, ::AST::ImplDef& impl) { diff --git a/src/main.cpp b/src/main.cpp index 0c181c29..2f392bb7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -224,7 +224,6 @@ int main(int argc, char *argv[]) if( params.test_harness ) { - // TODO: Generate harness main (and override the mrustc-main lang item) Expand_TestHarness(crate); } @@ -273,6 +272,10 @@ int main(int argc, char *argv[]) } } crate.m_crate_name = crate_name; + if( params.test_harness ) + { + crate.m_crate_name += "$test"; + } if( params.outfile == "" ) { switch( crate.m_crate_type ) -- cgit v1.2.3 From 188215b080fd7d18a1f0ba67bfc148e03ad39860 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 13 Apr 2017 17:11:45 +0800 Subject: Expand - Ordering of macro_use lookup --- src/expand/mod.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp index df0a26f2..88d16216 100644 --- a/src/expand/mod.cpp +++ b/src/expand/mod.cpp @@ -109,7 +109,8 @@ void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate return e; } } - // TODO: Shouldn't this use the _last_ located macro? Allowing later (local) defininitions to override it? + // Find the last macro of this name (allows later #[macro_use] definitions to override) + const MacroRules* last_mac = nullptr; for( const auto& mri : mac_mod.macro_imports_res() ) { //DEBUG("- " << mri.name); @@ -118,10 +119,14 @@ void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate if( input_ident != "" ) ERROR(mi_span, E0000, "macro_rules! macros can't take an ident"); - auto e = Macro_Invoke(name.c_str(), *mri.data, mv$(input_tt), mod); - return e; + last_mac = mri.data; } } + if( last_mac ) + { + auto e = Macro_Invoke(name.c_str(), *last_mac, mv$(input_tt), mod); + return e; + } } // Error - Unknown macro name -- cgit v1.2.3 From aa60fa39a73d59c5f4036dd18bfc60666800317b Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 13 Apr 2017 17:16:34 +0800 Subject: Parse - Allow :expr/:path after .. --- src/parse/expr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index d4edb208..15a3e179 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -669,6 +669,9 @@ bool Parse_IsTokValue(eTokenType tok_type) case TOK_PAREN_OPEN: case TOK_SQUARE_OPEN: + case TOK_INTERPOLATED_PATH: + case TOK_INTERPOLATED_EXPR: + case TOK_MACRO: case TOK_PIPE: -- cgit v1.2.3 From 0b37099dca13d7bb82e8843cfc3431e5e74784c8 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 14 Apr 2017 09:55:22 +0800 Subject: Lex - Fix incorrect encoding of UTF-8 escapes --- src/parse/lex.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parse/lex.cpp b/src/parse/lex.cpp index 7b99c433..57a0bedc 100644 --- a/src/parse/lex.cpp +++ b/src/parse/lex.cpp @@ -1010,12 +1010,12 @@ bool Codepoint::isxdigit() const { s += (char)(0xC0 | ((cp.v >> 6) & 0x1F)); s += (char)(0x80 | ((cp.v >> 0) & 0x3F)); } - else if( cp.v <= (0x0F+1)<<(2*6) ) { + else if( cp.v < (0x0F+1)<<(2*6) ) { s += (char)(0xE0 | ((cp.v >> 12) & 0x0F)); s += (char)(0x80 | ((cp.v >> 6) & 0x3F)); s += (char)(0x80 | ((cp.v >> 0) & 0x3F)); } - else if( cp.v <= (0x07+1)<<(3*6) ) { + else if( cp.v < (0x07+1)<<(3*6) ) { s += (char)(0xF0 | ((cp.v >> 18) & 0x07)); s += (char)(0x80 | ((cp.v >> 12) & 0x3F)); s += (char)(0x80 | ((cp.v >> 6) & 0x3F)); @@ -1035,12 +1035,12 @@ bool Codepoint::isxdigit() const { os << (char)(0xC0 | ((cp.v >> 6) & 0x1F)); os << (char)(0x80 | ((cp.v >> 0) & 0x3F)); } - else if( cp.v <= (0x0F+1)<<(2*6) ) { + else if( cp.v < (0x0F+1)<<(2*6) ) { os << (char)(0xE0 | ((cp.v >> 12) & 0x0F)); os << (char)(0x80 | ((cp.v >> 6) & 0x3F)); os << (char)(0x80 | ((cp.v >> 0) & 0x3F)); } - else if( cp.v <= (0x07+1)<<(2*6) ) { + else if( cp.v < (0x07+1)<<(2*6) ) { os << (char)(0xF0 | ((cp.v >> 18) & 0x07)); os << (char)(0x80 | ((cp.v >> 12) & 0x3F)); os << (char)(0x80 | ((cp.v >> 6) & 0x3F)); -- cgit v1.2.3 From 8a12037b9f8c3a18c860e6f392ba9cebb3ed5aa6 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 14 Apr 2017 12:40:53 +0800 Subject: AST Expr - Clone module pointer when cloning a block with a module --- src/ast/expr.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ast/expr.cpp b/src/ast/expr.cpp index c35700e7..dd586683 100644 --- a/src/ast/expr.cpp +++ b/src/ast/expr.cpp @@ -85,9 +85,7 @@ NODE(ExprNode_Block, { ::std::vector nodes; for(const auto& n : m_nodes) nodes.push_back( n->clone() ); - if( m_local_mod ) - TODO(get_pos(), "Handle cloning ExprNode_Block with a module"); - return NEWNODE(ExprNode_Block, m_is_unsafe, m_yields_final_value, mv$(nodes), nullptr); + return NEWNODE(ExprNode_Block, m_is_unsafe, m_yields_final_value, mv$(nodes), m_local_mod); }) NODE(ExprNode_Macro, { -- cgit v1.2.3 From 2acb5a8ac29ddc6f6894802d255d686fdb18329d Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 11:40:20 +0800 Subject: common - Expand FmtEscaped --- src/common.hpp | 15 ++------------- src/main.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/common.hpp b/src/common.hpp index 63125b0e..d3aca257 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -328,19 +328,8 @@ struct FmtEscaped { FmtEscaped(const ::std::string& s): s(s.c_str()) {} - friend ::std::ostream& operator<<(::std::ostream& os, const FmtEscaped& x) { - for(auto s = x.s; *s != '\0'; s ++) - { - switch(*s) - { - case '\n': os << "\\n"; break; - case '\\': os << "\\\\"; break; - case '"': os << "\\\""; break; - default: os << *s; break; - } - } - return os; - } + // See main.cpp + friend ::std::ostream& operator<<(::std::ostream& os, const FmtEscaped& x); }; // ------------------------------------------------------------------- diff --git a/src/main.cpp b/src/main.cpp index 2f392bb7..a9e4868b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -717,3 +717,54 @@ ProgramParams::ProgramParams(int argc, char *argv[]) } } + +::std::ostream& operator<<(::std::ostream& os, const FmtEscaped& x) +{ + os << ::std::hex; + for(auto s = x.s; *s != '\0'; s ++) + { + switch(*s) + { + case '\0': os << "\\0"; break; + case '\n': os << "\\n"; break; + case '\\': os << "\\\\"; break; + case '"': os << "\\\""; break; + default: + uint8_t v = *s; + if( v < 0x80 ) + { + if( v < ' ' || v > 0x7F ) + os << "\\u{" << ::std::hex << (unsigned int)v << "}"; + else + os << v; + } + else if( v < 0xC0 ) + ; + else if( v < 0xE0 ) + { + uint32_t val = (uint32_t)(v & 0x1F) << 6; + v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 6; + os << "\\u{" << ::std::hex << val << "}"; + } + else if( v < 0xF0 ) + { + uint32_t val = (uint32_t)(v & 0x0F) << 12; + v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 12; + v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 6; + os << "\\u{" << ::std::hex << val << "}"; + } + else if( v < 0xF8 ) + { + uint32_t val = (uint32_t)(v & 0x07) << 18; + v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 18; + v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 12; + v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 6; + os << "\\u{" << ::std::hex << val << "}"; + } + break; + } + } + os << ::std::dec; + return os; +} + -- cgit v1.2.3 From 8ba1fb776a5a61d3e8b7680e37232bd431f6f7f0 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 11:40:49 +0800 Subject: HIR Type Match - Handle matching two Infers --- src/hir/type.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/hir/type.cpp b/src/hir/type.cpp index 9b0bf766..7168da53 100644 --- a/src/hir/type.cpp +++ b/src/hir/type.cpp @@ -606,7 +606,26 @@ bool ::HIR::TypeRef::match_test_generics(const Span& sp, const ::HIR::TypeRef& x return Compare::Unequal; } TU_MATCH(::HIR::TypeRef::Data, (v.m_data, x.m_data), (te, xe), - (Infer, throw "";), + (Infer, + // Both sides are infer + switch(te.ty_class) + { + case ::HIR::InferClass::None: + case ::HIR::InferClass::Diverge: + return Compare::Fuzzy; + default: + switch(xe.ty_class) + { + case ::HIR::InferClass::None: + case ::HIR::InferClass::Diverge: + return Compare::Fuzzy; + default: + if( te.ty_class != xe.ty_class ) + return Compare::Unequal; + return Compare::Fuzzy; + } + } + ), (Generic, throw "";), (Primitive, return (te == xe ? Compare::Equal : Compare::Unequal); -- cgit v1.2.3 From 3a1797867580bc17a63a49bdf340da1a3188c676 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 11:41:05 +0800 Subject: Expand/test - Full path as test name --- src/expand/test.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/expand/test.cpp b/src/expand/test.cpp index 707d0795..01e566ff 100644 --- a/src/expand/test.cpp +++ b/src/expand/test.cpp @@ -22,7 +22,11 @@ class CTestHandler: if( crate.m_test_harness ) { ::AST::TestDesc td; - td.name = path.nodes().back().name(); + for(const auto& node : path.nodes()) + { + td.name += "::"; + td.name += node.name(); + } td.path = ::AST::Path(path); crate.m_tests.push_back( mv$(td) ); -- cgit v1.2.3 From 9c662e64f282249d07b08c7eaa99f8613a637934 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 11:41:47 +0800 Subject: Codegen C - Fix incorrect escaped values --- src/trans/codegen_c.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 165486ef..983bdfd4 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -193,6 +193,7 @@ namespace { ::std::stringstream cmd_ss; for(const auto& arg : args) { + // TODO: use a formatter specific to shell escaping cmd_ss << "\"" << FmtEscaped(arg) << "\" "; } DEBUG("- " << cmd_ss.str()); @@ -1376,6 +1377,7 @@ namespace { m_of << "\t__asm__ "; if(is_volatile) m_of << "__volatile__"; // TODO: Convert format string? + // TODO: Use a C-specific escaper here. m_of << "(\"" << (is_intel ? ".syntax intel; " : "") << FmtEscaped(e.tpl) << (is_intel ? ".syntax att; " : "") << "\""; m_of << ": "; for(unsigned int i = 0; i < e.outputs.size(); i ++ ) @@ -2718,7 +2720,7 @@ namespace { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else - m_of << "\\" << (unsigned int)v; + m_of << "\\" << ((unsigned int)v & 0xFF); } m_of << "\"" << ::std::dec; m_of << ";\n\t"; @@ -2924,7 +2926,7 @@ namespace { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else - m_of << "\\" << (unsigned int)v; + m_of << "\\" << ((unsigned int)v & 0xFF); } m_of << "\"" << ::std::dec; ), @@ -2934,7 +2936,7 @@ namespace { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else - m_of << "\\" << (unsigned int)v; + m_of << "\\" << ((unsigned int)v & 0xFF); } m_of << "\", " << ::std::dec << c.size() << ")"; ), -- cgit v1.2.3 From 95c5fc3244bce0a588f332122d97062fd583803d Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 13:03:40 +0800 Subject: MIR - Fix string printing to escape values --- src/mir/mir.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index da07ec5a..303f7764 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -33,20 +33,13 @@ namespace MIR { else if( v < 16 ) os << "\\x0" << (unsigned int)v; else - os << "\\x" << (unsigned int)v; + os << "\\x" << ((unsigned int)v & 0xFF); } os << "\""; os << ::std::dec; ), (StaticString, - os << "\""; - for(auto v : e) { - if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) - os << v; - else - os << "\\u{" << FMT(::std::hex << (unsigned int)v) << "}"; - } - os << "\""; + os << "\"" << FmtEscaped(e) << "\""; ), (Const, os << e.p; -- cgit v1.2.3 From 84a6016c712f0a0d3d7077becffbbd445c07ce9f Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 17:23:25 +0800 Subject: MIR - Print SetDropFlag statements --- src/mir/mir.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index 303f7764..3c16af37 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -466,11 +466,22 @@ namespace MIR { os << "), clobbers=[" << e.clobbers << "], flags=[" << e.flags << "])"; ), (SetDropFlag, + os << "df$" << e.idx << " = "; + if( e.other == ~0u ) + { + os << e.new_val; + } + else + { + os << (e.new_val ? "!" : "") << "df$" << e.other; + } ), (Drop, os << "drop(" << e.slot; if(e.kind == ::MIR::eDropKind::SHALLOW) os << " SHALLOW"; + if(e.flag_idx != ~0u) + os << "IF df$" << e.flag_idx; os << ")"; ) ) -- cgit v1.2.3 From 1bf71b5483649cbf8ee9f92805583a6cef1b2d39 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Apr 2017 17:30:36 +0800 Subject: MIR Gen - Refactor value tracking to split move-out and destructure --- src/mir/from_hir.hpp | 24 +- src/mir/mir_builder.cpp | 821 +++++++++++++++++++++++++++++++----------------- 2 files changed, 547 insertions(+), 298 deletions(-) diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp index 2a5da9fe..5b0b8904 100644 --- a/src/mir/from_hir.hpp +++ b/src/mir/from_hir.hpp @@ -53,10 +53,13 @@ enum class InvalidType { TAGGED_UNION_EX(VarState, (), Invalid, ( // Currently invalid (Invalid, InvalidType), - // Partially valid (Map of field states, Box is assumed to have one field) + // Partially valid (Map of field states) (Partial, struct { ::std::vector inner_states; - unsigned int outer_flag = ~0u; // If ~0u there's no condition on the outer + }), + (MovedOut, struct { + ::std::unique_ptr inner_state; + unsigned int outer_flag; }), // Optionally valid (integer indicates the drop flag index) (Optional, unsigned int), @@ -104,6 +107,14 @@ TAGGED_UNION(ScopeType, Variables, }) ); +enum class VarGroup +{ + Return, + Argument, + Variable, + Temporary, +}; + /// Helper class to construct MIR class MirBuilder { @@ -123,7 +134,8 @@ class MirBuilder bool m_result_valid; // TODO: Extra information. - //::std::vector m_arg_states; + VarState m_return_state; + ::std::vector m_arg_states; ::std::vector m_variable_states; ::std::vector m_temporary_states; @@ -229,11 +241,17 @@ public: // Helper - Marks a variable/... as moved (and checks if the move is valid) void moved_lvalue(const Span& sp, const ::MIR::LValue& lv); private: + const VarState& get_slot_state(const Span& sp, VarGroup ty, unsigned int idx, unsigned int skip_count=0) const; + VarState& get_slot_state_mut(const Span& sp, VarGroup ty, unsigned int idx); + const VarState& get_variable_state(const Span& sp, unsigned int idx, unsigned int skip_count=0) const; VarState& get_variable_state_mut(const Span& sp, unsigned int idx); const VarState& get_temp_state(const Span& sp, unsigned int idx, unsigned int skip_count=0) const; VarState& get_temp_state_mut(const Span& sp, unsigned int idx); + const VarState& get_val_state(const Span& sp, const ::MIR::LValue& lv, unsigned int skip_count=0); + VarState& get_val_state_mut(const Span& sp, const ::MIR::LValue& lv); + 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); diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 96bb28b3..ebfe68dc 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -32,8 +32,11 @@ MirBuilder::MirBuilder(const Span& sp, const StaticTraitResolve& resolve, const m_scopes.push_back( ScopeDef { sp, ScopeType::make_Temporaries({}) } ); m_scope_stack.push_back( 1 ); + m_arg_states.reserve( args.size() ); + for(size_t i = 0; i < args.size(); i ++ ) + m_arg_states.push_back( VarState::make_Valid({}) ); m_variable_states.reserve( output.named_variables.size() ); - for(unsigned int i = 0; i < output.named_variables.size(); i ++ ) + for(size_t i = 0; i < output.named_variables.size(); i ++ ) m_variable_states.push_back( VarState::make_Invalid(InvalidType::Uninit) ); } MirBuilder::~MirBuilder() @@ -313,10 +316,8 @@ void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int return ; } - DEBUG("DROP " << val); - auto stmt = ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val), flag }); - + DEBUG(stmt); m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); if( flag != ~0u ) @@ -332,8 +333,9 @@ void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsig // TODO: Ensure that the type is a Box? - DEBUG("DROP shallow " << val); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Drop({ ::MIR::eDropKind::SHALLOW, mv$(val), flag }) ); + auto stmt = ::MIR::Statement::make_Drop({ ::MIR::eDropKind::SHALLOW, mv$(val), flag }); + DEBUG(stmt); + m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); if( flag != ~0u ) { @@ -350,17 +352,23 @@ void MirBuilder::push_stmt_asm(const Span& sp, ::MIR::Statement::Data_Asm data) mark_value_assigned(sp, v.second); // 2. Push - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Asm( mv$(data) ) ); + auto stmt = ::MIR::Statement::make_Asm( mv$(data) ); + DEBUG(stmt); + m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); } void MirBuilder::push_stmt_set_dropflag_val(const Span& sp, unsigned int idx, bool value) { ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_SetDropFlag({ idx, value }) ); + auto stmt = ::MIR::Statement::make_SetDropFlag({ idx, value }); + DEBUG(stmt); + m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); } void MirBuilder::push_stmt_set_dropflag_other(const Span& sp, unsigned int idx, unsigned int other) { ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_SetDropFlag({ idx, false, other }) ); + auto stmt = ::MIR::Statement::make_SetDropFlag({ idx, false, other }); + DEBUG(stmt); + m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); } void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) @@ -830,7 +838,7 @@ namespace { static void merge_state(const Span& sp, MirBuilder& builder, const ::MIR::LValue& lv, VarState& old_state, const VarState& new_state) { - DEBUG(lv << " : " << old_state << " <= " << new_state); + TRACE_FUNCTION_FR(lv << " : " << old_state << " <= " << new_state, lv << " : " << old_state); switch(old_state.tag()) { case VarState::TAGDEAD: throw ""; @@ -863,48 +871,85 @@ namespace } return ; } - case VarState::TAG_Partial: { - const auto& nse = new_state.as_Partial(); + case VarState::TAG_MovedOut: { + const auto& nse = new_state.as_MovedOut(); + + // Create a new staet that is internally valid and uses the same drop flag + old_state = VarState::make_MovedOut({ box$(old_state.clone()), nse.outer_flag }); + auto& ose = old_state.as_MovedOut(); + if( ose.outer_flag != ~0u ) + { + // If the flag's default isn't false, then create a new flag that does have such a default + // - Other arm (old_state) uses default, this arm (new_state) can be manipulated + if( builder.get_drop_flag_default(sp, ose.outer_flag) != false ) + { + auto new_flag = builder.new_drop_flag(false); + builder.push_stmt_set_dropflag_other(sp, new_flag, nse.outer_flag); + ose.outer_flag = new_flag; + } + } + else + { + // In both arms, the container is valid. No need for a drop flag + } + bool is_box = false; - builder.with_val_type(sp, lv, [&](const auto& ty){ is_box = builder.is_type_owned_box(ty); }); - if( is_box ) { - ASSERT_BUG(sp, nse.inner_states.size() == 1, ""); + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_box = builder.is_type_owned_box(ty); + }); + if( is_box ) + { + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); } - if( nse.outer_flag != ~0u ) { - // Set the outer flag to `true` if its default isn't true - if( builder.get_drop_flag_default(sp, nse.outer_flag) != false ) { - builder.push_stmt_set_dropflag_val(sp, nse.outer_flag, false); - } + else + { + BUG(sp, "Handle MovedOut on non-Box"); + } + return ; } + case VarState::TAG_Partial: { + const auto& nse = new_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); - auto out = new_state.clone(); - auto& ose = out.as_Partial(); - if( ose.outer_flag == ~0u ) + // Create a partial filled with Invalid { - ose.outer_flag = builder.new_drop_flag_and_set(sp, true); // Only in this arm is the container valid + ::std::vector inner; inner.reserve( nse.inner_states.size() ); + for(size_t i = 0; i < nse.inner_states.size(); i++) + inner.push_back( old_state.clone() ); + old_state = VarState::make_Partial({ mv$(inner) }); } - if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], old_state); + auto& ose = old_state.as_Partial(); + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast(i) }), ose.inner_states[i], nse.inner_states[i]); + } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], old_state); + merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); } } - old_state = mv$(out); } return; } break; + // Valid <= ... case VarState::TAG_Valid: switch( new_state.tag() ) { case VarState::TAGDEAD: throw ""; + // Valid <= Invalid case VarState::TAG_Invalid: old_state = VarState::make_Optional( builder.new_drop_flag_and_set(sp, false) ); return ; + // Valid <= Valid case VarState::TAG_Valid: return ; + // Valid <= Optional case VarState::TAG_Optional: { auto flag_idx = new_state.as_Optional(); // Was valid, now optional. @@ -931,39 +976,78 @@ namespace } return ; } - case VarState::TAG_Partial: { - const auto& nse = new_state.as_Partial(); + // Valid <= MovedOut + case VarState::TAG_MovedOut: { + const auto& nse = new_state.as_MovedOut(); + + // Create a new staet that is internally valid and uses the same drop flag + old_state = VarState::make_MovedOut({ box$(VarState::make_Valid({})), nse.outer_flag }); + auto& ose = old_state.as_MovedOut(); + if( ose.outer_flag != ~0u ) + { + // If the flag's default isn't true, then create a new flag that does have such a default + // - Other arm (old_state) uses default, this arm (new_state) can be manipulated + if( builder.get_drop_flag_default(sp, ose.outer_flag) != true ) + { + auto new_flag = builder.new_drop_flag(true); + builder.push_stmt_set_dropflag_other(sp, new_flag, nse.outer_flag); + ose.outer_flag = new_flag; + } + } + else + { + // In both arms, the container is valid. No need for a drop flag + } + bool is_box = false; - builder.with_val_type(sp, lv, [&](const auto& ty){ is_box = builder.is_type_owned_box(ty); }); + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_box = builder.is_type_owned_box(ty); + }); + if( is_box ) { - ASSERT_BUG(sp, nse.inner_states.size() == 1, ""); + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); } - if( nse.outer_flag != ~0u ) { - // Set the outer flag to `true` if its default isn't true - if( builder.get_drop_flag_default(sp, nse.outer_flag) != true ) { - builder.push_stmt_set_dropflag_val(sp, nse.outer_flag, true); - } + else { + BUG(sp, "MovedOut on non-Box"); + } + return; } + // Valid <= Partial + case VarState::TAG_Partial: { + const auto& nse = new_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); - auto out = new_state.clone(); - auto& ose = out.as_Partial(); - if( ose.outer_flag == ~0u ) + // Create a partial filled with Valid { - ose.outer_flag = builder.new_drop_flag(true); // In both arms, the container is valid + ::std::vector inner; inner.reserve( nse.inner_states.size() ); + for(size_t i = 0; i < nse.inner_states.size(); i++) + inner.push_back( VarState::make_Valid({}) ); + old_state = VarState::make_Partial({ mv$(inner) }); } - if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], old_state); + auto& ose = old_state.as_Partial(); + if( is_enum ) { + auto ilv = ::MIR::LValue::make_Downcast({ box$(lv.clone()), 0 }); + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ilv, ose.inner_states[i], nse.inner_states[i]); + ilv.as_Downcast().variant_index ++; + } } else { + auto ilv = ::MIR::LValue::make_Field({ box$(lv.clone()), 0 }); for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], old_state); + merge_state(sp, builder, ilv, ose.inner_states[i], nse.inner_states[i]); + ilv.as_Field().field_index ++; } } - old_state = mv$(out); } return; } break; + // Optional <= ... case VarState::TAG_Optional: switch( new_state.tag() ) { @@ -983,42 +1067,64 @@ namespace #endif } return ; - case VarState::TAG_Partial: - TODO(sp, "Handle Optional->Partial in split scope"); + case VarState::TAG_MovedOut: + TODO(sp, "Handle Optional->MovedOut in split scope"); + case VarState::TAG_Partial: { + const auto& nse = new_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + assert( !builder.is_type_owned_box(ty) ); + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); + // Create a Partial filled with copies of the Optional + { + ::std::vector inner; + inner.reserve( nse.inner_states.size() ); + for(size_t i = 0; i < nse.inner_states.size(); i ++) + inner.push_back(old_state.clone()); + old_state = VarState::make_Partial({ mv$(inner) }); + } + auto& ose = old_state.as_Partial(); + // Propagate to inners + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast(i) }), ose.inner_states[i], nse.inner_states[i]); + } + } + else { + for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) + { + merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); + } + } + return; } } break; + case VarState::TAG_MovedOut: + TODO(sp, "Handle MovedOut->?"); + break; case VarState::TAG_Partial: { auto& ose = old_state.as_Partial(); - bool is_box = false; - builder.with_val_type(sp, lv, [&](const auto& ty){ is_box = builder.is_type_owned_box(ty); }); - if( is_box ) { - ASSERT_BUG(sp, ose.inner_states.size() == 1, ""); - } + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + assert( !builder.is_type_owned_box(ty) ); + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); // Need to tag for conditional shallow drop? Or just do that at the end of the split? // - End of the split means that the only optional state is outer drop. switch( new_state.tag() ) { case VarState::TAGDEAD: throw ""; case VarState::TAG_Invalid: - if( ose.outer_flag != ~0u ) { - // Set the outer flag to `false` if its default isn't false - if( builder.get_drop_flag_default(sp, ose.outer_flag) != false ) { - builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, false); - } - } - if( 0 ) - // - Fall through case VarState::TAG_Valid: - if( ose.outer_flag != ~0u ) { - // Set the outer flag to `true` if its default isn't true - if( builder.get_drop_flag_default(sp, ose.outer_flag) != true ) { - builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, true); + case VarState::TAG_Optional: + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast(i) }), ose.inner_states[i], new_state); } } - - if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], new_state); - } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { @@ -1026,17 +1132,16 @@ namespace } } return ; - case VarState::TAG_Optional: { - //auto flag_idx = new_state.as_Optional(); - TODO(sp, "Handle Partial->Optional in split scope"); - } return; + case VarState::TAG_MovedOut: + BUG(sp, "Partial->MovedOut not valid"); case VarState::TAG_Partial: { const auto& nse = new_state.as_Partial(); ASSERT_BUG(sp, ose.inner_states.size() == nse.inner_states.size(), "Partial->Partial with mismatched sizes - " << old_state << " <= " << new_state); - ASSERT_BUG(sp, ose.outer_flag == nse.outer_flag, "Partial->Partial with mismatched drop flags - " << old_state << " <= " << new_state); - if( is_box ) { - ASSERT_BUG(sp, nse.inner_states.size() == 1, ""); - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], nse.inner_states[0]); + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast(i) }), ose.inner_states[i], nse.inner_states[i]); + } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) @@ -1406,8 +1511,11 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: // TODO: Make data variants refer to associated types (unify enum and struct handling) TU_MATCHA( (variant.second), (ve), (Value, + DEBUG(""); + cb(::HIR::TypeRef::new_unit()); ), (Unit, + cb(::HIR::TypeRef::new_unit()); ), (Tuple, // HACK! Create tuple. @@ -1442,185 +1550,369 @@ bool MirBuilder::lvalue_is_copy(const Span& sp, const ::MIR::LValue& val) const DEBUG("[lvalue_is_copy] ty="<second; + } + } + } + else if( ty == VarGroup::Temporary ) { - if( ! skip_count -- ) + auto it = cur_arm.tmp_states.find(idx); + if( it != cur_arm.tmp_states.end() ) { - return it->second; + if( ! skip_count -- ) + { + return it->second; + } } } ) ) } - - ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); - return m_variable_states[idx]; + switch(ty) + { + case VarGroup::Return: + return m_return_state; + case VarGroup::Argument: + ASSERT_BUG(sp, idx < m_arg_states.size(), "Argument " << idx << " out of range for state table"); + return m_arg_states.at(idx); + case VarGroup::Variable: + ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); + return m_variable_states[idx]; + case VarGroup::Temporary: + ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); + return m_temporary_states[idx]; + } + BUG(sp, "Fell off the end of get_slot_state"); } -VarState& MirBuilder::get_variable_state_mut(const Span& sp, unsigned int idx) +VarState& MirBuilder::get_slot_state_mut(const Span& sp, VarGroup ty, unsigned int idx) { VarState* ret = nullptr; for( auto scope_idx : ::reverse(m_scope_stack) ) { auto& scope_def = m_scopes.at(scope_idx); - if( scope_def.data.is_Variables() ) + if( const auto* e = scope_def.data.opt_Variables() ) { - const auto& e = scope_def.data.as_Variables(); - auto it = ::std::find(e.vars.begin(), e.vars.end(), idx); - if( it != e.vars.end() ) { - break ; + if( ty == VarGroup::Variable ) + { + auto it = ::std::find(e->vars.begin(), e->vars.end(), idx); + if( it != e->vars.end() ) { + break ; + } + } + } + else if( const auto* e = scope_def.data.opt_Temporaries() ) + { + if( ty == VarGroup::Temporary ) + { + auto it = ::std::find(e->temporaries.begin(), e->temporaries.end(), idx); + if( it != e->temporaries.end() ) { + break ; + } } } else if( scope_def.data.is_Split() ) { + auto& e = scope_def.data.as_Split(); + auto& cur_arm = e.arms.back(); if( ! ret ) { - auto& e = scope_def.data.as_Split(); - auto& cur_arm = e.arms.back(); - auto it = cur_arm.var_states.find(idx); - if( it == cur_arm.var_states.end() ) + ::std::map* states; + switch(ty) { - DEBUG("Split new (scope " << scope_idx << ")"); - ret = &(cur_arm.var_states[idx] = get_variable_state(sp, idx).clone()); + case VarGroup::Return: states = nullptr; break; + case VarGroup::Argument: BUG(sp, "Mutating state of argument"); break; + case VarGroup::Variable: states = &cur_arm.var_states; break; + case VarGroup::Temporary: states = &cur_arm.tmp_states; break; } - else + + if( states ) { - DEBUG("Split existing (scope " << scope_idx << ")"); - ret = &it->second; + auto it = states->find(idx); + if( it == states->end() ) + { + DEBUG("Split new (scope " << scope_idx << ")"); + ret = &( (*states)[idx] = get_slot_state(sp, ty, idx).clone() ); + } + else + { + DEBUG("Split existing (scope " << scope_idx << ")"); + ret = &it->second; + } } } } else if( scope_def.data.is_Loop() ) { auto& e = scope_def.data.as_Loop(); - if( e.changed_vars.count(idx) == 0 ) + ::std::map* states = nullptr; + switch(ty) + { + case VarGroup::Return: states = nullptr; break; + case VarGroup::Argument: BUG(sp, "Mutating state of argument"); break; + case VarGroup::Variable: states = &e.changed_vars; break; + case VarGroup::Temporary: states = &e.changed_tmps; break; + } + + if( states ) { - auto state = e.exit_state_valid ? get_variable_state(sp, idx).clone() : VarState::make_Valid({}); - e.changed_vars.insert(::std::make_pair( idx, mv$(state) )); + if( states->count(idx) == 0 ) + { + auto state = e.exit_state_valid ? get_slot_state(sp, ty, idx).clone() : VarState::make_Valid({}); + states->insert(::std::make_pair( idx, mv$(state) )); + } } } else { } } - - if( !ret ) + if( ret ) { - DEBUG("Outer"); - ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); - return m_variable_states[idx]; + return *ret; } else { - return *ret; + switch(ty) + { + case VarGroup::Return: + return m_return_state; + case VarGroup::Argument: + ASSERT_BUG(sp, idx < m_arg_states.size(), "Argument " << idx << " out of range for state table"); + return m_arg_states.at(idx); + case VarGroup::Variable: + ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); + return m_variable_states[idx]; + case VarGroup::Temporary: + ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); + return m_temporary_states[idx]; + } + BUG(sp, "Fell off the end of get_slot_state_mut"); } } +const VarState& MirBuilder::get_variable_state(const Span& sp, unsigned int idx, unsigned int skip_count) const +{ + return get_slot_state(sp, VarGroup::Variable, idx, skip_count); +} +VarState& MirBuilder::get_variable_state_mut(const Span& sp, unsigned int idx) +{ + return get_slot_state_mut(sp, VarGroup::Variable, idx); +} const VarState& MirBuilder::get_temp_state(const Span& sp, unsigned int idx, unsigned int skip_count) const { - for( auto scope_idx : ::reverse(m_scope_stack) ) - { - const auto& scope_def = m_scopes.at(scope_idx); - if( scope_def.data.is_Temporaries() ) - { - const auto& e = scope_def.data.as_Temporaries(); - auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx); - if( it != e.temporaries.end() ) { - break ; - } - } - else if( scope_def.data.is_Split() ) - { - const auto& e = scope_def.data.as_Split(); - const auto& cur_arm = e.arms.back(); - auto it = cur_arm.tmp_states.find(idx); - if( it != cur_arm.tmp_states.end() ) - { - if( ! skip_count -- ) - { - return it->second; - } - } - } - } - - ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); - return m_temporary_states[idx]; + return get_slot_state(sp, VarGroup::Temporary, idx, skip_count); } VarState& MirBuilder::get_temp_state_mut(const Span& sp, unsigned int idx) { - VarState* ret = nullptr; - for( auto scope_idx : ::reverse(m_scope_stack) ) - { - auto& scope_def = m_scopes.at(scope_idx); - if( scope_def.data.is_Temporaries() ) - { - const auto& e = scope_def.data.as_Temporaries(); - auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx); - if( it != e.temporaries.end() ) { - break ; - } - } - else if( scope_def.data.is_Split() ) + return get_slot_state_mut(sp, VarGroup::Temporary, idx); +} + +const VarState& MirBuilder::get_val_state(const Span& sp, const ::MIR::LValue& lv, unsigned int skip_count) +{ + TODO(sp, ""); +} +VarState& MirBuilder::get_val_state_mut(const Span& sp, const ::MIR::LValue& lv) +{ + TRACE_FUNCTION_F(lv); + TU_MATCHA( (lv), (e), + (Variable, + return get_slot_state_mut(sp, VarGroup::Variable, e); + ), + (Temporary, + return get_slot_state_mut(sp, VarGroup::Temporary, e.idx); + ), + (Argument, + return get_slot_state_mut(sp, VarGroup::Argument, e.idx); + ), + (Static, + BUG(sp, "Attempting to mutate state of a static"); + ), + (Return, + BUG(sp, "Move of return value"); + return get_slot_state_mut(sp, VarGroup::Return, 0); + ), + (Field, + auto& ivs = get_val_state_mut(sp, *e.val); + VarState tpl; + TU_MATCHA( (ivs), (ivse), + (Invalid, + //BUG(sp, "Mutating inner state of an invalidated composite - " << lv); + tpl = VarState::make_Valid({}); + ), + (MovedOut, + BUG(sp, "Field on value with MovedOut state - " << lv); + ), + (Partial, + ), + (Optional, + tpl = ivs.clone(); + ), + (Valid, + tpl = VarState::make_Valid({}); + ) + ) + if( !ivs.is_Partial() ) { - if( ! ret ) - { - auto& e = scope_def.data.as_Split(); - auto& cur_arm = e.arms.back(); - auto it = cur_arm.tmp_states.find(idx); - if(it == cur_arm.tmp_states.end()) - { - ret = &(cur_arm.tmp_states[idx] = get_temp_state(sp, idx).clone()); + size_t n_flds = 0; + with_val_type(sp, *e.val, [&](const auto& ty) { + DEBUG("ty = " << ty); + if(const auto* e = ty.m_data.opt_Path()) { + ASSERT_BUG(sp, e->binding.is_Struct(), ""); + const auto& str = *e->binding.as_Struct(); + TU_MATCHA( (str.m_data), (se), + (Unit, + BUG(sp, "Field access of unit-like struct"); + ), + (Tuple, + n_flds = se.size(); + ), + (Named, + n_flds = se.size(); + ) + ) } - else - { - ret = &it->second; + else if(const auto* e = ty.m_data.opt_Tuple()) { + n_flds = e->size(); } - } + else if(const auto* e = ty.m_data.opt_Array()) { + n_flds = e->size_val; + } + else { + TODO(sp, "Determine field count for " << ty); + } + }); + ::std::vector inner_vs; inner_vs.reserve(n_flds); + for(size_t i = 0; i < n_flds; i++) + inner_vs.push_back( tpl.clone() ); + ivs = VarState::make_Partial({ mv$(inner_vs) }); } - else if( scope_def.data.is_Loop() ) + return ivs.as_Partial().inner_states.at(e.field_index); + ), + (Deref, + // HACK: If the dereferenced type is a Box ("owned_box") then hack in move and shallow drop + bool is_box = false; + if( this->m_lang_Box ) { - auto& e = scope_def.data.as_Loop(); - if( e.changed_tmps.count(idx) == 0 ) + with_val_type(sp, *e.val, [&](const auto& ty){ + DEBUG("ty = " << ty); + is_box = this->is_type_owned_box(ty); + }); + } + + + if( is_box ) + { + ::MIR::LValue inner_lv; + // 1. If the inner lvalue isn't a slot with move information, move out of the lvalue into a temporary (with standard temp scope) + TU_MATCH_DEF( ::MIR::LValue, (*e.val), (ei), + ( + with_val_type(sp, *e.val, [&](const auto& ty){ inner_lv = this->new_temporary(ty); }); + this->push_stmt_assign(sp, inner_lv.clone(), ::MIR::RValue( mv$(*e.val) )); + *e.val = inner_lv.clone(); + ), + (Variable, + inner_lv = ::MIR::LValue(ei); + ), + (Temporary, + inner_lv = ::MIR::LValue(ei); + ), + (Argument, + inner_lv = ::MIR::LValue(ei); + ) + ) + // 2. Mark the slot as requiring only a shallow drop + ::std::vector inner; + inner.push_back(VarState::make_Valid({})); + auto& ivs = get_val_state_mut(sp, inner_lv); + if( ! ivs.is_MovedOut() ) { - auto state = e.exit_state_valid ? get_temp_state(sp, idx).clone() : VarState::make_Valid({}); - e.changed_tmps.insert(::std::make_pair( idx, mv$(state) )); + unsigned int drop_flag = (ivs.is_Optional() ? ivs.as_Optional() : ~0u); + ivs = VarState::make_MovedOut({ box$(VarState::make_Valid({})), drop_flag }); } + return *ivs.as_MovedOut().inner_state; } else { + BUG(sp, "Move out of deref with non-Copy values - &move? - " << lv << " : " << FMT_CB(ss, this->with_val_type(sp, lv, [&](const auto& ty){ss<