Move all error reporting into rustc_trait_selection
This commit is contained in:
parent
f49738ba6c
commit
ce8a625092
76 changed files with 2541 additions and 2531 deletions
|
@ -1,3 +1,65 @@
|
|||
trait_selection_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] {""}
|
||||
}
|
||||
trait_selection_actual_impl_expl_but_actually_implements_trait = ...but it actually implements `{$trait_path}`{$has_lifetime ->
|
||||
[true] , for some specific lifetime `'{$lifetime}`
|
||||
*[false] {""}
|
||||
}
|
||||
trait_selection_actual_impl_expl_but_actually_ty_implements = ...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime ->
|
||||
[true] , for some specific lifetime `'{$lifetime}`
|
||||
*[false] {""}
|
||||
}
|
||||
|
||||
trait_selection_actual_impl_expl_expected_other_any = {$leading_ellipsis ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`...
|
||||
trait_selection_actual_impl_expl_expected_other_nothing = {$leading_ellipsis ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}`{$ty_or_sig}` must implement `{$trait_path}`
|
||||
|
||||
trait_selection_actual_impl_expl_expected_other_some = {$leading_ellipsis ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...
|
||||
trait_selection_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}`...
|
||||
trait_selection_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}`...
|
||||
trait_selection_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`
|
||||
trait_selection_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}`...
|
||||
trait_selection_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}`...
|
||||
trait_selection_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}`...
|
||||
trait_selection_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`
|
||||
trait_selection_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}`...
|
||||
trait_selection_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}`...
|
||||
trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len ->
|
||||
[one] argument
|
||||
*[other] arguments
|
||||
|
@ -8,8 +70,48 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur
|
|||
*[other] arguments
|
||||
}
|
||||
|
||||
trait_selection_ascribe_user_type_prove_predicate = ...so that the where clause holds
|
||||
|
||||
trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment
|
||||
|
||||
trait_selection_await_both_futures = consider `await`ing on both `Future`s
|
||||
trait_selection_await_future = consider `await`ing on the `Future`
|
||||
trait_selection_await_note = calling an async function returns a future
|
||||
|
||||
trait_selection_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`
|
||||
}
|
||||
|
||||
trait_selection_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
|
||||
|
||||
trait_selection_closure_fn_mut_label = closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here
|
||||
|
||||
trait_selection_closure_fn_once_label = closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment
|
||||
|
@ -19,18 +121,67 @@ trait_selection_closure_kind_mismatch = expected a closure that implements the `
|
|||
|
||||
trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here
|
||||
|
||||
trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
|
||||
trait_selection_consider_specifying_length = consider specifying the actual array length
|
||||
trait_selection_data_flows = ...but data{$label_var1_exists ->
|
||||
[true] {" "}from `{$label_var1}`
|
||||
*[false] {""}
|
||||
} flows{$label_var2_exists ->
|
||||
[true] {" "}into `{$label_var2}`
|
||||
*[false] {""}
|
||||
} here
|
||||
|
||||
trait_selection_data_lifetime_flow = ...but data with one lifetime flows into the other here
|
||||
trait_selection_data_returned = ...but data{$label_var1_exists ->
|
||||
[true] {" "}from `{$label_var1}`
|
||||
*[false] {""}
|
||||
} is returned here
|
||||
|
||||
trait_selection_declared_different = this parameter and the return type are declared with different lifetimes...
|
||||
trait_selection_declared_multiple = this type is declared with multiple lifetimes...
|
||||
trait_selection_disallowed_positional_argument = positional format arguments are not allowed here
|
||||
.help = only named format arguments with the name of one of the generic types are allowed in this context
|
||||
|
||||
trait_selection_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
|
||||
trait_selection_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement
|
||||
trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement
|
||||
trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement
|
||||
trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement
|
||||
|
||||
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
|
||||
|
||||
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
|
||||
.label = empty on-clause here
|
||||
|
||||
trait_selection_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}`
|
||||
|
||||
trait_selection_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type
|
||||
|
||||
trait_selection_explicit_lifetime_required_with_ident = explicit lifetime required in the type of `{$simple_ident}`
|
||||
.label = lifetime `{$named}` required
|
||||
|
||||
trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime required in parameter type
|
||||
.label = lifetime `{$named}` required
|
||||
|
||||
trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
|
||||
|
||||
trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same
|
||||
trait_selection_fps_cast = consider casting to a fn pointer
|
||||
trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`
|
||||
|
||||
trait_selection_fps_items_are_distinct = fn items are distinct from fn pointers
|
||||
trait_selection_fps_remove_ref = consider removing the reference
|
||||
trait_selection_fps_use_ref = consider using a reference
|
||||
trait_selection_fulfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime
|
||||
|
||||
trait_selection_full_type_written = the full type name has been written to '{$path}'
|
||||
|
||||
trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to previous definition of `{$option_name}`
|
||||
.other_label = `{$option_name}` is first declared here
|
||||
.label = `{$option_name}` is already declared here
|
||||
|
||||
trait_selection_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement
|
||||
trait_selection_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement
|
||||
trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}`
|
||||
|
||||
trait_selection_invalid_format_specifier = invalid format specifier
|
||||
|
@ -39,13 +190,52 @@ trait_selection_invalid_format_specifier = invalid format specifier
|
|||
trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]`
|
||||
.label = invalid on-clause here
|
||||
|
||||
trait_selection_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] {""}
|
||||
}
|
||||
}
|
||||
|
||||
trait_selection_lf_bound_not_satisfied = lifetime bound not satisfied
|
||||
trait_selection_lifetime_mismatch = lifetime mismatch
|
||||
|
||||
trait_selection_lifetime_param_suggestion = consider {$is_reuse ->
|
||||
[true] reusing
|
||||
*[false] introducing
|
||||
} a named lifetime parameter{$is_impl ->
|
||||
[true] {" "}and update trait if needed
|
||||
*[false] {""}
|
||||
}
|
||||
trait_selection_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime
|
||||
|
||||
trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute
|
||||
.help = only `message`, `note` and `label` are allowed as options
|
||||
.label = invalid option found here
|
||||
|
||||
trait_selection_meant_byte_literal = if you meant to write a byte literal, prefix with `b`
|
||||
trait_selection_meant_char_literal = if you meant to write a `char` literal, use single quotes
|
||||
trait_selection_meant_str_literal = if you meant to write a string literal, use double quotes
|
||||
trait_selection_mismatched_static_lifetime = incompatible lifetime on type
|
||||
trait_selection_missing_options_for_on_unimplemented_attr = missing options for `on_unimplemented` attribute
|
||||
.help = at least one of the `message`, `note` and `label` options are expected
|
||||
|
||||
trait_selection_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
|
||||
|
||||
trait_selection_msl_introduces_static = introduces a `'static` lifetime requirement
|
||||
trait_selection_msl_unmet_req = because this has an unmet lifetime requirement
|
||||
|
||||
trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc ->
|
||||
[none] {""}
|
||||
*[default] {" "}for type `{$self_desc}`
|
||||
|
@ -59,13 +249,214 @@ trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a
|
|||
.label = expected value here
|
||||
.note = eg `#[rustc_on_unimplemented(message="foo")]`
|
||||
|
||||
trait_selection_nothing = {""}
|
||||
|
||||
trait_selection_oc_cant_coerce = cannot coerce intrinsics to function pointers
|
||||
trait_selection_oc_closure_selfref = closure/coroutine type that references itself
|
||||
trait_selection_oc_const_compat = const not compatible with trait
|
||||
trait_selection_oc_fn_lang_correct_type = {$lang_item_name ->
|
||||
[panic_impl] `#[panic_handler]`
|
||||
*[lang_item_name] lang item `{$lang_item_name}`
|
||||
} function has wrong type
|
||||
trait_selection_oc_fn_main_correct_type = `main` function has wrong type
|
||||
trait_selection_oc_fn_start_correct_type = `#[start]` function has wrong type
|
||||
trait_selection_oc_generic = mismatched types
|
||||
|
||||
trait_selection_oc_if_else_different = `if` and `else` have incompatible types
|
||||
trait_selection_oc_intrinsic_correct_type = intrinsic has wrong type
|
||||
trait_selection_oc_match_compat = `match` arms have incompatible types
|
||||
trait_selection_oc_method_compat = method not compatible with trait
|
||||
trait_selection_oc_method_correct_type = mismatched `self` parameter type
|
||||
trait_selection_oc_no_diverge = `else` clause of `let...else` does not diverge
|
||||
trait_selection_oc_no_else = `if` may be missing an `else` clause
|
||||
trait_selection_oc_try_compat = `?` operator has incompatible types
|
||||
trait_selection_oc_type_compat = type not compatible with trait
|
||||
trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds
|
||||
.label = opaque type defined here
|
||||
|
||||
trait_selection_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type
|
||||
trait_selection_outlives_content = lifetime of reference outlives lifetime of borrowed content...
|
||||
|
||||
trait_selection_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it
|
||||
trait_selection_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}`
|
||||
|
||||
trait_selection_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
|
||||
|
||||
trait_selection_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
|
||||
trait_selection_prlf_defined_without_sub = the lifetime defined here...
|
||||
trait_selection_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)
|
||||
|
||||
trait_selection_prlf_must_outlive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here
|
||||
trait_selection_prlf_must_outlive_without_sup = ...must outlive the lifetime defined here
|
||||
trait_selection_reborrow = ...so that reference does not outlive borrowed content
|
||||
trait_selection_ref_longer_than_data = in type `{$ty}`, reference has a longer lifetime than the data it references
|
||||
|
||||
trait_selection_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at
|
||||
trait_selection_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
|
||||
[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
|
||||
}
|
||||
|
||||
trait_selection_relate_object_bound = ...so that it can be closed over into an object
|
||||
trait_selection_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}
|
||||
trait_selection_relate_param_bound_2 = ...that is required by this bound
|
||||
trait_selection_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied
|
||||
trait_selection_ril_because_of = because of this returned expression
|
||||
trait_selection_ril_introduced_by = requirement introduced by this return type
|
||||
trait_selection_ril_introduced_here = `'static` requirement introduced here
|
||||
trait_selection_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type
|
||||
|
||||
trait_selection_source_kind_closure_return =
|
||||
try giving this closure an explicit return type
|
||||
|
||||
# coroutine_kind may need to be translated
|
||||
trait_selection_source_kind_fully_qualified =
|
||||
try using a fully qualified path to specify the expected types
|
||||
|
||||
trait_selection_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}
|
||||
}
|
||||
|
||||
trait_selection_source_kind_subdiag_generic_suggestion =
|
||||
consider specifying the generic {$arg_count ->
|
||||
[one] argument
|
||||
*[other] arguments
|
||||
}
|
||||
|
||||
trait_selection_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] value of const parameter
|
||||
[const] value of the constant
|
||||
} `{$arg_name}` is specified
|
||||
[underscore] , where the placeholders `_` are specified
|
||||
*[empty] {""}
|
||||
}
|
||||
|
||||
trait_selection_srs_add = consider returning the local binding `{$ident}`
|
||||
trait_selection_srs_add_one = consider returning one of these bindings
|
||||
|
||||
trait_selection_srs_remove = consider removing this semicolon
|
||||
trait_selection_srs_remove_and_box = consider removing this semicolon and boxing the expressions
|
||||
trait_selection_stp_wrap_many = try wrapping the pattern in a variant of `{$path}`
|
||||
|
||||
trait_selection_stp_wrap_one = try wrapping the pattern in `{$variant}`
|
||||
trait_selection_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
|
||||
[fn_lang_correct_type] lang item function has the correct type
|
||||
[intrinsic_correct_type] intrinsic has the correct type
|
||||
[method_correct_type] method receiver has the correct type
|
||||
*[other] types are compatible
|
||||
}
|
||||
trait_selection_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
|
||||
[fn_lang_correct_type] lang item function has the correct type
|
||||
[intrinsic_correct_type] intrinsic has the correct type
|
||||
[method_correct_type] method receiver has the correct type
|
||||
*[other] types are compatible
|
||||
}
|
||||
|
||||
trait_selection_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}`
|
||||
|
||||
trait_selection_suggest_add_let_for_letchains = consider adding `let`
|
||||
|
||||
trait_selection_tid_consider_borrowing = consider borrowing this type parameter in the trait
|
||||
trait_selection_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
|
||||
|
||||
trait_selection_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output
|
||||
trait_selection_trait_has_no_impls = this trait has no implementations, consider adding one
|
||||
|
||||
trait_selection_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}`
|
||||
|
||||
trait_selection_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
|
||||
|
||||
trait_selection_try_cannot_convert = `?` operator cannot convert from `{$found}` to `{$expected}`
|
||||
|
||||
trait_selection_tuple_trailing_comma = use a trailing comma to create a tuple with one element
|
||||
|
||||
trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead
|
||||
trait_selection_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
|
||||
|
||||
trait_selection_types_declared_different = these two types are declared with different lifetimes...
|
||||
|
||||
trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
|
||||
|
||||
trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}`
|
||||
.help = expect either a generic argument name or {"`{Self}`"} as format argument
|
||||
|
||||
trait_selection_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable
|
||||
|
||||
trait_selection_where_copy_predicates = copy the `where` clause predicates from the trait
|
||||
|
||||
trait_selection_where_remove = remove the `where` clause
|
||||
trait_selection_wrapped_parser_error = {$description}
|
||||
.label = {$label}
|
||||
|
|
2199
compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
Normal file
2199
compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,163 @@
|
|||
//! Error Reporting for Anonymous Region Lifetime Errors
|
||||
//! where both the regions are anonymous.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
|
||||
use crate::error_reporting::infer::nice_region_error::util::AnonymousParamInfo;
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::AddLifetimeParamsSuggestion;
|
||||
use crate::errors::LifetimeMismatch;
|
||||
use crate::errors::LifetimeMismatchLabels;
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::SubregionOrigin;
|
||||
|
||||
use rustc_errors::Subdiagnostic;
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::Ty;
|
||||
use rustc_middle::ty::{Region, TyCtxt};
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
|
||||
///
|
||||
/// Consider a case where we have
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
|
||||
/// x.push(y);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The example gives
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
|
||||
/// --- --- these references are declared with different lifetimes...
|
||||
/// x.push(y);
|
||||
/// ^ ...but data from `y` flows into `x` here
|
||||
/// ```
|
||||
///
|
||||
/// It has been extended for the case of structs too.
|
||||
///
|
||||
/// Consider the example
|
||||
///
|
||||
/// ```no_run
|
||||
/// struct Ref<'a> { x: &'a u32 }
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo(mut x: Vec<Ref>, y: Ref) {
|
||||
/// --- --- these structs are declared with different lifetimes...
|
||||
/// x.push(y);
|
||||
/// ^ ...but data from `y` flows into `x` here
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// It will later be extended to trait objects.
|
||||
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> {
|
||||
let (span, sub, sup) = self.regions()?;
|
||||
|
||||
if let Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::ReferenceOutlivesReferent(..),
|
||||
..,
|
||||
)) = self.error
|
||||
{
|
||||
// This error doesn't make much sense in this case.
|
||||
return None;
|
||||
}
|
||||
|
||||
// Determine whether the sub and sup consist of both anonymous (elided) regions.
|
||||
let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?;
|
||||
|
||||
let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?;
|
||||
let scope_def_id_sup = anon_reg_sup.def_id;
|
||||
let bregion_sup = anon_reg_sup.bound_region;
|
||||
let scope_def_id_sub = anon_reg_sub.def_id;
|
||||
let bregion_sub = anon_reg_sub.bound_region;
|
||||
|
||||
let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?;
|
||||
|
||||
let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?;
|
||||
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
|
||||
ty_sub, sup, bregion_sup
|
||||
);
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
|
||||
ty_sup, sub, bregion_sub
|
||||
);
|
||||
|
||||
let (ty_sup, ty_fndecl_sup) = ty_sup;
|
||||
let (ty_sub, ty_fndecl_sub) = ty_sub;
|
||||
|
||||
let AnonymousParamInfo { param: anon_param_sup, .. } =
|
||||
self.find_param_with_region(sup, sup)?;
|
||||
let AnonymousParamInfo { param: anon_param_sub, .. } =
|
||||
self.find_param_with_region(sub, sub)?;
|
||||
|
||||
let sup_is_ret_type =
|
||||
self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
|
||||
let sub_is_ret_type =
|
||||
self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
|
||||
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
|
||||
sub_is_ret_type, sup_is_ret_type
|
||||
);
|
||||
|
||||
let labels = match (sup_is_ret_type, sub_is_ret_type) {
|
||||
(ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => {
|
||||
let param_span =
|
||||
if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span };
|
||||
LifetimeMismatchLabels::InRet {
|
||||
param_span,
|
||||
ret_span,
|
||||
span,
|
||||
label_var1: anon_param_sup.pat.simple_ident(),
|
||||
}
|
||||
}
|
||||
|
||||
(None, None) => LifetimeMismatchLabels::Normal {
|
||||
hir_equal: ty_sup.hir_id == ty_sub.hir_id,
|
||||
ty_sup: ty_sup.span,
|
||||
ty_sub: ty_sub.span,
|
||||
span,
|
||||
sup: anon_param_sup.pat.simple_ident(),
|
||||
sub: anon_param_sub.pat.simple_ident(),
|
||||
},
|
||||
};
|
||||
|
||||
let suggestion = AddLifetimeParamsSuggestion {
|
||||
tcx: self.tcx(),
|
||||
sub,
|
||||
ty_sup,
|
||||
ty_sub,
|
||||
add_note: true,
|
||||
generic_param_scope: self.generic_param_scope,
|
||||
};
|
||||
let err = LifetimeMismatch { span, labels, suggestion };
|
||||
let reported = self.tcx().dcx().emit_err(err);
|
||||
Some(reported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently only used in rustc_borrowck, probably should be
|
||||
/// removed in favour of public_errors::AddLifetimeParamsSuggestion
|
||||
pub fn suggest_adding_lifetime_params<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
generic_param_scope: LocalDefId,
|
||||
sub: Region<'tcx>,
|
||||
ty_sup: &'tcx Ty<'_>,
|
||||
ty_sub: &'tcx Ty<'_>,
|
||||
) {
|
||||
let suggestion = AddLifetimeParamsSuggestion {
|
||||
tcx,
|
||||
sub,
|
||||
ty_sup,
|
||||
ty_sub,
|
||||
add_note: false,
|
||||
generic_param_scope,
|
||||
};
|
||||
suggestion.add_to_diag(err);
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
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
|
||||
/// corresponding to the anonymous regions. The `nested_visitor.found_type`
|
||||
/// contains the anonymous type.
|
||||
///
|
||||
/// # Arguments
|
||||
/// region - the anonymous region corresponding to the anon_anon conflict
|
||||
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
|
||||
///
|
||||
/// # Example
|
||||
/// ```compile_fail
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8)
|
||||
/// { x.push(y); }
|
||||
/// ```
|
||||
/// The function returns the nested type corresponding to the anonymous region
|
||||
/// for e.g., `&u8` and `Vec<&u8>`.
|
||||
pub fn find_anon_type<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
region: Region<'tcx>,
|
||||
br: &ty::BoundRegionKind,
|
||||
) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
|
||||
let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?;
|
||||
let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?;
|
||||
|
||||
fn_sig
|
||||
.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.find_map(|arg| find_component_for_bound_region(tcx, arg, br))
|
||||
.map(|ty| (ty, fn_sig))
|
||||
}
|
||||
|
||||
// This method creates a FindNestedTypeVisitor which returns the type corresponding
|
||||
// to the anonymous region.
|
||||
fn find_component_for_bound_region<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
arg: &'tcx hir::Ty<'tcx>,
|
||||
br: &ty::BoundRegionKind,
|
||||
) -> Option<&'tcx hir::Ty<'tcx>> {
|
||||
FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
|
||||
.visit_ty(arg)
|
||||
.break_value()
|
||||
}
|
||||
|
||||
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
|
||||
// anonymous region. The example above would lead to a conflict between
|
||||
// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
|
||||
// would be invoked twice, once for each lifetime, and would
|
||||
// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
|
||||
// where that lifetime appears. This allows us to highlight the
|
||||
// specific part of the type in the error message.
|
||||
struct FindNestedTypeVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
// The bound_region corresponding to the Refree(freeregion)
|
||||
// associated with the anonymous region we are looking for.
|
||||
bound_region: ty::BoundRegionKind,
|
||||
current_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
|
||||
type Result = ControlFlow<&'tcx hir::Ty<'tcx>>;
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
|
||||
match arg.kind {
|
||||
hir::TyKind::BareFn(_) => {
|
||||
self.current_index.shift_in(1);
|
||||
intravisit::walk_ty(self, arg);
|
||||
self.current_index.shift_out(1);
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
hir::TyKind::TraitObject(bounds, ..) => {
|
||||
for bound in bounds {
|
||||
self.current_index.shift_in(1);
|
||||
self.visit_poly_trait_ref(bound);
|
||||
self.current_index.shift_out(1);
|
||||
}
|
||||
}
|
||||
|
||||
hir::TyKind::Ref(lifetime, _) => {
|
||||
// the lifetime of the Ref
|
||||
let hir_id = lifetime.hir_id;
|
||||
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(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
||||
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
||||
if id == def_id {
|
||||
return ControlFlow::Break(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// 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(rbv::ResolvedArg::LateBound(debruijn_index, _, id)),
|
||||
ty::BrNamed(def_id, _),
|
||||
) => {
|
||||
debug!(
|
||||
"FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
|
||||
debruijn_index
|
||||
);
|
||||
debug!("LateBound id={:?} def_id={:?}", id, def_id);
|
||||
if debruijn_index == self.current_index && id == def_id {
|
||||
return ControlFlow::Break(arg);
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Some(
|
||||
rbv::ResolvedArg::StaticLifetime
|
||||
| rbv::ResolvedArg::Free(_, _)
|
||||
| rbv::ResolvedArg::EarlyBound(_)
|
||||
| rbv::ResolvedArg::LateBound(_, _, _)
|
||||
| rbv::ResolvedArg::Error(_),
|
||||
)
|
||||
| None,
|
||||
_,
|
||||
) => {
|
||||
debug!("no arg found");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
|
||||
hir::TyKind::Path(_) => {
|
||||
// Prefer using the lifetime in type arguments rather than lifetime arguments.
|
||||
intravisit::walk_ty(self, arg)?;
|
||||
|
||||
// Call `walk_ty` as `visit_ty` is empty.
|
||||
return if intravisit::walk_ty(
|
||||
&mut TyPathVisitor {
|
||||
tcx: self.tcx,
|
||||
bound_region: self.bound_region,
|
||||
current_index: self.current_index,
|
||||
},
|
||||
arg,
|
||||
)
|
||||
.is_break()
|
||||
{
|
||||
ControlFlow::Break(arg)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
|
||||
// go on to visit `&Foo`
|
||||
intravisit::walk_ty(self, arg)
|
||||
}
|
||||
}
|
||||
|
||||
// The visitor captures the corresponding `hir::Ty` of the anonymous region
|
||||
// in the case of structs ie. `hir::TyKind::Path`.
|
||||
// This visitor would be invoked for each lifetime corresponding to a struct,
|
||||
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
|
||||
// where that lifetime appears. This allows us to highlight the
|
||||
// specific part of the type in the error message.
|
||||
struct TyPathVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
bound_region: ty::BoundRegionKind,
|
||||
current_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
|
||||
type Result = ControlFlow<()>;
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Map<'tcx> {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result {
|
||||
match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
|
||||
// the lifetime of the TyPath!
|
||||
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
||||
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
||||
if id == def_id {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
(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);
|
||||
if debruijn_index == self.current_index && id == def_id {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Some(
|
||||
rbv::ResolvedArg::StaticLifetime
|
||||
| rbv::ResolvedArg::EarlyBound(_)
|
||||
| rbv::ResolvedArg::LateBound(_, _, _)
|
||||
| rbv::ResolvedArg::Free(_, _)
|
||||
| rbv::ResolvedArg::Error(_),
|
||||
)
|
||||
| None,
|
||||
_,
|
||||
) => {
|
||||
debug!("no arg found");
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
|
||||
// ignore nested types
|
||||
//
|
||||
// If you have a type like `Foo<'a, &Ty>` we
|
||||
// are only interested in the immediate lifetimes ('a).
|
||||
//
|
||||
// Making `visit_ty` empty will ignore the `&Ty` embedded
|
||||
// inside, it will get reached by the outer visitor.
|
||||
debug!("`Ty` corresponding to a struct is {:?}", arg);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
|
||||
//! to hold.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq};
|
||||
use crate::errors::{
|
||||
DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime,
|
||||
};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::ObligationCauseCode;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{ErrorGuaranteed, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::TypeVisitor;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> {
|
||||
let error = self.error.as_ref()?;
|
||||
debug!("try_report_mismatched_static_lifetime {:?}", error);
|
||||
|
||||
let RegionResolutionError::ConcreteFailure(origin, sub, sup) = error.clone() else {
|
||||
return None;
|
||||
};
|
||||
if !sub.is_static() {
|
||||
return None;
|
||||
}
|
||||
let SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) = origin else {
|
||||
return None;
|
||||
};
|
||||
// If we added a "points at argument expression" obligation, we remove it here, we care
|
||||
// about the original obligation only.
|
||||
let code = match cause.code() {
|
||||
ObligationCauseCode::FunctionArg { parent_code, .. } => &*parent_code,
|
||||
code => code,
|
||||
};
|
||||
let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else {
|
||||
return None;
|
||||
};
|
||||
let (ObligationCauseCode::WhereClause(_, binding_span)
|
||||
| ObligationCauseCode::WhereClauseInExpr(_, binding_span, ..)) = *parent.code()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if binding_span.is_dummy() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: we should point at the lifetime
|
||||
let multi_span: MultiSpan = vec![binding_span].into();
|
||||
let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq {
|
||||
unmet_requirements: multi_span,
|
||||
binding_span,
|
||||
};
|
||||
|
||||
let expl = note_and_explain::RegionExplanation::new(
|
||||
self.tcx(),
|
||||
self.generic_param_scope,
|
||||
sup,
|
||||
Some(binding_span),
|
||||
note_and_explain::PrefixKind::Empty,
|
||||
note_and_explain::SuffixKind::Continues,
|
||||
);
|
||||
let mut impl_span = None;
|
||||
let mut implicit_static_lifetimes = Vec::new();
|
||||
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
|
||||
// If an impl is local, then maybe this isn't what they want. Try to
|
||||
// be as helpful as possible with implicit lifetimes.
|
||||
|
||||
// First, let's get the hir self type of the impl
|
||||
let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, .. }),
|
||||
..
|
||||
}) = impl_node
|
||||
else {
|
||||
bug!("Node not an impl.");
|
||||
};
|
||||
|
||||
// Next, let's figure out the set of trait objects with implicit static bounds
|
||||
let ty = self.tcx().type_of(*impl_def_id).instantiate_identity();
|
||||
let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(ty);
|
||||
let mut traits = vec![];
|
||||
for matching_def_id in v.0 {
|
||||
let mut hir_v =
|
||||
super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id);
|
||||
hir_v.visit_ty(impl_self_ty);
|
||||
}
|
||||
|
||||
if traits.is_empty() {
|
||||
// If there are no trait object traits to point at, either because
|
||||
// there aren't trait objects or because none are implicit, then just
|
||||
// write a single note on the impl itself.
|
||||
|
||||
impl_span = Some(self.tcx().def_span(*impl_def_id));
|
||||
} else {
|
||||
// Otherwise, point at all implicit static lifetimes
|
||||
|
||||
for span in &traits {
|
||||
implicit_static_lifetimes
|
||||
.push(ImplicitStaticLifetimeSubdiag::Note { span: *span });
|
||||
// It would be nice to put this immediately under the above note, but they get
|
||||
// pushed to the end.
|
||||
implicit_static_lifetimes
|
||||
.push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise just point out the impl.
|
||||
|
||||
impl_span = Some(self.tcx().def_span(*impl_def_id));
|
||||
}
|
||||
let err = MismatchedStaticLifetime {
|
||||
cause_span: cause.span,
|
||||
unmet_lifetime_reqs: multispan_subdiag,
|
||||
expl,
|
||||
does_not_outlive_static_from_impl: impl_span
|
||||
.map(|span| DoesNotOutliveStaticFromImpl::Spanned { span })
|
||||
.unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned),
|
||||
implicit_static_lifetimes,
|
||||
};
|
||||
let reported = self.tcx().dcx().emit_err(err);
|
||||
Some(reported)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::RegionResolutionError::*;
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
|
||||
mod different_lifetimes;
|
||||
pub mod find_anon_type;
|
||||
mod mismatched_static_lifetime;
|
||||
mod named_anon_conflict;
|
||||
pub(crate) mod placeholder_error;
|
||||
mod placeholder_relation;
|
||||
mod static_impl_trait;
|
||||
mod trait_impl_difference;
|
||||
mod util;
|
||||
|
||||
pub use different_lifetimes::suggest_adding_lifetime_params;
|
||||
pub use find_anon_type::find_anon_type;
|
||||
pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor};
|
||||
pub use util::find_param_with_region;
|
||||
|
||||
impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> {
|
||||
pub fn try_report_nice_region_error(
|
||||
&'cx self,
|
||||
generic_param_scope: LocalDefId,
|
||||
error: &RegionResolutionError<'tcx>,
|
||||
) -> Option<ErrorGuaranteed> {
|
||||
NiceRegionError::new(self, generic_param_scope, error.clone()).try_report()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NiceRegionError<'cx, 'tcx> {
|
||||
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
|
||||
/// The innermost definition that introduces generic parameters that may be involved in
|
||||
/// the region errors we are dealing with.
|
||||
generic_param_scope: LocalDefId,
|
||||
error: Option<RegionResolutionError<'tcx>>,
|
||||
regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
|
||||
pub fn new(
|
||||
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
error: RegionResolutionError<'tcx>,
|
||||
) -> Self {
|
||||
Self { cx, error: Some(error), regions: None, generic_param_scope }
|
||||
}
|
||||
|
||||
pub fn new_from_span(
|
||||
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
span: Span,
|
||||
sub: ty::Region<'tcx>,
|
||||
sup: ty::Region<'tcx>,
|
||||
) -> Self {
|
||||
Self { cx, error: None, regions: Some((span, sub, sup)), generic_param_scope }
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.cx.tcx
|
||||
}
|
||||
|
||||
pub fn try_report_from_nll(&self) -> Option<Diag<'tcx>> {
|
||||
// Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
|
||||
// the nice region errors are required when running under the MIR borrow checker.
|
||||
self.try_report_named_anon_conflict()
|
||||
.or_else(|| self.try_report_placeholder_conflict())
|
||||
.or_else(|| self.try_report_placeholder_relation())
|
||||
}
|
||||
|
||||
pub fn try_report(&self) -> Option<ErrorGuaranteed> {
|
||||
self.try_report_from_nll()
|
||||
.map(|diag| diag.emit())
|
||||
.or_else(|| self.try_report_impl_not_conforming_to_trait())
|
||||
.or_else(|| self.try_report_anon_anon_conflict())
|
||||
.or_else(|| self.try_report_static_impl_trait())
|
||||
.or_else(|| self.try_report_mismatched_static_lifetime())
|
||||
}
|
||||
|
||||
pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
|
||||
match (&self.error, self.regions) {
|
||||
(Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)),
|
||||
(Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => {
|
||||
Some((origin.span(), *sub, *sup))
|
||||
}
|
||||
(None, Some((span, sub, sup))) => Some((span, sub, sup)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//! Error Reporting for Anonymous Region Lifetime Errors
|
||||
//! where one region is named and the other is anonymous.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::ExplicitLifetimeRequired;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
|
||||
/// an anonymous region, emit an descriptive diagnostic error.
|
||||
pub(super) fn try_report_named_anon_conflict(&self) -> Option<Diag<'tcx>> {
|
||||
let (span, sub, sup) = self.regions()?;
|
||||
|
||||
debug!(
|
||||
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
|
||||
sub, sup, self.error,
|
||||
);
|
||||
|
||||
// Determine whether the sub and sup consist of one named region ('a)
|
||||
// and one anonymous (elided) region. If so, find the parameter arg
|
||||
// where the anonymous region appears (there must always be one; we
|
||||
// only introduced anonymous regions in parameters) as well as a
|
||||
// version new_ty of its type where the anonymous region is replaced
|
||||
// with the named one.
|
||||
let (named, anon, anon_param_info, region_info) = if sub.has_name()
|
||||
&& let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup)
|
||||
&& let Some(anon_param_info) = self.find_param_with_region(sup, sub)
|
||||
{
|
||||
(sub, sup, anon_param_info, region_info)
|
||||
} else if sup.has_name()
|
||||
&& let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub)
|
||||
&& let Some(anon_param_info) = self.find_param_with_region(sub, sup)
|
||||
{
|
||||
(sup, sub, anon_param_info, region_info)
|
||||
} else {
|
||||
return None; // inapplicable
|
||||
};
|
||||
|
||||
// Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect,
|
||||
// and can steer users down the wrong path.
|
||||
if named.is_static() {
|
||||
return None;
|
||||
}
|
||||
|
||||
debug!("try_report_named_anon_conflict: named = {:?}", named);
|
||||
debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info);
|
||||
debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
|
||||
|
||||
let param = anon_param_info.param;
|
||||
let new_ty = anon_param_info.param_ty;
|
||||
let new_ty_span = anon_param_info.param_ty_span;
|
||||
let br = anon_param_info.bound_region;
|
||||
let is_first = anon_param_info.is_first;
|
||||
let scope_def_id = region_info.def_id;
|
||||
let is_impl_item = region_info.is_impl_item;
|
||||
|
||||
match br {
|
||||
ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon => {}
|
||||
_ => {
|
||||
/* not an anonymous region */
|
||||
debug!("try_report_named_anon_conflict: not an anonymous region");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if is_impl_item {
|
||||
debug!("try_report_named_anon_conflict: impl item, bail out");
|
||||
return None;
|
||||
}
|
||||
|
||||
if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some()
|
||||
&& self.is_self_anon(is_first, scope_def_id)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let named = named.to_string();
|
||||
let err = match param.pat.simple_ident() {
|
||||
Some(simple_ident) => ExplicitLifetimeRequired::WithIdent {
|
||||
span,
|
||||
simple_ident,
|
||||
named,
|
||||
new_ty_span,
|
||||
new_ty,
|
||||
},
|
||||
None => ExplicitLifetimeRequired::WithParamType { span, named, new_ty_span, new_ty },
|
||||
};
|
||||
Some(self.tcx().sess.dcx().create_err(err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,496 @@
|
|||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{
|
||||
ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes,
|
||||
TraitPlaceholderMismatch, TyOrSig,
|
||||
};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::ValuePairs;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::{Diag, IntoDiagArg};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{self, RePlaceholder, Region, TyCtxt};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
// HACK(eddyb) maybe move this in a more central location.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Highlighted<'tcx, T> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
highlight: RegionHighlightMode<'tcx>,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>
|
||||
where
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
|
||||
{
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
rustc_errors::DiagArgValue::Str(self.to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Highlighted<'tcx, T> {
|
||||
fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
|
||||
Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
|
||||
where
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
|
||||
printer.region_highlight_mode = self.highlight;
|
||||
|
||||
self.value.print(&mut printer)?;
|
||||
f.write_str(&printer.into_buffer())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> NiceRegionError<'_, 'tcx> {
|
||||
/// When given a `ConcreteFailure` for a function with arguments containing a named region and
|
||||
/// an anonymous region, emit a descriptive diagnostic error.
|
||||
pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> {
|
||||
match &self.error {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// NB. The ordering of cases in this match is very
|
||||
// sensitive, because we are often matching against
|
||||
// specific cases and then using an `_` to match all
|
||||
// others.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Check for errors from comparing trait failures -- first
|
||||
// with two placeholders, then with one.
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
Some(*sub_placeholder),
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
Some(*sub_placeholder),
|
||||
None,
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
_,
|
||||
_,
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::UpperBoundUniverseConflict(
|
||||
vid,
|
||||
_,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
sup_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
None,
|
||||
cause,
|
||||
Some(*sub_region),
|
||||
Some(*sup_region),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
sup_region,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
(!sup_region.has_name()).then_some(*sup_region),
|
||||
cause,
|
||||
Some(*sub_region),
|
||||
None,
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_region,
|
||||
sup_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
(!sub_region.has_name()).then_some(*sub_region),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_region),
|
||||
values,
|
||||
),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_report_trait_placeholder_mismatch(
|
||||
&self,
|
||||
vid: Option<Region<'tcx>>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
sub_placeholder: Option<Region<'tcx>>,
|
||||
sup_placeholder: Option<Region<'tcx>>,
|
||||
value_pairs: &ValuePairs<'tcx>,
|
||||
) -> Option<Diag<'tcx>> {
|
||||
let (expected_args, found_args, trait_def_id) = match value_pairs {
|
||||
ValuePairs::TraitRefs(ExpectedFound { expected, found })
|
||||
if expected.def_id == found.def_id =>
|
||||
{
|
||||
// It's possible that the placeholders come from a binder
|
||||
// outside of this value pair. Use `no_bound_vars` as a
|
||||
// simple heuristic for that.
|
||||
(expected.args, found.args, expected.def_id)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(self.report_trait_placeholder_mismatch(
|
||||
vid,
|
||||
cause,
|
||||
sub_placeholder,
|
||||
sup_placeholder,
|
||||
trait_def_id,
|
||||
expected_args,
|
||||
found_args,
|
||||
))
|
||||
}
|
||||
|
||||
// error[E0308]: implementation of `Foo` does not apply to enough lifetimes
|
||||
// --> /home/nmatsakis/tmp/foo.rs:12:5
|
||||
// |
|
||||
// 12 | all::<&'static u32>();
|
||||
// | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
||||
// |
|
||||
// = note: Due to a where-clause on the function `all`,
|
||||
// = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
|
||||
// = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn report_trait_placeholder_mismatch(
|
||||
&self,
|
||||
vid: Option<Region<'tcx>>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
sub_placeholder: Option<Region<'tcx>>,
|
||||
sup_placeholder: Option<Region<'tcx>>,
|
||||
trait_def_id: DefId,
|
||||
expected_args: GenericArgsRef<'tcx>,
|
||||
actual_args: GenericArgsRef<'tcx>,
|
||||
) -> Diag<'tcx> {
|
||||
let span = cause.span();
|
||||
|
||||
let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
|
||||
if let ObligationCauseCode::WhereClause(def_id, span)
|
||||
| ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code()
|
||||
&& def_id != CRATE_DEF_ID.to_def_id()
|
||||
{
|
||||
(
|
||||
true,
|
||||
Some(span),
|
||||
Some(self.tcx().def_span(def_id)),
|
||||
None,
|
||||
self.tcx().def_path_str(def_id),
|
||||
)
|
||||
} else {
|
||||
(false, None, None, Some(span), String::new())
|
||||
};
|
||||
|
||||
let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
|
||||
self.cx.tcx,
|
||||
trait_def_id,
|
||||
expected_args,
|
||||
));
|
||||
let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
|
||||
self.cx.tcx,
|
||||
trait_def_id,
|
||||
actual_args,
|
||||
));
|
||||
|
||||
// Search the expected and actual trait references to see (a)
|
||||
// whether the sub/sup placeholders appear in them (sometimes
|
||||
// you have a trait ref like `T: Foo<fn(&u8)>`, where the
|
||||
// placeholder was created as part of an inner type) and (b)
|
||||
// whether the inference variable appears. In each case,
|
||||
// assign a counter value in each case if so.
|
||||
let mut counter = 0;
|
||||
let mut has_sub = None;
|
||||
let mut has_sup = None;
|
||||
|
||||
let mut actual_has_vid = None;
|
||||
let mut expected_has_vid = None;
|
||||
|
||||
self.tcx().for_each_free_region(&expected_trait_ref, |r| {
|
||||
if Some(r) == sub_placeholder && has_sub.is_none() {
|
||||
has_sub = Some(counter);
|
||||
counter += 1;
|
||||
} else if Some(r) == sup_placeholder && has_sup.is_none() {
|
||||
has_sup = Some(counter);
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
if Some(r) == vid && expected_has_vid.is_none() {
|
||||
expected_has_vid = Some(counter);
|
||||
counter += 1;
|
||||
}
|
||||
});
|
||||
|
||||
self.tcx().for_each_free_region(&actual_trait_ref, |r| {
|
||||
if Some(r) == vid && actual_has_vid.is_none() {
|
||||
actual_has_vid = Some(counter);
|
||||
counter += 1;
|
||||
}
|
||||
});
|
||||
|
||||
let actual_self_ty_has_vid =
|
||||
self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
|
||||
|
||||
let expected_self_ty_has_vid =
|
||||
self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
|
||||
|
||||
let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
|
||||
|
||||
debug!(
|
||||
?actual_has_vid,
|
||||
?expected_has_vid,
|
||||
?has_sub,
|
||||
?has_sup,
|
||||
?actual_self_ty_has_vid,
|
||||
?expected_self_ty_has_vid,
|
||||
);
|
||||
|
||||
let actual_impl_expl_notes = self.explain_actual_impl_that_was_found(
|
||||
sub_placeholder,
|
||||
sup_placeholder,
|
||||
has_sub,
|
||||
has_sup,
|
||||
expected_trait_ref,
|
||||
actual_trait_ref,
|
||||
vid,
|
||||
expected_has_vid,
|
||||
actual_has_vid,
|
||||
any_self_ty_has_vid,
|
||||
leading_ellipsis,
|
||||
);
|
||||
|
||||
self.tcx().dcx().create_err(TraitPlaceholderMismatch {
|
||||
span,
|
||||
satisfy_span,
|
||||
where_span,
|
||||
dup_span,
|
||||
def_id,
|
||||
trait_def_id: self.tcx().def_path_str(trait_def_id),
|
||||
actual_impl_expl_notes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add notes with details about the expected and actual trait refs, with attention to cases
|
||||
/// when placeholder regions are involved: either the trait or the self type containing
|
||||
/// them needs to be mentioned the closest to the placeholders.
|
||||
/// This makes the error messages read better, however at the cost of some complexity
|
||||
/// due to the number of combinations we have to deal with.
|
||||
fn explain_actual_impl_that_was_found(
|
||||
&self,
|
||||
sub_placeholder: Option<Region<'tcx>>,
|
||||
sup_placeholder: Option<Region<'tcx>>,
|
||||
has_sub: Option<usize>,
|
||||
has_sup: Option<usize>,
|
||||
expected_trait_ref: ty::TraitRef<'tcx>,
|
||||
actual_trait_ref: ty::TraitRef<'tcx>,
|
||||
vid: Option<Region<'tcx>>,
|
||||
expected_has_vid: Option<usize>,
|
||||
actual_has_vid: Option<usize>,
|
||||
any_self_ty_has_vid: bool,
|
||||
leading_ellipsis: bool,
|
||||
) -> Vec<ActualImplExplNotes<'tcx>> {
|
||||
// The weird thing here with the `maybe_highlighting_region` calls and the
|
||||
// the match inside is meant to be like this:
|
||||
//
|
||||
// - The match checks whether the given things (placeholders, etc) appear
|
||||
// in the types are about to print
|
||||
// - Meanwhile, the `maybe_highlighting_region` calls set up
|
||||
// highlights so that, if they do appear, we will replace
|
||||
// them `'0` and whatever. (This replacement takes place
|
||||
// inside the closure given to `maybe_highlighting_region`.)
|
||||
//
|
||||
// There is some duplication between the calls -- i.e., the
|
||||
// `maybe_highlighting_region` checks if (e.g.) `has_sub` is
|
||||
// None, an then we check again inside the closure, but this
|
||||
// setup sort of minimized the number of calls and so form.
|
||||
|
||||
let highlight_trait_ref = |trait_ref| Highlighted {
|
||||
tcx: self.tcx(),
|
||||
highlight: RegionHighlightMode::default(),
|
||||
value: trait_ref,
|
||||
};
|
||||
|
||||
let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
|
||||
|
||||
let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
|
||||
expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
|
||||
expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
|
||||
|
||||
let passive_voice = match (has_sub, has_sup) {
|
||||
(Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
|
||||
(None, None) => {
|
||||
expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
|
||||
match expected_has_vid {
|
||||
Some(_) => true,
|
||||
None => any_self_ty_has_vid,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (kind, ty_or_sig, trait_path) = if same_self_type {
|
||||
let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
|
||||
self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
|
||||
|
||||
if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
|
||||
{
|
||||
let closure_sig = self_ty.map(|closure| {
|
||||
if let ty::Closure(_, args) = closure.kind() {
|
||||
self.tcx()
|
||||
.signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe)
|
||||
} else {
|
||||
bug!("type is not longer closure");
|
||||
}
|
||||
});
|
||||
(
|
||||
ActualImplExpectedKind::Signature,
|
||||
TyOrSig::ClosureSig(closure_sig),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ActualImplExpectedKind::Other,
|
||||
TyOrSig::Ty(self_ty),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
}
|
||||
} else if passive_voice {
|
||||
(
|
||||
ActualImplExpectedKind::Passive,
|
||||
TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ActualImplExpectedKind::Other,
|
||||
TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
};
|
||||
|
||||
let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {
|
||||
(Some(n1), Some(n2)) => {
|
||||
(ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))
|
||||
}
|
||||
(Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),
|
||||
(None, None) => {
|
||||
if let Some(n) = expected_has_vid {
|
||||
(ActualImplExpectedLifetimeKind::Some, n, 0)
|
||||
} else {
|
||||
(ActualImplExpectedLifetimeKind::Nothing, 0, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let note_1 = ActualImplExplNotes::new_expected(
|
||||
kind,
|
||||
lt_kind,
|
||||
leading_ellipsis,
|
||||
ty_or_sig,
|
||||
trait_path,
|
||||
lifetime_1,
|
||||
lifetime_2,
|
||||
);
|
||||
|
||||
let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
|
||||
actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
|
||||
|
||||
let passive_voice = match actual_has_vid {
|
||||
Some(_) => any_self_ty_has_vid,
|
||||
None => true,
|
||||
};
|
||||
|
||||
let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());
|
||||
let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();
|
||||
let has_lifetime = actual_has_vid.is_some();
|
||||
let lifetime = actual_has_vid.unwrap_or_default();
|
||||
|
||||
let note_2 = if same_self_type {
|
||||
ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }
|
||||
} else if passive_voice {
|
||||
ActualImplExplNotes::ButActuallyImplementedForTy {
|
||||
trait_path,
|
||||
ty,
|
||||
has_lifetime,
|
||||
lifetime,
|
||||
}
|
||||
} else {
|
||||
ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }
|
||||
};
|
||||
|
||||
vec![note_1, note_2]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::PlaceholderRelationLfNotSatisfied;
|
||||
use crate::infer::{RegionResolutionError, SubregionOrigin};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_middle::ty::{self, RePlaceholder, Region};
|
||||
|
||||
impl<'tcx> NiceRegionError<'_, 'tcx> {
|
||||
/// Emitted wwhen given a `ConcreteFailure` when relating two placeholders.
|
||||
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
|
||||
match &self.error {
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::RelateRegionParamBound(span),
|
||||
Region(Interned(
|
||||
RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: sub_name, .. },
|
||||
..
|
||||
}),
|
||||
_,
|
||||
)),
|
||||
Region(Interned(
|
||||
RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: sup_name, .. },
|
||||
..
|
||||
}),
|
||||
_,
|
||||
)),
|
||||
)) => {
|
||||
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))
|
||||
}
|
||||
ty::BrAnon | ty::BrEnv => (None, None),
|
||||
};
|
||||
let (sup_span, sup_symbol) = match sup_name {
|
||||
ty::BrNamed(def_id, symbol) => {
|
||||
(Some(self.tcx().def_span(def_id)), Some(symbol))
|
||||
}
|
||||
ty::BrAnon | ty::BrEnv => (None, None),
|
||||
};
|
||||
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,
|
||||
sup_span,
|
||||
sub_symbol,
|
||||
sup_symbol,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
(Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasSup {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
sup_symbol,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
(Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasSub {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
sub_symbol,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
(Some(sub_span), Some(sup_span), _, _) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasNone {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
_ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () },
|
||||
};
|
||||
Some(self.tcx().dcx().create_err(diag))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,618 @@
|
|||
//! Error Reporting for static impl Traits.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{
|
||||
ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted,
|
||||
ReqIntroducedLocations,
|
||||
};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
|
||||
LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
|
||||
};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when the return type is a static `impl Trait`,
|
||||
/// `dyn Trait` or if a method call on a trait object introduces a static requirement.
|
||||
pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
|
||||
debug!("try_report_static_impl_trait(error={:?})", self.error);
|
||||
let tcx = self.tcx();
|
||||
let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
|
||||
RegionResolutionError::SubSupConflict(
|
||||
_,
|
||||
var_origin,
|
||||
sub_origin,
|
||||
sub_r,
|
||||
sup_origin,
|
||||
sup_r,
|
||||
spans,
|
||||
) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
|
||||
RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
|
||||
sub_r,
|
||||
sup_r,
|
||||
) if sub_r.is_static() => {
|
||||
// This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
|
||||
if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
|
||||
// This may have a closure and it would cause ICE
|
||||
// through `find_param_with_region` (#78262).
|
||||
let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
|
||||
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
|
||||
if fn_returns.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let param = self.find_param_with_region(*sup_r, *sub_r)?;
|
||||
let simple_ident = param.param.pat.simple_ident();
|
||||
|
||||
let (has_impl_path, impl_path) = match ctxt.assoc_item.container {
|
||||
AssocItemContainer::TraitContainer => {
|
||||
let id = ctxt.assoc_item.container_id(tcx);
|
||||
(true, tcx.def_path_str(id))
|
||||
}
|
||||
AssocItemContainer::ImplContainer => (false, String::new()),
|
||||
};
|
||||
|
||||
let mut err = self.tcx().dcx().create_err(ButCallingIntroduces {
|
||||
param_ty_span: param.param_ty_span,
|
||||
cause_span: cause.span,
|
||||
has_param_name: simple_ident.is_some(),
|
||||
param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
|
||||
has_lifetime: sup_r.has_name(),
|
||||
lifetime: sup_r.to_string(),
|
||||
assoc_item: ctxt.assoc_item.name,
|
||||
has_impl_path,
|
||||
impl_path,
|
||||
});
|
||||
if self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) {
|
||||
let reported = err.emit();
|
||||
return Some(reported);
|
||||
} else {
|
||||
err.cancel()
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
debug!(
|
||||
"try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
|
||||
var_origin, sub_origin, sub_r, sup_origin, sup_r
|
||||
);
|
||||
let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
|
||||
debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
|
||||
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) =
|
||||
if sup_origin.span().overlaps(param.param_ty_span) {
|
||||
// Account for `async fn` like in `async-await/issues/issue-62097.rs`.
|
||||
// The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
|
||||
// place (but with different `ctxt`, hence `overlaps` instead of `==` above).
|
||||
//
|
||||
// This avoids the following:
|
||||
//
|
||||
// LL | pub async fn run_dummy_fn(&self) {
|
||||
// | ^^^^^
|
||||
// | |
|
||||
// | this data with an anonymous lifetime `'_`...
|
||||
// | ...is captured here...
|
||||
(false, sup_origin.span())
|
||||
} else {
|
||||
(!sup_origin.span().overlaps(return_sp), param.param_ty_span)
|
||||
};
|
||||
|
||||
debug!("try_report_static_impl_trait: param_info={:?}", param);
|
||||
|
||||
let mut spans = spans.clone();
|
||||
|
||||
if mention_influencer {
|
||||
spans.push(sup_origin.span());
|
||||
}
|
||||
// We dedup the spans *ignoring* expansion context.
|
||||
spans.sort();
|
||||
spans.dedup_by_key(|span| (span.lo(), span.hi()));
|
||||
|
||||
// We try to make the output have fewer overlapping spans if possible.
|
||||
let require_span =
|
||||
if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
|
||||
|
||||
let spans_empty = spans.is_empty();
|
||||
let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
|
||||
let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
|
||||
Some(*bound)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut subdiag = None;
|
||||
|
||||
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
|
||||
if let ObligationCauseCode::ReturnValue(hir_id)
|
||||
| ObligationCauseCode::BlockTailExpression(hir_id, ..) = cause.code()
|
||||
{
|
||||
let parent_id = tcx.hir().get_parent_item(*hir_id);
|
||||
if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
|
||||
let mut span: MultiSpan = fn_decl.output.span().into();
|
||||
let mut spans = Vec::new();
|
||||
let mut add_label = true;
|
||||
if let hir::FnRetTy::Return(ty) = fn_decl.output {
|
||||
let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
|
||||
v.visit_ty(ty);
|
||||
if !v.0.is_empty() {
|
||||
span = v.0.clone().into();
|
||||
spans = v.0;
|
||||
add_label = false;
|
||||
}
|
||||
}
|
||||
let fn_decl_span = fn_decl.output.span();
|
||||
|
||||
subdiag = Some(ReqIntroducedLocations {
|
||||
span,
|
||||
spans,
|
||||
fn_decl_span,
|
||||
cause_span: cause.span,
|
||||
add_label,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let diag = ButNeedsToSatisfy {
|
||||
sp,
|
||||
influencer_point,
|
||||
spans: spans.clone(),
|
||||
// If any of the "captured here" labels appears on the same line or after
|
||||
// `require_span`, we put it on a note to ensure the text flows by appearing
|
||||
// always at the end.
|
||||
require_span_as_note: require_as_note.then_some(require_span),
|
||||
// We don't need a note, it's already at the end, it can be shown as a `span_label`.
|
||||
require_span_as_label: (!require_as_note).then_some(require_span),
|
||||
req_introduces_loc: subdiag,
|
||||
|
||||
has_lifetime: sup_r.has_name(),
|
||||
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,
|
||||
};
|
||||
|
||||
let mut err = self.tcx().dcx().create_err(diag);
|
||||
|
||||
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
|
||||
|
||||
let mut override_error_code = None;
|
||||
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
|
||||
&& let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code()
|
||||
// Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
|
||||
// `'static` lifetime when called as a method on a binding: `bar.qux()`.
|
||||
&& self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt)
|
||||
{
|
||||
override_error_code = Some(ctxt.assoc_item.name);
|
||||
}
|
||||
|
||||
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin
|
||||
&& let code = match cause.code() {
|
||||
ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
|
||||
_ => cause.code(),
|
||||
}
|
||||
&& let (
|
||||
&ObligationCauseCode::WhereClause(item_def_id, _)
|
||||
| &ObligationCauseCode::WhereClauseInExpr(item_def_id, ..),
|
||||
None,
|
||||
) = (code, override_error_code)
|
||||
{
|
||||
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
|
||||
// lifetime as above, but called using a fully-qualified path to the method:
|
||||
// `Foo::qux(bar)`.
|
||||
let mut v = TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(param.param_ty);
|
||||
if let Some((ident, self_ty)) =
|
||||
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
|
||||
&& self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
|
||||
{
|
||||
override_error_code = Some(ident.name);
|
||||
}
|
||||
}
|
||||
if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
|
||||
// Provide a more targeted error code and description.
|
||||
let retarget_subdiag = MoreTargeted { ident };
|
||||
retarget_subdiag.add_to_diag(&mut err);
|
||||
}
|
||||
|
||||
let arg = match param.param.pat.simple_ident() {
|
||||
Some(simple_ident) => format!("argument `{simple_ident}`"),
|
||||
None => "the argument".to_string(),
|
||||
};
|
||||
let captures = format!("captures data from {arg}");
|
||||
suggest_new_region_bound(
|
||||
tcx,
|
||||
&mut err,
|
||||
fn_returns,
|
||||
lifetime_name,
|
||||
Some(arg),
|
||||
captures,
|
||||
Some((param.param_ty_span, param.param_ty.to_string())),
|
||||
Some(anon_reg_sup.def_id),
|
||||
);
|
||||
|
||||
let reported = err.emit();
|
||||
Some(reported)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggest_new_region_bound(
|
||||
tcx: TyCtxt<'_>,
|
||||
err: &mut Diag<'_>,
|
||||
fn_returns: Vec<&rustc_hir::Ty<'_>>,
|
||||
lifetime_name: String,
|
||||
arg: Option<String>,
|
||||
captures: String,
|
||||
param: Option<(Span, String)>,
|
||||
scope_def_id: Option<LocalDefId>,
|
||||
) {
|
||||
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
|
||||
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
|
||||
let consider = "consider changing";
|
||||
let declare = "to declare that";
|
||||
let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound");
|
||||
let explicit_static =
|
||||
arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}"));
|
||||
let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
|
||||
let plus_lt = format!(" + {lifetime_name}");
|
||||
for fn_return in fn_returns {
|
||||
if fn_return.span.desugaring_kind().is_some() {
|
||||
// Skip `async` desugaring `impl Future`.
|
||||
continue;
|
||||
}
|
||||
match fn_return.kind {
|
||||
// FIXME(precise_captures): Suggest adding to `use<...>` list instead.
|
||||
TyKind::OpaqueDef(item_id, _, _) => {
|
||||
let item = tcx.hir().item(item_id);
|
||||
let ItemKind::OpaqueTy(opaque) = &item.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Get the identity type for this RPIT
|
||||
let did = item_id.owner_id.to_def_id();
|
||||
let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did));
|
||||
|
||||
if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
|
||||
GenericBound::Outlives(Lifetime {
|
||||
res: LifetimeName::Static, ident, ..
|
||||
}) => Some(ident.span),
|
||||
_ => None,
|
||||
}) {
|
||||
if let Some(explicit_static) = &explicit_static {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("{consider} `{ty}`'s {explicit_static}"),
|
||||
&lifetime_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let Some((param_span, ref param_ty)) = param {
|
||||
err.span_suggestion_verbose(
|
||||
param_span,
|
||||
add_static_bound,
|
||||
param_ty,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
} else if opaque.bounds.iter().any(|arg| {
|
||||
matches!(arg,
|
||||
GenericBound::Outlives(Lifetime { ident, .. })
|
||||
if ident.name.to_string() == lifetime_name )
|
||||
}) {
|
||||
} else {
|
||||
// get a lifetime name of existing named lifetimes if any
|
||||
let existing_lt_name = if let Some(id) = scope_def_id
|
||||
&& let Some(generics) = tcx.hir().get_generics(id)
|
||||
&& let named_lifetimes = generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| {
|
||||
matches!(
|
||||
p.kind,
|
||||
GenericParamKind::Lifetime {
|
||||
kind: hir::LifetimeParamKind::Explicit
|
||||
}
|
||||
)
|
||||
})
|
||||
.map(|p| {
|
||||
if let hir::ParamName::Plain(name) = p.name {
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|n| !matches!(n, None))
|
||||
.collect::<Vec<_>>()
|
||||
&& named_lifetimes.len() > 0
|
||||
{
|
||||
named_lifetimes[0].clone()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
|
||||
// if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
|
||||
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
|
||||
if let Some(id) = scope_def_id
|
||||
&& let Some(generics) = tcx.hir().get_generics(id)
|
||||
&& let mut spans_suggs =
|
||||
make_elided_region_spans_suggs(name, generics.params.iter())
|
||||
&& spans_suggs.len() > 1
|
||||
{
|
||||
let use_lt = if existing_lt_name == None {
|
||||
spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
|
||||
format!("you can introduce a named lifetime parameter `{name}`")
|
||||
} else {
|
||||
// make use the existing named lifetime
|
||||
format!("you can use the named lifetime parameter `{name}`")
|
||||
};
|
||||
spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
|
||||
err.multipart_suggestion_verbose(
|
||||
format!("{declare} `{ty}` {captures}, {use_lt}",),
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
fn_return.span.shrink_to_hi(),
|
||||
format!("{declare} `{ty}` {captures}, {explicit}",),
|
||||
&plus_lt,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::TraitObject(_, lt, _) => {
|
||||
if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
|
||||
err.span_suggestion_verbose(
|
||||
fn_return.span.shrink_to_hi(),
|
||||
format!("{declare} the trait object {captures}, {explicit}",),
|
||||
&plus_lt,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else if lt.ident.name.to_string() != lifetime_name {
|
||||
// With this check we avoid suggesting redundant bounds. This
|
||||
// would happen if there are nested impl/dyn traits and only
|
||||
// one of them has the bound we'd suggest already there, like
|
||||
// in `impl Foo<X = dyn Bar> + '_`.
|
||||
if let Some(explicit_static) = &explicit_static {
|
||||
err.span_suggestion_verbose(
|
||||
lt.ident.span,
|
||||
format!("{consider} the trait object's {explicit_static}"),
|
||||
&lifetime_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let Some((param_span, param_ty)) = param.clone() {
|
||||
err.span_suggestion_verbose(
|
||||
param_span,
|
||||
add_static_bound,
|
||||
param_ty,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_elided_region_spans_suggs<'a>(
|
||||
name: &str,
|
||||
generic_params: impl Iterator<Item = &'a GenericParam<'a>>,
|
||||
) -> Vec<(Span, String)> {
|
||||
let mut spans_suggs = Vec::new();
|
||||
let mut bracket_span = None;
|
||||
let mut consecutive_brackets = 0;
|
||||
|
||||
let mut process_consecutive_brackets =
|
||||
|span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| {
|
||||
if span
|
||||
.is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span))
|
||||
{
|
||||
consecutive_brackets += 1;
|
||||
} else if let Some(bracket_span) = bracket_span.take() {
|
||||
let sugg = std::iter::once("<")
|
||||
.chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", "))
|
||||
.chain([">"])
|
||||
.collect();
|
||||
spans_suggs.push((bracket_span.shrink_to_hi(), sugg));
|
||||
consecutive_brackets = 0;
|
||||
}
|
||||
bracket_span = span;
|
||||
};
|
||||
|
||||
for p in generic_params {
|
||||
if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind {
|
||||
match kind {
|
||||
MissingLifetimeKind::Underscore => {
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
spans_suggs.push((p.span, name.to_string()))
|
||||
}
|
||||
MissingLifetimeKind::Ampersand => {
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
spans_suggs.push((p.span.shrink_to_hi(), format!("{name} ")));
|
||||
}
|
||||
MissingLifetimeKind::Comma => {
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, ")));
|
||||
}
|
||||
MissingLifetimeKind::Brackets => {
|
||||
process_consecutive_brackets(Some(p.span), &mut spans_suggs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
|
||||
spans_suggs
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
pub fn get_impl_ident_and_self_ty_from_trait(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
trait_objects: &FxIndexSet<DefId>,
|
||||
) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
|
||||
match tcx.hir().get_if_local(def_id)? {
|
||||
Node::ImplItem(impl_item) => {
|
||||
let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
|
||||
if let hir::OwnerNode::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
}) = tcx.hir_owner_node(impl_did)
|
||||
{
|
||||
Some((impl_item.ident, self_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Node::TraitItem(trait_item) => {
|
||||
let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
|
||||
debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
|
||||
// The method being called is defined in the `trait`, but the `'static`
|
||||
// obligation comes from the `impl`. Find that `impl` so that we can point
|
||||
// at it in the suggestion.
|
||||
let trait_did = trait_id.to_def_id();
|
||||
tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
|
||||
if let Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
|
||||
}) = tcx.hir_node_by_def_id(impl_did)
|
||||
&& trait_objects.iter().all(|did| {
|
||||
// FIXME: we should check `self_ty` against the receiver
|
||||
// type in the `UnifyReceiver` context, but for now, use
|
||||
// this imperfect proxy. This will fail if there are
|
||||
// multiple `impl`s for the same trait like
|
||||
// `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
|
||||
// In that case, only the first one will get suggestions.
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
!traits.is_empty()
|
||||
})
|
||||
{
|
||||
Some((trait_item.ident, *self_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
|
||||
/// `'static` obligation. Suggest relaxing that implicit bound.
|
||||
fn find_impl_on_dyn_trait(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
ty: Ty<'_>,
|
||||
ctxt: &UnifyReceiverContext<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx();
|
||||
|
||||
// Find the method being called.
|
||||
let Ok(Some(instance)) = ty::Instance::try_resolve(
|
||||
tcx,
|
||||
ctxt.param_env,
|
||||
ctxt.assoc_item.def_id,
|
||||
self.cx.resolve_vars_if_possible(ctxt.args),
|
||||
) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let mut v = TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(ty);
|
||||
|
||||
// Get the `Ident` of the method being called and the corresponding `impl` (to point at
|
||||
// `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
|
||||
let Some((ident, self_ty)) =
|
||||
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Find the trait object types in the argument, so we point at *only* the trait object.
|
||||
self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
|
||||
}
|
||||
|
||||
fn suggest_constrain_dyn_trait_in_impl(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
found_dids: &FxIndexSet<DefId>,
|
||||
ident: Ident,
|
||||
self_ty: &hir::Ty<'_>,
|
||||
) -> bool {
|
||||
let mut suggested = false;
|
||||
for found_did in found_dids {
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
for &span in &traits {
|
||||
let subdiag = DynTraitConstraintSuggestion { span, ident };
|
||||
subdiag.add_to_diag(err);
|
||||
suggested = true;
|
||||
}
|
||||
}
|
||||
suggested
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<TyCtxt<'tcx>> for TraitObjectVisitor {
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) {
|
||||
match t.kind() {
|
||||
ty::Dynamic(preds, re, _) if re.is_static() => {
|
||||
if let Some(def_id) = preds.principal_def_id() {
|
||||
self.0.insert(def_id);
|
||||
}
|
||||
}
|
||||
_ => t.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
|
||||
pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
|
||||
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
|
||||
if let TyKind::TraitObject(
|
||||
poly_trait_refs,
|
||||
Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
|
||||
_,
|
||||
) = t.kind
|
||||
{
|
||||
for ptr in poly_trait_refs {
|
||||
if Some(self.1) == ptr.trait_ref.trait_def_id() {
|
||||
self.0.push(ptr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
walk_ty(self, t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::{Subtype, ValuePairs};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::print::RegionHighlightMode;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
|
||||
use rustc_span::Span;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
|
||||
pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
|
||||
let error = self.error.as_ref()?;
|
||||
debug!("try_report_impl_not_conforming_to_trait {:?}", error);
|
||||
if let RegionResolutionError::SubSupConflict(
|
||||
_,
|
||||
var_origin,
|
||||
sub_origin,
|
||||
_sub,
|
||||
sup_origin,
|
||||
_sup,
|
||||
_,
|
||||
) = error.clone()
|
||||
&& let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
|
||||
&& let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
|
||||
sub_trace.cause.code()
|
||||
&& sub_trace.values == sup_trace.values
|
||||
&& let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
|
||||
{
|
||||
// FIXME(compiler-errors): Don't like that this needs `Ty`s, but
|
||||
// all of the region highlighting machinery only deals with those.
|
||||
let guar = self.emit_err(
|
||||
var_origin.span(),
|
||||
Ty::new_fn_ptr(self.cx.tcx, expected),
|
||||
Ty::new_fn_ptr(self.cx.tcx, found),
|
||||
*trait_item_def_id,
|
||||
);
|
||||
return Some(guar);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn emit_err(
|
||||
&self,
|
||||
sp: Span,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
) -> ErrorGuaranteed {
|
||||
let trait_sp = self.tcx().def_span(trait_def_id);
|
||||
|
||||
// Mark all unnamed regions in the type with a number.
|
||||
// This diagnostic is called in response to lifetime errors, so be informative.
|
||||
struct HighlightBuilder<'tcx> {
|
||||
highlight: RegionHighlightMode<'tcx>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl<'tcx> HighlightBuilder<'tcx> {
|
||||
fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
|
||||
let mut builder =
|
||||
HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
|
||||
builder.visit_ty(ty);
|
||||
builder.highlight
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) {
|
||||
if !r.has_name() && self.counter <= 3 {
|
||||
self.highlight.highlighting_region(r, self.counter);
|
||||
self.counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let expected_highlight = HighlightBuilder::build(expected);
|
||||
let expected = self
|
||||
.cx
|
||||
.extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
|
||||
.name;
|
||||
let found_highlight = HighlightBuilder::build(found);
|
||||
let found =
|
||||
self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
|
||||
|
||||
// Get the span of all the used type parameters in the method.
|
||||
let assoc_item = self.tcx().associated_item(trait_def_id);
|
||||
let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
|
||||
match assoc_item.kind {
|
||||
ty::AssocKind::Fn => {
|
||||
let hir = self.tcx().hir();
|
||||
if let Some(hir_id) =
|
||||
assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id))
|
||||
{
|
||||
if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
|
||||
visitor.visit_fn_decl(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let diag = TraitImplDiff {
|
||||
sp,
|
||||
trait_sp,
|
||||
note: (),
|
||||
param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() },
|
||||
rel_help: visitor.types.is_empty().then_some(RelationshipHelp),
|
||||
expected,
|
||||
found,
|
||||
};
|
||||
|
||||
self.tcx().dcx().emit_err(diag)
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeParamSpanVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
types: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
||||
match arg.kind {
|
||||
hir::TyKind::Ref(_, ref mut_ty) => {
|
||||
// We don't want to suggest looking into borrowing `&T` or `&Self`.
|
||||
hir::intravisit::walk_ty(self, mut_ty.ty);
|
||||
return;
|
||||
}
|
||||
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
|
||||
[segment]
|
||||
if matches!(
|
||||
segment.res,
|
||||
Res::SelfTyParam { .. }
|
||||
| Res::SelfTyAlias { .. }
|
||||
| Res::Def(hir::def::DefKind::TyParam, _)
|
||||
) =>
|
||||
{
|
||||
self.types.push(path.span);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
hir::intravisit::walk_ty(self, arg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
//! Helper functions corresponding to lifetime errors due to
|
||||
//! anonymous regions.
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
|
||||
/// Information about the anonymous region we are searching for.
|
||||
#[derive(Debug)]
|
||||
pub struct AnonymousParamInfo<'tcx> {
|
||||
/// The parameter corresponding to the anonymous region.
|
||||
pub param: &'tcx hir::Param<'tcx>,
|
||||
/// The type corresponding to the anonymous region parameter.
|
||||
pub param_ty: Ty<'tcx>,
|
||||
/// The ty::BoundRegionKind corresponding to the anonymous region.
|
||||
pub bound_region: ty::BoundRegionKind,
|
||||
/// The `Span` of the parameter type.
|
||||
pub param_ty_span: Span,
|
||||
/// Signals that the argument is the first parameter in the declaration.
|
||||
pub is_first: bool,
|
||||
}
|
||||
|
||||
// This method walks the Type of the function body parameters using
|
||||
// `fold_regions()` function and returns the
|
||||
// &hir::Param of the function parameter corresponding to the anonymous
|
||||
// region and the Ty corresponding to the named region.
|
||||
// Currently only the case where the function declaration consists of
|
||||
// one named region and one anonymous region is handled.
|
||||
// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
|
||||
// Here, we would return the hir::Param for y, we return the type &'a
|
||||
// i32, which is the type of y but with the anonymous region replaced
|
||||
// with 'a, the corresponding bound region and is_first which is true if
|
||||
// the hir::Param is the first parameter in the function declaration.
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn find_param_with_region<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
anon_region: Region<'tcx>,
|
||||
replace_region: Region<'tcx>,
|
||||
) -> Option<AnonymousParamInfo<'tcx>> {
|
||||
let (id, bound_region) = match *anon_region {
|
||||
ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region),
|
||||
ty::ReEarlyParam(ebr) => {
|
||||
let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id;
|
||||
(tcx.parent(region_def), ty::BoundRegionKind::BrNamed(region_def, ebr.name))
|
||||
}
|
||||
_ => return None, // not a free region
|
||||
};
|
||||
|
||||
let hir = &tcx.hir();
|
||||
let def_id = id.as_local()?;
|
||||
|
||||
// FIXME: use def_kind
|
||||
// Don't perform this on closures
|
||||
match tcx.hir_node_by_def_id(generic_param_scope) {
|
||||
hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let body = hir.maybe_body_owned_by(def_id)?;
|
||||
|
||||
let owner_id = hir.body_owner(body.id());
|
||||
let fn_decl = hir.fn_decl_by_hir_id(owner_id)?;
|
||||
let poly_fn_sig = tcx.fn_sig(id).instantiate_identity();
|
||||
|
||||
let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
|
||||
body.params
|
||||
.iter()
|
||||
.take(if fn_sig.c_variadic {
|
||||
fn_sig.inputs().len()
|
||||
} else {
|
||||
assert_eq!(fn_sig.inputs().len(), body.params.len());
|
||||
body.params.len()
|
||||
})
|
||||
.enumerate()
|
||||
.find_map(|(index, param)| {
|
||||
// May return None; sometimes the tables are not yet populated.
|
||||
let ty = fn_sig.inputs()[index];
|
||||
let mut found_anon_region = false;
|
||||
let new_param_ty = tcx.fold_regions(ty, |r, _| {
|
||||
if r == anon_region {
|
||||
found_anon_region = true;
|
||||
replace_region
|
||||
} else {
|
||||
r
|
||||
}
|
||||
});
|
||||
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;
|
||||
AnonymousParamInfo {
|
||||
param,
|
||||
param_ty: new_param_ty,
|
||||
param_ty_span,
|
||||
bound_region,
|
||||
is_first,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
pub(super) fn find_param_with_region(
|
||||
&self,
|
||||
anon_region: Region<'tcx>,
|
||||
replace_region: Region<'tcx>,
|
||||
) -> Option<AnonymousParamInfo<'tcx>> {
|
||||
find_param_with_region(self.tcx(), self.generic_param_scope, anon_region, replace_region)
|
||||
}
|
||||
|
||||
// Here, we check for the case where the anonymous region
|
||||
// is in the return type as written by the user.
|
||||
// FIXME(#42703) - Need to handle certain cases here.
|
||||
pub(super) fn is_return_type_anon(
|
||||
&self,
|
||||
scope_def_id: LocalDefId,
|
||||
br: ty::BoundRegionKind,
|
||||
hir_sig: &hir::FnSig<'_>,
|
||||
) -> Option<Span> {
|
||||
let fn_ty = self.tcx().type_of(scope_def_id).instantiate_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();
|
||||
let future_output = if hir_sig.header.is_async() {
|
||||
ret_ty.map_bound(|ty| self.cx.get_impl_future_output_ty(ty)).transpose()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return match future_output {
|
||||
Some(output) if self.includes_region(output, br) => Some(span),
|
||||
None if self.includes_region(ret_ty, br) => Some(span),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn includes_region(
|
||||
&self,
|
||||
ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
|
||||
region: ty::BoundRegionKind,
|
||||
) -> bool {
|
||||
let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
|
||||
// We are only checking is any region meets the condition so order doesn't matter
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
late_bound_regions.iter().any(|r| *r == region)
|
||||
}
|
||||
|
||||
// Here we check for the case where anonymous region
|
||||
// corresponds to self and if yes, we display E0312.
|
||||
// FIXME(#42700) - Need to format self properly to
|
||||
// enable E0621 for it.
|
||||
pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool {
|
||||
is_first
|
||||
&& self
|
||||
.tcx()
|
||||
.opt_associated_item(scope_def_id.to_def_id())
|
||||
.is_some_and(|i| i.fn_has_self_parameter)
|
||||
}
|
||||
}
|
421
compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
Normal file
421
compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
Normal file
|
@ -0,0 +1,421 @@
|
|||
use crate::error_reporting::infer::{note_and_explain_region, TypeErrCtxt};
|
||||
use crate::errors::{
|
||||
note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
|
||||
RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::infer::{self, SubregionOrigin};
|
||||
use rustc_errors::{Diag, Subdiagnostic};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
use super::ObligationCauseAsDiagArg;
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
|
||||
match *origin {
|
||||
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
|
||||
span: trace.cause.span,
|
||||
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
|
||||
expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
|
||||
}
|
||||
.add_to_diag(err),
|
||||
infer::Reborrow(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
|
||||
}
|
||||
infer::RelateObjectBound(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_reference_outlives_referent,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: false,
|
||||
}
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::RelateParamBound(span, ty, opt_span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_relate_param_bound,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: opt_span.is_some(),
|
||||
}
|
||||
.add_to_diag(err);
|
||||
if let Some(span) = opt_span {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::CompareImplItemObligation { span, .. } => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::CheckAssociatedTypeBounds { ref parent, .. } => {
|
||||
self.note_region_origin(err, parent);
|
||||
}
|
||||
infer::AscribeUserTypeProvePredicate(span) => {
|
||||
RegionOriginNote::Plain {
|
||||
span,
|
||||
msg: fluent::infer_ascribe_user_type_prove_predicate,
|
||||
}
|
||||
.add_to_diag(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_concrete_failure(
|
||||
&self,
|
||||
generic_param_scope: LocalDefId,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
) -> Diag<'a> {
|
||||
let mut err = match origin {
|
||||
infer::Subtype(box trace) => {
|
||||
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
|
||||
let mut err = self.report_and_explain_type_error(trace, terr);
|
||||
match (*sub, *sup) {
|
||||
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
|
||||
(ty::RePlaceholder(_), _) => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"",
|
||||
sup,
|
||||
" doesn't meet the lifetime requirements",
|
||||
None,
|
||||
);
|
||||
}
|
||||
(_, ty::RePlaceholder(_)) => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"the required lifetime does not necessarily outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"",
|
||||
sup,
|
||||
"...",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"...does not necessarily outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
err
|
||||
}
|
||||
infer::Reborrow(span) => {
|
||||
let reference_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::RefValidFor,
|
||||
note_and_explain::SuffixKind::Continues,
|
||||
);
|
||||
let content_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::ContentValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(OutlivesContent {
|
||||
span,
|
||||
notes: reference_valid.into_iter().chain(content_valid).collect(),
|
||||
})
|
||||
}
|
||||
infer::RelateObjectBound(span) => {
|
||||
let object_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::TypeObjValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let pointer_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::SourcePointerValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(OutlivesBound {
|
||||
span,
|
||||
notes: object_valid.into_iter().chain(pointer_valid).collect(),
|
||||
})
|
||||
}
|
||||
infer::RelateParamBound(span, ty, opt_span) => {
|
||||
let prefix = match *sub {
|
||||
ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
|
||||
_ => note_and_explain::PrefixKind::TypeOutlive,
|
||||
};
|
||||
let suffix = if opt_span.is_some() {
|
||||
note_and_explain::SuffixKind::ReqByBinding
|
||||
} else {
|
||||
note_and_explain::SuffixKind::Empty
|
||||
};
|
||||
let note = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
opt_span,
|
||||
prefix,
|
||||
suffix,
|
||||
);
|
||||
self.dcx().create_err(FulfillReqLifetime {
|
||||
span,
|
||||
ty: self.resolve_vars_if_possible(ty),
|
||||
note,
|
||||
})
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
let param_instantiated = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfParamInstantiatedWith,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let param_must_outlive = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfParamMustOutlive,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(LfBoundNotSatisfied {
|
||||
span,
|
||||
notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
|
||||
})
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
let pointer_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::PointerValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let data_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::DataValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(RefLongerThanData {
|
||||
span,
|
||||
ty: self.resolve_vars_if_possible(ty),
|
||||
notes: pointer_valid.into_iter().chain(data_valid).collect(),
|
||||
})
|
||||
}
|
||||
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
|
||||
let mut err = self.infcx.report_extra_impl_obligation(
|
||||
span,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id,
|
||||
&format!("`{sup}: {sub}`"),
|
||||
);
|
||||
// We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
|
||||
if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
|
||||
&& generics.where_clause_span.contains(span)
|
||||
{
|
||||
self.suggest_copy_trait_method_bounds(
|
||||
trait_item_def_id,
|
||||
impl_item_def_id,
|
||||
&mut err,
|
||||
);
|
||||
}
|
||||
err
|
||||
}
|
||||
infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
|
||||
let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
|
||||
|
||||
// Don't mention the item name if it's an RPITIT, since that'll just confuse
|
||||
// folks.
|
||||
if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
|
||||
let trait_item_span = self.tcx.def_span(trait_item_def_id);
|
||||
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
|
||||
err.span_label(
|
||||
trait_item_span,
|
||||
format!("definition of `{item_name}` from trait"),
|
||||
);
|
||||
}
|
||||
|
||||
self.suggest_copy_trait_method_bounds(
|
||||
trait_item_def_id,
|
||||
impl_item_def_id,
|
||||
&mut err,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::AscribeUserTypeProvePredicate(span) => {
|
||||
let instantiated = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfInstantiatedWith,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let must_outlive = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfMustOutlive,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(LfBoundNotSatisfied {
|
||||
span,
|
||||
notes: instantiated.into_iter().chain(must_outlive).collect(),
|
||||
})
|
||||
}
|
||||
};
|
||||
if sub.is_error() || sup.is_error() {
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
pub fn suggest_copy_trait_method_bounds(
|
||||
&self,
|
||||
trait_item_def_id: DefId,
|
||||
impl_item_def_id: LocalDefId,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
// FIXME(compiler-errors): Right now this is only being used for region
|
||||
// predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
|
||||
// but right now it's not really very smart when it comes to implicit `Sized`
|
||||
// predicates and bounds on the trait itself.
|
||||
|
||||
let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
|
||||
return;
|
||||
};
|
||||
let trait_args = trait_ref
|
||||
.instantiate_identity()
|
||||
// Replace the explicit self type with `Self` for better suggestion rendering
|
||||
.with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
|
||||
.args;
|
||||
let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
|
||||
.rebase_onto(self.tcx, impl_def_id, trait_args);
|
||||
|
||||
let Ok(trait_predicates) =
|
||||
self.tcx
|
||||
.explicit_predicates_of(trait_item_def_id)
|
||||
.instantiate_own(self.tcx, trait_item_args)
|
||||
.map(|(pred, _)| {
|
||||
if pred.is_suggestable(self.tcx, false) {
|
||||
Ok(pred.to_string())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, ()>>()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let suggestion = if trait_predicates.is_empty() {
|
||||
WhereClauseSuggestions::Remove { span: generics.where_clause_span }
|
||||
} else {
|
||||
let space = if generics.where_clause_span.is_empty() { " " } else { "" };
|
||||
WhereClauseSuggestions::CopyPredicates {
|
||||
span: generics.where_clause_span,
|
||||
space,
|
||||
trait_predicates: trait_predicates.join(", "),
|
||||
}
|
||||
};
|
||||
err.subdiagnostic(suggestion);
|
||||
}
|
||||
|
||||
pub(super) fn report_placeholder_failure(
|
||||
&self,
|
||||
generic_param_scope: LocalDefId,
|
||||
placeholder_origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
) -> Diag<'a> {
|
||||
// I can't think how to do better than this right now. -nikomatsakis
|
||||
debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
|
||||
match placeholder_origin {
|
||||
infer::Subtype(box ref trace)
|
||||
if matches!(
|
||||
&trace.cause.code().peel_derives(),
|
||||
ObligationCauseCode::WhereClause(..)
|
||||
| ObligationCauseCode::WhereClauseInExpr(..)
|
||||
) =>
|
||||
{
|
||||
// Hack to get around the borrow checker because trace.cause has an `Rc`.
|
||||
if let ObligationCauseCode::WhereClause(_, span)
|
||||
| ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
|
||||
&trace.cause.code().peel_derives()
|
||||
&& !span.is_dummy()
|
||||
{
|
||||
let span = *span;
|
||||
self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
|
||||
.with_span_note(span, "the lifetime requirement is introduced here")
|
||||
} else {
|
||||
unreachable!(
|
||||
"control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
|
||||
)
|
||||
}
|
||||
}
|
||||
infer::Subtype(box trace) => {
|
||||
let terr = TypeError::RegionsPlaceholderMismatch;
|
||||
return self.report_and_explain_type_error(trace, terr);
|
||||
}
|
||||
_ => {
|
||||
return self.report_concrete_failure(
|
||||
generic_param_scope,
|
||||
placeholder_origin,
|
||||
sub,
|
||||
sup,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,941 @@
|
|||
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
|
||||
use rustc_errors::{pluralize, Diag, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_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};
|
||||
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub fn note_and_explain_type_err(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
err: TypeError<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
sp: Span,
|
||||
body_owner_def_id: DefId,
|
||||
) {
|
||||
debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
||||
match err {
|
||||
TypeError::ArgumentSorts(values, _) | TypeError::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::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..))
|
||||
if self.tcx.coroutine_is_async(def_id1)
|
||||
&& self.tcx.coroutine_is_async(def_id2) =>
|
||||
{
|
||||
diag.note("no two async blocks, even if identical, have the same type");
|
||||
diag.help(
|
||||
"consider pinning your async block and casting it to 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_verbose(
|
||||
sp.shrink_to_hi(),
|
||||
"use a float literal",
|
||||
".0",
|
||||
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::Inherent, _),
|
||||
ty::Alias(ty::Projection | ty::Inherent, _),
|
||||
) => {
|
||||
diag.note("an associated type was expected, but a different one was found");
|
||||
}
|
||||
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
||||
(ty::Param(p), ty::Alias(ty::Projection, proj))
|
||||
| (ty::Alias(ty::Projection, proj), ty::Param(p))
|
||||
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
|
||||
{
|
||||
let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
|
||||
let p_def_id = param.def_id;
|
||||
let p_span = tcx.def_span(p_def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
let parent = p_def_id.as_local().and_then(|id| {
|
||||
let local_id = tcx.local_def_id_to_hir_id(id);
|
||||
let generics = tcx.parent_hir_node(local_id).generics()?;
|
||||
Some((id, generics))
|
||||
});
|
||||
let mut note = true;
|
||||
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_args) = proj.trait_ref_and_own_args(tcx);
|
||||
let item_name = tcx.item_name(proj.def_id);
|
||||
let item_args = self.format_generic_args(assoc_args);
|
||||
|
||||
// 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,
|
||||
&proj.self_ty().to_string(),
|
||||
&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,
|
||||
&proj.self_ty().to_string(),
|
||||
&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);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
diag.help("type parameters must be constrained to match other types");
|
||||
if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
|
||||
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::CoroutineClosure(..) | ty::Coroutine(..),
|
||||
) => {
|
||||
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, "expected 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);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
}
|
||||
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
|
||||
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
|
||||
{
|
||||
self.expected_projection(
|
||||
diag,
|
||||
proj_ty,
|
||||
values,
|
||||
body_owner_def_id,
|
||||
cause.code(),
|
||||
);
|
||||
}
|
||||
(_, ty::Alias(ty::Projection | ty::Inherent, proj_ty))
|
||||
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
|
||||
{
|
||||
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::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias))
|
||||
if let Some(def_id) = t.principal_def_id()
|
||||
&& tcx
|
||||
.explicit_item_super_predicates(alias.def_id)
|
||||
.skip_binder()
|
||||
.iter()
|
||||
.any(|(pred, _span)| match pred.kind().skip_binder() {
|
||||
ty::ClauseKind::Trait(trait_predicate)
|
||||
if trait_predicate.polarity
|
||||
== ty::PredicatePolarity::Positive =>
|
||||
{
|
||||
trait_predicate.def_id() == def_id
|
||||
}
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
diag.help(format!(
|
||||
"you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \
|
||||
change the expected type as well",
|
||||
values.found, values.expected,
|
||||
));
|
||||
}
|
||||
(ty::Dynamic(t, _, ty::DynKind::Dyn), _)
|
||||
if let Some(def_id) = t.principal_def_id() =>
|
||||
{
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, values.found, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.help(format!(
|
||||
"`{}` implements `{trait_name}` so you could box the found value \
|
||||
and coerce it to the trait object `Box<dyn {trait_name}>`, you \
|
||||
will have to change the expected type as well",
|
||||
values.found,
|
||||
));
|
||||
}
|
||||
}
|
||||
(_, ty::Dynamic(t, _, ty::DynKind::Dyn))
|
||||
if let Some(def_id) = t.principal_def_id() =>
|
||||
{
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.help(format!(
|
||||
"`{}` implements `{trait_name}` so you could change the expected \
|
||||
type to `Box<dyn {trait_name}>`",
|
||||
values.expected,
|
||||
));
|
||||
}
|
||||
}
|
||||
(ty::Dynamic(t, _, ty::DynKind::DynStar), _)
|
||||
if let Some(def_id) = t.principal_def_id() =>
|
||||
{
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, values.found, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.help(format!(
|
||||
"`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \
|
||||
not enabled; that feature it is currently incomplete",
|
||||
values.found,
|
||||
));
|
||||
}
|
||||
}
|
||||
(_, ty::Alias(ty::Opaque, opaque_ty))
|
||||
| (ty::Alias(ty::Opaque, opaque_ty), _) => {
|
||||
if opaque_ty.def_id.is_local()
|
||||
&& matches!(
|
||||
tcx.def_kind(body_owner_def_id),
|
||||
DefKind::Fn
|
||||
| DefKind::Static { .. }
|
||||
| DefKind::Const
|
||||
| DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
)
|
||||
&& tcx.is_type_alias_impl_trait(opaque_ty.def_id)
|
||||
&& !tcx
|
||||
.opaque_types_defined_by(body_owner_def_id.expect_local())
|
||||
.contains(&opaque_ty.def_id.expect_local())
|
||||
{
|
||||
let sp = tcx
|
||||
.def_ident_span(body_owner_def_id)
|
||||
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
|
||||
diag.span_note(
|
||||
sp,
|
||||
"this item must have the opaque type in its signature in order to \
|
||||
be able to register hidden types",
|
||||
);
|
||||
}
|
||||
// If two if arms can be coerced to a trait object, provide a structured
|
||||
// suggestion.
|
||||
let ObligationCauseCode::IfExpression(cause) = cause.code() else {
|
||||
return;
|
||||
};
|
||||
let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(then) = blk.expr else {
|
||||
return;
|
||||
};
|
||||
let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(else_) = blk.expr else {
|
||||
return;
|
||||
};
|
||||
let expected = match values.found.kind() {
|
||||
ty::Alias(..) => values.expected,
|
||||
_ => values.found,
|
||||
};
|
||||
let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id);
|
||||
for (pred, _span) in preds.skip_binder() {
|
||||
let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if trait_predicate.polarity != ty::PredicatePolarity::Positive {
|
||||
continue;
|
||||
}
|
||||
let def_id = trait_predicate.def_id();
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, expected, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.multipart_suggestion(
|
||||
format!(
|
||||
"`{expected}` implements `{trait_name}` so you can box \
|
||||
both arms and coerce to the trait object \
|
||||
`Box<dyn {trait_name}>`",
|
||||
),
|
||||
vec![
|
||||
(then.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(
|
||||
then.span.shrink_to_hi(),
|
||||
format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
|
||||
),
|
||||
(else_.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(else_.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
(ty::FnPtr(sig), ty::FnDef(def_id, _))
|
||||
| (ty::FnDef(def_id, _), ty::FnPtr(sig)) => {
|
||||
if tcx.fn_sig(def_id).skip_binder().safety() < sig.safety() {
|
||||
diag.note(
|
||||
"unsafe functions cannot be coerced into safe function pointers",
|
||||
);
|
||||
}
|
||||
}
|
||||
(ty::Adt(_, _), ty::Adt(def, args))
|
||||
if let ObligationCauseCode::IfExpression(cause) = cause.code()
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
|
||||
&& let Some(then) = blk.expr
|
||||
&& def.is_box()
|
||||
&& let boxed_ty = args.type_at(0)
|
||||
&& let ty::Dynamic(t, _, _) = boxed_ty.kind()
|
||||
&& let Some(def_id) = t.principal_def_id()
|
||||
&& let mut impl_def_ids = vec![]
|
||||
&& let _ =
|
||||
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
|
||||
impl_def_ids.push(did)
|
||||
})
|
||||
&& let [_] = &impl_def_ids[..] =>
|
||||
{
|
||||
// We have divergent if/else arms where the expected value is a type that
|
||||
// implements the trait of the found boxed trait object.
|
||||
diag.multipart_suggestion(
|
||||
format!(
|
||||
"`{}` implements `{}` so you can box it to coerce to the trait \
|
||||
object `{}`",
|
||||
values.expected,
|
||||
tcx.item_name(def_id),
|
||||
values.found,
|
||||
),
|
||||
vec![
|
||||
(then.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(then.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
debug!(
|
||||
"note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
|
||||
values.expected,
|
||||
values.expected.kind(),
|
||||
values.found,
|
||||
values.found.kind(),
|
||||
);
|
||||
}
|
||||
TypeError::CyclicTy(ty) => {
|
||||
// Watch out for various cases of cyclic types and try to explain.
|
||||
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
|
||||
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",
|
||||
);
|
||||
}
|
||||
}
|
||||
TypeError::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 Diag<'_>,
|
||||
msg: impl Fn() -> String,
|
||||
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_args) = proj_ty.trait_ref_and_own_args(tcx);
|
||||
let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else {
|
||||
return false;
|
||||
};
|
||||
let Some(hir_generics) = item.generics() else {
|
||||
return false;
|
||||
};
|
||||
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
|
||||
// This will also work for `impl Trait`.
|
||||
let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
|
||||
return false;
|
||||
};
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let def_id = generics.type_param(param_ty, tcx).def_id;
|
||||
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_args,
|
||||
ty,
|
||||
&msg,
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (param_ty.index as usize) >= generics.parent_count {
|
||||
// The param comes from the current item, do not look at the parent. (#117209)
|
||||
return false;
|
||||
}
|
||||
// If associated item, look to constrain the params of the trait/impl.
|
||||
let hir_id = match item {
|
||||
hir::Node::ImplItem(item) => item.hir_id(),
|
||||
hir::Node::TraitItem(item) => item.hir_id(),
|
||||
_ => return false,
|
||||
};
|
||||
let parent = tcx.hir().get_parent_item(hir_id).def_id;
|
||||
self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
|
||||
}
|
||||
|
||||
/// 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 Diag<'_>,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
values: ExpectedFound<Ty<'tcx>>,
|
||||
body_owner_def_id: DefId,
|
||||
cause_code: &ObligationCauseCode<'_>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// Don't suggest constraining a projection to something containing itself
|
||||
if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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::CompareImplItem { .. });
|
||||
let assoc = tcx.associated_item(proj_ty.def_id);
|
||||
if 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`.
|
||||
} else {
|
||||
let point_at_assoc_fn = if callable_scope
|
||||
&& self.point_at_methods_that_satisfy_associated_type(
|
||||
diag,
|
||||
assoc.container_id(tcx),
|
||||
current_method_ident,
|
||||
proj_ty.def_id,
|
||||
values.expected,
|
||||
) {
|
||||
// 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".
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// 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 diag.code.is_some_and(|code| tcx.sess.teach(code)) {
|
||||
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 Diag<'_>,
|
||||
msg: impl Fn() -> String,
|
||||
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 {
|
||||
tcx.hir().expect_item(opaque_local_def_id).expect_opaque_ty()
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
|
||||
|
||||
self.constrain_generic_bound_associated_type_structured_suggestion(
|
||||
diag,
|
||||
trait_ref,
|
||||
opaque_hir_ty.bounds,
|
||||
assoc,
|
||||
assoc_args,
|
||||
ty,
|
||||
msg,
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn point_at_methods_that_satisfy_associated_type(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
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).instantiate_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 Diag<'_>,
|
||||
body_owner_def_id: DefId,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let Some(def_id) = body_owner_def_id.as_local() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// When `body_owner` is an `impl` or `trait` item, look in its associated types for
|
||||
// `expected` and point at it.
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let parent_id = tcx.hir().get_parent_item(hir_id);
|
||||
let item = tcx.hir_node_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 {
|
||||
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.defaultness(item.id.owner_id)
|
||||
{
|
||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||
if self.infcx.can_eq_shallow(param_env, assoc_ty, found) {
|
||||
diag.span_label(
|
||||
item.span,
|
||||
"associated type defaults can't be assumed inside the \
|
||||
trait defining them",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
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).instantiate_identity();
|
||||
if let hir::Defaultness::Default { has_value: true } =
|
||||
tcx.defaultness(item.id.owner_id)
|
||||
&& self.infcx.can_eq_shallow(param_env, assoc_ty, found)
|
||||
{
|
||||
diag.span_label(
|
||||
item.span,
|
||||
"associated type is `default` and may be overridden",
|
||||
);
|
||||
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 Diag<'_>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
bounds: hir::GenericBounds<'_>,
|
||||
assoc: ty::AssocItem,
|
||||
assoc_args: &[ty::GenericArg<'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
msg: impl Fn() -> String,
|
||||
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_args, 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 Diag<'_>,
|
||||
span: Span,
|
||||
assoc: ty::AssocItem,
|
||||
assoc_args: &[ty::GenericArg<'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
msg: impl Fn() -> String,
|
||||
) -> 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_args);
|
||||
(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::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| {
|
||||
cx.path_generic_args(|_| Ok(()), args)
|
||||
})
|
||||
.expect("could not write to `String`.")
|
||||
}
|
||||
}
|
1439
compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
Normal file
1439
compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,81 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::undo_log::NoUndo;
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct SubId(u32);
|
||||
impl ut::UnifyKey for SubId {
|
||||
type Value = ();
|
||||
#[inline]
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
fn from_index(i: u32) -> SubId {
|
||||
SubId(i)
|
||||
}
|
||||
fn tag() -> &'static str {
|
||||
"SubId"
|
||||
}
|
||||
}
|
||||
|
||||
/// When reporting ambiguity errors, we sometimes want to
|
||||
/// treat all inference vars which are subtypes of each
|
||||
/// others as if they are equal. For this case we compute
|
||||
/// the transitive closure of our subtype obligations here.
|
||||
///
|
||||
/// E.g. when encountering ambiguity errors, we want to suggest
|
||||
/// specifying some method argument or to add a type annotation
|
||||
/// to a local variable. Because subtyping cannot change the
|
||||
/// shape of a type, it's fine if the cause of the ambiguity error
|
||||
/// is only related to the suggested variable via subtyping.
|
||||
///
|
||||
/// Even for something like `let x = returns_arg(); x.method();` the
|
||||
/// type of `x` is only a supertype of the argument of `returns_arg`. We
|
||||
/// still want to suggest specifying the type of the argument.
|
||||
#[derive(Default)]
|
||||
pub struct SubRelations {
|
||||
map: FxHashMap<ty::TyVid, SubId>,
|
||||
table: ut::UnificationTableStorage<SubId>,
|
||||
}
|
||||
|
||||
impl SubRelations {
|
||||
fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
|
||||
let root_vid = infcx.root_var(vid);
|
||||
*self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
|
||||
}
|
||||
|
||||
pub fn add_constraints<'tcx>(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
|
||||
) {
|
||||
for p in obls {
|
||||
let (a, b) = match p.kind().skip_binder() {
|
||||
ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
|
||||
(a, b)
|
||||
}
|
||||
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
match (a.kind(), b.kind()) {
|
||||
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
|
||||
let a = self.get_id(infcx, a_vid);
|
||||
let b = self.get_id(infcx, b_vid);
|
||||
self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
|
||||
let a = self.get_id(infcx, a);
|
||||
let b = self.get_id(infcx, b);
|
||||
self.table.with_log(&mut NoUndo).unioned(a, b)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,898 @@
|
|||
use crate::error_reporting::infer::hir::Path;
|
||||
use core::ops::ControlFlow;
|
||||
use hir::def::CtorKind;
|
||||
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use hir::{LetStmt, QPath};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::MatchSource;
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
StatementAsExpression,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::errors::{
|
||||
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
||||
FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
|
||||
SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SuggestAsRefKind {
|
||||
Option,
|
||||
Result,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub(super) fn suggest_remove_semi_or_return_binding(
|
||||
&self,
|
||||
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)),
|
||||
]
|
||||
.into_iter()
|
||||
.find_map(|(id, ty)| {
|
||||
let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None };
|
||||
self.could_remove_semicolon(blk, ty)
|
||||
});
|
||||
match remove_semicolon {
|
||||
Some((sp, StatementAsExpression::NeedsBoxing)) => {
|
||||
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)) => {
|
||||
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_node(id)
|
||||
&& let Some(diag) = self.consider_returning_binding_diag(blk, ty)
|
||||
{
|
||||
ret = Some(diag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_tuple_pattern(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
||||
// some modifications due to that being in typeck and this being in infer.
|
||||
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
||||
if let ty::Adt(expected_adt, args) = exp_found.expected.kind() {
|
||||
let compatible_variants: Vec<_> = expected_adt
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|variant| {
|
||||
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
|
||||
})
|
||||
.filter_map(|variant| {
|
||||
let sole_field = &variant.single_field();
|
||||
let sole_field_ty = sole_field.ty(self.tcx, args);
|
||||
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
||||
let variant_path =
|
||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||
// FIXME #56861: DRYer prelude filtering
|
||||
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
||||
if let Some((_, path)) = path.split_once("::") {
|
||||
return Some(path.to_string());
|
||||
}
|
||||
}
|
||||
Some(variant_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
match &compatible_variants[..] {
|
||||
[] => {}
|
||||
[variant] => {
|
||||
let sugg = SuggestTuplePatternOne {
|
||||
variant: variant.to_owned(),
|
||||
span_low: cause.span.shrink_to_lo(),
|
||||
span_high: cause.span.shrink_to_hi(),
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
let sugg = SuggestTuplePatternMany {
|
||||
path: self.tcx.def_path_str(expected_adt.did()),
|
||||
cause_span: cause.span,
|
||||
compatible_variants,
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add `.await` when using futures:
|
||||
///
|
||||
/// ```compile_fail,E0308
|
||||
/// async fn make_u32() -> u32 {
|
||||
/// 22
|
||||
/// }
|
||||
///
|
||||
/// fn take_u32(x: u32) {}
|
||||
///
|
||||
/// async fn foo() {
|
||||
/// let x = make_u32();
|
||||
/// take_u32(x);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||
/// `.await` to the tail of the expression.
|
||||
pub(super) fn suggest_await_on_expect_found(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
|
||||
exp_span, exp_found.expected, exp_found.found,
|
||||
);
|
||||
|
||||
if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
|
||||
return;
|
||||
}
|
||||
|
||||
let subdiag = match (
|
||||
self.get_impl_future_output_ty(exp_found.expected),
|
||||
self.get_impl_future_output_ty(exp_found.found),
|
||||
) {
|
||||
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
||||
.code()
|
||||
{
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
Some(ConsiderAddingAwait::BothFuturesSugg {
|
||||
first: then_span.shrink_to_hi(),
|
||||
second: exp_span.shrink_to_hi(),
|
||||
})
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
prior_non_diverging_arms,
|
||||
..
|
||||
}) => {
|
||||
if let [.., arm_span] = &prior_non_diverging_arms[..] {
|
||||
Some(ConsiderAddingAwait::BothFuturesSugg {
|
||||
first: arm_span.shrink_to_hi(),
|
||||
second: exp_span.shrink_to_hi(),
|
||||
})
|
||||
} else {
|
||||
Some(ConsiderAddingAwait::BothFuturesHelp)
|
||||
}
|
||||
}
|
||||
_ => Some(ConsiderAddingAwait::BothFuturesHelp),
|
||||
},
|
||||
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
||||
// 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), origin_expr, .. } => {
|
||||
origin_expr.then_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);
|
||||
Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
ref prior_non_diverging_arms,
|
||||
..
|
||||
}) => Some({
|
||||
ConsiderAddingAwait::FutureSuggMultiple {
|
||||
spans: prior_non_diverging_arms
|
||||
.iter()
|
||||
.map(|arm| arm.shrink_to_hi())
|
||||
.collect(),
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(subdiag) = subdiag {
|
||||
diag.subdiagnostic(subdiag);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_accessing_field_where_appropriate(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
|
||||
cause, exp_found
|
||||
);
|
||||
if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
|
||||
if expected_def.is_enum() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((name, ty)) = expected_def
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
|
||||
.map(|field| (field.name, field.ty(self.tcx, expected_args)))
|
||||
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
|
||||
{
|
||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let suggestion = if expected_def.is_struct() {
|
||||
SuggestAccessingField::Safe { span, snippet, name, ty }
|
||||
} else if expected_def.is_union() {
|
||||
SuggestAccessingField::Unsafe { span, snippet, name, ty }
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
diag.subdiagnostic(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_turning_stmt_into_expr(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
let ty::error::ExpectedFound { expected, found } = exp_found;
|
||||
if !found.peel_refs().is_unit() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let node = self.tcx.hir_node(*hir_id);
|
||||
let mut blocks = vec![];
|
||||
if let hir::Node::Block(block) = node
|
||||
&& let Some(expr) = block.expr
|
||||
&& let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
|
||||
&& let Res::Local(local) = res
|
||||
&& let Node::LetStmt(LetStmt { init: Some(init), .. }) =
|
||||
self.tcx.parent_hir_node(*local)
|
||||
{
|
||||
fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
|
||||
match expr.kind {
|
||||
// `blk1` and `blk2` must be have the same types, it will be reported before reaching here
|
||||
hir::ExprKind::If(_, blk1, Some(blk2)) => {
|
||||
collect_blocks(blk1, blocks);
|
||||
collect_blocks(blk2, blocks);
|
||||
}
|
||||
hir::ExprKind::Match(_, arms, _) => {
|
||||
// all arms must have same types
|
||||
for arm in arms.iter() {
|
||||
collect_blocks(arm.body, blocks);
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Block(blk, _) => {
|
||||
blocks.push(blk);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
collect_blocks(init, &mut blocks);
|
||||
}
|
||||
|
||||
let expected_inner: Ty<'_> = expected.peel_refs();
|
||||
for block in blocks.iter() {
|
||||
self.consider_removing_semicolon(block, expected_inner, diag);
|
||||
}
|
||||
}
|
||||
|
||||
/// A common error is to add an extra semicolon:
|
||||
///
|
||||
/// ```compile_fail,E0308
|
||||
/// fn foo() -> usize {
|
||||
/// 22;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the final statement in a block is an
|
||||
/// expression with an explicit semicolon whose type is compatible
|
||||
/// with `expected_ty`. If so, it suggests removing the semicolon.
|
||||
pub fn consider_removing_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
diag: &mut Diag<'_>,
|
||||
) -> bool {
|
||||
if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
|
||||
if let StatementAsExpression::NeedsBoxing = boxed {
|
||||
diag.span_suggestion_verbose(
|
||||
span_semi,
|
||||
"consider removing this semicolon and boxing the expression",
|
||||
"",
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
diag.span_suggestion_short(
|
||||
span_semi,
|
||||
"remove this semicolon to return this value",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_function_pointers(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
|
||||
let ty::error::ExpectedFound { expected, found } = exp_found;
|
||||
let expected_inner = expected.peel_refs();
|
||||
let found_inner = found.peel_refs();
|
||||
if !expected_inner.is_fn() || !found_inner.is_fn() {
|
||||
return;
|
||||
}
|
||||
match (&expected_inner.kind(), &found_inner.kind()) {
|
||||
(ty::FnPtr(sig), ty::FnDef(did, args)) => {
|
||||
let expected_sig = &(self.normalize_fn_sig)(*sig);
|
||||
let found_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_args(*did, args);
|
||||
|
||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
||||
|| !sig.is_suggestable(self.tcx, true)
|
||||
|| self.tcx.intrinsic(*did).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let sugg = match (expected.is_ref(), found.is_ref()) {
|
||||
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
|
||||
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
|
||||
(true, true) => {
|
||||
diag.subdiagnostic(FnItemsAreDistinct);
|
||||
FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
|
||||
}
|
||||
(false, false) => {
|
||||
diag.subdiagnostic(FnItemsAreDistinct);
|
||||
FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
|
||||
}
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
|
||||
let expected_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
|
||||
let found_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
|
||||
|
||||
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
|
||||
diag.subdiagnostic(FnUniqTypes);
|
||||
}
|
||||
|
||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
||||
|| !found_sig.is_suggestable(self.tcx, true)
|
||||
|| !expected_sig.is_suggestable(self.tcx, true)
|
||||
|| self.tcx.intrinsic(*did1).is_some()
|
||||
|| self.tcx.intrinsic(*did2).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
|
||||
let sug = if found.is_ref() {
|
||||
FunctionPointerSuggestion::CastBothRef {
|
||||
span,
|
||||
fn_name,
|
||||
found_sig: *found_sig,
|
||||
expected_sig: *expected_sig,
|
||||
}
|
||||
} else {
|
||||
FunctionPointerSuggestion::CastBoth {
|
||||
span,
|
||||
fn_name,
|
||||
found_sig: *found_sig,
|
||||
expected_sig: *expected_sig,
|
||||
}
|
||||
};
|
||||
|
||||
diag.subdiagnostic(sug);
|
||||
}
|
||||
(ty::FnDef(did, args), ty::FnPtr(sig)) => {
|
||||
let expected_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
|
||||
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_args(*did, args);
|
||||
|
||||
let casting = if expected.is_ref() {
|
||||
format!("&({fn_name} as {found_sig})")
|
||||
} else {
|
||||
format!("{fn_name} as {found_sig}")
|
||||
};
|
||||
|
||||
diag.subdiagnostic(FnConsiderCasting { casting });
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn should_suggest_as_ref_kind(
|
||||
&self,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> Option<SuggestAsRefKind> {
|
||||
if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
|
||||
(expected.kind(), found.kind())
|
||||
{
|
||||
if let ty::Adt(found_def, found_args) = *found_ty.kind() {
|
||||
if exp_def == &found_def {
|
||||
let have_as_ref = &[
|
||||
(sym::Option, SuggestAsRefKind::Option),
|
||||
(sym::Result, SuggestAsRefKind::Result),
|
||||
];
|
||||
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||
}) {
|
||||
let mut show_suggestion = true;
|
||||
for (exp_ty, found_ty) in
|
||||
std::iter::zip(exp_args.types(), found_args.types())
|
||||
{
|
||||
match *exp_ty.kind() {
|
||||
ty::Ref(_, exp_ty, _) => {
|
||||
match (exp_ty.kind(), found_ty.kind()) {
|
||||
(_, ty::Param(_))
|
||||
| (_, ty::Infer(_))
|
||||
| (ty::Param(_), _)
|
||||
| (ty::Infer(_), _) => {}
|
||||
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
|
||||
_ => show_suggestion = false,
|
||||
};
|
||||
}
|
||||
ty::Param(_) | ty::Infer(_) => {}
|
||||
_ => show_suggestion = false,
|
||||
}
|
||||
}
|
||||
if show_suggestion {
|
||||
return Some(*msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
|
||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||
match self.should_suggest_as_ref_kind(expected, found) {
|
||||
Some(SuggestAsRefKind::Option) => Some(
|
||||
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||
`.as_ref()`",
|
||||
),
|
||||
Some(SuggestAsRefKind::Result) => Some(
|
||||
"you can convert from `&Result<T, E>` to \
|
||||
`Result<&T, &E>` using `.as_ref()`",
|
||||
),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
/// Try to find code with pattern `if Some(..) = expr`
|
||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||
pub(super) fn suggest_let_for_letchains(
|
||||
&self,
|
||||
cause: &ObligationCause<'_>,
|
||||
span: Span,
|
||||
) -> Option<TypeErrorAdditionalDiags> {
|
||||
/// Find the if expression with given span
|
||||
struct IfVisitor {
|
||||
pub found_if: bool,
|
||||
pub err_span: Span,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for IfVisitor {
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
|
||||
match ex.kind {
|
||||
hir::ExprKind::If(cond, _, _) => {
|
||||
self.found_if = true;
|
||||
walk_expr(self, cond)?;
|
||||
self.found_if = false;
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
_ => walk_expr(self, ex),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
|
||||
if let hir::StmtKind::Let(LetStmt {
|
||||
span,
|
||||
pat: hir::Pat { .. },
|
||||
ty: None,
|
||||
init: Some(_),
|
||||
..
|
||||
}) = &ex.kind
|
||||
&& self.found_if
|
||||
&& span.eq(&self.err_span)
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
walk_stmt(self, ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body| {
|
||||
IfVisitor { err_span: span, found_if: false }
|
||||
.visit_body(&body)
|
||||
.is_break()
|
||||
.then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
|
||||
})
|
||||
}
|
||||
|
||||
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime
|
||||
/// of the parameters to accept all lifetimes.
|
||||
pub(super) fn suggest_for_all_lifetime_closure(
|
||||
&self,
|
||||
span: Span,
|
||||
hir: hir::Node<'_>,
|
||||
exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
// 0. Extract fn_decl from hir
|
||||
let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
|
||||
..
|
||||
}) = hir
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let hir::Body { params, .. } = self.tcx.hir().body(*body);
|
||||
|
||||
// 1. Get the args of the closure.
|
||||
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
|
||||
let Some(expected) = exp_found.expected.args.get(1) else {
|
||||
return;
|
||||
};
|
||||
let Some(found) = exp_found.found.args.get(1) else {
|
||||
return;
|
||||
};
|
||||
let expected = expected.unpack();
|
||||
let found = found.unpack();
|
||||
// 3. Extract the tuple type from Fn trait and suggest the change.
|
||||
if let GenericArgKind::Type(expected) = expected
|
||||
&& let GenericArgKind::Type(found) = found
|
||||
&& let ty::Tuple(expected) = expected.kind()
|
||||
&& let ty::Tuple(found) = found.kind()
|
||||
&& expected.len() == found.len()
|
||||
{
|
||||
let mut suggestion = "|".to_string();
|
||||
let mut is_first = true;
|
||||
let mut has_suggestion = false;
|
||||
|
||||
for (((expected, found), param_hir), arg_hir) in
|
||||
expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
|
||||
{
|
||||
if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
suggestion += ", ";
|
||||
}
|
||||
|
||||
if let ty::Ref(expected_region, _, _) = expected.kind()
|
||||
&& let ty::Ref(found_region, _, _) = found.kind()
|
||||
&& expected_region.is_bound()
|
||||
&& !found_region.is_bound()
|
||||
&& let hir::TyKind::Infer = arg_hir.kind
|
||||
{
|
||||
// If the expected region is late bound, the found region is not, and users are asking compiler
|
||||
// to infer the type, we can suggest adding `: &_`.
|
||||
if param_hir.pat.span == param_hir.ty_span {
|
||||
// for `|x|`, `|_|`, `|x: impl Foo|`
|
||||
let Ok(pat) =
|
||||
self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
suggestion += &format!("{pat}: &_");
|
||||
} else {
|
||||
// for `|x: ty|`, `|_: ty|`
|
||||
let Ok(pat) =
|
||||
self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
suggestion += &format!("{pat}: &{ty}");
|
||||
}
|
||||
has_suggestion = true;
|
||||
} else {
|
||||
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
|
||||
return;
|
||||
};
|
||||
// Otherwise, keep it as-is.
|
||||
suggestion += &arg;
|
||||
}
|
||||
}
|
||||
suggestion += "|";
|
||||
|
||||
if has_suggestion {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"consider specifying the type of the closure parameters",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
|
||||
/// is enough to fix the error.
|
||||
pub fn could_remove_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<(Span, StatementAsExpression)> {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return None;
|
||||
}
|
||||
let last_stmt = blk.stmts.last()?;
|
||||
let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
|
||||
return None;
|
||||
};
|
||||
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
|
||||
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
||||
_ if last_expr_ty.references_error() => return None,
|
||||
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
||||
StatementAsExpression::CorrectType
|
||||
}
|
||||
(
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
|
||||
) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
|
||||
(
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }),
|
||||
) => {
|
||||
debug!(
|
||||
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
||||
last_def_id, last_bounds, exp_def_id, exp_bounds
|
||||
);
|
||||
|
||||
let last_local_id = last_def_id.as_local()?;
|
||||
let exp_local_id = exp_def_id.as_local()?;
|
||||
|
||||
match (
|
||||
&self.tcx.hir().expect_item(last_local_id).kind,
|
||||
&self.tcx.hir().expect_item(exp_local_id).kind,
|
||||
) {
|
||||
(
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
||||
) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
|
||||
left, right,
|
||||
) {
|
||||
(hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr))
|
||||
if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
||||
&& ml == mr =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
StatementAsExpression::NeedsBoxing
|
||||
}
|
||||
_ => StatementAsExpression::CorrectType,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let span = if last_stmt.span.from_expansion() {
|
||||
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
||||
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
||||
} else {
|
||||
self.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_extend_while_whitespace(last_expr.span)
|
||||
.shrink_to_hi()
|
||||
.with_hi(last_stmt.span.hi())
|
||||
};
|
||||
|
||||
Some((span, needs_box))
|
||||
}
|
||||
|
||||
/// Suggest returning a local binding with a compatible type if the block
|
||||
/// has no return expression.
|
||||
pub fn consider_returning_binding_diag(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<SuggestRemoveSemiOrReturnBinding> {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return None;
|
||||
}
|
||||
let mut shadowed = FxIndexSet::default();
|
||||
let mut candidate_idents = vec![];
|
||||
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
||||
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
||||
&& let Some(pat_ty) = self
|
||||
.typeck_results
|
||||
.as_ref()
|
||||
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
|
||||
{
|
||||
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
||||
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
||||
&& !(pat_ty, expected_ty).references_error()
|
||||
&& shadowed.insert(ident.name)
|
||||
{
|
||||
candidate_idents.push((*ident, pat_ty));
|
||||
}
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
for stmt in blk.stmts.iter().rev() {
|
||||
let hir::StmtKind::Let(local) = &stmt.kind else {
|
||||
continue;
|
||||
};
|
||||
local.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
match self.tcx.parent_hir_node(blk.hir_id) {
|
||||
hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
|
||||
hir::Node::Arm(hir::Arm { pat, .. }) => {
|
||||
pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
||||
| hir::Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_, body), ..
|
||||
})
|
||||
| hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
||||
..
|
||||
})
|
||||
| hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
||||
..
|
||||
}) => {
|
||||
for param in hir.body(*body).params {
|
||||
param.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
}
|
||||
hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::If(
|
||||
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
||||
then_block,
|
||||
_,
|
||||
),
|
||||
..
|
||||
}) if then_block.hir_id == *hir_id => {
|
||||
let_.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &candidate_idents[..] {
|
||||
[(ident, _ty)] => {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
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)
|
||||
{
|
||||
format!("\n{spacing}{ident}")
|
||||
} else {
|
||||
format!(" {ident}")
|
||||
};
|
||||
(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())
|
||||
{
|
||||
format!("\n{spacing} {ident}\n{spacing}")
|
||||
} else {
|
||||
format!(" {ident} ")
|
||||
};
|
||||
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
||||
(sm.span_extend_while_whitespace(left_span), sugg)
|
||||
};
|
||||
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<_>>();
|
||||
Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consider_returning_binding(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
) -> bool {
|
||||
let diag = self.consider_returning_binding_diag(blk, expected_ty);
|
||||
match diag {
|
||||
Some(diag) => {
|
||||
err.subdiagnostic(diag);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1,73 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_infer::traits::PredicateObligation;
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use crate::error_reporting::infer::sub_relations;
|
||||
|
||||
pub mod infer;
|
||||
pub mod traits;
|
||||
|
||||
/// A helper for building type related errors. The `typeck_results`
|
||||
/// field is only populated during an in-progress typeck.
|
||||
/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`.
|
||||
///
|
||||
/// You must only create this if you intend to actually emit an error (or
|
||||
/// perhaps a warning, though preferably not.) It provides a lot of utility
|
||||
/// methods which should not be used during the happy path.
|
||||
pub struct TypeErrCtxt<'a, 'tcx> {
|
||||
pub infcx: &'a InferCtxt<'tcx>,
|
||||
pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>,
|
||||
|
||||
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
|
||||
pub fallback_has_occurred: bool,
|
||||
|
||||
pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>,
|
||||
|
||||
pub autoderef_steps:
|
||||
Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
|
||||
}
|
||||
|
||||
#[extension(pub trait InferCtxtErrorExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Creates a `TypeErrCtxt` for emitting various inference errors.
|
||||
/// During typeck, use `FnCtxt::err_ctxt` instead.
|
||||
fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
|
||||
TypeErrCtxt {
|
||||
infcx: self,
|
||||
sub_relations: Default::default(),
|
||||
typeck_results: None,
|
||||
fallback_has_occurred: false,
|
||||
normalize_fn_sig: Box::new(|fn_sig| fn_sig),
|
||||
autoderef_steps: Box::new(|ty| {
|
||||
debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck");
|
||||
vec![(ty, vec![])]
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
pub fn dcx(&self) -> DiagCtxtHandle<'a> {
|
||||
self.infcx.dcx()
|
||||
}
|
||||
|
||||
/// This is just to avoid a potential footgun of accidentally
|
||||
/// dropping `typeck_results` by calling `InferCtxt::err_ctxt`
|
||||
#[deprecated(note = "you already have a `TypeErrCtxt`")]
|
||||
#[allow(unused)]
|
||||
pub fn err_ctxt(&self) -> ! {
|
||||
bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call");
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
|
||||
type Target = InferCtxt<'tcx>;
|
||||
fn deref(&self) -> &InferCtxt<'tcx> {
|
||||
self.infcx
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,21 +8,17 @@ use rustc_hir::def::{DefKind, Res};
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor as _;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_infer::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
|
||||
use rustc_infer::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
|
||||
use rustc_infer::traits::util::elaborate;
|
||||
use rustc_infer::traits::{
|
||||
Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
|
||||
};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
|
||||
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
|
||||
use crate::error_reporting::traits::{
|
||||
to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _,
|
||||
};
|
||||
use crate::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
|
||||
use crate::error_reporting::traits::{to_pretty_impl_header, FindExprBySpan};
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use crate::traits::ObligationCtxt;
|
||||
|
||||
|
@ -153,10 +149,12 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
|
|||
ambiguities
|
||||
}
|
||||
|
||||
#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
|
||||
pub(super) fn maybe_report_ambiguity(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
) -> ErrorGuaranteed {
|
||||
// Unable to successfully determine, probably means
|
||||
// insufficient type information, but could mean
|
||||
// ambiguous impls. The latter *ought* to be a
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
|
||||
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
|
||||
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
|
||||
use super::suggestions::get_explanation_based_on_obligation;
|
||||
use crate::error_reporting::infer::TyCategory;
|
||||
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
|
||||
use crate::error_reporting::traits::report_object_safety_error;
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::errors::{
|
||||
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
|
||||
};
|
||||
|
@ -24,10 +27,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_infer::error_reporting::infer::TyCategory;
|
||||
use rustc_infer::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_infer::infer::{InferOk, TypeTrace};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::traits::select::OverflowError;
|
||||
use rustc_middle::traits::SignatureMismatchData;
|
||||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||
|
@ -49,14 +49,11 @@ use super::{
|
|||
ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
|
||||
};
|
||||
|
||||
pub use rustc_infer::traits::error_reporting::*;
|
||||
|
||||
#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
/// The `root_obligation` parameter should be the `root_obligation` field
|
||||
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
|
||||
/// then it should be the same as `obligation`.
|
||||
fn report_selection_error(
|
||||
pub fn report_selection_error(
|
||||
&self,
|
||||
mut obligation: PredicateObligation<'tcx>,
|
||||
root_obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -682,9 +679,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool {
|
||||
pub(super) fn apply_do_not_recommend(
|
||||
&self,
|
||||
obligation: &mut PredicateObligation<'tcx>,
|
||||
) -> bool {
|
||||
let mut base_cause = obligation.cause.code().clone();
|
||||
let mut applied_do_not_recommend = false;
|
||||
loop {
|
||||
|
@ -1142,7 +1141,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
fn can_match_trait(
|
||||
&self,
|
||||
|
@ -1182,7 +1180,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
|
||||
// `error` occurring implies that `cond` occurs.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
|
||||
pub(super) fn error_implies(
|
||||
&self,
|
||||
cond: ty::Predicate<'tcx>,
|
||||
error: ty::Predicate<'tcx>,
|
||||
) -> bool {
|
||||
if cond == error {
|
||||
return true;
|
||||
}
|
||||
|
@ -1205,7 +1207,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn report_projection_error(
|
||||
pub(super) fn report_projection_error(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
error: &MismatchedProjectionTypes<'tcx>,
|
||||
|
@ -1455,7 +1457,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fuzzy_match_tys(
|
||||
pub fn fuzzy_match_tys(
|
||||
&self,
|
||||
mut a: Ty<'tcx>,
|
||||
mut b: Ty<'tcx>,
|
||||
|
@ -1535,7 +1537,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
|
||||
pub(super) fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
|
||||
match kind {
|
||||
hir::ClosureKind::Closure => "a closure",
|
||||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => "a coroutine",
|
||||
|
@ -1585,7 +1587,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_similar_impl_candidates(
|
||||
pub(super) fn find_similar_impl_candidates(
|
||||
&self,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) -> Vec<ImplCandidate<'tcx>> {
|
||||
|
@ -1615,7 +1617,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
candidates
|
||||
}
|
||||
|
||||
fn report_similar_impl_candidates(
|
||||
pub(super) fn report_similar_impl_candidates(
|
||||
&self,
|
||||
impl_candidates: &[ImplCandidate<'tcx>],
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
|
@ -1989,7 +1991,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// `trait_ref`.
|
||||
///
|
||||
/// For this to work, `new_self_ty` must have no escaping bound variables.
|
||||
fn mk_trait_obligation_with_new_self_ty(
|
||||
pub(super) fn mk_trait_obligation_with_new_self_ty(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
|
||||
|
@ -2041,7 +2043,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
fn note_obligation_cause(&self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>) {
|
||||
pub fn note_obligation_cause(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
) {
|
||||
// First, attempt to add note to this error with an async-await-specific
|
||||
// message, and fall back to regular note otherwise.
|
||||
if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
|
||||
|
@ -2067,7 +2073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_recursive_obligation(
|
||||
pub(super) fn is_recursive_obligation(
|
||||
&self,
|
||||
obligated_types: &mut Vec<Ty<'tcx>>,
|
||||
cause_code: &ObligationCauseCode<'tcx>,
|
||||
|
|
|
@ -5,28 +5,24 @@ pub mod on_unimplemented;
|
|||
mod overflow;
|
||||
pub mod suggestions;
|
||||
|
||||
use std::iter;
|
||||
use std::{fmt, iter};
|
||||
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_errors::{struct_span_code_err, Applicability, Diag, MultiSpan, E0038, E0276};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_infer::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_infer::traits::{
|
||||
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
|
||||
ObjectSafetyViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
|
||||
SelectionError,
|
||||
};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
|
||||
|
||||
use ambiguity::TypeErrCtxtAmbiguityExt as _;
|
||||
use fulfillment_errors::TypeErrCtxtExt as _;
|
||||
use suggestions::TypeErrCtxtExt as _;
|
||||
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
|
||||
|
||||
pub use self::fulfillment_errors::*;
|
||||
pub use self::infer_ctxt_ext::*;
|
||||
pub use self::overflow::*;
|
||||
|
||||
|
@ -137,9 +133,8 @@ pub enum DefIdOrName {
|
|||
Name(&'static str),
|
||||
}
|
||||
|
||||
#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
fn report_fulfillment_errors(
|
||||
pub fn report_fulfillment_errors(
|
||||
&self,
|
||||
mut errors: Vec<FulfillmentError<'tcx>>,
|
||||
) -> ErrorGuaranteed {
|
||||
|
@ -383,3 +378,194 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
|
|||
w.push(';');
|
||||
Some(w)
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
pub fn report_extra_impl_obligation(
|
||||
&self,
|
||||
error_span: Span,
|
||||
impl_item_def_id: LocalDefId,
|
||||
trait_item_def_id: DefId,
|
||||
requirement: &dyn fmt::Display,
|
||||
) -> Diag<'a> {
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
error_span,
|
||||
E0276,
|
||||
"impl has stricter requirements than trait"
|
||||
);
|
||||
|
||||
if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
|
||||
if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
|
||||
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
|
||||
err.span_label(span, format!("definition of `{item_name}` from trait"));
|
||||
}
|
||||
}
|
||||
|
||||
err.span_label(error_span, format!("impl has extra requirement {requirement}"));
|
||||
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_object_safety_error<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
span: Span,
|
||||
hir_id: Option<hir::HirId>,
|
||||
trait_def_id: DefId,
|
||||
violations: &[ObjectSafetyViolation],
|
||||
) -> Diag<'tcx> {
|
||||
let trait_str = tcx.def_path_str(trait_def_id);
|
||||
let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
|
||||
hir::Node::Item(item) => Some(item.ident.span),
|
||||
_ => None,
|
||||
});
|
||||
let mut err = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
span,
|
||||
E0038,
|
||||
"the trait `{}` cannot be made into an object",
|
||||
trait_str
|
||||
);
|
||||
err.span_label(span, format!("`{trait_str}` cannot be made into an object"));
|
||||
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let hir::Node::Ty(ty) = tcx.hir_node(hir_id)
|
||||
&& let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
|
||||
{
|
||||
let mut hir_id = hir_id;
|
||||
while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
|
||||
hir_id = ty.hir_id;
|
||||
}
|
||||
if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
|
||||
// Do not suggest `impl Trait` when dealing with things like super-traits.
|
||||
err.span_suggestion_verbose(
|
||||
ty.span.until(trait_ref.span),
|
||||
"consider using an opaque type instead",
|
||||
"impl ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut reported_violations = FxIndexSet::default();
|
||||
let mut multi_span = vec![];
|
||||
let mut messages = vec![];
|
||||
for violation in violations {
|
||||
if let ObjectSafetyViolation::SizedSelf(sp) = &violation
|
||||
&& !sp.is_empty()
|
||||
{
|
||||
// Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
|
||||
// with a `Span`.
|
||||
reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into()));
|
||||
}
|
||||
if reported_violations.insert(violation.clone()) {
|
||||
let spans = violation.spans();
|
||||
let msg = if trait_span.is_none() || spans.is_empty() {
|
||||
format!("the trait cannot be made into an object because {}", violation.error_msg())
|
||||
} else {
|
||||
format!("...because {}", violation.error_msg())
|
||||
};
|
||||
if spans.is_empty() {
|
||||
err.note(msg);
|
||||
} else {
|
||||
for span in spans {
|
||||
multi_span.push(span);
|
||||
messages.push(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let has_multi_span = !multi_span.is_empty();
|
||||
let mut note_span = MultiSpan::from_spans(multi_span.clone());
|
||||
if let (Some(trait_span), true) = (trait_span, has_multi_span) {
|
||||
note_span.push_span_label(trait_span, "this trait cannot be made into an object...");
|
||||
}
|
||||
for (span, msg) in iter::zip(multi_span, messages) {
|
||||
note_span.push_span_label(span, msg);
|
||||
}
|
||||
err.span_note(
|
||||
note_span,
|
||||
"for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
|
||||
to be resolvable dynamically; for more information visit \
|
||||
<https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
|
||||
);
|
||||
|
||||
// Only provide the help if its a local trait, otherwise it's not actionable.
|
||||
if trait_span.is_some() {
|
||||
let mut reported_violations: Vec<_> = reported_violations.into_iter().collect();
|
||||
reported_violations.sort();
|
||||
|
||||
let mut potential_solutions: Vec<_> =
|
||||
reported_violations.into_iter().map(|violation| violation.solution()).collect();
|
||||
potential_solutions.sort();
|
||||
// Allows us to skip suggesting that the same item should be moved to another trait multiple times.
|
||||
potential_solutions.dedup();
|
||||
for solution in potential_solutions {
|
||||
solution.add_to(&mut err);
|
||||
}
|
||||
}
|
||||
|
||||
let impls_of = tcx.trait_impls_of(trait_def_id);
|
||||
let impls = if impls_of.blanket_impls().is_empty() {
|
||||
impls_of
|
||||
.non_blanket_impls()
|
||||
.values()
|
||||
.flatten()
|
||||
.filter(|def_id| {
|
||||
!matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let externally_visible = if !impls.is_empty()
|
||||
&& let Some(def_id) = trait_def_id.as_local()
|
||||
// We may be executing this during typeck, which would result in cycle
|
||||
// if we used effective_visibilities query, which looks into opaque types
|
||||
// (and therefore calls typeck).
|
||||
&& tcx.resolutions(()).effective_visibilities.is_exported(def_id)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
match &impls[..] {
|
||||
[] => {}
|
||||
_ if impls.len() > 9 => {}
|
||||
[only] if externally_visible => {
|
||||
err.help(with_no_trimmed_paths!(format!(
|
||||
"only type `{}` is seen to implement the trait in this crate, consider using it \
|
||||
directly instead",
|
||||
tcx.type_of(*only).instantiate_identity(),
|
||||
)));
|
||||
}
|
||||
[only] => {
|
||||
err.help(with_no_trimmed_paths!(format!(
|
||||
"only type `{}` implements the trait, consider using it directly instead",
|
||||
tcx.type_of(*only).instantiate_identity(),
|
||||
)));
|
||||
}
|
||||
impls => {
|
||||
let types = impls
|
||||
.iter()
|
||||
.map(|t| {
|
||||
with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
err.help(format!(
|
||||
"the following types implement the trait, consider defining an enum where each \
|
||||
variant holds one of these types, implementing `{}` for this new enum and using \
|
||||
it instead:\n{}",
|
||||
trait_str,
|
||||
types.join("\n"),
|
||||
));
|
||||
}
|
||||
}
|
||||
if externally_visible {
|
||||
err.note(format!(
|
||||
"`{trait_str}` can be implemented in other crates; if you want to support your users \
|
||||
passing their own types here, you can't refer to a specific type",
|
||||
));
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{ObligationCauseCode, PredicateObligation};
|
||||
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::errors::{
|
||||
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
|
||||
};
|
||||
|
@ -13,8 +13,7 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_macros::{extension, LintDiagnostic};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
|
@ -41,7 +40,6 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
|
|||
sym::Trait,
|
||||
];
|
||||
|
||||
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
fn impl_similar_to(
|
||||
&self,
|
||||
|
@ -109,7 +107,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_unimplemented_note(
|
||||
pub fn on_unimplemented_note(
|
||||
&self,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
|
|
@ -5,17 +5,14 @@ use rustc_errors::{
|
|||
};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_infer::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_infer::traits::{Obligation, PredicateObligation};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::print::{FmtPrinter, Print};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::Span;
|
||||
use rustc_type_ir::Upcast;
|
||||
|
||||
use super::InferCtxtPrivExt;
|
||||
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt;
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
|
||||
pub enum OverflowCause<'tcx> {
|
||||
DeeplyNormalize(ty::AliasTerm<'tcx>),
|
||||
|
@ -38,7 +35,6 @@ pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
|
|||
));
|
||||
}
|
||||
|
||||
#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
/// Reports that an overflow has occurred and halts compilation. We
|
||||
/// halt compilation unconditionally because it is important that
|
||||
|
@ -46,7 +42,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// whose result could not be truly determined and thus we can't say
|
||||
/// if the program type checks or not -- and they are unusual
|
||||
/// occurrences in any case.
|
||||
fn report_overflow_error(
|
||||
pub fn report_overflow_error(
|
||||
&self,
|
||||
cause: OverflowCause<'tcx>,
|
||||
span: Span,
|
||||
|
@ -59,7 +55,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
FatalError.raise();
|
||||
}
|
||||
|
||||
fn build_overflow_error(
|
||||
pub fn build_overflow_error(
|
||||
&self,
|
||||
cause: OverflowCause<'tcx>,
|
||||
span: Span,
|
||||
|
@ -132,7 +128,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// whose result could not be truly determined and thus we can't say
|
||||
/// if the program type checks or not -- and they are unusual
|
||||
/// occurrences in any case.
|
||||
fn report_overflow_obligation<T>(
|
||||
pub fn report_overflow_obligation<T>(
|
||||
&self,
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
suggest_increasing_limit: bool,
|
||||
|
@ -165,7 +161,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// that we can give a more helpful error message (and, in particular,
|
||||
/// we do not suggest increasing the overflow limit, which is not
|
||||
/// going to help).
|
||||
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
|
||||
pub fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
|
||||
let cycle = self.resolve_vars_if_possible(cycle.to_owned());
|
||||
assert!(!cycle.is_empty());
|
||||
|
||||
|
@ -179,7 +175,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
fn report_overflow_no_abort(
|
||||
pub fn report_overflow_no_abort(
|
||||
&self,
|
||||
obligation: PredicateObligation<'tcx>,
|
||||
suggest_increasing_limit: bool,
|
||||
|
|
|
@ -5,11 +5,10 @@ use super::{
|
|||
PredicateObligation,
|
||||
};
|
||||
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::errors;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt};
|
||||
|
||||
use hir::def::CtorOf;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{
|
||||
|
@ -17,15 +16,15 @@ use rustc_errors::{
|
|||
Style, SuggestionStyle,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::CtorOf;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::is_range_literal;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
|
||||
use rustc_infer::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::hir::map;
|
||||
use rustc_middle::traits::IsConstable;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
|
@ -44,7 +43,6 @@ use std::assert_matches::debug_assert_matches;
|
|||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
|
||||
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
|
||||
use crate::infer::InferCtxtExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_middle::ty::print::{
|
||||
|
@ -241,9 +239,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
|
|||
}
|
||||
}
|
||||
|
||||
#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
fn suggest_restricting_param_bound(
|
||||
pub fn suggest_restricting_param_bound(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
|
@ -453,7 +450,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// When after several dereferencing, the reference satisfies the trait
|
||||
/// bound. This function provides dereference suggestion for this
|
||||
/// specific situation.
|
||||
fn suggest_dereferences(
|
||||
pub(super) fn suggest_dereferences(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -782,7 +779,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// We tried to apply the bound to an `fn` or closure. Check whether calling it would
|
||||
/// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling
|
||||
/// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.
|
||||
fn suggest_fn_call(
|
||||
pub(super) fn suggest_fn_call(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -898,7 +895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
true
|
||||
}
|
||||
|
||||
fn check_for_binding_assigned_block_without_tail_expression(
|
||||
pub(super) fn check_for_binding_assigned_block_without_tail_expression(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -974,7 +971,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_add_clone_to_arg(
|
||||
pub(super) fn suggest_add_clone_to_arg(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -1074,7 +1071,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// Extracts information about a callable type for diagnostics. This is a
|
||||
/// heuristic -- it doesn't necessarily mean that a type is always callable,
|
||||
/// because the callable type must also be well-formed to be called.
|
||||
fn extract_callable_info(
|
||||
pub fn extract_callable_info(
|
||||
&self,
|
||||
body_id: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -1200,7 +1197,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
|
||||
}
|
||||
|
||||
fn suggest_add_reference_to_arg(
|
||||
pub(super) fn suggest_add_reference_to_arg(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -1422,7 +1419,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Suggest borrowing the type
|
||||
fn suggest_borrowing_for_object_cast(
|
||||
pub(super) fn suggest_borrowing_for_object_cast(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -1457,7 +1454,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
|
||||
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
|
||||
/// suggest removing these references until we reach a type that implements the trait.
|
||||
fn suggest_remove_reference(
|
||||
pub(super) fn suggest_remove_reference(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -1578,7 +1575,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>) {
|
||||
pub(super) fn suggest_remove_await(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
let hir = self.tcx.hir();
|
||||
if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives()
|
||||
&& let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
|
||||
|
@ -1644,7 +1645,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
|
||||
/// Check if the trait bound is implemented for a different mutability and note it in the
|
||||
/// final error.
|
||||
fn suggest_change_mut(
|
||||
pub(super) fn suggest_change_mut(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -1720,7 +1721,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_semicolon_removal(
|
||||
pub(super) fn suggest_semicolon_removal(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -1762,7 +1763,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
|
||||
pub(super) fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
|
||||
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) =
|
||||
self.tcx.hir_node_by_def_id(obligation.cause.body_id)
|
||||
else {
|
||||
|
@ -1775,7 +1776,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if
|
||||
/// applicable and signal that the error has been expanded appropriately and needs to be
|
||||
/// emitted.
|
||||
fn suggest_impl_trait(
|
||||
pub(super) fn suggest_impl_trait(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -1865,7 +1866,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
true
|
||||
}
|
||||
|
||||
fn point_at_returns_when_relevant(
|
||||
pub(super) fn point_at_returns_when_relevant(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -1897,7 +1898,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn report_closure_arg_mismatch(
|
||||
pub(super) fn report_closure_arg_mismatch(
|
||||
&self,
|
||||
span: Span,
|
||||
found_span: Option<Span>,
|
||||
|
@ -2176,7 +2177,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_fully_qualified_path(
|
||||
pub(super) fn suggest_fully_qualified_path(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
item_def_id: DefId,
|
||||
|
@ -2243,7 +2244,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
///
|
||||
/// Returns `true` if an async-await specific note was added to the diagnostic.
|
||||
#[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))]
|
||||
fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
|
||||
pub(super) fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
|
||||
&self,
|
||||
err: &mut Diag<'_, G>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -2712,7 +2713,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
fn note_obligation_cause_code<G: EmissionGuarantee, T>(
|
||||
pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
|
||||
&self,
|
||||
body_id: LocalDefId,
|
||||
err: &mut Diag<'_, G>,
|
||||
|
@ -3554,7 +3555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
#[instrument(
|
||||
level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty())
|
||||
)]
|
||||
fn suggest_await_before_try(
|
||||
pub(super) fn suggest_await_before_try(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -3611,7 +3612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_floating_point_literal(
|
||||
pub(super) fn suggest_floating_point_literal(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -3635,7 +3636,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_derive(
|
||||
pub fn suggest_derive(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -3701,7 +3702,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_dereferencing_index(
|
||||
pub(super) fn suggest_dereferencing_index(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -4323,7 +4324,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
/// If the type that failed selection is an array or a reference to an array,
|
||||
/// but the trait is implemented for slices, suggest that the user converts
|
||||
/// the array into a slice.
|
||||
fn suggest_convert_to_slice(
|
||||
pub(super) fn suggest_convert_to_slice(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
@ -4395,7 +4396,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn explain_hrtb_projection(
|
||||
pub(super) fn explain_hrtb_projection(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
pred: ty::PolyTraitPredicate<'tcx>,
|
||||
|
@ -4461,7 +4462,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_desugaring_async_fn_in_trait(
|
||||
pub(super) fn suggest_desugaring_async_fn_in_trait(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
|
@ -4545,7 +4546,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> {
|
||||
pub fn ty_kind_suggestion(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<String> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let implements_default = |ty| {
|
||||
let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
|
||||
|
@ -4607,7 +4612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
fn suggest_add_result_as_return_type(
|
||||
pub(super) fn suggest_add_result_as_return_type(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
|
@ -4648,7 +4653,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn suggest_unsized_bound_if_applicable(
|
||||
pub(super) fn suggest_unsized_bound_if_applicable(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
|
|
File diff suppressed because it is too large
Load diff
183
compiler/rustc_trait_selection/src/errors/note_and_explain.rs
Normal file
183
compiler/rustc_trait_selection/src/errors/note_and_explain.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use crate::error_reporting::infer::nice_region_error::find_anon_type;
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::{symbol::kw, Span};
|
||||
|
||||
struct DescriptionCtx<'a> {
|
||||
span: Option<Span>,
|
||||
kind: &'a str,
|
||||
arg: String,
|
||||
}
|
||||
|
||||
impl<'a> DescriptionCtx<'a> {
|
||||
fn new<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
region: ty::Region<'tcx>,
|
||||
alt_span: Option<Span>,
|
||||
) -> Option<Self> {
|
||||
let (span, kind, arg) = match *region {
|
||||
ty::ReEarlyParam(br) => {
|
||||
let scope = tcx
|
||||
.parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
|
||||
.expect_local();
|
||||
let span = if let Some(param) =
|
||||
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
|
||||
{
|
||||
param.span
|
||||
} else {
|
||||
tcx.def_span(scope)
|
||||
};
|
||||
if br.has_name() {
|
||||
(Some(span), "as_defined", br.name.to_string())
|
||||
} else {
|
||||
(Some(span), "as_defined_anon", String::new())
|
||||
}
|
||||
}
|
||||
ty::ReLateParam(ref fr) => {
|
||||
if !fr.bound_region.is_named()
|
||||
&& let Some((ty, _)) =
|
||||
find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
|
||||
{
|
||||
(Some(ty.span), "defined_here", String::new())
|
||||
} else {
|
||||
let scope = fr.scope.expect_local();
|
||||
match fr.bound_region {
|
||||
ty::BoundRegionKind::BrNamed(_, name) => {
|
||||
let span = if let Some(param) = tcx
|
||||
.hir()
|
||||
.get_generics(scope)
|
||||
.and_then(|generics| generics.get_named(name))
|
||||
{
|
||||
param.span
|
||||
} else {
|
||||
tcx.def_span(scope)
|
||||
};
|
||||
if name == kw::UnderscoreLifetime {
|
||||
(Some(span), "as_defined_anon", String::new())
|
||||
} else {
|
||||
(Some(span), "as_defined", name.to_string())
|
||||
}
|
||||
}
|
||||
ty::BrAnon => {
|
||||
let span = Some(tcx.def_span(scope));
|
||||
(span, "defined_here", String::new())
|
||||
}
|
||||
_ => (Some(tcx.def_span(scope)), "defined_here_reg", region.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::ReStatic => (alt_span, "restatic", String::new()),
|
||||
|
||||
ty::RePlaceholder(_) | ty::ReError(_) => return None,
|
||||
|
||||
ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
|
||||
bug!("unexpected region for DescriptionCtx: {:?}", region);
|
||||
}
|
||||
};
|
||||
Some(DescriptionCtx { span, kind, arg })
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PrefixKind {
|
||||
Empty,
|
||||
RefValidFor,
|
||||
ContentValidFor,
|
||||
TypeObjValidFor,
|
||||
SourcePointerValidFor,
|
||||
TypeSatisfy,
|
||||
TypeOutlive,
|
||||
LfParamInstantiatedWith,
|
||||
LfParamMustOutlive,
|
||||
LfInstantiatedWith,
|
||||
LfMustOutlive,
|
||||
PointerValidFor,
|
||||
DataValidFor,
|
||||
}
|
||||
|
||||
pub enum SuffixKind {
|
||||
Empty,
|
||||
Continues,
|
||||
ReqByBinding,
|
||||
}
|
||||
|
||||
impl IntoDiagArg for PrefixKind {
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
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::DiagArgValue::Str(kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagArg for SuffixKind {
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
let kind = match self {
|
||||
Self::Empty => "empty",
|
||||
Self::Continues => "continues",
|
||||
Self::ReqByBinding => "req_by_binding",
|
||||
}
|
||||
.into();
|
||||
rustc_errors::DiagArgValue::Str(kind)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegionExplanation<'a> {
|
||||
desc: DescriptionCtx<'a>,
|
||||
prefix: PrefixKind,
|
||||
suffix: SuffixKind,
|
||||
}
|
||||
|
||||
impl RegionExplanation<'_> {
|
||||
pub fn new<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
region: ty::Region<'tcx>,
|
||||
alt_span: Option<Span>,
|
||||
prefix: PrefixKind,
|
||||
suffix: SuffixKind,
|
||||
) -> Option<Self> {
|
||||
Some(Self {
|
||||
desc: DescriptionCtx::new(tcx, generic_param_scope, region, alt_span)?,
|
||||
prefix,
|
||||
suffix,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Subdiagnostic for RegionExplanation<'_> {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
f: &F,
|
||||
) {
|
||||
diag.arg("pref_kind", self.prefix);
|
||||
diag.arg("suff_kind", self.suffix);
|
||||
diag.arg("desc_kind", self.desc.kind);
|
||||
diag.arg("desc_arg", self.desc.arg);
|
||||
|
||||
let msg = f(diag, fluent::trait_selection_region_explanation.into());
|
||||
if let Some(span) = self.desc.span {
|
||||
diag.span_note(span, msg);
|
||||
} else {
|
||||
diag.note(msg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,11 +22,14 @@
|
|||
#![feature(control_flow_enum)]
|
||||
#![feature(extract_if)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(yeet_expr)]
|
||||
#![recursion_limit = "512"] // For rustdoc
|
||||
// tidy-alphabetical-end
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt};
|
||||
use crate::error_reporting::traits::OverflowCause;
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::Debug;
|
|||
|
||||
use super::{FromSolverError, TraitEngine};
|
||||
use super::{FulfillmentContext, ScrubbedTraitError};
|
||||
use crate::error_reporting::traits::TypeErrCtxtExt;
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::regions::InferCtxtRegionExt;
|
||||
use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
|
||||
use crate::solve::NextSolverError;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::infer::{InferCtxt, TyOrConstInferVar};
|
||||
use crate::traits::normalize::normalize_with_depth_to;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
|
@ -25,6 +24,7 @@ use super::Unimplemented;
|
|||
use super::{const_evaluatable, ScrubbedTraitError};
|
||||
use super::{FulfillmentError, FulfillmentErrorCode};
|
||||
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::traits::project::PolyProjectionObligation;
|
||||
use crate::traits::project::ProjectionCacheKeyExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
|
|
@ -22,7 +22,7 @@ mod util;
|
|||
pub mod vtable;
|
||||
pub mod wf;
|
||||
|
||||
use crate::error_reporting::traits::TypeErrCtxtExt as _;
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::infer::outlives::env::OutlivesEnvironment;
|
||||
use crate::infer::{InferCtxt, TyCtxtInferExt};
|
||||
use crate::regions::InferCtxtRegionExt;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use super::SelectionContext;
|
||||
use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
|
||||
use crate::error_reporting::traits::OverflowCause;
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::solve::NextSolverError;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_infer::infer::at::At;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! `normalize_canonicalized_projection_ty` query when it encounters projections.
|
||||
|
||||
use crate::error_reporting::traits::OverflowCause;
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::infer::at::At;
|
||||
use crate::infer::canonical::OriginalQueryValues;
|
||||
use crate::infer::{InferCtxt, InferOk};
|
||||
|
|
|
@ -18,7 +18,7 @@ use super::{
|
|||
TraitQueryMode,
|
||||
};
|
||||
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
|
||||
use crate::solve::InferCtxtSelectExt as _;
|
||||
use crate::traits::normalize::normalize_with_depth;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue