Skip to content

Commit

Permalink
Auto merge of #106529 - Dylan-DPC:rollup-mvncmrk, r=Dylan-DPC
Browse files Browse the repository at this point in the history
Rollup of 5 pull requests

Successful merges:

 - #106400 (Point at expressions where inference refines an unexpected type)
 - #106491 (Fix error-index redirect to work with the back button.)
 - #106494 (Add regression test for #58355)
 - #106499 (fix [type error] for error E0029 and E0277)
 - #106502 (rustdoc: remove legacy user-select CSS)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Jan 6, 2023
2 parents ce8fbe7 + dd97619 commit 1146560
Show file tree
Hide file tree
Showing 24 changed files with 515 additions and 24 deletions.
223 changes: 220 additions & 3 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
use crate::FnCtxt;
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::MultiSpan;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{is_range_literal, Node};
use rustc_infer::infer::InferOk;
use rustc_middle::lint::in_external_macro;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut};
use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder};
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeVisitable};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{BytePos, Span};
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches;
use rustc_trait_selection::traits::ObligationCause;

use super::method::probe;
Expand All @@ -40,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.annotate_alternative_method_deref(err, expr, error);

// Use `||` to give these suggestions a precedence
let _ = self.suggest_missing_parentheses(err, expr)
let suggested = self.suggest_missing_parentheses(err, expr)
|| self.suggest_remove_last_method_call(err, expr, expected)
|| self.suggest_associated_const(err, expr, expected)
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
Expand All @@ -54,6 +59,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected);
if !suggested {
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
}
}

pub fn emit_coerce_suggestions(
Expand Down Expand Up @@ -205,6 +213,215 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(expected, Some(err))
}

pub fn point_at_expr_source_of_inferred_type(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
found: Ty<'tcx>,
expected: Ty<'tcx>,
) -> bool {
let map = self.tcx.hir();

let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; };
let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; };
let hir::def::Res::Local(hir_id) = p.res else { return false; };
let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; };
let Some(hir::Node::Local(hir::Local {
ty: None,
init: Some(init),
..
})) = map.find_parent(pat.hir_id) else { return false; };
let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; };
if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() {
return false;
}

// Locate all the usages of the relevant binding.
struct FindExprs<'hir> {
hir_id: hir::HirId,
uses: Vec<&'hir hir::Expr<'hir>>,
}
impl<'v> Visitor<'v> for FindExprs<'v> {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind
&& let hir::def::Res::Local(hir_id) = path.res
&& hir_id == self.hir_id
{
self.uses.push(ex);
}
hir::intravisit::walk_expr(self, ex);
}
}

let mut expr_finder = FindExprs { hir_id, uses: vec![] };
let id = map.get_parent_item(hir_id);
let hir_id: hir::HirId = id.into();

let Some(node) = map.find(hir_id) else { return false; };
let Some(body_id) = node.body_id() else { return false; };
let body = map.body(body_id);
expr_finder.visit_expr(body.value);
// Hack to make equality checks on types with inference variables and regions useful.
let mut eraser = BottomUpFolder {
tcx: self.tcx,
lt_op: |_| self.tcx.lifetimes.re_erased,
ct_op: |c| c,
ty_op: |t| match *t.kind() {
ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))),
ty::Infer(ty::IntVar(_)) => {
self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 }))
}
ty::Infer(ty::FloatVar(_)) => {
self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 }))
}
_ => t,
},
};
let mut prev = eraser.fold_ty(ty);
let mut prev_span = None;

for binding in expr_finder.uses {
// In every expression where the binding is referenced, we will look at that
// expression's type and see if it is where the incorrect found type was fully
// "materialized" and point at it. We will also try to provide a suggestion there.
if let Some(hir::Node::Expr(expr)
| hir::Node::Stmt(hir::Stmt {
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr),
..
})) = &map.find_parent(binding.hir_id)
&& let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind
&& rcvr.hir_id == binding.hir_id
&& let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
{
// We special case methods, because they can influence inference through the
// call's arguments and we can provide a more explicit span.
let sig = self.tcx.fn_sig(def_id);
let def_self_ty = sig.input(0).skip_binder();
let rcvr_ty = self.node_ty(rcvr.hir_id);
// Get the evaluated type *after* calling the method call, so that the influence
// of the arguments can be reflected in the receiver type. The receiver
// expression has the type *before* theis analysis is done.
let ty = match self.lookup_probe(
segment.ident,
rcvr_ty,
expr,
probe::ProbeScope::TraitsInScope,
) {
Ok(pick) => pick.self_ty,
Err(_) => rcvr_ty,
};
// Remove one layer of references to account for `&mut self` and
// `&self`, so that we can compare it against the binding.
let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) {
(ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty),
_ => (ty, def_self_ty),
};
let mut param_args = FxHashMap::default();
let mut param_expected = FxHashMap::default();
let mut param_found = FxHashMap::default();
if self.can_eq(self.param_env, ty, found).is_ok() {
// We only point at the first place where the found type was inferred.
for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() {
if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() {
// We found an argument that references a type parameter in `Self`,
// so we assume that this is the argument that caused the found
// type, which we know already because of `can_eq` above was first
// inferred in this method call.
let arg = &args[i];
let arg_ty = self.node_ty(arg.hir_id);
err.span_label(
arg.span,
&format!(
"this is of type `{arg_ty}`, which causes `{ident}` to be \
inferred as `{ty}`",
),
);
param_args.insert(param_ty, (arg, arg_ty));
}
}
}

