1
Fork 0

Make x.py check work

This commit is contained in:
Eric Holk 2022-08-30 12:39:28 -07:00
parent ddfcca48c6
commit 3c2d20ef0b
7 changed files with 211 additions and 134 deletions

View file

@ -701,6 +701,10 @@ fn codegen_stmt<'tcx>(
let operand = codegen_operand(fx, operand); let operand = codegen_operand(fx, operand);
operand.unsize_value(fx, lval); operand.unsize_value(fx, lval);
} }
Rvalue::Cast(CastKind::DynStar, _, _) => {
// FIXME(dyn-star)
unimplemented!()
}
Rvalue::Discriminant(place) => { Rvalue::Discriminant(place) => {
let place = codegen_place(fx, place); let place = codegen_place(fx, place);
let value = place.to_cvalue(fx); let value = place.to_cvalue(fx);

View file

@ -815,7 +815,7 @@ pub(crate) fn assert_assignable<'tcx>(
); );
// fn(&T) -> for<'l> fn(&'l T) is allowed // fn(&T) -> for<'l> fn(&'l T) is allowed
} }
(&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => {
for (from, to) in from_traits.iter().zip(to_traits) { for (from, to) in from_traits.iter().zip(to_traits) {
let from = let from =
fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from);

View file

@ -1039,7 +1039,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
| ty::Ref(_, _, _) | ty::Ref(_, _, _)
| ty::FnDef(_, _) | ty::FnDef(_, _)
| ty::FnPtr(_) | ty::FnPtr(_)
| ty::Dynamic(_, _) | ty::Dynamic(_, _, _)
| ty::Closure(_, _) | ty::Closure(_, _)
| ty::Generator(_, _, _) | ty::Generator(_, _, _)
| ty::GeneratorWitness(_) | ty::GeneratorWitness(_)

View file

@ -182,7 +182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
}) })
} }
ty::Dynamic(data, _, ty::TraitObjectRepresentation::Unsized) => { ty::Dynamic(data, _, ty::Dyn) => {
data.iter().find_map(|pred| { data.iter().find_map(|pred| {
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
&& Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()

View file

@ -2,11 +2,18 @@ use rustc_hir::Expr;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_middle::ty::{cast::CastKind, Ty};
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; use rustc_typeck::check::{
cast::{self, CastCheckResult},
FnCtxt, Inherited,
};
// check if the component types of the transmuted collection and the result have different ABI, // check if the component types of the transmuted collection and the result have different ABI,
// size or alignment // size or alignment
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { pub(super) fn is_layout_incompatible<'tcx>(
cx: &LateContext<'tcx>,
from: Ty<'tcx>,
to: Ty<'tcx>,
) -> bool {
if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
&& let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
&& let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
@ -29,7 +36,9 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
from_ty: Ty<'tcx>, from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>, to_ty: Ty<'tcx>,
) -> bool { ) -> bool {
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; use CastKind::{
AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast,
};
matches!( matches!(
check_cast(cx, e, from_ty, to_ty), check_cast(cx, e, from_ty, to_ty),
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
@ -40,7 +49,12 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
/// the cast. In certain cases, including some invalid casts from array references /// the cast. In certain cases, including some invalid casts from array references
/// to pointers, this may cause additional errors to be emitted and/or ICE error /// to pointers, this may cause additional errors to be emitted and/or ICE error
/// messages. This function will panic if that occurs. /// messages. This function will panic if that occurs.
fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> { fn check_cast<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
) -> Option<CastKind> {
let hir_id = e.hir_id; let hir_id = e.hir_id;
let local_def_id = hir_id.owner; let local_def_id = hir_id.owner;
@ -48,12 +62,9 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
// If we already have errors, we can't be sure we can pointer cast. // If we already have errors, we can't be sure we can pointer cast.
assert!( assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors");
!fn_ctxt.errors_reported_since_creation(),
"Newly created FnCtxt contained errors"
);
if let Ok(check) = CastCheck::new( if let CastCheckResult::Deferred(check) = cast::check_cast(
&fn_ctxt, e, from_ty, to_ty, &fn_ctxt, e, from_ty, to_ty,
// We won't show any error to the user, so we don't care what the span is here. // We won't show any error to the user, so we don't care what the span is here.
DUMMY_SP, DUMMY_SP, DUMMY_SP, DUMMY_SP,

View file

@ -18,7 +18,11 @@ use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>; type McfResult = Result<(), (Span, Cow<'static, str>)>;
pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult { pub fn is_min_const_fn<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
msrv: Option<RustcVersion>,
) -> McfResult {
let def_id = body.source.def_id(); let def_id = body.source.def_id();
let mut current = def_id; let mut current = def_id;
loop { loop {
@ -33,10 +37,18 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv:
| ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Trait(..) | ty::PredicateKind::Trait(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ObjectSafe(_) => {
ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), panic!("object safe predicate on function: {:#?}", predicate)
ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), }
ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => {
panic!("closure kind predicate on function: {:#?}", predicate)
}
ty::PredicateKind::Subtype(_) => {
panic!("subtype predicate on function: {:#?}", predicate)
}
ty::PredicateKind::Coerce(_) => {
panic!("coerce predicate on function: {:#?}", predicate)
}
} }
} }
match predicates.parent { match predicates.parent {
@ -77,22 +89,23 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
match ty.kind() { match ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => { ty::Ref(_, _, hir::Mutability::Mut) => {
return Err((span, "mutable references in const fn are unstable".into())); return Err((span, "mutable references in const fn are unstable".into()));
}, }
ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
ty::FnPtr(..) => { ty::FnPtr(..) => {
return Err((span, "function pointers in const fn are unstable".into())); return Err((span, "function pointers in const fn are unstable".into()));
}, }
ty::Dynamic(preds, _) => { ty::Dynamic(preds, _, _) => {
for pred in preds.iter() { for pred in preds.iter() {
match pred.skip_binder() { match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { ty::ExistentialPredicate::AutoTrait(_)
| ty::ExistentialPredicate::Projection(_) => {
return Err(( return Err((
span, span,
"trait bounds other than `Sized` \ "trait bounds other than `Sized` \
on const fn parameters are unstable" on const fn parameters are unstable"
.into(), .into(),
)); ));
}, }
ty::ExistentialPredicate::Trait(trait_ref) => { ty::ExistentialPredicate::Trait(trait_ref) => {
if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
return Err(( return Err((
@ -102,11 +115,11 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
.into(), .into(),
)); ));
} }
}, }
} }
} }
}, }
_ => {}, _ => {}
} }
} }
Ok(()) Ok(())
@ -120,10 +133,13 @@ fn check_rvalue<'tcx>(
span: Span, span: Span,
) -> McfResult { ) -> McfResult {
match rvalue { match rvalue {
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), Rvalue::ThreadLocalRef(_) => {
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { Err((span, "cannot access thread local storage in const fn".into()))
check_place(tcx, *place, span, body) }
}, Rvalue::Len(place)
| Rvalue::Discriminant(place)
| Rvalue::Ref(_, _, place)
| Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body),
Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body), Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body),
Rvalue::Repeat(operand, _) Rvalue::Repeat(operand, _)
| Rvalue::Use(operand) | Rvalue::Use(operand)
@ -136,7 +152,9 @@ fn check_rvalue<'tcx>(
) => check_operand(tcx, operand, span, body), ) => check_operand(tcx, operand, span, body),
Rvalue::Cast( Rvalue::Cast(
CastKind::Pointer( CastKind::Pointer(
PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, PointerCast::UnsafeFnPointer
| PointerCast::ClosureFnPointer(_)
| PointerCast::ReifyFnPointer,
), ),
_, _,
_, _,
@ -146,7 +164,10 @@ fn check_rvalue<'tcx>(
deref_ty.ty deref_ty.ty
} else { } else {
// We cannot allow this for now. // We cannot allow this for now.
return Err((span, "unsizing casts are only allowed for references right now".into())); return Err((
span,
"unsizing casts are only allowed for references right now".into(),
));
}; };
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
if let ty::Slice(_) | ty::Str = unsized_ty.kind() { if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
@ -157,10 +178,14 @@ fn check_rvalue<'tcx>(
// We just can't allow trait objects until we have figured out trait method calls. // We just can't allow trait objects until we have figured out trait method calls.
Err((span, "unsizing casts are not allowed in const fn".into())) Err((span, "unsizing casts are not allowed in const fn".into()))
} }
}, }
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => {
Err((span, "casting pointers to ints is unstable in const fn".into())) Err((span, "casting pointers to ints is unstable in const fn".into()))
}, }
Rvalue::Cast(CastKind::DynStar, _, _) => {
// FIXME(dyn-star)
unimplemented!()
}
// binops are fine on integers // binops are fine on integers
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
check_operand(tcx, lhs, span, body)?; check_operand(tcx, lhs, span, body)?;
@ -169,13 +194,12 @@ fn check_rvalue<'tcx>(
if ty.is_integral() || ty.is_bool() || ty.is_char() { if ty.is_integral() || ty.is_bool() || ty.is_char() {
Ok(()) Ok(())
} else { } else {
Err(( Err((span, "only int, `bool` and `char` operations are stable in const fn".into()))
span,
"only int, `bool` and `char` operations are stable in const fn".into(),
))
} }
}, }
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => {
Ok(())
}
Rvalue::UnaryOp(_, operand) => { Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx); let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() { if ty.is_integral() || ty.is_bool() {
@ -183,13 +207,13 @@ fn check_rvalue<'tcx>(
} else { } else {
Err((span, "only int and `bool` operations are stable in const fn".into())) Err((span, "only int and `bool` operations are stable in const fn".into()))
} }
}, }
Rvalue::Aggregate(_, operands) => { Rvalue::Aggregate(_, operands) => {
for operand in operands { for operand in operands {
check_operand(tcx, operand, span, body)?; check_operand(tcx, operand, span, body)?;
} }
Ok(()) Ok(())
}, }
} }
} }
@ -204,7 +228,7 @@ fn check_statement<'tcx>(
StatementKind::Assign(box (place, rval)) => { StatementKind::Assign(box (place, rval)) => {
check_place(tcx, *place, span, body)?; check_place(tcx, *place, span, body)?;
check_rvalue(tcx, body, def_id, rval, span) check_rvalue(tcx, body, def_id, rval, span)
}, }
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
// just an assignment // just an assignment
@ -214,14 +238,15 @@ fn check_statement<'tcx>(
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body), StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body),
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping {
rustc_middle::mir::CopyNonOverlapping { dst, src, count }, dst,
)) => { src,
count,
}) => {
check_operand(tcx, dst, span, body)?; check_operand(tcx, dst, span, body)?;
check_operand(tcx, src, span, body)?; check_operand(tcx, src, span, body)?;
check_operand(tcx, count, span, body) check_operand(tcx, count, span, body)
}, }
// These are all NOPs // These are all NOPs
StatementKind::StorageLive(_) StatementKind::StorageLive(_)
| StatementKind::StorageDead(_) | StatementKind::StorageDead(_)
@ -232,7 +257,12 @@ fn check_statement<'tcx>(
} }
} }
fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { fn check_operand<'tcx>(
tcx: TyCtxt<'tcx>,
operand: &Operand<'tcx>,
span: Span,
body: &Body<'tcx>,
) -> McfResult {
match operand { match operand {
Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) { Operand::Constant(c) => match c.check_static_ptr(tcx) {
@ -242,7 +272,12 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
} }
} }
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { fn check_place<'tcx>(
tcx: TyCtxt<'tcx>,
place: Place<'tcx>,
span: Span,
body: &Body<'tcx>,
) -> McfResult {
let mut cursor = place.projection.as_ref(); let mut cursor = place.projection.as_ref();
while let [ref proj_base @ .., elem] = *cursor { while let [ref proj_base @ .., elem] = *cursor {
cursor = proj_base; cursor = proj_base;
@ -255,12 +290,12 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
return Err((span, "accessing union fields is unstable".into())); return Err((span, "accessing union fields is unstable".into()));
} }
} }
}, }
ProjectionElem::ConstantIndex { .. } ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..) | ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Deref | ProjectionElem::Deref
| ProjectionElem::Index(_) => {}, | ProjectionElem::Index(_) => {}
} }
} }
@ -286,18 +321,16 @@ fn check_terminator<'a, 'tcx>(
TerminatorKind::DropAndReplace { place, value, .. } => { TerminatorKind::DropAndReplace { place, value, .. } => {
check_place(tcx, *place, span, body)?; check_place(tcx, *place, span, body)?;
check_operand(tcx, value, span, body) check_operand(tcx, value, span, body)
}, }
TerminatorKind::SwitchInt { TerminatorKind::SwitchInt { discr, switch_ty: _, targets: _ } => {
discr, check_operand(tcx, discr, span, body)
switch_ty: _, }
targets: _,
} => check_operand(tcx, discr, span, body),
TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into())) Err((span, "const fn generators are unstable".into()))
}, }
TerminatorKind::Call { TerminatorKind::Call {
func, func,
@ -342,17 +375,15 @@ fn check_terminator<'a, 'tcx>(
} else { } else {
Err((span, "can only call other const fns within const fn".into())) Err((span, "can only call other const fns within const fn".into()))
} }
}, }
TerminatorKind::Assert { TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => {
cond, check_operand(tcx, cond, span, body)
expected: _, }
msg: _,
target: _,
cleanup: _,
} => check_operand(tcx, cond, span, body),
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), TerminatorKind::InlineAsm { .. } => {
Err((span, "cannot use inline assembly in const fn".into()))
}
} }
} }

