diff options
-rw-r--r-- | src/mir/check_full.cpp | 136 | ||||
-rw-r--r-- | src/mir/helpers.cpp | 7 | ||||
-rw-r--r-- | src/mir/helpers.hpp | 1 |
3 files changed, 142 insertions, 2 deletions
diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index 992db468..1f527ff2 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -12,7 +12,8 @@ #include <mir/helpers.hpp> #include <mir/visit_crate_mir.hpp> -#define ENABLE_LEAK_DETECTOR 1 +// DISABLED: Unsizing intentionally leaks +#define ENABLE_LEAK_DETECTOR 0 namespace { @@ -151,6 +152,134 @@ namespace ensure_valid(mir_res, lv, vs, path); } private: + struct InvalidReason { + enum { + Unwritten, + Moved, + Invalidated, + } ty; + size_t bb; + size_t stmt; + + void fmt(::std::ostream& os) const { + switch(this->ty) + { + case Unwritten: os << "Not Written"; break; + case Moved: os << "Moved at BB" << bb << "/" << stmt; break; + case Invalidated: os << "Invalidated at BB" << bb << "/" << stmt; break; + } + } + }; + InvalidReason find_invalid_reason(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& root_lv) const + { + using ::MIR::visit::ValUsage; + using ::MIR::visit::visit_mir_lvalues; + + ::HIR::TypeRef tmp; + bool is_copy = mir_res.m_resolve.type_is_copy( mir_res.sp, mir_res.get_lvalue_type(tmp, root_lv) ); + + size_t cur_stmt = mir_res.get_cur_stmt_ofs(); + if( !is_copy ) + { + // Walk backwards through the BBs and find where it's used by value + assert(this->bb_path.size() > 0); + size_t bb_idx; + size_t stmt_idx; + + bool was_moved = false; + size_t moved_bb, moved_stmt; + auto visit_cb = [&](const auto& lv, auto vu) { + if(lv == root_lv && vu == ValUsage::Move) { + was_moved = true; + moved_bb = bb_idx; + moved_stmt = stmt_idx; + return false; + } + return false; + }; + // Most recent block (incomplete) + { + bb_idx = this->bb_path.back(); + const auto& bb = mir_res.m_fcn.blocks.at(bb_idx); + for(stmt_idx = cur_stmt; stmt_idx -- && !was_moved; ) + { + visit_mir_lvalues(bb.statements[stmt_idx], visit_cb); + } + } + for(size_t i = this->bb_path.size()-1; i -- && !was_moved; ) + { + bb_idx = this->bb_path[i]; + const auto& bb = mir_res.m_fcn.blocks.at(bb_idx); + stmt_idx = bb.statements.size(); + + visit_mir_lvalues(bb.terminator, visit_cb); + + for(stmt_idx = bb.statements.size(); stmt_idx -- && !was_moved; ) + { + visit_mir_lvalues(bb.statements[stmt_idx], visit_cb); + } + } + + if( was_moved ) + { + // Reason found, the value was moved + DEBUG("- Moved in BB" << moved_bb << "/" << moved_stmt); + return InvalidReason { InvalidReason::Moved, moved_bb, moved_stmt }; + } + } + else + { + // Walk backwards to find assignment (if none, it's never initialized) + assert(this->bb_path.size() > 0); + size_t bb_idx; + size_t stmt_idx; + + bool assigned = false; + auto visit_cb = [&](const auto& lv, auto vu) { + if(lv == root_lv && vu == ValUsage::Write) { + assigned = true; + //assigned_bb = this->bb_path[i]; + //assigned_stmt = j; + return true; + } + return false; + }; + + // Most recent block (incomplete) + { + bb_idx = this->bb_path.back(); + const auto& bb = mir_res.m_fcn.blocks.at(bb_idx); + for(stmt_idx = cur_stmt; stmt_idx -- && !assigned; ) + { + visit_mir_lvalues(bb.statements[stmt_idx], visit_cb); + } + } + for(size_t i = this->bb_path.size()-1; i -- && !assigned; ) + { + bb_idx = this->bb_path[i]; + const auto& bb = mir_res.m_fcn.blocks.at(bb_idx); + stmt_idx = bb.statements.size(); + + visit_mir_lvalues(bb.terminator, visit_cb); + + for(stmt_idx = bb.statements.size(); stmt_idx -- && !assigned; ) + { + visit_mir_lvalues(bb.statements[stmt_idx], visit_cb); + } + } + + if( !assigned ) + { + // Value wasn't ever assigned, that's why it's not valid. + DEBUG("- Not assigned"); + return InvalidReason { InvalidReason::Unwritten, 0, 0 }; + } + } + // If neither of the above return a reason, check for blocks that don't have the value valid. + // TODO: This requires access to the lifetime bitmaps to know where it was invalidated + DEBUG("- (assume) lifetime invalidated [is_copy=" << is_copy << "]"); + return InvalidReason { InvalidReason::Invalidated, 0, 0 }; + } void ensure_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& root_lv, const State& vs, ::std::vector<unsigned int>& path) const { if( vs.is_composite() ) @@ -168,7 +297,9 @@ namespace } else if( !vs.is_valid() ) { - MIR_BUG(mir_res, "Accessing invalidated lvalue - " << root_lv << " - field path=[" << path << "], BBs=[" << this->bb_path << "]"); + // Locate where it was invalidated. + auto reason = find_invalid_reason(mir_res, root_lv); + MIR_BUG(mir_res, "Accessing invalidated lvalue - " << root_lv << " - " << FMT_CB(s,reason.fmt(s);) << " - field path=[" << path << "], BBs=[" << this->bb_path << "]"); } else { @@ -577,6 +708,7 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio // If this state already exists in the map, skip if( ! block_entry_states[cur_block].add_state(state) ) { + DEBUG("BB" << cur_block << " - Nothing new"); continue ; } DEBUG("BB" << cur_block << " - " << state); diff --git a/src/mir/helpers.cpp b/src/mir/helpers.cpp index 2f787927..9242ccb7 100644 --- a/src/mir/helpers.cpp +++ b/src/mir/helpers.cpp @@ -34,6 +34,13 @@ void ::MIR::TypeResolve::print_msg(const char* tag, ::std::function<void(::std:: //throw CheckFailure {}; } +unsigned int ::MIR::TypeResolve::get_cur_stmt_ofs() const +{ + if( this->stmt_idx == STMT_TERM ) + return m_fcn.blocks.at(this->bb_idx).statements.size(); + else + return this->stmt_idx; +} const ::MIR::BasicBlock& ::MIR::TypeResolve::get_block(::MIR::BasicBlockId id) const { MIR_ASSERT(*this, id < m_fcn.blocks.size(), "Block ID " << id << " out of range"); diff --git a/src/mir/helpers.hpp b/src/mir/helpers.hpp index 8b4e8424..d7aefe25 100644 --- a/src/mir/helpers.hpp +++ b/src/mir/helpers.hpp @@ -82,6 +82,7 @@ public: this->bb_idx = bb_idx; this->stmt_idx = stmt_idx; } + unsigned int get_cur_stmt_ofs() const; void set_cur_stmt_term(unsigned int bb_idx) { this->bb_idx = bb_idx; this->stmt_idx = STMT_TERM; |