Skip to content

Commit c523b3f

Browse files
committed
Auto merge of #43403 - RalfJung:mir-validate, r=nikomatsakis
Add MIR Validate statement This adds statements to MIR that express when types are to be validated (following [Types as Contracts](https://internals.rust-lang.org/t/types-as-contracts/5562)). Obviously nothing is stabilized, and in fact a `-Z` flag has to be passed for behavior to even change at all. This is meant to make experimentation with Types as Contracts in miri possible. The design is definitely not final. Cc @nikomatsakis @aturon
2 parents 5431f42 + 7d8dc7a commit c523b3f

27 files changed

+840
-31
lines changed

src/librustc/hir/map/blocks.rs

+12
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ impl<'a> FnLikeNode<'a> {
192192
}
193193
}
194194

195+
pub fn unsafety(self) -> ast::Unsafety {
196+
match self.kind() {
197+
FnKind::ItemFn(_, _, unsafety, ..) => {
198+
unsafety
199+
}
200+
FnKind::Method(_, m, ..) => {
201+
m.unsafety
202+
}
203+
_ => ast::Unsafety::Normal
204+
}
205+
}
206+
195207
pub fn kind(self) -> FnKind<'a> {
196208
let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
197209
FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis, p.attrs)

src/librustc/hir/mod.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use rustc_data_structures::indexed_vec;
4848
use std::collections::BTreeMap;
4949
use std::fmt;
5050

51-
/// HIR doesn't commit to a concrete storage type and have its own alias for a vector.
51+
/// HIR doesn't commit to a concrete storage type and has its own alias for a vector.
5252
/// It can be `Vec`, `P<[T]>` or potentially `Box<[T]>`, or some other container with similar
5353
/// behavior. Unlike AST, HIR is mostly a static structure, so we can use an owned slice instead
5454
/// of `Vec` to avoid keeping extra capacity.
@@ -75,14 +75,14 @@ pub mod pat_util;
7575
pub mod print;
7676
pub mod svh;
7777

78-
/// A HirId uniquely identifies a node in the HIR of then current crate. It is
78+
/// A HirId uniquely identifies a node in the HIR of the current crate. It is
7979
/// composed of the `owner`, which is the DefIndex of the directly enclosing
8080
/// hir::Item, hir::TraitItem, or hir::ImplItem (i.e. the closest "item-like"),
8181
/// and the `local_id` which is unique within the given owner.
8282
///
8383
/// This two-level structure makes for more stable values: One can move an item
8484
/// around within the source code, or add or remove stuff before it, without
85-
/// the local_id part of the HirId changing, which is a very useful property
85+
/// the local_id part of the HirId changing, which is a very useful property in
8686
/// incremental compilation where we have to persist things through changes to
8787
/// the code base.
8888
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug,
@@ -701,6 +701,16 @@ pub enum Mutability {
701701
MutImmutable,
702702
}
703703

704+
impl Mutability {
705+
/// Return MutMutable only if both arguments are mutable.
706+
pub fn and(self, other: Self) -> Self {
707+
match self {
708+
MutMutable => other,
709+
MutImmutable => MutImmutable,
710+
}
711+
}
712+
}
713+
704714
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
705715
pub enum BinOp_ {
706716
/// The `+` operator (addition)

src/librustc/ich/impls_mir.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,12 @@ for mir::StatementKind<'tcx> {
226226
mir::StatementKind::StorageDead(ref lvalue) => {
227227
lvalue.hash_stable(hcx, hasher);
228228
}
229-
mir::StatementKind::EndRegion(ref extents) => {
230-
extents.hash_stable(hcx, hasher);
229+
mir::StatementKind::EndRegion(ref extent) => {
230+
extent.hash_stable(hcx, hasher);
231+
}
232+
mir::StatementKind::Validate(ref op, ref lvalues) => {
233+
op.hash_stable(hcx, hasher);
234+
lvalues.hash_stable(hcx, hasher);
231235
}
232236
mir::StatementKind::Nop => {}
233237
mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
@@ -239,6 +243,23 @@ for mir::StatementKind<'tcx> {
239243
}
240244
}
241245

