From cf94bdc0742c13e2a0cac864c478b8626b266e1b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:38 +0200 Subject: Merging upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_trait_selection/Cargo.toml | 1 - compiler/rustc_trait_selection/src/autoderef.rs | 4 +- compiler/rustc_trait_selection/src/errors.rs | 46 +- compiler/rustc_trait_selection/src/infer.rs | 26 +- compiler/rustc_trait_selection/src/lib.rs | 2 - .../rustc_trait_selection/src/traits/auto_trait.rs | 234 +++--- .../src/traits/chalk_fulfill.rs | 11 +- .../rustc_trait_selection/src/traits/codegen.rs | 89 ++- .../rustc_trait_selection/src/traits/coherence.rs | 102 ++- .../src/traits/const_evaluatable.rs | 63 +- .../rustc_trait_selection/src/traits/engine.rs | 6 +- .../src/traits/error_reporting/mod.rs | 871 ++++++++++----------- .../src/traits/error_reporting/on_unimplemented.rs | 12 +- .../src/traits/error_reporting/suggestions.rs | 370 ++++++--- .../rustc_trait_selection/src/traits/fulfill.rs | 84 +- compiler/rustc_trait_selection/src/traits/misc.rs | 111 ++- compiler/rustc_trait_selection/src/traits/mod.rs | 175 ++--- .../src/traits/object_safety.rs | 176 ++--- .../src/traits/outlives_bounds.rs | 2 +- .../rustc_trait_selection/src/traits/project.rs | 142 +++- .../src/traits/query/evaluate_obligation.rs | 2 +- .../src/traits/query/normalize.rs | 54 +- .../src/traits/query/type_op/custom.rs | 8 +- .../src/traits/query/type_op/mod.rs | 6 +- .../src/traits/relationships.rs | 2 +- .../src/traits/select/candidate_assembly.rs | 29 +- .../src/traits/select/confirmation.rs | 72 +- .../rustc_trait_selection/src/traits/select/mod.rs | 149 ++-- .../src/traits/specialize/mod.rs | 83 +- .../src/traits/specialize/specialization_graph.rs | 48 +- .../src/traits/structural_match.rs | 9 +- compiler/rustc_trait_selection/src/traits/util.rs | 14 +- compiler/rustc_trait_selection/src/traits/wf.rs | 46 +- 33 files changed, 1539 insertions(+), 1510 deletions(-) (limited to 'compiler/rustc_trait_selection') diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 566f236f2..67613e1a4 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] rustc_parse_format = { path = "../rustc_parse_format" } diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 36ab8f3bd..61cfeec4b 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -25,7 +25,7 @@ struct AutoderefSnapshot<'tcx> { pub struct Autoderef<'a, 'tcx> { // Meta infos: - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, span: Span, overloaded_span: Span, body_id: hir::HirId, @@ -94,7 +94,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { impl<'a, 'tcx> Autoderef<'a, 'tcx> { pub fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, span: Span, diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index ab0afc545..7f8705824 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,11 +1,11 @@ -use rustc_errors::{fluent, ErrorGuaranteed, Handler}; -use rustc_macros::SessionDiagnostic; -use rustc_middle::ty::{PolyTraitRef, Ty, Unevaluated}; -use rustc_session::{Limit, SessionDiagnostic}; +use rustc_errors::{fluent, ErrorGuaranteed, Handler, IntoDiagnostic}; +use rustc_macros::Diagnostic; +use rustc_middle::ty::{self, PolyTraitRef, Ty}; +use rustc_session::Limit; use rustc_span::{Span, Symbol}; -#[derive(SessionDiagnostic)] -#[diag(trait_selection::dump_vtable_entries)] +#[derive(Diagnostic)] +#[diag(trait_selection_dump_vtable_entries)] pub struct DumpVTableEntries<'a> { #[primary_span] pub span: Span, @@ -13,17 +13,17 @@ pub struct DumpVTableEntries<'a> { pub entries: String, } -#[derive(SessionDiagnostic)] -#[diag(trait_selection::unable_to_construct_constant_value)] +#[derive(Diagnostic)] +#[diag(trait_selection_unable_to_construct_constant_value)] pub struct UnableToConstructConstantValue<'a> { #[primary_span] pub span: Span, - pub unevaluated: Unevaluated<'a>, + pub unevaluated: ty::UnevaluatedConst<'a>, } -#[derive(SessionDiagnostic)] +#[derive(Diagnostic)] #[help] -#[diag(trait_selection::auto_deref_reached_recursion_limit, code = "E0055")] +#[diag(trait_selection_auto_deref_reached_recursion_limit, code = "E0055")] pub struct AutoDerefReachedRecursionLimit<'a> { #[primary_span] #[label] @@ -33,24 +33,24 @@ pub struct AutoDerefReachedRecursionLimit<'a> { pub crate_name: Symbol, } -#[derive(SessionDiagnostic)] -#[diag(trait_selection::empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] +#[derive(Diagnostic)] +#[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] pub struct EmptyOnClauseInOnUnimplemented { #[primary_span] #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(trait_selection::invalid_on_clause_in_rustc_on_unimplemented, code = "E0232")] +#[derive(Diagnostic)] +#[diag(trait_selection_invalid_on_clause_in_rustc_on_unimplemented, code = "E0232")] pub struct InvalidOnClauseInOnUnimplemented { #[primary_span] #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(trait_selection::no_value_in_rustc_on_unimplemented, code = "E0232")] +#[derive(Diagnostic)] +#[diag(trait_selection_no_value_in_rustc_on_unimplemented, code = "E0232")] #[note] pub struct NoValueInOnUnimplemented { #[primary_span] @@ -66,12 +66,12 @@ pub struct NegativePositiveConflict<'a> { pub positive_impl_span: Result, } -impl SessionDiagnostic<'_> for NegativePositiveConflict<'_> { +impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { fn into_diagnostic( self, handler: &Handler, ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { - let mut diag = handler.struct_err(fluent::trait_selection::negative_positive_conflict); + let mut diag = handler.struct_err(fluent::trait_selection_negative_positive_conflict); diag.set_arg("trait_desc", self.trait_desc); diag.set_arg( "self_desc", @@ -81,19 +81,19 @@ impl SessionDiagnostic<'_> for NegativePositiveConflict<'_> { diag.code(rustc_errors::error_code!(E0751)); match self.negative_impl_span { Ok(span) => { - diag.span_label(span, fluent::trait_selection::negative_implementation_here); + diag.span_label(span, fluent::negative_implementation_here); } Err(cname) => { - diag.note(fluent::trait_selection::negative_implementation_in_crate); + diag.note(fluent::negative_implementation_in_crate); diag.set_arg("negative_impl_cname", cname.to_string()); } } match self.positive_impl_span { Ok(span) => { - diag.span_label(span, fluent::trait_selection::positive_implementation_here); + diag.span_label(span, fluent::positive_implementation_here); } Err(cname) => { - diag.note(fluent::trait_selection::positive_implementation_in_crate); + diag.note(fluent::positive_implementation_in_crate); diag.set_arg("positive_impl_cname", cname.to_string()); } } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index ba403ab2d..a335f8e06 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -59,7 +59,7 @@ pub trait InferCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; } -impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { fn type_is_copy_modulo_regions( &self, param_env: ty::ParamEnv<'tcx>, @@ -69,7 +69,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { let ty = self.resolve_vars_if_possible(ty); if !(param_env, ty).needs_infer() { - return ty.is_copy_modulo_regions(self.tcx.at(span), param_env); + return ty.is_copy_modulo_regions(self.tcx, param_env); } let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); @@ -142,7 +142,7 @@ pub trait InferCtxtBuilderExt<'tcx> { fn enter_canonical_trait_query( &mut self, canonical_key: &Canonical<'tcx, K>, - operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible, + operation: impl FnOnce(&InferCtxt<'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible, ) -> Fallible> where K: TypeFoldable<'tcx>, @@ -170,25 +170,17 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { fn enter_canonical_trait_query( &mut self, canonical_key: &Canonical<'tcx, K>, - operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible, + operation: impl FnOnce(&InferCtxt<'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible, ) -> Fallible> where K: TypeFoldable<'tcx>, R: Debug + TypeFoldable<'tcx>, Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { - self.enter_with_canonical( - DUMMY_SP, - canonical_key, - |ref infcx, key, canonical_inference_vars| { - let mut fulfill_cx = >::new(infcx.tcx); - let value = operation(infcx, &mut *fulfill_cx, key)?; - infcx.make_canonicalized_query_response( - canonical_inference_vars, - value, - &mut *fulfill_cx, - ) - }, - ) + let (ref infcx, key, canonical_inference_vars) = + self.build_with_canonical(DUMMY_SP, canonical_key); + let mut fulfill_cx = >::new(infcx.tcx); + let value = operation(infcx, &mut *fulfill_cx, key)?; + infcx.make_canonicalized_query_response(canonical_inference_vars, value, &mut *fulfill_cx) } } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index d35f74974..5d52aa075 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -16,9 +16,7 @@ #![feature(control_flow_enum)] #![feature(drain_filter)] #![feature(hash_drain_filter)] -#![cfg_attr(bootstrap, feature(label_break_value))] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(let_else))] #![feature(if_let_guard)] #![feature(never_type)] #![feature(type_alias_impl_trait)] diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index bcdfa4f12..ed34ab95a 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -10,7 +10,7 @@ use crate::traits::project::ProjectAndUnifyResult; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{Region, RegionVid}; +use rustc_middle::ty::{PolyTraitRef, Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -66,13 +66,13 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// struct Foo { data: Box } /// ``` /// - /// then this might return that Foo: Send if T: Send (encoded in the AutoTraitResult type). - /// The analysis attempts to account for custom impls as well as other complex cases. This - /// result is intended for use by rustdoc and other such consumers. + /// then this might return that `Foo: Send` if `T: Send` (encoded in the AutoTraitResult + /// type). The analysis attempts to account for custom impls as well as other complex cases. + /// This result is intended for use by rustdoc and other such consumers. /// /// (Note that due to the coinductive nature of Send, the full and correct result is actually /// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field - /// types are all Send. So, in our example, we might have that Foo: Send if Box: Send. + /// types are all Send. So, in our example, we might have that `Foo: Send` if `Box: Send`. /// But this is often not the best way to present to the user.) /// /// Warning: The API should be considered highly unstable, and it may be refactored or removed @@ -90,143 +90,105 @@ impl<'tcx> AutoTraitFinder<'tcx> { let trait_pred = ty::Binder::dummy(trait_ref); - let bail_out = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - let result = selcx.select(&Obligation::new( - ObligationCause::dummy(), - orig_env, - trait_pred.to_poly_trait_predicate(), - )); - - match result { - Ok(Some(ImplSource::UserDefined(_))) => { - debug!( - "find_auto_trait_generics({:?}): \ - manual impl found, bailing out", - trait_ref - ); - return true; - } - _ => {} + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + for f in [ + PolyTraitRef::to_poly_trait_predicate, + PolyTraitRef::to_poly_trait_predicate_negative_polarity, + ] { + let result = + selcx.select(&Obligation::new(ObligationCause::dummy(), orig_env, f(&trait_pred))); + if let Ok(Some(ImplSource::UserDefined(_))) = result { + debug!( + "find_auto_trait_generics({:?}): \ + manual impl found, bailing out", + trait_ref + ); + // If an explicit impl exists, it always takes priority over an auto impl + return AutoTraitResult::ExplicitImpl; } - - let result = selcx.select(&Obligation::new( - ObligationCause::dummy(), - orig_env, - trait_pred.to_poly_trait_predicate_negative_polarity(), - )); - - match result { - Ok(Some(ImplSource::UserDefined(_))) => { - debug!( - "find_auto_trait_generics({:?}): \ - manual impl found, bailing out", - trait_ref - ); - true - } - _ => false, - } - }); - - // If an explicit impl exists, it always takes priority over an auto impl - if bail_out { - return AutoTraitResult::ExplicitImpl; } - tcx.infer_ctxt().enter(|infcx| { - let mut fresh_preds = FxHashSet::default(); + let infcx = tcx.infer_ctxt().build(); + let mut fresh_preds = FxHashSet::default(); + + // Due to the way projections are handled by SelectionContext, we need to run + // evaluate_predicates twice: once on the original param env, and once on the result of + // the first evaluate_predicates call. + // + // The problem is this: most of rustc, including SelectionContext and traits::project, + // are designed to work with a concrete usage of a type (e.g., Vec + // fn() { Vec }. This information will generally never change - given + // the 'T' in fn() { ... }, we'll never know anything else about 'T'. + // If we're unable to prove that 'T' implements a particular trait, we're done - + // there's nothing left to do but error out. + // + // However, synthesizing an auto trait impl works differently. Here, we start out with + // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing + // with - and progressively discover the conditions we need to fulfill for it to + // implement a certain auto trait. This ends up breaking two assumptions made by trait + // selection and projection: + // + // * We can always cache the result of a particular trait selection for the lifetime of + // an InfCtxt + // * Given a projection bound such as '::SomeItem = K', if 'T: + // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' + // + // We fix the first assumption by manually clearing out all of the InferCtxt's caches + // in between calls to SelectionContext.select. This allows us to keep all of the + // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift + // them between calls. + // + // We fix the second assumption by reprocessing the result of our first call to + // evaluate_predicates. Using the example of '::SomeItem = K', our first + // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, + // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing + // SelectionContext to return it back to us. + + let Some((new_env, user_env)) = self.evaluate_predicates( + &infcx, + trait_did, + ty, + orig_env, + orig_env, + &mut fresh_preds, + false, + ) else { + return AutoTraitResult::NegativeImpl; + }; + + let (full_env, full_user_env) = self + .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds, true) + .unwrap_or_else(|| { + panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) + }); - // Due to the way projections are handled by SelectionContext, we need to run - // evaluate_predicates twice: once on the original param env, and once on the result of - // the first evaluate_predicates call. - // - // The problem is this: most of rustc, including SelectionContext and traits::project, - // are designed to work with a concrete usage of a type (e.g., Vec - // fn() { Vec }. This information will generally never change - given - // the 'T' in fn() { ... }, we'll never know anything else about 'T'. - // If we're unable to prove that 'T' implements a particular trait, we're done - - // there's nothing left to do but error out. - // - // However, synthesizing an auto trait impl works differently. Here, we start out with - // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing - // with - and progressively discover the conditions we need to fulfill for it to - // implement a certain auto trait. This ends up breaking two assumptions made by trait - // selection and projection: - // - // * We can always cache the result of a particular trait selection for the lifetime of - // an InfCtxt - // * Given a projection bound such as '::SomeItem = K', if 'T: - // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' - // - // We fix the first assumption by manually clearing out all of the InferCtxt's caches - // in between calls to SelectionContext.select. This allows us to keep all of the - // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift - // them between calls. - // - // We fix the second assumption by reprocessing the result of our first call to - // evaluate_predicates. Using the example of '::SomeItem = K', our first - // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, - // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing - // SelectionContext to return it back to us. - - let Some((new_env, user_env)) = self.evaluate_predicates( - &infcx, - trait_did, - ty, - orig_env, - orig_env, - &mut fresh_preds, - false, - ) else { - return AutoTraitResult::NegativeImpl; - }; - - let (full_env, full_user_env) = self - .evaluate_predicates( - &infcx, - trait_did, - ty, - new_env, - user_env, - &mut fresh_preds, - true, - ) - .unwrap_or_else(|| { - panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) - }); - - debug!( - "find_auto_trait_generics({:?}): fulfilling \ - with {:?}", - trait_ref, full_env - ); - infcx.clear_caches(); - - // At this point, we already have all of the bounds we need. FulfillmentContext is used - // to store all of the necessary region/lifetime bounds in the InferContext, as well as - // an additional sanity check. - let errors = - super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did); - if !errors.is_empty() { - panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); - } + debug!( + "find_auto_trait_generics({:?}): fulfilling \ + with {:?}", + trait_ref, full_env + ); + infcx.clear_caches(); + + // At this point, we already have all of the bounds we need. FulfillmentContext is used + // to store all of the necessary region/lifetime bounds in the InferContext, as well as + // an additional sanity check. + let errors = + super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did); + if !errors.is_empty() { + panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); + } - infcx.process_registered_region_obligations(&Default::default(), full_env); + infcx.process_registered_region_obligations(&Default::default(), full_env); - let region_data = infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .region_constraint_data() - .clone(); + let region_data = + infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone(); - let vid_to_region = self.map_vid_to_region(®ion_data); + let vid_to_region = self.map_vid_to_region(®ion_data); - let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; + let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; - AutoTraitResult::PositiveImpl(auto_trait_callback(info)) - }) + AutoTraitResult::PositiveImpl(auto_trait_callback(info)) } } @@ -272,7 +234,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// user. fn evaluate_predicates( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, trait_did: DefId, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -834,7 +796,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let reported = tcx.sess.emit_err(UnableToConstructConstantValue { span: tcx.def_span(def_id), - unevaluated: unevaluated.expand(), + unevaluated: unevaluated, }); Err(ErrorHandled::Reported(reported)) } @@ -877,7 +839,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { pub fn clean_pred( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, p: ty::Predicate<'tcx>, ) -> ty::Predicate<'tcx> { infcx.freshen(p) diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 9ef7ac9a8..81e1d6449 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -28,7 +28,7 @@ impl FulfillmentContext<'_> { impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn normalize_projection_type( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, _param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, _cause: ObligationCause<'tcx>, @@ -38,7 +38,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn register_predicate_obligation( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { assert!(!infcx.is_in_snapshot()); @@ -49,7 +49,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { self.obligations.insert(obligation); } - fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec> { + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { { let errors = self.select_where_possible(infcx); @@ -71,10 +71,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .collect() } - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Vec> { + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { assert!(!infcx.is_in_snapshot()); let mut errors = Vec::new(); diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 08adbcbd4..8a62bf015 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -4,10 +4,12 @@ // general routines. use crate::infer::{DefiningAnchor, TyCtxtInferExt}; +use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::{ ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt, Unimplemented, }; +use rustc_infer::traits::FulfillmentErrorCode; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt}; @@ -27,52 +29,61 @@ pub fn codegen_select_candidate<'tcx>( // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - let mut infcx_builder = - tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble); - infcx_builder.enter(|infcx| { - //~^ HACK `Bubble` is required for - // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs - let mut selcx = SelectionContext::new(&infcx); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .build(); + //~^ HACK `Bubble` is required for + // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs + let mut selcx = SelectionContext::new(&infcx); - let obligation_cause = ObligationCause::dummy(); - let obligation = - Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); + let obligation_cause = ObligationCause::dummy(); + let obligation = + Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => return Err(CodegenObligationError::Ambiguity), - Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented), - Err(e) => { - bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) - } - }; + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => return Err(CodegenObligationError::Ambiguity), + Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented), + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) + } + }; - debug!(?selection); + debug!(?selection); - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = >::new(tcx); - let impl_source = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = >::new(tcx); + let impl_source = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); - // In principle, we only need to do this so long as `impl_source` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - let errors = fulfill_cx.select_all_or_error(&infcx); - if !errors.is_empty() { - return Err(CodegenObligationError::FulfillmentError); + // In principle, we only need to do this so long as `impl_source` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + // `rustc_monomorphize::collector` assumes there are no type errors. + // Cycle errors are the only post-monomorphization errors possible; emit them now so + // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization. + for err in errors { + if let FulfillmentErrorCode::CodeCycle(cycle) = err.code { + infcx.err_ctxt().report_overflow_error_cycle(&cycle); + } } + return Err(CodegenObligationError::FulfillmentError); + } - let impl_source = infcx.resolve_vars_if_possible(impl_source); - let impl_source = infcx.tcx.erase_regions(impl_source); + let impl_source = infcx.resolve_vars_if_possible(impl_source); + let impl_source = infcx.tcx.erase_regions(impl_source); - // Opaque types may have gotten their hidden types constrained, but we can ignore them safely - // as they will get constrained elsewhere, too. - // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // Opaque types may have gotten their hidden types constrained, but we can ignore them safely + // as they will get constrained elsewhere, too. + // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - Ok(&*tcx.arena.alloc(impl_source)) - }) + Ok(&*tcx.arena.alloc(impl_source)) } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 292787d4d..8aab75490 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -22,7 +22,6 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::sym; @@ -61,23 +60,17 @@ pub fn add_placeholder_note(err: &mut Diagnostic) { ); } -/// If there are types that satisfy both impls, invokes `on_overlap` +/// If there are types that satisfy both impls, returns `Some` /// with a suitably-freshened `ImplHeader` with those types -/// substituted. Otherwise, invokes `no_overlap`. -#[instrument(skip(tcx, skip_leak_check, on_overlap, no_overlap), level = "debug")] -pub fn overlapping_impls( +/// substituted. Otherwise, returns `None`. +#[instrument(skip(tcx, skip_leak_check), level = "debug")] +pub fn overlapping_impls( tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId, skip_leak_check: SkipLeakCheck, overlap_mode: OverlapMode, - on_overlap: F1, - no_overlap: F2, -) -> R -where - F1: FnOnce(OverlapResult<'_>) -> R, - F2: FnOnce() -> R, -{ +) -> Option> { // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. @@ -98,28 +91,24 @@ where if !may_overlap { // Some types involved are definitely different, so the impls couldn't possibly overlap. debug!("overlapping_impls: fast_reject early-exit"); - return no_overlap(); + return None; } - let overlaps = tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some() - }); - + let infcx = tcx.infer_ctxt().build(); + let selcx = &mut SelectionContext::intercrate(&infcx); + let overlaps = + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some(); if !overlaps { - return no_overlap(); + return None; } // In the case where we detect an error, run the check again, but // this time tracking intercrate ambiguity causes for better // diagnostics. (These take time and can lead to false errors.) - tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - selcx.enable_tracking_intercrate_ambiguity_causes(); - on_overlap( - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap(), - ) - }) + let infcx = tcx.infer_ctxt().build(); + let selcx = &mut SelectionContext::intercrate(&infcx); + selcx.enable_tracking_intercrate_ambiguity_causes(); + Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap()) } fn with_fresh_ty_vars<'cx, 'tcx>( @@ -168,7 +157,7 @@ fn overlap_within_probe<'cx, 'tcx>( impl1_def_id: DefId, impl2_def_id: DefId, overlap_mode: OverlapMode, - snapshot: &CombinedSnapshot<'_, 'tcx>, + snapshot: &CombinedSnapshot<'tcx>, ) -> Option> { let infcx = selcx.infcx(); @@ -198,7 +187,7 @@ fn overlap_within_probe<'cx, 'tcx>( } } - // We disable the leak when when creating the `snapshot` by using + // We disable the leak when creating the `snapshot` by using // `infcx.probe_maybe_disable_leak_check`. if infcx.leak_check(true, snapshot).is_err() { debug!("overlap: leak check failed"); @@ -299,37 +288,36 @@ fn negative_impl<'cx, 'tcx>( let tcx = selcx.infcx().tcx; // Create an infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let impl_env = tcx.param_env(impl1_def_id); - let subject1 = match traits::fully_normalize( - &infcx, - ObligationCause::dummy(), - impl_env, - tcx.impl_subject(impl1_def_id), - ) { - Ok(s) => s, - Err(err) => { - tcx.sess.delay_span_bug( - tcx.def_span(impl1_def_id), - format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), - ); - return false; - } - }; + let infcx = tcx.infer_ctxt().build(); + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let impl_env = tcx.param_env(impl1_def_id); + let subject1 = match traits::fully_normalize( + &infcx, + ObligationCause::dummy(), + impl_env, + tcx.impl_subject(impl1_def_id), + ) { + Ok(s) => s, + Err(err) => { + tcx.sess.delay_span_bug( + tcx.def_span(impl1_def_id), + format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), + ); + return false; + } + }; - // Attempt to prove that impl2 applies, given all of the above. - let selcx = &mut SelectionContext::new(&infcx); - let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); - let (subject2, obligations) = - impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); + // Attempt to prove that impl2 applies, given all of the above. + let selcx = &mut SelectionContext::new(&infcx); + let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); + let (subject2, obligations) = + impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); - !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) - }) + !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) } -fn equate<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, +fn equate<'tcx>( + infcx: &InferCtxt<'tcx>, impl_env: ty::ParamEnv<'tcx>, subject1: ImplSubject<'tcx>, subject2: ImplSubject<'tcx>, @@ -380,8 +368,8 @@ fn negative_impl_exists<'cx, 'tcx>( } #[instrument(level = "debug", skip(infcx))] -fn resolve_negative_obligation<'cx, 'tcx>( - infcx: InferCtxt<'cx, 'tcx>, +fn resolve_negative_obligation<'tcx>( + infcx: InferCtxt<'tcx>, o: &PredicateObligation<'tcx>, body_def_id: DefId, ) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 5a213987e..84038625f 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -9,14 +9,12 @@ //! `thir_abstract_const` which can then be checked for structural equality with other //! generic constants mentioned in the `caller_bounds` of the current environment. use rustc_errors::ErrorGuaranteed; -use rustc_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::{ walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable, }; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_session::lint; use rustc_span::Span; use std::iter; @@ -101,7 +99,7 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { a_uv == b_uv } // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // to evaluate `a_ct` and `b_ct` if they are fully concrete or something like // this, for now we just return false here. _ => false, } @@ -138,7 +136,7 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { #[instrument(skip(tcx), level = "debug")] pub fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, - (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + (a, b): (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>), param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { @@ -159,13 +157,22 @@ pub fn try_unify_abstract_consts<'tcx>( /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] -pub fn is_const_evaluatable<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, - uv: ty::Unevaluated<'tcx, ()>, +pub fn is_const_evaluatable<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, span: Span, ) -> Result<(), NotConstEvaluatable> { let tcx = infcx.tcx; + let uv = match ct.kind() { + ty::ConstKind::Unevaluated(uv) => uv, + ty::ConstKind::Param(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error(_) => return Ok(()), + ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), + }; if tcx.features().generic_const_exprs { if let Some(ct) = AbstractConst::new(tcx, uv)? { @@ -235,39 +242,25 @@ pub fn is_const_evaluatable<'cx, 'tcx>( .emit() } - Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() { - NotConstEvaluatable::MentionsInfer - } else if uv.has_param_types_or_consts() { - NotConstEvaluatable::MentionsParam - } else { - let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); - NotConstEvaluatable::Error(guar) - }), + Err(ErrorHandled::TooGeneric) => { + let err = if uv.has_non_region_infer() { + NotConstEvaluatable::MentionsInfer + } else if uv.has_non_region_param() { + NotConstEvaluatable::MentionsParam + } else { + let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); + NotConstEvaluatable::Error(guar) + }; + + Err(err) + }, Err(ErrorHandled::Linted) => { let reported = infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); Err(NotConstEvaluatable::Error(reported)) } Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), - Ok(_) => { - if uv.substs.has_param_types_or_consts() { - assert!(matches!(infcx.tcx.def_kind(uv.def.did), DefKind::AnonConst)); - let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); - - if mir_body.is_polymorphic { - let Some(local_def_id) = uv.def.did.as_local() else { return Ok(()) }; - tcx.struct_span_lint_hir( - lint::builtin::CONST_EVALUATABLE_UNCHECKED, - tcx.hir().local_def_id_to_hir_id(local_def_id), - span, - |err| { - err.build("cannot use constants which depend on generic parameters in types").emit(); - }) - } - } - - Ok(()) - }, + Ok(_) => Ok(()), } } } @@ -281,7 +274,7 @@ fn satisfied_from_param_env<'tcx>( for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { - if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + if let Some(b_ct) = AbstractConst::from_const(tcx, uv)? { let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; // Try to unify with each subtree in the AbstractConst to allow for diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index dba4d4f69..e0c8deec9 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -41,16 +41,16 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { /// Used if you want to have pleasant experience when dealing /// with obligations outside of hir or mir typeck. pub struct ObligationCtxt<'a, 'tcx> { - pub infcx: &'a InferCtxt<'a, 'tcx>, + pub infcx: &'a InferCtxt<'tcx>, engine: RefCell>>, } impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { Self { infcx, engine: RefCell::new(>::new(infcx.tcx)) } } - pub fn new_in_snapshot(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self { Self { infcx, engine: RefCell::new(>::new_in_snapshot(infcx.tcx)) } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index efdb1ace1..1217d264a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,10 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, - OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, + FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, + Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, + OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, + SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; @@ -22,6 +22,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; +use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::TypeTrace; use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; @@ -32,6 +33,8 @@ use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, }; +use rustc_session::Limit; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::{kw, sym}; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; @@ -41,8 +44,8 @@ use std::ops::ControlFlow; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; use crate::traits::specialize::to_pretty_impl_header; -use on_unimplemented::InferCtxtExt as _; -use suggestions::InferCtxtExt as _; +use on_unimplemented::TypeErrCtxtExt as _; +use suggestions::TypeErrCtxtExt as _; pub use rustc_infer::traits::error_reporting::*; @@ -63,6 +66,37 @@ pub struct ImplCandidate<'tcx> { } pub trait InferCtxtExt<'tcx> { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)>; + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected_args: Vec, + found_args: Vec, + is_closure: bool, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + constness: ty::BoundConstness, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; +} + +pub trait TypeErrCtxtExt<'tcx> { fn report_fulfillment_errors( &self, errors: &[FulfillmentError<'tcx>], @@ -78,6 +112,8 @@ pub trait InferCtxtExt<'tcx> { where T: fmt::Display + TypeFoldable<'tcx>; + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); + fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; /// The `root_obligation` parameter should be the `root_obligation` field @@ -90,12 +126,71 @@ pub trait InferCtxtExt<'tcx> { error: &SelectionError<'tcx>, fallback_has_occurred: bool, ); +} +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// Given some node representing a fn-like thing in the HIR map, /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)>; + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), + .. + }) => ( + fn_decl_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = + *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) => ( + sig.span, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::>(), + ), + Node::Ctor(ref variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {:?}", node), + }) + } /// Reports an error when the number of arguments needed by a /// trait match doesn't match the number that the expression @@ -107,21 +202,175 @@ pub trait InferCtxtExt<'tcx> { expected_args: Vec, found_args: Vec, is_closure: bool, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(&ArgKind::Tuple(_, ref fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {}", found_str)); + + // move |_| { ... } + // ^^^^^^^^-- def_span + // + // move |_| { ... } + // ^^^^^-- prefix + let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); + // move |_| { ... } + // ^^^-- pipe_span + let pipe_span = + if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + pipe_span, + &format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{}|", underscores), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{}|", sugg), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } - /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` - /// in that order, and returns the generic type corresponding to the - /// argument of that trait (corresponding to the closure arguments). fn type_implements_fn_trait( &self, param_env: ty::ParamEnv<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, constness: ty::BoundConstness, polarity: ty::ImplPolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; -} + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the substitutions + // of the trait are. + let var = self.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }); + let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]); + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef::new(trait_def_id, substs), + constness, + polarity, + }) + .to_predicate(self.tcx), + ); + let mut fulfill_cx = FulfillmentContext::new_in_snapshot(); + fulfill_cx.register_predicate_obligation(self, obligation); + if fulfill_cx.select_all_or_error(self).is_empty() { + return Ok(( + ty::ClosureKind::from_def_id(self.tcx, trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + Err(()) + }) + } +} +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn report_fulfillment_errors( &self, errors: &[FulfillmentError<'tcx>], @@ -251,6 +500,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { bug!(); } + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + err.help(&format!( + "consider increasing the recursion limit by adding a \ + `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + suggested_limit, + self.tcx.crate_name(LOCAL_CRATE), + )); + } + /// Reports that a cycle was detected which led to overflow and halts /// compilation. This is equivalent to `report_overflow_error` except /// that we can give a more helpful error message (and, in particular, @@ -498,10 +760,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ObligationCauseCode::ObjectCastObligation(concrete_ty, obj_ty) = obligation.cause.code().peel_derives() && - Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty); } + let mut unsatisfied_const = false; if trait_predicate.is_const_if_const() && obligation.param_env.is_const() { let non_const_predicate = trait_ref.without_const(); let non_const_obligation = Obligation { @@ -511,6 +774,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { recursion_depth: obligation.recursion_depth, }; if self.predicate_may_hold(&non_const_obligation) { + unsatisfied_const = true; err.span_note( span, &format!( @@ -606,11 +870,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( - obligation.param_env, - trait_ref.self_ty(), - trait_predicate.skip_binder().constness, - trait_predicate.skip_binder().polarity, - ) + obligation.param_env, + trait_ref.self_ty(), + trait_predicate.skip_binder().constness, + trait_predicate.skip_binder().polarity, + ) { // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following // suggestion to add trait bounds for the type, since we only typically implement @@ -661,8 +925,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); } } - } else if !trait_ref.has_infer_types_or_consts() - && self.predicate_can_apply(obligation.param_env, trait_ref) + } else if !trait_ref.has_non_region_infer() + && self.predicate_can_apply(obligation.param_env, trait_predicate) { // If a where-clause may be useful, remind the // user that they can add it. @@ -677,7 +941,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { None, obligation.cause.body_id, ); - } else if !suggested { + } else if !suggested && !unsatisfied_const { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( @@ -840,12 +1104,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Additional context information explaining why the closure only implements // a particular trait. - if let Some(typeck_results) = self.in_progress_typeck_results { + if let Some(typeck_results) = &self.typeck_results { let hir_id = self .tcx .hir() .local_def_id_to_hir_id(closure_def_id.expect_local()); - let typeck_results = typeck_results.borrow(); match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { (ty::ClosureKind::FnOnce, Some((span, place))) => { err.span_label( @@ -994,344 +1257,107 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { found_span, found_trait_ref, expected_trait_ref, + obligation.cause.code(), ) } else { let (closure_span, found) = found_did .and_then(|did| { let node = self.tcx.hir().get_if_local(did)?; - let (found_span, found) = self.get_fn_like_arguments(node)?; - Some((Some(found_span), found)) - }) - .unwrap_or((found_span, found)); - - self.report_arg_count_mismatch( - span, - closure_span, - expected, - found, - found_trait_ty.is_closure(), - ) - } - } - - TraitNotObjectSafe(did) => { - let violations = self.tcx.object_safety_violations(did); - report_object_safety_error(self.tcx, span, did, violations) - } - - SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { - bug!( - "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" - ) - } - SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { - if !self.tcx.features().generic_const_exprs { - let mut err = self.tcx.sess.struct_span_err( - span, - "constant expression depends on a generic parameter", - ); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - // - // Note that with `feature(generic_const_exprs)` this case should not - // be reachable. - err.note("this may fail depending on what value the parameter takes"); - err.emit(); - return; - } - - match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(uv) => { - let mut err = - self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = self.tcx.def_span(uv.def.did); - match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(&format!( - "try adding a `where` bound using this expression: `where [(); {}]:`", - snippet - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; - err - } - _ => { - span_bug!( - span, - "unexpected non-ConstEvaluatable predicate, this should not be reachable" - ) - } - } - } - - // Already reported in the query. - SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => { - // FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token. - self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); - return; - } - // Already reported. - Overflow(OverflowError::Error(_)) => { - self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); - return; - } - Overflow(_) => { - bug!("overflow should be handled before the `report_selection_error` path"); - } - SelectionError::ErrorReporting => { - bug!("ErrorReporting Overflow should not reach `report_selection_err` call") - } - }; - - self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - - err.emit(); - } - - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), - .. - }) => ( - fn_decl_span, - hir.body(body) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::>>()?, - ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref sig, _), .. - }) => ( - sig.span, - sig.decl - .inputs - .iter() - .map(|arg| match arg.kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::>(), - ), - Node::Ctor(ref variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {:?}", node), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option, - expected_args: Vec, - found_args: Vec, - is_closure: bool, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = matches!(other, &[ArgKind::Tuple(..)]); - match (arg_length, arguments.get(0)) { - (1, Some(&ArgKind::Tuple(_, ref fields))) => { - format!("a single {}-tuple as argument", fields.len()) + let (found_span, found) = self.get_fn_like_arguments(node)?; + Some((Some(found_span), found)) + }) + .unwrap_or((found_span, found)); + + self.report_arg_count_mismatch( + span, + closure_span, + expected, + found, + found_trait_ty.is_closure(), + ) } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {}", found_str)); - - // move |_| { ... } - // ^^^^^^^^-- def_span - // - // move |_| { ... } - // ^^^^^-- prefix - let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); - // move |_| { ... } - // ^^^-- pipe_span - let pipe_span = - if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - pipe_span, - &format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{}|", underscores), - Applicability::MachineApplicable, - ); + TraitNotObjectSafe(did) => { + let violations = self.tcx.object_safety_violations(did); + report_object_safety_error(self.tcx, span, did, violations) } - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{}|", sugg), - Applicability::MachineApplicable, + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { + bug!( + "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" + ) + } + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { + if !self.tcx.features().generic_const_exprs { + let mut err = self.tcx.sess.struct_span_err( + span, + "constant expression depends on a generic parameter", ); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(generic_const_exprs)` this case should not + // be reachable. + err.note("this may fail depending on what value the parameter takes"); + err.emit(); + return; } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] - && fields.len() == found_args.len() - && is_closure - { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::>() - .join(", ") + + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::ConstEvaluatable(ct) => { + let ty::ConstKind::Unevaluated(uv) = ct.kind() else { + bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + }; + let mut err = + self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(uv.def.did); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(&format!( + "try adding a `where` bound using this expression: `where [(); {}]:`", + snippet + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + err + } + _ => { + span_bug!( + span, + "unexpected non-ConstEvaluatable predicate, this should not be reachable" ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); + } + } } - } - - err - } - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - constness: ty::BoundConstness, - polarity: ty::ImplPolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { - self.commit_if_ok(|_| { - for trait_def_id in [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] { - let Some(trait_def_id) = trait_def_id else { continue }; - // Make a fresh inference variable so we can determine what the substitutions - // of the trait are. - let var = self.next_ty_var(TypeVariableOrigin { - span: DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, - }); - let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]); - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new(trait_def_id, substs), - constness, - polarity, - }) - .to_predicate(self.tcx), - ); - let mut fulfill_cx = FulfillmentContext::new_in_snapshot(); - fulfill_cx.register_predicate_obligation(self, obligation); - if fulfill_cx.select_all_or_error(self).is_empty() { - return Ok(( - ty::ClosureKind::from_def_id(self.tcx, trait_def_id) - .expect("expected to map DefId to ClosureKind"), - ty.rebind(self.resolve_vars_if_possible(var)), - )); - } + // Already reported in the query. + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => { + // FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token. + self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); + return; + } + // Already reported. + Overflow(OverflowError::Error(_)) => { + self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); + return; } + Overflow(_) => { + bug!("overflow should be handled before the `report_selection_error` path"); + } + SelectionError::ErrorReporting => { + bug!("ErrorReporting Overflow should not reach `report_selection_err` call") + } + }; - Err(()) - }) + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + + err.emit(); } } -trait InferCtxtPrivExt<'hir, 'tcx> { +trait InferCtxtPrivExt<'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; @@ -1412,7 +1438,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn predicate_can_apply( &self, param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>); @@ -1430,13 +1456,13 @@ trait InferCtxtPrivExt<'hir, 'tcx> { predicate: ty::Predicate<'tcx>, ); - fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'hir>); + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>); fn maybe_indirection_for_unsized( &self, err: &mut Diagnostic, - item: &'hir Item<'hir>, - param: &'hir GenericParam<'hir>, + item: &'tcx Item<'tcx>, + param: &'tcx GenericParam<'tcx>, ) -> bool; fn is_recursive_obligation( @@ -1446,7 +1472,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { ) -> bool; } -impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { @@ -1540,6 +1566,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } diag.emit(); } + FulfillmentErrorCode::CodeCycle(ref cycle) => { + self.report_overflow_error_cycle(cycle); + } } } @@ -1636,7 +1665,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .. }) | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::TyAlias(ty), + kind: hir::ImplItemKind::Type(ty), .. }), ) => Some((ty.span, format!("type mismatch resolving `{}`", predicate))), @@ -1895,7 +1924,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // FIXME(compiler-errors): This could be generalized, both to // be more granular, and probably look past other `#[fundamental]` // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_id.owner, self.tcx) + self.tcx + .visibility(def.did()) + .is_accessible_from(body_id.owner.def_id, self.tcx) } else { true } @@ -1905,16 +1936,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } let normalize = |candidate| { - self.tcx.infer_ctxt().enter(|ref infcx| { - let normalized = infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .normalize(candidate) - .ok(); - match normalized { - Some(normalized) => normalized.value, - None => candidate, - } - }) + let infcx = self.tcx.infer_ctxt().build(); + infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .normalize(candidate) + .map_or(candidate, |normalized| normalized.value) }; // Sort impl candidates so that ordering is consistent for UI tests. @@ -2088,7 +2114,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // Pick the first substitution that still contains inference variables as the one // we're going to emit an error for. If there are none (see above), fall back to // a more general error. - let subst = data.trait_ref.substs.iter().find(|s| s.has_infer_types_or_consts()); + let subst = data.trait_ref.substs.iter().find(|s| s.has_non_region_infer()); let mut err = if let Some(subst) = subst { self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true) @@ -2258,13 +2284,22 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { trait_impls.non_blanket_impls().len() ) }; - + let mut suggestions = vec![( + trait_path_segment.ident.span.shrink_to_lo(), + format!("<{} as ", self.tcx.type_of(impl_def_id)) + )]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext); + // get rid of :: between Trait and + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string(),)); + suggestions.push((generic_arg.span_ext.shrink_to_hi(), format!(">"))); + } else { + suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), format!(">"))); + } err.multipart_suggestion( message, - vec![ - (trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())), - (trait_path_segment.ident.span.shrink_to_hi(), format!(">")) - ], + suggestions, Applicability::MaybeIncorrect ); } @@ -2309,7 +2344,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .substs .iter() .chain(Some(data.term.into_arg())) - .find(|g| g.has_infer_types_or_consts()); + .find(|g| g.has_non_region_infer()); if let Some(subst) = subst { let mut err = self.emit_inference_failure_err( body_id, @@ -2338,7 +2373,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { if predicate.references_error() || self.is_tainted_by_errors() { return; } - let subst = data.substs.iter().find(|g| g.has_infer_types_or_consts()); + let subst = data.walk().find(|g| g.is_non_region_infer()); if let Some(subst) = subst { let err = self.emit_inference_failure_err( body_id, @@ -2478,10 +2513,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn predicate_can_apply( &self, param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { struct ParamToVarFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, var_map: FxHashMap, Ty<'tcx>>, } @@ -2522,7 +2557,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let obligation = Obligation::new( ObligationCause::dummy(), param_env, - cleaned_pred.without_const().to_predicate(selcx.tcx()), + cleaned_pred.to_predicate(selcx.tcx()), ); self.predicate_may_hold(&obligation) @@ -2567,12 +2602,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics<'hir>( - &self, - err: &mut Diagnostic, - span: Span, - node: Node<'hir>, - ) { + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { let Some(generics) = node.generics() else { return; }; @@ -2623,11 +2653,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); } - fn maybe_indirection_for_unsized<'hir>( + fn maybe_indirection_for_unsized( &self, err: &mut Diagnostic, - item: &'hir Item<'hir>, - param: &'hir GenericParam<'hir>, + item: &Item<'tcx>, + param: &GenericParam<'tcx>, ) -> bool { // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` @@ -2727,82 +2757,6 @@ impl<'v> Visitor<'v> for FindTypeParam { } } -pub fn recursive_type_with_infinite_size_error<'tcx>( - tcx: TyCtxt<'tcx>, - type_def_id: DefId, - spans: Vec<(Span, Option)>, -) { - assert!(type_def_id.is_local()); - let span = tcx.def_span(type_def_id); - let path = tcx.def_path_str(type_def_id); - let mut err = - struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path); - err.span_label(span, "recursive type has infinite size"); - for &(span, _) in &spans { - err.span_label(span, "recursive without indirection"); - } - let msg = format!( - "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable", - path, - ); - if spans.len() <= 4 { - // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed - err.multipart_suggestion( - &msg, - spans - .into_iter() - .flat_map(|(span, field_id)| { - if let Some(generic_span) = get_option_generic_from_field_id(tcx, field_id) { - // If we match an `Option` and can grab the span of the Option's generic, then - // suggest boxing the generic arg for a non-null niche optimization. - vec![ - (generic_span.shrink_to_lo(), "Box<".to_string()), - (generic_span.shrink_to_hi(), ">".to_string()), - ] - } else { - vec![ - (span.shrink_to_lo(), "Box<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ] - } - }) - .collect(), - Applicability::HasPlaceholders, - ); - } else { - err.help(&msg); - } - err.emit(); -} - -/// Extract the span for the generic type `T` of `Option` in a field definition -fn get_option_generic_from_field_id(tcx: TyCtxt<'_>, field_id: Option) -> Option { - let node = tcx.hir().find(field_id?); - - // Expect a field from our field_id - let Some(hir::Node::Field(field_def)) = node - else { bug!("Expected HirId corresponding to FieldDef, found: {:?}", node) }; - - // Match a type that is a simple QPath with no Self - let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = &field_def.ty.kind - else { return None }; - - // Check if the path we're checking resolves to Option - let hir::def::Res::Def(_, did) = path.res - else { return None }; - - // Bail if this path doesn't describe `::core::option::Option` - if !tcx.is_diagnostic_item(sym::Option, did) { - return None; - } - - // Match a single generic arg in the 0th path segment - let generic_arg = path.segments.last()?.args?.args.get(0)?; - - // Take the span out of the type, if it's a type - if let hir::GenericArg::Type(generic_ty) = generic_arg { Some(generic_ty.span) } else { None } -} - /// Summarizes information #[derive(Clone)] pub enum ArgKind { @@ -2847,3 +2801,8 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor { } } } + +pub enum DefIdOrName { + DefId(DefId), + Name(&'static str), +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index e11a42201..5eef54c63 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -1,17 +1,17 @@ use super::{ ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, }; -use crate::infer::InferCtxt; +use crate::infer::error_reporting::TypeErrCtxt; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, GenericParamDefKind}; use rustc_span::symbol::sym; use std::iter; use super::InferCtxtPrivExt; -pub trait InferCtxtExt<'tcx> { +pub trait TypeErrCtxtExt<'tcx> { /*private*/ fn impl_similar_to( &self, @@ -29,7 +29,7 @@ pub trait InferCtxtExt<'tcx> { ) -> OnUnimplementedNote; } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, trait_ref: ty::PolyTraitRef<'tcx>, @@ -164,6 +164,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); } + if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + flags.push((sym::cause, Some("MainFunctionType".to_string()))); + } + // Add all types without trimmed paths. ty::print::with_no_trimmed_paths!({ let generics = self.tcx.generics_of(def_id); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 13d9c1600..8c41d9d24 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,5 +1,5 @@ use super::{ - EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext, }; @@ -7,6 +7,7 @@ use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::normalize_to; +use hir::def::CtorOf; use hir::HirId; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -20,7 +21,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; +use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, @@ -28,9 +31,7 @@ use rustc_middle::ty::{ ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; -use rustc_session::Limit; -use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::fmt; @@ -62,7 +63,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { // meet an obligation fn try_get_upvar_span( &self, - infer_context: &InferCtxt<'a, 'tcx>, + infer_context: &InferCtxt<'tcx>, generator_did: DefId, ty_matches: F, ) -> Option @@ -168,7 +169,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { } // This trait is public to expose the diagnostics methods to clippy. -pub trait InferCtxtExt<'tcx> { +pub trait TypeErrCtxtExt<'tcx> { fn suggest_restricting_param_bound( &self, err: &mut Diagnostic, @@ -255,8 +256,15 @@ pub trait InferCtxtExt<'tcx> { found_span: Option, found: ty::PolyTraitRef<'tcx>, expected: ty::PolyTraitRef<'tcx>, + cause: &ObligationCauseCode<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn note_conflicting_closure_bounds( + &self, + cause: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ); + fn suggest_fully_qualified_path( &self, err: &mut Diagnostic, @@ -296,8 +304,6 @@ pub trait InferCtxtExt<'tcx> { ) where T: fmt::Display; - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); - /// Suggest to await before try: future? => future.await? fn suggest_await_before_try( &self, @@ -461,7 +467,7 @@ fn suggest_restriction<'tcx>( } } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_restricting_param_bound( &self, mut err: &mut Diagnostic, @@ -659,7 +665,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => {} } - hir_id = self.tcx.hir().local_def_id_to_hir_id(self.tcx.hir().get_parent_item(hir_id)); + hir_id = self.tcx.hir().get_parent_item(hir_id).into(); } } @@ -675,9 +681,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // It only make sense when suggesting dereferences for arguments let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else { return false; }; - let Some(typeck_results) = self.in_progress_typeck_results + let Some(typeck_results) = &self.typeck_results else { return false; }; - let typeck_results = typeck_results.borrow(); let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id) else { return false; }; let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) @@ -809,74 +814,136 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - // Skipping binder here, remapping below - let self_ty = trait_pred.self_ty().skip_binder(); - - let (def_id, output_ty, callable) = match *self_ty.kind() { - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), - ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), - _ => return false, - }; - let msg = format!("use parentheses to call the {}", callable); + if let ty::PredicateKind::Trait(trait_pred) = obligation.predicate.kind().skip_binder() + && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait() + { + // Don't suggest calling to turn an unsized type into a sized type + return false; + } - // "We should really create a single list of bound vars from the combined vars - // from the predicate and function, but instead we just liberate the function bound vars" - let output_ty = self.tcx.liberate_late_bound_regions(def_id, output_ty); + // This is duplicated from `extract_callable_info` in typeck, which + // relies on autoderef, so we can't use it here. + let found = trait_pred.self_ty().skip_binder().peel_refs(); + let Some((def_id_or_name, output, inputs)) = (match *found.kind() + { + ty::FnPtr(fn_sig) => { + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())) + } + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + Some(( + DefIdOrName::DefId(def_id), + fn_sig.output(), + fn_sig.inputs().map_bound(|inputs| &inputs[1..]), + )) + } + ty::Opaque(def_id, substs) => { + self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Dynamic(data, _, ty::Dyn) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, substs are shifted over by 1 + && let ty::Tuple(args) = proj.substs.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Param(_) => { + obligation.param_env.caller_bounds().iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + && proj.projection_ty.self_ty() == found + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::Name("type parameter"), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + _ => None, + }) else { return false; }; + let output = self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + LateBoundRegionConversionTime::FnCall, + output, + ); + let inputs = inputs.skip_binder().iter().map(|ty| { + self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + LateBoundRegionConversionTime::FnCall, + inputs.rebind(*ty), + ) + }); // Remapping bound vars here - let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output_ty)); + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output)); let new_obligation = self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); - - match self.evaluate_obligation(&new_obligation) { - Ok( - EvaluationResult::EvaluatedToOk - | EvaluationResult::EvaluatedToOkModuloRegions - | EvaluationResult::EvaluatedToOkModuloOpaqueTypes - | EvaluationResult::EvaluatedToAmbig, - ) => {} - _ => return false, + if !self.predicate_must_hold_modulo_regions(&new_obligation) { + return false; } - let hir = self.tcx.hir(); + // Get the name of the callable and the arguments to be used in the suggestion. - let (snippet, sugg) = match hir.get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { fn_decl, fn_decl_span, .. }), - .. - })) => { - err.span_label(*fn_decl_span, "consider calling this closure"); - let Some(name) = self.get_closure_name(def_id, err, &msg) else { - return false; - }; - let args = fn_decl.inputs.iter().map(|_| "_").collect::>().join(", "); - let sugg = format!("({})", args); - (format!("{}{}", name, sugg), sugg) - } - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) => { - err.span_label(ident.span, "consider calling this function"); - let body = hir.body(*body_id); - let args = body - .params - .iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - // FIXME: provide a better suggestion when encountering `SelfLower`, it - // should suggest a method call. - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - let sugg = format!("({})", args); - (format!("{}{}", ident, sugg), sugg) - } - _ => return false, + let hir = self.tcx.hir(); + + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => { + "use parentheses to construct this tuple struct".to_string() + } + DefKind::Ctor(CtorOf::Variant, _) => { + "use parentheses to construct this tuple variant".to_string() + } + kind => format!("use parentheses to call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("use parentheses to call this {name}"), }; + + let args = inputs + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::>() + .join(", "); + if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. }) && obligation.cause.span.can_be_used_for_suggestions() { @@ -887,11 +954,36 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_suggestion_verbose( obligation.cause.span.shrink_to_hi(), &msg, - sugg, + format!("({args})"), Applicability::HasPlaceholders, ); - } else { - err.help(&format!("{}: `{}`", msg, snippet)); + } else if let DefIdOrName::DefId(def_id) = def_id_or_name { + let name = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), + .. + })) => { + err.span_label(*fn_decl_span, "consider calling this closure"); + let Some(name) = self.get_closure_name(def_id, err, &msg) else { + return false; + }; + name.to_string() + } + Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { + err.span_label(ident.span, "consider calling this function"); + ident.to_string() + } + Some(hir::Node::Ctor(..)) => { + let name = self.tcx.def_path_str(def_id); + err.span_label( + self.tcx.def_span(def_id), + format!("consider calling the constructor for `{}`", name), + ); + name + } + _ => return false, + }; + err.help(&format!("{msg}: `{name}({args})`")); } true } @@ -1176,8 +1268,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &format!("this call returns `{}`", pred.self_ty()), ); } - if let Some(typeck_results) = - self.in_progress_typeck_results.map(|t| t.borrow()) + if let Some(typeck_results) = &self.typeck_results && let ty = typeck_results.expr_ty_adjusted(base) && let ty::FnDef(def_id, _substs) = ty.kind() && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = @@ -1231,7 +1322,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } let trait_pred = self.resolve_vars_if_possible(trait_pred); - if trait_pred.has_infer_types_or_consts() { + if trait_pred.has_non_region_infer() { // Do not ICE while trying to find if a reborrow would succeed on a trait with // unresolved bindings. return; @@ -1300,8 +1391,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { && let Some(stmt) = blk.stmts.last() && let hir::StmtKind::Semi(expr) = stmt.kind // Only suggest this if the expression behind the semicolon implements the predicate - && let Some(typeck_results) = self.in_progress_typeck_results - && let Some(ty) = typeck_results.borrow().expr_ty_opt(expr) + && let Some(typeck_results) = &self.typeck_results + && let Some(ty) = typeck_results.expr_ty_opt(expr) && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty)) )) @@ -1390,7 +1481,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); - let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); + let typeck_results = self.typeck_results.as_ref().unwrap(); let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; let ret_types = visitor @@ -1425,7 +1516,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut spans_and_needs_box = vec![]; match liberated_sig.output().kind() { - ty::Dynamic(predicates, _, _) => { + ty::Dynamic(predicates, _, ty::Dyn) => { let cause = ObligationCause::misc(ret_ty.span, fn_hir_id); let param_env = ty::ParamEnv::empty(); @@ -1573,7 +1664,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Point at all the `return`s in the function as they have failed trait bounds. let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); - let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); + let typeck_results = self.typeck_results.as_ref().unwrap(); for expr in &visitor.returns { if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) { let ty = self.resolve_vars_if_possible(returned_ty); @@ -1589,9 +1680,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { found_span: Option, found: ty::PolyTraitRef<'tcx>, expected: ty::PolyTraitRef<'tcx>, + cause: &ObligationCauseCode<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { pub(crate) fn build_fn_sig_ty<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { let inputs = trait_ref.skip_binder().substs.type_at(1); @@ -1650,9 +1742,68 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let signature_kind = format!("{argument_kind} signature"); err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); + self.note_conflicting_closure_bounds(cause, &mut err); + err } + // Add a note if there are two `Fn`-family bounds that have conflicting argument + // requirements, which will always cause a closure to have a type error. + fn note_conflicting_closure_bounds( + &self, + cause: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ) { + // First, look for an `ExprBindingObligation`, which means we can get + // the unsubstituted predicate list of the called function. And check + // that the predicate that we failed to satisfy is a `Fn`-like trait. + if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause + && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) + && let Some(pred) = predicates.predicates.get(*idx) + && let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() + && ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()).is_some() + { + let expected_self = + self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty())); + let expected_substs = self + .tcx + .anonymize_late_bound_regions(pred.kind().rebind(trait_pred.trait_ref.substs)); + + // Find another predicate whose self-type is equal to the expected self type, + // but whose substs don't match. + let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans) + .enumerate() + .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() { + ty::PredicateKind::Trait(trait_pred) + if ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()) + .is_some() + && other_idx != idx + // Make sure that the self type matches + // (i.e. constraining this closure) + && expected_self + == self.tcx.anonymize_late_bound_regions( + pred.kind().rebind(trait_pred.self_ty()), + ) + // But the substs don't match (i.e. incompatible args) + && expected_substs + != self.tcx.anonymize_late_bound_regions( + pred.kind().rebind(trait_pred.trait_ref.substs), + ) => + { + true + } + _ => false, + }); + // If we found one, then it's very likely the cause of the error. + if let Some((_, (_, other_pred_span))) = other_pred { + err.span_note( + *other_pred_span, + "closure inferred to have a different signature due to this bound", + ); + } + } + } + fn suggest_fully_qualified_path( &self, err: &mut Diagnostic, @@ -1841,12 +1992,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let span = self.tcx.def_span(generator_did); - let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()); let generator_did_root = self.tcx.typeck_root_def_id(generator_did); debug!( ?generator_did, ?generator_did_root, - in_progress_typeck_results.hir_owner = ?in_progress_typeck_results.as_ref().map(|t| t.hir_owner), + typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner), ?span, ); @@ -1901,7 +2051,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // type-checking; otherwise, get them by performing a query. This is needed to avoid // cycles. If we can't use resolved types because the generator comes from another crate, // we still provide a targeted error but without all the relevant spans. - let generator_data: Option> = match &in_progress_typeck_results { + let generator_data: Option> = match &self.typeck_results { Some(t) if t.hir_owner.to_def_id() == generator_did_root => { Some(GeneratorData::Local(&t)) } @@ -2707,19 +2857,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(Node::Expr(expr @ hir::Expr { kind: hir::ExprKind::Block(..), .. })) = hir.find(arg_hir_id) { - let in_progress_typeck_results = - self.in_progress_typeck_results.map(|t| t.borrow()); let parent_id = hir.get_parent_item(arg_hir_id); - let typeck_results: &TypeckResults<'tcx> = match &in_progress_typeck_results { + let typeck_results: &TypeckResults<'tcx> = match &self.typeck_results { Some(t) if t.hir_owner == parent_id => t, - _ => self.tcx.typeck(parent_id), + _ => self.tcx.typeck(parent_id.def_id), }; - let ty = typeck_results.expr_ty_adjusted(expr); - let span = expr.peel_blocks().span; + let expr = expr.peel_blocks(); + let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()); + let span = expr.span; if Some(span) != err.span.primary_span() { err.span_label( span, - &if ty.references_error() { + if ty.references_error() { String::new() } else { format!("this tail expression is of type `{:?}`", ty) @@ -2796,19 +2945,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - err.help(&format!( - "consider increasing the recursion limit by adding a \ - `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", - suggested_limit, - self.tcx.crate_name(LOCAL_CRATE), - )); - } - #[instrument( level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty()) )] @@ -2890,19 +3026,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit, .. } if *is_lit => span, _ => return, }; - match ( - trait_ref.skip_binder().self_ty().kind(), - trait_ref.skip_binder().substs.type_at(1).kind(), - ) { - (ty::Float(_), ty::Infer(InferTy::IntVar(_))) => { - err.span_suggestion_verbose( - rhs_span.shrink_to_hi(), - "consider using a floating-point literal by writing it with `.0`", - ".0", - Applicability::MaybeIncorrect, - ); - } - _ => {} + if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind() + && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().substs.type_at(1).kind() + { + err.span_suggestion_verbose( + rhs_span.shrink_to_hi(), + "consider using a floating-point literal by writing it with `.0`", + ".0", + Applicability::MaybeIncorrect, + ); } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a81fef60a..a417e1440 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -25,10 +25,9 @@ use super::Unimplemented; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; -use crate::traits::error_reporting::InferCtxtExt as _; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt; impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { /// Note that we include both the `ParamEnv` and the `Predicate`, @@ -103,7 +102,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { } /// Attempts to select obligations using `selcx`. - fn select(&mut self, selcx: &mut SelectionContext<'a, 'tcx>) -> Vec> { + fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); @@ -138,7 +137,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { #[instrument(level = "debug", skip(self, infcx, param_env, cause))] fn normalize_projection_type( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, @@ -166,7 +165,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn register_predicate_obligation( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { // this helps to reduce duplicate errors, as well as making @@ -183,7 +182,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); } - fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec> { + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { { let errors = self.select_where_possible(infcx); if !errors.is_empty() { @@ -194,12 +193,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect() } - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Vec> { - let mut selcx = SelectionContext::new(infcx); - self.select(&mut selcx) + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { + let selcx = SelectionContext::new(infcx); + self.select(selcx) } fn pending_obligations(&self) -> Vec> { @@ -211,8 +207,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { } } -struct FulfillProcessor<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, +struct FulfillProcessor<'a, 'tcx> { + selcx: SelectionContext<'a, 'tcx>, } fn mk_pending(os: Vec>) -> Vec> { @@ -221,9 +217,10 @@ fn mk_pending(os: Vec>) -> Vec ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { +impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { type Obligation = PendingPredicateObligation<'tcx>; type Error = FulfillmentErrorCode<'tcx>; + type OUT = Outcome; /// Identifies whether a predicate obligation needs processing. /// @@ -279,7 +276,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!(?obligation, "pre-resolve"); - if obligation.predicate.has_infer_types_or_consts() { + if obligation.predicate.has_non_region_infer() { obligation.predicate = self.selcx.infcx().resolve_vars_if_possible(obligation.predicate); } @@ -291,7 +288,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { if obligation.predicate.has_projections() { let mut obligations = Vec::new(); let predicate = crate::traits::project::try_normalize_with_depth_to( - self.selcx, + &mut self.selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, @@ -358,7 +355,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::RegionOutlives(data) => { - if infcx.considering_regions || data.has_placeholders() { + if infcx.considering_regions { infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } @@ -479,9 +476,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( - uv.substs - .iter() - .filter_map(TyOrConstInferVar::maybe_from_generic_arg), + uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); ProcessResult::Unchanged } @@ -495,19 +490,20 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + assert!( + self.selcx.tcx().features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); debug!(?c1, ?c2, "equating consts"); - let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs { - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.kind(), c2.kind()) - { - if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return ProcessResult::Changed(vec![]); - } + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.kind(), c2.kind()) + { + if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { + return ProcessResult::Changed(vec![]); } } @@ -569,7 +565,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ) } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { + if c1.has_non_region_infer() || c2.has_non_region_infer() { ProcessResult::Unchanged } else { // Two different constants using generic parameters ~> error. @@ -594,19 +590,21 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { &mut self, cycle: I, _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, - ) where + ) -> Result<(), FulfillmentErrorCode<'tcx>> + where I: Clone + Iterator>, { if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { debug!("process_child_obligations: coinductive match"); + Ok(()) } else { let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); + Err(FulfillmentErrorCode::CodeCycle(cycle)) } } } -impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { +impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { #[instrument(level = "debug", skip(self, obligation, stalled_on))] fn process_trait_obligation( &mut self, @@ -641,7 +639,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { // information about the types in the trait. stalled_on.clear(); stalled_on.extend(substs_infer_vars( - self.selcx, + &self.selcx, trait_obligation.predicate.map_bound(|pred| pred.trait_ref.substs), )); @@ -693,12 +691,12 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - match project::poly_project_and_unify_type(self.selcx, &project_obligation) { + match project::poly_project_and_unify_type(&mut self.selcx, &project_obligation) { ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)), ProjectAndUnifyResult::FailedNormalization => { stalled_on.clear(); stalled_on.extend(substs_infer_vars( - self.selcx, + &self.selcx, project_obligation.predicate.map_bound(|pred| pred.projection_ty.substs), )); ProcessResult::Unchanged @@ -716,7 +714,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { /// Returns the set of inference variables contained in `substs`. fn substs_infer_vars<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, + selcx: &SelectionContext<'a, 'tcx>, substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> impl Iterator> { selcx @@ -724,11 +722,11 @@ fn substs_infer_vars<'a, 'tcx>( .resolve_vars_if_possible(substs) .skip_binder() // ok because this check doesn't care about regions .iter() - .filter(|arg| arg.has_infer_types_or_consts()) + .filter(|arg| arg.has_non_region_infer()) .flat_map(|arg| { let mut walker = arg.walk(); while let Some(c) = walker.next() { - if !c.has_infer_types_or_consts() { + if !c.has_non_region_infer() { walker.visited.remove(&c); walker.skip_current_subtree(); } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index e1bd48ba8..be603c609 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; -use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::error_reporting::TypeErrCtxtExt; #[derive(Clone)] pub enum CopyImplementationError<'tcx> { @@ -23,65 +23,64 @@ pub fn can_type_implement_copy<'tcx>( parent_cause: ObligationCause<'tcx>, ) -> Result<(), CopyImplementationError<'tcx>> { // FIXME: (@jroesch) float this code up - tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.kind() { - // These types used to have a builtin impl. - // Now libcore provides that impl. - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) - | ty::Array(..) => return Ok(()), + let infcx = tcx.infer_ctxt().build(); + let (adt, substs) = match self_type.kind() { + // These types used to have a builtin impl. + // Now libcore provides that impl. + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => return Ok(()), - ty::Adt(adt, substs) => (adt, substs), + ty::Adt(adt, substs) => (adt, substs), - _ => return Err(CopyImplementationError::NotAnAdt), - }; + _ => return Err(CopyImplementationError::NotAnAdt), + }; - let mut infringing = Vec::new(); - for variant in adt.variants() { - for field in &variant.fields { - let ty = field.ty(tcx, substs); - if ty.references_error() { - continue; - } - let span = tcx.def_span(field.did); - // FIXME(compiler-errors): This gives us better spans for bad - // projection types like in issue-50480. - // If the ADT has substs, point to the cause we are given. - // If it does not, then this field probably doesn't normalize - // to begin with, and point to the bad field's span instead. - let cause = if field - .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did())) - .has_param_types_or_consts() - { - parent_cause.clone() - } else { - ObligationCause::dummy_with_span(span) - }; - match traits::fully_normalize(&infcx, cause, param_env, ty) { - Ok(ty) => { - if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { - infringing.push((field, ty)); - } - } - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - } - }; + let mut infringing = Vec::new(); + for variant in adt.variants() { + for field in &variant.fields { + let ty = field.ty(tcx, substs); + if ty.references_error() { + continue; } + let span = tcx.def_span(field.did); + // FIXME(compiler-errors): This gives us better spans for bad + // projection types like in issue-50480. + // If the ADT has substs, point to the cause we are given. + // If it does not, then this field probably doesn't normalize + // to begin with, and point to the bad field's span instead. + let cause = if field + .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did())) + .has_non_region_param() + { + parent_cause.clone() + } else { + ObligationCause::dummy_with_span(span) + }; + match traits::fully_normalize(&infcx, cause, param_env, ty) { + Ok(ty) => { + if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { + infringing.push((field, ty)); + } + } + Err(errors) => { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + }; } - if !infringing.is_empty() { - return Err(CopyImplementationError::InfrigingFields(infringing)); - } - if adt.has_dtor(tcx) { - return Err(CopyImplementationError::HasDestructor); - } + } + if !infringing.is_empty() { + return Err(CopyImplementationError::InfrigingFields(infringing)); + } + if adt.has_dtor(tcx) { + return Err(CopyImplementationError::HasDestructor); + } - Ok(()) - }) + Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 40596078f..0bf54c096 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -26,7 +26,7 @@ pub mod wf; use crate::errors::DumpVTableEntries; use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -34,12 +34,11 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, DefIdTree, GenericParamDefKind, Subst, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, - VtblEntry, + self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, VtblEntry, }; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_span::{sym, Span}; use smallvec::SmallVec; @@ -130,7 +129,7 @@ pub fn predicates_for_generics<'tcx>( move |(idx, (predicate, span))| Obligation { cause: cause(idx, span), recursion_depth: 0, - param_env: param_env, + param_env, predicate, }, ) @@ -141,8 +140,8 @@ pub fn predicates_for_generics<'tcx>( /// `bound` or is not known to meet bound (note that this is /// conservative towards *no impl*, which is the opposite of the /// `evaluate` methods). -pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn type_known_to_meet_bound_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, def_id: DefId, @@ -171,7 +170,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( result ); - if result && ty.has_infer_types_or_consts() { + if result && ty.has_non_region_infer() { // Because of inference "guessing", selection can sometimes claim // to succeed while the success requires a guess. To ensure // this function's result remains infallible, we must confirm @@ -235,54 +234,51 @@ fn do_normalize_predicates<'tcx>( // by wfcheck anyway, so I'm not sure we have to check // them here too, and we will remove this function when // we move over to lazy normalization *anyway*. - tcx.infer_ctxt().ignoring_regions().enter(|infcx| { - let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { - Ok(predicates) => predicates, - Err(errors) => { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } - }; + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { + Ok(predicates) => predicates, + Err(errors) => { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return Err(reported); + } + }; - debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); + debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); - // We can use the `elaborated_env` here; the region code only - // cares about declarations like `'a: 'b`. - let outlives_env = OutlivesEnvironment::new(elaborated_env); + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); + + // FIXME: It's very weird that we ignore region obligations but apparently + // still need to use `resolve_regions` as we need the resolved regions in + // the normalized predicates. + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + tcx.sess.delay_span_bug( + span, + format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), + ); + } - // FIXME: It's very weird that we ignore region obligations but apparently - // still need to use `resolve_regions` as we need the resolved regions in - // the normalized predicates. - let errors = infcx.resolve_regions(&outlives_env); - if !errors.is_empty() { - tcx.sess.delay_span_bug( + match infcx.fully_resolve(predicates) { + Ok(predicates) => Ok(predicates), + Err(fixup_err) => { + // If we encounter a fixup error, it means that some type + // variable wound up unconstrained. I actually don't know + // if this can happen, and I certainly don't expect it to + // happen often, but if it did happen it probably + // represents a legitimate failure due to some kind of + // unconstrained variable. + // + // @lcnr: Let's still ICE here for now. I want a test case + // for that. + span_bug!( span, - format!( - "failed region resolution while normalizing {elaborated_env:?}: {errors:?}" - ), + "inference variables in normalized parameter environment: {}", + fixup_err ); } - - match infcx.fully_resolve(predicates) { - Ok(predicates) => Ok(predicates), - Err(fixup_err) => { - // If we encounter a fixup error, it means that some type - // variable wound up unconstrained. I actually don't know - // if this can happen, and I certainly don't expect it to - // happen often, but if it did happen it probably - // represents a legitimate failure due to some kind of - // unconstrained variable. - // - // @lcnr: Let's still ICE here for now. I want a test case - // for that. - span_bug!( - span, - "inference variables in normalized parameter environment: {}", - fixup_err - ); - } - } - }) + } } // FIXME: this is gonna need to be removed ... @@ -394,8 +390,8 @@ pub fn normalize_param_env_or_error<'tcx>( } /// Normalize a type and process all resulting obligations, returning any errors -pub fn fully_normalize<'a, 'tcx, T>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn fully_normalize<'tcx, T>( + infcx: &InferCtxt<'tcx>, cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, value: T, @@ -430,8 +426,8 @@ where /// Process an obligation (and any nested obligations that come from it) to /// completion, returning any errors -pub fn fully_solve_obligation<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn fully_solve_obligation<'tcx>( + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) -> Vec> { let mut engine = >::new(infcx.tcx); @@ -441,8 +437,8 @@ pub fn fully_solve_obligation<'a, 'tcx>( /// Process a set of obligations (and any nested obligations that come from them) /// to completion -pub fn fully_solve_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn fully_solve_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, obligations: impl IntoIterator>, ) -> Vec> { let mut engine = >::new(infcx.tcx); @@ -453,8 +449,8 @@ pub fn fully_solve_obligations<'a, 'tcx>( /// Process a bound (and any nested obligations that come from it) to completion. /// This is a convenience function for traits that have no generic arguments, such /// as auto traits, and builtin traits like Copy or Sized. -pub fn fully_solve_bound<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn fully_solve_bound<'tcx>( + infcx: &InferCtxt<'tcx>, cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, @@ -474,21 +470,20 @@ pub fn impossible_predicates<'tcx>( ) -> bool { debug!("impossible_predicates(predicates={:?})", predicates); - let result = tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::reveal_all(); - let ocx = ObligationCtxt::new(&infcx); - let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates); - for predicate in predicates { - let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); - ocx.register_obligation(obligation); - } - let errors = ocx.select_all_or_error(); + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let ocx = ObligationCtxt::new(&infcx); + let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates); + for predicate in predicates { + let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); + ocx.register_obligation(obligation); + } + let errors = ocx.select_all_or_error(); - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - !errors.is_empty() - }); + let result = !errors.is_empty(); debug!("impossible_predicates = {:?}", result); result } @@ -579,18 +574,16 @@ fn is_impossible_method<'tcx>( } }); - tcx.infer_ctxt().ignoring_regions().enter(|ref infcx| { - for obligation in predicates_for_trait { - // Ignore overflow error, to be conservative. - if let Ok(result) = infcx.evaluate_obligation(&obligation) - && !result.may_apply() - { - return true; - } + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + for obligation in predicates_for_trait { + // Ignore overflow error, to be conservative. + if let Ok(result) = infcx.evaluate_obligation(&obligation) + && !result.may_apply() + { + return true; } - - false - }) + } + false } #[derive(Clone, Debug)] @@ -771,12 +764,9 @@ fn dump_vtable_entries<'tcx>( }); } -fn own_existential_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyExistentialTraitRef<'tcx>, -) -> &'tcx [DefId] { +fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] { let trait_methods = tcx - .associated_items(trait_ref.def_id()) + .associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Fn); // Now list each method's DefId (for within its trait). @@ -785,7 +775,7 @@ fn own_existential_vtable_entries<'tcx>( let def_id = trait_method.def_id; // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { + if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) { debug!("own_existential_vtable_entry: not vtable safe"); return None; } @@ -817,7 +807,7 @@ fn vtable_entries<'tcx>( // Lookup the shape of vtable for the trait. let own_existential_entries = - tcx.own_existential_vtable_entries(existential_trait_ref); + tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); let own_entries = own_existential_entries.iter().copied().map(|def_id| { debug!("vtable_entries: trait_method={:?}", def_id); @@ -953,10 +943,9 @@ pub fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( }), ); - let implsrc = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - selcx.select(&obligation).unwrap() - }); + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + let implsrc = selcx.select(&obligation).unwrap(); let Some(ImplSource::TraitUpcasting(implsrc_traitcasting)) = implsrc else { bug!(); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index f2779ce2d..0bb25a74d 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -8,20 +8,20 @@ //! - not reference the erased type `Self` except for in this receiver; //! - not have generic type parameters. -use super::elaborate_predicates; +use super::{elaborate_predicates, elaborate_trait_ref}; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; use hir::def::DefKind; -use rustc_errors::{FatalError, MultiSpan}; +use rustc_errors::{DelayDm, FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; -use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; +use rustc_middle::ty::{GenericArg, InternalSubsts}; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; @@ -164,37 +164,42 @@ fn lint_object_unsafe_trait( ) { // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. // It's also hard to get a use site span, so we use the method definition span. - tcx.struct_span_lint_hir(WHERE_CLAUSES_OBJECT_SAFETY, hir::CRATE_HIR_ID, span, |lint| { - let mut err = lint.build(&format!( - "the trait `{}` cannot be made into an object", - tcx.def_path_str(trait_def_id) - )); - let node = tcx.hir().get_if_local(trait_def_id); - let mut spans = MultiSpan::from_span(span); - if let Some(hir::Node::Item(item)) = node { - spans.push_span_label(item.ident.span, "this trait cannot be made into an object..."); - spans.push_span_label(span, format!("...because {}", violation.error_msg())); - } else { - spans.push_span_label( - span, - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ), + tcx.struct_span_lint_hir( + WHERE_CLAUSES_OBJECT_SAFETY, + hir::CRATE_HIR_ID, + span, + DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))), + |err| { + let node = tcx.hir().get_if_local(trait_def_id); + let mut spans = MultiSpan::from_span(span); + if let Some(hir::Node::Item(item)) = node { + spans.push_span_label( + item.ident.span, + "this trait cannot be made into an object...", + ); + spans.push_span_label(span, format!("...because {}", violation.error_msg())); + } else { + spans.push_span_label( + span, + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ), + ); + }; + err.span_note( + spans, + "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ + call to be resolvable dynamically; for more information visit \ + ", ); - }; - err.span_note( - spans, - "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ - call to be resolvable dynamically; for more information visit \ - ", - ); - if node.is_some() { - // Only provide the help if its a local trait, otherwise it's not - violation.solution(&mut err); - } - err.emit(); - }); + if node.is_some() { + // Only provide the help if its a local trait, otherwise it's not + violation.solution(err); + } + err + }, + ); } fn sized_trait_bound_spans<'tcx>( @@ -442,19 +447,6 @@ fn virtual_call_violation_for_method<'tcx>( return Some(MethodViolationCode::Generic); } - if tcx - .predicates_of(method.def_id) - .predicates - .iter() - // A trait object can't claim to live more than the concrete type, - // so outlives predicates will always hold. - .cloned() - .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) - { - return Some(MethodViolationCode::WhereClauseReferencesSelf); - } - let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. @@ -533,6 +525,21 @@ fn virtual_call_violation_for_method<'tcx>( } } + // NOTE: This check happens last, because it results in a lint, and not a + // hard error. + if tcx + .predicates_of(method.def_id) + .predicates + .iter() + // A trait object can't claim to live more than the concrete type, + // so outlives predicates will always hold. + .cloned() + .filter(|(p, _)| p.to_opt_type_outlives().is_none()) + .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) + { + return Some(MethodViolationCode::WhereClauseReferencesSelf); + } + None } @@ -560,51 +567,44 @@ fn receiver_for_self_ty<'tcx>( /// Creates the object type for the current trait. For example, /// if the current trait is `Deref`, then this will be /// `dyn Deref + 'static`. +#[instrument(level = "trace", skip(tcx), ret)] fn object_ty_for_trait<'tcx>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, lifetime: ty::Region<'tcx>, ) -> Ty<'tcx> { - debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id); - let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); + debug!(?trait_ref); let trait_predicate = trait_ref.map_bound(|trait_ref| { ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) }); - - let mut associated_types = traits::supertraits(tcx, trait_ref) - .flat_map(|super_trait_ref| { - tcx.associated_items(super_trait_ref.def_id()) - .in_definition_order() - .map(move |item| (super_trait_ref, item)) - }) - .filter(|(_, item)| item.kind == ty::AssocKind::Type) - .collect::>(); - - // existential predicates need to be in a specific order - associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id)); - - let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { - // We *can* get bound lifetimes here in cases like - // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. - super_trait_ref.map_bound(|super_trait_ref| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - term: tcx.mk_projection(item.def_id, super_trait_ref.substs).into(), - item_def_id: item.def_id, - substs: super_trait_ref.substs, - }) + debug!(?trait_predicate); + + let mut elaborated_predicates: Vec<_> = elaborate_trait_ref(tcx, trait_ref) + .filter_map(|obligation| { + debug!(?obligation); + let pred = obligation.predicate.to_opt_poly_projection_pred()?; + Some(pred.map_bound(|p| { + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + item_def_id: p.projection_ty.item_def_id, + substs: p.projection_ty.substs, + term: p.term, + }) + })) }) - }); + .collect(); + // NOTE: Since #37965, the existential predicates list has depended on the + // list of predicates to be sorted. This is mostly to enforce that the primary + // predicate comes first. + elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + elaborated_predicates.dedup(); let existential_predicates = tcx - .mk_poly_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); - - let object_ty = tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn); - - debug!("object_ty_for_trait: object_ty=`{}`", object_ty); + .mk_poly_existential_predicates(iter::once(trait_predicate).chain(elaborated_predicates)); + debug!(?existential_predicates); - object_ty + tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn) } /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a @@ -729,10 +729,9 @@ fn receiver_is_dispatchable<'tcx>( Obligation::new(ObligationCause::dummy(), param_env, predicate) }; - tcx.infer_ctxt().enter(|ref infcx| { - // the receiver is dispatchable iff the obligation holds - infcx.predicate_must_hold_modulo_regions(&obligation) - }) + let infcx = tcx.infer_ctxt().build(); + // the receiver is dispatchable iff the obligation holds + infcx.predicate_must_hold_modulo_regions(&obligation) } fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( @@ -838,21 +837,14 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } } - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow { + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { // Constants can only influence object safety if they reference `Self`. // This is only possible for unevaluated constants, so we walk these here. // - // If `AbstractConst::new` returned an error we already failed compilation + // If `AbstractConst::from_const` returned an error we already failed compilation // so we don't have to emit an additional error here. - // - // We currently recurse into abstract consts here but do not recurse in - // `is_const_evaluatable`. This means that the object safety check is more - // liberal than the const eval check. - // - // This shouldn't really matter though as we can't really use any - // constants which are not considered const evaluatable. use rustc_middle::ty::abstract_const::Node; - if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { + if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) { Node::Leaf(leaf) => self.visit_const(leaf), Node::Cast(_, _, ty) => self.visit_ty(ty), @@ -861,7 +853,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } }) } else { - ControlFlow::CONTINUE + ct.super_visit_with(self) } } } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 3008dfcad..108dae092 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -26,7 +26,7 @@ pub trait InferCtxtExt<'a, 'tcx> { ) -> Bounds<'a, 'tcx>; } -impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> { +impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { /// Implied bounds are region relationships that we deduce /// automatically. The idea is that (e.g.) a caller must check that a /// function's argument types are well-formed immediately before diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index a25fb8543..e4284b9d3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -18,7 +18,7 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; -use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; @@ -30,7 +30,6 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable}; use rustc_middle::ty::DefIdTree; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; @@ -63,7 +62,8 @@ enum ProjectionCandidate<'tcx> { /// From a where-clause in the env or object type ParamEnv(ty::PolyProjectionPredicate<'tcx>), - /// From the definition of `Trait` when you have something like <::B as Trait2>::C + /// From the definition of `Trait` when you have something like + /// `<::B as Trait2>::C`. TraitDef(ty::PolyProjectionPredicate<'tcx>), /// Bounds specified on an object type @@ -72,7 +72,15 @@ enum ProjectionCandidate<'tcx> { /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), - ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), + ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>), +} + +#[derive(PartialEq, Eq, Debug)] +enum ImplTraitInTraitCandidate<'tcx> { + // The `impl Trait` from a trait function's default body + Trait, + // A concrete type provided from a trait's `impl Trait` from an impl + Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), } enum ProjectionCandidateSet<'tcx> { @@ -256,7 +264,7 @@ fn project_and_unify_type<'cx, 'tcx>( }; debug!(?normalized, ?obligations, "project_and_unify_type result"); let actual = obligation.predicate.term; - // For an example where this is neccessary see src/test/ui/impl-trait/nested-return-type2.rs + // For an example where this is necessary see src/test/ui/impl-trait/nested-return-type2.rs // This allows users to omit re-mentioning all bounds on an associated type and just use an // `impl Trait` for the assoc type to add more bounds. let InferOk { value: actual, obligations: new } = @@ -514,7 +522,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { self.param_env, ty, ); - self.selcx.infcx().report_overflow_error(&obligation, true); + self.selcx.infcx().err_ctxt().report_overflow_error(&obligation, true); } let substs = substs.fold_with(self); @@ -557,21 +565,6 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { .flatten() .unwrap_or_else(|| ty.super_fold_with(self).into()) }; - // For cases like #95134 we would like to catch overflows early - // otherwise they slip away away and cause ICE. - let recursion_limit = self.tcx().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) - // HACK: Don't overflow when running cargo doc see #100991 - && !self.tcx().sess.opts.actually_rustdoc - { - let obligation = Obligation::with_depth( - self.cause.clone(), - recursion_limit.0, - self.param_env, - ty, - ); - self.selcx.infcx().report_overflow_error(&obligation, true); - } debug!( ?self.depth, ?ty, @@ -664,7 +657,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } pub struct BoundVarReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, // These three maps track the bound variable that were replaced by placeholders. It might be // nice to remove these since we already have the `kind` in the placeholder; we really just need // the `var` (but we *could* bring that into scope if we were to track them as we pass them). @@ -692,7 +685,7 @@ pub struct BoundVarReplacer<'me, 'tcx> { /// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during /// normalization as well, at which point this function will be unnecessary and can be removed. pub fn with_replaced_escaping_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>, R: TypeFoldable<'tcx>>( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, universe_indices: &'a mut Vec>, value: T, f: impl FnOnce(T) -> R, @@ -718,7 +711,7 @@ impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that /// use a binding level above `universe_indices.len()`, we fail. pub fn replace_bound_vars>( - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, universe_indices: &'me mut Vec>, value: T, ) -> ( @@ -838,7 +831,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { // The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, mapped_regions: BTreeMap, mapped_types: BTreeMap, mapped_consts: BTreeMap, ty::BoundVar>, @@ -848,7 +841,7 @@ pub struct PlaceholderReplacer<'me, 'tcx> { impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { pub fn replace_placeholders>( - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, mapped_regions: BTreeMap, mapped_types: BTreeMap, mapped_consts: BTreeMap, ty::BoundVar>, @@ -1319,6 +1312,19 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( let tcx = selcx.tcx(); if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder { let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id); + // If we are trying to project an RPITIT with trait's default `Self` parameter, + // then we must be within a default trait body. + if obligation.predicate.self_ty() + == ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id) + .type_at(0) + && tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value() + { + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( + ImplTraitInTraitCandidate::Trait, + )); + return; + } + let trait_def_id = tcx.parent(trait_fn_def_id); let trait_substs = obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id)); @@ -1330,7 +1336,9 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( let _ = selcx.infcx().commit_if_ok(|_| match selcx.select(&obligation.with(trait_predicate)) { Ok(Some(super::ImplSource::UserDefined(data))) => { - candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data)); + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( + ImplTraitInTraitCandidate::Impl(data), + )); Ok(()) } Ok(None) => { @@ -1368,7 +1376,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( ); } -/// In the case of a nested projection like <::FooT as Bar>::BarT, we may find +/// In the case of a nested projection like `<::FooT as Bar>::BarT`, we may find /// that the definition of `Foo` has some clues: /// /// ```ignore (illustrative) @@ -1489,7 +1497,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( candidate_set.push_candidate(ctor(data)); if potentially_unnormalized_candidates - && !obligation.predicate.has_infer_types_or_consts() + && !obligation.predicate.has_non_region_infer() { // HACK: Pick the first trait def candidate for a fully // inferred predicate. This is to allow duplicates that @@ -1751,8 +1759,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::ConstDestruct(_) - | super::ImplSource::Tuple => { + | super::ImplSource::ConstDestruct(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1793,9 +1800,18 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } - ProjectionCandidate::ImplTraitInTrait(data) => { + ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => { confirm_impl_trait_in_trait_candidate(selcx, obligation, data) } + // If we're projecting an RPITIT for a default trait body, that's just + // the same def-id, but as an opaque type (with regular RPIT semantics). + ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress { + term: selcx + .tcx() + .mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), + obligations: vec![], + }, }; // When checking for cycle during evaluation, we compare predicates with @@ -1830,8 +1846,7 @@ fn confirm_select_candidate<'cx, 'tcx>( | super::ImplSource::Builtin(..) | super::ImplSource::TraitUpcasting(_) | super::ImplSource::TraitAlias(..) - | super::ImplSource::ConstDestruct(_) - | super::ImplSource::Tuple => { + | super::ImplSource::ConstDestruct(_) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, @@ -2142,15 +2157,15 @@ fn confirm_impl_candidate<'cx, 'tcx>( let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); let did = ty::WithOptConstParam::unknown(assoc_ty.item.def_id); - let kind = ty::ConstKind::Unevaluated(ty::Unevaluated::new(did, identity_substs)); + let kind = ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); ty.map_bound(|ty| tcx.mk_const(ty::ConstS { ty, kind }).into()) } else { ty.map_bound(|ty| ty.into()) }; - if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { + if !check_substs_compatible(tcx, &assoc_ty.item, substs) { let err = tcx.ty_error_with_message( obligation.cause.span, - "impl item and trait item have different parameter counts", + "impl item and trait item have different parameters", ); Progress { term: err.into(), obligations: nested } } else { @@ -2159,6 +2174,44 @@ fn confirm_impl_candidate<'cx, 'tcx>( } } +// Verify that the trait item and its implementation have compatible substs lists +fn check_substs_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_ty: &ty::AssocItem, + substs: ty::SubstsRef<'tcx>, +) -> bool { + fn check_substs_compatible_inner<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + args: &'tcx [ty::GenericArg<'tcx>], + ) -> bool { + if generics.count() != args.len() { + return false; + } + + let (parent_args, own_args) = args.split_at(generics.parent_count); + + if let Some(parent) = generics.parent + && let parent_generics = tcx.generics_of(parent) + && !check_substs_compatible_inner(tcx, parent_generics, parent_args) { + return false; + } + + for (param, arg) in std::iter::zip(&generics.params, own_args) { + match (¶m.kind, arg.unpack()) { + (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} + _ => return false, + } + } + + true + } + + check_substs_compatible_inner(tcx, tcx.generics_of(assoc_ty.def_id), substs.as_slice()) +} + fn confirm_impl_trait_in_trait_candidate<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -2175,8 +2228,21 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( return Progress { term: tcx.ty_error().into(), obligations }; } + // Use the default `impl Trait` for the trait, e.g., for a default trait body + if leaf_def.item.container == ty::AssocItemContainer::TraitContainer { + return Progress { + term: tcx + .mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), + obligations, + }; + } + let impl_fn_def_id = leaf_def.item.def_id; - let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, trait_fn_def_id, data.substs); + // Rebase from {trait}::{fn}::{opaque} to {impl}::{fn}::{opaque}, + // since `data.substs` are the impl substs. + let impl_fn_substs = + obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs); let cause = ObligationCause::new( obligation.cause.span, 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 32669e23d..c84f128dd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -31,7 +31,7 @@ pub trait InferCtxtExt<'tcx> { ) -> EvaluationResult; } -impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// Evaluates whether the predicate can be satisfied (by any means) /// in the given `ParamEnv`. fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 40acabf62..58e4597b7 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -5,17 +5,16 @@ use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; -use rustc_middle::mir; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; +use rustc_span::DUMMY_SP; use std::ops::ControlFlow; @@ -155,7 +154,7 @@ impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor { } struct QueryNormalizer<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, cause: &'cx ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, obligations: Vec>, @@ -214,7 +213,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { self.param_env, ty, ); - self.infcx.report_overflow_error(&obligation, true); + self.infcx.err_ctxt().report_overflow_error(&obligation, true); } let generic_ty = self.tcx().bound_type_of(def_id); @@ -255,7 +254,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - bug!("unexpected ambiguity: {:?} {:?}", c_data, result); + // Rustdoc normalizes possibly not well-formed types, so only + // treat this as a bug if we're not in rustdoc. + if !tcx.sess.opts.actually_rustdoc { + tcx.sess.delay_span_bug( + DUMMY_SP, + format!("unexpected ambiguity: {:?} {:?}", c_data, result), + ); + } + return Err(NoSolution); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( @@ -298,7 +305,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - bug!("unexpected ambiguity: {:?} {:?}", c_data, result); + // Rustdoc normalizes possibly not well-formed types, so only + // treat this as a bug if we're not in rustdoc. + if !tcx.sess.opts.actually_rustdoc { + tcx.sess.delay_span_bug( + DUMMY_SP, + format!("unexpected ambiguity: {:?} {:?}", c_data, result), + ); + } + return Err(NoSolution); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( @@ -348,31 +363,6 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { )) } - fn try_fold_mir_const( - &mut self, - constant: mir::ConstantKind<'tcx>, - ) -> Result, Self::Error> { - Ok(match constant { - mir::ConstantKind::Ty(c) => { - let const_folded = c.try_super_fold_with(self)?; - match const_folded.kind() { - ty::ConstKind::Value(valtree) => { - let tcx = self.infcx.tcx; - let ty = const_folded.ty(); - let const_val = tcx.valtree_to_const_val((ty, valtree)); - debug!(?ty, ?valtree, ?const_val); - - mir::ConstantKind::Val(const_val, ty) - } - _ => mir::ConstantKind::Ty(const_folded), - } - } - mir::ConstantKind::Val(_, _) | mir::ConstantKind::Unevaluated(..) => { - constant.try_super_fold_with(self)? - } - }) - } - #[inline] fn try_fold_predicate( &mut self, 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 18988861a..6bf3ed0d0 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 @@ -16,7 +16,7 @@ pub struct CustomTypeOp { impl CustomTypeOp { pub fn new<'tcx, R>(closure: F, description: G) -> Self where - F: FnOnce(&InferCtxt<'_, 'tcx>) -> Fallible>, + F: FnOnce(&InferCtxt<'tcx>) -> Fallible>, G: Fn() -> String, { CustomTypeOp { closure, description } @@ -25,7 +25,7 @@ impl CustomTypeOp { impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp where - F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'tcx>) -> Fallible>, + F: for<'a, 'cx> FnOnce(&'a InferCtxt<'tcx>) -> Fallible>, G: Fn() -> String, { type Output = R; @@ -36,7 +36,7 @@ where /// 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>) -> Fallible> { + fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } @@ -57,7 +57,7 @@ where /// 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: super::TypeOp<'tcx, Output = R>, R>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, op: impl FnOnce() -> Fallible>, ) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> { // During NLL, we expect that nobody will register region 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 8a7916570..29ae8ae6b 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 @@ -32,7 +32,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { /// 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>) -> Fallible>; + fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible>; } /// The output from performing a type op @@ -78,7 +78,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> Fallible<( Self::QueryResponse, @@ -120,7 +120,7 @@ where type Output = Q::QueryResponse; type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; - fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible> { + fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible> { let mut region_constraints = QueryRegionConstraints::default(); let (output, error_info, mut obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints)?; diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index 8148e2b78..8cf500a46 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, ToPredicate}; pub(crate) fn update<'tcx, T>( engine: &mut T, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, ) where T: TraitEngine<'tcx>, 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 a80527f63..4c5bc3339 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,6 +6,7 @@ //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use hir::LangItem; +use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::traits::ObligationCause; @@ -173,7 +174,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); - let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); + let needs_infer = stack.obligation.predicate.has_non_region_infer(); // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including @@ -362,7 +363,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - candidates.vec.extend(result.into_iter().map(ProjectionCandidate)); + candidates + .vec + .extend(result.into_iter().map(|(idx, constness)| ProjectionCandidate(idx, constness))); } /// Given an obligation like ``, searches the obligations that the caller @@ -622,7 +625,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - _ => candidates.vec.push(AutoImplCandidate(def_id)), + _ => candidates.vec.push(AutoImplCandidate), } } } @@ -823,13 +826,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { DEREF_INTO_DYN_SUPERTRAIT, obligation.cause.body_id, obligation.cause.span, - |lint| { - lint.build(&format!( - "`{}` implements `Deref` with supertrait `{}` as output", - source, - deref_output_ty - )).emit(); - }, + DelayDm(|| format!( + "`{}` implements `Deref` with supertrait `{}` as output", + source, deref_output_ty + )), + |lint| lint, ); return; } @@ -888,11 +889,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - if obligation.has_param_types_or_consts() { + if obligation.has_non_region_param() { return; } - if obligation.has_infer_types_or_consts() { + if obligation.has_non_region_infer() { candidates.ambiguous = true; return; } @@ -913,7 +914,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); if self.tcx().is_trait_alias(def_id) { - candidates.vec.push(TraitAliasCandidate(def_id)); + candidates.vec.push(TraitAliasCandidate); } } @@ -1020,7 +1021,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = self.infcx().shallow_resolve(obligation.self_ty().skip_binder()); match self_ty.kind() { ty::Tuple(_) => { - candidates.vec.push(TupleCandidate); + candidates.vec.push(BuiltinCandidate { has_nested: false }); } ty::Infer(ty::TyVar(_)) => { candidates.ambiguous = true; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index d1deef784..ed22058c6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,9 +11,10 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; -use rustc_middle::ty::{ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{ + self, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef, + ToPolyTraitRef, ToPredicate, Ty, TyCtxt, +}; use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; @@ -63,15 +64,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id)) } - AutoImplCandidate(trait_def_id) => { - let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); + AutoImplCandidate => { + let data = self.confirm_auto_impl_candidate(obligation); ImplSource::AutoImpl(data) } - ProjectionCandidate(idx) => { + ProjectionCandidate(idx, constness) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; - // FIXME(jschievink): constness - ImplSource::Param(obligations, ty::BoundConstness::NotConst) + ImplSource::Param(obligations, constness) } ObjectCandidate(idx) => { @@ -100,8 +100,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { PointeeCandidate => ImplSource::Pointee(ImplSourcePointeeData), - TraitAliasCandidate(alias_def_id) => { - let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); + TraitAliasCandidate => { + let data = self.confirm_trait_alias_candidate(obligation); ImplSource::TraitAlias(data) } @@ -126,8 +126,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; ImplSource::ConstDestruct(data) } - - TupleCandidate => ImplSource::Tuple, }; if !obligation.predicate.is_const_if_const() { @@ -290,8 +288,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let scope = type_at(2).skip_binder(); - let assume = - rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, const_at(3)); + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, const_at(3)) else { + return Err(Unimplemented); + }; let cause = obligation.cause.clone(); @@ -315,13 +315,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_auto_impl_candidate( &mut self, obligation: &TraitObligation<'tcx>, - trait_def_id: DefId, ) -> ImplSourceAutoImplData> { - debug!(?obligation, ?trait_def_id, "confirm_auto_impl_candidate"); + debug!(?obligation, "confirm_auto_impl_candidate"); let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty()); let types = self.constituent_types_for_ty(self_ty); - self.vtable_auto_impl(obligation, trait_def_id, types) + self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types) } /// See `confirm_auto_impl_candidate`. @@ -619,17 +618,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + + // Confirm the `type Output: Sized;` bound that is present on `FnOnce` + let cause = obligation.derived_cause(BuiltinDerivedObligation); + // The binder on the Fn obligation is "less" important than the one on + // the signature, as evidenced by how we treat it during projection. + // The safe thing to do here is to liberate it, though, which should + // have no worse effect than skipping the binder here. + let liberated_fn_ty = + self.infcx.replace_bound_vars_with_placeholders(obligation.predicate.rebind(self_ty)); + let output_ty = self + .infcx + .replace_bound_vars_with_placeholders(liberated_fn_ty.fn_sig(self.tcx()).output()); + let output_ty = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth, + output_ty, + &mut nested, + ); + let tr = ty::Binder::dummy(ty::TraitRef::new( + self.tcx().require_lang_item(LangItem::Sized, None), + self.tcx().mk_substs_trait(output_ty, &[]), + )); + nested.push(Obligation::new( + cause, + obligation.param_env, + tr.to_poly_trait_predicate().to_predicate(self.tcx()), + )); + Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested }) } fn confirm_trait_alias_candidate( &mut self, obligation: &TraitObligation<'tcx>, - alias_def_id: DefId, ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> { - debug!(?obligation, ?alias_def_id, "confirm_trait_alias_candidate"); + debug!(?obligation, "confirm_trait_alias_candidate"); + let alias_def_id = obligation.predicate.def_id(); let predicate = self.infcx().replace_bound_vars_with_placeholders(obligation.predicate); let trait_ref = predicate.trait_ref; let trait_def_id = trait_ref.def_id; @@ -1196,6 +1225,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Never | ty::Foreign(_) => {} + // `ManuallyDrop` is trivially drop + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().manually_drop() => {} + // These types are built-in, so we can fast-track by registering // nested predicates for their constituent type(s) ty::Array(ty, _) | ty::Slice(ty) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 75bd2c89f..9ebff4892 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -20,7 +20,7 @@ use super::{ }; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; -use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; @@ -35,9 +35,8 @@ use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_span::symbol::sym; @@ -94,7 +93,7 @@ impl IntercrateAmbiguityCause { } pub struct SelectionContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, /// Freshener used specifically for entries on the obligation /// stack. This ensures that all entries on the stack at one time @@ -215,7 +214,7 @@ enum BuiltinImplConditions<'tcx> { } impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx, freshener: infcx.freshener_keep_static(), @@ -225,28 +224,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: true, - intercrate_ambiguity_causes: None, - query_mode: TraitQueryMode::Standard, - } + pub fn intercrate(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { intercrate: true, ..SelectionContext::new(infcx) } } pub fn with_query_mode( - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, query_mode: TraitQueryMode, ) -> SelectionContext<'cx, 'tcx> { debug!(?query_mode, "with_query_mode"); - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: false, - intercrate_ambiguity_causes: None, - query_mode, - } + SelectionContext { query_mode, ..SelectionContext::new(infcx) } } /// Enables tracking of intercrate ambiguity causes. See @@ -266,7 +253,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.intercrate_ambiguity_causes.take().unwrap_or_default() } - pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { + pub fn infcx(&self) -> &'cx InferCtxt<'tcx> { self.infcx } @@ -689,19 +676,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + assert!( + self.tcx().features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); - if self.tcx().features().generic_const_exprs { - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.kind(), c2.kind()) - { - if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return Ok(EvaluatedToOk); - } + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.kind(), c2.kind()) + { + if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) { + return Ok(EvaluatedToOk); } } @@ -741,7 +730,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { + if c1.has_non_region_infer() || c2.has_non_region_infer() { Ok(EvaluatedToAmbig) } else { // Two different constants using generic parameters ~> error. @@ -839,7 +828,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// must be met of course). One obvious case this comes up is /// marker traits like `Send`. Think of a linked list: /// - /// struct List { data: T, next: Option>> } + /// struct List { data: T, next: Option>> } /// /// `Box>` will be `Send` if `T` is `Send` and /// `Option>>` is `Send`, and in turn @@ -926,38 +915,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let unbound_input_types = stack.fresh_trait_pred.skip_binder().trait_ref.substs.types().any(|ty| ty.is_fresh()); - if stack.obligation.polarity() != ty::ImplPolarity::Negative { - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = with_no_trimmed_paths!({ - IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - } - }); - - debug!(?cause, "evaluate_stack: pushing cause"); - self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause); - } - } - } - return Ok(EvaluatedToAmbig); - } - } - if unbound_input_types && stack.iter().skip(1).any(|prev| { stack.obligation.param_env == prev.obligation.param_env @@ -1140,7 +1097,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ErrorGuaranteed::unchecked_claim_error_was_emitted(), )); } - self.infcx.report_overflow_error(error_obligation, true); + self.infcx.err_ctxt().report_overflow_error(error_obligation, true); } TraitQueryMode::Canonical => { return Err(OverflowError::Canonical); @@ -1192,8 +1149,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(def_id) if tcx.constness(def_id) == hir::Constness::Const => {} // const param ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {} + // const projection + ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) => {} // auto trait impl - AutoImplCandidate(..) => {} + AutoImplCandidate => {} // generator, this will raise error in other places // or ignore error with const_async_blocks feature GeneratorCandidate => {} @@ -1399,7 +1358,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &TraitObligation<'tcx>, - ) -> smallvec::SmallVec<[usize; 2]> { + ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> { let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate); let placeholder_trait_predicate = self.infcx().replace_bound_vars_with_placeholders(poly_trait_predicate); @@ -1447,7 +1406,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => false, } }) { - return Some(idx); + return Some((idx, pred.constness)); } } None @@ -1563,7 +1522,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !generics.params.is_empty() && obligation.predicate.substs[generics.parent_count..] .iter() - .any(|&p| p.has_infer_types_or_consts() && self.infcx.shallow_resolve(p) != p) + .any(|&p| p.has_non_region_infer() && self.infcx.shallow_resolve(p) != p) { ProjectionMatchesProjection::Ambiguous } else { @@ -1605,13 +1564,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, - // `DiscriminantKindCandidate`, `ConstDestructCandidate`, and `TupleCandidate` + // `DiscriminantKindCandidate`, `ConstDestructCandidate` // to anything else. // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. match (&other.candidate, &victim.candidate) { - (_, AutoImplCandidate(..)) | (AutoImplCandidate(..), _) => { + (_, AutoImplCandidate) | (AutoImplCandidate, _) => { bug!( "default implementations shouldn't be recorded \ when there are other valid candidates" @@ -1626,8 +1585,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate | PointeeCandidate - | ConstDestructCandidate(_) - | TupleCandidate, + | ConstDestructCandidate(_), _, ) => true, ( @@ -1635,8 +1593,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate | PointeeCandidate - | ConstDestructCandidate(_) - | TupleCandidate, + | ConstDestructCandidate(_), ) => false, (ParamCandidate(other), ParamCandidate(victim)) => { @@ -1681,11 +1638,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..) + | TraitAliasCandidate | ObjectCandidate(_) - | ProjectionCandidate(_), + | ProjectionCandidate(..), ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). is_global(cand) @@ -1699,7 +1656,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ParamCandidate(ref cand), ) => { // Prefer these to a global where-clause bound @@ -1707,20 +1664,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { is_global(cand) && other.evaluation.must_apply_modulo_regions() } - (ProjectionCandidate(i), ProjectionCandidate(j)) + (ProjectionCandidate(i, _), ProjectionCandidate(j, _)) | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. i < j && !needs_infer } - (ObjectCandidate(_), ProjectionCandidate(_)) - | (ProjectionCandidate(_), ObjectCandidate(_)) => { + (ObjectCandidate(_), ProjectionCandidate(..)) + | (ProjectionCandidate(..), ObjectCandidate(_)) => { bug!("Have both object and projection candidate") } // Arbitrarily give projection and object candidates priority. ( - ObjectCandidate(_) | ProjectionCandidate(_), + ObjectCandidate(_) | ProjectionCandidate(..), ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1729,7 +1686,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ) => true, ( @@ -1741,18 +1698,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..), - ObjectCandidate(_) | ProjectionCandidate(_), + | TraitAliasCandidate, + ObjectCandidate(_) | ProjectionCandidate(..), ) => false, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. - // This requires us to know *for sure* that the `other` impl applies - // i.e., `EvaluatedToOk`. + // While this requires us to know *for sure* that the `other` impl applies + // we still use modulo regions here. // - // FIXME(@lcnr): Using `modulo_regions` here seems kind of scary - // to me but is required for `std` to compile, so I didn't change it - // for now. + // This is fine as specialization currently assumes that specializing + // impls have to be always applicable, meaning that the only allowed + // region constraints may be constraints also present on the default impl. let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { if tcx.specializes((other_def, victim_def)) { @@ -1822,7 +1779,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ImplCandidate(_) | ClosureCandidate | GeneratorCandidate @@ -1831,7 +1788,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ) => false, } } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 7d299e30a..c89165858 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -17,10 +17,10 @@ use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::{struct_span_err, EmissionGuarantee, LintDiagnosticBuilder}; +use rustc_errors::{struct_span_err, DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, ImplSubject, TyCtxt}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::{Span, DUMMY_SP}; @@ -73,8 +73,8 @@ pub struct OverlapError { /// through associated type projection. We deal with such cases by using /// *fulfillment* to relate the two impls, requiring that all projections are /// resolved. -pub fn translate_substs<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn translate_substs<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, source_impl: DefId, source_substs: SubstsRef<'tcx>, @@ -149,13 +149,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); // Create an infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - let impl1_trait_ref = match traits::fully_normalize( - &infcx, - ObligationCause::dummy(), - penv, - impl1_trait_ref, - ) { + let infcx = tcx.infer_ctxt().build(); + let impl1_trait_ref = + match traits::fully_normalize(&infcx, ObligationCause::dummy(), penv, impl1_trait_ref) { Ok(impl1_trait_ref) => impl1_trait_ref, Err(_errors) => { tcx.sess.delay_span_bug( @@ -166,9 +162,8 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, } }; - // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() - }) + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() } /// Attempt to fulfill all obligations of `target_impl` after unification with @@ -176,8 +171,8 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, /// generics of `target_impl`, including both those needed to unify with /// `source_trait_ref` and those whose identity is determined via a where /// clause in the impl. -fn fulfill_implication<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +fn fulfill_implication<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, source_trait_ref: ty::TraitRef<'tcx>, target_impl: DefId, @@ -350,26 +345,12 @@ fn report_conflicting_impls( // Work to be done after we've built the DiagnosticBuilder. We have to define it // now because the struct_lint methods don't return back the DiagnosticBuilder // that's passed in. - fn decorate( + fn decorate<'a, 'b, G: EmissionGuarantee>( tcx: TyCtxt<'_>, overlap: OverlapError, - used_to_be_allowed: Option, impl_span: Span, - err: LintDiagnosticBuilder<'_, G>, - ) -> G { - let msg = format!( - "conflicting implementations of trait `{}`{}{}", - overlap.trait_desc, - overlap - .self_desc - .clone() - .map_or_else(String::new, |ty| { format!(" for type `{}`", ty) }), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", - _ => "", - } - ); - let mut err = err.build(&msg); + err: &'b mut DiagnosticBuilder<'a, G>, + ) -> &'b mut DiagnosticBuilder<'a, G> { match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); @@ -384,7 +365,9 @@ fn report_conflicting_impls( } Err(cname) => { let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s), + Some(s) => { + format!("conflicting implementation in crate `{}`:\n- {}", cname, s) + } None => format!("conflicting implementation in crate `{}`", cname), }; err.note(&msg); @@ -392,28 +375,33 @@ fn report_conflicting_impls( } for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); + cause.add_intercrate_ambiguity_hint(err); } if overlap.involves_placeholder { - coherence::add_placeholder_note(&mut err); + coherence::add_placeholder_note(err); } - err.emit() + err } + let msg = format!( + "conflicting implementations of trait `{}`{}{}", + overlap.trait_desc, + overlap.self_desc.as_deref().map_or_else(String::new, |ty| format!(" for type `{ty}`")), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", + _ => "", + } + ); + match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() || tcx.orphan_check_impl(impl_def_id).is_ok() { - let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); - Some(decorate( - tcx, - overlap, - used_to_be_allowed, - impl_span, - LintDiagnosticBuilder::new(err), - )) + let mut err = struct_span_err!(tcx.sess, impl_span, E0119, "{msg}",); + decorate(tcx, overlap, impl_span, &mut err); + Some(err.emit()) } else { Some(tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check")) }; @@ -428,9 +416,8 @@ fn report_conflicting_impls( lint, tcx.hir().local_def_id_to_hir_id(impl_def_id), impl_span, - |ldb| { - decorate(tcx, overlap, used_to_be_allowed, impl_span, ldb); - }, + msg, + |err| decorate(tcx, overlap, impl_span, err), ); } }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index fcb73b43f..63f89a33e 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -137,9 +137,8 @@ impl ChildrenExt<'_> for Children { impl_def_id, traits::SkipLeakCheck::default(), overlap_mode, - |_| true, - || false, - ); + ) + .is_some(); let error = create_overlap_error(overlap); @@ -162,34 +161,29 @@ impl ChildrenExt<'_> for Children { impl_def_id, traits::SkipLeakCheck::Yes, overlap_mode, - |overlap| { - if let Some(overlap_kind) = - tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) - { - match overlap_kind { - ty::ImplOverlapKind::Permitted { marker: _ } => {} - ty::ImplOverlapKind::Issue33140 => { - *last_lint_mut = Some(FutureCompatOverlapError { - error: create_overlap_error(overlap), - kind: FutureCompatOverlapErrorKind::Issue33140, - }); - } + ) + .map_or(Ok((false, false)), |overlap| { + if let Some(overlap_kind) = + tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) + { + match overlap_kind { + ty::ImplOverlapKind::Permitted { marker: _ } => {} + ty::ImplOverlapKind::Issue33140 => { + *last_lint_mut = Some(FutureCompatOverlapError { + error: create_overlap_error(overlap), + kind: FutureCompatOverlapErrorKind::Issue33140, + }); } - - return Ok((false, false)); } - let le = tcx.specializes((impl_def_id, possible_sibling)); - let ge = tcx.specializes((possible_sibling, impl_def_id)); + return Ok((false, false)); + } - if le == ge { - report_overlap_error(overlap, last_lint_mut) - } else { - Ok((le, ge)) - } - }, - || Ok((false, false)), - )?; + let le = tcx.specializes((impl_def_id, possible_sibling)); + let ge = tcx.specializes((possible_sibling, impl_def_id)); + + if le == ge { report_overlap_error(overlap, last_lint_mut) } else { Ok((le, ge)) } + })?; if le && !ge { debug!( diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 5829a0f92..932dbbb81 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -68,7 +68,7 @@ pub fn search_for_adt_const_param_violation<'tcx>( /// Note that this does *not* recursively check if the substructure of `adt_ty` /// implements the traits. fn type_marked_structural<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, adt_ty: Ty<'tcx>, cause: ObligationCause<'tcx>, ) -> bool { @@ -265,9 +265,8 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { pub fn provide(providers: &mut Providers) { providers.has_structural_eq_impls = |tcx, ty| { - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::dummy(); - type_marked_structural(&infcx, ty, cause) - }) + let infcx = tcx.infer_ctxt().build(); + let cause = ObligationCause::dummy(); + type_marked_structural(&infcx, ty, cause) }; } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 0f5dff01c..ed47d2f83 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -5,8 +5,8 @@ use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{GenericArg, SubstsRef}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; pub use rustc_infer::traits::{self, util::*}; @@ -268,10 +268,7 @@ pub fn count_own_vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> usize { - let existential_trait_ref = - trait_ref.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - let existential_trait_ref = tcx.erase_regions(existential_trait_ref); - tcx.own_existential_vtable_entries(existential_trait_ref).len() + tcx.own_existential_vtable_entries(trait_ref.def_id()).len() } /// Given an upcast trait object described by `object`, returns the @@ -282,15 +279,10 @@ pub fn get_vtable_index_of_object_method<'tcx, N>( object: &super::ImplSourceObjectData<'tcx, N>, method_def_id: DefId, ) -> Option { - let existential_trait_ref = object - .upcast_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - let existential_trait_ref = tcx.erase_regions(existential_trait_ref); - // Count number of methods preceding the one we are selecting and // add them to the total offset. if let Some(index) = tcx - .own_existential_vtable_entries(existential_trait_ref) + .own_existential_vtable_entries(object.upcast_trait_ref.def_id()) .iter() .copied() .position(|def_id| def_id == method_def_id) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 5ea28fb47..fc0a9f690 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -14,8 +14,8 @@ use std::iter; /// inference variable, returns `None`, because we are not able to /// make any progress at all. This is to prevent "livelock" where we /// say "$0 is WF if $0 is WF". -pub fn obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn obligations<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, recursion_depth: usize, @@ -79,8 +79,8 @@ pub fn obligations<'a, 'tcx>( /// well-formed. For example, if there is a trait `Set` defined like /// `trait Set`, then the trait reference `Foo: Set` is WF /// if `Bar: Eq`. -pub fn trait_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn trait_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, trait_pred: &ty::TraitPredicate<'tcx>, @@ -102,8 +102,8 @@ pub fn trait_obligations<'a, 'tcx>( } #[instrument(skip(infcx), ret)] -pub fn predicate_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn predicate_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, predicate: ty::Predicate<'tcx>, @@ -148,13 +148,8 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(a.into()); wf.compute(b.into()); } - ty::PredicateKind::ConstEvaluatable(uv) => { - let obligations = wf.nominal_obligations(uv.def.did, uv.substs); - wf.out.extend(obligations); - - for arg in uv.substs.iter() { - wf.compute(arg); - } + ty::PredicateKind::ConstEvaluatable(ct) => { + wf.compute(ct.into()); } ty::PredicateKind::ConstEquate(c1, c2) => { wf.compute(c1.into()); @@ -219,12 +214,14 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( trait_ref, item, cause, pred ); let (items, impl_def_id) = match item { - Some(hir::Item { kind: hir::ItemKind::Impl(impl_), def_id, .. }) => (impl_.items, *def_id), + Some(hir::Item { kind: hir::ItemKind::Impl(impl_), owner_id, .. }) => { + (impl_.items, *owner_id) + } _ => return, }; let fix_span = |impl_item_ref: &hir::ImplItemRef| match tcx.hir().impl_item(impl_item_ref.id).kind { - hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::TyAlias(ty) => ty.span, + hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::Type(ty) => ty.span, _ => impl_item_ref.span, }; @@ -241,7 +238,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.item_def_id) && let Some(impl_item_span) = items .iter() - .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .find(|item| item.id.owner_id.to_def_id() == impl_item_id) .map(fix_span) { cause.span = impl_item_span; @@ -256,7 +253,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( tcx.impl_item_implementor_ids(impl_def_id).get(&item_def_id) && let Some(impl_item_span) = items .iter() - .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .find(|item| item.id.owner_id.to_def_id() == impl_item_id) .map(fix_span) { cause.span = impl_item_span; @@ -275,7 +272,7 @@ impl<'tcx> WfPredicates<'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } - fn normalize(self, infcx: &InferCtxt<'_, 'tcx>) -> Vec> { + fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec> { let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let mut obligations = Vec::with_capacity(self.out.len()); @@ -392,7 +389,8 @@ impl<'tcx> WfPredicates<'tcx> { // `i32: Clone` // `i32: Copy` // ] - let obligations = self.nominal_obligations(data.item_def_id, data.substs); + // Projection types do not require const predicates. + let obligations = self.nominal_obligations_without_const(data.item_def_id, data.substs); self.out.extend(obligations); let tcx = self.tcx(); @@ -449,14 +447,14 @@ impl<'tcx> WfPredicates<'tcx> { // obligations are handled by the parent (e.g. `ty::Ref`). GenericArgKind::Lifetime(_) => continue, - GenericArgKind::Const(constant) => { - match constant.kind() { + GenericArgKind::Const(ct) => { + match ct.kind() { ty::ConstKind::Unevaluated(uv) => { let obligations = self.nominal_obligations(uv.def.did, uv.substs); self.out.extend(obligations); let predicate = - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv)) + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)) .to_predicate(self.tcx()); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( @@ -473,7 +471,7 @@ impl<'tcx> WfPredicates<'tcx> { cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(constant.into())) + ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())) .to_predicate(self.tcx()), )); } @@ -549,7 +547,7 @@ impl<'tcx> WfPredicates<'tcx> { } ty::FnDef(did, substs) => { - let obligations = self.nominal_obligations(did, substs); + let obligations = self.nominal_obligations_without_const(did, substs); self.out.extend(obligations); } -- cgit v1.2.3