Skip to content

Rollup of 5 pull requests #106529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b83fa06
Add regression test for #58355
JohnTitor Jan 5, 2023
ea43eb3
Point at expressions where inference refines an unexpected type
estebank Jan 3, 2023
05c30b0
Skip macros to avoid talking about bindings the user can't see
estebank Jan 3, 2023
48094a4
More eagerly resolve expr `ty`s before writing them
estebank Jan 3, 2023
6b0cce4
review comments: do not always point at init expr
estebank Jan 3, 2023
9cc8d86
Tweak output
estebank Jan 4, 2023
4ac7d1c
Formatting
estebank Jan 4, 2023
ad82eed
Use `BottomUpFolder`
estebank Jan 4, 2023
c905f5e
Account for type error on method arg caused by earlier inference
estebank Jan 5, 2023
f571862
Suggest changing argument on type error
estebank Jan 5, 2023
98f3936
review comment: potentially produce more suggestions for arg type mis…
estebank Jan 5, 2023
b182259
review comments: reword
estebank Jan 5, 2023
f98f2fc
fix rebase
estebank Jan 5, 2023
0de182a
rustdoc: remove legacy user-select CSS
notriddle Jan 5, 2023
f2ad85a
Fix error-index redirect to work with back button.
ehuss Jan 5, 2023
72c3082
error-index: Don't generate 404 instead of removing it.
ehuss Jan 5, 2023
10dbcf0
fix [type error] for error E0029 and E0277
Jan 5, 2023
6ae0f08
Rollup merge of #106400 - estebank:type-errs, r=compiler-errors
Dylan-DPC Jan 6, 2023
2b6e38c
Rollup merge of #106491 - ehuss:error-index-redirect, r=GuillaumeGome…
Dylan-DPC Jan 6, 2023
4b094c5
Rollup merge of #106494 - JohnTitor:issue-58355, r=compiler-errors
Dylan-DPC Jan 6, 2023
a8f7ec2
Rollup merge of #106499 - lyming2007:issue-105946-fix, r=estebank
Dylan-DPC Jan 6, 2023
dd97619
Rollup merge of #106502 - notriddle:notriddle/user-select, r=Guillaum…
Dylan-DPC Jan 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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