View file

@ -14,8 +14,9 @@ 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, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind,
Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, ProjectionTy, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
UintTy, VariantDef, 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};
@ -166,9 +167,7 @@ pub fn implements_trait_with_env<'tcx>(
} }
let ty_params = tcx.mk_substs(ty_params.iter()); let ty_params = tcx.mk_substs(ty_params.iter());
tcx.infer_ctxt().enter(|infcx| { tcx.infer_ctxt().enter(|infcx| {
infcx infcx.type_implements_trait(trait_id, ty, ty_params, param_env).must_apply_modulo_regions()
.type_implements_trait(trait_id, ty, ty_params, param_env)
.must_apply_modulo_regions()
}) })
} }
@ -185,11 +184,14 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() { match ty.kind() {
ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use), ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use), ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { ty::Slice(ty)
| ty::Array(ty, _)
| ty::RawPtr(ty::TypeAndMut { ty, .. })
| ty::Ref(_, ty, _) => {
// for the Array case we don't need to care for the len == 0 case // for the Array case we don't need to care for the len == 0 case
// because we don't want to lint functions returning empty arrays // because we don't want to lint functions returning empty arrays
is_must_use_ty(cx, *ty) is_must_use_ty(cx, *ty)
}, }
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
ty::Opaque(def_id, _) => { ty::Opaque(def_id, _) => {
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
@ -200,8 +202,8 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
} }
} }
false false
}, }
ty::Dynamic(binder, _) => { ty::Dynamic(binder, _, _) => {
for predicate in binder.iter() { for predicate in binder.iter() {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
@ -210,7 +212,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
} }
} }
false false
}, }
_ => false, _ => false,
} }
} }
@ -220,7 +222,11 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
// not succeed // not succeed
/// Checks if `Ty` is normalizable. This function is useful /// Checks if `Ty` is normalizable. This function is useful
/// to avoid crashes on `layout_of`. /// to avoid crashes on `layout_of`.
pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { pub fn is_normalizable<'tcx>(
cx: &LateContext<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> bool {
is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default()) is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
} }
@ -240,15 +246,14 @@ fn is_normalizable_helper<'tcx>(
if infcx.at(&cause, param_env).normalize(ty).is_ok() { if infcx.at(&cause, param_env).normalize(ty).is_ok() {
match ty.kind() { match ty.kind() {
ty::Adt(def, substs) => def.variants().iter().all(|variant| { ty::Adt(def, substs) => def.variants().iter().all(|variant| {
variant variant.fields.iter().all(|field| {
.fields is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)
.iter() })
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
}), }),
_ => ty.walk().all(|generic_arg| match generic_arg.unpack() { _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
GenericArgKind::Type(inner_ty) if inner_ty != ty => { GenericArgKind::Type(inner_ty) if inner_ty != ty => {
is_normalizable_helper(cx, param_env, inner_ty, cache) is_normalizable_helper(cx, param_env, inner_ty, cache)
}, }
_ => true, // if inner_ty == ty, we've already checked it _ => true, // if inner_ty == ty, we've already checked it
}), }),
} }
@ -273,7 +278,9 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
match *ty.kind() { match *ty.kind() {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true, ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), ty::Array(inner_type, _) | ty::Slice(inner_type) => {
is_recursively_primitive_type(inner_type)
}
ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type), ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
_ => false, _ => false,
} }
@ -313,11 +320,9 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
/// Returns `false` if the `LangItem` is not defined. /// Returns `false` if the `LangItem` is not defined.
pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
match ty.kind() { match ty.kind() {
ty::Adt(adt, _) => cx ty::Adt(adt, _) => {
.tcx cx.tcx.lang_items().require(lang_item).map_or(false, |li| li == adt.did())
.lang_items() }
.require(lang_item)
.map_or(false, |li| li == adt.did()),
_ => false, _ => false,
} }
} }
@ -342,7 +347,11 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
/// deallocate memory. For these types, and composites containing them, changing the drop order /// deallocate memory. For these types, and composites containing them, changing the drop order
/// won't result in any observable side effects. /// won't result in any observable side effects.
pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { fn needs_ordered_drop_inner<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
seen: &mut FxHashSet<Ty<'tcx>>,
) -> bool {
if !seen.insert(ty) { if !seen.insert(ty) {
return false; return false;
} }
@ -393,11 +402,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
/// removed. /// removed.
pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
if let ty::Ref(_, ty, _) = ty.kind() { if let ty::Ref(_, ty, _) = ty.kind() { peel(*ty, count + 1) } else { (ty, count) }
peel(*ty, count + 1)
} else {
(ty, count)
}
} }
peel(ty, 0) peel(ty, 0)
} }
@ -452,17 +457,18 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
return false; return false;
} }
substs_a substs_a.iter().zip(substs_b.iter()).all(|(arg_a, arg_b)| {
.iter() match (arg_a.unpack(), arg_b.unpack()) {
.zip(substs_b.iter()) (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => {
.all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { inner_a == inner_b
(GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, }
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
same_type_and_consts(type_a, type_b) same_type_and_consts(type_a, type_b)
}, }
_ => true, _ => true,
}) }
}, })
}
_ => a == b, _ => a == b,
} }
} }
@ -478,7 +484,10 @@ pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
} }
/// Gets an iterator over all predicates which apply to the given item. /// Gets an iterator over all predicates which apply to the given item.
pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> { pub fn all_predicates_of(
tcx: TyCtxt<'_>,
id: DefId,
) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
let mut next_id = Some(id); let mut next_id = Some(id);
iter::from_fn(move || { iter::from_fn(move || {
next_id.take().map(|id| { next_id.take().map(|id| {
@ -508,7 +517,7 @@ impl<'tcx> ExprFnSig<'tcx> {
} else { } else {
Some(sig.input(i)) Some(sig.input(i))
} }
}, }
Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])), Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])), Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
} }
@ -517,7 +526,10 @@ impl<'tcx> ExprFnSig<'tcx> {
/// Gets the argument type at the given offset. For closures this will also get the type as /// Gets the argument type at the given offset. For closures this will also get the type as
/// written. This will return `None` when the index is out of bounds only for variadic /// written. This will return `None` when the index is out of bounds only for variadic
/// functions, otherwise this will panic. /// functions, otherwise this will panic.
pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> { pub fn input_with_hir(
self,
i: usize,
) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
match self { match self {
Self::Sig(sig, _) => { Self::Sig(sig, _) => {
if sig.c_variadic() { if sig.c_variadic() {
@ -528,7 +540,7 @@ impl<'tcx> ExprFnSig<'tcx> {
} else { } else {
Some((None, sig.input(i))) Some((None, sig.input(i)))
} }
}, }
Self::Closure(decl, sig) => Some(( Self::Closure(decl, sig) => Some((
decl.and_then(|decl| decl.inputs.get(i)), decl.and_then(|decl| decl.inputs.get(i)),
sig.input(0).map_bound(|ty| ty.tuple_fields()[i]), sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
@ -547,17 +559,15 @@ impl<'tcx> ExprFnSig<'tcx> {
} }
pub fn predicates_id(&self) -> Option<DefId> { pub fn predicates_id(&self) -> Option<DefId> {
if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self { if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self { id } else { None }
id
} else {
None
}
} }
} }
/// If the expression is function like, get the signature for it. /// If the expression is function like, get the signature for it.
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> { pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
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(id))) Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
} else { } else {
ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
@ -571,15 +581,17 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
} }
match *ty.kind() { match *ty.kind() {
ty::Closure(id, subs) => { ty::Closure(id, subs) => {
let decl = id let decl = id.as_local().and_then(|id| {
.as_local() cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id))
.and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id))); });
Some(ExprFnSig::Closure(decl, subs.as_closure().sig())) Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
}, }
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), ty::FnDef(id, subs) => {
Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id)))
}
ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)), ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)),
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
ty::Dynamic(bounds, _) => { ty::Dynamic(bounds, _, _) => {
let lang_items = cx.tcx.lang_items(); let lang_items = cx.tcx.lang_items();
match bounds.principal() { match bounds.principal() {
Some(bound) Some(bound)
@ -589,16 +601,19 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
{ {
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().unwrap())); .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None)) Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
}, }
_ => None, _ => None,
} }
}, }
ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { 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), Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
_ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)), _ => sig_for_projection(cx, proj)
.or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
}, },
ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None), ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
_ => None, _ => None,
@ -629,7 +644,7 @@ fn sig_from_bounds<'tcx>(
return None; return None;
} }
inputs = Some(i); inputs = Some(i);
}, }
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 =>
@ -639,7 +654,7 @@ fn sig_from_bounds<'tcx>(
return None; return None;
} }
output = Some(pred.kind().rebind(p.term.ty().unwrap())); output = Some(pred.kind().rebind(p.term.ty().unwrap()));
}, }
_ => (), _ => (),
} }
} }
@ -647,7 +662,10 @@ fn sig_from_bounds<'tcx>(
inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id)) inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
} }
fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> { fn sig_for_projection<'tcx>(
cx: &LateContext<'tcx>,
ty: ProjectionTy<'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();
@ -673,8 +691,10 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
return None; return None;
} }
inputs = Some(i); inputs = Some(i);
}, }
PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => { PredicateKind::Projection(p)
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
{
if output.is_some() { if output.is_some() {
// Multiple different fn trait impls. Is this even allowed? // Multiple different fn trait impls. Is this even allowed?
return None; return None;
@ -683,7 +703,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap())) pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
.subst(cx.tcx, ty.substs), .subst(cx.tcx, ty.substs),
); );
}, }
_ => (), _ => (),
} }
} }
@ -777,7 +797,10 @@ pub fn for_each_top_level_late_bound_region<B>(
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
} }
fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> { fn visit_binder<T: TypeVisitable<'tcx>>(
&mut self,
t: &Binder<'tcx, T>,
) -> ControlFlow<Self::BreakTy> {
self.index += 1; self.index += 1;
let res = t.super_visit_with(self); let res = t.super_visit_with(self);
self.index -= 1; self.index -= 1;
@ -791,19 +814,27 @@ pub fn for_each_top_level_late_bound_region<B>(
pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> { pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
match res { match res {
Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()), Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)), Res::Def(DefKind::Variant, id) => {
Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()), Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id))
}
Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => {
Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant())
}
Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => { Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
let var_id = cx.tcx.parent(id); let var_id = cx.tcx.parent(id);
Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id)) Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
}, }
Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()), Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
_ => None, _ => None,
} }
} }
/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`. /// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool { pub fn ty_is_fn_once_param<'tcx>(
tcx: TyCtxt<'_>,
ty: Ty<'tcx>,
predicates: &'tcx [Predicate<'_>],
) -> bool {
let ty::Param(ty) = *ty.kind() else { let ty::Param(ty) = *ty.kind() else {
return false; return false;
}; };