Better support projection types when finding the signature for an expression

This commit is contained in:
Jason Newcomb 2022-01-25 16:26:30 -05:00
parent b776fb8294
commit c107c97e69

View file

@ -14,8 +14,8 @@ use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Region, RegionKind, Ty, self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, Region,
TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr, RegionKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr,
}; };
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_span::{sym, Span, Symbol, DUMMY_SP};
@ -530,7 +530,11 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) { if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id))) Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
} else { } else {
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
}
}
fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
match *ty.kind() { match *ty.kind() {
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())), ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))), ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
@ -546,58 +550,98 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
let output = bounds let output = bounds
.projection_bounds() .projection_bounds()
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id())) .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
.map(|p| p.map_bound(|p| p.term.ty().expect("return type was a const"))); .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output)) Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
}, },
_ => None, _ => None,
} }
}, },
ty::Param(_) | ty::Projection(..) => { ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
_ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
},
ty::Param(_) => sig_from_bounds(cx, ty),
_ => None,
}
}
fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
let mut inputs = None; let mut inputs = None;
let mut output = None; let mut output = None;
let lang_items = cx.tcx.lang_items(); let lang_items = cx.tcx.lang_items();
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) { for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
let mut is_input = false; match pred.kind().skip_binder() {
if let Some(ty) = pred
.kind()
.map_bound(|pred| match pred {
PredicateKind::Trait(p) PredicateKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id()) if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id())) || lang_items.fn_once_trait() == Some(p.def_id()))
&& p.self_ty() == ty => && p.self_ty() == ty =>
{ {
is_input = true; if inputs.is_some() {
Some(p.trait_ref.substs.type_at(1)) // Multiple different fn trait impls. Is this even allowed?
return None;
}
inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
}, },
PredicateKind::Projection(p) PredicateKind::Projection(p)
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
&& p.projection_ty.self_ty() == ty => && p.projection_ty.self_ty() == ty =>
{ {
is_input = false; if output.is_some() {
p.term.ty()
},
_ => None,
})
.transpose()
{
if is_input && inputs.is_none() {
inputs = Some(ty);
} else if !is_input && output.is_none() {
output = Some(ty);
} else {
// Multiple different fn trait impls. Is this even allowed? // Multiple different fn trait impls. Is this even allowed?
return None; return None;
} }
output = Some(pred.kind().rebind(p.term.ty().unwrap()));
},
_ => (),
} }
} }
inputs.map(|ty| ExprFnSig::Trait(ty, output)) inputs.map(|ty| ExprFnSig::Trait(ty, output))
}
fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
let mut inputs = None;
let mut output = None;
let lang_items = cx.tcx.lang_items();
for pred in cx
.tcx
.bound_explicit_item_bounds(ty.item_def_id)
.transpose_iter()
.map(|x| x.map_bound(|(p, _)| p))
{
match pred.0.kind().skip_binder() {
PredicateKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id())) =>
{
if inputs.is_some() {
// Multiple different fn trait impls. Is this even allowed?
return None;
}
inputs = Some(
pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
.subst(cx.tcx, ty.substs),
);
}, },
_ => None, PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
if output.is_some() {
// Multiple different fn trait impls. Is this even allowed?
return None;
}
output = Some(
pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
.subst(cx.tcx, ty.substs),
);
},
_ => (),
} }
} }
inputs.map(|ty| ExprFnSig::Trait(ty, output))
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]