From f783bb7405b58bf5a5ca3ba81a6a96b2307ed7b7 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Wed, 29 Apr 2026 07:28:11 +0200 Subject: [PATCH 1/4] move trait prototype bare min core support --- compiler/rustc_ast_lowering/src/item.rs | 8 +- .../src/diagnostics/region_errors.rs | 1 + .../rustc_borrowck/src/region_infer/mod.rs | 1 + .../src/type_check/canonical.rs | 45 +++++++++- compiler/rustc_borrowck/src/type_check/mod.rs | 4 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/lang_items.rs | 2 + compiler/rustc_hir_analysis/src/check/mod.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 5 ++ .../src/collect/item_bounds.rs | 18 +--- .../src/collect/predicates_of.rs | 27 +----- .../src/hir_ty_lowering/bounds.rs | 61 ++++++++++++- .../src/hir_ty_lowering/dyn_trait.rs | 11 +++ .../src/hir_ty_lowering/mod.rs | 2 +- .../src/multiple_supertrait_upcastable.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 1 + compiler/rustc_middle/src/traits/mod.rs | 2 + compiler/rustc_middle/src/ty/context.rs | 6 +- .../src/ty/context/impl_interner.rs | 4 + .../rustc_middle/src/ty/typeck_results.rs | 2 +- .../src/solve/trait_goals.rs | 6 +- compiler/rustc_span/src/symbol.rs | 1 + .../src/error_reporting/traits/suggestions.rs | 3 + .../src/traits/select/candidate_assembly.rs | 4 +- compiler/rustc_type_ir/src/interner.rs | 2 + library/core/src/clone.rs | 16 ++-- library/core/src/lib.rs | 1 + library/core/src/marker.rs | 85 ++++++++++++++----- .../feature-gates/feature-gate-move-trait.rs | 16 ++++ .../default_auto_traits/extern-types.rs | 5 +- 30 files changed, 261 insertions(+), 84 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-move-trait.rs diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 3e0bc072a8d04..f7b37fc4f0351 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -552,6 +552,12 @@ impl<'hir> LoweringContext<'_, 'hir> { let constness = self.lower_constness(*constness); let impl_restriction = self.lower_impl_restriction(impl_restriction); let ident = self.lower_ident(*ident); + // nia: fixme: should only allow ?Move in particular + let policy = if self.tcx.features().move_trait() { + RelaxedBoundPolicy::Allowed + } else { + RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::SuperTrait) + }; let (generics, (safety, items, bounds)) = self.lower_generics( generics, id, @@ -559,7 +565,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this| { let bounds = this.lower_param_bounds( bounds, - RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::SuperTrait), + policy, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ); let items = this.arena.alloc_from_iter( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index f9c91c3371516..765f2969ef230 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -53,6 +53,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => "generic argument ", ConstraintCategory::TypeAnnotation(_) => "type annotation ", ConstraintCategory::SizedBound => "proving this value is `Sized` ", + ConstraintCategory::MoveBound => "proving this value is `Move` ", ConstraintCategory::CopyBound => "copying this value ", ConstraintCategory::OpaqueType => "opaque type ", ConstraintCategory::ClosureUpvar(_) => "closure capture ", diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2a759387788a7..80ba00b096b0e 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1781,6 +1781,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { | ConstraintCategory::CallArgument(_) | ConstraintCategory::CopyBound | ConstraintCategory::SizedBound + | ConstraintCategory::MoveBound | ConstraintCategory::Assignment | ConstraintCategory::Usage | ConstraintCategory::ClosureUpvar(_) => 2, diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 9c8093d793c35..1f66cde1a4024 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -1,6 +1,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_middle::bug; @@ -8,8 +9,13 @@ use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Unnormalized, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; -use rustc_trait_selection::traits::ObligationCause; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_trait_selection::solve::NoSolution; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; +use rustc_trait_selection::traits::{ + Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, +}; use tracing::{debug, instrument}; use super::{Locations, NormalizeLocation, TypeChecker}; @@ -183,6 +189,43 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } + /// `Move` proofs may error during MIR typeck, so handle them separately. + pub(super) fn prove_move_predicate(&mut self, ty: Ty<'tcx>, locations: Locations) { + let tcx = self.tcx(); + let span = locations.span(self.body); + + let trait_ref = + ty::TraitRef::new(tcx, tcx.require_lang_item(rustc_hir::LangItem::Move, span), [ty]); + let predicate: ty::Predicate<'_> = + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Trait( + ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive }, + ))) + .upcast(tcx); + let key = self.infcx.param_env.and(type_op::prove_predicate::ProvePredicate { predicate }); + let op = CustomTypeOp::new( + |ocx| { + let res = type_op::QueryTypeOp::perform_locally_with_next_solver(ocx, key, span); + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { + // Reprove `Move` with diagnostics + let ocx = ObligationCtxt::new_with_diagnostics(ocx.infcx); + ocx.register_obligation(Obligation::new( + tcx, + ObligationCause::new(span, CRATE_DEF_ID, ObligationCauseCode::MovingMove), + self.infcx.param_env, + predicate, + )); + let errs = ocx.evaluate_obligations_error_on_ambiguity(); + ocx.infcx.err_ctxt().report_fulfillment_errors(errs); + }; + res + }, + "move query type op", + ); + + let _: Result<_, ErrorGuaranteed> = + self.fully_perform_op(locations, ConstraintCategory::MoveBound, op); + } + pub(super) fn normalize(&mut self, value: T, location: impl NormalizeLocation) -> T where T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8998ced10bf9e..15afefd287054 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1827,6 +1827,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // that if a value of some type could implement `Copy`, then // it must. self.prove_trait_ref(trait_ref, location.to_locations(), ConstraintCategory::CopyBound); + } else if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) = context + && tcx.features().move_trait() + { + self.prove_move_predicate(place_ty.ty, location.to_locations()); } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 65bb487c7b8eb..f679692b8e4fd 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -636,6 +636,8 @@ declare_features! ( (unstable, mips_target_feature, "1.27.0", Some(150253)), /// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns. (unstable, more_qualified_paths, "1.54.0", Some(86935)), + /// The `Move` autotrait. + (incomplete, move_trait, "CURRENT_RUSTC_VERSION", Some(149607)), /// The `movrs` target feature on x86. (unstable, movrs_target_feature, "1.88.0", Some(137976)), /// Allows the `multiple_supertrait_upcastable` lint. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index ea014e8a275b5..d74ff78984644 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -262,6 +262,8 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + Move, sym::move_trait, move_trait, Target::Trait, GenericRequirement::None; + OrderingEnum, sym::Ordering, ordering_enum, Target::Enum, GenericRequirement::Exact(0); PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 3225e00b24b26..cb6ba24b31d2b 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -340,7 +340,7 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::Trait(trait_predicate) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); - if !tcx.is_default_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) { + if !tcx.is_implicit_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) { // Do not add that restriction to the list if it is a positive requirement. entry.push(trait_predicate.def_id()); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 63753aee383a0..780b208bf032b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1174,6 +1174,11 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuarant return Ok(()); } + if tcx.is_lang_item(def_id.into(), LangItem::Move) && !tcx.features().move_trait() { + // `Move` trivially holds if the feature is disabled. + return Ok(()); + } + let trait_def = tcx.trait_def(def_id); if trait_def.is_marker || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 4409f2c068eb8..c5c8cfa0a6218 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -54,14 +54,7 @@ fn associated_type_bounds<'tcx>( | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { // Implicit bounds are added to associated types unless a `?Trait` bound is found. - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - item_ty, - hir_bounds, - ImpliedBoundsContext::AssociatedTypeOrImplTrait, - span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, item_ty, hir_bounds, @@ -380,14 +373,7 @@ fn opaque_type_bounds<'tcx>( | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - item_ty, - hir_bounds, - ImpliedBoundsContext::AssociatedTypeOrImplTrait, - span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, item_ty, hir_bounds, diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ad46f011a79cb..a897c66bbe7b2 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -196,14 +196,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen PredicateFilter::All, OverlappingAsssocItemConstraints::Allowed, ); - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - tcx.types.self_param, - self_bounds, - ImpliedBoundsContext::TraitDef(def_id), - span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, tcx.types.self_param, self_bounds, @@ -235,14 +228,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen let param_ty = icx.lowerer().lower_ty_param(param.hir_id); let mut bounds = Vec::new(); // Implicit bounds are added to type params unless a `?Trait` bound is found - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - param_ty, - &[], - ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), - param.span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, param_ty, &[], @@ -691,14 +677,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - self_param_ty, - superbounds, - ImpliedBoundsContext::TraitDef(trait_def_id), - item.span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, self_param_ty, superbounds, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6e2c7a82af3b1..7276b90722236 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -151,6 +151,20 @@ fn add_trait_bound<'tcx>( } impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Adds implicit sizedness, `Move`, and default trait bounds. + pub(crate) fn add_implicit_bounds( + &self, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + self_ty: Ty<'tcx>, + hir_bounds: &'tcx [hir::GenericBound<'tcx>], + context: ImpliedBoundsContext<'tcx>, + span: Span, + ) { + self.add_implicit_move_bound(bounds, self_ty, hir_bounds, context, span); + self.add_default_traits(bounds, self_ty, hir_bounds, context, span); + self.add_implicit_sizedness_bounds(bounds, self_ty, hir_bounds, context, span); + } + /// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type. /// /// - On parameters, opaque type and associated types, add default `Sized` bound if no explicit @@ -234,6 +248,51 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + pub(crate) fn add_implicit_move_bound( + &self, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + self_ty: Ty<'tcx>, + hir_bounds: &[hir::GenericBound<'tcx>], + context: ImpliedBoundsContext<'tcx>, + span: Span, + ) { + let tcx = self.tcx(); + + // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` + if find_attr!(tcx, crate, RustcNoImplicitBounds) { + return; + } + + match context { + ImpliedBoundsContext::TraitDef(trait_did) => { + // Don't add default move supertrait to default traits. + let did = trait_did.to_def_id(); + if tcx.trait_is_auto(did) + || tcx.is_sizedness_trait(did) + || tcx.is_default_trait(did) + { + return; + } + } + ImpliedBoundsContext::AssociatedTypeOrImplTrait | ImpliedBoundsContext::TyParam(..) => { + } + } + + let relaxed_bounds = collect_relaxed_bounds(hir_bounds, context); + self.reject_duplicate_relaxed_bounds(relaxed_bounds); + + let Some(move_did) = tcx.lang_items().move_trait() else { + if tcx.features().move_trait() { + let _ = tcx.require_lang_item(hir::LangItem::Move, span); + } + return; + }; + let move_bounds = collect_bounds(hir_bounds, context, move_did); + if !move_bounds.any() { + add_trait_bound(tcx, bounds, self_ty, move_did, span); + } + } + pub(crate) fn add_default_traits( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, @@ -317,7 +376,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); if let Res::Def(DefKind::Trait, def_id) = trait_ref.path.res - && (tcx.is_lang_item(def_id, hir::LangItem::Sized) || tcx.is_default_trait(def_id)) + && (tcx.is_lang_item(def_id, hir::LangItem::Sized) || tcx.is_implicit_trait(def_id)) { return; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index a498e97403881..a8b0de3854b7a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -76,6 +76,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + // Don't use `add_implicit_bounds` directly to skip adding `Sized`. + self.add_implicit_move_bound( + &mut user_written_bounds, + dummy_self, + &hir_bounds + .iter() + .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + .collect::>(), + ImpliedBoundsContext::AssociatedTypeOrImplTrait, + span, + ); self.add_default_traits( &mut user_written_bounds, dummy_self, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 473a32bac03a9..48116c57d712d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3200,7 +3200,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { PredicateFilter::All, OverlappingAsssocItemConstraints::Allowed, ); - self.add_implicit_sizedness_bounds( + self.add_implicit_bounds( &mut bounds, self_ty, hir_bounds, diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index be6bd68c8140f..f4d8a432aeedf 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { .map(Unnormalized::skip_norm_wip) .filter_map(|(pred, _)| pred.as_trait_clause()) .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)) - .filter(|pred| !cx.tcx.is_default_trait(pred.def_id())); + .filter(|pred| !cx.tcx.is_implicit_trait(pred.def_id())); if direct_super_traits_iter.count() > 1 { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index bd029278e7584..cb5b9de19f39e 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -120,6 +120,7 @@ pub enum ConstraintCategory<'tcx> { CallArgument(Option>), CopyBound, SizedBound, + MoveBound, Assignment, /// A constraint that came from a usage of a variable (e.g. in an ADT expression /// like `Foo { field: my_val }`) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index daa0df4013272..7211e357ab369 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -241,6 +241,8 @@ pub enum ObligationCauseCode<'tcx> { SizedCallReturnType, /// Yield type must be `Sized`. SizedYieldType, + /// Moved values must be `Move`. + MovingMove, /// Inline asm operand type must be `Sized`. InlineAsmSized, /// Captured closure type must be `Sized`. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d1048a65a7be0..775f851303e70 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -888,7 +888,7 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_memory_dedup(alloc, salt) } - /// Traits added on all bounds by default, excluding `Sized` which is treated separately. + /// Traits added on all bounds by default, excluding `Sized` and `Move` which are treated separately. pub fn default_traits(self) -> &'static [rustc_hir::LangItem] { if self.sess.opts.unstable_opts.experimental_default_bounds { &[ @@ -906,6 +906,10 @@ impl<'tcx> TyCtxt<'tcx> { self.default_traits().iter().any(|&default_trait| self.is_lang_item(def_id, default_trait)) } + pub fn is_implicit_trait(self, def_id: DefId) -> bool { + self.is_default_trait(def_id) || matches!(self.as_lang_item(def_id), Some(LangItem::Move)) + } + pub fn is_sizedness_trait(self, def_id: DefId) -> bool { matches!(self.as_lang_item(def_id), Some(LangItem::Sized | LangItem::MetaSized)) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 6f6317d53d97b..49b9fe16d7e16 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -471,6 +471,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_default_trait(def_id) } + fn is_implicit_trait(self, def_id: Self::TraitId) -> bool { + self.is_implicit_trait(def_id) + } + fn is_sizedness_trait(self, def_id: DefId) -> bool { self.is_sizedness_trait(def_id) } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 1287047581196..abd7ed750b4a8 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -203,7 +203,7 @@ pub struct TypeckResults<'tcx> { /// Goals proven during HIR typeck which may be potentially region dependent. /// - /// Borrowck *uniquifies* regions which may cause these goal to be ambiguous in MIR + /// Borrowck *uniquifies* regions which may cause these goals to be ambiguous in MIR /// type check. We ICE if goals fail in borrowck to detect bugs during MIR building or /// missed checks in HIR typeck. To avoid ICE due to region dependence we store all /// goals which may be region dependent and reprove them in case borrowck encounters diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index b97d895bec15f..a2922aa9c1091 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1253,9 +1253,11 @@ where Some(self.forced_ambiguity(MaybeInfo::AMBIGUOUS)) } - // Backward compatibility for default auto traits. + // Backward compatibility for default auto traits & `Move`. // Test: ui/traits/default_auto_traits/extern-types.rs - ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(), + ty::Foreign(..) if self.cx().is_implicit_trait(goal.predicate.def_id()) => { + check_impls() + } // These types cannot be structurally decomposed into constituent // types, and therefore have no built-in auto impl. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 695103a249b49..7a798346c0691 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1323,6 +1323,7 @@ symbols! { movbe_target_feature, move_ref_pattern, move_size_limit, + move_trait, movrs_target_feature, msp430, mul, 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 148f1471b1b66..dcf7afa31367d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3927,6 +3927,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::SizedYieldType => { err.note("the yield type of a coroutine must have a statically known size"); } + ObligationCauseCode::MovingMove => { + err.note("moved values must be of a type that is `Move`"); + } ObligationCauseCode::AssignmentLhsSized => { err.note("the left-hand-side of an assignment must have a statically known size"); } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f7614e7c9730a..085da5cbeee5a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -777,9 +777,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // still be provided by a manual implementation for // this trait and type. - // Backward compatibility for default auto traits. + // Backward compatibility for default auto traits & `Move`. // Test: ui/traits/default_auto_traits/extern-types.rs - if self.tcx().is_default_trait(def_id) { + if self.tcx().is_implicit_trait(def_id) { check_impls() } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index fba43b9cffbe2..696149c3ff0e3 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -336,6 +336,8 @@ pub trait Interner: fn is_default_trait(self, def_id: Self::TraitId) -> bool; + fn is_implicit_trait(self, def_id: Self::TraitId) -> bool; + fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool; fn as_lang_item(self, def_id: Self::DefId) -> Option; diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index f2fa6fd0ca3e9..c0fd19c4641b5 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -602,7 +602,7 @@ unsafe impl CloneToUninit for crate::bstr::ByteStr { /// in `rustc_trait_selection`. mod impls { use super::TrivialClone; - use crate::marker::PointeeSized; + use crate::marker::{Move, PointeeSized}; macro_rules! impl_clone { ($($t:ty)*) => { @@ -647,7 +647,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] - impl const Clone for *const T { + impl const Clone for *const T { #[inline(always)] fn clone(&self) -> Self { *self @@ -657,11 +657,11 @@ mod impls { #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] - unsafe impl const TrivialClone for *const T {} + unsafe impl const TrivialClone for *const T {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] - impl const Clone for *mut T { + impl const Clone for *mut T { #[inline(always)] fn clone(&self) -> Self { *self @@ -671,12 +671,12 @@ mod impls { #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] - unsafe impl const TrivialClone for *mut T {} + unsafe impl const TrivialClone for *mut T {} /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] - impl const Clone for &T { + impl const Clone for &T { #[inline(always)] #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { @@ -687,9 +687,9 @@ mod impls { #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] - unsafe impl const TrivialClone for &T {} + unsafe impl const TrivialClone for &T {} /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] - impl !Clone for &mut T {} + impl !Clone for &mut T {} } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3451b2976bb73..167f7363767ad 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -150,6 +150,7 @@ #![feature(macro_metavar_expr_concat)] #![feature(marker_trait_attr)] #![feature(min_specialization)] +#![feature(move_trait)] #![feature(multiple_supertrait_upcastable)] #![feature(must_not_suspend)] #![feature(negative_impls)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index f56a4d7308e90..b5254a96f7f5b 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -232,7 +232,7 @@ pub trait PointeeSized { #[lang = "unsize"] #[rustc_deny_explicit_impl] #[rustc_dyn_incompatible_trait] -pub trait Unsize: PointeeSized { +pub trait Unsize: PointeeSized + ?Move { // Empty. } @@ -254,7 +254,7 @@ pub trait Unsize: PointeeSized { #[unstable(feature = "structural_match", issue = "31434")] #[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] #[lang = "structural_peq"] -pub trait StructuralPartialEq { +pub trait StructuralPartialEq: ?Move { // Empty. } @@ -267,9 +267,9 @@ marker_impls! { char, str /* Technically requires `[u8]: StructuralPartialEq` */, (), - {T, const N: usize} [T; N], - {T} [T], - {T: PointeeSized} &T, + {T: ?Move, const N: usize} [T; N], + {T: ?Move} [T], + {T: PointeeSized + ?Move} &T, } /// Types whose values can be duplicated simply by copying bits. @@ -475,8 +475,8 @@ marker_impls! { isize, i8, i16, i32, i64, i128, f16, f32, f64, f128, bool, char, - {T: PointeeSized} *const T, - {T: PointeeSized} *mut T, + {T: PointeeSized + ?Move} *const T, + {T: PointeeSized + ?Move} *mut T, } @@ -485,7 +485,7 @@ impl Copy for ! {} /// Shared references can be copied, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] -impl Copy for &T {} +impl Copy for &T {} /// Marker trait for the types that are allowed in union fields and unsafe /// binder types. @@ -669,9 +669,9 @@ pub unsafe auto trait Sync { } #[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for *const T {} +impl !Sync for *const T {} #[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for *mut T {} +impl !Sync for *mut T {} /// Zero-sized type used to mark things that "act like" they own a `T`. /// @@ -808,43 +808,43 @@ impl !Sync for *mut T {} /// [drop check]: Drop#drop-check #[lang = "phantom_data"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct PhantomData; +pub struct PhantomData; #[stable(feature = "rust1", since = "1.0.0")] -impl Hash for PhantomData { +impl Hash for PhantomData { #[inline] fn hash(&self, _: &mut H) {} } #[stable(feature = "rust1", since = "1.0.0")] -impl cmp::PartialEq for PhantomData { +impl cmp::PartialEq for PhantomData { fn eq(&self, _other: &PhantomData) -> bool { true } } #[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Eq for PhantomData {} +impl cmp::Eq for PhantomData {} #[stable(feature = "rust1", since = "1.0.0")] -impl cmp::PartialOrd for PhantomData { +impl cmp::PartialOrd for PhantomData { fn partial_cmp(&self, _other: &PhantomData) -> Option { Option::Some(cmp::Ordering::Equal) } } #[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Ord for PhantomData { +impl cmp::Ord for PhantomData { fn cmp(&self, _other: &PhantomData) -> cmp::Ordering { cmp::Ordering::Equal } } #[stable(feature = "rust1", since = "1.0.0")] -impl Copy for PhantomData {} +impl Copy for PhantomData {} #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for PhantomData { +impl Clone for PhantomData { fn clone(&self) -> Self { *self } @@ -852,18 +852,18 @@ impl Clone for PhantomData { #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] -unsafe impl TrivialClone for PhantomData {} +unsafe impl TrivialClone for PhantomData {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] -impl const Default for PhantomData { +impl const Default for PhantomData { fn default() -> Self { Self } } #[unstable(feature = "structural_match", issue = "31434")] -impl StructuralPartialEq for PhantomData {} +impl StructuralPartialEq for PhantomData {} /// Compiler-internal trait used to indicate the type of enum discriminants. /// @@ -906,7 +906,7 @@ pub trait DiscriminantKind { pub unsafe auto trait Freeze {} #[unstable(feature = "freeze", issue = "121675")] -impl !Freeze for UnsafeCell {} +impl !Freeze for UnsafeCell {} marker_impls! { #[unstable(feature = "freeze", issue = "121675")] unsafe Freeze for @@ -928,7 +928,7 @@ marker_impls! { pub unsafe auto trait UnsafeUnpin {} #[unstable(feature = "unsafe_unpin", issue = "125735")] -impl !UnsafeUnpin for UnsafePinned {} +impl !UnsafeUnpin for UnsafePinned {} marker_impls! { #[unstable(feature = "unsafe_unpin", issue = "125735")] unsafe UnsafeUnpin for @@ -1347,3 +1347,42 @@ pub macro CoercePointee($item:item) { pub trait CoercePointeeValidated { /* compiler built-in */ } + +/// Marks a type as freely movable to another address in memory. +/// +/// By default, all types are assumed to be `Move`. Similar to [`Sized`], the +/// syntax `T: ?Move` can be used to relax this bound for some type `T`. However, +/// `Move` bounds are implicitly added in *all* cases unless explicitly opted out +/// of - notably, in trait definitions and associated types. If the `move_trait` +/// feature is enabled, these can similarly be opted out of: +/// ``` +/// #![feature(move_trait)] +/// use std::marker::Move; +/// +/// trait Foo: ?Move { +/// type Bar: ?Move; +/// } +/// ``` +#[lang = "move_trait"] +#[rustc_unsafe_specialization_marker] +#[rustc_coinductive] +#[diagnostic::on_unimplemented( + message = "values of type `{Self}` may not be movable", + label = "may not be movable" +)] +#[unstable(feature = "move_trait", issue = "149607")] +pub unsafe auto trait Move { + // Empty. +} + +marker_impls! { + #[unstable(feature = "move_trait", issue = "149607")] + unsafe Move for + {T: PointeeSized + ?Move} *const T, + {T: PointeeSized + ?Move} *mut T, + {T: PointeeSized + ?Move} &T, + {T: PointeeSized + ?Move} &mut T, + {T: PointeeSized + ?Move} PhantomData, + {T: PointeeSized + ?Move} pattern_type!(*const T is !null), + {T: PointeeSized + ?Move} pattern_type!(*mut T is !null), +} diff --git a/tests/ui/feature-gates/feature-gate-move-trait.rs b/tests/ui/feature-gates/feature-gate-move-trait.rs new file mode 100644 index 0000000000000..7ff24c5f274af --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-move-trait.rs @@ -0,0 +1,16 @@ +#![feature(move_trait)] +#![feature(negative_impls)] + +use std::marker::Move; + +struct A; +struct B; +unsafe impl !Move for B {} + +fn main() { + let a = A; + let _new_a = a; + + let b = B; + let _new_b = b; +} diff --git a/tests/ui/traits/default_auto_traits/extern-types.rs b/tests/ui/traits/default_auto_traits/extern-types.rs index df106d83171c9..c0b9ae628b6b1 100644 --- a/tests/ui/traits/default_auto_traits/extern-types.rs +++ b/tests/ui/traits/default_auto_traits/extern-types.rs @@ -2,7 +2,7 @@ //@ revisions: current next //@ [next] compile-flags: -Znext-solver -#![feature(auto_traits, extern_types, lang_items, negative_impls, no_core, rustc_attrs)] +#![feature(auto_traits, extern_types, lang_items, move_trait, negative_impls, no_core, rustc_attrs)] #![allow(incomplete_features)] #![no_std] #![no_core] @@ -19,6 +19,9 @@ trait Sized: MetaSized {} #[lang = "copy"] pub trait Copy {} +#[lang = "move_trait"] +unsafe auto trait Move {} + #[lang = "default_trait1"] auto trait Leak {} From 97218d337e2e301669bbb177fa3ecd379fa56a7b Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Mon, 4 May 2026 09:56:00 +0200 Subject: [PATCH 2/4] fixup candidate selection --- compiler/rustc_borrowck/src/type_check/mod.rs | 6 ++++- .../src/traits/select/mod.rs | 25 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 15afefd287054..957ebd2709246 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1808,6 +1808,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_place(place, context, location); let tcx = self.tcx(); let place_ty = place.ty(self.body, tcx); + let move_tr = tcx.features().move_trait(); if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { let trait_ref = ty::TraitRef::new( tcx, @@ -1827,8 +1828,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // that if a value of some type could implement `Copy`, then // it must. self.prove_trait_ref(trait_ref, location.to_locations(), ConstraintCategory::CopyBound); + if move_tr { + self.prove_move_predicate(place_ty.ty, location.to_locations()); + } } else if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) = context - && tcx.features().move_trait() + && move_tr { self.prove_move_predicate(place_ty.ty, location.to_locations()); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9eca0f31a4402..9a52569474479 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -479,9 +479,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let has_non_region_infer = stack.obligation.predicate.has_non_region_infer(); let candidate_preference_mode = CandidatePreferenceMode::compute(self.tcx(), stack.obligation.predicate.def_id()); - if let Some(candidate) = - self.winnow_candidates(has_non_region_infer, candidate_preference_mode, candidates) - { + let is_default_auto_trait = self + .tcx() + .as_lang_item(stack.obligation.predicate.def_id()) + .is_some_and(|lang_item| matches!(lang_item, LangItem::Move)); + if let Some(candidate) = self.winnow_candidates( + has_non_region_infer, + is_default_auto_trait, + candidate_preference_mode, + candidates, + ) { self.filter_reservation_impls(candidate) } else { Ok(None) @@ -1851,6 +1858,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn winnow_candidates( &mut self, has_non_region_infer: bool, + is_default_auto_trait: bool, candidate_preference_mode: CandidatePreferenceMode, mut candidates: Vec>, ) -> Option> { @@ -1859,8 +1867,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } // We prefer `Sized` candidates over everything. - let mut sized_candidates = - candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate)); + let mut sized_candidates = candidates.iter().filter(|c| { + matches!(c.candidate, SizedCandidate) + || (is_default_auto_trait + && matches!(c.candidate, AutoImplCandidate | ImplCandidate(..))) + }); if let Some(sized_candidate) = sized_candidates.next() { // There should only ever be a single sized candidate // as they would otherwise overlap. @@ -1914,7 +1925,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { }); // Extract non-nested alias bound candidates, will be preferred over where bounds if // we're proving an auto-trait, sizedness trait or default trait. - if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) { + if is_default_auto_trait + || matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) + { match alias_bounds .clone() .filter_map(|(idx, kind)| (kind == AliasBoundKind::SelfBounds).then_some(idx)) From cd8838078c6a257e5a82ae894c0c3839c8d3c2f1 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Mon, 4 May 2026 10:25:45 +0200 Subject: [PATCH 3/4] oopers --- compiler/rustc_borrowck/src/type_check/canonical.rs | 1 - compiler/rustc_hir_analysis/src/check/wfcheck.rs | 5 ----- 2 files changed, 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 1f66cde1a4024..85b18168c694b 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -10,7 +10,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Unnormalized, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::solve::NoSolution; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 780b208bf032b..63753aee383a0 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1174,11 +1174,6 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuarant return Ok(()); } - if tcx.is_lang_item(def_id.into(), LangItem::Move) && !tcx.features().move_trait() { - // `Move` trivially holds if the feature is disabled. - return Ok(()); - } - let trait_def = tcx.trait_def(def_id); if trait_def.is_marker || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) From 87d4002421825d91aa701a2b971a02202cee6c39 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Fri, 8 May 2026 16:33:58 +0200 Subject: [PATCH 4/4] address most of the review, needs cleanup --- .../src/type_check/canonical.rs | 32 ++---- compiler/rustc_borrowck/src/type_check/mod.rs | 30 +++-- compiler/rustc_hir_analysis/src/check/mod.rs | 5 +- .../src/collect/item_bounds.rs | 2 + .../src/collect/predicates_of.rs | 14 +-- .../src/hir_ty_lowering/bounds.rs | 52 ++++----- .../src/hir_ty_lowering/dyn_trait.rs | 34 ++++-- .../src/hir_ty_lowering/mod.rs | 1 + compiler/rustc_infer/src/traits/engine.rs | 10 +- compiler/rustc_infer/src/traits/mod.rs | 2 +- .../src/multiple_supertrait_upcastable.rs | 3 +- compiler/rustc_middle/src/traits/select.rs | 9 ++ compiler/rustc_middle/src/ty/context.rs | 6 +- .../src/ty/context/impl_interner.rs | 4 +- .../src/solve/trait_goals.rs | 4 +- .../src/traits/engine.rs | 9 ++ .../rustc_trait_selection/src/traits/mod.rs | 6 + .../src/traits/query/type_op/custom.rs | 107 ++++++++++++++---- .../src/traits/query/type_op/mod.rs | 13 ++- .../src/traits/select/candidate_assembly.rs | 2 +- .../src/traits/select/mod.rs | 23 +--- compiler/rustc_type_ir/src/interner.rs | 2 +- 22 files changed, 229 insertions(+), 141 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 85b18168c694b..afc09f183f149 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -1,7 +1,6 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; -use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_middle::bug; @@ -9,12 +8,9 @@ use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Unnormalized, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; -use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use rustc_trait_selection::traits::query::type_op::custom::FallibleCustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; -use rustc_trait_selection::traits::{ - Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, -}; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; use tracing::{debug, instrument}; use super::{Locations, NormalizeLocation, TypeChecker}; @@ -200,23 +196,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive }, ))) .upcast(tcx); - let key = self.infcx.param_env.and(type_op::prove_predicate::ProvePredicate { predicate }); - let op = CustomTypeOp::new( + let op = FallibleCustomTypeOp::new( |ocx| { - let res = type_op::QueryTypeOp::perform_locally_with_next_solver(ocx, key, span); - if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { - // Reprove `Move` with diagnostics - let ocx = ObligationCtxt::new_with_diagnostics(ocx.infcx); - ocx.register_obligation(Obligation::new( - tcx, - ObligationCause::new(span, CRATE_DEF_ID, ObligationCauseCode::MovingMove), - self.infcx.param_env, - predicate, - )); - let errs = ocx.evaluate_obligations_error_on_ambiguity(); - ocx.infcx.err_ctxt().report_fulfillment_errors(errs); - }; - res + ocx.register_obligation(Obligation::new( + ocx.infcx.tcx, + ObligationCause::dummy_with_span(span), + self.infcx.param_env, + predicate, + )); + Ok(()) }, "move query type op", ); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 957ebd2709246..9842b62c79001 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -18,7 +18,7 @@ use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::{ BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, }; -use rustc_infer::traits::PredicateObligations; +use rustc_infer::traits::{PredicateObligations, ScrubbedTraitError}; use rustc_middle::bug; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -1808,7 +1808,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_place(place, context, location); let tcx = self.tcx(); let place_ty = place.ty(self.body, tcx); - let move_tr = tcx.features().move_trait(); if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { let trait_ref = ty::TraitRef::new( tcx, @@ -1828,11 +1827,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // that if a value of some type could implement `Copy`, then // it must. self.prove_trait_ref(trait_ref, location.to_locations(), ConstraintCategory::CopyBound); - if move_tr { - self.prove_move_predicate(place_ty.ty, location.to_locations()); - } - } else if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) = context - && move_tr + } + + if tcx.features().move_trait() + && matches!( + context, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) + | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + ) { self.prove_move_predicate(place_ty.ty, location.to_locations()); } @@ -2576,10 +2578,16 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { span: Span, ) -> Result, ErrorGuaranteed> { let (mut output, region_constraints) = - scrape_region_constraints(infcx, root_def_id, "InstantiateOpaqueType", span, |ocx| { - ocx.register_obligations(self.obligations.clone()); - Ok(()) - })?; + scrape_region_constraints::<_, _, ScrubbedTraitError<'tcx>>( + infcx, + root_def_id, + "InstantiateOpaqueType", + span, + |ocx| { + ocx.register_obligations(self.obligations.clone()); + Ok(()) + }, + )?; self.region_constraints = Some(region_constraints); output.error_info = Some(self); Ok(output) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index cb6ba24b31d2b..09dab15306bb6 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -340,7 +340,10 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::Trait(trait_predicate) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); - if !tcx.is_implicit_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) { + // nia: fixme: metasized + if !tcx.is_implicit_trait(def_id, false) + && !tcx.is_lang_item(def_id, LangItem::Sized) + { // Do not add that restriction to the list if it is a positive requirement. entry.push(trait_predicate.def_id()); } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index c5c8cfa0a6218..31bf48fe5fe49 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -60,6 +60,7 @@ fn associated_type_bounds<'tcx>( hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, + true, ); // Also collect `where Self::Assoc: Trait` from the parent trait's where clauses. @@ -379,6 +380,7 @@ fn opaque_type_bounds<'tcx>( hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, + true, ); } //`ConstIfConst` is only interested in `[const]` bounds. diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a897c66bbe7b2..b1a2f0730a570 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -202,6 +202,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen self_bounds, ImpliedBoundsContext::TraitDef(def_id), span, + true, ); predicates.extend(bounds); } @@ -234,6 +235,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen &[], ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), param.span, + true, ); trace!(?bounds); predicates.extend(bounds); @@ -683,6 +685,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( superbounds, ImpliedBoundsContext::TraitDef(trait_def_id), item.span, + true, ); } //`ConstIfConst` is only interested in `[const]` bounds. @@ -972,19 +975,14 @@ impl<'tcx> ItemCtxt<'tcx> { match param.kind { hir::GenericParamKind::Type { .. } => { let param_ty = self.lowerer().lower_ty_param(param.hir_id); - self.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - param_ty, - &[], - ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), - param.span, - ); - self.lowerer().add_default_traits( + // nia: fixme: should this add move? + self.lowerer().add_implicit_bounds( &mut bounds, param_ty, &[], ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), param.span, + true, ); } hir::GenericParamKind::Lifetime { .. } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 7276b90722236..af1ede40df834 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -120,7 +120,7 @@ fn collect_bounds<'a, 'tcx>( fn collect_sizedness_bounds<'tcx>( tcx: TyCtxt<'tcx>, - hir_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &[hir::GenericBound<'tcx>], context: ImpliedBoundsContext<'tcx>, span: Span, ) -> CollectedSizednessBounds { @@ -156,13 +156,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, - hir_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &[hir::GenericBound<'tcx>], context: ImpliedBoundsContext<'tcx>, span: Span, + including_sized: bool, ) { + // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` + if find_attr!(self.tcx(), crate, RustcNoImplicitBounds) { + return; + } + self.add_implicit_move_bound(bounds, self_ty, hir_bounds, context, span); self.add_default_traits(bounds, self_ty, hir_bounds, context, span); - self.add_implicit_sizedness_bounds(bounds, self_ty, hir_bounds, context, span); + if including_sized { + self.add_implicit_sizedness_bounds(bounds, self_ty, hir_bounds, context, span); + } } /// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type. @@ -173,21 +181,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// bounds are present. /// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if /// a `?Sized` bound is present. - pub(crate) fn add_implicit_sizedness_bounds( + fn add_implicit_sizedness_bounds( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, - hir_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &[hir::GenericBound<'tcx>], context: ImpliedBoundsContext<'tcx>, span: Span, ) { let tcx = self.tcx(); - // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` - if find_attr!(tcx, crate, RustcNoImplicitBounds) { - return; - } - let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span); let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span); @@ -248,7 +251,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub(crate) fn add_implicit_move_bound( + fn add_implicit_move_bound( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, @@ -257,12 +260,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) { let tcx = self.tcx(); - - // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` - if find_attr!(tcx, crate, RustcNoImplicitBounds) { - return; - } - match context { ImpliedBoundsContext::TraitDef(trait_did) => { // Don't add default move supertrait to default traits. @@ -293,7 +290,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub(crate) fn add_default_traits( + fn add_default_traits( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, @@ -309,7 +306,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `experimental_default_bounds` bound to the `bounds` if appropriate. /// /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`. - pub(crate) fn add_default_trait( + fn add_default_trait( &self, trait_: hir::LangItem, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, @@ -329,23 +326,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if let Some(trait_did) = tcx.lang_items().get(trait_) - && self.should_add_default_traits(trait_did, hir_bounds, context) + && !collect_bounds(hir_bounds, context, trait_did).any() { add_trait_bound(tcx, bounds, self_ty, trait_did, span); } } - /// Returns `true` if default trait bound should be added. - fn should_add_default_traits<'a>( - &self, - trait_def_id: DefId, - hir_bounds: &'a [hir::GenericBound<'tcx>], - context: ImpliedBoundsContext<'tcx>, - ) -> bool { - let collected = collect_bounds(hir_bounds, context, trait_def_id); - !find_attr!(self.tcx(), crate, RustcNoImplicitBounds) && !collected.any() - } - fn reject_duplicate_relaxed_bounds(&self, relaxed_bounds: SmallVec<[&PolyTraitRef<'_>; 1]>) { let tcx = self.tcx(); @@ -376,7 +362,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); if let Res::Def(DefKind::Trait, def_id) = trait_ref.path.res - && (tcx.is_lang_item(def_id, hir::LangItem::Sized) || tcx.is_implicit_trait(def_id)) + && (tcx.is_lang_item(def_id, hir::LangItem::Sized) + || tcx.is_implicit_trait(def_id, false)) + // nia: todo: is metasized ok to include so we can simplify this? { return; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index a8b0de3854b7a..0e80f2884ab3a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -76,18 +76,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - // Don't use `add_implicit_bounds` directly to skip adding `Sized`. - self.add_implicit_move_bound( - &mut user_written_bounds, - dummy_self, - &hir_bounds - .iter() - .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) - .collect::>(), - ImpliedBoundsContext::AssociatedTypeOrImplTrait, - span, - ); - self.add_default_traits( + self.add_implicit_bounds( &mut user_written_bounds, dummy_self, &hir_bounds @@ -96,7 +85,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::>(), ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, + false, ); + // self.add_implicit_move_bound( + // &mut user_written_bounds, + // dummy_self, + // &hir_bounds + // .iter() + // .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + // .collect::>(), + // ImpliedBoundsContext::AssociatedTypeOrImplTrait, + // span, + // ); + // self.add_default_traits( + // &mut user_written_bounds, + // dummy_self, + // &hir_bounds + // .iter() + // .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + // .collect::>(), + // ImpliedBoundsContext::AssociatedTypeOrImplTrait, + // span, + // ); let (mut elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 48116c57d712d..49aeb99792791 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3206,6 +3206,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, hir_ty.span, + true, ); self.register_trait_ascription_bounds(bounds, hir_ty.hir_id, hir_ty.span); self_ty diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 39fff48de6aa6..48b8e7571f5e1 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -33,6 +33,10 @@ impl<'tcx> ScrubbedTraitError<'tcx> { } } +impl<'tcx> EngineError<'tcx> for ScrubbedTraitError<'tcx> { + fn try_report_errors(_infcx: &InferCtxt<'tcx>, _errors: Vec) {} +} + pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { /// Requires that `ty` must implement the trait with `def_id` in /// the given environment. This trait must not have any type @@ -117,6 +121,10 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { ) -> PredicateObligations<'tcx>; } -pub trait FromSolverError<'tcx, E>: Debug + 'tcx { +pub trait EngineError<'tcx>: Sized + 'tcx { + fn try_report_errors(infcx: &InferCtxt<'tcx>, errors: Vec); +} + +pub trait FromSolverError<'tcx, E>: EngineError<'tcx> + Debug { fn from_solver_error(infcx: &InferCtxt<'tcx>, error: E) -> Self; } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 0536a6c909507..45b343462c865 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -20,7 +20,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; use rustc_span::Span; use thin_vec::ThinVec; -pub use self::engine::{FromSolverError, ScrubbedTraitError, TraitEngine}; +pub use self::engine::{EngineError, FromSolverError, ScrubbedTraitError, TraitEngine}; pub(crate) use self::project::UndoLog; pub use self::project::{ MismatchedProjectionTypes, Normalized, NormalizedTerm, ProjectionCache, ProjectionCacheEntry, diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index f4d8a432aeedf..b8cd68b3e716e 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { .map(Unnormalized::skip_norm_wip) .filter_map(|(pred, _)| pred.as_trait_clause()) .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)) - .filter(|pred| !cx.tcx.is_implicit_trait(pred.def_id())); + // nia: fixme: sized + .filter(|pred| !cx.tcx.is_implicit_trait(pred.def_id(), false)); if direct_super_traits_iter.count() > 1 { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 92f7ed0cb19f6..46686701ec824 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -182,6 +182,15 @@ pub enum SelectionCandidate<'tcx> { BikeshedGuaranteedNoDropCandidate, } +impl SelectionCandidate<'_> { + pub fn is_impl_candidate(&self) -> bool { + matches!( + self, + SelectionCandidate::AutoImplCandidate | SelectionCandidate::ImplCandidate(..) + ) + } +} + /// The result of trait evaluation. The order is important /// here as the evaluation of a list is the maximum of the /// evaluations. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 775f851303e70..2c65b076b6d1e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -906,8 +906,10 @@ impl<'tcx> TyCtxt<'tcx> { self.default_traits().iter().any(|&default_trait| self.is_lang_item(def_id, default_trait)) } - pub fn is_implicit_trait(self, def_id: DefId) -> bool { - self.is_default_trait(def_id) || matches!(self.as_lang_item(def_id), Some(LangItem::Move)) + pub fn is_implicit_trait(self, def_id: DefId, including_sized: bool) -> bool { + self.is_default_trait(def_id) + || matches!(self.as_lang_item(def_id), Some(LangItem::Move)) + || (including_sized && self.is_sizedness_trait(def_id)) } pub fn is_sizedness_trait(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 49b9fe16d7e16..58b61fda45254 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -471,8 +471,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_default_trait(def_id) } - fn is_implicit_trait(self, def_id: Self::TraitId) -> bool { - self.is_implicit_trait(def_id) + fn is_implicit_trait(self, def_id: Self::TraitId, including_sized: bool) -> bool { + self.is_implicit_trait(def_id, including_sized) } fn is_sizedness_trait(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index a2922aa9c1091..b65712cbfea8c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1253,9 +1253,9 @@ where Some(self.forced_ambiguity(MaybeInfo::AMBIGUOUS)) } - // Backward compatibility for default auto traits & `Move`. + // Backward compatibility for default auto traits. // Test: ui/traits/default_auto_traits/extern-types.rs - ty::Foreign(..) if self.cx().is_implicit_trait(goal.predicate.def_id()) => { + ty::Foreign(..) if self.cx().is_implicit_trait(goal.predicate.def_id(), false) => { check_impls() } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 6cddd79544906..dd49b3d217c99 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -52,6 +52,15 @@ pub struct ObligationCtxt<'a, 'tcx, E = ScrubbedTraitError<'tcx>> { engine: RefCell>>, } +impl<'a, 'tcx, E> ObligationCtxt<'a, 'tcx, E> +where + E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>, +{ + pub fn new_in(infcx: &'a InferCtxt<'tcx>) -> Self { + Self { infcx, engine: RefCell::new(>::new(infcx)) } + } +} + impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>> { pub fn new_with_diagnostics(infcx: &'a InferCtxt<'tcx>) -> Self { Self { infcx, engine: RefCell::new(>::new(infcx)) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index e0eeca23c6d46..71091db6f9bce 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -109,6 +109,12 @@ impl<'tcx> FulfillmentError<'tcx> { } } +impl<'tcx> EngineError<'tcx> for FulfillmentError<'tcx> { + fn try_report_errors(infcx: &InferCtxt<'tcx>, errors: Vec) { + infcx.err_ctxt().report_fulfillment_errors(errors); + } +} + #[derive(Clone, TypeVisitable)] pub enum FulfillmentErrorCode<'tcx> { /// Inherently impossible to fulfill; this trait is implemented if and only diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 812e087a0cf69..8c0a9bff1be89 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -3,6 +3,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::region_constraints::RegionConstraintData; +use rustc_infer::traits::{FromSolverError, ScrubbedTraitError}; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -10,8 +11,9 @@ use tracing::info; use crate::infer::InferCtxt; use crate::infer::canonical::query_response; -use crate::traits::ObligationCtxt; +use crate::solve::NextSolverError; use crate::traits::query::type_op::TypeOpOutput; +use crate::traits::{FulfillmentError, ObligationCtxt, OldSolverError}; pub struct CustomTypeOp { closure: F, @@ -50,7 +52,14 @@ where info!("fully_perform({:?})", self); } - Ok(scrape_region_constraints(infcx, root_def_id, self.description, span, self.closure)?.0) + Ok(scrape_region_constraints::<_, _, ScrubbedTraitError<'_>>( + infcx, + root_def_id, + self.description, + span, + self.closure, + )? + .0) } } @@ -60,18 +69,71 @@ impl fmt::Debug for CustomTypeOp { } } +pub struct FallibleCustomTypeOp { + closure: F, + description: &'static str, +} + +impl FallibleCustomTypeOp { + pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self + where + F: FnOnce(&ObligationCtxt<'_, 'tcx, FulfillmentError<'tcx>>) -> Result, + { + FallibleCustomTypeOp { closure, description } + } +} + +impl<'tcx, F, R> super::TypeOp<'tcx> for FallibleCustomTypeOp +where + F: FnOnce(&ObligationCtxt<'_, 'tcx, FulfillmentError<'tcx>>) -> Result, + R: fmt::Debug + TypeFoldable>, +{ + type Output = R; + type ErrorInfo = !; + + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + root_def_id: LocalDefId, + span: Span, + ) -> Result, ErrorGuaranteed> { + if cfg!(debug_assertions) { + info!("fully_perform({:?})", self); + } + + Ok(scrape_region_constraints::<_, _, FulfillmentError<'tcx>>( + infcx, + root_def_id, + self.description, + span, + self.closure, + )? + .0) + } +} + +impl fmt::Debug for FallibleCustomTypeOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.description.fmt(f) + } +} + /// Executes `op` and then scrapes out all the "old style" region /// constraints that result, creating query-region-constraints. -pub fn scrape_region_constraints<'tcx, Op, R>( +pub fn scrape_region_constraints<'tcx, Op, R, E>( infcx: &InferCtxt<'tcx>, root_def_id: LocalDefId, name: &'static str, span: Span, - op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, + op: impl FnOnce(&ObligationCtxt<'_, 'tcx, E>) -> Result, ) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> where R: TypeFoldable>, Op: super::TypeOp<'tcx, Output = R>, + E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>, { // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the @@ -90,9 +152,18 @@ where ); let value = infcx.commit_if_ok(|_| { - let ocx = ObligationCtxt::new(infcx); - let value = op(&ocx).map_err(|_| { - infcx.tcx.check_potentially_region_dependent_goals(root_def_id).err().unwrap_or_else( + let ocx = ObligationCtxt::::new_in(infcx); + op(&ocx) + .and_then(|v| { + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + if errors.is_empty() { + Ok(v) + } else { + E::try_report_errors(infcx, errors); + Err(NoSolution) + } + }) + .map_err(|_| { // FIXME: In this region-dependent context, `type_op` should only fail due to // region-dependent goals. Any other kind of failure indicates a bug and we // should ICE. @@ -120,23 +191,13 @@ where // // If we ICE here on any non-region-dependent failure, we would trigger ICEs // too often for such cases. For now, we emit a delayed bug instead. - || { - infcx + match infcx.tcx.check_potentially_region_dependent_goals(root_def_id) { + Ok(_) => infcx .dcx() - .span_delayed_bug(span, format!("error performing operation: {name}")) - }, - ) - })?; - let errors = ocx.evaluate_obligations_error_on_ambiguity(); - if errors.is_empty() { - Ok(value) - } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) { - Err(guar) - } else { - Err(infcx.dcx().delayed_bug(format!( - "errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}" - ))) - } + .span_delayed_bug(span, format!("error performing operation: {name}")), + Err(e) => e, + } + }) })?; // Next trait solver performs operations locally, and normalize goals should resolve vars. diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index b2c1f9a5eed14..770aec13148db 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -2,7 +2,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; -use rustc_infer::traits::PredicateObligations; +use rustc_infer::traits::{PredicateObligations, ScrubbedTraitError}; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -175,15 +175,20 @@ where // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually // collecting region constraints via `region_constraints`. - let (mut output, _) = - scrape_region_constraints(infcx, root_def_id, "fully_perform", span, |ocx| { + let (mut output, _) = scrape_region_constraints::<_, _, ScrubbedTraitError<'_>>( + infcx, + root_def_id, + "fully_perform", + span, + |ocx| { let (output, ei, obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; error_info = ei; ocx.register_obligations(obligations); Ok(output) - })?; + }, + )?; output.error_info = error_info; if let Some(QueryRegionConstraints { constraints, assumptions }) = output.constraints { region_constraints.constraints.extend(constraints.iter().cloned()); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 085da5cbeee5a..cacdb9a7c4412 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -779,7 +779,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Backward compatibility for default auto traits & `Move`. // Test: ui/traits/default_auto_traits/extern-types.rs - if self.tcx().is_implicit_trait(def_id) { + if self.tcx().is_implicit_trait(def_id, false) { check_impls() } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9a52569474479..98f6c49413121 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -479,16 +479,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let has_non_region_infer = stack.obligation.predicate.has_non_region_infer(); let candidate_preference_mode = CandidatePreferenceMode::compute(self.tcx(), stack.obligation.predicate.def_id()); - let is_default_auto_trait = self - .tcx() - .as_lang_item(stack.obligation.predicate.def_id()) - .is_some_and(|lang_item| matches!(lang_item, LangItem::Move)); - if let Some(candidate) = self.winnow_candidates( - has_non_region_infer, - is_default_auto_trait, - candidate_preference_mode, - candidates, - ) { + if let Some(candidate) = + self.winnow_candidates(has_non_region_infer, candidate_preference_mode, candidates) + { self.filter_reservation_impls(candidate) } else { Ok(None) @@ -1858,7 +1851,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn winnow_candidates( &mut self, has_non_region_infer: bool, - is_default_auto_trait: bool, candidate_preference_mode: CandidatePreferenceMode, mut candidates: Vec>, ) -> Option> { @@ -1868,9 +1860,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // We prefer `Sized` candidates over everything. let mut sized_candidates = candidates.iter().filter(|c| { - matches!(c.candidate, SizedCandidate) - || (is_default_auto_trait - && matches!(c.candidate, AutoImplCandidate | ImplCandidate(..))) + matches!(c.candidate, SizedCandidate) // nia: prefmode::marker && matches(AutoImpl) + || (matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) && c.candidate.is_impl_candidate()) }); if let Some(sized_candidate) = sized_candidates.next() { // There should only ever be a single sized candidate @@ -1925,9 +1916,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { }); // Extract non-nested alias bound candidates, will be preferred over where bounds if // we're proving an auto-trait, sizedness trait or default trait. - if is_default_auto_trait - || matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) - { + if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) { match alias_bounds .clone() .filter_map(|(idx, kind)| (kind == AliasBoundKind::SelfBounds).then_some(idx)) diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 696149c3ff0e3..281ea70befcfc 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -336,7 +336,7 @@ pub trait Interner: fn is_default_trait(self, def_id: Self::TraitId) -> bool; - fn is_implicit_trait(self, def_id: Self::TraitId) -> bool; + fn is_implicit_trait(self, def_id: Self::TraitId, including_sized: bool) -> bool; fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool;