Address code review comments

This commit is contained in:
Eric Holk 2022-08-30 12:44:00 -07:00
parent de42ac3970
commit cf04547b0b
27 changed files with 199 additions and 261 deletions

View file

@ -19,7 +19,6 @@ use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY,
}; };
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::feature_err;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
@ -754,19 +753,7 @@ impl<'a> AstValidator<'a> {
self.maybe_lint_missing_abi(sig_span, ty.id); self.maybe_lint_missing_abi(sig_span, ty.id);
} }
} }
TyKind::TraitObject(ref bounds, syntax, ..) => { TyKind::TraitObject(ref bounds, ..) => {
if syntax == TraitObjectSyntax::DynStar
&& !self.session.features_untracked().dyn_star
{
feature_err(
&self.session.parse_sess,
sym::dyn_star,
ty.span,
"dyn* trait objects are unstable",
)
.emit();
}
let mut any_lifetime_bounds = false; let mut any_lifetime_bounds = false;
for bound in bounds { for bound in bounds {
if let GenericBound::Outlives(ref lifetime) = *bound { if let GenericBound::Outlives(ref lifetime) = *bound {

View file

@ -573,6 +573,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::TyKind::Never => { ast::TyKind::Never => {
gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental"); gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
} }
ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::DynStar, ..) => {
gate_feature_post!(&self, dyn_star, ty.span, "dyn* trait objects are unstable");
}
_ => {} _ => {}
} }
visit::walk_ty(self, ty) visit::walk_ty(self, ty)

View file

@ -816,6 +816,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, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => { (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => {
// FIXME(dyn-star): Do the right thing with DynKinds
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

@ -367,7 +367,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.ret(llval); bx.ret(llval);
} }
#[tracing::instrument(level = "debug", skip(self, helper, bx))] #[tracing::instrument(level = "trace", skip(self, helper, bx))]
fn codegen_drop_terminator( fn codegen_drop_terminator(
&mut self, &mut self,
helper: TerminatorCodegenHelper<'tcx>, helper: TerminatorCodegenHelper<'tcx>,

View file

@ -113,11 +113,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() { if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() {
// Initial cast from sized to dyn trait // Initial cast from sized to dyn trait
let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?; let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?;
let ptr = self.read_immediate(src)?.to_scalar(); let vtable = Scalar::from_maybe_pointer(vtable, self);
// FIXME(dyn-star): This should not use new_dyn_trait, but let data = self.read_immediate(src)?.to_scalar();
// it does exactly the same thing (makes a scalar pair)... let _assert_pointer_sized = data.to_pointer(self)?;
// so maybe we should just duplicate/rename the function. let val = Immediate::ScalarPair(data, vtable);
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
self.write_immediate(val, dest)?; self.write_immediate(val, dest)?;
} else { } else {
bug!() bug!()
@ -327,7 +326,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
} }
(_, &ty::Dynamic(ref data, _, _repr)) => { (_, &ty::Dynamic(ref data, _, ty::Dyn)) => {
// Initial cast from sized to dyn trait // Initial cast from sized to dyn trait
let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?; let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
let ptr = self.read_scalar(src)?; let ptr = self.read_scalar(src)?;

View file

@ -570,7 +570,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
CastKind::DynStar => { CastKind::DynStar => {
// FIXME: make sure nothing needs to be done here. // FIXME(dyn-star): make sure nothing needs to be done here.
} }
// Nothing to check here // Nothing to check here
CastKind::PointerFromExposedAddress CastKind::PointerFromExposedAddress

View file

@ -381,7 +381,7 @@ declare_features! (
/// Allows `#[doc(masked)]`. /// Allows `#[doc(masked)]`.
(active, doc_masked, "1.21.0", Some(44027), None), (active, doc_masked, "1.21.0", Some(44027), None),
/// Allows `dyn* Trait` objects. /// Allows `dyn* Trait` objects.
(active, dyn_star, "1.65.0", Some(91611), None), (incomplete, dyn_star, "CURRENT_RUSTC_VERSION", Some(91611), None),
/// Allows `X..Y` patterns. /// Allows `X..Y` patterns.
(active, exclusive_range_pattern, "1.11.0", Some(37854), None), (active, exclusive_range_pattern, "1.11.0", Some(37854), None),
/// Allows exhaustive pattern matching on types that contain uninhabited types. /// Allows exhaustive pattern matching on types that contain uninhabited types.

View file

@ -626,11 +626,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
} }
ty::Dynamic(_, _, ty::DynStar) => { ty::Dynamic(_, _, ty::DynStar) => {
let mut pointer = scalar_unit(Int(dl.ptr_sized_integer(), false)); let mut data = scalar_unit(Int(dl.ptr_sized_integer(), false));
pointer.valid_range_mut().start = 1; data.valid_range_mut().start = 0;
let mut vtable = scalar_unit(Pointer); let mut vtable = scalar_unit(Pointer);
vtable.valid_range_mut().start = 1; vtable.valid_range_mut().start = 1;
tcx.intern_layout(self.scalar_pair(pointer, vtable)) tcx.intern_layout(self.scalar_pair(data, vtable))
} }
// Arrays and slices. // Arrays and slices.
@ -2474,8 +2474,7 @@ where
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize), ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize),
// FIXME(eholk): Do the right thing with trait object representation ty::Dynamic(_, _, ty::Dyn) => {
ty::Dynamic(_, _, _repr) => {
TyMaybeWithLayout::Ty(tcx.mk_imm_ref( TyMaybeWithLayout::Ty(tcx.mk_imm_ref(
tcx.lifetimes.re_static, tcx.lifetimes.re_static,
tcx.mk_array(tcx.types.usize, 3), tcx.mk_array(tcx.types.usize, 3),
@ -3379,7 +3378,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
Ok(self.tcx.arena.alloc(fn_abi)) Ok(self.tcx.arena.alloc(fn_abi))
} }
#[tracing::instrument(level = "debug", skip(self))] #[tracing::instrument(level = "trace", skip(self))]
fn fn_abi_adjust_for_abi( fn fn_abi_adjust_for_abi(
&self, &self,
fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>, fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>,

View file

@ -699,7 +699,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
// This could also be a different Unsize instruction, like // This could also be a different Unsize instruction, like
// from a fixed sized array to a slice. But we are only // from a fixed sized array to a slice. But we are only
// interested in things that produce a vtable. // interested in things that produce a vtable.
if (target_ty.is_trait() || target_ty.is_dyn_star()) && !source_ty.is_trait() { if (target_ty.is_trait() && !source_ty.is_trait())
|| (target_ty.is_dyn_star() && !source_ty.is_dyn_star())
{
create_mono_items_for_vtable_methods( create_mono_items_for_vtable_methods(
self.tcx, self.tcx,
target_ty, target_ty,

View file

@ -579,9 +579,7 @@ impl<'a> Parser<'a> {
self.bump(); // `dyn` self.bump(); // `dyn`
// parse dyn* types // parse dyn* types
let dyn_star = matches!(self.token.kind, TokenKind::BinOp(token::Star)); let syntax = if self.eat(&TokenKind::BinOp(token::Star)) {
let syntax = if dyn_star {
self.bump(); // `*`
TraitObjectSyntax::DynStar TraitObjectSyntax::DynStar
} else { } else {
TraitObjectSyntax::Dyn TraitObjectSyntax::Dyn

View file

@ -627,10 +627,13 @@ fn encode_ty<'tcx>(
} }
// Trait types // Trait types
ty::Dynamic(predicates, region, _repr) => { ty::Dynamic(predicates, region, kind) => {
// u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
// vendor extended type. // vendor extended type.
let mut s = String::from("u3dynI"); let mut s = String::from(match kind {
ty::Dyn => "u3dynI",
ty::DynStar => "u7dynstarI",
});
s.push_str(&encode_predicates(tcx, predicates, dict, options)); s.push_str(&encode_predicates(tcx, predicates, dict, options));
s.push_str(&encode_region(tcx, *region, dict, options)); s.push_str(&encode_region(tcx, *region, dict, options));
s.push('E'); s.push('E');

View file

@ -479,8 +479,12 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
})?; })?;
} }
ty::Dynamic(predicates, r, _repr) => { ty::Dynamic(predicates, r, kind) => {
self.push("D"); self.push(match kind {
ty::Dyn => "D",
// FIXME(dyn-star): need to update v0 mangling docs
ty::DynStar => "D*",
});
self = self.print_dyn_existential(predicates)?; self = self.print_dyn_existential(predicates)?;
self = r.print(self)?; self = r.print(self)?;
} }

View file

@ -1067,7 +1067,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
object_ty: Ty<'tcx>, object_ty: Ty<'tcx>,
) { ) {
let ty::Dynamic(predicates, _, _) = object_ty.kind() else { return; }; let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; };
let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty); let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty);
for predicate in predicates.iter() { for predicate in predicates.iter() {
@ -1365,7 +1365,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let trait_pred = self.resolve_vars_if_possible(trait_pred); let trait_pred = self.resolve_vars_if_possible(trait_pred);
let ty = trait_pred.skip_binder().self_ty(); let ty = trait_pred.skip_binder().self_ty();
let is_object_safe = match ty.kind() { let is_object_safe = match ty.kind() {
ty::Dynamic(predicates, _, _) => { ty::Dynamic(predicates, _, ty::Dyn) => {
// If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`. // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
predicates predicates
.principal_def_id() .principal_def_id()

View file

@ -890,9 +890,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut nested = vec![]; let mut nested = vec![];
match (source.kind(), target.kind()) { match (source.kind(), target.kind()) {
// Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping).
(&ty::Dynamic(ref data_a, r_a, repr_a), &ty::Dynamic(ref data_b, r_b, repr_b)) (&ty::Dynamic(ref data_a, r_a, ty::Dyn), &ty::Dynamic(ref data_b, r_b, ty::Dyn)) => {
if repr_a == repr_b =>
{
// See `assemble_candidates_for_unsizing` for more info. // See `assemble_candidates_for_unsizing` for more info.
// We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`.
let iter = data_a let iter = data_a
@ -911,7 +909,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map(ty::Binder::dummy), .map(ty::Binder::dummy),
); );
let existential_predicates = tcx.mk_poly_existential_predicates(iter); let existential_predicates = tcx.mk_poly_existential_predicates(iter);
let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_b); let source_trait = tcx.mk_dynamic(existential_predicates, r_b, ty::Dyn);
// Require that the traits involved in this upcast are **equal**; // Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed. // only the **lifetime bound** is changed.
@ -938,7 +936,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
} }
// `T` -> `Trait` // `T` -> `Trait`
(_, &ty::Dynamic(ref data, r, _repr)) => { (_, &ty::Dynamic(ref data, r, ty::Dyn)) => {
let mut object_dids = data.auto_traits().chain(data.principal_def_id()); let mut object_dids = data.auto_traits().chain(data.principal_def_id());
if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) {
return Err(TraitNotObjectSafe(did)); return Err(TraitNotObjectSafe(did));

View file

@ -326,7 +326,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
)), )),
}) })
} }
ty::Dynamic(predicates, region, _repr) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy { // FIXME(dyn-star): handle the dynamic kind (dyn or dyn*)
ty::Dynamic(predicates, region, _kind) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy {
bounds: predicates.lower_into(interner), bounds: predicates.lower_into(interner),
lifetime: region.lower_into(interner), lifetime: region.lower_into(interner),
}), }),

View file

@ -19,26 +19,33 @@ use rustc_data_structures::stable_hasher::HashStable;
use rustc_serialize::{Decodable, Decoder, Encodable}; use rustc_serialize::{Decodable, Decoder, Encodable};
/// Specifies how a trait object is represented. /// Specifies how a trait object is represented.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] #[derive(
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
Encodable,
Decodable,
HashStable_Generic
)]
pub enum DynKind { pub enum DynKind {
/// An unsized `dyn Trait` object /// An unsized `dyn Trait` object
Dyn, Dyn,
/// A sized `dyn* Trait` object /// A sized `dyn* Trait` object
///
/// These objects are represented as a `(data, vtable)` pair where `data` is a ptr-sized value
/// (often a pointer to the real object, but not necessarily) and `vtable` is a pointer to
/// the vtable for `dyn* Trait`. The representation is essentially the same as `&dyn Trait`
/// or similar, but the drop function included in the vtable is responsible for freeing the
/// underlying storage if needed. This allows a `dyn*` object to be treated agnostically with
/// respect to whether it points to a `Box<T>`, `Rc<T>`, etc.
DynStar, DynStar,
} }
// Manually implemented because deriving HashStable requires rustc_query_system, which would
// create a cyclic dependency.
impl<CTX> HashStable<CTX> for DynKind {
fn hash_stable(
&self,
hcx: &mut CTX,
hasher: &mut rustc_data_structures::stable_hasher::StableHasher,
) {
std::mem::discriminant(self).hash_stable(hcx, hasher);
}
}
/// Defines the kinds of types used by the type system. /// Defines the kinds of types used by the type system.
/// ///
/// Types written by the user start out as `hir::TyKind` and get /// Types written by the user start out as `hir::TyKind` and get

View file

@ -105,7 +105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(match *t.kind() { Ok(match *t.kind() {
ty::Slice(_) | ty::Str => Some(PointerKind::Length), ty::Slice(_) | ty::Str => Some(PointerKind::Length),
ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())), ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())),
ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() {
None => Some(PointerKind::Thin), None => Some(PointerKind::Thin),
Some(f) => { Some(f) => {
@ -142,6 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::Generator(..) | ty::Generator(..)
| ty::Adt(..) | ty::Adt(..)
| ty::Never | ty::Never
| ty::Dynamic(_, _, ty::DynStar)
| ty::Error(_) => { | ty::Error(_) => {
let reported = self let reported = self
.tcx .tcx
@ -246,7 +247,7 @@ fn check_dyn_star_cast<'tcx>(
let cause = ObligationCause::new( let cause = ObligationCause::new(
expr.span, expr.span,
fcx.body_id, fcx.body_id,
// FIXME: Use a better obligation cause code // FIXME(dyn-star): Use a better obligation cause code
ObligationCauseCode::MiscObligation, ObligationCauseCode::MiscObligation,
); );
@ -927,10 +928,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
// FIXME: this needs more conditions... // FIXME(dyn-star): this needs more conditions...
(_, DynStar) => Ok(CastKind::DynStarCast), (_, DynStar) => Ok(CastKind::DynStarCast),
// FIXME: do we want to allow dyn* upcasting or other casts? // FIXME(dyn-star): do we want to allow dyn* upcasting or other casts?
(DynStar, _) => Err(CastError::IllegalCast), (DynStar, _) => Err(CastError::IllegalCast),
} }
} }

View file

@ -1,6 +1,6 @@
// run-pass // run-pass
#![feature(dyn_star)] #![feature(dyn_star)]
#![allow(unused)] #![allow(unused, incomplete_features)]
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -1,6 +1,7 @@
// run-pass // run-pass
// check-run-results // check-run-results
#![feature(dyn_star)] #![feature(dyn_star)]
#![allow(incomplete_features)]
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -1,4 +1,5 @@
#![feature(dyn_star)] #![feature(dyn_star)]
#![allow(incomplete_features)]
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -1,5 +1,5 @@
error[E0277]: the trait bound `{integer}: Foo` is not satisfied error[E0277]: the trait bound `{integer}: Foo` is not satisfied
--> $DIR/error.rs:9:27 --> $DIR/error.rs:10:27
| |
LL | let dyn_i: dyn* Foo = i as dyn* Foo; LL | let dyn_i: dyn* Foo = i as dyn* Foo;
| ^ the trait `Foo` is not implemented for `{integer}` | ^ the trait `Foo` is not implemented for `{integer}`

View file

@ -1,5 +1,6 @@
// run-pass // run-pass
#![feature(dyn_star)] #![feature(dyn_star)]
#![allow(incomplete_features)]
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -1,5 +1,6 @@
// run-pass // run-pass
#![feature(dyn_star)] #![feature(dyn_star)]
#![allow(incomplete_features)]
trait Foo { trait Foo {
fn get(&self) -> usize; fn get(&self) -> usize;

View file

@ -3,6 +3,7 @@
// check-pass // check-pass
#![feature(dyn_star)] #![feature(dyn_star)]
#![allow(incomplete_features)]
pub fn dyn_star_parameter(_: dyn* Send) { pub fn dyn_star_parameter(_: dyn* Send) {
} }

View file

@ -2,18 +2,11 @@ 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::{ use rustc_typeck::check::{cast::{self, CastCheckResult}, FnCtxt, Inherited};
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>( pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
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))
@ -36,9 +29,7 @@ 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::{ use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
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)
@ -49,12 +40,7 @@ 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>( fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
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;
@ -62,7 +48,10 @@ fn check_cast<'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!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors"); assert!(
!fn_ctxt.errors_reported_since_creation(),
"Newly created FnCtxt contained errors"
);
if let CastCheckResult::Deferred(check) = cast::check_cast( if let CastCheckResult::Deferred(check) = cast::check_cast(
&fn_ctxt, e, from_ty, to_ty, &fn_ctxt, e, from_ty, to_ty,

View file

@ -18,11 +18,7 @@ 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>( pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
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 {
@ -37,18 +33,10 @@ pub fn is_min_const_fn<'a, 'tcx>(
| ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Trait(..) | ty::PredicateKind::Trait(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
ty::PredicateKind::ObjectSafe(_) => { ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
panic!("object safe 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::ClosureKind(..) => { ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate),
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 {
@ -89,23 +77,22 @@ 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::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
| 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((
@ -115,11 +102,11 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
.into(), .into(),
)); ));
} }
} },
} }
} }
} },
_ => {} _ => {},
} }
} }
Ok(()) Ok(())
@ -133,13 +120,10 @@ fn check_rvalue<'tcx>(
span: Span, span: Span,
) -> McfResult { ) -> McfResult {
match rvalue { match rvalue {
Rvalue::ThreadLocalRef(_) => { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
Err((span, "cannot access thread local storage in const fn".into())) Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
} 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)
@ -152,9 +136,7 @@ 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::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
| PointerCast::ClosureFnPointer(_)
| PointerCast::ReifyFnPointer,
), ),
_, _,
_, _,
@ -164,10 +146,7 @@ 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(( return Err((span, "unsizing casts are only allowed for references right now".into()));
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() {
@ -178,14 +157,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, _, _) => { Rvalue::Cast(CastKind::DynStar, _, _) => {
// FIXME(dyn-star) // FIXME(dyn-star)
unimplemented!() 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)?;
@ -194,12 +173,13 @@ 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((span, "only int, `bool` and `char` operations are stable in const fn".into())) Err((
span,
"only int, `bool` and `char` operations are stable in const fn".into(),
))
} }
} },
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
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() {
@ -207,13 +187,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(())
} },
} }
} }
@ -228,7 +208,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
@ -238,15 +218,13 @@ 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::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
dst, rustc_middle::mir::CopyNonOverlapping { dst, src, count },
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(_)
@ -257,12 +235,7 @@ fn check_statement<'tcx>(
} }
} }
fn check_operand<'tcx>( fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
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) {
@ -272,12 +245,7 @@ fn check_operand<'tcx>(
} }
} }
fn check_place<'tcx>( fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
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;
@ -290,12 +258,12 @@ fn check_place<'tcx>(
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(_) => {},
} }
} }
@ -321,16 +289,18 @@ 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 { discr, switch_ty: _, targets: _ } => { TerminatorKind::SwitchInt {
check_operand(tcx, discr, span, body) discr,
} 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,
@ -375,15 +345,17 @@ 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 { cond, expected: _, msg: _, target: _, cleanup: _ } => { TerminatorKind::Assert {
check_operand(tcx, cond, span, body) cond,
} expected: _,
msg: _,
target: _,
cleanup: _,
} => check_operand(tcx, cond, span, body),
TerminatorKind::InlineAsm { .. } => { TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
Err((span, "cannot use inline assembly in const fn".into()))
}
} }
} }

