blob: a66ddd27dbb2ee178b2fccfc6924e63fb7ee797a [file] [log] [blame]
//! This module contains code to equate the input/output types appearing
//! in the MIR with the expected input/output types from the function
//! signature. This requires a bit of processing, as the expected types
//! are supplied to us before normalization and may contain opaque
//! `impl Trait` instances. In contrast, the input/output types found in
//! the MIR (specifically, in the special local variables for the
//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
//! contain revealed `impl Trait` values).
use rustc_index::vec::Idx;
use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::*;
use rustc_middle::ty::Ty;
use rustc_span::Span;
use crate::universal_regions::UniversalRegions;
use super::{Locations, TypeChecker};
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
#[instrument(skip(self, body, universal_regions), level = "debug")]
pub(super) fn equate_inputs_and_outputs(
&mut self,
body: &Body<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
normalized_inputs_and_output: &[Ty<'tcx>],
) {
let (&normalized_output_ty, normalized_input_tys) =
normalized_inputs_and_output.split_last().unwrap();
debug!(?normalized_output_ty);
debug!(?normalized_input_tys);
let mir_def_id = body.source.def_id().expect_local();
// If the user explicitly annotated the input types, extract
// those.
//
// e.g., `|x: FxHashMap<_, &'static u32>| ...`
let user_provided_sig;
if !self.tcx().is_closure(mir_def_id.to_def_id()) {
user_provided_sig = None;
} else {
let typeck_results = self.tcx().typeck(mir_def_id);
user_provided_sig = typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()).map(
|user_provided_poly_sig| {
// Instantiate the canonicalized variables from
// user-provided signature (e.g., the `_` in the code
// above) with fresh variables.
let poly_sig = self.instantiate_canonical_with_fresh_inference_vars(
body.span,
&user_provided_poly_sig,
);
// Replace the bound items in the fn sig with fresh
// variables, so that they represent the view from
// "inside" the closure.
self.infcx.replace_bound_vars_with_fresh_vars(
body.span,
LateBoundRegionConversionTime::FnCall,
poly_sig,
)
},
);
}
debug!(?normalized_input_tys, ?body.local_decls);
// Equate expected input tys with those in the MIR.
for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
if argument_index + 1 >= body.local_decls.len() {
self.tcx()
.sess
.delay_span_bug(body.span, "found more normalized_input_ty than local_decls");
break;
}
// In MIR, argument N is stored in local N+1.
let local = Local::new(argument_index + 1);
let mir_input_ty = body.local_decls[local].ty;
let mir_input_span = body.local_decls[local].source_info.span;
self.equate_normalized_input_or_output(
normalized_input_ty,
mir_input_ty,
mir_input_span,
);
}
if let Some(user_provided_sig) = user_provided_sig {
for (argument_index, &user_provided_input_ty) in
user_provided_sig.inputs().iter().enumerate()
{
// In MIR, closures begin an implicit `self`, so
// argument N is stored in local N+2.
let local = Local::new(argument_index + 2);
let mir_input_ty = body.local_decls[local].ty;
let mir_input_span = body.local_decls[local].source_info.span;
// If the user explicitly annotated the input types, enforce those.
let user_provided_input_ty =
self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
self.equate_normalized_input_or_output(
user_provided_input_ty,
mir_input_ty,
mir_input_span,
);
}
}
debug!(
"equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}",
body.yield_ty(),
universal_regions.yield_ty
);
// We will not have a universal_regions.yield_ty if we yield (by accident)
// outside of a generator and return an `impl Trait`, so emit a delay_span_bug
// because we don't want to panic in an assert here if we've already got errors.
if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() {
self.tcx().sess.delay_span_bug(
body.span,
&format!(
"Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})",
body.yield_ty(),
universal_regions.yield_ty,
),
);
}
if let (Some(mir_yield_ty), Some(ur_yield_ty)) =
(body.yield_ty(), universal_regions.yield_ty)
{
let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty, yield_span);
}
// Return types are a bit more complex. They may contain opaque `impl Trait` types.
let mir_output_ty = body.local_decls[RETURN_PLACE].ty;
let output_span = body.local_decls[RETURN_PLACE].source_info.span;
if let Err(terr) = self.eq_types(
normalized_output_ty,
mir_output_ty,
Locations::All(output_span),
ConstraintCategory::BoringNoLocation,
) {
span_mirbug!(
self,
Location::START,
"equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
normalized_output_ty,
mir_output_ty,
terr
);
};
// If the user explicitly annotated the output types, enforce those.
// Note that this only happens for closures.
if let Some(user_provided_sig) = user_provided_sig {
let user_provided_output_ty = user_provided_sig.output();
let user_provided_output_ty =
self.normalize(user_provided_output_ty, Locations::All(output_span));
if let Err(err) = self.eq_types(
user_provided_output_ty,
mir_output_ty,
Locations::All(output_span),
ConstraintCategory::BoringNoLocation,
) {
span_mirbug!(
self,
Location::START,
"equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
mir_output_ty,
user_provided_output_ty,
err
);
}
}
}
#[instrument(skip(self), level = "debug")]
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
if let Err(_) =
self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
{
// FIXME(jackh726): This is a hack. It's somewhat like
// `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd
// like to normalize *before* inserting into `local_decls`, but
// doing so ends up causing some other trouble.
let b = self.normalize(b, Locations::All(span));
// Note: if we have to introduce new placeholders during normalization above, then we won't have
// added those universes to the universe info, which we would want in `relate_tys`.
if let Err(terr) =
self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
{
span_mirbug!(
self,
Location::START,
"equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`",
a,
b,
terr
);
}
}
}
}