Skip to content

Commit f9f52c8

Browse files
committed
construct a PatCtxt
1 parent 919123d commit f9f52c8

File tree

6 files changed

+220
-69
lines changed

6 files changed

+220
-69
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,6 @@ pub enum ExprKind<'tcx> {
369369

370370
region_scope: region::Scope,
371371
//lint_level: LintLevel,
372-
373372
arms: Box<[ArmId]>,
374373
match_source: MatchSource,
375374
},

compiler/rustc_mir_build/src/builder/expr/into.rs

Lines changed: 150 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use rustc_middle::mir::*;
88
use rustc_middle::span_bug;
99
use rustc_middle::thir::*;
1010
use rustc_middle::ty::CanonicalUserTypeAnnotation;
11+
use rustc_middle::ty::util::Discr;
12+
use rustc_pattern_analysis::constructor::Constructor;
13+
use rustc_pattern_analysis::rustc::RustcPatCtxt;
1114
use rustc_span::source_map::Spanned;
1215
use tracing::{debug, instrument};
1316

@@ -244,6 +247,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
244247
ExprKind::LoopMatch { state, region_scope, ref arms, .. } => {
245248
// FIXME add diagram
246249

250+
let dropless_arena = rustc_arena::DroplessArena::default();
251+
let typeck_results = this.tcx.typeck(this.def_id);
252+
let lint_level = this.tcx.local_def_id_to_hir_id(this.def_id);
253+
254+
// the PatCtxt is normally used in pattern exhaustiveness checking, but reused here
255+
// because it performs normalization and const evaluation.
256+
let cx = RustcPatCtxt {
257+
tcx: this.tcx,
258+
typeck_results,
259+
module: this.tcx.parent_module(lint_level).to_def_id(),
260+
// FIXME(#132279): We're in a body, should handle opaques.
261+
typing_env: rustc_middle::ty::TypingEnv::non_body_analysis(
262+
this.tcx,
263+
this.def_id,
264+
),
265+
dropless_arena: &dropless_arena,
266+
match_lint_level: lint_level,
267+
whole_match_span: Some(rustc_span::Span::default()),
268+
scrut_span: rustc_span::Span::default(),
269+
refutable: true,
270+
known_valid_scrutinee: true,
271+
};
272+
247273
let loop_block = this.cfg.start_new_block();
248274

249275
// Start the loop.
@@ -264,59 +290,139 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
264290
this.diverge_from(loop_block);
265291

266292
let state_place = unpack!(body_block = this.as_place(body_block, state));
293+
let state_ty = this.thir.exprs[state].ty;
267294

268-
unpack!(
269-
body_block = this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
270-
let unreachable_block = this.cfg.start_new_block();
271-
this.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable);
295+
// the type of the value that is switched on by the `SwitchInt`
296+
let discr_ty = match state_ty {
297+
ty if ty.is_enum() => ty.discriminant_ty(this.tcx),
298+
ty if ty.is_integral() => ty,
299+
_ => todo!(),
300+
};
272301

273-
let mut arm_blocks = arms.iter().map(|&arm| {
274-
let block = this.cfg.start_new_block();
275-
match &this.thir[arm].pattern.kind {
276-
PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => {
277-
assert!(subpatterns.is_empty());
302+
let rvalue = match state_ty {
303+
ty if ty.is_enum() => Rvalue::Discriminant(state_place),
304+
ty if ty.is_integral() => Rvalue::Use(Operand::Copy(state_place)),
305+
_ => todo!(),
306+
};
278307

279-
let discr = adt_def.discriminants(this.tcx).find(|(var, _discr)| var == variant_index).unwrap().1;
308+
// block and arm of the wildcard pattern (if any)
309+
let mut otherwise = None;
280310

281-
(discr, block, arm)
282-
}
283-
_ => panic!(),
284-
}
285-
}).collect::<Vec<_>>();
286-
arm_blocks.sort_by_cached_key(|&(discr, _, _)|{
287-
match &this.thir[arms[0]].pattern.kind {
288-
PatKind::Variant { adt_def, ..} => {
289-
adt_def.discriminants(this.tcx).position(|(_, i)| discr.val == i.val).unwrap()
311+
unpack!(
312+
body_block = this.in_scope(
313+
(region_scope, source_info),
314+
LintLevel::Inherited,
315+
move |this| {
316+
let mut arm_blocks = Vec::with_capacity(arms.len());
317+
for &arm in arms {
318+
let pat = &this.thir[arm].pattern;
319+
let deconstructed_pat = cx.lower_pat(pat);
320+
321+
match deconstructed_pat.ctor() {
322+
Constructor::Variant(variant_index) => {
323+
let PatKind::Variant { adt_def, .. } = pat.kind else {
324+
unreachable!()
325+
};
326+
327+
let discr = adt_def
328+
.discriminant_for_variant(this.tcx, *variant_index);
329+
330+
let block = this.cfg.start_new_block();
331+
arm_blocks.push((discr, block, arm))
332+
}
333+
Constructor::IntRange(int_range) => {
334+
assert!(int_range.is_singleton());
335+
let Some(value) = int_range.lo.as_finite_uint() else {
336+
todo!()
337+
};
338+
339+
let discr =
340+
Discr { val: value, ty: **deconstructed_pat.ty() };
341+
342+
let block = this.cfg.start_new_block();
343+
arm_blocks.push((discr, block, arm))
344+
}
345+
Constructor::Wildcard => {
346+
otherwise = Some((this.cfg.start_new_block(), arm));
347+
}
348+
other => todo!("{:?}", other),
290349
}
291-
_ => panic!()
292350
}
293-
});
294351

295-
let targets = SwitchTargets::new(
296-
arm_blocks.iter().map(|&(discr, block, _arm)| (discr.val, block)),
297-
unreachable_block,
298-
);
299-
this.in_const_continuable_scope(loop_block, targets.clone(), state_place, |this| {
300-
let discr_ty = match &this.thir[arms[0]].pattern.kind {
301-
PatKind::Variant { adt_def, ..} => {
302-
adt_def.discriminants(this.tcx).next().unwrap().1.ty
352+
arm_blocks.sort_by_cached_key(|&(discr, _, _)| {
353+
match &this.thir[arms[0]].pattern.kind {
354+
PatKind::Variant { adt_def, .. } => adt_def
355+
.discriminants(this.tcx)
356+
.position(|(_, i)| discr.val == i.val)
357+
.unwrap(),
358+
PatKind::ExpandedConstant { .. } => 0,
359+
PatKind::Constant { .. } => 0,
360+
other => todo!("{:?}", other),
303361
}
304-
_ => panic!()
305-
};
306-
let discr = this.temp(discr_ty, source_info.span);
307-
this.cfg.push_assign(body_block, source_info, discr, Rvalue::Discriminant(state_place));
308-
let discr = Operand::Copy(discr);
309-
this.cfg.terminate(body_block, source_info, TerminatorKind::SwitchInt { discr, targets });
310-
311-
for (_discr, mut block, arm) in arm_blocks {
312-
let empty_place = this.get_unit_temp();
313-
unpack!(block = this.expr_into_dest(empty_place, block, this.thir[arm].body));
314-
this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
315-
}
362+
});
363+
364+
let targets = SwitchTargets::new(
365+
arm_blocks
366+
.iter()
367+
.map(|&(discr, block, _arm)| (discr.val, block)),
368+
if let Some((block, _)) = otherwise {
369+
block
370+
} else {
371+
let unreachable_block = this.cfg.start_new_block();
372+
this.cfg.terminate(
373+
unreachable_block,
374+
source_info,
375+
TerminatorKind::Unreachable,
376+
);
377+
unreachable_block
378+
},
379+
);
380+
381+
this.in_const_continuable_scope(
382+
loop_block,
383+
targets.clone(),
384+
state_place,
385+
|this| {
386+
let discr = this.temp(discr_ty, source_info.span);
387+
this.cfg.push_assign(
388+
body_block,
389+
source_info,
390+
discr,
391+
rvalue,
392+
);
393+
let discr = Operand::Copy(discr);
394+
this.cfg.terminate(
395+
body_block,
396+
source_info,
397+
TerminatorKind::SwitchInt { discr, targets },
398+
);
316399

317-
None
318-
})
319-
})
400+
let it = arm_blocks
401+
.into_iter()
402+
.map(|(_, block, arm)| (block, arm))
403+
.chain(otherwise);
404+
405+
for (mut block, arm) in it {
406+
let empty_place = this.get_unit_temp();
407+
unpack!(
408+
block = this.expr_into_dest(
409+
empty_place,
410+
block,
411+
this.thir[arm].body
412+
)
413+
);
414+
this.cfg.terminate(
415+
block,
416+
source_info,
417+
TerminatorKind::Unreachable,
418+
);
419+
}
420+
421+
None
422+
},
423+
)
424+
}
425+
)
320426
);
321427

322428
this.cfg.goto(body_block, source_info, loop_block);

compiler/rustc_mir_build/src/builder/expr/stmt.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
9292
ExprKind::Break { label, value } => {
9393
this.break_scope(block, value, BreakableTarget::Break(label), source_info)
9494
}
95-
ExprKind::ConstContinue { label, value } => {
96-
this.break_scope(block, Some(value), BreakableTarget::ConstContinue(label), source_info)
97-
}
95+
ExprKind::ConstContinue { label, value } => this.break_scope(
96+
block,
97+
Some(value),
98+
BreakableTarget::ConstContinue(label),
99+
source_info,
100+
),
98101
ExprKind::Return { value } => {
99102
this.break_scope(block, value, BreakableTarget::Return, source_info)
100103
}

compiler/rustc_mir_build/src/builder/scope.rs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ that contains only loops and breakable blocks. It tracks where a `break`,
8383

8484
use std::mem;
8585

86+
use rustc_ast::LitKind;
8687
use rustc_data_structures::fx::FxHashMap;
8788
use rustc_hir::HirId;
8889
use rustc_index::{IndexSlice, IndexVec};
@@ -728,43 +729,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
728729
.unwrap_or_else(|| {
729730
span_bug!(span, "no enclosing const-continuable scope found")
730731
});
731-
let state_place = self.scopes.const_continuable_scopes[break_index].state_place;
732732

733733
let rustc_middle::thir::ExprKind::Scope { value, .. } =
734734
self.thir[value.unwrap()].kind
735735
else {
736736
panic!();
737737
};
738-
let rustc_middle::thir::ExprKind::Adt(value_adt) = &self.thir[value].kind else {
739-
panic!();
738+
739+
let scope = &self.scopes.const_continuable_scopes[break_index];
740+
741+
let state_ty = self.local_decls[scope.state_place.as_local().unwrap()].ty;
742+
let discriminant_ty = match state_ty {
743+
ty if ty.is_enum() => ty.discriminant_ty(self.tcx),
744+
ty if ty.is_integral() => ty,
745+
_ => todo!(),
746+
};
747+
748+
let rvalue = match state_ty {
749+
ty if ty.is_enum() => Rvalue::Discriminant(scope.state_place),
750+
ty if ty.is_integral() => Rvalue::Use(Operand::Copy(scope.state_place)),
751+
_ => todo!(),
740752
};
741753

742-
//dbg!(&self.thir[value], value_adt);
754+
let real_target = match &self.thir[value].kind {
755+
rustc_middle::thir::ExprKind::Adt(value_adt) => scope
756+
.match_arms
757+
.target_for_value(u128::from(value_adt.variant_index.as_u32())),
758+
rustc_middle::thir::ExprKind::Literal { lit, neg: _ } => match lit.node {
759+
LitKind::Int(pu, _) => scope.match_arms.target_for_value(pu.get()),
760+
_ => todo!(),
761+
},
762+
other => todo!("{other:?}"),
763+
};
743764

744765
self.block_context.push(BlockFrame::SubExpr);
766+
let state_place = scope.state_place;
745767
block = self.expr_into_dest(state_place, block, value).into_block();
746768
self.block_context.pop();
747769

748-
let discr_ty =
749-
self.local_decls[state_place.as_local().unwrap()].ty.discriminant_ty(self.tcx);
750-
let discr = self.temp(discr_ty, source_info.span);
770+
let discr = self.temp(discriminant_ty, source_info.span);
751771
let scope = &self.scopes.const_continuable_scopes[break_index];
752-
self.cfg.push_assign(
753-
block,
754-
source_info,
755-
discr,
756-
Rvalue::Discriminant(scope.state_place),
757-
);
772+
self.cfg.push_assign(block, source_info, discr, rvalue);
758773
self.cfg.terminate(
759774
block,
760775
source_info,
761-
TerminatorKind::FalseEdge {
762-
real_target: self.scopes.const_continuable_scopes[break_index]
763-
.match_arms
764-
.target_for_value(u128::from(value_adt.variant_index.as_u32())),
765-
imaginary_target: self.scopes.const_continuable_scopes[break_index]
766-
.loop_head,
767-
},
776+
TerminatorKind::FalseEdge { real_target, imaginary_target: scope.loop_head },
768777
);
769778

770779
return self.cfg.start_new_block().unit();

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
331331
| WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
332332

333333
// These diverge.
334-
Become { .. } | Break { .. } | Continue { .. } | ConstContinue { .. } | Return { .. } => true,
334+
Become { .. }
335+
| Break { .. }
336+
| Continue { .. }
337+
| ConstContinue { .. }
338+
| Return { .. } => true,
335339

336340
// These are statements that evaluate to `()`.
337341
Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//@ run-pass
2+
3+
#![feature(loop_match)]
4+
5+
fn main() {
6+
let mut state = 0;
7+
#[loop_match]
8+
'a: loop {
9+
state = 'blk: {
10+
match state {
11+
1 => {
12+
if true {
13+
#[const_continue]
14+
break 'blk 2;
15+
} else {
16+
// No drops allowed at this point
17+
#[const_continue]
18+
break 'blk 0;
19+
}
20+
}
21+
0 => {
22+
#[const_continue]
23+
break 'blk 1;
24+
}
25+
2 => break 'a,
26+
_ => break 'a,
27+
}
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)