Directly construct Inherited.

This commit is contained in:
Camille GILLOT 2023-03-09 19:53:59 +00:00
parent 35a0961bbc
commit 83da9a89d8
4 changed files with 148 additions and 173 deletions

View file

@ -4,7 +4,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap; use rustc_hir::HirIdMap;
use rustc_infer::infer;
use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt};
use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
@ -73,40 +72,16 @@ impl<'tcx> Deref for Inherited<'tcx> {
} }
} }
/// A temporary returned by `Inherited::build(...)`. This is necessary
/// for multiple `InferCtxt` to share the same `typeck_results`
/// without using `Rc` or something similar.
pub struct InheritedBuilder<'tcx> {
infcx: infer::InferCtxtBuilder<'tcx>,
typeck_results: RefCell<ty::TypeckResults<'tcx>>,
}
impl<'tcx> Inherited<'tcx> { impl<'tcx> Inherited<'tcx> {
pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
InheritedBuilder { let infcx = tcx
infcx: tcx .infer_ctxt()
.infer_ctxt() .ignoring_regions()
.ignoring_regions() .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
.with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)), .build();
typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
}
}
}
impl<'tcx> InheritedBuilder<'tcx> {
pub fn enter<F, R>(mut self, f: F) -> R
where
F: FnOnce(&Inherited<'tcx>) -> R,
{
f(&Inherited::new(self.infcx.build(), self.typeck_results))
}
}
impl<'tcx> Inherited<'tcx> {
fn new(infcx: InferCtxt<'tcx>, typeck_results: RefCell<ty::TypeckResults<'tcx>>) -> Self {
let tcx = infcx.tcx;
Inherited { Inherited {
typeck_results, typeck_results,

View file

@ -45,13 +45,14 @@ mod rvalue_scopes;
mod upvar; mod upvar;
mod writeback; mod writeback;
pub use diverges::Diverges; pub use fn_ctxt::FnCtxt;
pub use expectation::Expectation; pub use inherited::Inherited;
pub use fn_ctxt::*;
pub use inherited::{Inherited, InheritedBuilder};
use crate::check::check_fn; use crate::check::check_fn;
use crate::coercion::DynamicCoerceMany; use crate::coercion::DynamicCoerceMany;
use crate::diverges::Diverges;
use crate::expectation::Expectation;
use crate::fn_ctxt::RawTy;
use crate::gather_locals::GatherLocalsVisitor; use crate::gather_locals::GatherLocalsVisitor;
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_errors::{ use rustc_errors::{
@ -206,135 +207,135 @@ fn typeck_with_fallback<'tcx>(
}); });
let body = tcx.hir().body(body_id); let body = tcx.hir().body(body_id);
let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id);
let param_env = tcx.param_env(def_id); let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { param_env.without_const()
param_env.without_const() } else {
param_env
};
let inh = Inherited::new(tcx, def_id);
let mut fcx = FnCtxt::new(&inh, param_env, def_id);
if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
} else { } else {
param_env tcx.fn_sig(def_id).subst_identity()
}; };
let mut fcx = FnCtxt::new(&inh, param_env, def_id);
if let Some(hir::FnSig { header, decl, .. }) = fn_sig { check_abi(tcx, id, span, fn_sig.abi());
let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
} else {
tcx.fn_sig(def_id).subst_identity()
};
check_abi(tcx, id, span, fn_sig.abi()); // Compute the function signature from point of view of inside the fn.
let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
let fn_sig = fcx.normalize(body.value.span, fn_sig);
// Compute the function signature from point of view of inside the fn. check_fn(&mut fcx, fn_sig, decl, def_id, body, None);
let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); } else {
let fn_sig = fcx.normalize(body.value.span, fn_sig); let expected_type = body_ty
.and_then(|ty| match ty.kind {
check_fn(&mut fcx, fn_sig, decl, def_id, body, None); hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)),
} else { _ => None,
let expected_type = body_ty })
.and_then(|ty| match ty.kind { .unwrap_or_else(|| match tcx.hir().get(id) {
hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)), Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) {
_ => None, Node::Expr(&hir::Expr {
}) kind: hir::ExprKind::ConstBlock(ref anon_const),
.unwrap_or_else(|| match tcx.hir().get(id) { ..
Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) { }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
Node::Expr(&hir::Expr { kind: TypeVariableOriginKind::TypeInference,
kind: hir::ExprKind::ConstBlock(ref anon_const), span,
.. }),
}) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. })
if anon_const.hir_id == id =>
{
fcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference, kind: TypeVariableOriginKind::TypeInference,
span, span,
}), })
Node::Ty(&hir::Ty { }
kind: hir::TyKind::Typeof(ref anon_const), .. Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
}) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
kind: TypeVariableOriginKind::TypeInference, let operand_ty = asm.operands.iter().find_map(|(op, _op_sp)| match op {
span, hir::InlineAsmOperand::Const { anon_const }
}), if anon_const.hir_id == id =>
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) {
| Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { // Inline assembly constants must be integers.
let operand_ty = Some(fcx.next_int_var())
asm.operands.iter().find_map(|(op, _op_sp)| match op { }
hir::InlineAsmOperand::Const { anon_const } hir::InlineAsmOperand::SymFn { anon_const }
if anon_const.hir_id == id => if anon_const.hir_id == id =>
{ {
// Inline assembly constants must be integers. Some(fcx.next_ty_var(TypeVariableOrigin {
Some(fcx.next_int_var()) kind: TypeVariableOriginKind::MiscVariable,
} span,
hir::InlineAsmOperand::SymFn { anon_const } }))
if anon_const.hir_id == id => }
{ _ => None,
Some(fcx.next_ty_var(TypeVariableOrigin { });
kind: TypeVariableOriginKind::MiscVariable, operand_ty.unwrap_or_else(fallback)
span, }
}))
}
_ => None,
});
operand_ty.unwrap_or_else(fallback)
}
_ => fallback(),
},
_ => fallback(), _ => fallback(),
}); },
_ => fallback(),
});
let expected_type = fcx.normalize(body.value.span, expected_type); let expected_type = fcx.normalize(body.value.span, expected_type);
fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
// Gather locals in statics (because of block expressions). // Gather locals in statics (because of block expressions).
GatherLocalsVisitor::new(&fcx).visit_body(body); GatherLocalsVisitor::new(&fcx).visit_body(body);
fcx.check_expr_coercable_to_type(&body.value, expected_type, None); fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
fcx.write_ty(id, expected_type); fcx.write_ty(id, expected_type);
}; };
fcx.type_inference_fallback(); fcx.type_inference_fallback();
// Even though coercion casts provide type hints, we check casts after fallback for // Even though coercion casts provide type hints, we check casts after fallback for
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion. // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
fcx.check_casts(); fcx.check_casts();
fcx.select_obligations_where_possible(|_| {}); fcx.select_obligations_where_possible(|_| {});
// Closure and generator analysis may run after fallback // Closure and generator analysis may run after fallback
// because they don't constrain other type variables. // because they don't constrain other type variables.
// Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
let prev_constness = fcx.param_env.constness(); let prev_constness = fcx.param_env.constness();
fcx.param_env = fcx.param_env.without_const(); fcx.param_env = fcx.param_env.without_const();
fcx.closure_analyze(body); fcx.closure_analyze(body);
fcx.param_env = fcx.param_env.with_constness(prev_constness); fcx.param_env = fcx.param_env.with_constness(prev_constness);
assert!(fcx.deferred_call_resolutions.borrow().is_empty()); assert!(fcx.deferred_call_resolutions.borrow().is_empty());
// Before the generator analysis, temporary scopes shall be marked to provide more // Before the generator analysis, temporary scopes shall be marked to provide more
// precise information on types to be captured. // precise information on types to be captured.
fcx.resolve_rvalue_scopes(def_id.to_def_id()); fcx.resolve_rvalue_scopes(def_id.to_def_id());
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
let ty = fcx.normalize(span, ty); let ty = fcx.normalize(span, ty);
fcx.require_type_is_sized(ty, span, code); fcx.require_type_is_sized(ty, span, code);
} }
fcx.select_obligations_where_possible(|_| {}); fcx.select_obligations_where_possible(|_| {});
debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
// This must be the last thing before `report_ambiguity_errors`. // This must be the last thing before `report_ambiguity_errors`.
fcx.resolve_generator_interiors(def_id.to_def_id()); fcx.resolve_generator_interiors(def_id.to_def_id());
debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
if let None = fcx.infcx.tainted_by_errors() { if let None = fcx.infcx.tainted_by_errors() {
fcx.report_ambiguity_errors(); fcx.report_ambiguity_errors();
} }
if let None = fcx.infcx.tainted_by_errors() { if let None = fcx.infcx.tainted_by_errors() {
fcx.check_transmutes(); fcx.check_transmutes();
} }
fcx.check_asms(); fcx.check_asms();
fcx.infcx.skip_region_resolution(); fcx.infcx.skip_region_resolution();
fcx.resolve_type_vars_in_body(body) let typeck_results = fcx.resolve_type_vars_in_body(body);
});
// Consistency check our TypeckResults instance can hold all ItemLocalIds // Consistency check our TypeckResults instance can hold all ItemLocalIds
// it will need to hold. // it will need to hold.