View file

@ -14,9 +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, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy,
ProjectionTy, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
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};
@ -167,7 +166,9 @@ 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.type_implements_trait(trait_id, ty, ty_params, param_env).must_apply_modulo_regions() infcx
.type_implements_trait(trait_id, ty, ty_params, param_env)
.must_apply_modulo_regions()
}) })
} }
@ -184,14 +185,11 @@ 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::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, 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) {
@ -202,7 +200,7 @@ 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() {
@ -212,7 +210,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
} }
} }
false false
} },
_ => false, _ => false,
} }
} }
@ -222,11 +220,7 @@ 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>( pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
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())
} }
@ -246,14 +240,15 @@ 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.fields.iter().all(|field| { variant
is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache) .fields
}) .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
}), }),
} }
@ -278,9 +273,7 @@ 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) => { ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(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,
} }
@ -320,9 +313,11 @@ 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, _) => { ty::Adt(adt, _) => cx
cx.tcx.lang_items().require(lang_item).map_or(false, |li| li == adt.did()) .tcx
} .lang_items()
.require(lang_item)
.map_or(false, |li| li == adt.did()),
_ => false, _ => false,
} }
} }
@ -347,11 +342,7 @@ 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>( fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
seen: &mut FxHashSet<Ty<'tcx>>,
) -> bool {
if !seen.insert(ty) { if !seen.insert(ty) {
return false; return false;
} }
@ -402,7 +393,11 @@ 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() { peel(*ty, count + 1) } else { (ty, count) } if let ty::Ref(_, ty, _) = ty.kind() {
peel(*ty, count + 1)
} else {
(ty, count)
}
} }
peel(ty, 0) peel(ty, 0)
} }
@ -457,18 +452,17 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
return false; return false;
} }
substs_a.iter().zip(substs_b.iter()).all(|(arg_a, arg_b)| { substs_a
match (arg_a.unpack(), arg_b.unpack()) { .iter()
(GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => { .zip(substs_b.iter())
inner_a == inner_b .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
} (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,
} }
} }
@ -484,10 +478,7 @@ 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( pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
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| {
@ -517,7 +508,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])),
} }
@ -526,10 +517,7 @@ 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( pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
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() {
@ -540,7 +528,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]),
@ -559,15 +547,17 @@ 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 { id } else { None } if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
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) = if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
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())
@ -581,14 +571,12 @@ 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.as_local().and_then(|id| { let decl = id
cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)) .as_local()
}); .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) => { ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
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, _, _) => {
@ -601,19 +589,16 @@ 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| { .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
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) _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
.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,
@ -644,7 +629,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 =>
@ -654,7 +639,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()));
} },
_ => (), _ => (),
} }
} }
@ -662,10 +647,7 @@ 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>( fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'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();
@ -691,10 +673,8 @@ fn sig_for_projection<'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() =>
{
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;
@ -703,7 +683,7 @@ fn sig_for_projection<'tcx>(
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),
); );
} },
_ => (), _ => (),
} }
} }
@ -797,10 +777,7 @@ pub fn for_each_top_level_late_bound_region<B>(
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
} }
fn visit_binder<T: TypeVisitable<'tcx>>( fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
&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;
@ -814,27 +791,19 @@ 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) => { Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
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::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>( pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
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;
}; };