Move trait selection error reporting to its own top-level module
This commit is contained in:
@@ -0,0 +1 @@
|
||||
pub mod traits;
|
||||
@@ -0,0 +1,136 @@
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
|
||||
use rustc_infer::traits::util::elaborate;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use crate::traits::ObligationCtxt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CandidateSource {
|
||||
DefId(DefId),
|
||||
ParamEnv(Span),
|
||||
}
|
||||
|
||||
pub fn compute_applicable_impls_for_diagnostics<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
) -> Vec<CandidateSource> {
|
||||
let tcx = infcx.tcx;
|
||||
let param_env = obligation.param_env;
|
||||
|
||||
let predicate_polarity = obligation.predicate.skip_binder().polarity;
|
||||
|
||||
let impl_may_apply = |impl_def_id| {
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
|
||||
let obligation_trait_ref = ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
placeholder_obligation.trait_ref,
|
||||
);
|
||||
|
||||
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
|
||||
let impl_trait_ref =
|
||||
tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
|
||||
let impl_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
|
||||
|
||||
if let Err(_) =
|
||||
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
|
||||
let impl_polarity = impl_trait_header.polarity;
|
||||
|
||||
match (impl_polarity, predicate_polarity) {
|
||||
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
|
||||
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
let obligations = tcx
|
||||
.predicates_of(impl_def_id)
|
||||
.instantiate(tcx, impl_args)
|
||||
.into_iter()
|
||||
.map(|(predicate, _)| {
|
||||
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
|
||||
})
|
||||
// Kinda hacky, but let's just throw away obligations that overflow.
|
||||
// This may reduce the accuracy of this check (if the obligation guides
|
||||
// inference or it actually resulted in error after others are processed)
|
||||
// ... but this is diagnostics code.
|
||||
.filter(|obligation| {
|
||||
infcx.next_trait_solver() || infcx.evaluate_obligation(obligation).is_ok()
|
||||
});
|
||||
ocx.register_obligations(obligations);
|
||||
|
||||
ocx.select_where_possible().is_empty()
|
||||
})
|
||||
};
|
||||
|
||||
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
|
||||
let obligation_trait_ref = ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
placeholder_obligation.trait_ref,
|
||||
);
|
||||
|
||||
let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
|
||||
DUMMY_SP,
|
||||
BoundRegionConversionTime::HigherRankedType,
|
||||
poly_trait_predicate,
|
||||
);
|
||||
let param_env_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
|
||||
|
||||
if let Err(_) = ocx.eq(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
obligation_trait_ref,
|
||||
param_env_trait_ref,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ocx.select_where_possible().is_empty()
|
||||
})
|
||||
};
|
||||
|
||||
let mut ambiguities = Vec::new();
|
||||
|
||||
tcx.for_each_relevant_impl(
|
||||
obligation.predicate.def_id(),
|
||||
obligation.predicate.skip_binder().trait_ref.self_ty(),
|
||||
|impl_def_id| {
|
||||
if infcx.probe(|_| impl_may_apply(impl_def_id)) {
|
||||
ambiguities.push(CandidateSource::DefId(impl_def_id))
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let predicates =
|
||||
tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
|
||||
for (pred, span) in elaborate(tcx, predicates.into_iter()) {
|
||||
let kind = pred.kind();
|
||||
if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder()
|
||||
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
|
||||
{
|
||||
if kind.rebind(trait_pred.trait_ref)
|
||||
== ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id()))
|
||||
{
|
||||
ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id())))
|
||||
} else {
|
||||
ambiguities.push(CandidateSource::ParamEnv(span))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ambiguities
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
|
||||
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Node;
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
use super::ArgKind;
|
||||
|
||||
pub use rustc_infer::traits::error_reporting::*;
|
||||
|
||||
#[extension(pub trait InferCtxtExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Given some node representing a fn-like thing in the HIR map,
|
||||
/// returns a span and `ArgKind` information that describes the
|
||||
/// arguments it expects. This can be supplied to
|
||||
/// `report_arg_count_mismatch`.
|
||||
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
let hir = self.tcx.hir();
|
||||
Some(match node {
|
||||
Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
|
||||
..
|
||||
}) => (
|
||||
fn_decl_span,
|
||||
fn_arg_span,
|
||||
hir.body(body)
|
||||
.params
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat
|
||||
{
|
||||
Some(ArgKind::Tuple(
|
||||
Some(span),
|
||||
args.iter()
|
||||
.map(|pat| {
|
||||
sm.span_to_snippet(pat.span)
|
||||
.ok()
|
||||
.map(|snippet| (snippet, "_".to_owned()))
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?,
|
||||
))
|
||||
} else {
|
||||
let name = sm.span_to_snippet(arg.pat.span).ok()?;
|
||||
Some(ArgKind::Arg(name, "_".to_owned()))
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<ArgKind>>>()?,
|
||||
),
|
||||
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
|
||||
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
|
||||
| Node::TraitItem(&hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(ref sig, _), ..
|
||||
}) => (
|
||||
sig.span,
|
||||
None,
|
||||
sig.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| match arg.kind {
|
||||
hir::TyKind::Tup(tys) => ArgKind::Tuple(
|
||||
Some(arg.span),
|
||||
vec![("_".to_owned(), "_".to_owned()); tys.len()],
|
||||
),
|
||||
_ => ArgKind::empty(),
|
||||
})
|
||||
.collect::<Vec<ArgKind>>(),
|
||||
),
|
||||
Node::Ctor(variant_data) => {
|
||||
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
|
||||
(span, None, vec![ArgKind::empty(); variant_data.fields().len()])
|
||||
}
|
||||
_ => panic!("non-FnLike node found: {node:?}"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Reports an error when the number of arguments needed by a
|
||||
/// trait match doesn't match the number that the expression
|
||||
/// provides.
|
||||
fn report_arg_count_mismatch(
|
||||
&self,
|
||||
span: Span,
|
||||
found_span: Option<Span>,
|
||||
expected_args: Vec<ArgKind>,
|
||||
found_args: Vec<ArgKind>,
|
||||
is_closure: bool,
|
||||
closure_arg_span: Option<Span>,
|
||||
) -> Diag<'_> {
|
||||
let kind = if is_closure { "closure" } else { "function" };
|
||||
|
||||
let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
|
||||
let arg_length = arguments.len();
|
||||
let distinct = matches!(other, &[ArgKind::Tuple(..)]);
|
||||
match (arg_length, arguments.get(0)) {
|
||||
(1, Some(ArgKind::Tuple(_, fields))) => {
|
||||
format!("a single {}-tuple as argument", fields.len())
|
||||
}
|
||||
_ => format!(
|
||||
"{} {}argument{}",
|
||||
arg_length,
|
||||
if distinct && arg_length > 1 { "distinct " } else { "" },
|
||||
pluralize!(arg_length)
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let expected_str = args_str(&expected_args, &found_args);
|
||||
let found_str = args_str(&found_args, &expected_args);
|
||||
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0593,
|
||||
"{} is expected to take {}, but it takes {}",
|
||||
kind,
|
||||
expected_str,
|
||||
found_str,
|
||||
);
|
||||
|
||||
err.span_label(span, format!("expected {kind} that takes {expected_str}"));
|
||||
|
||||
if let Some(found_span) = found_span {
|
||||
err.span_label(found_span, format!("takes {found_str}"));
|
||||
|
||||
// Suggest to take and ignore the arguments with expected_args_length `_`s if
|
||||
// found arguments is empty (assume the user just wants to ignore args in this case).
|
||||
// For example, if `expected_args_length` is 2, suggest `|_, _|`.
|
||||
if found_args.is_empty() && is_closure {
|
||||
let underscores = vec!["_"; expected_args.len()].join(", ");
|
||||
err.span_suggestion_verbose(
|
||||
closure_arg_span.unwrap_or(found_span),
|
||||
format!(
|
||||
"consider changing the closure to take and ignore the expected argument{}",
|
||||
pluralize!(expected_args.len())
|
||||
),
|
||||
format!("|{underscores}|"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
|
||||
if fields.len() == expected_args.len() {
|
||||
let sugg = fields
|
||||
.iter()
|
||||
.map(|(name, _)| name.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
err.span_suggestion_verbose(
|
||||
found_span,
|
||||
"change the closure to take multiple arguments instead of a single tuple",
|
||||
format!("|{sugg}|"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
|
||||
&& fields.len() == found_args.len()
|
||||
&& is_closure
|
||||
{
|
||||
let sugg = format!(
|
||||
"|({}){}|",
|
||||
found_args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
ArgKind::Arg(name, _) => name.to_owned(),
|
||||
_ => "_".to_owned(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
// add type annotations if available
|
||||
if found_args.iter().any(|arg| match arg {
|
||||
ArgKind::Arg(_, ty) => ty != "_",
|
||||
_ => false,
|
||||
}) {
|
||||
format!(
|
||||
": ({})",
|
||||
fields
|
||||
.iter()
|
||||
.map(|(_, ty)| ty.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
found_span,
|
||||
"change the closure to accept a tuple instead of individual arguments",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
|
||||
/// in that order, and returns the generic type corresponding to the
|
||||
/// argument of that trait (corresponding to the closure arguments).
|
||||
fn type_implements_fn_trait(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: ty::Binder<'tcx, Ty<'tcx>>,
|
||||
polarity: ty::PredicatePolarity,
|
||||
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
|
||||
self.commit_if_ok(|_| {
|
||||
for trait_def_id in [
|
||||
self.tcx.lang_items().fn_trait(),
|
||||
self.tcx.lang_items().fn_mut_trait(),
|
||||
self.tcx.lang_items().fn_once_trait(),
|
||||
] {
|
||||
let Some(trait_def_id) = trait_def_id else { continue };
|
||||
// Make a fresh inference variable so we can determine what the generic parameters
|
||||
// of the trait are.
|
||||
let var = self.next_ty_var(DUMMY_SP);
|
||||
// FIXME(effects)
|
||||
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
|
||||
let obligation = Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
|
||||
);
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
ocx.register_obligation(obligation);
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok((
|
||||
self.tcx
|
||||
.fn_trait_kind_from_def_id(trait_def_id)
|
||||
.expect("expected to map DefId to ClosureKind"),
|
||||
ty.rebind(self.resolve_vars_if_possible(var)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
})
|
||||
}
|
||||
}
|
||||
242
compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
Normal file
242
compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
Normal file
@@ -0,0 +1,242 @@
|
||||
// ignore-tidy-filelength :(
|
||||
|
||||
pub mod ambiguity;
|
||||
mod infer_ctxt_ext;
|
||||
pub mod on_unimplemented;
|
||||
pub mod suggestions;
|
||||
mod type_err_ctxt_ext;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub use self::infer_ctxt_ext::*;
|
||||
pub use self::type_err_ctxt_ext::*;
|
||||
|
||||
// When outputting impl candidates, prefer showing those that are more similar.
|
||||
//
|
||||
// We also compare candidates after skipping lifetimes, which has a lower
|
||||
// priority than exact matches.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum CandidateSimilarity {
|
||||
Exact { ignoring_lifetimes: bool },
|
||||
Fuzzy { ignoring_lifetimes: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ImplCandidate<'tcx> {
|
||||
pub trait_ref: ty::TraitRef<'tcx>,
|
||||
pub similarity: CandidateSimilarity,
|
||||
impl_def_id: DefId,
|
||||
}
|
||||
|
||||
enum GetSafeTransmuteErrorAndReason {
|
||||
Silent,
|
||||
Error { err_msg: String, safe_transmute_explanation: Option<String> },
|
||||
}
|
||||
|
||||
struct UnsatisfiedConst(pub bool);
|
||||
|
||||
/// Crude way of getting back an `Expr` from a `Span`.
|
||||
pub struct FindExprBySpan<'hir> {
|
||||
pub span: Span,
|
||||
pub result: Option<&'hir hir::Expr<'hir>>,
|
||||
pub ty_result: Option<&'hir hir::Ty<'hir>>,
|
||||
pub include_closures: bool,
|
||||
pub tcx: TyCtxt<'hir>,
|
||||
}
|
||||
|
||||
impl<'hir> FindExprBySpan<'hir> {
|
||||
pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self {
|
||||
Self { span, result: None, ty_result: None, tcx, include_closures: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for FindExprBySpan<'v> {
|
||||
type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||
if self.span == ex.span {
|
||||
self.result = Some(ex);
|
||||
} else {
|
||||
if let hir::ExprKind::Closure(..) = ex.kind
|
||||
&& self.include_closures
|
||||
&& let closure_header_sp = self.span.with_hi(ex.span.hi())
|
||||
&& closure_header_sp == ex.span
|
||||
{
|
||||
self.result = Some(ex);
|
||||
}
|
||||
hir::intravisit::walk_expr(self, ex);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
|
||||
if self.span == ty.span {
|
||||
self.ty_result = Some(ty);
|
||||
} else {
|
||||
hir::intravisit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
|
||||
/// `param: ?Sized` would be a valid constraint.
|
||||
struct FindTypeParam {
|
||||
param: rustc_span::Symbol,
|
||||
invalid_spans: Vec<Span>,
|
||||
nested: bool,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for FindTypeParam {
|
||||
fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
|
||||
// Skip where-clauses, to avoid suggesting indirection for type parameters found there.
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
|
||||
// We collect the spans of all uses of the "bare" type param, like in `field: T` or
|
||||
// `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
|
||||
// valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
|
||||
// obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
|
||||
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
|
||||
// in that case should make what happened clear enough.
|
||||
match ty.kind {
|
||||
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
|
||||
hir::TyKind::Path(hir::QPath::Resolved(None, path))
|
||||
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
|
||||
{
|
||||
if !self.nested {
|
||||
debug!(?ty, "FindTypeParam::visit_ty");
|
||||
self.invalid_spans.push(ty.span);
|
||||
}
|
||||
}
|
||||
hir::TyKind::Path(_) => {
|
||||
let prev = self.nested;
|
||||
self.nested = true;
|
||||
hir::intravisit::walk_ty(self, ty);
|
||||
self.nested = prev;
|
||||
}
|
||||
_ => {
|
||||
hir::intravisit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Summarizes information
|
||||
#[derive(Clone)]
|
||||
pub enum ArgKind {
|
||||
/// An argument of non-tuple type. Parameters are (name, ty)
|
||||
Arg(String, String),
|
||||
|
||||
/// An argument of tuple type. For a "found" argument, the span is
|
||||
/// the location in the source of the pattern. For an "expected"
|
||||
/// argument, it will be None. The vector is a list of (name, ty)
|
||||
/// strings for the components of the tuple.
|
||||
Tuple(Option<Span>, Vec<(String, String)>),
|
||||
}
|
||||
|
||||
impl ArgKind {
|
||||
fn empty() -> ArgKind {
|
||||
ArgKind::Arg("_".to_owned(), "_".to_owned())
|
||||
}
|
||||
|
||||
/// Creates an `ArgKind` from the expected type of an
|
||||
/// argument. It has no name (`_`) and an optional source span.
|
||||
pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
|
||||
match t.kind() {
|
||||
ty::Tuple(tys) => ArgKind::Tuple(
|
||||
span,
|
||||
tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
|
||||
),
|
||||
_ => ArgKind::Arg("_".to_owned(), t.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HasNumericInferVisitor;
|
||||
|
||||
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
|
||||
type Result = ControlFlow<()>;
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DefIdOrName {
|
||||
DefId(DefId),
|
||||
Name(&'static str),
|
||||
}
|
||||
|
||||
/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
|
||||
/// string.
|
||||
pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
|
||||
use std::fmt::Write;
|
||||
|
||||
let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
|
||||
let mut w = "impl".to_owned();
|
||||
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
|
||||
|
||||
// FIXME: Currently only handles ?Sized.
|
||||
// Needs to support ?Move and ?DynSized when they are implemented.
|
||||
let mut types_without_default_bounds = FxIndexSet::default();
|
||||
let sized_trait = tcx.lang_items().sized_trait();
|
||||
|
||||
let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
|
||||
if !arg_names.is_empty() {
|
||||
types_without_default_bounds.extend(args.types());
|
||||
w.push('<');
|
||||
w.push_str(&arg_names.join(", "));
|
||||
w.push('>');
|
||||
}
|
||||
|
||||
write!(
|
||||
w,
|
||||
" {} for {}",
|
||||
trait_ref.print_only_trait_path(),
|
||||
tcx.type_of(impl_def_id).instantiate_identity()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The predicates will contain default bounds like `T: Sized`. We need to
|
||||
// remove these bounds, and add `T: ?Sized` to any untouched type parameters.
|
||||
let predicates = tcx.predicates_of(impl_def_id).predicates;
|
||||
let mut pretty_predicates =
|
||||
Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
|
||||
|
||||
for (p, _) in predicates {
|
||||
if let Some(poly_trait_ref) = p.as_trait_clause() {
|
||||
if Some(poly_trait_ref.def_id()) == sized_trait {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pretty_predicates.push(p.to_string());
|
||||
}
|
||||
|
||||
pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
|
||||
|
||||
if !pretty_predicates.is_empty() {
|
||||
write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap();
|
||||
}
|
||||
|
||||
w.push(';');
|
||||
Some(w)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user