diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 31ca0e46091cc..2a92a4b48edcc 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -415,10 +415,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infcx.instantiate_canonical(span, &query_input.canonical); let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value; debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); + let prev_opaque_entries = self.inner.borrow_mut().opaque_types().num_entries(); MethodAutoderefStepsResult { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { - self_ty: self - .make_query_response_ignoring_pending_obligations(var_values, self_ty), + self_ty: self.make_query_response_ignoring_pending_obligations( + var_values, + self_ty, + prev_opaque_entries, + ), self_ty_is_opaque: false, autoderefs: 0, from_unsafe_deref: false, @@ -607,6 +611,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`"); } } + let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); // We accept not-yet-defined opaque types in the autoderef // chain to support recursive calls. We do error if the final @@ -650,8 +655,11 @@ pub(crate) fn method_autoderef_steps<'tcx>( .zip(reachable_via_deref) .map(|((ty, d), reachable_via_deref)| { let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + ty, + prev_opaque_entries, + ), self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, @@ -671,8 +679,11 @@ pub(crate) fn method_autoderef_steps<'tcx>( .by_ref() .map(|(ty, d)| { let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + ty, + prev_opaque_entries, + ), self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, @@ -692,11 +703,19 @@ pub(crate) fn method_autoderef_steps<'tcx>( let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy { reached_raw_pointer, - ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + final_ty, + prev_opaque_entries, + ), }), ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, - ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + final_ty, + prev_opaque_entries, + ), }), ty::Array(elem_ty, _) => { let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1; @@ -704,6 +723,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, Ty::new_slice(infcx.tcx, *elem_ty), + prev_opaque_entries, ), self_ty_is_opaque: false, autoderefs, diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 65b0f82114329..846123b8aad93 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -24,7 +24,8 @@ use crate::infer::canonical::{ }; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, InferResult, SubregionOrigin, TypeOutlivesConstraint, + DefineOpaqueTypes, InferCtxt, InferOk, InferResult, OpaqueTypeStorageEntries, SubregionOrigin, + TypeOutlivesConstraint, }; use crate::traits::query::NoSolution; use crate::traits::{ObligationCause, PredicateObligations, ScrubbedTraitError, TraitEngine}; @@ -81,6 +82,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, inference_vars: CanonicalVarValues<'tcx>, answer: T, + prev_entries: OpaqueTypeStorageEntries, ) -> Canonical<'tcx, QueryResponse<'tcx, T>> where T: Debug + TypeFoldable>, @@ -96,7 +98,7 @@ impl<'tcx> InferCtxt<'tcx> { self.inner .borrow_mut() .opaque_type_storage - .iter_opaque_types() + .opaque_types_added_since(prev_entries) .map(|(k, v)| (k, v.ty)) .collect() } else { diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 246152f5390c6..72985ff607789 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -35,6 +35,8 @@ use rustc_span::def_id::LOCAL_CRATE; use crate::QueryConfigRestored; +/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that +/// crate does not have direct access to [`TyCtxt`]. #[derive(Copy, Clone)] pub struct QueryCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, @@ -47,15 +49,6 @@ impl<'tcx> QueryCtxt<'tcx> { } } -impl<'tcx> std::ops::Deref for QueryCtxt<'tcx> { - type Target = TyCtxt<'tcx>; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.tcx - } -} - impl<'tcx> HasDepContext for QueryCtxt<'tcx> { type Deps = rustc_middle::dep_graph::DepsType; type DepContext = TyCtxt<'tcx>; @@ -69,14 +62,16 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> { impl QueryContext for QueryCtxt<'_> { #[inline] fn jobserver_proxy(&self) -> &Proxy { - &*self.jobserver_proxy + &self.tcx.jobserver_proxy } #[inline] fn next_job_id(self) -> QueryJobId { QueryJobId( - NonZero::new(self.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed)) - .unwrap(), + NonZero::new( + self.tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed), + ) + .unwrap(), ) } @@ -113,7 +108,8 @@ impl QueryContext for QueryCtxt<'_> { self, prev_dep_node_index: SerializedDepNodeIndex, ) -> Option { - self.query_system + self.tcx + .query_system .on_disk_cache .as_ref() .and_then(|c| c.load_side_effect(self.tcx, prev_dep_node_index)) @@ -122,7 +118,7 @@ impl QueryContext for QueryCtxt<'_> { #[inline(never)] #[cold] fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { - if let Some(c) = self.query_system.on_disk_cache.as_ref() { + if let Some(c) = self.tcx.query_system.on_disk_cache.as_ref() { c.store_side_effect(dep_node_index, side_effect) } } @@ -140,7 +136,9 @@ impl QueryContext for QueryCtxt<'_> { // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes // when accessing the `ImplicitCtxt`. tls::with_related_context(self.tcx, move |current_icx| { - if depth_limit && !self.recursion_limit().value_within_limit(current_icx.query_depth) { + if depth_limit + && !self.tcx.recursion_limit().value_within_limit(current_icx.query_depth) + { self.depth_limit_error(token); } @@ -161,16 +159,16 @@ impl QueryContext for QueryCtxt<'_> { let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); let (info, depth) = job.find_dep_kind_root(query_map); - let suggested_limit = match self.recursion_limit() { + let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), limit => limit * 2, }; - self.sess.dcx().emit_fatal(QueryOverflow { + self.tcx.sess.dcx().emit_fatal(QueryOverflow { span: info.job.span, note: QueryOverflowNote { desc: info.query.description, depth }, suggested_limit, - crate_name: self.crate_name(LOCAL_CRATE), + crate_name: self.tcx.crate_name(LOCAL_CRATE), }); } } @@ -367,7 +365,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>( Q: super::QueryConfigRestored<'tcx>, Q::RestoredValue: Encodable>, { - let _timer = qcx.profiler().generic_activity_with_arg("encode_query_results_for", query.name()); + let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name()); assert!(query.query_state(qcx).all_inactive()); let cache = query.query_cache(qcx); @@ -389,8 +387,7 @@ pub(crate) fn query_key_hash_verify<'tcx>( query: impl QueryConfig>, qcx: QueryCtxt<'tcx>, ) { - let _timer = - qcx.profiler().generic_activity_with_arg("query_key_hash_verify_for", query.name()); + let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); let mut map = UnordMap::default(); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8fd228211f3c3..c4391c3ec4fc4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -270,6 +270,7 @@ symbols! { Into, IntoFuture, IntoIterator, + IntoIteratorItem, IoBufRead, IoLines, IoRead, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 1c08b5e331422..d54f3812350d9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4390,6 +4390,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, path_segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>], + prev_ty: Ty<'_>, err: &mut Diag<'_, G>, ) { let tcx = self.tcx; @@ -4403,6 +4404,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let TypeError::Sorts(expected_found) = diff else { continue; }; + if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id) + && path_segment.ident.name == sym::iter + && self.can_eq( + param_env, + Ty::new_ref( + tcx, + tcx.lifetimes.re_erased, + expected_found.found, + ty::Mutability::Not, + ), + *ty, + ) + && let [] = args + { + // Used `.iter()` when `.into_iter()` was likely meant. + err.span_suggestion_verbose( + path_segment.ident.span, + format!("consider consuming the `{prev_ty}` to construct the `Iterator`"), + "into_iter".to_string(), + Applicability::MachineApplicable, + ); + } + if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id) + && path_segment.ident.name == sym::into_iter + && self.can_eq( + param_env, + expected_found.found, + Ty::new_ref(tcx, tcx.lifetimes.re_erased, *ty, ty::Mutability::Not), + ) + && let [] = args + { + // Used `.into_iter()` when `.iter()` was likely meant. + err.span_suggestion_verbose( + path_segment.ident.span, + format!( + "consider not consuming the `{prev_ty}` to construct the `Iterator`" + ), + "iter".to_string(), + Applicability::MachineApplicable, + ); + } if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) && path_segment.ident.name == sym::map && self.can_eq(param_env, expected_found.found, *ty) @@ -4515,6 +4557,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { expr = rcvr_expr; let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env); + prev_ty = self.resolve_vars_if_possible( + typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), + ); self.look_for_iterator_item_mistakes( &assocs_in_this_method, typeck_results, @@ -4522,12 +4567,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_env, path_segment, args, + prev_ty, err, ); assocs.push(assocs_in_this_method); - prev_ty = self.resolve_vars_if_possible( - typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), - ); if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index cdf81385bdafb..c3b9a0f0b7a4e 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -281,6 +281,7 @@ pub trait FromIterator: Sized { #[stable(feature = "rust1", since = "1.0.0")] pub trait IntoIterator { /// The type of the elements being iterated over. + #[rustc_diagnostic_item = "IntoIteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] type Item; diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.fixed b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed new file mode 100644 index 0000000000000..e841b1605f11a --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ edition:2021 +// Suggest using the right `IntoIterator` method. #68095 +fn main() { + let _a = [0, 1, 2].iter().chain([3, 4, 5].iter()); //~ ERROR E0271 + let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271 + // These don't have appropriate suggestions yet. + // let c = [0, 1, 2].iter().chain([3, 4, 5]); + // let d = [0, 1, 2].iter().chain(vec![3, 4, 5]); +} diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.rs b/tests/ui/iterators/into_iter-when-iter-was-intended.rs new file mode 100644 index 0000000000000..8d4376aa0ae6d --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.rs @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ edition:2021 +// Suggest using the right `IntoIterator` method. #68095 +fn main() { + let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271 + let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); //~ ERROR E0271 + // These don't have appropriate suggestions yet. + // let c = [0, 1, 2].iter().chain([3, 4, 5]); + // let d = [0, 1, 2].iter().chain(vec![3, 4, 5]); +} diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.stderr b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr new file mode 100644 index 0000000000000..f26db9781b13a --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr @@ -0,0 +1,48 @@ +error[E0271]: type mismatch resolving ` as IntoIterator>::Item == &{integer}` + --> $DIR/into_iter-when-iter-was-intended.rs:5:37 + | +LL | let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); + | ----- ^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer + | | + | required by a bound introduced by this call + | +note: the method call chain might not have had the expected associated types + --> $DIR/into_iter-when-iter-was-intended.rs:5:47 + | +LL | let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); + | --------- ^^^^^^^^^^^ `IntoIterator::Item` is `{integer}` here + | | + | this expression has type `[{integer}; 3]` +note: required by a bound in `std::iter::Iterator::chain` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider not consuming the `[{integer}; 3]` to construct the `Iterator` + | +LL - let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); +LL + let _a = [0, 1, 2].iter().chain([3, 4, 5].iter()); + | + +error[E0271]: type mismatch resolving ` as IntoIterator>::Item == {integer}` + --> $DIR/into_iter-when-iter-was-intended.rs:6:42 + | +LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); + | ----- ^^^^^^^^^^^^^^^^ expected integer, found `&{integer}` + | | + | required by a bound introduced by this call + | +note: the method call chain might not have had the expected associated types + --> $DIR/into_iter-when-iter-was-intended.rs:6:52 + | +LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); + | --------- ^^^^^^ `IntoIterator::Item` is `&{integer}` here + | | + | this expression has type `[{integer}; 3]` +note: required by a bound in `std::iter::Iterator::chain` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider consuming the `&[{integer}]` to construct the `Iterator` + | +LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter()); + | +++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs new file mode 100644 index 0000000000000..d8375a62bb376 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs @@ -0,0 +1,38 @@ +//@ compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative/issues/263 +// Previously `method_auto_deref_steps` would also return opaque +// types which have already been defined in the parent context. +// +// We then handled these opaque types by emitting `AliasRelate` goals +// when instantiating its result, assuming that operation to be infallible. +// By returning opaque type constraints from the parent context and +// constraining the hidden type without reproving the item bounds of +// the opaque, this ended up causing ICE. + +use std::ops::Deref; +trait Trait {} +struct Inv(*mut T); +impl Trait for i32 {} +impl Deref for Inv { + type Target = u32; + fn deref(&self) -> &Self::Target { + todo!() + } +} + +fn mk() -> T { todo!() } +fn foo() -> Inv { + //~^ ERROR: the trait bound `u32: Trait` is not satisfied [E0277] + let mut x: Inv<_> = mk(); + if false { + return x; + //~^ ERROR: the trait bound `u32: Trait` is not satisfied [E0277] + } + + x.count_ones(); + x + //~^ ERROR: mismatched types [E0308] +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr new file mode 100644 index 0000000000000..c421222309a03 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr @@ -0,0 +1,43 @@ +error[E0277]: the trait bound `u32: Trait` is not satisfied + --> $DIR/method_autoderef_constraints.rs:29:16 + | +LL | return x; + | ^ the trait `Trait` is not implemented for `u32` + | +help: the trait `Trait` is implemented for `i32` + --> $DIR/method_autoderef_constraints.rs:16:1 + | +LL | impl Trait for i32 {} + | ^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/method_autoderef_constraints.rs:34:5 + | +LL | fn foo() -> Inv { + | --------------- + | | | + | | the expected opaque type + | expected `Inv` because of return type +... +LL | x + | ^ types differ + | + = note: expected struct `Inv` + found struct `Inv` + +error[E0277]: the trait bound `u32: Trait` is not satisfied + --> $DIR/method_autoderef_constraints.rs:25:1 + | +LL | fn foo() -> Inv { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `u32` + | +help: the trait `Trait` is implemented for `i32` + --> $DIR/method_autoderef_constraints.rs:16:1 + | +LL | impl Trait for i32 {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`.