246+
impl<'a, 'gcx, 'tcx, T> HashStable<StableHashingContext<'a, 'gcx, 'tcx>>
247+
for mir::ValidationOperand<'tcx, T>
248+
where T: HashStable<StableHashingContext<'a, 'gcx, 'tcx>>
249+
{
250+
fn hash_stable<W: StableHasherResult>(&self,
251+
hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
252+
hasher: &mut StableHasher<W>)
253+
{
254+
self.lval.hash_stable(hcx, hasher);
255+
self.ty.hash_stable(hcx, hasher);
256+
self.re.hash_stable(hcx, hasher);
257+
self.mutbl.hash_stable(hcx, hasher);
258+
}
259+
}
260+
261+
impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(extent) });
262+
242263
impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for mir::Lvalue<'tcx> {
243264
fn hash_stable<W: StableHasherResult>(&self,
244265
hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,

src/librustc/mir/mod.rs

+81-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use ty::{self, AdtDef, ClosureSubsts, Region, Ty};
2525
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
2626
use util::ppaux;
2727
use rustc_back::slice;
28-
use hir::InlineAsm;
28+
use hir::{self, InlineAsm};
2929
use std::ascii;
3030
use std::borrow::{Cow};
3131
use std::cell::Ref;
@@ -818,12 +818,18 @@ pub enum StatementKind<'tcx> {
818818
/// End the current live range for the storage of the local.
819819
StorageDead(Lvalue<'tcx>),
820820

821+
/// Execute a piece of inline Assembly.
821822
InlineAsm {
822823
asm: Box<InlineAsm>,
823824
outputs: Vec<Lvalue<'tcx>>,
824825
inputs: Vec<Operand<'tcx>>
825826
},
826827

828+
/// Assert the given lvalues to be valid inhabitants of their type. These statements are
829+
/// currently only interpreted by miri and only generated when "-Z mir-emit-validate" is passed.
830+
/// See <https://internals.rust-lang.org/t/types-as-contracts/5562/73> for more details.
831+
Validate(ValidationOp, Vec<ValidationOperand<'tcx, Lvalue<'tcx>>>),
832+
827833
/// Mark one terminating point of an extent (i.e. static region).
828834
/// (The starting point(s) arise implicitly from borrows.)
829835
EndRegion(CodeExtent),
@@ -832,13 +838,65 @@ pub enum StatementKind<'tcx> {
832838
Nop,
833839
}
834840

841+
/// The `ValidationOp` describes what happens with each of the operands of a
842+
/// `Validate` statement.
843+
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, PartialEq, Eq)]
844+
pub enum ValidationOp {
845+
/// Recursively traverse the lvalue following the type and validate that all type
846+
/// invariants are maintained. Furthermore, acquire exclusive/read-only access to the
847+
/// memory reachable from the lvalue.
848+
Acquire,
849+
/// Recursive traverse the *mutable* part of the type and relinquish all exclusive
850+
/// access.
851+
Release,
852+
/// Recursive traverse the *mutable* part of the type and relinquish all exclusive
853+
/// access *until* the given region ends. Then, access will be recovered.
854+
Suspend(CodeExtent),
855+
}
856+
857+
impl Debug for ValidationOp {
858+
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
859+
use self::ValidationOp::*;
860+
match *self {
861+
Acquire => write!(fmt, "Acquire"),
862+
Release => write!(fmt, "Release"),
863+
// (reuse lifetime rendering policy from ppaux.)
864+
Suspend(ref ce) => write!(fmt, "Suspend({})", ty::ReScope(*ce)),
865+
}
866+
}
867+
}
868+
869+
// This is generic so that it can be reused by miri
870+
#[derive(Clone, RustcEncodable, RustcDecodable)]
871+
pub struct ValidationOperand<'tcx, T> {
872+
pub lval: T,
873+
pub ty: Ty<'tcx>,
874+
pub re: Option<CodeExtent>,
875+
pub mutbl: hir::Mutability,
876+
}
877+
878+
impl<'tcx, T: Debug> Debug for ValidationOperand<'tcx, T> {
879+
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
880+
write!(fmt, "{:?}: {:?}", self.lval, self.ty)?;
881+
if let Some(ce) = self.re {
882+
// (reuse lifetime rendering policy from ppaux.)
883+
write!(fmt, "/{}", ty::ReScope(ce))?;
884+
}
885+
if let hir::MutImmutable = self.mutbl {
886+
write!(fmt, " (imm)")?;
887+
}
888+
Ok(())
889+
}
890+
}
891+
835892
impl<'tcx> Debug for Statement<'tcx> {
836893
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
837894
use self::StatementKind::*;
838895
match self.kind {
839896
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
840897
// (reuse lifetime rendering policy from ppaux.)
841898
EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
899+
Validate(ref op, ref lvalues) => write!(fmt, "Validate({:?}, {:?})", op, lvalues),
842900
StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv),
843901
StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
844902
SetDiscriminant{lvalue: ref lv, variant_index: index} => {
@@ -1481,6 +1539,21 @@ impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> {
14811539
}
14821540
}
14831541

1542+
impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Lvalue<'tcx>> {
1543+
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
1544+
ValidationOperand {
1545+
lval: self.lval.fold_with(folder),
1546+
ty: self.ty.fold_with(folder),
1547+
re: self.re,
1548+
mutbl: self.mutbl,
1549+
}
1550+
}
1551+
1552+
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
1553+
self.lval.visit_with(visitor) || self.ty.visit_with(visitor)
1554+
}
1555+
}
1556+
14841557
impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
14851558
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
14861559
use mir::StatementKind::*;
@@ -1505,6 +1578,10 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
15051578
// trait with a `fn fold_extent`.
15061579
EndRegion(ref extent) => EndRegion(extent.clone()),
15071580

