use crate::infer::InferCtxt;
use crate::traits::object_safety::object_safety_violations;
use crate::ty::TypeckTables;
-use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style,
use rustc_span::{MultiSpan, Span, DUMMY_SP};
use std::fmt;
-use rustc_error_codes::*;
-
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
crate fn suggest_restricting_param_bound(
&self,
} else {
" where"
},
- trait_ref.to_predicate(),
+ trait_ref.without_const().to_predicate(),
),
Applicability::MachineApplicable,
);
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
- let new_obligation =
- Obligation::new(ObligationCause::dummy(), param_env, new_trait_ref.to_predicate());
+ let new_obligation = Obligation::new(
+ ObligationCause::dummy(),
+ param_env,
+ new_trait_ref.without_const().to_predicate(),
+ );
if self.predicate_must_hold_modulo_regions(&new_obligation) {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
// We have a very specific type of error, where just borrowing this argument
// Visit to make sure there's a single `return` type to suggest `impl Trait`,
// otherwise suggest using `Box<dyn Trait>` or an enum.
- let mut visitor = ReturnsVisitor(vec![]);
+ let mut visitor = ReturnsVisitor::default();
visitor.visit_body(&body);
let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
- let mut ret_types = visitor.0.iter().filter_map(|expr| tables.node_type_opt(expr.hir_id));
- let (last_ty, all_returns_have_same_type) =
- ret_types.clone().fold((None, true), |(last_ty, mut same), returned_ty| {
- same &= last_ty.map_or(true, |ty| ty == returned_ty);
- (Some(returned_ty), same)
- });
+ let mut ret_types = visitor
+ .returns
+ .iter()
+ .filter_map(|expr| tables.node_type_opt(expr.hir_id))
+ .map(|ty| self.resolve_vars_if_possible(&ty));
+ let (last_ty, all_returns_have_same_type) = ret_types.clone().fold(
+ (None, true),
+ |(last_ty, mut same): (std::option::Option<Ty<'_>>, bool), ty| {
+ let ty = self.resolve_vars_if_possible(&ty);
+ same &= last_ty.map_or(true, |last_ty| last_ty == ty) && ty.kind != ty::Error;
+ (Some(ty), same)
+ },
+ );
let all_returns_conform_to_trait =
if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) {
match ty_ret_ty.kind {
})
})
}
- _ => true,
+ _ => false,
}
} else {
true
// Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
// Get all the return values and collect their span and suggestion.
let mut suggestions = visitor
- .0
+ .returns
.iter()
.map(|expr| {
(
{
let body = hir.body(*body_id);
// Point at all the `return`s in the function as they have failed trait bounds.
- let mut visitor = ReturnsVisitor(vec![]);
+ let mut visitor = ReturnsVisitor::default();
visitor.visit_body(&body);
let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
- for expr in &visitor.0 {
+ for expr in &visitor.returns {
if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) {
let ty = self.resolve_vars_if_possible(&returned_ty);
err.span_label(expr.span, &format!("this returned value is of type `{}`", ty));
// the type. The last generator has information about where the bound was introduced. At
// least one generator should be present for this diagnostic to be modified.
let (mut trait_ref, mut target_ty) = match obligation.predicate {
- ty::Predicate::Trait(p) => {
+ ty::Predicate::Trait(p, _) => {
(Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty()))
}
_ => (None, None),
err.note(&format!("required because it appears within the type `{}`", ty));
obligated_types.push(ty);
- let parent_predicate = parent_trait_ref.to_predicate();
+ let parent_predicate = parent_trait_ref.without_const().to_predicate();
if !self.is_recursive_obligation(obligated_types, &data.parent_code) {
self.note_obligation_cause_code(
err,
parent_trait_ref.print_only_trait_path(),
parent_trait_ref.skip_binder().self_ty()
));
- let parent_predicate = parent_trait_ref.to_predicate();
+ let parent_predicate = parent_trait_ref.without_const().to_predicate();
self.note_obligation_cause_code(
err,
&parent_predicate,
/// Collect all the returned expressions within the input expression.
/// Used to point at the return spans when we want to suggest some change to them.
-struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>);
+#[derive(Default)]
+struct ReturnsVisitor<'v> {
+ returns: Vec<&'v hir::Expr<'v>>,
+ in_block_tail: bool,
+}
impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
type Map = rustc::hir::map::Map<'v>;
}
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
- if let hir::ExprKind::Ret(Some(ex)) = ex.kind {
- self.0.push(ex);
+ // Visit every expression to detect `return` paths, either through the function's tail
+ // expression or `return` statements. We walk all nodes to find `return` statements, but
+ // we only care about tail expressions when `in_block_tail` is `true`, which means that
+ // they're in the return path of the function body.
+ match ex.kind {
+ hir::ExprKind::Ret(Some(ex)) => {
+ self.returns.push(ex);
+ }
+ hir::ExprKind::Block(block, _) if self.in_block_tail => {
+ self.in_block_tail = false;
+ for stmt in block.stmts {
+ hir::intravisit::walk_stmt(self, stmt);
+ }
+ self.in_block_tail = true;
+ if let Some(expr) = block.expr {
+ self.visit_expr(expr);
+ }
+ }
+ hir::ExprKind::Match(_, arms, _) if self.in_block_tail => {
+ for arm in arms {
+ self.visit_expr(arm.body);
+ }
+ }
+ // We need to walk to find `return`s in the entire body.
+ _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex),
+ _ => self.returns.push(ex),
}
- hir::intravisit::walk_expr(self, ex);
}
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
+ assert!(!self.in_block_tail);
if body.generator_kind().is_none() {
if let hir::ExprKind::Block(block, None) = body.value.kind {
- if let Some(expr) = block.expr {
- self.0.push(expr);
+ if block.expr.is_some() {
+ self.in_block_tail = true;
}
}
}