diff options
Diffstat (limited to 'compiler/rustc_infer/src')
51 files changed, 2611 insertions, 1330 deletions
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs new file mode 100644 index 000000000..2131d1906 --- /dev/null +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -0,0 +1,505 @@ +use hir::GenericParamKind; +use rustc_errors::{ + fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, + MultiSpan, SubdiagnosticMessage, +}; +use rustc_hir as hir; +use rustc_hir::{FnRetTy, Ty}; +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_middle::ty::{Region, TyCtxt}; +use rustc_span::symbol::kw; +use rustc_span::{symbol::Ident, BytePos, Span}; + +use crate::infer::error_reporting::{ + need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind}, + ObligationCauseAsDiagArg, +}; + +pub mod note_and_explain; + +#[derive(Diagnostic)] +#[diag(infer_opaque_hidden_type)] +pub struct OpaqueHiddenTypeDiag { + #[primary_span] + #[label] + pub span: Span, + #[note(opaque_type)] + pub opaque_type: Span, + #[note(hidden_type)] + pub hidden_type: Span, +} + +#[derive(Diagnostic)] +#[diag(infer_type_annotations_needed, code = "E0282")] +pub struct AnnotationRequired<'a> { + #[primary_span] + pub span: Span, + pub source_kind: &'static str, + pub source_name: &'a str, + #[label] + pub failure_span: Option<Span>, + #[subdiagnostic] + pub bad_label: Option<InferenceBadError<'a>>, + #[subdiagnostic] + pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, + #[subdiagnostic] + pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, +} + +// Copy of `AnnotationRequired` for E0283 +#[derive(Diagnostic)] +#[diag(infer_type_annotations_needed, code = "E0283")] +pub struct AmbigousImpl<'a> { + #[primary_span] + pub span: Span, + pub source_kind: &'static str, + pub source_name: &'a str, + #[label] + pub failure_span: Option<Span>, + #[subdiagnostic] + pub bad_label: Option<InferenceBadError<'a>>, + #[subdiagnostic] + pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, + #[subdiagnostic] + pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, +} + +// Copy of `AnnotationRequired` for E0284 +#[derive(Diagnostic)] +#[diag(infer_type_annotations_needed, code = "E0284")] +pub struct AmbigousReturn<'a> { + #[primary_span] + pub span: Span, + pub source_kind: &'static str, + pub source_name: &'a str, + #[label] + pub failure_span: Option<Span>, + #[subdiagnostic] + pub bad_label: Option<InferenceBadError<'a>>, + #[subdiagnostic] + pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, + #[subdiagnostic] + pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, +} + +#[derive(Diagnostic)] +#[diag(infer_need_type_info_in_generator, code = "E0698")] +pub struct NeedTypeInfoInGenerator<'a> { + #[primary_span] + pub span: Span, + pub generator_kind: GeneratorKindAsDiagArg, + #[subdiagnostic] + pub bad_label: InferenceBadError<'a>, +} + +// Used when a better one isn't available +#[derive(Subdiagnostic)] +#[label(infer_label_bad)] +pub struct InferenceBadError<'a> { + #[primary_span] + pub span: Span, + pub bad_kind: &'static str, + pub prefix_kind: UnderspecifiedArgKind, + pub has_parent: bool, + pub prefix: &'a str, + pub parent_prefix: &'a str, + pub parent_name: String, + pub name: String, +} + +#[derive(Subdiagnostic)] +pub enum SourceKindSubdiag<'a> { + #[suggestion_verbose( + infer_source_kind_subdiag_let, + code = ": {type_name}", + applicability = "has-placeholders" + )] + LetLike { + #[primary_span] + span: Span, + name: String, + type_name: String, + kind: &'static str, + x_kind: &'static str, + prefix_kind: UnderspecifiedArgKind, + prefix: &'a str, + arg_name: String, + }, + #[label(infer_source_kind_subdiag_generic_label)] + GenericLabel { + #[primary_span] + span: Span, + is_type: bool, + param_name: String, + parent_exists: bool, + parent_prefix: String, + parent_name: String, + }, + #[suggestion_verbose( + infer_source_kind_subdiag_generic_suggestion, + code = "::<{args}>", + applicability = "has-placeholders" + )] + GenericSuggestion { + #[primary_span] + span: Span, + arg_count: usize, + args: String, + }, +} + +#[derive(Subdiagnostic)] +pub enum SourceKindMultiSuggestion<'a> { + #[multipart_suggestion_verbose( + infer_source_kind_fully_qualified, + applicability = "has-placeholders" + )] + FullyQualified { + #[suggestion_part(code = "{def_path}({adjustment}")] + span_lo: Span, + #[suggestion_part(code = "{successor_pos}")] + span_hi: Span, + def_path: String, + adjustment: &'a str, + successor_pos: &'a str, + }, + #[multipart_suggestion_verbose( + infer_source_kind_closure_return, + applicability = "has-placeholders" + )] + ClosureReturn { + #[suggestion_part(code = "{start_span_code}")] + start_span: Span, + start_span_code: String, + #[suggestion_part(code = " }}")] + end_span: Option<Span>, + }, +} + +impl<'a> SourceKindMultiSuggestion<'a> { + pub fn new_fully_qualified( + span: Span, + def_path: String, + adjustment: &'a str, + successor: (&'a str, BytePos), + ) -> Self { + Self::FullyQualified { + span_lo: span.shrink_to_lo(), + span_hi: span.shrink_to_hi().with_hi(successor.1), + def_path, + adjustment, + successor_pos: successor.0, + } + } + + pub fn new_closure_return( + ty_info: String, + data: &'a FnRetTy<'a>, + should_wrap_expr: Option<Span>, + ) -> Self { + let (arrow, post) = match data { + FnRetTy::DefaultReturn(_) => ("-> ", " "), + _ => ("", ""), + }; + let (start_span, start_span_code, end_span) = match should_wrap_expr { + Some(end_span) => { + (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post), Some(end_span)) + } + None => (data.span(), format!("{}{}{}", arrow, ty_info, post), None), + }; + Self::ClosureReturn { start_span, start_span_code, end_span } + } +} + +pub enum RegionOriginNote<'a> { + Plain { + span: Span, + msg: DiagnosticMessage, + }, + WithName { + span: Span, + msg: DiagnosticMessage, + name: &'a str, + continues: bool, + }, + WithRequirement { + span: Span, + requirement: ObligationCauseAsDiagArg<'a>, + expected_found: Option<(DiagnosticStyledString, DiagnosticStyledString)>, + }, +} + +impl AddToDiagnostic for RegionOriginNote<'_> { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let mut label_or_note = |span, msg: DiagnosticMessage| { + let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); + let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); + let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); + if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { + diag.span_label(span, msg); + } else if span_is_primary && expanded_sub_count == 0 { + diag.note(msg); + } else { + diag.span_note(span, msg); + } + }; + match self { + RegionOriginNote::Plain { span, msg } => { + label_or_note(span, msg); + } + RegionOriginNote::WithName { span, msg, name, continues } => { + label_or_note(span, msg); + diag.set_arg("name", name); + diag.set_arg("continues", continues); + } + RegionOriginNote::WithRequirement { + span, + requirement, + expected_found: Some((expected, found)), + } => { + label_or_note(span, fluent::infer_subtype); + diag.set_arg("requirement", requirement); + + diag.note_expected_found(&"", expected, &"", found); + } + RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { + // FIXME: this really should be handled at some earlier stage. Our + // handling of region checking when type errors are present is + // *terrible*. + label_or_note(span, fluent::infer_subtype_2); + diag.set_arg("requirement", requirement); + } + }; + } +} + +pub enum LifetimeMismatchLabels { + InRet { + param_span: Span, + ret_span: Span, + span: Span, + label_var1: Option<Ident>, + }, + Normal { + hir_equal: bool, + ty_sup: Span, + ty_sub: Span, + span: Span, + sup: Option<Ident>, + sub: Option<Ident>, + }, +} + +impl AddToDiagnostic for LifetimeMismatchLabels { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + match self { + LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { + diag.span_label(param_span, fluent::infer_declared_different); + diag.span_label(ret_span, fluent::infer_nothing); + diag.span_label(span, fluent::infer_data_returned); + diag.set_arg("label_var1_exists", label_var1.is_some()); + diag.set_arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); + } + LifetimeMismatchLabels::Normal { + hir_equal, + ty_sup, + ty_sub, + span, + sup: label_var1, + sub: label_var2, + } => { + if hir_equal { + diag.span_label(ty_sup, fluent::infer_declared_multiple); + diag.span_label(ty_sub, fluent::infer_nothing); + diag.span_label(span, fluent::infer_data_lifetime_flow); + } else { + diag.span_label(ty_sup, fluent::infer_types_declared_different); + diag.span_label(ty_sub, fluent::infer_nothing); + diag.span_label(span, fluent::infer_data_flows); + diag.set_arg("label_var1_exists", label_var1.is_some()); + diag.set_arg( + "label_var1", + label_var1.map(|x| x.to_string()).unwrap_or_default(), + ); + diag.set_arg("label_var2_exists", label_var2.is_some()); + diag.set_arg( + "label_var2", + label_var2.map(|x| x.to_string()).unwrap_or_default(), + ); + } + } + } + } +} + +pub struct AddLifetimeParamsSuggestion<'a> { + pub tcx: TyCtxt<'a>, + pub sub: Region<'a>, + pub ty_sup: &'a Ty<'a>, + pub ty_sub: &'a Ty<'a>, + pub add_note: bool, +} + +impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let mut mk_suggestion = || { + let ( + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, + ) = (self.ty_sub, self.ty_sup) else { + return false; + }; + + if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { + return false; + }; + + let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else { + return false; + }; + + let hir_id = self.tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); + + let node = self.tcx.hir().get(hir_id); + let is_impl = matches!(&node, hir::Node::ImplItem(_)); + let generics = match node { + hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn(_, ref generics, ..), + .. + }) + | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) + | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, + _ => return false, + }; + + let suggestion_param_name = generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .find(|i| *i != kw::UnderscoreLifetime); + let introduce_new = suggestion_param_name.is_none(); + let suggestion_param_name = + suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); + + debug!(?lifetime_sup.span); + debug!(?lifetime_sub.span); + let make_suggestion = |span: rustc_span::Span| { + if span.is_empty() { + (span, format!("{}, ", suggestion_param_name)) + } else if let Ok("&") = self.tcx.sess.source_map().span_to_snippet(span).as_deref() + { + (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) + } else { + (span, suggestion_param_name.clone()) + } + }; + let mut suggestions = + vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; + + if introduce_new { + let new_param_suggestion = if let Some(first) = + generics.params.iter().find(|p| !p.name.ident().span.is_empty()) + { + (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) + } else { + (generics.span, format!("<{}>", suggestion_param_name)) + }; + + suggestions.push(new_param_suggestion); + } + + diag.multipart_suggestion( + fluent::infer_lifetime_param_suggestion, + suggestions, + Applicability::MaybeIncorrect, + ); + diag.set_arg("is_impl", is_impl); + true + }; + if mk_suggestion() && self.add_note { + diag.note(fluent::infer_lifetime_param_suggestion_elided); + } + } +} + +#[derive(Diagnostic)] +#[diag(infer_lifetime_mismatch, code = "E0623")] +pub struct LifetimeMismatch<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub labels: LifetimeMismatchLabels, + #[subdiagnostic] + pub suggestion: AddLifetimeParamsSuggestion<'a>, +} + +pub struct IntroducesStaticBecauseUnmetLifetimeReq { + pub unmet_requirements: MultiSpan, + pub binding_span: Span, +} + +impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { + fn add_to_diagnostic_with<F>(mut self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + self.unmet_requirements + .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); + diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req); + } +} + +// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that +#[derive(Subdiagnostic)] +pub enum DoesNotOutliveStaticFromImpl { + #[note(infer_does_not_outlive_static_from_impl)] + Spanned { + #[primary_span] + span: Span, + }, + #[note(infer_does_not_outlive_static_from_impl)] + Unspanned, +} + +#[derive(Subdiagnostic)] +pub enum ImplicitStaticLifetimeSubdiag { + #[note(infer_implicit_static_lifetime_note)] + Note { + #[primary_span] + span: Span, + }, + #[suggestion_verbose( + infer_implicit_static_lifetime_suggestion, + code = " + '_", + applicability = "maybe-incorrect" + )] + Sugg { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(infer_mismatched_static_lifetime)] +pub struct MismatchedStaticLifetime<'a> { + #[primary_span] + pub cause_span: Span, + #[subdiagnostic] + pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, + #[subdiagnostic] + pub expl: Option<note_and_explain::RegionExplanation<'a>>, + #[subdiagnostic] + pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl, + #[subdiagnostic(eager)] + pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>, +} diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs new file mode 100644 index 000000000..6a29d8562 --- /dev/null +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -0,0 +1,177 @@ +use crate::infer::error_reporting::nice_region_error::find_anon_type; +use rustc_errors::{ + self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage, +}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::{symbol::kw, Span}; + +#[derive(Default)] +struct DescriptionCtx<'a> { + span: Option<Span>, + kind: &'a str, + arg: String, + num_arg: u32, +} + +impl<'a> DescriptionCtx<'a> { + fn new<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + alt_span: Option<Span>, + ) -> Option<Self> { + let mut me = DescriptionCtx::default(); + me.span = alt_span; + match *region { + ty::ReEarlyBound(_) | ty::ReFree(_) => { + return Self::from_early_bound_and_free_regions(tcx, region); + } + ty::ReStatic => { + me.kind = "restatic"; + } + + ty::RePlaceholder(_) => return None, + + // FIXME(#13998) RePlaceholder should probably print like + // ReFree rather than dumping Debug output on the user. + // + // We shouldn't really be having unification failures with ReVar + // and ReLateBound though. + ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => { + me.kind = "revar"; + me.arg = format!("{:?}", region); + } + }; + Some(me) + } + + fn from_early_bound_and_free_regions<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + ) -> Option<Self> { + let mut me = DescriptionCtx::default(); + let scope = region.free_region_binding_scope(tcx).expect_local(); + match *region { + ty::ReEarlyBound(ref br) => { + let mut sp = tcx.def_span(scope); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) + { + sp = param.span; + } + if br.has_name() { + me.kind = "as_defined"; + me.arg = br.name.to_string(); + } else { + me.kind = "as_defined_anon"; + }; + me.span = Some(sp) + } + ty::ReFree(ref fr) => { + if !fr.bound_region.is_named() + && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) + { + me.kind = "defined_here"; + me.span = Some(ty.span); + } else { + match fr.bound_region { + ty::BoundRegionKind::BrNamed(_, name) => { + let mut sp = tcx.def_span(scope); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) + { + sp = param.span; + } + if name == kw::UnderscoreLifetime { + me.kind = "as_defined_anon"; + } else { + me.kind = "as_defined"; + me.arg = name.to_string(); + }; + me.span = Some(sp); + } + ty::BrAnon(idx) => { + me.kind = "anon_num_here"; + me.num_arg = idx+1; + me.span = Some(tcx.def_span(scope)); + }, + _ => { + me.kind = "defined_here_reg"; + me.arg = region.to_string(); + me.span = Some(tcx.def_span(scope)); + }, + } + } + } + _ => bug!(), + } + Some(me) + } + + fn add_to(self, diag: &mut rustc_errors::Diagnostic) { + diag.set_arg("desc_kind", self.kind); + diag.set_arg("desc_arg", self.arg); + diag.set_arg("desc_num_arg", self.num_arg); + } +} + +pub enum PrefixKind { + Empty, +} + +pub enum SuffixKind { + Continues, +} + +impl IntoDiagnosticArg for PrefixKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self { + Self::Empty => "empty", + } + .into(); + rustc_errors::DiagnosticArgValue::Str(kind) + } +} + +impl IntoDiagnosticArg for SuffixKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self { + Self::Continues => "continues", + } + .into(); + rustc_errors::DiagnosticArgValue::Str(kind) + } +} + +pub struct RegionExplanation<'a> { + desc: DescriptionCtx<'a>, + prefix: PrefixKind, + suffix: SuffixKind, +} + +impl RegionExplanation<'_> { + pub fn new<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + alt_span: Option<Span>, + prefix: PrefixKind, + suffix: SuffixKind, + ) -> Option<Self> { + Some(Self { desc: DescriptionCtx::new(tcx, region, alt_span)?, prefix, suffix }) + } +} + +impl AddToDiagnostic for RegionExplanation<'_> { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + if let Some(span) = self.desc.span { + diag.span_note(span, fluent::infer_region_explanation); + } else { + diag.note(fluent::infer_region_explanation); + } + self.desc.add_to(diag); + diag.set_arg("pref_kind", self.prefix); + diag.set_arg("suff_kind", self.suffix); + } +} diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 130214a65..5ff3779fa 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::relate::{Relate, TypeRelation}; use rustc_middle::ty::{Const, ImplSubject}; pub struct At<'a, 'tcx> { - pub infcx: &'a InferCtxt<'a, 'tcx>, + pub infcx: &'a InferCtxt<'tcx>, pub cause: &'a ObligationCause<'tcx>, pub param_env: ty::ParamEnv<'tcx>, /// Whether we should define opaque types @@ -48,9 +48,9 @@ pub struct Trace<'a, 'tcx> { trace: TypeTrace<'tcx>, } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { #[inline] - pub fn at( + pub fn at<'a>( &'a self, cause: &'a ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -66,7 +66,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { tcx: self.tcx, defining_use_anchor: self.defining_use_anchor, considering_regions: self.considering_regions, - in_progress_typeck_results: self.in_progress_typeck_results, inner: self.inner.clone(), skip_leak_check: self.skip_leak_check.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), @@ -74,10 +73,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { evaluation_cache: self.evaluation_cache.clone(), reported_trait_errors: self.reported_trait_errors.clone(), reported_closure_mismatch: self.reported_closure_mismatch.clone(), - tainted_by_errors_flag: self.tainted_by_errors_flag.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), err_count_on_creation: self.err_count_on_creation, in_snapshot: self.in_snapshot.clone(), universe: self.universe.clone(), + normalize_fn_sig_for_diagnostic: self + .normalize_fn_sig_for_diagnostic + .as_ref() + .map(|f| f.clone()), } } } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index ca7862c9d..a3ff70363 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -20,7 +20,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::Idx; use smallvec::SmallVec; -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Canonicalizes a query value `V`. When we canonicalize a query, /// we not only canonicalize unbound inference variables, but we /// *also* replace all free regions whatsoever. So for example a @@ -180,11 +180,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match *r { - ty::ReFree(_) - | ty::ReErased - | ty::ReStatic - | ty::ReEmpty(ty::UniverseIndex::ROOT) - | ty::ReEarlyBound(..) => r, + ty::ReFree(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r, ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) }, @@ -199,10 +195,6 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { ) } - ty::ReEmpty(ui) => { - bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME - } - _ => { // Other than `'static` or `'empty`, the query // response should be executing in a fully @@ -324,7 +316,7 @@ impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { } struct Canonicalizer<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, tcx: TyCtxt<'tcx>, variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>, query_state: &'cx mut OriginalQueryValues<'tcx>, @@ -372,7 +364,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { debug!( "canonical: region var found with vid {:?}, \ opportunistically resolved to {:?}", - vid, r + vid, resolved_vid ); let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid)); self.canonicalize_mode.canonicalize_free_region(self, r) @@ -381,7 +373,6 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { ty::ReStatic | ty::ReEarlyBound(..) | ty::ReFree(_) - | ty::ReEmpty(_) | ty::RePlaceholder(..) | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), } @@ -530,7 +521,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { /// `canonicalize_query` and `canonicalize_response`. fn canonicalize<V>( value: V, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, tcx: TyCtxt<'tcx>, canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'tcx>, diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index a9294a85e..06ca2534d 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -36,7 +36,7 @@ mod canonicalizer; pub mod query_response; mod substitute; -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Creates a substitution S for the canonical value with fresh /// inference variables and applies it to the canonical value. /// Returns both the instantiated result *and* the substitution S. diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 8dc20544f..a299a3e57 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -16,12 +16,13 @@ use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelating use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin}; use crate::traits::query::{Fallible, NoSolution}; -use crate::traits::TraitEngine; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; +use crate::traits::{PredicateObligations, TraitEngine}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_index::vec::IndexVec; use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::TypeRelation; @@ -31,7 +32,7 @@ use rustc_span::Span; use std::fmt::Debug; use std::iter; -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// This method is meant to be invoked as the final step of a canonical query /// implementation. It is given: /// @@ -63,8 +64,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, { let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?; + debug!("query_response = {:#?}", query_response); let canonical_result = self.canonicalize_response(query_response); - debug!("canonical_result = {:#?}", canonical_result); Ok(self.tcx.arena.alloc(canonical_result)) @@ -125,13 +126,17 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { debug!("ambig_errors = {:#?}", ambig_errors); let region_obligations = self.take_registered_region_obligations(); + debug!(?region_obligations); let region_constraints = self.with_region_constraints(|region_constraints| { make_query_region_constraints( tcx, - region_obligations.iter().map(|r_o| (r_o.sup_type, r_o.sub_region)), + region_obligations + .iter() + .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())), region_constraints, ) }); + debug!(?region_constraints); let certainty = if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous }; @@ -246,6 +251,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // the original values `v_o` that was canonicalized into a // variable... + let constraint_category = cause.to_constraint_category(); + for (index, original_value) in original_values.var_values.iter().enumerate() { // ...with the value `v_r` of that variable from the query. let result_value = query_response.substitute_projected(self.tcx, &result_subst, |v| { @@ -261,12 +268,14 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => { // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. if v_o != v_r { - output_query_region_constraints - .outlives - .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); - output_query_region_constraints - .outlives - .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); + output_query_region_constraints.outlives.push(( + ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)), + constraint_category, + )); + output_query_region_constraints.outlives.push(( + ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)), + constraint_category, + )); } } @@ -312,7 +321,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // Screen out `'a: 'a` cases -- we skip the binder here but // only compare the inner values to one another, so they are still at // consistent binding levels. - let ty::OutlivesPredicate(k1, r2) = r_c.skip_binder(); + let ty::OutlivesPredicate(k1, r2) = r_c.0.skip_binder(); if k1 != r2.into() { Some(r_c) } else { None } }), ); @@ -500,7 +509,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { for &(a, b) in &query_response.value.opaque_types { let a = substitute_value(self.tcx, &result_subst, a); let b = substitute_value(self.tcx, &result_subst, b); - obligations.extend(self.handle_opaque_type(a, b, true, cause, param_env)?.obligations); + obligations.extend(self.at(cause, param_env).eq(a, b)?.obligations); } Ok(InferOk { value: result_subst, obligations }) @@ -557,7 +566,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Obligation<'tcx, ty::Predicate<'tcx>> { - let ty::OutlivesPredicate(k1, r2) = predicate.skip_binder(); + let ty::OutlivesPredicate(k1, r2) = predicate.0.skip_binder(); let atom = match k1.unpack() { GenericArgKind::Lifetime(r1) => { @@ -572,7 +581,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { span_bug!(cause.span, "unexpected const outlives {:?}", predicate); } }; - let predicate = predicate.rebind(atom).to_predicate(self.tcx); + let predicate = predicate.0.rebind(atom).to_predicate(self.tcx); Obligation::new(cause, param_env, predicate) } @@ -623,7 +632,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// creates query region constraints. pub fn make_query_region_constraints<'tcx>( tcx: TyCtxt<'tcx>, - outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>, + outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>, ConstraintCategory<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, ) -> QueryRegionConstraints<'tcx> { let RegionConstraintData { constraints, verifys, givens, member_constraints } = @@ -632,28 +641,35 @@ pub fn make_query_region_constraints<'tcx>( assert!(verifys.is_empty()); assert!(givens.is_empty()); + debug!(?constraints); + let outlives: Vec<_> = constraints .iter() - .map(|(k, _)| match *k { - // Swap regions because we are going from sub (<=) to outlives - // (>=). - Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( - tcx.mk_region(ty::ReVar(v2)).into(), - tcx.mk_region(ty::ReVar(v1)), - ), - Constraint::VarSubReg(v1, r2) => { - ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) - } - Constraint::RegSubVar(r1, v2) => { - ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) - } - Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), + .map(|(k, origin)| { + // no bound vars in the code above + let constraint = ty::Binder::dummy(match *k { + // Swap regions because we are going from sub (<=) to outlives + // (>=). + Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( + tcx.mk_region(ty::ReVar(v2)).into(), + tcx.mk_region(ty::ReVar(v1)), + ), + Constraint::VarSubReg(v1, r2) => { + ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) + } + Constraint::RegSubVar(r1, v2) => { + ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) + } + Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), + }); + (constraint, origin.to_constraint_category()) }) - .map(ty::Binder::dummy) // no bound vars in the code above .chain( outlives_obligations - .map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r)) - .map(ty::Binder::dummy), // no bound vars in the code above + // no bound vars in the code above + .map(|(ty, r, constraint_category)| { + (ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), r)), constraint_category) + }), ) .collect(); @@ -661,7 +677,7 @@ pub fn make_query_region_constraints<'tcx>( } struct QueryTypeRelatingDelegate<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, obligations: &'a mut Vec<PredicateObligation<'tcx>>, param_env: ty::ParamEnv<'tcx>, cause: &'a ObligationCause<'tcx>, @@ -725,17 +741,11 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { true } - fn register_opaque_type( + fn register_opaque_type_obligations( &mut self, - a: Ty<'tcx>, - b: Ty<'tcx>, - a_is_expected: bool, + obligations: PredicateObligations<'tcx>, ) -> Result<(), TypeError<'tcx>> { - self.obligations.extend( - self.infcx - .handle_opaque_type(a, b, a_is_expected, &self.cause, self.param_env)? - .obligations, - ); + self.obligations.extend(obligations); Ok(()) } } diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs index 34b611342..389afe22e 100644 --- a/compiler/rustc_infer/src/infer/canonical/substitute.rs +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -72,15 +72,16 @@ where value } else { let delegate = FnMutDelegate { - regions: |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { + regions: &mut |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { GenericArgKind::Lifetime(l) => l, r => bug!("{:?} is a region but value is {:?}", br, r), }, - types: |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { + types: &mut |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { GenericArgKind::Type(ty) => ty, r => bug!("{:?} is a type but value is {:?}", bound_ty, r), }, - consts: |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() { + consts: &mut |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() + { GenericArgKind::Const(ct) => ct, c => bug!("{:?} is a const but value is {:?}", bound_ct, c), }, diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 8bf1de34a..b5427f639 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -43,7 +43,7 @@ use rustc_span::{Span, DUMMY_SP}; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { - pub infcx: &'infcx InferCtxt<'infcx, 'tcx>, + pub infcx: &'infcx InferCtxt<'tcx>, pub trace: TypeTrace<'tcx>, pub cause: Option<ty::relate::Cause>, pub param_env: ty::ParamEnv<'tcx>, @@ -63,7 +63,7 @@ pub enum RelationDir { EqTo, } -impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { pub fn super_combine_tys<R>( &self, relation: &mut R, @@ -147,11 +147,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ty::ConstKind::Infer(InferConst::Var(a_vid)), ty::ConstKind::Infer(InferConst::Var(b_vid)), ) => { - self.inner - .borrow_mut() - .const_unification_table() - .unify_var_var(a_vid, b_vid) - .map_err(|e| const_unification_error(a_is_expected, e))?; + self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid); return Ok(a); } @@ -246,21 +242,17 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid } .relate(ct, ct)?; - self.inner - .borrow_mut() - .const_unification_table() - .unify_var_value( - target_vid, - ConstVarValue { - origin: ConstVariableOrigin { - kind: ConstVariableOriginKind::ConstInference, - span: DUMMY_SP, - }, - val: ConstVariableValue::Known { value }, + self.inner.borrow_mut().const_unification_table().union_value( + target_vid, + ConstVarValue { + origin: ConstVariableOrigin { + kind: ConstVariableOriginKind::ConstInference, + span: DUMMY_SP, }, - ) - .map(|()| value) - .map_err(|e| const_unification_error(vid_is_expected, e)) + val: ConstVariableValue::Known { value }, + }, + ); + Ok(value) } fn unify_integral_variable( @@ -391,7 +383,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { /// Preconditions: /// /// - `for_vid` is a "root vid" - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] fn generalize( &self, ty: Ty<'tcx>, @@ -435,15 +427,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { cache: SsoHashMap::new(), }; - let ty = match generalize.relate(ty, ty) { - Ok(ty) => ty, - Err(e) => { - debug!(?e, "failure"); - return Err(e); - } - }; + let ty = generalize.relate(ty, ty)?; let needs_wf = generalize.needs_wf; - trace!(?ty, ?needs_wf, "success"); Ok(Generalization { ty, needs_wf }) } @@ -467,7 +452,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { } struct Generalizer<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, /// The span, used when creating new type variables and things. cause: &'cx ObligationCause<'tcx>, @@ -493,12 +478,13 @@ struct Generalizer<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, - cache: SsoHashMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>, + cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>, } /// Result from a generalization operation. This includes /// not only the generalized type, but also a bool flag /// indicating whether further WF checks are needed. +#[derive(Debug)] struct Generalization<'tcx> { ty: Ty<'tcx>, @@ -599,8 +585,8 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be == - if let Some(result) = self.cache.get(&t) { - return result.clone(); + if let Some(&result) = self.cache.get(&t) { + return Ok(result); } debug!("generalize: t={:?}", t); @@ -670,10 +656,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { Ok(t) } _ => relate::super_relate_tys(self, t, t), - }; + }?; - self.cache.insert(t, result.clone()); - return result; + self.cache.insert(t, result); + Ok(result) } fn regions( @@ -694,7 +680,6 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { ty::RePlaceholder(..) | ty::ReVar(..) - | ty::ReEmpty(_) | ty::ReStatic | ty::ReEarlyBound(..) | ty::ReFree(..) => { @@ -749,10 +734,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } } - ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) - if self.tcx().lazy_normalization() => - { - assert_eq!(promoted, None); + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => { let substs = self.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), @@ -761,7 +743,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { )?; Ok(self.tcx().mk_const(ty::ConstS { ty: c.ty(), - kind: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), + kind: ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }), })) } _ => relate::super_relate_consts(self, c, c), @@ -776,13 +758,6 @@ pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); } -pub fn const_unification_error<'tcx>( - a_is_expected: bool, - (a, b): (ty::Const<'tcx>, ty::Const<'tcx>), -) -> TypeError<'tcx> { - TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a, b)) -} - fn int_unification_error<'tcx>( a_is_expected: bool, v: (ty::IntVarValue, ty::IntVarValue), @@ -800,7 +775,7 @@ fn float_unification_error<'tcx>( } struct ConstInferUnifier<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, span: Span, @@ -856,10 +831,9 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { debug_assert_eq!(t, _t); - debug!("ConstInferUnifier: t={:?}", t); match t.kind() { &ty::Infer(ty::TyVar(vid)) => { @@ -883,12 +857,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { .borrow_mut() .type_variables() .new_var(self.for_universe, origin); - let u = self.tcx().mk_ty_var(new_var_id); - debug!( - "ConstInferUnifier: replacing original vid={:?} with new={:?}", - vid, u - ); - Ok(u) + Ok(self.tcx().mk_ty_var(new_var_id)) } } } @@ -914,7 +883,6 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { ty::RePlaceholder(..) | ty::ReVar(..) - | ty::ReEmpty(_) | ty::ReStatic | ty::ReEarlyBound(..) | ty::ReFree(..) => { @@ -932,14 +900,13 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn consts( &mut self, c: ty::Const<'tcx>, _c: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug_assert_eq!(c, _c); - debug!("ConstInferUnifier: c={:?}", c); match c.kind() { ty::ConstKind::Infer(InferConst::Var(vid)) => { @@ -980,19 +947,17 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } } - ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) - if self.tcx().lazy_normalization() => - { - assert_eq!(promoted, None); + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => { let substs = self.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), substs, substs, )?; + Ok(self.tcx().mk_const(ty::ConstS { ty: c.ty(), - kind: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), + kind: ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }), })) } _ => relate::super_relate_consts(self, c, c), diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 3b1798ca7..59728148a 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -110,6 +110,25 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { .obligations, ); } + // Optimization of GeneratorWitness relation since we know that all + // free regions are replaced with bound regions during construction. + // This greatly speeds up equating of GeneratorWitness. + (&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => { + let a_types = infcx.tcx.anonymize_bound_vars(a_types); + let b_types = infcx.tcx.anonymize_bound_vars(b_types); + if a_types.bound_vars() == b_types.bound_vars() { + let (a_types, b_types) = infcx.replace_bound_vars_with_placeholders( + a_types.map_bound(|a_types| (a_types, b_types.skip_binder())), + ); + for (a, b) in std::iter::zip(a_types, b_types) { + self.relate(a, b)?; + } + } else { + return Err(ty::error::TypeError::Sorts(ty::relate::expected_found( + self, a, b, + ))); + } + } _ => { self.fields.infcx.super_combine_tys(self, a, b)?; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 20864c657..9ff703e52 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -51,6 +51,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; +use crate::infer::ExpectedFound; use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -58,30 +59,57 @@ use crate::traits::{ }; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::Node; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::{ - self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, + self, error::TypeError, Binder, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, }; use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; -use std::ops::ControlFlow; +use std::ops::{ControlFlow, Deref}; use std::{cmp, fmt, iter}; mod note; -mod need_type_info; +pub(crate) mod need_type_info; pub use need_type_info::TypeAnnotationNeeded; pub mod nice_region_error; +/// A helper for building type related errors. The `typeck_results` +/// field is only populated during an in-progress typeck. +/// Get an instance by calling `InferCtxt::err` or `FnCtxt::infer_err`. +pub struct TypeErrCtxt<'a, 'tcx> { + pub infcx: &'a InferCtxt<'tcx>, + pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>, +} + +impl TypeErrCtxt<'_, '_> { + /// This is just to avoid a potential footgun of accidentally + /// dropping `typeck_results` by calling `InferCtxt::err_ctxt` + #[deprecated(note = "you already have a `TypeErrCtxt`")] + #[allow(unused)] + pub fn err_ctxt(&self) -> ! { + bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call"); + } +} + +impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { + type Target = InferCtxt<'tcx>; + fn deref(&self) -> &InferCtxt<'tcx> { + &self.infcx + } +} + pub(super) fn note_and_explain_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut Diagnostic, @@ -95,11 +123,6 @@ pub(super) fn note_and_explain_region<'tcx>( msg_span_from_free_region(tcx, region, alt_span) } - ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), alt_span), - - // uh oh, hope no user ever sees THIS - ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), alt_span), - ty::RePlaceholder(_) => return, // FIXME(#13998) RePlaceholder should probably print like @@ -138,8 +161,6 @@ fn msg_span_from_free_region<'tcx>( (msg, Some(span)) } ty::ReStatic => ("the static lifetime".to_owned(), alt_span), - ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), alt_span), - ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), alt_span), _ => bug!("{:?}", region), } } @@ -249,17 +270,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( // Explain the region we are capturing. match *hidden_region { - ty::ReEmpty(ty::UniverseIndex::ROOT) => { - // All lifetimes shorter than the function body are `empty` in - // lexical region resolution. The default explanation of "an empty - // lifetime" isn't really accurate here. - let message = format!( - "hidden type `{}` captures lifetime smaller than the function body", - hidden_ty - ); - err.span_note(span, &message); - } - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) => { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { // Assuming regionck succeeded (*), we ought to always be // capturing *some* region from the fn header, and hence it // ought to be free. So under normal circumstances, we will go @@ -318,7 +329,38 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( err } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { + pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> { + if let ty::Opaque(def_id, substs) = ty.kind() { + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + // Future::Output + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; + + let bounds = self.tcx.bound_explicit_item_bounds(*def_id); + + for (predicate, _) in bounds.subst_iter_copied(self.tcx, substs) { + let output = predicate + .kind() + .map_bound(|kind| match kind { + ty::PredicateKind::Projection(projection_predicate) + if projection_predicate.projection_ty.item_def_id == item_def_id => + { + projection_predicate.term.ty() + } + _ => None, + }) + .transpose(); + if output.is_some() { + // We don't account for multiple `Future::Output = Ty` constraints. + return output; + } + } + } + None + } +} + +impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub fn report_region_errors( &self, generic_param_scope: LocalDefId, @@ -385,7 +427,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::UpperBoundUniverseConflict( _, _, - var_universe, + _, sup_origin, sup_r, ) => { @@ -396,7 +438,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // placeholder. In practice, we expect more // tailored errors that don't really use this // value. - let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe)); + let sub_r = self.tcx.lifetimes.re_erased; self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); } @@ -457,7 +499,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: &TypeError<'tcx>) { + fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: TypeError<'tcx>) { use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::print::Printer; @@ -561,7 +603,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } }; - match *terr { + match terr { TypeError::Sorts(ref exp_found) => { // if they are both "path types", there's a chance of ambiguity // due to different versions of the same crate @@ -583,7 +625,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err: &mut Diagnostic, cause: &ObligationCause<'tcx>, exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, - terr: &TypeError<'tcx>, + terr: TypeError<'tcx>, ) { match *cause.code() { ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { @@ -592,13 +634,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { // don't show type `_` if span.desugaring_kind() == Some(DesugaringKind::ForLoop) - && let ty::Adt(def, substs) = ty.kind() - && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) + && let ty::Adt(def, substs) = ty.kind() + && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) { err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0))); } else { - err.span_label(span, format!("this expression has type `{}`", ty)); - } + err.span_label(span, format!("this expression has type `{}`", ty)); + } } if let Some(ty::error::ExpectedFound { found, .. }) = exp_found && ty.is_box() && ty.boxed_ty() == found @@ -634,8 +676,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id); let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind { let arg_expr = args.first().expect("try desugaring call w/out arg"); - self.in_progress_typeck_results.and_then(|typeck_results| { - typeck_results.borrow().expr_ty_opt(arg_expr) + self.typeck_results.as_ref().and_then(|typeck_results| { + typeck_results.expr_ty_opt(arg_expr) }) } else { bug!("try desugaring w/out call expr as scrutinee"); @@ -739,12 +781,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.help("...or use `match` instead of `let...else`"); } _ => { - if let ObligationCauseCode::BindingObligation(_, binding_span) = - cause.code().peel_derives() + if let ObligationCauseCode::BindingObligation(_, span) + | ObligationCauseCode::ExprBindingObligation(_, span, ..) + = cause.code().peel_derives() + && let TypeError::RegionsPlaceholderMismatch = terr { - if matches!(terr, TypeError::RegionsPlaceholderMismatch) { - err.span_note(*binding_span, "the lifetime requirement is introduced here"); - } + err.span_note( * span, + "the lifetime requirement is introduced here"); } } } @@ -960,12 +1003,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn normalize_fn_sig_for_diagnostic(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> { + if let Some(normalize) = &self.normalize_fn_sig_for_diagnostic { + normalize(self, sig) + } else { + sig + } + } + /// Given two `fn` signatures highlight only sub-parts that are different. fn cmp_fn_sig( &self, sig1: &ty::PolyFnSig<'tcx>, sig2: &ty::PolyFnSig<'tcx>, ) -> (DiagnosticStyledString, DiagnosticStyledString) { + let sig1 = &self.normalize_fn_sig_for_diagnostic(*sig1); + let sig2 = &self.normalize_fn_sig_for_diagnostic(*sig2); + let get_lifetimes = |sig| { use rustc_hir::def::Namespace; let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) @@ -1422,9 +1476,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the message in `secondary_span` as the primary label, and apply the message that would /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on /// E0271, like `src/test/ui/issues/issue-39970.stderr`. - #[tracing::instrument( + #[instrument( level = "debug", - skip(self, diag, secondary_span, swap_secondary_and_primary, force_label) + skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label) )] pub fn note_type_err( &self, @@ -1432,9 +1486,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, secondary_span: Option<(Span, String)>, mut values: Option<ValuePairs<'tcx>>, - terr: &TypeError<'tcx>, + terr: TypeError<'tcx>, swap_secondary_and_primary: bool, - force_label: bool, + prefer_label: bool, ) { let span = cause.span(); @@ -1574,23 +1628,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Some(values) => { let values = self.resolve_vars_if_possible(values); let (is_simple_error, exp_found) = match values { - ValuePairs::Terms(infer::ExpectedFound { - expected: ty::Term::Ty(expected), - found: ty::Term::Ty(found), - }) => { - let is_simple_err = expected.is_simple_text() && found.is_simple_text(); - OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) - .report(diag); - - ( - is_simple_err, - Mismatch::Variable(infer::ExpectedFound { expected, found }), - ) + ValuePairs::Terms(infer::ExpectedFound { expected, found }) => { + match (expected.unpack(), found.unpack()) { + (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { + let is_simple_err = + expected.is_simple_text() && found.is_simple_text(); + OpaqueTypesVisitor::visit_expected_found( + self.tcx, expected, found, span, + ) + .report(diag); + + ( + is_simple_err, + Mismatch::Variable(infer::ExpectedFound { expected, found }), + ) + } + (ty::TermKind::Const(_), ty::TermKind::Const(_)) => { + (false, Mismatch::Fixed("constant")) + } + _ => (false, Mismatch::Fixed("type")), + } } ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => { (false, Mismatch::Fixed("trait")) } - _ => (false, Mismatch::Fixed("type")), + ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")), }; let vals = match self.values_str(values) { Some((expected, found)) => Some((expected, found)), @@ -1612,7 +1674,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { TypeError::ObjectUnsafeCoercion(_) => {} _ => { let mut label_or_note = |span: Span, msg: &str| { - if force_label || &[span] == diag.span.primary_spans() { + if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() { diag.span_label(span, msg); } else { diag.span_note(span, msg); @@ -1649,8 +1711,114 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ), Mismatch::Fixed(s) => (s.into(), s.into(), None), }; - match (&terr, expected == found) { - (TypeError::Sorts(values), extra) => { + + enum Similar<'tcx> { + Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> }, + PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> }, + PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> }, + } + + let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| { + if let ty::Adt(expected, _) = expected.kind() && let Some(primitive) = found.primitive_symbol() { + let path = self.tcx.def_path(expected.did()).data; + let name = path.last().unwrap().data.get_opt_name(); + if name == Some(primitive) { + return Some(Similar::PrimitiveFound { expected: *expected, found }); + } + } else if let Some(primitive) = expected.primitive_symbol() && let ty::Adt(found, _) = found.kind() { + let path = self.tcx.def_path(found.did()).data; + let name = path.last().unwrap().data.get_opt_name(); + if name == Some(primitive) { + return Some(Similar::PrimitiveExpected { expected, found: *found }); + } + } else if let ty::Adt(expected, _) = expected.kind() && let ty::Adt(found, _) = found.kind() { + if !expected.did().is_local() && expected.did().krate == found.did().krate { + // Most likely types from different versions of the same crate + // are in play, in which case this message isn't so helpful. + // A "perhaps two different versions..." error is already emitted for that. + return None; + } + let f_path = self.tcx.def_path(found.did()).data; + let e_path = self.tcx.def_path(expected.did()).data; + + if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last()) && e_last == f_last { + return Some(Similar::Adts{expected: *expected, found: *found}); + } + } + None + }; + + match terr { + // If two types mismatch but have similar names, mention that specifically. + TypeError::Sorts(values) if let Some(s) = similarity(values) => { + let diagnose_primitive = + |prim: Ty<'tcx>, + shadow: Ty<'tcx>, + defid: DefId, + diagnostic: &mut Diagnostic| { + let name = shadow.sort_string(self.tcx); + diagnostic.note(format!( + "{prim} and {name} have similar names, but are actually distinct types" + )); + diagnostic + .note(format!("{prim} is a primitive defined by the language")); + let def_span = self.tcx.def_span(defid); + let msg = if defid.is_local() { + format!("{name} is defined in the current crate") + } else { + let crate_name = self.tcx.crate_name(defid.krate); + format!("{name} is defined in crate `{crate_name}") + }; + diagnostic.span_note(def_span, msg); + }; + + let diagnose_adts = + |expected_adt : ty::AdtDef<'tcx>, + found_adt: ty::AdtDef<'tcx>, + diagnostic: &mut Diagnostic| { + let found_name = values.found.sort_string(self.tcx); + let expected_name = values.expected.sort_string(self.tcx); + + let found_defid = found_adt.did(); + let expected_defid = expected_adt.did(); + + diagnostic.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types")); + for (defid, name) in + [(found_defid, found_name), (expected_defid, expected_name)] + { + let def_span = self.tcx.def_span(defid); + + let msg = if found_defid.is_local() && expected_defid.is_local() { + let module = self + .tcx + .parent_module_from_def_id(defid.expect_local()) + .to_def_id(); + let module_name = self.tcx.def_path(module).to_string_no_crate_verbose(); + format!("{name} is defined in module `crate{module_name}` of the current crate") + } else if defid.is_local() { + format!("{name} is defined in the current crate") + } else { + let crate_name = self.tcx.crate_name(defid.krate); + format!("{name} is defined in crate `{crate_name}`") + }; + diagnostic.span_note(def_span, msg); + } + }; + + match s { + Similar::Adts{expected, found} => { + diagnose_adts(expected, found, diag) + } + Similar::PrimitiveFound{expected, found: prim} => { + diagnose_primitive(prim, values.expected, expected.did(), diag) + } + Similar::PrimitiveExpected{expected: prim, found} => { + diagnose_primitive(prim, values.found, found.did(), diag) + } + } + } + TypeError::Sorts(values) => { + let extra = expected == found; let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { (true, ty::Opaque(def_id, _)) => { let sm = self.tcx.sess.source_map(); @@ -1662,6 +1830,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pos.col.to_usize() + 1, ) } + (true, ty::Projection(proj)) + if self.tcx.def_kind(proj.item_def_id) + == DefKind::ImplTraitPlaceholder => + { + let sm = self.tcx.sess.source_map(); + let pos = sm.lookup_char_pos(self.tcx.def_span(proj.item_def_id).lo()); + 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(), }; @@ -1690,10 +1871,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } } - (TypeError::ObjectUnsafeCoercion(_), _) => { + TypeError::ObjectUnsafeCoercion(_) => { diag.note_unsuccessful_coercion(found, expected); } - (_, _) => { + _ => { debug!( "note_type_err: exp_found={:?}, expected={:?} found={:?}", exp_found, expected, found @@ -1713,7 +1894,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty::error::TypeError::Sorts(terr) if exp_found.map_or(false, |ef| terr.found == ef.found) => { - Some(*terr) + Some(terr) } _ => exp_found, }; @@ -1738,7 +1919,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // In some (most?) cases cause.body_id points to actual body, but in some cases // it's an actual definition. According to the comments (e.g. in - // librustc_typeck/check/compare_method.rs:compare_predicate_entailment) the latter + // rustc_hir_analysis/check/compare_method.rs:compare_predicate_entailment) the latter // is relied upon by some other code. This might (or might not) need cleanup. let body_owner_def_id = self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| { @@ -1750,6 +1931,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() && let Some(def_id) = def_id.as_local() + && terr.involves_regions() { let span = self.tcx.def_span(def_id); diag.span_note(span, "this closure does not fulfill the lifetime requirements"); @@ -1829,36 +2011,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> { - if let ty::Opaque(def_id, substs) = ty.kind() { - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - // Future::Output - let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - - let bounds = self.tcx.bound_explicit_item_bounds(*def_id); - - for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { - let predicate = predicate.subst(self.tcx, substs); - let output = predicate - .kind() - .map_bound(|kind| match kind { - ty::PredicateKind::Projection(projection_predicate) - if projection_predicate.projection_ty.item_def_id == item_def_id => - { - projection_predicate.term.ty() - } - _ => None, - }) - .transpose(); - if output.is_some() { - // We don't account for multiple `Future::Output = Ty` constraints. - return output; - } - } - } - None - } - /// A possible error is to forget to add `.await` when using futures: /// /// ```compile_fail,E0308 @@ -2033,26 +2185,40 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut Diagnostic, ) { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found) + { + diag.span_suggestion( + span, + msg, + // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&` + format!("{}.as_ref()", snippet.trim_start_matches('&')), + Applicability::MachineApplicable, + ); + } + } + + pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> { if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) = - (exp_found.expected.kind(), exp_found.found.kind()) + (expected.kind(), found.kind()) { if let ty::Adt(found_def, found_substs) = *found_ty.kind() { - let path_str = format!("{:?}", exp_def); if exp_def == &found_def { - let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \ - `.as_ref()`"; - let result_msg = "you can convert from `&Result<T, E>` to \ - `Result<&T, &E>` using `.as_ref()`"; let have_as_ref = &[ - ("std::option::Option", opt_msg), - ("core::option::Option", opt_msg), - ("std::result::Result", result_msg), - ("core::result::Result", result_msg), + ( + sym::Option, + "you can convert from `&Option<T>` to `Option<&T>` using \ + `.as_ref()`", + ), + ( + sym::Result, + "you can convert from `&Result<T, E>` to \ + `Result<&T, &E>` using `.as_ref()`", + ), ]; - if let Some(msg) = have_as_ref - .iter() - .find_map(|(path, msg)| (&path_str == path).then_some(msg)) - { + if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { + self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) + }) { let mut show_suggestion = true; for (exp_ty, found_ty) in iter::zip(exp_substs.types(), found_substs.types()) @@ -2072,26 +2238,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => show_suggestion = false, } } - if let (Ok(snippet), true) = - (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion) - { - diag.span_suggestion( - span, - *msg, - format!("{}.as_ref()", snippet), - Applicability::MachineApplicable, - ); + if show_suggestion { + return Some(*msg); } } } } } + None } pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, - terr: &TypeError<'tcx>, + terr: TypeError<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { use crate::traits::ObligationCauseCode::MatchExpressionArm; @@ -2111,6 +2271,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str) } FailureCode::Error0308(failure_str) => { + fn escape_literal(s: &str) -> String { + let mut escaped = String::with_capacity(s.len()); + let mut chrs = s.chars().peekable(); + while let Some(first) = chrs.next() { + match (first, chrs.peek()) { + ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => { + escaped.push('\\'); + escaped.push(delim); + chrs.next(); + } + ('"' | '\'', _) => { + escaped.push('\\'); + escaped.push(first) + } + (c, _) => escaped.push(c), + }; + } + escaped + } let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str); if let Some((expected, found)) = trace.values.ty() { match (expected.kind(), found.kind()) { @@ -2132,7 +2311,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_suggestion( span, "if you meant to write a `char` literal, use single quotes", - format!("'{}'", code), + format!("'{}'", escape_literal(code)), Applicability::MachineApplicable, ); } @@ -2147,7 +2326,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_suggestion( span, "if you meant to write a `str` literal, use double quotes", - format!("\"{}\"", code), + format!("\"{}\"", escape_literal(code)), Applicability::MachineApplicable, ); } @@ -2254,11 +2433,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return None; } - Some(match (exp_found.expected, exp_found.found) { - (ty::Term::Ty(expected), ty::Term::Ty(found)) => self.cmp(expected, found), - (expected, found) => ( - DiagnosticStyledString::highlighted(expected.to_string()), - DiagnosticStyledString::highlighted(found.to_string()), + Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) { + (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => self.cmp(expected, found), + _ => ( + DiagnosticStyledString::highlighted(exp_found.expected.to_string()), + DiagnosticStyledString::highlighted(exp_found.found.to_string()), ), }) } @@ -2298,7 +2477,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: Option<SubregionOrigin<'tcx>>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. let generics = self.tcx.generics_of(generic_param_scope); @@ -2314,7 +2493,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // We do this to avoid suggesting code that ends up as `T: 'a'b`, // instead we suggest `T: 'a + 'b` in that case. let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let ast_generics = self.tcx.hir().get_generics(hir_id.owner); + let ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id); let bounds = ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id)); // `sp` only covers `T`, change it so that it covers @@ -2355,6 +2534,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), GenericKind::Projection(ref p) => format!("the associated type `{}`", p), + GenericKind::Opaque(def_id, substs) => { + format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs)) + } }; if let Some(SubregionOrigin::CompareImplItemObligation { @@ -2376,19 +2558,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { type_param_span: Option<(Span, bool)>, bound_kind: GenericKind<'tcx>, sub: S, + add_lt_sugg: Option<(Span, String)>, ) { let msg = "consider adding an explicit lifetime bound"; if let Some((sp, has_lifetimes)) = type_param_span { let suggestion = if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) }; - err.span_suggestion_verbose( - sp, - &format!("{}...", msg), - suggestion, + let mut suggestions = vec![(sp, suggestion)]; + if let Some(add_lt_sugg) = add_lt_sugg { + suggestions.push(add_lt_sugg); + } + err.multipart_suggestion_verbose( + format!("{msg}..."), + suggestions, Applicability::MaybeIncorrect, // Issue #41966 ); } else { - let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,); + let consider = format!("{} `{}: {}`...", msg, bound_kind, sub); err.help(&consider); } } @@ -2404,7 +2590,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let mut sugg = vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))]; - if let Some(lt) = add_lt_sugg { + if let Some(lt) = add_lt_sugg.clone() { sugg.push(lt); sugg.rotate_right(1); } @@ -2430,7 +2616,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for h in self.tcx.hir().parent_iter(param.hir_id) { break 'origin match h.1 { Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::TyAlias(..), + kind: hir::ImplItemKind::Type(..), generics, .. }) @@ -2510,7 +2696,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // for the bound is not suitable for suggestions when `-Zverbose` is set because it // uses `Debug` output, so we handle it specially here so that suggestions are // always correct. - binding_suggestion(&mut err, type_param_span, bound_kind, name); + binding_suggestion(&mut err, type_param_span, bound_kind, name, None); err } @@ -2523,7 +2709,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "{} may not live long enough", labeled_user_string ); - binding_suggestion(&mut err, type_param_span, bound_kind, "'static"); + binding_suggestion(&mut err, type_param_span, bound_kind, "'static", None); err } @@ -2557,7 +2743,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { new_binding_suggestion(&mut err, type_param_span); } _ => { - binding_suggestion(&mut err, type_param_span, bound_kind, new_lt); + binding_suggestion( + &mut err, + type_param_span, + bound_kind, + new_lt, + add_lt_sugg, + ); } } } @@ -2659,70 +2851,98 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Float types, respectively). When comparing two ADTs, these rules apply recursively. pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { let (a, b) = self.resolve_vars_if_possible((a, b)); - match (a.kind(), b.kind()) { - (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { - if def_a != def_b { - return false; - } + SameTypeModuloInfer(self).relate(a, b).is_ok() + } +} - substs_a - .types() - .zip(substs_b.types()) - .all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => { - if did_a != did_b { - return false; - } +struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>); - substs_a - .types() - .zip(substs_b.types()) - .all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_))) +impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.0.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + // Unused, only for consts which we treat as always equal + ty::ParamEnv::empty() + } + + fn tag(&self) -> &'static str { + "SameTypeModuloInfer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance<T: relate::Relate<'tcx>>( + &mut self, + _variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> relate::RelateResult<'tcx, T> { + self.relate(a, b) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + match (a.kind(), b.kind()) { + (ty::Int(_) | ty::Uint(_), ty::Infer(ty::InferTy::IntVar(_))) | ( - &ty::Infer(ty::InferTy::IntVar(_)), - &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ty::Infer(ty::InferTy::IntVar(_)), + ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)), ) - | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | (ty::Float(_), ty::Infer(ty::InferTy::FloatVar(_))) | ( - &ty::Infer(ty::InferTy::FloatVar(_)), - &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ty::Infer(ty::InferTy::FloatVar(_)), + ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)), ) - | (&ty::Infer(ty::InferTy::TyVar(_)), _) - | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, - (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { - mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b) - } - (&ty::RawPtr(a), &ty::RawPtr(b)) => { - a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty) - } - (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b), - (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => { - self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct - } - (&ty::Tuple(a), &ty::Tuple(b)) => { - if a.len() != b.len() { - return false; - } - std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::FnPtr(a), &ty::FnPtr(b)) => { - let a = a.skip_binder().inputs_and_output; - let b = b.skip_binder().inputs_and_output; - if a.len() != b.len() { - return false; - } - std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - // FIXME(compiler-errors): This needs to be generalized more - _ => a == b, + | (ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, ty::Infer(ty::InferTy::TyVar(_))) => Ok(a), + (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch), + _ => relate::super_relate_tys(self, a, b), } } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + if (a.is_var() && b.is_free_or_static()) + || (b.is_var() && a.is_free_or_static()) + || (a.is_var() && b.is_var()) + || a == b + { + Ok(a) + } else { + Err(TypeError::Mismatch) + } + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: relate::Relate<'tcx>, + { + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + _b: ty::Const<'tcx>, + ) -> relate::RelateResult<'tcx, ty::Const<'tcx>> { + // FIXME(compiler-errors): This could at least do some first-order + // relation + Ok(a) + } } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { fn report_inference_failure( &self, var_origin: RegionVariableOrigin, @@ -2781,12 +3001,12 @@ pub enum FailureCode { } pub trait ObligationCauseExt<'tcx> { - fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode; + fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode; fn as_requirement_str(&self) -> &'static str; } impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { - fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode { + fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode { use self::FailureCode::*; use crate::traits::ObligationCauseCode::*; match self.code() { @@ -2823,7 +3043,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { TypeError::IntrinsicCast => { Error0308("cannot coerce intrinsics to function pointers") } - TypeError::ObjectUnsafeCoercion(did) => Error0038(*did), + TypeError::ObjectUnsafeCoercion(did) => Error0038(did), _ => Error0308("mismatched types"), }, } @@ -2853,6 +3073,30 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { } } +/// Newtype to allow implementing IntoDiagnosticArg +pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>); + +impl IntoDiagnosticArg for ObligationCauseAsDiagArg<'_> { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + use crate::traits::ObligationCauseCode::*; + let kind = match self.0.code() { + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => "method_compat", + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => "type_compat", + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => "const_compat", + ExprAssignable => "expr_assignable", + IfExpression { .. } => "if_else_different", + IfExpressionWithNoElse => "no_else", + MainFunctionType => "fn_main_correct_type", + StartFunctionType => "fn_start_correct_type", + IntrinsicType => "intristic_correct_type", + MethodReceiver => "method_correct_type", + _ => "other", + } + .into(); + rustc_errors::DiagnosticArgValue::Str(kind) + } +} + /// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks /// extra information about each type, but we only care about the category. #[derive(Clone, Copy, PartialEq, Eq, Hash)] @@ -2886,7 +3130,7 @@ impl TyCategory { } } -impl<'tcx> InferCtxt<'_, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Given a [`hir::Block`], get the span of its last expression or /// statement, peeling off any inner blocks. pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { @@ -2913,7 +3157,9 @@ impl<'tcx> InferCtxt<'_, 'tcx> { _ => rustc_span::DUMMY_SP, } } +} +impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// Be helpful when the user wrote `{... expr; }` and taking the `;` off /// is enough to fix the error. pub fn could_remove_semicolon( @@ -2930,7 +3176,7 @@ impl<'tcx> InferCtxt<'_, 'tcx> { let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { return None; }; - let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?; + let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?; let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { _ if last_expr_ty.references_error() => return None, _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => { @@ -3013,8 +3259,9 @@ impl<'tcx> InferCtxt<'_, 'tcx> { let mut find_compatible_candidates = |pat: &hir::Pat<'_>| { if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind && let Some(pat_ty) = self - .in_progress_typeck_results - .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id)) + .typeck_results + .as_ref() + .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id)) { let pat_ty = self.resolve_vars_if_possible(pat_ty); if self.same_type_modulo_infer(pat_ty, expected_ty) diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 561d1354e..7b3178e61 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -1,6 +1,12 @@ +use crate::errors::{ + AmbigousImpl, AmbigousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator, + SourceKindMultiSuggestion, SourceKindSubdiag, +}; +use crate::infer::error_reporting::TypeErrCtxt; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::InferCtxt; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::IntoDiagnostic; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; @@ -11,8 +17,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, InferConst}; +use rustc_middle::ty::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; use rustc_span::symbol::{kw, Ident}; use rustc_span::{BytePos, Span}; @@ -60,38 +66,49 @@ pub struct InferenceDiagnosticsParentData { name: String, } +#[derive(Clone)] pub enum UnderspecifiedArgKind { Type { prefix: Cow<'static, str> }, Const { is_parameter: bool }, } impl InferenceDiagnosticsData { - /// Generate a label for a generic argument which can't be inferred. When not - /// much is known about the argument, `use_diag` may be used to describe the - /// labeled value. - fn cannot_infer_msg(&self) -> String { - if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) { - return "cannot infer type".to_string(); - } - - let suffix = match &self.parent { - Some(parent) => parent.suffix_string(), - None => String::new(), - }; - - // For example: "cannot infer type for type parameter `T`" - format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix) + fn can_add_more_info(&self) -> bool { + !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. })) } - fn where_x_is_specified(&self, in_type: Ty<'_>) -> String { + fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str { if in_type.is_ty_infer() { - String::new() + "empty" } else if self.name == "_" { // FIXME: Consider specializing this message if there is a single `_` // in the type. - ", where the placeholders `_` are specified".to_string() + "underscore" } else { - format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name) + "has_name" + } + } + + /// Generate a label for a generic argument which can't be inferred. When not + /// much is known about the argument, `use_diag` may be used to describe the + /// labeled value. + fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> { + let has_parent = self.parent.is_some(); + let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" }; + let (parent_prefix, parent_name) = self + .parent + .as_ref() + .map(|parent| (parent.prefix, parent.name.clone())) + .unwrap_or_default(); + InferenceBadError { + span, + bad_kind, + prefix_kind: self.kind.clone(), + prefix: self.kind.try_get_prefix().unwrap_or_default(), + name: self.name.clone(), + has_parent, + parent_prefix, + parent_name, } } } @@ -113,23 +130,29 @@ impl InferenceDiagnosticsParentData { fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> { Self::for_parent_def_id(tcx, tcx.parent(def_id)) } +} - fn suffix_string(&self) -> String { - format!(" declared on the {} `{}`", self.prefix, self.name) +impl IntoDiagnosticArg for UnderspecifiedArgKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self { + Self::Type { .. } => "type", + Self::Const { is_parameter: true } => "const_with_param", + Self::Const { is_parameter: false } => "const", + }; + rustc_errors::DiagnosticArgValue::Str(kind.into()) } } impl UnderspecifiedArgKind { - fn prefix_string(&self) -> Cow<'static, str> { + fn try_get_prefix(&self) -> Option<&str> { match self { - Self::Type { prefix } => format!("type for {}", prefix).into(), - Self::Const { is_parameter: true } => "the value of const parameter".into(), - Self::Const { is_parameter: false } => "the value of the constant".into(), + Self::Type { prefix } => Some(prefix.as_ref()), + Self::Const { .. } => None, } } } -fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { +fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { let mut printer = FmtPrinter::new(infcx.tcx, ns); let ty_getter = move |ty_vid| { if infcx.probe_ty_var(ty_vid).is_ok() { @@ -160,7 +183,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr printer } -fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { +fn ty_to_string<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String { let printer = fmt_printer(infcx, Namespace::TypeNS); let ty = infcx.resolve_vars_if_possible(ty); match ty.kind() { @@ -177,9 +200,9 @@ fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { } /// We don't want to directly use `ty_to_string` for closures as their type isn't really -/// something users are familar with. Directly printing the `fn_sig` of closures also +/// something users are familiar with. Directly printing the `fn_sig` of closures also /// doesn't work as they actually use the "rust-call" API. -fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { +fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String { let ty::Closure(_, substs) = ty.kind() else { unreachable!() }; let fn_sig = substs.as_closure().sig(); let args = fn_sig @@ -203,7 +226,7 @@ fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String format!("fn({}){}", args, ret) } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Extracts data used by diagnostic for either types or constants /// which were stuck during inference. pub fn extract_inference_diagnostics_data( @@ -295,7 +318,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - /// Used as a fallback in [InferCtxt::emit_inference_failure_err] + /// Used as a fallback in [TypeErrCtxt::emit_inference_failure_err] /// in case we weren't able to get a better error. fn bad_inference_failure_err( &self, @@ -303,13 +326,48 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { arg_data: InferenceDiagnosticsData, error_code: TypeAnnotationNeeded, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let error_code = error_code.into(); - let mut err = - self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code); - err.span_label(span, arg_data.cannot_infer_msg()); - err + let source_kind = "other"; + let source_name = ""; + let failure_span = None; + let infer_subdiags = Vec::new(); + let multi_suggestions = Vec::new(); + let bad_label = Some(arg_data.make_bad_error(span)); + match error_code { + TypeAnnotationNeeded::E0282 => AnnotationRequired { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0283 => AmbigousImpl { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0284 => AmbigousReturn { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + } } +} +impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub fn emit_inference_failure_err( &self, body_id: Option<hir::BodyId>, @@ -321,14 +379,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let arg = self.resolve_vars_if_possible(arg); let arg_data = self.extract_inference_diagnostics_data(arg, None); - let Some(typeck_results) = self.in_progress_typeck_results else { + let Some(typeck_results) = &self.typeck_results else { // If we don't have any typeck results we're outside // of a body, so we won't be able to get better info // here. return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let typeck_results = typeck_results.borrow(); - let typeck_results = &typeck_results; let mut local_visitor = FindInferSourceVisitor::new(&self, typeck_results, arg); if let Some(body_id) = body_id { @@ -340,48 +396,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code) }; - let error_code = error_code.into(); - let mut err = self.tcx.sess.struct_span_err_with_code( - span, - &format!("type annotations needed{}", kind.ty_msg(self)), - error_code, - ); - - if should_label_span && !failure_span.overlaps(span) { - err.span_label(failure_span, "type must be known at this point"); - } + let (source_kind, name) = kind.ty_localized_msg(self); + let failure_span = if should_label_span && !failure_span.overlaps(span) { + Some(failure_span) + } else { + None + }; + let mut infer_subdiags = Vec::new(); + let mut multi_suggestions = Vec::new(); match kind { InferSourceKind::LetBinding { insert_span, pattern_name, ty } => { - let suggestion_msg = if let Some(name) = pattern_name { - format!( - "consider giving `{}` an explicit type{}", - name, - arg_data.where_x_is_specified(ty) - ) - } else { - format!( - "consider giving this pattern a type{}", - arg_data.where_x_is_specified(ty) - ) - }; - err.span_suggestion_verbose( - insert_span, - &suggestion_msg, - format!(": {}", ty_to_string(self, ty)), - Applicability::HasPlaceholders, - ); + infer_subdiags.push(SourceKindSubdiag::LetLike { + span: insert_span, + name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new), + x_kind: arg_data.where_x_is_kind(ty), + prefix_kind: arg_data.kind.clone(), + prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), + arg_name: arg_data.name, + kind: if pattern_name.is_some() { "with_pattern" } else { "other" }, + type_name: ty_to_string(self, ty), + }); } InferSourceKind::ClosureArg { insert_span, ty } => { - err.span_suggestion_verbose( - insert_span, - &format!( - "consider giving this closure parameter an explicit type{}", - arg_data.where_x_is_specified(ty) - ), - format!(": {}", ty_to_string(self, ty)), - Applicability::HasPlaceholders, - ); + infer_subdiags.push(SourceKindSubdiag::LetLike { + span: insert_span, + name: String::new(), + x_kind: arg_data.where_x_is_kind(ty), + prefix_kind: arg_data.kind.clone(), + prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), + arg_name: arg_data.name, + kind: "closure", + type_name: ty_to_string(self, ty), + }); } InferSourceKind::GenericArg { insert_span, @@ -389,23 +436,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { generics_def_id, def_id: _, generic_args, + have_turbofish, } => { let generics = self.tcx.generics_of(generics_def_id); let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); - let cannot_infer_msg = format!( - "cannot infer {} of the {} parameter `{}`{}", - if is_type { "type" } else { "the value" }, - if is_type { "type" } else { "const" }, - generics.params[argument_index].name, - // We use the `generics_def_id` here, as even when suggesting `None::<T>`, - // the type parameter `T` was still declared on the enum, not on the - // variant. + let (parent_exists, parent_prefix, parent_name) = InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) - .map_or(String::new(), |parent| parent.suffix_string()), - ); + .map_or((false, String::new(), String::new()), |parent| { + (true, parent.prefix.to_string(), parent.name) + }); - err.span_label(span, cannot_infer_msg); + infer_subdiags.push(SourceKindSubdiag::GenericLabel { + span, + is_type, + param_name: generics.params[argument_index].name.to_string(), + parent_exists, + parent_prefix, + parent_name, + }); let args = fmt_printer(self, Namespace::TypeNS) .comma_sep(generic_args.iter().copied().map(|arg| { @@ -435,15 +484,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .unwrap() .into_buffer(); - err.span_suggestion_verbose( - insert_span, - &format!( - "consider specifying the generic argument{}", - pluralize!(generic_args.len()), - ), - format!("::<{}>", args), - Applicability::HasPlaceholders, - ); + if !have_turbofish { + infer_subdiags.push(SourceKindSubdiag::GenericSuggestion { + span: insert_span, + arg_count: generic_args.len(), + args, + }); + } } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => { let printer = fmt_printer(self, Namespace::ValueNS); @@ -468,39 +515,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => "", }; - let suggestion = vec![ - (receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")), - (receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()), - ]; - err.multipart_suggestion_verbose( - "try using a fully qualified path to specify the expected types", - suggestion, - Applicability::HasPlaceholders, - ); + multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( + receiver.span, + def_path, + adjustment, + successor, + )); } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let ret = ty_to_string(self, ty); - let (arrow, post) = match data { - FnRetTy::DefaultReturn(_) => ("-> ", " "), - _ => ("", ""), - }; - let suggestion = match should_wrap_expr { - Some(end_span) => vec![ - (data.span(), format!("{}{}{}{{ ", arrow, ret, post)), - (end_span, " }".to_string()), - ], - None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))], - }; - err.multipart_suggestion_verbose( - "try giving this closure an explicit return type", - suggestion, - Applicability::HasPlaceholders, - ); + let ty_info = ty_to_string(self, ty); + multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( + ty_info, + data, + should_wrap_expr, + )); } } - err + match error_code { + TypeAnnotationNeeded::E0282 => AnnotationRequired { + span, + source_kind, + source_name: &name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label: None, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0283 => AmbigousImpl { + span, + source_kind, + source_name: &name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label: None, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0284 => AmbigousReturn { + span, + source_kind, + source_name: &name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label: None, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + } } +} +impl<'tcx> InferCtxt<'tcx> { pub fn need_type_info_err_in_generator( &self, kind: hir::GeneratorKind, @@ -510,15 +576,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty); let data = self.extract_inference_diagnostics_data(ty.into(), None); - let mut err = struct_span_err!( - self.tcx.sess, + NeedTypeInfoInGenerator { + bad_label: data.make_bad_error(span), span, - E0698, - "type inside {} must be known in this context", - kind, - ); - err.span_label(span, data.cannot_infer_msg()); - err + generator_kind: GeneratorKindAsDiagArg(kind), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) + } +} + +pub struct GeneratorKindAsDiagArg(pub hir::GeneratorKind); + +impl IntoDiagnosticArg for GeneratorKindAsDiagArg { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self.0 { + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "async_block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "async_closure", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "async_fn", + hir::GeneratorKind::Gen => "generator", + }; + rustc_errors::DiagnosticArgValue::Str(kind.into()) } } @@ -545,6 +622,7 @@ enum InferSourceKind<'tcx> { generics_def_id: DefId, def_id: DefId, generic_args: &'tcx [GenericArg<'tcx>], + have_turbofish: bool, }, FullyQualifiedMethodCall { receiver: &'tcx Expr<'tcx>, @@ -579,22 +657,22 @@ impl<'tcx> InferSource<'tcx> { } impl<'tcx> InferSourceKind<'tcx> { - fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String { + fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String) { match *self { InferSourceKind::LetBinding { ty, .. } | InferSourceKind::ClosureArg { ty, .. } | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { - format!(" for the closure `{}`", closure_as_fn_str(infcx, ty)) + ("closure", closure_as_fn_str(infcx, ty)) } else if !ty.is_ty_infer() { - format!(" for `{}`", ty_to_string(infcx, ty)) + ("normal", ty_to_string(infcx, ty)) } else { - String::new() + ("other", String::new()) } } // FIXME: We should be able to add some additional info here. InferSourceKind::GenericArg { .. } - | InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(), + | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new()), } } } @@ -605,6 +683,7 @@ struct InsertableGenericArgs<'tcx> { substs: SubstsRef<'tcx>, generics_def_id: DefId, def_id: DefId, + have_turbofish: bool, } /// A visitor which searches for the "best" spot to use in the inference error. @@ -615,7 +694,7 @@ struct InsertableGenericArgs<'tcx> { /// While doing so, the currently best spot is stored in `infer_source`. /// For details on how we rank spots, see [Self::source_cost] struct FindInferSourceVisitor<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, typeck_results: &'a TypeckResults<'tcx>, target: GenericArg<'tcx>, @@ -627,7 +706,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, typeck_results: &'a TypeckResults<'tcx>, target: GenericArg<'tcx>, ) -> Self { @@ -823,14 +902,21 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // impl is currently the `DefId` of `Output` in the trait definition // which makes this somewhat difficult and prevents us from just // using `self.path_inferred_subst_iter` here. - hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _) => { - if let Some(ty) = self.opt_node_type(expr.hir_id) { - if let ty::Adt(_, substs) = ty.kind() { - return Box::new(self.resolved_path_inferred_subst_iter(path, substs)); - } + hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _) + // FIXME(TaKO8Ki): Ideally we should support this. For that + // we have to map back from the self type to the + // type alias though. That's difficult. + // + // See the `need_type_info/issue-103053.rs` test for + // a example. + if !matches!(path.res, Res::Def(DefKind::TyAlias, _)) => { + if let Some(ty) = self.opt_node_type(expr.hir_id) + && let ty::Adt(_, substs) = ty.kind() + { + return Box::new(self.resolved_path_inferred_subst_iter(path, substs)); } } - hir::ExprKind::MethodCall(segment, _, _) => { + hir::ExprKind::MethodCall(segment, ..) => { if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) { let generics = tcx.generics_of(def_id); let insertable: Option<_> = try { @@ -838,13 +924,14 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { None? } let substs = self.node_substs_opt(expr.hir_id)?; - let span = tcx.hir().span(segment.hir_id?); + let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id, + have_turbofish: false, } }; return Box::new(insertable.into_iter()); @@ -862,6 +949,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { substs: SubstsRef<'tcx>, ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a { let tcx = self.infcx.tcx; + let have_turbofish = path.segments.iter().any(|segment| { + segment.args.map_or(false, |args| args.args.iter().any(|arg| arg.is_ty_or_const())) + }); // The last segment of a path often has `Res::Err` and the // correct `Res` is the one of the whole path. // @@ -871,7 +961,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { let generics_def_id = tcx.res_generics_def_id(path.res)?; let generics = tcx.generics_of(generics_def_id); if generics.has_impl_trait() { - None? + None?; } let insert_span = path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi()); @@ -880,25 +970,27 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { substs, generics_def_id, def_id: path.res.def_id(), + have_turbofish, } }; path.segments .iter() .filter_map(move |segment| { - let res = segment.res?; + let res = segment.res; let generics_def_id = tcx.res_generics_def_id(res)?; let generics = tcx.generics_of(generics_def_id); if generics.has_impl_trait() { return None; } - let span = tcx.hir().span(segment.hir_id?); + let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); Some(InsertableGenericArgs { insert_span, substs, generics_def_id, def_id: res.def_id(), + have_turbofish, }) }) .chain(last_segment_using_path_data) @@ -925,9 +1017,15 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { if !segment.infer_args || generics.has_impl_trait() { None?; } - let span = tcx.hir().span(segment.hir_id?); + let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); - InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id } + InsertableGenericArgs { + insert_span, + substs, + generics_def_id: def_id, + def_id, + have_turbofish: false, + } }; let parent_def_id = generics.parent.unwrap(); @@ -950,7 +1048,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } // There cannot be inference variables in the self type, // so there's nothing for us to do here. - Res::SelfTy { .. } => {} + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => {} _ => warn!( "unexpected path: def={:?} substs={:?} path={:?}", def, substs, path, @@ -1050,7 +1148,13 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { for args in self.expr_inferred_subst_iter(expr) { debug!(?args); - let InsertableGenericArgs { insert_span, substs, generics_def_id, def_id } = args; + let InsertableGenericArgs { + insert_span, + substs, + generics_def_id, + def_id, + have_turbofish, + } = args; let generics = tcx.generics_of(generics_def_id); if let Some(argument_index) = generics .own_substs(substs) @@ -1061,7 +1165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { let generic_args = &generics.own_substs_no_defaults(tcx, substs) [generics.own_counts().lifetimes..]; let span = match expr.kind { - ExprKind::MethodCall(path, _, _) => path.ident.span, + ExprKind::MethodCall(path, ..) => path.ident.span, _ => expr.span, }; @@ -1073,6 +1177,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { generics_def_id, def_id, generic_args, + have_turbofish, }, }); } @@ -1110,7 +1215,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { }) .any(|generics| generics.has_impl_trait()) }; - if let ExprKind::MethodCall(path, args, span) = expr.kind + if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind && let Some(substs) = self.node_substs_opt(expr.hir_id) && substs.iter().any(|arg| self.generic_arg_contains_target(arg)) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) @@ -1118,12 +1223,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { && !has_impl_trait(def_id) { let successor = - args.get(1).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); + args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); let substs = self.infcx.resolve_vars_if_possible(substs); self.update_infer_source(InferSource { span: path.ident.span, kind: InferSourceKind::FullyQualifiedMethodCall { - receiver: args.first().unwrap(), + receiver, successor, substs, def_id, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index 9a2ab3e32..da0271a34 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -1,6 +1,9 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where both the regions are anonymous. +use crate::errors::AddLifetimeParamsSuggestion; +use crate::errors::LifetimeMismatch; +use crate::errors::LifetimeMismatchLabels; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; use crate::infer::error_reporting::nice_region_error::NiceRegionError; @@ -8,11 +11,10 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::SubregionOrigin; use crate::infer::TyCtxt; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; -use rustc_hir as hir; -use rustc_hir::{GenericParamKind, Ty}; +use rustc_errors::AddToDiagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; +use rustc_hir::Ty; use rustc_middle::ty::Region; -use rustc_span::symbol::kw; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when both the concerned regions are anonymous. @@ -98,137 +100,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sub_is_ret_type = self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); - let span_label_var1 = match anon_param_sup.pat.simple_ident() { - Some(simple_ident) => format!(" from `{}`", simple_ident), - None => String::new(), - }; - - let span_label_var2 = match anon_param_sub.pat.simple_ident() { - Some(simple_ident) => format!(" into `{}`", simple_ident), - None => String::new(), - }; - debug!( "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", sub_is_ret_type, sup_is_ret_type ); - let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - - match (sup_is_ret_type, sub_is_ret_type) { + let labels = match (sup_is_ret_type, sub_is_ret_type) { (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { let param_span = if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; - - err.span_label( + LifetimeMismatchLabels::InRet { param_span, - "this parameter and the return type are declared with different lifetimes...", - ); - err.span_label(ret_span, ""); - err.span_label(span, format!("...but data{} is returned here", span_label_var1)); - } - - (None, None) => { - if ty_sup.hir_id == ty_sub.hir_id { - err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); - err.span_label(ty_sub.span, ""); - err.span_label(span, "...but data with one lifetime flows into the other here"); - } else { - err.span_label( - ty_sup.span, - "these two types are declared with different lifetimes...", - ); - err.span_label(ty_sub.span, ""); - err.span_label( - span, - format!("...but data{} flows{} here", span_label_var1, span_label_var2), - ); + ret_span, + span, + label_var1: anon_param_sup.pat.simple_ident(), } } - } - if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) { - err.note("each elided lifetime in input position becomes a distinct lifetime"); - } + (None, None) => LifetimeMismatchLabels::Normal { + hir_equal: ty_sup.hir_id == ty_sub.hir_id, + ty_sup: ty_sup.span, + ty_sub: ty_sub.span, + span, + sup: anon_param_sup.pat.simple_ident(), + sub: anon_param_sub.pat.simple_ident(), + }, + }; - let reported = err.emit(); + let suggestion = + AddLifetimeParamsSuggestion { tcx: self.tcx(), sub, ty_sup, ty_sub, add_note: true }; + let err = LifetimeMismatch { span, labels, suggestion }; + let reported = self.tcx().sess.emit_err(err); Some(reported) } } +/// Currently only used in rustc_borrowck, probably should be +/// removed in favour of public_errors::AddLifetimeParamsSuggestion pub fn suggest_adding_lifetime_params<'tcx>( tcx: TyCtxt<'tcx>, sub: Region<'tcx>, - ty_sup: &Ty<'_>, - ty_sub: &Ty<'_>, + ty_sup: &'tcx Ty<'_>, + ty_sub: &'tcx Ty<'_>, err: &mut Diagnostic, -) -> bool { - let ( - hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, - hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, - ) = (ty_sub, ty_sup) else { - return false; - }; - - if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { - return false; - }; - - let Some(anon_reg) = tcx.is_suitable_region(sub) else { - return false; - }; - - let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); - - let node = tcx.hir().get(hir_id); - let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let generics = match node { - hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. }) - | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) - | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, - _ => return false, - }; - - let suggestion_param_name = generics - .params - .iter() - .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) - .map(|p| p.name.ident().name) - .find(|i| *i != kw::UnderscoreLifetime); - let introduce_new = suggestion_param_name.is_none(); - let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); - - debug!(?lifetime_sup.span); - debug!(?lifetime_sub.span); - let make_suggestion = |span: rustc_span::Span| { - if span.is_empty() { - (span, format!("{}, ", suggestion_param_name)) - } else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() { - (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) - } else { - (span, suggestion_param_name.clone()) - } - }; - let mut suggestions = - vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; - - if introduce_new { - let new_param_suggestion = - if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) { - (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) - } else { - (generics.span, format!("<{}>", suggestion_param_name)) - }; - - suggestions.push(new_param_suggestion); - } - - let mut sugg = String::from("consider introducing a named lifetime parameter"); - if is_impl { - sugg.push_str(" and update trait if needed"); - } - err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect); - - true +) { + let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false }; + suggestion.add_to_diagnostic(err); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index c1b201da6..d8f540b74 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -91,7 +91,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { hir::TyKind::TraitObject(bounds, ..) => { for bound in bounds { self.current_index.shift_in(1); - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); } } @@ -103,7 +103,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { self.found_type = Some(arg); @@ -133,7 +133,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { Some( rl::Region::Static | rl::Region::Free(_, _) - | rl::Region::EarlyBound(_, _) + | rl::Region::EarlyBound(_) | rl::Region::LateBound(_, _, _), ) | None, @@ -188,7 +188,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { self.found_it = true; @@ -209,7 +209,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { ( Some( rl::Region::Static - | rl::Region::EarlyBound(_, _) + | rl::Region::EarlyBound(_) | rl::Region::LateBound(_, _, _) | rl::Region::Free(_, _), ) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 893ca3cf7..c5f2a1a3f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -1,13 +1,16 @@ //! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate //! to hold. +use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq}; +use crate::errors::{ + DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime, +}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::error_reporting::note_and_explain_region; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::TypeVisitor; @@ -35,15 +38,27 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else { return None; }; - let ObligationCauseCode::BindingObligation(_def_id, binding_span) = *parent.code() else { + let (ObligationCauseCode::BindingObligation(_, binding_span) | ObligationCauseCode::ExprBindingObligation(_, binding_span, ..)) + = *parent.code() else { return None; }; - let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); + // FIXME: we should point at the lifetime - let mut multi_span: MultiSpan = vec![binding_span].into(); - multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement"); - err.span_note(multi_span, "because this has an unmet lifetime requirement"); - note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); + let multi_span: MultiSpan = vec![binding_span].into(); + let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq { + unmet_requirements: multi_span, + binding_span, + }; + + let expl = note_and_explain::RegionExplanation::new( + self.tcx(), + sup, + Some(binding_span), + note_and_explain::PrefixKind::Empty, + note_and_explain::SuffixKind::Continues, + ); + let mut impl_span = None; + let mut implicit_static_lifetimes = Vec::new(); if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { // If an impl is local, then maybe this isn't what they want. Try to // be as helpful as possible with implicit lifetimes. @@ -72,31 +87,34 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // there aren't trait objects or because none are implicit, then just // write a single note on the impl itself. - let impl_span = self.tcx().def_span(*impl_def_id); - err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + impl_span = Some(self.tcx().def_span(*impl_def_id)); } else { // Otherwise, point at all implicit static lifetimes - err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); for span in &traits { - err.span_note(*span, "this has an implicit `'static` lifetime requirement"); + implicit_static_lifetimes + .push(ImplicitStaticLifetimeSubdiag::Note { span: *span }); // It would be nice to put this immediately under the above note, but they get // pushed to the end. - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + implicit_static_lifetimes + .push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() }); } } } else { // Otherwise just point out the impl. - let impl_span = self.tcx().def_span(*impl_def_id); - err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + impl_span = Some(self.tcx().def_span(*impl_def_id)); } - let reported = err.emit(); + let err = MismatchedStaticLifetime { + cause_span: cause.span, + unmet_lifetime_reqs: multispan_subdiag, + expl, + does_not_outlive_static_from_impl: impl_span + .map(|span| DoesNotOutliveStaticFromImpl::Spanned { span }) + .unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned), + implicit_static_lifetimes, + }; + let reported = self.tcx().sess.emit_err(err); Some(reported) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index 53d9acf7d..aaf5a7af0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -1,6 +1,6 @@ +use crate::infer::error_reporting::TypeErrCtxt; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::lexical_region_resolve::RegionResolutionError::*; -use crate::infer::InferCtxt; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::source_map::Span; @@ -19,34 +19,34 @@ pub use find_anon_type::find_anon_type; pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor}; pub use util::find_param_with_region; -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { - pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { +impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> { + pub fn try_report_nice_region_error(&'cx self, error: &RegionResolutionError<'tcx>) -> bool { NiceRegionError::new(self, error.clone()).try_report().is_some() } } pub struct NiceRegionError<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + cx: &'cx TypeErrCtxt<'cx, 'tcx>, error: Option<RegionResolutionError<'tcx>>, regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>, } impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self { - Self { infcx, error: Some(error), regions: None } + pub fn new(cx: &'cx TypeErrCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self { + Self { cx, error: Some(error), regions: None } } pub fn new_from_span( - infcx: &'cx InferCtxt<'cx, 'tcx>, + cx: &'cx TypeErrCtxt<'cx, 'tcx>, span: Span, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, ) -> Self { - Self { infcx, error: None, regions: Some((span, sub, sup)) } + Self { cx, error: None, regions: Some((span, sub, sup)) } } fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx + self.cx.tcx } pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index 998699158..a58516829 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -211,7 +211,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { ); let mut err = self.tcx().sess.struct_span_err(span, &msg); - let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = *cause.code() { + let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) + | ObligationCauseCode::ExprItemObligation(def_id, ..) = + *cause.code() + { err.span_label(span, "doesn't satisfy where-clause"); err.span_label( self.tcx().def_span(def_id), @@ -223,12 +226,12 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { false }; - let expected_trait_ref = self.infcx.resolve_vars_if_possible(ty::TraitRef { + let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef { def_id: trait_def_id, substs: expected_substs, }); let actual_trait_ref = self - .infcx + .cx .resolve_vars_if_possible(ty::TraitRef { def_id: trait_def_id, substs: actual_substs }); // Search the expected and actual trait references to see (a) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 9886c572a..9bf755d7f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -185,8 +185,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() { let parent_id = tcx.hir().get_parent_item(*hir_id); - let parent_id = tcx.hir().local_def_id_to_hir_id(parent_id); - if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id) { + if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) { let mut span: MultiSpan = fn_decl.output.span().into(); let mut add_label = true; if let hir::FnRetTy::Return(ty) = fn_decl.output { @@ -232,7 +231,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), _ => cause.code(), } - && let (&ObligationCauseCode::ItemObligation(item_def_id), None) = (code, override_error_code) + && let (&ObligationCauseCode::ItemObligation(item_def_id) | &ObligationCauseCode::ExprItemObligation(item_def_id, ..), None) = (code, override_error_code) { // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` // lifetime as above, but called using a fully-qualified path to the method: @@ -287,8 +286,8 @@ pub fn suggest_new_region_bound( ) { debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); // FIXME: account for the need of parens in `&(dyn Trait + '_)` - let consider = "consider changing the"; - let declare = "to declare that the"; + let consider = "consider changing"; + let declare = "to declare that"; let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name); let explicit_static = arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg)); @@ -300,12 +299,16 @@ pub fn suggest_new_region_bound( continue; } match fn_return.kind { - TyKind::OpaqueDef(item_id, _) => { + TyKind::OpaqueDef(item_id, _, _) => { let item = tcx.hir().item(item_id); let ItemKind::OpaqueTy(opaque) = &item.kind else { return; }; + // Get the identity type for this RPIT + let did = item_id.owner_id.to_def_id(); + let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did)); + if let Some(span) = opaque .bounds .iter() @@ -322,7 +325,7 @@ pub fn suggest_new_region_bound( if let Some(explicit_static) = &explicit_static { err.span_suggestion_verbose( span, - &format!("{} `impl Trait`'s {}", consider, explicit_static), + &format!("{consider} `{ty}`'s {explicit_static}"), &lifetime_name, Applicability::MaybeIncorrect, ); @@ -352,12 +355,7 @@ pub fn suggest_new_region_bound( } else { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), - &format!( - "{declare} `impl Trait` {captures}, {explicit}", - declare = declare, - captures = captures, - explicit = explicit, - ), + &format!("{declare} `{ty}` {captures}, {explicit}",), &plus_lt, Applicability::MaybeIncorrect, ); @@ -368,7 +366,7 @@ pub fn suggest_new_region_bound( err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), &format!( - "{declare} trait object {captures}, {explicit}", + "{declare} the trait object {captures}, {explicit}", declare = declare, captures = captures, explicit = explicit, @@ -385,7 +383,7 @@ pub fn suggest_new_region_bound( if let Some(explicit_static) = &explicit_static { err.span_suggestion_verbose( lt.span, - &format!("{} trait object's {}", consider, explicit_static), + &format!("{} the trait object's {}", consider, explicit_static), &lifetime_name, Applicability::MaybeIncorrect, ); @@ -415,7 +413,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let tcx = self.tcx(); match tcx.hir().get_if_local(def_id) { Some(Node::ImplItem(impl_item)) => { - match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id())) { + match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id) + { Some(Node::Item(Item { kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. @@ -425,7 +424,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } Some(Node::TraitItem(trait_item)) => { let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); - match tcx.hir().find_by_def_id(trait_did) { + match tcx.hir().find_by_def_id(trait_did.def_id) { Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { // The method being called is defined in the `trait`, but the `'static` // obligation comes from the `impl`. Find that `impl` so that we can point @@ -486,7 +485,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { tcx, ctxt.param_env, ctxt.assoc_item.def_id, - self.infcx.resolve_vars_if_possible(ctxt.substs), + self.cx.resolve_vars_if_possible(ctxt.substs), ) else { return false; }; @@ -544,7 +543,7 @@ pub struct TraitObjectVisitor(pub FxHashSet<DefId>); impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match t.kind() { - ty::Dynamic(preds, re) if re.is_static() => { + ty::Dynamic(preds, re, _) if re.is_static() => { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index da465a764..5d536e982 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -84,12 +84,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let expected_highlight = HighlightBuilder::build(self.tcx(), expected); let expected = self - .infcx + .cx .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) .name; let found_highlight = HighlightBuilder::build(self.tcx(), found); let found = - self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; + self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; err.span_label(sp, &format!("found `{}`", found)); err.span_label(trait_sp, &format!("expected `{}`", expected)); @@ -154,16 +154,12 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { } hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { [segment] - if segment - .res - .map(|res| { - matches!( - res, - Res::SelfTy { trait_: _, alias_to: _ } - | Res::Def(hir::def::DefKind::TyParam, _) - ) - }) - .unwrap_or(false) => + if matches!( + segment.res, + Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } + | Res::Def(hir::def::DefKind::TyParam, _) + ) => { self.types.push(path.span); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 3e9d491af..f1461d701 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let ret_ty = fn_ty.fn_sig(self.tcx()).output(); let span = hir_sig.decl.output.span(); let future_output = if hir_sig.header.is_async() { - ret_ty.map_bound(|ty| self.infcx.get_impl_future_output_ty(ty)).transpose() + ret_ty.map_bound(|ty| self.cx.get_impl_future_output_ty(ty)).transpose() } else { None }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index c1940c5c0..41b115f33 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -1,100 +1,90 @@ -use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt}; -use crate::infer::{self, InferCtxt, SubregionOrigin}; -use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use crate::errors::RegionOriginNote; +use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; +use crate::infer::{self, SubregionOrigin}; +use rustc_errors::{ + fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, +}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Region}; -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +use super::ObligationCauseAsDiagArg; + +impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { - let mut label_or_note = |span, msg: &str| { - let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); - let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); - let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); - if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { - err.span_label(span, msg); - } else if span_is_primary && expanded_sub_count == 0 { - err.note(msg); - } else { - err.span_note(span, msg); - } - }; match *origin { - infer::Subtype(ref trace) => { - if let Some((expected, found)) = self.values_str(trace.values) { - label_or_note( - trace.cause.span, - &format!("...so that the {}", trace.cause.as_requirement_str()), - ); - - err.note_expected_found(&"", expected, &"", found); - } else { - // FIXME: this really should be handled at some earlier stage. Our - // handling of region checking when type errors are present is - // *terrible*. - - label_or_note( - trace.cause.span, - &format!("...so that {}", trace.cause.as_requirement_str()), - ); - } + infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { + span: trace.cause.span, + requirement: ObligationCauseAsDiagArg(trace.cause.clone()), + expected_found: self.values_str(trace.values), } + .add_to_diagnostic(err), infer::Reborrow(span) => { - label_or_note(span, "...so that reference does not outlive borrowed content"); + RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err) } infer::ReborrowUpvar(span, ref upvar_id) => { let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - label_or_note(span, &format!("...so that closure can access `{}`", var_name)); + RegionOriginNote::WithName { + span, + msg: fluent::infer_reborrow, + name: &var_name.to_string(), + continues: false, + } + .add_to_diagnostic(err); } infer::RelateObjectBound(span) => { - label_or_note(span, "...so that it can be closed over into an object"); + RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } + .add_to_diagnostic(err); } infer::DataBorrowed(ty, span) => { - label_or_note( + RegionOriginNote::WithName { span, - &format!( - "...so that the type `{}` is not borrowed for too long", - self.ty_to_string(ty) - ), - ); + msg: fluent::infer_data_borrowed, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diagnostic(err); } infer::ReferenceOutlivesReferent(ty, span) => { - label_or_note( + RegionOriginNote::WithName { span, - &format!( - "...so that the reference type `{}` does not outlive the data it points at", - self.ty_to_string(ty) - ), - ); + msg: fluent::infer_reference_outlives_referent, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diagnostic(err); } - infer::RelateParamBound(span, t, opt_span) => { - label_or_note( + infer::RelateParamBound(span, ty, opt_span) => { + RegionOriginNote::WithName { span, - &format!( - "...so that the type `{}` will meet its required lifetime bounds{}", - self.ty_to_string(t), - if opt_span.is_some() { "..." } else { "" }, - ), - ); + msg: fluent::infer_relate_param_bound, + name: &self.ty_to_string(ty), + continues: opt_span.is_some(), + } + .add_to_diagnostic(err); if let Some(span) = opt_span { - err.span_note(span, "...that is required by this bound"); + RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } + .add_to_diagnostic(err); } } infer::RelateRegionParamBound(span) => { - label_or_note( - span, - "...so that the declared lifetime parameter bounds are satisfied", - ); + RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } + .add_to_diagnostic(err); } infer::CompareImplItemObligation { span, .. } => { - label_or_note( - span, - "...so that the definition in impl matches the definition from the trait", - ); + RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } + .add_to_diagnostic(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_diagnostic(err); + } } } @@ -107,7 +97,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match origin { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, &terr); + let mut err = self.report_and_explain_type_error(trace, terr); match (*sub, *sup) { (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} (ty::RePlaceholder(_), _) => { @@ -374,6 +364,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } + infer::AscribeUserTypeProvePredicate(span) => { + let mut err = + struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); + note_and_explain_region( + self.tcx, + &mut err, + "lifetime instantiated with ", + sup, + "", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "but lifetime must outlive ", + sub, + "", + None, + ); + err + } } } @@ -390,10 +401,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if matches!( &trace.cause.code().peel_derives(), ObligationCauseCode::BindingObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) ) => { // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::BindingObligation(_, span) = + if let ObligationCauseCode::BindingObligation(_, span) + | ObligationCauseCode::ExprBindingObligation(_, span, ..) = &trace.cause.code().peel_derives() { let span = *span; @@ -406,7 +419,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } infer::Subtype(box trace) => { let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, &terr); + return self.report_and_explain_type_error(trace, terr); } _ => return self.report_concrete_failure(placeholder_origin, sub, sup), } diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index d566634a4..728d691a2 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -27,13 +27,13 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct FreeRegionMap<'tcx> { // Stores the relation `a < b`, where `a` and `b` are regions. // // Invariant: only free regions like `'x` or `'static` are stored // in this relation, not scopes. - relation: TransitiveRelation<Region<'tcx>>, + pub(crate) relation: TransitiveRelation<Region<'tcx>>, } impl<'tcx> FreeRegionMap<'tcx> { @@ -45,15 +45,6 @@ impl<'tcx> FreeRegionMap<'tcx> { self.relation.is_empty() } - // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - // (with the exception that `'static: 'x` is not notable) - pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { - debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if sub.is_free_or_static() && sup.is_free() { - self.relation.add(sub, sup) - } - } - /// Tests whether `r_a <= r_b`. /// /// Both regions must meet `is_free_or_static`. diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 84004d2b2..ff5d1a05a 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -38,7 +38,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVi use std::collections::hash_map::Entry; pub struct TypeFreshener<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, ty_freshen_count: u32, const_freshen_count: u32, ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>, @@ -47,7 +47,7 @@ pub struct TypeFreshener<'a, 'tcx> { } impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> { TypeFreshener { infcx, ty_freshen_count: 0, @@ -126,7 +126,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::ReFree(_) | ty::ReVar(_) | ty::RePlaceholder(..) - | ty::ReEmpty(_) | ty::ReErased => { // replace all free regions with 'erased self.tcx().lifetimes.re_erased diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs index 2f0eadce6..6dd6c4e1f 100644 --- a/compiler/rustc_infer/src/infer/fudge.rs +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -43,7 +43,7 @@ struct VariableLengths { region_constraints_len: usize, } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { fn variable_lengths(&self) -> VariableLengths { let mut inner = self.inner.borrow_mut(); VariableLengths { @@ -167,7 +167,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub struct InferenceFudger<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>), int_vars: Range<IntVid>, float_vars: Range<FloatVid>, diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs index 1570a08f3..6ffefcb7a 100644 --- a/compiler/rustc_infer/src/infer/glb.rs +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -113,7 +113,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { } impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'tcx> { self.fields.infcx } diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index d0d9efe15..28c87a115 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -59,7 +59,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { } } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Replaces all bound variables (lifetimes, types, and constants) bound by /// `binder` with placeholder variables in a new universe. This means that the /// new placeholders can only be named by inference variables created after @@ -69,7 +69,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// For more details visit the relevant sections of the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T where T: TypeFoldable<'tcx> + Copy, @@ -81,19 +81,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let next_universe = self.create_next_universe(); let delegate = FnMutDelegate { - regions: |br: ty::BoundRegion| { + regions: &mut |br: ty::BoundRegion| { self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { universe: next_universe, name: br.kind, })) }, - types: |bound_ty: ty::BoundTy| { + types: &mut |bound_ty: ty::BoundTy| { self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: next_universe, name: bound_ty.var, })) }, - consts: |bound_var: ty::BoundVar, ty| { + consts: &mut |bound_var: ty::BoundVar, ty| { self.tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { universe: next_universe, @@ -104,9 +104,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }; - let result = self.tcx.replace_bound_vars_uncached(binder, delegate); - debug!(?next_universe, ?result); - result + debug!(?next_universe); + self.tcx.replace_bound_vars_uncached(binder, delegate) } /// See [RegionConstraintCollector::leak_check][1]. @@ -115,7 +114,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn leak_check( &self, overly_polymorphic: bool, - snapshot: &CombinedSnapshot<'_, 'tcx>, + snapshot: &CombinedSnapshot<'tcx>, ) -> RelateResult<'tcx, ()> { // If the user gave `-Zno-leak-check`, or we have been // configured to skip the leak check, then skip the leak check diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 1e3293efa..eba65361a 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::{self, Ty}; /// GLB moves "down" the lattice (to smaller values); LUB moves /// "up" the lattice (to bigger values). pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> { - fn infcx(&self) -> &'f InferCtxt<'f, 'tcx>; + fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 3783cfb4c..5f13b2b3d 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -15,8 +15,9 @@ use rustc_data_structures::graph::implementation::{ use rustc_data_structures::intern::Interned; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::PlaceholderRegion; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use rustc_middle::ty::{ReEarlyBound, ReErased, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; @@ -51,6 +52,13 @@ pub struct LexicalRegionResolutions<'tcx> { #[derive(Copy, Clone, Debug)] pub(crate) enum VarValue<'tcx> { + /// Empty lifetime is for data that is never accessed. We tag the + /// empty lifetime with a universe -- the idea is that we don't + /// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable. + /// Therefore, the `'empty` in a universe `U` is less than all + /// regions visible from `U`, but not less than regions not visible + /// from `U`. + Empty(ty::UniverseIndex), Value(Region<'tcx>), ErrorValue, } @@ -117,7 +125,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { &mut self, errors: &mut Vec<RegionResolutionError<'tcx>>, ) -> LexicalRegionResolutions<'tcx> { - let mut var_data = self.construct_var_data(self.tcx()); + let mut var_data = self.construct_var_data(); if cfg!(debug_assertions) { self.dump_constraints(); @@ -137,13 +145,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// Initially, the value for all variables is set to `'empty`, the /// empty region. The `expansion` phase will grow this larger. - fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { + fn construct_var_data(&self) -> LexicalRegionResolutions<'tcx> { LexicalRegionResolutions { values: IndexVec::from_fn_n( |vid| { let vid_universe = self.var_infos[vid].universe; - let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe)); - VarValue::Value(re_empty) + VarValue::Empty(vid_universe) }, self.num_vars(), ), @@ -189,20 +196,131 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// Gets the LUb of a given region and the empty region + fn lub_empty(&self, a_region: Region<'tcx>) -> Result<Region<'tcx>, PlaceholderRegion> { + match *a_region { + ReLateBound(..) | ReErased => { + bug!("cannot relate region: {:?}", a_region); + } + + ReVar(v_id) => { + span_bug!( + self.var_infos[v_id].origin.span(), + "lub invoked with non-concrete regions: {:?}", + a_region, + ); + } + + ReStatic => { + // nothing lives longer than `'static` + Ok(self.tcx().lifetimes.re_static) + } + + ReEarlyBound(_) | ReFree(_) => { + // All empty regions are less than early-bound, free, + // and scope regions. + Ok(a_region) + } + + RePlaceholder(placeholder) => Err(placeholder), + } + } + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { + // In the first pass, we expand region vids according to constraints we + // have previously found. In the second pass, we loop through the region + // vids we expanded and expand *across* region vids (effectively + // "expanding" new `RegSubVar` constraints). + + // Tracks the `VarSubVar` constraints generated for each region vid. We + // later use this to expand across vids. let mut constraints = IndexVec::from_elem_n(Vec::new(), var_values.values.len()); + // Tracks the changed region vids. let mut changes = Vec::new(); for constraint in self.data.constraints.keys() { - let (a_vid, a_region, b_vid, b_data) = match *constraint { + match *constraint { Constraint::RegSubVar(a_region, b_vid) => { let b_data = var_values.value_mut(b_vid); - (None, a_region, b_vid, b_data) + + if self.expand_node(a_region, b_vid, b_data) { + changes.push(b_vid); + } } Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) { VarValue::ErrorValue => continue, + VarValue::Empty(a_universe) => { + let b_data = var_values.value_mut(b_vid); + + let changed = (|| match *b_data { + VarValue::Empty(b_universe) => { + // Empty regions are ordered according to the universe + // they are associated with. + let ui = a_universe.min(b_universe); + + debug!( + "Expanding value of {:?} \ + from empty lifetime with universe {:?} \ + to empty lifetime with universe {:?}", + b_vid, b_universe, ui + ); + + *b_data = VarValue::Empty(ui); + true + } + VarValue::Value(cur_region) => { + let lub = match self.lub_empty(cur_region) { + Ok(r) => r, + // If the empty and placeholder regions are in the same universe, + // then the LUB is the Placeholder region (which is the cur_region). + // If they are not in the same universe, the LUB is the Static lifetime. + Err(placeholder) if a_universe == placeholder.universe => { + cur_region + } + Err(_) => self.tcx().lifetimes.re_static, + }; + + if lub == cur_region { + return false; + } + + debug!( + "Expanding value of {:?} from {:?} to {:?}", + b_vid, cur_region, lub + ); + + *b_data = VarValue::Value(lub); + true + } + + VarValue::ErrorValue => false, + })(); + + if changed { + changes.push(b_vid); + } + match b_data { + VarValue::Value(Region(Interned(ReStatic, _))) + | VarValue::ErrorValue => (), + _ => { + constraints[a_vid].push((a_vid, b_vid)); + constraints[b_vid].push((a_vid, b_vid)); + } + } + } VarValue::Value(a_region) => { let b_data = var_values.value_mut(b_vid); - (Some(a_vid), a_region, b_vid, b_data) + + if self.expand_node(a_region, b_vid, b_data) { + changes.push(b_vid); + } + match b_data { + VarValue::Value(Region(Interned(ReStatic, _))) + | VarValue::ErrorValue => (), + _ => { + constraints[a_vid].push((a_vid, b_vid)); + constraints[b_vid].push((a_vid, b_vid)); + } + } } }, Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { @@ -210,18 +328,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // is done, in `collect_errors`. continue; } - }; - if self.expand_node(a_region, b_vid, b_data) { - changes.push(b_vid); - } - if let Some(a_vid) = a_vid { - match b_data { - VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue => (), - _ => { - constraints[a_vid].push((a_vid, b_vid)); - constraints[b_vid].push((a_vid, b_vid)); - } - } } } @@ -242,6 +348,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// Expands the value of the region represented with `b_vid` with current + /// value `b_data` to the lub of `b_data` and `a_region`. The corresponds + /// with the constraint `'?b: 'a` (`'a <: '?b`), where `'a` is some known + /// region and `'?b` is some region variable. fn expand_node( &self, a_region: Region<'tcx>, @@ -263,14 +373,28 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } match *b_data { + VarValue::Empty(empty_ui) => { + let lub = match self.lub_empty(a_region) { + Ok(r) => r, + // If this empty region is from a universe that can + // name the placeholder, then the placeholder is + // larger; otherwise, the only ancestor is `'static`. + Err(placeholder) if empty_ui.can_name(placeholder.universe) => { + self.tcx().mk_region(RePlaceholder(placeholder)) + } + Err(_) => self.tcx().lifetimes.re_static, + }; + + debug!("Expanding value of {:?} from empty lifetime to {:?}", b_vid, lub); + + *b_data = VarValue::Value(lub); + true + } VarValue::Value(cur_region) => { // This is a specialized version of the `lub_concrete_regions` // check below for a common case, here purely as an // optimization. let b_universe = self.var_infos[b_vid].universe; - if let ReEmpty(a_universe) = *a_region && a_universe == b_universe { - return false; - } let mut lub = self.lub_concrete_regions(a_region, cur_region); if lub == cur_region { @@ -300,6 +424,78 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// True if `a <= b`. + fn sub_region_values(&self, a: VarValue<'tcx>, b: VarValue<'tcx>) -> bool { + match (a, b) { + // Error region is `'static` + (VarValue::ErrorValue, _) | (_, VarValue::ErrorValue) => return true, + (VarValue::Empty(a_ui), VarValue::Empty(b_ui)) => { + // Empty regions are ordered according to the universe + // they are associated with. + a_ui.min(b_ui) == b_ui + } + (VarValue::Value(a), VarValue::Empty(_)) => { + match *a { + ReLateBound(..) | ReErased => { + bug!("cannot relate region: {:?}", a); + } + + ReVar(v_id) => { + span_bug!( + self.var_infos[v_id].origin.span(), + "lub_concrete_regions invoked with non-concrete region: {:?}", + a + ); + } + + ReStatic | ReEarlyBound(_) | ReFree(_) => { + // nothing lives longer than `'static` + + // All empty regions are less than early-bound, free, + // and scope regions. + + false + } + + RePlaceholder(_) => { + // The LUB is either `a` or `'static` + false + } + } + } + (VarValue::Empty(a_ui), VarValue::Value(b)) => { + match *b { + ReLateBound(..) | ReErased => { + bug!("cannot relate region: {:?}", b); + } + + ReVar(v_id) => { + span_bug!( + self.var_infos[v_id].origin.span(), + "lub_concrete_regions invoked with non-concrete regions: {:?}", + b + ); + } + + ReStatic | ReEarlyBound(_) | ReFree(_) => { + // nothing lives longer than `'static` + // All empty regions are less than early-bound, free, + // and scope regions. + true + } + + RePlaceholder(placeholder) => { + // If this empty region is from a universe that can + // name the placeholder, then the placeholder is + // larger; otherwise, the only ancestor is `'static`. + if a_ui.can_name(placeholder.universe) { true } else { false } + } + } + } + (VarValue::Value(a), VarValue::Value(b)) => self.sub_concrete_regions(a, b), + } + } + /// True if `a <= b`, but not defined over inference variables. #[instrument(level = "trace", skip(self))] fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool { @@ -333,9 +529,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// /// Neither `a` nor `b` may be an inference variable (hence the /// term "concrete regions"). - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self), ret)] fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - let r = match (*a, *b) { + match (*a, *b) { (ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } @@ -355,37 +551,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().lifetimes.re_static } - (ReEmpty(_), ReEarlyBound(_) | ReFree(_)) => { - // All empty regions are less than early-bound, free, - // and scope regions. - b - } - - (ReEarlyBound(_) | ReFree(_), ReEmpty(_)) => { - // All empty regions are less than early-bound, free, - // and scope regions. - a - } - - (ReEmpty(a_ui), ReEmpty(b_ui)) => { - // Empty regions are ordered according to the universe - // they are associated with. - let ui = a_ui.min(b_ui); - self.tcx().mk_region(ReEmpty(ui)) - } - - (ReEmpty(empty_ui), RePlaceholder(placeholder)) - | (RePlaceholder(placeholder), ReEmpty(empty_ui)) => { - // If this empty region is from a universe that can - // name the placeholder, then the placeholder is - // larger; otherwise, the only ancestor is `'static`. - if empty_ui.can_name(placeholder.universe) { - self.tcx().mk_region(RePlaceholder(placeholder)) - } else { - self.tcx().lifetimes.re_static - } - } - (ReEarlyBound(_) | ReFree(_), ReEarlyBound(_) | ReFree(_)) => { self.region_rels.lub_free_regions(a, b) } @@ -399,11 +564,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().lifetimes.re_static } } - }; - - debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r); - - r + } } /// After expansion is complete, go and check upper bounds (i.e., @@ -512,7 +673,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { for (node_vid, value) in var_data.values.iter_enumerated() { match *value { - VarValue::Value(_) => { /* Inference successful */ } + VarValue::Empty(_) | VarValue::Value(_) => { /* Inference successful */ } VarValue::ErrorValue => { // Inference impossible: this value contains // inconsistent constraints. @@ -833,12 +994,25 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } VerifyBound::OutlivedBy(r) => { - self.sub_concrete_regions(min, var_values.normalize(self.tcx(), *r)) + let a = match *min { + ty::ReVar(rid) => var_values.values[rid], + _ => VarValue::Value(min), + }; + let b = match **r { + ty::ReVar(rid) => var_values.values[rid], + _ => VarValue::Value(*r), + }; + self.sub_region_values(a, b) } - VerifyBound::IsEmpty => { - matches!(*min, ty::ReEmpty(_)) - } + VerifyBound::IsEmpty => match *min { + ty::ReVar(rid) => match var_values.values[rid] { + VarValue::ErrorValue => false, + VarValue::Empty(_) => true, + VarValue::Value(_) => false, + }, + _ => false, + }, VerifyBound::AnyBound(bs) => { bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min)) @@ -880,6 +1054,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { ) -> ty::Region<'tcx> { let result = match *r { ty::ReVar(rid) => match self.values[rid] { + VarValue::Empty(_) => r, VarValue::Value(r) => r, VarValue::ErrorValue => tcx.lifetimes.re_static, }, diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index 9f96d52c8..d6e56fcb7 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -119,7 +119,7 @@ impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { } impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'tcx> { self.fields.infcx } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index d7d1b5fa2..ffb020398 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -20,6 +20,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; +use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -32,12 +33,13 @@ pub use rustc_middle::ty::IntVarValue; use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid}; use rustc_span::symbol::Symbol; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; -use std::cell::{Cell, Ref, RefCell}; +use std::cell::{Cell, RefCell}; use std::fmt; use self::combine::CombineFields; +use self::error_reporting::TypeErrCtxt; use self::free_regions::RegionRelations; use self::lexical_region_resolve::LexicalRegionResolutions; use self::outlives::env::OutlivesEnvironment; @@ -251,7 +253,7 @@ pub enum DefiningAnchor { Error, } -pub struct InferCtxt<'a, 'tcx> { +pub struct InferCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, /// The `DefId` of the item in whose context we are performing inference or typeck. @@ -271,12 +273,6 @@ pub struct InferCtxt<'a, 'tcx> { /// solving is left to borrowck instead. pub considering_regions: bool, - /// During type-checking/inference of a body, `in_progress_typeck_results` - /// contains a reference to the typeck results being built up, which are - /// used for reading closure kinds/signatures as they are inferred, - /// and for error reporting logic to read arbitrary node types. - pub in_progress_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>, - pub inner: RefCell<InferCtxtInner<'tcx>>, /// If set, this flag causes us to skip the 'leak check' during @@ -316,12 +312,12 @@ pub struct InferCtxt<'a, 'tcx> { /// /// Don't read this flag directly, call `is_tainted_by_errors()` /// and `set_tainted_by_errors()`. - tainted_by_errors_flag: Cell<bool>, + tainted_by_errors: Cell<Option<ErrorGuaranteed>>, /// Track how many errors were reported when this infcx is created. /// If the number of errors increases, that's also a sign (line /// `tainted_by_errors`) to avoid reporting certain kinds of errors. - // FIXME(matthewjasper) Merge into `tainted_by_errors_flag` + // FIXME(matthewjasper) Merge into `tainted_by_errors` err_count_on_creation: usize, /// This flag is true while there is an active snapshot. @@ -337,6 +333,9 @@ pub struct InferCtxt<'a, 'tcx> { /// when we enter into a higher-ranked (`for<..>`) type or trait /// bound. universe: Cell<ty::UniverseIndex>, + + normalize_fn_sig_for_diagnostic: + Option<Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>, } /// See the `error_reporting` module for more details. @@ -350,12 +349,11 @@ pub enum ValuePairs<'tcx> { impl<'tcx> ValuePairs<'tcx> { pub fn ty(&self) -> Option<(Ty<'tcx>, Ty<'tcx>)> { - if let ValuePairs::Terms(ExpectedFound { - expected: ty::Term::Ty(expected), - found: ty::Term::Ty(found), - }) = self + if let ValuePairs::Terms(ExpectedFound { expected, found }) = self + && let Some(expected) = expected.ty() + && let Some(found) = found.ty() { - Some((*expected, *found)) + Some((expected, found)) } else { None } @@ -406,7 +404,11 @@ pub enum SubregionOrigin<'tcx> { /// Comparing the signature and requirements of an impl method against /// the containing trait. - CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId }, + CompareImplItemObligation { + span: Span, + impl_item_def_id: LocalDefId, + trait_item_def_id: DefId, + }, /// Checking that the bounds of a trait's associated type hold for a given impl CheckAssociatedTypeBounds { @@ -414,12 +416,24 @@ pub enum SubregionOrigin<'tcx> { impl_item_def_id: LocalDefId, trait_item_def_id: DefId, }, + + AscribeUserTypeProvePredicate(Span), } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(SubregionOrigin<'_>, 32); +impl<'tcx> SubregionOrigin<'tcx> { + pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> { + match self { + Self::Subtype(type_trace) => type_trace.cause.to_constraint_category(), + Self::AscribeUserTypeProvePredicate(span) => ConstraintCategory::Predicate(*span), + _ => ConstraintCategory::BoringNoLocation, + } + } +} + /// Times when we replace late-bound regions with variables: #[derive(Clone, Copy, Debug)] pub enum LateBoundRegionConversionTime { @@ -504,7 +518,7 @@ pub enum FixupError<'tcx> { } /// See the `region_obligations` field for more information. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct RegionObligation<'tcx> { pub sub_region: ty::Region<'tcx>, pub sup_type: Ty<'tcx>, @@ -532,14 +546,13 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { } } -/// A temporary returned by `tcx.infer_ctxt()`. This is necessary -/// for multiple `InferCtxt` to share the same `in_progress_typeck_results` -/// without using `Rc` or something similar. +/// Used to configure inference contexts before their creation pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, defining_use_anchor: DefiningAnchor, considering_regions: bool, - fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, + normalize_fn_sig_for_diagnostic: + Option<Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>, } pub trait TyCtxtInferExt<'tcx> { @@ -552,25 +565,17 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { tcx: self, defining_use_anchor: DefiningAnchor::Error, considering_regions: true, - fresh_typeck_results: None, + normalize_fn_sig_for_diagnostic: None, } } } impl<'tcx> InferCtxtBuilder<'tcx> { - /// Used only by `rustc_typeck` during body type-checking/inference, - /// will initialize `in_progress_typeck_results` with fresh `TypeckResults`. - /// Will also change the scope for opaque type defining use checks to the given owner. - pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self { - self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner))); - self.with_opaque_type_inference(DefiningAnchor::Bind(table_owner)) - } - /// Whenever the `InferCtxt` should be able to handle defining uses of opaque types, /// you need to call this function. Otherwise the opaque type will be treated opaquely. /// /// It is only meant to be called in two places, for typeck - /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used + /// (via `Inherited::build`) and for the inference context used /// in mir borrowck. pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor) -> Self { self.defining_use_anchor = defining_use_anchor; @@ -582,6 +587,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } + pub fn with_normalize_fn_sig_for_diagnostic( + mut self, + fun: Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>, + ) -> Self { + self.normalize_fn_sig_for_diagnostic = Some(fun); + self + } + /// Given a canonical value `C` as a starting point, create an /// inference context that contains each of the bound values /// within instantiated as a fresh variable. The `f` closure is @@ -589,47 +602,45 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// `V` and a substitution `S`. This substitution `S` maps from /// the bound values in `C` to their instantiated values in `V` /// (in other words, `S(C) = V`). - pub fn enter_with_canonical<T, R>( + pub fn build_with_canonical<T>( &mut self, span: Span, canonical: &Canonical<'tcx, T>, - f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>, T, CanonicalVarValues<'tcx>) -> R, - ) -> R + ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where T: TypeFoldable<'tcx>, { - self.enter(|infcx| { - let (value, subst) = - infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); - f(infcx, value, subst) - }) + let infcx = self.build(); + let (value, subst) = infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); + (infcx, value, subst) } - pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R { + pub fn build(&mut self) -> InferCtxt<'tcx> { let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, - ref fresh_typeck_results, + ref normalize_fn_sig_for_diagnostic, } = *self; - let in_progress_typeck_results = fresh_typeck_results.as_ref(); - f(InferCtxt { + InferCtxt { tcx, defining_use_anchor, considering_regions, - in_progress_typeck_results, inner: RefCell::new(InferCtxtInner::new()), lexical_region_resolutions: RefCell::new(None), selection_cache: Default::default(), evaluation_cache: Default::default(), reported_trait_errors: Default::default(), reported_closure_mismatch: Default::default(), - tainted_by_errors_flag: Cell::new(false), + tainted_by_errors: Cell::new(None), err_count_on_creation: tcx.sess.err_count(), in_snapshot: Cell::new(false), skip_leak_check: Cell::new(false), universe: Cell::new(ty::UniverseIndex::ROOT), - }) + normalize_fn_sig_for_diagnostic: normalize_fn_sig_for_diagnostic + .as_ref() + .map(|f| f.clone()), + } } } @@ -641,7 +652,7 @@ impl<'tcx, T> InferOk<'tcx, T> { /// Extracts `value`, registering any obligations into `fulfill_cx`. pub fn into_value_registering_obligations( self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx>, ) -> T { let InferOk { value, obligations } = self; @@ -657,29 +668,34 @@ impl<'tcx> InferOk<'tcx, ()> { } #[must_use = "once you start a snapshot, you should always consume it"] -pub struct CombinedSnapshot<'a, 'tcx> { +pub struct CombinedSnapshot<'tcx> { undo_snapshot: Snapshot<'tcx>, region_constraints_snapshot: RegionSnapshot, universe: ty::UniverseIndex, was_in_snapshot: bool, - _in_progress_typeck_results: Option<Ref<'a, ty::TypeckResults<'tcx>>>, } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { + /// Creates a `TypeErrCtxt` for emitting various inference errors. + /// During typeck, use `FnCtxt::infer_err` instead. + pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { + TypeErrCtxt { infcx: self, typeck_results: None } + } + /// calls `tcx.try_unify_abstract_consts` after /// canonicalizing the consts. #[instrument(skip(self), level = "debug")] pub fn try_unify_abstract_consts( &self, - a: ty::Unevaluated<'tcx, ()>, - b: ty::Unevaluated<'tcx, ()>, + a: ty::UnevaluatedConst<'tcx>, + b: ty::UnevaluatedConst<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> bool { // Reject any attempt to unify two unevaluated constants that contain inference // variables, since inference variables in queries lead to ICEs. - if a.substs.has_infer_types_or_consts() - || b.substs.has_infer_types_or_consts() - || param_env.has_infer_types_or_consts() + if a.substs.has_non_region_infer() + || b.substs.has_non_region_infer() + || param_env.has_non_region_infer() { debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); return false; @@ -704,7 +720,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// if this is not a type variable. /// /// No attempt is made to resolve `ty`. - pub fn type_var_origin(&'a self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> { + pub fn type_var_origin(&self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> { match *ty.kind() { ty::Infer(ty::TyVar(vid)) => { Some(*self.inner.borrow_mut().type_variables().var_origin(vid)) @@ -745,7 +761,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { vars } - fn combine_fields( + fn combine_fields<'a>( &'a self, trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -787,7 +803,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { result } - fn start_snapshot(&self) -> CombinedSnapshot<'a, 'tcx> { + fn start_snapshot(&self) -> CombinedSnapshot<'tcx> { debug!("start_snapshot()"); let in_snapshot = self.in_snapshot.replace(true); @@ -799,22 +815,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), universe: self.universe(), was_in_snapshot: in_snapshot, - // Borrow typeck results "in progress" (i.e., during typeck) - // to ban writes from within a snapshot to them. - _in_progress_typeck_results: self - .in_progress_typeck_results - .map(|typeck_results| typeck_results.borrow()), } } #[instrument(skip(self, snapshot), level = "debug")] - fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) { + fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'tcx>) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe, was_in_snapshot, - _in_progress_typeck_results, } = snapshot; self.in_snapshot.set(was_in_snapshot); @@ -826,13 +836,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } #[instrument(skip(self, snapshot), level = "debug")] - fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { + fn commit_from(&self, snapshot: CombinedSnapshot<'tcx>) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _, was_in_snapshot, - _in_progress_typeck_results, } = snapshot; self.in_snapshot.set(was_in_snapshot); @@ -844,7 +853,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self, f), level = "debug")] pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where - F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>, + F: FnOnce(&CombinedSnapshot<'tcx>) -> Result<T, E>, { let snapshot = self.start_snapshot(); let r = f(&snapshot); @@ -864,7 +873,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self, f), level = "debug")] pub fn probe<R, F>(&self, f: F) -> R where - F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + F: FnOnce(&CombinedSnapshot<'tcx>) -> R, { let snapshot = self.start_snapshot(); let r = f(&snapshot); @@ -876,7 +885,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self, f), level = "debug")] pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R where - F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + F: FnOnce(&CombinedSnapshot<'tcx>) -> R, { let snapshot = self.start_snapshot(); let was_skip_leak_check = self.skip_leak_check.get(); @@ -896,7 +905,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders pub fn region_constraints_added_in_snapshot( &self, - snapshot: &CombinedSnapshot<'a, 'tcx>, + snapshot: &CombinedSnapshot<'tcx>, ) -> Option<bool> { self.inner .borrow_mut() @@ -904,7 +913,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) } - pub fn opaque_types_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'a, 'tcx>) -> bool { + pub fn opaque_types_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'tcx>) -> bool { self.inner.borrow().undo_log.opaque_types_in_snapshot(&snapshot.undo_snapshot) } @@ -988,7 +997,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, predicate: ty::PolyCoercePredicate<'tcx>, - ) -> Option<InferResult<'tcx, ()>> { + ) -> Result<InferResult<'tcx, ()>, (TyVid, TyVid)> { let subtype_predicate = predicate.map_bound(|p| ty::SubtypePredicate { a_is_expected: false, // when coercing from `a` to `b`, `b` is expected a: p.a, @@ -1002,7 +1011,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, predicate: ty::PolySubtypePredicate<'tcx>, - ) -> Option<InferResult<'tcx, ()>> { + ) -> Result<InferResult<'tcx, ()>, (TyVid, TyVid)> { // Check for two unresolved inference variables, in which case we can // make no progress. This is partly a micro-optimization, but it's // also an opportunity to "sub-unify" the variables. This isn't @@ -1021,12 +1030,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match (r_a.kind(), r_b.kind()) { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { self.inner.borrow_mut().type_variables().sub(a_vid, b_vid); - return None; + return Err((a_vid, b_vid)); } _ => {} } - Some(self.commit_if_ok(|_snapshot| { + Ok(self.commit_if_ok(|_snapshot| { let ty::SubtypePredicate { a_is_expected, a, b } = self.replace_bound_vars_with_placeholders(predicate); @@ -1141,8 +1150,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Return the universe that the region `r` was created in. For /// most regions (e.g., `'static`, named regions from the user, /// etc) this is the root universe U0. For inference variables or - /// placeholders, however, it will return the universe which which - /// they are associated. + /// placeholders, however, it will return the universe which they + /// are associated. pub fn universe_of_region(&self, r: ty::Region<'tcx>) -> ty::UniverseIndex { self.inner.borrow_mut().unwrap_region_constraints().universe(r) } @@ -1227,23 +1236,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn is_tainted_by_errors(&self) -> bool { debug!( "is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ - tainted_by_errors_flag={})", + tainted_by_errors={})", self.tcx.sess.err_count(), self.err_count_on_creation, - self.tainted_by_errors_flag.get() + self.tainted_by_errors.get().is_some() ); if self.tcx.sess.err_count() > self.err_count_on_creation { return true; // errors reported since this infcx was made } - self.tainted_by_errors_flag.get() + self.tainted_by_errors.get().is_some() } /// Set the "tainted by errors" flag to true. We call this when we /// observe an error from a prior pass. pub fn set_tainted_by_errors(&self) { debug!("set_tainted_by_errors()"); - self.tainted_by_errors_flag.set(true) + self.tainted_by_errors.set(Some( + self.tcx.sess.delay_span_bug(DUMMY_SP, "`InferCtxt` incorrectly tainted by errors"), + )); } pub fn skip_region_resolution(&self) { @@ -1272,7 +1283,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { assert!(old_value.is_none()); } - /// Process the region constraints and return any any errors that + /// Process the region constraints and return any errors that /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. @@ -1306,32 +1317,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { errors } - - /// Process the region constraints and report any errors that - /// result. After this, no more unification operations should be - /// done -- or the compiler will panic -- but it is legal to use - /// `resolve_vars_if_possible` as well as `fully_resolve`. - /// - /// Make sure to call [`InferCtxt::process_registered_region_obligations`] - /// first, or preferrably use [`InferCtxt::check_region_obligations_and_report_errors`] - /// to do both of these operations together. - pub fn resolve_regions_and_report_errors( - &self, - generic_param_scope: LocalDefId, - outlives_env: &OutlivesEnvironment<'tcx>, - ) { - let errors = self.resolve_regions(outlives_env); - - if !self.is_tainted_by_errors() { - // As a heuristic, just skip reporting region errors - // altogether if other errors have been reported while - // this infcx was in use. This is totally hokey but - // otherwise we have a hard time separating legit region - // errors from silly ones. - self.report_region_errors(generic_param_scope, &errors); - } - } - /// Obtains (and clears) the current set of region /// constraints. The inference context is still usable: further /// unifications will simply add new constraints. @@ -1484,62 +1469,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { * except during the writeback phase. */ - resolve::fully_resolve(self, value) - } - - // [Note-Type-error-reporting] - // An invariant is that anytime the expected or actual type is Error (the special - // error type, meaning that an error occurred when typechecking this expression), - // this is a derived error. The error cascaded from another error (that was already - // reported), so it's not useful to display it to the user. - // The following methods implement this logic. - // They check if either the actual or expected type is Error, and don't print the error - // in this case. The typechecker should only ever report type errors involving mismatched - // types using one of these methods, and should not call span_err directly for such - // errors. - - pub fn type_error_struct_with_diag<M>( - &self, - sp: Span, - mk_diag: M, - actual_ty: Ty<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> - where - M: FnOnce(String) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>, - { - let actual_ty = self.resolve_vars_if_possible(actual_ty); - debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); - - let mut err = mk_diag(self.ty_to_string(actual_ty)); - - // Don't report an error if actual type is `Error`. - if actual_ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - } - - pub fn report_mismatched_types( - &self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - err: TypeError<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let trace = TypeTrace::types(cause, true, expected, actual); - self.report_and_explain_type_error(trace, &err) - } - - pub fn report_mismatched_consts( - &self, - cause: &ObligationCause<'tcx>, - expected: ty::Const<'tcx>, - actual: ty::Const<'tcx>, - err: TypeError<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let trace = TypeTrace::consts(cause, true, expected, actual); - self.report_and_explain_type_error(trace, &err) + let value = resolve::fully_resolve(self, value); + assert!( + value.as_ref().map_or(true, |value| !value.needs_infer()), + "`{value:?}` is not fully resolved" + ); + value } pub fn replace_bound_vars_with_fresh_vars<T>( @@ -1556,7 +1491,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } struct ToFreshVars<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, span: Span, lbrct: LateBoundRegionConversionTime, map: FxHashMap<ty::BoundVar, ty::GenericArg<'tcx>>, @@ -1656,7 +1591,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn try_const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::Unevaluated<'tcx>, + unevaluated: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, span: Option<Span>, ) -> Result<ty::Const<'tcx>, ErrorHandled> { @@ -1691,7 +1626,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn const_eval_resolve( &self, mut param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::Unevaluated<'tcx>, + unevaluated: ty::UnevaluatedConst<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx> { let mut substs = self.resolve_vars_if_possible(unevaluated.substs); @@ -1699,8 +1634,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Postpone the evaluation of constants whose substs depend on inference // variables - if substs.has_infer_types_or_consts() { - let ac = AbstractConst::new(self.tcx, unevaluated.shrink()); + if substs.has_non_region_infer() { + let ac = AbstractConst::new(self.tcx, unevaluated); match ac { Ok(None) => { substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); @@ -1722,11 +1657,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!(?param_env_erased); debug!(?substs_erased); - let unevaluated = ty::Unevaluated { - def: unevaluated.def, - substs: substs_erased, - promoted: unevaluated.promoted, - }; + let unevaluated = ty::UnevaluatedConst { def: unevaluated.def, substs: substs_erased }; // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. @@ -1785,6 +1716,86 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } +impl<'tcx> TypeErrCtxt<'_, 'tcx> { + /// Process the region constraints and report any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_vars_if_possible` as well as `fully_resolve`. + /// + /// Make sure to call [`InferCtxt::process_registered_region_obligations`] + /// first, or preferably use [`InferCtxt::check_region_obligations_and_report_errors`] + /// to do both of these operations together. + pub fn resolve_regions_and_report_errors( + &self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + let errors = self.resolve_regions(outlives_env); + + if !self.is_tainted_by_errors() { + // As a heuristic, just skip reporting region errors + // altogether if other errors have been reported while + // this infcx was in use. This is totally hokey but + // otherwise we have a hard time separating legit region + // errors from silly ones. + self.report_region_errors(generic_param_scope, &errors); + } + } + + // [Note-Type-error-reporting] + // An invariant is that anytime the expected or actual type is Error (the special + // error type, meaning that an error occurred when typechecking this expression), + // this is a derived error. The error cascaded from another error (that was already + // reported), so it's not useful to display it to the user. + // The following methods implement this logic. + // They check if either the actual or expected type is Error, and don't print the error + // in this case. The typechecker should only ever report type errors involving mismatched + // types using one of these methods, and should not call span_err directly for such + // errors. + + pub fn type_error_struct_with_diag<M>( + &self, + sp: Span, + mk_diag: M, + actual_ty: Ty<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> + where + M: FnOnce(String) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>, + { + let actual_ty = self.resolve_vars_if_possible(actual_ty); + debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); + + let mut err = mk_diag(self.ty_to_string(actual_ty)); + + // Don't report an error if actual type is `Error`. + if actual_ty.references_error() { + err.downgrade_to_delayed_bug(); + } + + err + } + + pub fn report_mismatched_types( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err) + } + + pub fn report_mismatched_consts( + &self, + cause: &ObligationCause<'tcx>, + expected: ty::Const<'tcx>, + actual: ty::Const<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err) + } +} + /// Helper for `ty_or_const_infer_var_changed` (see comment on that), currently /// used only for `traits::fulfill`'s list of `stalled_on` inference variables. #[derive(Copy, Clone, Debug)] @@ -1814,7 +1825,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> { /// Tries to extract an inference variable from a type, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). - pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> { + fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> { match *ty.kind() { ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), @@ -1825,7 +1836,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> { /// Tries to extract an inference variable from a constant, returns `None` /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). - pub fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> { + fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> { match ct.kind() { ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), _ => None, @@ -1854,7 +1865,7 @@ impl<'tcx> TypeFolder<'tcx> for InferenceLiteralEraser<'tcx> { } struct ShallowResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { @@ -1937,6 +1948,18 @@ impl<'tcx> TypeTrace<'tcx> { } } + pub fn poly_trait_refs( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: ty::PolyTraitRef<'tcx>, + b: ty::PolyTraitRef<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } + } + pub fn consts( cause: &ObligationCause<'tcx>, a_is_expected: bool, @@ -1962,6 +1985,7 @@ impl<'tcx> SubregionOrigin<'tcx> { DataBorrowed(_, a) => a, ReferenceOutlivesReferent(_, a) => a, CompareImplItemObligation { span, .. } => span, + AscribeUserTypeProvePredicate(span) => span, CheckAssociatedTypeBounds { ref parent, .. } => parent.span(), } } @@ -1994,6 +2018,10 @@ impl<'tcx> SubregionOrigin<'tcx> { parent: Box::new(default()), }, + traits::ObligationCauseCode::AscribeUserTypeProvePredicate(span) => { + SubregionOrigin::AscribeUserTypeProvePredicate(span) + } + _ => default(), } } @@ -2015,16 +2043,6 @@ impl RegionVariableOrigin { } } -impl<'tcx> fmt::Debug for RegionObligation<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "RegionObligation(sub_region={:?}, sup_type={:?})", - self.sub_region, self.sup_type - ) - } -} - /// Replaces substs that reference param or infer variables with suitable /// placeholders. This function is meant to remove these param and infer /// substs when they're not actually needed to evaluate a constant. @@ -2034,21 +2052,17 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>( ) -> SubstsRef<'tcx> { tcx.mk_substs(substs.iter().enumerate().map(|(idx, arg)| { match arg.unpack() { - GenericArgKind::Type(_) - if arg.has_param_types_or_consts() || arg.has_infer_types_or_consts() => - { + GenericArgKind::Type(_) if arg.has_non_region_param() || arg.has_non_region_infer() => { tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, name: ty::BoundVar::from_usize(idx), })) .into() } - GenericArgKind::Const(ct) - if ct.has_infer_types_or_consts() || ct.has_param_types_or_consts() => - { + GenericArgKind::Const(ct) if ct.has_non_region_infer() || ct.has_non_region_param() => { let ty = ct.ty(); // If the type references param or infer, replace that too... - if ty.has_param_types_or_consts() || ty.has_infer_types_or_consts() { + if ty.has_non_region_param() || ty.has_non_region_infer() { bug!("const `{ct}`'s type should not reference params or types"); } tcx.mk_const(ty::ConstS { diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index bab4f3e9e..600f94f09 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -25,7 +25,9 @@ use crate::infer::combine::ConstEquateRelation; use crate::infer::InferCtxt; use crate::infer::{ConstVarValue, ConstVariableValue}; use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::traits::PredicateObligation; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -44,7 +46,7 @@ pub struct TypeRelating<'me, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, /// Callback to use when we deduce an outlives relationship. delegate: D, @@ -91,11 +93,9 @@ pub trait TypeRelatingDelegate<'tcx> { ); fn const_equate(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); - fn register_opaque_type( + fn register_opaque_type_obligations( &mut self, - a: Ty<'tcx>, - b: Ty<'tcx>, - a_is_expected: bool, + obligations: Vec<PredicateObligation<'tcx>>, ) -> Result<(), TypeError<'tcx>>; /// Creates a new universe index. Used when instantiating placeholders. @@ -149,11 +149,7 @@ impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { - pub fn new( - infcx: &'me InferCtxt<'me, 'tcx>, - delegate: D, - ambient_variance: ty::Variance, - ) -> Self { + pub fn new(infcx: &'me InferCtxt<'tcx>, delegate: D, ambient_variance: ty::Variance) -> Self { Self { infcx, delegate, @@ -357,7 +353,7 @@ where // In NLL, we don't have type inference variables // floating around, so we can do this rather imprecise // variant of the occurs-check. - assert!(!generalized_ty.has_infer_types_or_consts()); + assert!(!generalized_ty.has_non_region_infer()); } self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); @@ -396,6 +392,37 @@ where generalizer.relate(value, value) } + + fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + let mut generalize = |ty, ty_is_expected| { + let var = self.infcx.next_ty_var_id_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.delegate.span(), + }, + ty::UniverseIndex::ROOT, + ); + if ty_is_expected { + self.relate_ty_var((ty, var)) + } else { + self.relate_ty_var((var, ty)) + } + }; + let (a, b) = match (a.kind(), b.kind()) { + (&ty::Opaque(..), _) => (a, generalize(b, false)?), + (_, &ty::Opaque(..)) => (generalize(a, true)?, b), + _ => unreachable!(), + }; + let cause = ObligationCause::dummy_with_span(self.delegate.span()); + let obligations = self + .infcx + .handle_opaque_type(a, b, true, &cause, self.delegate.param_env())? + .obligations; + self.delegate.register_opaque_type_obligations(obligations)?; + trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated"); + Ok(a) + } } /// When we instantiate an inference variable with a value in @@ -516,7 +543,7 @@ where true } - #[instrument(skip(self, info), level = "trace")] + #[instrument(skip(self, info), level = "trace", ret)] fn relate_with_variance<T: Relate<'tcx>>( &mut self, variance: ty::Variance, @@ -534,8 +561,6 @@ where self.ambient_variance = old_ambient_variance; - debug!(?r); - Ok(r) } @@ -572,32 +597,16 @@ where (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), (&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => { - self.infcx.super_combine_tys(self, a, b) + infcx.super_combine_tys(self, a, b).or_else(|err| { + self.tcx().sess.delay_span_bug( + self.delegate.span(), + "failure to relate an opaque to itself should result in an error later on", + ); + if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } + }) } (&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) if did.is_local() => { - let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; - let mut generalize = |ty, ty_is_expected| { - let var = infcx.next_ty_var_id_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.delegate.span(), - }, - ty::UniverseIndex::ROOT, - ); - if ty_is_expected { - self.relate_ty_var((ty, var)) - } else { - self.relate_ty_var((var, ty)) - } - }; - let (a, b) = match (a.kind(), b.kind()) { - (&ty::Opaque(..), _) => (a, generalize(b, false)?), - (_, &ty::Opaque(..)) => (generalize(a, true)?, b), - _ => unreachable!(), - }; - self.delegate.register_opaque_type(a, b, true)?; - trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated"); - Ok(a) + self.relate_opaques(a, b) } (&ty::Projection(projection_ty), _) @@ -859,7 +868,7 @@ struct TypeGeneralizer<'me, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, delegate: &'me mut D, diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index e579afbf3..a982f11f7 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -1,13 +1,16 @@ +use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{DefiningAnchor, InferCtxt, InferOk}; use crate::traits; +use hir::def::DefKind; use hir::def_id::{DefId, LocalDefId}; use hir::{HirId, OpaqueTyOrigin}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::vec_map::VecMap; use rustc_hir as hir; use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::subst::{GenericArgKind, Subst}; +use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{ self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, @@ -39,7 +42,7 @@ pub struct OpaqueTypeDecl<'tcx> { pub origin: hir::OpaqueTyOrigin, } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// This is a backwards compatibility hack to prevent breaking changes from /// lazy TAIT around RPIT handling. pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<'tcx>>( @@ -72,7 +75,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // for opaque types, and then use that kind to fix the spans for type errors // that we see later on. let ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, + kind: TypeVariableOriginKind::OpaqueTypeInference(def_id), span, }); obligations.extend( @@ -100,7 +103,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return Ok(InferOk { value: (), obligations: vec![] }); } let (a, b) = if a_is_expected { (a, b) } else { (b, a) }; - let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { + let process = |a: Ty<'tcx>, b: Ty<'tcx>, a_is_expected| match *a.kind() { ty::Opaque(def_id, substs) if def_id.is_local() => { let def_id = def_id.expect_local(); let origin = match self.defining_use_anchor { @@ -153,22 +156,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(OpaqueTyOrigin::TyAlias) = did2.as_local().and_then(|did2| self.opaque_type_origin(did2, cause.span)) { - self.tcx - .sess - .struct_span_err( - cause.span, - "opaque type's hidden type cannot be another opaque type from the same scope", - ) - .span_label(cause.span, "one of the two opaque types used here has to be outside its defining scope") - .span_note( - self.tcx.def_span(def_id), - "opaque type whose hidden type is being assigned", - ) - .span_note( - self.tcx.def_span(did2), - "opaque type being used as hidden type", - ) - .emit(); + self.tcx.sess.emit_err(OpaqueHiddenTypeDiag { + span: cause.span, + hidden_type: self.tcx.def_span(did2), + opaque_type: self.tcx.def_span(def_id), + }); } } Some(self.register_hidden_type( @@ -177,25 +169,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { param_env, b, origin, + a_is_expected, )) } _ => None, }; - if let Some(res) = process(a, b) { + if let Some(res) = process(a, b, true) { res - } else if let Some(res) = process(b, a) { + } else if let Some(res) = process(b, a, false) { res } else { - // Rerun equality check, but this time error out due to - // different types. - match self.at(cause, param_env).define_opaque_types(false).eq(a, b) { - Ok(_) => span_bug!( - cause.span, - "opaque types are never equal to anything but themselves: {:#?}", - (a.kind(), b.kind()) - ), - Err(e) => Err(e), - } + let (a, b) = self.resolve_vars_if_possible((a, b)); + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) } } @@ -400,7 +385,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }); } - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> { let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let parent_def_id = match self.defining_use_anchor { @@ -431,16 +416,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { in_definition_scope.then_some(*origin) } - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { - let origin = match self.tcx.hir().expect_item(def_id).kind { + match self.tcx.hir().expect_item(def_id).kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin, ref itemkind => { span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind) } - }; - trace!(?origin); - origin + } } } @@ -530,15 +513,16 @@ impl UseKind { } } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { #[instrument(skip(self), level = "debug")] - pub fn register_hidden_type( + fn register_hidden_type( &self, opaque_type_key: OpaqueTypeKey<'tcx>, cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, hidden_ty: Ty<'tcx>, origin: hir::OpaqueTyOrigin, + a_is_expected: bool, ) -> InferResult<'tcx, ()> { let tcx = self.tcx; let OpaqueTypeKey { def_id, substs } = opaque_type_key; @@ -557,21 +541,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin, ); if let Some(prev) = prev { - obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations; + obligations = + self.at(&cause, param_env).eq_exp(a_is_expected, prev, hidden_ty)?.obligations; } let item_bounds = tcx.bound_explicit_item_bounds(def_id.to_def_id()); - for predicate in item_bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { - debug!(?predicate); - let predicate = predicate.subst(tcx, substs); - + for (predicate, _) in item_bounds.subst_iter_copied(tcx, substs) { let predicate = predicate.fold_with(&mut BottomUpFolder { tcx, ty_op: |ty| match *ty.kind() { // We can't normalize associated types from `rustc_infer`, // but we can eagerly register inference variables for them. - ty::Projection(projection_ty) if !projection_ty.has_escaping_bound_vars() => { + // FIXME(RPITIT): Don't replace RPITITs with inference vars. + ty::Projection(projection_ty) + if !projection_ty.has_escaping_bound_vars() + && tcx.def_kind(projection_ty.item_def_id) + != DefKind::ImplTraitPlaceholder => + { self.infer_projection( param_env, projection_ty, @@ -587,6 +574,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { hidden_ty } + // FIXME(RPITIT): This can go away when we move to associated types + ty::Projection(proj) + if def_id.to_def_id() == proj.item_def_id && substs == proj.substs => + { + hidden_ty + } _ => ty, }, lt_op: |lt| lt, @@ -635,7 +628,7 @@ fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hi let scope = tcx.hir().get_defining_scope(opaque_hir_id); // We walk up the node tree until we hit the root or the scope of the opaque type. while hir_id != scope && hir_id != hir::CRATE_HIR_ID { - hir_id = tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(hir_id)); + hir_id = tcx.hir().get_parent_item(hir_id).into(); } // Syntactically, we are allowed to define the concrete type if: let res = hir_id == scope; diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index fb12da0cc..4d124554a 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -29,7 +29,7 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { } } - #[instrument(level = "debug")] + #[instrument(level = "debug", ret)] pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> { std::mem::take(&mut self.opaque_types) } diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index b2d7f4a66..14ee9f051 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -3,8 +3,9 @@ // RFC for reference. use rustc_data_structures::sso::SsoHashSet; +use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, SubstsRef, Ty, TyCtxt, TypeVisitable}; use smallvec::{smallvec, SmallVec}; #[derive(Debug)] @@ -45,6 +46,8 @@ pub enum Component<'tcx> { // them. This gives us room to improve the regionck reasoning in // the future without breaking backwards compat. EscapingProjection(Vec<Component<'tcx>>), + + Opaque(DefId, SubstsRef<'tcx>), } /// Push onto `out` all the things that must outlive `'a` for the condition @@ -120,6 +123,17 @@ fn compute_components<'tcx>( out.push(Component::Param(p)); } + // Ignore lifetimes found in opaque types. Opaque types can + // have lifetimes in their substs which their hidden type doesn't + // actually use. If we inferred that an opaque type is outlived by + // its parameter lifetimes, then we could prove that any lifetime + // outlives any other lifetime, which is unsound. + // See https://github.com/rust-lang/rust/issues/84305 for + // more details. + ty::Opaque(def_id, substs) => { + out.push(Component::Opaque(def_id, substs)); + }, + // For projections, we prefer to generate an obligation like // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the // regionck more ways to prove that it holds. However, @@ -168,7 +182,6 @@ fn compute_components<'tcx>( ty::Float(..) | // OutlivesScalar ty::Never | // ... ty::Adt(..) | // OutlivesNominalType - ty::Opaque(..) | // OutlivesNominalType (ish) ty::Foreign(..) | // OutlivesNominalType ty::Str | // OutlivesScalar (ish) ty::Slice(..) | // ... diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index b2decd64f..33543135d 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -2,6 +2,7 @@ use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; use super::explicit_outlives_bounds; @@ -51,23 +52,49 @@ pub struct OutlivesEnvironment<'tcx> { region_bound_pairs: RegionBoundPairs<'tcx>, } +/// Builder of OutlivesEnvironment. +#[derive(Debug)] +struct OutlivesEnvironmentBuilder<'tcx> { + param_env: ty::ParamEnv<'tcx>, + region_relation: TransitiveRelationBuilder<Region<'tcx>>, + region_bound_pairs: RegionBoundPairs<'tcx>, +} + /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. pub type RegionBoundPairs<'tcx> = FxIndexSet<ty::OutlivesPredicate<GenericKind<'tcx>, Region<'tcx>>>; -impl<'a, 'tcx> OutlivesEnvironment<'tcx> { - pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { - let mut env = OutlivesEnvironment { +impl<'tcx> OutlivesEnvironment<'tcx> { + /// Create a builder using `ParamEnv` and add explicit outlives bounds into it. + fn builder(param_env: ty::ParamEnv<'tcx>) -> OutlivesEnvironmentBuilder<'tcx> { + let mut builder = OutlivesEnvironmentBuilder { param_env, - free_region_map: Default::default(), + region_relation: Default::default(), region_bound_pairs: Default::default(), }; - env.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); + builder.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); - env + builder + } + + #[inline] + /// Create a new `OutlivesEnvironment` without extra outlives bounds. + pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { + Self::builder(param_env).build() + } + + /// Create a new `OutlivesEnvironment` with extra outlives bounds. + pub fn with_bounds( + param_env: ty::ParamEnv<'tcx>, + infcx: Option<&InferCtxt<'tcx>>, + extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>, + ) -> Self { + let mut builder = Self::builder(param_env); + builder.add_outlives_bounds(infcx, extra_bounds); + builder.build() } /// Borrows current value of the `free_region_map`. @@ -79,6 +106,18 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { &self.region_bound_pairs } +} + +impl<'tcx> OutlivesEnvironmentBuilder<'tcx> { + #[inline] + #[instrument(level = "debug")] + fn build(self) -> OutlivesEnvironment<'tcx> { + OutlivesEnvironment { + param_env: self.param_env, + free_region_map: FreeRegionMap { relation: self.region_relation.freeze() }, + region_bound_pairs: self.region_bound_pairs, + } + } /// Processes outlives bounds that are known to hold, whether from implied or other sources. /// @@ -86,11 +125,8 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// contain inference variables, it must be supplied, in which /// case we will register "givens" on the inference context. (See /// `RegionConstraintData`.) - pub fn add_outlives_bounds<I>( - &mut self, - infcx: Option<&InferCtxt<'a, 'tcx>>, - outlives_bounds: I, - ) where + fn add_outlives_bounds<I>(&mut self, infcx: Option<&InferCtxt<'tcx>>, outlives_bounds: I) + where I: IntoIterator<Item = OutlivesBound<'tcx>>, { // Record relationships such as `T:'x` that don't go into the @@ -106,6 +142,10 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { self.region_bound_pairs .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } + OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => { + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a)); + } OutlivesBound::RegionSubRegion(r_a, r_b) => { if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) { infcx @@ -122,7 +162,9 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { // system to be more general and to make use // of *every* relationship that arises here, // but presently we do not.) - self.free_region_map.relate_regions(r_a, r_b); + if r_a.is_free_or_static() && r_b.is_free() { + self.region_relation.add(r_a, r_b) + } } } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 2a085288f..2d19d1823 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -9,7 +9,7 @@ pub mod verify; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; -#[instrument(level = "debug", skip(param_env))] +#[instrument(level = "debug", skip(param_env), ret)] pub fn explicit_outlives_bounds<'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index ad052f58c..6ca884799 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -68,12 +68,14 @@ use crate::infer::{ }; use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::undo_log::UndoLogs; +use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; +use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable}; use smallvec::smallvec; -impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Registers that the given region obligation must be resolved /// from within the scope of `body_id`. These regions are enqueued /// and later processed by regionck, when full type information is @@ -92,12 +94,14 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { sub_region: Region<'tcx>, cause: &ObligationCause<'tcx>, ) { + debug!(?sup_type, ?sub_region, ?cause); let origin = SubregionOrigin::from_obligation_cause(cause, || { infer::RelateParamBound( cause.span, sup_type, match cause.code().peel_derives() { - ObligationCauseCode::BindingObligation(_, span) => Some(*span), + ObligationCauseCode::BindingObligation(_, span) + | ObligationCauseCode::ExprBindingObligation(_, span, ..) => Some(*span), _ => None, }, ) @@ -161,7 +165,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let outlives = &mut TypeOutlives::new(self, self.tcx, ®ion_bound_pairs, None, param_env); - outlives.type_must_outlive(origin, sup_type, sub_region); + let category = origin.to_constraint_category(); + outlives.type_must_outlive(origin, sup_type, sub_region, category); } } @@ -178,7 +183,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { outlives_env.param_env, ); - self.resolve_regions_and_report_errors(generic_param_scope, outlives_env) + self.err_ctxt().resolve_regions_and_report_errors(generic_param_scope, outlives_env) } } @@ -205,6 +210,7 @@ pub trait TypeOutlivesDelegate<'tcx> { origin: SubregionOrigin<'tcx>, a: ty::Region<'tcx>, b: ty::Region<'tcx>, + constraint_category: ConstraintCategory<'tcx>, ); fn push_verify( @@ -247,19 +253,19 @@ where /// - `origin`, the reason we need this constraint /// - `ty`, the type `T` /// - `region`, the region `'a` + #[instrument(level = "debug", skip(self))] pub fn type_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, region: ty::Region<'tcx>, + category: ConstraintCategory<'tcx>, ) { - debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", ty, region, origin); - assert!(!ty.has_escaping_bound_vars()); let mut components = smallvec![]; push_outlives_components(self.tcx, ty, &mut components); - self.components_must_outlive(origin, &components, region); + self.components_must_outlive(origin, &components, region, category); } fn components_must_outlive( @@ -267,21 +273,25 @@ where origin: infer::SubregionOrigin<'tcx>, components: &[Component<'tcx>], region: ty::Region<'tcx>, + category: ConstraintCategory<'tcx>, ) { for component in components.iter() { let origin = origin.clone(); match component { Component::Region(region1) => { - self.delegate.push_sub_region_constraint(origin, region, *region1); + self.delegate.push_sub_region_constraint(origin, region, *region1, category); } Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, *param_ty); } + Component::Opaque(def_id, substs) => { + self.opaque_must_outlive(*def_id, substs, origin, region) + } Component::Projection(projection_ty) => { self.projection_must_outlive(origin, region, *projection_ty); } Component::EscapingProjection(subcomponents) => { - self.components_must_outlive(origin, &subcomponents, region); + self.components_must_outlive(origin, &subcomponents, region, category); } Component::UnresolvedInferenceVariable(v) => { // ignore this, we presume it will yield an error @@ -308,17 +318,69 @@ where ); let generic = GenericKind::Param(param_ty); - let verify_bound = self.verify_bound.generic_bound(generic); + let verify_bound = self.verify_bound.param_bound(param_ty); self.delegate.push_verify(origin, generic, region, verify_bound); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] + fn opaque_must_outlive( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + ) { + self.generic_must_outlive( + origin, + region, + GenericKind::Opaque(def_id, substs), + def_id, + substs, + true, + |ty| match *ty.kind() { + ty::Opaque(def_id, substs) => (def_id, substs), + _ => bug!("expected only projection types from env, not {:?}", ty), + }, + ); + } + + #[instrument(level = "debug", skip(self))] fn projection_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, ) { + self.generic_must_outlive( + origin, + region, + GenericKind::Projection(projection_ty), + projection_ty.item_def_id, + projection_ty.substs, + false, + |ty| match ty.kind() { + ty::Projection(projection_ty) => (projection_ty.item_def_id, projection_ty.substs), + _ => bug!("expected only projection types from env, not {:?}", ty), + }, + ); + } + + #[instrument(level = "debug", skip(self, filter))] + fn generic_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + generic: GenericKind<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + is_opaque: bool, + filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>), + ) { + // An optimization for a common case with opaque types. + if substs.is_empty() { + return; + } + // This case is thorny for inference. The fundamental problem is // that there are many cases where we have choice, and inference // doesn't like choice (the current region inference in @@ -337,16 +399,15 @@ where // These are guaranteed to apply, no matter the inference // results. let trait_bounds: Vec<_> = - self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect(); + self.verify_bound.declared_region_bounds(def_id, substs).collect(); debug!(?trait_bounds); // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. - let mut approx_env_bounds = - self.verify_bound.projection_approx_declared_bounds_from_env(projection_ty); - debug!("projection_must_outlive: approx_env_bounds={:?}", approx_env_bounds); + let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic); + debug!(?approx_env_bounds); // Remove outlives bounds that we get from the environment but // which are also deducible from the trait. This arises (cc @@ -360,14 +421,8 @@ where // If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait` // will be invoked with `['b => ^1]` and so we will get `^1` returned. let bound = bound_outlives.skip_binder(); - match *bound.0.kind() { - ty::Projection(projection_ty) => self - .verify_bound - .projection_declared_bounds_from_trait(projection_ty) - .all(|r| r != bound.1), - - _ => panic!("expected only projection types from env, not {:?}", bound.0), - } + let (def_id, substs) = filter(bound.0); + self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1) }); // If declared bounds list is empty, the only applicable rule is @@ -384,23 +439,11 @@ where // the problem is to add `T: 'r`, which isn't true. So, if there are no // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. - let needs_infer = projection_ty.needs_infer(); - if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer { - debug!("projection_must_outlive: no declared bounds"); - - for k in projection_ty.substs { - match k.unpack() { - GenericArgKind::Lifetime(lt) => { - self.delegate.push_sub_region_constraint(origin.clone(), region, lt); - } - GenericArgKind::Type(ty) => { - self.type_must_outlive(origin.clone(), ty, region); - } - GenericArgKind::Const(_) => { - // Const parameters don't impose constraints. - } - } - } + let needs_infer = substs.needs_infer(); + if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) { + debug!("no declared bounds"); + + self.substs_must_outlive(substs, origin, region); return; } @@ -430,9 +473,10 @@ where .all(|b| b == Some(trait_bounds[0])) { let unique_bound = trait_bounds[0]; - debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound); - debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.delegate.push_sub_region_constraint(origin, region, unique_bound); + debug!(?unique_bound); + debug!("unique declared bound appears in trait ref"); + let category = origin.to_constraint_category(); + self.delegate.push_sub_region_constraint(origin, region, unique_bound, category); return; } @@ -441,19 +485,51 @@ where // projection outlive; in some cases, this may add insufficient // edges into the inference graph, leading to inference failures // even though a satisfactory solution exists. - let generic = GenericKind::Projection(projection_ty); - let verify_bound = self.verify_bound.generic_bound(generic); + let verify_bound = self.verify_bound.projection_opaque_bounds( + generic, + def_id, + substs, + &mut Default::default(), + ); debug!("projection_must_outlive: pushing {:?}", verify_bound); self.delegate.push_verify(origin, generic, region, verify_bound); } + + fn substs_must_outlive( + &mut self, + substs: SubstsRef<'tcx>, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + ) { + let constraint = origin.to_constraint_category(); + for k in substs { + match k.unpack() { + GenericArgKind::Lifetime(lt) => { + self.delegate.push_sub_region_constraint( + origin.clone(), + region, + lt, + constraint, + ); + } + GenericArgKind::Type(ty) => { + self.type_must_outlive(origin.clone(), ty, region, constraint); + } + GenericArgKind::Const(_) => { + // Const parameters don't impose constraints. + } + } + } + } } -impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> { +impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'tcx> { fn push_sub_region_constraint( &mut self, origin: SubregionOrigin<'tcx>, a: ty::Region<'tcx>, b: ty::Region<'tcx>, + _constraint_category: ConstraintCategory<'tcx>, ) { self.sub_regions(origin, a, b) } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 772e297b7..a5c21f0fb 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -34,7 +34,7 @@ use crate::infer::region_constraints::VerifyIfEq; /// like are used. This is a particular challenge since this function is invoked /// very late in inference and hence cannot make use of the normal inference /// machinery. -#[tracing::instrument(level = "debug", skip(tcx, param_env))] +#[instrument(level = "debug", skip(tcx, param_env))] pub fn extract_verify_if_eq<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -71,7 +71,7 @@ pub fn extract_verify_if_eq<'tcx>( } /// True if a (potentially higher-ranked) outlives -#[tracing::instrument(level = "debug", skip(tcx, param_env))] +#[instrument(level = "debug", skip(tcx, param_env))] pub(super) fn can_match_erased_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -110,7 +110,7 @@ impl<'tcx> Match<'tcx> { /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern /// is already bound to a different value. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn bind( &mut self, br: ty::BoundRegion, @@ -174,7 +174,14 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { #[instrument(skip(self), level = "debug")] fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - if pattern == value { Ok(pattern) } else { relate::super_relate_tys(self, pattern, value) } + if let ty::Error(_) = pattern.kind() { + // Unlike normal `TypeRelation` rules, `ty::Error` does not equal any type. + self.no_match() + } else if pattern == value { + Ok(pattern) + } else { + relate::super_relate_tys(self, pattern, value) + } } #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index c7d7ef40d..f470b2eb8 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -2,11 +2,10 @@ use crate::infer::outlives::components::{compute_components_recursive, Component use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::region_constraints::VerifyIfEq; use crate::infer::{GenericKind, VerifyBound}; -use rustc_data_structures::captures::Captures; use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, Subst}; -use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt}; +use rustc_middle::ty::GenericArg; +use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, SubstsRef, Ty, TyCtxt}; use smallvec::smallvec; @@ -38,25 +37,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { Self { tcx, region_bound_pairs, implicit_region_bound, param_env } } - /// Returns a "verify bound" that encodes what we know about - /// `generic` and the regions it outlives. - pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { - let mut visited = SsoHashSet::new(); - match generic { - GenericKind::Param(param_ty) => self.param_bound(param_ty), - GenericKind::Projection(projection_ty) => { - self.projection_bound(projection_ty, &mut visited) - } - } - } - - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { - debug!("param_bound(param_ty={:?})", param_ty); - + #[instrument(level = "debug", skip(self))] + pub fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { // Start with anything like `T: 'a` we can scrape from the // environment. If the environment contains something like // `for<'a> T: 'a`, then we know that `T` outlives everything. let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty); + debug!(?declared_bounds_from_env); let mut param_bounds = vec![]; for declared_bound in declared_bounds_from_env { let bound_region = declared_bound.map_bound(|outlives| outlives.1); @@ -65,6 +52,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { param_bounds.push(VerifyBound::OutlivedBy(region)); } else { // This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here. + debug!("found that {param_ty:?} outlives any lifetime, returning empty vector"); return VerifyBound::AllBounds(vec![]); } } @@ -72,6 +60,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // Add in the default bound of fn body that applies to all in // scope type parameters: if let Some(r) = self.implicit_region_bound { + debug!("adding implicit region bound of {r:?}"); param_bounds.push(VerifyBound::OutlivedBy(r)); } @@ -103,41 +92,31 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// the clause from the environment only applies if `'0 = 'a`, /// which we don't know yet. But we would still include `'b` in /// this list. - pub fn projection_approx_declared_bounds_from_env( + pub fn approx_declared_bounds_from_env( &self, - projection_ty: ty::ProjectionTy<'tcx>, + generic: GenericKind<'tcx>, ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> { - let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); + let projection_ty = generic.to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(projection_ty); self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty) } - /// Searches the where-clauses in scope for regions that - /// `projection_ty` is known to outlive. Currently requires an - /// exact match. - pub fn projection_declared_bounds_from_trait( + #[instrument(level = "debug", skip(self, visited))] + pub fn projection_opaque_bounds( &self, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> { - self.declared_projection_bounds_from_trait(projection_ty) - } - - pub fn projection_bound( - &self, - projection_ty: ty::ProjectionTy<'tcx>, + generic: GenericKind<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, visited: &mut SsoHashSet<GenericArg<'tcx>>, ) -> VerifyBound<'tcx> { - debug!("projection_bound(projection_ty={:?})", projection_ty); - - let projection_ty_as_ty = - self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + let generic_ty = generic.to_ty(self.tcx); // Search the env for where clauses like `P: 'a`. - let env_bounds = self - .projection_approx_declared_bounds_from_env(projection_ty) + let projection_opaque_bounds = self + .approx_declared_bounds_from_env(generic) .into_iter() .map(|binder| { - if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty { + if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == generic_ty { // Micro-optimize if this is an exact match (this // occurs often when there are no region variables // involved). @@ -147,21 +126,19 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { VerifyBound::IfEq(verify_if_eq_b) } }); - // Extend with bounds that we can find from the trait. - let trait_bounds = self - .projection_declared_bounds_from_trait(projection_ty) - .map(|r| VerifyBound::OutlivedBy(r)); + let trait_bounds = + self.declared_region_bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)); // see the extensive comment in projection_must_outlive let recursive_bound = { let mut components = smallvec![]; - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - compute_components_recursive(self.tcx, ty.into(), &mut components, visited); + compute_components_recursive(self.tcx, generic_ty.into(), &mut components, visited); self.bound_from_components(&components, visited) }; - VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) + VerifyBound::AnyBound(projection_opaque_bounds.chain(trait_bounds).collect()) + .or(recursive_bound) } fn bound_from_components( @@ -193,7 +170,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { match *component { Component::Region(lt) => VerifyBound::OutlivedBy(lt), Component::Param(param_ty) => self.param_bound(param_ty), - Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited), + Component::Opaque(did, substs) => self.projection_opaque_bounds( + GenericKind::Opaque(did, substs), + did, + substs, + visited, + ), + Component::Projection(projection_ty) => self.projection_opaque_bounds( + GenericKind::Projection(projection_ty), + projection_ty.item_def_id, + projection_ty.substs, + visited, + ), Component::EscapingProjection(ref components) => { self.bound_from_components(components, visited) } @@ -291,30 +279,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// } /// ``` /// - /// then this function would return `'x`. This is subject to the - /// limitations around higher-ranked bounds described in - /// `region_bounds_declared_on_associated_item`. - fn declared_projection_bounds_from_trait( - &self, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> { - debug!("projection_bounds(projection_ty={:?})", projection_ty); - let tcx = self.tcx; - self.region_bounds_declared_on_associated_item(projection_ty.item_def_id) - .map(move |r| EarlyBinder(r).subst(tcx, projection_ty.substs)) - } - - /// Given the `DefId` of an associated item, returns any region - /// bounds attached to that associated item from the trait definition. - /// - /// For example: - /// - /// ```rust - /// trait Foo<'a> { - /// type Bar: 'a; - /// } - /// ``` - /// /// If we were given the `DefId` of `Foo::Bar`, we would return /// `'a`. You could then apply the substitutions from the /// projection to convert this into your namespace. This also @@ -334,17 +298,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// This is for simplicity, and because we are not really smart /// enough to cope with such bounds anywhere. - fn region_bounds_declared_on_associated_item( + pub fn declared_region_bounds( &self, - assoc_item_def_id: DefId, + def_id: DefId, + substs: SubstsRef<'tcx>, ) -> impl Iterator<Item = ty::Region<'tcx>> { let tcx = self.tcx; - let bounds = tcx.item_bounds(assoc_item_def_id); + let bounds = tcx.item_bounds(def_id); + trace!("{:#?}", bounds); bounds .into_iter() .filter_map(|p| p.to_opt_type_outlives()) .filter_map(|p| p.no_bound_vars()) .map(|b| b.1) + .map(move |r| EarlyBinder(r).subst(tcx, substs)) } /// Searches through a predicate list for a predicate `T: 'a`. diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index b45a6514d..9f12bc972 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -6,7 +6,7 @@ use crate::traits::{Obligation, PredicateObligation}; use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::InferCtxt; -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Instead of normalizing an associated type projection, /// this function generates an inference variable and registers /// an obligation that this inference variable must be the result diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 397efe6ee..90858e307 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -66,7 +66,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { tcx: TyCtxt<'tcx>, overly_polymorphic: bool, max_universe: ty::UniverseIndex, - snapshot: &CombinedSnapshot<'_, 'tcx>, + snapshot: &CombinedSnapshot<'tcx>, ) -> RelateResult<'tcx, ()> { debug!( "leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})", diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0d4472a1c..67b3da687 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -12,8 +12,10 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; +use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion}; +use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::ReStatic; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ReLateBound, ReVar}; @@ -168,6 +170,7 @@ pub struct Verify<'tcx> { pub enum GenericKind<'tcx> { Param(ty::ParamTy), Projection(ty::ProjectionTy<'tcx>), + Opaque(DefId, SubstsRef<'tcx>), } /// Describes the things that some `GenericKind` value `G` is known to @@ -187,7 +190,7 @@ pub enum GenericKind<'tcx> { /// } /// ``` /// This is described with an `AnyRegion('a, 'b)` node. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, TypeFoldable, TypeVisitable)] pub enum VerifyBound<'tcx> { /// See [`VerifyIfEq`] docs IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>), @@ -426,21 +429,21 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { data } - pub fn data(&self) -> &RegionConstraintData<'tcx> { + pub(super) fn data(&self) -> &RegionConstraintData<'tcx> { &self.data } - pub fn start_snapshot(&mut self) -> RegionSnapshot { + pub(super) fn start_snapshot(&mut self) -> RegionSnapshot { debug!("RegionConstraintCollector: start_snapshot"); RegionSnapshot { any_unifications: self.any_unifications } } - pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { + pub(super) fn rollback_to(&mut self, snapshot: RegionSnapshot) { debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); self.any_unifications = snapshot.any_unifications; } - pub fn new_region_var( + pub(super) fn new_region_var( &mut self, universe: ty::UniverseIndex, origin: RegionVariableOrigin, @@ -455,12 +458,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } /// Returns the universe for the given variable. - pub fn var_universe(&self, vid: RegionVid) -> ty::UniverseIndex { + pub(super) fn var_universe(&self, vid: RegionVid) -> ty::UniverseIndex { self.var_infos[vid].universe } /// Returns the origin for the given variable. - pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { + pub(super) fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { self.var_infos[vid].origin } @@ -492,7 +495,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { self.undo_log.push(AddVerify(index)); } - pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { + pub(super) fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { // cannot add givens once regions are resolved if self.data.givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); @@ -501,7 +504,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } } - pub fn make_eqregion( + pub(super) fn make_eqregion( &mut self, origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, @@ -530,7 +533,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } } - pub fn member_constraint( + pub(super) fn member_constraint( &mut self, key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, @@ -554,7 +557,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } #[instrument(skip(self, origin), level = "debug")] - pub fn make_subregion( + pub(super) fn make_subregion( &mut self, origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, @@ -585,7 +588,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } } - pub fn verify_generic_bound( + pub(super) fn verify_generic_bound( &mut self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, @@ -595,7 +598,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { self.add_verify(Verify { kind, origin, region: sub, bound }); } - pub fn lub_regions( + pub(super) fn lub_regions( &mut self, tcx: TyCtxt<'tcx>, origin: SubregionOrigin<'tcx>, @@ -613,7 +616,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } } - pub fn glb_regions( + pub(super) fn glb_regions( &mut self, tcx: TyCtxt<'tcx>, origin: SubregionOrigin<'tcx>, @@ -634,7 +637,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } /// Resolves the passed RegionVid to the root RegionVid in the unification table - pub fn opportunistic_resolve_var(&mut self, rid: ty::RegionVid) -> ty::RegionVid { + pub(super) fn opportunistic_resolve_var(&mut self, rid: ty::RegionVid) -> ty::RegionVid { self.unification_table().find(rid).vid } @@ -699,7 +702,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => { ty::UniverseIndex::ROOT } - ty::ReEmpty(ui) => ui, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReVar(vid) => self.var_universe(vid), ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), @@ -748,6 +750,9 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => write!(f, "{:?}", p), GenericKind::Projection(ref p) => write!(f, "{:?}", p), + GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| { + write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap())) + }), } } } @@ -757,6 +762,9 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => write!(f, "{}", p), GenericKind::Projection(ref p) => write!(f, "{}", p), + GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| { + write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap())) + }), } } } @@ -766,6 +774,7 @@ impl<'tcx> GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => p.to_ty(tcx), GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), + GenericKind::Opaque(def_id, substs) => tcx.mk_opaque(def_id, substs), } } } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 3d99f0958..4db4ff238 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,6 +1,5 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{FixupError, FixupResult, InferCtxt, Span}; -use rustc_middle::mir; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor}; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable, TypeVisitable}; @@ -16,12 +15,12 @@ use std::ops::ControlFlow; /// useful for printing messages etc but also required at various /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { OpportunisticVarResolver { infcx } } } @@ -32,7 +31,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_infer_types_or_consts() { + if !t.has_non_region_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); @@ -41,17 +40,13 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { } fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> { - if !ct.has_infer_types_or_consts() { + if !ct.has_non_region_infer() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { let ct = self.infcx.shallow_resolve(ct); ct.super_fold_with(self) } } - - fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { - constant.super_fold_with(self) - } } /// The opportunistic region resolver opportunistically resolves regions @@ -62,11 +57,11 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { /// If you want to resolve type and const variables as well, call /// [InferCtxt::resolve_vars_if_possible] first. pub struct OpportunisticRegionResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> OpportunisticRegionResolver<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { OpportunisticRegionResolver { infcx } } } @@ -116,11 +111,11 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { /// It does not construct the fully resolved type (which might /// involve some hashing and so forth). pub struct UnresolvedTypeFinder<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { UnresolvedTypeFinder { infcx } } } @@ -167,7 +162,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { /// Full type resolution replaces all type and region variables with /// their concrete results. If any variable cannot be replaced (never unified, etc) /// then an `Err` result is returned. -pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, value: T) -> FixupResult<'tcx, T> +pub fn fully_resolve<'tcx, T>(infcx: &InferCtxt<'tcx>, value: T) -> FixupResult<'tcx, T> where T: TypeFoldable<'tcx>, { @@ -177,7 +172,7 @@ where // N.B. This type is not public because the protocol around checking the // `err` field is not enforceable otherwise. struct FullTypeResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index b27571275..97354ba5d 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -2,7 +2,6 @@ use super::combine::{CombineFields, RelationDir}; use super::SubregionOrigin; use crate::infer::combine::ConstEquateRelation; -use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::traits::Obligation; use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::TypeVisitable; @@ -11,8 +10,8 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. -pub struct Sub<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, +pub struct Sub<'combine, 'a, 'tcx> { + fields: &'combine mut CombineFields<'a, 'tcx>, a_is_expected: bool, } @@ -129,30 +128,37 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { (&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) if self.fields.define_opaque_types && did.is_local() => { - let mut generalize = |ty, ty_is_expected| { - let var = infcx.next_ty_var_id_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.fields.trace.cause.span, - }, - ty::UniverseIndex::ROOT, - ); - self.fields.instantiate(ty, RelationDir::SubtypeOf, var, ty_is_expected)?; - Ok(infcx.tcx.mk_ty_var(var)) - }; - let (a, b) = if self.a_is_expected { (a, b) } else { (b, a) }; - let (a, b) = match (a.kind(), b.kind()) { - (&ty::Opaque(..), _) => (a, generalize(b, true)?), - (_, &ty::Opaque(..)) => (generalize(a, false)?, b), - _ => unreachable!(), - }; self.fields.obligations.extend( infcx - .handle_opaque_type(a, b, true, &self.fields.trace.cause, self.param_env())? + .handle_opaque_type( + a, + b, + self.a_is_expected, + &self.fields.trace.cause, + self.param_env(), + )? .obligations, ); Ok(a) } + // Optimization of GeneratorWitness relation since we know that all + // free regions are replaced with bound regions during construction. + // This greatly speeds up subtyping of GeneratorWitness. + (&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => { + let a_types = infcx.tcx.anonymize_bound_vars(a_types); + let b_types = infcx.tcx.anonymize_bound_vars(b_types); + if a_types.bound_vars() == b_types.bound_vars() { + let (a_types, b_types) = infcx.replace_bound_vars_with_placeholders( + a_types.map_bound(|a_types| (a_types, b_types.skip_binder())), + ); + for (a, b) in std::iter::zip(a_types, b_types) { + self.relate(a, b)?; + } + Ok(a) + } else { + Err(ty::error::TypeError::Sorts(ty::relate::expected_found(self, a, b))) + } + } _ => { self.fields.infcx.super_combine_tys(self, a, b)?; diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index a0e2965b6..7ff086452 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -122,6 +122,7 @@ pub enum TypeVariableOriginKind { MiscVariable, NormalizeProjectionType, TypeInference, + OpaqueTypeInference(DefId), TypeParameterDefinition(Symbol, Option<DefId>), /// One of the upvars or closure kind parameters in a `ClosureSubsts` diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index 74a26ebc3..611961ab1 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -100,7 +100,7 @@ impl Default for InferCtxtUndoLogs<'_> { } /// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any -/// action that is convertable into an UndoLog (per the From impls above). +/// action that is convertible into an UndoLog (per the From impls above). impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx> where UndoLog<'tcx>: From<T>, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 7769a68ba..e040634ed 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -2,7 +2,7 @@ //! //! - **Type inference.** The type inference code can be found in the `infer` module; //! this code handles low-level equality and subtyping operations. The -//! type check pass in the compiler is found in the `rustc_typeck` crate. +//! type check pass in the compiler is found in the `rustc_hir_analysis` crate. //! //! For more information about how rustc works, see the [rustc dev guide]. //! @@ -17,9 +17,8 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extend_one)] -#![feature(label_break_value)] #![feature(let_chains)] -#![feature(let_else)] +#![feature(if_let_guard)] #![feature(min_specialization)] #![feature(never_type)] #![feature(try_blocks)] @@ -35,5 +34,6 @@ extern crate tracing; #[macro_use] extern crate rustc_middle; +mod errors; pub mod infer; pub mod traits; diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 736278ba0..b2b985a22 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -10,7 +10,7 @@ use super::{ObligationCause, PredicateObligation}; pub trait TraitEngine<'tcx>: 'tcx { fn normalize_projection_type( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, @@ -21,7 +21,7 @@ pub trait TraitEngine<'tcx>: 'tcx { /// parameters (except for `Self`). fn register_bound( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, def_id: DefId, @@ -41,14 +41,13 @@ pub trait TraitEngine<'tcx>: 'tcx { fn register_predicate_obligation( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ); - fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>>; + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>; - fn select_where_possible(&mut self, infcx: &InferCtxt<'_, 'tcx>) - -> Vec<FulfillmentError<'tcx>>; + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>; fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>; @@ -58,7 +57,7 @@ pub trait TraitEngine<'tcx>: 'tcx { pub trait TraitEngineExt<'tcx> { fn register_predicate_obligations( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, ); } @@ -66,7 +65,7 @@ pub trait TraitEngineExt<'tcx> { impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { fn register_predicate_obligations( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, ) { for obligation in obligations { diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 95b6c4ce1..f8b5009a5 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -10,7 +10,7 @@ use rustc_span::Span; use std::fmt; use std::iter; -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { pub fn report_extra_impl_obligation( &self, error_span: Span, diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 4df4de21a..c8600ded9 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -67,6 +67,14 @@ impl<'tcx> PredicateObligation<'tcx> { recursion_depth: self.recursion_depth, }) } + + pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> { + self.param_env = self.param_env.without_const(); + if let ty::PredicateKind::Trait(trait_pred) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() { + self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Trait(trait_pred.without_const()))); + } + self + } } impl<'tcx> TraitObligation<'tcx> { @@ -105,6 +113,8 @@ pub struct FulfillmentError<'tcx> { #[derive(Clone)] pub enum FulfillmentErrorCode<'tcx> { + /// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented. + CodeCycle(Vec<Obligation<'tcx, ty::Predicate<'tcx>>>), CodeSelectionError(SelectionError<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>), CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 573d2d1e3..1c6ab6a08 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -47,6 +47,7 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { write!(f, "CodeConstEquateError({:?}, {:?})", a, b) } super::CodeAmbiguity => write!(f, "Ambiguity"), + super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle), } } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index f5a1edf6d..e12c069dc 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -246,6 +246,13 @@ impl<'tcx> Elaborator<'tcx> { Component::UnresolvedInferenceVariable(_) => None, + Component::Opaque(def_id, substs) => { + let ty = tcx.mk_opaque(def_id, substs); + Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, r_min, + ))) + } + Component::Projection(projection) => { // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`. // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`. @@ -262,8 +269,9 @@ impl<'tcx> Elaborator<'tcx> { None } }) - .map(ty::Binder::dummy) - .map(|predicate_kind| predicate_kind.to_predicate(tcx)) + .map(|predicate_kind| { + bound_predicate.rebind(predicate_kind).to_predicate(tcx) + }) .filter(|&predicate| visited.insert(predicate)) .map(|predicate| { predicate_obligation( |