From 0fe876925e59aad4765b415d9caaf262a6d43c4c Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Mon, 18 May 2020 23:39:10 +0200 Subject: Infer return type of loops with value breaks. --- crates/ra_hir_ty/src/infer/expr.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'crates/ra_hir_ty/src/infer') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 0b67d216a..c7aa67fbe 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); @@ -102,13 +102,13 @@ impl<'a> InferenceContext<'a> { } // FIXME handle break with value if ctxt.may_break { - Ty::unit() + ctxt.break_ty } else { Ty::simple(TypeCtor::Never) } } Expr::While { condition, body } => { - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); // while let is desugared to a match loop, so this is always simple while self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); @@ -120,7 +120,7 @@ impl<'a> InferenceContext<'a> { Expr::For { iterable, body, pat } => { let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); let pat_ty = self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); @@ -229,12 +229,21 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { + let mut has_val_ty = None; + if let Some(expr) = expr { - // FIXME handle break with value - self.infer_expr(*expr, &Expectation::none()); + has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); } + if let Some(ctxt) = self.breakables.last_mut() { ctxt.may_break = true; + if let Some(val_ty) = has_val_ty { + if ctxt.break_ty == Ty::Unknown { + ctxt.break_ty = val_ty; + } else if ctxt.break_ty != val_ty { + // TODO: Unify partially matching type information (Option<{unknown}> + Option => Option) + } + } } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, -- cgit v1.2.3 From 6eaa669da0c7b3730a309db5e320126653b88997 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:03:59 +0200 Subject: loop return value inference: coerce_merge branches --- crates/ra_hir_ty/src/infer/expr.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'crates/ra_hir_ty/src/infer') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index c7aa67fbe..83702ada0 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,14 +93,17 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); + self.breakables.push(BreakableContext { + may_break: false, + break_ty: self.table.new_type_var(), + }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); if ctxt.may_break { self.diverges = Diverges::Maybe; } - // FIXME handle break with value + if ctxt.may_break { ctxt.break_ty } else { @@ -229,26 +232,31 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { - let mut has_val_ty = None; + let val_ty = if let Some(expr) = expr { + self.infer_expr(*expr, &Expectation::none()) + } else { + Ty::unit() + }; - if let Some(expr) = expr { - has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); - } + let mut has_brkctx = false; - if let Some(ctxt) = self.breakables.last_mut() { - ctxt.may_break = true; - if let Some(val_ty) = has_val_ty { - if ctxt.break_ty == Ty::Unknown { - ctxt.break_ty = val_ty; - } else if ctxt.break_ty != val_ty { - // TODO: Unify partially matching type information (Option<{unknown}> + Option => Option) - } - } + if self.breakables.last().is_some() { + has_brkctx = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } + + if has_brkctx { + let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + let ctxt = self.breakables.last_mut().expect("This is a bug"); + ctxt.may_break = true; + ctxt.break_ty = merged_type; + } + Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { -- cgit v1.2.3 From 45021cae551826727c32c7499c68ca48d046890f Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 22:52:15 +0200 Subject: Apply suggestion of @flodiebold: Get rid of multiple unwraps --- crates/ra_hir_ty/src/infer/expr.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir_ty/src/infer') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83702ada0..b28724f0e 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -238,25 +238,23 @@ impl<'a> InferenceContext<'a> { Ty::unit() }; - let mut has_brkctx = false; + let last_ty = if let Some(ctxt) = self.breakables.last() { + ctxt.break_ty.clone() + } else { + Ty::Unknown + }; - if self.breakables.last().is_some() { - has_brkctx = true; + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + if let Some(ctxt) = self.breakables.last_mut() { + ctxt.break_ty = merged_type; + ctxt.may_break = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } - if has_brkctx { - let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); - let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); - - let ctxt = self.breakables.last_mut().expect("This is a bug"); - ctxt.may_break = true; - ctxt.break_ty = merged_type; - } - Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { -- cgit v1.2.3 From ecac5d7de2192873c24b7b06d4964d188d8abe6a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 12:59:20 +0200 Subject: Switch to new magic marks --- crates/ra_hir_ty/src/infer/coerce.rs | 6 +++--- crates/ra_hir_ty/src/infer/pat.rs | 4 ++-- crates/ra_hir_ty/src/infer/unify.rs | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'crates/ra_hir_ty/src/infer') diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 173ec59ed..2ee9adb16 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -5,7 +5,7 @@ //! See: https://doc.rust-lang.org/nomicon/coercions.html use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; -use test_utils::tested_by; +use test_utils::mark; use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; @@ -34,7 +34,7 @@ impl<'a> InferenceContext<'a> { ty1.clone() } else { if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { - tested_by!(coerce_fn_reification); + mark::hit!(coerce_fn_reification); // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 @@ -44,7 +44,7 @@ impl<'a> InferenceContext<'a> { let ptr_ty2 = Ty::fn_ptr(sig2); self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) } else { - tested_by!(coerce_merge_fail_fallback); + mark::hit!(coerce_merge_fail_fallback); // For incompatible types, we use the latter one as result // to be better recovery for `if` without `else`. ty2.clone() diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 54ec870df..4006f595d 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs @@ -10,7 +10,7 @@ use hir_def::{ FieldId, }; use hir_expand::name::Name; -use test_utils::tested_by; +use test_utils::mark; use super::{BindingMode, Expectation, InferenceContext}; use crate::{utils::variant_data, Substs, Ty, TypeCtor}; @@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> { } } } else if let Pat::Ref { .. } = &body[pat] { - tested_by!(match_ergonomics_ref); + mark::hit!(match_ergonomics_ref); // When you encounter a `&pat` pattern, reset to Move. // This is so that `w` is by value: `let (_, &w) = &(1, &2);` default_bm = BindingMode::Move; diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index ab0bc8b70..269495ca0 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; -use test_utils::tested_by; +use test_utils::mark; use super::{InferenceContext, Obligation}; use crate::{ @@ -313,7 +313,7 @@ impl InferenceTable { // more than once for i in 0..3 { if i > 0 { - tested_by!(type_var_resolves_to_int_var); + mark::hit!(type_var_resolves_to_int_var); } match &*ty { Ty::Infer(tv) => { @@ -342,7 +342,7 @@ impl InferenceTable { Ty::Infer(tv) => { let inner = tv.to_inner(); if tv_stack.contains(&inner) { - tested_by!(type_var_cycles_resolve_as_possible); + mark::hit!(type_var_cycles_resolve_as_possible); // recursive type return tv.fallback_value(); } @@ -369,7 +369,7 @@ impl InferenceTable { Ty::Infer(tv) => { let inner = tv.to_inner(); if tv_stack.contains(&inner) { - tested_by!(type_var_cycles_resolve_completely); + mark::hit!(type_var_cycles_resolve_completely); // recursive type return tv.fallback_value(); } -- cgit v1.2.3