1581+
Validate(ref op, ref lvals) =>
1582+
Validate(op.clone(),
1583+
lvals.iter().map(|operand| operand.fold_with(folder)).collect()),
1584+
15081585
Nop => Nop,
15091586
};
15101587
Statement {
@@ -1530,6 +1607,9 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
15301607
// trait with a `fn visit_extent`.
15311608
EndRegion(ref _extent) => false,
15321609

1610+
Validate(ref _op, ref lvalues) =>
1611+
lvalues.iter().any(|ty_and_lvalue| ty_and_lvalue.visit_with(visitor)),
1612+
15331613
Nop => false,
15341614
}
15351615
}

src/librustc/mir/visit.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,13 @@ macro_rules! make_mir_visitor {
338338
self.visit_assign(block, lvalue, rvalue, location);
339339
}
340340
StatementKind::EndRegion(_) => {}
341+
StatementKind::Validate(_, ref $($mutability)* lvalues) => {
342+
for operand in lvalues {
343+
self.visit_lvalue(& $($mutability)* operand.lval,
344+
LvalueContext::Validate, location);
345+
self.visit_ty(& $($mutability)* operand.ty, Lookup::Loc(location));
346+
}
347+
}
341348
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
342349
self.visit_lvalue(lvalue, LvalueContext::Store, location);
343350
}
@@ -789,6 +796,9 @@ pub enum LvalueContext<'tcx> {
789796
// Starting and ending a storage live range
790797
StorageLive,
791798
StorageDead,
799+
800+
// Validation command
801+
Validate,
792802
}
793803

794804
impl<'tcx> LvalueContext<'tcx> {
@@ -835,7 +845,8 @@ impl<'tcx> LvalueContext<'tcx> {
835845
LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
836846
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
837847
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume |
838-
LvalueContext::StorageLive | LvalueContext::StorageDead => false,
848+
LvalueContext::StorageLive | LvalueContext::StorageDead |
849+
LvalueContext::Validate => false,
839850
}
840851
}
841852

