diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index f70b17e33624b..c6b29fe36fd9c 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -24,9 +24,9 @@ use crate::universal_regions::DefiningTy; impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// Check explicit closure signature annotation, /// e.g., `|x: FxIndexMap<_, &'static u32>| ...`. - #[instrument(skip(self, body), level = "debug")] - pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) { - let mir_def_id = body.source.def_id().expect_local(); + #[instrument(skip(self), level = "debug")] + pub(super) fn check_signature_annotation(&mut self) { + let mir_def_id = self.body.source.def_id().expect_local(); if !self.tcx().is_closure_like(mir_def_id.to_def_id()) { return; @@ -38,9 +38,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // (e.g., the `_` in the code above) with fresh variables. // Then replace the bound items in the fn sig with fresh variables, // so that they represent the view from "inside" the closure. - let user_provided_sig = self.instantiate_canonical(body.span, &user_provided_poly_sig); + let user_provided_sig = self.instantiate_canonical(self.body.span, &user_provided_poly_sig); let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars( - body.span, + self.body.span, BoundRegionConversionTime::FnCall, user_provided_sig, ); @@ -66,12 +66,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Ty::new_tup(self.tcx(), user_provided_sig.inputs()), args.tupled_upvars_ty(), args.coroutine_captures_by_ref_ty(), - self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(body.span), || { - RegionCtxt::Unknown - }), + self.infcx + .next_region_var(RegionVariableOrigin::MiscVariable(self.body.span), || { + RegionCtxt::Unknown + }), ); - let next_ty_var = || self.infcx.next_ty_var(body.span); + let next_ty_var = || self.infcx.next_ty_var(self.body.span); let output_ty = Ty::new_coroutine( self.tcx(), self.tcx().coroutine_for_closure(mir_def_id), @@ -107,9 +108,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip_eq( // In MIR, closure args begin with an implicit `self`. // Also, coroutines have a resume type which may be implicitly `()`. - body.args_iter() + self.body + .args_iter() .skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 }) - .map(|local| &body.local_decls[local]), + .map(|local| &self.body.local_decls[local]), ) { self.ascribe_user_type_skip_wf( arg_decl.ty, @@ -119,7 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // If the user explicitly annotated the output type, enforce it. - let output_decl = &body.local_decls[RETURN_PLACE]; + let output_decl = &self.body.local_decls[RETURN_PLACE]; self.ascribe_user_type_skip_wf( output_decl.ty, ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())), @@ -127,12 +129,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - #[instrument(skip(self, body), level = "debug")] - pub(super) fn equate_inputs_and_outputs( - &mut self, - body: &Body<'tcx>, - normalized_inputs_and_output: &[Ty<'tcx>], - ) { + #[instrument(skip(self), level = "debug")] + pub(super) fn equate_inputs_and_outputs(&mut self, normalized_inputs_and_output: &[Ty<'tcx>]) { let (&normalized_output_ty, normalized_input_tys) = normalized_inputs_and_output.split_last().unwrap(); @@ -141,18 +139,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // 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() { + if argument_index + 1 >= self.body.local_decls.len() { self.tcx() .dcx() - .span_bug(body.span, "found more normalized_input_ty than local_decls"); + .span_bug(self.body.span, "found more normalized_input_ty than local_decls"); } // In MIR, argument N is stored in local N+1. let local = Local::from_usize(argument_index + 1); - let mir_input_ty = body.local_decls[local].ty; + let mir_input_ty = self.body.local_decls[local].ty; - let mir_input_span = body.local_decls[local].source_info.span; + let mir_input_span = self.body.local_decls[local].source_info.span; self.equate_normalized_input_or_output( normalized_input_ty, mir_input_ty, @@ -160,8 +158,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - if let Some(mir_yield_ty) = body.yield_ty() { - let yield_span = body.local_decls[RETURN_PLACE].source_info.span; + if let Some(mir_yield_ty) = self.body.yield_ty() { + let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output( self.universal_regions.yield_ty.unwrap(), mir_yield_ty, @@ -169,8 +167,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - if let Some(mir_resume_ty) = body.resume_ty() { - let yield_span = body.local_decls[RETURN_PLACE].source_info.span; + if let Some(mir_resume_ty) = self.body.resume_ty() { + let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output( self.universal_regions.resume_ty.unwrap(), mir_resume_ty, @@ -179,8 +177,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // 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; + let mir_output_ty = self.body.local_decls[RETURN_PLACE].ty; + let output_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index dcc179030020d..f17ad23f4bf64 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -31,7 +31,6 @@ mod trace; /// performed before pub(super) fn generate<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, - body: &Body<'tcx>, location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, @@ -51,23 +50,16 @@ pub(super) fn generate<'a, 'tcx>( // We do record these regions in the polonius context, since they're used to differentiate // relevant and boring locals, which is a key distinction used later in diagnostics. if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { - let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); + let (_, boring_locals) = + compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals = boring_locals.into_iter().collect(); free_regions = typeck.universal_regions.universal_regions_iter().collect(); } let (relevant_live_locals, boring_locals) = - compute_relevant_live_locals(typeck.tcx(), &free_regions, body); - - trace::trace( - typeck, - body, - location_map, - flow_inits, - move_data, - relevant_live_locals, - boring_locals, - ); + compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); + + trace::trace(typeck, location_map, flow_inits, move_data, relevant_live_locals, boring_locals); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. @@ -76,7 +68,7 @@ pub(super) fn generate<'a, 'tcx>( &mut typeck.constraints.liveness_constraints, &typeck.universal_regions, &mut typeck.polonius_liveness, - body, + typeck.body, ); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 32d398dc1605c..7718644b9a9a8 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -39,17 +39,15 @@ use crate::type_check::{NormalizeLocation, TypeChecker}; /// this respects `#[may_dangle]` annotations). pub(super) fn trace<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, - body: &Body<'tcx>, location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, ) { - let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body); + let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body); let cx = LivenessContext { typeck, - body, flow_inits, location_map, local_use_map, @@ -69,14 +67,13 @@ pub(super) fn trace<'a, 'tcx>( /// Contextual state for the type-liveness coroutine. struct LivenessContext<'a, 'typeck, 'b, 'tcx> { /// Current type-checker, giving us our inference context etc. + /// + /// This also stores the body we're currently analyzing. typeck: &'a mut TypeChecker<'typeck, 'tcx>, /// Defines the `PointIndex` mapping location_map: &'a DenseLocationMap, - /// MIR we are analyzing. - body: &'a Body<'tcx>, - /// Mapping to/from the various indices used for initialization tracking. move_data: &'a MoveData<'tcx>, @@ -139,7 +136,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { self.compute_use_live_points_for(local); self.compute_drop_live_points_for(local); - let local_ty = self.cx.body.local_decls[local].ty; + let local_ty = self.cx.body().local_decls[local].ty; if !self.use_live_at.is_empty() { self.cx.add_use_live_facts_for(local_ty, &self.use_live_at); @@ -164,8 +161,8 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { /// and can therefore safely be dropped. fn dropck_boring_locals(&mut self, boring_locals: Vec) { for local in boring_locals { - let local_ty = self.cx.body.local_decls[local].ty; - let local_span = self.cx.body.local_decls[local].source_info.span; + let local_ty = self.cx.body().local_decls[local].ty; + let local_span = self.cx.body().local_decls[local].source_info.span; let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ let typeck = &self.cx.typeck; move || LivenessContext::compute_drop_data(typeck, local_ty, local_span) @@ -173,7 +170,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { drop_data.dropck_result.report_overflows( self.cx.typeck.infcx.tcx, - self.cx.body.local_decls[local].source_info.span, + self.cx.typeck.body.local_decls[local].source_info.span, local_ty, ); } @@ -202,7 +199,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { .var_dropped_at .iter() .filter_map(|&(local, location_index)| { - let local_ty = self.cx.body.local_decls[local].ty; + let local_ty = self.cx.body().local_decls[local].ty; if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() { return None; } @@ -278,9 +275,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { let block = self.cx.location_map.to_location(block_start).block; self.stack.extend( - self.cx.body.basic_blocks.predecessors()[block] + self.cx.body().basic_blocks.predecessors()[block] .iter() - .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) + .map(|&pred_bb| self.cx.body().terminator_loc(pred_bb)) .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)), ); } @@ -305,7 +302,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // Find the drops where `local` is initialized. for drop_point in self.cx.local_use_map.drops(local) { let location = self.cx.location_map.to_location(drop_point); - debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,); + debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,); if self.cx.initialized_at_terminator(location.block, mpi) && self.drop_live_at.insert(drop_point) @@ -351,7 +348,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // block. One of them may be either a definition or use // live point. let term_location = self.cx.location_map.to_location(term_point); - debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,); + debug_assert_eq!(self.cx.body().terminator_loc(term_location.block), term_location,); let block = term_location.block; let entry_point = self.cx.location_map.entry_point(term_location.block); for p in (entry_point..term_point).rev() { @@ -376,7 +373,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } } - let body = self.cx.body; + let body = self.cx.typeck.body; for &pred_block in body.basic_blocks.predecessors()[block].iter() { debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,); @@ -403,7 +400,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { continue; } - let pred_term_loc = self.cx.body.terminator_loc(pred_block); + let pred_term_loc = self.cx.body().terminator_loc(pred_block); let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc); // If the terminator of this predecessor either *assigns* @@ -463,6 +460,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { + fn body(&self) -> &Body<'tcx> { + self.typeck.body + } /// Returns `true` if the local variable (or some part of it) is initialized at the current /// cursor position. Callers should call one of the `seek` methods immediately before to point /// the cursor to the desired location. @@ -481,7 +481,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// DROP of some local variable will have an effect -- note that /// drops, as they may unwind, are always terminators. fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block)); + self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block)); self.initialized_at_curr_loc(mpi) } @@ -491,7 +491,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// **Warning:** Does not account for the result of `Call` /// instructions. fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block)); + self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block)); self.initialized_at_curr_loc(mpi) } @@ -526,7 +526,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { values::pretty_print_points(self.location_map, live_at.iter()), ); - let local_span = self.body.local_decls()[dropped_local].source_info.span; + let local_span = self.body().local_decls()[dropped_local].source_info.span; let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ let typeck = &self.typeck; move || Self::compute_drop_data(typeck, dropped_ty, local_span) @@ -544,7 +544,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { drop_data.dropck_result.report_overflows( self.typeck.infcx.tcx, - self.body.source_info(*drop_locations.first().unwrap()).span, + self.typeck.body.source_info(*drop_locations.first().unwrap()).span, dropped_ty, ); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c1e23cb54117b..d2eb7a52f78a0 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -156,6 +156,7 @@ pub(crate) fn type_check<'a, 'tcx>( infcx, last_span: body.span, body, + promoted, user_type_annotations: &body.user_type_annotations, region_bound_pairs, known_type_outlives_obligations, @@ -170,15 +171,11 @@ pub(crate) fn type_check<'a, 'tcx>( }; typeck.check_user_type_annotations(); + typeck.visit_body(body); + typeck.equate_inputs_and_outputs(&normalized_inputs_and_output); + typeck.check_signature_annotation(); - let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span }; - verifier.visit_body(body); - - typeck.typeck_mir(body); - typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output); - typeck.check_signature_annotation(body); - - liveness::generate(&mut typeck, body, &location_map, flow_inits, move_data); + liveness::generate(&mut typeck, &location_map, flow_inits, move_data); let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); @@ -212,317 +209,302 @@ enum FieldAccessError { OutOfRange { field_count: usize }, } -/// Verifies that MIR types are sane. -/// -/// FIXME: This should be merged with the actual `TypeChecker`. -struct TypeVerifier<'a, 'b, 'tcx> { - typeck: &'a mut TypeChecker<'b, 'tcx>, - promoted: &'b IndexSlice>, +/// The MIR type checker. Visits the MIR and enforces all the +/// constraints needed for it to be valid and well-typed. Along the +/// way, it accrues region constraints -- these can later be used by +/// NLL region checking. +struct TypeChecker<'a, 'tcx> { + infcx: &'a BorrowckInferCtxt<'tcx>, last_span: Span, + body: &'a Body<'tcx>, + /// The bodies of all promoteds. As promoteds have a completely separate CFG + /// recursing into them may corrupt your data structures if you're not careful. + promoted: &'a IndexSlice>, + /// User type annotations are shared between the main MIR and the MIR of + /// all of the promoted items. + user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, + region_bound_pairs: RegionBoundPairs<'tcx>, + known_type_outlives_obligations: Vec>, + implicit_region_bound: ty::Region<'tcx>, + reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, + universal_regions: &'a UniversalRegions<'tcx>, + location_table: &'a PoloniusLocationTable, + polonius_facts: &'a mut Option, + borrow_set: &'a BorrowSet<'tcx>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. + polonius_liveness: Option, } -impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { - fn visit_span(&mut self, span: Span) { - if !span.is_dummy() { - self.last_span = span; +/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions +/// inference computation. +pub(crate) struct MirTypeckResults<'tcx> { + pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, + pub(crate) universal_region_relations: Frozen>, + pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, + pub(crate) polonius_context: Option, +} + +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +pub(crate) struct MirTypeckRegionConstraints<'tcx> { + /// Maps from a `ty::Placeholder` to the corresponding + /// `PlaceholderIndex` bit that we will use for it. + /// + /// To keep everything in sync, do not insert this set + /// directly. Instead, use the `placeholder_region` helper. + pub(crate) placeholder_indices: PlaceholderIndices, + + /// Each time we add a placeholder to `placeholder_indices`, we + /// also create a corresponding "representative" region vid for + /// that wraps it. This vector tracks those. This way, when we + /// convert the same `ty::RePlaceholder(p)` twice, we can map to + /// the same underlying `RegionVid`. + pub(crate) placeholder_index_to_region: IndexVec>, + + /// In general, the type-checker is not responsible for enforcing + /// liveness constraints; this job falls to the region inferencer, + /// which performs a liveness analysis. However, in some limited + /// cases, the MIR type-checker creates temporary regions that do + /// not otherwise appear in the MIR -- in particular, the + /// late-bound regions that it instantiates at call-sites -- and + /// hence it must report on their liveness constraints. + pub(crate) liveness_constraints: LivenessValues, + + pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, + + pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, + + pub(crate) universe_causes: FxIndexMap>, + + pub(crate) type_tests: Vec>, +} + +impl<'tcx> MirTypeckRegionConstraints<'tcx> { + /// Creates a `Region` for a given `PlaceholderRegion`, or returns the + /// region that corresponds to a previously created one. + fn placeholder_region( + &mut self, + infcx: &InferCtxt<'tcx>, + placeholder: ty::PlaceholderRegion, + ) -> ty::Region<'tcx> { + let placeholder_index = self.placeholder_indices.insert(placeholder); + match self.placeholder_index_to_region.get(placeholder_index) { + Some(&v) => v, + None => { + let origin = NllRegionVariableOrigin::Placeholder(placeholder); + let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); + self.placeholder_index_to_region.push(region); + region + } } } +} - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.super_place(place, context, location); - let tcx = self.tcx(); - let place_ty = place.ty(self.body(), tcx); - if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Copy, Some(self.last_span)), - [place_ty.ty], - ); +/// The `Locations` type summarizes *where* region constraints are +/// required to hold. Normally, this is at a particular point which +/// created the obligation, but for constraints that the user gave, we +/// want the constraint to hold at all points. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Locations { + /// Indicates that a type constraint should always be true. This + /// is particularly important in the new borrowck analysis for + /// things like the type of the return slot. Consider this + /// example: + /// + /// ```compile_fail,E0515 + /// fn foo<'a>(x: &'a u32) -> &'a u32 { + /// let y = 22; + /// return &y; // error + /// } + /// ``` + /// + /// Here, we wind up with the signature from the return type being + /// something like `&'1 u32` where `'1` is a universal region. But + /// the type of the return slot `_0` is something like `&'2 u32` + /// where `'2` is an existential region variable. The type checker + /// requires that `&'2 u32 = &'1 u32` -- but at what point? In the + /// older NLL analysis, we required this only at the entry point + /// to the function. By the nature of the constraints, this wound + /// up propagating to all points reachable from start (because + /// `'1` -- as a universal region -- is live everywhere). In the + /// newer analysis, though, this doesn't work: `_0` is considered + /// dead at the start (it has no usable value) and hence this type + /// equality is basically a no-op. Then, later on, when we do `_0 + /// = &'3 y`, that region `'3` never winds up related to the + /// universal region `'1` and hence no error occurs. Therefore, we + /// use Locations::All instead, which ensures that the `'1` and + /// `'2` are equal everything. We also use this for other + /// user-given type annotations; e.g., if the user wrote `let mut + /// x: &'static u32 = ...`, we would ensure that all values + /// assigned to `x` are of `'static` lifetime. + /// + /// The span points to the place the constraint arose. For example, + /// it points to the type in a user-given type annotation. If + /// there's no sensible span then it's DUMMY_SP. + All(Span), - // To have a `Copy` operand, the type `T` of the - // value must be `Copy`. Note that we prove that `T: Copy`, - // rather than using the `is_copy_modulo_regions` - // test. This is important because - // `is_copy_modulo_regions` ignores the resulting region - // obligations and assumes they pass. This can result in - // bounds from `Copy` impls being unsoundly ignored (e.g., - // #29149). Note that we decide to use `Copy` before knowing - // whether the bounds fully apply: in effect, the rule is - // that if a value of some type could implement `Copy`, then - // it must. - self.typeck.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::CopyBound, - ); + /// An outlives constraint that only has to hold at a single location, + /// usually it represents a point where references flow from one spot to + /// another (e.g., `x = y`) + Single(Location), +} + +impl Locations { + pub fn from_location(&self) -> Option { + match self { + Locations::All(_) => None, + Locations::Single(from_location) => Some(*from_location), } } - fn visit_projection_elem( - &mut self, - place: PlaceRef<'tcx>, - elem: PlaceElem<'tcx>, - context: PlaceContext, - location: Location, - ) { - let tcx = self.tcx(); - let base_ty = place.ty(self.body(), tcx); - match elem { - // All these projections don't add any constraints, so there's nothing to - // do here. We check their invariants in the MIR validator after all. - ProjectionElem::Deref - | ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..) => {} - ProjectionElem::Field(field, fty) => { - let fty = self.typeck.normalize(fty, location); - let ty = base_ty.field_ty(tcx, field); - let ty = self.typeck.normalize(ty, location); - debug!(?fty, ?ty); - - if let Err(terr) = self.typeck.relate_types( - ty, - context.ambient_variance(), - fty, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!(self, place, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); - } - } - ProjectionElem::OpaqueCast(ty) => { - let ty = self.typeck.normalize(ty, location); - self.typeck - .relate_types( - ty, - context.ambient_variance(), - base_ty.ty, - location.to_locations(), - ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast), - ) - .unwrap(); - } - ProjectionElem::UnwrapUnsafeBinder(ty) => { - let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else { - unreachable!(); - }; - let found_ty = self.typeck.infcx.instantiate_binder_with_fresh_vars( - self.body().source_info(location).span, - BoundRegionConversionTime::HigherRankedType, - binder_ty.into(), - ); - self.typeck - .relate_types( - ty, - context.ambient_variance(), - found_ty, - location.to_locations(), - ConstraintCategory::Boring, - ) - .unwrap(); - } - ProjectionElem::Subtype(_) => { - bug!("ProjectionElem::Subtype shouldn't exist in borrowck") - } + /// Gets a span representing the location. + pub fn span(&self, body: &Body<'_>) -> Span { + match self { + Locations::All(span) => *span, + Locations::Single(l) => body.source_info(*l).span, } } +} - #[instrument(level = "debug", skip(self))] - fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) { - self.super_const_operand(constant, location); - let ty = constant.const_.ty(); +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } - self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| { - let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region); - self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location); - }); + fn body(&self) -> &Body<'tcx> { + self.body + } - let locations = location.to_locations(); - if let Some(annotation_index) = constant.user_ty { - if let Err(terr) = self.typeck.relate_type_and_user_type( - constant.const_.ty(), - ty::Invariant, - &UserTypeProjection { base: annotation_index, projs: vec![] }, - locations, - ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), - ) { - let annotation = &self.typeck.user_type_annotations[annotation_index]; - span_mirbug!( - self, - constant, - "bad constant user type {:?} vs {:?}: {:?}", - annotation, - constant.const_.ty(), - terr, - ); - } + fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { + if let ty::RePlaceholder(placeholder) = r.kind() { + self.constraints.placeholder_region(self.infcx, placeholder).as_var() } else { - let tcx = self.tcx(); - let maybe_uneval = match constant.const_ { - Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { - Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) - } - _ => None, - }, - Const::Unevaluated(uv, _) => Some(uv), - _ => None, - }; - - if let Some(uv) = maybe_uneval { - if let Some(promoted) = uv.promoted { - let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, - promoted: &Body<'tcx>, - ty, - san_ty| { - if let Err(terr) = verifier.typeck.eq_types( - ty, - san_ty, - locations, - ConstraintCategory::Boring, - ) { - span_mirbug!( - verifier, - promoted, - "bad promoted type ({:?}: {:?}): {:?}", - ty, - san_ty, - terr - ); - }; - }; - - let promoted_body = &self.promoted[promoted]; - self.verify_promoted(promoted_body, location); - - let promoted_ty = promoted_body.return_ty(); - check_err(self, promoted_body, ty, promoted_ty); - } else { - self.typeck.ascribe_user_type( - constant.const_.ty(), - ty::UserType::new(ty::UserTypeKind::TypeOf( - uv.def, - UserArgs { args: uv.args, user_self_ty: None }, - )), - locations.span(self.typeck.body), - ); - } - } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { - let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity(); - let normalized_ty = self.typeck.normalize(unnormalized_ty, locations); - let literal_ty = constant.const_.ty().builtin_deref(true).unwrap(); - - if let Err(terr) = self.typeck.eq_types( - literal_ty, - normalized_ty, - locations, - ConstraintCategory::Boring, - ) { - span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr); - } - } + self.universal_regions.to_region_vid(r) + } + } - if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() { - let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); - self.typeck.normalize_and_prove_instantiated_predicates( - def_id, - instantiated_predicates, - locations, - ); + fn unsized_feature_enabled(&self) -> bool { + let features = self.tcx().features(); + features.unsized_locals() || features.unsized_fn_params() + } - assert!(!matches!( - tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), - Some(DefKind::Impl { of_trait: true }) - )); - self.typeck.prove_predicates( - args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), - locations, - ConstraintCategory::Boring, - ); + /// Equate the inferred type and the annotated type for user type annotations + #[instrument(skip(self), level = "debug")] + fn check_user_type_annotations(&mut self) { + debug!(?self.user_type_annotations); + let tcx = self.tcx(); + for user_annotation in self.user_type_annotations { + let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; + let annotation = self.instantiate_canonical(span, user_ty); + if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind + && let DefKind::InlineConst = tcx.def_kind(def) + { + assert!(annotation.bounds.is_empty()); + self.check_inline_const(inferred_ty, def.expect_local(), args, span); + } else { + self.ascribe_user_type(inferred_ty, annotation, span); } } } - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - self.super_local_decl(local, local_decl); + #[instrument(skip(self, data), level = "debug")] + fn push_region_constraints( + &mut self, + locations: Locations, + category: ConstraintCategory<'tcx>, + data: &QueryRegionConstraints<'tcx>, + ) { + debug!("constraints generated: {:#?}", data); - for user_ty in - local_decl.user_ty.as_deref().into_iter().flat_map(UserTypeProjections::projections) - { - let span = self.typeck.user_type_annotations[user_ty.base].span; + constraint_conversion::ConstraintConversion::new( + self.infcx, + self.universal_regions, + &self.region_bound_pairs, + self.implicit_region_bound, + self.infcx.param_env, + &self.known_type_outlives_obligations, + locations, + locations.span(self.body), + category, + self.constraints, + ) + .convert_all(data); + } - let ty = if local_decl.is_nonref_binding() { - local_decl.ty - } else if let &ty::Ref(_, rty, _) = local_decl.ty.kind() { - // If we have a binding of the form `let ref x: T = ..` - // then remove the outermost reference so we can check the - // type annotation for the remaining type. - rty - } else { - bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); - }; + /// Try to relate `sub <: sup` + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + ) -> Result<(), NoSolution> { + // Use this order of parameters because the sup type is usually the + // "expected" type in diagnostics. + self.relate_types(sup, ty::Contravariant, sub, locations, category) + } - if let Err(terr) = self.typeck.relate_type_and_user_type( - ty, - ty::Invariant, - user_ty, - Locations::All(span), - ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration), - ) { - span_mirbug!( - self, - local, - "bad user type on variable {:?}: {:?} != {:?} ({:?})", - local, - local_decl.ty, - local_decl.user_ty, - terr, - ); - } - } + #[instrument(skip(self, category), level = "debug")] + fn eq_types( + &mut self, + expected: Ty<'tcx>, + found: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + ) -> Result<(), NoSolution> { + self.relate_types(expected, ty::Invariant, found, locations, category) } - #[instrument(level = "debug", skip(self))] - fn visit_body(&mut self, body: &Body<'tcx>) { - // We intentionally do not recurse into `body.required_consts` or - // `body.mentioned_items` here as the MIR at this phase should still - // refer to all items and we don't want to check them multiple times. + #[instrument(skip(self), level = "debug")] + fn relate_type_and_user_type( + &mut self, + a: Ty<'tcx>, + v: ty::Variance, + user_ty: &UserTypeProjection, + locations: Locations, + category: ConstraintCategory<'tcx>, + ) -> Result<(), NoSolution> { + let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; + trace!(?annotated_type); + let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); - for (local, local_decl) in body.local_decls.iter_enumerated() { - self.visit_local_decl(local, local_decl); - } + let tcx = self.infcx.tcx; - for (block, block_data) in body.basic_blocks.iter_enumerated() { - let mut location = Location { block, statement_index: 0 }; - for stmt in &block_data.statements { - if !stmt.source_info.span.is_dummy() { - self.last_span = stmt.source_info.span; - } - self.visit_statement(stmt, location); - location.statement_index += 1; + for proj in &user_ty.projs { + if !self.infcx.next_trait_solver() + && let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() + { + // There is nothing that we can compare here if we go through an opaque type. + // We're always in its defining scope as we can otherwise not project through + // it, so we're constraining it anyways. + return Ok(()); } - - self.visit_terminator(block_data.terminator(), location); + let projected_ty = curr_projected_ty.projection_ty_core( + tcx, + proj, + |this, field, ()| { + let ty = this.field_ty(tcx, field); + self.structurally_resolve(ty, locations) + }, + |_, _| unreachable!(), + ); + curr_projected_ty = projected_ty; } - } -} + trace!(?curr_projected_ty); -impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.typeck.body - } + let ty = curr_projected_ty.ty; + self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; - fn tcx(&self) -> TyCtxt<'tcx> { - self.typeck.infcx.tcx + Ok(()) } - fn verify_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) { + fn check_promoted(&mut self, promoted_body: &'a Body<'tcx>, location: Location) { // Determine the constraints from the promoted MIR by running the type // checker on the promoted MIR, then transfer the constraints back to // the main MIR, changing the locations to the provided location. - let parent_body = mem::replace(&mut self.typeck.body, promoted_body); + let parent_body = mem::replace(&mut self.body, promoted_body); // Use new sets of constraints and closure bounds so that we can // modify their locations. @@ -530,24 +512,23 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let mut constraints = Default::default(); let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); - // Don't try to add borrow_region facts for the promoted MIR + // Don't try to add borrow_region facts for the promoted MIR as they refer + // to the wrong locations. let mut swap_constraints = |this: &mut Self| { - mem::swap(this.typeck.polonius_facts, polonius_facts); - mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints); - mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints); + mem::swap(this.polonius_facts, polonius_facts); + mem::swap(&mut this.constraints.outlives_constraints, &mut constraints); + mem::swap(&mut this.constraints.liveness_constraints, &mut liveness_constraints); }; swap_constraints(self); self.visit_body(promoted_body); - self.typeck.typeck_mir(promoted_body); + self.body = parent_body; - self.typeck.body = parent_body; // Merge the outlives constraints back in, at the given location. swap_constraints(self); - let locations = location.to_locations(); for constraint in constraints.outlives().iter() { let mut constraint = *constraint; @@ -560,7 +541,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // temporary from the user's point of view. constraint.category = ConstraintCategory::Boring; } - self.typeck.constraints.outlives_constraints.push(constraint) + self.constraints.outlives_constraints.push(constraint) } // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region @@ -570,387 +551,131 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // unordered. #[allow(rustc::potential_query_instability)] for region in liveness_constraints.live_regions_unordered() { - self.typeck.constraints.liveness_constraints.add_location(region, location); + self.constraints.liveness_constraints.add_location(region, location); } } -} -/// The MIR type checker. Visits the MIR and enforces all the -/// constraints needed for it to be valid and well-typed. Along the -/// way, it accrues region constraints -- these can later be used by -/// NLL region checking. -struct TypeChecker<'a, 'tcx> { - infcx: &'a BorrowckInferCtxt<'tcx>, - last_span: Span, - body: &'a Body<'tcx>, - /// User type annotations are shared between the main MIR and the MIR of - /// all of the promoted items. - user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, - region_bound_pairs: RegionBoundPairs<'tcx>, - known_type_outlives_obligations: Vec>, - implicit_region_bound: ty::Region<'tcx>, - reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, - universal_regions: &'a UniversalRegions<'tcx>, - location_table: &'a PoloniusLocationTable, - polonius_facts: &'a mut Option, - borrow_set: &'a BorrowSet<'tcx>, - constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. - polonius_liveness: Option, + fn check_inline_const( + &mut self, + inferred_ty: Ty<'tcx>, + def_id: LocalDefId, + args: UserArgs<'tcx>, + span: Span, + ) { + assert!(args.user_self_ty.is_none()); + let tcx = self.tcx(); + let const_ty = tcx.type_of(def_id).instantiate(tcx, args.args); + if let Err(terr) = + self.eq_types(const_ty, inferred_ty, Locations::All(span), ConstraintCategory::Boring) + { + span_bug!( + span, + "bad inline const pattern: ({:?} = {:?}) {:?}", + const_ty, + inferred_ty, + terr + ); + } + let args = self.infcx.resolve_vars_if_possible(args.args); + let predicates = self.prove_closure_bounds(tcx, def_id, args, Locations::All(span)); + self.normalize_and_prove_instantiated_predicates( + def_id.to_def_id(), + predicates, + Locations::All(span), + ); + } } -/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions -/// inference computation. -pub(crate) struct MirTypeckResults<'tcx> { - pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, - pub(crate) universal_region_relations: Frozen>, - pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, - pub(crate) polonius_context: Option, -} - -/// A collection of region constraints that must be satisfied for the -/// program to be considered well-typed. -pub(crate) struct MirTypeckRegionConstraints<'tcx> { - /// Maps from a `ty::Placeholder` to the corresponding - /// `PlaceholderIndex` bit that we will use for it. - /// - /// To keep everything in sync, do not insert this set - /// directly. Instead, use the `placeholder_region` helper. - pub(crate) placeholder_indices: PlaceholderIndices, - - /// Each time we add a placeholder to `placeholder_indices`, we - /// also create a corresponding "representative" region vid for - /// that wraps it. This vector tracks those. This way, when we - /// convert the same `ty::RePlaceholder(p)` twice, we can map to - /// the same underlying `RegionVid`. - pub(crate) placeholder_index_to_region: IndexVec>, - - /// In general, the type-checker is not responsible for enforcing - /// liveness constraints; this job falls to the region inferencer, - /// which performs a liveness analysis. However, in some limited - /// cases, the MIR type-checker creates temporary regions that do - /// not otherwise appear in the MIR -- in particular, the - /// late-bound regions that it instantiates at call-sites -- and - /// hence it must report on their liveness constraints. - pub(crate) liveness_constraints: LivenessValues, - - pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, - - pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, - - pub(crate) universe_causes: FxIndexMap>, - - pub(crate) type_tests: Vec>, -} - -impl<'tcx> MirTypeckRegionConstraints<'tcx> { - /// Creates a `Region` for a given `PlaceholderRegion`, or returns the - /// region that corresponds to a previously created one. - fn placeholder_region( - &mut self, - infcx: &InferCtxt<'tcx>, - placeholder: ty::PlaceholderRegion, - ) -> ty::Region<'tcx> { - let placeholder_index = self.placeholder_indices.insert(placeholder); - match self.placeholder_index_to_region.get(placeholder_index) { - Some(&v) => v, - None => { - let origin = NllRegionVariableOrigin::Placeholder(placeholder); - let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); - self.placeholder_index_to_region.push(region); - region - } +impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { + fn visit_span(&mut self, span: Span) { + if !span.is_dummy() { + debug!(?span); + self.last_span = span; } } -} - -/// The `Locations` type summarizes *where* region constraints are -/// required to hold. Normally, this is at a particular point which -/// created the obligation, but for constraints that the user gave, we -/// want the constraint to hold at all points. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum Locations { - /// Indicates that a type constraint should always be true. This - /// is particularly important in the new borrowck analysis for - /// things like the type of the return slot. Consider this - /// example: - /// - /// ```compile_fail,E0515 - /// fn foo<'a>(x: &'a u32) -> &'a u32 { - /// let y = 22; - /// return &y; // error - /// } - /// ``` - /// - /// Here, we wind up with the signature from the return type being - /// something like `&'1 u32` where `'1` is a universal region. But - /// the type of the return slot `_0` is something like `&'2 u32` - /// where `'2` is an existential region variable. The type checker - /// requires that `&'2 u32 = &'1 u32` -- but at what point? In the - /// older NLL analysis, we required this only at the entry point - /// to the function. By the nature of the constraints, this wound - /// up propagating to all points reachable from start (because - /// `'1` -- as a universal region -- is live everywhere). In the - /// newer analysis, though, this doesn't work: `_0` is considered - /// dead at the start (it has no usable value) and hence this type - /// equality is basically a no-op. Then, later on, when we do `_0 - /// = &'3 y`, that region `'3` never winds up related to the - /// universal region `'1` and hence no error occurs. Therefore, we - /// use Locations::All instead, which ensures that the `'1` and - /// `'2` are equal everything. We also use this for other - /// user-given type annotations; e.g., if the user wrote `let mut - /// x: &'static u32 = ...`, we would ensure that all values - /// assigned to `x` are of `'static` lifetime. - /// - /// The span points to the place the constraint arose. For example, - /// it points to the type in a user-given type annotation. If - /// there's no sensible span then it's DUMMY_SP. - All(Span), - - /// An outlives constraint that only has to hold at a single location, - /// usually it represents a point where references flow from one spot to - /// another (e.g., `x = y`) - Single(Location), -} -impl Locations { - pub fn from_location(&self) -> Option { - match self { - Locations::All(_) => None, - Locations::Single(from_location) => Some(*from_location), - } - } + #[instrument(skip(self, body), level = "debug")] + fn visit_body(&mut self, body: &Body<'tcx>) { + debug_assert!(std::ptr::eq(self.body, body)); - /// Gets a span representing the location. - pub fn span(&self, body: &Body<'_>) -> Span { - match self { - Locations::All(span) => *span, - Locations::Single(l) => body.source_info(*l).span, + for (local, local_decl) in body.local_decls.iter_enumerated() { + self.visit_local_decl(local, local_decl); } - } -} -impl<'a, 'tcx> TypeChecker<'a, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.body - } + for (block, block_data) in body.basic_blocks.iter_enumerated() { + let mut location = Location { block, statement_index: 0 }; + for stmt in &block_data.statements { + self.visit_statement(stmt, location); + location.statement_index += 1; + } - fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.constraints.placeholder_region(self.infcx, placeholder).as_var() - } else { - self.universal_regions.to_region_vid(r) + self.visit_terminator(block_data.terminator(), location); + self.check_iscleanup(block_data); } } - fn unsized_feature_enabled(&self) -> bool { - let features = self.tcx().features(); - features.unsized_locals() || features.unsized_fn_params() - } - - /// Equate the inferred type and the annotated type for user type annotations #[instrument(skip(self), level = "debug")] - fn check_user_type_annotations(&mut self) { - debug!(?self.user_type_annotations); + fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) { + self.super_statement(stmt, location); let tcx = self.tcx(); - for user_annotation in self.user_type_annotations { - let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; - let annotation = self.instantiate_canonical(span, user_ty); - if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind - && let DefKind::InlineConst = tcx.def_kind(def) - { - assert!(annotation.bounds.is_empty()); - self.check_inline_const(inferred_ty, def.expect_local(), args, span); - } else { - self.ascribe_user_type(inferred_ty, annotation, span); - } - } - } - - #[instrument(skip(self, data), level = "debug")] - fn push_region_constraints( - &mut self, - locations: Locations, - category: ConstraintCategory<'tcx>, - data: &QueryRegionConstraints<'tcx>, - ) { - debug!("constraints generated: {:#?}", data); - - constraint_conversion::ConstraintConversion::new( - self.infcx, - self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.infcx.param_env, - &self.known_type_outlives_obligations, - locations, - locations.span(self.body), - category, - self.constraints, - ) - .convert_all(data); - } + match &stmt.kind { + StatementKind::Assign(box (place, rv)) => { + // Assignments to temporaries are not "interesting"; + // they are not caused by the user, but rather artifacts + // of lowering. Assignments to other sorts of places *are* interesting + // though. + let category = match place.as_local() { + Some(RETURN_PLACE) => { + let defining_ty = &self.universal_regions.defining_ty; + if defining_ty.is_const() { + if tcx.is_static(defining_ty.def_id()) { + ConstraintCategory::UseAsStatic + } else { + ConstraintCategory::UseAsConst + } + } else { + ConstraintCategory::Return(ReturnConstraint::Normal) + } + } + Some(l) + if matches!( + self.body.local_decls[l].local_info(), + LocalInfo::AggregateTemp + ) => + { + ConstraintCategory::Usage + } + Some(l) if !self.body.local_decls[l].is_user_variable() => { + ConstraintCategory::Boring + } + _ => ConstraintCategory::Assignment, + }; + debug!( + "assignment category: {:?} {:?}", + category, + place.as_local().map(|l| &self.body.local_decls[l]) + ); - /// Try to relate `sub <: sup` - fn sub_types( - &mut self, - sub: Ty<'tcx>, - sup: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory<'tcx>, - ) -> Result<(), NoSolution> { - // Use this order of parameters because the sup type is usually the - // "expected" type in diagnostics. - self.relate_types(sup, ty::Contravariant, sub, locations, category) - } - - #[instrument(skip(self, category), level = "debug")] - fn eq_types( - &mut self, - expected: Ty<'tcx>, - found: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory<'tcx>, - ) -> Result<(), NoSolution> { - self.relate_types(expected, ty::Invariant, found, locations, category) - } - - #[instrument(skip(self), level = "debug")] - fn relate_type_and_user_type( - &mut self, - a: Ty<'tcx>, - v: ty::Variance, - user_ty: &UserTypeProjection, - locations: Locations, - category: ConstraintCategory<'tcx>, - ) -> Result<(), NoSolution> { - let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; - trace!(?annotated_type); - let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); - - let tcx = self.infcx.tcx; - - for proj in &user_ty.projs { - if !self.infcx.next_trait_solver() - && let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() - { - // There is nothing that we can compare here if we go through an opaque type. - // We're always in its defining scope as we can otherwise not project through - // it, so we're constraining it anyways. - return Ok(()); - } - let projected_ty = curr_projected_ty.projection_ty_core( - tcx, - proj, - |this, field, ()| { - let ty = this.field_ty(tcx, field); - self.structurally_resolve(ty, locations) - }, - |_, _| unreachable!(), - ); - curr_projected_ty = projected_ty; - } - trace!(?curr_projected_ty); - - let ty = curr_projected_ty.ty; - self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; - - Ok(()) - } - - fn check_inline_const( - &mut self, - inferred_ty: Ty<'tcx>, - def_id: LocalDefId, - args: UserArgs<'tcx>, - span: Span, - ) { - assert!(args.user_self_ty.is_none()); - let tcx = self.tcx(); - let const_ty = tcx.type_of(def_id).instantiate(tcx, args.args); - if let Err(terr) = - self.eq_types(const_ty, inferred_ty, Locations::All(span), ConstraintCategory::Boring) - { - span_bug!( - span, - "bad inline const pattern: ({:?} = {:?}) {:?}", - const_ty, - inferred_ty, - terr - ); - } - let args = self.infcx.resolve_vars_if_possible(args.args); - let predicates = self.prove_closure_bounds(tcx, def_id, args, Locations::All(span)); - self.normalize_and_prove_instantiated_predicates( - def_id.to_def_id(), - predicates, - Locations::All(span), - ); - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - #[instrument(skip(self, body), level = "debug")] - fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { - let tcx = self.tcx(); - debug!("stmt kind: {:?}", stmt.kind); - match &stmt.kind { - StatementKind::Assign(box (place, rv)) => { - // Assignments to temporaries are not "interesting"; - // they are not caused by the user, but rather artifacts - // of lowering. Assignments to other sorts of places *are* interesting - // though. - let category = match place.as_local() { - Some(RETURN_PLACE) => { - let defining_ty = &self.universal_regions.defining_ty; - if defining_ty.is_const() { - if tcx.is_static(defining_ty.def_id()) { - ConstraintCategory::UseAsStatic - } else { - ConstraintCategory::UseAsConst - } - } else { - ConstraintCategory::Return(ReturnConstraint::Normal) - } - } - Some(l) - if matches!(body.local_decls[l].local_info(), LocalInfo::AggregateTemp) => - { - ConstraintCategory::Usage - } - Some(l) if !body.local_decls[l].is_user_variable() => { - ConstraintCategory::Boring - } - _ => ConstraintCategory::Assignment, - }; - debug!( - "assignment category: {:?} {:?}", - category, - place.as_local().map(|l| &body.local_decls[l]) - ); - - let place_ty = place.ty(body, tcx).ty; - debug!(?place_ty); - let place_ty = self.normalize(place_ty, location); - debug!("place_ty normalized: {:?}", place_ty); - let rv_ty = rv.ty(body, tcx); - debug!(?rv_ty); - let rv_ty = self.normalize(rv_ty, location); - debug!("normalized rv_ty: {:?}", rv_ty); - if let Err(terr) = - self.sub_types(rv_ty, place_ty, location.to_locations(), category) - { - span_mirbug!( - self, - stmt, - "bad assignment ({:?} = {:?}): {:?}", - place_ty, - rv_ty, - terr - ); - } + let place_ty = place.ty(self.body, tcx).ty; + debug!(?place_ty); + let place_ty = self.normalize(place_ty, location); + debug!("place_ty normalized: {:?}", place_ty); + let rv_ty = rv.ty(self.body, tcx); + debug!(?rv_ty); + let rv_ty = self.normalize(rv_ty, location); + debug!("normalized rv_ty: {:?}", rv_ty); + if let Err(terr) = + self.sub_types(rv_ty, place_ty, location.to_locations(), category) + { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } if let Some(annotation_index) = self.rvalue_user_ty(rv) { if let Err(terr) = self.relate_type_and_user_type( @@ -972,7 +697,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - self.check_rvalue(body, rv, location); if !self.unsized_feature_enabled() { let trait_ref = ty::TraitRef::new( tcx, @@ -987,7 +711,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(box (place, projection), variance) => { - let place_ty = place.ty(body, tcx).ty; + let place_ty = place.ty(self.body, tcx).ty; if let Err(terr) = self.relate_type_and_user_type( place_ty, *variance, @@ -1007,14 +731,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } } - StatementKind::Intrinsic(box kind) => match kind { - NonDivergingIntrinsic::Assume(op) => self.check_operand(op, location), - NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( - stmt.source_info.span, - "Unexpected NonDivergingIntrinsic::CopyNonOverlapping, should only appear after lowering_intrinsics", - ), - }, - StatementKind::FakeRead(..) + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) + | StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::Retag { .. } @@ -1023,19 +741,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | StatementKind::PlaceMention(..) | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => {} - StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) + | StatementKind::Deinit(..) + | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } } } - #[instrument(skip(self, body, term_location), level = "debug")] - fn check_terminator( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - term_location: Location, - ) { + #[instrument(skip(self), level = "debug")] + fn visit_terminator(&mut self, term: &Terminator<'tcx>, term_location: Location) { + self.super_terminator(term, term_location); let tcx = self.tcx(); debug!("terminator kind: {:?}", term.kind); match &term.kind { @@ -1053,9 +769,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } TerminatorKind::SwitchInt { discr, .. } => { - self.check_operand(discr, term_location); - - let switch_ty = discr.ty(body, tcx); + let switch_ty = discr.ty(self.body, tcx); if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); } @@ -1069,12 +783,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { _ => unreachable!(), }; - self.check_operand(func, term_location); - for arg in args { - self.check_operand(&arg.node, term_location); - } - - let func_ty = func.ty(body, tcx); + let func_ty = func.ty(self.body, tcx); debug!("func_ty.kind: {:?}", func_ty.kind()); let sig = match func_ty.kind() { @@ -1142,7 +851,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } if let TerminatorKind::Call { destination, target, .. } = term.kind { - self.check_call_dest(body, term, &sig, destination, target, term_location); + self.check_call_dest(term, &sig, destination, target, term_location); } // The ordinary liveness rules will ensure that all @@ -1157,32 +866,28 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.constraints.liveness_constraints.add_location(region_vid, term_location); } - self.check_call_inputs(body, term, func, &sig, args, term_location, call_source); + self.check_call_inputs(term, func, &sig, args, term_location, call_source); } TerminatorKind::Assert { cond, msg, .. } => { - self.check_operand(cond, term_location); - - let cond_ty = cond.ty(body, tcx); + let cond_ty = cond.ty(self.body, tcx); if cond_ty != tcx.types.bool { span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); } if let AssertKind::BoundsCheck { len, index } = &**msg { - if len.ty(body, tcx) != tcx.types.usize { + if len.ty(self.body, tcx) != tcx.types.usize { span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) } - if index.ty(body, tcx) != tcx.types.usize { + if index.ty(self.body, tcx) != tcx.types.usize { span_mirbug!(self, index, "bounds-check index non-usize {:?}", index) } } } TerminatorKind::Yield { value, resume_arg, .. } => { - self.check_operand(value, term_location); - - match body.yield_ty() { + match self.body.yield_ty() { None => span_mirbug!(self, term, "yield in non-coroutine"), Some(ty) => { - let value_ty = value.ty(body, tcx); + let value_ty = value.ty(self.body, tcx); if let Err(terr) = self.sub_types( value_ty, ty, @@ -1201,10 +906,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - match body.resume_ty() { + match self.body.resume_ty() { None => span_mirbug!(self, term, "yield in non-coroutine"), Some(ty) => { - let resume_ty = resume_arg.ty(body, tcx); + let resume_ty = resume_arg.ty(self.body, tcx); if let Err(terr) = self.sub_types( ty, resume_ty.ty, @@ -1226,395 +931,76 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - fn check_call_dest( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: Place<'tcx>, - target: Option, - term_location: Location, - ) { - let tcx = self.tcx(); - match target { - Some(_) => { - let dest_ty = destination.ty(body, tcx).ty; - let dest_ty = self.normalize(dest_ty, term_location); - let category = match destination.as_local() { - Some(RETURN_PLACE) => { - if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) = - self.universal_regions.defining_ty - { - if tcx.is_static(def_id) { - ConstraintCategory::UseAsStatic - } else { - ConstraintCategory::UseAsConst - } - } else { - ConstraintCategory::Return(ReturnConstraint::Normal) - } - } - Some(l) if !body.local_decls[l].is_user_variable() => { - ConstraintCategory::Boring - } - // The return type of a call is interesting for diagnostics. - _ => ConstraintCategory::Assignment, - }; + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + self.super_local_decl(local, local_decl); - let locations = term_location.to_locations(); + for user_ty in + local_decl.user_ty.as_deref().into_iter().flat_map(UserTypeProjections::projections) + { + let span = self.user_type_annotations[user_ty.base].span; - if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) { - span_mirbug!( - self, - term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, - sig.output(), - terr - ); - } + let ty = if local_decl.is_nonref_binding() { + local_decl.ty + } else if let &ty::Ref(_, rty, _) = local_decl.ty.kind() { + // If we have a binding of the form `let ref x: T = ..` + // then remove the outermost reference so we can check the + // type annotation for the remaining type. + rty + } else { + bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); + }; - // When `unsized_fn_params` and `unsized_locals` are both not enabled, - // this check is done at `check_local`. - if self.unsized_feature_enabled() { - let span = term.source_info.span; - self.ensure_place_sized(dest_ty, span); - } + if let Err(terr) = self.relate_type_and_user_type( + ty, + ty::Invariant, + user_ty, + Locations::All(span), + ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration), + ) { + span_mirbug!( + self, + local, + "bad user type on variable {:?}: {:?} != {:?} ({:?})", + local, + local_decl.ty, + local_decl.user_ty, + terr, + ); } - None => { - // The signature in this call can reference region variables, - // so erase them before calling a query. - let output_ty = self.tcx().erase_regions(sig.output()); - if !output_ty.is_privately_uninhabited( - self.tcx(), - self.infcx.typing_env(self.infcx.param_env), - ) { - span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); + } + + // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls + // and nullary ops are checked in `check_call_dest`. + if !self.unsized_feature_enabled() { + match self.body.local_kind(local) { + LocalKind::ReturnPointer | LocalKind::Arg => { + // return values of normal functions are required to be + // sized by typeck, but return values of ADT constructors are + // not because we don't include a `Self: Sized` bounds on them. + // + // Unbound parts of arguments were never required to be Sized + // - maybe we should make that a warning. + return; + } + LocalKind::Temp => { + let span = local_decl.source_info.span; + let ty = local_decl.ty; + self.ensure_place_sized(ty, span); } } } } - #[instrument(level = "debug", skip(self, body, term, func, term_location, call_source))] - fn check_call_inputs( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - func: &Operand<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Spanned>], - term_location: Location, - call_source: CallSource, - ) { - if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) { - span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); - } - - let func_ty = func.ty(body, self.infcx.tcx); - if let ty::FnDef(def_id, _) = *func_ty.kind() { - // Some of the SIMD intrinsics are special: they need a particular argument to be a - // constant. (Eventually this should use const-generics, but those are not up for the - // task yet: https://github.com/rust-lang/rust/issues/85229.) - if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) = - self.tcx().intrinsic(def_id).map(|i| i.name) - { - let idx = match name { - sym::simd_shuffle => 2, - _ => 1, - }; - if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) { - self.tcx().dcx().emit_err(SimdIntrinsicArgConst { - span: term.source_info.span, - arg: idx + 1, - intrinsic: name.to_string(), - }); - } - } - } - debug!(?func_ty); - - for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() { - let op_arg_ty = op_arg.node.ty(body, self.tcx()); - - let op_arg_ty = self.normalize(op_arg_ty, term_location); - let category = if call_source.from_hir_call() { - ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty))) - } else { - ConstraintCategory::Boring - }; - if let Err(terr) = - self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category) - { - span_mirbug!( - self, - term, - "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, - fn_arg, - op_arg_ty, - terr - ); - } - } - } - - fn check_iscleanup(&mut self, body: &Body<'tcx>, block_data: &BasicBlockData<'tcx>) { - let is_cleanup = block_data.is_cleanup; - self.last_span = block_data.terminator().source_info.span; - match block_data.terminator().kind { - TerminatorKind::Goto { target } => { - self.assert_iscleanup(body, block_data, target, is_cleanup) - } - TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets.all_targets() { - self.assert_iscleanup(body, block_data, *target, is_cleanup); - } - } - TerminatorKind::UnwindResume => { - if !is_cleanup { - span_mirbug!(self, block_data, "resume on non-cleanup block!") - } - } - TerminatorKind::UnwindTerminate(_) => { - if !is_cleanup { - span_mirbug!(self, block_data, "terminate on non-cleanup block!") - } - } - TerminatorKind::Return => { - if is_cleanup { - span_mirbug!(self, block_data, "return on cleanup block") - } - } - TerminatorKind::TailCall { .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "tailcall on cleanup block") - } - } - TerminatorKind::CoroutineDrop { .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "coroutine_drop in cleanup block") - } - } - TerminatorKind::Yield { resume, drop, .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "yield in cleanup block") - } - self.assert_iscleanup(body, block_data, resume, is_cleanup); - if let Some(drop) = drop { - self.assert_iscleanup(body, block_data, drop, is_cleanup); - } - } - TerminatorKind::Unreachable => {} - TerminatorKind::Drop { target, unwind, .. } - | TerminatorKind::Assert { target, unwind, .. } => { - self.assert_iscleanup(body, block_data, target, is_cleanup); - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - TerminatorKind::Call { ref target, unwind, .. } => { - if let &Some(target) = target { - self.assert_iscleanup(body, block_data, target, is_cleanup); - } - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - TerminatorKind::FalseEdge { real_target, imaginary_target } => { - self.assert_iscleanup(body, block_data, real_target, is_cleanup); - self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup); - } - TerminatorKind::FalseUnwind { real_target, unwind } => { - self.assert_iscleanup(body, block_data, real_target, is_cleanup); - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - TerminatorKind::InlineAsm { ref targets, unwind, .. } => { - for &target in targets { - self.assert_iscleanup(body, block_data, target, is_cleanup); - } - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - } - } - - fn assert_iscleanup( - &mut self, - body: &Body<'tcx>, - ctxt: &dyn fmt::Debug, - bb: BasicBlock, - iscleanuppad: bool, - ) { - if body[bb].is_cleanup != iscleanuppad { - span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad); - } - } - - fn assert_iscleanup_unwind( - &mut self, - body: &Body<'tcx>, - ctxt: &dyn fmt::Debug, - unwind: UnwindAction, - is_cleanup: bool, - ) { - match unwind { - UnwindAction::Cleanup(unwind) => { - if is_cleanup { - span_mirbug!(self, ctxt, "unwind on cleanup block") - } - self.assert_iscleanup(body, ctxt, unwind, true); - } - UnwindAction::Continue => { - if is_cleanup { - span_mirbug!(self, ctxt, "unwind on cleanup block") - } - } - UnwindAction::Unreachable | UnwindAction::Terminate(_) => (), - } - } - - fn check_local(&mut self, body: &Body<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { - match body.local_kind(local) { - LocalKind::ReturnPointer | LocalKind::Arg => { - // return values of normal functions are required to be - // sized by typeck, but return values of ADT constructors are - // not because we don't include a `Self: Sized` bounds on them. - // - // Unbound parts of arguments were never required to be Sized - // - maybe we should make that a warning. - return; - } - LocalKind::Temp => {} - } - - // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls - // and nullary ops are checked in `check_call_dest`. - if !self.unsized_feature_enabled() { - let span = local_decl.source_info.span; - let ty = local_decl.ty; - self.ensure_place_sized(ty, span); - } - } - - fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { - let tcx = self.tcx(); - - // Erase the regions from `ty` to get a global type. The - // `Sized` bound in no way depends on precise regions, so this - // shouldn't affect `is_sized`. - let erased_ty = tcx.erase_regions(ty); - // FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here. - if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) { - // in current MIR construction, all non-control-flow rvalue - // expressions evaluate through `as_temp` or `into` a return - // slot or local, so to find all unsized rvalues it is enough - // to check all temps, return slots and locals. - if self.reported_errors.replace((ty, span)).is_none() { - // While this is located in `nll::typeck` this error is not - // an NLL error, it's a required check to prevent creation - // of unsized rvalues in a call expression. - self.tcx().dcx().emit_err(MoveUnsized { ty, span }); - } - } - } - - fn aggregate_field_ty( - &mut self, - ak: &AggregateKind<'tcx>, - field_index: FieldIdx, - location: Location, - ) -> Result, FieldAccessError> { - let tcx = self.tcx(); - - match *ak { - AggregateKind::Adt(adt_did, variant_index, args, _, active_field_index) => { - let def = tcx.adt_def(adt_did); - let variant = &def.variant(variant_index); - let adj_field_index = active_field_index.unwrap_or(field_index); - if let Some(field) = variant.fields.get(adj_field_index) { - Ok(self.normalize(field.ty(tcx, args), location)) - } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) - } - } - AggregateKind::Closure(_, args) => { - match args.as_closure().upvar_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_closure().upvar_tys().len(), - }), - } - } - AggregateKind::Coroutine(_, args) => { - // It doesn't make sense to look at a field beyond the prefix; - // these require a variant index, and are not initialized in - // aggregate rvalues. - match args.as_coroutine().prefix_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine().prefix_tys().len(), - }), - } - } - AggregateKind::CoroutineClosure(_, args) => { - match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine_closure().upvar_tys().len(), - }), - } - } - AggregateKind::Array(ty) => Ok(ty), - AggregateKind::Tuple | AggregateKind::RawPtr(..) => { - unreachable!("This should have been covered in check_rvalues"); - } - } - } - - fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) { - debug!(?op, ?location, "check_operand"); - - if let Operand::Constant(constant) = op { - let maybe_uneval = match constant.const_ { - Const::Val(..) | Const::Ty(_, _) => None, - Const::Unevaluated(uv, _) => Some(uv), - }; - - if let Some(uv) = maybe_uneval { - if uv.promoted.is_none() { - let tcx = self.tcx(); - let def_id = uv.def; - if tcx.def_kind(def_id) == DefKind::InlineConst { - let def_id = def_id.expect_local(); - let predicates = self.prove_closure_bounds( - tcx, - def_id, - uv.args, - location.to_locations(), - ); - self.normalize_and_prove_instantiated_predicates( - def_id.to_def_id(), - predicates, - location.to_locations(), - ); - } - } - } - } - } - - #[instrument(skip(self, body), level = "debug")] - fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + #[instrument(skip(self), level = "debug")] + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); let tcx = self.tcx(); - let span = body.source_info(location).span; - + let span = self.body.source_info(location).span; match rvalue { - Rvalue::Aggregate(ak, ops) => { - for op in ops { - self.check_operand(op, location); - } - self.check_aggregate_rvalue(body, rvalue, ak, ops, location) - } + Rvalue::Aggregate(ak, ops) => self.check_aggregate_rvalue(rvalue, ak, ops, location), Rvalue::Repeat(operand, len) => { - self.check_operand(operand, location); - - let array_ty = rvalue.ty(body.local_decls(), tcx); + let array_ty = rvalue.ty(self.body.local_decls(), tcx); self.prove_predicate( ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())), Locations::Single(location), @@ -1633,7 +1019,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } Operand::Move(place) => { // Make sure that repeated elements implement `Copy`. - let ty = place.ty(body, tcx).ty; + let ty = place.ty(self.body, tcx).ty; let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::Copy, Some(span)), @@ -1666,9 +1052,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} - Rvalue::ShallowInitBox(operand, ty) => { - self.check_operand(operand, location); - + Rvalue::ShallowInitBox(_operand, ty) => { let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::Sized, Some(span)), @@ -1683,12 +1067,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } Rvalue::Cast(cast_kind, op, ty) => { - self.check_operand(op, location); - match *cast_kind { CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => { let is_implicit_coercion = coercion_source == CoercionSource::Implicit; - let src_ty = op.ty(body, tcx); + let src_ty = op.ty(self.body, tcx); let mut src_sig = src_ty.fn_sig(tcx); if let ty::FnDef(def_id, _) = src_ty.kind() && let ty::FnPtr(_, target_hdr) = *ty.kind() @@ -1697,7 +1079,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { && let Some(safe_sig) = tcx.adjust_target_feature_sig( *def_id, src_sig, - body.source.def_id(), + self.body.source.def_id(), ) { src_sig = safe_sig; @@ -1790,7 +1172,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { PointerCoercion::ClosureFnPointer(safety), coercion_source, ) => { - let sig = match op.ty(body, tcx).kind() { + let sig = match op.ty(self.body, tcx).kind() { ty::Closure(_, args) => args.as_closure().sig(), _ => bug!(), }; @@ -1819,7 +1201,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { PointerCoercion::UnsafeFnPointer, coercion_source, ) => { - let fn_sig = op.ty(body, tcx).fn_sig(tcx); + let fn_sig = op.ty(self.body, tcx).fn_sig(tcx); // The type that we see in the fcx is like // `foo::<'a, 'b>`, where `foo` is the path to a @@ -1853,7 +1235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)), - [op.ty(body, tcx), ty], + [op.ty(self.body, tcx), ty], ); let is_implicit_coercion = coercion_source == CoercionSource::Implicit; @@ -1879,7 +1261,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { _ => panic!("Invalid dyn* cast_ty"), }; - let self_ty = op.ty(body, tcx); + let self_ty = op.ty(self.body, tcx); let is_implicit_coercion = coercion_source == CoercionSource::Implicit; self.prove_predicates( @@ -1906,7 +1288,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { PointerCoercion::MutToConstPointer, coercion_source, ) => { - let ty::RawPtr(ty_from, hir::Mutability::Mut) = op.ty(body, tcx).kind() + let ty::RawPtr(ty_from, hir::Mutability::Mut) = + op.ty(self.body, tcx).kind() else { span_mirbug!(self, rvalue, "unexpected base type for cast {:?}", ty,); return; @@ -1934,7 +1317,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerCoercion(PointerCoercion::ArrayToPointer, coercion_source) => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let opt_ty_elem_mut = match ty_from.kind() { ty::RawPtr(array_ty, array_mut) => match array_ty.kind() { @@ -1997,7 +1380,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerExposeProvenance => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2015,7 +1398,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerWithExposedProvenance => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2032,7 +1415,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::IntToInt => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2049,7 +1432,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::IntToFloat => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2066,7 +1449,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::FloatToInt => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2083,7 +1466,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::FloatToFloat => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2100,7 +1483,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::FnPtrToPtr => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2117,7 +1500,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::PtrToPtr => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { @@ -2192,103 +1575,627 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - Rvalue::Ref(region, _borrow_kind, borrowed_place) => { - self.add_reborrow_constraint(body, location, *region, borrowed_place); - } + Rvalue::Ref(region, _borrow_kind, borrowed_place) => { + self.add_reborrow_constraint(location, *region, borrowed_place); + } + + Rvalue::BinaryOp( + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, + box (left, right), + ) => { + let ty_left = left.ty(self.body, tcx); + match ty_left.kind() { + // Types with regions are comparable if they have a common super-type. + ty::RawPtr(_, _) | ty::FnPtr(..) => { + let ty_right = right.ty(self.body, tcx); + let common_ty = + self.infcx.next_ty_var(self.body.source_info(location).span); + self.sub_types( + ty_left, + common_ty, + location.to_locations(), + ConstraintCategory::CallArgument(None), + ) + .unwrap_or_else(|err| { + bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) + }); + if let Err(terr) = self.sub_types( + ty_right, + common_ty, + location.to_locations(), + ConstraintCategory::CallArgument(None), + ) { + span_mirbug!( + self, + rvalue, + "unexpected comparison types {:?} and {:?} yields {:?}", + ty_left, + ty_right, + terr + ) + } + } + // For types with no regions we can just check that the + // both operands have the same type. + ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) + if ty_left == right.ty(self.body, tcx) => {} + // Other types are compared by trait methods, not by + // `Rvalue::BinaryOp`. + _ => span_mirbug!( + self, + rvalue, + "unexpected comparison types {:?} and {:?}", + ty_left, + right.ty(self.body, tcx) + ), + } + } + + Rvalue::WrapUnsafeBinder(op, ty) => { + let operand_ty = op.ty(self.body, self.tcx()); + let ty::UnsafeBinder(binder_ty) = *ty.kind() else { + unreachable!(); + }; + let expected_ty = self.infcx.instantiate_binder_with_fresh_vars( + self.body().source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.sub_types( + operand_ty, + expected_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } + + Rvalue::Use(_) + | Rvalue::UnaryOp(_, _) + | Rvalue::CopyForDeref(_) + | Rvalue::BinaryOp(..) + | Rvalue::RawPtr(..) + | Rvalue::ThreadLocalRef(..) + | Rvalue::Len(..) + | Rvalue::Discriminant(..) + | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { + self.super_operand(op, location); + if let Operand::Constant(constant) = op { + let maybe_uneval = match constant.const_ { + Const::Val(..) | Const::Ty(_, _) => None, + Const::Unevaluated(uv, _) => Some(uv), + }; + + if let Some(uv) = maybe_uneval { + if uv.promoted.is_none() { + let tcx = self.tcx(); + let def_id = uv.def; + if tcx.def_kind(def_id) == DefKind::InlineConst { + let def_id = def_id.expect_local(); + let predicates = self.prove_closure_bounds( + tcx, + def_id, + uv.args, + location.to_locations(), + ); + self.normalize_and_prove_instantiated_predicates( + def_id.to_def_id(), + predicates, + location.to_locations(), + ); + } + } + } + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) { + self.super_const_operand(constant, location); + let ty = constant.const_.ty(); + + self.infcx.tcx.for_each_free_region(&ty, |live_region| { + let live_region_vid = self.universal_regions.to_region_vid(live_region); + self.constraints.liveness_constraints.add_location(live_region_vid, location); + }); + + let locations = location.to_locations(); + if let Some(annotation_index) = constant.user_ty { + if let Err(terr) = self.relate_type_and_user_type( + constant.const_.ty(), + ty::Invariant, + &UserTypeProjection { base: annotation_index, projs: vec![] }, + locations, + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), + ) { + let annotation = &self.user_type_annotations[annotation_index]; + span_mirbug!( + self, + constant, + "bad constant user type {:?} vs {:?}: {:?}", + annotation, + constant.const_.ty(), + terr, + ); + } + } else { + let tcx = self.tcx(); + let maybe_uneval = match constant.const_ { + Const::Ty(_, ct) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) + } + _ => None, + }, + Const::Unevaluated(uv, _) => Some(uv), + _ => None, + }; + + if let Some(uv) = maybe_uneval { + if let Some(promoted) = uv.promoted { + let promoted_body = &self.promoted[promoted]; + self.check_promoted(promoted_body, location); + let promoted_ty = promoted_body.return_ty(); + if let Err(terr) = + self.eq_types(ty, promoted_ty, locations, ConstraintCategory::Boring) + { + span_mirbug!( + self, + promoted, + "bad promoted type ({:?}: {:?}): {:?}", + ty, + promoted_ty, + terr + ); + }; + } else { + self.ascribe_user_type( + constant.const_.ty(), + ty::UserType::new(ty::UserTypeKind::TypeOf( + uv.def, + UserArgs { args: uv.args, user_self_ty: None }, + )), + locations.span(self.body), + ); + } + } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { + let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity(); + let normalized_ty = self.normalize(unnormalized_ty, locations); + let literal_ty = constant.const_.ty().builtin_deref(true).unwrap(); + + if let Err(terr) = + self.eq_types(literal_ty, normalized_ty, locations, ConstraintCategory::Boring) + { + span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr); + } + } + + if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() { + let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); + self.normalize_and_prove_instantiated_predicates( + def_id, + instantiated_predicates, + locations, + ); + + assert!(!matches!( + tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), + Some(DefKind::Impl { of_trait: true }) + )); + self.prove_predicates( + args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), + locations, + ConstraintCategory::Boring, + ); + } + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.super_place(place, context, location); + let tcx = self.tcx(); + let place_ty = place.ty(self.body, tcx); + if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Copy, Some(self.last_span)), + [place_ty.ty], + ); + + // To have a `Copy` operand, the type `T` of the + // value must be `Copy`. Note that we prove that `T: Copy`, + // rather than using the `is_copy_modulo_regions` + // test. This is important because + // `is_copy_modulo_regions` ignores the resulting region + // obligations and assumes they pass. This can result in + // bounds from `Copy` impls being unsoundly ignored (e.g., + // #29149). Note that we decide to use `Copy` before knowing + // whether the bounds fully apply: in effect, the rule is + // that if a value of some type could implement `Copy`, then + // it must. + self.prove_trait_ref(trait_ref, location.to_locations(), ConstraintCategory::CopyBound); + } + } + + fn visit_projection_elem( + &mut self, + place: PlaceRef<'tcx>, + elem: PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) { + let tcx = self.tcx(); + let base_ty = place.ty(self.body(), tcx); + match elem { + // All these projections don't add any constraints, so there's nothing to + // do here. We check their invariants in the MIR validator after all. + ProjectionElem::Deref + | ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) => {} + ProjectionElem::Field(field, fty) => { + let fty = self.normalize(fty, location); + let ty = base_ty.field_ty(tcx, field); + let ty = self.normalize(ty, location); + debug!(?fty, ?ty); + + if let Err(terr) = self.relate_types( + ty, + context.ambient_variance(), + fty, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!(self, place, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); + } + } + ProjectionElem::OpaqueCast(ty) => { + let ty = self.normalize(ty, location); + self.relate_types( + ty, + context.ambient_variance(), + base_ty.ty, + location.to_locations(), + ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast), + ) + .unwrap(); + } + ProjectionElem::UnwrapUnsafeBinder(ty) => { + let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else { + unreachable!(); + }; + let found_ty = self.infcx.instantiate_binder_with_fresh_vars( + self.body.source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.relate_types( + ty, + context.ambient_variance(), + found_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } + ProjectionElem::Subtype(_) => { + bug!("ProjectionElem::Subtype shouldn't exist in borrowck") + } + } + } +} + +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn check_call_dest( + &mut self, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: Place<'tcx>, + target: Option, + term_location: Location, + ) { + let tcx = self.tcx(); + match target { + Some(_) => { + let dest_ty = destination.ty(self.body, tcx).ty; + let dest_ty = self.normalize(dest_ty, term_location); + let category = match destination.as_local() { + Some(RETURN_PLACE) => { + if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) = + self.universal_regions.defining_ty + { + if tcx.is_static(def_id) { + ConstraintCategory::UseAsStatic + } else { + ConstraintCategory::UseAsConst + } + } else { + ConstraintCategory::Return(ReturnConstraint::Normal) + } + } + Some(l) if !self.body.local_decls[l].is_user_variable() => { + ConstraintCategory::Boring + } + // The return type of a call is interesting for diagnostics. + _ => ConstraintCategory::Assignment, + }; + + let locations = term_location.to_locations(); + + if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); + } + + // When `unsized_fn_params` and `unsized_locals` are both not enabled, + // this check is done at `check_local`. + if self.unsized_feature_enabled() { + let span = term.source_info.span; + self.ensure_place_sized(dest_ty, span); + } + } + None => { + // The signature in this call can reference region variables, + // so erase them before calling a query. + let output_ty = self.tcx().erase_regions(sig.output()); + if !output_ty.is_privately_uninhabited( + self.tcx(), + self.infcx.typing_env(self.infcx.param_env), + ) { + span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); + } + } + } + } + + #[instrument(level = "debug", skip(self, term, func, term_location, call_source))] + fn check_call_inputs( + &mut self, + term: &Terminator<'tcx>, + func: &Operand<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Spanned>], + term_location: Location, + call_source: CallSource, + ) { + if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) { + span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); + } + + let func_ty = func.ty(self.body, self.infcx.tcx); + if let ty::FnDef(def_id, _) = *func_ty.kind() { + // Some of the SIMD intrinsics are special: they need a particular argument to be a + // constant. (Eventually this should use const-generics, but those are not up for the + // task yet: https://github.com/rust-lang/rust/issues/85229.) + if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) = + self.tcx().intrinsic(def_id).map(|i| i.name) + { + let idx = match name { + sym::simd_shuffle => 2, + _ => 1, + }; + if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) { + self.tcx().dcx().emit_err(SimdIntrinsicArgConst { + span: term.source_info.span, + arg: idx + 1, + intrinsic: name.to_string(), + }); + } + } + } + debug!(?func_ty); + + for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() { + let op_arg_ty = op_arg.node.ty(self.body, self.tcx()); - Rvalue::BinaryOp( - BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, - box (left, right), - ) => { - self.check_operand(left, location); - self.check_operand(right, location); + let op_arg_ty = self.normalize(op_arg_ty, term_location); + let category = if call_source.from_hir_call() { + ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty))) + } else { + ConstraintCategory::Boring + }; + if let Err(terr) = + self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category) + { + span_mirbug!( + self, + term, + "bad arg #{:?} ({:?} <- {:?}): {:?}", + n, + fn_arg, + op_arg_ty, + terr + ); + } + } + } - let ty_left = left.ty(body, tcx); - match ty_left.kind() { - // Types with regions are comparable if they have a common super-type. - ty::RawPtr(_, _) | ty::FnPtr(..) => { - let ty_right = right.ty(body, tcx); - let common_ty = self.infcx.next_ty_var(body.source_info(location).span); - self.sub_types( - ty_left, - common_ty, - location.to_locations(), - ConstraintCategory::CallArgument(None), - ) - .unwrap_or_else(|err| { - bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) - }); - if let Err(terr) = self.sub_types( - ty_right, - common_ty, - location.to_locations(), - ConstraintCategory::CallArgument(None), - ) { - span_mirbug!( - self, - rvalue, - "unexpected comparison types {:?} and {:?} yields {:?}", - ty_left, - ty_right, - terr - ) - } - } - // For types with no regions we can just check that the - // both operands have the same type. - ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) - if ty_left == right.ty(body, tcx) => {} - // Other types are compared by trait methods, not by - // `Rvalue::BinaryOp`. - _ => span_mirbug!( - self, - rvalue, - "unexpected comparison types {:?} and {:?}", - ty_left, - right.ty(body, tcx) - ), + fn check_iscleanup(&mut self, block_data: &BasicBlockData<'tcx>) { + let is_cleanup = block_data.is_cleanup; + match block_data.terminator().kind { + TerminatorKind::Goto { target } => { + self.assert_iscleanup(block_data, target, is_cleanup) + } + TerminatorKind::SwitchInt { ref targets, .. } => { + for target in targets.all_targets() { + self.assert_iscleanup(block_data, *target, is_cleanup); } } - - Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => { - self.check_operand(operand, location); + TerminatorKind::UnwindResume => { + if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + } + } + TerminatorKind::UnwindTerminate(_) => { + if !is_cleanup { + span_mirbug!(self, block_data, "terminate on non-cleanup block!") + } + } + TerminatorKind::Return => { + if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + } + } + TerminatorKind::TailCall { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "tailcall on cleanup block") + } + } + TerminatorKind::CoroutineDrop { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "coroutine_drop in cleanup block") + } + } + TerminatorKind::Yield { resume, drop, .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "yield in cleanup block") + } + self.assert_iscleanup(block_data, resume, is_cleanup); + if let Some(drop) = drop { + self.assert_iscleanup(block_data, drop, is_cleanup); + } + } + TerminatorKind::Unreachable => {} + TerminatorKind::Drop { target, unwind, .. } + | TerminatorKind::Assert { target, unwind, .. } => { + self.assert_iscleanup(block_data, target, is_cleanup); + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); + } + TerminatorKind::Call { ref target, unwind, .. } => { + if let &Some(target) = target { + self.assert_iscleanup(block_data, target, is_cleanup); + } + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); + } + TerminatorKind::FalseEdge { real_target, imaginary_target } => { + self.assert_iscleanup(block_data, real_target, is_cleanup); + self.assert_iscleanup(block_data, imaginary_target, is_cleanup); + } + TerminatorKind::FalseUnwind { real_target, unwind } => { + self.assert_iscleanup(block_data, real_target, is_cleanup); + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); } - Rvalue::CopyForDeref(place) => { - let op = &Operand::Copy(*place); - self.check_operand(op, location); + TerminatorKind::InlineAsm { ref targets, unwind, .. } => { + for &target in targets { + self.assert_iscleanup(block_data, target, is_cleanup); + } + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); } + } + } - Rvalue::BinaryOp(_, box (left, right)) => { - self.check_operand(left, location); - self.check_operand(right, location); + fn assert_iscleanup(&mut self, ctxt: &dyn fmt::Debug, bb: BasicBlock, iscleanuppad: bool) { + if self.body[bb].is_cleanup != iscleanuppad { + span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad); + } + } + + fn assert_iscleanup_unwind( + &mut self, + ctxt: &dyn fmt::Debug, + unwind: UnwindAction, + is_cleanup: bool, + ) { + match unwind { + UnwindAction::Cleanup(unwind) => { + if is_cleanup { + span_mirbug!(self, ctxt, "unwind on cleanup block") + } + self.assert_iscleanup(ctxt, unwind, true); + } + UnwindAction::Continue => { + if is_cleanup { + span_mirbug!(self, ctxt, "unwind on cleanup block") + } } + UnwindAction::Unreachable | UnwindAction::Terminate(_) => (), + } + } - Rvalue::WrapUnsafeBinder(op, ty) => { - self.check_operand(op, location); - let operand_ty = op.ty(self.body, self.tcx()); + fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { + let tcx = self.tcx(); - let ty::UnsafeBinder(binder_ty) = *ty.kind() else { - unreachable!(); - }; - let expected_ty = self.infcx.instantiate_binder_with_fresh_vars( - self.body().source_info(location).span, - BoundRegionConversionTime::HigherRankedType, - binder_ty.into(), - ); - self.sub_types( - operand_ty, - expected_ty, - location.to_locations(), - ConstraintCategory::Boring, - ) - .unwrap(); + // Erase the regions from `ty` to get a global type. The + // `Sized` bound in no way depends on precise regions, so this + // shouldn't affect `is_sized`. + let erased_ty = tcx.erase_regions(ty); + // FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here. + if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) { + // in current MIR construction, all non-control-flow rvalue + // expressions evaluate through `as_temp` or `into` a return + // slot or local, so to find all unsized rvalues it is enough + // to check all temps, return slots and locals. + if self.reported_errors.replace((ty, span)).is_none() { + // While this is located in `nll::typeck` this error is not + // an NLL error, it's a required check to prevent creation + // of unsized rvalues in a call expression. + self.tcx().dcx().emit_err(MoveUnsized { ty, span }); } + } + } - Rvalue::RawPtr(..) - | Rvalue::ThreadLocalRef(..) - | Rvalue::Len(..) - | Rvalue::Discriminant(..) - | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} + fn aggregate_field_ty( + &mut self, + ak: &AggregateKind<'tcx>, + field_index: FieldIdx, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + match *ak { + AggregateKind::Adt(adt_did, variant_index, args, _, active_field_index) => { + let def = tcx.adt_def(adt_did); + let variant = &def.variant(variant_index); + let adj_field_index = active_field_index.unwrap_or(field_index); + if let Some(field) = variant.fields.get(adj_field_index) { + Ok(self.normalize(field.ty(tcx, args), location)) + } else { + Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + } + } + AggregateKind::Closure(_, args) => { + match args.as_closure().upvar_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), + None => Err(FieldAccessError::OutOfRange { + field_count: args.as_closure().upvar_tys().len(), + }), + } + } + AggregateKind::Coroutine(_, args) => { + // It doesn't make sense to look at a field beyond the prefix; + // these require a variant index, and are not initialized in + // aggregate rvalues. + match args.as_coroutine().prefix_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), + None => Err(FieldAccessError::OutOfRange { + field_count: args.as_coroutine().prefix_tys().len(), + }), + } + } + AggregateKind::CoroutineClosure(_, args) => { + match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), + None => Err(FieldAccessError::OutOfRange { + field_count: args.as_coroutine_closure().upvar_tys().len(), + }), + } + } + AggregateKind::Array(ty) => Ok(ty), + AggregateKind::Tuple | AggregateKind::RawPtr(..) => { + unreachable!("This should have been covered in check_rvalues"); + } } } @@ -2326,7 +2233,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn check_aggregate_rvalue( &mut self, - body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, aggregate_kind: &AggregateKind<'tcx>, operands: &IndexSlice>, @@ -2359,7 +2265,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { continue; } }; - let operand_ty = operand.ty(body, tcx); + let operand_ty = operand.ty(self.body, tcx); let operand_ty = self.normalize(operand_ty, location); if let Err(terr) = self.sub_types( @@ -2389,7 +2295,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// - `borrowed_place`: the place `P` being borrowed fn add_reborrow_constraint( &mut self, - body: &Body<'tcx>, location: Location, borrow_region: ty::Region<'tcx>, borrowed_place: &Place<'tcx>, @@ -2428,7 +2333,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let def = self.body.source.def_id().expect_local(); let upvars = tcx.closure_captures(def); let field = - path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body); + path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), self.body); let category = if let Some(field) = field { ConstraintCategory::ClosureUpvar(field) } else { @@ -2440,7 +2345,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match elem { ProjectionElem::Deref => { - let base_ty = base.ty(body, tcx).ty; + let base_ty = base.ty(self.body, tcx).ty; debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); match base_ty.kind() { @@ -2449,7 +2354,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { sup: ref_region.as_var(), sub: borrow_region.as_var(), locations: location.to_locations(), - span: location.to_locations().span(body), + span: location.to_locations().span(self.body), category, variance_info: ty::VarianceDiagInfo::default(), from_closure: false, @@ -2633,30 +2538,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { tcx.predicates_of(def_id).instantiate(tcx, args) } - - #[instrument(skip(self, body), level = "debug")] - fn typeck_mir(&mut self, body: &Body<'tcx>) { - self.last_span = body.span; - debug!(?body.span); - - for (local, local_decl) in body.local_decls.iter_enumerated() { - self.check_local(body, local, local_decl); - } - - for (block, block_data) in body.basic_blocks.iter_enumerated() { - let mut location = Location { block, statement_index: 0 }; - for stmt in &block_data.statements { - if !stmt.source_info.span.is_dummy() { - self.last_span = stmt.source_info.span; - } - self.check_stmt(body, stmt, location); - location.statement_index += 1; - } - - self.check_terminator(body, block_data.terminator(), location); - self.check_iscleanup(body, block_data); - } - } } trait NormalizeLocation: fmt::Debug + Copy { diff --git a/tests/ui/consts/const-unsized.stderr b/tests/ui/consts/const-unsized.stderr index 8328e19aac22d..cee364b33f7a0 100644 --- a/tests/ui/consts/const-unsized.stderr +++ b/tests/ui/consts/const-unsized.stderr @@ -58,18 +58,18 @@ error[E0507]: cannot move out of a shared reference LL | static STATIC_BAR: str = *"bar"; | ^^^^^^ move occurs because value has type `str`, which does not implement the `Copy` trait -error[E0161]: cannot move a value of type `str` - --> $DIR/const-unsized.rs:20:48 - | -LL | println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); - | ^^^^^^^^^ the size of `str` cannot be statically determined - error[E0161]: cannot move a value of type `dyn Debug + Sync` --> $DIR/const-unsized.rs:20:38 | LL | println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); | ^^^^^^^ the size of `dyn Debug + Sync` cannot be statically determined +error[E0161]: cannot move a value of type `str` + --> $DIR/const-unsized.rs:20:48 + | +LL | println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); + | ^^^^^^^^^ the size of `str` cannot be statically determined + error: aborting due to 10 previous errors Some errors have detailed explanations: E0161, E0277, E0507. diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr index 55a5e4cd4971f..0fca91fd2f2f6 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -7,12 +7,12 @@ LL | let _ = x as &dyn Bar<'static, 'a>; // Error | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:22:18 + --> $DIR/type-checking-test-4.rs:22:13 | LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'a, 'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough --> $DIR/type-checking-test-4.rs:28:5