Stop exporting `TypeckRootCtxt` and `FnCtxt`.

While they have many convenient APIs, it is better to expose dedicated functions for them
This commit is contained in:
Oli Scherer 2024-04-08 10:36:15 +00:00
parent 7a495cc13d
commit a9edbfda32
9 changed files with 57 additions and 63 deletions

View File

@ -40,17 +40,19 @@ use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::cast::{CastKind, CastTy};
use rustc_middle::ty::error::TypeError; use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef};
use rustc_session::lint; use rustc_session::lint;
use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::DUMMY_SP;
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
/// Reifies a cast check to be checked once we have full type information for /// Reifies a cast check to be checked once we have full type information for
/// a function context. /// a function context.
#[derive(Debug)] #[derive(Debug)]
pub struct CastCheck<'tcx> { pub(crate) struct CastCheck<'tcx> {
/// The expression whose value is being casted /// The expression whose value is being casted
expr: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>,
/// The source type for the cast expression /// The source type for the cast expression
@ -60,8 +62,6 @@ pub struct CastCheck<'tcx> {
cast_ty: Ty<'tcx>, cast_ty: Ty<'tcx>,
cast_span: Span, cast_span: Span,
span: Span, span: Span,
/// whether the cast is made in a const context or not.
pub constness: hir::Constness,
} }
/// The kind of pointer and associated metadata (thin, length or vtable) - we /// The kind of pointer and associated metadata (thin, length or vtable) - we
@ -194,18 +194,45 @@ fn make_invalid_casting_error<'a, 'tcx>(
) )
} }
/// If a cast from `from_ty` to `to_ty` is valid, returns a `Some` containing the kind
/// of the cast.
///
/// This is a helper used from clippy.
pub fn check_cast<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
e: &'tcx hir::Expr<'tcx>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
) -> Option<CastKind> {
let hir_id = e.hir_id;
let local_def_id = hir_id.owner.def_id;
let root_ctxt = crate::TypeckRootCtxt::new(tcx, local_def_id);
let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, local_def_id);
if let Ok(check) = CastCheck::new(
&fn_ctxt, e, from_ty, to_ty,
// We won't show any errors to the user, so the span is irrelevant here.
DUMMY_SP, DUMMY_SP,
) {
check.do_check(&fn_ctxt).ok()
} else {
None
}
}
impl<'a, 'tcx> CastCheck<'tcx> { impl<'a, 'tcx> CastCheck<'tcx> {
pub fn new( pub(crate) fn new(
fcx: &FnCtxt<'a, 'tcx>, fcx: &FnCtxt<'a, 'tcx>,
expr: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>,
expr_ty: Ty<'tcx>, expr_ty: Ty<'tcx>,
cast_ty: Ty<'tcx>, cast_ty: Ty<'tcx>,
cast_span: Span, cast_span: Span,
span: Span, span: Span,
constness: hir::Constness,
) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> {
let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span);
let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span };
// For better error messages, check for some obviously unsized // For better error messages, check for some obviously unsized
// cases now. We do a more thorough check at the end, once // cases now. We do a more thorough check at the end, once
@ -644,7 +671,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
/// Checks a cast, and report an error if one exists. In some cases, this /// Checks a cast, and report an error if one exists. In some cases, this
/// can return Ok and create type errors in the fcx rather than returning /// can return Ok and create type errors in the fcx rather than returning
/// directly. coercion-cast is handled in check instead of here. /// directly. coercion-cast is handled in check instead of here.
pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> { fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
use rustc_middle::ty::cast::CastTy::*; use rustc_middle::ty::cast::CastTy::*;
use rustc_middle::ty::cast::IntTy::*; use rustc_middle::ty::cast::IntTy::*;

View File

@ -1318,6 +1318,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
/// Check whether `ty` can be coerced to `output_ty`.
/// Used from clippy.
pub fn can_coerce<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
ty: Ty<'tcx>,
output_ty: Ty<'tcx>,
) -> bool {
let root_ctxt = crate::typeck_root_ctxt::TypeckRootCtxt::new(tcx, body_id);
let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, body_id);
fn_ctxt.can_coerce(ty, output_ty)
}
/// CoerceMany encapsulates the pattern you should use when you have /// CoerceMany encapsulates the pattern you should use when you have
/// many expressions that are all getting coerced to a common /// many expressions that are all getting coerced to a common
/// type. This arises, for example, when you have a match (the result /// type. This arises, for example, when you have a match (the result

View File

