blob: afba812a8e7bb527238b748fb4372fc2042aef5f [file] [log] [blame]
mod _impl;
mod adjust_fulfillment_errors;
mod arg_matrix;
mod checks;
mod inspect_obligations;
mod suggestions;
use rustc_errors::ErrorGuaranteed;
use crate::coercion::DynamicCoerceMany;
use crate::fallback::DivergingFallbackBehavior;
use crate::fn_ctxt::checks::DivergingBlockBehavior;
use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt};
use hir::def_id::CRATE_DEF_ID;
use rustc_errors::DiagCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::infer;
use rustc_infer::infer::error_reporting::sub_relations::SubRelations;
use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, sym, Span, DUMMY_SP};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
use std::cell::{Cell, RefCell};
use std::ops::Deref;
/// The `FnCtxt` stores type-checking context needed to type-check bodies of
/// functions, closures, and `const`s, including performing type inference
/// with [`InferCtxt`].
///
/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures*
/// and thus does not perform type inference.
///
/// See [`ItemCtxt`]'s docs for more.
///
/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt
/// [`InferCtxt`]: infer::InferCtxt
pub(crate) struct FnCtxt<'a, 'tcx> {
pub(super) body_id: LocalDefId,
/// The parameter environment used for proving trait obligations
/// in this function. This can change when we descend into
/// closures (as they bring new things into scope), hence it is
/// not part of `Inherited` (as of the time of this writing,
/// closures do not yet change the environment, but they will
/// eventually).
pub(super) param_env: ty::ParamEnv<'tcx>,
/// If `Some`, this stores coercion information for returned
/// expressions. If `None`, this is in a context where return is
/// inappropriate, such as a const expression.
///
/// This is a `RefCell<DynamicCoerceMany>`, which means that we
/// can track all the return expressions and then use them to
/// compute a useful coercion from the set, similar to a match
/// expression or other branching context. You can use methods
/// like `expected_ty` to access the declared return type (if
/// any).
pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
/// First span of a return site that we find. Used in error messages.
pub(super) ret_coercion_span: Cell<Option<Span>>,
pub(super) coroutine_types: Option<CoroutineTypes<'tcx>>,
/// Whether the last checked node generates a divergence (e.g.,
/// `return` will set this to `Always`). In general, when entering
/// an expression or other node in the tree, the initial value
/// indicates whether prior parts of the containing expression may
/// have diverged. It is then typically set to `Maybe` (and the
/// old value remembered) for processing the subparts of the
/// current expression. As each subpart is processed, they may set
/// the flag to `Always`, etc. Finally, at the end, we take the
/// result and "union" it with the original value, so that when we
/// return the flag indicates if any subpart of the parent
/// expression (up to and including this part) has diverged. So,
/// if you read it after evaluating a subexpression `X`, the value
/// you get indicates whether any subexpression that was
/// evaluating up to and including `X` diverged.
///
/// We currently use this flag only for diagnostic purposes:
///
/// - To warn about unreachable code: if, after processing a
/// sub-expression but before we have applied the effects of the
/// current node, we see that the flag is set to `Always`, we
/// can issue a warning. This corresponds to something like
/// `foo(return)`; we warn on the `foo()` expression. (We then
/// update the flag to `WarnedAlways` to suppress duplicate
/// reports.) Similarly, if we traverse to a fresh statement (or
/// tail expression) from an `Always` setting, we will issue a
/// warning. This corresponds to something like `{return;
/// foo();}` or `{return; 22}`, where we would warn on the
/// `foo()` or `22`.
///
/// An expression represents dead code if, after checking it,
/// the diverges flag is set to something other than `Maybe`.
pub(super) diverges: Cell<Diverges>,
/// If one of the function arguments is a never pattern, this counts as diverging code. This
/// affect typechecking of the function body.
pub(super) function_diverges_because_of_empty_arguments: Cell<Diverges>,
/// Whether the currently checked node is the whole body of the function.
pub(super) is_whole_body: Cell<bool>,
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
pub(super) root_ctxt: &'a TypeckRootCtxt<'tcx>,
pub(super) fallback_has_occurred: Cell<bool>,
pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
pub(super) diverging_block_behavior: DivergingBlockBehavior,
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn new(
root_ctxt: &'a TypeckRootCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
) -> FnCtxt<'a, 'tcx> {
let (diverging_fallback_behavior, diverging_block_behavior) =
parse_never_type_options_attr(root_ctxt.tcx);
FnCtxt {
body_id,
param_env,
ret_coercion: None,
ret_coercion_span: Cell::new(None),
coroutine_types: None,
diverges: Cell::new(Diverges::Maybe),
function_diverges_because_of_empty_arguments: Cell::new(Diverges::Maybe),
is_whole_body: Cell::new(false),
enclosing_breakables: RefCell::new(EnclosingBreakables {
stack: Vec::new(),
by_id: Default::default(),
}),
root_ctxt,
fallback_has_occurred: Cell::new(false),
diverging_fallback_behavior,
diverging_block_behavior,
}
}
pub(crate) fn dcx(&self) -> &'tcx DiagCtxt {
self.tcx.dcx()
}
pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
ObligationCause::new(span, self.body_id, code)
}
pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
self.cause(span, ObligationCauseCode::Misc)
}
pub fn sess(&self) -> &Session {
self.tcx.sess
}
/// Creates an `TypeErrCtxt` with a reference to the in-progress
/// `TypeckResults` which is used for diagnostics.
/// Use [`InferCtxt::err_ctxt`] to start one without a `TypeckResults`.
///
/// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
let mut sub_relations = SubRelations::default();
sub_relations.add_constraints(
self,
self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate),
);
TypeErrCtxt {
infcx: &self.infcx,
sub_relations: RefCell::new(sub_relations),
typeck_results: Some(self.typeck_results.borrow()),
fallback_has_occurred: self.fallback_has_occurred.get(),
normalize_fn_sig: Box::new(|fn_sig| {
if fn_sig.has_escaping_bound_vars() {
return fn_sig;
}
self.probe(|_| {
let ocx = ObligationCtxt::new(self);
let normalized_fn_sig =
ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
if ocx.select_all_or_error().is_empty() {
let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig);
if !normalized_fn_sig.has_infer() {
return normalized_fn_sig;
}
}
fn_sig
})
}),
autoderef_steps: Box::new(|ty| {
let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors();
let mut steps = vec![];
while let Some((ty, _)) = autoderef.next() {
steps.push((ty, autoderef.current_obligations()));
}
steps
}),
}
}
}
impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
type Target = TypeckRootCtxt<'tcx>;
fn deref(&self) -> &Self::Target {
self.root_ctxt
}
}
impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> {
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
self.tcx
}
fn item_def_id(&self) -> DefId {
self.body_id.to_def_id()
}
fn allow_infer(&self) -> bool {
true
}
fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
let v = match def {
Some(def) => infer::RegionParameterDefinition(span, def.name),
None => infer::MiscVariable(span),
};
Some(self.next_region_var(v))
}
fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
match param {
Some(param) => self.var_for_def(span, param).as_type().unwrap(),
None => self.next_ty_var(span),
}
}
fn ct_infer(
&self,
ty: Ty<'tcx>,
param: Option<&ty::GenericParamDef>,
span: Span,
) -> Const<'tcx> {
// FIXME ideally this shouldn't use unwrap
match param {
Some(
param @ ty::GenericParamDef {
kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. },
..
},
) => self.var_for_effect(param).as_const().unwrap(),
Some(param) => self.var_for_def(span, param).as_const().unwrap(),
None => self.next_const_var(ty, span),
}
}
fn probe_ty_param_bounds(
&self,
_: Span,
def_id: LocalDefId,
_: Ident,
) -> ty::GenericPredicates<'tcx> {
let tcx = self.tcx;
let item_def_id = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
// HACK(eddyb) should get the original `Span`.
let span = tcx.def_span(def_id);
ty::GenericPredicates {
parent: None,
predicates: tcx.arena.alloc_from_iter(
self.param_env.caller_bounds().iter().filter_map(|predicate| {
match predicate.kind().skip_binder() {
ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
Some((predicate, span))
}
_ => None,
}
}),
),
}
}
fn lower_assoc_ty(
&self,
span: Span,
item_def_id: DefId,
item_segment: &hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Ty<'tcx> {
let trait_ref = self.instantiate_binder_with_fresh_vars(
span,
infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id),
poly_trait_ref,
);
let item_args = self.lowerer().lower_generic_args_of_assoc_item(
span,
item_def_id,
item_segment,
trait_ref.args,
);
Ty::new_projection(self.tcx(), item_def_id, item_args)
}
fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
match ty.kind() {
ty::Adt(adt_def, _) => Some(*adt_def),
// FIXME(#104767): Should we handle bound regions here?
ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _)
if !ty.has_escaping_bound_vars() =>
{
self.normalize(span, ty).ty_adt_def()
}
_ => None,
}
}
fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
// FIXME: normalization and escaping regions
let ty = if !ty.has_escaping_bound_vars() {
// NOTE: These obligations are 100% redundant and are implied by
// WF obligations that are registered elsewhere, but they have a
// better cause code assigned to them in `add_required_obligations_for_hir`.
// This means that they should shadow obligations with worse spans.
if let ty::Alias(ty::Projection | ty::Weak, ty::AliasTy { args, def_id, .. }) =
ty.kind()
{
self.add_required_obligations_for_hir(span, *def_id, args, hir_id);
}
self.normalize(span, ty)
} else {
ty
};
self.write_ty(hir_id, ty)
}
fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
Some(&self.infcx)
}
fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
self.infcx.set_tainted_by_errors(e)
}
}
/// The `ty` representation of a user-provided type. Depending on the use-site
/// we want to either use the unnormalized or the normalized form of this type.
///
/// This is a bridge between the interface of HIR ty lowering, which outputs a raw
/// `Ty`, and the API in this module, which expect `Ty` to be fully normalized.
#[derive(Clone, Copy, Debug)]
pub struct LoweredTy<'tcx> {
/// The unnormalized type provided by the user.
pub raw: Ty<'tcx>,
/// The normalized form of `raw`, stored here for efficiency.
pub normalized: Ty<'tcx>,
}
impl<'tcx> LoweredTy<'tcx> {
pub fn from_raw(fcx: &FnCtxt<'_, 'tcx>, span: Span, raw: Ty<'tcx>) -> LoweredTy<'tcx> {
// FIXME(-Znext-solver): We're still figuring out how to best handle
// normalization and this doesn't feel too great. We should look at this
// code again before stabilizing it.
let normalized = if fcx.next_trait_solver() {
fcx.try_structurally_resolve_type(span, raw)
} else {
fcx.normalize(span, raw)
};
LoweredTy { raw, normalized }
}
}
fn parse_never_type_options_attr(
tcx: TyCtxt<'_>,
) -> (DivergingFallbackBehavior, DivergingBlockBehavior) {
use DivergingFallbackBehavior::*;
// Error handling is dubious here (unwraps), but that's probably fine for an internal attribute.
// Just don't write incorrect attributes <3
let mut fallback = None;
let mut block = None;
let items = tcx
.get_attr(CRATE_DEF_ID, sym::rustc_never_type_options)
.map(|attr| attr.meta_item_list().unwrap())
.unwrap_or_default();
for item in items {
if item.has_name(sym::fallback) && fallback.is_none() {
let mode = item.value_str().unwrap();
match mode {
sym::unit => fallback = Some(FallbackToUnit),
sym::niko => fallback = Some(FallbackToNiko),
sym::never => fallback = Some(FallbackToNever),
sym::no => fallback = Some(NoFallback),
_ => {
tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)"));
}
};
continue;
}
if item.has_name(sym::diverging_block_default) && block.is_none() {
let default = item.value_str().unwrap();
match default {
sym::unit => block = Some(DivergingBlockBehavior::Unit),
sym::never => block = Some(DivergingBlockBehavior::Never),
_ => {
tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)"));
}
};
continue;
}
tcx.dcx().span_err(
item.span(),
format!(
"unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)",
item.name_or_empty()
),
);
}
let fallback = fallback.unwrap_or_else(|| {
if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit }
});
let block = block.unwrap_or_default();
(fallback, block)
}