// Here we find, for a type param `T`, the type that `T` is in the current
// method call *and* in the original expected type. That way, we can see if we
// can give any structured suggestion for the function argument.
let mut c = CollectAllMismatches {
infcx: &self.infcx,
param_env: self.param_env,
errors: vec![],
};
let _ = c.relate(def_self_ty, ty);
for error in c.errors {
if let TypeError::Sorts(error) = error {
param_found.insert(error.expected, error.found);
}
}
c.errors = vec![];
let _ = c.relate(def_self_ty, expected);
for error in c.errors {
if let TypeError::Sorts(error) = error {
param_expected.insert(error.expected, error.found);
}
}
for (param, (arg, arg_ty)) in param_args.iter() {
let Some(expected) = param_expected.get(param) else { continue; };
let Some(found) = param_found.get(param) else { continue; };
if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; }
self.emit_coerce_suggestions(err, arg, *found, *expected, None, None);
}

let ty = eraser.fold_ty(ty);
if ty.references_error() {
break;
}
if ty != prev
&& param_args.is_empty()
&& self.can_eq(self.param_env, ty, found).is_ok()
{
// We only point at the first place where the found type was inferred.
err.span_label(
segment.ident.span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
);
break;
} else if !param_args.is_empty() {
break;
}
prev = ty;
} else {
let ty = eraser.fold_ty(self.node_ty(binding.hir_id));
if ty.references_error() {
break;
}
if ty != prev
&& let Some(span) = prev_span
&& self.can_eq(self.param_env, ty, found).is_ok()
{
// We only point at the first place where the found type was inferred.
// We use the *previous* span because if the type is known *here* it means
// it was *evaluated earlier*. We don't do this for method calls because we
// evaluate the method's self type eagerly, but not in any other case.
err.span_label(
span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
);
break;
}
prev = ty;
}
if binding.hir_id == expr.hir_id {
// Do not look at expressions that come after the expression we were originally
// evaluating and had a type error.
break;
}
prev_span = Some(binding.span);
}
true
}

fn annotate_expected_due_to_let_ty(
&self,
err: &mut Diagnostic,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) => self.check_expr_path(qpath, expr, args),
_ => self.check_expr_kind(expr, expected),
});
let ty = self.resolve_vars_if_possible(ty);

// Warn for non-block expressions with diverging children.
match expr.kind {
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
full_call_span,
format!("arguments to this {} are incorrect", call_name),
);
if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) =
(callee_ty, &call_expr.kind)
{
// Type that would have accepted this argument if it hadn't been inferred earlier.
// FIXME: We leave an inference variable for now, but it'd be nice to get a more
// specific type to increase the accuracy of the diagnostic.
let expected = self.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: full_call_span,
});
self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty);
}
// Call out where the function is defined
self.label_fn_like(
&mut err,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs),
_ => span_bug!(span, "Impossible, verified above."),
}
if (lhs, rhs).references_error() {
err.downgrade_to_delayed_bug();
}
if self.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"In a match expression, only numbers and characters can be matched \
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl<'tcx> InferCtxt<'tcx> {
.as_local()
.map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some())
};
let value = value.fold_with(&mut ty::fold::BottomUpFolder {
let value = value.fold_with(&mut BottomUpFolder {
tcx: self.tcx,
lt_op: |lt| lt,
ct_op: |ct| ct,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {

self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);

err.emit();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ pub trait TypeErrCtxtExt<'tcx> {

fn point_at_returns_when_relevant(
&self,
err: &mut Diagnostic,
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
obligation: &PredicateObligation<'tcx>,
);

Expand Down Expand Up @@ -1685,7 +1685,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {

fn point_at_returns_when_relevant(
&self,
err: &mut Diagnostic,
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
obligation: &PredicateObligation<'tcx>,
) {
match obligation.cause.code().peel_derives() {
Expand All @@ -1707,7 +1707,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
for expr in &visitor.returns {
if let Some(returned_ty) = typeck_results.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));
if ty.references_error() {
// don't print out the [type error] here
err.delay_as_bug();
} else {
err.span_label(
expr.span,
&format!("this returned value is of type `{}`", ty),
);
}
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,6 @@ ul.block, .block li {
overflow: initial;
text-align: right;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
padding: 14px 8px;
color: var(--src-line-numbers-span-color);
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/type/issue-58355.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![crate_type = "lib"]

pub fn foo(callback: fn() -> dyn ToString) {
let mut x: Option<Box<dyn Fn() -> dyn ToString>> = None;
x = Some(Box::new(callback));
//~^ ERROR: the size for values of type `dyn ToString` cannot be known at compilation time
}
13 changes: 13 additions & 0 deletions src/test/ui/type/issue-58355.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0277]: the size for values of type `dyn ToString` cannot be known at compilation time
--> $DIR/issue-58355.rs:5:14
|
LL | x = Some(Box::new(callback));
| ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: within `fn() -> dyn ToString`, the trait `Sized` is not implemented for `dyn ToString`
= note: required because it appears within the type `fn() -> dyn ToString`
= note: required for the cast from `fn() -> dyn ToString` to the object type `dyn Fn() -> (dyn ToString + 'static)`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
Loading

0 comments on commit 1146560

Please sign in to comment.