summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2016-10-28 10:19:10 +0800
committerJohn Hodge <tpg@mutabah.net>2016-10-28 10:19:10 +0800
commitcaa1e891a0216c7b85b822039900ef28cfbb32a7 (patch)
tree28c5f1d317530efe3ee2ab4a972b020f928779b6 /src
parenta6e07200a058b8cc50902a99407e5afbcaa927b3 (diff)
downloadmrust-caa1e891a0216c7b85b822039900ef28cfbb32a7.tar.gz
HIR Typecheck Expr - Detect diverge from method call
Diffstat (limited to 'src')
-rw-r--r--src/hir_typeck/expr_cs.cpp70
1 files changed, 66 insertions, 4 deletions
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp
index f1acef62..a89e3ef3 100644
--- a/src/hir_typeck/expr_cs.cpp
+++ b/src/hir_typeck/expr_cs.cpp
@@ -539,12 +539,36 @@ namespace {
// - Not yielded - so don't equate the return
snp->visit(*this);
- if( is_diverge(snp->m_res_type) ) {
- diverges = true;
+ // NOTE: If the final statement in the block diverges, mark this as diverging
+ bool defer = false;
+ if( !diverges )
+ {
+ TU_IFLET(::HIR::TypeRef::Data, this->context.get_type(snp->m_res_type).m_data, Infer, e,
+ switch(e.ty_class)
+ {
+ case ::HIR::InferClass::Integer:
+ case ::HIR::InferClass::Float:
+ diverges = false;
+ break;
+ default:
+ defer = true;
+ break;
+ }
+ )
+ else if( is_diverge(snp->m_res_type) ) {
+ diverges = true;
+ }
+ else {
+ diverges = false;
+ }
}
// If a statement in this block diverges
- if( diverges ) {
+ if( defer ) {
+ DEBUG("Block final node returns _, derfer diverge check");
+ this->context.add_revisit(node);
+ }
+ else if( diverges ) {
DEBUG("Block diverges, yield !");
this->context.equate_types(node.span(), node.m_res_type, ::HIR::TypeRef::new_diverge());
}
@@ -1801,7 +1825,45 @@ namespace {
}
void visit(::HIR::ExprNode_Block& node) override {
- no_revisit(node);
+
+ const auto is_diverge = [&](const ::HIR::TypeRef& rty)->bool {
+ const auto& ty = this->context.get_type(rty);
+ // TODO: Search the entire type for `!`? (What about pointers to it? or Option/Result?)
+ // - A correct search will search for unconditional (ignoring enums with a non-! variant) non-rawptr instances of ! in the type
+ return ty.m_data.is_Diverge() || (ty.m_data.is_Infer() && ty.m_data.as_Infer().ty_class == ::HIR::InferClass::Diverge);
+ };
+
+ const auto& last_ty = this->context.get_type( node.m_nodes.back()->m_res_type );
+
+ bool diverges = false;
+ // NOTE: If the final statement in the block diverges, mark this as diverging
+ TU_IFLET(::HIR::TypeRef::Data, last_ty.m_data, Infer, e,
+ switch(e.ty_class)
+ {
+ case ::HIR::InferClass::Integer:
+ case ::HIR::InferClass::Float:
+ diverges = false;
+ break;
+ default:
+ return ;
+ }
+ )
+ else if( is_diverge(last_ty) ) {
+ diverges = true;
+ }
+ else {
+ diverges = false;
+ }
+ // If a statement in this block diverges
+ if( diverges ) {
+ DEBUG("Block diverges, yield !");
+ this->context.equate_types(node.span(), node.m_res_type, ::HIR::TypeRef::new_diverge());
+ }
+ else {
+ DEBUG("Block doesn't diverge but doesn't yield a value, yield ()");
+ this->context.equate_types(node.span(), node.m_res_type, ::HIR::TypeRef::new_unit());
+ }
+ this->m_completed = true;
}
void visit(::HIR::ExprNode_Return& node) override {
no_revisit(node);