diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
45 files changed, 1695 insertions, 1858 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index bcb6ac13b8f..091773009e9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -196,7 +196,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(predicate, _)| { predicate @@ -435,7 +435,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, terr: TypeError<'tcx>, param_env: Option<ParamEnv<'tcx>>, - path: &mut Option<PathBuf>, ) { match *cause.code() { ObligationCauseCode::Pattern { @@ -458,7 +457,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("this is an iterator with items of type `{}`", args.type_at(0)), ); } else { - let expected_ty = self.tcx.short_ty_string(expected_ty, path); + let expected_ty = self.tcx.short_string(expected_ty, err.long_ty_path()); err.span_label(span, format!("this expression has type `{expected_ty}`")); } } @@ -1392,9 +1391,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { mut values: Option<ty::ParamEnvAnd<'tcx, ValuePairs<'tcx>>>, terr: TypeError<'tcx>, prefer_label: bool, + override_span: Option<Span>, ) { - let span = cause.span; - + // We use `override_span` when we want the error to point at a `Span` other than + // `cause.span`. This is used in E0271, when a closure is passed in where the return type + // isn't what was expected. We want to point at the closure's return type (or expression), + // instead of the expression where the closure is passed as call argument. + let span = override_span.unwrap_or(cause.span); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. if let TypeError::CyclicTy(_) = terr { @@ -1541,7 +1544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (false, Mismatch::Fixed("existential projection")) } }; - let Some(vals) = self.values_str(values, cause) else { + let Some(vals) = self.values_str(values, cause, diag.long_ty_path()) else { // Derived error. Cancel the emitter. // NOTE(eddyb) this was `.cancel()`, but `diag` // is borrowed, so we can't fully defuse it. @@ -1596,9 +1599,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } - let mut path = None; - if let Some((expected, found, p)) = expected_found { - path = p; + if let Some((expected, found)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( ef.expected.prefix_string(self.tcx), @@ -1721,32 +1722,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } TypeError::Sorts(values) => { - let extra = expected == found; + let extra = expected == found + // Ensure that we don't ever say something like + // expected `impl Trait` (opaque type `impl Trait`) + // found `impl Trait` (opaque type `impl Trait`) + && values.expected.sort_string(self.tcx) + != values.found.sort_string(self.tcx); let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { (true, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) => { let sm = self.tcx.sess.source_map(); let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); - format!( + DiagStyledString::normal(format!( " (opaque type at <{}:{}:{}>)", sm.filename_for_diagnostics(&pos.file.name), pos.line, pos.col.to_usize() + 1, - ) + )) } (true, ty::Alias(ty::Projection, proj)) if self.tcx.is_impl_trait_in_trait(proj.def_id) => { let sm = self.tcx.sess.source_map(); let pos = sm.lookup_char_pos(self.tcx.def_span(proj.def_id).lo()); - format!( + DiagStyledString::normal(format!( " (trait associated opaque type at <{}:{}:{}>)", sm.filename_for_diagnostics(&pos.file.name), pos.line, pos.col.to_usize() + 1, - ) + )) } - (true, _) => format!(" ({})", ty.sort_string(self.tcx)), - (false, _) => "".to_string(), + (true, _) => { + let mut s = DiagStyledString::normal(" ("); + s.push_highlighted(ty.sort_string(self.tcx)); + s.push_normal(")"); + s + } + (false, _) => DiagStyledString::normal(""), }; if !(values.expected.is_simple_text() && values.found.is_simple_text()) || (exp_found.is_some_and(|ef| { @@ -1763,23 +1774,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } })) { - if let Some(ExpectedFound { found: found_ty, .. }) = exp_found { + if let Some(ExpectedFound { found: found_ty, .. }) = exp_found + && !self.tcx.ty_is_opaque_future(found_ty) + { // `Future` is a special opaque type that the compiler // will try to hide in some case such as `async fn`, so // to make an error more use friendly we will // avoid to suggest a mismatch type with a // type that the user usually are not using // directly such as `impl Future<Output = u8>`. - if !self.tcx.ty_is_opaque_future(found_ty) { - diag.note_expected_found_extra( - &expected_label, - expected, - &found_label, - found, - &sort_string(values.expected), - &sort_string(values.found), - ); - } + diag.note_expected_found_extra( + &expected_label, + expected, + &found_label, + found, + sort_string(values.expected), + sort_string(values.found), + ); } } } @@ -1844,7 +1855,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_tuple_pattern(cause, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); - self.suggest_function_pointers(cause, span, &exp_found, diag); + self.suggest_function_pointers(cause, span, &exp_found, terr, diag); self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); } } @@ -1874,11 +1885,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr, param_env, &mut path); - if let Some(path) = path { - diag.note(format!("the full type name has been written to '{}'", path.display())); - diag.note("consider using `--verbose` to print the full type name to the console"); - } + self.note_error_origin(diag, cause, exp_found, terr, param_env); debug!(?diag); } @@ -1887,6 +1894,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trace: &TypeTrace<'tcx>, terr: TypeError<'tcx>, + path: &mut Option<PathBuf>, ) -> Vec<TypeErrorAdditionalDiags> { let mut suggestions = Vec::new(); let span = trace.cause.span; @@ -1965,7 +1973,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) | ObligationCauseCode::BlockTailExpression(.., source)) = code && let hir::MatchSource::TryDesugar(_) = source - && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values, &trace.cause) + && let Some((expected_ty, found_ty)) = self.values_str(trace.values, &trace.cause, path) { suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { found: found_ty.content(), @@ -2023,14 +2031,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => None, }; if let Some(tykind) = tykind - && let hir::TyKind::Array(_, length) = tykind - && let Some((scalar, ty)) = sz.found.try_to_scalar() - && ty == self.tcx.types.usize + && let hir::TyKind::Array(_, length_arg) = tykind + && let Some(length_val) = sz.found.try_to_target_usize(self.tcx) { - let span = length.span(); Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { - span, - length: scalar.to_target_usize(&self.tcx).unwrap(), + span: length_arg.span(), + length: length_val, }) } else { None @@ -2046,12 +2052,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); let span = trace.cause.span; + let mut path = None; let failure_code = trace.cause.as_failure_code_diag( terr, span, - self.type_error_additional_suggestions(&trace, terr), + self.type_error_additional_suggestions(&trace, terr, &mut path), ); let mut diag = self.dcx().create_err(failure_code); + *diag.long_ty_path() = path; self.note_type_err( &mut diag, &trace.cause, @@ -2059,6 +2067,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(param_env.and(trace.values)), terr, false, + None, ); diag } @@ -2095,10 +2104,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, values: ValuePairs<'tcx>, cause: &ObligationCause<'tcx>, - ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { + file: &mut Option<PathBuf>, + ) -> Option<(DiagStyledString, DiagStyledString)> { match values { ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found), + ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, file), ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), @@ -2108,7 +2118,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { found: exp_found.found.print_trait_sugared(), }; match self.expected_found_str(pretty_exp_found) { - Some((expected, found, _)) if expected == found => { + Some((expected, found)) if expected == found => { self.expected_found_str(exp_found) } ret => ret, @@ -2130,9 +2140,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (None, None) }; - let (exp, fnd) = - self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2); - Some((exp, fnd, None)) + Some(self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2)) } } } @@ -2140,7 +2148,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str_term( &self, exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>, - ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { + path: &mut Option<PathBuf>, + ) -> Option<(DiagStyledString, DiagStyledString)> { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; @@ -2155,21 +2164,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len = self.tcx.sess().diagnostic_width() + 40; let exp_s = exp.content(); let fnd_s = fnd.content(); - let mut path = None; if exp_s.len() > len { - let exp_s = self.tcx.short_ty_string(expected, &mut path); + let exp_s = self.tcx.short_string(expected, path); exp = DiagStyledString::highlighted(exp_s); } if fnd_s.len() > len { - let fnd_s = self.tcx.short_ty_string(found, &mut path); + let fnd_s = self.tcx.short_string(found, path); fnd = DiagStyledString::highlighted(fnd_s); } - (exp, fnd, path) + (exp, fnd) } _ => ( DiagStyledString::highlighted(exp_found.expected.to_string()), DiagStyledString::highlighted(exp_found.found.to_string()), - None, ), }) } @@ -2178,7 +2185,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str<T: fmt::Display + TypeFoldable<TyCtxt<'tcx>>>( &self, exp_found: ty::error::ExpectedFound<T>, - ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { + ) -> Option<(DiagStyledString, DiagStyledString)> { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; @@ -2187,7 +2194,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(( DiagStyledString::highlighted(exp_found.expected.to_string()), DiagStyledString::highlighted(exp_found.found.to_string()), - None, )) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 68a53381230..9e7e96dddd7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -18,6 +18,8 @@ use rustc_middle::ty::{ TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, }; use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::TypeVisitableExt; use tracing::{debug, instrument, warn}; use super::nice_region_error::placeholder_error::Highlighted; @@ -155,27 +157,92 @@ impl UnderspecifiedArgKind { } } -struct ClosureEraser<'tcx> { - tcx: TyCtxt<'tcx>, +struct ClosureEraser<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, } -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> { +impl<'a, 'tcx> ClosureEraser<'a, 'tcx> { + fn new_infer(&mut self) -> Ty<'tcx> { + self.infcx.next_ty_var(DUMMY_SP) + } +} + +impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { match ty.kind() { ty::Closure(_, args) => { + // For a closure type, we turn it into a function pointer so that it gets rendered + // as `fn(args) -> Ret`. let closure_sig = args.as_closure().sig(); Ty::new_fn_ptr( - self.tcx, - self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe), + self.cx(), + self.cx().signature_unclosure(closure_sig, hir::Safety::Safe), ) } - _ => ty.super_fold_with(self), + ty::Adt(_, args) if !args.iter().any(|a| a.has_infer()) => { + // We have a type that doesn't have any inference variables, so we replace + // the whole thing with `_`. The type system already knows about this type in + // its entirety and it is redundant to specify it for the user. The user only + // needs to specify the type parameters that we *couldn't* figure out. + self.new_infer() + } + ty::Adt(def, args) => { + let generics = self.cx().generics_of(def.did()); + let generics: Vec<bool> = generics + .own_params + .iter() + .map(|param| param.default_value(self.cx()).is_some()) + .collect(); + let ty = Ty::new_adt( + self.cx(), + *def, + self.cx().mk_args_from_iter(generics.into_iter().zip(args.iter()).map( + |(has_default, arg)| { + if arg.has_infer() { + // This param has an unsubstituted type variable, meaning that this + // type has a (potentially deeply nested) type parameter from the + // corresponding type's definition. We have explicitly asked this + // type to not be hidden. In either case, we keep the type and don't + // substitute with `_` just yet. + arg.fold_with(self) + } else if has_default { + // We have a type param that has a default type, like the allocator + // in Vec. We decided to show `Vec` itself, because it hasn't yet + // been replaced by an `_` `Infer`, but we want to ensure that the + // type parameter with default types does *not* get replaced with + // `_` because then we'd end up with `Vec<_, _>`, instead of + // `Vec<_>`. + arg + } else if let GenericArgKind::Type(_) = arg.kind() { + // We don't replace lifetime or const params, only type params. + self.new_infer().into() + } else { + arg.fold_with(self) + } + }, + )), + ); + ty + } + _ if ty.has_infer() => { + // This type has a (potentially nested) type parameter that we couldn't figure out. + // We will print this depth of type, so at least the type name and at least one of + // its type parameters. + ty.super_fold_with(self) + } + // We don't have an unknown type parameter anywhere, replace with `_`. + _ => self.new_infer(), } } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + // Avoid accidentally erasing the type of the const. + c + } } fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { @@ -219,9 +286,9 @@ fn ty_to_string<'tcx>( ) -> String { let mut printer = fmt_printer(infcx, Namespace::TypeNS); let ty = infcx.resolve_vars_if_possible(ty); - // We use `fn` ptr syntax for closures, but this only works when the closure - // does not capture anything. - let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx }); + // We use `fn` ptr syntax for closures, but this only works when the closure does not capture + // anything. We also remove all type parameters that are fully known to the type system. + let ty = ty.fold_with(&mut ClosureEraser { infcx }); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in @@ -713,7 +780,7 @@ impl<'tcx> InferSourceKind<'tcx> { if ty.is_closure() { ("closure", closure_as_fn_str(infcx, ty), path) } else if !ty.is_ty_or_numeric_infer() { - ("normal", infcx.tcx.short_ty_string(ty, &mut path), path) + ("normal", infcx.tcx.short_string(ty, &mut path), path) } else { ("other", String::new(), path) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index dfbef39e9e1..039e21cb556 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -323,9 +323,12 @@ pub fn suggest_new_region_bound( .params .iter() .filter(|p| { - matches!(p.kind, GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - }) + matches!( + p.kind, + GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) }) .map(|p| { if let hir::ParamName::Plain(name) = p.name { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs deleted file mode 100644 index beae9962f7f..00000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs +++ /dev/null @@ -1,422 +0,0 @@ -use rustc_errors::{Diag, Subdiagnostic}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; -use rustc_span::kw; -use tracing::debug; - -use super::ObligationCauseAsDiagArg; -use crate::error_reporting::infer::{TypeErrCtxt, note_and_explain_region}; -use crate::errors::{ - FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, RefLongerThanData, - RegionOriginNote, WhereClauseSuggestions, note_and_explain, -}; -use crate::fluent_generated as fluent; -use crate::infer::{self, SubregionOrigin}; - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { - match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), - } - .add_to_diag(err), - infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) - } - infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diag(err); - } - infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diag(err); - } - infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diag(err); - if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); - } - } - infer::RelateRegionParamBound(span, _) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); - } - infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(err); - } - infer::CheckAssociatedTypeBounds { ref parent, .. } => { - self.note_region_origin(err, parent); - } - infer::AscribeUserTypeProvePredicate(span) => { - RegionOriginNote::Plain { - span, - msg: fluent::infer_ascribe_user_type_prove_predicate, - } - .add_to_diag(err); - } - } - } - - pub(super) fn report_concrete_failure( - &self, - generic_param_scope: LocalDefId, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - let mut err = match origin { - infer::Subtype(box trace) => { - let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); - match (*sub, *sup) { - (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} - (ty::RePlaceholder(_), _) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - " doesn't meet the lifetime requirements", - None, - ); - } - (_, ty::RePlaceholder(_)) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "the required lifetime does not necessarily outlive ", - sub, - "", - None, - ); - } - _ => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...does not necessarily outlive ", - sub, - "", - None, - ); - } - } - err - } - infer::Reborrow(span) => { - let reference_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::RefValidFor, - note_and_explain::SuffixKind::Continues, - ); - let content_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::ContentValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesContent { - span, - notes: reference_valid.into_iter().chain(content_valid).collect(), - }) - } - infer::RelateObjectBound(span) => { - let object_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::TypeObjValidFor, - note_and_explain::SuffixKind::Empty, - ); - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::SourcePointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesBound { - span, - notes: object_valid.into_iter().chain(pointer_valid).collect(), - }) - } - infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { - ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, - _ => note_and_explain::PrefixKind::TypeOutlive, - }; - let suffix = if opt_span.is_some() { - note_and_explain::SuffixKind::ReqByBinding - } else { - note_and_explain::SuffixKind::Empty - }; - let note = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - opt_span, - prefix, - suffix, - ); - self.dcx().create_err(FulfillReqLifetime { - span, - ty: self.resolve_vars_if_possible(ty), - note, - }) - } - infer::RelateRegionParamBound(span, _) => { - let param_instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfParamInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let param_must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfParamMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), - }) - } - infer::ReferenceOutlivesReferent(ty, span) => { - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::PointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - let data_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::DataValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(RefLongerThanData { - span, - ty: self.resolve_vars_if_possible(ty), - notes: pointer_valid.into_iter().chain(data_valid).collect(), - }) - } - infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{sup}: {sub}`"), - ); - // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause - if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) - && generics.where_clause_span.contains(span) - { - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - } - err - } - infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); - - // Don't mention the item name if it's an RPITIT, since that'll just confuse - // folks. - if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{item_name}` from trait"), - ); - } - - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - err - } - infer::AscribeUserTypeProvePredicate(span) => { - let instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: instantiated.into_iter().chain(must_outlive).collect(), - }) - } - }; - if sub.is_error() || sup.is_error() { - err.downgrade_to_delayed_bug(); - } - err - } - - pub fn suggest_copy_trait_method_bounds( - &self, - trait_item_def_id: DefId, - impl_item_def_id: LocalDefId, - err: &mut Diag<'_>, - ) { - // FIXME(compiler-errors): Right now this is only being used for region - // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, - // but right now it's not really very smart when it comes to implicit `Sized` - // predicates and bounds on the trait itself. - - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { - return; - }; - let trait_args = trait_ref - .instantiate_identity() - // Replace the explicit self type with `Self` for better suggestion rendering - .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) - .args; - let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) - .rebase_onto(self.tcx, impl_def_id, trait_args); - - let Ok(trait_predicates) = - self.tcx - .explicit_predicates_of(trait_item_def_id) - .instantiate_own(self.tcx, trait_item_args) - .map(|(pred, _)| { - if pred.is_suggestable(self.tcx, false) { - Ok(pred.to_string()) - } else { - Err(()) - } - }) - .collect::<Result<Vec<_>, ()>>() - else { - return; - }; - - let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { - return; - }; - - let suggestion = if trait_predicates.is_empty() { - WhereClauseSuggestions::Remove { span: generics.where_clause_span } - } else { - let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - WhereClauseSuggestions::CopyPredicates { - span: generics.where_clause_span, - space, - trait_predicates: trait_predicates.join(", "), - } - }; - err.subdiagnostic(suggestion); - } - - pub(super) fn report_placeholder_failure( - &self, - generic_param_scope: LocalDefId, - placeholder_origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - // I can't think how to do better than this right now. -nikomatsakis - debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); - match placeholder_origin { - infer::Subtype(box ref trace) - if matches!( - &trace.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - ) => - { - // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - &trace.cause.code().peel_derives() - && !span.is_dummy() - { - let span = *span; - self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) - .with_span_note(span, "the lifetime requirement is introduced here") - } else { - unreachable!( - "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." - ) - } - } - infer::Subtype(box trace) => { - let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); - } - _ => { - return self.report_concrete_failure( - generic_param_scope, - placeholder_origin, - sub, - sup, - ); - } - } - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 1dd09fe7aaf..e8d14b89d69 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -293,7 +293,7 @@ impl<T> Trait<T> for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx - .explicit_item_super_predicates(alias.def_id) + .explicit_item_self_bounds(alias.def_id) .skip_binder() .iter() .any(|(pred, _span)| match pred.kind().skip_binder() { @@ -422,7 +422,7 @@ impl<T> Trait<T> for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); + let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 3acca47025c..f35a5349ecb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -221,7 +221,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { span: trace.cause.span, requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values, &trace.cause).map(|(e, f, _)| (e, f)), + expected_found: self.values_str(trace.values, &trace.cause, err.long_ty_path()), } .add_to_diag(err), infer::Reborrow(span) => { @@ -946,10 +946,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let infer::Subtype(ref sup_trace) = sup_origin && let infer::Subtype(ref sub_trace) = sub_origin - && let Some((sup_expected, sup_found, _)) = - self.values_str(sup_trace.values, &sup_trace.cause) - && let Some((sub_expected, sub_found, _)) = - self.values_str(sub_trace.values, &sup_trace.cause) + && let Some((sup_expected, sup_found)) = + self.values_str(sup_trace.values, &sup_trace.cause, err.long_ty_path()) + && let Some((sub_expected, sub_found)) = + self.values_str(sub_trace.values, &sup_trace.cause, err.long_ty_path()) && sub_expected == sup_expected && sub_found == sup_found { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index af7e56961b7..562000e28ac 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -12,6 +12,7 @@ use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, StatementAsExpression, }; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{Span, sym}; @@ -20,7 +21,7 @@ use tracing::debug; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::hir::Path; use crate::errors::{ - ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; @@ -381,14 +382,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_function_pointers( + pub fn suggest_function_pointers_impl( &self, - cause: &ObligationCause<'tcx>, - span: Span, + span: Option<Span>, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut Diag<'_>, ) { - debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); let ty::error::ExpectedFound { expected, found } = exp_found; let expected_inner = expected.peel_refs(); let found_inner = found.peel_refs(); @@ -411,8 +410,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } + let Some(span) = span else { + let casting = format!("{fn_name} as {sig}"); + diag.subdiagnostic(FnItemsAreDistinct); + diag.subdiagnostic(FnConsiderCasting { casting }); + return; + }; + let sugg = match (expected.is_ref(), found.is_ref()) { - (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, + (true, false) => { + FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() } + } (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, (true, true) => { diag.subdiagnostic(FnItemsAreDistinct); @@ -420,7 +428,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (false, false) => { diag.subdiagnostic(FnItemsAreDistinct); - FunctionPointerSuggestion::Cast { span, fn_name, sig } + FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig } } }; diag.subdiagnostic(sugg); @@ -445,6 +453,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let fn_name = self.tcx.def_path_str_with_args(*did2, args2); + + let Some(span) = span else { + diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig }); + return; + }; + let sug = if found.is_ref() { FunctionPointerSuggestion::CastBothRef { span, @@ -454,8 +468,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } else { FunctionPointerSuggestion::CastBoth { - span, - fn_name, + span: span.shrink_to_hi(), found_sig: *found_sig, expected_sig: *expected_sig, } @@ -488,6 +501,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; } + pub(super) fn suggest_function_pointers( + &self, + cause: &ObligationCause<'tcx>, + span: Span, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + terr: TypeError<'tcx>, + diag: &mut Diag<'_>, + ) { + debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); + + if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() { + self.suggest_function_pointers_impl(Some(span), exp_found, diag); + } else if let TypeError::Sorts(exp_found) = terr { + self.suggest_function_pointers_impl(None, &exp_found, diag); + } + } + pub fn should_suggest_as_ref_kind( &self, expected: Ty<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index b4d294a70c0..6beb108bc3a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -163,6 +163,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let predicate = self.resolve_vars_if_possible(obligation.predicate); let span = obligation.cause.span; + let mut file = None; debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); @@ -179,7 +180,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return e; } - if let Err(guar) = self.tcx.ensure().coherent_trait(trait_pred.def_id()) { + if let Err(guar) = self.tcx.ensure_ok().coherent_trait(trait_pred.def_id()) { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. return guar; @@ -245,7 +246,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, E0283, "type annotations needed: cannot satisfy `{}`", - predicate, + self.tcx.short_string(predicate, &mut file), ) }; @@ -292,7 +293,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.cancel(); return e; } - err.note(format!("cannot satisfy `{predicate}`")); + let pred = self.tcx.short_string(predicate, &mut file); + err.note(format!("cannot satisfy `{pred}`")); let impl_candidates = self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap()); if impl_candidates.len() < 40 { @@ -511,8 +513,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return e; } - if let Err(guar) = - self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id)) + if let Err(guar) = self + .tcx + .ensure_ok() + .coherent_trait(self.tcx.parent(data.projection_term.def_id)) { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. @@ -524,6 +528,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .iter() .chain(Some(data.term.into_arg())) .find(|g| g.has_non_region_infer()); + let predicate = self.tcx.short_string(predicate, &mut file); if let Some(arg) = arg { self.emit_inference_failure_err( obligation.cause.body_id, @@ -539,8 +544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.dcx(), span, E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, + "type annotations needed: cannot satisfy `{predicate}`", ) .with_span_label(span, format!("cannot satisfy `{predicate}`")) } @@ -565,12 +569,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err } else { // If we can't find a generic parameter, just print a generic error + let predicate = self.tcx.short_string(predicate, &mut file); struct_span_code_err!( self.dcx(), span, E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, + "type annotations needed: cannot satisfy `{predicate}`", ) .with_span_label(span, format!("cannot satisfy `{predicate}`")) } @@ -590,6 +594,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } + let alias = self.tcx.short_string(alias, &mut file); struct_span_code_err!( self.dcx(), span, @@ -603,16 +608,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } + let predicate = self.tcx.short_string(predicate, &mut file); struct_span_code_err!( self.dcx(), span, E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, + "type annotations needed: cannot satisfy `{predicate}`", ) .with_span_label(span, format!("cannot satisfy `{predicate}`")) } }; + *err.long_ty_path() = file; self.note_obligation_cause(&mut err, obligation); err.emit() } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5021fd8bf83..1b43820bac0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1,5 +1,6 @@ use core::ops::ControlFlow; use std::borrow::Cow; +use std::path::PathBuf; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::FxHashMap; @@ -9,7 +10,6 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, pluralize, struct_span_code_err, }; -use rustc_hir::def::Namespace; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; @@ -20,8 +20,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{ - FmtPrinter, Print, PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, - PrintTraitRefExt as _, with_forced_trimmed_paths, + PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, PrintTraitRefExt as _, + with_forced_trimmed_paths, }; use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; use rustc_middle::{bug, span_bug}; @@ -60,6 +60,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> ErrorGuaranteed { let tcx = self.tcx; let mut span = obligation.cause.span; + let mut long_ty_file = None; let mut err = match *error { SelectionError::Unimplemented => { @@ -169,11 +170,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Err(guar) = self.fn_arg_obligation(&obligation) { return guar; } - let mut file = None; let (post_message, pre_message, type_def) = self .get_parent_trait_ref(obligation.cause.code()) .map(|(t, s)| { - let t = self.tcx.short_ty_string(t, &mut file); + let t = self.tcx.short_string(t, &mut long_ty_file); ( format!(" in `{t}`"), format!("within `{t}`, "), @@ -181,12 +181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }) .unwrap_or_default(); - let file_note = file.as_ref().map(|file| format!( - "the full trait has been written to '{}'", - file.display(), - )); - - let mut long_ty_file = None; let OnUnimplementedNote { message, @@ -223,6 +217,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None, append_const_msg, post_message, + &mut long_ty_file, ); let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait) @@ -251,14 +246,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); + *err.long_ty_path() = long_ty_file; - if let Some(long_ty_file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - long_ty_file.display(), - )); - err.note("consider using `--verbose` to print the full type name to the console"); - } let mut suggested = false; if is_try_conversion { suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err); @@ -309,7 +298,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return err.emit(); } - file_note.map(|note| err.note(note)); if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! @@ -708,13 +696,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None, TypeError::Sorts(ty::error::ExpectedFound::new(expected_ty, ct_ty)), false, + None, ); diag } }; self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); err.emit() } } @@ -761,14 +749,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref: predicate.trait_ref, polarity: ty::PredicatePolarity::Positive, }); + let mut file = None; let err_msg = self.get_standard_error_message( trait_ref, None, Some(predicate.constness()), None, String::new(), + &mut file, ); let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); + *diag.long_ty_path() = file; if !self.predicate_may_hold(&Obligation::new( self.tcx, ObligationCause::dummy(), @@ -814,7 +805,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "Async", ); self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); return Some(err.emit()); } } @@ -860,7 +850,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "", ); self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); return Some(err.emit()); } @@ -876,7 +865,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { kind: expected_kind.as_str(), }); self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); return Some(err.emit()); } } @@ -931,14 +919,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); - let body_id = match self.tcx.hir_node(hir_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn { body: body_id, .. }, .. - }) => body_id, - _ => return false, - }; - let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span }) - .visit_body(self.tcx.hir().body(*body_id)) + let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false }; + let ControlFlow::Break(expr) = + (FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir().body(body_id)) else { return false; }; @@ -1385,22 +1368,53 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => (None, error.err), }; - let msg = values + let mut file = None; + let (msg, span, closure_span) = values .and_then(|(predicate, normalized_term, expected_term)| { - self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) + self.maybe_detailed_projection_msg( + obligation.cause.span, + predicate, + normalized_term, + expected_term, + &mut file, + ) }) .unwrap_or_else(|| { - let mut cx = FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(10), - ); - with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", { - self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); - cx.into_buffer() - })) + ( + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.tcx + .short_string(self.resolve_vars_if_possible(predicate), &mut file), + )), + obligation.cause.span, + None, + ) }); - let mut diag = struct_span_code_err!(self.dcx(), obligation.cause.span, E0271, "{msg}"); + let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}"); + *diag.long_ty_path() = file; + if let Some(span) = closure_span { + // Mark the closure decl so that it is seen even if we are pointing at the return + // type or expression. + // + // error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns + // `Unit3`, but it returns `Unit4` + // --> $DIR/foo.rs:43:17 + // | + // LL | let v = Unit2.m( + // | - required by a bound introduced by this call + // ... + // LL | f: |x| { + // | --- /* this span */ + // LL | drop(x); + // LL | Unit4 + // | ^^^^^ expected `Unit3`, found `Unit4` + // | + diag.span_label(span, "this closure"); + if !span.overlaps(obligation.cause.span) { + // Point at the binding corresponding to the closure where it is used. + diag.span_label(obligation.cause.span, "closure used here"); + } + } let secondary_span = (|| { let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = @@ -1443,15 +1457,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty.span, with_forced_trimmed_paths!(Cow::from(format!( "type mismatch resolving `{}`", - { - let mut cx = FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(5), - ); - self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); - cx.into_buffer() - } + self.tcx.short_string( + self.resolve_vars_if_possible(predicate), + diag.long_ty_path() + ), ))), true, )), @@ -1471,6 +1480,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }), err, false, + Some(span), ); self.note_obligation_cause(&mut diag, obligation); diag.emit() @@ -1479,34 +1489,65 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn maybe_detailed_projection_msg( &self, + mut span: Span, projection_term: ty::AliasTerm<'tcx>, normalized_ty: ty::Term<'tcx>, expected_ty: ty::Term<'tcx>, - ) -> Option<String> { + file: &mut Option<PathBuf>, + ) -> Option<(String, Span, Option<Span>)> { let trait_def_id = projection_term.trait_def_id(self.tcx); let self_ty = projection_term.self_ty(); with_forced_trimmed_paths! { if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) { - let fn_kind = self_ty.prefix_string(self.tcx); + let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() { + let def_span = self.tcx.def_span(def_id); + if let Some(local_def_id) = def_id.as_local() + && let node = self.tcx.hir_node_by_def_id(local_def_id) + && let Some(fn_decl) = node.fn_decl() + && let Some(id) = node.body_id() + { + span = match fn_decl.output { + hir::FnRetTy::Return(ty) => ty.span, + hir::FnRetTy::DefaultReturn(_) => { + let body = self.tcx.hir().body(id); + match body.value.kind { + hir::ExprKind::Block( + hir::Block { expr: Some(expr), .. }, + _, + ) => expr.span, + hir::ExprKind::Block( + hir::Block { + expr: None, stmts: [.., last], .. + }, + _, + ) => last.span, + _ => body.value.span, + } + } + }; + } + (span, Some(def_span)) + } else { + (span, None) + }; let item = match self_ty.kind() { ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), - _ => self_ty.to_string(), + _ => self.tcx.short_string(self_ty, file), }; - Some(format!( - "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ - returns `{normalized_ty}`", - )) + Some((format!( + "expected `{item}` to return `{expected_ty}`, but it returns `{normalized_ty}`", + ), span, closure_span)) } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) { - Some(format!( + Some((format!( "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ resolves to `{normalized_ty}`" - )) + ), span, None)) } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { - Some(format!( + Some((format!( "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \ yields `{normalized_ty}`" - )) + ), span, None)) } else { None } @@ -1796,13 +1837,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait"); - err.highlighted_span_note(span, vec![ - StringPart::normal("there are ".to_string()), - StringPart::highlighted("multiple different versions".to_string()), - StringPart::normal(" of crate `".to_string()), - StringPart::highlighted(format!("{crate_name}")), - StringPart::normal("` in the dependency graph\n".to_string()), - ]); + err.highlighted_span_note( + span, + vec![ + StringPart::normal("there are ".to_string()), + StringPart::highlighted("multiple different versions".to_string()), + StringPart::normal(" of crate `".to_string()), + StringPart::highlighted(format!("{crate_name}")), + StringPart::normal("` in the dependency graph\n".to_string()), + ], + ); if points_at_type { // We only clarify that the same type from different crate versions are not the // same when we *find* the same type coming from different crate versions, otherwise @@ -1923,8 +1967,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::normal(" implemented for `"), ]); if types_content.0 == types_content.1 { - let ty = - self.tcx.short_ty_string(obligation_trait_ref.self_ty(), &mut None); + let ty = self + .tcx + .short_string(obligation_trait_ref.self_ty(), err.long_ty_path()); msg.push(StringPart::normal(ty)); } else { msg.extend(types.0.0); @@ -1969,6 +2014,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted(exp_found.found.to_string()), StringPart::normal("`"), ]); + self.suggest_function_pointers_impl(None, &exp_found, err); } true @@ -2280,7 +2326,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -2289,15 +2334,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.code(), &mut vec![], &mut Default::default(), - &mut long_ty_file, ); - if let Some(file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - err.note("consider using `--verbose` to print the full type name to the console"); - } self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = @@ -2341,6 +2378,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { predicate_constness: Option<ty::BoundConstness>, append_const_msg: Option<AppendConstMessage>, post_message: String, + long_ty_file: &mut Option<PathBuf>, ) -> String { message .and_then(|cannot_do_this| { @@ -2364,7 +2402,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .unwrap_or_else(|| { format!( "the trait bound `{}` is not satisfied{post_message}", - trait_predicate.print_with_bound_constness(predicate_constness) + self.tcx.short_string( + trait_predicate.print_with_bound_constness(predicate_constness), + long_ty_file, + ), ) }) } @@ -2784,7 +2825,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.span_note(self.tcx.def_span(def_id), "opaque type is declared here"); self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err) } @@ -2940,12 +2980,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .inputs .iter() .map(|arg| match arg.kind { - hir::TyKind::Tup(tys) => { - ArgKind::Tuple(Some(arg.span), vec![ - ("_".to_owned(), "_".to_owned()); - tys.len() - ]) - } + hir::TyKind::Tup(tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), _ => ArgKind::empty(), }) .collect::<Vec<ArgKind>>(), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 8111983c539..658fb4009d5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -306,7 +306,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let ObligationCauseCode::WhereClause(..) | ObligationCauseCode::WhereClauseInExpr(..) = code { - let mut long_ty_file = None; self.note_obligation_cause_code( error.obligation.cause.body_id, &mut diag, @@ -315,17 +314,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { code, &mut vec![], &mut Default::default(), - &mut long_ty_file, ); - if let Some(file) = long_ty_file { - diag.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - diag.note( - "consider using `--verbose` to print the full type name to the console", - ); - } } diag.emit() } @@ -482,11 +471,10 @@ pub fn report_dyn_incompatibility<'tcx>( for (span, msg) in iter::zip(multi_span, messages) { note_span.push_span_label(span, msg); } - // FIXME(dyn_compat_renaming): Update the URL. err.span_note( note_span, "for a trait to be dyn compatible it needs to allow building a vtable\n\ - for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", + for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>", ); // Only provide the help if its a local trait, otherwise it's not actionable. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 4e0b097db4c..518323f6526 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -799,7 +799,7 @@ impl<'tcx> OnUnimplementedFormatString { let mut result = Ok(()); for token in &mut parser { match token { - Piece::String(_) => (), // Normal string, no need to check it + Piece::Lit(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => { let format_spec = a.format; if self.is_diagnostic_namespace_variant @@ -932,7 +932,7 @@ impl<'tcx> OnUnimplementedFormatString { let value = match param.kind { GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { if let Some(ty) = trait_ref.args[param.index as usize].as_type() { - tcx.short_ty_string(ty, long_ty_file) + tcx.short_string(ty, long_ty_file) } else { trait_ref.args[param.index as usize].to_string() } @@ -950,7 +950,7 @@ impl<'tcx> OnUnimplementedFormatString { let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); let constructed_message = (&mut parser) .map(|p| match p { - Piece::String(s) => s.to_owned(), + Piece::Lit(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { Position::ArgumentNamed(arg) => { let s = Symbol::intern(arg); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index d82acc4e054..c5ed74420d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -141,7 +141,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.span, suggest_increasing_limit, |err| { - let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -150,17 +149,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.code(), &mut vec![], &mut Default::default(), - &mut long_ty_file, ); - if let Some(file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - err.note( - "consider using `--verbose` to print the full type name to the console", - ); - } }, ); } @@ -196,7 +185,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { suggest_increasing_limit, ); self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); err.emit() } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 471105773e2..527d2e54e43 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3,7 +3,6 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; -use std::path::PathBuf; use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; @@ -641,10 +640,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Empty suggestions with empty spans ICE with debug assertions if steps == 0 { - return (msg.trim_end_matches(" and dereferencing instead"), vec![( - prefix_span, - String::new(), - )]); + return ( + msg.trim_end_matches(" and dereferencing instead"), + vec![(prefix_span, String::new())], + ); } let derefs = "*".repeat(steps); let needs_parens = steps > 0 @@ -1087,28 +1086,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), )) } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self - .tcx - .item_super_predicates(def_id) - .instantiate(self.tcx, args) - .iter() - .find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + self.tcx.item_self_bounds(def_id).instantiate(self.tcx, args).iter().find_map( + |pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && self .tcx .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }), + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.expect_type()), + 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() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) @@ -1298,30 +1296,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Because of this, we modify the error to refer to the original obligation and // return early in the caller. - let msg = format!("the trait bound `{old_pred}` is not satisfied"); + let msg = format!( + "the trait bound `{}` is not satisfied", + self.tcx.short_string(old_pred, err.long_ty_path()), + ); + let self_ty_str = + self.tcx.short_string(old_pred.self_ty().skip_binder(), err.long_ty_path()); if has_custom_message { err.note(msg); } else { err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)]; } - let mut file = None; err.span_label( span, format!( - "the trait `{}` is not implemented for `{}`", - old_pred.print_modifiers_and_trait_path(), - self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file), + "the trait `{}` is not implemented for `{self_ty_str}`", + old_pred.print_modifiers_and_trait_path() ), ); - if let Some(file) = file { - err.note(format!( - "the full type name has been written to '{}'", - file.display() - )); - err.note( - "consider using `--verbose` to print full type name to the console", - ); - } if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { err.span_suggestions( @@ -1773,7 +1765,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; err.code(E0746); - err.primary_message("return type cannot have an unboxed trait object"); + err.primary_message("return type cannot be a trait object without pointer indirection"); err.children.clear(); let span = obligation.cause.span; @@ -1789,25 +1781,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } else { ("dyn ", span.shrink_to_lo()) }; - let alternatively = if visitor - .returns - .iter() - .map(|expr| self.typeck_results.as_ref().unwrap().expr_ty_adjusted_opt(expr)) - .collect::<FxHashSet<_>>() - .len() - <= 1 - { - err.span_suggestion_verbose( - impl_span, - "consider returning an `impl Trait` instead of a `dyn Trait`", - "impl ", - Applicability::MaybeIncorrect, - ); - "alternatively, " - } else { - err.help("if there were a single returned type, you could use `impl Trait` instead"); - "" - }; + + err.span_suggestion_verbose( + impl_span, + "consider returning an `impl Trait` instead of a `dyn Trait`", + "impl ", + Applicability::MaybeIncorrect, + ); let mut sugg = vec![ (span.shrink_to_lo(), format!("Box<{pre}")), @@ -1839,7 +1819,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.multipart_suggestion( format!( - "{alternatively}box the return type, and wrap all of the returned values in \ + "alternatively, box the return type, and wrap all of the returned values in \ `Box::new`", ), sugg, @@ -1849,41 +1829,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { true } - pub(super) fn point_at_returns_when_relevant( - &self, - err: &mut Diag<'_>, - obligation: &PredicateObligation<'tcx>, - ) { - match obligation.cause.code().peel_derives() { - ObligationCauseCode::SizedReturnType => {} - _ => return, - } - - let hir = self.tcx.hir(); - let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn { body: body_id, .. }, .. - }) = node - { - let body = hir.body(*body_id); - // 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.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); - if ty.references_error() { - // don't print out the [type error] here - err.downgrade_to_delayed_bug(); - } else { - err.span_label(expr.span, format!("this returned value is of type `{ty}`")); - } - } - } - } - } - pub(super) fn report_closure_arg_mismatch( &self, span: Span, @@ -2690,7 +2635,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Add a note for the item obligation that remains - normally a note pointing to the // bound that introduced the obligation (e.g. `T: Send`). debug!(?next_code); - let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -2699,7 +2643,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { next_code.unwrap(), &mut Vec::new(), &mut Default::default(), - &mut long_ty_file, ); } @@ -2712,7 +2655,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause_code: &ObligationCauseCode<'tcx>, obligated_types: &mut Vec<Ty<'tcx>>, seen_requirements: &mut FxHashSet<DefId>, - long_ty_file: &mut Option<PathBuf>, ) where T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>, { @@ -2770,6 +2712,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } + ObligationCauseCode::ArrayLen(array_ty) => { + err.note(format!("the length of array `{array_ty}` must be type `usize`")); + } ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } @@ -2963,9 +2908,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::Coercion { source, target } => { let source = - tcx.short_ty_string(self.resolve_vars_if_possible(source), long_ty_file); + tcx.short_string(self.resolve_vars_if_possible(source), err.long_ty_path()); let target = - tcx.short_ty_string(self.resolve_vars_if_possible(target), long_ty_file); + tcx.short_string(self.resolve_vars_if_possible(target), err.long_ty_path()); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); @@ -3250,7 +3195,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if !is_upvar_tys_infer_tuple { - let ty_str = tcx.short_ty_string(ty, long_ty_file); + let ty_str = tcx.short_string(ty, err.long_ty_path()); let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { @@ -3328,7 +3273,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } else { @@ -3341,7 +3285,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause_code.peel_derives(), obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3351,7 +3294,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.resolve_vars_if_possible(data.derived.parent_trait_pred); let parent_def_id = parent_trait_pred.def_id(); let self_ty_str = - tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file); + tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path()); let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string(); let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`"); let mut is_auto_trait = false; @@ -3447,8 +3390,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { count, pluralize!(count) )); - let self_ty = tcx - .short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file); + let self_ty = tcx.short_string( + parent_trait_pred.skip_binder().self_ty(), + err.long_ty_path(), + ); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3464,7 +3409,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3503,7 +3447,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.derived.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3517,7 +3460,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3534,7 +3476,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3549,7 +3490,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { nested, obligated_types, seen_requirements, - long_ty_file, ) }); let mut multispan = MultiSpan::from(span); @@ -3580,7 +3520,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3613,14 +3552,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); - tcx.disabled_nightly_features(err, Some(tcx.local_def_id_to_hir_id(body_id)), [( - String::new(), - sym::trivial_bounds, - )]); + tcx.disabled_nightly_features( + err, + Some(tcx.local_def_id_to_hir_id(body_id)), + [(String::new(), sym::trivial_bounds)], + ); } ObligationCauseCode::OpaqueReturnType(expr_info) => { let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { - let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file); + let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); let expr = tcx.hir().expect_expr(hir_id); (expr_ty, expr) } else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id() @@ -3635,7 +3575,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() && self.can_eq(param_env, pred.self_ty(), expr_ty) { - let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file); + let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); (expr_ty, expr) } else { return; @@ -5229,7 +5169,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( format!( "{pre_message}the trait `{}` is not implemented for{desc} `{}`", trait_predicate.print_modifiers_and_trait_path(), - tcx.short_ty_string(trait_predicate.self_ty().skip_binder(), &mut None), + tcx.short_string(trait_predicate.self_ty().skip_binder(), &mut None), ) } else { // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 2dfa72972ba..62cac5b17bd 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; -use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt}; +use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt}; use rustc_span::{BytePos, Ident, Span, Symbol, kw}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; @@ -23,15 +23,6 @@ use crate::fluent_generated as fluent; pub mod note_and_explain; #[derive(Diagnostic)] -#[diag(trait_selection_dump_vtable_entries)] -pub struct DumpVTableEntries<'a> { - #[primary_span] - pub span: Span, - pub trait_ref: PolyTraitRef<'a>, - pub entries: String, -} - -#[derive(Diagnostic)] #[diag(trait_selection_unable_to_construct_constant_value)] pub struct UnableToConstructConstantValue<'a> { #[primary_span] @@ -1400,15 +1391,13 @@ pub struct OpaqueCapturesLifetime<'tcx> { pub enum FunctionPointerSuggestion<'a> { #[suggestion( trait_selection_fps_use_ref, - code = "&{fn_name}", + code = "&", style = "verbose", applicability = "maybe-incorrect" )] UseRef { #[primary_span] span: Span, - #[skip_arg] - fn_name: String, }, #[suggestion( trait_selection_fps_remove_ref, @@ -1438,7 +1427,7 @@ pub enum FunctionPointerSuggestion<'a> { }, #[suggestion( trait_selection_fps_cast, - code = "{fn_name} as {sig}", + code = " as {sig}", style = "verbose", applicability = "maybe-incorrect" )] @@ -1446,13 +1435,11 @@ pub enum FunctionPointerSuggestion<'a> { #[primary_span] span: Span, #[skip_arg] - fn_name: String, - #[skip_arg] sig: Binder<'a, FnSig<'a>>, }, #[suggestion( trait_selection_fps_cast_both, - code = "{fn_name} as {found_sig}", + code = " as {found_sig}", style = "hidden", applicability = "maybe-incorrect" )] @@ -1460,8 +1447,6 @@ pub enum FunctionPointerSuggestion<'a> { #[primary_span] span: Span, #[skip_arg] - fn_name: String, - #[skip_arg] found_sig: Binder<'a, FnSig<'a>>, expected_sig: Binder<'a, FnSig<'a>>, }, @@ -1497,6 +1482,12 @@ pub struct FnConsiderCasting { } #[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting_both)] +pub struct FnConsiderCastingBoth<'a> { + pub sig: Binder<'a, FnSig<'a>>, +} + +#[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( trait_selection_suggest_accessing_field, diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 863b6e293ff..55171754618 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,10 +1,73 @@ +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{self, Ty}; use crate::traits::ScrubbedTraitError; +use crate::traits::outlives_bounds::InferCtxtExt; + +#[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] +impl<'tcx> OutlivesEnvironment<'tcx> { + fn new( + infcx: &InferCtxt<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>, + ) -> Self { + Self::new_with_implied_bounds_compat( + infcx, + body_id, + param_env, + assumed_wf_tys, + !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, + ) + } + + fn new_with_implied_bounds_compat( + infcx: &InferCtxt<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>, + implied_bounds_compat: bool, + ) -> Self { + let mut bounds = vec![]; + + for bound in param_env.caller_bounds() { + if let Some(mut type_outlives) = bound.as_type_outlives_clause() { + if infcx.next_trait_solver() { + match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( + infcx.at(&ObligationCause::dummy(), param_env), + type_outlives, + ) { + Ok(new) => type_outlives = new, + Err(_) => { + infcx.dcx().delayed_bug(format!("could not normalize `{bound}`")); + } + } + } + bounds.push(type_outlives); + } + } + + // FIXME: This needs to be modified so that we normalize the known type + // outlives obligations then elaborate them into their region/type components. + // Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even + // if we can normalize `'a`. + OutlivesEnvironment::from_normalized_bounds( + param_env, + bounds, + infcx.implied_bounds_tys_with_compat( + body_id, + param_env, + assumed_wf_tys, + implied_bounds_compat, + ), + ) + } +} #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { @@ -16,9 +79,24 @@ impl<'tcx> InferCtxt<'tcx> { /// doing something specific for normalization. fn resolve_regions( &self, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>, + ) -> Vec<RegionResolutionError<'tcx>> { + self.resolve_regions_with_outlives_env(&OutlivesEnvironment::new( + self, + body_id, + param_env, + assumed_wf_tys, + )) + } + + /// Don't call this directly unless you know what you're doing. + fn resolve_regions_with_outlives_env( + &self, outlives_env: &OutlivesEnvironment<'tcx>, ) -> Vec<RegionResolutionError<'tcx>> { - self.resolve_regions_with_normalize(outlives_env, |ty, origin| { + self.resolve_regions_with_normalize(&outlives_env, |ty, origin| { let ty = self.resolve_vars_if_possible(ty); if self.next_trait_solver() { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index acd00d9f74f..58d8a3a6254 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues, @@ -43,8 +43,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.tcx } - type Span = Span; - fn build_with_canonical<V>( interner: TyCtxt<'tcx>, canonical: &CanonicalQueryInput<'tcx, V>, @@ -98,9 +96,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< param_env: ty::ParamEnv<'tcx>, arg: ty::GenericArg<'tcx>, ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> { - crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| { - obligations.into_iter().map(|obligation| obligation.into()).collect() - }) + crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) + .map(|obligations| { + obligations.into_iter().map(|obligation| obligation.into()).collect() + }) } fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { @@ -146,9 +145,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn instantiate_canonical_var_with_infer( &self, cv_info: CanonicalVarInfo<'tcx>, + span: Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ty::GenericArg<'tcx> { - self.0.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map) + self.0.instantiate_canonical_var(span, cv_info, universe_map) } fn insert_hidden_type( @@ -174,11 +174,13 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals); } - fn inject_new_hidden_type_unchecked(&self, key: ty::OpaqueTypeKey<'tcx>, hidden_ty: Ty<'tcx>) { - self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { - ty: hidden_ty, - span: DUMMY_SP, - }) + fn inject_new_hidden_type_unchecked( + &self, + key: ty::OpaqueTypeKey<'tcx>, + hidden_ty: Ty<'tcx>, + span: Span, + ) { + self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span }) } fn reset_opaque_types(&self) { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 2b7da4bc5ff..c238e708ab8 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,25 +1,21 @@ use std::marker::PhantomData; use std::mem; -use std::ops::ControlFlow; use rustc_data_structures::thinvec::ExtractIf; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ - self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause, - ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine, + FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_middle::{bug, span_bug}; use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; -use tracing::{instrument, trace}; +use tracing::instrument; +use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; -use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; -use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError}; +use crate::traits::{FulfillmentError, ScrubbedTraitError}; + +mod derive_errors; /// A trait engine using the new trait solver. /// @@ -86,7 +82,7 @@ impl<'tcx> ObligationStorage<'tcx> { self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { let goal = o.clone().into(); let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No) + .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) .0; matches!(result, Ok((HasChanged::Yes, _))) })); @@ -167,7 +163,7 @@ where for obligation in self.obligations.unstalled_for_select() { let goal = obligation.clone().into(); let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No) + .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) .0; self.inspect_evaluated_obligation(infcx, &obligation, &result); let (changed, certainty) = match result { @@ -244,483 +240,3 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<' } } } - -fn fulfillment_error_for_no_solution<'tcx>( - infcx: &InferCtxt<'tcx>, - root_obligation: PredicateObligation<'tcx>, -) -> FulfillmentError<'tcx> { - let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); - - let code = match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { - FulfillmentErrorCode::Project( - // FIXME: This could be a `Sorts` if the term is a type - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { - let ct_ty = match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { - infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) - } - ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env), - ty::ConstKind::Value(ty, _) => ty, - kind => span_bug!( - obligation.cause.span, - "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" - ), - }; - FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { - ct, - ct_ty, - expected_ty, - }) - } - ty::PredicateKind::NormalizesTo(..) => { - FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) - } - ty::PredicateKind::AliasRelate(_, _, _) => { - FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) - } - ty::PredicateKind::Subtype(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - obligation.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(a, b); - FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) - } - ty::PredicateKind::Coerce(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - obligation.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(b, a); - FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) - } - ty::PredicateKind::Clause(_) - | ty::PredicateKind::DynCompatible(_) - | ty::PredicateKind::Ambiguous => { - FulfillmentErrorCode::Select(SelectionError::Unimplemented) - } - ty::PredicateKind::ConstEquate(..) => { - bug!("unexpected goal: {obligation:?}") - } - }; - - FulfillmentError { obligation, code, root_obligation } -} - -fn fulfillment_error_for_stalled<'tcx>( - infcx: &InferCtxt<'tcx>, - root_obligation: PredicateObligation<'tcx>, -) -> FulfillmentError<'tcx> { - let (code, refine_obligation) = infcx.probe(|_| { - match <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No) - .0 - { - Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } - Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => ( - FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, - // Don't look into overflows because we treat overflows weirdly anyways. - // We discard the inference constraints from overflowing goals, so - // recomputing the goal again during `find_best_leaf_obligation` may apply - // inference guidance that makes other goals go from ambig -> pass, for example. - // - // FIXME: We should probably just look into overflows here. - false, - ), - Ok((_, Certainty::Yes)) => { - bug!("did not expect successful goal when collecting ambiguity errors") - } - Err(_) => { - bug!("did not expect selection error when collecting ambiguity errors") - } - } - }); - - FulfillmentError { - obligation: if refine_obligation { - find_best_leaf_obligation(infcx, &root_obligation, true) - } else { - root_obligation.clone() - }, - code, - root_obligation, - } -} - -fn fulfillment_error_for_overflow<'tcx>( - infcx: &InferCtxt<'tcx>, - root_obligation: PredicateObligation<'tcx>, -) -> FulfillmentError<'tcx> { - FulfillmentError { - obligation: find_best_leaf_obligation(infcx, &root_obligation, true), - code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, - root_obligation, - } -} - -fn find_best_leaf_obligation<'tcx>( - infcx: &InferCtxt<'tcx>, - obligation: &PredicateObligation<'tcx>, - consider_ambiguities: bool, -) -> PredicateObligation<'tcx> { - let obligation = infcx.resolve_vars_if_possible(obligation.clone()); - // FIXME: we use a probe here as the `BestObligation` visitor does not - // check whether it uses candidates which get shadowed by where-bounds. - // - // We should probably fix the visitor to not do so instead, as this also - // means the leaf obligation may be incorrect. - infcx - .fudge_inference_if_ok(|| { - infcx - .visit_proof_tree(obligation.clone().into(), &mut BestObligation { - obligation: obligation.clone(), - consider_ambiguities, - }) - .break_value() - .ok_or(()) - }) - .unwrap_or(obligation) -} - -struct BestObligation<'tcx> { - obligation: PredicateObligation<'tcx>, - consider_ambiguities: bool, -} - -impl<'tcx> BestObligation<'tcx> { - fn with_derived_obligation( - &mut self, - derived_obligation: PredicateObligation<'tcx>, - and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result, - ) -> <Self as ProofTreeVisitor<'tcx>>::Result { - let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); - let res = and_then(self); - self.obligation = old_obligation; - res - } - - /// Filter out the candidates that aren't interesting to visit for the - /// purposes of reporting errors. For ambiguities, we only consider - /// candidates that may hold. For errors, we only consider candidates that - /// *don't* hold and which have impl-where clauses that also don't hold. - fn non_trivial_candidates<'a>( - &self, - goal: &'a inspect::InspectGoal<'a, 'tcx>, - ) -> Vec<inspect::InspectCandidate<'a, 'tcx>> { - let mut candidates = goal.candidates(); - match self.consider_ambiguities { - true => { - // If we have an ambiguous obligation, we must consider *all* candidates - // that hold, or else we may guide inference causing other goals to go - // from ambig -> pass/fail. - candidates.retain(|candidate| candidate.result().is_ok()); - } - false => { - // If we have >1 candidate, one may still be due to "boring" reasons, like - // an alias-relate that failed to hold when deeply evaluated. We really - // don't care about reasons like this. - if candidates.len() > 1 { - candidates.retain(|candidate| { - goal.infcx().probe(|_| { - candidate.instantiate_nested_goals(self.span()).iter().any( - |nested_goal| { - matches!( - nested_goal.source(), - GoalSource::ImplWhereBound - | GoalSource::AliasBoundConstCondition - | GoalSource::InstantiateHigherRanked - | GoalSource::AliasWellFormed - ) && match self.consider_ambiguities { - true => { - matches!( - nested_goal.result(), - Ok(Certainty::Maybe(MaybeCause::Ambiguity)) - ) - } - false => matches!(nested_goal.result(), Err(_)), - } - }, - ) - }) - }); - } - - // Prefer a non-rigid candidate if there is one. - if candidates.len() > 1 { - candidates.retain(|candidate| { - !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. }) - }); - } - } - } - - candidates - } -} - -impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { - type Result = ControlFlow<PredicateObligation<'tcx>>; - - fn span(&self) -> rustc_span::Span { - self.obligation.cause.span - } - - #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] - fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result { - let candidates = self.non_trivial_candidates(goal); - trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>()); - - let [candidate] = candidates.as_slice() else { - return ControlFlow::Break(self.obligation.clone()); - }; - - // Don't walk into impls that have `do_not_recommend`. - if let inspect::ProbeKind::TraitCandidate { - source: CandidateSource::Impl(impl_def_id), - result: _, - } = candidate.kind() - && goal.infcx().tcx.do_not_recommend_impl(impl_def_id) - { - return ControlFlow::Break(self.obligation.clone()); - } - - let tcx = goal.infcx().tcx; - // FIXME: Also, what about considering >1 layer up the stack? May be necessary - // for normalizes-to. - let pred_kind = goal.goal().predicate.kind(); - let child_mode = match pred_kind.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - ChildMode::Trait(pred_kind.rebind(pred)) - } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => { - ChildMode::Host(pred_kind.rebind(pred)) - } - ty::PredicateKind::NormalizesTo(normalizes_to) - if matches!( - normalizes_to.alias.kind(tcx), - ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst - ) => - { - ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate { - trait_ref: normalizes_to.alias.trait_ref(tcx), - polarity: ty::PredicatePolarity::Positive, - })) - } - _ => ChildMode::PassThrough, - }; - - let nested_goals = candidate.instantiate_nested_goals(self.span()); - - // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as - // an actual candidate, instead we should treat them as if the impl was never considered to - // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written - // instead of `impl<T: FnPtr> Trait for T`. - // - // We do this as a separate loop so that we do not choose to tell the user about some nested - // goal before we encounter a `T: FnPtr` nested goal. - for nested_goal in &nested_goals { - if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() - && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() - && poly_trait_pred.def_id() == fn_ptr_trait - && let Err(NoSolution) = nested_goal.result() - { - return ControlFlow::Break(self.obligation.clone()); - } - } - - let mut impl_where_bound_count = 0; - for nested_goal in nested_goals { - trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); - - let make_obligation = |cause| Obligation { - cause, - param_env: nested_goal.goal().param_env, - predicate: nested_goal.goal().predicate, - recursion_depth: self.obligation.recursion_depth + 1, - }; - - let obligation; - match (child_mode, nested_goal.source()) { - (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => { - continue; - } - (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { - obligation = make_obligation(derive_cause( - tcx, - candidate.kind(), - self.obligation.cause.clone(), - impl_where_bound_count, - parent_trait_pred, - )); - impl_where_bound_count += 1; - } - ( - ChildMode::Host(parent_host_pred), - GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, - ) => { - obligation = make_obligation(derive_host_cause( - tcx, - candidate.kind(), - self.obligation.cause.clone(), - impl_where_bound_count, - parent_host_pred, - )); - impl_where_bound_count += 1; - } - // Skip over a higher-ranked predicate. - (_, GoalSource::InstantiateHigherRanked) => { - obligation = self.obligation.clone(); - } - (ChildMode::PassThrough, _) - | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { - obligation = make_obligation(self.obligation.cause.clone()); - } - } - - // Skip nested goals that aren't the *reason* for our goal's failure. - match self.consider_ambiguities { - true if matches!( - nested_goal.result(), - Ok(Certainty::Maybe(MaybeCause::Ambiguity)) - ) => {} - false if matches!(nested_goal.result(), Err(_)) => {} - _ => continue, - } - - self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; - } - - // alias-relate may fail because the lhs or rhs can't be normalized, - // and therefore is treated as rigid. - if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() { - if let Some(obligation) = goal - .infcx() - .visit_proof_tree_at_depth( - goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())), - goal.depth() + 1, - self, - ) - .break_value() - { - return ControlFlow::Break(obligation); - } else if let Some(obligation) = goal - .infcx() - .visit_proof_tree_at_depth( - goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())), - goal.depth() + 1, - self, - ) - .break_value() - { - return ControlFlow::Break(obligation); - } - } - - ControlFlow::Break(self.obligation.clone()) - } -} - -#[derive(Debug, Copy, Clone)] -enum ChildMode<'tcx> { - // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, - // and skip all `GoalSource::Misc`, which represent useless obligations - // such as alias-eq which may not hold. - Trait(ty::PolyTraitPredicate<'tcx>), - // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, - // and skip all `GoalSource::Misc`, which represent useless obligations - // such as alias-eq which may not hold. - Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>), - // Skip trying to derive an `ObligationCause` from this obligation, and - // report *all* sub-obligations as if they came directly from the parent - // obligation. - PassThrough, -} - -fn derive_cause<'tcx>( - tcx: TyCtxt<'tcx>, - candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>, - mut cause: ObligationCause<'tcx>, - idx: usize, - parent_trait_pred: ty::PolyTraitPredicate<'tcx>, -) -> ObligationCause<'tcx> { - match candidate_kind { - inspect::ProbeKind::TraitCandidate { - source: CandidateSource::Impl(impl_def_id), - result: _, - } => { - if let Some((_, span)) = - tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx) - { - cause = cause.derived_cause(parent_trait_pred, |derived| { - ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: Some(idx), - span, - })) - }) - } - } - inspect::ProbeKind::TraitCandidate { - source: CandidateSource::BuiltinImpl(..), - result: _, - } => { - cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived); - } - _ => {} - }; - cause -} - -fn derive_host_cause<'tcx>( - tcx: TyCtxt<'tcx>, - candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>, - mut cause: ObligationCause<'tcx>, - idx: usize, - parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, -) -> ObligationCause<'tcx> { - match candidate_kind { - inspect::ProbeKind::TraitCandidate { - source: CandidateSource::Impl(impl_def_id), - result: _, - } => { - if let Some((_, span)) = tcx - .predicates_of(impl_def_id) - .instantiate_identity(tcx) - .into_iter() - .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map( - |(trait_ref, span)| { - ( - trait_ref.to_host_effect_clause( - tcx, - parent_host_pred.skip_binder().constness, - ), - span, - ) - }, - )) - .nth(idx) - { - cause = - cause.derived_host_cause(parent_host_pred, |derived| { - ObligationCauseCode::ImplDerivedHost(Box::new( - traits::ImplDerivedHostCause { derived, impl_def_id, span }, - )) - }) - } - } - inspect::ProbeKind::TraitCandidate { - source: CandidateSource::BuiltinImpl(..), - result: _, - } => { - cause = - cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost); - } - _ => {} - }; - cause -} diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs new file mode 100644 index 00000000000..7364b4aa343 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -0,0 +1,531 @@ +use std::ops::ControlFlow; + +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; +use rustc_infer::traits::{ + self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + PredicateObligation, SelectionError, +}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; +use rustc_type_ir::solve::{Goal, NoSolution}; +use tracing::{instrument, trace}; + +use crate::solve::Certainty; +use crate::solve::delegate::SolverDelegate; +use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf}; + +pub(super) fn fulfillment_error_for_no_solution<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); + + let code = match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { + FulfillmentErrorCode::Project( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env), + ty::ConstKind::Value(cv) => cv.ty, + kind => span_bug!( + obligation.cause.span, + "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } + ty::PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + ty::PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + ty::PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(a, b); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + ty::PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(b, a); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + ty::PredicateKind::Clause(_) + | ty::PredicateKind::DynCompatible(_) + | ty::PredicateKind::Ambiguous => { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) + } + ty::PredicateKind::ConstEquate(..) => { + bug!("unexpected goal: {obligation:?}") + } + }; + + FulfillmentError { obligation, code, root_obligation } +} + +pub(super) fn fulfillment_error_for_stalled<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + let (code, refine_obligation) = infcx.probe(|_| { + match <&SolverDelegate<'tcx>>::from(infcx) + .evaluate_root_goal( + root_obligation.clone().into(), + GenerateProofTree::No, + root_obligation.cause.span, + ) + .0 + { + Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { + (FulfillmentErrorCode::Ambiguity { overflow: None }, true) + } + Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => ( + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, + // Don't look into overflows because we treat overflows weirdly anyways. + // We discard the inference constraints from overflowing goals, so + // recomputing the goal again during `find_best_leaf_obligation` may apply + // inference guidance that makes other goals go from ambig -> pass, for example. + // + // FIXME: We should probably just look into overflows here. + false, + ), + Ok((_, Certainty::Yes)) => { + bug!("did not expect successful goal when collecting ambiguity errors") + } + Err(_) => { + bug!("did not expect selection error when collecting ambiguity errors") + } + } + }); + + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } +} + +pub(super) fn fulfillment_error_for_overflow<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + +fn find_best_leaf_obligation<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &PredicateObligation<'tcx>, + consider_ambiguities: bool, +) -> PredicateObligation<'tcx> { + let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. + infcx + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree( + obligation.clone().into(), + &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, + ) + .break_value() + .ok_or(()) + }) + .unwrap_or(obligation) +} + +struct BestObligation<'tcx> { + obligation: PredicateObligation<'tcx>, + consider_ambiguities: bool, +} + +impl<'tcx> BestObligation<'tcx> { + fn with_derived_obligation( + &mut self, + derived_obligation: PredicateObligation<'tcx>, + and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result, + ) -> <Self as ProofTreeVisitor<'tcx>>::Result { + let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); + let res = and_then(self); + self.obligation = old_obligation; + res + } + + /// Filter out the candidates that aren't interesting to visit for the + /// purposes of reporting errors. For ambiguities, we only consider + /// candidates that may hold. For errors, we only consider candidates that + /// *don't* hold and which have impl-where clauses that also don't hold. + fn non_trivial_candidates<'a>( + &self, + goal: &'a inspect::InspectGoal<'a, 'tcx>, + ) -> Vec<inspect::InspectCandidate<'a, 'tcx>> { + let mut candidates = goal.candidates(); + match self.consider_ambiguities { + true => { + // If we have an ambiguous obligation, we must consider *all* candidates + // that hold, or else we may guide inference causing other goals to go + // from ambig -> pass/fail. + candidates.retain(|candidate| candidate.result().is_ok()); + } + false => { + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals(self.span()).iter().any( + |nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition + | GoalSource::InstantiateHigherRanked + | GoalSource::AliasWellFormed + ) && match (self.consider_ambiguities, nested_goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) + | (false, Err(_)) => true, + _ => false, + } + }, + ) + }) + }); + } + + // Prefer a non-rigid candidate if there is one. + if candidates.len() > 1 { + candidates.retain(|candidate| { + !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. }) + }); + } + } + } + + candidates + } + + /// HACK: We walk the nested obligations for a well-formed arg manually, + /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. + /// Ideally we'd be able to track this better. + fn visit_well_formed_goal( + &mut self, + candidate: &inspect::InspectCandidate<'_, 'tcx>, + arg: ty::GenericArg<'tcx>, + ) -> ControlFlow<PredicateObligation<'tcx>> { + let infcx = candidate.goal().infcx(); + let param_env = candidate.goal().goal().param_env; + let body_id = self.obligation.cause.body_id; + + for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id) + .into_iter() + .flatten() + { + let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( + GoalSource::Misc, + Goal::new(infcx.tcx, obligation.param_env, obligation.predicate), + self.span(), + ); + // Skip nested goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, nested_goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => continue, + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { + type Result = ControlFlow<PredicateObligation<'tcx>>; + + fn span(&self) -> rustc_span::Span { + self.obligation.cause.span + } + + #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + let candidates = self.non_trivial_candidates(goal); + trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>()); + + let [candidate] = candidates.as_slice() else { + return ControlFlow::Break(self.obligation.clone()); + }; + + // Don't walk into impls that have `do_not_recommend`. + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } = candidate.kind() + && goal.infcx().tcx.do_not_recommend_impl(impl_def_id) + { + return ControlFlow::Break(self.obligation.clone()); + } + + let tcx = goal.infcx().tcx; + // FIXME: Also, what about considering >1 layer up the stack? May be necessary + // for normalizes-to. + let pred_kind = goal.goal().predicate.kind(); + let child_mode = match pred_kind.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + ChildMode::Trait(pred_kind.rebind(pred)) + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => { + ChildMode::Host(pred_kind.rebind(pred)) + } + ty::PredicateKind::NormalizesTo(normalizes_to) + if matches!( + normalizes_to.alias.kind(tcx), + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst + ) => + { + ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate { + trait_ref: normalizes_to.alias.trait_ref(tcx), + polarity: ty::PredicatePolarity::Positive, + })) + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + return self.visit_well_formed_goal(candidate, arg); + } + _ => ChildMode::PassThrough, + }; + + let nested_goals = candidate.instantiate_nested_goals(self.span()); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written + // instead of `impl<T: FnPtr> Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() + && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && poly_trait_pred.def_id() == fn_ptr_trait + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + + let mut impl_where_bound_count = 0; + for nested_goal in nested_goals { + trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + + let make_obligation = |cause| Obligation { + cause, + param_env: nested_goal.goal().param_env, + predicate: nested_goal.goal().predicate, + recursion_depth: self.obligation.recursion_depth + 1, + }; + + let obligation; + match (child_mode, nested_goal.source()) { + (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => { + continue; + } + (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { + obligation = make_obligation(derive_cause( + tcx, + candidate.kind(), + self.obligation.cause.clone(), + impl_where_bound_count, + parent_trait_pred, + )); + impl_where_bound_count += 1; + } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(derive_host_cause( + tcx, + candidate.kind(), + self.obligation.cause.clone(), + impl_where_bound_count, + parent_host_pred, + )); + impl_where_bound_count += 1; + } + // Skip over a higher-ranked predicate. + (_, GoalSource::InstantiateHigherRanked) => { + obligation = self.obligation.clone(); + } + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { + obligation = make_obligation(self.obligation.cause.clone()); + } + } + + // Skip nested goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, nested_goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => continue, + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() { + if let Some(obligation) = goal + .infcx() + .visit_proof_tree_at_depth( + goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())), + goal.depth() + 1, + self, + ) + .break_value() + { + return ControlFlow::Break(obligation); + } else if let Some(obligation) = goal + .infcx() + .visit_proof_tree_at_depth( + goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())), + goal.depth() + 1, + self, + ) + .break_value() + { + return ControlFlow::Break(obligation); + } + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +#[derive(Debug, Copy, Clone)] +enum ChildMode<'tcx> { + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Trait(ty::PolyTraitPredicate<'tcx>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>), + // Skip trying to derive an `ObligationCause` from this obligation, and + // report *all* sub-obligations as if they came directly from the parent + // obligation. + PassThrough, +} + +fn derive_cause<'tcx>( + tcx: TyCtxt<'tcx>, + candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>, + mut cause: ObligationCause<'tcx>, + idx: usize, + parent_trait_pred: ty::PolyTraitPredicate<'tcx>, +) -> ObligationCause<'tcx> { + match candidate_kind { + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } => { + if let Some((_, span)) = + tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx) + { + cause = cause.derived_cause(parent_trait_pred, |derived| { + ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause { + derived, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: Some(idx), + span, + })) + }) + } + } + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(..), + result: _, + } => { + cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived); + } + _ => {} + }; + cause +} + +fn derive_host_cause<'tcx>( + tcx: TyCtxt<'tcx>, + candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>, + mut cause: ObligationCause<'tcx>, + idx: usize, + parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, +) -> ObligationCause<'tcx> { + match candidate_kind { + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } => { + if let Some((_, span)) = tcx + .predicates_of(impl_def_id) + .instantiate_identity(tcx) + .into_iter() + .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map( + |(trait_ref, span)| { + ( + trait_ref.to_host_effect_clause( + tcx, + parent_host_pred.skip_binder().constness, + ), + span, + ) + }, + )) + .nth(idx) + { + cause = + cause.derived_host_cause(parent_host_pred, |derived| { + ObligationCauseCode::ImplDerivedHost(Box::new( + traits::ImplDerivedHostCause { derived, impl_def_id, span }, + )) + }) + } + } + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(..), + result: _, + } => { + cause = + cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost); + } + _ => {} + }; + cause +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e735020a63e..9f4ee54bd4c 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let goals = instantiated_goals .into_iter() - .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { - let unconstrained_term = match term.unpack() { - ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(), - ty::TermKind::Const(_) => infcx.next_const_var(span).into(), - }; - let goal = - goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term }); - // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the - // expected term. This means that candidates which only fail due to nested goals - // and which normalize to a different term then the final result could ICE: when - // building their proof tree, the expected term was unconstrained, but when - // instantiating the candidate it is already constrained to the result of another - // candidate. - let proof_tree = infcx - .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1); - InspectGoal::new( - infcx, - self.goal.depth + 1, - proof_tree.unwrap(), - Some(NormalizesToTermHack { term, unconstrained_term }), - source, - ) - } - _ => { - // We're using a probe here as evaluating a goal could constrain - // inference variables by choosing one candidate. If we then recurse - // into another candidate who ends up with different inference - // constraints, we get an ICE if we already applied the constraints - // from the chosen candidate. - let proof_tree = infcx - .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1) - .unwrap(); - InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) - } - }) + .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span)) .collect(); (goals, opt_impl_args) } + pub fn instantiate_proof_tree_for_nested_goal( + &self, + source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + span: Span, + ) -> InspectGoal<'a, 'tcx> { + let infcx = self.goal.infcx; + match goal.predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { + let unconstrained_term = match term.unpack() { + ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(), + ty::TermKind::Const(_) => infcx.next_const_var(span).into(), + }; + let goal = + goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term }); + // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the + // expected term. This means that candidates which only fail due to nested goals + // and which normalize to a different term then the final result could ICE: when + // building their proof tree, the expected term was unconstrained, but when + // instantiating the candidate it is already constrained to the result of another + // candidate. + let proof_tree = + infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1); + InspectGoal::new( + infcx, + self.goal.depth + 1, + proof_tree.unwrap(), + Some(NormalizesToTermHack { term, unconstrained_term }), + source, + ) + } + _ => { + // We're using a probe here as evaluating a goal could constrain + // inference variables by choosing one candidate. If we then recurse + // into another candidate who ends up with different inference + // constraints, we get an ICE if we already applied the constraints + // from the chosen candidate. + let proof_tree = infcx + .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span).1) + .unwrap(); + InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) + } + } + } + /// Visit all nested goals of this candidate, rolling back /// all inference constraints. pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result { @@ -430,8 +440,11 @@ impl<'tcx> InferCtxt<'tcx> { depth: usize, visitor: &mut V, ) -> V::Result { - let (_, proof_tree) = - <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(goal, GenerateProofTree::Yes); + let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal( + goal, + GenerateProofTree::Yes, + visitor.span(), + ); let proof_tree = proof_tree.unwrap(); visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index a724705ffe9..232357dc71a 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -130,11 +130,12 @@ where self.depth += 1; let new_infer_ct = infcx.next_const_var(self.at.cause.span); - let obligation = - Obligation::new(tcx, self.at.cause.clone(), self.at.param_env, ty::NormalizesTo { - alias: uv.into(), - term: new_infer_ct.into(), - }); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + ty::NormalizesTo { alias: uv.into(), term: new_infer_ct.into() }, + ); let result = if infcx.predicate_may_hold(&obligation) { self.fulfill_cx.register_predicate_obligation(infcx, obligation); @@ -252,20 +253,20 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - deeply_normalize_with_skipped_universes(self.at, ty, vec![ - None; - ty.outer_exclusive_binder() - .as_usize() - ]) + deeply_normalize_with_skipped_universes( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self)) } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - deeply_normalize_with_skipped_universes(self.at, ct, vec![ - None; - ct.outer_exclusive_binder() - .as_usize() - ]) + deeply_normalize_with_skipped_universes( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self)) } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 9a53e8a5d51..1fca2f4da7e 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,6 +6,7 @@ use std::iter; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::unord::UnordSet; +use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::ty::{Region, RegionVid}; use tracing::debug; @@ -13,6 +14,7 @@ use tracing::debug; use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; +use crate::regions::OutlivesEnvironmentBuildExt; use crate::traits::project::ProjectAndUnifyResult; // FIXME(twk): this is obviously not nice to duplicate like that @@ -158,7 +160,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}"); } - let outlives_env = OutlivesEnvironment::new(full_env); + let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty)); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 50d47d20e1a..3b40882ef57 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -9,7 +9,7 @@ use std::fmt::Debug; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::PredicateObligations; use rustc_middle::bug; @@ -27,7 +27,6 @@ use tracing::{debug, instrument, warn}; use super::ObligationCtxt; use crate::error_reporting::traits::suggest_new_overflow_limit; use crate::infer::InferOk; -use crate::infer::outlives::env::OutlivesEnvironment; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect}; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -369,9 +368,10 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( overflowing_predicates: ambiguities .into_iter() .filter(|error| { - matches!(error.code, FulfillmentErrorCode::Ambiguity { - overflow: Some(true) - }) + matches!( + error.code, + FulfillmentErrorCode::Ambiguity { overflow: Some(true) } + ) }) .map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate)) .collect(), @@ -492,13 +492,16 @@ fn plug_infer_with_placeholders<'tcx>( // Comparing against a type variable never registers hidden types anyway DefineOpaqueTypes::Yes, ty, - Ty::new_placeholder(self.infcx.tcx, ty::Placeholder { - universe: self.universe, - bound: ty::BoundTy { - var: self.next_var(), - kind: ty::BoundTyKind::Anon, + Ty::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundTy { + var: self.next_var(), + kind: ty::BoundTyKind::Anon, + }, }, - }), + ), ) else { bug!("we always expect to be able to plug an infer var with placeholder") @@ -518,10 +521,10 @@ fn plug_infer_with_placeholders<'tcx>( // registration happening anyway. DefineOpaqueTypes::Yes, ct, - ty::Const::new_placeholder(self.infcx.tcx, ty::Placeholder { - universe: self.universe, - bound: self.next_var(), - }), + ty::Const::new_placeholder( + self.infcx.tcx, + ty::Placeholder { universe: self.universe, bound: self.next_var() }, + ), ) else { bug!("we always expect to be able to plug an infer var with placeholder") @@ -546,13 +549,16 @@ fn plug_infer_with_placeholders<'tcx>( // Lifetimes don't contain opaque types (or any types for that matter). DefineOpaqueTypes::Yes, r, - ty::Region::new_placeholder(self.infcx.tcx, ty::Placeholder { - universe: self.universe, - bound: ty::BoundRegion { - var: self.next_var(), - kind: ty::BoundRegionKind::Anon, + ty::Region::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundRegion { + var: self.next_var(), + kind: ty::BoundRegionKind::Anon, + }, }, - }), + ), ) else { bug!("we always expect to be able to plug an infer var with placeholder") @@ -596,8 +602,7 @@ fn try_prove_negated_where_clause<'tcx>( // FIXME: We could use the assumed_wf_types from both impls, I think, // if that wasn't implemented just for LocalDefId, and we'd need to do // the normalization ourselves since this is totally fallible... - let outlives_env = OutlivesEnvironment::new(param_env); - let errors = ocx.resolve_regions(&outlives_env); + let errors = ocx.resolve_regions(CRATE_DEF_ID, param_env, []); if !errors.is_empty() { return false; } @@ -765,9 +770,9 @@ fn search_ambiguity_causes<'tcx>( causes: &mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>, ) { infcx.probe(|_| { - infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { - cache: Default::default(), - causes, - }) + infcx.visit_proof_tree( + goal, + &mut AmbiguityCausesVisitor { cache: Default::default(), causes }, + ) }); } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 446f9eaa348..bdee6ca2afe 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>( ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Value(_, _) + | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => return Ok(()), ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), }; diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 66491d9abe1..617bc87a9d2 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -4,19 +4,16 @@ //! //! [^1]: Formerly known as "object safety". -use std::iter; use std::ops::ControlFlow; -use rustc_abi::BackendRepr; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, Upcast, + self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, }; use rustc_span::Span; use rustc_type_ir::elaborate; @@ -64,7 +61,7 @@ fn is_dyn_compatible(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { } /// We say a method is *vtable safe* if it can be invoked on a trait -/// object. Note that object-safe traits can have some +/// object. Note that dyn-compatible traits can have some /// non-vtable-safe methods, so long as they require `Self: Sized` or /// otherwise ensure that they cannot be used when `Self = Trait`. pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool { @@ -109,14 +106,6 @@ fn dyn_compatibility_violations_for_trait( violations.push(DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans)); } - if violations.is_empty() { - for item in tcx.associated_items(trait_def_id).in_definition_order() { - if let ty::AssocKind::Fn = item.kind { - check_receiver_correct(tcx, trait_def_id, *item); - } - } - } - violations } @@ -421,7 +410,7 @@ fn virtual_call_violations_for_method<'tcx>( 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. - // However, this is already considered object-safe. We allow it as a special case here. + // However, this is already considered dyn compatible. We allow it as a special case here. // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows // `Receiver: Unsize<Receiver[Self => dyn Trait]>`. if receiver_ty != tcx.types.self_param { @@ -499,55 +488,6 @@ fn virtual_call_violations_for_method<'tcx>( errors } -/// This code checks that `receiver_is_dispatchable` is correctly implemented. -/// -/// This check is outlined from the dyn-compatibility check to avoid cycles with -/// layout computation, which relies on knowing whether methods are dyn-compatible. -fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) { - if !is_vtable_safe_method(tcx, trait_def_id, method) { - return; - } - - let method_def_id = method.def_id; - let sig = tcx.fn_sig(method_def_id).instantiate_identity(); - let typing_env = ty::TypingEnv::non_body_analysis(tcx, method_def_id); - let receiver_ty = tcx.liberate_late_bound_regions(method_def_id, sig.input(0)); - - if receiver_ty == tcx.types.self_param { - // Assumed OK, may change later if unsized_locals permits `self: Self` as dispatchable. - return; - } - - // e.g., `Rc<()>` - let unit_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method_def_id); - match tcx.layout_of(typing_env.as_query_input(unit_receiver_ty)).map(|l| l.backend_repr) { - Ok(BackendRepr::Scalar(..)) => (), - abi => { - tcx.dcx().span_delayed_bug( - tcx.def_span(method_def_id), - format!("receiver {unit_receiver_ty:?} when `Self = ()` should have a Scalar ABI; found {abi:?}"), - ); - } - } - - let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static); - - // e.g., `Rc<dyn Trait>` - let trait_object_receiver = - receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method_def_id); - match tcx.layout_of(typing_env.as_query_input(trait_object_receiver)).map(|l| l.backend_repr) { - Ok(BackendRepr::ScalarPair(..)) => (), - abi => { - tcx.dcx().span_delayed_bug( - tcx.def_span(method_def_id), - format!( - "receiver {trait_object_receiver:?} when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" - ), - ); - } - } -} - /// Performs a type instantiation to produce the version of `receiver_ty` when `Self = self_ty`. /// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`. fn receiver_for_self_ty<'tcx>( @@ -569,49 +509,6 @@ fn receiver_for_self_ty<'tcx>( result } -/// Creates the object type for the current trait. For example, -/// if the current trait is `Deref`, then this will be -/// `dyn Deref<Target = Self::Target> + '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> { - let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); - debug!(?trait_ref); - - let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait( - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), - )); - debug!(?trait_predicate); - - let pred: ty::Predicate<'tcx> = trait_ref.upcast(tcx); - let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred]) - .filter_map(|pred| { - debug!(?pred); - let pred = pred.as_projection_clause()?; - Some(pred.map_bound(|p| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( - tcx, p, - )) - })) - }) - .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_from_iter( - iter::once(trait_predicate).chain(elaborated_predicates), - ); - debug!(?existential_predicates); - - Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn) -} - /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a /// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type /// in the following way: @@ -631,7 +528,7 @@ fn object_ty_for_trait<'tcx>( /// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`. /// /// The only case where the receiver is not dispatchable, but is still a valid receiver -/// type (just not object-safe), is when there is more than one level of pointer indirection. +/// type (just not dyn compatible), is when there is more than one level of pointer indirection. /// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there /// is no way, or at least no inexpensive way, to coerce the receiver from the version where /// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 4a3983fca31..9f3178f8879 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -8,7 +8,6 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_infer::traits::PredicateObligations; use rustc_macros::extension; @@ -217,14 +216,15 @@ where /// will result in region constraints getting ignored. pub fn resolve_regions_and_report_errors( self, - generic_param_scope: LocalDefId, - outlives_env: &OutlivesEnvironment<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>, ) -> Result<(), ErrorGuaranteed> { - let errors = self.infcx.resolve_regions(outlives_env); + let errors = self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys); if errors.is_empty() { Ok(()) } else { - Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors)) + Err(self.infcx.err_ctxt().report_region_errors(body_id, &errors)) } } @@ -235,9 +235,11 @@ where #[must_use] pub fn resolve_regions( self, - outlives_env: &OutlivesEnvironment<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>, ) -> Vec<RegionResolutionError<'tcx>> { - self.infcx.resolve_regions(outlives_env) + self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys) } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7529ee128f5..f2d2dd2f3ce 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -479,7 +479,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::ConstKind::Error(_) => { return ProcessResult::Changed(PendingPredicateObligations::new()); } - ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Value(cv) => cv.ty, ty::ConstKind::Unevaluated(uv) => { infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 7a67b943e94..79e178150de 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -4,13 +4,10 @@ use std::assert_matches::assert_matches; use hir::LangItem; use rustc_ast::Mutability; -use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; -use super::outlives_bounds::InferCtxtExt; use crate::regions::InferCtxtRegionExt; use crate::traits::{self, FulfillmentError, ObligationCause}; @@ -170,15 +167,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( } // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( - param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]); if !errors.is_empty() { infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors))); continue; @@ -261,15 +250,7 @@ pub fn all_fields_implement_trait<'tcx>( } // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( - param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]); if !errors.is_empty() { infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fe5ad003a7e..105cb917571 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::regions::InferCtxtRegionExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +#[derive(Debug)] pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, pub code: FulfillmentErrorCode<'tcx>, @@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> { } } -impl<'tcx> Debug for FulfillmentError<'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code) - } -} - #[derive(Clone)] pub enum FulfillmentErrorCode<'tcx> { /// Inherently impossible to fulfill; this trait is implemented if and only @@ -290,12 +285,10 @@ fn do_normalize_predicates<'tcx>( // 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); + let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); if !errors.is_empty() { tcx.dcx().span_delayed_bug( span, @@ -667,13 +660,16 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( self.idx += 1; idx }; - Ty::new_placeholder(self.tcx, ty::PlaceholderType { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundTy { - var: ty::BoundVar::from_u32(idx), - kind: ty::BoundTyKind::Anon, + Ty::new_placeholder( + self.tcx, + ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundTy { + var: ty::BoundVar::from_u32(idx), + kind: ty::BoundTyKind::Anon, + }, }, - }) + ) } else { t.super_fold_with(self) } @@ -681,14 +677,17 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { if let ty::ConstKind::Infer(_) = c.kind() { - ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundVar::from_u32({ - let idx = self.idx; - self.idx += 1; - idx - }), - }) + ty::Const::new_placeholder( + self.tcx, + ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundVar::from_u32({ + let idx = self.idx; + self.idx += 1; + idx + }), + }, + ) } else { c.super_fold_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 23dabe32ff2..18932695807 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,4 +1,3 @@ -use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; @@ -12,9 +11,6 @@ use tracing::instrument; use crate::infer::InferCtxt; use crate::traits::{ObligationCause, ObligationCtxt}; -pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; -pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; - /// 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 @@ -110,36 +106,18 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds } -#[extension(pub trait InferCtxtExt<'a, 'tcx>)] -impl<'a, 'tcx: 'a> InferCtxt<'tcx> { - /// Do *NOT* call this directly. - fn implied_bounds_tys_compat( - &'a self, - param_env: ParamEnv<'tcx>, +#[extension(pub trait InferCtxtExt<'tcx>)] +impl<'tcx> InferCtxt<'tcx> { + /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment` + /// instead if you're interested in the implied bounds for a given signature. + fn implied_bounds_tys_with_compat<Tys: IntoIterator<Item = Ty<'tcx>>>( + &self, body_id: LocalDefId, - tys: &'a FxIndexSet<Ty<'tcx>>, - compat: bool, - ) -> BoundsCompat<'a, 'tcx> { - tys.iter() - .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat)) - } - - /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat` - /// with `compat` set to `true`, otherwise `false`. - fn implied_bounds_tys( - &'a self, param_env: ParamEnv<'tcx>, - body_id: LocalDefId, - tys: &'a FxIndexSet<Ty<'tcx>>, - ) -> Bounds<'a, 'tcx> { - tys.iter().flat_map(move |ty| { - implied_outlives_bounds( - self, - param_env, - body_id, - *ty, - !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, - ) - }) + tys: Tys, + compat: bool, + ) -> impl Iterator<Item = OutlivesBound<'tcx>> { + tys.into_iter() + .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat)) } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 537b042bde5..de5aaa5bd97 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1670,14 +1670,18 @@ fn confirm_closure_candidate<'cx, 'tcx>( } else { let upvars_projection_def_id = tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None); - let tupled_upvars_ty = Ty::new_projection(tcx, upvars_projection_def_id, [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(), - tcx.lifetimes.re_static.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ]); + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(kind_ty), + Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(), + tcx.lifetimes.re_static.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); sig.to_coroutine( tcx, args.parent_args(), @@ -1795,14 +1799,18 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( // will project to the right upvars for the generator, appending the inputs and // coroutine upvars respecting the closure kind. // N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`. - let tupled_upvars_ty = Ty::new_projection(tcx, upvars_projection_def_id, [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, goal_kind).into(), - env_region.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ]); + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(kind_ty), + Ty::from_closure_kind(tcx, goal_kind).into(), + env_region.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); sig.to_coroutine( tcx, args.parent_args(), @@ -1816,17 +1824,16 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( name => bug!("no such associated type: {name}"), }; let projection_term = match item_name { - sym::CallOnceFuture | sym::Output => { - ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - self_ty, - sig.tupled_inputs_ty, - ]) - } - sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - ty::GenericArg::from(self_ty), - sig.tupled_inputs_ty.into(), - env_region.into(), - ]), + sym::CallOnceFuture | sym::Output => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [self_ty, sig.tupled_inputs_ty], + ), + sym::CallRefFuture => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()], + ), name => bug!("no such associated type: {name}"), }; @@ -1846,17 +1853,20 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( name => bug!("no such associated type: {name}"), }; let projection_term = match item_name { - sym::CallOnceFuture | sym::Output => { - ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - self_ty, - Ty::new_tup(tcx, sig.inputs()), - ]) - } - sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - ty::GenericArg::from(self_ty), - Ty::new_tup(tcx, sig.inputs()).into(), - env_region.into(), - ]), + sym::CallOnceFuture | sym::Output => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [self_ty, Ty::new_tup(tcx, sig.inputs())], + ), + sym::CallRefFuture => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [ + ty::GenericArg::from(self_ty), + Ty::new_tup(tcx, sig.inputs()).into(), + env_region.into(), + ], + ), name => bug!("no such associated type: {name}"), }; @@ -1879,11 +1889,11 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( sym::CallOnceFuture | sym::Output => { ty::AliasTerm::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]]) } - sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - ty::GenericArg::from(self_ty), - sig.inputs()[0].into(), - env_region.into(), - ]), + sym::CallRefFuture => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()], + ), name => bug!("no such associated type: {name}"), }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 26ba1511b54..92098e20448 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -6,7 +6,8 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::traits::{ObligationCause, ObligationCtxt}; +use crate::traits::query::normalize::QueryNormalizeExt; +use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to @@ -90,6 +91,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn compute_dropck_outlives_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>, + span: Span, ) -> Result<DropckOutlivesResult<'tcx>, NoSolution> { let tcx = ocx.infcx.tcx; let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal; @@ -135,7 +137,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); - let cause = ObligationCause::dummy(); + let cause = ObligationCause::dummy_with_span(span); let mut constraints = DropckConstraint::empty(); while let Some((ty, depth)) = ty_stack.pop() { debug!( @@ -171,18 +173,13 @@ pub fn compute_dropck_outlives_inner<'tcx>( // do not themselves define a destructor", more or less. We have // to push them onto the stack to be expanded. for ty in constraints.dtorck_types.drain(..) { - let normalized_ty = ocx.normalize(&cause, param_env, ty); - - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}"); - return Err(NoSolution); - } + let Normalized { value: ty, obligations } = + ocx.infcx.at(&cause, param_env).query_normalize(ty)?; + ocx.register_obligations(obligations); - let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty); - debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty); + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - match normalized_ty.kind() { + match ty.kind() { // All parameters live for the duration of the // function. ty::Param(..) => {} @@ -190,12 +187,12 @@ pub fn compute_dropck_outlives_inner<'tcx>( // A projection that we couldn't resolve - it // might have a destructor. ty::Alias(..) => { - result.kinds.push(normalized_ty.into()); + result.kinds.push(ty.into()); } _ => { - if ty_set.insert(normalized_ty) { - ty_stack.push((normalized_ty, depth + 1)); + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); } } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 254dee794f1..4eecde00eaa 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -30,8 +30,9 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result<Self::QueryResponse, NoSolution> { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, span) } } @@ -41,11 +42,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { pub fn type_op_ascribe_user_type_with_span<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, - span: Option<Span>, + span: Span, ) -> Result<(), NoSolution> { let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); - let span = span.unwrap_or(DUMMY_SP); match user_ty.kind { UserTypeKind::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, UserTypeKind::TypeOf(def_id, user_args) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index fe47e837dfb..ec0b7903396 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; -use rustc_span::DUMMY_SP; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::{DUMMY_SP, Span}; use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -45,11 +45,12 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result<Self::QueryResponse, NoSolution> { if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span) } else { - compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty, span) } } } @@ -58,13 +59,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { let normalize_op = |ty| -> Result<_, NoSolution> { // We must normalize the type so we can compute the right outlives components. // for example, if we have some constrained param type like `T: Trait<Out = U>`, // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. let ty = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) .map_err(|_| NoSolution)?; if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); @@ -90,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // From the full set of obligations, just filter down to the region relationships. for obligation in - wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten() + wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID) + .into_iter() + .flatten() { assert!(!obligation.has_escaping_bound_vars()); let Some(pred) = obligation.predicate.kind().no_bound_vars() else { @@ -142,6 +146,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { let tcx = ocx.infcx.tcx; @@ -171,8 +176,8 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // FIXME(@lcnr): It's not really "always fine", having fewer implied // bounds can be backward incompatible, e.g. #101951 was caused by // us not dealing with inference vars in `TypeOutlives` predicates. - let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) - .unwrap_or_default(); + let obligations = + wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, span).unwrap_or_default(); for obligation in obligations { debug!(?obligation); @@ -255,7 +260,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // Need to manually normalize in the new solver as `wf::obligations` does not. if ocx.infcx.next_trait_solver() { ty_a = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty_a) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty_a) .map_err(|_| NoSolution)?; } let mut components = smallvec![]; 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 54fce914bb6..68feb19c55b 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 @@ -92,6 +92,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result<Self::QueryResponse, NoSolution>; fn fully_perform_into( @@ -152,7 +153,7 @@ where if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, - |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self), + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), "query type op", span, )? diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 94df222932e..2f6bbd7f4cf 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -2,9 +2,10 @@ use std::fmt; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; -pub use rustc_middle::traits::query::type_op::Normalize; +pub use rustc_middle::traits::query::type_op::{DeeplyNormalize, Normalize}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -27,11 +28,53 @@ where } fn perform_locally_with_next_solver( + _ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + _span: Span, + ) -> Result<Self::QueryResponse, NoSolution> { + Ok(key.value.value) + } +} + +impl<'tcx, T> super::QueryTypeOp<'tcx> for DeeplyNormalize<T> +where + T: Normalizable<'tcx> + 'tcx, +{ + type QueryResponse = T; + + fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> { + if !key.value.value.has_aliases() { Some(key.value.value) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { + T::type_op_method( + tcx, + CanonicalQueryInput { + typing_mode: canonicalized.typing_mode, + canonical: canonicalized.canonical.unchecked_map( + |ty::ParamEnvAnd { param_env, value }| ty::ParamEnvAnd { + param_env, + value: Normalize { value: value.value }, + }, + ), + }, + ) + } + + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result<Self::QueryResponse, NoSolution> { - // FIXME(-Znext-solver): shouldn't be using old normalizer - Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) + ocx.deeply_normalize( + &ObligationCause::dummy_with_span(span), + key.param_env, + key.value.value, + ) + .map_err(|_| NoSolution) } } @@ -79,3 +122,14 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { tcx.type_op_normalize_fn_sig(canonicalized) } } + +/// This impl is not needed, since we never normalize type outlives predicates +/// in the old solver, but is required by trait bounds to be happy. +impl<'tcx> Normalizable<'tcx> for ty::PolyTypeOutlivesPredicate<'tcx> { + fn type_op_method( + _tcx: TyCtxt<'tcx>, + _canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { + unreachable!("we never normalize PolyTypeOutlivesPredicate") + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index fa05f901f66..99a2779aa82 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,5 +1,6 @@ use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -28,7 +29,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result<Self::QueryResponse, NoSolution> { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value)) + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value), span) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index b2dab379262..4f9e2e79d62 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -57,10 +58,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result<Self::QueryResponse, NoSolution> { ocx.register_obligation(Obligation::new( ocx.infcx.tcx, - ObligationCause::dummy(), + ObligationCause::dummy_with_span(span), key.param_env, key.value.predicate, )); 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 13a6744c2e9..5b362b2356e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -12,9 +12,7 @@ use hir::LangItem; use hir::def_id::DefId; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; -use rustc_infer::traits::{ - Obligation, ObligationCause, PolyTraitObligation, PredicateObligations, SelectionError, -}; +use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; @@ -23,8 +21,6 @@ use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack}; -use crate::traits; -use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::util; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -106,6 +102,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidate_for_tuple(obligation, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) { + self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + obligation, + &mut candidates, + ); } else { if tcx.is_lang_item(def_id, LangItem::Clone) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -904,54 +905,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } - /// Temporary migration for #89190 - fn need_migrate_deref_output_trait_object( - &mut self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: &ObligationCause<'tcx>, - ) -> Option<ty::PolyExistentialTraitRef<'tcx>> { - // Don't drop any candidates in intercrate mode, as it's incomplete. - // (Not that it matters, since `Unsize` is not a stable trait.) - // - // FIXME(@lcnr): This should probably only trigger during analysis, - // disabling candidates during codegen is also questionable. - if let TypingMode::Coherence = self.infcx.typing_mode() { - return None; - } - - let tcx = self.tcx(); - if tcx.features().trait_upcasting() { - return None; - } - - // <ty as Deref> - let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]); - - let obligation = - traits::Obligation::new(tcx, cause.clone(), param_env, ty::Binder::dummy(trait_ref)); - if !self.infcx.predicate_may_hold(&obligation) { - return None; - } - - self.infcx.probe(|_| { - let ty = traits::normalize_projection_ty( - self, - param_env, - ty::AliasTy::new_from_args(tcx, tcx.lang_items().deref_target()?, trait_ref.args), - cause.clone(), - 0, - // We're *intentionally* throwing these away, - // since we don't actually use them. - &mut PredicateObligations::new(), - ) - .as_type() - .unwrap(); - - if let ty::Dynamic(data, ..) = ty.kind() { data.principal() } else { None } - }) - } - /// Searches for unsizing that might apply to `obligation`. fn assemble_candidates_for_unsizing( &mut self, @@ -972,11 +925,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // T: Trait // so it seems ok if we (conservatively) fail to accept that `Unsize` // obligation above. Should be possible to extend this in the future. - let Some(source) = obligation.self_ty().no_bound_vars() else { + let Some(trait_pred) = obligation.predicate.no_bound_vars() else { // Don't add any candidates if there are bound regions. return; }; - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let source = trait_pred.self_ty(); + let target = trait_pred.trait_ref.args.type_at(1); debug!(?source, ?target, "assemble_candidates_for_unsizing"); @@ -1019,15 +973,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let principal_a = a_data.principal().unwrap(); let target_trait_did = principal_def_id_b.unwrap(); let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); - if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object( - source, - obligation.param_env, - &obligation.cause, - ) { - if deref_trait_ref.def_id() == target_trait_did { - return; - } - } for (idx, upcast_trait_ref) in util::supertraits(self.tcx(), source_trait_ref).enumerate() @@ -1244,4 +1189,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match obligation.predicate.self_ty().skip_binder().kind() { + ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(_) + | ty::Array(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => { + candidates.vec.push(BikeshedGuaranteedNoDropCandidate); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + 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 729ae3f2c2a..32cbb97e314 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_type_ir::elaborate; +use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; @@ -130,6 +131,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TraitUpcastingUnsizeCandidate(idx) => { self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } + + BikeshedGuaranteedNoDropCandidate => { + self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation) + } }; // The obligations returned by confirmation are recursively evaluated @@ -301,11 +306,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let make_transmute_obl = |src, dst| { let transmute_trait = obligation.predicate.def_id(); let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); - let trait_ref = ty::TraitRef::new(tcx, transmute_trait, [ - ty::GenericArg::from(dst), - ty::GenericArg::from(src), - ty::GenericArg::from(assume), - ]); + let trait_ref = ty::TraitRef::new( + tcx, + transmute_trait, + [ + ty::GenericArg::from(dst), + ty::GenericArg::from(src), + ty::GenericArg::from(assume), + ], + ); Obligation::with_depth( tcx, obligation.cause.clone(), @@ -316,10 +325,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let make_freeze_obl = |ty| { - let trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Freeze, None), [ - ty::GenericArg::from(ty), - ]); + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Freeze, None), + [ty::GenericArg::from(ty)], + ); Obligation::with_depth( tcx, obligation.cause.clone(), @@ -863,10 +873,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - sig.tupled_inputs_ty, - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, sig.tupled_inputs_ty], + ) }) } _ => { @@ -892,10 +903,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::CoroutineClosure(_, args) => { let args = args.as_coroutine_closure(); let trait_ref = args.coroutine_closure_sig().map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - sig.tupled_inputs_ty, - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, sig.tupled_inputs_ty], + ) }); // Note that unlike below, we don't need to check `Future + Sized` for @@ -906,10 +918,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::FnDef(..) | ty::FnPtr(..) => { let sig = self_ty.fn_sig(tcx); let trait_ref = sig.map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - Ty::new_tup(tcx, sig.inputs()), - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, Ty::new_tup(tcx, sig.inputs())], + ) }); // We must additionally check that the return type impls `Future + Sized`. @@ -934,10 +947,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let args = args.as_closure(); let sig = args.sig(); let trait_ref = sig.map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - sig.inputs()[0], - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, sig.inputs()[0]], + ) }); // We must additionally check that the return type impls `Future + Sized`. @@ -1295,10 +1309,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate. let tail_unsize_obligation = obligation.with( tcx, - ty::TraitRef::new(tcx, obligation.predicate.def_id(), [ - source_tail, - target_tail, - ]), + ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [source_tail, target_tail], + ), ); nested.push(tail_unsize_obligation); @@ -1336,6 +1351,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }) } + + fn confirm_bikeshed_guaranteed_no_drop_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> ImplSource<'tcx, PredicateObligation<'tcx>> { + let mut obligations = thin_vec![]; + + let tcx = self.tcx(); + let self_ty = obligation.predicate.self_ty(); + match *self_ty.skip_binder().kind() { + // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. + ty::Ref(..) => {} + // `ManuallyDrop<T>` always implements `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() => {} + // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if + // their constituent types implement `BikeshedGuaranteedNoDrop`. + ty::Tuple(tys) => { + obligations.extend(tys.iter().map(|elem_ty| { + obligation.with( + tcx, + self_ty.rebind(ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [elem_ty], + )), + ) + })); + } + ty::Array(elem_ty, _) => { + obligations.push(obligation.with( + tcx, + self_ty.rebind(ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [elem_ty], + )), + )); + } + + // All other types implement `BikeshedGuaranteedNoDrop` only if + // they implement `Copy`. We could be smart here and short-circuit + // some trivially `Copy`/`!Copy` types, but there's no benefit. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => { + obligations.push(obligation.with( + tcx, + self_ty.map_bound(|ty| { + ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Copy, Some(obligation.cause.span)), + [ty], + ) + }), + )); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + } + + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } } /// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures) @@ -1359,10 +1461,11 @@ fn pointer_like_goal_for_rpitit<'tcx>( ty::GenericParamDefKind::Lifetime => { let kind = ty::BoundRegionKind::Named(arg.def_id, tcx.item_name(arg.def_id)); bound_vars.push(ty::BoundVariableKind::Region(kind)); - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, + ) .into() } ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { @@ -1371,9 +1474,11 @@ fn pointer_like_goal_for_rpitit<'tcx>( }); ty::Binder::bind_with_vars( - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), [ - Ty::new_projection_from_args(tcx, rpitit_item, args), - ]), + ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), + [Ty::new_projection_from_args(tcx, rpitit_item, args)], + ), tcx.mk_bound_variable_kinds(&bound_vars), ) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0cc0d7f786b..436ce3dddd9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -962,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(EvaluatedToAmbig); } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), - ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Value(cv) => cv.ty, ty::ConstKind::Unevaluated(uv) => { self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args) } @@ -1620,9 +1620,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // projections, we will never be able to equate, e.g. `<T as Tr>::A` // with `<<T as Tr>::A as Tr>::A`. let relevant_bounds = if in_parent_alias_type { - self.tcx().item_non_self_assumptions(alias_ty.def_id) + self.tcx().item_non_self_bounds(alias_ty.def_id) } else { - self.tcx().item_super_predicates(alias_ty.def_id) + self.tcx().item_self_bounds(alias_ty.def_id) }; for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { @@ -1949,7 +1949,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitAliasCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinObjectCandidate - | BuiltinUnsizeCandidate => false, + | BuiltinUnsizeCandidate + | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. ParamCandidate(_) | ImplCandidate(_) => true, @@ -2422,9 +2423,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } else { // If this is an ill-formed auto/built-in trait, then synthesize // new error args for the missing generics. - let err_args = ty::GenericArgs::extend_with_error(tcx, trait_def_id, &[ - normalized_ty.into(), - ]); + let err_args = ty::GenericArgs::extend_with_error( + tcx, + trait_def_id, + &[normalized_ty.into()], + ); ty::TraitRef::new_from_args(tcx, trait_def_id, err_args) }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 401b41c796d..cb3e81f5477 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -575,7 +575,7 @@ fn report_conflicting_impls<'tcx>( match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() - || tcx.ensure().orphan_check_impl(impl_def_id).is_ok() + || tcx.ensure_ok().orphan_check_impl(impl_def_id).is_ok() { let mut err = tcx.dcx().struct_span_err(impl_span, msg()); err.code(E0119); 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 c351cf5aaac..f39c611d19f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -379,7 +379,7 @@ pub(crate) fn assoc_def( // Ensure that the impl is constrained, otherwise projection may give us // bad unconstrained infer vars. if let Some(impl_def_id) = impl_def_id.as_local() { - tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; + tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; } let item = tcx.associated_item(impl_item_id); @@ -402,7 +402,7 @@ pub(crate) fn assoc_def( if assoc_item.item.container == ty::AssocItemContainer::Impl && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local() { - tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; + tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; } Ok(assoc_item) diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index c9fb2a757e1..d30363ec158 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -25,7 +25,7 @@ use tracing::debug; /// trait Bar {} /// trait Foo = Bar + Bar; /// -/// let not_object_safe: dyn Foo; // bad, two `Bar` principals. +/// let dyn_incompatible: dyn Foo; // bad, two `Bar` principals. /// ``` pub fn expand_trait_aliases<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index b5bc8364c7b..0024316b877 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,26 +2,22 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; -use rustc_infer::infer::at::ToTrace; -use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry, }; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::DUMMY_SP; use smallvec::{SmallVec, smallvec}; use tracing::debug; -use crate::errors::DumpVTableEntries; -use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method}; +use crate::traits::{impossible_predicates, is_vtable_safe_method}; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { MetadataDSA, - TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool }, + TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool }, } /// Prepare the segments for a vtable @@ -29,7 +25,7 @@ pub enum VtblSegment<'tcx> { // about our `Self` type here. pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, ) -> Option<T> { prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() @@ -39,7 +35,7 @@ pub fn prepare_vtable_segments<'tcx, T>( /// such that we can use `?` in the body. fn prepare_vtable_segments_inner<'tcx, T>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, ) -> ControlFlow<T> { // The following constraints holds for the final arrangement. @@ -92,7 +88,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( let mut emit_vptr_on_new_entry = false; let mut visited = PredicateSet::new(tcx); let predicate = trait_ref.upcast(tcx); - let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = + let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> = smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; visited.insert(predicate); @@ -125,10 +121,18 @@ fn prepare_vtable_segments_inner<'tcx, T>( let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); let mut direct_super_traits_iter = tcx - .explicit_super_predicates_of(inner_most_trait_ref.def_id()) + .explicit_super_predicates_of(inner_most_trait_ref.def_id) .iter_identity_copied() .filter_map(move |(pred, _)| { - pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause() + pred.instantiate_supertrait(tcx, ty::Binder::dummy(inner_most_trait_ref)) + .as_trait_clause() + }) + .map(move |pred| { + tcx.normalize_erasing_late_bound_regions( + ty::TypingEnv::fully_monomorphized(), + pred, + ) + .trait_ref }); // Find an unvisited supertrait @@ -136,16 +140,11 @@ fn prepare_vtable_segments_inner<'tcx, T>( .find(|&super_trait| visited.insert(super_trait.upcast(tcx))) { // Push it to the stack for the next iteration of 'diving_in to pick up - Some(unvisited_super_trait) => { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); - stack.push(( - next_super_trait, - emit_vptr_on_new_entry, - maybe_iter(Some(direct_super_traits_iter)), - )) - } + Some(next_super_trait) => stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + maybe_iter(Some(direct_super_traits_iter)), + )), // There are no more unvisited direct super traits, dive-in finished None => break 'diving_in, @@ -154,8 +153,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { - let has_entries = - has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()); + let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id); segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: inner_most_trait_ref, @@ -169,11 +167,6 @@ fn prepare_vtable_segments_inner<'tcx, T>( if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.upcast(tcx))) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_inner_most_trait_ref = - next_inner_most_trait_ref.map_bound(|t| t.trait_ref); - stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); // just pushed a new trait onto the stack, so we need to go through its super traits @@ -192,15 +185,6 @@ fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> { i.into_iter().flatten() } -fn dump_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - trait_ref: ty::PolyTraitRef<'tcx>, - entries: &[VtblEntry<'tcx>], -) { - tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") }); -} - fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some() } @@ -239,8 +223,15 @@ fn own_existential_vtable_entries_iter( /// that come from `trait_ref`, including its supertraits. fn vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, ) -> &'tcx [VtblEntry<'tcx>] { + debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param()); + debug_assert_eq!( + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref), + trait_ref, + "vtable trait ref should be normalized" + ); + debug!("vtable_entries({:?})", trait_ref); let mut entries = vec![]; @@ -251,33 +242,26 @@ fn vtable_entries<'tcx>( entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES); } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let existential_trait_ref = trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref); // Lookup the shape of vtable for the trait. let own_existential_entries = - tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); + 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); // The method may have some early-bound lifetimes; add regions for those. - let args = trait_ref.map_bound(|trait_ref| { + // FIXME: Is this normalize needed? + let args = tcx.normalize_erasing_regions( + ty::TypingEnv::fully_monomorphized(), GenericArgs::for_item(tcx, def_id, |param, _| match param.kind { GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { trait_ref.args[param.index as usize] } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let args = tcx.normalize_erasing_late_bound_regions( - ty::TypingEnv::fully_monomorphized(), - args, + }), ); // It's possible that the method relies on where-clauses that @@ -317,11 +301,6 @@ fn vtable_entries<'tcx>( let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback); - if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) { - let sp = tcx.def_span(trait_ref.def_id()); - dump_vtable_entries(tcx, sp, trait_ref, &entries); - } - tcx.arena.alloc_from_iter(entries) } @@ -329,14 +308,28 @@ fn vtable_entries<'tcx>( // for `Supertrait`'s methods in the vtable of `Subtrait`. pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize { debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); + debug_assert_eq!( + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key), + key, + "vtable trait ref should be normalized" + ); let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { bug!(); }; - let source_principal = - source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = tcx.instantiate_bound_regions_with_erased( + source.principal().unwrap().with_self_ty(tcx, key.self_ty()), + ); + + // We're monomorphizing a call to a dyn trait object that can never be constructed. + if tcx.instantiate_and_check_impossible_predicates(( + source_principal.def_id, + source_principal.args, + )) { + return 0; + } - let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key)); + let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key); let vtable_segment_callback = { let mut vptr_offset = 0; @@ -346,17 +339,14 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { - if trait_refs_are_compatible( - tcx, - vtable_principal - .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), - target_principal, - ) { + if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal) + == target_principal + { return ControlFlow::Break(vptr_offset); } vptr_offset += - tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); + tcx.own_existential_vtable_entries(vtable_principal.def_id).len(); if emit_vptr { vptr_offset += 1; @@ -382,20 +372,35 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( ), ) -> Option<usize> { debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); + debug_assert_eq!( + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key), + key, + "upcasting trait refs should be normalized" + ); + let (source, target) = key; // If the target principal is `None`, we can just return `None`. - let ty::Dynamic(target, _, _) = *target.kind() else { + let ty::Dynamic(target_data, _, _) = *target.kind() else { bug!(); }; - let target_principal = target.principal()?; + let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?); // Given that we have a target principal, it is a bug for there not to be a source principal. - let ty::Dynamic(source, _, _) = *source.kind() else { + let ty::Dynamic(source_data, _, _) = *source.kind() else { bug!(); }; - let source_principal = - source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = tcx.instantiate_bound_regions_with_erased( + source_data.principal().unwrap().with_self_ty(tcx, source), + ); + + // We're monomorphizing a dyn trait object upcast that can never be constructed. + if tcx.instantiate_and_check_impossible_predicates(( + source_principal.def_id, + source_principal.args, + )) { + return None; + } let vtable_segment_callback = { let mut vptr_offset = 0; @@ -406,13 +411,10 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( } VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { vptr_offset += - tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); - if trait_refs_are_compatible( - tcx, - vtable_principal - .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), - target_principal, - ) { + tcx.own_existential_vtable_entries(vtable_principal.def_id).len(); + if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal) + == target_principal + { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -432,41 +434,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap() } -fn trait_refs_are_compatible<'tcx>( - tcx: TyCtxt<'tcx>, - hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>, - hr_target_principal: ty::PolyExistentialTraitRef<'tcx>, -) -> bool { - if hr_vtable_principal.def_id() != hr_target_principal.def_id() { - return false; - } - - let (infcx, param_env) = - tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized()); - let ocx = ObligationCtxt::new(&infcx); - let hr_source_principal = - ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal); - let hr_target_principal = - ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal); - infcx.enter_forall(hr_target_principal, |target_principal| { - let source_principal = infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - hr_source_principal, - ); - let Ok(()) = ocx.eq_trace( - &ObligationCause::dummy(), - param_env, - ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal), - target_principal, - source_principal, - ) else { - return false; - }; - ocx.select_all_or_error().is_empty() - }) -} - pub(super) fn provide(providers: &mut Providers) { *providers = Providers { own_existential_vtable_entries, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9d32eb05386..2f72b44f6b6 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -8,8 +8,9 @@ use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_session::parse::feature_err; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::{Span, sym}; use tracing::{debug, instrument, trace}; use crate::infer::InferCtxt; @@ -89,6 +90,8 @@ pub fn unnormalized_obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, arg: GenericArg<'tcx>, + span: Span, + body_id: LocalDefId, ) -> Option<PredicateObligations<'tcx>> { debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); @@ -106,8 +109,8 @@ pub fn unnormalized_obligations<'tcx>( let mut wf = WfPredicates { infcx, param_env, - body_id: CRATE_DEF_ID, - span: DUMMY_SP, + body_id, + span, out: PredicateObligations::new(), recursion_depth: 0, item: None, @@ -689,7 +692,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); // Note that the len being WF is implicitly checked while visiting. // Here we just check that it's of type usize. - let cause = self.cause(ObligationCauseCode::Misc); + let cause = self.cause(ObligationCauseCode::ArrayLen(t)); self.out.push(traits::Obligation::with_depth( tcx, cause, @@ -702,8 +705,47 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { )); } - ty::Pat(subty, _) => { + ty::Pat(subty, pat) => { self.require_sized(subty, ObligationCauseCode::Misc); + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + let mut check = |c| { + let cause = self.cause(ObligationCauseCode::Misc); + self.out.push(traits::Obligation::with_depth( + tcx, + cause.clone(), + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType(c, subty), + )), + )); + if !tcx.features().generic_pattern_types() { + if c.has_param() { + if self.span.is_dummy() { + self.tcx().dcx().delayed_bug( + "feature error should be reported elsewhere, too", + ); + } else { + feature_err( + &self.tcx().sess, + sym::generic_pattern_types, + self.span, + "wraparound pattern type ranges cause monomorphization time errors", + ) + .emit(); + } + } + } + }; + if let Some(start) = start { + check(start) + } + if let Some(end) = end { + check(end) + } + } + } } ty::Tuple(tys) => { @@ -828,8 +870,28 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { // Let the visitor iterate into the argument/return // types appearing in the fn signature. } - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binders): We should also recurse into the binder here. + ty::UnsafeBinder(ty) => { + // FIXME(unsafe_binders): For now, we have no way to express + // that a type must be `ManuallyDrop` OR `Copy` (or a pointer). + if !ty.has_escaping_bound_vars() { + self.out.push(traits::Obligation::new( + self.tcx(), + self.cause(ObligationCauseCode::Misc), + self.param_env, + ty.map_bound(|ty| { + ty::TraitRef::new( + self.tcx(), + self.tcx().require_lang_item( + LangItem::BikeshedGuaranteedNoDrop, + Some(self.span), + ), + [ty], + ) + }), + )); + } + + // We recurse into the binder below. } ty::Dynamic(data, r, _) => {  | 
