summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/expr.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /compiler/rustc_hir_typeck/src/expr.rs
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck/src/expr.rs')
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs528
1 files changed, 316 insertions, 212 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 9f439a2b3..7bd2c3f8b 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -21,7 +21,7 @@ use crate::{
TupleArgumentsFlag::DontTupleArguments,
};
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
pluralize, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder,
@@ -75,7 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// coercions from ! to `expected`.
if ty.is_never() {
if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
- let reported = self.tcx().sess.delay_span_bug(
+ let reported = self.tcx().sess.span_delayed_bug(
expr.span,
"expression with never type wound up being adjusted",
);
@@ -277,7 +277,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
match expr.kind {
- ExprKind::Lit(ref lit) => self.check_lit(&lit, expected),
+ ExprKind::Lit(ref lit) => self.check_lit(lit, expected),
ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected),
ExprKind::Assign(lhs, rhs, span) => {
self.check_expr_assign(expr, expected, lhs, rhs, span)
@@ -289,8 +289,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::AddrOf(kind, mutbl, oprnd) => {
self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
}
- ExprKind::Path(QPath::LangItem(lang_item, _, hir_id)) => {
- self.check_lang_item_path(lang_item, expr, hir_id)
+ ExprKind::Path(QPath::LangItem(lang_item, _)) => {
+ self.check_lang_item_path(lang_item, expr)
}
ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]),
ExprKind::InlineAsm(asm) => {
@@ -298,9 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id));
self.check_expr_asm(asm)
}
- ExprKind::OffsetOf(container, ref fields) => {
- self.check_offset_of(container, fields, expr)
- }
+ ExprKind::OffsetOf(container, fields) => self.check_offset_of(container, fields, expr),
ExprKind::Break(destination, ref expr_opt) => {
self.check_expr_break(destination, expr_opt.as_deref(), expr)
}
@@ -319,17 +317,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_expr_loop(body, source, expected, expr)
}
ExprKind::Match(discrim, arms, match_src) => {
- self.check_match(expr, &discrim, arms, expected, match_src)
+ self.check_match(expr, discrim, arms, expected, match_src)
}
ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected),
- ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected),
- ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected),
+ ExprKind::Block(body, _) => self.check_block_with_expected(body, expected),
+ ExprKind::Call(callee, args) => self.check_call(expr, callee, args, expected),
ExprKind::MethodCall(segment, receiver, args, _) => {
self.check_method_call(expr, segment, receiver, args, expected)
}
ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr),
ExprKind::Type(e, t) => {
- let ascribed_ty = self.to_ty_saving_user_provided_ty(&t);
+ let ascribed_ty = self.to_ty_saving_user_provided_ty(t);
let ty = self.check_expr_with_hint(e, ascribed_ty);
self.demand_eqtype(e.span, ascribed_ty, ty);
ascribed_ty
@@ -347,7 +345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Struct(qpath, fields, ref base_expr) => {
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
}
- ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
+ ExprKind::Field(base, field) => self.check_field(expr, base, field, expected),
ExprKind::Index(base, idx, brackets_span) => {
self.check_expr_index(base, idx, expr, brackets_span)
}
@@ -368,7 +366,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::UnOp::Not | hir::UnOp::Neg => expected,
hir::UnOp::Deref => NoExpectation,
};
- let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
+ let mut oprnd_t = self.check_expr_with_expectation(oprnd, expected_inner);
if !oprnd_t.references_error() {
oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
@@ -436,7 +434,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
});
let ty =
- self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl));
+ self.check_expr_with_expectation_and_needs(oprnd, hint, Needs::maybe_mut_place(mutbl));
let tm = ty::TypeAndMut { ty, mutbl };
match kind {
@@ -499,9 +497,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
lang_item: hir::LangItem,
expr: &'tcx hir::Expr<'tcx>,
- hir_id: Option<hir::HirId>,
) -> Ty<'tcx> {
- self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id, hir_id).1
+ self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1
}
pub(crate) fn check_expr_path(
@@ -517,7 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Res::Err => {
self.suggest_assoc_method_call(segs);
let e =
- self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
+ self.tcx.sess.span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
Ty::new_error(tcx, e)
}
@@ -528,14 +525,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
};
- if let ty::FnDef(did, callee_args) = *ty.kind() {
+ if let ty::FnDef(did, _) = *ty.kind() {
let fn_sig = ty.fn_sig(tcx);
- // HACK: whenever we get a FnDef in a non-const context, enforce effects to get the
- // default `host = true` to avoid inference errors later.
- if tcx.hir().body_const_context(self.body_id).is_none() {
- self.enforce_context_effects(expr.hir_id, qpath.span(), did, callee_args);
- }
if tcx.fn_sig(did).skip_binder().abi() == RustIntrinsic
&& tcx.item_name(did) == sym::transmute
{
@@ -564,7 +556,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let span = args.get(i).map(|a| a.span).unwrap_or(expr.span);
let input = self.instantiate_binder_with_fresh_vars(
span,
- infer::LateBoundRegionConversionTime::FnCall,
+ infer::BoundRegionConversionTime::FnCall,
fn_sig.input(i),
);
self.require_type_is_sized_deferred(
@@ -582,7 +574,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// with fresh vars.
let output = self.instantiate_binder_with_fresh_vars(
expr.span,
- infer::LateBoundRegionConversionTime::FnCall,
+ infer::BoundRegionConversionTime::FnCall,
fn_sig.output(),
);
self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType);
@@ -626,15 +618,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
- let coerce_to = match opt_coerce_to {
- Some(c) => c,
- None => {
- // If the loop context is not a `loop { }`, then break with
- // a value is illegal, and `opt_coerce_to` will be `None`.
- // Return error in that case (#114529).
- return Ty::new_misc_error(tcx);
- }
- };
+ // If the loop context is not a `loop { }`, then break with
+ // a value is illegal, and `opt_coerce_to` will be `None`.
+ // Set expectation to error in that case and set tainted
+ // by error (#114529)
+ let coerce_to = opt_coerce_to.unwrap_or_else(|| {
+ let guar = tcx.sess.span_delayed_bug(
+ expr.span,
+ "illegal break with value found but no error reported",
+ );
+ self.set_tainted_by_errors(guar);
+ Ty::new_error(tcx, guar)
+ });
// Recurse without `enclosing_breakables` borrowed.
e_ty = self.check_expr_with_hint(e, coerce_to);
@@ -660,7 +655,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if let Some(ref mut coerce) = ctxt.coerce {
- if let Some(ref e) = expr_opt {
+ if let Some(e) = expr_opt {
coerce.coerce(self, &cause, e, e_ty);
} else {
assert!(e_ty.is_unit());
@@ -669,11 +664,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self,
&cause,
|mut err| {
+ self.suggest_missing_semicolon(&mut err, expr, e_ty, false);
self.suggest_mismatched_types_on_tail(
&mut err, expr, ty, e_ty, target_id,
);
let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
- self.annotate_loop_expected_due_to_inference(&mut err, expr, error);
+ self.annotate_loop_expected_due_to_inference(err, expr, error);
if let Some(val) = ty_kind_suggestion(ty) {
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
@@ -887,7 +883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
kind: hir::ImplItemKind::Fn(..),
span: encl_fn_span,
..
- })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
+ })) = self.tcx.opt_hir_node_by_def_id(encl_item_id.def_id)
{
// We are inside a function body, so reporting "return statement
// outside of function body" needs an explanation.
@@ -1001,7 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
then: impl FnOnce(&hir::Expr<'_>),
) {
let mut parent = self.tcx.hir().parent_id(original_expr_id);
- while let Some(node) = self.tcx.hir().find(parent) {
+ while let Some(node) = self.tcx.opt_hir_node(parent) {
match node {
hir::Node::Expr(hir::Expr {
kind:
@@ -1133,8 +1129,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// The likely cause of this is `if foo = bar { .. }`.
let actual_ty = Ty::new_unit(self.tcx);
let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap();
- let lhs_ty = self.check_expr(&lhs);
- let rhs_ty = self.check_expr(&rhs);
+ let lhs_ty = self.check_expr(lhs);
+ let rhs_ty = self.check_expr(rhs);
let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) {
(Applicability::MachineApplicable, true)
} else if let ExprKind::Binary(
@@ -1145,7 +1141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
// if x == 1 && y == 2 { .. }
// +
- let actual_lhs_ty = self.check_expr(&rhs_expr);
+ let actual_lhs_ty = self.check_expr(rhs_expr);
(Applicability::MaybeIncorrect, self.can_coerce(rhs_ty, actual_lhs_ty))
} else if let ExprKind::Binary(
Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
@@ -1155,7 +1151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
// if x == 1 && y == 2 { .. }
// +
- let actual_rhs_ty = self.check_expr(&lhs_expr);
+ let actual_rhs_ty = self.check_expr(lhs_expr);
(Applicability::MaybeIncorrect, self.can_coerce(actual_rhs_ty, lhs_ty))
} else {
(Applicability::MaybeIncorrect, false)
@@ -1192,7 +1188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ty::new_error(self.tcx, reported);
}
- let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
+ let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace);
let suggest_deref_binop = |err: &mut Diagnostic, rhs_ty: Ty<'tcx>| {
if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
@@ -1219,7 +1215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This is (basically) inlined `check_expr_coercible_to_type`, but we want
// to suggest an additional fixup here in `suggest_deref_binop`.
- let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty);
+ let rhs_ty = self.check_expr_with_hint(rhs, lhs_ty);
if let (_, Some(mut diag)) =
self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No)
{
@@ -1280,7 +1276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || {
- self.check_block_no_value(&body);
+ self.check_block_no_value(body);
});
if ctxt.may_break {
@@ -1296,7 +1292,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// permit break with a value [1].
if ctxt.coerce.is_none() && !ctxt.may_break {
// [1]
- self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break");
+ self.tcx.sess.span_delayed_bug(body.span, "no coercion, but loop may not break");
}
ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| Ty::new_unit(self.tcx))
}
@@ -1310,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
args: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
- let rcvr_t = self.check_expr(&rcvr);
+ let rcvr_t = self.check_expr(rcvr);
// no need to check for bot/err -- callee does that
let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t);
let span = segment.ident.span;
@@ -1319,9 +1315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(method) => {
// We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
// trigger this codepath causing `structurally_resolve_type` to emit an error.
-
- self.enforce_context_effects(expr.hir_id, expr.span, method.def_id, method.args);
- self.write_method_call(expr.hir_id, method);
+ self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
Ok(method)
}
Err(error) => {
@@ -1344,7 +1338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
// Call the generic checker.
- self.check_method_argument_types(span, expr, method, &args, DontTupleArguments, expected)
+ self.check_method_argument_types(span, expr, method, args, DontTupleArguments, expected)
}
fn check_expr_cast(
@@ -1442,12 +1436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
&& let Some(span) = self.tcx.hir().opt_span(hir_id)
{
- match self
- .tcx
- .sess
- .diagnostic()
- .steal_diagnostic(span, StashKey::UnderscoreForArrayLengths)
- {
+ match self.tcx.sess.dcx().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) {
Some(mut err) => {
err.span_suggestion(
span,
@@ -1475,7 +1464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let fcx = FnCtxt::new(self, self.param_env, def_id);
crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
- let ty = fcx.check_expr_with_expectation(&body.value, expected);
+ let ty = fcx.check_expr_with_expectation(body.value, expected);
fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized);
fcx.write_ty(block.hir_id, ty);
ty
@@ -1504,7 +1493,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (element_ty, t) = match uty {
Some(uty) => {
- self.check_expr_coercible_to_type(&element, uty, None);
+ self.check_expr_coercible_to_type(element, uty, None);
(uty, uty)
}
None => {
@@ -1512,7 +1501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
kind: TypeVariableOriginKind::MiscVariable,
span: element.span,
});
- let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {});
+ let element_ty = self.check_expr_has_type_or_error(element, ty, |_| {});
(element_ty, ty)
}
};
@@ -1608,10 +1597,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds {
Some(fs) if i < fs.len() => {
let ety = fs[i];
- self.check_expr_coercible_to_type(&e, ety, None);
+ self.check_expr_coercible_to_type(e, ety, None);
ety
}
- _ => self.check_expr_with_expectation(&e, NoExpectation),
+ _ => self.check_expr_with_expectation(e, NoExpectation),
});
let tuple = Ty::new_tup_from_iter(self.tcx, elt_ts_iter);
if let Err(guar) = tuple.error_reported() {
@@ -1737,9 +1726,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Make sure to give a type to the field even if there's
// an error, so we can continue type-checking.
- let ty = self.check_expr_with_hint(&field.expr, field_type);
+ let ty = self.check_expr_with_hint(field.expr, field_type);
let (_, diag) =
- self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No);
+ self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No);
if let Some(mut diag) = diag {
if idx == ast_fields.len() - 1 {
@@ -1897,7 +1886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect();
if !private_fields.is_empty() {
- self.report_private_fields(adt_ty, span, private_fields, ast_fields);
+ self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields);
} else {
self.report_missing_fields(
adt_ty,
@@ -1917,10 +1906,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
) {
for field in fields {
- self.check_expr(&field.expr);
+ self.check_expr(field.expr);
}
if let Some(base) = *base_expr {
- self.check_expr(&base);
+ self.check_expr(base);
}
}
@@ -1933,7 +1922,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 8 | foo::Foo {};
/// | ^^^^^^^^ missing `you_can_use_this_field`
///
- /// error: aborting due to previous error
+ /// error: aborting due to 1 previous error
/// ```
fn report_missing_fields(
&self,
@@ -2008,11 +1997,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
!= range_def_id
{
// Suppress any range expr type mismatches
- if let Some(mut diag) = self
- .tcx
- .sess
- .diagnostic()
- .steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
+ if let Some(mut diag) =
+ self.tcx.sess.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
{
diag.delay_as_bug();
}
@@ -2050,12 +2036,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 8 | foo::Foo {};
/// | ^^^^^^^^
///
- /// error: aborting due to previous error
+ /// error: aborting due to 1 previous error
/// ```
fn report_private_fields(
&self,
adt_ty: Ty<'tcx>,
span: Span,
+ expr_span: Span,
private_fields: Vec<&ty::FieldDef>,
used_fields: &'tcx [hir::ExprField<'tcx>],
) {
@@ -2092,14 +2079,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
format!("{} and `{last}` ", names.join(", "))
}
- [] => unreachable!(),
+ [] => bug!("expected at least one private field to report"),
};
err.note(format!(
- "... and other private field{s} {names}that {were} not provided",
+ "{}private field{s} {names}that {were} not provided",
+ if used_fields.is_empty() { "" } else { "...and other " },
s = pluralize!(remaining_private_fields_len),
were = pluralize!("was", remaining_private_fields_len),
));
}
+
+ if let ty::Adt(def, _) = adt_ty.kind() {
+ let def_id = def.did();
+ let mut items = self
+ .tcx
+ .inherent_impls(def_id)
+ .iter()
+ .flat_map(|i| self.tcx.associated_items(i).in_definition_order())
+ // Only assoc fn with no receivers.
+ .filter(|item| {
+ matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter
+ })
+ .filter_map(|item| {
+ // Only assoc fns that return `Self`
+ let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder();
+ let ret_ty = fn_sig.output();
+ let ret_ty =
+ self.tcx.normalize_erasing_late_bound_regions(self.param_env, ret_ty);
+ if !self.can_eq(self.param_env, ret_ty, adt_ty) {
+ return None;
+ }
+ let input_len = fn_sig.inputs().skip_binder().len();
+ let order = !item.name.as_str().starts_with("new");
+ Some((order, item.name, input_len))
+ })
+ .collect::<Vec<_>>();
+ items.sort_by_key(|(order, _, _)| *order);
+ let suggestion = |name, args| {
+ format!(
+ "::{name}({})",
+ std::iter::repeat("_").take(args).collect::<Vec<_>>().join(", ")
+ )
+ };
+ match &items[..] {
+ [] => {}
+ [(_, name, args)] => {
+ err.span_suggestion_verbose(
+ span.shrink_to_hi().with_hi(expr_span.hi()),
+ format!("you might have meant to use the `{name}` associated function"),
+ suggestion(name, *args),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ err.span_suggestions(
+ span.shrink_to_hi().with_hi(expr_span.hi()),
+ "you might have meant to use an associated function to build this type",
+ items
+ .iter()
+ .map(|(_, name, args)| suggestion(name, *args))
+ .collect::<Vec<String>>(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ if let Some(default_trait) = self.tcx.get_diagnostic_item(sym::Default)
+ && self
+ .infcx
+ .type_implements_trait(default_trait, [adt_ty], self.param_env)
+ .may_apply()
+ {
+ err.multipart_suggestion(
+ "consider using the `Default` trait",
+ vec![
+ (span.shrink_to_lo(), "<".to_string()),
+ (
+ span.shrink_to_hi().with_hi(expr_span.hi()),
+ " as std::default::Default>::default()".to_string(),
+ ),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
err.emit();
}
@@ -2116,7 +2179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let guar = self
.tcx
.sess
- .delay_span_bug(expr.span, "parser recovered but no error was emitted");
+ .span_delayed_bug(expr.span, "parser recovered but no error was emitted");
self.set_tainted_by_errors(guar);
return guar;
}
@@ -2191,7 +2254,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(field_name) =
find_best_match_for_name(&available_field_names, field.ident.name, None)
{
- err.span_suggestion(
+ err.span_label(field.ident.span, "unknown field");
+ err.span_suggestion_verbose(
field.ident.span,
"a field with a similar name exists",
field_name,
@@ -2274,7 +2338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match deref_base_ty.kind() {
ty::Adt(base_def, args) if !base_def.is_enum() => {
debug!("struct named {:?}", deref_base_ty);
- let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ let body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id);
let fields = &base_def.non_enum_variant().fields;
@@ -2331,7 +2395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let guar = if field.name == kw::Empty {
- self.tcx.sess.delay_span_bug(field.span, "field name with no name")
+ self.tcx.sess.span_delayed_bug(field.span, "field name with no name")
} else if self.method_exists(field, base_ty, expr.hir_id, expected.only_has_type(self)) {
self.ban_take_value_of_method(expr, base_ty, field)
} else if !base_ty.is_primitive_ty() {
@@ -2420,35 +2484,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty: Ty<'tcx>,
) {
let Some(output_ty) = self.get_impl_future_output_ty(ty) else {
+ err.span_label(field_ident.span, "unknown field");
return;
};
- let mut add_label = true;
- if let ty::Adt(def, _) = output_ty.kind() {
- // no field access on enum type
- if !def.is_enum() {
- if def
- .non_enum_variant()
- .fields
- .iter()
- .any(|field| field.ident(self.tcx) == field_ident)
- {
- add_label = false;
- err.span_label(
- field_ident.span,
- "field not available in `impl Future`, but it is available in its `Output`",
- );
- err.span_suggestion_verbose(
- base.span.shrink_to_hi(),
- "consider `await`ing on the `Future` and access the field of its `Output`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- }
+ let ty::Adt(def, _) = output_ty.kind() else {
+ err.span_label(field_ident.span, "unknown field");
+ return;
+ };
+ // no field access on enum type
+ if def.is_enum() {
+ err.span_label(field_ident.span, "unknown field");
+ return;
}
- if add_label {
- err.span_label(field_ident.span, format!("field not found in `{ty}`"));
+ if !def.non_enum_variant().fields.iter().any(|field| field.ident(self.tcx) == field_ident) {
+ err.span_label(field_ident.span, "unknown field");
+ return;
}
+ err.span_label(
+ field_ident.span,
+ "field not available in `impl Future`, but it is available in its `Output`",
+ );
+ err.span_suggestion_verbose(
+ base.span.shrink_to_hi(),
+ "consider `await`ing on the `Future` and access the field of its `Output`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
}
fn ban_nonexisting_field(
@@ -2471,16 +2532,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::RawPtr(..) => {
self.suggest_first_deref_field(&mut err, expr, base, ident);
}
- ty::Adt(def, _) if !def.is_enum() => {
- self.suggest_fields_on_recordish(&mut err, expr, def, ident);
- }
ty::Param(param_ty) => {
+ err.span_label(ident.span, "unknown field");
self.point_at_param_definition(&mut err, param_ty);
}
ty::Alias(ty::Opaque, _) => {
self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
}
- _ => {}
+ _ => {
+ err.span_label(ident.span, "unknown field");
+ }
}
self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
@@ -2624,7 +2685,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let param_def_id = generic_param.def_id;
let param_hir_id = match param_def_id.as_local() {
- Some(x) => self.tcx.hir().local_def_id_to_hir_id(x),
+ Some(x) => self.tcx.local_def_id_to_hir_id(x),
None => return,
};
let param_span = self.tcx.hir().span(param_hir_id);
@@ -2633,34 +2694,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(param_span, format!("type parameter '{param_name}' declared here"));
}
- fn suggest_fields_on_recordish(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- def: ty::AdtDef<'tcx>,
- field: Ident,
- ) {
- let available_field_names = self.available_field_names(def.non_enum_variant(), expr, &[]);
- if let Some(suggested_field_name) =
- find_best_match_for_name(&available_field_names, field.name, None)
- {
- err.span_suggestion(
- field.span,
- "a field with a similar name exists",
- suggested_field_name,
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_label(field.span, "unknown field");
- if !available_field_names.is_empty() {
- err.note(format!(
- "available fields are: {}",
- self.name_series_display(available_field_names),
- ));
- }
- }
- }
-
fn maybe_suggest_array_indexing(
&self,
err: &mut Diagnostic,
@@ -2669,6 +2702,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
field: Ident,
len: ty::Const<'tcx>,
) {
+ err.span_label(field.span, "unknown field");
if let (Some(len), Ok(user_index)) =
(len.try_eval_target_usize(self.tcx, self.param_env), field.as_str().parse::<u64>())
&& let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span)
@@ -2691,6 +2725,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base: &hir::Expr<'_>,
field: Ident,
) {
+ err.span_label(field.span, "unknown field");
if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
let msg = format!("`{base}` is a raw pointer; try dereferencing it");
let suggestion = format!("(*{base}).{field}");
@@ -2709,7 +2744,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut err = type_error_struct!(
self.tcx().sess,
- field.span,
+ span,
expr_t,
E0609,
"no field `{field}` on type `{expr_t}`",
@@ -2717,10 +2752,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// try to add a suggestion in case the field is a nested field of a field of the Adt
let mod_id = self.tcx.parent_module(id).to_def_id();
- if let Some((fields, args)) =
- self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
+ let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind()
+ && (self.tcx.is_diagnostic_item(sym::Result, def.did())
+ || self.tcx.is_diagnostic_item(sym::Option, def.did()))
+ && let Some(arg) = args.get(0)
+ && let Some(ty) = arg.as_type()
+ {
+ (ty, "unwrap().")
+ } else {
+ (expr_t, "")
+ };
+ for (found_fields, args) in
+ self.get_field_candidates_considering_privacy(span, ty, mod_id, id)
{
- let candidate_fields: Vec<_> = fields
+ let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
+ let mut candidate_fields: Vec<_> = found_fields
+ .into_iter()
.filter_map(|candidate_field| {
self.check_for_nested_field_satisfying(
span,
@@ -2729,17 +2776,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
args,
vec![],
mod_id,
+ id,
)
})
.map(|mut field_path| {
field_path.pop();
field_path
.iter()
- .map(|id| id.name.to_ident_string())
- .collect::<Vec<String>>()
- .join(".")
+ .map(|id| format!("{}.", id.name.to_ident_string()))
+ .collect::<String>()
})
.collect::<Vec<_>>();
+ candidate_fields.sort();
let len = candidate_fields.len();
if len > 0 {
@@ -2750,9 +2798,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if len > 1 { "some" } else { "one" },
if len > 1 { "have" } else { "has" },
),
- candidate_fields.iter().map(|path| format!("{path}.")),
+ candidate_fields.iter().map(|path| format!("{unwrap}{path}")),
Applicability::MaybeIncorrect,
);
+ } else {
+ if let Some(field_name) = find_best_match_for_name(&field_names, field.name, None) {
+ err.span_suggestion_verbose(
+ field.span,
+ "a field with a similar name exists",
+ format!("{unwrap}{}", field_name),
+ Applicability::MaybeIncorrect,
+ );
+ } else if !field_names.is_empty() {
+ let is = if field_names.len() == 1 { " is" } else { "s are" };
+ err.note(format!(
+ "available field{is}: {}",
+ self.name_series_display(field_names),
+ ));
+ }
}
}
err
@@ -2781,33 +2844,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
base_ty: Ty<'tcx>,
mod_id: DefId,
- ) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, GenericArgsRef<'tcx>)> {
+ hir_id: hir::HirId,
+ ) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> {
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
- for (base_t, _) in self.autoderef(span, base_ty) {
- match base_t.kind() {
- ty::Adt(base_def, args) if !base_def.is_enum() => {
- let tcx = self.tcx;
- let fields = &base_def.non_enum_variant().fields;
- // Some struct, e.g. some that impl `Deref`, have all private fields
- // because you're expected to deref them to access the _real_ fields.
- // This, for example, will help us suggest accessing a field through a `Box<T>`.
- if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
- continue;
+ self.autoderef(span, base_ty)
+ .filter_map(move |(base_t, _)| {
+ match base_t.kind() {
+ ty::Adt(base_def, args) if !base_def.is_enum() => {
+ let tcx = self.tcx;
+ let fields = &base_def.non_enum_variant().fields;
+ // Some struct, e.g. some that impl `Deref`, have all private fields
+ // because you're expected to deref them to access the _real_ fields.
+ // This, for example, will help us suggest accessing a field through a `Box<T>`.
+ if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
+ return None;
+ }
+ return Some((
+ fields
+ .iter()
+ .filter(move |field| {
+ field.vis.is_accessible_from(mod_id, tcx)
+ && self.is_field_suggestable(field, hir_id, span)
+ })
+ // For compile-time reasons put a limit on number of fields we search
+ .take(100)
+ .collect::<Vec<_>>(),
+ *args,
+ ));
}
- return Some((
- fields
- .iter()
- .filter(move |field| field.vis.is_accessible_from(mod_id, tcx))
- // For compile-time reasons put a limit on number of fields we search
- .take(100),
- args,
- ));
+ _ => None,
}
- _ => {}
- }
- }
- None
+ })
+ .collect()
}
/// This method is called after we have encountered a missing field error to recursively
@@ -2820,6 +2889,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
subst: GenericArgsRef<'tcx>,
mut field_path: Vec<Ident>,
mod_id: DefId,
+ hir_id: HirId,
) -> Option<Vec<Ident>> {
debug!(
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
@@ -2835,20 +2905,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let field_ty = candidate_field.ty(self.tcx, subst);
if matches(candidate_field, field_ty) {
return Some(field_path);
- } else if let Some((nested_fields, subst)) =
- self.get_field_candidates_considering_privacy(span, field_ty, mod_id)
- {
- // recursively search fields of `candidate_field` if it's a ty::Adt
- for field in nested_fields {
- if let Some(field_path) = self.check_for_nested_field_satisfying(
- span,
- matches,
- field,
- subst,
- field_path.clone(),
- mod_id,
- ) {
- return Some(field_path);
+ } else {
+ for (nested_fields, subst) in
+ self.get_field_candidates_considering_privacy(span, field_ty, mod_id, hir_id)
+ {
+ // recursively search fields of `candidate_field` if it's a ty::Adt
+ for field in nested_fields {
+ if let Some(field_path) = self.check_for_nested_field_satisfying(
+ span,
+ matches,
+ field,
+ subst,
+ field_path.clone(),
+ mod_id,
+ hir_id,
+ ) {
+ return Some(field_path);
+ }
}
}
}
@@ -2863,8 +2936,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
brackets_span: Span,
) -> Ty<'tcx> {
- let base_t = self.check_expr(&base);
- let idx_t = self.check_expr(&idx);
+ let base_t = self.check_expr(base);
+ let idx_t = self.check_expr(idx);
if base_t.references_error() {
base_t
@@ -2877,7 +2950,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// two-phase not needed because index_ty is never mutable
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
self.select_obligations_where_possible(|errors| {
- self.point_at_index_if_possible(errors, idx.span)
+ self.point_at_index(errors, idx.span);
});
element_ty
}
@@ -2905,7 +2978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut needs_note = true;
// If the index is an integer, we can show the actual
// fixed expression:
- if let ExprKind::Lit(ref lit) = idx.kind
+ if let ExprKind::Lit(lit) = idx.kind
&& let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node
&& i < types
.len()
@@ -2978,7 +3051,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
};
- self.commit_if_ok(|_| {
+ self.commit_if_ok(|snapshot| {
+ let outer_universe = self.universe();
+
let ocx = ObligationCtxt::new(self);
let impl_args = self.fresh_args_for_item(base_expr.span, impl_def_id);
let impl_trait_ref =
@@ -2988,7 +3063,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Match the impl self type against the base ty. If this fails,
// we just skip this impl, since it's not particularly useful.
let impl_trait_ref = ocx.normalize(&cause, self.param_env, impl_trait_ref);
- ocx.eq(&cause, self.param_env, impl_trait_ref.self_ty(), base_ty)?;
+ ocx.eq(&cause, self.param_env, base_ty, impl_trait_ref.self_ty())?;
// Register the impl's predicates. One of these predicates
// must be unsatisfied, or else we wouldn't have gotten here
@@ -3024,11 +3099,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ty::new_projection(self.tcx, index_trait_output_def_id, impl_trait_ref.args),
);
- let errors = ocx.select_where_possible();
+ let true_errors = ocx.select_where_possible();
+
+ // Do a leak check -- we can't really report report a useful error here,
+ // but it at least avoids an ICE when the error has to do with higher-ranked
+ // lifetimes.
+ self.leak_check(outer_universe, Some(snapshot))?;
+
+ // Bail if we have ambiguity errors, which we can't report in a useful way.
+ let ambiguity_errors = ocx.select_all_or_error();
+ if true_errors.is_empty() && !ambiguity_errors.is_empty() {
+ return Err(NoSolution);
+ }
+
// There should be at least one error reported. If not, we
// will still delay a span bug in `report_fulfillment_errors`.
Ok::<_, NoSolution>((
- self.err_ctxt().report_fulfillment_errors(errors),
+ self.err_ctxt().report_fulfillment_errors(true_errors),
impl_trait_ref.args.type_at(1),
element_ty,
))
@@ -3036,16 +3123,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.ok()
}
- fn point_at_index_if_possible(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- span: Span,
- ) {
+ fn point_at_index(&self, errors: &mut Vec<traits::FulfillmentError<'tcx>>, span: Span) {
+ let mut seen_preds = FxHashSet::default();
+ // We re-sort here so that the outer most root obligations comes first, as we have the
+ // subsequent weird logic to identify *every* relevant obligation for proper deduplication
+ // of diagnostics.
+ errors.sort_by_key(|error| error.root_obligation.recursion_depth);
for error in errors {
- match error.obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate))
- if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => {
+ match (
+ error.root_obligation.predicate.kind().skip_binder(),
+ error.obligation.predicate.kind().skip_binder(),
+ ) {
+ (ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)), _)
+ if self.tcx.lang_items().index_trait() == Some(predicate.trait_ref.def_id) =>
+ {
+ seen_preds.insert(error.obligation.predicate.kind().skip_binder());
+ }
+ (_, ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)))
+ if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) =>
+ {
+ seen_preds.insert(error.obligation.predicate.kind().skip_binder());
}
+ (root, pred) if seen_preds.contains(&pred) || seen_preds.contains(&root) => {}
_ => continue,
}
error.obligation.cause.span = span;
@@ -3060,7 +3159,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
match self.resume_yield_tys {
Some((resume_ty, yield_ty)) => {
- self.check_expr_coercible_to_type(&value, yield_ty, None);
+ self.check_expr_coercible_to_type(value, yield_ty, None);
resume_ty
}
@@ -3069,7 +3168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// information. Hence, we check the source of the yield expression here and check its
// value's type against `()` (this check should always hold).
None if src.is_await() => {
- self.check_expr_coercible_to_type(&value, Ty::new_unit(self.tcx), None);
+ self.check_expr_coercible_to_type(value, Ty::new_unit(self.tcx), None);
Ty::new_unit(self.tcx)
}
_ => {
@@ -3163,7 +3262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match container.kind() {
ty::Adt(container_def, args) if container_def.is_enum() => {
- let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ let block = self.tcx.local_def_id_to_hir_id(self.body_id);
let (ident, _def_scope) =
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
@@ -3173,19 +3272,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sym::offset_of_enum,
ident.span,
"using enums in offset_of is experimental",
- ).emit();
+ )
+ .emit();
}
- let Some((index, variant)) = container_def.variants()
+ let Some((index, variant)) = container_def
+ .variants()
.iter_enumerated()
- .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) else {
+ .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident)
+ else {
let mut err = type_error_struct!(
self.tcx().sess,
ident.span,
container,
E0599,
"no variant named `{ident}` found for enum `{container}`",
- );
+ );
err.span_label(field.span, "variant not found");
err.emit();
break;
@@ -3197,7 +3299,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
container,
E0795,
"`{ident}` is an enum variant; expected field at end of `offset_of`",
- );
+ );
err.span_label(field.span, "enum variant");
err.emit();
break;
@@ -3205,16 +3307,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (subident, sub_def_scope) =
self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block);
- let Some((subindex, field)) = variant.fields
+ let Some((subindex, field)) = variant
+ .fields
.iter_enumerated()
- .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident) else {
+ .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident)
+ else {
let mut err = type_error_struct!(
self.tcx().sess,
ident.span,
container,
E0609,
"no field named `{subfield}` on enum variant `{container}::{ident}`",
- );
+ );
err.span_label(field.span, "this enum variant...");
err.span_label(subident.span, "...does not have this field");
err.emit();
@@ -3240,7 +3344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue;
}
ty::Adt(container_def, args) => {
- let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ let block = self.tcx.local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);