diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:20:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:20:39 +0000 |
commit | 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch) | |
tree | 3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /compiler/rustc_hir_typeck/src | |
parent | Releasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.tar.xz rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.zip |
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck/src')
26 files changed, 1031 insertions, 907 deletions
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index e19ef2ff3..6c2ce6272 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -299,7 +299,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // check that the `if` expr without `else` is the fn body's expr if expr.span == sp { - return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { + return self.get_fn_decl(hir_id).and_then(|(_, fn_decl, _)| { let span = fn_decl.output.span(); let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; Some((span, format!("expected `{snippet}` because of this return type"))) @@ -538,8 +538,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(rpitit): This will need to be fixed when we move to associated types assert!(matches!( *trait_pred.trait_ref.self_ty().kind(), - ty::Alias(_, ty::AliasTy { def_id, substs, .. }) - if def_id == rpit_def_id && substs == substs + ty::Alias(_, ty::AliasTy { def_id, substs: alias_substs, .. }) + if def_id == rpit_def_id && substs == alias_substs )); ty::PredicateKind::Clause(ty::Clause::Trait( trait_pred.with_self_ty(self.tcx, ty), @@ -548,8 +548,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => { assert!(matches!( *proj_pred.projection_ty.self_ty().kind(), - ty::Alias(_, ty::AliasTy { def_id, substs, .. }) - if def_id == rpit_def_id && substs == substs + ty::Alias(_, ty::AliasTy { def_id, substs: alias_substs, .. }) + if def_id == rpit_def_id && substs == alias_substs )); proj_pred = proj_pred.with_self_ty(self.tcx, ty); ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred)) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 6a0d5c011..5235710a2 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -311,9 +311,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_decl_span = if hir.body(body).generator_kind == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) { - // Actually need to unwrap a few more layers of HIR to get to + // Actually need to unwrap one more layer of HIR to get to // the _real_ closure... - let async_closure = hir.parent_id(hir.parent_id(parent_hir_id)); + let async_closure = hir.parent_id(parent_hir_id); if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. @@ -668,7 +668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty) - && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) + && !self.type_is_sized_modulo_regions(self.param_env, output_ty) { let descr = match maybe_def { DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 316c2a7ee..1481c038c 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -96,20 +96,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let t = self.resolve_vars_if_possible(t); t.error_reported()?; - if self.type_is_sized_modulo_regions(self.param_env, t, span) { + if self.type_is_sized_modulo_regions(self.param_env, t) { return Ok(Some(PointerKind::Thin)); } Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), - ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { - None => Some(PointerKind::Thin), - Some(f) => { - let field_ty = self.field_ty(span, f, substs); - self.pointer_kind(field_ty, span)? + ty::Adt(def, substs) if def.is_struct() => { + match def.non_enum_variant().fields.raw.last() { + None => Some(PointerKind::Thin), + Some(f) => { + let field_ty = self.field_ty(span, f, substs); + self.pointer_kind(field_ty, span)? + } } - }, + } ty::Tuple(fields) => match fields.last() { None => Some(PointerKind::Thin), Some(&f) => self.pointer_kind(f, span)?, @@ -722,7 +724,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); - if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span) + if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty) && !self.cast_ty.has_infer_types() { self.report_cast_to_unsized_type(fcx); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index d84fabb78..8c2495e1d 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -2,13 +2,12 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; -use hir::def::DefKind; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::LateBoundRegionConversionTime; +use rustc_infer::infer::{DefineOpaqueTypes, LateBoundRegionConversionTime}; use rustc_infer::infer::{InferOk, InferResult}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::subst::InternalSubsts; @@ -205,15 +204,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut expected_sig = None; let mut expected_kind = None; - for obligation in traits::elaborate_predicates_with_span( + for (pred, span) in traits::elaborate( self.tcx, // Reverse the obligations here, since `elaborate_*` uses a stack, // and we want to keep inference generally in the same order of // the registered obligations. predicates.rev(), - ) { - debug!(?obligation.predicate); - let bound_predicate = obligation.predicate.kind(); + ) + // We only care about self bounds + .filter_only_self() + { + debug!(?pred); + let bound_predicate = pred.kind(); // Given a Projection predicate, we can potentially infer // the complete signature. @@ -221,9 +223,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder() { let inferred_sig = self.normalize( - obligation.cause.span, + span, self.deduce_sig_from_projection( - Some(obligation.cause.span), + Some(span), bound_predicate.rebind(proj_predicate), ), ); @@ -398,7 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Here: /// - E would be `fn(&u32) -> &u32`. - /// - S would be `fn(&u32) -> + /// - S would be `fn(&u32) -> ?T` /// - E' is `&'!0 u32 -> &'!0 u32` /// - S' is `&'?0 u32 -> ?T` /// @@ -563,10 +565,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { // Check that E' = S'. let cause = self.misc(hir_ty.span); - let InferOk { value: (), obligations } = self - .at(&cause, self.param_env) - .define_opaque_types(true) - .eq(*expected_ty, supplied_ty)?; + let InferOk { value: (), obligations } = self.at(&cause, self.param_env).eq( + DefineOpaqueTypes::Yes, + *expected_ty, + supplied_ty, + )?; all_obligations.extend(obligations); } @@ -576,10 +579,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { supplied_sig.output(), ); let cause = &self.misc(decl.output.span()); - let InferOk { value: (), obligations } = self - .at(cause, self.param_env) - .define_opaque_types(true) - .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; + let InferOk { value: (), obligations } = self.at(cause, self.param_env).eq( + DefineOpaqueTypes::Yes, + expected_sigs.liberated_sig.output(), + supplied_output_ty, + )?; all_obligations.extend(obligations); let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty)); @@ -713,14 +717,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .subst_iter_copied(self.tcx, substs) .find_map(|(p, s)| get_future_output(p, s))?, ty::Error(_) => return None, - ty::Alias(ty::Projection, proj) - if self.tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder => - { - self.tcx - .bound_explicit_item_bounds(proj.def_id) - .subst_iter_copied(self.tcx, proj.substs) - .find_map(|(p, s)| get_future_output(p, s))? - } + ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => self + .tcx + .bound_explicit_item_bounds(proj.def_id) + .subst_iter_copied(self.tcx, proj.substs) + .find_map(|(p, s)| get_future_output(p, s))?, _ => span_bug!( self.tcx.def_span(expr_def_id), "async fn generator return type not an inference variable: {ret_ty}" diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 00b86890b..8fa3bcd68 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -45,8 +45,8 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Expr; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{Coercion, InferOk, InferResult}; -use rustc_infer::traits::Obligation; +use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; +use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -143,11 +143,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); self.commit_if_ok(|_| { - let at = self.at(&self.cause, self.fcx.param_env).define_opaque_types(true); + let at = self.at(&self.cause, self.fcx.param_env); if self.use_lub { - at.lub(b, a) + at.lub(DefineOpaqueTypes::Yes, b, a) } else { - at.sup(b, a) + at.sup(DefineOpaqueTypes::Yes, b, a) .map(|InferOk { value: (), obligations }| InferOk { value: a, obligations }) } }) @@ -175,7 +175,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // so this will have the side-effect of making sure we have no ambiguities // due to `[type error]` and `_` not coercing together. let _ = self.commit_if_ok(|_| { - self.at(&self.cause, self.param_env).define_opaque_types(true).eq(a, b) + self.at(&self.cause, self.param_env).eq(DefineOpaqueTypes::Yes, a, b) }); return success(vec![], self.fcx.tcx.ty_error(guar), vec![]); } @@ -597,13 +597,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // and almost never more than 3. By using a SmallVec we avoid an // allocation, at the (very small) cost of (occasionally) having to // shift subsequent elements down when removing the front element. - let mut queue: SmallVec<[_; 4]> = smallvec![traits::predicate_for_trait_def( + let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new( self.tcx, - self.fcx.param_env, cause, - coerce_unsized_did, - 0, - [coerce_source, coerce_target] + self.fcx.param_env, + self.tcx.mk_trait_ref(coerce_unsized_did, [coerce_source, coerce_target]) )]; let mut has_unsized_tuple_coercion = false; @@ -651,9 +649,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let self_ty = trait_pred.skip_binder().self_ty(); let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); - match (&self_ty.kind(), &unsize_ty.kind()) { - (ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) - if self.type_var_is_sized(*v) => + match (self_ty.kind(), unsize_ty.kind()) { + (&ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) + if self.type_var_is_sized(v) => { debug!("coerce_unsized: have sized infer {:?}", v); coercion.obligations.push(obligation); @@ -1101,9 +1099,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (ty::FnDef(..), ty::FnDef(..)) => { // Don't reify if the function types have a LUB, i.e., they // are the same function and their parameters have a LUB. - match self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - { + match self.commit_if_ok(|_| { + self.at(cause, self.param_env).lub( + DefineOpaqueTypes::No, + prev_ty, + new_ty, + ) + }) { // We have a LUB of prev_ty and new_ty, just return it. Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), Err(_) => { @@ -1153,7 +1155,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sig = self .at(cause, self.param_env) .trace(prev_ty, new_ty) - .lub(a_sig, b_sig) + .lub(DefineOpaqueTypes::No, a_sig, b_sig) .map(|ok| self.register_infer_ok_obligations(ok))?; // Reify both sides and return the reified fn pointer type. @@ -1237,7 +1239,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); return self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) + .commit_if_ok(|_| { + self.at(cause, self.param_env).lub(DefineOpaqueTypes::No, prev_ty, new_ty) + }) .map(|ok| self.register_infer_ok_obligations(ok)); } } @@ -1248,8 +1252,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(e) = first_error { Err(e) } else { - self.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - .map(|ok| self.register_infer_ok_obligations(ok)) + self.commit_if_ok(|_| { + self.at(cause, self.param_env).lub(DefineOpaqueTypes::No, prev_ty, new_ty) + }) + .map(|ok| self.register_infer_ok_obligations(ok)) } } Ok(ok) => { @@ -1487,8 +1493,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { assert!(expression_ty.is_unit(), "if let hack without unit type"); fcx.at(cause, fcx.param_env) // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs - .define_opaque_types(true) - .eq_exp(label_expression_as_expected, expression_ty, self.merged_ty()) + .eq_exp( + DefineOpaqueTypes::Yes, + label_expression_as_expected, + expression_ty, + self.merged_ty(), + ) .map(|infer_ok| { fcx.register_infer_ok_obligations(infer_ok); expression_ty @@ -1710,12 +1720,13 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); } } - fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + fcx.get_node_fn_decl(parent) + .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) } else { fcx.get_fn_decl(parent_id) }; - if let Some((fn_decl, can_suggest)) = fn_decl { + if let Some((fn_id, fn_decl, can_suggest)) = fn_decl { if blk_id.is_none() { pointing_at_return_type |= fcx.suggest_missing_return_type( &mut err, @@ -1723,7 +1734,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, can_suggest, - fcx.tcx.hir().get_parent_item(id).into(), + fn_id, ); } if !pointing_at_return_type { @@ -1734,17 +1745,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let parent_id = fcx.tcx.hir().get_parent_item(id); let parent_item = fcx.tcx.hir().get_by_def_id(parent_id.def_id); - if let (Some(expr), Some(_), Some((fn_decl, _, _))) = + if let (Some(expr), Some(_), Some((fn_id, fn_decl, _, _))) = (expression, blk_id, fcx.get_node_fn_decl(parent_item)) { fcx.suggest_missing_break_or_return_expr( - &mut err, - expr, - fn_decl, - expected, - found, - id, - parent_id.into(), + &mut err, expr, fn_decl, expected, found, id, fn_id, ); } @@ -1870,7 +1875,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { - if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) + if let Some((_, fn_decl, _)) = fcx.get_fn_decl(blk_id) && let hir::FnRetTy::Return(ty) = fn_decl.output && let ty = fcx.astconv().ast_ty_to_ty( ty) && let ty::Dynamic(..) = ty.kind() diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 7ba57b3b7..13442c316 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,6 +1,5 @@ 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; @@ -8,19 +7,18 @@ 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_infer::infer::{DefineOpaqueTypes, 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::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, TypeVisitableExt}; +use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeFoldable}; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_target::abi::FieldIdx; 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; @@ -61,9 +59,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected) || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected) - || self.note_result_coercion(err, expr, expected, expr_ty); + || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty); + if !suggested { - self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span); + self.note_source_of_type_mismatch_constraint( + err, + expr, + TypeMismatchSource::Ty(expected), + ); } } @@ -83,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_expected_due_to_let_ty(err, expr, error); self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error); self.note_type_is_not_clone(err, expected, expr_ty, expr); - self.note_internal_mutation_in_method(err, expr, expected, expr_ty); + self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty); self.check_for_range_as_method_call(err, expr, expr_ty, expected); self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected); self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty); @@ -113,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - match self.at(cause, self.param_env).define_opaque_types(true).sup(expected, actual) { + match self.at(cause, self.param_env).sup(DefineOpaqueTypes::Yes, expected, actual) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None @@ -143,7 +146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - match self.at(cause, self.param_env).define_opaque_types(true).eq(expected, actual) { + match self.at(cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, actual) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None @@ -217,37 +220,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } - pub fn point_at_expr_source_of_inferred_type( + /// Notes the point at which a variable is constrained to some type incompatible + /// with some expectation given by `source`. + pub fn note_source_of_type_mismatch_constraint( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - found: Ty<'tcx>, - expected: Ty<'tcx>, - mismatch_span: Span, + source: TypeMismatchSource<'tcx>, ) -> bool { - let map = self.tcx.hir(); + let hir = 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; - } + let hir::def::Res::Local(local_hir_id) = p.res else { return false; }; + let hir::Node::Pat(pat) = hir.get(local_hir_id) else { return false; }; + let (init_ty_hir_id, init) = match hir.get_parent(pat.hir_id) { + hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init), + hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)), + _ => return false, + }; + let Some(init_ty) = self.node_ty_opt(init_ty_hir_id) else { return false; }; // Locate all the usages of the relevant binding. - struct FindExprs<'hir> { + struct FindExprs<'tcx> { hir_id: hir::HirId, - uses: Vec<&'hir hir::Expr<'hir>>, + uses: Vec<&'tcx hir::Expr<'tcx>>, } - impl<'v> Visitor<'v> for FindExprs<'v> { - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + impl<'tcx> Visitor<'tcx> for FindExprs<'tcx> { + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { 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 @@ -258,180 +258,205 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - 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); + let mut expr_finder = FindExprs { hir_id: local_hir_id, uses: init.into_iter().collect() }; + let body = + hir.body(hir.maybe_body_owned_by(self.body_id).expect("expected item to have body")); expr_finder.visit_expr(body.value); - // Hack to make equality checks on types with inference variables and regions useful. - let mut eraser = BottomUpFolder { + + use rustc_infer::infer::type_variable::*; + use rustc_middle::infer::unify_key::*; + // Replaces all of the variables in the given type with a fresh inference variable. + let mut fudger = BottomUpFolder { tcx: self.tcx, + ty_op: |ty| { + if let ty::Infer(infer) = ty.kind() { + match infer { + ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }), + ty::InferTy::IntVar(_) => self.next_int_var(), + ty::InferTy::FloatVar(_) => self.next_float_var(), + _ => bug!(), + } + } else { + ty + } + }, lt_op: |_| self.tcx.lifetimes.re_erased, - ct_op: |c| c, - ty_op: |t| match *t.kind() { - ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)), - ty::Infer(ty::IntVar(_)) => self.tcx.mk_int_var(ty::IntVid { index: 0 }), - ty::Infer(ty::FloatVar(_)) => self.tcx.mk_float_var(ty::FloatVid { index: 0 }), - _ => t, + ct_op: |ct| { + if let ty::ConstKind::Infer(_) = ct.kind() { + self.next_const_var( + ct.ty(), + ConstVariableOrigin { + kind: ConstVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }, + ) + } else { + ct + } }, }; - let mut prev = eraser.fold_ty(ty); - let mut prev_span: Option<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).subst_identity(); - let def_self_ty = sig.input(0).skip_binder(); - let param_tys = sig.inputs().skip_binder().iter().skip(1); - // If there's an arity mismatch, pointing out the call as the source of an inference - // can be misleading, so we skip it. - if param_tys.len() != args.len() { - continue; - } - 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_for_diagnostic( - segment.ident, - rcvr_ty, - expr, - probe::ProbeScope::TraitsInScope, - None, - ) { - Ok(pick) => eraser.fold_ty(pick.self_ty), - Err(_) => rcvr_ty, + + let expected_ty = match source { + TypeMismatchSource::Ty(expected_ty) => expected_ty, + // Try to deduce what the possible value of `expr` would be if the + // incompatible arg were compatible. For example, given `Vec<i32>` + // and `vec.push(1u32)`, we ideally want to deduce that the type of + // `vec` *should* have been `Vec<u32>`. This will allow us to then + // run the subsequent code with this expectation, finding out exactly + // when this type diverged from our expectation. + TypeMismatchSource::Arg { call_expr, incompatible_arg: idx } => { + let hir::ExprKind::MethodCall(segment, _, args, _) = call_expr.kind else { + return false; }; - // 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 Some(arg_ty) = self.node_ty_opt(args[idx].hir_id) else { + return false; }; - 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) { - // We only point at the first place where the found type was inferred. - for (param_ty, arg) in param_tys.zip(args) { - 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_ty = self.node_ty(arg.hir_id); - if !arg.span.overlaps(mismatch_span) { - 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)); - } - } + let possible_rcvr_ty = expr_finder.uses.iter().find_map(|binding| { + let possible_rcvr_ty = self.node_ty_opt(binding.hir_id)?; + // Fudge the receiver, so we can do new inference on it. + let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger); + let method = self + .lookup_method( + possible_rcvr_ty, + segment, + DUMMY_SP, + call_expr, + binding, + args, + ) + .ok()?; + // Unify the method signature with our incompatible arg, to + // do inference in the *opposite* direction and to find out + // what our ideal rcvr ty would look like. + let _ = self + .at(&ObligationCause::dummy(), self.param_env) + .eq(DefineOpaqueTypes::No, method.sig.inputs()[idx + 1], arg_ty) + .ok()?; + self.select_obligations_where_possible(|errs| { + // Yeet the errors, we're already reporting errors. + errs.clear(); + }); + Some(self.resolve_vars_if_possible(possible_rcvr_ty)) + }); + if let Some(rcvr_ty) = possible_rcvr_ty { + rcvr_ty + } else { + return false; } + } + }; - // 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![], + // If our expected_ty does not equal init_ty, then it *began* as incompatible. + // No need to note in this case... + if !self.can_eq(self.param_env, expected_ty, init_ty.fold_with(&mut fudger)) { + return false; + } + + for window in expr_finder.uses.windows(2) { + // Bindings always update their recorded type after the fact, so we + // need to look at the *following* usage's type to see when the + // binding became incompatible. + let [binding, next_usage] = *window else { continue; }; + + // Don't go past the binding (always gonna be a nonsense label if so) + if binding.hir_id == expr.hir_id { + break; + } + + let Some(next_use_ty) = self.node_ty_opt(next_usage.hir_id) else { continue; }; + + // If the type is not constrained in a way making it not possible to + // equate with `expected_ty` by this point, skip. + if self.can_eq(self.param_env, expected_ty, next_use_ty.fold_with(&mut fudger)) { + continue; + } + + if let hir::Node::Expr(parent_expr) = hir.get_parent(binding.hir_id) + && let hir::ExprKind::MethodCall(segment, rcvr, args, _) = parent_expr.kind + && rcvr.hir_id == binding.hir_id + { + // If our binding became incompatible while it was a receiver + // to a method call, we may be able to make a better guess to + // the source of a type mismatch. + let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; }; + let rcvr_ty = rcvr_ty.fold_with(&mut fudger); + let Ok(method) = + self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args) + else { + continue; }; - 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) { 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) + let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger); + let ideal_method = self + .lookup_method(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args) + .ok() + .and_then(|method| { + let _ = self.at(&ObligationCause::dummy(), self.param_env) + .eq(DefineOpaqueTypes::No, ideal_rcvr_ty, expected_ty) + .ok()?; + Some(method) + }); + + // Find what argument caused our rcvr to become incompatible + // with the expected ty. + for (idx, (expected_arg_ty, arg_expr)) in + std::iter::zip(&method.sig.inputs()[1..], args).enumerate() { - // We only point at the first place where the found type was inferred. - if !segment.ident.span.overlaps(mismatch_span) { + let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; }; + let arg_ty = arg_ty.fold_with(&mut fudger); + let _ = self.try_coerce( + arg_expr, + arg_ty, + *expected_arg_ty, + AllowTwoPhase::No, + None, + ); + self.select_obligations_where_possible(|errs| { + // Yeet the errors, we're already reporting errors. + errs.clear(); + }); + // If our rcvr, after inference due to unifying the signature + // with the expected argument type, is still compatible with + // the rcvr, then it must've not been the source of blame. + if self.can_eq(self.param_env, rcvr_ty, expected_ty) { + continue; + } + err.span_label(arg_expr.span, format!("this argument has type `{arg_ty}`...")); 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) - { - // 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. - if !span.overlaps(mismatch_span) { - err.span_label( - span, - with_forced_trimmed_paths!(format!( - "here the type of `{ident}` is inferred to be `{ty}`", - )), + binding.span, + format!("... which causes `{ident}` to have type `{next_use_ty}`"), + ); + // Using our "ideal" method signature, suggest a fix to this + // blame arg, if possible. Don't do this if we're coming from + // arg mismatch code, because we'll possibly suggest a mutually + // incompatible fix at the original mismatch site. + if matches!(source, TypeMismatchSource::Ty(_)) + && let Some(ideal_method) = ideal_method + { + self.emit_type_mismatch_suggestions( + err, + arg_expr, + arg_ty, + self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]), + None, + None, ); } - break; + return true; } - 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); + err.span_label( + binding.span, + format!("here the type of `{ident}` is inferred to be `{next_use_ty}`"), + ); + return true; } - true + + // We must've not found something that constrained the expr. + false } fn annotate_expected_due_to_let_ty( @@ -707,7 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - pub(crate) fn note_result_coercion( + pub(crate) fn suggest_coercing_result_via_try_operator( &self, err: &mut Diagnostic, expr: &hir::Expr<'tcx>, @@ -850,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant.fields.len() == 1 }) .filter_map(|variant| { - let sole_field = &variant.fields[0]; + let sole_field = &variant.fields[FieldIdx::from_u32(0)]; let field_is_local = sole_field.did.is_local(); let field_is_accessible = @@ -1480,14 +1505,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For this suggestion to make sense, the type would need to be `Copy`, // or we have to be moving out of a `Box<T>` - if self.type_is_copy_modulo_regions(self.param_env, expected, sp) + if self.type_is_copy_modulo_regions(self.param_env, expected) // FIXME(compiler-errors): We can actually do this if the checked_ty is // `steps` layers of boxes, not just one, but this is easier and most likely. || (checked_ty.is_box() && steps == 1) { let deref_kind = if checked_ty.is_box() { "unboxing the value" - } else if checked_ty.is_region_ptr() { + } else if checked_ty.is_ref() { "dereferencing the borrow" } else { "dereferencing the type" @@ -2093,3 +2118,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +pub enum TypeMismatchSource<'tcx> { + /// Expected the binding to have the given type, but it was found to have + /// a different type. Find out when that type first became incompatible. + Ty(Ty<'tcx>), + /// When we fail during method argument checking, try to find out if a previous + /// expression has constrained the method's receiver in a way that makes the + /// argument's type incompatible. + Arg { call_expr: &'tcx hir::Expr<'tcx>, incompatible_arg: usize }, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7fc4ccb04..6ffa0134f 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -36,6 +36,7 @@ use rustc_hir_analysis::astconv::AstConv as _; use rustc_hir_analysis::check::ty_kind_suggestion; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::InferOk; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability; @@ -49,6 +50,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; @@ -118,7 +120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - pub(super) fn check_expr_coercable_to_type( + pub(super) fn check_expr_coercible_to_type( &self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, @@ -230,7 +232,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = ensure_sufficient_stack(|| match &expr.kind { hir::ExprKind::Path( - qpath @ hir::QPath::Resolved(..) | qpath @ hir::QPath::TypeRelative(..), + qpath @ (hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)), ) => self.check_expr_path(qpath, expr, args), _ => self.check_expr_kind(expr, expected), }); @@ -283,7 +285,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; match expr.kind { - ExprKind::Box(subexpr) => self.check_expr_box(subexpr, 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) => { @@ -358,16 +359,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { - let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { - ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), - _ => NoExpectation, - }); - let referent_ty = self.check_expr_with_expectation(expr, expected_inner); - self.require_type_is_sized(referent_ty, expr.span, traits::SizedBoxType); - self.tcx.mk_box(referent_ty) - } - fn check_expr_unary( &self, unop: hir::UnOp, @@ -798,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.ret_coercion_span.set(Some(expr.span)); } let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); - if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { + if let Some((_, fn_decl, _)) = self.get_fn_decl(expr.hir_id) { coercion.coerce_forced_unit( self, &cause, @@ -1137,7 +1128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - // This is (basically) inlined `check_expr_coercable_to_type`, but we want + // 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); if let (_, Some(mut diag)) = @@ -1410,7 +1401,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (element_ty, t) = match uty { Some(uty) => { - self.check_expr_coercable_to_type(&element, uty, None); + self.check_expr_coercible_to_type(&element, uty, None); (uty, uty) } None => { @@ -1487,7 +1478,7 @@ 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_coercable_to_type(&e, ety, None); + self.check_expr_coercible_to_type(&e, ety, None); ety } _ => self.check_expr_with_expectation(&e, NoExpectation), @@ -1571,8 +1562,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut remaining_fields = variant .fields - .iter() - .enumerate() + .iter_enumerated() .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) .collect::<FxHashMap<_, _>>(); @@ -1683,7 +1673,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(_) = remaining_fields.remove(&ident) { let target_ty = self.field_ty(base_expr.span, f, substs); let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup(target_ty, fru_ty) { + match self.at(&cause, self.param_env).sup( + DefineOpaqueTypes::No, + target_ty, + fru_ty, + ) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations) } @@ -1821,7 +1815,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, adt_ty: Ty<'tcx>, span: Span, - remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>, + remaining_fields: FxHashMap<Ident, (FieldIdx, &ty::FieldDef)>, variant: &'tcx ty::VariantDef, ast_fields: &'tcx [hir::ExprField<'tcx>], substs: SubstsRef<'tcx>, @@ -2215,11 +2209,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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; - if let Some(index) = fields - .iter() - .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident) + if let Some((index, field)) = fields + .iter_enumerated() + .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident) { - let field = &fields[index]; let field_ty = self.field_ty(expr.span, field, substs); // Save the index of all fields regardless of their visibility in case // of error recovery. @@ -2236,15 +2229,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } ty::Tuple(tys) => { - let fstr = field.as_str(); - if let Ok(index) = fstr.parse::<usize>() { - if fstr == index.to_string() { + if let Ok(index) = field.as_str().parse::<usize>() { + if field.name == sym::integer(index) { if let Some(&field_ty) = tys.get(index) { let adjustments = self.adjust_steps(&autoderef); self.apply_adjustments(base, adjustments); self.register_predicates(autoderef.into_obligations()); - self.write_field_index(expr.hir_id, index); + self.write_field_index(expr.hir_id, FieldIdx::from_usize(index)); return field_ty; } } @@ -2818,23 +2810,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "cannot index into a value of type `{base_t}`", ); // Try to give some advice about indexing tuples. - if let ty::Tuple(..) = base_t.kind() { + if let ty::Tuple(types) = base_t.kind() { 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 ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node { - let snip = self.tcx.sess.source_map().span_to_snippet(base.span); - if let Ok(snip) = snip { - err.span_suggestion( - expr.span, - "to access tuple elements, use", - format!("{snip}.{i}"), - Applicability::MachineApplicable, - ); - needs_note = false; - } + if let ExprKind::Lit(ref lit) = idx.kind + && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node + && i < types.len().try_into().expect("expected tuple index to be < usize length") + { + let snip = self.tcx.sess.source_map().span_to_snippet(base.span); + if let Ok(snip) = snip { + err.span_suggestion( + expr.span, + "to access tuple elements, use", + format!("{snip}.{i}"), + Applicability::MachineApplicable, + ); + needs_note = false; } + } else if let ExprKind::Path(..) = idx.peel_borrows().kind { + err.span_label(idx.span, "cannot access tuple elements at a variable index"); } if needs_note { err.help( @@ -2874,7 +2869,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { match self.resume_yield_tys { Some((resume_ty, yield_ty)) => { - self.check_expr_coercable_to_type(&value, yield_ty, None); + self.check_expr_coercible_to_type(&value, yield_ty, None); resume_ty } @@ -2883,7 +2878,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_coercable_to_type(&value, self.tcx.mk_unit(), None); + self.check_expr_coercible_to_type(&value, self.tcx.mk_unit(), None); self.tcx.mk_unit() } _ => { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index b9a058d6b..ee1c6fbfd 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -14,12 +14,11 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::PatKind; -use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt}; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::FIRST_VARIANT; use ty::BorrowKind::ImmBorrow; use crate::mem_categorization as mc; @@ -356,10 +355,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.walk_captures(closure); } - hir::ExprKind::Box(ref base) => { - self.consume_expr(base); - } - hir::ExprKind::Yield(value, _) => { self.consume_expr(value); } @@ -544,7 +539,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { match with_place.place.ty().kind() { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. - for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { + for (f_index, with_field) in adt.non_enum_variant().fields.iter_enumerated() { let is_mentioned = fields .iter() .any(|f| self.mc.typeck_results.opt_field_index(f.hir_id) == Some(f_index)); @@ -553,7 +548,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { &*with_expr, with_place.clone(), with_field.ty(self.tcx(), substs), - ProjectionKind::Field(f_index as u32, VariantIdx::new(0)), + ProjectionKind::Field(f_index, FIRST_VARIANT), ); self.delegate_consume(&field_place, field_place.hir_id); } @@ -564,7 +559,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // struct; however, when EUV is run during typeck, it // may not. This will generate an error earlier in typeck, // so we can just ignore it. - if !self.tcx().sess.has_errors().is_some() { + if self.tcx().sess.has_errors().is_none() { span_bug!(with_expr.span, "with expression doesn't evaluate to a struct"); } } @@ -871,10 +866,7 @@ fn copy_or_move<'a, 'tcx>( mc: &mc::MemCategorizationContext<'a, 'tcx>, place_with_id: &PlaceWithHirId<'tcx>, ) -> ConsumeMode { - if !mc.type_is_copy_modulo_regions( - place_with_id.place.ty(), - mc.tcx().hir().span(place_with_id.hir_id), - ) { + if !mc.type_is_copy_modulo_regions(place_with_id.place.ty()) { ConsumeMode::Move } else { ConsumeMode::Copy diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 60e55c7b0..f736f7a96 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -19,13 +19,13 @@ use rustc_hir_analysis::astconv::{ }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::InferResult; +use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, TyCtxt, UserType, + self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType, }; use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; @@ -33,6 +33,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; +use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; @@ -147,7 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { + pub fn write_field_index(&self, hir_id: hir::HirId, index: FieldIdx) { self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); } @@ -301,16 +302,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, def_id: DefId, substs: SubstsRef<'tcx>, - ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) { + ) -> ty::InstantiatedPredicates<'tcx> { let bounds = self.tcx.predicates_of(def_id); - let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect(); let result = bounds.instantiate(self.tcx, substs); let result = self.normalize(span, result); - debug!( - "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", - bounds, substs, result, spans, - ); - (result, spans) + debug!("instantiate_bounds(bounds={:?}, substs={:?}) = {:?}", bounds, substs, result); + result } pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T @@ -562,7 +559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = self.tcx.hir().body(body_id).value.span; let ok = self .at(&self.misc(span), self.param_env) - .eq(interior, witness) + .eq(DefineOpaqueTypes::No, interior, witness) .expect("Failed to unify generator interior type"); let mut obligations = ok.obligations; @@ -581,11 +578,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(in super::super) fn report_ambiguity_errors(&self) { - let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(); + let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(self); if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id); + self.err_ctxt().report_fulfillment_errors(&errors); } } @@ -598,7 +595,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id); + self.err_ctxt().report_fulfillment_errors(&result); } } @@ -670,7 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) // N.B., this predicate is created by breaking down a @@ -902,56 +899,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. + /// Given a function `Node`, return its `HirId` and `FnDecl` if it exists. Given a closure + /// that is the child of a function, return that function's `HirId` and `FnDecl` instead. + /// This may seem confusing at first, but this is used in diagnostics for `async fn`, + /// for example, where most of the type checking actually happens within a nested closure, + /// but we often want access to the parent function's signature. + /// + /// Otherwise, return false. pub(in super::super) fn get_node_fn_decl( &self, node: Node<'tcx>, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { + ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { match node { - Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => { // This is less than ideal, it will not suggest a return type span on any // method called `main`, regardless of whether it is actually the entry point, // but it will still present it as the reason for the expected type. - Some((&sig.decl, ident, ident.name != sym::main)) + Some(( + hir::HirId::make_owner(owner_id.def_id), + &sig.decl, + ident, + ident.name != sym::main, + )) } Node::TraitItem(&hir::TraitItem { ident, kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, .. - }) => Some((&sig.decl, ident, true)), + }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, true)), Node::ImplItem(&hir::ImplItem { ident, kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, .. - }) => Some((&sig.decl, ident, false)), - Node::Expr(&hir::Expr { - hir_id, - kind: hir::ExprKind::Closure(..), - .. - }) if let Some(Node::Expr(&hir::Expr { - hir_id, - kind: hir::ExprKind::Call(..), - .. - })) = self.tcx.hir().find_parent(hir_id) && - let Some(Node::Item(&hir::Item { + }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)), + Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. }) + if let Some(Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + })) = self.tcx.hir().find_parent(hir_id) => Some(( + hir::HirId::make_owner(owner_id.def_id), + &sig.decl, ident, - kind: hir::ItemKind::Fn(ref sig, ..), - .. - })) = self.tcx.hir().find_parent(hir_id) => { - Some((&sig.decl, ident, ident.name != sym::main)) - }, + ident.name != sym::main, + )), _ => None, } } - /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a + /// Given a `HirId`, return the `HirId` of the enclosing function, its `FnDecl`, and whether a /// suggestion can be made, `None` otherwise. - pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { + pub fn get_fn_decl( + &self, + blk_id: hir::HirId, + ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { let parent = self.tcx.hir().get(blk_id); - self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + self.get_node_fn_decl(parent) + .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) }) } @@ -959,44 +974,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - expected: Ty<'tcx>, + expected: Option<Ty<'tcx>>, found: Ty<'tcx>, ) { if found != self.tcx.types.unit { return; } - if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { - if self - .typeck_results + + let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else { + return; + }; + + let rcvr_has_the_expected_type = self + .typeck_results + .borrow() + .expr_ty_adjusted_opt(rcvr) + .and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs())) + .unwrap_or(false); + + let prev_call_mutates_and_returns_unit = || { + self.typeck_results .borrow() - .expr_ty_adjusted_opt(rcvr) - .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) - { - return; - } - let mut sp = MultiSpan::from_span(path_segment.ident.span); - sp.push_span_label( - path_segment.ident.span, - format!( - "this call modifies {} in-place", - match rcvr.kind { - ExprKind::Path(QPath::Resolved( - None, - hir::Path { segments: [segment], .. }, - )) => format!("`{}`", segment.ident), - _ => "its receiver".to_string(), - } - ), - ); + .type_dependent_def_id(expr.hir_id) + .map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder()) + .and_then(|sig| sig.inputs_and_output.split_last()) + .map(|(output, inputs)| { + output.is_unit() + && inputs + .get(0) + .and_then(|self_ty| self_ty.ref_mutability()) + .map_or(false, rustc_ast::Mutability::is_mut) + }) + .unwrap_or(false) + }; + + if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) { + return; + } + + let mut sp = MultiSpan::from_span(path_segment.ident.span); + sp.push_span_label( + path_segment.ident.span, + format!( + "this call modifies {} in-place", + match rcvr.kind { + ExprKind::Path(QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) => format!("`{}`", segment.ident), + _ => "its receiver".to_string(), + } + ), + ); + + let modifies_rcvr_note = + format!("method `{}` modifies its receiver in-place", path_segment.ident); + if rcvr_has_the_expected_type { sp.push_span_label( rcvr.span, "you probably want to use this value after calling the method...", ); + err.span_note(sp, &modifies_rcvr_note); + err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } else if let ExprKind::MethodCall(..) = rcvr.kind { err.span_note( sp, - &format!("method `{}` modifies its receiver in-place", path_segment.ident), + modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.", ); - err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } else { + err.span_note(sp, &modifies_rcvr_note); } } @@ -1319,7 +1365,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This also occurs for an enum variant on a type alias. let impl_ty = self.normalize(span, tcx.type_of(impl_def_id).subst(tcx, substs)); let self_ty = self.normalize(span, self_ty); - match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) { + match self.at(&self.misc(span), self.param_env).eq( + DefineOpaqueTypes::No, + impl_ty, + self_ty, + ) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { self.tcx.sess.delay_span_bug( @@ -1367,41 +1417,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let param_env = self.param_env; - let remap = match self.tcx.def_kind(def_id) { - // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when - // `Self: Trait` is satisfied because it does not matter whether the impl is `const`. - // Therefore we have to remap the param env here to be non-const. - hir::def::DefKind::AssocConst => true, - hir::def::DefKind::AssocFn - if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait => - { - // N.B.: All callsites to this function involve checking a path expression. - // - // When instantiating a trait method as a function item, it does not actually matter whether - // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as - // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would - // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a - // `const fn` pointer. - // - // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy - // `~const FnOnce` or can be coerced to `const fn` pointer. - true - } - _ => false, - }; - let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); + let bounds = self.instantiate_bounds(span, def_id, &substs); - for mut obligation in traits::predicates_for_generics( + for obligation in traits::predicates_for_generics( |idx, predicate_span| { traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) }, param_env, bounds, ) { - if remap { - obligation = obligation.without_const(self.tcx); - } - self.register_predicate(obligation); + // N.B. We are remapping all predicates to non-const since we don't know if we just + // want them as function pointers or we are calling them from a const-context. The + // actual checking will occur in `rustc_const_eval::transform::check_consts`. + self.register_predicate(obligation.without_const(self.tcx)); } } @@ -1415,7 +1443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() - .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true) .emit() }); let err = self.tcx.ty_error(e); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index b09886fe3..f879ccbb3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -3,10 +3,8 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::ty::{ - self, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, -}; -use rustc_span::{self, Span}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_span::{self, symbol::kw, Span}; use rustc_trait_selection::traits; use std::ops::ControlFlow; @@ -27,17 +25,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let generics = self.tcx.generics_of(def_id); let predicate_substs = match unsubstituted_pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs, - ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => pred.projection_ty.substs, - _ => ty::List::empty(), + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs.to_vec(), + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { + pred.projection_ty.substs.to_vec() + } + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(arg, ty)) => { + vec![ty.into(), arg.into()] + } + ty::PredicateKind::ConstEvaluatable(e) => vec![e.into()], + _ => return false, }; - let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { - predicate_substs.types().find_map(|ty| { - ty.walk().find_map(|arg| { + let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| { + predicate_substs.iter().find_map(|arg| { + arg.walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Param(param_ty) = ty.kind() - && matches(param_ty) + && let ty::Param(param_ty) = *ty.kind() + && matches(ty::ParamTerm::Ty(param_ty)) + { + Some(arg) + } else if let ty::GenericArgKind::Const(ct) = arg.unpack() + && let ty::ConstKind::Param(param_ct) = ct.kind() + && matches(ty::ParamTerm::Const(param_ct)) { Some(arg) } else { @@ -49,26 +58,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Prefer generics that are local to the fn item, since these are likely // to be the cause of the unsatisfied predicate. - let mut param_to_point_at = find_param_matching(&|param_ty| { - self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id + let mut param_to_point_at = find_param_matching(&|param_term| { + self.tcx.parent(generics.param_at(param_term.index(), self.tcx).def_id) == def_id }); // Fall back to generic that isn't local to the fn item. This will come // from a trait or impl, for example. - let mut fallback_param_to_point_at = find_param_matching(&|param_ty| { - self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id - && param_ty.name != rustc_span::symbol::kw::SelfUpper + let mut fallback_param_to_point_at = find_param_matching(&|param_term| { + self.tcx.parent(generics.param_at(param_term.index(), self.tcx).def_id) != def_id + && !matches!(param_term, ty::ParamTerm::Ty(ty) if ty.name == kw::SelfUpper) }); // Finally, the `Self` parameter is possibly the reason that the predicate // is unsatisfied. This is less likely to be true for methods, because // method probe means that we already kinda check that the predicates due // to the `Self` type are true. - let mut self_param_to_point_at = - find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper); + let mut self_param_to_point_at = find_param_matching( + &|param_term| matches!(param_term, ty::ParamTerm::Ty(ty) if ty.name == kw::SelfUpper), + ); // Finally, for ambiguity-related errors, we actually want to look // for a parameter that is the source of the inference type left // over in this predicate. - if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code { + if let traits::FulfillmentErrorCode::CodeAmbiguity { .. } = error.code { fallback_param_to_point_at = None; self_param_to_point_at = None; param_to_point_at = @@ -227,15 +237,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id)); let Some((index, _)) = own_substs .iter() - .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) .enumerate() .find(|(_, arg)| **arg == param_to_point_at) else { return false }; let Some(arg) = segment .args() .args - .iter() - .filter(|arg| matches!(arg, hir::GenericArg::Type(_))) - .nth(index) else { return false; }; + .get(index) else { return false; }; error.obligation.cause.span = arg .span() .find_ancestor_in_same_ctxt(error.obligation.cause.span) @@ -302,7 +309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter(|field| { let field_ty = field.ty(self.tcx, identity_substs); - Self::find_param_in_ty(field_ty.into(), param_to_point_at) + find_param_in_ty(field_ty.into(), param_to_point_at) }) .collect(); @@ -348,7 +355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .inputs() .iter() .enumerate() - .filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at)) + .filter(|(_, ty)| find_param_in_ty((**ty).into(), param_to_point_at)) .collect(); // If there's one field that references the given generic, great! if let [(idx, _)] = args_referencing_param.as_slice() @@ -459,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize. /// /// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be - /// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be + /// reported as an error. If it is `Ok`, then it means it refined successful. If it is `Err`, then it may be /// only a partial success - but it cannot be refined even further. fn blame_specific_expr_if_possible_for_derived_predicate_obligation( &self, @@ -527,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - in_ty: `(Option<Vec<T>, bool)` /// we would drill until we arrive at `vec![1, 2, 3]`. /// - /// If successful, we return `Ok(refined_expr)`. If unsuccesful, we return `Err(partially_refined_expr`), + /// If successful, we return `Ok(refined_expr)`. If unsuccessful, we return `Err(partially_refined_expr`), /// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to /// `foo()` and then return `Err("foo()")`. /// @@ -571,8 +578,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find out which of `in_ty_elements` refer to `param`. // FIXME: It may be better to take the first if there are multiple, // just so that the error points to a smaller expression. - let Some((drill_expr, drill_ty)) = Self::is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| { - Self::find_param_in_ty((*in_ty_elem).into(), param) + let Some((drill_expr, drill_ty)) = is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| { + find_param_in_ty((*in_ty_elem).into(), param) })) else { // The param is not mentioned, or it is mentioned in multiple indexes. return Err(expr); @@ -620,10 +627,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We need to know which of the generic parameters mentions our target param. // We expect that at least one of them does, since it is expected to be mentioned. let Some((drill_generic_index, generic_argument_type)) = - Self::is_iterator_singleton( + is_iterator_singleton( in_ty_adt_generic_args.iter().enumerate().filter( |(_index, in_ty_generic)| { - Self::find_param_in_ty(*in_ty_generic, param) + find_param_in_ty(*in_ty_generic, param) }, ), ) else { @@ -743,10 +750,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We need to know which of the generic parameters mentions our target param. // We expect that at least one of them does, since it is expected to be mentioned. let Some((drill_generic_index, generic_argument_type)) = - Self::is_iterator_singleton( + is_iterator_singleton( in_ty_adt_generic_args.iter().enumerate().filter( |(_index, in_ty_generic)| { - Self::find_param_in_ty(*in_ty_generic, param) + find_param_in_ty(*in_ty_generic, param) }, ), ) else { @@ -785,14 +792,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // outer contextual information. // (1) Find the (unique) field index which mentions the type in our constraint: - let Some((field_index, field_type)) = Self::is_iterator_singleton( + let Some((field_index, field_type)) = is_iterator_singleton( in_ty_adt .variant_with_id(variant_def_id) .fields .iter() .map(|field| field.ty(self.tcx, *in_ty_adt_generic_args)) .enumerate() - .filter(|(_index, field_type)| Self::find_param_in_ty((*field_type).into(), param)) + .filter(|(_index, field_type)| find_param_in_ty((*field_type).into(), param)) ) else { return Err(expr); }; @@ -825,20 +832,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(expr) } +} - // FIXME: This can be made into a private, non-impl function later. - /// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references - /// to the given `param_to_point_at`. Returns `true` if it finds any use of the param. - pub fn find_param_in_ty( - ty: ty::GenericArg<'tcx>, - param_to_point_at: ty::GenericArg<'tcx>, - ) -> bool { - let mut walk = ty.walk(); - while let Some(arg) = walk.next() { - if arg == param_to_point_at { - return true; - } - if let ty::GenericArgKind::Type(ty) = arg.unpack() +/// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references +/// to the given `param_to_point_at`. Returns `true` if it finds any use of the param. +fn find_param_in_ty<'tcx>( + ty: ty::GenericArg<'tcx>, + param_to_point_at: ty::GenericArg<'tcx>, +) -> bool { + let mut walk = ty.walk(); + while let Some(arg) = walk.next() { + if arg == param_to_point_at { + return true; + } + if let ty::GenericArgKind::Type(ty) = arg.unpack() && let ty::Alias(ty::Projection, ..) = ty.kind() { // This logic may seem a bit strange, but typically when @@ -849,16 +856,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in some UI tests. walk.skip_current_subtree(); } - } - false } + false +} - // FIXME: This can be made into a private, non-impl function later. - /// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise. - pub fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> { - match (iterator.next(), iterator.next()) { - (_, Some(_)) => None, - (first, _) => first, - } +/// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise. +fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> { + match (iterator.next(), iterator.next()) { + (_, Some(_)) => None, + (first, _) => first, } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a46bdeb41..ea1b52daa 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -24,14 +24,14 @@ use rustc_hir_analysis::structured_errors::StructuredDiagnostic; use rustc_index::vec::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; +use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; +use rustc_middle::ty::{self, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, sym, Span}; +use rustc_span::{self, sym, BytePos, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; @@ -301,9 +301,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 3. Check if the formal type is a supertype of the checked one // and register any such obligations for future type checks - let supertype_error = self - .at(&self.misc(provided_arg.span), self.param_env) - .sup(formal_input_ty, coerced_ty); + let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup( + DefineOpaqueTypes::No, + formal_input_ty, + coerced_ty, + ); let subtyping_error = match supertype_error { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); @@ -470,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err_code: &str, fn_def_id: Option<DefId>, call_span: Span, - call_expr: &hir::Expr<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, ) { // Next, let's construct the error let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind { @@ -585,7 +587,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Using probe here, since we don't want this subtyping to affect inference. let subtyping_error = self.probe(|_| { - self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err() + self.at(&self.misc(arg_span), self.param_env) + .sup(DefineOpaqueTypes::No, formal_input_ty, coerced_ty) + .err() }); // Same as above: if either the coerce type or the checked type is an error type, @@ -764,7 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; } @@ -803,24 +807,20 @@ 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) + + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind + && provided_idx.as_usize() == expected_idx.as_usize() { - // 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( + self.note_source_of_type_mismatch_constraint( &mut err, rcvr, - expected, - callee_ty, - provided_arg_span, + crate::demand::TypeMismatchSource::Arg { + call_expr, + incompatible_arg: provided_idx.as_usize(), + }, ); } + // Call out where the function is defined self.label_fn_like( &mut err, @@ -890,8 +890,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut errors = errors.into_iter().peekable(); + let mut only_extras_so_far = errors + .peek() + .map_or(false, |first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); let mut suggestions = vec![]; while let Some(error) = errors.next() { + only_extras_so_far &= matches!(error, Error::Extra(_)); + match error { Error::Invalid(provided_idx, expected_idx, compatibility) => { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; @@ -937,10 +942,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if arg_idx.index() > 0 && let Some((_, prev)) = provided_arg_tys .get(ProvidedIdx::from_usize(arg_idx.index() - 1) - ) { - // Include previous comma - span = prev.shrink_to_hi().to(span); - } + ) { + // Include previous comma + span = prev.shrink_to_hi().to(span); + } + + // Is last argument for deletion in a row starting from the 0-th argument? + // Then delete the next comma, so we are not left with `f(, ...)` + // + // fn f() {} + // - f(0, 1,) + // + f() + if only_extras_so_far + && errors + .peek() + .map_or(true, |next_error| !matches!(next_error, Error::Extra(_))) + { + let next = provided_arg_tys + .get(arg_idx + 1) + .map(|&(_, sp)| sp) + .unwrap_or_else(|| { + // Subtract one to move before `)` + call_expr.span.with_lo(call_expr.span.hi() - BytePos(1)) + }); + + // Include next comma + span = span.until(next); + } + suggestions.push((span, String::new())); suggestion_text = match suggestion_text { @@ -1158,11 +1187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // index position where it *should have been*, which is *after* the previous one. if let Some(provided_idx) = provided_idx { prev = provided_idx.index() as i64; + continue; } let idx = ProvidedIdx::from_usize((prev + 1) as usize); - if let None = provided_idx - && let Some((_, arg_span)) = provided_arg_tys.get(idx) - { + if let Some((_, arg_span)) = provided_arg_tys.get(idx) { + prev += 1; // There is a type that was *not* found anywhere, so it isn't a move, but a // replacement and we look at what type it should have been. This will allow us // To suggest a multipart suggestion when encountering `foo(1, "")` where the def @@ -1380,7 +1409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(init.span, local_ty, init_ty); init_ty } else { - self.check_expr_coercable_to_type(init, local_ty, None) + self.check_expr_coercible_to_type(init, local_ty, None) } } @@ -1665,7 +1694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) + self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident)) } /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 1dea3e6f9..c6fd0b610 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -211,13 +211,13 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn get_type_parameter_bounds( &self, _: Span, - def_id: DefId, + def_id: LocalDefId, _: Ident, ) -> ty::GenericPredicates<'tcx> { let tcx = self.tcx; - let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); + let item_def_id = tcx.hir().ty_param_owner(def_id); let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; ty::GenericPredicates { parent: None, predicates: tcx.arena.alloc_from_iter( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c49621b7c..5fda4e191 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -16,7 +16,7 @@ use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty, + self, suggest_constraining_type_params, Binder, IsSuggestable, ToPredicate, Ty, TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; @@ -64,8 +64,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); self.suggest_missing_semicolon(err, expr, expected, false); let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); + if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { pointing_at_return_type = self.suggest_missing_return_type( err, &fn_decl, @@ -166,8 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { - let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id); - self.err_ctxt().extract_callable_info(body_hir_id, self.param_env, ty) + self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty) } pub fn suggest_two_fn_call( @@ -669,6 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This routine checks if the return type is left as default, the method is not part of an /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return /// type. + #[instrument(level = "trace", skip(self, err))] pub(in super::super) fn suggest_missing_return_type( &self, err: &mut Diagnostic, @@ -705,28 +704,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true } } - hir::FnRetTy::Return(ty) => { - let span = ty.span; - - if let hir::TyKind::OpaqueDef(item_id, ..) = ty.kind - && let hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(op_ty), - .. - }) = self.tcx.hir().get(item_id.hir_id()) - && let hir::OpaqueTy { - bounds: [bound], .. - } = op_ty - && let hir::GenericBound::LangItemTrait( - hir::LangItem::Future, _, _, generic_args) = bound - && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args - && let hir::TypeBinding { kind, .. } = ty_binding - && let hir::TypeBindingKind::Equality { term } = kind - && let hir::Term::Ty(term_ty) = term { + hir::FnRetTy::Return(hir_ty) => { + let span = hir_ty.span; + + if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind + && let hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(op_ty), + .. + }) = self.tcx.hir().get(item_id.hir_id()) + && let [hir::GenericBound::LangItemTrait( + hir::LangItem::Future, _, _, generic_args)] = op_ty.bounds + && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args + && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(term) } = ty_binding.kind + { // Check if async function's return type was omitted. // Don't emit suggestions if the found type is `impl Future<...>`. - debug!("suggest_missing_return_type: found = {:?}", found); + debug!(?found); if found.is_suggestable(self.tcx, false) { - if term_ty.span.is_empty() { + if term.span.is_empty() { err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); return true; } else { @@ -737,11 +732,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. - debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let ty = self.astconv().ast_ty_to_ty(ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); + debug!("return type {:?}", hir_ty); + let ty = self.astconv().ast_ty_to_ty(hir_ty); + debug!("return type {:?}", ty); + debug!("expected type {:?}", expected); + let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); let ty = Binder::bind_with_vars(ty, bound_vars); let ty = self.normalize(span, ty); let ty = self.tcx.erase_late_bound_regions(ty); @@ -988,13 +983,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .must_apply_modulo_regions() { - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), - "consider using clone here", - ".clone()", - Applicability::MachineApplicable, - ); - return true; + let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!(": {}.clone()", ident), + None => ".clone()".to_string() + }; + + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + "consider using clone here", + suggestion, + Applicability::MachineApplicable, + ); + return true; } false } @@ -1015,11 +1015,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut suggest_copied_or_cloned = || { let expr_inner_ty = substs.type_at(0); let expected_inner_ty = expected_substs.type_at(0); - if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() - && self.can_eq(self.param_env, *ty, expected_inner_ty) + if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() + && self.can_eq(self.param_env, ty, expected_inner_ty) { let def_path = self.tcx.def_path_str(adt_def.did()); - if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) { + if self.type_is_copy_modulo_regions(self.param_env, ty) { diag.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( @@ -1033,9 +1033,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( self, self.param_env, - *ty, + ty, clone_did, - expr.span ) { diag.span_suggestion_verbose( @@ -1156,13 +1155,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - diag.span_suggestion( + let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!(": {}.is_some()", ident), + None => ".is_some()".to_string(), + }; + + diag.span_suggestion_verbose( expr.span.shrink_to_hi(), "use `Option::is_some` to test if the `Option` has a value", - ".is_some()", + suggestion, Applicability::MachineApplicable, ); - true } diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index 7c0402b1c..3e9a9ce1b 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -190,7 +190,6 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { // // Some of these may be interesting in the future ExprKind::Path(..) - | ExprKind::Box(..) | ExprKind::ConstBlock(..) | ExprKind::Array(..) | ExprKind::Call(..) @@ -478,7 +477,6 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { | ExprKind::AssignOp(..) | ExprKind::Binary(..) | ExprKind::Block(..) - | ExprKind::Box(..) | ExprKind::Cast(..) | ExprKind::Closure { .. } | ExprKind::ConstBlock(..) @@ -528,8 +526,9 @@ impl DropRangesBuilder { let mut next = <_>::from(0u32); for value in tracked_values { for_each_consumable(hir, value, |value| { - if !tracked_value_map.contains_key(&value) { - tracked_value_map.insert(value, next); + if let std::collections::hash_map::Entry::Vacant(e) = tracked_value_map.entry(value) + { + e.insert(next); next = next + 1; } }); diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index 2e41c2041..f39710804 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -13,7 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; -use rustc_infer::infer::RegionVariableOrigin; +use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin}; use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitableExt}; @@ -239,8 +239,7 @@ pub fn resolve_interior<'a, 'tcx>( // typeck had previously found constraints that would cause them to be related. let mut counter = 0; - let mut mk_bound_region = |span| { - let kind = ty::BrAnon(counter, span); + let mut mk_bound_region = |kind| { let var = ty::BoundVar::from_u32(counter); counter += 1; ty::BoundRegion { var, kind } @@ -252,24 +251,23 @@ pub fn resolve_interior<'a, 'tcx>( let origin = fcx.region_var_origin(vid); match origin { RegionVariableOrigin::EarlyBoundRegion(span, _) => { - mk_bound_region(Some(span)) + mk_bound_region(ty::BrAnon(Some(span))) } - _ => mk_bound_region(None), + _ => mk_bound_region(ty::BrAnon(None)), } } - // FIXME: these should use `BrNamed` ty::ReEarlyBound(region) => { - mk_bound_region(Some(fcx.tcx.def_span(region.def_id))) + mk_bound_region(ty::BrNamed(region.def_id, region.name)) } ty::ReLateBound(_, ty::BoundRegion { kind, .. }) | ty::ReFree(ty::FreeRegion { bound_region: kind, .. }) => match kind { - ty::BoundRegionKind::BrAnon(_, span) => mk_bound_region(span), - ty::BoundRegionKind::BrNamed(def_id, _) => { - mk_bound_region(Some(fcx.tcx.def_span(def_id))) + ty::BoundRegionKind::BrAnon(span) => mk_bound_region(ty::BrAnon(span)), + ty::BoundRegionKind::BrNamed(def_id, sym) => { + mk_bound_region(ty::BrNamed(def_id, sym)) } - ty::BoundRegionKind::BrEnv => mk_bound_region(None), + ty::BoundRegionKind::BrEnv => mk_bound_region(ty::BrAnon(None)), }, - _ => mk_bound_region(None), + _ => mk_bound_region(ty::BrAnon(None)), }; let r = fcx.tcx.mk_re_late_bound(current_depth, br); r @@ -293,10 +291,7 @@ pub fn resolve_interior<'a, 'tcx>( type_causes, FnMutDelegate { regions: &mut |br| { - let kind = match br.kind { - ty::BrAnon(_, span) => ty::BrAnon(counter, span), - _ => br.kind, - }; + let kind = br.kind; let var = ty::BoundVar::from_usize(bound_vars.len()); bound_vars.push(ty::BoundVariableKind::Region(kind)); counter += 1; @@ -313,8 +308,7 @@ pub fn resolve_interior<'a, 'tcx>( // Extract type components to build the witness type. let type_list = fcx.tcx.mk_type_list_from_iter(type_causes.iter().map(|cause| cause.ty)); let bound_vars = fcx.tcx.mk_bound_variable_kinds(&bound_vars); - let witness = - fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); + let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars)); drop(typeck_results); // Store the generator types and spans into the typeck results for this generator. @@ -327,7 +321,11 @@ pub fn resolve_interior<'a, 'tcx>( ); // Unify the type variable inside the generator with the new witness - match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { + match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq( + DefineOpaqueTypes::No, + interior, + witness, + ) { Ok(ok) => fcx.register_infer_ok_obligations(ok), _ => bug!("failed to relate {interior} and {witness}"), } diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 26020382d..4110b176b 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -4,7 +4,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; -use rustc_infer::infer; use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -58,8 +57,6 @@ pub struct Inherited<'tcx> { pub(super) deferred_generator_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, - pub(super) body_id: Option<hir::BodyId>, - /// Whenever we introduce an adjustment from `!` into a type variable, /// we record that type variable here. This is later used to inform /// fallback. See the `fallback` module for details. @@ -75,48 +72,16 @@ impl<'tcx> Deref for Inherited<'tcx> { } } -/// A temporary returned by `Inherited::build(...)`. This is necessary -/// for multiple `InferCtxt` to share the same `typeck_results` -/// without using `Rc` or something similar. -pub struct InheritedBuilder<'tcx> { - infcx: infer::InferCtxtBuilder<'tcx>, - def_id: LocalDefId, - typeck_results: RefCell<ty::TypeckResults<'tcx>>, -} - impl<'tcx> Inherited<'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; - InheritedBuilder { - infcx: tcx - .infer_ctxt() - .ignoring_regions() - .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)), - def_id, - typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), - } - } -} - -impl<'tcx> InheritedBuilder<'tcx> { - pub fn enter<F, R>(mut self, f: F) -> R - where - F: FnOnce(&Inherited<'tcx>) -> R, - { - let def_id = self.def_id; - f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results)) - } -} - -impl<'tcx> Inherited<'tcx> { - fn new( - infcx: InferCtxt<'tcx>, - def_id: LocalDefId, - typeck_results: RefCell<ty::TypeckResults<'tcx>>, - ) -> Self { - let tcx = infcx.tcx; - let body_id = tcx.hir().maybe_body_owned_by(def_id); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)) + .build(); + let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); Inherited { typeck_results, @@ -130,7 +95,6 @@ impl<'tcx> Inherited<'tcx> { deferred_asm_checks: RefCell::new(Vec::new()), deferred_generator_interiors: RefCell::new(Vec::new()), diverging_type_vars: RefCell::new(Default::default()), - body_id, infer_var_info: RefCell::new(Default::default()), } } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 19d2befc4..106f5bcd7 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -4,7 +4,7 @@ use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_target::abi::{Pointer, VariantIdx}; +use rustc_target::abi::{FieldIdx, Pointer, VariantIdx}; use super::FnCtxt; @@ -28,7 +28,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { } if def.variant(data_idx).fields.len() == 1 { - return def.variant(data_idx).fields[0].ty(tcx, substs); + return def.variant(data_idx).fields[FieldIdx::from_u32(0)].ty(tcx, substs); } } @@ -84,6 +84,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let skeleton_string = |ty: Ty<'tcx>, sk| match sk { Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), + Ok(SizeSkeleton::Generic(size)) => { + if let Some(size) = size.try_eval_target_usize(tcx, self.param_env) { + format!("{size} bytes") + } else { + format!("generic size {size}") + } + } Err(LayoutError::Unknown(bad)) => { if bad == ty { "this type does not have a fixed size".to_owned() diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index e397dfd45..45890abad 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -45,13 +45,14 @@ mod rvalue_scopes; mod upvar; mod writeback; -pub use diverges::Diverges; -pub use expectation::Expectation; -pub use fn_ctxt::*; -pub use inherited::{Inherited, InheritedBuilder}; +pub use fn_ctxt::FnCtxt; +pub use inherited::Inherited; use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; +use crate::diverges::Diverges; +use crate::expectation::Expectation; +use crate::fn_ctxt::RawTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ @@ -74,7 +75,7 @@ use rustc_session::Session; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } #[macro_export] macro_rules! type_error_struct { @@ -105,10 +106,9 @@ pub struct LocalTy<'tcx> { /// (notably closures), `typeck_results(def_id)` would wind up /// redirecting to the owning function. fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, + node: Node<'_>, ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match tcx.hir().get(id) { + match node { Node::Item(item) => match item.kind { hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { Some((body, Some(ty), None)) @@ -142,8 +142,7 @@ fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() + primary_body_of(tcx.hir().get_by_def_id(def_id)).is_some() } else { false } @@ -198,143 +197,140 @@ fn typeck_with_fallback<'tcx>( } let id = tcx.hir().local_def_id_to_hir_id(def_id); + let node = tcx.hir().get(id); let span = tcx.hir().span(id); // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { + let (body_id, body_ty, fn_sig) = primary_body_of(node).unwrap_or_else(|| { span_bug!(span, "can't type-check body of {:?}", def_id); }); let body = tcx.hir().body(body_id); - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { - param_env.without_const() + let param_env = tcx.param_env(def_id); + let param_env = if tcx.has_attr(def_id, sym::rustc_do_not_const_check) { + param_env.without_const() + } else { + param_env + }; + let inh = Inherited::new(tcx, def_id); + let mut fcx = FnCtxt::new(&inh, param_env, def_id); + + if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { + fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) } else { - param_env + tcx.fn_sig(def_id).subst_identity() }; - let mut fcx = FnCtxt::new(&inh, param_env, def_id); - - if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { - fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) - } else { - tcx.fn_sig(def_id).subst_identity() - }; - check_abi(tcx, id, span, fn_sig.abi()); + check_abi(tcx, id, span, fn_sig.abi()); - // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = fcx.normalize(body.value.span, fn_sig); + // Compute the function signature from point of view of inside the fn. + let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); + let fn_sig = fcx.normalize(body.value.span, fn_sig); - check_fn(&mut fcx, fn_sig, decl, def_id, body, None); - } else { - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)), - _ => None, - }) - .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), - .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Ty(&hir::Ty { - kind: hir::TyKind::Typeof(ref anon_const), .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - let operand_ty = - asm.operands.iter().find_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - if anon_const.hir_id == id => - { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }); - operand_ty.unwrap_or_else(fallback) + check_fn(&mut fcx, fn_sig, decl, def_id, body, None); + } else { + let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + })) + } else if let Node::AnonConst(_) = node { + match tcx.hir().get(tcx.hir().parent_id(id)) { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::ConstBlock(ref anon_const), .. + }) if anon_const.hir_id == id => Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + })), + Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. }) + if anon_const.hir_id == id => + { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + })) + } + Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) + | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { + asm.operands.iter().find_map(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => { + // Inline assembly constants must be integers. + Some(fcx.next_int_var()) + } + hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + })) } - _ => fallback(), - }, - _ => fallback(), - }); + _ => None, + }) + } + _ => None, + } + } else { + None + }; + let expected_type = expected_type.unwrap_or_else(fallback); - let expected_type = fcx.normalize(body.value.span, expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); + let expected_type = fcx.normalize(body.value.span, expected_type); + fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); + // Gather locals in statics (because of block expressions). + GatherLocalsVisitor::new(&fcx).visit_body(body); - fcx.check_expr_coercable_to_type(&body.value, expected_type, None); + fcx.check_expr_coercible_to_type(&body.value, expected_type, None); - fcx.write_ty(id, expected_type); - }; + fcx.write_ty(id, expected_type); + }; - fcx.type_inference_fallback(); - - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - fcx.select_obligations_where_possible(|_| {}); - - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) - let prev_constness = fcx.param_env.constness(); - fcx.param_env = fcx.param_env.without_const(); - fcx.closure_analyze(body); - fcx.param_env = fcx.param_env.with_constness(prev_constness); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the generator analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); - - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize(span, ty); - fcx.require_type_is_sized(ty, span, code); - } + fcx.type_inference_fallback(); + + // Even though coercion casts provide type hints, we check casts after fallback for + // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. + fcx.check_casts(); + fcx.select_obligations_where_possible(|_| {}); + + // Closure and generator analysis may run after fallback + // because they don't constrain other type variables. + // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) + let prev_constness = fcx.param_env.constness(); + fcx.param_env = fcx.param_env.without_const(); + fcx.closure_analyze(body); + fcx.param_env = fcx.param_env.with_constness(prev_constness); + assert!(fcx.deferred_call_resolutions.borrow().is_empty()); + // Before the generator analysis, temporary scopes shall be marked to provide more + // precise information on types to be captured. + fcx.resolve_rvalue_scopes(def_id.to_def_id()); + + for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { + let ty = fcx.normalize(span, ty); + fcx.require_type_is_sized(ty, span, code); + } - fcx.select_obligations_where_possible(|_| {}); + fcx.select_obligations_where_possible(|_| {}); - debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); + debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); - // This must be the last thing before `report_ambiguity_errors`. - fcx.resolve_generator_interiors(def_id.to_def_id()); + // This must be the last thing before `report_ambiguity_errors`. + fcx.resolve_generator_interiors(def_id.to_def_id()); - debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); + debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); - if let None = fcx.infcx.tainted_by_errors() { - fcx.report_ambiguity_errors(); - } + if let None = fcx.infcx.tainted_by_errors() { + fcx.report_ambiguity_errors(); + } - if let None = fcx.infcx.tainted_by_errors() { - fcx.check_transmutes(); - } + if let None = fcx.infcx.tainted_by_errors() { + fcx.check_transmutes(); + } - fcx.check_asms(); + fcx.check_asms(); - fcx.infcx.skip_region_resolution(); + fcx.infcx.skip_region_resolution(); - fcx.resolve_type_vars_in_body(body) - }); + let typeck_results = fcx.resolve_type_vars_in_body(body); // Consistency check our TypeckResults instance can hold all ItemLocalIds // it will need to hold. diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 4d3969d28..6c861b593 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -59,10 +59,9 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::PatKind; -use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_span::Span; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_trait_selection::infer::InferCtxtExt; pub(crate) trait HirNode { @@ -120,8 +119,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.infcx.tcx } - pub(crate) fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) + pub(crate) fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { + self.infcx.type_is_copy_modulo_regions(self.param_env, ty) } fn resolve_vars_if_possible<T>(&self, value: T) -> T @@ -331,7 +330,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { expr, base, expr_ty, - ProjectionKind::Field(field_idx as u32, VariantIdx::new(0)), + ProjectionKind::Field(field_idx, FIRST_VARIANT), )) } @@ -382,7 +381,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::Box(..) | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), } } @@ -562,7 +560,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => { // Structs and Unions have only have one variant. - Ok(VariantIdx::new(0)) + Ok(FIRST_VARIANT) } _ => bug!("expected ADT path, found={:?}", res), } @@ -676,7 +674,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { let subpat_ty = self.pat_ty_adjusted(subpat)?; - let projection_kind = ProjectionKind::Field(i as u32, VariantIdx::new(0)); + let projection_kind = + ProjectionKind::Field(FieldIdx::from_usize(i), FIRST_VARIANT); let sub_place = self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); self.cat_pattern_(sub_place, subpat, op)?; @@ -691,7 +690,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { let subpat_ty = self.pat_ty_adjusted(subpat)?; - let projection_kind = ProjectionKind::Field(i as u32, variant_index); + let projection_kind = + ProjectionKind::Field(FieldIdx::from_usize(i), variant_index); let sub_place = self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); self.cat_pattern_(sub_place, subpat, op)?; @@ -716,7 +716,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { pat, place_with_id.clone(), field_ty, - ProjectionKind::Field(field_index as u32, variant_index), + ProjectionKind::Field(field_index, variant_index), ); self.cat_pattern_(field_place, fp.pat, op)?; } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 169f128e0..9155a3d8d 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -8,7 +8,7 @@ use rustc_hir_analysis::astconv::generics::{ check_generic_arg_count_for_call, create_substs_for_generic_args, }; use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use rustc_infer::infer::{self, InferOk}; +use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; @@ -478,7 +478,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { substs, })), ); - match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) { + match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::No, method_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); } @@ -574,19 +574,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Option<Span> { let sized_def_id = self.tcx.lang_items().sized_trait()?; - traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) + traits::elaborate(self.tcx, predicates.predicates.iter().copied()) // We don't care about regions here. - .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { + .filter_map(|pred| match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) if trait_pred.def_id() == sized_def_id => { let span = predicates .iter() - .find_map( - |(p, span)| { - if p == obligation.predicate { Some(span) } else { None } - }, - ) + .find_map(|(p, span)| if p == pred { Some(span) } else { None }) .unwrap_or(rustc_span::DUMMY_SP); Some((trait_pred, span)) } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3bef5cfcd..4fd778910 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -13,6 +13,7 @@ use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; @@ -699,7 +700,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { - let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else { + let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp) { @@ -792,6 +793,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // a `&self` method will wind up with an argument type like `&dyn Trait`. let trait_ref = principal.with_self_ty(self.tcx, self_ty); self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| { + if new_trait_ref.has_non_region_late_bound() { + this.tcx.sess.delay_span_bug( + this.span, + "tried to select method from HRTB with non-lifetime bound vars", + ); + return; + } + let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); let (xform_self_ty, xform_ret_ty) = @@ -836,24 +845,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { - let trait_ref = this.erase_late_bound_regions(poly_trait_ref); + let trait_ref = this.instantiate_binder_with_fresh_vars( + this.span, + infer::LateBoundRegionConversionTime::FnCall, + poly_trait_ref, + ); let (xform_self_ty, xform_ret_ty) = this.xform_self_ty(item, trait_ref.self_ty(), trait_ref.substs); - // Because this trait derives from a where-clause, it - // should not contain any inference variables or other - // artifacts. This means it is safe to put into the - // `WhereClauseCandidate` and (eventually) into the - // `WhereClausePick`. - assert!(!trait_ref.substs.needs_infer()); - this.push_candidate( Candidate { xform_self_ty, @@ -929,7 +935,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some(self_ty) = self_ty { if self .at(&ObligationCause::dummy(), self.param_env) - .sup(fty.inputs()[0], self_ty) + .sup(DefineOpaqueTypes::No, fty.inputs()[0], self_ty) .is_err() { return false; @@ -963,7 +969,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { bound_trait_ref.def_id(), )); } else { - let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref); + let new_trait_ref = self.instantiate_binder_with_fresh_vars( + self.span, + infer::LateBoundRegionConversionTime::FnCall, + bound_trait_ref, + ); let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs); @@ -1026,12 +1036,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { true } }) + // ensure that we don't suggest unstable methods + .filter(|candidate| { + // note that `DUMMY_SP` is ok here because it is only used for + // suggestions and macro stuff which isn't applicable here. + !matches!( + self.tcx.eval_stability(candidate.item.def_id, None, DUMMY_SP, None), + stability::EvalResult::Deny { .. } + ) + }) .map(|candidate| candidate.item.ident(self.tcx)) .filter(|&name| set.insert(name)) .collect(); // Sort them by the name so we have a stable result. - names.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap()); + names.sort_by(|a, b| a.as_str().cmp(b.as_str())); names } @@ -1338,6 +1357,7 @@ impl<'tcx> Pick<'tcx> { container: _, trait_item_def_id: _, fn_has_self_parameter: _, + opt_rpitit_info: _, }, kind: _, import_ids: _, @@ -1434,9 +1454,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { CandidateSource::Trait(candidate.item.container_id(self.tcx)) } TraitCandidate(trait_ref) => self.probe(|_| { - let _ = self - .at(&ObligationCause::dummy(), self.param_env) - .sup(candidate.xform_self_ty, self_ty); + let _ = self.at(&ObligationCause::dummy(), self.param_env).sup( + DefineOpaqueTypes::No, + candidate.xform_self_ty, + self_ty, + ); match self.select_trait_candidate(trait_ref) { Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => { // If only a single impl matches, make the error message point @@ -1463,10 +1485,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.probe(|_| { // First check that the self type can be related. - let sub_obligations = match self - .at(&ObligationCause::dummy(), self.param_env) - .sup(probe.xform_self_ty, self_ty) - { + let sub_obligations = match self.at(&ObligationCause::dummy(), self.param_env).sup( + DefineOpaqueTypes::No, + probe.xform_self_ty, + self_ty, + ) { Ok(InferOk { obligations, value: () }) => obligations, Err(err) => { debug!("--> cannot relate self-types {:?}", err); @@ -1508,23 +1531,18 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Convert the bounds into obligations. let impl_obligations = traits::predicates_for_generics( - |_idx, span| { - let misc = traits::ObligationCause::misc(span, self.body_id); - let parent_trait_pred = ty::Binder::dummy(ty::TraitPredicate { - trait_ref: ty::TraitRef::from_method(self.tcx, impl_def_id, substs), - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }); - misc.derived_cause(parent_trait_pred, |derived| { - traits::ImplDerivedObligation(Box::new( - traits::ImplDerivedObligationCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: None, - span, - }, - )) - }) + |idx, span| { + let code = if span.is_dummy() { + traits::ExprItemObligation(impl_def_id, self.scope_expr_id, idx) + } else { + traits::ExprBindingObligation( + impl_def_id, + span, + self.scope_expr_id, + idx, + ) + }; + ObligationCause::new(self.span, self.body_id, code) }, self.param_env, impl_bounds, @@ -1541,8 +1559,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if !self.predicate_may_hold(&o) { result = ProbeResult::NoMatch; let parent_o = o.clone(); - let implied_obligations = - traits::elaborate_obligations(self.tcx, vec![o]); + let implied_obligations = traits::elaborate(self.tcx, vec![o]); for o in implied_obligations { let parent = if o == parent_o { None @@ -1681,7 +1698,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let ProbeResult::Match = result && self .at(&ObligationCause::dummy(), self.param_env) - .sup(return_ty, xform_ret_ty) + .sup(DefineOpaqueTypes::No, return_ty, xform_ret_ty) .is_err() { result = ProbeResult::BadReturnType; @@ -1742,7 +1759,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> { debug!("probing for method names similar to {:?}", self.method_name); - let steps = self.steps.clone(); self.probe(|_| { let mut pcx = ProbeContext::new( self.fcx, @@ -1750,8 +1766,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.mode, self.method_name, self.return_type, - &self.orig_steps_var_values, - steps, + self.orig_steps_var_values, + self.steps, self.scope_expr_id, ); pcx.allow_similar_names = true; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 60d56263d..900a6fa0d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -27,7 +27,7 @@ use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; -use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; @@ -42,7 +42,7 @@ use rustc_trait_selection::traits::{ use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; use rustc_hir::intravisit::Visitor; -use std::cmp::Ordering; +use std::cmp::{self, Ordering}; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -245,6 +245,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + fn suggest_missing_writer( + &self, + rcvr_ty: Ty<'tcx>, + args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]), + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); + let mut err = + struct_span_err!(self.tcx.sess, args.0.span, E0599, "cannot write into `{}`", ty_str); + err.span_note( + args.0.span, + "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", + ); + if let ExprKind::Lit(_) = args.0.kind { + err.span_help( + args.0.span.shrink_to_lo(), + "a writer is needed before this format string", + ); + }; + + err + } + pub fn report_no_match_method_error( &self, mut span: Span, @@ -278,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // We could pass the file for long types into these two, but it isn't strictly necessary - // given how targetted they are. + // given how targeted they are. if self.suggest_wrapping_range_with_parens( tcx, rcvr_ty, @@ -323,16 +345,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - rcvr_ty.prefix_string(self.tcx), - ty_str_reported, - ); + let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.map_or(false, |def_id| { + tcx.is_diagnostic_item(sym::write_macro, def_id) + || tcx.is_diagnostic_item(sym::writeln_macro, def_id) + }) && item_name.name == Symbol::intern("write_fmt"); + let mut err = if is_write + && let Some(args) = args + { + self.suggest_missing_writer(rcvr_ty, args) + } else { + struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + rcvr_ty.prefix_string(self.tcx), + ty_str_reported, + ) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } @@ -348,6 +380,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.downgrade_to_delayed_bug(); } + if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { + err.help(&format!( + "method `poll` found on `Pin<&mut {ty_str}>`, \ + see documentation for `std::pin::Pin`" + )); + err.help("self type must be pinned to call `Future::poll`, \ + see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice" + ); + } + if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self), @@ -405,6 +447,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); probe.is_ok() }); + + self.note_internal_mutation_in_method( + &mut err, + rcvr_expr, + expected.to_option(&self), + rcvr_ty, + ); } let mut custom_span_label = false; @@ -612,19 +661,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find all the requirements that come from a local `impl` block. let mut skip_list: FxHashSet<_> = Default::default(); let mut spanned_predicates = FxHashMap::default(); - for (p, parent_p, impl_def_id, cause) in unsatisfied_predicates - .iter() - .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) - .filter_map(|(p, parent, c)| match c.code() { - ObligationCauseCode::ImplDerivedObligation(data) - if matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) => - { - Some((p, parent, data.impl_or_alias_def_id, data)) + for (p, parent_p, cause) in unsatisfied_predicates { + // Extract the predicate span and parent def id of the cause, + // if we have one. + let (item_def_id, cause_span) = match cause.as_ref().map(|cause| cause.code()) { + Some(ObligationCauseCode::ImplDerivedObligation(data)) => { + (data.impl_or_alias_def_id, data.span) } - _ => None, - }) - { - match self.tcx.hir().get_if_local(impl_def_id) { + Some( + ObligationCauseCode::ExprBindingObligation(def_id, span, _, _) + | ObligationCauseCode::BindingObligation(def_id, span), + ) => (*def_id, *span), + _ => continue, + }; + + // Don't point out the span of `WellFormed` predicates. + if !matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) { + continue; + }; + + match self.tcx.hir().get_if_local(item_def_id) { // Unmet obligation comes from a `derive` macro, point at it once to // avoid multiple span labels pointing at the same place. Some(Node::Item(hir::Item { @@ -669,7 +725,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); for param in generics.params { - if param.span == cause.span && sized_pred { + if param.span == cause_span && sized_pred { let (sp, sugg) = match param.colon_span { Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), None => (param.span.shrink_to_hi(), ": ?Sized"), @@ -692,9 +748,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (FxHashSet::default(), FxHashSet::default(), Vec::new()) }); entry.2.push(p); - if cause.span != *item_span { - entry.0.insert(cause.span); - entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); + if cause_span != *item_span { + entry.0.insert(cause_span); + entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); } else { if let Some(trait_ref) = of_trait { entry.0.insert(trait_ref.path.span); @@ -726,9 +782,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let entry = entry.or_insert_with(|| { (FxHashSet::default(), FxHashSet::default(), Vec::new()) }); - entry.0.insert(cause.span); + entry.0.insert(cause_span); entry.1.insert((ident.span, "")); - entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); + entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); entry.2.push(p); } Some(node) => unreachable!("encountered `{node:?}`"), @@ -1164,7 +1220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .inputs() .skip_binder() .get(0) - .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) + .filter(|ty| ty.is_ref() && !rcvr_ty.is_ref()) .copied() .unwrap_or(rcvr_ty), }; @@ -1247,7 +1303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self .autoderef(sugg_span, rcvr_ty) .find(|(rcvr_ty, _)| { - DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer } + DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey } .types_may_unify(*rcvr_ty, impl_ty) }) .map_or(impl_ty, |(ty, _)| ty) @@ -1506,7 +1562,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .any(|info| self.associated_value(info.def_id, item_name).is_some()); let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsInfer) + simplify_type(tcx, ty, TreatParams::AsCandidateKey) .and_then(|simp| { tcx.incoherent_impls(simp) .iter() @@ -1766,7 +1822,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .variants() .iter() .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; + let [field] = &variant.fields.raw[..] else { return None; }; let field_ty = field.ty(tcx, substs); // Skip `_`, since that'll just lead to ambiguity. @@ -2517,7 +2573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !candidates.is_empty() { // Sort from most relevant to least relevant. - candidates.sort_by(|a, b| a.cmp(b).reverse()); + candidates.sort_by_key(|&info| cmp::Reverse(info)); candidates.dedup(); let param_type = match rcvr_ty.kind() { @@ -2636,7 +2692,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // cases where a positive bound implies a negative impl. (candidates, Vec::new()) } else if let Some(simp_rcvr_ty) = - simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder) + simplify_type(self.tcx, rcvr_ty, TreatParams::ForLookup) { let mut potential_candidates = Vec::new(); let mut explicitly_negative = Vec::new(); @@ -2651,7 +2707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|imp_did| { let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); let imp_simp = - simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); + simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup); imp_simp.map_or(false, |s| s == simp_rcvr_ty) }) { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 80279ed96..a52c94cb0 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -12,16 +12,14 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{ - self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, -}; +use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{self, FulfillmentError}; +use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt}; use rustc_type_ir::sty::TyKind::*; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -103,9 +101,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { // && and || are a simple case. - self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); + self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None); let lhs_diverges = self.diverges.get(); - self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); + self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None); // Depending on the LHS' value, the RHS can never execute. self.diverges.set(lhs_diverges); @@ -148,10 +146,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, op, ); - self.demand_suptype(expr.span, builtin_return_ty, return_ty); + self.demand_eqtype(expr.span, builtin_return_ty, return_ty); + builtin_return_ty + } else { + return_ty } - - return_ty } } } @@ -254,7 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // see `NB` above - let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); + let rhs_ty = self.check_expr_coercible_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { @@ -433,7 +432,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.type_is_copy_modulo_regions( self.param_env, *lhs_deref_ty, - lhs_expr.span, ) { suggest_deref_binop(*lhs_deref_ty); } @@ -775,7 +773,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, Some(trait_did)) => { let (obligation, _) = self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types)); - Err(rustc_trait_selection::traits::fully_solve_obligation(self, obligation)) + // FIXME: This should potentially just add the obligation to the `FnCtxt` + let ocx = ObligationCtxt::new(&self.infcx); + ocx.register_obligation(obligation); + Err(ocx.select_all_or_error()) } } } @@ -962,21 +963,3 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool } } } - -struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span); - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TypeParamEraser<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.0.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { - ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.1, - }), - _ => ty.super_fold_with(self), - } - } -} diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c36c75e44..af0bd26de 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -19,6 +19,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, DUMMY_SP}; +use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; @@ -36,6 +37,10 @@ pointers. If you encounter this error you should try to avoid dereferencing the You can read more about trait objects in the Trait Objects section of the Reference: \ https://doc.rust-lang.org/reference/types.html#trait-objects"; +fn is_number(text: &str) -> bool { + text.chars().all(|c: char| c.is_digit(10)) +} + /// Information about the expected type at the top level of type checking a pattern. /// /// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic! @@ -238,15 +243,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Note that there are two tests to check that this remains true // (`regions-reassign-{match,let}-bound-pointer.rs`). // - // 2. Things go horribly wrong if we use subtype. The reason for - // THIS is a fairly subtle case involving bound regions. See the - // `givens` field in `region_constraints`, as well as the test + // 2. An outdated issue related to the old HIR borrowck. See the test // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, - // for details. Short version is that we must sometimes detect - // relationships between specific region variables and regions - // bound in a closure signature, and that detection gets thrown - // off when we substitute fresh region variables here to enable - // subtyping. } /// Compute the new expected type and default binding mode from the old ones @@ -1098,11 +1096,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!("unexpected pattern type {:?}", pat_ty); }; for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { - let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); + let field = &variant.fields[FieldIdx::from_usize(i)]; + let field_ty = self.field_ty(subpat.span, field, substs); self.check_pat(subpat, field_ty, def_bm, ti); self.tcx.check_stability( - variant.fields[i].did, + variant.fields[FieldIdx::from_usize(i)].did, Some(pat.hir_id), subpat.span, None, @@ -1110,7 +1109,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else { // Pattern has wrong number of fields. - let e = self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); + let e = + self.e0023(pat.span, res, qpath, subpats, &variant.fields.raw, expected, had_err); on_error(e); return tcx.ty_error(e); } @@ -1340,8 +1340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Index the struct fields' types. let field_map = variant .fields - .iter() - .enumerate() + .iter_enumerated() .map(|(i, field)| (field.ident(self.tcx).normalize_to_macros_2_0(), (i, field))) .collect::<FxHashMap<_, _>>(); @@ -1678,7 +1677,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], variant: &ty::VariantDef, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - if let (Some(CtorKind::Fn), PatKind::Struct(qpath, ..)) = (variant.ctor_kind(), &pat.kind) { + if let (Some(CtorKind::Fn), PatKind::Struct(qpath, pattern_fields, ..)) = + (variant.ctor_kind(), &pat.kind) + { + let is_tuple_struct_match = !pattern_fields.is_empty() + && pattern_fields.iter().map(|field| field.ident.name.as_str()).all(is_number); + if is_tuple_struct_match { + return None; + } + let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { s.print_qpath(qpath, false) }); @@ -1900,7 +1907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prefix, unmentioned_fields .iter() - .map(|(_, name)| name.to_string()) + .map(|(_, name)| { + let field_name = name.to_string(); + if is_number(&field_name) { + format!("{}: _", field_name) + } else { + field_name + } + }) .collect::<Vec<_>>() .join(", "), if have_inaccessible_fields { ", .." } else { "" }, diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 4a432328c..41a6ad80b 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -49,8 +49,7 @@ use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_index::vec::Idx; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::FIRST_VARIANT; use std::iter; @@ -424,7 +423,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // closures. We want to make sure any adjustment that might make us move the place into // the closure gets handled. let (place, capture_kind) = - restrict_precision_for_drop_types(self, place, capture_kind, usage_span); + restrict_precision_for_drop_types(self, place, capture_kind); capture_info.capture_kind = capture_kind; (place, capture_info) @@ -712,10 +711,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - unreachable!( - "we captured two identical projections: capture1 = {:?}, capture2 = {:?}", - capture1, capture2 + self.tcx.sess.delay_span_bug( + closure_span, + &format!( + "two identical projections: ({:?}, {:?})", + capture1.place.projections, capture2.place.projections + ), ); + std::cmp::Ordering::Equal }); } @@ -1402,7 +1405,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProjectionKind::Field(..) )) ); - def.variants().get(VariantIdx::new(0)).unwrap().fields.iter().enumerate().any( + def.variants().get(FIRST_VARIANT).unwrap().fields.iter_enumerated().any( |(i, field)| { let paths_using_field = captured_by_move_projs .iter() @@ -1410,7 +1413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ProjectionKind::Field(field_idx, _) = projs.first().unwrap().kind { - if (field_idx as usize) == i { Some(&projs[1..]) } else { None } + if field_idx == i { Some(&projs[1..]) } else { None } } else { unreachable!(); } @@ -1443,7 +1446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter_map(|projs| { if let ProjectionKind::Field(field_idx, _) = projs.first().unwrap().kind { - if (field_idx as usize) == i { Some(&projs[1..]) } else { None } + if field_idx.index() == i { Some(&projs[1..]) } else { None } } else { unreachable!(); } @@ -1498,7 +1501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool { - self.tcx.has_attr(closure_def_id.to_def_id(), sym::rustc_capture_analysis) + self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } fn log_capture_analysis_first_pass( @@ -1822,9 +1825,8 @@ fn restrict_precision_for_drop_types<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, mut place: Place<'tcx>, mut curr_mode: ty::UpvarCapture, - span: Span, ) -> (Place<'tcx>, ty::UpvarCapture) { - let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty(), span); + let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty()); if let (false, UpvarCapture::ByValue) = (is_copy_type, curr_mode) { for i in 0..place.projections.len() { @@ -1891,14 +1893,13 @@ fn restrict_capture_precision( for (i, proj) in place.projections.iter().enumerate() { match proj.kind { - ProjectionKind::Index => { - // Arrays are completely captured, so we drop Index projections + ProjectionKind::Index | ProjectionKind::Subslice => { + // Arrays are completely captured, so we drop Index and Subslice projections truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i); return (place, curr_mode); } ProjectionKind::Deref => {} ProjectionKind::Field(..) => {} // ignore - ProjectionKind::Subslice => {} // We never capture this } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 00348f3af..e876fa275 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -44,8 +44,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This attribute causes us to dump some writeback information // in the form of errors, which is used for unit tests. - let rustc_dump_user_substs = - self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs); + let rustc_dump_user_substs = self.tcx.has_attr(item_def_id, sym::rustc_dump_user_substs); let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs); for param in body.params { @@ -748,7 +747,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { .infcx .err_ctxt() .emit_inference_failure_err( - Some(self.body.id()), + self.tcx.hir().body_owner_def_id(self.body.id()), self.span.to_span(self.tcx), p.into(), E0282, |