From f4e19c68786211f3c3cf2593442629599678800a Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 11 Sep 2025 13:08:36 +0200 Subject: [PATCH 1/2] support calls on opaque types :< --- compiler/rustc_hir_analysis/src/autoderef.rs | 15 ++- compiler/rustc_hir_typeck/src/callee.rs | 26 +++- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 37 +++--- compiler/rustc_hir_typeck/src/method/mod.rs | 29 ++++- compiler/rustc_hir_typeck/src/op.rs | 15 ++- compiler/rustc_hir_typeck/src/place_op.rs | 28 ++++- compiler/rustc_infer/src/infer/context.rs | 3 + compiler/rustc_infer/src/infer/mod.rs | 54 ++++++++ .../src/solve/assembly/mod.rs | 40 ++++-- .../src/solve/eval_ctxt/canonical.rs | 25 ++-- .../src/solve/eval_ctxt/mod.rs | 76 +++++++----- .../rustc_next_trait_solver/src/solve/mod.rs | 35 +++--- .../src/solve/search_graph.rs | 2 +- .../src/solve/fulfill.rs | 4 +- .../src/solve/fulfill/derive_errors.rs | 22 ++-- .../src/solve/inspect/analyse.rs | 2 +- .../rustc_trait_selection/src/solve/select.rs | 8 +- .../src/traits/coherence.rs | 2 +- .../src/traits/query/evaluate_obligation.rs | 17 +++ compiler/rustc_type_ir/src/infer_ctxt.rs | 3 +- compiler/rustc_type_ir/src/solve/mod.rs | 80 +++++++++++- .../ambiguous-ops.current.stderr | 62 ++++++++++ .../non-defining-uses/ambiguous-ops.rs | 117 ++++++++++++++++++ .../function-call-on-infer.rs | 73 +++++++++++ .../impl-deref-function-call.rs | 56 +++++++++ .../shex_compat-regression-test.rs | 19 +++ .../return-block-type-inference-15965.stderr | 6 +- ...oxed-closures-failed-recursive-fn-2.stderr | 2 +- 28 files changed, 734 insertions(+), 124 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index e8237471e1b68..88bd3339e4e18 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -68,7 +68,14 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { return None; } - if self.state.cur_ty.is_ty_var() { + // We want to support method and function calls for `impl Deref`. + // + // To do so we don't eagerly bail if the current type is the hidden type of an + // opaque type and instead return `None` in `fn overloaded_deref_ty` if the + // opaque does not have a `Deref` item-bound. + if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind() + && !self.infcx.has_opaques_with_sub_unified_hidden_type(vid) + { return None; } @@ -160,7 +167,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.param_env, ty::Binder::dummy(trait_ref), ); - if !self.infcx.next_trait_solver() && !self.infcx.predicate_may_hold(&obligation) { + // We detect whether the self type implements `Deref` before trying to + // structurally normalize. We use `predicate_may_hold_opaque_types_jank` + // to support not-yet-defined opaque types. It will succeed for `impl Deref` + // but fail for `impl OtherTrait`. + if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) { debug!("overloaded_deref_ty: cannot match obligation"); return None; } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index c6a4d78dcc830..f59fcab46661f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -25,6 +25,7 @@ use tracing::{debug, instrument}; use super::method::MethodCallee; use super::method::probe::ProbeScope; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; +use crate::method::TreatNotYetDefinedOpaques; use crate::{errors, fluent_generated}; /// Checks that it is legal to call methods of the trait corresponding @@ -78,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.check_expr(callee_expr), }; - let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty); + let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty); let mut autoderef = self.autoderef(callee_expr.span, expr_ty); let mut result = None; @@ -200,7 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], autoderef: &Autoderef<'a, 'tcx>, ) -> Option> { - let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); + let adjusted_ty = + self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty()); // If the callee is a function pointer or a closure, then we're all set. match *adjusted_ty.kind() { @@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + ty::Infer(ty::TyVar(vid)) => { + // If we end up with an inference variable which is not the hidden type of + // an opaque, emit an error. + if !self.has_opaques_with_sub_unified_hidden_type(vid) { + self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty); + return None; + } + } + ty::Error(_) => { return None; } @@ -367,26 +378,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) }); + // We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty` + // is `Box` we choose `FnOnce` instead of `Fn`. + // + // We try all the different call traits in order and choose the first + // one which may apply. So if we treat opaques as inference variables + // `Box: Fn` is considered ambiguous and chosen. if let Some(ok) = self.lookup_method_for_operator( self.misc(call_expr.span), method_name, trait_def_id, adjusted_ty, opt_input_type, + TreatNotYetDefinedOpaques::AsRigid, ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { // Check for &self vs &mut self in the method signature. Since this is either // the Fn or FnMut trait, it should be one of those. - let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else { + let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else { bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut") }; // For initial two-phase borrow // deployment, conservatively omit // overloaded function call ops. - let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No); autoref = Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7370124e800da..3444523974af2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1469,24 +1469,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = self.try_structurally_resolve_type(sp, ty); - if !ty.is_ty_var() { - ty - } else { - let e = self.tainted_by_errors().unwrap_or_else(|| { - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - sp, - ty.into(), - TypeAnnotationNeeded::E0282, - true, - ) - .emit() - }); - let err = Ty::new_error(self.tcx, e); - self.demand_suptype(sp, err, ty); - err - } + if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) } + } + + #[cold] + pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let guar = self.tainted_by_errors().unwrap_or_else(|| { + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + sp, + ty.into(), + TypeAnnotationNeeded::E0282, + true, + ) + .emit() + }); + let err = Ty::new_error(self.tcx, guar); + self.demand_suptype(sp, err, ty); + err } pub(crate) fn structurally_resolve_const( diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 652644ad78cce..04f112e4a39cc 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -317,7 +317,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )?; Ok(pick) } +} + +/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// +/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while +/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary +/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// types as rigid to support `impl Deref` and +/// `Box`. +/// +/// We only want to treat opaque types as rigid if we need to eagerly choose +/// between multiple candidates. We otherwise treat them as ordinary inference +/// variable to avoid rejecting otherwise correct code. +#[derive(Debug)] +pub(super) enum TreatNotYetDefinedOpaques { + AsInfer, + AsRigid, +} +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs @@ -331,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_def_id: DefId, self_ty: Ty<'tcx>, opt_rhs_ty: Option>, + treat_opaques: TreatNotYetDefinedOpaques, ) -> Option>> { // Construct a trait-reference `self_ty : Trait` let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind { @@ -360,7 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Now we want to know if this can be matched - if !self.predicate_may_hold(&obligation) { + let matches_trait = match treat_opaques { + TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation), + TreatNotYetDefinedOpaques::AsRigid => { + self.predicate_may_hold_opaque_types_jank(&obligation) + } + }; + + if !matches_trait { debug!("--> Cannot match obligation"); // Cannot be matched, no such method resolution is possible. return None; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 11defc3aa0339..054435379434c 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -21,6 +21,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::FnCtxt; use super::method::MethodCallee; use crate::Expectation; +use crate::method::TreatNotYetDefinedOpaques; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` @@ -974,8 +975,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let method = - self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); + // We don't consider any other candidates if this lookup fails + // so we can freely treat opaque types as inference variables here + // to allow more code to compile. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + let method = self.lookup_method_for_operator( + cause.clone(), + opname, + trait_did, + lhs_ty, + opt_rhs_ty, + treat_opaques, + ); match method { Some(ok) => { let method = self.register_infer_ok_obligations(ok); diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 1125e98408045..a48db2cc855c0 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -12,7 +12,7 @@ use rustc_span::{Span, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -use crate::method::MethodCallee; +use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; use crate::{FnCtxt, PlaceOp}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -210,7 +210,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty) + // FIXME(trait-system-refactor-initiative#231): we may want to treat + // opaque types as rigid here to support `impl Deref>`. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + imm_op, + imm_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } fn try_mutable_overloaded_place_op( @@ -230,7 +240,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty) + // We have to replace the operator with the mutable variant for the + // program to compile, so we don't really have a choice here and want + // to just try using `DerefMut` even if its not in the item bounds + // of the opaque. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + mut_op, + mut_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 14cc590720ac5..5ffa7304efaff 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -302,6 +302,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { .map(|(k, h)| (k, h.ty)) .collect() } + fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec> { + self.opaques_with_sub_unified_hidden_type(ty) + } fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9d3886aff1c1a..c9fc124d3bf8d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1004,6 +1004,60 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } + pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool { + if !self.next_trait_solver() { + return false; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return true; + } + } + + false + }) + } + + /// Searches for an opaque type key whose hidden type is related to `ty_vid`. + /// + /// This only checks for a subtype relation, it does not require equality. + pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec> { + // Avoid accidentally allowing more code to compile with the old solver. + if !self.next_trait_solver() { + return vec![]; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + // This is iffy, can't call `type_variables()` as we're already + // borrowing the `opaque_type_storage` here. + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_opaque_types() + .filter_map(|(key, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some(ty::AliasTy::new_from_args( + self.tcx, + key.def_id.into(), + key.args, + )); + } + } + + None + }) + .collect() + } + #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index fb777496e31eb..62dea547890b4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -23,7 +23,8 @@ use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, - MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_no_inference_or_external_constraints, + MaybeCause, NoSolution, OpaqueTypesJank, ParamEnvSource, QueryResult, + has_no_inference_or_external_constraints, }; enum AliasBoundKind { @@ -474,7 +475,7 @@ where // // cc trait-system-refactor-initiative#105 let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); - let certainty = Certainty::Maybe(cause); + let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood }; self.probe_trait_candidate(source) .enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty)) } @@ -974,11 +975,21 @@ where candidates: &mut Vec>, ) { let self_ty = goal.predicate.self_ty(); - // If the self type is sub unified with any opaque type, we - // also look at blanket impls for it. - let mut assemble_blanket_impls = false; - for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) { - assemble_blanket_impls = true; + // We only use this hack during HIR typeck. + let opaque_types = match self.typing_mode() { + TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty), + TypingMode::Coherence + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => vec![], + }; + + if opaque_types.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + return; + } + + for &alias_ty in &opaque_types { debug!("self ty is sub unified with {alias_ty:?}"); struct ReplaceOpaque { @@ -1028,10 +1039,11 @@ where } } - // We also need to consider blanket impls for not-yet-defined opaque types. + // If the self type is sub unified with any opaque type, we also look at blanket + // impls for it. // // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example. - if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() { + if assemble_from.should_assemble_impl_candidates() { let cx = self.cx(); cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { // For every `default impl`, there's always a non-default `impl` @@ -1062,7 +1074,15 @@ where } if candidates.is_empty() { - candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::ErrorIfRigidSelfTy, + }; + candidates + .extend(self.probe_trait_candidate(source).enter(|this| { + this.evaluate_added_goals_and_make_canonical_response(certainty) + })); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 169832ca5fbde..889588afe61ff 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -15,6 +15,7 @@ use rustc_index::IndexVec; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, @@ -141,8 +142,10 @@ where } }; - if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) = - certainty + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty { // If we have overflow, it's probable that we're substituting a type // into itself infinitely and any partial substitutions in the query @@ -155,7 +158,7 @@ where // // Changing this to retain some constraints in the future // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause)); + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); } let external_constraints = @@ -199,10 +202,13 @@ where .count(); if num_non_region_vars > self.cx().recursion_limit() { debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - })); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); } } } @@ -216,13 +222,14 @@ where /// ambiguity but return constrained variables to guide inference. pub(in crate::solve) fn make_ambiguous_response_no_constraints( &self, - maybe_cause: MaybeCause, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, ) -> CanonicalResponse { response_no_constraints_raw( self.cx(), self.max_input_universe, self.variables, - Certainty::Maybe(maybe_cause), + Certainty::Maybe { cause, opaque_types_jank }, ) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 3e3a5246f3d76..5df7c92d88145 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -8,6 +8,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -151,6 +152,15 @@ pub trait SolverDelegateEvalExt: SolverDelegate { stalled_on: Option>, ) -> Result, NoSolution>; + /// Checks whether evaluating `goal` may hold while treating not-yet-defined + /// opaque types as being kind of rigid. + /// + /// See the comment on [OpaqueTypesJank] for more details. + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal::Predicate>, + ) -> bool; + /// Check whether evaluating `goal` with a depth of `root_depth` may /// succeed. This only returns `false` if the goal is guaranteed to /// not hold. In case evaluation overflows and fails with ambiguity this @@ -193,6 +203,24 @@ where }) } + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal::Predicate>, + ) -> bool { + self.probe(|| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, None) + }) + .is_ok_and(|r| match r.certainty { + Certainty::Yes => true, + Certainty::Maybe { cause: _, opaque_types_jank } => match opaque_types_jank { + OpaqueTypesJank::AllGood => true, + OpaqueTypesJank::ErrorIfRigidSelfTy => false, + }, + }) + }) + } + fn root_goal_may_hold_with_depth( &self, root_depth: usize, @@ -407,8 +435,12 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) = - stalled_on + if let Some(GoalStalledOn { + num_opaques, + ref stalled_vars, + ref sub_roots, + stalled_certainty, + }) = stalled_on && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) && !sub_roots .iter() @@ -419,7 +451,7 @@ where NestedNormalizationGoals::empty(), GoalEvaluation { goal, - certainty: Certainty::Maybe(stalled_cause), + certainty: stalled_certainty, has_changed: HasChanged::No, stalled_on, }, @@ -468,7 +500,7 @@ where let stalled_on = match certainty { Certainty::Yes => None, - Certainty::Maybe(stalled_cause) => match has_changed { + Certainty::Maybe { .. } => match has_changed { // FIXME: We could recompute a *new* set of stalled variables by walking // through the orig values, resolving, and computing the root vars of anything // that is not resolved. Only when *these* have changed is it meaningful @@ -518,7 +550,7 @@ where .len(), stalled_vars, sub_roots, - stalled_cause, + stalled_certainty: certainty, }) } }, @@ -634,7 +666,7 @@ where if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, None)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -710,7 +742,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, with_resolved_vars, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -724,7 +756,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -1184,28 +1216,12 @@ where pub(crate) fn opaques_with_sub_unified_hidden_type( &self, self_ty: I::Ty, - ) -> impl Iterator> + use<'a, D, I> { - let delegate = self.delegate; - delegate - .clone_opaque_types_lookup_table() - .into_iter() - .chain(delegate.clone_duplicate_opaque_types()) - .filter_map(move |(key, hidden_ty)| { - if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { - if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { - if delegate.sub_unification_table_root_var(self_vid) - == delegate.sub_unification_table_root_var(hidden_vid) - { - return Some(ty::AliasTy::new_from_args( - delegate.cx(), - key.def_id.into(), - key.args, - )); - } - } - } - None - }) + ) -> Vec> { + if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() { + self.delegate.opaques_with_sub_unified_hidden_type(vid) + } else { + vec![] + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index cd27c9c26c1fd..fb900b592d13b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -158,9 +158,10 @@ where if self.may_use_unstable_feature(param_env, symbol) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Ambiguity, - )) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }) } } @@ -278,18 +279,21 @@ where fn bail_with_ambiguity(&mut self, candidates: &[Candidate]) -> CanonicalResponse { debug_assert!(candidates.len() > 1); - let maybe_cause = - candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| { - // Pull down the certainty of `Certainty::Yes` to ambiguity when combining + let (cause, opaque_types_jank) = candidates.iter().fold( + (MaybeCause::Ambiguity, OpaqueTypesJank::AllGood), + |(c, jank), candidates| { + // We pull down the certainty of `Certainty::Yes` to ambiguity when combining // these responses, b/c we're combining more than one response and this we // don't know which one applies. - let candidate = match candidates.result.value.certainty { - Certainty::Yes => MaybeCause::Ambiguity, - Certainty::Maybe(candidate) => candidate, - }; - maybe_cause.or(candidate) - }); - self.make_ambiguous_response_no_constraints(maybe_cause) + match candidates.result.value.certainty { + Certainty::Yes => (c, jank), + Certainty::Maybe { cause, opaque_types_jank } => { + (c.or(cause), jank.or(opaque_types_jank)) + } + } + }, + ); + self.make_ambiguous_response_no_constraints(cause, opaque_types_jank) } /// If we fail to merge responses we flounder and return overflow or ambiguity. @@ -427,6 +431,7 @@ pub struct GoalStalledOn { pub num_opaques: usize, pub stalled_vars: Vec, pub sub_roots: Vec, - /// The cause that will be returned on subsequent evaluations if this goal remains stalled. - pub stalled_cause: MaybeCause, + /// The certainty that will be returned on subsequent evaluations if this + /// goal remains stalled. + pub stalled_certainty: Certainty, } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index f0342e0523ff4..289325d70550c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -93,7 +93,7 @@ where fn is_ambiguous_result(result: QueryResult) -> bool { result.is_ok_and(|response| { has_no_inference_or_external_constraints(response) - && matches!(response.value.certainty, Certainty::Maybe(_)) + && matches!(response.value.certainty, Certainty::Maybe { .. }) }) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 575e0472e0e38..bff4f6ce3fc6b 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -204,7 +204,7 @@ where // // Only goals proven via the trait solver should be region dependent. Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.obligations.register(obligation, None); } } @@ -258,7 +258,7 @@ where infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); } } - Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index e31d1052d16c3..eef0ddcbf5965 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -95,15 +95,17 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( root_obligation.cause.span, None, ) { - Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, .. }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, @@ -266,7 +268,8 @@ impl<'tcx> BestObligation<'tcx> { ); // Skip nested goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + | (false, Err(_)) => {} _ => continue, } @@ -407,7 +410,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { let tcx = goal.infcx().tcx; // Skip goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } _ => return ControlFlow::Continue(()), } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 342d7121fc373..086a7a44786d6 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -332,7 +332,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert_matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) ); } inspect::ProbeStep::NestedProbe(ref probe) => { diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index fb1adc2fd2ac3..8d01c880f8c59 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -62,7 +62,7 @@ impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) { return ControlFlow::Break(Ok(None)); } @@ -95,7 +95,7 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + if matches!(other.result().unwrap(), Certainty::Maybe { .. }) { return false; } @@ -143,13 +143,13 @@ fn to_selection<'tcx>( span: Span, cand: inspect::InspectCandidate<'_, 'tcx>, ) -> Option> { - if let Certainty::Maybe(..) = cand.shallow_certainty() { + if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } let nested = match cand.result().expect("expected positive result") { Certainty::Yes => thin_vec![], - Certainty::Maybe(_) => cand + Certainty::Maybe { .. } => cand .instantiate_nested_goals(span) .into_iter() .map(|nested| { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 39afd77a8b682..8e8c7dd7c9d48 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -682,7 +682,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // was irrelevant. match goal.result() { Ok(Certainty::Yes) | Err(NoSolution) => return, - Ok(Certainty::Maybe(_)) => {} + Ok(Certainty::Maybe { .. }) => {} } // For bound predicates we simply call `infcx.enter_forall` diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 9e1a2a3e7d286..ae731505abfa5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,8 +1,11 @@ +use rustc_infer::traits::solve::Goal; use rustc_macros::extension; use rustc_middle::span_bug; +use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use crate::infer::InferCtxt; use crate::infer::canonical::OriginalQueryValues; +use crate::solve::SolverDelegate; use crate::traits::{ EvaluationResult, ObligationCtxt, OverflowError, PredicateObligation, SelectionContext, }; @@ -15,6 +18,20 @@ impl<'tcx> InferCtxt<'tcx> { self.evaluate_obligation_no_overflow(obligation).may_apply() } + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if self.next_trait_solver() { + <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new( + self.tcx, + obligation.param_env, + obligation.predicate, + )) + } else { + self.predicate_may_hold(obligation) + } + } + /// Evaluates whether the predicate can be satisfied in the given /// `ParamEnv`, and returns `false` if not certain. However, this is /// not entirely accurate if inference variables are involved. diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 56962b4597b88..f743b84bce689 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -6,7 +6,7 @@ use crate::fold::TypeFoldable; use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; -use crate::{self as ty, Interner}; +use crate::{self as ty, Interner, TyVid}; /// The current typing mode of an inference context. We unfortunately have some /// slightly different typing rules depending on the current context. See the @@ -271,6 +271,7 @@ pub trait InferCtxtLike: Sized { &self, prev_entries: Self::OpaqueTypeStorageEntries, ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + fn opaques_with_sub_unified_hidden_type(&self, ty: TyVid) -> Vec>; fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b48a8f46ebef7..1a1606d82685c 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -269,11 +269,70 @@ impl NestedNormalizationGoals { #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum Certainty { Yes, - Maybe(MaybeCause), + Maybe { cause: MaybeCause, opaque_types_jank: OpaqueTypesJank }, +} + +/// Supporting not-yet-defined opaque types in HIR typeck is somewhat +/// challenging. Ideally we'd normalize them to a new inference variable +/// and just defer type inference which relies on the opaque until we've +/// constrained the hidden type. +/// +/// This doesn't work for method and function calls as we need to guide type +/// inference for the function arguments. We treat not-yet-defined opaque types +/// as if they were rigid instead in these places. +/// +/// When we encounter a `?hidden_type_of_opaque: Trait` goal, we use the +/// item bounds and blanket impls to guide inference by constraining other type +/// variables, see `EvalCtxt::try_assemble_bounds_via_registered_opaques`. We +/// always keep the certainty as `Maybe` so that we properly prove these goals +/// once the hidden type has been constrained. +/// +/// If we fail to prove the trait goal via item bounds or blanket impls, the +/// goal would have errored if the opaque type were rigid. In this case, we +/// set `OpaqueTypesJank::ErrorIfRigidSelfTy` in the [Certainty]. +/// +/// Places in HIR typeck where we want to treat not-yet-defined opaque types as if +/// they were kind of rigid then use `fn root_goal_may_hold_opaque_types_jank` which +/// returns `false` if the goal doesn't hold or if `OpaqueTypesJank::ErrorIfRigidSelfTy` +/// is set (i.e. proving it required relies on some `?hidden_ty: NotInItemBounds` goal). +/// +/// This is subtly different from actually treating not-yet-defined opaque types as +/// rigid, e.g. it allows constraining opaque types if they are not the self-type of +/// a goal. It is good enough for now and only matters for very rare type inference +/// edge cases. We can improve this later on if necessary. +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum OpaqueTypesJank { + AllGood, + ErrorIfRigidSelfTy, +} +impl OpaqueTypesJank { + fn and(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::AllGood, OpaqueTypesJank::AllGood) => OpaqueTypesJank::AllGood, + (OpaqueTypesJank::ErrorIfRigidSelfTy, _) | (_, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + } + } + + pub fn or(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::ErrorIfRigidSelfTy, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + (OpaqueTypesJank::AllGood, _) | (_, OpaqueTypesJank::AllGood) => { + OpaqueTypesJank::AllGood + } + } + } } impl Certainty { - pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + pub const AMBIGUOUS: Certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }; /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -290,14 +349,23 @@ impl Certainty { pub fn and(self, other: Certainty) -> Certainty { match (self, other) { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, - (Certainty::Yes, Certainty::Maybe(_)) => other, - (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)), + (Certainty::Yes, Certainty::Maybe { .. }) => other, + (Certainty::Maybe { .. }, Certainty::Yes) => self, + ( + Certainty::Maybe { cause: a_cause, opaque_types_jank: a_jank }, + Certainty::Maybe { cause: b_cause, opaque_types_jank: b_jank }, + ) => Certainty::Maybe { + cause: a_cause.and(b_cause), + opaque_types_jank: a_jank.and(b_jank), + }, } } pub const fn overflow(suggest_increasing_limit: bool) -> Certainty { - Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }) + Certainty::Maybe { + cause: MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }, + opaque_types_jank: OpaqueTypesJank::AllGood, + } } } diff --git a/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr new file mode 100644 index 0000000000000..c54c1bba028cb --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr @@ -0,0 +1,62 @@ +error[E0369]: cannot add `{integer}` to `impl Sized` + --> $DIR/ambiguous-ops.rs:17:15 + | +LL | add() + 1 + | ----- ^ - {integer} + | | + | impl Sized + +error[E0368]: binary assignment operation `*=` cannot be applied to type `impl Sized` + --> $DIR/ambiguous-ops.rs:31:9 + | +LL | temp *= 2; + | ----^^^^^ + | | + | cannot use `*=` on type `impl Sized` + +error[E0614]: type `DerefWrapper` cannot be dereferenced + --> $DIR/ambiguous-ops.rs:57:22 + | +LL | let _rarw = &*explicit_deref(); + | ^^^^^^^^^^^^^^^^^ can't be dereferenced + +error[E0614]: type `DerefWrapper` cannot be dereferenced + --> $DIR/ambiguous-ops.rs:69:9 + | +LL | *explicit_deref_mut() = 1; + | ^^^^^^^^^^^^^^^^^^^^^ can't be dereferenced + +error[E0277]: the type `impl Sized` cannot be indexed by `_` + --> $DIR/ambiguous-ops.rs:94:18 + | +LL | let _y = explicit_index()[0]; + | ^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_` + | + = help: the trait `Index<_>` is not implemented for `impl Sized` +note: required for `IndexWrapper` to implement `Index<_>` + --> $DIR/ambiguous-ops.rs:81:22 + | +LL | impl, U> Index for IndexWrapper { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the type `impl Sized` cannot be indexed by `_` + --> $DIR/ambiguous-ops.rs:106:9 + | +LL | explicit_index_mut()[0] = 1; + | ^^^^^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_` + | + = help: the trait `Index<_>` is not implemented for `impl Sized` +note: required for `IndexWrapper` to implement `Index<_>` + --> $DIR/ambiguous-ops.rs:81:22 + | +LL | impl, U> Index for IndexWrapper { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0368, E0369, E0614. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs new file mode 100644 index 0000000000000..0aa5715339dc2 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs @@ -0,0 +1,117 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass + +// Make sure we support non-call operations for opaque types even if +// its not part of its item bounds. + +use std::ops::{Deref, DerefMut, Index, IndexMut}; + +fn mk() -> T { + todo!() +} + +fn add() -> impl Sized { + let unconstrained = if false { + add() + 1 + //[current]~^ ERROR cannot add `{integer}` to `impl Sized + } else { + let with_infer = mk(); + let _ = with_infer + 1; + with_infer + }; + let _: u32 = unconstrained; + 1u32 +} + +fn mul_assign() -> impl Sized { + if false { + let mut temp = mul_assign(); + temp *= 2; + //[current]~^ ERROR binary assignment operation `*=` cannot be applied to type `impl Sized` + } + + let mut with_infer = mk(); + with_infer *= 2; + let _: u32 = with_infer; + + 1u32 +} + +struct DerefWrapper(T); +impl Deref for DerefWrapper { + type Target = T::Target; + fn deref(&self) -> &Self::Target { + &*self.0 + } +} +impl DerefMut for DerefWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} + +fn explicit_deref() -> DerefWrapper { + if false { + let _rarw = &*explicit_deref(); + //[current]~^ ERROR type `DerefWrapper` cannot be dereferenced + + let mut with_infer = DerefWrapper(mk()); + let _rarw = &*with_infer; + with_infer + } else { + DerefWrapper(&1u32) + } +} +fn explicit_deref_mut() -> DerefWrapper { + if false { + *explicit_deref_mut() = 1; + //[current]~^ ERROR type `DerefWrapper` cannot be dereferenced + + let mut with_infer = DerefWrapper(Default::default()); + *with_infer = 1; + with_infer + } else { + DerefWrapper(Box::new(1u32)) + } +} + +struct IndexWrapper(T); +impl, U> Index for IndexWrapper { + type Output = T::Output; + fn index(&self, index: U) -> &Self::Output { + &self.0[index] + } +} +impl, U> IndexMut for IndexWrapper { + fn index_mut(&mut self, index: U) -> &mut Self::Output { + &mut self.0[index] + } +} +fn explicit_index() -> IndexWrapper { + if false { + let _y = explicit_index()[0]; + //[current]~^ ERROR the type `impl Sized` cannot be indexed by `_` + + let with_infer = IndexWrapper(Default::default()); + let _y = with_infer[0]; + with_infer + } else { + IndexWrapper([1u32]) + } +} +fn explicit_index_mut() -> IndexWrapper { + if false { + explicit_index_mut()[0] = 1; + //[current]~^ ERROR the type `impl Sized` cannot be indexed by `_` + + let mut with_infer = IndexWrapper(Default::default()); + with_infer[0] = 1; + with_infer + } else { + IndexWrapper([1u32]) + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs b/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs new file mode 100644 index 0000000000000..9b9156ee4c716 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs @@ -0,0 +1,73 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. Make sure calling +// opaque types works. + +fn fn_trait() -> impl Fn() { + if false { + let f = fn_trait(); + f(); + } + + || () +} + +fn fn_trait_ref() -> impl Fn() { + if false { + let f = &fn_trait(); + f(); + } + || () +} + +fn fn_mut() -> impl FnMut() -> usize { + if false { + let mut f = fn_mut(); + f(); + } + + let mut state = 0; + move || { + state += 1; + state + } +} + +fn fn_mut_ref() -> impl FnMut() -> usize { + if false { + let mut f = &mut fn_mut(); + f(); + } + + let mut state = 0; + move || { + state += 1; + state + } +} + + +fn fn_once() -> impl FnOnce() { + if false { + let mut f = fn_once(); + f(); + } + + let string = String::new(); + move || drop(string) +} + +fn fn_once_ref() -> impl FnOnce() { + if false { + let mut f = Box::new(fn_once_ref()); + f(); + } + + let string = String::new(); + move || drop(string) +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs b/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs new file mode 100644 index 0000000000000..5ff0dae55cc35 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs @@ -0,0 +1,56 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. We want to +// be able to step through `impl Deref` in its defining scope. +use std::ops::{Deref, DerefMut}; +fn impl_deref_fn() -> impl Deref usize)> { + if false { + let func = impl_deref_fn(); + func(|s| s.len()); + } + + &((|_| ()) as fn(_)) +} + +fn impl_deref_impl_fn() -> impl Deref { + if false { + let func = impl_deref_impl_fn(); + func(); + } + + &|| () +} + +fn impl_deref_impl_deref_impl_fn() -> impl Deref> { + if false { + let func = impl_deref_impl_deref_impl_fn(); + func(); + } + + &&|| () +} + + +fn impl_deref_mut_impl_fn() -> impl DerefMut { + if false { + let func = impl_deref_impl_fn(); + func(); + } + + Box::new(|| ()) +} + + +fn impl_deref_mut_impl_fn_mut() -> impl DerefMut { + if false { + let mut func = impl_deref_mut_impl_fn_mut(); + func(); + } + + let mut state = 0; + Box::new(move || state += 1) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs b/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs new file mode 100644 index 0000000000000..aa1b51d6906e4 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. + +struct ShExCompactPrinter; + +struct TripleExpr; + +impl ShExCompactPrinter { + fn pp_triple_expr(&self) -> impl Fn(&TripleExpr, &ShExCompactPrinter) + '_ { + move |te, printer| { + printer.pp_triple_expr()(te, printer); + } + } +} +fn main() {} diff --git a/tests/ui/inference/return-block-type-inference-15965.stderr b/tests/ui/inference/return-block-type-inference-15965.stderr index fc4f2defe7ffa..bfee904192204 100644 --- a/tests/ui/inference/return-block-type-inference-15965.stderr +++ b/tests/ui/inference/return-block-type-inference-15965.stderr @@ -1,10 +1,8 @@ error[E0282]: type annotations needed --> $DIR/return-block-type-inference-15965.rs:5:9 | -LL | / { return () } -LL | | -LL | | () - | |______^ cannot infer type +LL | { return () } + | ^^^^^^^^^^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr index 058dbb1e220f6..739182e120b4f 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr @@ -5,7 +5,7 @@ LL | let mut closure0 = None; | ^^^^^^^^^^^^ ... LL | return c(); - | --- type must be known at this point + | - type must be known at this point | help: consider giving `closure0` an explicit type, where the placeholders `_` are specified | From 9913c47da2b616fee57f308071d6adc39bff4568 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 15 Sep 2025 14:39:01 +0200 Subject: [PATCH 2/2] add tests, silence type annotations needed errors for opaques --- compiler/rustc_hir_typeck/src/opaque_types.rs | 34 ++++++----- ...ncorrect-choice-diagnostics.current.stderr | 53 +++++++++++++++++ ...r-incorrect-choice-diagnostics.next.stderr | 57 +++++++++++++++++++ .../call-expr-incorrect-choice-diagnostics.rs | 52 +++++++++++++++++ .../deref-constrains-self-ty.current.stderr | 16 ++++++ .../deref-constrains-self-ty.rs | 28 +++++++++ ...uble-wrap-with-defining-use.current.stderr | 4 +- .../double-wrap-with-defining-use.rs | 1 - .../ice-issue-146191.current.stderr | 2 +- .../ice-issue-146191.next.stderr | 10 +--- .../non-defining-uses/ice-issue-146191.rs | 1 - .../two_tait_defining_each_other2.next.stderr | 8 +-- .../two_tait_defining_each_other2.rs | 3 +- 13 files changed, 231 insertions(+), 38 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 97feac3d0099a..5cefa506b5a06 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -117,21 +117,25 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) } UsageKind::UnconstrainedHiddenType(hidden_type) => { - let infer_var = hidden_type - .ty - .walk() - .filter_map(ty::GenericArg::as_term) - .find(|term| term.is_infer()) - .unwrap_or_else(|| hidden_type.ty.into()); - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - hidden_type.span, - infer_var, - TypeAnnotationNeeded::E0282, - false, - ) - .emit() + if let Some(guar) = self.tainted_by_errors() { + guar + } else { + let infer_var = hidden_type + .ty + .walk() + .filter_map(ty::GenericArg::as_term) + .find(|term| term.is_infer()) + .unwrap_or_else(|| hidden_type.ty.into()); + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + hidden_type.span, + infer_var, + TypeAnnotationNeeded::E0282, + false, + ) + .emit() + } } UsageKind::HasDefiningUse => continue, }; diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr new file mode 100644 index 0000000000000..e213dab5d9618 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr @@ -0,0 +1,53 @@ +error[E0382]: use of moved value: `var` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9 + | +LL | let mut var = item_bound_is_too_weak(); + | ------- move occurs because `var` has type `impl FnOnce()`, which does not implement the `Copy` trait +LL | var(); + | ----- `var` moved due to this call +LL | var(); + | ^^^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9 + | +LL | var(); + | ^^^ + +error[E0618]: expected function, found `impl Sized` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9 + | +LL | fn opaque_type_no_impl_fn() -> impl Sized { + | ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `impl Sized` +LL | if false { +LL | opaque_type_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `impl Sized` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9 + | +LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + | --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `impl Sized` +LL | if false { +LL | opaque_type_no_impl_fn_incorrect()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `impl Deref` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9 + | +LL | fn opaque_type_deref_no_impl_fn() -> impl Deref { + | -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `impl Deref` +LL | if false { +LL | opaque_type_deref_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0382, E0618. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr new file mode 100644 index 0000000000000..5678349cad32d --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr @@ -0,0 +1,57 @@ +error[E0382]: use of moved value: `var` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9 + | +LL | let mut var = item_bound_is_too_weak(); + | ------- move occurs because `var` has type `{closure@$DIR/call-expr-incorrect-choice-diagnostics.rs:19:5: 19:12}`, which does not implement the `Copy` trait +LL | var(); + | ----- `var` moved due to this call +LL | var(); + | ^^^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9 + | +LL | var(); + | ^^^ +help: consider cloning the value if the performance cost is acceptable + | +LL | var.clone()(); + | ++++++++ + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9 + | +LL | fn opaque_type_no_impl_fn() -> impl Sized { + | ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `_` +LL | if false { +LL | opaque_type_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9 + | +LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + | --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `_` +LL | if false { +LL | opaque_type_no_impl_fn_incorrect()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9 + | +LL | fn opaque_type_deref_no_impl_fn() -> impl Deref { + | -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `_` +LL | if false { +LL | opaque_type_deref_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0382, E0618. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs new file mode 100644 index 0000000000000..1d73985f78a12 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs @@ -0,0 +1,52 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// Testing the errors in case we've made a wrong choice when +// calling an opaque. + +use std::ops::Deref; + +fn item_bound_is_too_weak() -> impl FnOnce() { + if false { + let mut var = item_bound_is_too_weak(); + var(); + var(); + //~^ ERROR use of moved value: `var` + } + + let mut state = String::new(); + move || state.push('a') +} + +fn opaque_type_no_impl_fn() -> impl Sized { + if false { + opaque_type_no_impl_fn()(); + //[current]~^ ERROR expected function, found `impl Sized` + //[next]~^^ ERROR expected function, found `_` + } + + 1 +} + +fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + if false { + opaque_type_no_impl_fn_incorrect()(); + //[current]~^ ERROR expected function, found `impl Sized` + //[next]~^^ ERROR expected function, found `_` + } + + || () +} + +fn opaque_type_deref_no_impl_fn() -> impl Deref { + if false { + opaque_type_deref_no_impl_fn()(); + //[current]~^ ERROR expected function, found `impl Deref` + //[next]~^^ ERROR expected function, found `_` + } + + &1 +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr new file mode 100644 index 0000000000000..bbe90e5873d04 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `len` found for struct `Wrapper` in the current scope + --> $DIR/deref-constrains-self-ty.rs:22:32 + | +LL | struct Wrapper(T); + | ----------------- method `len` not found for this struct +... +LL | let _ = Wrapper(foo()).len(); + | ^^^ method not found in `Wrapper` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `len`, perhaps you need to implement it: + candidate #1: `ExactSizeIterator` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs new file mode 100644 index 0000000000000..d143878bc7466 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass + +// A test which shows that autoderef can constrain opaque types even +// though it's supposed to treat not-yet-defined opaque types as +// mostly rigid. I don't think this should necessarily compile :shrug: +use std::ops::Deref; + +struct Wrapper(T); + +impl Deref for Wrapper> { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo() -> impl Sized { + if false { + let _ = Wrapper(foo()).len(); + //[current]~^ ERROR no method named `len` found for struct `Wrapper` in the current scope + } + + std::iter::once(1).collect() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr index 30424ec58f913..bac5b3e0cf441 100644 --- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic type parameter, found `impl Foo` - --> $DIR/double-wrap-with-defining-use.rs:12:26 + --> $DIR/double-wrap-with-defining-use.rs:11:26 | LL | fn a(x: T) -> impl Foo { | - this generic parameter must be used with a generic type parameter @@ -7,7 +7,7 @@ LL | if true { x } else { a(a(x)) } | ^^^^^^^ error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/double-wrap-with-defining-use.rs:12:26 + --> $DIR/double-wrap-with-defining-use.rs:11:26 | LL | if true { x } else { a(a(x)) } | ^^^^^^^ diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs index 734b1920772f7..39b327eff18ed 100644 --- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs @@ -1,7 +1,6 @@ // Regression test for ICE from issue #140545 // The error message is confusing and wrong, but that's a different problem (#139350) -//@ edition:2018 //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr index ccbe2d3593c66..5dc66f454652a 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr @@ -5,7 +5,7 @@ LL | fn create_complex_future() -> impl Future { | ^^^^^^^^^^^^^^^^ the trait `ReturnsSend` is not implemented for `()` | help: this trait has no implementations, consider adding one - --> $DIR/ice-issue-146191.rs:14:1 + --> $DIR/ice-issue-146191.rs:13:1 | LL | trait ReturnsSend {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr index e8b551c65fc0c..4a88359ca9679 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr @@ -4,14 +4,6 @@ error[E0282]: type annotations needed LL | async { create_complex_future().await } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type -error[E0282]: type annotations needed - --> $DIR/ice-issue-146191.rs:8:5 - | -LL | async { create_complex_future().await } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs index 356f7d01eb9b7..84f139da4e32a 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs @@ -8,7 +8,6 @@ fn create_complex_future() -> impl Future { async { create_complex_future().await } //[current]~^ ERROR recursion in an async block requires //[next]~^^ ERROR type annotations needed - //[next]~| ERROR type annotations needed } trait ReturnsSend {} diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr index 785e5fdeb6433..9b18a9715f21f 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr @@ -4,12 +4,6 @@ error[E0282]: type annotations needed LL | fn muh(x: A) -> B { | ^ cannot infer type -error[E0282]: type annotations needed - --> $DIR/two_tait_defining_each_other2.rs:14:5 - | -LL | x // B's hidden type is A (opaquely) - | ^ cannot infer type - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs index 99262f4bc4b38..ec2963249f9da 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs @@ -12,8 +12,7 @@ trait Foo {} fn muh(x: A) -> B { //[next]~^ ERROR: type annotations needed x // B's hidden type is A (opaquely) - //[next]~^ ERROR: type annotations needed - //[current]~^^ ERROR opaque type's hidden type cannot be another opaque type + //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } struct Bar;