@ -1390,15 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else { } else {
// Defer other checks until we're done type checking. // Defer other checks until we're done type checking.
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
match cast::CastCheck::new( match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) {
self,
e,
t_expr,
t_cast,
t.span,
expr.span,
hir::Constness::NotConst,
) {
Ok(cast_check) => { Ok(cast_check) => {
debug!( debug!(
"check_expr_cast: deferring cast from {:?} to {:?}: {:?}", "check_expr_cast: deferring cast from {:?} to {:?}: {:?}",

View File

@ -38,7 +38,7 @@ use std::ops::Deref;
/// ///
/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt
/// [`InferCtxt`]: infer::InferCtxt /// [`InferCtxt`]: infer::InferCtxt
pub struct FnCtxt<'a, 'tcx> { pub(crate) struct FnCtxt<'a, 'tcx> {
pub(super) body_id: LocalDefId, pub(super) body_id: LocalDefId,
/// The parameter environment used for proving trait obligations /// The parameter environment used for proving trait obligations

View File

@ -42,8 +42,9 @@ mod typeck_root_ctxt;
mod upvar; mod upvar;
mod writeback; mod writeback;
pub use fn_ctxt::FnCtxt; pub use coercion::can_coerce;
pub use typeck_root_ctxt::TypeckRootCtxt; use fn_ctxt::FnCtxt;
use typeck_root_ctxt::TypeckRootCtxt;
use crate::check::check_fn; use crate::check::check_fn;
use crate::coercion::DynamicCoerceMany; use crate::coercion::DynamicCoerceMany;

View File

@ -27,7 +27,7 @@ use std::ops::Deref;
/// `bar()` will each have their own `FnCtxt`, but they will /// `bar()` will each have their own `FnCtxt`, but they will
/// share the inference context, will process obligations together, /// share the inference context, will process obligations together,
/// can access each other's local types (scoping permitted), etc. /// can access each other's local types (scoping permitted), etc.
pub struct TypeckRootCtxt<'tcx> { pub(crate) struct TypeckRootCtxt<'tcx> {
pub(super) infcx: InferCtxt<'tcx>, pub(super) infcx: InferCtxt<'tcx>,
pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>, pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>,

View File

@ -12,7 +12,6 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node};
use rustc_hir_typeck::{FnCtxt, TypeckRootCtxt};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::Mutability; use rustc_middle::mir::Mutability;
@ -437,9 +436,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
Node::Item(item) => { Node::Item(item) => {
if let ItemKind::Fn(_, _, body_id) = &item.kind if let ItemKind::Fn(_, _, body_id) = &item.kind
&& let output_ty = return_ty(cx, item.owner_id) && let output_ty = return_ty(cx, item.owner_id)
&& let root_ctxt = TypeckRootCtxt::new(cx.tcx, item.owner_id.def_id) && rustc_hir_typeck::can_coerce(cx.tcx, cx.param_env, item.owner_id.def_id, ty, output_ty)
&& let fn_ctxt = FnCtxt::new(&root_ctxt, cx.param_env, item.owner_id.def_id)
&& fn_ctxt.can_coerce(ty, output_ty)
{ {
if has_lifetime(output_ty) && has_lifetime(ty) { if has_lifetime(output_ty) && has_lifetime(ty) {
return false; return false;

View File

@ -1,4 +1,4 @@
use super::utils::check_cast; use rustc_hir_typeck::cast::check_cast;
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(
) -> bool { ) -> bool {
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let mut sugg = match check_cast(cx, e, from_ty, to_ty) { let mut sugg = match check_cast(cx.tcx, cx.param_env, e, from_ty, to_ty) {
Some(FnPtrAddrCast | PtrAddrCast) if const_context => return false, Some(FnPtrAddrCast | PtrAddrCast) if const_context => return false,
Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => {
Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)

View File

@ -1,10 +1,5 @@
use rustc_hir as hir;
use rustc_hir::Expr;
use rustc_hir_typeck::{cast, FnCtxt, TypeckRootCtxt};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::cast::CastKind;
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use rustc_span::DUMMY_SP;
// 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
@ -20,35 +15,3 @@ pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx
false false
} }
} }
/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
/// 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
/// messages. This function will panic if that occurs.
pub(super) 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 local_def_id = hir_id.owner.def_id;
let root_ctxt = TypeckRootCtxt::new(cx.tcx, local_def_id);
let fn_ctxt = FnCtxt::new(&root_ctxt, cx.param_env, local_def_id);
if let Ok(check) = cast::CastCheck::new(
&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.
DUMMY_SP,
DUMMY_SP,
hir::Constness::NotConst,
) {
check.do_check(&fn_ctxt).ok()
} else {
None
}
}