diff options
Diffstat (limited to 'compiler/rustc_hir_typeck')
34 files changed, 2444 insertions, 1162 deletions
diff --git a/compiler/rustc_hir_typeck/locales/en-US.ftl b/compiler/rustc_hir_typeck/locales/en-US.ftl new file mode 100644 index 000000000..adfcbc36a --- /dev/null +++ b/compiler/rustc_hir_typeck/locales/en-US.ftl @@ -0,0 +1,79 @@ +hir_typeck_field_multiply_specified_in_initializer = + field `{$ident}` specified more than once + .label = used more than once + .previous_use_label = first use of `{$ident}` + +hir_typeck_copy_impl_on_type_with_dtor = + the trait `Copy` may not be implemented for this type; the type has a destructor + .label = `Copy` not allowed on types with destructors + +hir_typeck_multiple_relaxed_default_bounds = + type parameter has more than one relaxed default bound, only one is supported + +hir_typeck_copy_impl_on_non_adt = + the trait `Copy` may not be implemented for this type + .label = type is not a structure or enumeration + +hir_typeck_trait_object_declared_with_no_traits = + at least one trait is required for an object type + .alias_span = this alias does not contain a trait + +hir_typeck_functional_record_update_on_non_struct = + functional record update syntax requires a struct + +hir_typeck_return_stmt_outside_of_fn_body = + return statement outside of function body + .encl_body_label = the return is part of this body... + .encl_fn_label = ...not the enclosing function body + +hir_typeck_yield_expr_outside_of_generator = + yield expression outside of generator literal + +hir_typeck_struct_expr_non_exhaustive = + cannot create non-exhaustive {$what} using struct expression + +hir_typeck_method_call_on_unknown_type = + the type of this value must be known to call a method on a raw pointer on it + +hir_typeck_address_of_temporary_taken = cannot take address of a temporary + .label = temporary value + +hir_typeck_add_return_type_add = try adding a return type + +hir_typeck_add_return_type_missing_here = a return type might be missing here + +hir_typeck_expected_default_return_type = expected `()` because of default return type + +hir_typeck_expected_return_type = expected `{$expected}` because of return type + +hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` + +hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function + +hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item +hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count} + +hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize` + +hir_typeck_lang_start_incorrect_param = parameter {$param_num} of the `start` lang item is incorrect + .suggestion = change the type from `{$found_ty}` to `{$expected_ty}` + +hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect + .suggestion = change the type from `{$found_ty}` to `{$expected_ty}` + +hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` +hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` +hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide + +hir_typeck_convert_to_str = try converting the passed type into a `&str` + +hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters + +hir_typeck_fru_note = this expression may have been misinterpreted as a `..` range expression +hir_typeck_fru_expr = this expression does not end in a comma... +hir_typeck_fru_expr2 = ... so this is interpreted as a `..` range expression, instead of functional record update syntax +hir_typeck_fru_suggestion = + to set the remaining fields{$expr -> + [NONE]{""} + *[other] {" "}from `{$expr}` + }, separate the last named field with a comma diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index b6f19d3cc..e19ef2ff3 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut)); } // Now typecheck the blocks. @@ -188,8 +188,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir = self.tcx.hir(); // First, check that we're actually in the tail of a function. - let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) = - hir.get(self.body_id) else { return; }; + let Some(body_id) = hir.maybe_body_owned_by(self.body_id) else { return; }; + let body = hir.body(body_id); + let hir::ExprKind::Block(block, _) = body.value.kind else { return; }; let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. }) = block.innermost_block().stmts.last() else { return; }; if last_expr.hir_id != expr.hir_id { @@ -198,7 +199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Next, make sure that we have no type expectation. let Some(ret) = hir - .find_by_def_id(self.body_id.owner.def_id) + .find_by_def_id(self.body_id) .and_then(|owner| owner.fn_decl()) .map(|decl| decl.output.span()) else { return; }; let Expectation::IsLast(stmt) = expectation else { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index b617821fb..6a0d5c011 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::SubstsRef; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -156,7 +156,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // fnmut vs fnonce. If so, we have to defer further processing. if self.closure_kind(substs).is_none() { let closure_sig = substs.as_closure().sig(); - let closure_sig = self.replace_bound_vars_with_fresh_vars( + let closure_sig = self.instantiate_binder_with_fresh_vars( call_expr.span, infer::FnCall, closure_sig, @@ -232,7 +232,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(trait_def_id) = opt_trait_def_id else { continue }; let opt_input_type = opt_arg_exprs.map(|arg_exprs| { - self.tcx.mk_tup(arg_exprs.iter().map(|e| { + self.tcx.mk_tup_from_iter(arg_exprs.iter().map(|e| { self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span: e.span, @@ -247,6 +247,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adjusted_ty, opt_input_type.as_ref().map(slice::from_ref), ) { + // Check for `self` receiver on the method, otherwise we can't use this as a `Fn*` trait. + if !self.tcx.associated_item(ok.value.def_id).fn_has_self_parameter { + self.tcx.sess.delay_span_bug( + call_expr.span, + "input to overloaded call fn is not a self receiver", + ); + return None; + } + let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { @@ -257,7 +266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // caused an error elsewhere. self.tcx .sess - .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?"); + .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref"); return None; }; @@ -271,6 +280,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { target: method.sig.inputs()[0], }); } + return Some((autoref, method)); } } @@ -367,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let (fn_sig, def_id) = match *callee_ty.kind() { ty::FnDef(def_id, subst) => { - let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst); + let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst); // Unit testing: function items annotated with // `#[rustc_evaluate_where_clauses]` trigger special output @@ -428,7 +438,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); - return self.tcx.ty_error_with_guaranteed(err); + return self.tcx.ty_error(err); } }; @@ -437,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // renormalize the associated types at this point, since they // previously appeared within a `Binder<>` and hence would not // have been normalized before. - let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); + let fn_sig = self.instantiate_binder_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); let fn_sig = self.normalize(call_expr.span, fn_sig); // Call the generic checker. @@ -661,7 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) { let descr = match maybe_def { - DefIdOrName::DefId(def_id) => self.tcx.def_kind(def_id).descr(def_id), + DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id), DefIdOrName::Name(name) => name, }; err.span_label( @@ -823,7 +833,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { ); err.help( "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \ - and have associated `call`/`call_mut`/`call_once` functions", + and have correctly defined `call`/`call_mut`/`call_once` methods", ); err.emit(); } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 712f9b87a..316c2a7ee 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -41,7 +41,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; use rustc_session::Session; use rustc_span::def_id::{DefId, LOCAL_CRATE}; @@ -130,6 +130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Array(..) | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) | ty::RawPtr(_) | ty::Ref(..) | ty::FnDef(..) diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 57feefbca..bf8259ff7 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -43,7 +43,7 @@ pub(super) fn check_fn<'a, 'tcx>( let ret_ty = fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars( declared_ret_ty, - body.value.hir_id, + fn_def_id, decl.output.span(), fcx.param_env, )); @@ -74,15 +74,13 @@ pub(super) fn check_fn<'a, 'tcx>( // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` // (as it's created inside the body itself, not passed in from outside). - let maybe_va_list = if fn_sig.c_variadic { + let maybe_va_list = fn_sig.c_variadic.then(|| { let span = body.params.last().unwrap().span; let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()])) - } else { - None - }; + tcx.type_of(va_list_did).subst(tcx, &[region.into()]) + }); // Add formal parameters. let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); @@ -90,7 +88,7 @@ pub(super) fn check_fn<'a, 'tcx>( for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // Check the pattern. let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + fcx.check_pat_top(¶m.pat, param_ty, ty_span, None); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings @@ -130,7 +128,12 @@ pub(super) fn check_fn<'a, 'tcx>( let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { let interior = fcx .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); + fcx.deferred_generator_interiors.borrow_mut().push(( + fn_def_id, + body.id(), + interior, + gen_kind, + )); let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); Some(GeneratorTypes { @@ -167,12 +170,12 @@ pub(super) fn check_fn<'a, 'tcx>( // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` if let Some(panic_impl_did) = tcx.lang_items().panic_impl() - && panic_impl_did == hir.local_def_id(fn_id).to_def_id() + && panic_impl_did == fn_def_id.to_def_id() { check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); } - if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() { + if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == fn_def_id.to_def_id() { check_lang_start_fn(tcx, fn_sig, decl, fn_def_id); } @@ -259,11 +262,9 @@ fn check_lang_start_fn<'tcx>( // for example `start`'s generic should be a type parameter let generics = tcx.generics_of(def_id); let fn_generic = generics.param_at(0, tcx); - let generic_tykind = - ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name }); - let generic_ty = tcx.mk_ty(generic_tykind); + let generic_ty = tcx.mk_ty_param(fn_generic.index, fn_generic.name); let expected_fn_sig = - tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust); + tcx.mk_fn_sig([], generic_ty, false, hir::Unsafety::Normal, Abi::Rust); let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig)); // we emit the same error to suggest changing the arg no matter what's wrong with the arg diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 12a2abfa7..d84fabb78 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -3,8 +3,8 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use hir::def::DefKind; +use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -12,9 +12,11 @@ use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::ArgKind; @@ -80,7 +82,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?bound_sig, ?liberated_sig); - let mut fcx = FnCtxt::new(self, self.param_env.without_const(), body.value.hir_id); + let mut fcx = FnCtxt::new(self, self.param_env.without_const(), closure.def_id); let generator_types = check_fn( &mut fcx, liberated_sig, @@ -125,7 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the `closures` table. let sig = bound_sig.map_bound(|sig| { self.tcx.mk_fn_sig( - iter::once(self.tcx.intern_tup(sig.inputs())), + [self.tcx.mk_tup(sig.inputs())], sig.output(), sig.c_variadic, sig.unsafety, @@ -231,7 +233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { struct MentionsTy<'tcx> { expected_ty: Ty<'tcx>, } - impl<'tcx> TypeVisitor<'tcx> for MentionsTy<'tcx> { + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MentionsTy<'tcx> { type BreakTy = (); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -288,21 +290,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_def_id = projection.trait_def_id(tcx); let is_fn = tcx.is_fn_trait(trait_def_id); - let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); - let is_gen = gen_trait == trait_def_id; + + let gen_trait = tcx.lang_items().gen_trait(); + let is_gen = gen_trait == Some(trait_def_id); + if !is_fn && !is_gen { debug!("not fn or generator"); return None; } - if is_gen { - // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return` - // associated item and not yield. - let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1]; - if return_assoc_item != projection.projection_def_id() { - debug!("not return assoc item of generator"); - return None; - } + // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return` + // associated item and not yield. + if is_gen && self.tcx.associated_item(projection.projection_def_id()).name != sym::Return { + debug!("not `Return` assoc item of `Generator`"); + return None; } let input_tys = if is_fn { @@ -326,7 +327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?ret_param_ty); let sig = projection.rebind(self.tcx.mk_fn_sig( - input_tys.iter(), + input_tys, ret_param_ty, false, hir::Unsafety::Normal, @@ -488,17 +489,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let expected_span = expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id)); - self.report_arg_count_mismatch( - expected_span, - closure_span, - expected_args, - found_args, - true, - closure_arg_span, - ) - .emit(); - - let error_sig = self.error_sig_of_closure(decl); + let guar = self + .report_arg_count_mismatch( + expected_span, + closure_span, + expected_args, + found_args, + true, + closure_arg_span, + ) + .emit(); + + let error_sig = self.error_sig_of_closure(decl, guar); self.closure_sigs(expr_def_id, body, error_sig) } @@ -544,7 +546,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .map(|(hir_ty, &supplied_ty)| { // Instantiate (this part of..) S to S', i.e., with fresh variables. - self.replace_bound_vars_with_fresh_vars( + self.instantiate_binder_with_fresh_vars( hir_ty.span, LateBoundRegionConversionTime::FnCall, // (*) binder moved to here @@ -561,12 +563,14 @@ 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).eq(*expected_ty, supplied_ty)?; + let InferOk { value: (), obligations } = self + .at(&cause, self.param_env) + .define_opaque_types(true) + .eq(*expected_ty, supplied_ty)?; all_obligations.extend(obligations); } - let supplied_output_ty = self.replace_bound_vars_with_fresh_vars( + let supplied_output_ty = self.instantiate_binder_with_fresh_vars( decl.output.span(), LateBoundRegionConversionTime::FnCall, supplied_sig.output(), @@ -574,6 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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)?; all_obligations.extend(obligations); @@ -620,8 +625,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // function. Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => { debug!("closure is async fn body"); - self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id) - .unwrap_or_else(|| { + let def_id = self.tcx.hir().body_owner_def_id(body.id()); + self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else( + || { // AFAIK, deducing the future output // always succeeds *except* in error cases // like #65159. I'd like to return Error @@ -630,7 +636,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // *have* reported an // error. --nikomatsakis astconv.ty_infer(None, decl.output.span()) - }) + }, + ) } _ => astconv.ty_infer(None, decl.output.span()), @@ -665,7 +672,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn deduce_future_output_from_obligations( &self, expr_def_id: LocalDefId, - body_id: hir::HirId, + body_def_id: LocalDefId, ) -> Option<Ty<'tcx>> { let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn") @@ -725,7 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let InferOk { value: output_ty, obligations } = self .replace_opaque_types_with_inference_vars( output_ty, - body_id, + body_def_id, self.tcx.def_span(expr_def_id), self.param_env, ); @@ -787,13 +794,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Converts the types that the user supplied, in case that doing /// so should yield an error, but returns back a signature where /// all parameters are of type `TyErr`. - fn error_sig_of_closure(&self, decl: &hir::FnDecl<'_>) -> ty::PolyFnSig<'tcx> { + fn error_sig_of_closure( + &self, + decl: &hir::FnDecl<'_>, + guar: ErrorGuaranteed, + ) -> ty::PolyFnSig<'tcx> { let astconv: &dyn AstConv<'_> = self; + let err_ty = self.tcx.ty_error(guar); let supplied_arguments = decl.inputs.iter().map(|a| { // Convert the types that the user supplied (if any), but ignore them. astconv.ast_ty_to_ty(a); - self.tcx.ty_error() + err_ty }); if let hir::FnRetTy::Return(ref output) = decl.output { @@ -802,7 +814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = ty::Binder::dummy(self.tcx.mk_fn_sig( supplied_arguments, - self.tcx.ty_error(), + err_ty, decl.c_variadic, hir::Unsafety::Normal, Abi::RustCall, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bbf7b81a2..00b86890b 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -54,7 +54,7 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; @@ -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); if self.use_lub { - self.at(&self.cause, self.fcx.param_env).lub(b, a) + at.lub(b, a) } else { - self.at(&self.cause, self.fcx.param_env) - .sup(b, a) + at.sup(b, a) .map(|InferOk { value: (), obligations }| InferOk { value: a, obligations }) } }) @@ -170,12 +170,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("Coerce.tys({:?} => {:?})", a, b); // Just ignore error types. - if a.references_error() || b.references_error() { + if let Err(guar) = (a, b).error_reported() { // Best-effort try to unify these types -- we're already on the error path, // 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).eq(a, b)); - return success(vec![], self.fcx.tcx.ty_error(), vec![]); + let _ = self.commit_if_ok(|_| { + self.at(&self.cause, self.param_env).define_opaque_types(true).eq(a, b) + }); + return success(vec![], self.fcx.tcx.ty_error(guar), vec![]); } // Coercing from `!` to any type is allowed: @@ -765,7 +767,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.cause.clone(), self.param_env, ty::Binder::dummy( - self.tcx.at(self.cause.span).mk_trait_ref(hir::LangItem::PointerSized, [a]), + self.tcx.at(self.cause.span).mk_trait_ref(hir::LangItem::PointerLike, [a]), ), )); @@ -995,7 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (adjustments, _) = self.register_infer_ok_obligations(ok); self.apply_adjustments(expr, adjustments); - Ok(if expr_ty.references_error() { self.tcx.ty_error() } else { target }) + Ok(if let Err(guar) = expr_ty.error_reported() { self.tcx.ty_error(guar) } else { target }) } /// Same as `try_coerce()`, but without side-effects. @@ -1046,7 +1048,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.param_env, ) .may_apply() - .then(|| deref_ty) + .then_some(deref_ty) }) } @@ -1432,8 +1434,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // If we see any error types, just propagate that error // upwards. - if expression_ty.references_error() || self.merged_ty().references_error() { - self.final_ty = Some(fcx.tcx.ty_error()); + if let Err(guar) = (expression_ty, self.merged_ty()).error_reported() { + self.final_ty = Some(fcx.tcx.ty_error(guar)); return; } @@ -1484,6 +1486,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Another example is `break` with no argument expression. 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()) .map(|infer_ok| { fcx.register_infer_ok_obligations(infer_ok); @@ -1613,12 +1617,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); } + let reported = err.emit_unless(unsized_return); - self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported)); + self.final_ty = Some(fcx.tcx.ty_error(reported)); } } } + fn note_unreachable_loop_return( &self, err: &mut Diagnostic, @@ -1821,7 +1827,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { .trait_ref() .and_then(|t| t.trait_def_id()) .map_or(false, |def_id| { - fcx.tcx.object_safety_violations(def_id).is_empty() + fcx.tcx.check_is_object_safe(def_id) }) }) } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index f4c4d4310..7ba57b3b7 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,9 +1,11 @@ use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::CtorKind; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; use rustc_infer::infer::InferOk; @@ -11,11 +13,14 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; +use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder}; +use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeVisitableExt}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches; use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -40,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_alternative_method_deref(err, expr, error); // Use `||` to give these suggestions a precedence - let _ = self.suggest_missing_parentheses(err, expr) + let suggested = self.suggest_missing_parentheses(err, expr) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) @@ -54,7 +59,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) - || self.suggest_floating_point_literal(err, expr, 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); + if !suggested { + self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span); + } } pub fn emit_coerce_suggestions( @@ -73,7 +83,6 @@ 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_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, 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); @@ -104,7 +113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - match self.at(cause, self.param_env).sup(expected, actual) { + match self.at(cause, self.param_env).define_opaque_types(true).sup(expected, actual) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None @@ -134,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - match self.at(cause, self.param_env).eq(expected, actual) { + match self.at(cause, self.param_env).define_opaque_types(true).eq(expected, actual) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None @@ -208,6 +217,223 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } + pub fn point_at_expr_source_of_inferred_type( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + mismatch_span: Span, + ) -> bool { + let map = self.tcx.hir(); + + let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; }; + let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; }; + let hir::def::Res::Local(hir_id) = p.res else { return false; }; + let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; }; + let Some(hir::Node::Local(hir::Local { + ty: None, + init: Some(init), + .. + })) = map.find_parent(pat.hir_id) else { return false; }; + let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; }; + if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() { + return false; + } + + // Locate all the usages of the relevant binding. + struct FindExprs<'hir> { + hir_id: hir::HirId, + uses: Vec<&'hir hir::Expr<'hir>>, + } + impl<'v> Visitor<'v> for FindExprs<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind + && let hir::def::Res::Local(hir_id) = path.res + && hir_id == self.hir_id + { + self.uses.push(ex); + } + hir::intravisit::walk_expr(self, ex); + } + } + + let mut expr_finder = FindExprs { hir_id, uses: vec![] }; + let id = map.get_parent_item(hir_id); + let hir_id: hir::HirId = id.into(); + + let Some(node) = map.find(hir_id) else { return false; }; + let Some(body_id) = node.body_id() else { return false; }; + let body = map.body(body_id); + expr_finder.visit_expr(body.value); + // Hack to make equality checks on types with inference variables and regions useful. + let mut eraser = BottomUpFolder { + tcx: self.tcx, + lt_op: |_| self.tcx.lifetimes.re_erased, + ct_op: |c| c, + ty_op: |t| match *t.kind() { + ty::Infer(ty::TyVar(_)) => 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, + }, + }; + 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, + }; + // Remove one layer of references to account for `&mut self` and + // `&self`, so that we can compare it against the binding. + let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) { + (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty), + _ => (ty, def_self_ty), + }; + let mut param_args = FxHashMap::default(); + let mut param_expected = FxHashMap::default(); + let mut param_found = FxHashMap::default(); + if self.can_eq(self.param_env, ty, found) { + // 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)); + } + } + } + + // Here we find, for a type param `T`, the type that `T` is in the current + // method call *and* in the original expected type. That way, we can see if we + // can give any structured suggestion for the function argument. + let mut c = CollectAllMismatches { + infcx: &self.infcx, + param_env: self.param_env, + errors: vec![], + }; + let _ = c.relate(def_self_ty, ty); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_found.insert(error.expected, error.found); + } + } + c.errors = vec![]; + let _ = c.relate(def_self_ty, expected); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_expected.insert(error.expected, error.found); + } + } + for (param, (arg, arg_ty)) in param_args.iter() { + let Some(expected) = param_expected.get(param) else { continue; }; + let Some(found) = param_found.get(param) else { continue; }; + if !self.can_eq(self.param_env, *arg_ty, *found) { 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) + { + // We only point at the first place where the found type was inferred. + if !segment.ident.span.overlaps(mismatch_span) { + 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}`", + )), + ); + } + break; + } + prev = ty; + } + if binding.hir_id == expr.hir_id { + // Do not look at expressions that come after the expression we were originally + // evaluating and had a type error. + break; + } + prev_span = Some(binding.span); + } + true + } + fn annotate_expected_due_to_let_ty( &self, err: &mut Diagnostic, @@ -379,6 +605,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let substs = ty::InternalSubsts::for_item(self.tcx, m.def_id, |param, _| { self.var_for_def(deref.span, param) }); + let mutability = + match self.tcx.fn_sig(m.def_id).skip_binder().input(0).skip_binder().kind() { + ty::Ref(_, _, hir::Mutability::Mut) => "&mut ", + ty::Ref(_, _, _) => "&", + _ => "", + }; vec![ ( deref.span.until(base.span), @@ -387,11 +619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { with_no_trimmed_paths!( self.tcx.def_path_str_with_substs(m.def_id, substs,) ), - match self.tcx.fn_sig(m.def_id).input(0).skip_binder().kind() { - ty::Ref(_, _, hir::Mutability::Mut) => "&mut ", - ty::Ref(_, _, _) => "&", - _ => "", - }, + mutability, ), ), match &args[..] { @@ -479,6 +707,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + pub(crate) fn note_result_coercion( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + let ty::Adt(e, substs_e) = expected.kind() else { return false; }; + let ty::Adt(f, substs_f) = found.kind() else { return false; }; + if e.did() != f.did() { + return false; + } + if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { + return false; + } + let map = self.tcx.hir(); + if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) + && let hir::ExprKind::Ret(_) = expr.kind + { + // `return foo;` + } else if map.get_return_block(expr.hir_id).is_some() { + // Function's tail expression. + } else { + return false; + } + let e = substs_e.type_at(1); + let f = substs_f.type_at(1); + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::Into).unwrap(), + [f, e], + self.param_env, + ) + .must_apply_modulo_regions() + { + err.multipart_suggestion( + "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \ + in `Ok` so the expression remains of type `Result`", + vec![ + (expr.span.shrink_to_lo(), "Ok(".to_string()), + (expr.span.shrink_to_hi(), "?)".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( @@ -491,7 +769,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(expected_adt, substs) = expected.kind() { if let hir::ExprKind::Field(base, ident) = expr.kind { let base_ty = self.typeck_results.borrow().expr_ty(base); - if self.can_eq(self.param_env, base_ty, expected).is_ok() + if self.can_eq(self.param_env, base_ty, expected) && let Some(base_span) = base.span.find_ancestor_inside(expr.span) { err.span_suggestion_verbose( @@ -762,7 +1040,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match method.kind { ty::AssocKind::Fn => { method.fn_has_self_parameter - && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1 + && self.tcx.fn_sig(method.def_id).skip_binder().inputs().skip_binder().len() + == 1 } _ => false, } @@ -990,10 +1269,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ``` let ref_ty = match mutability { hir::Mutability::Mut => { - self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty) + self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, checked_ty) } hir::Mutability::Not => { - self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty) + self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, checked_ty) } }; if self.can_coerce(ref_ty, expected) { @@ -1015,6 +1294,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg_sp = receiver.span; } } + + if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind + && let Some(1) = self.deref_steps(expected, checked_ty) { + // We have `*&T`, check if what was expected was `&T`. + // If so, we may want to suggest removing a `*`. + sugg_sp = sugg_sp.with_hi(inner.span.lo()); + return Some(( + sugg_sp, + "consider removing deref here".to_string(), + "".to_string(), + Applicability::MachineApplicable, + true, + false, + )); + } + if let Ok(src) = sm.span_to_snippet(sugg_sp) { let needs_parens = match expr.kind { // parenthesize if needed (Issue #46756) @@ -1067,7 +1362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr), _, &ty::Ref(_, checked, _), - ) if self.can_sub(self.param_env, checked, expected).is_ok() => { + ) if self.can_sub(self.param_env, checked, expected) => { // We have `&T`, check if what was expected was `T`. If so, // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { @@ -1713,7 +2008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; }; let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else { return; }; - if self.can_eq(self.param_env, expected_ty, ty).is_ok() { + if self.can_eq(self.param_env, expected_ty, ty) { err.span_suggestion_short( stmt.span.with_lo(tail_expr.span.hi()), "remove this semicolon", @@ -1742,7 +2037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args: &[hir::Expr<'_>], kind: CallableKind| { let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap(); - let fn_ty = self.tcx.bound_type_of(def_id).0; + let fn_ty = self.tcx.type_of(def_id).skip_binder(); if !fn_ty.is_fn() { return; } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 5b4fd5e4a..3eee2278d 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,8 +1,13 @@ //! Errors emitted by `rustc_hir_typeck`. +use crate::fluent_generated as fluent; use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; -use rustc_span::{symbol::Ident, Span}; +use rustc_span::{ + edition::{Edition, LATEST_STABLE_EDITION}, + symbol::Ident, + Span, +}; #[derive(Diagnostic)] #[diag(hir_typeck_field_multiply_specified_in_initializer, code = "E0062")] @@ -10,7 +15,7 @@ pub struct FieldMultiplySpecifiedInInitializer { #[primary_span] #[label] pub span: Span, - #[label(previous_use_label)] + #[label(hir_typeck_previous_use_label)] pub prev_span: Span, pub ident: Ident, } @@ -20,9 +25,9 @@ pub struct FieldMultiplySpecifiedInInitializer { pub struct ReturnStmtOutsideOfFnBody { #[primary_span] pub span: Span, - #[label(encl_body_label)] + #[label(hir_typeck_encl_body_label)] pub encl_body_span: Option<Span>, - #[label(encl_fn_label)] + #[label(hir_typeck_encl_fn_label)] pub encl_fn_span: Option<Span>, } @@ -153,20 +158,17 @@ impl AddToDiagnostic for TypeMismatchFruTypo { // Only explain that `a ..b` is a range if it's split up if self.expr_span.between(self.fru_span).is_empty() { - diag.span_note( - self.expr_span.to(self.fru_span), - rustc_errors::fluent::hir_typeck_fru_note, - ); + diag.span_note(self.expr_span.to(self.fru_span), fluent::hir_typeck_fru_note); } else { let mut multispan: MultiSpan = vec![self.expr_span, self.fru_span].into(); - multispan.push_span_label(self.expr_span, rustc_errors::fluent::hir_typeck_fru_expr); - multispan.push_span_label(self.fru_span, rustc_errors::fluent::hir_typeck_fru_expr2); - diag.span_note(multispan, rustc_errors::fluent::hir_typeck_fru_note); + multispan.push_span_label(self.expr_span, fluent::hir_typeck_fru_expr); + multispan.push_span_label(self.fru_span, fluent::hir_typeck_fru_expr2); + diag.span_note(multispan, fluent::hir_typeck_fru_note); } diag.span_suggestion( self.expr_span.shrink_to_hi(), - rustc_errors::fluent::hir_typeck_fru_suggestion, + fluent::hir_typeck_fru_suggestion, ", ", Applicability::MaybeIncorrect, ); @@ -205,3 +207,24 @@ pub struct LangStartIncorrectRetTy<'tcx> { pub expected_ty: Ty<'tcx>, pub found_ty: Ty<'tcx>, } + +#[derive(Subdiagnostic)] +pub enum HelpUseLatestEdition { + #[help(hir_typeck_help_set_edition_cargo)] + #[note(hir_typeck_note_edition_guide)] + Cargo { edition: Edition }, + #[help(hir_typeck_help_set_edition_standalone)] + #[note(hir_typeck_note_edition_guide)] + Standalone { edition: Edition }, +} + +impl HelpUseLatestEdition { + pub fn new() -> Self { + let edition = LATEST_STABLE_EDITION; + if std::env::var_os("CARGO").is_some() { + Self::Cargo { edition } + } else { + Self::Standalone { edition } + } + } +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index bc7474cdf..7fc4ccb04 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -8,7 +8,7 @@ use crate::coercion::DynamicCoerceMany; use crate::errors::TypeMismatchFruTypo; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use crate::errors::{ - FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, YieldExprOutsideOfGenerator, }; use crate::fatally_break_rust; @@ -23,8 +23,8 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, - ErrorGuaranteed, StashKey, + pluralize, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, + DiagnosticId, ErrorGuaranteed, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -42,11 +42,11 @@ use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; +use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_session::parse::feature_err; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; -use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_target::spec::abi::Abi::RustIntrinsic; @@ -88,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { target.to_owned() } else { - self.tcx().ty_error_with_guaranteed(reported) + self.tcx().ty_error(reported) }; } @@ -313,7 +313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.types.never } else { // There was an error; make type-check fail. - tcx.ty_error() + tcx.ty_error_misc() } } ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), @@ -354,7 +354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected), ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), - hir::ExprKind::Err => tcx.ty_error(), + hir::ExprKind::Err(guar) => tcx.ty_error(guar), } } @@ -402,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } - oprnd_t = tcx.ty_error_with_guaranteed(err.emit()); + oprnd_t = tcx.ty_error(err.emit()); } } hir::UnOp::Not => { @@ -452,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tm = ty::TypeAndMut { ty, mutbl }; match kind { - _ if tm.ty.references_error() => self.tcx.ty_error(), + _ if tm.ty.references_error() => self.tcx.ty_error_misc(), hir::BorrowKind::Raw => { self.check_named_place_expr(oprnd); self.tcx.mk_ptr(tm) @@ -531,18 +531,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); - tcx.ty_error_with_guaranteed(e) + tcx.ty_error(e) } Res::Def(DefKind::Variant, _) => { let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, "E0533", "value"); - tcx.ty_error_with_guaranteed(e) + tcx.ty_error(e) } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; if let ty::FnDef(did, ..) = *ty.kind() { let fn_sig = ty.fn_sig(tcx); - if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute { + if tcx.fn_sig(did).skip_binder().abi() == RustIntrinsic + && tcx.item_name(did) == sym::transmute + { let from = fn_sig.inputs().skip_binder()[0]; let to = fn_sig.output().skip_binder(); // We defer the transmute to the end of typeck, once all inference vars have @@ -566,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // placeholder lifetimes with probing, we just replace higher lifetimes // with fresh vars. let span = args.get(i).map(|a| a.span).unwrap_or(expr.span); - let input = self.replace_bound_vars_with_fresh_vars( + let input = self.instantiate_binder_with_fresh_vars( span, infer::LateBoundRegionConversionTime::FnCall, fn_sig.input(i), @@ -584,7 +586,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Also, as we just want to check sizedness, instead of introducing // placeholder lifetimes with probing, we just replace higher lifetimes // with fresh vars. - let output = self.replace_bound_vars_with_fresh_vars( + let output = self.instantiate_binder_with_fresh_vars( expr.span, infer::LateBoundRegionConversionTime::FnCall, fn_sig.output(), @@ -632,7 +634,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the loop context is not a `loop { }`, then break with // a value is illegal, and `opt_coerce_to` will be `None`. // Just set expectation to error in that case. - let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); + let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error_misc()); // Recurse without `enclosing_breakables` borrowed. e_ty = self.check_expr_with_hint(e, coerce_to); @@ -852,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Point any obligations that were registered due to opaque type // inference at the return expression. self.select_obligations_where_possible(|errors| { - self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty); + self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty, return_expr.span); }); } } @@ -862,9 +864,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors: &mut Vec<traits::FulfillmentError<'tcx>>, span: Span, return_expr_ty: Ty<'tcx>, + return_span: Span, ) { // Don't point at the whole block if it's empty - if span == self.tcx.hir().span(self.body_id) { + if span == return_span { return; } for err in errors { @@ -1030,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let result_ty = coerce.complete(self); - if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty } + if let Err(guar) = cond_ty.error_reported() { self.tcx.ty_error(guar) } else { result_ty } } /// Type check assignment expression `expr` of form `lhs = rhs`. @@ -1106,7 +1109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the assignment expression itself is ill-formed, don't // bother emitting another error let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()); - return self.tcx.ty_error_with_guaranteed(reported); + return self.tcx.ty_error(reported); } let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); @@ -1152,8 +1155,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); - if lhs_ty.references_error() || rhs_ty.references_error() { - self.tcx.ty_error() + if let Err(guar) = (lhs_ty, rhs_ty).error_reported() { + self.tcx.ty_error(guar) } else { self.tcx.mk_unit() } @@ -1271,8 +1274,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let t_expr = self.resolve_vars_if_possible(t_expr); // Eagerly check for some obvious errors. - if t_expr.references_error() || t_cast.references_error() { - self.tcx.ty_error() + if let Err(guar) = (t_expr, t_cast).error_reported() { + self.tcx.ty_error(guar) } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); @@ -1293,7 +1296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_cast_checks.push(cast_check); t_cast } - Err(_) => self.tcx.ty_error(), + Err(guar) => self.tcx.ty_error(guar), } } } @@ -1374,7 +1377,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let body = self.tcx.hir().body(anon_const.body); // Create a new function context. - let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id); + let def_id = anon_const.def_id; + let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id); crate::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(&body.value, expected); @@ -1392,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let tcx = self.tcx; let count = self.array_length_to_const(count); - if let Some(count) = count.try_eval_usize(tcx, self.param_env) { + if let Some(count) = count.try_eval_target_usize(tcx, self.param_env) { self.suggest_array_len(expr, count); } @@ -1419,13 +1423,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if element_ty.references_error() { - return tcx.ty_error(); + if let Err(guar) = element_ty.error_reported() { + return tcx.ty_error(guar); } self.check_repeat_element_needs_copy_bound(element, count, element_ty); - tcx.mk_ty(ty::Array(t, count)) + tcx.mk_array_with_const_len(t, count) } fn check_repeat_element_needs_copy_bound( @@ -1459,7 +1463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we // don't copy that one element, we move it. Only check for Copy if the length is larger. - if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) { let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn }; self.require_type_meets(element_ty, element.span, code, lang_item); @@ -1488,9 +1492,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => self.check_expr_with_expectation(&e, NoExpectation), }); - let tuple = self.tcx.mk_tup(elt_ts_iter); - if tuple.references_error() { - self.tcx.ty_error() + let tuple = self.tcx.mk_tup_from_iter(elt_ts_iter); + if let Err(guar) = tuple.error_reported() { + self.tcx.ty_error(guar) } else { self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); tuple @@ -1506,9 +1510,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) -> Ty<'tcx> { // Find the relevant variant - let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else { - self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.ty_error(); + let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) { + Ok(data) => data, + Err(guar) => { + self.check_struct_fields_on_error(fields, base_expr); + return self.tcx.ty_error(guar); + } }; // Prohibit struct expressions when non-exhaustive flag is set. @@ -1590,12 +1597,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.field_ty(field.span, v_field, substs) } else { error_happened = true; - if let Some(prev_span) = seen_fields.get(&ident) { + let guar = if let Some(prev_span) = seen_fields.get(&ident) { tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { span: field.ident.span, prev_span: *prev_span, ident, - }); + }) } else { self.report_unknown_field( adt_ty, @@ -1604,10 +1611,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast_fields, adt.variant_descr(), expr_span, - ); - } + ) + }; - tcx.ty_error() + tcx.ty_error(guar) }; // Make sure to give a type to the field even if there's @@ -1990,14 +1997,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { skip_fields: &[hir::ExprField<'_>], kind_name: &str, expr_span: Span, - ) { + ) -> ErrorGuaranteed { if variant.is_recovered() { - self.set_tainted_by_errors( - self.tcx - .sess - .delay_span_bug(expr_span, "parser recovered but no error was emitted"), - ); - return; + let guar = self + .tcx + .sess + .delay_span_bug(expr_span, "parser recovered but no error was emitted"); + self.set_tainted_by_errors(guar); + return guar; } let mut err = self.err_ctxt().type_error_struct_with_diag( field.ident.span, @@ -2111,7 +2118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; } } - err.emit(); + err.emit() } // Return a hint about the closest match in field names @@ -2151,13 +2158,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant: &'tcx ty::VariantDef, access_span: Span, ) -> Vec<Symbol> { + let body_owner_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id); variant .fields .iter() .filter(|field| { let def_scope = self .tcx - .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) + .adjust_ident_and_get_scope( + field.ident(self.tcx), + variant.def_id, + body_owner_hir_id, + ) .1; field.vis.is_accessible_from(def_scope, self.tcx) && !matches!( @@ -2199,8 +2211,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match deref_base_ty.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { debug!("struct named {:?}", deref_base_ty); + let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id); let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); + 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() @@ -2246,11 +2259,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (#90483) apply adjustments to avoid ExprUseVisitor from // creating erroneous projection. self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, base_ty, field, did, expected.only_has_type(self)); - return self.tcx().ty_error(); + let guar = self.ban_private_field_access( + expr, + base_ty, + field, + did, + expected.only_has_type(self), + ); + return self.tcx().ty_error(guar); } - if field.name == kw::Empty { + let guar = if field.name == kw::Empty { + self.tcx.sess.delay_span_bug(field.span, "field name with no name") } else if self.method_exists( field, base_ty, @@ -2258,9 +2278,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true, expected.only_has_type(self), ) { - self.ban_take_value_of_method(expr, base_ty, field); + self.ban_take_value_of_method(expr, base_ty, field) } else if !base_ty.is_primitive_ty() { - self.ban_nonexisting_field(field, base, expr, base_ty); + self.ban_nonexisting_field(field, base, expr, base_ty) } else { let field_name = field.to_string(); let mut err = type_error_struct!( @@ -2329,10 +2349,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - err.emit(); - } + err.emit() + }; - self.tcx().ty_error() + self.tcx().ty_error(guar) } fn suggest_await_on_field_access( @@ -2378,7 +2398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, base_ty: Ty<'tcx>, - ) { + ) -> ErrorGuaranteed { debug!( "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", ident, base, expr, base_ty @@ -2423,10 +2443,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We know by construction that `<expr>.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. err.note("to `.await` a `Future`, switch to Rust 2018 or later"); - err.help_use_latest_edition(); + HelpUseLatestEdition::new().add_to_diagnostic(&mut err); } - err.emit(); + err.emit() } fn ban_private_field_access( @@ -2436,9 +2456,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: Ident, base_did: DefId, return_ty: Option<Ty<'tcx>>, - ) { + ) -> ErrorGuaranteed { let struct_path = self.tcx().def_path_str(base_did); - let kind_name = self.tcx().def_kind(base_did).descr(base_did); + let kind_name = self.tcx().def_descr(base_did); let mut err = struct_span_err!( self.tcx().sess, field.span, @@ -2459,10 +2479,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ); } - err.emit(); + err.emit() } - fn ban_take_value_of_method(&self, expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident) { + fn ban_take_value_of_method( + &self, + expr: &hir::Expr<'tcx>, + expr_t: Ty<'tcx>, + field: Ident, + ) -> ErrorGuaranteed { let mut err = type_error_struct!( self.tcx().sess, field.span, @@ -2534,11 +2559,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("methods are immutable and cannot be assigned to"); } - err.emit(); + err.emit() } fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let generics = self.tcx.generics_of(self.body_id); let generic_param = generics.type_param(¶m, self.tcx); if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { return; @@ -2592,7 +2617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, ) { if let (Some(len), Ok(user_index)) = - (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>()) + (len.try_eval_target_usize(self.tcx, self.param_env), field.as_str().parse::<u64>()) && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { let help = "instead of using tuple indexing, use array indexing"; @@ -2819,7 +2844,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } let reported = err.emit(); - self.tcx.ty_error_with_guaranteed(reported) + self.tcx.ty_error(reported) } } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index c8cda0dc9..b9a058d6b 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -301,7 +301,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { hir::ExprKind::Continue(..) | hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Err => {} + | hir::ExprKind::Err(_) => {} hir::ExprKind::Loop(blk, ..) => { self.walk_block(blk); diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index dde879780..b7ae621c6 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -104,7 +104,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // type, `?T` is not considered unsolved, but `?I` is. The // same is true for float variables.) let fallback = match ty.kind() { - _ if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), + _ if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e), ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, _ => match diverging_fallback.get(&ty) { @@ -196,8 +196,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> { debug!("calculate_diverging_fallback({:?})", unsolved_variables); - let relationships = self.fulfillment_cx.borrow_mut().relationships().clone(); - // Construct a coercion graph where an edge `A -> B` indicates // a type variable is that is coerced let coercion_graph = self.create_coercion_graph(); @@ -281,9 +279,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { roots_reachable_from_non_diverging, ); - debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations()); debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations()); - debug!("relationships: {:#?}", relationships); // For each diverging variable, figure out whether it can // reach a member of N. If so, it falls back to `()`. Else @@ -297,16 +293,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .depth_first_search(root_vid) .any(|n| roots_reachable_from_non_diverging.visited(n)); - let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false }; + let mut found_infer_var_info = ty::InferVarInfo { self_in_trait: false, output: false }; - for (vid, rel) in relationships.iter() { - if self.root_var(*vid) == root_vid { - relationship.self_in_trait |= rel.self_in_trait; - relationship.output |= rel.output; + for (vid, info) in self.inh.infer_var_info.borrow().iter() { + if self.infcx.root_var(*vid) == root_vid { + found_infer_var_info.self_in_trait |= info.self_in_trait; + found_infer_var_info.output |= info.output; } } - if relationship.self_in_trait && relationship.output { + if found_infer_var_info.self_in_trait && found_infer_var_info.output { // This case falls back to () to ensure that the code pattern in // tests/ui/never_type/fallback-closure-ret.rs continues to // compile when never_type_fallback is enabled. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6ed8adb47..60e55c7b0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -23,16 +23,16 @@ use rustc_infer::infer::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; +use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType, + self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, TyCtxt, UserType, }; use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; @@ -315,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.register_infer_ok_obligations( self.at(&self.misc(span), self.param_env).normalize(value), @@ -443,7 +443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // sufficiently enforced with erased regions. =) fn can_contain_user_lifetime_bounds<T>(t: T) -> bool where - T: TypeVisitable<'tcx>, + T: TypeVisitable<TyCtxt<'tcx>>, { t.has_free_regions() || t.has_projections() || t.has_infer_types() } @@ -451,11 +451,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => t, - None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), + None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e), None => { bug!( - "no type for node {}: {} in fcx {}", - id, + "no type for node {} in fcx {}", self.tcx.hir().node_to_string(id), self.tag() ); @@ -466,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => Some(t), - None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error_with_guaranteed(e)), + None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error(e)), None => None, } } @@ -517,16 +516,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { + if self.tcx.sess.opts.unstable_opts.drop_tracking_mir { + self.save_generator_interior_predicates(def_id); + return; + } + + self.select_obligations_where_possible(|_| {}); + let mut generators = self.deferred_generator_interiors.borrow_mut(); - for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(|_| {}); + for (_, body_id, interior, kind) in generators.drain(..) { crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); + self.select_obligations_where_possible(|_| {}); + } + } + + /// Unify the inference variables corresponding to generator witnesses, and save all the + /// predicates that were stalled on those inference variables. + /// + /// This process allows to conservatively save all predicates that do depend on the generator + /// interior types, for later processing by `check_generator_obligations`. + /// + /// We must not attempt to select obligations after this method has run, or risk query cycle + /// ICE. + #[instrument(level = "debug", skip(self))] + fn save_generator_interior_predicates(&self, def_id: DefId) { + // Try selecting all obligations that are not blocked on inference variables. + // Once we start unifying generator witnesses, trying to select obligations on them will + // trigger query cycle ICEs, as doing so requires MIR. + self.select_obligations_where_possible(|_| {}); + + let generators = std::mem::take(&mut *self.deferred_generator_interiors.borrow_mut()); + debug!(?generators); + + for &(expr_def_id, body_id, interior, _) in generators.iter() { + debug!(?expr_def_id); + + // Create the `GeneratorWitness` type that we will unify with `interior`. + let substs = ty::InternalSubsts::identity_for_item( + self.tcx, + self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), + ); + let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs); + + // Unify `interior` with `witness` and collect all the resulting obligations. + let span = self.tcx.hir().body(body_id).value.span; + let ok = self + .at(&self.misc(span), self.param_env) + .eq(interior, witness) + .expect("Failed to unify generator interior type"); + let mut obligations = ok.obligations; + + // Also collect the obligations that were unstalled by this unification. + obligations + .extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx)); + + let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)).collect(); + debug!(?obligations); + self.typeck_results + .borrow_mut() + .generator_interior_predicates + .insert(expr_def_id, obligations); } } #[instrument(skip(self), level = "debug")] - pub(in super::super) fn select_all_obligations_or_error(&self) { - let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); + pub(in super::super) fn report_ambiguity_errors(&self) { + let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(); if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); @@ -608,12 +663,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) // N.B., this predicate is created by breaking down a @@ -644,7 +701,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { - vec![self.tcx.ty_error(); len] + let ty_error = self.tcx.ty_error_misc(); + vec![ty_error; len] } /// Unifies the output type with the expected type early, for more coercions @@ -680,7 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind() && let Some(def_id) = def_id.as_local() - && self.opaque_type_origin(def_id, DUMMY_SP).is_some() { + && self.opaque_type_origin(def_id).is_some() { return None; } } @@ -720,9 +778,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let def_kind = self.tcx.def_kind(def_id); let item_ty = if let DefKind::Variant = def_kind { - self.tcx.bound_type_of(self.tcx.parent(def_id)) + self.tcx.type_of(self.tcx.parent(def_id)) } else { - self.tcx.bound_type_of(def_id) + self.tcx.type_of(def_id) }; let substs = self.fresh_substs_for_item(span, def_id); let ty = item_ty.subst(self.tcx, substs); @@ -866,6 +924,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::ImplItemKind::Fn(ref sig, ..), .. }) => 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 { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + .. + })) = self.tcx.hir().find_parent(hir_id) => { + Some((&sig.decl, ident, ident.name != sym::main)) + }, _ => None, } } @@ -926,43 +1000,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn note_need_for_fn_pointer( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let (sig, did, substs) = match (&expected.kind(), &found.kind()) { - (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); - if sig1 != sig2 { - return; - } - err.note( - "different `fn` items always have unique types, even if their signatures are \ - the same", - ); - (sig1, *did1, substs1) - } - (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs); - if sig1 != *sig2 { - return; - } - (sig1, *did, substs) - } - _ => return, - }; - err.help(&format!("change the expected type to be function pointer `{}`", sig)); - err.help(&format!( - "if the expected type is due to type inference, cast the expected `fn` to a function \ - pointer: `{} as {}`", - self.tcx.def_path_str_with_substs(did, substs), - sig - )); - } - // Instantiates the given path, which must refer to an item with the given // number of type parameters and type. #[instrument(skip(self, span), level = "debug")] @@ -1095,7 +1132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or(false); let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id)); + let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).subst_identity()); match ty.normalized.ty_adt_def() { Some(adt_def) if adt_def.has_ctor() => { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); @@ -1125,7 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } let reported = err.emit(); - return (tcx.ty_error_with_guaranteed(reported), res); + return (tcx.ty_error(reported), res); } } } else { @@ -1191,7 +1228,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { let tcx = self.fcx.tcx(); - self.fcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into() + self.fcx + .ct_infer( + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + Some(param), + inf.span, + ) + .into() } _ => unreachable!(), } @@ -1213,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have a default, then we it doesn't matter that we're not // inferring the type arguments: we provide the default where any // is missing. - tcx.bound_type_of(param.def_id).subst(tcx, substs.unwrap()).into() + tcx.type_of(param.def_id).subst(tcx, substs.unwrap()).into() } else { // If no type arguments were provided, we have to infer them. // This case also occurs as a result of some malformed input, e.g. @@ -1261,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Substitute the values for the type parameters into the type of // the referenced item. - let ty = tcx.bound_type_of(def_id); + let ty = tcx.type_of(def_id); assert!(!substs.has_escaping_bound_vars()); assert!(!ty.0.has_escaping_bound_vars()); let ty_substituted = self.normalize(span, ty.subst(tcx, substs)); @@ -1272,7 +1317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type parameters, which we can infer by unifying the provided `Self` // with the substituted impl type. // This also occurs for an enum variant on a type alias. - let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs)); + 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) { Ok(ok) => self.register_infer_ok_obligations(ok), @@ -1373,7 +1418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) .emit() }); - let err = self.tcx.ty_error_with_guaranteed(e); + let err = self.tcx.ty_error(e); self.demand_suptype(sp, err, ty); err } 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 new file mode 100644 index 000000000..b09886fe3 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -0,0 +1,864 @@ +use crate::FnCtxt; +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_trait_selection::traits; + +use std::ops::ControlFlow; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn adjust_fulfillment_error_for_expr_obligation( + &self, + error: &mut traits::FulfillmentError<'tcx>, + ) -> bool { + let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx)) + = *error.obligation.cause.code().peel_derives() else { return false; }; + let hir = self.tcx.hir(); + let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; }; + + let Some(unsubstituted_pred) = + self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) + else { return false; }; + + 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(), + }; + + let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { + predicate_substs.types().find_map(|ty| { + ty.walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(param_ty) = ty.kind() + && matches(param_ty) + { + Some(arg) + } else { + None + } + }) + }) + }; + + // 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 + }); + // 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 + }); + // 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); + + // 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 { + fallback_param_to_point_at = None; + self_param_to_point_at = None; + param_to_point_at = + self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); + } + + if self.closure_span_overlaps_error(error, expr.span) { + return false; + } + + match &expr.kind { + hir::ExprKind::Path(qpath) => { + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(callee, args), + hir_id: call_hir_id, + span: call_span, + .. + }) = hir.get_parent(expr.hir_id) + && callee.hir_id == expr.hir_id + { + if self.closure_span_overlaps_error(error, *call_span) { + return false; + } + + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.blame_specific_arg_if_possible( + error, + def_id, + param, + *call_hir_id, + callee.span, + None, + args, + ) + { + return true; + } + } + } + // Notably, we only point to params that are local to the + // item we're checking, since those are the ones we are able + // to look in the final `hir::PathSegment` for. Everything else + // would require a deeper search into the `qpath` than I think + // is worthwhile. + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; + } + } + hir::ExprKind::MethodCall(segment, receiver, args, ..) => { + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.blame_specific_arg_if_possible( + error, + def_id, + param, + hir_id, + segment.ident.span, + Some(receiver), + args, + ) { + return true; + } + } + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) + { + return true; + } + } + hir::ExprKind::Struct(qpath, fields, ..) => { + if let Res::Def( + hir::def::DefKind::Struct | hir::def::DefKind::Variant, + variant_def_id, + ) = self.typeck_results.borrow().qpath_res(qpath, hir_id) + { + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + { + if let Some(param) = param { + let refined_expr = self.point_at_field_if_possible( + def_id, + param, + variant_def_id, + fields, + ); + + match refined_expr { + None => {} + Some((refined_expr, _)) => { + error.obligation.cause.span = refined_expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(refined_expr.span); + return true; + } + } + } + } + } + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; + } + } + _ => {} + } + + false + } + + fn point_at_path_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param: ty::GenericArg<'tcx>, + qpath: &hir::QPath<'tcx>, + ) -> bool { + match qpath { + hir::QPath::Resolved(_, path) => { + if let Some(segment) = path.segments.last() + && self.point_at_generic_if_possible(error, def_id, param, segment) + { + return true; + } + } + hir::QPath::TypeRelative(_, segment) => { + if self.point_at_generic_if_possible(error, def_id, param, segment) { + return true; + } + } + _ => {} + } + + false + } + + fn point_at_generic_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + segment: &hir::PathSegment<'tcx>, + ) -> bool { + let own_substs = self + .tcx + .generics_of(def_id) + .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; }; + error.obligation.cause.span = arg + .span() + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(arg.span()); + true + } + + fn find_ambiguous_parameter_in<T: TypeVisitable<TyCtxt<'tcx>>>( + &self, + item_def_id: DefId, + t: T, + ) -> Option<ty::GenericArg<'tcx>> { + struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId); + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindAmbiguousParameter<'_, 'tcx> { + type BreakTy = ty::GenericArg<'tcx>; + fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> { + if let Some(origin) = self.0.type_var_origin(ty) + && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) = + origin.kind + && let generics = self.0.tcx.generics_of(self.1) + && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) + && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1) + .get(index as usize) + { + ControlFlow::Break(*subst) + } else { + ty.super_visit_with(self) + } + } + } + t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value() + } + + fn closure_span_overlaps_error( + &self, + error: &traits::FulfillmentError<'tcx>, + span: Span, + ) -> bool { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), + ) = error.code + && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() + && span.overlaps(self.tcx.def_span(*def_id)) + { + true + } else { + false + } + } + + fn point_at_field_if_possible( + &self, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + variant_def_id: DefId, + expr_fields: &[hir::ExprField<'tcx>], + ) -> Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)> { + let def = self.tcx.adt_def(def_id); + + let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id); + let fields_referencing_param: Vec<_> = def + .variant_with_id(variant_def_id) + .fields + .iter() + .filter(|field| { + let field_ty = field.ty(self.tcx, identity_substs); + Self::find_param_in_ty(field_ty.into(), param_to_point_at) + }) + .collect(); + + if let [field] = fields_referencing_param.as_slice() { + for expr_field in expr_fields { + // Look for the ExprField that matches the field, using the + // same rules that check_expr_struct uses for macro hygiene. + if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx) + { + return Some((expr_field.expr, self.tcx.type_of(field.did).subst_identity())); + } + } + } + + None + } + + /// - `blame_specific_*` means that the function will recursively traverse the expression, + /// looking for the most-specific-possible span to blame. + /// + /// - `point_at_*` means that the function will only go "one level", pointing at the specific + /// expression mentioned. + /// + /// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside + /// the provided function call expression, and mark it as responsible for the fullfillment + /// error. + fn blame_specific_arg_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + call_hir_id: hir::HirId, + callee_span: Span, + receiver: Option<&'tcx hir::Expr<'tcx>>, + args: &'tcx [hir::Expr<'tcx>], + ) -> bool { + let ty = self.tcx.type_of(def_id).subst_identity(); + if !ty.is_fn() { + return false; + } + let sig = ty.fn_sig(self.tcx).skip_binder(); + let args_referencing_param: Vec<_> = sig + .inputs() + .iter() + .enumerate() + .filter(|(_, ty)| Self::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() + && let Some(arg) = receiver + .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) { + + error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span); + + if let hir::Node::Expr(arg_expr) = self.tcx.hir().get(arg.hir_id) { + // This is more specific than pointing at the entire argument. + self.blame_specific_expr_if_possible(error, arg_expr) + } + + error.obligation.cause.map_code(|parent_code| { + ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id: arg.hir_id, + call_hir_id, + parent_code, + } + }); + return true; + } else if args_referencing_param.len() > 0 { + // If more than one argument applies, then point to the callee span at least... + // We have chance to fix this up further in `point_at_generics_if_possible` + error.obligation.cause.span = callee_span; + } + + false + } + + /** + * Recursively searches for the most-specific blamable expression. + * For example, if you have a chain of constraints like: + * - want `Vec<i32>: Copy` + * - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>` + * - because `(Option<Vec<i32>, bool)` needs `Option<Vec<i32>>: Copy` because `impl <A: Copy, B: Copy> Copy for (A, B)` + * then if you pass in `(Some(vec![1, 2, 3]), false)`, this helper `point_at_specific_expr_if_possible` + * will find the expression `vec![1, 2, 3]` as the "most blameable" reason for this missing constraint. + * + * This function only updates the error span. + */ + pub fn blame_specific_expr_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) { + // Whether it succeeded or failed, it likely made some amount of progress. + // In the very worst case, it's just the same `expr` we originally passed in. + let expr = match self.blame_specific_expr_if_possible_for_obligation_cause_code( + &error.obligation.cause.code(), + expr, + ) { + Ok(expr) => expr, + Err(expr) => expr, + }; + + // Either way, use this expression to update the error span. + // If it doesn't overlap the existing span at all, use the original span. + // FIXME: It would possibly be better to do this more continuously, at each level... + error.obligation.cause.span = expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(error.obligation.cause.span); + } + + fn blame_specific_expr_if_possible_for_obligation_cause_code( + &self, + obligation_cause_code: &traits::ObligationCauseCode<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { + match obligation_cause_code { + traits::ObligationCauseCode::ExprBindingObligation(_, _, _, _) => { + // This is the "root"; we assume that the `expr` is already pointing here. + // Therefore, we return `Ok` so that this `expr` can be refined further. + Ok(expr) + } + traits::ObligationCauseCode::ImplDerivedObligation(impl_derived) => self + .blame_specific_expr_if_possible_for_derived_predicate_obligation( + impl_derived, + expr, + ), + _ => { + // We don't recognize this kind of constraint, so we cannot refine the expression + // any further. + Err(expr) + } + } + } + + /// We want to achieve the error span in the following example: + /// + /// ```ignore (just for demonstration) + /// struct Burrito<Filling> { + /// filling: Filling, + /// } + /// impl <Filling: Delicious> Delicious for Burrito<Filling> {} + /// fn eat_delicious_food<Food: Delicious>(_food: Food) {} + /// + /// fn will_type_error() { + /// eat_delicious_food(Burrito { filling: Kale }); + /// } // ^--- The trait bound `Kale: Delicious` + /// // is not satisfied + /// ``` + /// + /// Without calling this function, the error span will cover the entire argument expression. + /// + /// Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent + /// 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 + /// only a partial success - but it cannot be refined even further. + fn blame_specific_expr_if_possible_for_derived_predicate_obligation( + &self, + obligation: &traits::ImplDerivedObligationCause<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { + // First, we attempt to refine the `expr` for our span using the parent obligation. + // If this cannot be done, then we are already stuck, so we stop early (hence the use + // of the `?` try operator here). + let expr = self.blame_specific_expr_if_possible_for_obligation_cause_code( + &*obligation.derived.parent_code, + expr, + )?; + + // This is the "trait" (meaning, the predicate "proved" by this `impl`) which provides the `Self` type we care about. + // For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of + // that struct type. + let impl_trait_self_ref = if self.tcx.is_trait_alias(obligation.impl_or_alias_def_id) { + self.tcx.mk_trait_ref( + obligation.impl_or_alias_def_id, + ty::InternalSubsts::identity_for_item(self.tcx, obligation.impl_or_alias_def_id), + ) + } else { + self.tcx + .impl_trait_ref(obligation.impl_or_alias_def_id) + .map(|impl_def| impl_def.skip_binder()) + // It is possible that this is absent. In this case, we make no progress. + .ok_or(expr)? + }; + + // We only really care about the `Self` type itself, which we extract from the ref. + let impl_self_ty: Ty<'tcx> = impl_trait_self_ref.self_ty(); + + let impl_predicates: ty::GenericPredicates<'tcx> = + self.tcx.predicates_of(obligation.impl_or_alias_def_id); + let Some(impl_predicate_index) = obligation.impl_def_predicate_index else { + // We don't have the index, so we can only guess. + return Err(expr); + }; + + if impl_predicate_index >= impl_predicates.predicates.len() { + // This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things. + return Err(expr); + } + let relevant_broken_predicate: ty::PredicateKind<'tcx> = + impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder(); + + match relevant_broken_predicate { + ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => { + // ... + self.blame_specific_part_of_expr_corresponding_to_generic_param( + broken_trait.trait_ref.self_ty().into(), + expr, + impl_self_ty.into(), + ) + } + _ => Err(expr), + } + } + + /// Drills into `expr` to arrive at the equivalent location of `find_generic_param` in `in_ty`. + /// For example, given + /// - expr: `(Some(vec![1, 2, 3]), false)` + /// - param: `T` + /// - 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`), + /// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to + /// `foo()` and then return `Err("foo()")`. + /// + /// This means that you can (and should) use the `?` try operator to chain multiple calls to this + /// function with different types, since you can only continue drilling the second time if you + /// succeeded the first time. + fn blame_specific_part_of_expr_corresponding_to_generic_param( + &self, + param: ty::GenericArg<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + in_ty: ty::GenericArg<'tcx>, + ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { + if param == in_ty { + // The types match exactly, so we have drilled as far as we can. + return Ok(expr); + } + + let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else { + return Err(expr); + }; + + if let ( + hir::ExprKind::AddrOf(_borrow_kind, _borrow_mutability, borrowed_expr), + ty::Ref(_ty_region, ty_ref_type, _ty_mutability), + ) = (&expr.kind, in_ty.kind()) + { + // We can "drill into" the borrowed expression. + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + borrowed_expr, + (*ty_ref_type).into(), + ); + } + + if let (hir::ExprKind::Tup(expr_elements), ty::Tuple(in_ty_elements)) = + (&expr.kind, in_ty.kind()) + { + if in_ty_elements.len() != expr_elements.len() { + return Err(expr); + } + // 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) + })) else { + // The param is not mentioned, or it is mentioned in multiple indexes. + return Err(expr); + }; + + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + drill_expr, + drill_ty.into(), + ); + } + + if let ( + hir::ExprKind::Struct(expr_struct_path, expr_struct_fields, _expr_struct_rest), + ty::Adt(in_ty_adt, in_ty_adt_generic_args), + ) = (&expr.kind, in_ty.kind()) + { + // First, confirm that this struct is the same one as in the types, and if so, + // find the right variant. + let Res::Def(expr_struct_def_kind, expr_struct_def_id) = self.typeck_results.borrow().qpath_res(expr_struct_path, expr.hir_id) else { + return Err(expr); + }; + + let variant_def_id = match expr_struct_def_kind { + hir::def::DefKind::Struct => { + if in_ty_adt.did() != expr_struct_def_id { + // FIXME: Deal with type aliases? + return Err(expr); + } + expr_struct_def_id + } + hir::def::DefKind::Variant => { + // If this is a variant, its parent is the type definition. + if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) { + // FIXME: Deal with type aliases? + return Err(expr); + } + expr_struct_def_id + } + _ => { + return Err(expr); + } + }; + + // 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( + in_ty_adt_generic_args.iter().enumerate().filter( + |(_index, in_ty_generic)| { + Self::find_param_in_ty(*in_ty_generic, param) + }, + ), + ) else { + return Err(expr); + }; + + let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did()); + if drill_generic_index >= struct_generic_parameters.params.len() { + return Err(expr); + } + + let param_to_point_at_in_struct = self.tcx.mk_param_from_def( + struct_generic_parameters.param_at(drill_generic_index, self.tcx), + ); + + // We make 3 steps: + // Suppose we have a type like + // ```ignore (just for demonstration) + // struct ExampleStruct<T> { + // enabled: bool, + // item: Option<(usize, T, bool)>, + // } + // + // f(ExampleStruct { + // enabled: false, + // item: Some((0, Box::new(String::new()), 1) }, true)), + // }); + // ``` + // Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants + // for `String: Copy`, which isn't true here. + // + // (1) First, we drill into `.item` and highlight that expression + // (2) Then we use the template type `Option<(usize, T, bool)>` to + // drill into the `T`, arriving at a `Box<String>` expression. + // (3) Then we keep going, drilling into this expression using our + // outer contextual information. + + // (1) Find the (unique) field which mentions the type in our constraint: + let (field_expr, field_type) = self + .point_at_field_if_possible( + in_ty_adt.did(), + param_to_point_at_in_struct, + variant_def_id, + expr_struct_fields, + ) + .ok_or(expr)?; + + // (2) Continue drilling into the struct, ignoring the struct's + // generic argument types. + let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param( + param_to_point_at_in_struct, + field_expr, + field_type.into(), + )?; + + // (3) Continue drilling into the expression, having "passed + // through" the struct entirely. + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + expr, + generic_argument_type, + ); + } + + if let ( + hir::ExprKind::Call(expr_callee, expr_args), + ty::Adt(in_ty_adt, in_ty_adt_generic_args), + ) = (&expr.kind, in_ty.kind()) + { + let hir::ExprKind::Path(expr_callee_path) = &expr_callee.kind else { + // FIXME: This case overlaps with another one worth handling, + // which should happen above since it applies to non-ADTs: + // we can drill down into regular generic functions. + return Err(expr); + }; + // This is (possibly) a constructor call, like `Some(...)` or `MyStruct(a, b, c)`. + + let Res::Def(expr_struct_def_kind, expr_ctor_def_id) = self.typeck_results.borrow().qpath_res(expr_callee_path, expr_callee.hir_id) else { + return Err(expr); + }; + + let variant_def_id = match expr_struct_def_kind { + hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => { + if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) { + // FIXME: Deal with type aliases? + return Err(expr); + } + self.tcx.parent(expr_ctor_def_id) + } + hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => { + // For a typical enum like + // `enum Blah<T> { Variant(T) }` + // we get the following resolutions: + // - expr_ctor_def_id ::: DefId(0:29 ~ source_file[b442]::Blah::Variant::{constructor#0}) + // - self.tcx.parent(expr_ctor_def_id) ::: DefId(0:28 ~ source_file[b442]::Blah::Variant) + // - self.tcx.parent(self.tcx.parent(expr_ctor_def_id)) ::: DefId(0:26 ~ source_file[b442]::Blah) + + // Therefore, we need to go up once to obtain the variant and up twice to obtain the type. + // Note that this pattern still holds even when we `use` a variant or `use` an enum type to rename it, or chain `use` expressions + // together; this resolution is handled automatically by `qpath_res`. + + // FIXME: Deal with type aliases? + if in_ty_adt.did() == self.tcx.parent(self.tcx.parent(expr_ctor_def_id)) { + // The constructor definition refers to the "constructor" of the variant: + // For example, `Some(5)` triggers this case. + self.tcx.parent(expr_ctor_def_id) + } else { + // FIXME: Deal with type aliases? + return Err(expr); + } + } + _ => { + return Err(expr); + } + }; + + // 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( + in_ty_adt_generic_args.iter().enumerate().filter( + |(_index, in_ty_generic)| { + Self::find_param_in_ty(*in_ty_generic, param) + }, + ), + ) else { + return Err(expr); + }; + + let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did()); + if drill_generic_index >= struct_generic_parameters.params.len() { + return Err(expr); + } + + let param_to_point_at_in_struct = self.tcx.mk_param_from_def( + struct_generic_parameters.param_at(drill_generic_index, self.tcx), + ); + + // We make 3 steps: + // Suppose we have a type like + // ```ignore (just for demonstration) + // struct ExampleStruct<T> { + // enabled: bool, + // item: Option<(usize, T, bool)>, + // } + // + // f(ExampleStruct { + // enabled: false, + // item: Some((0, Box::new(String::new()), 1) }, true)), + // }); + // ``` + // Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants + // for `String: Copy`, which isn't true here. + // + // (1) First, we drill into `.item` and highlight that expression + // (2) Then we use the template type `Option<(usize, T, bool)>` to + // drill into the `T`, arriving at a `Box<String>` expression. + // (3) Then we keep going, drilling into this expression using our + // 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( + 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)) + ) else { + return Err(expr); + }; + + if field_index >= expr_args.len() { + return Err(expr); + } + + // (2) Continue drilling into the struct, ignoring the struct's + // generic argument types. + let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param( + param_to_point_at_in_struct, + &expr_args[field_index], + field_type.into(), + )?; + + // (3) Continue drilling into the expression, having "passed + // through" the struct entirely. + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + expr, + generic_argument_type, + ); + } + + // At this point, none of the basic patterns matched. + // One major possibility which remains is that we have a function call. + // In this case, it's often possible to dive deeper into the call to find something to blame, + // but this is not always possible. + + 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() + && let ty::Alias(ty::Projection, ..) = ty.kind() + { + // This logic may seem a bit strange, but typically when + // we have a projection type in a function signature, the + // argument that's being passed into that signature is + // not actually constraining that projection's substs in + // a meaningful way. So we skip it, and see improvements + // in some UI tests. + walk.skip_current_subtree(); + } + } + 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, + } + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2d841d53f..a46bdeb41 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -10,7 +10,9 @@ use crate::{ }; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; +use rustc_errors::{ + pluralize, Applicability, Diagnostic, DiagnosticId, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -25,8 +27,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; use rustc_span::{self, sym, Span}; @@ -34,8 +36,6 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; -use std::ops::ControlFlow; -use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { @@ -73,13 +73,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); let ty = self.resolve_vars_if_possible(ty); if ty.has_non_region_infer() { - self.tcx.ty_error() + self.tcx.ty_error_misc() } else { self.tcx.erase_regions(ty) } }; InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty) - .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id)); + .check_asm(asm, enclosing_id); } } @@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let err_inputs = match tuple_arguments { DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs)], + TupleArguments => vec![self.tcx.mk_tup(&err_inputs)], }; self.check_argument_types( @@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, method.ok().map(|method| method.def_id), ); - return self.tcx.ty_error(); + return self.tcx.ty_error_misc(); } let method = method.unwrap(); @@ -534,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .typeck_results .borrow() .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| tcx.ty_error()); + .unwrap_or_else(|| tcx.ty_error_misc()); (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) }) .collect(); @@ -641,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() { // Wrap up the N provided arguments starting at this position in a tuple. - let provided_as_tuple = tcx.mk_tup( + let provided_as_tuple = tcx.mk_tup_from_iter( provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()), ); @@ -756,15 +756,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } errors.drain_filter(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; - 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(_)) { - self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); - return true; - } - false - }); + let Error::Invalid( + provided_idx, + expected_idx, + Compatibility::Incompatible(Some(e)), + ) = error else { return false }; + 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(_)) { + self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); + return true; + } + false + }); // We're done if we found errors, but we already emitted them. if errors.is_empty() { @@ -798,6 +803,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { full_call_span, format!("arguments to this {} are incorrect", call_name), ); + if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) = + (callee_ty, &call_expr.kind) + { + // Type that would have accepted this argument if it hadn't been inferred earlier. + // FIXME: We leave an inference variable for now, but it'd be nice to get a more + // specific type to increase the accuracy of the diagnostic. + let expected = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: full_call_span, + }); + self.point_at_expr_source_of_inferred_type( + &mut err, + rcvr, + expected, + callee_ty, + provided_arg_span, + ); + } // Call out where the function is defined self.label_fn_like( &mut err, @@ -847,7 +870,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut suggestion_text = SuggestionText::None; + let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| { + if ty.is_unit() { + "()".to_string() + } else if ty.is_suggestable(tcx, false) { + format!("/* {} */", ty) + } else if let Some(fn_def_id) = fn_def_id + && self.tcx.def_kind(fn_def_id).is_fn_like() + && let self_implicit = + matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize + && let Some(arg) = self.tcx.fn_arg_names(fn_def_id) + .get(expected_idx.as_usize() + self_implicit) + && arg.name != kw::SelfLower + { + format!("/* {} */", arg.name) + } else { + "/* value */".to_string() + } + }; + let mut errors = errors.into_iter().peekable(); + let mut suggestions = vec![]; while let Some(error) = errors.next() { match error { Error::Invalid(provided_idx, expected_idx, compatibility) => { @@ -888,12 +931,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "".to_string() }; labels - .push((provided_span, format!("argument{} unexpected", provided_ty_name))); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; + .push((provided_span, format!("unexpected argument{}", provided_ty_name))); + let mut span = provided_span; + if span.can_be_used_for_suggestions() { + 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); + } + suggestions.push((span, String::new())); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Remove(false), + SuggestionText::Remove(_) => SuggestionText::Remove(true), + _ => SuggestionText::DidYouMean, + }; + } } Error::Missing(expected_idx) => { // If there are multiple missing arguments adjacent to each other, @@ -1078,6 +1133,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + // Incorporate the argument changes in the removal suggestion. + // When a type is *missing*, and the rest are additional, we want to suggest these with a + // multipart suggestion, but in order to do so we need to figure out *where* the arg that + // was provided but had the wrong type should go, because when looking at `expected_idx` + // that is the position in the argument list in the definition, while `provided_idx` will + // not be present. So we have to look at what the *last* provided position was, and point + // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's + // probably a better more involved change we can make to make this work. + // For example, if we have + // ``` + // fn foo(i32, &'static str) {} + // foo((), (), ()); + // ``` + // what should be suggested is + // ``` + // foo(/* i32 */, /* &str */); + // ``` + // which includes the replacement of the first two `()` for the correct type, and the + // removal of the last `()`. + let mut prev = -1; + for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { + // We want to point not at the *current* argument expression index, but rather at the + // 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; + } + let idx = ProvidedIdx::from_usize((prev + 1) as usize); + if let None = provided_idx + && let Some((_, arg_span)) = provided_arg_tys.get(idx) + { + // 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 + // was `fn foo(())`. + let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; + suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx))); + } + } + // If we have less than 5 things to say, it would be useful to call out exactly what's wrong if labels.len() <= 5 { for (span, label) in labels { @@ -1095,7 +1189,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(format!("provide the argument{}", if plural { "s" } else { "" })) } SuggestionText::Remove(plural) => { - Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) + err.multipart_suggestion( + &format!("remove the extra argument{}", if plural { "s" } else { "" }), + suggestions, + Applicability::HasPlaceholders, + ); + None } SuggestionText::Swap => Some("swap these arguments".to_string()), SuggestionText::Reorder => Some("reorder these arguments".to_string()), @@ -1134,20 +1233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Propose a placeholder of the correct type let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - if expected_ty.is_unit() { - "()".to_string() - } else if expected_ty.is_suggestable(tcx, false) { - format!("/* {} */", expected_ty) - } else if let Some(fn_def_id) = fn_def_id - && self.tcx.def_kind(fn_def_id).is_fn_like() - && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize - && let Some(arg) = self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit) - && arg.name != kw::SelfLower - { - format!("/* {} */", arg.name) - } else { - "/* value */".to_string() - } + ty_to_snippet(expected_ty, expected_idx) }; suggestion += &suggestion_text; } @@ -1201,7 +1287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_ty.unwrap_or_else(|| self.next_float_var()) } ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err => tcx.ty_error(), + ast::LitKind::Err => tcx.ty_error_misc(), } } @@ -1209,15 +1295,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, qpath: &QPath<'_>, hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { + ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { let path_span = qpath.span(); let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); let variant = match def { Res::Err => { - self.set_tainted_by_errors( - self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"), - ); - return None; + let guar = + self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(guar); + return Err(guar); } Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() { Some(adt) => { @@ -1245,28 +1331,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check bounds on type arguments used in the path. self.add_required_obligations_for_hir(path_span, did, substs, hir_id); - Some((variant, ty.normalized)) + Ok((variant, ty.normalized)) } else { - match ty.normalized.kind() { - ty::Error(_) => { + Err(match *ty.normalized.kind() { + ty::Error(guar) => { // E0071 might be caused by a spelling error, which will have // already caused an error message and probably a suggestion // elsewhere. Refrain from emitting more unhelpful errors here // (issue #88844). + guar } - _ => { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.normalized.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - } - } - None + _ => struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.normalized.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(), + }) } } @@ -1313,11 +1397,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does the expected pattern type originate from an expression and what is the span? let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) } // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. }; // Type check the pattern. Override if necessary to avoid knock-on errors. @@ -1422,11 +1506,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerce = if blk.targeted_by_break { CoerceMany::new(coerce_to_ty) } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) }; let prev_diverges = self.diverges.get(); @@ -1630,9 +1710,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>, ) { - if ty.references_error() { + if let Err(guar) = ty.error_reported() { // Override the types everywhere with `err()` to avoid knock on errors. - let err = self.tcx.ty_error(); + let err = self.tcx.ty_error(guar); self.write_ty(hir_id, err); self.write_ty(pat.hir_id, err); let local_ty = LocalTy { decl_ty: err, revealed_ty: err }; @@ -1652,7 +1732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); - let ty = self.astconv().res_to_ty(self_ty, path, true); + let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true); (path.res, self.handle_raw_ty(path_span, ty)) } QPath::TypeRelative(ref qself, ref segment) => { @@ -1661,7 +1741,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self .astconv() .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let ty = + result.map(|(ty, _, _)| ty).unwrap_or_else(|guar| self.tcx().ty_error(guar)); let ty = self.handle_raw_ty(path_span, ty); let result = result.map(|(_, kind, def_id)| (kind, def_id)); @@ -1739,353 +1820,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn adjust_fulfillment_error_for_expr_obligation( - &self, - error: &mut traits::FulfillmentError<'tcx>, - ) -> bool { - let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx)) - = *error.obligation.cause.code().peel_derives() else { return false; }; - let hir = self.tcx.hir(); - let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; }; - - let Some(unsubstituted_pred) = - self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) - else { return false; }; - - 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(), - }; - - let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { - predicate_substs.types().find_map(|ty| { - ty.walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Param(param_ty) = ty.kind() - && matches(param_ty) - { - Some(arg) - } else { - None - } - }) - }) - }; - - // 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 - }); - // 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 - }); - // 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); - - // 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 { - fallback_param_to_point_at = None; - self_param_to_point_at = None; - param_to_point_at = - self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); - } - - if self.closure_span_overlaps_error(error, expr.span) { - return false; - } - - match &expr.kind { - hir::ExprKind::Path(qpath) => { - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(callee, args), - hir_id: call_hir_id, - span: call_span, - .. - }) = hir.get_parent(expr.hir_id) - && callee.hir_id == expr.hir_id - { - if self.closure_span_overlaps_error(error, *call_span) { - return false; - } - - for param in - [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() - { - if self.point_at_arg_if_possible( - error, - def_id, - param, - *call_hir_id, - callee.span, - None, - args, - ) - { - return true; - } - } - } - // Notably, we only point to params that are local to the - // item we're checking, since those are the ones we are able - // to look in the final `hir::PathSegment` for. Everything else - // would require a deeper search into the `qpath` than I think - // is worthwhile. - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) - { - return true; - } - } - hir::ExprKind::MethodCall(segment, receiver, args, ..) => { - for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() - { - if self.point_at_arg_if_possible( - error, - def_id, - param, - hir_id, - segment.ident.span, - Some(receiver), - args, - ) { - return true; - } - } - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) - { - return true; - } - } - hir::ExprKind::Struct(qpath, fields, ..) => { - if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = - self.typeck_results.borrow().qpath_res(qpath, hir_id) - { - for param in - [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - { - if let Some(param) = param - && self.point_at_field_if_possible( - error, - def_id, - param, - variant_def_id, - fields, - ) - { - return true; - } - } - } - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) - { - return true; - } - } - _ => {} - } - - false - } - - fn closure_span_overlaps_error( - &self, - error: &traits::FulfillmentError<'tcx>, - span: Span, - ) -> bool { - if let traits::FulfillmentErrorCode::CodeSelectionError( - traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), - ) = error.code - && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() - && span.overlaps(self.tcx.def_span(*def_id)) - { - true - } else { - false - } - } - - fn point_at_arg_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param_to_point_at: ty::GenericArg<'tcx>, - call_hir_id: hir::HirId, - callee_span: Span, - receiver: Option<&'tcx hir::Expr<'tcx>>, - args: &'tcx [hir::Expr<'tcx>], - ) -> bool { - let ty = self.tcx.type_of(def_id); - if !ty.is_fn() { - return false; - } - let sig = ty.fn_sig(self.tcx).skip_binder(); - let args_referencing_param: Vec<_> = sig - .inputs() - .iter() - .enumerate() - .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at)) - .collect(); - // If there's one field that references the given generic, great! - if let [(idx, _)] = args_referencing_param.as_slice() - && let Some(arg) = receiver - .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) { - error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span); - error.obligation.cause.map_code(|parent_code| { - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: arg.hir_id, - call_hir_id, - parent_code, - } - }); - return true; - } else if args_referencing_param.len() > 0 { - // If more than one argument applies, then point to the callee span at least... - // We have chance to fix this up further in `point_at_generics_if_possible` - error.obligation.cause.span = callee_span; - } - - false - } - - fn point_at_field_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param_to_point_at: ty::GenericArg<'tcx>, - variant_def_id: DefId, - expr_fields: &[hir::ExprField<'tcx>], - ) -> bool { - let def = self.tcx.adt_def(def_id); - - let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id); - let fields_referencing_param: Vec<_> = def - .variant_with_id(variant_def_id) - .fields - .iter() - .filter(|field| { - let field_ty = field.ty(self.tcx, identity_substs); - find_param_in_ty(field_ty, param_to_point_at) - }) - .collect(); - - if let [field] = fields_referencing_param.as_slice() { - for expr_field in expr_fields { - // Look for the ExprField that matches the field, using the - // same rules that check_expr_struct uses for macro hygiene. - if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx) - { - error.obligation.cause.span = expr_field - .expr - .span - .find_ancestor_in_same_ctxt(error.obligation.cause.span) - .unwrap_or(expr_field.span); - return true; - } - } - } - - false - } - - fn point_at_path_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param: ty::GenericArg<'tcx>, - qpath: &QPath<'tcx>, - ) -> bool { - match qpath { - hir::QPath::Resolved(_, path) => { - if let Some(segment) = path.segments.last() - && self.point_at_generic_if_possible(error, def_id, param, segment) - { - return true; - } - } - hir::QPath::TypeRelative(_, segment) => { - if self.point_at_generic_if_possible(error, def_id, param, segment) { - return true; - } - } - _ => {} - } - - false - } - - fn point_at_generic_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param_to_point_at: ty::GenericArg<'tcx>, - segment: &hir::PathSegment<'tcx>, - ) -> bool { - let own_substs = self - .tcx - .generics_of(def_id) - .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; }; - error.obligation.cause.span = arg - .span() - .find_ancestor_in_same_ctxt(error.obligation.cause.span) - .unwrap_or(arg.span()); - true - } - - fn find_ambiguous_parameter_in<T: TypeVisitable<'tcx>>( - &self, - item_def_id: DefId, - t: T, - ) -> Option<ty::GenericArg<'tcx>> { - struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId); - impl<'tcx> TypeVisitor<'tcx> for FindAmbiguousParameter<'_, 'tcx> { - type BreakTy = ty::GenericArg<'tcx>; - fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> { - if let Some(origin) = self.0.type_var_origin(ty) - && let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) = - origin.kind - && let generics = self.0.tcx.generics_of(self.1) - && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) - && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1) - .get(index as usize) - { - ControlFlow::Break(*subst) - } else { - ty.super_visit_with(self) - } - } - } - t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value() - } - fn label_fn_like( &self, err: &mut Diagnostic, @@ -2114,7 +1848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *callee_ty.kind() { ty::Param(param) => { let param = - self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); + self.tcx.generics_of(self.body_id).type_param(¶m, self.tcx); if param.kind.is_synthetic() { // if it's `impl Fn() -> ..` then just fall down to the def-id based logic def_id = param.def_id; @@ -2123,7 +1857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // and point at that. let instantiated = self .tcx - .explicit_predicates_of(self.body_id.owner) + .explicit_predicates_of(self.body_id) .instantiate_identity(self.tcx); // FIXME(compiler-errors): This could be problematic if something has two // fn-like predicates with different args, but callable types really never @@ -2200,8 +1934,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { spans.push_span_label(param.span, ""); } - let def_kind = self.tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + err.span_note(spans, &format!("{} defined here", self.tcx.def_descr(def_id))); } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id) && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind { @@ -2214,31 +1947,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_note(span, &format!("{} defined here", kind)); } else { - let def_kind = self.tcx.def_kind(def_id); err.span_note( self.tcx.def_span(def_id), - &format!("{} defined here", def_kind.descr(def_id)), + &format!("{} defined here", self.tcx.def_descr(def_id)), ); } } } - -fn find_param_in_ty<'tcx>(ty: Ty<'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; - } else 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 - // we have a projection type in a function signature, the - // argument that's being passed into that signature is - // not actually constraining that projection's substs in - // a meaningful way. So we skip it, and see improvements - // in some UI tests. - walk.skip_current_subtree(); - } - } - false -} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 428fde642..1dea3e6f9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -1,4 +1,5 @@ mod _impl; +mod adjust_fulfillment_errors; mod arg_matrix; mod checks; mod suggestions; @@ -10,14 +11,14 @@ pub use suggestions::*; use crate::coercion::DynamicCoerceMany; use crate::{Diverges, EnclosingBreakables, Inherited}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span, DUMMY_SP}; @@ -38,7 +39,7 @@ use std::ops::Deref; /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt pub struct FnCtxt<'a, 'tcx> { - pub(super) body_id: hir::HirId, + pub(super) body_id: LocalDefId, /// The parameter environment used for proving trait obligations /// in this function. This can change when we descend into @@ -117,7 +118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn new( inh: &'a Inherited<'tcx>, param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, + body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { FnCtxt { body_id, @@ -204,7 +205,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } fn item_def_id(&self) -> DefId { - self.body_id.owner.to_def_id() + self.body_id.to_def_id() } fn get_type_parameter_bounds( @@ -288,7 +289,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { item_segment: &hir::PathSegment<'_>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { - let trait_ref = self.replace_bound_vars_with_fresh_vars( + let trait_ref = self.instantiate_binder_with_fresh_vars( span, infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), poly_trait_ref, @@ -324,6 +325,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty }; self.write_ty(hir_id, ty) } + + fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> { + Some(&self.infcx) + } } /// Represents a user-provided type in the raw form (never normalized). diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 4d673ac91..c49621b7c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,6 +1,7 @@ use super::FnCtxt; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; @@ -13,9 +14,10 @@ use rustc_hir::{ use rustc_hir_analysis::astconv::AstConv; 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, - TypeVisitable, + TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -31,7 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results .borrow() .liberated_fn_sigs() - .get(self.tcx.hir().parent_id(self.body_id)) + .get(self.tcx.hir().local_def_id_to_hir_id(self.body_id)) .copied() } @@ -119,7 +121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), - kind => format!("call this {}", kind.descr(def_id)), + kind => format!("call this {}", self.tcx.def_kind_descr(kind, def_id)), }, DefIdOrName::Name(name) => format!("call this {name}"), }; @@ -164,7 +166,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { - self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty) + 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) } pub fn suggest_two_fn_call( @@ -337,7 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { CtorOf::Variant => "an enum variant", })); } else { - let descr = kind.descr(def_id); + let descr = self.tcx.def_kind_descr(kind, def_id); err.span_label(sp, format!("{descr} `{name}` defined here")); } return true; @@ -413,11 +416,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(adt, _) = peeled.kind() && Some(adt.did()) == self.tcx.lang_items().string() { + let sugg = if ref_cnt == 0 { + ".as_deref()" + } else { + ".map(|x| x.as_str())" + }; err.span_suggestion_verbose( expr.span.shrink_to_hi(), - "try converting the passed type into a `&str`", - format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), - Applicability::MaybeIncorrect, + fluent::hir_typeck_convert_to_str, + sugg, + Applicability::MachineApplicable, ); return true; } @@ -681,7 +689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { - if found.is_suggestable(self.tcx, false) { + if let Some(found) = found.make_suggestable(self.tcx, false) { err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); return true; } else if let ty::Closure(_, substs) = found.kind() @@ -698,10 +706,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } 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 { + // 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); + if found.is_suggestable(self.tcx, false) { + if term_ty.span.is_empty() { + err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); + return true; + } else { + err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); + } + } + } + // 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 span = ty.span; 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); @@ -980,7 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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).is_ok() + && 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) { @@ -1019,7 +1055,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) && adt_def.did() == result_did // Check that the error types are equal - && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() + && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)) { return suggest_copied_or_cloned(); } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) @@ -1238,6 +1274,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they + /// pass in a literal 0 to an raw pointer. + #[instrument(skip(self, err))] + pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected_ty: Ty<'tcx>, + ) -> bool { + // Expected type needs to be a raw pointer. + let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else { + return false; + }; + + // Provided expression needs to be a literal `0`. + let ExprKind::Lit(Spanned { + node: rustc_ast::LitKind::Int(0, _), + span, + }) = expr.kind else { + return false; + }; + + // We need to find a null pointer symbol to suggest + let null_sym = match mutbl { + hir::Mutability::Not => sym::ptr_null, + hir::Mutability::Mut => sym::ptr_null_mut, + }; + let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else { + return false; + }; + let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did)); + + // We have satisfied all requirements to provide a suggestion. Emit it. + err.span_suggestion( + span, + format!("if you meant to create a null pointer, use `{null_path_str}()`"), + null_path_str + "()", + Applicability::MachineApplicable, + ); + + true + } + pub(crate) fn suggest_associated_const( &self, err: &mut Diagnostic, @@ -1258,16 +1337,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Path { segments: [segment], .. }, )) | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => { - let self_ty = self.astconv().ast_ty_to_ty(ty); - if let Ok(pick) = self.probe_for_name( - Mode::Path, - Ident::new(capitalized_name, segment.ident.span), - Some(expected_ty), - IsSuggestion(true), - self_ty, - expr.hir_id, - ProbeScope::TraitsInScope, - ) { + if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id) + && let Ok(pick) = self.probe_for_name( + Mode::Path, + Ident::new(capitalized_name, segment.ident.span), + Some(expected_ty), + IsSuggestion(true), + self_ty, + expr.hir_id, + ProbeScope::TraitsInScope, + ) + { (pick.item, segment) } else { return false; @@ -1299,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Same item return false; } - let item_ty = self.tcx.type_of(item.def_id); + let item_ty = self.tcx.type_of(item.def_id).subst_identity(); // FIXME(compiler-errors): This check is *so* rudimentary if item_ty.needs_subst() { return false; @@ -1379,6 +1459,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { generics, diag, vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(), + None, ); } else { self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]); diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 15dd3412c..38445f284 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -5,6 +5,7 @@ use rustc_hir::PatKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::Ty; use rustc_middle::ty::UserType; +use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_trait_selection::traits; @@ -156,7 +157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { _: &'tcx hir::FnDecl<'tcx>, _: hir::BodyId, _: Span, - _: hir::HirId, + _: LocalDefId, ) { } } 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 b3dd3031d..7c0402b1c 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 @@ -12,7 +12,7 @@ use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::{ hir::map::Map, - ty::{ParamEnv, TyCtxt, TypeVisitable, TypeckResults}, + ty::{ParamEnv, TyCtxt, TypeVisitableExt, TypeckResults}, }; use std::mem::swap; @@ -219,7 +219,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | ExprKind::Struct(..) | ExprKind::Repeat(..) | ExprKind::Yield(..) - | ExprKind::Err => (), + | ExprKind::Err(_) => (), } } @@ -483,7 +483,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { | ExprKind::Closure { .. } | ExprKind::ConstBlock(..) | ExprKind::DropTemps(..) - | ExprKind::Err + | ExprKind::Err(_) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::InlineAsm(..) diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index ed3d89031..fa3887362 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_middle::{ hir::place::{PlaceBase, Projection, ProjectionKind}, - ty::TypeVisitable, + ty::TypeVisitableExt, }; pub(super) fn find_consumed_and_borrowed<'a, 'tcx>( diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index 7af526053..2e41c2041 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -16,7 +16,7 @@ use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; use rustc_infer::infer::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, TypeVisitable}; +use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Span; use smallvec::{smallvec, SmallVec}; @@ -271,15 +271,13 @@ pub fn resolve_interior<'a, 'tcx>( }, _ => mk_bound_region(None), }; - let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); + let r = fcx.tcx.mk_re_late_bound(current_depth, br); r }); - if captured_tys.insert(ty) { + captured_tys.insert(ty).then(|| { cause.ty = ty; - Some(cause) - } else { - None - } + cause + }) }) .collect(); @@ -302,7 +300,7 @@ pub fn resolve_interior<'a, 'tcx>( let var = ty::BoundVar::from_usize(bound_vars.len()); bound_vars.push(ty::BoundVariableKind::Region(kind)); counter += 1; - fcx.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { var, kind })) + fcx.tcx.mk_re_late_bound(ty::INNERMOST, ty::BoundRegion { var, kind }) }, types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"), consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"), @@ -313,8 +311,8 @@ pub fn resolve_interior<'a, 'tcx>( }; // Extract type components to build the witness type. - let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); - let bound_vars = fcx.tcx.mk_bound_variable_kinds(bound_vars.iter()); + 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())); @@ -364,7 +362,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { let ty = tcx.mk_ref( // Use `ReErased` as `resolve_interior` is going to replace all the // regions anyway. - tcx.mk_region(ty::ReErased), + tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, ); self.interior_visitor.record( @@ -647,7 +645,8 @@ fn check_must_not_suspend_ty<'tcx>( hir_id, SuspendCheckData { descr_pre, - plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize + plural_len: len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0) + as usize + 1, ..data }, diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index b33e7b8d6..26020382d 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -1,16 +1,17 @@ use super::callee::DeferredCallResolution; -use rustc_data_structures::fx::FxHashSet; +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::TypeVisitable; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _}; use std::cell::RefCell; use std::ops::Deref; @@ -55,7 +56,7 @@ pub struct Inherited<'tcx> { pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>, pub(super) deferred_generator_interiors: - RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, + RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, pub(super) body_id: Option<hir::BodyId>, @@ -63,6 +64,8 @@ pub struct Inherited<'tcx> { /// we record that type variable here. This is later used to inform /// fallback. See the `fallback` module for details. pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>, + + pub(super) infer_var_info: RefCell<FxHashMap<ty::TyVid, ty::InferVarInfo>>, } impl<'tcx> Deref for Inherited<'tcx> { @@ -128,6 +131,7 @@ impl<'tcx> Inherited<'tcx> { deferred_generator_interiors: RefCell::new(Vec::new()), diverging_type_vars: RefCell::new(Default::default()), body_id, + infer_var_info: RefCell::new(Default::default()), } } @@ -136,6 +140,9 @@ impl<'tcx> Inherited<'tcx> { if obligation.has_escaping_bound_vars() { span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); } + + self.update_infer_var_info(&obligation); + self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); } @@ -152,4 +159,43 @@ impl<'tcx> Inherited<'tcx> { self.register_predicates(infer_ok.obligations); infer_ok.value } + + pub fn update_infer_var_info(&self, obligation: &PredicateObligation<'tcx>) { + let infer_var_info = &mut self.infer_var_info.borrow_mut(); + + // (*) binder skipped + if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder() + && let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) + && self.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id) + { + let new_self_ty = self.tcx.types.unit; + + // Then construct a new obligation with Self = () added + // to the ParamEnv, and see if it holds. + let o = obligation.with(self.tcx, + obligation + .predicate + .kind() + .rebind( + // (*) binder moved here + ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_ty(self.tcx, new_self_ty))) + ), + ); + // Don't report overflow errors. Otherwise equivalent to may_hold. + if let Ok(result) = self.probe(|_| self.evaluate_obligation(&o)) && result.may_apply() { + infer_var_info.entry(ty).or_default().self_in_trait = true; + } + } + + if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) = + obligation.predicate.kind().skip_binder() + { + // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, + // we need to make it into one. + if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) { + debug!("infer_var_info: {:?}.output = true", vid); + infer_var_info.entry(vid).or_default().output = true; + } + } + } } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 3c873024c..19d2befc4 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -3,7 +3,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_target::abi::{Pointer, VariantIdx}; use super::FnCtxt; @@ -38,6 +38,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { let tcx = self.tcx; + let dl = &tcx.data_layout; let span = tcx.hir().span(hir_id); let normalize = |ty| { let ty = self.resolve_vars_if_possible(ty); @@ -69,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Special-case transmuting from `typeof(function)` and // `Option<typeof(function)>` to present a clearer error. let from = unpack_option_like(tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&tcx) { + if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer(dl.instruction_address_space).size(&tcx) { struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type") .note(&format!("source type: {from}")) .note(&format!("target type: {to}")) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 7ddf9eaa4..e397dfd45 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -5,6 +5,7 @@ #![feature(min_specialization)] #![feature(control_flow_enum)] #![feature(drain_filter)] +#![feature(option_as_slice)] #![allow(rustc::potential_query_instability)] #![recursion_limit = "256"] @@ -53,7 +54,10 @@ use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + struct_span_err, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, MultiSpan, + SubdiagnosticMessage, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; @@ -61,13 +65,16 @@ use rustc_hir::{HirIdMap, Node}; use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::check::check_abi; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_macros::fluent_messages; use rustc_middle::traits; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config; use rustc_session::Session; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::Span; +use rustc_span::{sym, Span}; + +fluent_messages! { "../locales/en-US.ftl" } #[macro_export] macro_rules! type_error_struct { @@ -154,7 +161,7 @@ fn typeck_const_arg<'tcx>( tcx: TyCtxt<'tcx>, (did, param_did): (LocalDefId, DefId), ) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did); + let fallback = move || tcx.type_of(param_did).subst_identity(); typeck_with_fallback(tcx, did, fallback) } @@ -162,7 +169,7 @@ fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tc if let Some(param_did) = tcx.opt_const_param_of(def_id) { tcx.typeck_const_arg((def_id, param_did)) } else { - let fallback = move || tcx.type_of(def_id.to_def_id()); + let fallback = move || tcx.type_of(def_id.to_def_id()).subst_identity(); typeck_with_fallback(tcx, def_id, fallback) } } @@ -201,13 +208,18 @@ fn typeck_with_fallback<'tcx>( let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id); - let mut fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { + param_env.without_const() + } else { + param_env + }; + 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) + tcx.fn_sig(def_id).subst_identity() }; check_abi(tcx, id, span, fn_sig.abi()); @@ -294,14 +306,24 @@ fn typeck_with_fallback<'tcx>( // 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()); - fcx.resolve_generator_interiors(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_all_obligations_or_error(); + fcx.select_obligations_where_possible(|_| {}); + + 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()); + + 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.check_transmutes(); diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 48c75cde9..4d3969d28 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -51,8 +51,7 @@ use rustc_middle::hir::place::*; use rustc_middle::ty::adjustment; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; @@ -127,7 +126,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { fn resolve_vars_if_possible<T>(&self, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.infcx.resolve_vars_if_possible(value) } @@ -155,8 +154,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { None if self.is_tainted_by_errors() => Err(()), None => { bug!( - "no type for node {}: {} in mem_categorization", - id, + "no type for node {} in mem_categorization", self.tcx().hir().node_to_string(id) ); } @@ -385,7 +383,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) | hir::ExprKind::Box(..) - | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), + | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), } } @@ -603,7 +601,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - // FIXME(#19596) This is a workaround, but there should be a better way to do this fn cat_pattern_<F>( &self, mut place_with_id: PlaceWithHirId<'tcx>, @@ -639,7 +636,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // `&&Some(x,)` `place_foo` // `&Some(x,)` `deref { place_foo}` // `Some(x,)` `deref { deref { place_foo }}` - // (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place + // `(x,)` `field0 { deref { deref { place_foo }}}` <- resulting place // // The above example has no adjustments. If the code were instead the (after adjustments, // equivalent) version diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 372ea30eb..169f128e0 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{self, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, Ty}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use rustc_middle::ty::{InternalSubsts, UserSubsts, UserType}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits; @@ -262,7 +262,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let original_poly_trait_ref = principal.with_self_ty(this.tcx, object_ty); let upcast_poly_trait_ref = this.upcast(original_poly_trait_ref, trait_def_id); let upcast_trait_ref = - this.replace_bound_vars_with_fresh_vars(upcast_poly_trait_ref); + this.instantiate_binder_with_fresh_vars(upcast_poly_trait_ref); debug!( "original_poly_trait_ref={:?} upcast_trait_ref={:?} target_trait={:?}", original_poly_trait_ref, upcast_trait_ref, trait_def_id @@ -285,7 +285,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { probe::WhereClausePick(poly_trait_ref) => { // Where clauses can have bound regions in them. We need to instantiate // those to convert from a poly-trait-ref to a trait-ref. - self.replace_bound_vars_with_fresh_vars(poly_trait_ref).substs + self.instantiate_binder_with_fresh_vars(poly_trait_ref).substs } } } @@ -384,7 +384,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { let tcx = self.cfcx.tcx(); - self.cfcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into() + self.cfcx + .ct_infer( + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + Some(param), + inf.span, + ) + .into() } _ => unreachable!(), } @@ -503,12 +511,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { debug!("method_predicates after subst = {:?}", method_predicates); - let sig = self.tcx.bound_fn_sig(def_id); - - let sig = sig.subst(self.tcx, all_substs); + let sig = self.tcx.fn_sig(def_id).subst(self.tcx, all_substs); debug!("type scheme substituted, sig={:?}", sig); - let sig = self.replace_bound_vars_with_fresh_vars(sig); + let sig = self.instantiate_binder_with_fresh_vars(sig); debug!("late-bound lifetimes from method instantiated, sig={:?}", sig); (sig, method_predicates) @@ -627,10 +633,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { upcast_trait_refs.into_iter().next().unwrap() } - fn replace_bound_vars_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T + fn instantiate_binder_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T where - T: TypeFoldable<'tcx> + Copy, + T: TypeFoldable<TyCtxt<'tcx>> + Copy, { - self.fcx.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, value) + self.fcx.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, value) } } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index b810a967a..0456dd56c 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -20,7 +20,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitable}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt}; use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -76,7 +76,7 @@ pub struct NoMatchData<'tcx> { pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, pub out_of_scope_traits: Vec<DefId>, - pub lev_candidate: Option<ty::AssocItem>, + pub similar_candidate: Option<ty::AssocItem>, pub mode: probe::Mode, } @@ -145,7 +145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .map(|pick| { let sig = self.tcx.fn_sig(pick.item.def_id); - sig.inputs().skip_binder().len().saturating_sub(1) + sig.skip_binder().inputs().skip_binder().len().saturating_sub(1) }) .unwrap_or(0); @@ -380,6 +380,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); return None; }; + + if method_item.kind != ty::AssocKind::Fn { + self.tcx.sess.delay_span_bug(tcx.def_span(method_item.def_id), "not a method"); + return None; + } + let def_id = method_item.def_id; let generics = tcx.generics_of(def_id); @@ -399,10 +405,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // N.B., instantiate late-bound regions before normalizing the // function signature so that normalization does not need to deal // with bound regions. - let fn_sig = tcx.bound_fn_sig(def_id); - let fn_sig = fn_sig.subst(self.tcx, substs); + let fn_sig = tcx.fn_sig(def_id).subst(self.tcx, substs); let fn_sig = - self.replace_bound_vars_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig); + self.instantiate_binder_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig); let InferOk { value, obligations: o } = self.at(&obligation.cause, self.param_env).normalize(fn_sig); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a24814313..3bef5cfcd 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,24 +9,23 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +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::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::ToPredicate; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; -use rustc_span::lev_distance::{ - find_best_match_for_name_with_substrings, lev_distance_with_substrings, +use rustc_span::edit_distance::{ + edit_distance_with_substrings, find_best_match_for_name_with_substrings, }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; @@ -70,7 +69,7 @@ struct ProbeContext<'a, 'tcx> { impl_dups: FxHashSet<DefId>, /// When probing for names, include names that are close to the - /// requested name (by Levenshtein distance) + /// requested name (by edit distance) allow_similar_names: bool, /// Some(candidate) if there is a private candidate @@ -461,7 +460,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { static_candidates: Vec::new(), unsatisfied_predicates: Vec::new(), out_of_scope_traits: Vec::new(), - lev_candidate: None, + similar_candidate: None, mode, })); } @@ -508,16 +507,16 @@ fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) - .include_raw_pointers() - .silence_errors(); + let mut autoderef = + Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef .by_ref() .map(|(ty, d)| { let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty), + self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -610,10 +609,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { let is_accessible = if let Some(name) = self.method_name { let item = candidate.item; - let def_scope = self - .tcx - .adjust_ident_and_get_scope(name, item.container_id(self.tcx), self.body_id) - .1; + let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id); + let def_scope = + self.tcx.adjust_ident_and_get_scope(name, item.container_id(self.tcx), hir_id).1; item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) } else { true @@ -736,7 +734,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("impl_ty: {:?}", impl_ty); // Determine the receiver type that the method itself expects. - let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(&item, impl_ty, impl_substs); + let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, impl_ty, impl_substs); debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty); // We can't use normalize_associated_types_in as it will pollute the @@ -797,7 +795,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); + this.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs); this.push_candidate( Candidate { xform_self_ty, @@ -827,6 +825,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) @@ -837,6 +836,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); @@ -845,7 +845,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = this.erase_late_bound_regions(poly_trait_ref); let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs); + 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 @@ -916,31 +916,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn matches_return_type( &self, - method: &ty::AssocItem, + method: ty::AssocItem, self_ty: Option<Ty<'tcx>>, expected: Ty<'tcx>, ) -> bool { match method.kind { - ty::AssocKind::Fn => { - let fty = self.tcx.bound_fn_sig(method.def_id); - self.probe(|_| { - let substs = self.fresh_substs_for_item(self.span, method.def_id); - let fty = fty.subst(self.tcx, substs); - let fty = - self.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, fty); - - if let Some(self_ty) = self_ty { - if self - .at(&ObligationCause::dummy(), self.param_env) - .sup(fty.inputs()[0], self_ty) - .is_err() - { - return false; - } + ty::AssocKind::Fn => self.probe(|_| { + let substs = self.fresh_substs_for_item(self.span, method.def_id); + let fty = self.tcx.fn_sig(method.def_id).subst(self.tcx, substs); + let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty); + + if let Some(self_ty) = self_ty { + if self + .at(&ObligationCause::dummy(), self.param_env) + .sup(fty.inputs()[0], self_ty) + .is_err() + { + return false; } - self.can_sub(self.param_env, fty.output(), expected).is_ok() - }) - } + } + self.can_sub(self.param_env, fty.output(), expected) + }), _ => false, } } @@ -955,24 +951,35 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { - // For trait aliases, assume all supertraits are relevant. - let bounds = iter::once(ty::Binder::dummy(trait_ref)); - self.elaborate_bounds(bounds, |this, new_trait_ref, item| { - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - import_ids: import_ids.clone(), - kind: TraitCandidate(new_trait_ref), - }, - false, - ); - }); + // For trait aliases, recursively assume all explicitly named traits are relevant + for expansion in traits::expand_trait_aliases( + self.tcx, + iter::once((ty::Binder::dummy(trait_ref), self.span)), + ) { + let bound_trait_ref = expansion.trait_ref(); + for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { + if !self.has_applicable_self(&item) { + self.record_static_candidate(CandidateSource::Trait( + bound_trait_ref.def_id(), + )); + } else { + let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref); + + let (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs); + self.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + import_ids: import_ids.clone(), + kind: TraitCandidate(new_trait_ref), + }, + false, + ); + } + } + } } else { debug_assert!(self.tcx.is_trait(trait_def_id)); if self.tcx.trait_is_auto(trait_def_id) { @@ -987,7 +994,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } let (xform_self_ty, xform_ret_ty) = - self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); + self.xform_self_ty(item, trait_ref.self_ty(), trait_substs); self.push_candidate( Candidate { xform_self_ty, @@ -1014,7 +1021,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { - self.matches_return_type(&candidate.item, None, return_ty) + self.matches_return_type(candidate.item, None, return_ty) } else { true } @@ -1076,29 +1083,20 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some((kind, def_id)) = private_candidate { return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits)); } - let lev_candidate = self.probe_for_lev_candidate()?; + let similar_candidate = self.probe_for_similar_candidate()?; Err(MethodError::NoMatch(NoMatchData { static_candidates, unsatisfied_predicates, out_of_scope_traits, - lev_candidate, + similar_candidate, mode: self.mode, })) } fn pick_core(&self) -> Option<PickResult<'tcx>> { - let pick = self.pick_all_method(Some(&mut vec![])); - - // In this case unstable picking is done by `pick_method`. - if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { - return pick; - } - - if pick.is_none() { - return self.pick_all_method(None); - } - pick + // Pick stable methods only first, and consider unstable candidates if not found. + self.pick_all_method(Some(&mut vec![])).or_else(|| self.pick_all_method(None)) } fn pick_all_method( @@ -1237,54 +1235,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }) } - fn pick_method_with_unstable(&self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { - debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); - - let mut possibly_unsatisfied_predicates = Vec::new(); - - for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { - debug!("searching {} candidates", kind); - let res = self.consider_candidates( - self_ty, - candidates, - &mut possibly_unsatisfied_predicates, - Some(&mut vec![]), - ); - if res.is_some() { - return res; - } - } - - for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { - debug!("searching unstable {kind} candidates"); - let res = self.consider_candidates( - self_ty, - candidates, - &mut possibly_unsatisfied_predicates, - None, - ); - if res.is_some() { - return res; - } - } - - self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates); - None - } - fn pick_method( &self, self_ty: Ty<'tcx>, mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { - if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { - return self.pick_method_with_unstable(self_ty); - } - debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); @@ -1358,13 +1313,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return Some(Err(MethodError::Ambiguity(sources))); } - applicable_candidates.pop().map(|(probe, status)| { - if status == ProbeResult::Match { + applicable_candidates.pop().map(|(probe, status)| match status { + ProbeResult::Match => { Ok(probe .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default())) - } else { - Err(MethodError::BadReturnType) } + ProbeResult::NoMatch | ProbeResult::BadReturnType => Err(MethodError::BadReturnType), }) } } @@ -1412,8 +1366,8 @@ impl<'tcx> Pick<'tcx> { span, format!( "{} {} with this name may be added to the standard library in the future", - def_kind.article(), - def_kind.descr(self.item.def_id), + tcx.def_kind_descr_article(def_kind, self.item.def_id), + tcx.def_kind_descr(def_kind, self.item.def_id), ), |lint| { match (self.item.kind, self.item.container) { @@ -1482,7 +1436,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { TraitCandidate(trait_ref) => self.probe(|_| { let _ = self .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) .sup(candidate.xform_self_ty, self_ty); match self.select_trait_candidate(trait_ref) { Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => { @@ -1512,7 +1465,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // First check that the self type can be related. let sub_obligations = match self .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) .sup(probe.xform_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => obligations, @@ -1567,7 +1519,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::ImplDerivedObligation(Box::new( traits::ImplDerivedObligationCause { derived, - impl_def_id, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: None, span, }, )) @@ -1728,7 +1681,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let ProbeResult::Match = result && self .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) .sup(return_ty, xform_ret_ty) .is_err() { @@ -1786,8 +1738,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// Similarly to `probe_for_return_type`, this method attempts to find the best matching /// candidate method where the method name may have been misspelled. Similarly to other - /// Levenshtein based suggestions, we provide at most one such suggestion. - fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> { + /// edit distance based suggestions, we provide at most one such suggestion. + 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(); @@ -1831,6 +1783,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { None, ) } + .or_else(|| { + applicable_close_candidates + .iter() + .find(|cand| self.matches_by_doc_alias(cand.def_id)) + .map(|cand| cand.name) + }) .unwrap(); Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name)) } @@ -1867,7 +1825,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn xform_self_ty( &self, - item: &ty::AssocItem, + item: ty::AssocItem, impl_ty: Ty<'tcx>, substs: SubstsRef<'tcx>, ) -> (Ty<'tcx>, Option<Ty<'tcx>>) { @@ -1881,7 +1839,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> { - let fn_sig = self.tcx.bound_fn_sig(method); + let fn_sig = self.tcx.fn_sig(method); debug!(?fn_sig); assert!(!substs.has_escaping_bound_vars()); @@ -1924,27 +1882,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, impl_def_id: DefId, ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) { - (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) - } - - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var(self.tcx.type_of(param.def_id), origin).into() - } - }) + (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) } /// Replaces late-bound-regions bound by `value` with `'static` using @@ -1967,7 +1905,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// so forth. fn erase_late_bound_regions<T>(&self, value: ty::Binder<'tcx, T>) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.tcx.erase_late_bound_regions(value) } @@ -1981,6 +1919,38 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + /// Determine if the associated item withe the given DefId matches + /// the desired name via a doc alias. + fn matches_by_doc_alias(&self, def_id: DefId) -> bool { + let Some(name) = self.method_name else { return false; }; + let Some(local_def_id) = def_id.as_local() else { return false; }; + let hir_id = self.fcx.tcx.hir().local_def_id_to_hir_id(local_def_id); + let attrs = self.fcx.tcx.hir().attrs(hir_id); + for attr in attrs { + let sym::doc = attr.name_or_empty() else { continue; }; + let Some(values) = attr.meta_item_list() else { continue; }; + for v in values { + if v.name_or_empty() != sym::alias { + continue; + } + if let Some(nested) = v.meta_item_list() { + // #[doc(alias("foo", "bar"))] + for n in nested { + if let Some(lit) = n.lit() && name.as_str() == lit.symbol.as_str() { + return true; + } + } + } else if let Some(meta) = v.meta_item() + && let Some(lit) = meta.name_value_literal() + && name.as_str() == lit.symbol.as_str() { + // #[doc(alias = "foo")] + return true; + } + } + } + false + } + /// Finds the method with the appropriate name (or return type, as the case may be). If /// `allow_similar_names` is set, find methods with close-matching names. // The length of the returned iterator is nearly always 0 or 1 and this @@ -1996,8 +1966,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if !self.is_relevant_kind_for_mode(x.kind) { return false; } - match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist) - { + if self.matches_by_doc_alias(x.def_id) { + return true; + } + match edit_distance_with_substrings( + name.as_str(), + x.name.as_str(), + max_dist, + ) { Some(d) => d > 0, None => false, } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2e1fc4c38..60d56263d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -27,11 +27,11 @@ 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, TypeVisitable}; +use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; -use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; +use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span}; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -160,7 +160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { - let kind = kind.descr(def_id); + let kind = self.tcx.def_kind_descr(kind, def_id); let mut err = struct_span_err!( self.tcx.sess, item_name.span, @@ -259,10 +259,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mode = no_match_data.mode; let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); - let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty)); + let (ty_str, ty_file) = tcx.short_ty_string(rcvr_ty); + let short_ty_str = with_forced_trimmed_paths!(rcvr_ty.to_string()); let is_method = mode == Mode::MethodCall; let unsatisfied_predicates = &no_match_data.unsatisfied_predicates; - let lev_candidate = no_match_data.lev_candidate; + let similar_candidate = no_match_data.similar_candidate; let item_kind = if is_method { "method" } else if rcvr_ty.is_enum() { @@ -276,11 +277,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if self.suggest_wrapping_range_with_parens(tcx, rcvr_ty, source, span, item_name, &ty_str) - || self.suggest_constraining_numerical_ty( - tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str, - ) - { + // We could pass the file for long types into these two, but it isn't strictly necessary + // given how targetted they are. + if self.suggest_wrapping_range_with_parens( + tcx, + rcvr_ty, + source, + span, + item_name, + &short_ty_str, + ) || self.suggest_constraining_numerical_ty( + tcx, + rcvr_ty, + source, + span, + item_kind, + item_name, + &short_ty_str, + ) { return None; } span = item_name.span; @@ -319,6 +333,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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()), ""); + } + let ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { + short_ty_str + } else { + ty_str + }; + if let Some(file) = ty_file { + err.note(&format!("the full type name has been written to '{}'", file.display(),)); + } if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } @@ -352,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty_span = match rcvr_ty.kind() { ty::Param(param_type) => { - Some(param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id())) + Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id())) } ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), _ => None, @@ -403,7 +428,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, sugg_span, ); - self.note_candidates_on_method_error( rcvr_ty, item_name, @@ -496,9 +520,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Param(_) => { // Account for `fn` items like in `issue-35677.rs` to // suggest restricting its type params. - let parent_body = - hir.body_owner(hir::BodyId { hir_id: self.body_id }); - Some(hir.get(parent_body)) + Some(hir.get_by_def_id(self.body_id)) } ty::Adt(def, _) => { def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) @@ -555,7 +577,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `<Foo as Iterator>::Item = String`. let projection_ty = pred.skip_binder().projection_ty; - let substs_with_infer_self = tcx.mk_substs( + let substs_with_infer_self = tcx.mk_substs_from_iter( iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) .chain(projection_ty.substs.iter().skip(1)), ); @@ -597,7 +619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ObligationCauseCode::ImplDerivedObligation(data) if matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) => { - Some((p, parent, data.impl_def_id, data)) + Some((p, parent, data.impl_or_alias_def_id, data)) } _ => None, }) @@ -695,7 +717,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } Some(Node::Item(hir::Item { - ident, kind: hir::ItemKind::Trait(..), .. + ident, + kind: hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..), + .. })) => { skip_list.insert(p); let entry = spanned_predicates.entry(ident.span); @@ -829,7 +853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let primary_message = primary_message.unwrap_or_else(|| { format!( "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \ - but its trait bounds were not satisfied" + but its trait bounds were not satisfied" ) }); err.set_primary_message(&primary_message); @@ -887,8 +911,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // different from the received one // So we avoid suggestion method with Box<Self> // for instance - self.tcx.at(span).type_of(*def_id) != rcvr_ty - && self.tcx.at(span).type_of(*def_id) != rcvr_ty + self.tcx.at(span).type_of(*def_id).subst_identity() + != rcvr_ty + && self.tcx.at(span).type_of(*def_id).subst_identity() + != rcvr_ty } (Mode::Path, false, _) => true, _ => false, @@ -908,7 +934,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .take(limit) .map(|impl_item| { - format!("- `{}`", self.tcx.at(span).type_of(*impl_item)) + format!( + "- `{}`", + self.tcx.at(span).type_of(*impl_item).subst_identity() + ) }) .collect::<Vec<_>>() .join("\n"); @@ -937,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) - && lev_candidate.is_none() + && similar_candidate.is_none() && !custom_span_label { label_span_not_found(&mut err); @@ -988,7 +1017,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggestion) = lev_distance::find_best_match_for_name( + if let Some(suggestion) = edit_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), item_name.name, None, @@ -1015,20 +1044,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if fallback_span { err.span_label(span, msg); } - } else if let Some(lev_candidate) = lev_candidate { + } else if let Some(similar_candidate) = similar_candidate { // Don't emit a suggestion if we found an actual method // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() { - let def_kind = lev_candidate.kind.as_def_kind(); + let def_kind = similar_candidate.kind.as_def_kind(); // Methods are defined within the context of a struct and their first parameter is always self, // which represents the instance of the struct the method is being called on // Associated functions don’t take self as a parameter and // they are not methods because they don’t have an instance of the struct to work with. - if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { + if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter { err.span_suggestion( span, "there is a method with a similar name", - lev_candidate.name, + similar_candidate.name, Applicability::MaybeIncorrect, ); } else { @@ -1036,10 +1065,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, &format!( "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), + self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id), + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) ), - lev_candidate.name, + similar_candidate.name, Applicability::MaybeIncorrect, ); } @@ -1085,7 +1114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; - let impl_ty = self.tcx.at(span).type_of(impl_did); + let impl_ty = self.tcx.at(span).type_of(impl_did).subst_identity(); let insertion = match self.tcx.impl_trait_ref(impl_did) { None => String::new(), @@ -1131,6 +1160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::AssocKind::Fn => self .tcx .fn_sig(item.def_id) + .subst_identity() .inputs() .skip_binder() .get(0) @@ -1145,7 +1175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path, ty, item.kind, - item.def_id, + self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id), sugg_span, idx, self.tcx.sess.source_map(), @@ -1181,7 +1211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path, rcvr_ty, item.kind, - item.def_id, + self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id), sugg_span, idx, self.tcx.sess.source_map(), @@ -1213,7 +1243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // When the "method" is resolved through dereferencing, we really want the // original type that has the associated function for accurate suggestions. // (#61411) - let impl_ty = self.tcx.type_of(*impl_did); + let impl_ty = self.tcx.type_of(*impl_did).subst_identity(); let target_ty = self .autoderef(sugg_span, rcvr_ty) .find(|(rcvr_ty, _)| { @@ -1225,7 +1255,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(def, substs) = target_ty.kind() { // If there are any inferred arguments, (`{integer}`), we should replace // them with underscores to allow the compiler to infer them - let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| { + let infer_substs = self.tcx.mk_substs_from_iter(substs.into_iter().map(|arg| { if !arg.is_suggestable(self.tcx, true) { has_unsuggestable_args = true; match arg.unpack() { @@ -1267,7 +1297,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(assoc) = self.associated_value(*impl_did, item_name) && assoc.kind == ty::AssocKind::Fn { - let sig = self.tcx.fn_sig(assoc.def_id); + let sig = self.tcx.fn_sig(assoc.def_id).subst_identity(); sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() { None } else { @@ -1343,7 +1373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }); if let Some((field, field_ty)) = field_receiver { - let scope = tcx.parent_module(self.body_id); + let scope = tcx.parent_module_from_def_id(self.body_id); let is_accessible = field.vis.is_accessible_from(scope, tcx); if is_accessible { @@ -1433,8 +1463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); - let range_ty = - self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + let range_ty = self.tcx.type_of(range_def_id).subst(self.tcx, &[actual.into()]); let pick = self.lookup_probe_for_diagnostic( item_name, @@ -1593,7 +1622,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { return }; let map = self.infcx.tcx.hir(); - let body = map.body(rustc_hir::BodyId { hir_id: self.body_id }); + let body_id = self.tcx.hir().body_owned_by(self.body_id); + let body = map.body(body_id); struct LetVisitor<'a> { result: Option<&'a hir::Expr<'a>>, ident_name: Symbol, @@ -2100,7 +2130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // just changing the path. && pick.item.fn_has_self_parameter && let Some(self_ty) = - self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0) + self.tcx.fn_sig(pick.item.def_id).subst_identity().inputs().skip_binder().get(0) && self_ty.is_ref() { let suggested_path = match deref_ty.kind() { @@ -2195,7 +2225,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true }); - let module_did = self.tcx.parent_module(self.body_id); + let module_did = self.tcx.parent_module_from_def_id(self.body_id); let (module, _, _) = self.tcx.hir().get_module(module_did); let span = module.spans.inject_use_span; @@ -2353,7 +2383,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implement the `AsRef` trait. let skip = skippable.contains(&did) || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) - || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().inputs().len() != inputs_len); + || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box<Self>` as a receiver if it only works because of // an autoderef to `&self` @@ -2517,7 +2547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Obtain the span for `param` and use it for a structured suggestion. if let Some(param) = param_type { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let generics = self.tcx.generics_of(self.body_id.to_def_id()); let type_param = generics.type_param(param, self.tcx); let hir = self.tcx.hir(); if let Some(def_id) = type_param.def_id.as_local() { @@ -2733,7 +2763,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // check the method arguments number if let Ok(pick) = probe && let fn_sig = self.tcx.fn_sig(pick.item.def_id) && - let fn_args = fn_sig.skip_binder().inputs() && + let fn_args = fn_sig.skip_binder().skip_binder().inputs() && fn_args.len() == args.len() + 1 { err.span_suggestion_verbose( method_name.span.shrink_to_hi(), @@ -2826,7 +2856,7 @@ fn print_disambiguation_help<'tcx>( trait_name: String, rcvr_ty: Ty<'_>, kind: ty::AssocKind, - def_id: DefId, + def_kind_descr: &'static str, span: Span, candidate: Option<usize>, source_map: &source_map::SourceMap, @@ -2859,7 +2889,7 @@ fn print_disambiguation_help<'tcx>( span, &format!( "disambiguate the {} for {}", - kind.as_def_kind().descr(def_id), + def_kind_descr, if let Some(candidate) = candidate { format!("candidate #{}", candidate) } else { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 78cea1f4d..80279ed96 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable, + self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -297,7 +297,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output() } // error types are considered "builtin" - Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), + Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => { + self.tcx.ty_error_misc() + } Err(errors) => { let (_, trait_def_id) = lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); @@ -335,7 +337,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("cannot divide `{lhs_ty}` by `{rhs_ty}`") } hir::BinOpKind::Rem => { - format!("cannot mod `{lhs_ty}` by `{rhs_ty}`") + format!( + "cannot calculate the remainder of `{lhs_ty}` divided by `{rhs_ty}`" + ) } hir::BinOpKind::BitAnd => { format!("no implementation for `{lhs_ty} & {rhs_ty}`") @@ -488,9 +492,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(output_def_id) = output_def_id && let Some(trait_def_id) = trait_def_id && self.tcx.parent(output_def_id) == trait_def_id - && output_ty.is_suggestable(self.tcx, false) + && let Some(output_ty) = output_ty.make_suggestable(self.tcx, false) { - Some(("Output", *output_ty)) + Some(("Output", output_ty)) } else { None } @@ -516,7 +520,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } let reported = err.emit(); - self.tcx.ty_error_with_guaranteed(reported) + self.tcx.ty_error(reported) } }; @@ -629,7 +633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(errors) => { let actual = self.resolve_vars_if_possible(operand_ty); - if !actual.references_error() { + let guar = actual.error_reported().err().unwrap_or_else(|| { let mut err = struct_span_err!( self.tcx.sess, ex.span, @@ -699,9 +703,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - } - self.tcx.ty_error() + err.emit() + }); + self.tcx.ty_error(guar) } } } @@ -745,14 +749,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let opname = Ident::with_dummy_span(opname); - let input_types = - opt_rhs.as_ref().map(|(_, ty)| std::slice::from_ref(ty)).unwrap_or_default(); + let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip(); + let input_types = opt_rhs_ty.as_slice(); let cause = self.cause( span, traits::BinOp { - rhs_span: opt_rhs.map(|(expr, _)| expr.span), - is_lit: opt_rhs - .map_or(false, |(expr, _)| matches!(expr.kind, hir::ExprKind::Lit(_))), + rhs_span: opt_rhs_expr.map(|expr| expr.span), + is_lit: opt_rhs_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), output_ty: expected.only_has_type(self), }, ); @@ -961,8 +965,8 @@ 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<'tcx> for TypeParamEraser<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TypeParamEraser<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.0.tcx } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 467992452..c36c75e44 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -12,10 +12,10 @@ use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; -use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, DUMMY_SP}; @@ -46,7 +46,7 @@ struct TopInfo<'tcx> { /// Was the origin of the `span` from a scrutinee expression? /// /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, /// The span giving rise to the `expected` type, if one could be provided. /// /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: @@ -74,7 +74,8 @@ struct TopInfo<'tcx> { impl<'tcx> FnCtxt<'_, 'tcx> { fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { - let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; + let code = + Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() }; self.cause(cause_span, code) } @@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) + let mut diag = + self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?; + if let Some(expr) = ti.origin_expr { + self.suggest_fn_call(&mut diag, expr, expected, |output| { + self.can_eq(self.param_env, output, actual) + }); + } + Some(diag) } fn demand_eqtype_pat( @@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option<Span>, - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, ) { let info = TopInfo { expected, origin_expr, span }; self.check_pat(pat, expected, INITIAL_BM, info); @@ -467,8 +475,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. - self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.ty_error(); + let guar = self.emit_err_pat_range(span, lhs, rhs); + return self.tcx.ty_error(guar); } // Unify each side with `expected`. @@ -488,7 +496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { demand_eqtype(&mut rhs, lhs); if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { - return self.tcx.ty_error(); + return self.tcx.ty_error_misc(); } // Find the unified type and check if it's of numeric or char type again. @@ -503,8 +511,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some((ref mut fail, _, _)) = rhs { *fail = true; } - self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.ty_error(); + let guar = self.emit_err_pat_range(span, lhs, rhs); + return self.tcx.ty_error(guar); } ty } @@ -520,7 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, lhs: Option<(bool, Ty<'tcx>, Span)>, rhs: Option<(bool, Ty<'tcx>, Span)>, - ) { + ) -> ErrorGuaranteed { let span = match (lhs, rhs) { (Some((true, ..)), Some((true, ..))) => span, (Some((true, _, sp)), _) => sp, @@ -565,7 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type between two end-points, you can use a guard.", ); } - err.emit(); + err.emit() } fn check_pat_ident( @@ -667,7 +675,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { match (expected.kind(), actual.kind(), ba) { (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) - if self.can_eq(self.param_env, *inner_ty, actual).is_ok() => + if self.can_eq(self.param_env, *inner_ty, actual) => { err.span_suggestion_verbose( span.shrink_to_lo(), @@ -677,7 +685,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) - if self.can_eq(self.param_env, expected, *inner_ty).is_ok() => + if self.can_eq(self.param_env, expected, *inner_ty) => { err.span_suggestion_verbose( span.with_hi(span.lo() + BytePos(4)), @@ -799,29 +807,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn check_dereferenceable(&self, span: Span, expected: Ty<'tcx>, inner: &Pat<'_>) -> bool { + pub fn check_dereferenceable( + &self, + span: Span, + expected: Ty<'tcx>, + inner: &Pat<'_>, + ) -> Result<(), ErrorGuaranteed> { if let PatKind::Binding(..) = inner.kind && let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) && let ty::Dynamic(..) = mt.ty.kind() { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error. - let type_str = self.ty_to_string(expected); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0033, - "type `{}` cannot be dereferenced", - type_str - ); - err.span_label(span, format!("type `{type_str}` cannot be dereferenced")); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); - } - err.emit(); - return false; - } - true + // This is "x = SomeTrait" being reduced from + // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error. + let type_str = self.ty_to_string(expected); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0033, + "type `{}` cannot be dereferenced", + type_str + ); + err.span_label(span, format!("type `{type_str}` cannot be dereferenced")); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); + } + return Err(err.emit()); + } + Ok(()) } fn check_pat_struct( @@ -835,13 +847,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: TopInfo<'tcx>, ) -> Ty<'tcx> { // Resolve the path and check the definition for errors. - let Some((variant, pat_ty)) = self.check_struct_path(qpath, pat.hir_id) else { - let err = self.tcx.ty_error(); - for field in fields { - let ti = ti; - self.check_pat(field.pat, err, def_bm, ti); + let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { + Ok(data) => data, + Err(guar) => { + let err = self.tcx.ty_error(guar); + for field in fields { + let ti = ti; + self.check_pat(field.pat, err, def_bm, ti); + } + return err; } - return err; }; // Type-check the path. @@ -851,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, def_bm, ti) { pat_ty } else { - self.tcx.ty_error() + self.tcx.ty_error_misc() } } @@ -871,12 +886,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Res::Err => { let e = tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0533", expected); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } Res::SelfCtor(..) | Res::Def( @@ -1019,7 +1034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let on_error = |e| { for pat in subpats { - self.check_pat(pat, tcx.ty_error_with_guaranteed(e), def_bm, ti); + self.check_pat(pat, tcx.ty_error(e), def_bm, ti); } }; let report_unexpected_res = |res: Res| { @@ -1036,7 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); on_error(e); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } // Type-check the path. @@ -1044,7 +1059,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); if !pat_ty.is_fn() { let e = report_unexpected_res(res); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } let variant = match res { @@ -1052,11 +1067,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); on_error(e); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { let e = report_unexpected_res(res); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -1097,7 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pattern has wrong number of fields. let e = self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); on_error(e); - return tcx.ty_error_with_guaranteed(e); + return tcx.ty_error(e); } pat_ty } @@ -1287,17 +1302,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }, ) }); - let element_tys = tcx.mk_type_list(element_tys_iter); - let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); + let element_tys = tcx.mk_type_list_from_iter(element_tys_iter); + let pat_ty = tcx.mk_tup(element_tys); if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.ty_error_with_guaranteed(reported)); + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error(reported)); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, tcx.ty_error_with_guaranteed(reported), def_bm, ti); + self.check_pat(elem, tcx.ty_error(reported), def_bm, ti); } - tcx.mk_tup(element_tys_iter) + tcx.mk_tup_from_iter(element_tys_iter) } else { for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { self.check_pat(elem, element_tys[i], def_bm, ti); @@ -1341,9 +1356,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ident = tcx.adjust_ident(field.ident, variant.def_id); let field_ty = match used_fields.entry(ident) { Occupied(occupied) => { - self.error_field_already_bound(span, field.ident, *occupied.get()); no_field_errors = false; - tcx.ty_error() + let guar = self.error_field_already_bound(span, field.ident, *occupied.get()); + tcx.ty_error(guar) } Vacant(vacant) => { vacant.insert(span); @@ -1357,7 +1372,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|| { inexistent_fields.push(field); no_field_errors = false; - tcx.ty_error() + tcx.ty_error_misc() }) } }; @@ -1528,7 +1543,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn error_field_already_bound(&self, span: Span, ident: Ident, other_field: Span) { + fn error_field_already_bound( + &self, + span: Span, + ident: Ident, + other_field: Span, + ) -> ErrorGuaranteed { struct_span_err!( self.tcx.sess, span, @@ -1538,7 +1558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .span_label(span, format!("multiple uses of `{ident}` in pattern")) .span_label(other_field, format!("first use of `{ident}`")) - .emit(); + .emit() } fn error_inexistent_fields( @@ -1911,19 +1931,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let (box_ty, inner_ty) = if self.check_dereferenceable(span, expected, inner) { - // Here, `demand::subtype` is good enough, but I don't - // think any errors can be introduced by using `demand::eqtype`. - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let box_ty = tcx.mk_box(inner_ty); - self.demand_eqtype_pat(span, expected, box_ty, ti); - (box_ty, inner_ty) - } else { - let err = tcx.ty_error(); - (err, err) + let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) { + Ok(()) => { + // Here, `demand::subtype` is good enough, but I don't + // think any errors can be introduced by using `demand::eqtype`. + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let box_ty = tcx.mk_box(inner_ty); + self.demand_eqtype_pat(span, expected, box_ty, ti); + (box_ty, inner_ty) + } + Err(guar) => { + let err = tcx.ty_error(guar); + (err, err) + } }; self.check_pat(inner, inner_ty, def_bm, ti); box_ty @@ -1941,37 +1964,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); - let (ref_ty, inner_ty) = if self.check_dereferenceable(pat.span, expected, inner) { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. - - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag(pat.span, expected, ref_ty, ti); - - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); + let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match *expected.kind() { + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), + _ => { + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + let err = self.demand_eqtype_pat_diag(pat.span, expected, ref_ty, ti); + + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, pat); + err.emit(); + } + (ref_ty, inner_ty) } - (ref_ty, inner_ty) } } - } else { - let err = tcx.ty_error(); - (err, err) + Err(guar) => { + let err = tcx.ty_error(guar); + (err, err) + } }; self.check_pat(inner, inner_ty, def_bm, ti); ref_ty @@ -2019,10 +2045,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Slice(element_ty) => (element_ty, Some(expected), expected), // The expected type must be an array or slice, but was neither, so error. _ => { - if !expected.references_error() { - self.error_expected_array_or_slice(span, expected, ti); - } - let err = self.tcx.ty_error(); + let guar = expected + .error_reported() + .err() + .unwrap_or_else(|| self.error_expected_array_or_slice(span, expected, ti)); + let err = self.tcx.ty_error(guar); (err, Some(err), err) } }; @@ -2055,7 +2082,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, min_len: u64, ) -> (Option<Ty<'tcx>>, Ty<'tcx>) { - if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { + let guar = if let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env) { // Now we know the length... if slice.is_none() { // ...and since there is no variable-length pattern, @@ -2065,7 +2092,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return (None, arr_ty); } - self.error_scrutinee_inconsistent_length(span, min_len, len); + self.error_scrutinee_inconsistent_length(span, min_len, len) } else if let Some(pat_len) = len.checked_sub(min_len) { // The variable-length pattern was there, // so it has an array type with the remaining elements left as its size... @@ -2073,7 +2100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // ...however, in this case, there were no remaining elements. // That is, the slice pattern requires more than the array type offers. - self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); + self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len) } } else if slice.is_none() { // We have a pattern with a fixed length, @@ -2085,14 +2112,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We have a variable-length pattern and don't know the array length. // This happens if we have e.g., // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - self.error_scrutinee_unfixed_length(span); - } + self.error_scrutinee_unfixed_length(span) + }; // If we get here, we must have emitted an error. - (Some(self.tcx.ty_error()), arr_ty) + (Some(self.tcx.ty_error(guar)), arr_ty) } - fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { + fn error_scrutinee_inconsistent_length( + &self, + span: Span, + min_len: u64, + size: u64, + ) -> ErrorGuaranteed { struct_span_err!( self.tcx.sess, span, @@ -2103,10 +2135,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { size, ) .span_label(span, format!("expected {} element{}", size, pluralize!(size))) - .emit(); + .emit() } - fn error_scrutinee_with_rest_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { + fn error_scrutinee_with_rest_inconsistent_length( + &self, + span: Span, + min_len: u64, + size: u64, + ) -> ErrorGuaranteed { struct_span_err!( self.tcx.sess, span, @@ -2120,20 +2157,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, format!("pattern cannot match array of {} element{}", size, pluralize!(size),), ) - .emit(); + .emit() } - fn error_scrutinee_unfixed_length(&self, span: Span) { + fn error_scrutinee_unfixed_length(&self, span: Span) -> ErrorGuaranteed { struct_span_err!( self.tcx.sess, span, E0730, "cannot pattern-match on an array without a fixed length", ) - .emit(); + .emit() } - fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>, ti: TopInfo<'tcx>) { + fn error_expected_array_or_slice( + &self, + span: Span, + expected_ty: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) -> ErrorGuaranteed { let mut err = struct_span_err!( self.tcx.sess, span, @@ -2146,7 +2188,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("the semantics of slice patterns changed recently; see issue #62254"); } else if self.autoderef(span, expected_ty) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) - && let (Some(span), true) = (ti.span, ti.origin_expr) + && let Some(span) = ti.span + && let Some(_) = ti.origin_expr && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let ty = self.resolve_vars_if_possible(ti.expected); @@ -2176,7 +2219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } err.span_label(span, format!("pattern cannot match with input type `{expected_ty}`")); - err.emit(); + err.emit() } fn is_slice_or_array_or_vector(&self, ty: Ty<'tcx>) -> (bool, Ty<'tcx>) { diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ae0df5aa8..2cca45de5 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -11,7 +11,6 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already. @@ -91,10 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } let reported = err.emit(); - Some(( - self.tcx.ty_error_with_guaranteed(reported), - self.tcx.ty_error_with_guaranteed(reported), - )) + Some((self.tcx.ty_error(reported), self.tcx.ty_error(reported))) } /// To type-check `base_expr[index_expr]`, we progressively autoderef @@ -396,11 +392,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1)) } }; - let arg_tys = match arg_ty { - None => &[], - Some(ref ty) => slice::from_ref(ty), - }; - + let arg_tys = arg_ty.as_slice(); let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); let method = match method { Some(ok) => self.register_infer_ok_obligations(ok), diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e12a575d5..4a432328c 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We now fake capture information for all variables that are mentioned within the closure // We do this after handling migrations so that min_captures computes before - if !enable_precise_capture(self.tcx, span) { + if !enable_precise_capture(span) { let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { @@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have an origin, store it. if let Some(origin) = origin { - let origin = if enable_precise_capture(self.tcx, span) { + let origin = if enable_precise_capture(span) { (origin.0, origin.1) } else { (origin.0, Place { projections: vec![], ..origin.1 }) @@ -301,7 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: - let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter()); + let final_tupled_upvars_type = self.tcx.mk_tup(&final_upvar_tys); self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type); let fake_reads = delegate @@ -315,8 +315,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().closure_size_eval.insert( closure_def_id, ClosureSizeProfileData { - before_feature_tys: self.tcx.mk_tup(before_feature_tys.into_iter()), - after_feature_tys: self.tcx.mk_tup(after_feature_tys.into_iter()), + before_feature_tys: self.tcx.mk_tup(&before_feature_tys), + after_feature_tys: self.tcx.mk_tup(&after_feature_tys), }, ); } @@ -526,10 +526,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), }; + let var_ident = self.tcx.hir().ident(var_hir_id); let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { let mutability = self.determine_capture_mutability(&typeck_results, &place); let min_cap_list = vec![ty::CapturedPlace { + var_ident, place, info: capture_info, mutability, @@ -628,6 +630,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ancestor_found { let mutability = self.determine_capture_mutability(&typeck_results, &place); let captured_place = ty::CapturedPlace { + var_ident, place, info: updated_capture_info, mutability, @@ -1240,8 +1243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// This will make more sense with an example: /// - /// ```rust - /// #![feature(capture_disjoint_fields)] + /// ```rust,edition2021 /// /// struct FancyInteger(i32); // This implements Drop /// @@ -2221,7 +2223,7 @@ fn determine_place_ancestry_relation<'tcx>( /// || drop(&*m.a.field_of_a) /// // Here we really do want to capture `*m.a` because that outlives `'static` /// -/// // If we capture `m`, then the closure no longer outlives `'static' +/// // If we capture `m`, then the closure no longer outlives `'static` /// // it is constrained to `'a` /// } /// ``` @@ -2247,12 +2249,10 @@ fn truncate_capture_for_optimization( (place, curr_mode) } -/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if -/// user is using Rust Edition 2021 or higher. -/// +/// Precise capture is enabled if user is using Rust Edition 2021 or higher. /// `span` is the span of the closure. -fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool { +fn enable_precise_capture(span: Span) -> bool { // We use span here to ensure that if the closure was generated by a macro with a different // edition. - tcx.features().capture_disjoint_fields || span.rust_2021() + span.rust_2021() } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 250f4cd3f..00348f3af 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -14,7 +14,7 @@ use rustc_middle::hir::place::Place as HirPlace; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::TypeckResults; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -40,8 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, body: &'tcx hir::Body<'tcx>, ) -> &'tcx ty::TypeckResults<'tcx> { - let item_id = self.tcx.hir().body_owner(body.id()); - let item_def_id = self.tcx.hir().local_def_id(item_id); + let item_def_id = self.tcx.hir().body_owner_def_id(body.id()); // This attribute causes us to dump some writeback information // in the form of errors, which is used for unit tests. @@ -55,7 +54,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type only exists for constants and statics, not functions. match self.tcx.hir().body_owner_kind(item_def_id) { hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => { - wbcx.visit_node_id(body.value.span, item_id); + let item_hir_id = self.tcx.hir().local_def_id_to_hir_id(item_def_id); + wbcx.visit_node_id(body.value.span, item_hir_id); } hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => (), } @@ -545,6 +545,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); self.typeck_results.generator_interior_types = fcx_typeck_results.generator_interior_types.clone(); + for (&expr_def_id, predicates) in fcx_typeck_results.generator_interior_predicates.iter() { + let predicates = self.resolve(predicates.clone(), &self.fcx.tcx.def_span(expr_def_id)); + self.typeck_results.generator_interior_predicates.insert(expr_def_id, predicates); + } } #[instrument(skip(self), level = "debug")] @@ -557,7 +561,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { struct RecursionChecker { def_id: LocalDefId, } - impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { + impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for RecursionChecker { type BreakTy = (); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *t.kind() { @@ -681,7 +685,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn resolve<T>(&mut self, x: T, span: &dyn Locatable) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { let mut resolver = Resolver::new(self.fcx, span, self.body); let x = x.fold_with(&mut resolver); @@ -759,8 +763,8 @@ struct EraseEarlyRegions<'tcx> { tcx: TyCtxt<'tcx>, } -impl<'tcx> TypeFolder<'tcx> for EraseEarlyRegions<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEarlyRegions<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -775,8 +779,8 @@ impl<'tcx> TypeFolder<'tcx> for EraseEarlyRegions<'tcx> { } } -impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { +impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } @@ -793,7 +797,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); let e = self.report_error(t); self.replaced_with_error = Some(e); - self.tcx().ty_error_with_guaranteed(e) + self.interner().ty_error(e) } } } @@ -810,7 +814,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); let e = self.report_error(ct); self.replaced_with_error = Some(e); - self.tcx().const_error_with_guaranteed(ct.ty(), e) + self.interner().const_error_with_guaranteed(ct.ty(), e) } } } |