View file

@ -369,10 +369,10 @@ 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)
&& Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| { && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id); && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
fn_ctxt.can_coerce(ty, output_ty) && 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

@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>(
let hir_id = e.hir_id; let hir_id = e.hir_id;
let local_def_id = hir_id.owner.def_id; let local_def_id = hir_id.owner.def_id;
Inherited::build(cx.tcx, local_def_id).enter(|inherited| { let inherited = Inherited::new(cx.tcx, local_def_id);
let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_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"
);
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,
) {
let res = check.do_check(&fn_ctxt);
// do_check's documentation says that it might return Ok and create
// errors in the fcx instead of returning Err in some cases. Those cases
// should be filtered out before getting here.
assert!( assert!(
!fn_ctxt.errors_reported_since_creation(), !fn_ctxt.errors_reported_since_creation(),
"Newly created FnCtxt contained errors" "`fn_ctxt` contained errors after cast check!"
); );
if let Ok(check) = cast::CastCheck::new( res.ok()
&fn_ctxt, } else {
e, None
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,
) {
let res = check.do_check(&fn_ctxt);
// do_check's documentation says that it might return Ok and create
// errors in the fcx instead of returning Err in some cases. Those cases
// should be filtered out before getting here.
assert!(
!fn_ctxt.errors_reported_since_creation(),
"`fn_ctxt` contained errors after cast check!"
);
res.ok()
} else {
None
}
})
} }