Skip to content

Commit a6fad3f

Browse files
committed
Add more fake borrows to matches
1 parent b55bb2e commit a6fad3f

File tree

3 files changed

+171
-58
lines changed

3 files changed

+171
-58
lines changed

src/librustc/ich/impls_mir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ for mir::StatementKind<'gcx> {
273273
}
274274
}
275275

276-
impl_stable_hash_for!(enum mir::FakeReadCause { ForMatch, ForLet });
276+
impl_stable_hash_for!(enum mir::FakeReadCause { ForMatchGuard, ForMatchedPlace, ForLet });
277277

278278
impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
279279
for mir::ValidationOperand<'gcx, T>

src/librustc/mir/mod.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ impl From<Mutability> for hir::Mutability {
451451
}
452452
}
453453

454-
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
454+
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)]
455455
pub enum BorrowKind {
456456
/// Data must be immutable and is aliasable.
457457
Shared,
@@ -1693,7 +1693,11 @@ pub enum FakeReadCause {
16931693
///
16941694
/// This should ensure that you cannot change the variant for an enum
16951695
/// while you are in the midst of matching on it.
1696-
ForMatch,
1696+
ForMatchGuard,
1697+
1698+
/// `let x: !; match x {}` doesn't generate any read of x so we need to
1699+
/// generate a read of x to check that it is initialized and safe.
1700+
ForMatchedPlace,
16971701

16981702
/// Officially, the semantics of
16991703
///
@@ -1794,7 +1798,7 @@ impl<'tcx> Debug for Statement<'tcx> {
17941798

17951799
/// A path to a value; something that can be evaluated without
17961800
/// changing or disturbing program state.
1797-
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
1801+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
17981802
pub enum Place<'tcx> {
17991803
/// local variable
18001804
Local(Local),
@@ -1811,7 +1815,7 @@ pub enum Place<'tcx> {
18111815

18121816
/// The def-id of a static, along with its normalized type (which is
18131817
/// stored to avoid requiring normalization when reading MIR).
1814-
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
1818+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
18151819
pub struct Static<'tcx> {
18161820
pub def_id: DefId,
18171821
pub ty: Ty<'tcx>,
@@ -1826,13 +1830,13 @@ impl_stable_hash_for!(struct Static<'tcx> {
18261830
/// or `*B` or `B[index]`. Note that it is parameterized because it is
18271831
/// shared between `Constant` and `Place`. See the aliases
18281832
/// `PlaceProjection` etc below.
1829-
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
1833+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
18301834
pub struct Projection<'tcx, B, V, T> {
18311835
pub base: B,
18321836
pub elem: ProjectionElem<'tcx, V, T>,
18331837
}
18341838

1835-
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
1839+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
18361840
pub enum ProjectionElem<'tcx, V, T> {
18371841
Deref,
18381842
Field(Field, T),

src/librustc_mir/build/matches/mod.rs

Lines changed: 160 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,39 +57,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5757
// See issue #47412 for this hole being discovered in the wild.
5858
//
5959
// HACK(eddyb) Work around the above issue by adding a dummy inspection
60-
// of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
61-
// (which will work regardless of type) and storing the result in a temp.
60+
// of `discriminant_place`, specifically by applying `ReadForMatch`.
6261
//
63-
// NOTE: Under NLL, the above issue should no longer occur because it
64-
// injects a borrow of the matched input, which should have the same effect
65-
// as eddyb's hack. Once NLL is the default, we can remove the hack.
66-
67-
let dummy_source_info = self.source_info(discriminant_span);
68-
let dummy_access = Rvalue::Discriminant(discriminant_place.clone());
69-
let dummy_ty = dummy_access.ty(&self.local_decls, tcx);
70-
let dummy_temp = self.temp(dummy_ty, dummy_source_info.span);
71-
self.cfg
72-
.push_assign(block, dummy_source_info, &dummy_temp, dummy_access);
62+
// NOTE: ReadForMatch also checks that the discriminant is initialized.
63+
// This is currently needed to not allow matching on an uninitialized,
64+
// uninhabited value. If we get never patterns, those will check that
65+
// the place is initialized, and so this read would only be used to
66+
// check safety.
7367

7468
let source_info = self.source_info(discriminant_span);
75-
let borrowed_input_temp = if tcx.generate_borrow_of_any_match_input() {
76-
// The region is unknown at this point; we rely on NLL
77-
// inference to find an appropriate one. Therefore you can
78-
// only use this when NLL is turned on.
79-
assert!(tcx.use_mir_borrowck());
80-
let borrowed_input = Rvalue::Ref(
81-
tcx.types.re_empty,
82-
BorrowKind::Shared,
69+
self.cfg.push(block, Statement {
70+
source_info,
71+
kind: StatementKind::FakeRead(
72+
FakeReadCause::ForMatchedPlace,
8373
discriminant_place.clone(),
84-
);
85-
let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx);
86-
let borrowed_input_temp = self.temp(borrowed_input_ty, span);
87-
self.cfg
88-
.push_assign(block, source_info, &borrowed_input_temp, borrowed_input);
89-
Some(borrowed_input_temp)
90-
} else {
91-
None
92-
};
74+
),
75+
});
9376

9477
let mut arm_blocks = ArmBlocks {
9578
blocks: arms.iter().map(|_| self.cfg.start_new_block()).collect(),
@@ -118,6 +101,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
118101
.map(|_| self.cfg.start_new_block())
119102
.collect();
120103

104+
let mut has_guard = false;
105+
121106
// assemble a list of candidates: there is one candidate per
122107
// pattern, which means there may be more than one candidate
123108
// *per arm*. These candidates are kept sorted such that the
@@ -140,24 +125,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
140125
.map(
141126
|(
142127
(arm_index, pat_index, pattern, guard),
143-
(pre_binding_block, next_candidate_pre_binding_block),
128+
(pre_binding_block, next_candidate_pre_binding_block)
144129
)| {
145-
if let (true, Some(borrow_temp)) =
146-
(tcx.emit_read_for_match(), borrowed_input_temp.clone())
147-
{
148-
// Inject a fake read, see comments on `FakeReadCause::ForMatch`.
149-
let pattern_source_info = self.source_info(pattern.span);
150-
self.cfg.push(
151-
*pre_binding_block,
152-
Statement {
153-
source_info: pattern_source_info,
154-
kind: StatementKind::FakeRead(
155-
FakeReadCause::ForMatch,
156-
borrow_temp.clone(),
157-
),
158-
},
159-
);
160-
}
130+
has_guard |= guard.is_some();
161131

162132
// One might ask: why not build up the match pair such that it
163133
// matches via `borrowed_input_temp.deref()` instead of
@@ -202,9 +172,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
202172
TerminatorKind::Unreachable,
203173
);
204174

175+
// Maps a place to the kind of Fake borrow that we want to perform on
176+
// it: either Shallow or Shared, depending on whether the place is
177+
// bound in the match, or just switched on.
178+
// If there are no match guards then we don't need any fake borrows,
179+
// so don't track them.
180+
let mut fake_borrows = if has_guard && tcx.generate_borrow_of_any_match_input() {
181+
Some(FxHashMap())
182+
} else {
183+
None
184+
};
185+
186+
let pre_binding_blocks: Vec<_> = candidates
187+
.iter()
188+
.map(|cand| (cand.pre_binding_block, cand.span))
189+
.collect();
190+
205191
// this will generate code to test discriminant_place and
206192
// branch to the appropriate arm block
207-
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
193+
let otherwise = self.match_candidates(
194+
discriminant_span,
195+
&mut arm_blocks,
196+
candidates,
197+
block,
198+
&mut fake_borrows,
199+
);
208200

209201
if !otherwise.is_empty() {
210202
// All matches are exhaustive. However, because some matches
@@ -224,6 +216,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
224216
}
225217
}
226218

219+
if let Some(fake_borrows) = fake_borrows {
220+
self.add_fake_borrows(&pre_binding_blocks, fake_borrows, source_info, block);
221+
}
222+
227223
// all the arm blocks will rejoin here
228224
let end_block = self.cfg.start_new_block();
229225

@@ -714,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
714710
/// up the list of candidates and recurse with a non-exhaustive
715711
/// list. This is important to keep the size of the generated code
716712
/// under control. See `test_candidates` for more details.
713+
///
714+
/// If `add_fake_borrows` is true, then places which need fake borrows
715+
/// will be added to it.
717716
fn match_candidates<'pat>(
718717
&mut self,
719718
span: Span,
720719
arm_blocks: &mut ArmBlocks,
721720
mut candidates: Vec<Candidate<'pat, 'tcx>>,
722721
mut block: BasicBlock,
722+
fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
723723
) -> Vec<BasicBlock> {
724724
debug!(
725725
"matched_candidate(span={:?}, block={:?}, candidates={:?})",
@@ -747,6 +747,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
747747
);
748748
let mut unmatched_candidates = candidates.split_off(fully_matched);
749749

750+
// Insert a *Shared* borrow of any places that are bound.
751+
if let Some(fake_borrows) = fake_borrows {
752+
for Binding { source, .. }
753+
in candidates.iter().flat_map(|candidate| &candidate.bindings)
754+
{
755+
fake_borrows.insert(source.clone(), BorrowKind::Shared);
756+
}
757+
}
758+
750759
let fully_matched_with_guard = candidates.iter().take_while(|c| c.guard.is_some()).count();
751760

752761
let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() {
@@ -783,7 +792,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
783792
return vec![];
784793
} else {
785794
let target = self.cfg.start_new_block();
786-
return self.match_candidates(span, arm_blocks, unmatched_candidates, target);
795+
return self.match_candidates(
796+
span,
797+
arm_blocks,
798+
unmatched_candidates,
799+
target,
800+
&mut None,
801+
);
787802
}
788803
}
789804
}
@@ -796,7 +811,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
796811

797812
// Test candidates where possible.
798813
let (otherwise, tested_candidates) =
799-
self.test_candidates(span, arm_blocks, &unmatched_candidates, block);
814+
self.test_candidates(span, arm_blocks, &unmatched_candidates, block, fake_borrows);
800815

801816
// If the target candidates were exhaustive, then we are done.
802817
// But for borrowck continue build decision tree.
@@ -810,7 +825,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
810825

811826
// Otherwise, let's process those remaining candidates.
812827
let join_block = self.join_otherwise_blocks(span, otherwise);
813-
self.match_candidates(span, arm_blocks, untested_candidates, join_block)
828+
self.match_candidates(span, arm_blocks, untested_candidates, join_block, &mut None)
814829
}
815830

