diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 1e6dadae363..ff73f135ce6 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -277,6 +277,10 @@ for mir::StatementKind<'gcx> { op.hash_stable(hcx, hasher); places.hash_stable(hcx, hasher); } + mir::StatementKind::UserAssertTy(ref ty, ref local) => { + ty.hash_stable(hcx, hasher); + local.hash_stable(hcx, hasher); + } mir::StatementKind::Nop => {} mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { asm.hash_stable(hcx, hasher); diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 64a1729982a..c36e401e8d6 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1253,6 +1253,15 @@ pub enum StatementKind<'tcx> { /// (The starting point(s) arise implicitly from borrows.) EndRegion(region::Scope), + /// Encodes a user's type assertion. These need to be preserved intact so that NLL can respect + /// them. For example: + /// + /// let (a, b): (T, U) = y; + /// + /// Here we would insert a `UserAssertTy<(T, U)>(y)` instruction to check that the type of `y` + /// is the right thing. + UserAssertTy(Ty<'tcx>, Local), + /// No-op. Useful for deleting instructions without affecting statement indices. Nop, } @@ -1324,6 +1333,7 @@ impl<'tcx> Debug for Statement<'tcx> { InlineAsm { ref asm, ref outputs, ref inputs } => { write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs) }, + UserAssertTy(ref ty, ref local) => write!(fmt, "UserAssertTy({:?}, {:?})", ty, local), Nop => write!(fmt, "nop"), } } @@ -2184,6 +2194,7 @@ EnumTypeFoldableImpl! { (StatementKind::InlineAsm) { asm, outputs, inputs }, (StatementKind::Validate)(a, b), (StatementKind::EndRegion)(a), + (StatementKind::UserAssertTy)(a, b), (StatementKind::Nop), } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 650af8dc4d9..cc5d9692e7a 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -144,6 +144,13 @@ macro_rules! make_mir_visitor { self.super_operand(operand, location); } + fn visit_user_assert_ty(&mut self, + ty: & $($mutability)* Ty<'tcx>, + local: & $($mutability)* Local, + location: Location) { + self.super_user_assert_ty(ty, local, location); + } + fn visit_place(&mut self, place: & $($mutability)* Place<'tcx>, context: PlaceContext<'tcx>, @@ -376,6 +383,10 @@ macro_rules! make_mir_visitor { self.visit_operand(input, location); } } + StatementKind::UserAssertTy(ref $($mutability)* ty, + ref $($mutability)* local) => { + self.visit_user_assert_ty(ty, local, location); + } StatementKind::Nop => {} } } @@ -619,6 +630,14 @@ macro_rules! make_mir_visitor { } } + fn super_user_assert_ty(&mut self, + ty: & $($mutability)* Ty<'tcx>, + local: & $($mutability)* Local, + location: Location) { + self.visit_ty(ty, TyContext::Location(location)); + self.visit_local(local, PlaceContext::Validate, location); + } + fn super_place(&mut self, place: & $($mutability)* Place<'tcx>, context: PlaceContext<'tcx>, diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 6e1a798910d..305df37466d 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -392,11 +392,13 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // ignored when consuming results (update to // flow_state already handled). } - StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { - // `Nop`, `Validate`, and `StorageLive` are irrelevant + StatementKind::Nop | + StatementKind::UserAssertTy(..) | + StatementKind::Validate(..) | + StatementKind::StorageLive(..) => { + // `Nop`, `UserAssertTy`, `Validate`, and `StorageLive` are irrelevant // to borrow check. } - StatementKind::StorageDead(local) => { self.access_place( ContextKind::StorageDead.new(location), diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 022831b5a92..f070691a5cf 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -766,6 +766,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { | StatementKind::InlineAsm { .. } | StatementKind::EndRegion(_) | StatementKind::Validate(..) + | StatementKind::UserAssertTy(..) | StatementKind::Nop => {} } } diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 7281fb59663..02d2522b6ba 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -102,6 +102,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { remainder_scope, init_scope, pattern, + ty, initializer, lint_level } => { @@ -120,12 +121,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { opt_destruction_scope.map(|de|(de, source_info)), block, |this| { let scope = (init_scope, source_info); this.in_scope(scope, lint_level, block, |this| { - this.expr_into_pattern(block, pattern, init) + this.expr_into_pattern(block, ty, pattern, init) }) })); } else { this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| { this.storage_live_binding(block, node, span); + if let Some(ty) = ty { + this.user_assert_ty(block, ty, node, span); + } this.schedule_drop_for_binding(node, span); }) } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 229e33dcd78..4353eb5e4bd 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -145,8 +145,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { end_block.unit() } + pub fn user_assert_ty(&mut self, block: BasicBlock, ty: Ty<'tcx>, var: NodeId, span: Span) { + let local_id = self.var_indices[&var]; + let source_info = self.source_info(span); + self.cfg.push(block, Statement { + source_info, + kind: StatementKind::UserAssertTy(ty, local_id), + }); + } + pub fn expr_into_pattern(&mut self, mut block: BasicBlock, + ty: Option>, irrefutable_pat: Pattern<'tcx>, initializer: ExprRef<'tcx>) -> BlockAnd<()> { @@ -156,6 +166,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { var, subpattern: None, .. } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span); + + if let Some(ty) = ty { + self.user_assert_ty(block, ty, var, irrefutable_pat.span); + } + unpack!(block = self.into(&place, block, initializer)); self.schedule_drop_for_binding(var, irrefutable_pat.span); block.unit() diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 499ed55fad4..fb3042014df 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -678,6 +678,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { mir::StatementKind::SetDiscriminant { .. } | mir::StatementKind::StorageLive(..) | mir::StatementKind::Validate(..) | + mir::StatementKind::UserAssertTy(..) | mir::StatementKind::Nop => {} } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index d6f419f6cfb..cbf4c822769 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -298,6 +298,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } StatementKind::EndRegion(_) | StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => {} } } diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 6f6258f52f7..f0497338303 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -76,12 +76,14 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, first_statement_index: region::FirstStatementIndex::new(index), }); + let ty = local.ty.clone().map(|ty| cx.tables().node_id_to_type(ty.hir_id)); let pattern = cx.pattern_from_hir(&local.pat); result.push(StmtRef::Mirror(Box::new(Stmt { kind: StmtKind::Let { remainder_scope: remainder_scope, init_scope: region::Scope::Node(hir_id.local_id), pattern, + ty, initializer: local.init.to_ref(), lint_level: cx.lint_level_of(local.id), }, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 5f60a134fb1..138944189ff 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -93,10 +93,13 @@ pub enum StmtKind<'tcx> { /// lifetime of temporaries init_scope: region::Scope, - /// let = ... + /// let : ty = ... pattern: Pattern<'tcx>, - /// let pat = ... + /// let pat: = init ... + ty: Option>, + + /// let pat: ty = ... initializer: Option>, /// the lint level for this let-statement diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 4e1750caf26..f1d58ff5e88 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -89,6 +89,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { M::end_region(self, Some(ce))?; } + UserAssertTy(..) => {} + // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 86d08dec2b9..02c70399664 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -105,6 +105,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { StatementKind::StorageDead(..) | StatementKind::EndRegion(..) | StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => { // safe (at least as emitted during MIR construction) } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 81b740c917b..a06571d0abd 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -234,6 +234,8 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx simplify_branches::SimplifyBranches::new("initial"), remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("early-opt"), + // Remove all `UserAssertTy` statements. + cleanup_post_borrowck::CleanUserAssertTy, // These next passes must be executed together add_call_guards::CriticalCallEdges, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 59a872a23b0..8e2f98d2769 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1099,6 +1099,7 @@ This does not pose a problem by itself because they can't be accessed directly." StatementKind::InlineAsm {..} | StatementKind::EndRegion(_) | StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => {} } }); diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index cd80d25c410..6d365012525 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -50,6 +50,7 @@ impl RemoveNoopLandingPads { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::EndRegion(_) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => { // These are all nops in a landing pad (there's some // borrowck interaction between EndRegion and storage diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 76283edac72..45e7a0d3f4c 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -163,6 +163,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir::StatementKind::InlineAsm { .. } | mir::StatementKind::EndRegion(_) | mir::StatementKind::Validate(..) | + mir::StatementKind::UserAssertTy(..) | mir::StatementKind::Nop => continue, mir::StatementKind::SetDiscriminant{ .. } => span_bug!(stmt.source_info.span, diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 4a4ce63cc1d..a4e056c6b58 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -90,6 +90,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { StatementKind::StorageLive(..) => "StatementKind::StorageLive", StatementKind::StorageDead(..) => "StatementKind::StorageDead", StatementKind::InlineAsm { .. } => "StatementKind::InlineAsm", + StatementKind::UserAssertTy(..) => "StatementKind::UserAssertTy", StatementKind::Nop => "StatementKind::Nop", }, &statement.kind); self.super_statement(block, statement, location); diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index b5b74849401..579b07929a2 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -84,6 +84,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { } mir::StatementKind::EndRegion(_) | mir::StatementKind::Validate(..) | + mir::StatementKind::UserAssertTy(..) | mir::StatementKind::Nop => bx, } }