@@ -847,7 +858,8 @@ impl<'tcx> LvalueContext<'tcx> {
847858
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume => true,
848859
LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | LvalueContext::Store |
849860
LvalueContext::Call | LvalueContext::Projection(Mutability::Mut) |
850-
LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead => false,
861+
LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead |
862+
LvalueContext::Validate => false,
851863
}
852864
}
853865

src/librustc/session/config.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
10251025
"the directory the MIR is dumped into"),
10261026
dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED],
10271027
"if set, exclude the pass number when dumping MIR (used in tests)"),
1028+
mir_emit_validate: usize = (0, parse_uint, [TRACKED],
1029+
"emit Validate MIR statements, interpreted e.g. by miri (0: do not emit; 1: if function \
1030+
contains unsafe block, only validate arguments; 2: always emit full validation)"),
10281031
perf_stats: bool = (false, parse_bool, [UNTRACKED],
10291032
"print some performance-related statistics"),
10301033
hir_stats: bool = (false, parse_bool, [UNTRACKED],

src/librustc_driver/driver.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -929,25 +929,32 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
929929
passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir);
930930
passes.push_pass(MIR_CONST, mir::transform::rustc_peek::SanityCheck);
931931

932+
// We compute "constant qualifications" betwen MIR_CONST and MIR_VALIDATED.
933+
932934
// What we need to run borrowck etc.
933935
passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
934936
passes.push_pass(MIR_VALIDATED,
935937
mir::transform::simplify_branches::SimplifyBranches::new("initial"));
936938
passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
937939
passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL);
938940

939-
// Optimizations begin.
940-
passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
941-
passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("no-landing-pads"));
941+
// borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.
942942

943-
// From here on out, regions are gone.
944-
passes.push_pass(MIR_OPTIMIZED, mir::transform::erase_regions::EraseRegions);
943+
// These next passes must be executed together
944+
passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
945945
passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::AddCallGuards);
946946
passes.push_pass(MIR_OPTIMIZED, mir::transform::elaborate_drops::ElaborateDrops);
947947
passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
948948
passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("elaborate-drops"));
949-
950949
// No lifetime analysis based on borrowing can be done from here on out.
950+
951+
// AddValidation needs to run after ElaborateDrops and before EraseRegions.
952+
passes.push_pass(MIR_OPTIMIZED, mir::transform::add_validation::AddValidation);
953+
954+
// From here on out, regions are gone.
955+
passes.push_pass(MIR_OPTIMIZED, mir::transform::erase_regions::EraseRegions);
956+
957+
// Optimizations begin.
951958
passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline);
952959
passes.push_pass(MIR_OPTIMIZED, mir::transform::instcombine::InstCombine);
953960
passes.push_pass(MIR_OPTIMIZED, mir::transform::deaggregator::Deaggregator);

src/librustc_mir/dataflow/drop_flag_effects.rs

+1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
289289
mir::StatementKind::StorageDead(_) |
290290
mir::StatementKind::InlineAsm { .. } |
291291
mir::StatementKind::EndRegion(_) |
292+
mir::StatementKind::Validate(..) |
292293
mir::StatementKind::Nop => {}
293294
},
294295
None => {

src/librustc_mir/dataflow/impls/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
486486
mir::StatementKind::StorageDead(_) |
487487
mir::StatementKind::InlineAsm { .. } |
488488
mir::StatementKind::EndRegion(_) |
489+
mir::StatementKind::Validate(..) |
489490
mir::StatementKind::Nop => {}
490491
}
491492
}

src/librustc_mir/dataflow/move_paths/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
416416
}
417417
StatementKind::InlineAsm { .. } |
418418
StatementKind::EndRegion(_) |
419+
StatementKind::Validate(..) |
419420
StatementKind::Nop => {}
420421
}
421422
}

0 commit comments

Comments
 (0)