816831
fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
@@ -950,6 +965,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
950965
arm_blocks: &mut ArmBlocks,
951966
candidates: &[Candidate<'pat, 'tcx>],
952967
block: BasicBlock,
968+
fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
953969
) -> (Vec<BasicBlock>, usize) {
954970
// extract the match-pair from the highest priority candidate
955971
let match_pair = &candidates.first().unwrap().match_pairs[0];
@@ -990,6 +1006,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9901006
_ => {}
9911007
}
9921008

1009+
// Insert a Shallow borrow of any places that is switched on.
1010+
fake_borrows.as_mut().map(|fb| {
1011+
fb.entry(match_pair.place.clone()).or_insert(BorrowKind::Shallow)
1012+
});
1013+
9931014
// perform the test, branching to one of N blocks. For each of
9941015
// those N possible outcomes, create a (initially empty)
9951016
// vector of candidates. Those are the candidates that still
@@ -1026,7 +1047,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10261047
.into_iter()
10271048
.zip(target_candidates)
10281049
.flat_map(|(target_block, target_candidates)| {
1029-
self.match_candidates(span, arm_blocks, target_candidates, target_block)
1050+
self.match_candidates(
1051+
span,
1052+
arm_blocks,
1053+
target_candidates,
1054+
target_block,
1055+
fake_borrows,
1056+
)
10301057
})
10311058
.collect();
10321059

