diff options
Diffstat (limited to 'compiler/rustc_infer')
48 files changed, 2634 insertions, 1104 deletions
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index aced787d6..02ac83a5e 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -15,7 +15,6 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } -rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_infer/locales/en-US.ftl b/compiler/rustc_infer/locales/en-US.ftl new file mode 100644 index 000000000..15780898d --- /dev/null +++ b/compiler/rustc_infer/locales/en-US.ftl @@ -0,0 +1,350 @@ +infer_opaque_hidden_type = + opaque type's hidden type cannot be another opaque type from the same scope + .label = one of the two opaque types used here has to be outside its defining scope + .opaque_type = opaque type whose hidden type is being assigned + .hidden_type = opaque type being used as hidden type + +infer_type_annotations_needed = {$source_kind -> + [closure] type annotations needed for the closure `{$source_name}` + [normal] type annotations needed for `{$source_name}` + *[other] type annotations needed +} + .label = type must be known at this point + +infer_label_bad = {$bad_kind -> + *[other] cannot infer type + [more_info] cannot infer {$prefix_kind -> + *[type] type for {$prefix} + [const_with_param] the value of const parameter + [const] the value of the constant + } `{$name}`{$has_parent -> + [true] {" "}declared on the {$parent_prefix} `{$parent_name}` + *[false] {""} + } +} + +infer_source_kind_subdiag_let = {$kind -> + [with_pattern] consider giving `{$name}` an explicit type + [closure] consider giving this closure parameter an explicit type + *[other] consider giving this pattern a type +}{$x_kind -> + [has_name] , where the {$prefix_kind -> + *[type] type for {$prefix} + [const_with_param] the value of const parameter + [const] the value of the constant + } `{$arg_name}` is specified + [underscore] , where the placeholders `_` are specified + *[empty] {""} +} + +infer_source_kind_subdiag_generic_label = + cannot infer {$is_type -> + [true] type + *[false] the value + } of the {$is_type -> + [true] type + *[false] const + } {$parent_exists -> + [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}` + *[false] parameter {$param_name} + } + +infer_source_kind_subdiag_generic_suggestion = + consider specifying the generic {$arg_count -> + [one] argument + *[other] arguments + } + +infer_source_kind_fully_qualified = + try using a fully qualified path to specify the expected types + +infer_source_kind_closure_return = + try giving this closure an explicit return type + +# generator_kind may need to be translated +infer_need_type_info_in_generator = + type inside {$generator_kind -> + [async_block] `async` block + [async_closure] `async` closure + [async_fn] `async fn` body + *[generator] generator + } must be known in this context + + +infer_subtype = ...so that the {$requirement -> + [method_compat] method type is compatible with trait + [type_compat] associated type is compatible with trait + [const_compat] const is compatible with trait + [expr_assignable] expression is assignable + [if_else_different] `if` and `else` have incompatible types + [no_else] `if` missing an `else` returns `()` + [fn_main_correct_type] `main` function has the correct type + [fn_start_correct_type] `#[start]` function has the correct type + [intristic_correct_type] intrinsic has the correct type + [method_correct_type] method receiver has the correct type + *[other] types are compatible +} +infer_subtype_2 = ...so that {$requirement -> + [method_compat] method type is compatible with trait + [type_compat] associated type is compatible with trait + [const_compat] const is compatible with trait + [expr_assignable] expression is assignable + [if_else_different] `if` and `else` have incompatible types + [no_else] `if` missing an `else` returns `()` + [fn_main_correct_type] `main` function has the correct type + [fn_start_correct_type] `#[start]` function has the correct type + [intristic_correct_type] intrinsic has the correct type + [method_correct_type] method receiver has the correct type + *[other] types are compatible +} + +infer_reborrow = ...so that reference does not outlive borrowed content +infer_reborrow_upvar = ...so that closure can access `{$name}` +infer_relate_object_bound = ...so that it can be closed over into an object +infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at +infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues -> + [true] ... + *[false] {""} +} +infer_relate_param_bound_2 = ...that is required by this bound +infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied +infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait +infer_ascribe_user_type_prove_predicate = ...so that the where clause holds + +infer_nothing = {""} + +infer_lifetime_mismatch = lifetime mismatch + +infer_declared_different = this parameter and the return type are declared with different lifetimes... +infer_data_returned = ...but data{$label_var1_exists -> + [true] {" "}from `{$label_var1}` + *[false] {""} +} is returned here + +infer_data_lifetime_flow = ...but data with one lifetime flows into the other here +infer_declared_multiple = this type is declared with multiple lifetimes... +infer_types_declared_different = these two types are declared with different lifetimes... +infer_data_flows = ...but data{$label_var1_exists -> + [true] {" "}from `{$label_var1}` + *[false] -> {""} +} flows{$label_var2_exists -> + [true] {" "}into `{$label_var2}` + *[false] -> {""} +} here + +infer_lifetime_param_suggestion = consider introducing a named lifetime parameter{$is_impl -> + [true] {" "}and update trait if needed + *[false] {""} +} +infer_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime + +infer_region_explanation = {$pref_kind -> + *[should_not_happen] [{$pref_kind}] + [ref_valid_for] ...the reference is valid for + [content_valid_for] ...but the borrowed content is only valid for + [type_obj_valid_for] object type is valid for + [source_pointer_valid_for] source pointer is only valid for + [type_satisfy] type must satisfy + [type_outlive] type must outlive + [lf_param_instantiated_with] lifetime parameter instantiated with + [lf_param_must_outlive] but lifetime parameter must outlive + [lf_instantiated_with] lifetime instantiated with + [lf_must_outlive] but lifetime must outlive + [pointer_valid_for] the pointer is valid for + [data_valid_for] but the referenced data is only valid for + [empty] {""} +}{$pref_kind -> + [empty] {""} + *[other] {" "} +}{$desc_kind -> + *[should_not_happen] [{$desc_kind}] + [restatic] the static lifetime + [revar] lifetime {$desc_arg} + [as_defined] the lifetime `{$desc_arg}` as defined here + [as_defined_anon] the anonymous lifetime as defined here + [defined_here] the anonymous lifetime defined here + [anon_num_here] the anonymous lifetime #{$desc_num_arg} defined here + [defined_here_reg] the lifetime `{$desc_arg}` as defined here +}{$suff_kind -> + *[should_not_happen] [{$suff_kind}] + [empty]{""} + [continues] ... + [req_by_binding] {" "}as required by this binding +} + +infer_outlives_content = lifetime of reference outlives lifetime of borrowed content... +infer_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type +infer_fullfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime +infer_lf_bound_not_satisfied = lifetime bound not satisfied +infer_borrowed_too_long = a value of type `{$ty}` is borrowed for too long +infer_ref_longer_than_data = in type `{$ty}`, reference has a longer lifetime than the data it references + +infer_mismatched_static_lifetime = incompatible lifetime on type +infer_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl` +infer_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement +infer_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement +infer_msl_introduces_static = introduces a `'static` lifetime requirement +infer_msl_unmet_req = because this has an unmet lifetime requirement +infer_msl_trait_note = this has an implicit `'static` lifetime requirement +infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement +infer_suggest_add_let_for_letchains = consider adding `let` + +infer_explicit_lifetime_required_with_ident = explicit lifetime required in the type of `{$simple_ident}` + .label = lifetime `{$named}` required + +infer_explicit_lifetime_required_with_param_type = explicit lifetime required in parameter type + .label = lifetime `{$named}` required + +infer_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}` + +infer_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type + +infer_actual_impl_expl_expected_signature_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +infer_actual_impl_expl_expected_signature_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_signature_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}` +infer_actual_impl_expl_expected_passive_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +infer_actual_impl_expl_expected_passive_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_passive_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}` +infer_actual_impl_expl_expected_other_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +infer_actual_impl_expl_expected_other_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_other_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_other_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}` + +infer_actual_impl_expl_but_actually_implements_trait = ...but it actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} +infer_actual_impl_expl_but_actually_implemented_for_ty = ...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} +infer_actual_impl_expl_but_actually_ty_implements = ...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} + +infer_trait_placeholder_mismatch = implementation of `{$trait_def_id}` is not general enough + .label_satisfy = doesn't satisfy where-clause + .label_where = due to a where-clause on `{$def_id}`... + .label_dup = implementation of `{$trait_def_id}` is not general enough + +infer_trait_impl_diff = `impl` item signature doesn't match `trait` item signature + .found = found `{$found}` + .expected = expected `{$expected}` + .expected_found = expected signature `{$expected}` + {" "}found signature `{$found}` + +infer_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output +infer_tid_consider_borrowing = consider borrowing this type parameter in the trait +infer_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + +infer_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement +infer_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement +infer_dtcs_has_req_note = the used `impl` has a `'static` requirement +infer_dtcs_suggestion = consider relaxing the implicit `'static` requirement + +infer_but_calling_introduces = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$lifetime_kind -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement + .label1 = {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` + } + .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> + [true] `impl` of `{$impl_path}` + *[false] inherent `impl` + } + +infer_but_needs_to_satisfy = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but it needs to satisfy a `'static` lifetime requirement + .influencer = this data with {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` + }... + .require = {$spans_empty -> + *[true] ...is used and required to live as long as `'static` here + [false] ...and is required to live as long as `'static` here + } + .used_here = ...is used here... + .introduced_by_bound = `'static` lifetime requirement introduced by this bound + +infer_more_targeted = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but calling `{$ident}` introduces an implicit `'static` lifetime requirement + +infer_ril_introduced_here = `'static` requirement introduced here +infer_ril_introduced_by = requirement introduced by this return type +infer_ril_because_of = because of this returned expression +infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type + +infer_where_remove = remove the `where` clause +infer_where_copy_predicates = copy the `where` clause predicates from the trait + +infer_srs_remove_and_box = consider removing this semicolon and boxing the expressions +infer_srs_remove = consider removing this semicolon +infer_srs_add = consider returning the local binding `{$ident}` +infer_srs_add_one = consider returning one of these bindings + +infer_await_both_futures = consider `await`ing on both `Future`s +infer_await_future = consider `await`ing on the `Future` +infer_await_note = calling an async function returns a future + +infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here... +infer_prlf_defined_without_sub = the lifetime defined here... +infer_prlf_must_oultive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here +infer_prlf_must_oultive_without_sup = ...must outlive the lifetime defined here +infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information) + +infer_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds + .label = opaque type defined here diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 033a1842e..6bbd3fd3e 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,6 +1,6 @@ use hir::GenericParamKind; use rustc_errors::{ - fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, + AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessage, }; use rustc_hir as hir; @@ -12,9 +12,10 @@ use rustc_span::symbol::kw; use rustc_span::Symbol; use rustc_span::{symbol::Ident, BytePos, Span}; -use crate::infer::error_reporting::nice_region_error::placeholder_error::Highlighted; +use crate::fluent_generated as fluent; use crate::infer::error_reporting::{ need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind}, + nice_region_error::placeholder_error::Highlighted, ObligationCauseAsDiagArg, }; @@ -26,9 +27,9 @@ pub struct OpaqueHiddenTypeDiag { #[primary_span] #[label] pub span: Span, - #[note(opaque_type)] + #[note(infer_opaque_type)] pub opaque_type: Span, - #[note(hidden_type)] + #[note(infer_hidden_type)] pub hidden_type: Span, } @@ -768,11 +769,11 @@ impl<'tcx> ActualImplExplNotes<'tcx> { pub struct TraitPlaceholderMismatch<'tcx> { #[primary_span] pub span: Span, - #[label(label_satisfy)] + #[label(infer_label_satisfy)] pub satisfy_span: Option<Span>, - #[label(label_where)] + #[label(infer_label_where)] pub where_span: Option<Span>, - #[label(label_dup)] + #[label(infer_label_dup)] pub dup_span: Option<Span>, pub def_id: String, pub trait_def_id: String, @@ -808,11 +809,11 @@ pub struct RelationshipHelp; #[diag(infer_trait_impl_diff)] pub struct TraitImplDiff { #[primary_span] - #[label(found)] + #[label(infer_found)] pub sp: Span, - #[label(expected)] + #[label(infer_expected)] pub trait_sp: Span, - #[note(expected_found)] + #[note(infer_expected_found)] pub note: (), #[subdiagnostic] pub param_help: ConsiderBorrowingParamHelp, @@ -852,10 +853,10 @@ impl AddToDiagnostic for DynTraitConstraintSuggestion { #[derive(Diagnostic)] #[diag(infer_but_calling_introduces, code = "E0772")] pub struct ButCallingIntroduces { - #[label(label1)] + #[label(infer_label1)] pub param_ty_span: Span, #[primary_span] - #[label(label2)] + #[label(infer_label2)] pub cause_span: Span, pub has_param_name: bool, @@ -913,21 +914,246 @@ impl AddToDiagnostic for MoreTargeted { pub struct ButNeedsToSatisfy { #[primary_span] pub sp: Span, - #[label(influencer)] + #[label(infer_influencer)] pub influencer_point: Span, - #[label(used_here)] + #[label(infer_used_here)] pub spans: Vec<Span>, - #[label(require)] + #[label(infer_require)] pub require_span_as_label: Option<Span>, - #[note(require)] + #[note(infer_require)] pub require_span_as_note: Option<Span>, - #[note(introduced_by_bound)] + #[note(infer_introduced_by_bound)] pub bound: Option<Span>, #[subdiagnostic] pub req_introduces_loc: Option<ReqIntroducedLocations>, + pub has_param_name: bool, + pub param_name: String, pub spans_empty: bool, pub has_lifetime: bool, pub lifetime: String, } + +#[derive(Diagnostic)] +#[diag(infer_outlives_content, code = "E0312")] +pub struct OutlivesContent<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(infer_outlives_bound, code = "E0476")] +pub struct OutlivesBound<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(infer_fullfill_req_lifetime, code = "E0477")] +pub struct FullfillReqLifetime<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, + #[subdiagnostic] + pub note: Option<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(infer_lf_bound_not_satisfied, code = "E0478")] +pub struct LfBoundNotSatisfied<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(infer_ref_longer_than_data, code = "E0491")] +pub struct RefLongerThanData<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Subdiagnostic)] +pub enum WhereClauseSuggestions { + #[suggestion( + infer_where_remove, + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + Remove { + #[primary_span] + span: Span, + }, + #[suggestion( + infer_where_copy_predicates, + code = "{space}where {trait_predicates}", + applicability = "machine-applicable", + style = "verbose" + )] + CopyPredicates { + #[primary_span] + span: Span, + space: &'static str, + trait_predicates: String, + }, +} + +#[derive(Subdiagnostic)] +pub enum SuggestRemoveSemiOrReturnBinding { + #[multipart_suggestion(infer_srs_remove_and_box, applicability = "machine-applicable")] + RemoveAndBox { + #[suggestion_part(code = "Box::new(")] + first_lo: Span, + #[suggestion_part(code = ")")] + first_hi: Span, + #[suggestion_part(code = "Box::new(")] + second_lo: Span, + #[suggestion_part(code = ")")] + second_hi: Span, + #[suggestion_part(code = "")] + sp: Span, + }, + #[suggestion( + infer_srs_remove, + style = "short", + code = "", + applicability = "machine-applicable" + )] + Remove { + #[primary_span] + sp: Span, + }, + #[suggestion( + infer_srs_add, + style = "verbose", + code = "{code}", + applicability = "maybe-incorrect" + )] + Add { + #[primary_span] + sp: Span, + code: String, + ident: Ident, + }, + #[note(infer_srs_add_one)] + AddOne { + #[primary_span] + spans: MultiSpan, + }, +} + +#[derive(Subdiagnostic)] +pub enum ConsiderAddingAwait { + #[help(infer_await_both_futures)] + BothFuturesHelp, + #[multipart_suggestion(infer_await_both_futures, applicability = "maybe-incorrect")] + BothFuturesSugg { + #[suggestion_part(code = ".await")] + first: Span, + #[suggestion_part(code = ".await")] + second: Span, + }, + #[suggestion( + infer_await_future, + code = ".await", + style = "verbose", + applicability = "maybe-incorrect" + )] + FutureSugg { + #[primary_span] + span: Span, + }, + #[note(infer_await_note)] + FutureSuggNote { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + infer_await_future, + style = "verbose", + applicability = "maybe-incorrect" + )] + FutureSuggMultiple { + #[suggestion_part(code = ".await")] + spans: Vec<Span>, + }, +} + +#[derive(Diagnostic)] +pub enum PlaceholderRelationLfNotSatisfied { + #[diag(infer_lf_bound_not_satisfied)] + HasBoth { + #[primary_span] + span: Span, + #[note(infer_prlf_defined_with_sub)] + sub_span: Span, + #[note(infer_prlf_must_oultive_with_sup)] + sup_span: Span, + sub_symbol: Symbol, + sup_symbol: Symbol, + #[note(infer_prlf_known_limitation)] + note: (), + }, + #[diag(infer_lf_bound_not_satisfied)] + HasSub { + #[primary_span] + span: Span, + #[note(infer_prlf_defined_with_sub)] + sub_span: Span, + #[note(infer_prlf_must_oultive_without_sup)] + sup_span: Span, + sub_symbol: Symbol, + #[note(infer_prlf_known_limitation)] + note: (), + }, + #[diag(infer_lf_bound_not_satisfied)] + HasSup { + #[primary_span] + span: Span, + #[note(infer_prlf_defined_without_sub)] + sub_span: Span, + #[note(infer_prlf_must_oultive_with_sup)] + sup_span: Span, + sup_symbol: Symbol, + #[note(infer_prlf_known_limitation)] + note: (), + }, + #[diag(infer_lf_bound_not_satisfied)] + HasNone { + #[primary_span] + span: Span, + #[note(infer_prlf_defined_without_sub)] + sub_span: Span, + #[note(infer_prlf_must_oultive_without_sup)] + sup_span: Span, + #[note(infer_prlf_known_limitation)] + note: (), + }, + #[diag(infer_lf_bound_not_satisfied)] + OnlyPrimarySpan { + #[primary_span] + span: Span, + #[note(infer_prlf_known_limitation)] + note: (), + }, +} + +#[derive(Diagnostic)] +#[diag(infer_opaque_captures_lifetime, code = "E0700")] +pub struct OpaqueCapturesLifetime<'tcx> { + #[primary_span] + pub span: Span, + #[label] + pub opaque_ty_span: Span, + pub opaque_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7aaa5ce2f..ef543b1fb 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,7 +1,6 @@ +use crate::fluent_generated as fluent; use crate::infer::error_reporting::nice_region_error::find_anon_type; -use rustc_errors::{ - self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage, -}; +use rustc_errors::{self, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -31,6 +30,8 @@ impl<'a> DescriptionCtx<'a> { ty::RePlaceholder(_) => return None, + ty::ReError(_) => return None, + // FIXME(#13998) RePlaceholder should probably print like // ReFree rather than dumping Debug output on the user. // @@ -119,16 +120,42 @@ impl<'a> DescriptionCtx<'a> { pub enum PrefixKind { Empty, + RefValidFor, + ContentValidFor, + TypeObjValidFor, + SourcePointerValidFor, + TypeSatisfy, + TypeOutlive, + LfParamInstantiatedWith, + LfParamMustOutlive, + LfInstantiatedWith, + LfMustOutlive, + PointerValidFor, + DataValidFor, } pub enum SuffixKind { + Empty, Continues, + ReqByBinding, } impl IntoDiagnosticArg for PrefixKind { fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { let kind = match self { Self::Empty => "empty", + Self::RefValidFor => "ref_valid_for", + Self::ContentValidFor => "content_valid_for", + Self::TypeObjValidFor => "type_obj_valid_for", + Self::SourcePointerValidFor => "source_pointer_valid_for", + Self::TypeSatisfy => "type_satisfy", + Self::TypeOutlive => "type_outlive", + Self::LfParamInstantiatedWith => "lf_param_instantiated_with", + Self::LfParamMustOutlive => "lf_param_must_outlive", + Self::LfInstantiatedWith => "lf_instantiated_with", + Self::LfMustOutlive => "lf_must_outlive", + Self::PointerValidFor => "pointer_valid_for", + Self::DataValidFor => "data_valid_for", } .into(); rustc_errors::DiagnosticArgValue::Str(kind) @@ -138,7 +165,9 @@ impl IntoDiagnosticArg for PrefixKind { impl IntoDiagnosticArg for SuffixKind { fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { let kind = match self { + Self::Empty => "empty", Self::Continues => "continues", + Self::ReqByBinding => "req_by_binding", } .into(); rustc_errors::DiagnosticArgValue::Str(kind) @@ -164,17 +193,19 @@ impl RegionExplanation<'_> { } impl AddToDiagnostic for RegionExplanation<'_> { - fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: 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); + let desc_span = self.desc.span; + self.desc.add_to(diag); + let msg = f(diag, fluent::infer_region_explanation.into()); + if let Some(span) = desc_span { + diag.span_note(span, msg); + } else { + diag.note(msg); + } } } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index d816a9ed3..7d9bae735 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -55,7 +55,7 @@ impl<'tcx> InferCtxt<'tcx> { cause: &'a ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> At<'a, 'tcx> { - At { infcx: self, cause, param_env, define_opaque_types: true } + At { infcx: self, cause, param_env, define_opaque_types: false } } /// Forks the inference context, creating a new inference context with the same inference @@ -369,6 +369,34 @@ impl<'tcx> ToTrace<'tcx> for Const<'tcx> { } } +impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + use GenericArgKind::*; + TypeTrace { + cause: cause.clone(), + values: match (a.unpack(), b.unpack()) { + (Lifetime(a), Lifetime(b)) => Regions(ExpectedFound::new(a_is_expected, a, b)), + (Type(a), Type(b)) => Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + (Const(a), Const(b)) => { + Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) + } + + (Lifetime(_), Type(_) | Const(_)) + | (Type(_), Lifetime(_) | Const(_)) + | (Const(_), Lifetime(_) | Type(_)) => { + bug!("relating different kinds: {a:?} {b:?}") + } + }, + } + } +} + impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> { fn to_trace( _: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 091635e6c..678c4a0be 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -12,7 +12,7 @@ use crate::infer::InferCtxt; use rustc_middle::ty::flags::FlagComputation; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags}; +use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; use std::sync::atomic::Ordering; use rustc_data_structures::fx::FxHashMap; @@ -41,7 +41,7 @@ impl<'tcx> InferCtxt<'tcx> { query_state: &mut OriginalQueryValues<'tcx>, ) -> Canonical<'tcx, V> where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); @@ -50,7 +50,7 @@ impl<'tcx> InferCtxt<'tcx> { /// Like [Self::canonicalize_query], but preserves distinct universes. For /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and - /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1` + /// `'?1` is in `U3` would be canonicalized to have `?0` in `U1` and `'?1` /// in `U2`. /// /// This is used for Chalk integration. @@ -60,7 +60,7 @@ impl<'tcx> InferCtxt<'tcx> { query_state: &mut OriginalQueryValues<'tcx>, ) -> Canonical<'tcx, V> where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); @@ -100,7 +100,7 @@ impl<'tcx> InferCtxt<'tcx> { /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result pub fn canonicalize_response<V>(&self, value: V) -> Canonical<'tcx, V> where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { let mut query_state = OriginalQueryValues::default(); Canonicalizer::canonicalize( @@ -114,7 +114,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonical<'tcx, V> where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { let mut query_state = OriginalQueryValues::default(); Canonicalizer::canonicalize( @@ -136,7 +136,7 @@ impl<'tcx> InferCtxt<'tcx> { query_state: &mut OriginalQueryValues<'tcx>, ) -> Canonical<'tcx, V> where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); @@ -203,12 +203,10 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { // rust-lang/rust#57464: `impl Trait` can leak local // scopes (in manner violating typeck). Therefore, use // `delay_span_bug` to allow type error over an ICE. - ty::tls::with(|tcx| { - tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!("unexpected region in query response: `{:?}`", r), - ); - }); + canonicalizer.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("unexpected region in query response: `{:?}`", r), + ); r } } @@ -328,14 +326,14 @@ struct Canonicalizer<'cx, 'tcx> { binder_index: ty::DebruijnIndex, } -impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.binder_index.shift_in(1); let t = t.super_fold_with(self); @@ -365,12 +363,13 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { opportunistically resolved to {:?}", vid, resolved_vid ); - let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid)); + let r = self.tcx.mk_re_var(resolved_vid); self.canonicalize_mode.canonicalize_free_region(self, r) } ty::ReStatic | ty::ReEarlyBound(..) + | ty::ReError(_) | ty::ReFree(_) | ty::RePlaceholder(..) | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), @@ -419,10 +418,15 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { bug!("encountered a fresh type during canonicalization") } - ty::Placeholder(placeholder) => self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) }, - t, - ), + ty::Placeholder(mut placeholder) => { + if !self.canonicalize_mode.preserve_universes() { + placeholder.universe = ty::UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) }, + t, + ) + } ty::Bound(debruijn, _) => { if debruijn >= self.binder_index { @@ -435,6 +439,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) | ty::Bool | ty::Char | ty::Int(..) @@ -525,7 +530,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { query_state: &mut OriginalQueryValues<'tcx>, ) -> Canonical<'tcx, V> where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { let needs_canonical_flags = if canonicalize_region_mode.any() { TypeFlags::NEEDS_INFER | @@ -567,7 +572,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders()); let canonical_variables = - tcx.intern_canonical_var_infos(&canonicalizer.universe_canonicalized_variables()); + tcx.mk_canonical_var_infos(&canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -737,8 +742,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { ) -> ty::Region<'tcx> { let var = self.canonical_var(info, r.into()); let br = ty::BoundRegion { var, kind: ty::BrAnon(var.as_u32(), None) }; - let region = ty::ReLateBound(self.binder_index, br); - self.tcx().mk_region(region) + self.interner().mk_re_late_bound(self.binder_index, br) } /// Given a type variable `ty_var` of the given kind, first check @@ -752,7 +756,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.fold_ty(bound_to) } else { let var = self.canonical_var(info, ty_var.into()); - self.tcx().mk_ty(ty::Bound(self.binder_index, var.into())) + self.interner().mk_bound(self.binder_index, var.into()) } } @@ -771,7 +775,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.fold_const(bound_to) } else { let var = self.canonical_var(info, const_var.into()); - self.tcx().mk_const( + self.interner().mk_const( ty::ConstKind::Bound(self.binder_index, var), self.fold_ty(const_var.ty()), ) diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index e59715b70..ce230afda 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -26,11 +26,11 @@ use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVari use rustc_index::vec::IndexVec; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BoundVar, List}; +use rustc_middle::ty::{self, List, TyCtxt}; use rustc_span::source_map::Span; pub use rustc_middle::infer::canonical::*; -use substitute::CanonicalExt; +pub use substitute::CanonicalExt; mod canonicalizer; pub mod query_response; @@ -55,7 +55,7 @@ impl<'tcx> InferCtxt<'tcx> { canonical: &Canonical<'tcx, T>, ) -> (T, CanonicalVarValues<'tcx>) where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { // For each universe that is referred to in the incoming // query, create a universe in our local inference context. In @@ -87,19 +87,24 @@ impl<'tcx> InferCtxt<'tcx> { variables: &List<CanonicalVarInfo<'tcx>>, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { - let var_values: IndexVec<BoundVar, GenericArg<'tcx>> = variables - .iter() - .map(|info| self.instantiate_canonical_var(span, info, &universe_map)) - .collect(); - - CanonicalVarValues { var_values } + CanonicalVarValues { + var_values: self.tcx.mk_substs_from_iter( + variables + .iter() + .map(|info| self.instantiate_canonical_var(span, info, &universe_map)), + ), + } } /// Given the "info" about a canonical variable, creates a fresh /// variable for it. If this is an existentially quantified /// variable, then you'll get a new inference variable; if it is a /// universally quantified variable, you get a placeholder. - fn instantiate_canonical_var( + /// + /// FIXME(-Ztrait-solver=next): This is public because it's used by the + /// new trait solver which has a different canonicalization routine. + /// We should somehow deduplicate all of this. + pub fn instantiate_canonical_var( &self, span: Span, cv_info: CanonicalVarInfo<'tcx>, @@ -123,7 +128,7 @@ impl<'tcx> InferCtxt<'tcx> { CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, name }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = ty::PlaceholderType { universe: universe_mapped, name }; - self.tcx.mk_ty(ty::Placeholder(placeholder_mapped)).into() + self.tcx.mk_placeholder(placeholder_mapped).into() } CanonicalVarKind::Region(ui) => self @@ -136,7 +141,7 @@ impl<'tcx> InferCtxt<'tcx> { CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion { universe, name }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = ty::PlaceholderRegion { universe: universe_mapped, name }; - self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() + self.tcx.mk_re_placeholder(placeholder_mapped).into() } CanonicalVarKind::Const(ui, ty) => self diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 3d49182f0..436d29c24 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -12,12 +12,12 @@ use crate::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues, QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, }; -use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; +use crate::infer::nll_relate::{TypeRelating, TypeRelatingDelegate}; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin}; use crate::traits::query::{Fallible, NoSolution}; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; -use crate::traits::{PredicateObligations, TraitEngine}; +use crate::traits::{PredicateObligations, TraitEngine, TraitEngineExt}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_index::vec::IndexVec; @@ -27,7 +27,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, BoundVar, ToPredicate, Ty, TyCtxt}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::fmt::Debug; use std::iter; @@ -59,7 +59,7 @@ impl<'tcx> InferCtxt<'tcx> { fulfill_cx: &mut dyn TraitEngine<'tcx>, ) -> Fallible<CanonicalQueryResponse<'tcx, T>> where - T: Debug + TypeFoldable<'tcx>, + T: Debug + TypeFoldable<TyCtxt<'tcx>>, Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, { let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?; @@ -85,7 +85,7 @@ impl<'tcx> InferCtxt<'tcx> { answer: T, ) -> Canonical<'tcx, QueryResponse<'tcx, T>> where - T: Debug + TypeFoldable<'tcx>, + T: Debug + TypeFoldable<TyCtxt<'tcx>>, { self.canonicalize_response(QueryResponse { var_values: inference_vars, @@ -106,7 +106,7 @@ impl<'tcx> InferCtxt<'tcx> { fulfill_cx: &mut dyn TraitEngine<'tcx>, ) -> Result<QueryResponse<'tcx, T>, NoSolution> where - T: Debug + TypeFoldable<'tcx>, + T: Debug + TypeFoldable<TyCtxt<'tcx>>, { let tcx = self.tcx; @@ -151,11 +151,21 @@ impl<'tcx> InferCtxt<'tcx> { }) } - /// FIXME: This method should only be used for canonical queries and therefore be private. - /// - /// As the new solver does canonicalization slightly differently, this is also used there - /// for now. This should hopefully change fairly soon. - pub fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + /// Used by the new solver as that one takes the opaque types at the end of a probe + /// to deal with multiple candidates without having to recompute them. + pub fn clone_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + self.inner + .borrow() + .opaque_type_storage + .opaque_types + .iter() + .map(|&(k, ref v)| { + (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty) + }) + .collect() + } + + fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) .into_iter() .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) @@ -180,7 +190,7 @@ impl<'tcx> InferCtxt<'tcx> { query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, ) -> InferResult<'tcx, R> where - R: Debug + TypeFoldable<'tcx>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, { let InferOk { value: result_subst, mut obligations } = self.query_response_substitution(cause, param_env, original_values, query_response)?; @@ -242,7 +252,7 @@ impl<'tcx> InferCtxt<'tcx> { output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> InferResult<'tcx, R> where - R: Debug + TypeFoldable<'tcx>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, { let InferOk { value: result_subst, mut obligations } = self .query_response_substitution_guess(cause, param_env, original_values, query_response)?; @@ -268,14 +278,12 @@ impl<'tcx> InferCtxt<'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)), - constraint_category, - )); - output_query_region_constraints.outlives.push(( - ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)), - constraint_category, - )); + output_query_region_constraints + .outlives + .push((ty::OutlivesPredicate(v_o.into(), v_r), constraint_category)); + output_query_region_constraints + .outlives + .push((ty::OutlivesPredicate(v_r.into(), v_o), constraint_category)); } } @@ -318,10 +326,8 @@ impl<'tcx> InferCtxt<'tcx> { query_response.value.region_constraints.outlives.iter().filter_map(|&r_c| { let r_c = substitute_value(self.tcx, &result_subst, r_c); - // 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.0.skip_binder(); + // Screen out `'a: 'a` cases. + let ty::OutlivesPredicate(k1, r2) = r_c.0; if k1 != r2.into() { Some(r_c) } else { None } }), ); @@ -360,7 +366,7 @@ impl<'tcx> InferCtxt<'tcx> { query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> where - R: Debug + TypeFoldable<'tcx>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, { debug!( "query_response_substitution(original_values={:#?}, query_response={:#?})", @@ -397,6 +403,7 @@ impl<'tcx> InferCtxt<'tcx> { /// will instantiate fresh inference variables for each canonical /// variable instead. Therefore, the result of this method must be /// properly unified + #[instrument(level = "debug", skip(self, cause, param_env))] fn query_response_substitution_guess<R>( &self, cause: &ObligationCause<'tcx>, @@ -405,13 +412,8 @@ impl<'tcx> InferCtxt<'tcx> { query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> where - R: Debug + TypeFoldable<'tcx>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, { - debug!( - "query_response_substitution_guess(original_values={:#?}, query_response={:#?})", - original_values, query_response, - ); - // For each new universe created in the query result that did // not appear in the original query, create a local // superuniverse. @@ -482,11 +484,8 @@ impl<'tcx> InferCtxt<'tcx> { // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. let result_subst = CanonicalVarValues { - var_values: query_response - .variables - .iter() - .enumerate() - .map(|(index, info)| { + var_values: self.tcx.mk_substs_from_iter( + query_response.variables.iter().enumerate().map(|(index, info)| { if info.is_existential() { match opt_values[BoundVar::new(index)] { Some(k) => k, @@ -499,8 +498,8 @@ impl<'tcx> InferCtxt<'tcx> { universe_map[u.as_usize()] }) } - }) - .collect(), + }), + ), }; let mut obligations = vec![]; @@ -509,7 +508,9 @@ impl<'tcx> InferCtxt<'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.at(cause, param_env).eq(a, b)?.obligations); + debug!(?a, ?b, "constrain opaque type"); + obligations + .extend(self.at(cause, param_env).define_opaque_types(true).eq(a, b)?.obligations); } Ok(InferOk { value: result_subst, obligations }) @@ -530,7 +531,7 @@ impl<'tcx> InferCtxt<'tcx> { query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, ) -> InferResult<'tcx, ()> where - R: Debug + TypeFoldable<'tcx>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, { // A closure that yields the result value for the given // canonical variable; this is taken from @@ -562,11 +563,11 @@ impl<'tcx> InferCtxt<'tcx> { pub fn query_outlives_constraint_to_obligation( &self, - predicate: QueryOutlivesConstraint<'tcx>, + (predicate, _): QueryOutlivesConstraint<'tcx>, cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Obligation<'tcx, ty::Predicate<'tcx>> { - let ty::OutlivesPredicate(k1, r2) = predicate.0.skip_binder(); + let ty::OutlivesPredicate(k1, r2) = predicate; let atom = match k1.unpack() { GenericArgKind::Lifetime(r1) => { @@ -581,7 +582,7 @@ impl<'tcx> InferCtxt<'tcx> { span_bug!(cause.span, "unexpected const outlives {:?}", predicate); } }; - let predicate = predicate.0.rebind(atom); + let predicate = ty::Binder::dummy(atom); Obligation::new(self.tcx, cause, param_env, predicate) } @@ -646,31 +647,25 @@ pub fn make_query_region_constraints<'tcx>( let outlives: Vec<_> = constraints .iter() .map(|(k, origin)| { - // no bound vars in the code above - let constraint = ty::Binder::dummy(match *k { + let constraint = 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::VarSubVar(v1, v2) => { + ty::OutlivesPredicate(tcx.mk_re_var(v2).into(), tcx.mk_re_var(v1)) + } Constraint::VarSubReg(v1, r2) => { - ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) + ty::OutlivesPredicate(r2.into(), tcx.mk_re_var(v1)) } Constraint::RegSubVar(r1, v2) => { - ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) + ty::OutlivesPredicate(tcx.mk_re_var(v2).into(), r1) } Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), - }); + }; (constraint, origin.to_constraint_category()) }) - .chain( - outlives_obligations - // no bound vars in the code above - .map(|(ty, r, constraint_category)| { - (ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), r)), constraint_category) - }), - ) + .chain(outlives_obligations.map(|(ty, r, constraint_category)| { + (ty::OutlivesPredicate(ty.into(), r), constraint_category) + })) .collect(); QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } @@ -696,13 +691,17 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { self.infcx.create_next_universe() } - fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> { + fn next_existential_region_var( + &mut self, + from_forall: bool, + _name: Option<Symbol>, + ) -> ty::Region<'tcx> { let origin = NllRegionVariableOrigin::Existential { from_forall }; self.infcx.next_nll_region_var(origin) } fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> { - self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder)) + self.infcx.tcx.mk_re_placeholder(placeholder) } fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { @@ -729,10 +728,6 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { }); } - fn normalization() -> NormalizationStrategy { - NormalizationStrategy::Eager - } - fn forbid_inference_vars() -> bool { true } diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs index 389afe22e..cac3b4072 100644 --- a/compiler/rustc_infer/src/infer/canonical/substitute.rs +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -11,12 +11,14 @@ use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, TyCtxt}; -pub(super) trait CanonicalExt<'tcx, V> { +/// FIXME(-Ztrait-solver=next): This or public because it is shared with the +/// new trait solver implementation. We should deduplicate canonicalization. +pub trait CanonicalExt<'tcx, V> { /// Instantiate the wrapped value, replacing each canonical value /// with the value given in `var_values`. fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V where - V: TypeFoldable<'tcx>; + V: TypeFoldable<TyCtxt<'tcx>>; /// Allows one to apply a substitute to some subset of /// `self.value`. Invoke `projection_fn` with `self.value` to get @@ -31,13 +33,13 @@ pub(super) trait CanonicalExt<'tcx, V> { projection_fn: impl FnOnce(&V) -> T, ) -> T where - T: TypeFoldable<'tcx>; + T: TypeFoldable<TyCtxt<'tcx>>; } impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> { fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V where - V: TypeFoldable<'tcx>, + V: TypeFoldable<TyCtxt<'tcx>>, { self.substitute_projected(tcx, var_values, |value| value.clone()) } @@ -49,7 +51,7 @@ impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> { projection_fn: impl FnOnce(&V) -> T, ) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { assert_eq!(self.variables.len(), var_values.len()); let value = projection_fn(&self.value); @@ -66,22 +68,21 @@ pub(super) fn substitute_value<'tcx, T>( value: T, ) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { if var_values.var_values.is_empty() { value } else { let delegate = FnMutDelegate { - regions: &mut |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { + regions: &mut |br: ty::BoundRegion| match var_values[br.var].unpack() { GenericArgKind::Lifetime(l) => l, r => bug!("{:?} is a region but value is {:?}", br, r), }, - types: &mut |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { + types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].unpack() { GenericArgKind::Type(ty) => ty, r => bug!("{:?} is a type but value is {:?}", bound_ty, r), }, - consts: &mut |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() - { + consts: &mut |bound_ct: ty::BoundVar, _| match 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 72676b718..33292e871 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -31,13 +31,18 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; +use rustc_middle::infer::canonical::OriginalQueryValues; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{ + self, AliasKind, FallibleTypeFolder, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable, + TypeSuperFoldable, TypeVisitableExt, +}; use rustc_middle::ty::{IntType, UintType}; use rustc_span::{Span, DUMMY_SP}; @@ -71,7 +76,7 @@ impl<'tcx> InferCtxt<'tcx> { b: Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> where - R: TypeRelation<'tcx>, + R: ObligationEmittingRelation<'tcx>, { let a_is_expected = relation.a_is_expected(); @@ -119,6 +124,15 @@ impl<'tcx> InferCtxt<'tcx> { Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) } + (ty::Alias(AliasKind::Projection, _), _) if self.tcx.trait_solver_next() => { + relation.register_type_equate_obligation(a, b); + Ok(b) + } + (_, ty::Alias(AliasKind::Projection, _)) if self.tcx.trait_solver_next() => { + relation.register_type_equate_obligation(b, a); + Ok(a) + } + _ => ty::relate::super_relate_tys(relation, a, b), } } @@ -130,7 +144,7 @@ impl<'tcx> InferCtxt<'tcx> { b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> where - R: ConstEquateRelation<'tcx>, + R: ObligationEmittingRelation<'tcx>, { debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); if a == b { @@ -140,7 +154,33 @@ impl<'tcx> InferCtxt<'tcx> { let a = self.shallow_resolve(a); let b = self.shallow_resolve(b); - let a_is_expected = relation.a_is_expected(); + // We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the + // correct type for the generic param they are an argument for. However there have been a number of cases + // historically where asserting that the types are equal has found bugs in the compiler so this is valuable + // to check even if it is a bit nasty impl wise :( + // + // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find + // ourselves with a check to find bugs being required for code to compile because it made inference progress. + self.probe(|_| { + if a.ty() == b.ty() { + return; + } + + // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the + // two const param's types are able to be equal has to go through a canonical query with the actual logic + // in `rustc_trait_selection`. + let canonical = self.canonicalize_query( + (relation.param_env(), a.ty(), b.ty()), + &mut OriginalQueryValues::default(), + ); + + if let Err(NoSolution) = self.tcx.check_tys_might_be_eq(canonical) { + self.tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,), + ); + } + }); match (a.kind(), b.kind()) { ( @@ -158,17 +198,17 @@ impl<'tcx> InferCtxt<'tcx> { } (ty::ConstKind::Infer(InferConst::Var(vid)), _) => { - return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected); + return self.unify_const_variable(vid, b); } (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { - return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected); + return self.unify_const_variable(vid, a); } (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => { // FIXME(#59490): Need to remove the leak check to accommodate // escaping bound variables here. if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { - relation.const_equate_obligation(a, b); + relation.register_const_equate_obligation(a, b); } return Ok(b); } @@ -176,7 +216,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#59490): Need to remove the leak check to accommodate // escaping bound variables here. if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { - relation.const_equate_obligation(a, b); + relation.register_const_equate_obligation(a, b); } return Ok(a); } @@ -223,10 +263,8 @@ impl<'tcx> InferCtxt<'tcx> { #[instrument(level = "debug", skip(self))] fn unify_const_variable( &self, - param_env: ty::ParamEnv<'tcx>, target_vid: ty::ConstVid<'tcx>, ct: ty::Const<'tcx>, - vid_is_expected: bool, ) -> RelateResult<'tcx, ty::Const<'tcx>> { let (for_universe, span) = { let mut inner = self.inner.borrow_mut(); @@ -239,8 +277,12 @@ impl<'tcx> InferCtxt<'tcx> { ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span), } }; - let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid } - .relate(ct, ct)?; + let value = ct.try_fold_with(&mut ConstInferUnifier { + infcx: self, + span, + for_universe, + target_vid, + })?; self.inner.borrow_mut().const_unification_table().union_value( target_vid, @@ -432,32 +474,18 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { Ok(Generalization { ty, needs_wf }) } - pub fn add_const_equate_obligation( - &mut self, - a_is_expected: bool, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) { - let predicate = if a_is_expected { - ty::PredicateKind::ConstEquate(a, b) - } else { - ty::PredicateKind::ConstEquate(b, a) - }; - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::Binder::dummy(predicate), - )); + pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.obligations.extend(obligations.into_iter()); + } + + pub fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>) { + self.obligations.extend(obligations.into_iter().map(|to_pred| { + Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred) + })) } pub fn mark_ambiguous(&mut self) { - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Ambiguous), - )); + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]); } } @@ -702,6 +730,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { return Ok(r); } + ty::ReError(_) => { + return Ok(r); + } + ty::RePlaceholder(..) | ty::ReVar(..) | ty::ReStatic @@ -772,11 +804,39 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } -pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { +pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { + /// Register obligations that must hold in order for this relation to hold + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); + + /// Register predicates that must hold in order for this relation to hold. Uses + /// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should + /// be used if control over the obligaton causes is required. + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>); + /// Register an obligation that both constants must be equal to each other. /// /// If they aren't equal then the relation doesn't hold. - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); + fn register_const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + + self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() { + ty::PredicateKind::AliasEq(a.into(), b.into()) + } else { + ty::PredicateKind::ConstEquate(a, b) + })]); + } + + /// Register an obligation that both types must be equal to each other. + /// + /// If they aren't equal then the relation doesn't hold. + fn register_type_equate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasEq( + a.into(), + b.into(), + ))]); + } } fn int_unification_error<'tcx>( @@ -800,8 +860,6 @@ struct ConstInferUnifier<'cx, 'tcx> { span: Span, - param_env: ty::ParamEnv<'tcx>, - for_universe: ty::UniverseIndex, /// The vid of the const variable that is in the process of being @@ -810,61 +868,15 @@ struct ConstInferUnifier<'cx, 'tcx> { target_vid: ty::ConstVid<'tcx>, } -// We use `TypeRelation` here to propagate `RelateResult` upwards. -// -// Both inputs are expected to be the same. -impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn intercrate(&self) -> bool { - assert!(!self.infcx.intercrate); - false - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } - - fn tag(&self) -> &'static str { - "ConstInferUnifier" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn mark_ambiguous(&mut self) { - bug!() - } +impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for ConstInferUnifier<'_, 'tcx> { + type Error = TypeError<'tcx>; - fn relate_with_variance<T: Relate<'tcx>>( - &mut self, - _variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - // We don't care about variance here. - self.relate(a, b) - } - - fn binders<T>( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx } #[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); - + fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, TypeError<'tcx>> { match t.kind() { &ty::Infer(ty::TyVar(vid)) => { let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); @@ -872,7 +884,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { match probe { TypeVariableValue::Known { value: u } => { debug!("ConstOccursChecker: known value {:?}", u); - self.tys(u, u) + u.try_fold_with(self) } TypeVariableValue::Unknown { universe } => { if self.for_universe.can_name(universe) { @@ -887,27 +899,26 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { .borrow_mut() .type_variables() .new_var(self.for_universe, origin); - Ok(self.tcx().mk_ty_var(new_var_id)) + Ok(self.interner().mk_ty_var(new_var_id)) } } } ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t), - _ => relate::super_relate_tys(self, t, t), + _ => t.try_super_fold_with(self), } } - fn regions( + #[instrument(level = "debug", skip(self), ret)] + fn try_fold_region( &mut self, r: ty::Region<'tcx>, - _r: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug_assert_eq!(r, _r); + ) -> Result<ty::Region<'tcx>, TypeError<'tcx>> { debug!("ConstInferUnifier: r={:?}", r); match *r { // Never make variables for regions bound within the type itself, // nor for erased regions. - ty::ReLateBound(..) | ty::ReErased => { + ty::ReLateBound(..) | ty::ReErased | ty::ReError(_) => { return Ok(r); } @@ -930,14 +941,8 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } - #[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); - + #[instrument(level = "debug", skip(self), ret)] + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, TypeError<'tcx>> { match c.kind() { ty::ConstKind::Infer(InferConst::Var(vid)) => { // Check if the current unification would end up @@ -958,7 +963,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { let var_value = self.infcx.inner.borrow_mut().const_unification_table().probe_value(vid); match var_value.val { - ConstVariableValue::Known { value: u } => self.consts(u, u), + ConstVariableValue::Known { value: u } => u.try_fold_with(self), ConstVariableValue::Unknown { universe } => { if self.for_universe.can_name(universe) { Ok(c) @@ -972,22 +977,12 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { }, }, ); - Ok(self.tcx().mk_const(new_var_id, c.ty())) + Ok(self.interner().mk_const(new_var_id, c.ty())) } } } } - 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::UnevaluatedConst { def, substs }, c.ty())) - } - _ => relate::super_relate_consts(self, c, c), + _ => c.try_super_fold_with(self), } } } diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 46e7813d9..54a62326e 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -1,10 +1,12 @@ -use super::combine::{CombineFields, ConstEquateRelation, RelationDir}; +use crate::traits::PredicateObligations; + +use super::combine::{CombineFields, ObligationEmittingRelation, RelationDir}; use super::Subtype; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::TyVar; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_hir::def_id::DefId; @@ -129,7 +131,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { 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( + let (a_types, b_types) = infcx.instantiate_binder_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) { @@ -198,8 +200,12 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { } } -impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); +impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 28fd03b87..8a2b800af 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -49,6 +49,7 @@ use super::lexical_region_resolve::RegionResolutionError; use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; +use crate::errors; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; @@ -60,17 +61,19 @@ use crate::traits::{ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg}; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan}; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::Node; use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::{ self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, + TypeVisitable, TypeVisitableExt, }; use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; @@ -79,6 +82,7 @@ use std::path::PathBuf; use std::{cmp, fmt, iter}; mod note; +mod note_and_explain; mod suggest; pub(crate) mod need_type_info; @@ -126,19 +130,16 @@ pub(super) fn note_and_explain_region<'tcx>( alt_span: Option<Span>, ) { let (description, span) = match *region { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { - msg_span_from_free_region(tcx, region, alt_span) + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::RePlaceholder(_) | ty::ReStatic => { + msg_span_from_named_region(tcx, region, alt_span) } - ty::RePlaceholder(_) => return, + ty::ReError(_) => return, - // 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 => { - (format!("lifetime {:?}", region), alt_span) + (format!("lifetime `{region}`"), alt_span) } }; @@ -152,12 +153,12 @@ fn explain_free_region<'tcx>( region: ty::Region<'tcx>, suffix: &str, ) { - let (description, span) = msg_span_from_free_region(tcx, region, None); + let (description, span) = msg_span_from_named_region(tcx, region, None); label_msg_span(err, prefix, description, span, suffix); } -fn msg_span_from_free_region<'tcx>( +fn msg_span_from_named_region<'tcx>( tcx: TyCtxt<'tcx>, region: ty::Region<'tcx>, alt_span: Option<Span>, @@ -168,6 +169,18 @@ fn msg_span_from_free_region<'tcx>( (msg, Some(span)) } ty::ReStatic => ("the static lifetime".to_owned(), alt_span), + ty::RePlaceholder(ty::PlaceholderRegion { + name: ty::BoundRegionKind::BrNamed(def_id, name), + .. + }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), + ty::RePlaceholder(ty::PlaceholderRegion { + name: ty::BoundRegionKind::BrAnon(_, Some(span)), + .. + }) => (format!("the anonymous lifetime defined here"), Some(span)), + ty::RePlaceholder(ty::PlaceholderRegion { + name: ty::BoundRegionKind::BrAnon(_, None), + .. + }) => (format!("an anonymous lifetime"), None), _ => bug!("{:?}", region), } } @@ -269,15 +282,13 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( span: Span, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, - opaque_ty: ty::OpaqueTypeKey<'tcx>, + opaque_ty_key: ty::OpaqueTypeKey<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let opaque_ty = tcx.mk_opaque(opaque_ty.def_id.to_def_id(), opaque_ty.substs); - let mut err = struct_span_err!( - tcx.sess, + let mut err = tcx.sess.create_err(errors::OpaqueCapturesLifetime { span, - E0700, - "hidden type for `{opaque_ty}` captures lifetime that does not appear in bounds", - ); + opaque_ty: tcx.mk_opaque(opaque_ty_key.def_id.to_def_id(), opaque_ty_key.substs), + opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), + }); // Explain the region we are capturing. match *hidden_region { @@ -311,6 +322,9 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ) } } + ty::ReError(_) => { + err.delay_as_bug(); + } _ => { // Ugh. This is a painful case: the hidden region is not one // that we can easily summarize or explain. This can happen @@ -743,15 +757,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let msg = "`match` arms have incompatible types"; err.span_label(outer, msg); - self.suggest_remove_semi_or_return_binding( - err, + if let Some(subdiag) = self.suggest_remove_semi_or_return_binding( prior_arm_block_id, prior_arm_ty, prior_arm_span, arm_block_id, arm_ty, arm_span, - ); + ) { + err.subdiagnostic(subdiag); + } if let Some(ret_sp) = opt_suggest_box_span { // Get return type span and point to it. self.suggest_boxing_for_return_impl_trait( @@ -776,15 +791,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Some(sp) = outer_span { err.span_label(sp, "`if` and `else` have incompatible types"); } - self.suggest_remove_semi_or_return_binding( - err, + if let Some(subdiag) = self.suggest_remove_semi_or_return_binding( Some(then_id), then_ty, then_span, Some(else_id), else_ty, else_span, - ); + ) { + err.subdiagnostic(subdiag); + } if let Some(ret_sp) = opt_suggest_box_span { self.suggest_boxing_for_return_impl_trait( err, @@ -908,7 +924,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) -> Option<()> { // FIXME/HACK: Go back to `SubstsRef` to use its inherent methods, // ideally that shouldn't be necessary. - let sub = self.tcx.intern_substs(sub); + let sub = self.tcx.mk_substs(sub); for (i, ta) in sub.types().enumerate() { if ta == other_ty { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty); @@ -1344,8 +1360,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); + let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); let mut values = self.cmp_fn_sig(&sig1, &sig2); let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1)); let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2)); @@ -1356,7 +1372,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); + let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); let mut values = self.cmp_fn_sig(&sig1, sig2); values.0.push_highlighted(format!( " {{{}}}", @@ -1366,7 +1382,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => { - let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); + let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); let mut values = self.cmp_fn_sig(sig1, &sig2); values.1.push_normal(format!( " {{{}}}", @@ -1433,8 +1449,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { impl<'tcx> OpaqueTypesVisitor<'tcx> { fn visit_expected_found( tcx: TyCtxt<'tcx>, - expected: impl TypeVisitable<'tcx>, - found: impl TypeVisitable<'tcx>, + expected: impl TypeVisitable<TyCtxt<'tcx>>, + found: impl TypeVisitable<TyCtxt<'tcx>>, ignore_span: Span, ) -> Self { let mut types_visitor = OpaqueTypesVisitor { @@ -1468,57 +1484,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { for (key, values) in types.iter() { let count = values.len(); let kind = key.descr(); - let mut returned_async_output_error = false; for &sp in values { - if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error { - if [sp] != err.span.primary_spans() { - let mut span: MultiSpan = sp.into(); - span.push_span_label( - sp, - format!( - "checked the `Output` of this `async fn`, {}{} {}{}", - if count > 1 { "one of the " } else { "" }, - target, - kind, - pluralize!(count), - ), - ); - err.span_note( - span, - "while checking the return type of the `async fn`", - ); - } else { - err.span_label( - sp, - format!( - "checked the `Output` of this `async fn`, {}{} {}{}", - if count > 1 { "one of the " } else { "" }, - target, - kind, - pluralize!(count), - ), - ); - err.note("while checking the return type of the `async fn`"); - } - returned_async_output_error = true; - } else { - err.span_label( - sp, - format!( - "{}{} {}{}", - if count == 1 { "the " } else { "one of the " }, - target, - kind, - pluralize!(count), - ), - ); - } + err.span_label( + sp, + format!( + "{}{} {}{}", + if count == 1 { "the " } else { "one of the " }, + target, + kind, + pluralize!(count), + ), + ); } } } } - impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { + impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypesVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { let span = self.tcx.def_span(def_id); @@ -1535,7 +1517,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // | // = note: expected unit type `()` // found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]` - if !self.ignore_span.overlaps(span) { + // + // Also ignore opaque `Future`s that come from async fns. + if !self.ignore_span.overlaps(span) + && !span.is_desugaring(DesugaringKind::Async) + { self.types.entry(kind).or_default().insert(span); } } @@ -1611,16 +1597,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { format!("expected this to be `{}`", expected) } else { - terr.to_string() + terr.to_string(self.tcx).to_string() }; label_or_note(sp, &terr); label_or_note(span, &msg); } else { - label_or_note(span, &terr.to_string()); + label_or_note(span, &terr.to_string(self.tcx)); label_or_note(sp, &msg); } } else { - label_or_note(span, &terr.to_string()); + if let Some(values) = values + && let Some((e, f)) = values.ty() + && let TypeError::ArgumentSorts(..) | TypeError::Sorts(_) = terr + { + let e = self.tcx.erase_regions(e); + let f = self.tcx.erase_regions(f); + let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); + let found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); + if expected == found { + label_or_note(span, &terr.to_string(self.tcx)); + } else { + label_or_note(span, &format!("expected {expected}, found {found}")); + } + } else { + label_or_note(span, &terr.to_string(self.tcx)); + } } if let Some((expected, found, exp_p, found_p)) = expected_found { @@ -1688,7 +1689,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { 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}") + format!("{name} is defined in crate `{crate_name}`") }; diagnostic.span_note(def_span, msg); }; @@ -1791,14 +1792,24 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } })) { - diag.note_expected_found_extra( - &expected_label, - expected, - &found_label, - found, - &sort_string(values.expected, exp_p), - &sort_string(values.found, found_p), - ); + if let Some(ExpectedFound { found: found_ty, .. }) = exp_found { + // `Future` is a special opaque type that the compiler + // will try to hide in some case such as `async fn`, so + // to make an error more use friendly we will + // avoid to suggest a mismatch type with a + // type that the user usually are not usign + // directly such as `impl Future<Output = u8>`. + if !self.tcx.ty_is_opaque_future(found_ty) { + diag.note_expected_found_extra( + &expected_label, + expected, + &found_label, + found, + &sort_string(values.expected, exp_p), + &sort_string(values.found, found_p), + ); + } + } } } _ => { @@ -1841,19 +1852,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); + self.suggest_function_pointers(cause, span, &exp_found, diag); } } - // 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 - // rustc_hir_analysis/check/compare_impl_item.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(|| { - self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id }) - }); self.check_and_note_conflicting_crates(diag, terr); - self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id()); + + self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); + if let Some(exp_found) = exp_found + && let exp_found = TypeError::Sorts(exp_found) + && exp_found != terr + { + self.note_and_explain_type_err( + diag, + exp_found, + cause, + span, + cause.body_id.to_def_id(), + ); + } if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() @@ -1929,7 +1946,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (ty::Uint(ty::UintTy::U8), ty::Char) => { if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - && code.chars().next().map_or(false, |c| c.is_ascii()) + && !code.starts_with("\\u") // forbid all Unicode escapes + && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII { err.span_suggestion( span, @@ -1976,6 +1994,70 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (ty::Bool, ty::Tuple(list)) => if list.len() == 0 { self.suggest_let_for_letchains(&mut err, &trace.cause, span); } + (ty::Array(_, _), ty::Array(_, _)) => 'block: { + let hir = self.tcx.hir(); + let TypeError::FixedArraySize(sz) = terr else { + break 'block; + }; + let tykind = match hir.find_by_def_id(trace.cause.body_id) { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_, _, body_id), + .. + })) => { + let body = hir.body(*body_id); + struct LetVisitor<'v> { + span: Span, + result: Option<&'v hir::Ty<'v>>, + } + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { + if self.result.is_some() { + return; + } + // Find a local statement where the initializer has + // the same span as the error and the type is specified. + if let hir::Stmt { + kind: hir::StmtKind::Local(hir::Local { + init: Some(hir::Expr { + span: init_span, + .. + }), + ty: Some(array_ty), + .. + }), + .. + } = s + && init_span == &self.span { + self.result = Some(*array_ty); + } + } + } + let mut visitor = LetVisitor {span, result: None}; + visitor.visit_body(body); + visitor.result.map(|r| &r.peel_refs().kind) + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(ty, _), + .. + })) => { + Some(&ty.peel_refs().kind) + } + _ => None + }; + + if let Some(tykind) = tykind + && let hir::TyKind::Array(_, length) = tykind + && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let Some(span) = self.tcx.hir().opt_span(*hir_id) + { + err.span_suggestion( + span, + "consider specifying the actual array length", + sz.found, + Applicability::MaybeIncorrect, + ); + } + } _ => {} } } @@ -2123,7 +2205,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } /// Returns a string of the form "expected `{}`, found `{}`". - fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>( + fn expected_found_str<T: fmt::Display + TypeFoldable<TyCtxt<'tcx>>>( &self, exp_found: ty::error::ExpectedFound<T>, ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)> @@ -2552,7 +2634,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note_expected_found(&"", sup_expected, &"", sup_found); - err.emit(); + if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug(); + } else { + err.emit(); + } return; } @@ -2568,7 +2654,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); self.note_region_origin(&mut err, &sub_origin); - err.emit(); + if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug(); + } else { + err.emit(); + } } /// Determine whether an error associated with the given span and definition @@ -2585,7 +2675,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or /// FloatVar inference type are compatible with themselves or their concrete types (Int and /// 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 { + pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool { let (a, b) = self.resolve_vars_if_possible((a, b)); SameTypeModuloInfer(self).relate(a, b).is_ok() } @@ -2847,6 +2937,7 @@ impl IntoDiagnosticArg for ObligationCauseAsDiagArg<'_> { pub enum TyCategory { Closure, Opaque, + OpaqueFuture, Generator(hir::GeneratorKind), Foreign, } @@ -2856,6 +2947,7 @@ impl TyCategory { match self { Self::Closure => "closure", Self::Opaque => "opaque type", + Self::OpaqueFuture => "future", Self::Generator(gk) => gk.descr(), Self::Foreign => "foreign type", } @@ -2864,7 +2956,11 @@ impl TyCategory { pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> { match *ty.kind() { ty::Closure(def_id, _) => Some((Self::Closure, def_id)), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => Some((Self::Opaque, def_id)), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { + let kind = + if tcx.ty_is_opaque_future(ty) { Self::OpaqueFuture } else { Self::Opaque }; + Some((kind, def_id)) + } ty::Generator(def_id, ..) => { Some((Self::Generator(tcx.generator_kind(def_id).unwrap()), def_id)) } 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 b8c843a8a..e242900fd 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 @@ -122,7 +122,7 @@ impl InferenceDiagnosticsParentData { tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string(); Some(InferenceDiagnosticsParentData { - prefix: tcx.def_kind(parent_def_id).descr(parent_def_id), + prefix: tcx.def_descr(parent_def_id), name: parent_name, }) } @@ -158,8 +158,12 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte if infcx.probe_ty_var(ty_vid).is_ok() { warn!("resolved ty var in error message"); } - if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = - infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind + + let mut infcx_inner = infcx.inner.borrow_mut(); + let ty_vars = infcx_inner.type_variables(); + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind + && !var_origin.span.from_expansion() { Some(name) } else { @@ -254,7 +258,7 @@ impl<'tcx> InferCtxt<'tcx> { if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind { - if name != kw::SelfUpper { + if name != kw::SelfUpper && !var_origin.span.from_expansion() { return InferenceDiagnosticsData { name: name.to_string(), span: Some(var_origin.span), @@ -780,7 +784,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // The sources are listed in order of preference here. let tcx = self.infcx.tcx; let ctx = CostCtxt { tcx }; - let base_cost = match source.kind { + match source.kind { InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty), InferSourceKind::GenericArg { def_id, generic_args, .. } => { @@ -797,17 +801,17 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => { 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } } - }; - - let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 }; - - base_cost + suggestion_may_apply + } } /// Uses `fn source_cost` to determine whether this inference source is preferable to /// previous sources. We generally prefer earlier sources. #[instrument(level = "debug", skip(self))] fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) { + if new_source.from_expansion() { + return; + } + let cost = self.source_cost(&new_source) + self.attempt; debug!(?cost); self.attempt += 1; @@ -819,6 +823,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // `let x: _ = iter.collect();`, as this is a very common case. *def_id = Some(did); } + if cost < self.infer_source_cost { self.infer_source_cost = cost; self.infer_source = Some(new_source); @@ -1056,8 +1061,8 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { }; let parent_def_id = generics.parent.unwrap(); - if tcx.def_kind(parent_def_id) == DefKind::Impl { - let parent_ty = tcx.bound_type_of(parent_def_id).subst(tcx, substs); + if let DefKind::Impl { .. } = tcx.def_kind(parent_def_id) { + let parent_ty = tcx.type_of(parent_def_id).subst(tcx, substs); match (parent_ty.kind(), &ty.kind) { ( ty::Adt(def, substs), 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 39f4d5022..fec04af23 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 @@ -2,7 +2,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::resolve_lifetime as rl; +use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::{self, Region, TyCtxt}; /// This function calls the `visit_ty` method for the parameters @@ -99,11 +99,11 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { hir::TyKind::Ref(ref lifetime, _) => { // the lifetime of the Ref let hir_id = lifetime.hir_id; - match (self.tcx.named_region(hir_id), self.bound_region) { + match (self.tcx.named_bound_var(hir_id), self.bound_region) { // 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(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { self.found_type = Some(arg); @@ -115,7 +115,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // error. We will then search the function parameters for a bound // region at the right depth with the same index ( - Some(rl::Region::LateBound(debruijn_index, _, id)), + Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _), ) => { debug!( @@ -131,10 +131,11 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { ( Some( - rl::Region::Static - | rl::Region::Free(_, _) - | rl::Region::EarlyBound(_) - | rl::Region::LateBound(_, _, _), + rbv::ResolvedArg::StaticLifetime + | rbv::ResolvedArg::Free(_, _) + | rbv::ResolvedArg::EarlyBound(_) + | rbv::ResolvedArg::LateBound(_, _, _) + | rbv::ResolvedArg::Error(_), ) | None, _, @@ -186,9 +187,9 @@ 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) { + match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => { + (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { self.found_it = true; @@ -196,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { } } - (Some(rl::Region::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => { + (Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); debug!("id={:?}", id); debug!("def_id={:?}", def_id); @@ -208,10 +209,11 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { ( Some( - rl::Region::Static - | rl::Region::EarlyBound(_) - | rl::Region::LateBound(_, _, _) - | rl::Region::Free(_, _), + rbv::ResolvedArg::StaticLifetime + | rbv::ResolvedArg::EarlyBound(_) + | rbv::ResolvedArg::LateBound(_, _, _) + | rbv::ResolvedArg::Free(_, _) + | rbv::ResolvedArg::Error(_), ) | None, _, 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 1067ccda2..2c63a3904 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 @@ -72,7 +72,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }; // Next, let's figure out the set of trait objects with implicit static bounds - let ty = self.tcx().type_of(*impl_def_id); + let ty = self.tcx().type_of(*impl_def_id).subst_identity(); let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default()); v.visit_ty(ty); let mut traits = vec![]; 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 99431567e..c1ea0a0d9 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 @@ -14,7 +14,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt}; +use rustc_middle::ty::{self, RePlaceholder, Region, TyCtxt}; use std::fmt; @@ -79,7 +79,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { sup_placeholder @ Region(Interned(RePlaceholder(_), _)), _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ReVar(*vid))), + Some(self.tcx().mk_re_var(*vid)), cause, Some(*sub_placeholder), Some(*sup_placeholder), @@ -95,7 +95,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { _, _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ReVar(*vid))), + Some(self.tcx().mk_re_var(*vid)), cause, Some(*sub_placeholder), None, @@ -111,7 +111,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { sup_placeholder @ Region(Interned(RePlaceholder(_), _)), _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ReVar(*vid))), + Some(self.tcx().mk_re_var(*vid)), cause, None, Some(*sup_placeholder), @@ -127,7 +127,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { sup_placeholder @ Region(Interned(RePlaceholder(_), _)), _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ReVar(*vid))), + Some(self.tcx().mk_re_var(*vid)), cause, None, Some(*sup_placeholder), @@ -141,7 +141,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { SubregionOrigin::Subtype(box TypeTrace { cause, values }), sup_placeholder @ Region(Interned(RePlaceholder(_), _)), )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ReVar(*vid))), + Some(self.tcx().mk_re_var(*vid)), cause, None, Some(*sup_placeholder), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs index 9534bce54..e8d94f0c0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs @@ -1,5 +1,8 @@ -use crate::infer::{ - error_reporting::nice_region_error::NiceRegionError, RegionResolutionError, SubregionOrigin, +use crate::{ + errors::PlaceholderRelationLfNotSatisfied, + infer::{ + error_reporting::nice_region_error::NiceRegionError, RegionResolutionError, SubregionOrigin, + }, }; use rustc_data_structures::intern::Interned; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; @@ -16,8 +19,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { Region(Interned(RePlaceholder(ty::Placeholder { name: sub_name, .. }), _)), Region(Interned(RePlaceholder(ty::Placeholder { name: sup_name, .. }), _)), )) => { - let msg = "lifetime bound not satisfied"; - let mut err = self.tcx().sess.struct_span_err(*span, msg); + let span = *span; let (sub_span, sub_symbol) = match sub_name { ty::BrNamed(def_id, symbol) => { (Some(self.tcx().def_span(def_id)), Some(symbol)) @@ -32,41 +34,47 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { ty::BrAnon(_, span) => (*span, None), ty::BrEnv => (None, None), }; - match (sub_span, sup_span, sub_symbol, sup_symbol) { - (Some(sub_span), Some(sup_span), Some(sub_symbol), Some(sup_symbol)) => { - err.span_note( + let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) { + (Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => { + PlaceholderRelationLfNotSatisfied::HasBoth { + span, sub_span, - format!("the lifetime `{sub_symbol}` defined here..."), - ); - err.span_note( sup_span, - format!("...must outlive the lifetime `{sup_symbol}` defined here"), - ); + sub_symbol, + sup_symbol, + note: (), + } } - (Some(sub_span), Some(sup_span), _, Some(sup_symbol)) => { - err.span_note(sub_span, "the lifetime defined here..."); - err.span_note( + (Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => { + PlaceholderRelationLfNotSatisfied::HasSup { + span, + sub_span, sup_span, - format!("...must outlive the lifetime `{sup_symbol}` defined here"), - ); + sup_symbol, + note: (), + } } - (Some(sub_span), Some(sup_span), Some(sub_symbol), _) => { - err.span_note( + (Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => { + PlaceholderRelationLfNotSatisfied::HasSub { + span, sub_span, - format!("the lifetime `{sub_symbol}` defined here..."), - ); - err.span_note(sup_span, "...must outlive the lifetime defined here"); + sup_span, + sub_symbol, + note: (), + } } (Some(sub_span), Some(sup_span), _, _) => { - err.span_note(sub_span, "the lifetime defined here..."); - err.span_note(sup_span, "...must outlive the lifetime defined here"); + PlaceholderRelationLfNotSatisfied::HasNone { + span, + sub_span, + sup_span, + note: (), + } } - _ => {} - } - err.note("this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"); - Some(err) + _ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () }, + }; + Some(self.tcx().sess.create_err(diag)) } - _ => None, } } 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 49ad3ce50..b06ff10d8 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 @@ -98,6 +98,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sp = var_origin.span(); let return_sp = sub_origin.span(); let param = self.find_param_with_region(*sup_r, *sub_r)?; + let simple_ident = param.param.pat.simple_ident(); let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; let (mention_influencer, influencer_point) = @@ -187,7 +188,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { req_introduces_loc: subdiag, has_lifetime: sup_r.has_name(), - lifetime: sup_r.to_string(), + lifetime: lifetime_name.clone(), + has_param_name: simple_ident.is_some(), + param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), spans_empty, bound, }; @@ -536,7 +539,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. pub struct TraitObjectVisitor(pub FxIndexSet<DefId>); -impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { +impl<'tcx> TypeVisitor<TyCtxt<'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() => { 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 40c0c806e..2875448ee 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 @@ -75,7 +75,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } - impl<'tcx> ty::visit::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { + impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { if !r.has_name() && self.counter <= 3 { self.highlight.highlighting_region(r, self.counter); 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 fd26d7d29..db4b8af46 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 @@ -65,7 +65,7 @@ pub fn find_param_with_region<'tcx>( let owner_id = hir.body_owner(body_id); let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); - let poly_fn_sig = tcx.fn_sig(id); + let poly_fn_sig = tcx.fn_sig(id).subst_identity(); let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); let body = hir.body(body_id); @@ -90,20 +90,18 @@ pub fn find_param_with_region<'tcx>( r } }); - if found_anon_region { + found_anon_region.then(|| { let ty_hir_id = fn_decl.inputs[index].hir_id; let param_ty_span = hir.span(ty_hir_id); let is_first = index == 0; - Some(AnonymousParamInfo { + AnonymousParamInfo { param, param_ty: new_param_ty, param_ty_span, bound_region, is_first, - }) - } else { - None - } + } + }) }) } @@ -125,7 +123,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { br: ty::BoundRegionKind, hir_sig: &hir::FnSig<'_>, ) -> Option<Span> { - let fn_ty = self.tcx().type_of(scope_def_id); + let fn_ty = self.tcx().type_of(scope_def_id).subst_identity(); if let ty::FnDef(_, _) = fn_ty.kind() { let ret_ty = fn_ty.fn_sig(self.tcx()).output(); let span = hir_sig.decl.output.span(); @@ -145,7 +143,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn includes_region( &self, - ty: Binder<'tcx, impl TypeVisitable<'tcx>>, + ty: Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>, region: ty::BoundRegionKind, ) -> bool { let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index b18cbd404..7ffe1fd20 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -1,9 +1,12 @@ -use crate::errors::RegionOriginNote; +use crate::errors::{ + note_and_explain, FullfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, + RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, +}; +use crate::fluent_generated as fluent; use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; use crate::infer::{self, SubregionOrigin}; use rustc_errors::{ - fluent, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, + AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, }; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::traits::ObligationCauseCode; @@ -78,7 +81,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sub: Region<'tcx>, sup: Region<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - match origin { + let mut err = match origin { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); let mut err = self.report_and_explain_type_error(trace, terr); @@ -119,130 +122,105 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err } infer::Reborrow(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0312, - "lifetime of reference outlives lifetime of borrowed content..." - ); - note_and_explain_region( + let reference_valid = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "...the reference is valid for ", sub, - "...", None, + note_and_explain::PrefixKind::RefValidFor, + note_and_explain::SuffixKind::Continues, ); - note_and_explain_region( + let content_valid = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "...but the borrowed content is only valid for ", sup, - "", None, + note_and_explain::PrefixKind::ContentValidFor, + note_and_explain::SuffixKind::Empty, ); - err + OutlivesContent { + span, + notes: reference_valid.into_iter().chain(content_valid).collect(), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) } infer::RelateObjectBound(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0476, - "lifetime of the source pointer does not outlive lifetime bound of the \ - object type" - ); - note_and_explain_region( + let object_valid = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "object type is valid for ", sub, - "", None, + note_and_explain::PrefixKind::TypeObjValidFor, + note_and_explain::SuffixKind::Empty, ); - note_and_explain_region( + let pointer_valid = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "source pointer is only valid for ", sup, - "", None, + note_and_explain::PrefixKind::SourcePointerValidFor, + note_and_explain::SuffixKind::Empty, ); - err + OutlivesBound { + span, + notes: object_valid.into_iter().chain(pointer_valid).collect(), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) } infer::RelateParamBound(span, ty, opt_span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0477, - "the type `{}` does not fulfill the required lifetime", - self.ty_to_string(ty) + let prefix = match *sub { + ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, + _ => note_and_explain::PrefixKind::TypeOutlive, + }; + let suffix = if opt_span.is_some() { + note_and_explain::SuffixKind::ReqByBinding + } else { + note_and_explain::SuffixKind::Empty + }; + let note = note_and_explain::RegionExplanation::new( + self.tcx, sub, opt_span, prefix, suffix, ); - match *sub { - ty::ReStatic => note_and_explain_region( - self.tcx, - &mut err, - "type must satisfy ", - sub, - if opt_span.is_some() { " as required by this binding" } else { "" }, - opt_span, - ), - _ => note_and_explain_region( - self.tcx, - &mut err, - "type must outlive ", - sub, - if opt_span.is_some() { " as required by this binding" } else { "" }, - opt_span, - ), - } - err + FullfillReqLifetime { span, ty: self.resolve_vars_if_possible(ty), note } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) } infer::RelateRegionParamBound(span) => { - let mut err = - struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); - note_and_explain_region( + let param_instantiated = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "lifetime parameter instantiated with ", sup, - "", None, + note_and_explain::PrefixKind::LfParamInstantiatedWith, + note_and_explain::SuffixKind::Empty, ); - note_and_explain_region( + let param_must_outlive = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "but lifetime parameter must outlive ", sub, - "", None, + note_and_explain::PrefixKind::LfParamMustOutlive, + note_and_explain::SuffixKind::Empty, ); - err + LfBoundNotSatisfied { + span, + notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) } infer::ReferenceOutlivesReferent(ty, span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0491, - "in type `{}`, reference has a longer lifetime than the data it references", - self.ty_to_string(ty) - ); - note_and_explain_region( + let pointer_valid = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "the pointer is valid for ", sub, - "", None, + note_and_explain::PrefixKind::PointerValidFor, + note_and_explain::SuffixKind::Empty, ); - note_and_explain_region( + let data_valid = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "but the referenced data is only valid for ", sup, - "", None, + note_and_explain::PrefixKind::DataValidFor, + note_and_explain::SuffixKind::Empty, ); - err + RefLongerThanData { + span, + ty: self.resolve_vars_if_possible(ty), + notes: pointer_valid.into_iter().chain(data_valid).collect(), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) } infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { let mut err = self.report_extra_impl_obligation( @@ -279,27 +257,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err } infer::AscribeUserTypeProvePredicate(span) => { - let mut err = - struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); - note_and_explain_region( + let instantiated = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "lifetime instantiated with ", sup, - "", None, + note_and_explain::PrefixKind::LfInstantiatedWith, + note_and_explain::SuffixKind::Empty, ); - note_and_explain_region( + let must_outlive = note_and_explain::RegionExplanation::new( self.tcx, - &mut err, - "but lifetime must outlive ", sub, - "", None, + note_and_explain::PrefixKind::LfMustOutlive, + note_and_explain::SuffixKind::Empty, ); - err + LfBoundNotSatisfied { + span, + notes: instantiated.into_iter().chain(must_outlive).collect(), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) } + }; + if sub.is_error() || sup.is_error() { + err.delay_as_bug(); } + err } pub fn suggest_copy_trait_method_bounds( @@ -343,22 +325,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { return; }; - if trait_predicates.is_empty() { - err.span_suggestion_verbose( - generics.where_clause_span, - "remove the `where` clause", - String::new(), - Applicability::MachineApplicable, - ); + let suggestion = if trait_predicates.is_empty() { + WhereClauseSuggestions::Remove { span: generics.where_clause_span } } else { let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - err.span_suggestion_verbose( - generics.where_clause_span, - "copy the `where` clause predicates from the trait", - format!("{space}where {}", trait_predicates.join(", ")), - Applicability::MachineApplicable, - ); - } + WhereClauseSuggestions::CopyPredicates { + span: generics.where_clause_span, + space, + trait_predicates: trait_predicates.join(", "), + } + }; + err.subdiagnostic(suggestion); } pub(super) fn report_placeholder_failure( diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs new file mode 100644 index 000000000..b33729d0b --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -0,0 +1,700 @@ +use super::TypeErrCtxt; +use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; +use rustc_errors::{pluralize, Diagnostic, MultiSpan}; +use rustc_hir::{self as hir, def::DefKind}; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::print::Printer; +use rustc_middle::{ + traits::ObligationCause, + ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty}, +}; +use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol}; + +impl<'tcx> TypeErrCtxt<'_, 'tcx> { + pub fn note_and_explain_type_err( + &self, + diag: &mut Diagnostic, + err: TypeError<'tcx>, + cause: &ObligationCause<'tcx>, + sp: Span, + body_owner_def_id: DefId, + ) { + use ty::error::TypeError::*; + debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); + + let tcx = self.tcx; + + match err { + ArgumentSorts(values, _) | Sorts(values) => { + match (values.expected.kind(), values.found.kind()) { + (ty::Closure(..), ty::Closure(..)) => { + diag.note("no two closures, even if identical, have the same type"); + diag.help("consider boxing your closure and/or using it as a trait object"); + } + (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => { + // Issue #63167 + diag.note("distinct uses of `impl Trait` result in different opaque types"); + } + (ty::Float(_), ty::Infer(ty::IntVar(_))) + if let Ok( + // Issue #53280 + snippet, + ) = tcx.sess.source_map().span_to_snippet(sp) => + { + if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { + diag.span_suggestion( + sp, + "use a float literal", + format!("{}.0", snippet), + MachineApplicable, + ); + } + } + (ty::Param(expected), ty::Param(found)) => { + let generics = tcx.generics_of(body_owner_def_id); + let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id); + if !sp.contains(e_span) { + diag.span_label(e_span, "expected type parameter"); + } + let f_span = tcx.def_span(generics.type_param(found, tcx).def_id); + if !sp.contains(f_span) { + diag.span_label(f_span, "found type parameter"); + } + diag.note( + "a type parameter was expected, but a different one was found; \ + you might be missing a type parameter or trait bound", + ); + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => { + diag.note("an associated type was expected, but a different one was found"); + } + (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p)) + if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder => + { + let p_def_id = tcx + .generics_of(body_owner_def_id) + .type_param(p, tcx) + .def_id; + let p_span = tcx.def_span(p_def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + let hir = tcx.hir(); + let mut note = true; + let parent = p_def_id + .as_local() + .and_then(|id| { + let local_id = hir.local_def_id_to_hir_id(id); + let generics = tcx.hir().find_parent(local_id)?.generics()?; + Some((id, generics)) + }); + if let Some((local_id, generics)) = parent + { + // Synthesize the associated type restriction `Add<Output = Expected>`. + // FIXME: extract this logic for use in other diagnostics. + let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx); + let item_name = tcx.item_name(proj.def_id); + let item_args = self.format_generic_args(assoc_substs); + + // Here, we try to see if there's an existing + // trait implementation that matches the one that + // we're suggesting to restrict. If so, find the + // "end", whether it be at the end of the trait + // or the end of the generic arguments. + let mut matching_span = None; + let mut matched_end_of_args = false; + for bound in generics.bounds_for_param(local_id) { + let potential_spans = bound + .bounds + .iter() + .find_map(|bound| { + let bound_trait_path = bound.trait_ref()?.path; + let def_id = bound_trait_path.res.opt_def_id()?; + let generic_args = bound_trait_path.segments.iter().last().map(|path| path.args()); + (def_id == trait_ref.def_id).then_some((bound_trait_path.span, generic_args)) + }); + + if let Some((end_of_trait, end_of_args)) = potential_spans { + let args_span = end_of_args.and_then(|args| args.span()); + matched_end_of_args = args_span.is_some(); + matching_span = args_span + .or_else(|| Some(end_of_trait)) + .map(|span| span.shrink_to_hi()); + break; + } + } + + if matched_end_of_args { + // Append suggestion to the end of our args + let path = format!(", {}{} = {}",item_name, item_args, p); + note = !suggest_constraining_type_param( + tcx, + generics, + diag, + &format!("{}", proj.self_ty()), + &path, + None, + matching_span, + ); + } else { + // Suggest adding a bound to an existing trait + // or if the trait doesn't exist, add the trait + // and the suggested bounds. + let path = format!("<{}{} = {}>", item_name, item_args, p); + note = !suggest_constraining_type_param( + tcx, + generics, + diag, + &format!("{}", proj.self_ty()), + &path, + None, + matching_span, + ); + } + } + if note { + diag.note("you might be missing a type parameter or trait bound"); + } + } + (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) + | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + diag.help("type parameters must be constrained to match other types"); + if tcx.sess.teach(&diag.get_code().unwrap()) { + diag.help( + "given a type parameter `T` and a method `foo`: +``` +trait Trait<T> { fn foo(&self) -> T; } +``` +the only ways to implement method `foo` are: +- constrain `T` with an explicit type: +``` +impl Trait<String> for X { + fn foo(&self) -> String { String::new() } +} +``` +- add a trait bound to `T` and call a method on that trait that returns `Self`: +``` +impl<T: std::default::Default> Trait<T> for X { + fn foo(&self) -> T { <T as std::default::Default>::default() } +} +``` +- change `foo` to return an argument of type `T`: +``` +impl<T> Trait<T> for X { + fn foo(&self, x: T) -> T { x } +} +```", + ); + } + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + diag.help(&format!( + "every closure has a distinct type and so could not always match the \ + caller-chosen type of parameter `{}`", + p + )); + } + (ty::Param(p), _) | (_, ty::Param(p)) => { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + } + (ty::Alias(ty::Projection, proj_ty), _) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => { + self.expected_projection( + diag, + proj_ty, + values, + body_owner_def_id, + cause.code(), + ); + } + (_, ty::Alias(ty::Projection, proj_ty)) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.found, values.expected, + ); + if !(self.suggest_constraining_opaque_associated_type( + diag, + &msg, + proj_ty, + values.expected, + ) || self.suggest_constraint( + diag, + &msg, + body_owner_def_id, + proj_ty, + values.expected, + )) { + diag.help(&msg); + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + } + (ty::FnPtr(_), ty::FnDef(def, _)) + if let hir::def::DefKind::Fn = tcx.def_kind(def) => { + diag.note( + "when the arguments and return types match, functions can be coerced \ + to function pointers", + ); + } + _ => {} + } + debug!( + "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", + values.expected, + values.expected.kind(), + values.found, + values.found.kind(), + ); + } + CyclicTy(ty) => { + // Watch out for various cases of cyclic types and try to explain. + if ty.is_closure() || ty.is_generator() { + diag.note( + "closures cannot capture themselves or take themselves as argument;\n\ + this error may be the result of a recent compiler bug-fix,\n\ + see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\ + for more information", + ); + } + } + TargetFeatureCast(def_id) => { + let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); + diag.note( + "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" + ); + diag.span_labels(target_spans, "`#[target_feature]` added here"); + } + _ => {} + } + } + + fn suggest_constraint( + &self, + diag: &mut Diagnostic, + msg: &str, + body_owner_def_id: DefId, + proj_ty: &ty::AliasTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + let assoc = tcx.associated_item(proj_ty.def_id); + let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx); + if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) { + if let Some(hir_generics) = item.generics() { + // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`. + // This will also work for `impl Trait`. + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { + let generics = tcx.generics_of(body_owner_def_id); + generics.type_param(param_ty, tcx).def_id + } else { + return false; + }; + let Some(def_id) = def_id.as_local() else { + return false; + }; + + // First look in the `where` clause, as this might be + // `fn foo<T>(x: T) where T: Trait`. + for pred in hir_generics.bounds_for_param(def_id) { + if self.constrain_generic_bound_associated_type_structured_suggestion( + diag, + &trait_ref, + pred.bounds, + assoc, + assoc_substs, + ty, + msg, + false, + ) { + return true; + } + } + } + } + false + } + + /// An associated type was expected and a different type was found. + /// + /// We perform a few different checks to see what we can suggest: + /// + /// - In the current item, look for associated functions that return the expected type and + /// suggest calling them. (Not a structured suggestion.) + /// - If any of the item's generic bounds can be constrained, we suggest constraining the + /// associated type to the found type. + /// - If the associated type has a default type and was expected inside of a `trait`, we + /// mention that this is disallowed. + /// - If all other things fail, and the error is not because of a mismatch between the `trait` + /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc + /// fn that returns the type. + fn expected_projection( + &self, + diag: &mut Diagnostic, + proj_ty: &ty::AliasTy<'tcx>, + values: ExpectedFound<Ty<'tcx>>, + body_owner_def_id: DefId, + cause_code: &ObligationCauseCode<'_>, + ) { + let tcx = self.tcx; + + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.expected, values.found + ); + let body_owner = tcx.hir().get_if_local(body_owner_def_id); + let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); + + // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. + let callable_scope = matches!( + body_owner, + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), + ) + ); + let impl_comparison = + matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. }); + let assoc = tcx.associated_item(proj_ty.def_id); + if !callable_scope || impl_comparison { + // We do not want to suggest calling functions when the reason of the + // type error is a comparison of an `impl` with its `trait` or when the + // scope is outside of a `Body`. + } else { + // If we find a suitable associated function that returns the expected type, we don't + // want the more general suggestion later in this method about "consider constraining + // the associated type or calling a method that returns the associated type". + let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( + diag, + assoc.container_id(tcx), + current_method_ident, + proj_ty.def_id, + values.expected, + ); + // Possibly suggest constraining the associated type to conform to the + // found type. + if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found) + || point_at_assoc_fn + { + return; + } + } + + self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found); + + if self.point_at_associated_type(diag, body_owner_def_id, values.found) { + return; + } + + if !impl_comparison { + // Generic suggestion when we can't be more specific. + if callable_scope { + diag.help(&format!( + "{} or calling a method that returns `{}`", + msg, values.expected + )); + } else { + diag.help(&msg); + } + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + if tcx.sess.teach(&diag.get_code().unwrap()) { + diag.help( + "given an associated type `T` and a method `foo`: +``` +trait Trait { +type T; +fn foo(&self) -> Self::T; +} +``` +the only way of implementing method `foo` is to constrain `T` with an explicit associated type: +``` +impl Trait for X { +type T = String; +fn foo(&self) -> Self::T { String::new() } +} +```", + ); + } + } + + /// When the expected `impl Trait` is not defined in the current item, it will come from + /// a return type. This can occur when dealing with `TryStream` (#71035). + fn suggest_constraining_opaque_associated_type( + &self, + diag: &mut Diagnostic, + msg: &str, + proj_ty: &ty::AliasTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let assoc = tcx.associated_item(proj_ty.def_id); + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() { + let opaque_local_def_id = def_id.as_local(); + let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { + match &tcx.hir().expect_item(opaque_local_def_id).kind { + hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty, + _ => bug!("The HirId comes from a `ty::Opaque`"), + } + } else { + return false; + }; + + let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx); + + self.constrain_generic_bound_associated_type_structured_suggestion( + diag, + &trait_ref, + opaque_hir_ty.bounds, + assoc, + assoc_substs, + ty, + msg, + true, + ) + } else { + false + } + } + + fn point_at_methods_that_satisfy_associated_type( + &self, + diag: &mut Diagnostic, + assoc_container_id: DefId, + current_method_ident: Option<Symbol>, + proj_ty_item_def_id: DefId, + expected: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let items = tcx.associated_items(assoc_container_id); + // Find all the methods in the trait that could be called to construct the + // expected associated type. + // FIXME: consider suggesting the use of associated `const`s. + let methods: Vec<(Span, String)> = items + .in_definition_order() + .filter(|item| { + ty::AssocKind::Fn == item.kind + && Some(item.name) != current_method_ident + && !tcx.is_doc_hidden(item.def_id) + }) + .filter_map(|item| { + let method = tcx.fn_sig(item.def_id).subst_identity(); + match *method.output().skip_binder().kind() { + ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. }) + if item_def_id == proj_ty_item_def_id => + { + Some(( + tcx.def_span(item.def_id), + format!("consider calling `{}`", tcx.def_path_str(item.def_id)), + )) + } + _ => None, + } + }) + .collect(); + if !methods.is_empty() { + // Use a single `help:` to show all the methods in the trait that can + // be used to construct the expected associated type. + let mut span: MultiSpan = + methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into(); + let msg = format!( + "{some} method{s} {are} available that return{r} `{ty}`", + some = if methods.len() == 1 { "a" } else { "some" }, + s = pluralize!(methods.len()), + are = pluralize!("is", methods.len()), + r = if methods.len() == 1 { "s" } else { "" }, + ty = expected + ); + for (sp, label) in methods.into_iter() { + span.push_span_label(sp, label); + } + diag.span_help(span, &msg); + return true; + } + false + } + + fn point_at_associated_type( + &self, + diag: &mut Diagnostic, + body_owner_def_id: DefId, + found: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let Some(hir_id) = body_owner_def_id.as_local() else { + return false; + }; + let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id); + // When `body_owner` is an `impl` or `trait` item, look in its associated types for + // `expected` and point at it. + let parent_id = tcx.hir().get_parent_item(hir_id); + let item = tcx.hir().find_by_def_id(parent_id.def_id); + + debug!("expected_projection parent item {:?}", item); + + let param_env = tcx.param_env(body_owner_def_id); + + match item { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { + // FIXME: account for `#![feature(specialization)]` + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type => { + // FIXME: account for returning some type in a trait fn impl that has + // an assoc type as a return type (#72076). + if let hir::Defaultness::Default { has_value: true } = + tcx.impl_defaultness(item.id.owner_id) + { + let assoc_ty = tcx.type_of(item.id.owner_id).subst_identity(); + if self.infcx.can_eq(param_env, assoc_ty, found) { + diag.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + return true; + } + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { items, .. }), + .. + })) => { + for item in &items[..] { + if let hir::AssocItemKind::Type = item.kind { + let assoc_ty = tcx.type_of(item.id.owner_id).subst_identity(); + + if self.infcx.can_eq(param_env, assoc_ty, found) { + diag.span_label(item.span, "expected this associated type"); + return true; + } + } + } + } + _ => {} + } + false + } + + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` + /// requirement, provide a structured suggestion to constrain it to a given type `ty`. + /// + /// `is_bound_surely_present` indicates whether we know the bound we're looking for is + /// inside `bounds`. If that's the case then we can consider `bounds` containing only one + /// trait bound as the one we're looking for. This can help in cases where the associated + /// type is defined on a supertrait of the one present in the bounds. + fn constrain_generic_bound_associated_type_structured_suggestion( + &self, + diag: &mut Diagnostic, + trait_ref: &ty::TraitRef<'tcx>, + bounds: hir::GenericBounds<'_>, + assoc: ty::AssocItem, + assoc_substs: &[ty::GenericArg<'tcx>], + ty: Ty<'tcx>, + msg: &str, + is_bound_surely_present: bool, + ) -> bool { + // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. + + let trait_bounds = bounds.iter().filter_map(|bound| match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr), + _ => None, + }); + + let matching_trait_bounds = trait_bounds + .clone() + .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)) + .collect::<Vec<_>>(); + + let span = match &matching_trait_bounds[..] { + &[ptr] => ptr.span, + &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] { + &[ptr] => ptr.span, + _ => return false, + }, + _ => return false, + }; + + self.constrain_associated_type_structured_suggestion( + diag, + span, + assoc, + assoc_substs, + ty, + msg, + ) + } + + /// Given a span corresponding to a bound, provide a structured suggestion to set an + /// associated type to a given type `ty`. + fn constrain_associated_type_structured_suggestion( + &self, + diag: &mut Diagnostic, + span: Span, + assoc: ty::AssocItem, + assoc_substs: &[ty::GenericArg<'tcx>], + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + let tcx = self.tcx; + + if let Ok(has_params) = + tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) + { + let (span, sugg) = if has_params { + let pos = span.hi() - BytePos(1); + let span = Span::new(pos, pos, span.ctxt(), span.parent()); + (span, format!(", {} = {}", assoc.ident(tcx), ty)) + } else { + let item_args = self.format_generic_args(assoc_substs); + (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty)) + }; + diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); + return true; + } + false + } + + pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String { + FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS) + .path_generic_args(Ok, args) + .expect("could not write to `String`.") + .into_buffer() + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 5b02956a1..55dcfd05e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -8,24 +8,25 @@ use rustc_middle::traits::{ StatementAsExpression, }; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self as ty, Ty, TypeVisitable}; +use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{sym, BytePos, Span}; -use crate::errors::SuggAddLetForLetChains; +use crate::errors::{ + ConsiderAddingAwait, SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding, +}; use super::TypeErrCtxt; impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn suggest_remove_semi_or_return_binding( &self, - err: &mut Diagnostic, first_id: Option<hir::HirId>, first_ty: Ty<'tcx>, first_span: Span, second_id: Option<hir::HirId>, second_ty: Ty<'tcx>, second_span: Span, - ) { + ) -> Option<SuggestRemoveSemiOrReturnBinding> { let remove_semicolon = [ (first_id, self.resolve_vars_if_possible(second_ty)), (second_id, self.resolve_vars_if_possible(first_ty)), @@ -37,35 +38,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }); match remove_semicolon { Some((sp, StatementAsExpression::NeedsBoxing)) => { - err.multipart_suggestion( - "consider removing this semicolon and boxing the expressions", - vec![ - (first_span.shrink_to_lo(), "Box::new(".to_string()), - (first_span.shrink_to_hi(), ")".to_string()), - (second_span.shrink_to_lo(), "Box::new(".to_string()), - (second_span.shrink_to_hi(), ")".to_string()), - (sp, String::new()), - ], - Applicability::MachineApplicable, - ); + Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox { + first_lo: first_span.shrink_to_lo(), + first_hi: first_span.shrink_to_hi(), + second_lo: second_span.shrink_to_lo(), + second_hi: second_span.shrink_to_hi(), + sp, + }) } Some((sp, StatementAsExpression::CorrectType)) => { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - "", - Applicability::MachineApplicable, - ); + Some(SuggestRemoveSemiOrReturnBinding::Remove { sp }) } None => { + let mut ret = None; for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { if let Some(id) = id && let hir::Node::Block(blk) = self.tcx.hir().get(id) - && self.consider_returning_binding(blk, ty, err) + && let Some(diag) = self.consider_returning_binding_diag(blk, ty) { + ret = Some(diag); break; } } + ret } } } @@ -198,7 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } - match ( + let subdiag = match ( self.get_impl_future_output_ty(exp_found.expected), self.get_impl_future_output_ty(exp_found.found), ) { @@ -207,82 +202,68 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { let then_span = self.find_block_span_from_hir_id(*then_id); - diag.multipart_suggestion( - "consider `await`ing on both `Future`s", - vec![ - (then_span.shrink_to_hi(), ".await".to_string()), - (exp_span.shrink_to_hi(), ".await".to_string()), - ], - Applicability::MaybeIncorrect, - ); + Some(ConsiderAddingAwait::BothFuturesSugg { + first: then_span.shrink_to_hi(), + second: exp_span.shrink_to_hi(), + }) } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { prior_arms, .. }) => { if let [.., arm_span] = &prior_arms[..] { - diag.multipart_suggestion( - "consider `await`ing on both `Future`s", - vec![ - (arm_span.shrink_to_hi(), ".await".to_string()), - (exp_span.shrink_to_hi(), ".await".to_string()), - ], - Applicability::MaybeIncorrect, - ); + Some(ConsiderAddingAwait::BothFuturesSugg { + first: arm_span.shrink_to_hi(), + second: exp_span.shrink_to_hi(), + }) } else { - diag.help("consider `await`ing on both `Future`s"); + Some(ConsiderAddingAwait::BothFuturesHelp) } } - _ => { - diag.help("consider `await`ing on both `Future`s"); - } + _ => Some(ConsiderAddingAwait::BothFuturesHelp), }, (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { - diag.span_suggestion_verbose( - exp_span.shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await", - Applicability::MaybeIncorrect, - ); + // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic + diag.subdiagnostic(ConsiderAddingAwait::FutureSugg { + span: exp_span.shrink_to_hi(), + }); + Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span }) } (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() { ObligationCauseCode::Pattern { span: Some(then_span), .. } => { - diag.span_suggestion_verbose( - then_span.shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await", - Applicability::MaybeIncorrect, - ); + Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() }) } ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { let then_span = self.find_block_span_from_hir_id(*then_id); - diag.span_suggestion_verbose( - then_span.shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await", - Applicability::MaybeIncorrect, - ); + Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() }) } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { ref prior_arms, .. - }) => { - diag.multipart_suggestion_verbose( - "consider `await`ing on the `Future`", - prior_arms - .iter() - .map(|arm| (arm.shrink_to_hi(), ".await".to_string())) - .collect(), - Applicability::MaybeIncorrect, - ); - } - _ => {} + }) => Some({ + ConsiderAddingAwait::FutureSuggMultiple { + spans: prior_arms.iter().map(|arm| arm.shrink_to_hi()).collect(), + } + }), + _ => None, }, - _ => {} + _ => None, + }; + if let Some(subdiag) = subdiag { + diag.subdiagnostic(subdiag); } } + pub fn suggest_await_on_future(&self, diag: &mut Diagnostic, sp: Span) { + diag.span_suggestion_verbose( + sp.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + pub(super) fn suggest_accessing_field_where_appropriate( &self, cause: &ObligationCause<'tcx>, @@ -351,6 +332,118 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } + pub(super) fn suggest_function_pointers( + &self, + cause: &ObligationCause<'tcx>, + span: Span, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + diag: &mut Diagnostic, + ) { + debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); + let ty::error::ExpectedFound { expected, found } = exp_found; + let expected_inner = expected.peel_refs(); + let found_inner = found.peel_refs(); + if !expected_inner.is_fn() || !found_inner.is_fn() { + return; + } + match (&expected_inner.kind(), &found_inner.kind()) { + (ty::FnPtr(sig), ty::FnDef(did, substs)) => { + let expected_sig = &(self.normalize_fn_sig)(*sig); + let found_sig = + &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).subst(self.tcx, substs)); + + let fn_name = self.tcx.def_path_str_with_substs(*did, substs); + + if !self.same_type_modulo_infer(*found_sig, *expected_sig) + || !sig.is_suggestable(self.tcx, true) + || ty::util::is_intrinsic(self.tcx, *did) + { + return; + } + + let (msg, sug) = match (expected.is_ref(), found.is_ref()) { + (true, false) => { + let msg = "consider using a reference"; + let sug = format!("&{fn_name}"); + (msg, sug) + } + (false, true) => { + let msg = "consider removing the reference"; + let sug = format!("{fn_name}"); + (msg, sug) + } + (true, true) => { + diag.note("fn items are distinct from fn pointers"); + let msg = "consider casting to a fn pointer"; + let sug = format!("&({fn_name} as {sig})"); + (msg, sug) + } + (false, false) => { + diag.note("fn items are distinct from fn pointers"); + let msg = "consider casting to a fn pointer"; + let sug = format!("{fn_name} as {sig}"); + (msg, sug) + } + }; + diag.span_suggestion_verbose(span, msg, sug, Applicability::MaybeIncorrect); + } + (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { + let expected_sig = + &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).subst(self.tcx, substs1)); + let found_sig = + &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2)); + + if self.same_type_modulo_infer(*expected_sig, *found_sig) { + diag.note("different fn items have unique types, even if their signatures are the same"); + } + + if !self.same_type_modulo_infer(*found_sig, *expected_sig) + || !found_sig.is_suggestable(self.tcx, true) + || !expected_sig.is_suggestable(self.tcx, true) + || ty::util::is_intrinsic(self.tcx, *did1) + || ty::util::is_intrinsic(self.tcx, *did2) + { + return; + } + + let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2); + let sug = if found.is_ref() { + format!("&({fn_name} as {found_sig})") + } else { + format!("{fn_name} as {found_sig}") + }; + + let msg = format!( + "consider casting both fn items to fn pointers using `as {expected_sig}`" + ); + + diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect); + } + (ty::FnDef(did, substs), ty::FnPtr(sig)) => { + let expected_sig = + &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).subst(self.tcx, substs)); + let found_sig = &(self.normalize_fn_sig)(*sig); + + if !self.same_type_modulo_infer(*found_sig, *expected_sig) { + return; + } + + let fn_name = self.tcx.def_path_str_with_substs(*did, substs); + + let casting = if expected.is_ref() { + format!("&({fn_name} as {found_sig})") + } else { + format!("{fn_name} as {found_sig}") + }; + + diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting)); + } + _ => { + return; + } + }; + } + 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, _)) = (expected.kind(), found.kind()) @@ -411,8 +504,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span: Span, ) { let hir = self.tcx.hir(); - let fn_hir_id = hir.parent_id(cause.body_id); - if let Some(node) = self.tcx.hir().find(fn_hir_id) && + if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) && let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_sig, _, body_id), .. }) = node { @@ -549,16 +641,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// Suggest returning a local binding with a compatible type if the block /// has no return expression. - pub fn consider_returning_binding( + pub fn consider_returning_binding_diag( &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, - err: &mut Diagnostic, - ) -> bool { + ) -> Option<SuggestRemoveSemiOrReturnBinding> { let blk = blk.innermost_block(); // Do not suggest if we have a tail expr. if blk.expr.is_some() { - return false; + return None; } let mut shadowed = FxIndexSet::default(); let mut candidate_idents = vec![]; @@ -627,7 +718,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { match &candidate_idents[..] { [(ident, _ty)] => { let sm = self.tcx.sess.source_map(); - if let Some(stmt) = blk.stmts.last() { + let (span, sugg) = if let Some(stmt) = blk.stmts.last() { let stmt_span = sm.stmt_span(stmt.span, blk.span); let sugg = if sm.is_multiline(blk.span) && let Some(spacing) = sm.indentation_before(stmt_span) @@ -636,12 +727,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } else { format!(" {ident}") }; - err.span_suggestion_verbose( - stmt_span.shrink_to_hi(), - format!("consider returning the local binding `{ident}`"), - sugg, - Applicability::MaybeIncorrect, - ); + (stmt_span.shrink_to_hi(), sugg) } else { let sugg = if sm.is_multiline(blk.span) && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) @@ -651,21 +737,34 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!(" {ident} ") }; let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); - err.span_suggestion_verbose( + ( sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), - format!("consider returning the local binding `{ident}`"), sugg, - Applicability::MaybeIncorrect, - ); - } - true + ) + }; + Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident }) } values if (1..3).contains(&values.len()) => { let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); - err.span_note(spans, "consider returning one of these bindings"); + Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() }) + } + _ => None, + } + } + + pub fn consider_returning_binding( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let diag = self.consider_returning_binding_diag(blk, expected_ty); + match diag { + Some(diag) => { + err.subdiagnostic(diag); true } - _ => false, + None => false, } } } diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 8f53b1ccd..f09f93abf 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -34,7 +34,7 @@ use super::InferCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_middle::infer::unify_key::ToType; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitableExt}; use std::collections::hash_map::Entry; pub struct TypeFreshener<'a, 'tcx> { @@ -58,14 +58,9 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { } } - fn freshen_ty<F>( - &mut self, - opt_ty: Option<Ty<'tcx>>, - key: ty::InferTy, - freshener: F, - ) -> Ty<'tcx> + fn freshen_ty<F>(&mut self, opt_ty: Option<Ty<'tcx>>, key: ty::InferTy, mk_fresh: F) -> Ty<'tcx> where - F: FnOnce(u32) -> ty::InferTy, + F: FnOnce(u32) -> Ty<'tcx>, { if let Some(ty) = opt_ty { return ty.fold_with(self); @@ -76,7 +71,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { Entry::Vacant(entry) => { let index = self.ty_freshen_count; self.ty_freshen_count += 1; - let t = self.infcx.tcx.mk_ty_infer(freshener(index)); + let t = mk_fresh(index); entry.insert(t); t } @@ -110,8 +105,8 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { } } -impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -126,92 +121,36 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::ReFree(_) | ty::ReVar(_) | ty::RePlaceholder(..) + | ty::ReError(_) | ty::ReErased => { // replace all free regions with 'erased - self.tcx().lifetimes.re_erased + self.interner().lifetimes.re_erased } ty::ReStatic => { if self.keep_static { r } else { - self.tcx().lifetimes.re_erased + self.interner().lifetimes.re_erased } } } } + #[inline] fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if !t.needs_infer() && !t.has_erasable_regions() { - return t; - } + t + } else { + match *t.kind() { + ty::Infer(v) => self.fold_infer_ty(v).unwrap_or(t), - let tcx = self.infcx.tcx; + // This code is hot enough that a non-debug assertion here makes a noticeable + // difference on benchmarks like `wg-grammar`. + #[cfg(debug_assertions)] + ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t), - match *t.kind() { - ty::Infer(ty::TyVar(v)) => { - let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); - self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy) + _ => t.super_fold_with(self), } - - ty::Infer(ty::IntVar(v)) => self.freshen_ty( - self.infcx - .inner - .borrow_mut() - .int_unification_table() - .probe_value(v) - .map(|v| v.to_type(tcx)), - ty::IntVar(v), - ty::FreshIntTy, - ), - - ty::Infer(ty::FloatVar(v)) => self.freshen_ty( - self.infcx - .inner - .borrow_mut() - .float_unification_table() - .probe_value(v) - .map(|v| v.to_type(tcx)), - ty::FloatVar(v), - ty::FreshFloatTy, - ), - - ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => { - if ct >= self.ty_freshen_count { - bug!( - "Encountered a freshend type with id {} \ - but our counter is only at {}", - ct, - self.ty_freshen_count - ); - } - t - } - - ty::Generator(..) - | ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Adt(..) - | ty::Str - | ty::Error(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Dynamic(..) - | ty::Never - | ty::Tuple(..) - | ty::Alias(..) - | ty::Foreign(..) - | ty::Param(..) - | ty::Closure(..) - | ty::GeneratorWitness(..) => t.super_fold_with(self), - - ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t), } } @@ -252,3 +191,54 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { } } } + +impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { + // This is separate from `fold_ty` to keep that method small and inlinable. + #[inline(never)] + fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option<Ty<'tcx>> { + match v { + ty::TyVar(v) => { + let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); + Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| self.infcx.tcx.mk_fresh_ty(n))) + } + + ty::IntVar(v) => Some( + self.freshen_ty( + self.infcx + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.infcx.tcx)), + ty::IntVar(v), + |n| self.infcx.tcx.mk_fresh_int_ty(n), + ), + ), + + ty::FloatVar(v) => Some( + self.freshen_ty( + self.infcx + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.infcx.tcx)), + ty::FloatVar(v), + |n| self.infcx.tcx.mk_fresh_float_ty(n), + ), + ), + + ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => { + if ct >= self.ty_freshen_count { + bug!( + "Encountered a freshend type with id {} \ + but our counter is only at {}", + ct, + self.ty_freshen_count + ); + } + None + } + } + } +} diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs index 6dd6c4e1f..86c2c2be4 100644 --- a/compiler/rustc_infer/src/infer/fudge.rs +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -98,7 +98,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where F: FnOnce() -> Result<T, E>, - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { let variable_lengths = self.variable_lengths(); let (mut fudger, value) = self.probe(|_| { @@ -175,8 +175,8 @@ pub struct InferenceFudger<'a, 'tcx> { const_vars: (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>), } -impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.infcx.tcx } diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs index 21b68ce99..49df393d8 100644 --- a/compiler/rustc_infer/src/infer/glb.rs +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -1,14 +1,13 @@ //! Greatest lower bound. See [`lattice`]. -use super::combine::CombineFields; +use super::combine::{CombineFields, ObligationEmittingRelation}; use super::lattice::{self, LatticeDir}; use super::InferCtxt; use super::Subtype; -use crate::infer::combine::ConstEquateRelation; -use crate::traits::{ObligationCause, PredicateObligation}; +use crate::traits::{ObligationCause, PredicateObligations}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; /// "Greatest lower bound" (common subtype) pub struct Glb<'combine, 'infcx, 'tcx> { @@ -79,7 +78,8 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { debug!("{}.regions({:?}, {:?})", self.tag(), a, b); let origin = Subtype(Box::new(self.fields.trace.clone())); - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( + // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 + Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( self.tcx(), origin, a, @@ -135,10 +135,6 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, &self.fields.trace.cause } - fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) { - self.fields.obligations.extend(obligations) - } - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let mut sub = self.fields.sub(self.a_is_expected); sub.relate(v, a)?; @@ -151,8 +147,12 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, } } -impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); +impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index 817ae10c7..d1897cf24 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -6,7 +6,7 @@ use super::{HigherRankedType, InferCtxt}; use crate::infer::CombinedSnapshot; use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Binder, TypeFoldable}; +use rustc_middle::ty::{self, Binder, TyCtxt, TypeFoldable}; impl<'a, 'tcx> CombineFields<'a, 'tcx> { /// Checks whether `for<..> sub <: for<..> sup` holds. @@ -38,13 +38,13 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { // First, we instantiate each bound region in the supertype with a // fresh placeholder region. Note that this automatically creates // a new universe if needed. - let sup_prime = self.infcx.replace_bound_vars_with_placeholders(sup); + let sup_prime = self.infcx.instantiate_binder_with_placeholders(sup); // Next, we instantiate each bound region in the subtype // with a fresh region variable. These region variables -- // but no other pre-existing region variables -- can name // the placeholders. - let sub_prime = self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, sub); + let sub_prime = self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub); debug!("a_prime={:?}", sub_prime); debug!("b_prime={:?}", sup_prime); @@ -70,9 +70,9 @@ impl<'tcx> InferCtxt<'tcx> { /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html #[instrument(level = "debug", skip(self), ret)] - pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T + pub fn instantiate_binder_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T where - T: TypeFoldable<'tcx> + Copy, + T: TypeFoldable<TyCtxt<'tcx>> + Copy, { if let Some(inner) = binder.no_bound_vars() { return inner; @@ -82,16 +82,16 @@ impl<'tcx> InferCtxt<'tcx> { let delegate = FnMutDelegate { regions: &mut |br: ty::BoundRegion| { - self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { + self.tcx.mk_re_placeholder(ty::PlaceholderRegion { universe: next_universe, name: br.kind, - })) + }) }, types: &mut |bound_ty: ty::BoundTy| { - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + self.tcx.mk_placeholder(ty::PlaceholderType { universe: next_universe, - name: bound_ty.var, - })) + name: bound_ty.kind, + }) }, consts: &mut |bound_var: ty::BoundVar, ty| { self.tcx diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 4dbb4b4d7..f377ac1d1 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -17,11 +17,12 @@ //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) +use super::combine::ObligationEmittingRelation; use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::InferCtxt; -use crate::traits::{ObligationCause, PredicateObligation}; -use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use crate::traits::ObligationCause; +use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty}; @@ -30,13 +31,11 @@ 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> { +pub trait LatticeDir<'f, 'tcx>: ObligationEmittingRelation<'tcx> { fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; - fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>); - fn define_opaque_types(&self) -> bool; // Relates the type `v` to `a` and `b` such that `v` represents @@ -113,7 +112,7 @@ where | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if this.define_opaque_types() && def_id.is_local() => { - this.add_obligations( + this.register_obligations( infcx .handle_opaque_type(a, b, this.a_is_expected(), this.cause(), this.param_env())? .obligations, 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 ce8aec804..2c4803550 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -17,7 +17,7 @@ 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, ReErased, ReFree, ReStatic}; +use rustc_middle::ty::{ReEarlyBound, ReErased, ReError, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; @@ -70,7 +70,7 @@ pub enum RegionResolutionError<'tcx> { /// `o` requires that `a <= b`, but this does not hold ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - /// `GenericBoundFailure(p, s, a) + /// `GenericBoundFailure(p, s, a)`: /// /// The parameter/associated-type `p` must be known to outlive the lifetime /// `a` (but none of the known bounds are sufficient). @@ -216,6 +216,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { Ok(self.tcx().lifetimes.re_static) } + ReError(_) => Ok(a_region), + ReEarlyBound(_) | ReFree(_) => { // All empty regions are less than early-bound, free, // and scope regions. @@ -380,7 +382,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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)) + self.tcx().mk_re_placeholder(placeholder) } Err(_) => self.tcx().lifetimes.re_static, }; @@ -436,6 +438,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Value(a), VarValue::Empty(_)) => { match *a { + // this is always on an error path, + // so it doesn't really matter if it's shorter or longer than an empty region + ReError(_) => false, + ReLateBound(..) | ReErased => { bug!("cannot relate region: {:?}", a); } @@ -465,6 +471,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Empty(a_ui), VarValue::Value(b)) => { match *b { + // this is always on an error path, + // so it doesn't really matter if it's shorter or longer than an empty region + ReError(_) => false, + ReLateBound(..) | ReErased => { bug!("cannot relate region: {:?}", b); } @@ -546,6 +556,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } + (ReError(_), _) => a, + + (_, ReError(_)) => b, + (ReStatic, _) | (_, ReStatic) => { // nothing lives longer than `'static` self.tcx().lifetimes.re_static @@ -1018,7 +1032,7 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { impl<'tcx> LexicalRegionResolutions<'tcx> { fn normalize<T>(&self, tcx: TyCtxt<'tcx>, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { tcx.fold_regions(value, |r, _db| self.resolve_region(tcx, r)) } diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index c07ac1d3a..c871ccb21 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -1,14 +1,13 @@ //! Least upper bound. See [`lattice`]. -use super::combine::CombineFields; +use super::combine::{CombineFields, ObligationEmittingRelation}; use super::lattice::{self, LatticeDir}; use super::InferCtxt; use super::Subtype; -use crate::infer::combine::ConstEquateRelation; -use crate::traits::{ObligationCause, PredicateObligation}; +use crate::traits::{ObligationCause, PredicateObligations}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; /// "Least upper bound" (common supertype) pub struct Lub<'combine, 'infcx, 'tcx> { @@ -79,7 +78,8 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { debug!("{}.regions({:?}, {:?})", self.tag(), a, b); let origin = Subtype(Box::new(self.fields.trace.clone())); - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( + // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 + Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( self.tcx(), origin, a, @@ -126,12 +126,6 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { } } -impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); - } -} - impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { fn infcx(&self) -> &'infcx InferCtxt<'tcx> { self.fields.infcx @@ -141,10 +135,6 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, &self.fields.trace.cause } - fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) { - self.fields.obligations.extend(obligations) - } - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let mut sub = self.fields.sub(self.a_is_expected); sub.relate(a, v)?; @@ -156,3 +146,13 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, self.fields.define_opaque_types } } + +impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations) + } +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index f0e42c1fc..bd1f96635 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -4,6 +4,7 @@ pub use self::LateBoundRegionConversionTime::*; pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; +pub use combine::ObligationEmittingRelation; use self::opaque_types::OpaqueTypeStorage; pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; @@ -28,9 +29,9 @@ use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; -use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; pub use rustc_middle::ty::IntVarValue; -use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -155,7 +156,7 @@ pub struct InferCtxtInner<'tcx> { undo_log: InferCtxtUndoLogs<'tcx>, /// Caches for opaque type inference. - pub opaque_type_storage: OpaqueTypeStorage<'tcx>, + opaque_type_storage: OpaqueTypeStorage<'tcx>, } impl<'tcx> InferCtxtInner<'tcx> { @@ -194,41 +195,17 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - fn int_unification_table( - &mut self, - ) -> ut::UnificationTable< - ut::InPlace< - ty::IntVid, - &mut ut::UnificationStorage<ty::IntVid>, - &mut InferCtxtUndoLogs<'tcx>, - >, - > { + fn int_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::IntVid> { self.int_unification_storage.with_log(&mut self.undo_log) } #[inline] - fn float_unification_table( - &mut self, - ) -> ut::UnificationTable< - ut::InPlace< - ty::FloatVid, - &mut ut::UnificationStorage<ty::FloatVid>, - &mut InferCtxtUndoLogs<'tcx>, - >, - > { + fn float_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::FloatVid> { self.float_unification_storage.with_log(&mut self.undo_log) } #[inline] - fn const_unification_table( - &mut self, - ) -> ut::UnificationTable< - ut::InPlace< - ty::ConstVid<'tcx>, - &mut ut::UnificationStorage<ty::ConstVid<'tcx>>, - &mut InferCtxtUndoLogs<'tcx>, - >, - > { + fn const_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::ConstVid<'tcx>> { self.const_unification_storage.with_log(&mut self.undo_log) } @@ -263,7 +240,7 @@ pub struct InferCtxt<'tcx> { /// short lived InferCtxt within queries. The opaque type obligations are forwarded /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. /// - /// It is default value is `DefiningAnchor::Error`, this way it is easier to catch errors that + /// Its default value is `DefiningAnchor::Error`, this way it is easier to catch errors that /// might come up during inference or typeck. pub defining_use_anchor: DefiningAnchor, @@ -616,7 +593,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { canonical: &Canonical<'tcx, T>, ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { let infcx = self.build(); let (value, subst) = infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); @@ -696,7 +673,7 @@ impl<'tcx> InferCtxt<'tcx> { self.in_snapshot.get() } - pub fn freshen<T: TypeFoldable<'tcx>>(&self, t: T) -> T { + pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } @@ -879,30 +856,20 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup); } - pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx> + pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool where T: at::ToTrace<'tcx>, { let origin = &ObligationCause::dummy(); - self.probe(|_| { - self.at(origin, param_env).sub(a, b).map(|InferOk { obligations: _, .. }| { - // Ignore obligations, since we are unrolling - // everything anyway. - }) - }) + self.probe(|_| self.at(origin, param_env).sub(a, b).is_ok()) } - pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx> + pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool where T: at::ToTrace<'tcx>, { let origin = &ObligationCause::dummy(); - self.probe(|_| { - self.at(origin, param_env).eq(a, b).map(|InferOk { obligations: _, .. }| { - // Ignore obligations, since we are unrolling - // everything anyway. - }) - }) + self.probe(|_| self.at(origin, param_env).eq(a, b).is_ok()) } #[instrument(skip(self), level = "debug")] @@ -995,7 +962,7 @@ impl<'tcx> InferCtxt<'tcx> { Ok(self.commit_if_ok(|_snapshot| { let ty::SubtypePredicate { a_is_expected, a, b } = - self.replace_bound_vars_with_placeholders(predicate); + self.instantiate_binder_with_placeholders(predicate); let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; @@ -1008,7 +975,7 @@ impl<'tcx> InferCtxt<'tcx> { cause: &traits::ObligationCause<'tcx>, predicate: ty::PolyRegionOutlivesPredicate<'tcx>, ) { - let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(predicate); + let ty::OutlivesPredicate(r_a, r_b) = self.instantiate_binder_with_placeholders(predicate); let origin = SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span)); self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` @@ -1102,7 +1069,7 @@ impl<'tcx> InferCtxt<'tcx> { ) -> ty::Region<'tcx> { let region_var = self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, origin); - self.tcx.mk_region(ty::ReVar(region_var)) + self.tcx.mk_re_var(region_var) } /// Return the universe that the region `r` was created in. For @@ -1120,11 +1087,13 @@ impl<'tcx> InferCtxt<'tcx> { } /// Just a convenient wrapper of `next_region_var` for using during NLL. + #[instrument(skip(self), level = "debug")] pub fn next_nll_region_var(&self, origin: NllRegionVariableOrigin) -> ty::Region<'tcx> { self.next_region_var(RegionVariableOrigin::Nll(origin)) } /// Just a convenient wrapper of `next_region_var` for using during NLL. + #[instrument(skip(self), level = "debug")] pub fn next_nll_region_var_in_universe( &self, origin: NllRegionVariableOrigin, @@ -1175,7 +1144,15 @@ impl<'tcx> InferCtxt<'tcx> { origin, val: ConstVariableValue::Unknown { universe: self.universe() }, }); - self.tcx.mk_const(const_var_id, self.tcx.type_of(param.def_id)).into() + self.tcx + .mk_const( + const_var_id, + self.tcx + .type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + ) + .into() } } } @@ -1370,7 +1347,7 @@ impl<'tcx> InferCtxt<'tcx> { /// will be resolving them as well, e.g. in a loop). pub fn shallow_resolve<T>(&self, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { value.fold_with(&mut ShallowResolver { infcx: self }) } @@ -1387,10 +1364,10 @@ impl<'tcx> InferCtxt<'tcx> { /// at will. pub fn resolve_vars_if_possible<T>(&self, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { - if !value.needs_infer() { - return value; // Avoid duplicated subst-folding. + if !value.has_non_region_infer() { + return value; } let mut r = resolve::OpportunisticVarResolver::new(self); value.fold_with(&mut r) @@ -1398,7 +1375,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn resolve_numeric_literals_with_default<T>(&self, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { if !value.needs_infer() { return value; // Avoid duplicated subst-folding. @@ -1413,7 +1390,7 @@ impl<'tcx> InferCtxt<'tcx> { value: &T, ) -> Option<(ty::Term<'tcx>, Option<Span>)> where - T: TypeVisitable<'tcx>, + T: TypeVisitable<TyCtxt<'tcx>>, { value.visit_with(&mut resolve::UnresolvedTypeOrConstFinder::new(self)).break_value() } @@ -1428,17 +1405,14 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn fully_resolve<T: TypeFoldable<'tcx>>(&self, value: T) -> FixupResult<'tcx, T> { - /*! - * Attempts to resolve all type/region/const variables in - * `value`. Region inference must have been run already (e.g., - * by calling `resolve_regions_and_report_errors`). If some - * variable was never unified, an `Err` results. - * - * This method is idempotent, but it not typically not invoked - * except during the writeback phase. - */ - + /// Attempts to resolve all type/region/const variables in + /// `value`. Region inference must have been run already (e.g., + /// by calling `resolve_regions_and_report_errors`). If some + /// variable was never unified, an `Err` results. + /// + /// This method is idempotent, but it not typically not invoked + /// except during the writeback phase. + pub fn fully_resolve<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> FixupResult<'tcx, T> { let value = resolve::fully_resolve(self, value); assert!( value.as_ref().map_or(true, |value| !value.needs_infer()), @@ -1447,14 +1421,21 @@ impl<'tcx> InferCtxt<'tcx> { value } - pub fn replace_bound_vars_with_fresh_vars<T>( + // Instantiates the bound variables in a given binder with fresh inference + // variables in the current universe. + // + // Use this method if you'd like to find some substitution of the binder's + // variables (e.g. during a method call). If there isn't a [`LateBoundRegionConversionTime`] + // that corresponds to your use case, consider whether or not you should + // use [`InferCtxt::instantiate_binder_with_placeholders`] instead. + pub fn instantiate_binder_with_fresh_vars<T>( &self, span: Span, lbrct: LateBoundRegionConversionTime, value: ty::Binder<'tcx, T>, ) -> T where - T: TypeFoldable<'tcx> + Copy, + T: TypeFoldable<TyCtxt<'tcx>> + Copy, { if let Some(inner) = value.no_bound_vars() { return inner; @@ -1746,7 +1727,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // 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, @@ -1844,8 +1824,8 @@ struct InferenceLiteralEraser<'tcx> { tcx: TyCtxt<'tcx>, } -impl<'tcx> TypeFolder<'tcx> for InferenceLiteralEraser<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceLiteralEraser<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } @@ -1862,17 +1842,41 @@ struct ShallowResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, } -impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ShallowResolver<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.infcx.tcx } /// If `ty` is a type variable of some kind, resolve it one level /// (but do not resolve types found in the result). If `typ` is /// not a type variable, just return it unmodified. + #[inline] fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Infer(ty::TyVar(v)) => { + if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { + self.infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .val + .known() + .unwrap_or(ct) + } else { + ct + } + } +} + +impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { + // This is separate from `fold_ty` to keep that method small and inlinable. + #[inline(never)] + fn fold_infer_ty(&mut self, v: InferTy) -> Option<Ty<'tcx>> { + match v { + ty::TyVar(v) => { // Not entirely obvious: if `typ` is a type variable, // it can be resolved to an int/float variable, which // can then be recursively resolved, hence the @@ -1886,41 +1890,26 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { // Note: if these two lines are combined into one we get // dynamic borrow errors on `self.inner`. let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); - known.map_or(ty, |t| self.fold_ty(t)) + known.map(|t| self.fold_ty(t)) } - ty::Infer(ty::IntVar(v)) => self + ty::IntVar(v) => self .infcx .inner .borrow_mut() .int_unification_table() .probe_value(v) - .map_or(ty, |v| v.to_type(self.infcx.tcx)), + .map(|v| v.to_type(self.infcx.tcx)), - ty::Infer(ty::FloatVar(v)) => self + ty::FloatVar(v) => self .infcx .inner .borrow_mut() .float_unification_table() .probe_value(v) - .map_or(ty, |v| v.to_type(self.infcx.tcx)), - - _ => ty, - } - } + .map(|v| v.to_type(self.infcx.tcx)), - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { - self.infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(vid) - .val - .known() - .unwrap_or(ct) - } else { - ct + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None, } } } @@ -2044,24 +2033,24 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>( ) -> SubstsRef<'tcx> { struct ReplaceParamAndInferWithPlaceholder<'tcx> { tcx: TyCtxt<'tcx>, - idx: usize, + idx: u32, } - impl<'tcx> TypeFolder<'tcx> for ReplaceParamAndInferWithPlaceholder<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { + impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceParamAndInferWithPlaceholder<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if let ty::Infer(_) = t.kind() { - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + self.tcx.mk_placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, - name: ty::BoundVar::from_usize({ + name: ty::BoundTyKind::Anon({ let idx = self.idx; self.idx += 1; idx }), - })) + }) } else { t.super_fold_with(self) } @@ -2077,7 +2066,7 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>( self.tcx.mk_const( ty::PlaceholderConst { universe: ty::UniverseIndex::ROOT, - name: ty::BoundVar::from_usize({ + name: ty::BoundVar::from_u32({ let idx = self.idx; self.idx += 1; idx diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index f235cb5ab..573cd91a2 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -21,26 +21,21 @@ //! thing we relate in chalk are basically domain goals and their //! constituents) -use crate::infer::combine::ConstEquateRelation; use crate::infer::InferCtxt; use crate::infer::{ConstVarValue, ConstVariableValue}; use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::traits::{Obligation, PredicateObligation}; +use crate::traits::{Obligation, PredicateObligations}; 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}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::fmt::Debug; use std::ops::ControlFlow; -#[derive(PartialEq)] -pub enum NormalizationStrategy { - Lazy, - Eager, -} +use super::combine::ObligationEmittingRelation; pub struct TypeRelating<'me, 'tcx, D> where @@ -55,7 +50,7 @@ where /// /// - Covariant means `a <: b`. /// - Contravariant means `b <: a`. - /// - Invariant means `a == b. + /// - Invariant means `a == b`. /// - Bivariant means that it doesn't matter. ambient_variance: ty::Variance, @@ -92,7 +87,7 @@ pub trait TypeRelatingDelegate<'tcx> { info: ty::VarianceDiagInfo<'tcx>, ); - fn register_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>); + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); /// Creates a new universe index. Used when instantiating placeholders. fn create_next_universe(&mut self) -> ty::UniverseIndex; @@ -105,7 +100,11 @@ pub trait TypeRelatingDelegate<'tcx> { /// we will invoke this method to instantiate `'a` with an /// inference variable (though `'b` would be instantiated first, /// as a placeholder). - fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>; + fn next_existential_region_var( + &mut self, + was_placeholder: bool, + name: Option<Symbol>, + ) -> ty::Region<'tcx>; /// Creates a new region variable representing a /// higher-ranked region that is instantiated universally. @@ -125,9 +124,6 @@ pub trait TypeRelatingDelegate<'tcx> { /// relation stating that `'?0: 'a`). fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; - /// Define the normalization strategy to use, eager or lazy. - fn normalization() -> NormalizationStrategy; - /// Enables some optimizations if we do not expect inference variables /// in the RHS of the relation. fn forbid_inference_vars() -> bool; @@ -196,7 +192,7 @@ where let placeholder = ty::PlaceholderRegion { universe, name: br.kind }; delegate.next_placeholder_region(placeholder) } else { - delegate.next_existential_region_var(true) + delegate.next_existential_region_var(true, br.kind.get_name()) } } }; @@ -265,38 +261,6 @@ where self.delegate.push_outlives(sup, sub, info); } - /// Relate a projection type and some value type lazily. This will always - /// succeed, but we push an additional `ProjectionEq` goal depending - /// on the value type: - /// - if the value type is any type `T` which is not a projection, we push - /// `ProjectionEq(projection = T)`. - /// - if the value type is another projection `other_projection`, we create - /// a new inference variable `?U` and push the two goals - /// `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`. - fn relate_projection_ty( - &mut self, - projection_ty: ty::AliasTy<'tcx>, - value_ty: Ty<'tcx>, - ) -> Ty<'tcx> { - use rustc_span::DUMMY_SP; - - match *value_ty.kind() { - ty::Alias(ty::Projection, other_projection_ty) => { - let var = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }); - // FIXME(lazy-normalization): This will always ICE, because the recursive - // call will end up in the _ arm below. - self.relate_projection_ty(projection_ty, var); - self.relate_projection_ty(other_projection_ty, var); - var - } - - _ => bug!("should never be invoked with eager normalization"), - } - } - /// Relate a type inference variable with a value type. This works /// by creating a "generalization" G of the value where all the /// lifetimes are replaced with fresh inference values. This @@ -335,12 +299,6 @@ where return Ok(value_ty); } - ty::Alias(ty::Projection, projection_ty) - if D::normalization() == NormalizationStrategy::Lazy => - { - return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid))); - } - _ => (), } @@ -627,18 +585,6 @@ where self.relate_opaques(a, b) } - (&ty::Alias(ty::Projection, projection_ty), _) - if D::normalization() == NormalizationStrategy::Lazy => - { - Ok(self.relate_projection_ty(projection_ty, b)) - } - - (_, &ty::Alias(ty::Projection, projection_ty)) - if D::normalization() == NormalizationStrategy::Lazy => - { - Ok(self.relate_projection_ty(projection_ty, a)) - } - _ => { debug!(?a, ?b, ?self.ambient_variance); @@ -663,13 +609,13 @@ where debug!(?v_b); if self.ambient_covariance() { - // Covariance: a <= b. Hence, `b: a`. - self.push_outlives(v_b, v_a, self.ambient_variance_info); + // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. + self.push_outlives(v_a, v_b, self.ambient_variance_info); } if self.ambient_contravariance() { - // Contravariant: b <= a. Hence, `a: b`. - self.push_outlives(v_a, v_b, self.ambient_variance_info); + // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. + self.push_outlives(v_b, v_a, self.ambient_variance_info); } Ok(a) @@ -813,16 +759,23 @@ where } } -impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> +impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { - fn const_equate_obligation(&mut self, _a: ty::Const<'tcx>, _b: ty::Const<'tcx>) { - // We don't have to worry about the equality of consts during borrow checking - // as consts always have a static lifetime. - // FIXME(oli-obk): is this really true? We can at least have HKL and with - // inline consts we may have further lifetimes that may be unsound to treat as - // 'static. + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { + self.delegate.register_obligations( + obligations + .into_iter() + .map(|to_pred| { + Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) + }) + .collect(), + ); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.delegate.register_obligations(obligations); } } @@ -840,8 +793,8 @@ struct ScopeInstantiator<'me, 'tcx> { bound_region_scope: &'me mut BoundRegionScope<'tcx>, } -impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { - fn visit_binder<T: TypeVisitable<'tcx>>( +impl<'me, 'tcx> TypeVisitor<TyCtxt<'tcx>> for ScopeInstantiator<'me, 'tcx> { + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( &mut self, t: &ty::Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index c54c66eab..d5c824d4c 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -3,7 +3,7 @@ use crate::infer::{DefiningAnchor, InferCtxt, InferOk}; use crate::traits; use hir::def::DefKind; use hir::def_id::{DefId, LocalDefId}; -use hir::{HirId, OpaqueTyOrigin}; +use hir::OpaqueTyOrigin; use rustc_data_structures::sync::Lrc; use rustc_data_structures::vec_map::VecMap; use rustc_hir as hir; @@ -13,7 +13,7 @@ use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{ self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitor, + TypeVisitable, TypeVisitableExt, TypeVisitor, }; use rustc_span::Span; @@ -45,10 +45,10 @@ pub struct OpaqueTypeDecl<'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>>( + pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<TyCtxt<'tcx>>>( &self, value: T, - body_id: HirId, + body_id: LocalDefId, span: Span, param_env: ty::ParamEnv<'tcx>, ) -> InferOk<'tcx, T> { @@ -57,9 +57,7 @@ impl<'tcx> InferCtxt<'tcx> { } let mut obligations = vec![]; let replace_opaque_type = |def_id: DefId| { - def_id - .as_local() - .map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some()) + def_id.as_local().map_or(false, |def_id| self.opaque_type_origin(def_id).is_some()) }; let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx, @@ -144,9 +142,9 @@ impl<'tcx> InferCtxt<'tcx> { // let x = || foo(); // returns the Opaque assoc with `foo` // } // ``` - self.opaque_type_origin(def_id, cause.span)? + self.opaque_type_origin(def_id)? } - DefiningAnchor::Bubble => self.opaque_ty_origin_unchecked(def_id, cause.span), + DefiningAnchor::Bubble => self.opaque_type_origin_unchecked(def_id), DefiningAnchor::Error => return None, }; if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() { @@ -155,9 +153,8 @@ impl<'tcx> InferCtxt<'tcx> { // no one encounters it in practice. // It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, // where it is of no concern, so we only check for TAITs. - if let Some(OpaqueTyOrigin::TyAlias) = b_def_id - .as_local() - .and_then(|b_def_id| self.opaque_type_origin(b_def_id, cause.span)) + if let Some(OpaqueTyOrigin::TyAlias) = + b_def_id.as_local().and_then(|b_def_id| self.opaque_type_origin(b_def_id)) { self.tcx.sess.emit_err(OpaqueHiddenTypeDiag { span: cause.span, @@ -371,24 +368,18 @@ impl<'tcx> InferCtxt<'tcx> { }); } + /// Returns the origin of the opaque type `def_id` if we're currently + /// in its defining scope. #[instrument(skip(self), level = "trace", ret)] - pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> { + pub fn opaque_type_origin(&self, def_id: LocalDefId) -> 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 { DefiningAnchor::Bubble | DefiningAnchor::Error => return None, DefiningAnchor::Bind(bind) => bind, }; - let item_kind = &self.tcx.hir().expect_item(def_id).kind; - - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { - span_bug!( - span, - "weird opaque type: {:#?}, {:#?}", - def_id, - item_kind - ) - }; - let in_definition_scope = match *origin { + + let origin = self.opaque_type_origin_unchecked(def_id); + let in_definition_scope = match origin { // Async `impl Trait` hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, // Anonymous `impl Trait` @@ -398,16 +389,17 @@ impl<'tcx> InferCtxt<'tcx> { may_define_opaque_type(self.tcx, parent_def_id, opaque_hir_id) } }; - trace!(?origin); - in_definition_scope.then_some(*origin) + in_definition_scope.then_some(origin) } + /// Returns the origin of the opaque type `def_id` even if we are not in its + /// defining scope. #[instrument(skip(self), level = "trace", ret)] - fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { + fn opaque_type_origin_unchecked(&self, def_id: LocalDefId) -> OpaqueTyOrigin { 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) + bug!("weird opaque type: {:?}, {:#?}", def_id, itemkind) } } } @@ -431,11 +423,11 @@ pub struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { pub op: OP, } -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> where OP: FnMut(ty::Region<'tcx>), { - fn visit_binder<T: TypeVisitable<'tcx>>( + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( &mut self, t: &ty::Binder<'tcx, T>, ) -> ControlFlow<Self::BreakTy> { @@ -553,8 +545,11 @@ impl<'tcx> InferCtxt<'tcx> { origin, ); if let Some(prev) = prev { - obligations = - self.at(&cause, param_env).eq_exp(a_is_expected, prev, hidden_ty)?.obligations; + obligations = self + .at(&cause, param_env) + .define_opaque_types(true) + .eq_exp(a_is_expected, prev, hidden_ty)? + .obligations; } let item_bounds = tcx.bound_explicit_item_bounds(def_id.to_def_id()); diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 3d86279b0..ff23087fe 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -4,7 +4,7 @@ use rustc_data_structures::sso::SsoHashSet; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use smallvec::{smallvec, SmallVec}; #[derive(Debug)] @@ -112,7 +112,7 @@ fn compute_components<'tcx>( } // All regions are bound inside a witness - ty::GeneratorWitness(..) => (), + ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => (), // OutlivesTypeParameterEnv -- the actual checking that `X:'a` // is implied by the environment is done in regionck. diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 4daa25767..83f3d5a74 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -21,6 +21,8 @@ pub fn explicit_outlives_bounds<'tcx>( .filter_map(move |kind| match kind { ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::WellFormed(..) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 0194549a8..bbe7d4c63 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -69,7 +69,7 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitableExt}; use smallvec::smallvec; impl<'tcx> InferCtxt<'tcx> { 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 10b474efd..3c6cc2b90 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -1,7 +1,7 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{ self, error::TypeError, @@ -186,7 +186,8 @@ 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 let ty::Error(_) = pattern.kind() { + // FIXME(non_lifetime_binders): What to do here? + if matches!(pattern.kind(), ty::Error(_) | ty::Bound(..)) { // Unlike normal `TypeRelation` rules, `ty::Error` does not equal any type. self.no_match() } else if pattern == value { diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 94de9bc2d..bae246418 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -207,6 +207,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// In some cases, such as when `erased_ty` represents a `ty::Param`, however, /// the result is precise. + #[instrument(level = "debug", skip(self))] fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 4667d99ff..f79504770 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -21,16 +21,28 @@ impl<'tcx> InferCtxt<'tcx> { recursion_depth: usize, obligations: &mut Vec<PredicateObligation<'tcx>>, ) -> Ty<'tcx> { - let def_id = projection_ty.def_id; - let ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: self.tcx.def_span(def_id), - }); - let projection = - ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, term: ty_var.into() }); - let obligation = - Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection); - obligations.push(obligation); - ty_var + if self.tcx.trait_solver_next() { + // FIXME(-Ztrait-solver=next): Instead of branching here, + // completely change the normalization routine with the new solver. + // + // The new solver correctly handles projection equality so this hack + // is not necessary. if re-enabled it should emit `PredicateKind::AliasEq` + // not `PredicateKind::Clause(Clause::Projection(..))` as in the new solver + // `Projection` is used as `normalizes-to` which will fail for `<T as Trait>::Assoc eq ?0`. + return projection_ty.to_ty(self.tcx); + } else { + let def_id = projection_ty.def_id; + let ty_var = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.tcx.def_span(def_id), + }); + let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( + ty::ProjectionPredicate { projection_ty, term: ty_var.into() }, + ))); + let obligation = + Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection); + obligations.push(obligation); + ty_var + } } } 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 c46edc33f..e413b2bb5 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -280,7 +280,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> { placeholder1: ty::PlaceholderRegion, placeholder2: ty::PlaceholderRegion, ) -> TypeError<'tcx> { - self.error(placeholder1, self.tcx.mk_region(ty::RePlaceholder(placeholder2))) + self.error(placeholder1, self.tcx.mk_re_placeholder(placeholder2)) } fn error( @@ -413,19 +413,19 @@ impl<'tcx> MiniGraph<'tcx> { for undo_entry in undo_log { match undo_entry { &AddConstraint(Constraint::VarSubVar(a, b)) => { - each_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); + each_edge(tcx.mk_re_var(a), tcx.mk_re_var(b)); } &AddConstraint(Constraint::RegSubVar(a, b)) => { - each_edge(a, tcx.mk_region(ReVar(b))); + each_edge(a, tcx.mk_re_var(b)); } &AddConstraint(Constraint::VarSubReg(a, b)) => { - each_edge(tcx.mk_region(ReVar(a)), b); + each_edge(tcx.mk_re_var(a), b); } &AddConstraint(Constraint::RegSubReg(a, b)) => { each_edge(a, b); } &AddGiven(a, b) => { - each_edge(a, tcx.mk_region(ReVar(b))); + each_edge(a, tcx.mk_re_var(b)); } &AddVerify(i) => span_bug!( verifys[i].origin.span(), diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0428481b7..872f61747 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -249,7 +249,7 @@ pub enum VerifyBound<'tcx> { /// in that case we can show `'b: 'c`. But if `'?x` winds up being something /// else, the bound isn't relevant. /// -/// In the [`VerifyBound`], this struct is enclosed in `Binder to account +/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account /// for cases like /// /// ```rust @@ -651,7 +651,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { let unified_region = self.unification_table().probe_value(rid); unified_region.0.unwrap_or_else(|| { let root = self.unification_table().find(rid).vid; - tcx.reuse_or_mk_region(region, ty::ReVar(root)) + tcx.mk_re_var(root) }) } _ => region, @@ -675,7 +675,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { ) -> Region<'tcx> { let vars = TwoRegions { a, b }; if let Some(&c) = self.combine_map(t).get(&vars) { - return tcx.mk_region(ReVar(c)); + return tcx.mk_re_var(c); } let a_universe = self.universe(a); let b_universe = self.universe(b); @@ -683,7 +683,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { let c = self.new_region_var(c_universe, MiscVariable(origin.span())); self.combine_map(t).insert(vars, c); self.undo_log.push(AddCombination(t, vars)); - let new_r = tcx.mk_region(ReVar(c)); + let new_r = tcx.mk_re_var(c); for old_r in [a, b] { match t { Glb => self.make_subregion(origin.clone(), new_r, old_r), @@ -696,9 +696,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex { match *region { - ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => { - ty::UniverseIndex::ROOT - } + ty::ReStatic + | ty::ReErased + | ty::ReFree(..) + | ty::ReEarlyBound(..) + | ty::ReError(_) => ty::UniverseIndex::ROOT, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReVar(vid) => self.var_universe(vid), ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 65b90aa3d..5bb358329 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -2,8 +2,8 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{FixupError, FixupResult, InferCtxt, Span}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; 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}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; use std::ops::ControlFlow; @@ -16,26 +16,29 @@ 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<'tcx>, + // The shallow resolver is used to resolve inference variables at every + // level of the type. + shallow_resolver: crate::infer::ShallowResolver<'a, 'tcx>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { infcx } + OpportunisticVarResolver { shallow_resolver: crate::infer::ShallowResolver { infcx } } } } -impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.infcx.tcx +impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticVarResolver<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + TypeFolder::interner(&self.shallow_resolver) } + #[inline] fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { 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); + let t = self.shallow_resolver.fold_ty(t); t.super_fold_with(self) } } @@ -44,7 +47,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { 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); + let ct = self.shallow_resolver.fold_const(ct); ct.super_fold_with(self) } } @@ -67,8 +70,8 @@ impl<'a, 'tcx> OpportunisticRegionResolver<'a, 'tcx> { } } -impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticRegionResolver<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -89,7 +92,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { .borrow_mut() .unwrap_region_constraints() .opportunistic_resolve_var(rid); - TypeFolder::tcx(self).reuse_or_mk_region(r, ty::ReVar(resolved)) + TypeFolder::interner(self).mk_re_var(resolved) } _ => r, } @@ -121,7 +124,7 @@ impl<'a, 'tcx> UnresolvedTypeOrConstFinder<'a, 'tcx> { } } -impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeOrConstFinder<'a, 'tcx> { +impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for UnresolvedTypeOrConstFinder<'a, 'tcx> { type BreakTy = (ty::Term<'tcx>, Option<Span>); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { let t = self.infcx.shallow_resolve(t); @@ -194,7 +197,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeOrConstFinder<'a, 'tcx> { /// then an `Err` result is returned. pub fn fully_resolve<'tcx, T>(infcx: &InferCtxt<'tcx>, value: T) -> FixupResult<'tcx, T> where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { value.try_fold_with(&mut FullTypeResolver { infcx }) } @@ -205,10 +208,10 @@ struct FullTypeResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, } -impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { +impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> { type Error = FixupError<'tcx>; - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.infcx.tcx } diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index bd38b52ba..3e8c2052d 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -1,10 +1,9 @@ use super::combine::{CombineFields, RelationDir}; -use super::SubregionOrigin; +use super::{ObligationEmittingRelation, SubregionOrigin}; -use crate::infer::combine::ConstEquateRelation; -use crate::traits::Obligation; +use crate::traits::{Obligation, PredicateObligations}; use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty, TyCtxt}; use std::mem; @@ -127,7 +126,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { (&ty::Error(e), _) | (_, &ty::Error(e)) => { infcx.set_tainted_by_errors(e); - Ok(self.tcx().ty_error_with_guaranteed(e)) + Ok(self.tcx().ty_error(e)) } ( @@ -161,7 +160,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { 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( + let (a_types, b_types) = infcx.instantiate_binder_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) { @@ -191,12 +190,13 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { // from the "cause" field, we could perhaps give more tailored // error messages. let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) self.fields .infcx .inner .borrow_mut() .unwrap_region_constraints() - .make_subregion(origin, a, b); + .make_subregion(origin, b, a); Ok(a) } @@ -227,8 +227,12 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { } } -impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); +impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 4c119a443..bdc313c21 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -13,6 +13,7 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extend_one)] @@ -33,6 +34,11 @@ extern crate tracing; #[macro_use] extern crate rustc_middle; +use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; +use rustc_macros::fluent_messages; + mod errors; pub mod infer; pub mod traits; + +fluent_messages! { "../locales/en-US.ftl" } diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index d3519f4b3..f75344f20 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -1,6 +1,5 @@ use crate::infer::InferCtxt; use crate::traits::Obligation; -use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, ToPredicate, Ty}; @@ -37,13 +36,19 @@ pub trait TraitEngine<'tcx>: 'tcx { obligation: PredicateObligation<'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 collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>>; + fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>; - fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships>; + /// Among all pending obligations, collect those are stalled on a inference variable which has + /// changed since the last call to `select_where_possible`. Those obligations are marked as + /// successful and returned. + fn drain_unstalled_obligations( + &mut self, + infcx: &InferCtxt<'tcx>, + ) -> Vec<PredicateObligation<'tcx>>; } pub trait TraitEngineExt<'tcx> { @@ -52,6 +57,8 @@ pub trait TraitEngineExt<'tcx> { infcx: &InferCtxt<'tcx>, obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, ); + + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>; } impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { @@ -64,4 +71,13 @@ impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { self.register_predicate_obligation(infcx, obligation); } } + + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + + self.collect_remaining_errors() + } } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 026713b6a..3a8289966 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -8,6 +8,7 @@ mod project; mod structural_impls; pub mod util; +use hir::def_id::LocalDefId; use rustc_hir as hir; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Const, ToPredicate, Ty, TyCtxt}; @@ -146,7 +147,7 @@ impl<'tcx, O> Obligation<'tcx, O> { pub fn misc( tcx: TyCtxt<'tcx>, span: Span, - body_id: hir::HirId, + body_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, trait_ref: impl ToPredicate<'tcx, O>, ) -> Obligation<'tcx, O> { diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 1c6ab6a08..3a5273b03 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -1,8 +1,8 @@ use crate::traits; use crate::traits::project::Normalized; -use rustc_middle::ty; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable}; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{self, TyCtxt}; use std::fmt; use std::ops::ControlFlow; @@ -61,8 +61,13 @@ impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> { /////////////////////////////////////////////////////////////////////////// // TypeFoldable implementations. -impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx, O> { - fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { +impl<'tcx, O: TypeFoldable<TyCtxt<'tcx>>> TypeFoldable<TyCtxt<'tcx>> + for traits::Obligation<'tcx, O> +{ + fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { Ok(traits::Obligation { cause: self.cause, recursion_depth: self.recursion_depth, @@ -72,8 +77,10 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx } } -impl<'tcx, O: TypeVisitable<'tcx>> TypeVisitable<'tcx> for traits::Obligation<'tcx, O> { - fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { +impl<'tcx, O: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>> + for traits::Obligation<'tcx, O> +{ + fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { self.predicate.visit_with(visitor)?; self.param_env.visit_with(visitor) } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index cd5bde2a7..c07ff5165 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -116,11 +116,11 @@ pub fn elaborate_predicates_with_span<'tcx>( pub fn elaborate_obligations<'tcx>( tcx: TyCtxt<'tcx>, - mut obligations: Vec<PredicateObligation<'tcx>>, + obligations: Vec<PredicateObligation<'tcx>>, ) -> Elaborator<'tcx> { - let mut visited = PredicateSet::new(tcx); - obligations.retain(|obligation| visited.insert(obligation.predicate)); - Elaborator { stack: obligations, visited } + let mut elaborator = Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx) }; + elaborator.extend_deduped(obligations); + elaborator } fn predicate_obligation<'tcx>( @@ -132,6 +132,15 @@ fn predicate_obligation<'tcx>( } impl<'tcx> Elaborator<'tcx> { + fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>) { + // Only keep those bounds that we haven't already seen. + // This is necessary to prevent infinite recursion in some + // cases. One common case is when people define + // `trait Sized: Sized { }` rather than `trait Sized { }`. + // let visited = &mut self.visited; + self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate))); + } + pub fn filter_to_traits(self) -> FilterToTraits<Self> { FilterToTraits::new(self) } @@ -145,40 +154,34 @@ impl<'tcx> Elaborator<'tcx> { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let obligations = predicates.predicates.iter().map(|&(mut pred, span)| { - // when parent predicate is non-const, elaborate it to non-const predicates. - if data.constness == ty::BoundConstness::NotConst { - pred = pred.without_const(tcx); - } - - let cause = obligation.cause.clone().derived_cause( - bound_predicate.rebind(data), - |derived| { - traits::ImplDerivedObligation(Box::new( - traits::ImplDerivedObligationCause { - derived, - impl_def_id: data.def_id(), - span, - }, - )) - }, - ); - predicate_obligation( - pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), - obligation.param_env, - cause, - ) - }); + let obligations = + predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| { + // when parent predicate is non-const, elaborate it to non-const predicates. + if data.constness == ty::BoundConstness::NotConst { + pred = pred.without_const(tcx); + } + + let cause = obligation.cause.clone().derived_cause( + bound_predicate.rebind(data), + |derived| { + traits::ImplDerivedObligation(Box::new( + traits::ImplDerivedObligationCause { + derived, + impl_or_alias_def_id: data.def_id(), + impl_def_predicate_index: Some(index), + span, + }, + )) + }, + ); + predicate_obligation( + pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), + obligation.param_env, + cause, + ) + }); debug!(?data, ?obligations, "super_predicates"); - - // Only keep those bounds that we haven't already seen. - // This is necessary to prevent infinite recursion in some - // cases. One common case is when people define - // `trait Sized: Sized { }` rather than `trait Sized { }`. - let visited = &mut self.visited; - let obligations = obligations.filter(|o| visited.insert(o.predicate)); - - self.stack.extend(obligations); + self.extend_deduped(obligations); } ty::PredicateKind::WellFormed(..) => { // Currently, we do not elaborate WF predicates, @@ -235,10 +238,9 @@ impl<'tcx> Elaborator<'tcx> { return; } - let visited = &mut self.visited; let mut components = smallvec![]; push_outlives_components(tcx, ty_max, &mut components); - self.stack.extend( + self.extend_deduped( components .into_iter() .filter_map(|component| match component { @@ -278,7 +280,6 @@ impl<'tcx> Elaborator<'tcx> { .map(|predicate_kind| { bound_predicate.rebind(predicate_kind).to_predicate(tcx) }) - .filter(|&predicate| visited.insert(predicate)) .map(|predicate| { predicate_obligation( predicate, @@ -292,6 +293,12 @@ impl<'tcx> Elaborator<'tcx> { // Nothing to elaborate } ty::PredicateKind::Ambiguous => {} + ty::PredicateKind::AliasEq(..) => { + // No + } + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { + // Nothing to elaborate + } } } } |