@@ -1504,4 +1531,86 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
15041531
debug!("declare_binding: vars={:?}", locals);
15051532
self.var_indices.insert(var_id, locals);
15061533
}
1534+
1535+
// Determine the fake borrows that are needed to ensure that the place
1536+
// will evaluate to the same thing until an arm has been chosen.
1537+
fn add_fake_borrows<'pat>(
1538+
&mut self,
1539+
pre_binding_blocks: &[(BasicBlock, Span)],
1540+
fake_borrows: FxHashMap<Place<'tcx>, BorrowKind>,
1541+
source_info: SourceInfo,
1542+
start_block: BasicBlock,
1543+
) {
1544+
let tcx = self.hir.tcx();
1545+
1546+
debug!("add_fake_borrows pre_binding_blocks = {:?}, fake_borrows = {:?}",
1547+
pre_binding_blocks, fake_borrows);
1548+
1549+
let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
1550+
1551+
// Insert a Shallow borrow of the prefixes of any fake borrows.
1552+
for (place, borrow_kind) in fake_borrows
1553+
{
1554+
{
1555+
let mut prefix_cursor = &place;
1556+
while let Place::Projection(box Projection { base, elem }) = prefix_cursor {
1557+
if let ProjectionElem::Deref = elem {
1558+
// Insert a shallow borrow after a deref. For other
1559+
// projections the borrow of prefix_cursor will
1560+
// conflict with any mutation of base.
1561+
all_fake_borrows.push((base.clone(), BorrowKind::Shallow));
1562+
}
1563+
prefix_cursor = base;
1564+
}
1565+
}
1566+
1567+
all_fake_borrows.push((place, borrow_kind));
1568+
}
1569+
1570+
// Deduplicate and ensure a deterministic order.
1571+
all_fake_borrows.sort();
1572+
all_fake_borrows.dedup();
1573+
1574+
debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
1575+
1576+
// Add fake borrows to the start of the match and reads of them before
1577+
// the start of each arm.
1578+
let mut borrowed_input_temps = Vec::with_capacity(all_fake_borrows.len());
1579+
1580+
for (matched_place, borrow_kind) in all_fake_borrows {
1581+
let borrowed_input =
1582+
Rvalue::Ref(tcx.types.re_empty, borrow_kind, matched_place.clone());
1583+
let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx);
1584+
let borrowed_input_temp = self.temp(borrowed_input_ty, source_info.span);
1585+
self.cfg.push_assign(
1586+
start_block,
1587+
source_info,
1588+
&borrowed_input_temp,
1589+
borrowed_input
1590+
);
1591+
borrowed_input_temps.push(borrowed_input_temp);
1592+
}
1593+
1594+
// FIXME: This could be a lot of reads (#fake borrows * #patterns).
1595+
// The false edges that we currently generate would allow us to only do
1596+
// this on the last Candidate, but it's possible that there might not be
1597+
// so many false edges in the future, so we read for all Candidates for
1598+
// now.
1599+
// Another option would be to make our own block and add our own false
1600+
// edges to it.
1601+
if tcx.emit_read_for_match() {
1602+
for &(pre_binding_block, span) in pre_binding_blocks {
1603+
let pattern_source_info = self.source_info(span);
1604+
for temp in &borrowed_input_temps {
1605+
self.cfg.push(pre_binding_block, Statement {
1606+
source_info: pattern_source_info,
1607+
kind: StatementKind::FakeRead(
1608+
FakeReadCause::ForMatchGuard,
1609+
temp.clone(),
1610+
),
1611+
});
1612+
}
1613+
}
1614+
}
1615+
}
15071616
}

0 commit comments

Comments
 (0)