Skip to content

Commit ebf5c3c

Browse files
committed
[WIP] Support const blocks in const_continue
1 parent 85a7a11 commit ebf5c3c

File tree

6 files changed

+80
-51
lines changed

6 files changed

+80
-51
lines changed

compiler/rustc_mir_build/src/builder/matches/mod.rs

+62-36
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ use std::mem;
1111
use std::sync::Arc;
1212

1313
use rustc_abi::VariantIdx;
14-
use rustc_ast::LitKind;
1514
use rustc_data_structures::fx::FxIndexMap;
1615
use rustc_data_structures::stack::ensure_sufficient_stack;
1716
use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node};
18-
use rustc_middle::bug;
1917
use rustc_middle::middle::region;
2018
use rustc_middle::mir::{self, *};
2119
use rustc_middle::thir::{self, *};
22-
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
20+
use rustc_middle::ty::{
21+
self, CanonicalUserTypeAnnotation, Ty, TypeVisitableExt, ValTree, ValTreeKind,
22+
};
23+
use rustc_middle::{bug, span_bug};
2324
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
2425
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
2526
use tracing::{debug, instrument};
@@ -2870,7 +2871,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
28702871
pub(crate) fn static_pattern_match(
28712872
&self,
28722873
cx: &RustcPatCtxt<'_, 'tcx>,
2873-
value: ExprId,
2874+
constant: ConstOperand<'tcx>,
28742875
arms: &[ArmId],
28752876
built_match_tree: &BuiltMatchTree<'tcx>,
28762877
) -> Option<BasicBlock> {
@@ -2888,56 +2889,81 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
28882889
.or_else(|| branch.sub_branches.last())
28892890
.unwrap();
28902891

2891-
match self.static_pattern_match_help(value, &pat.pat) {
2892+
match self.static_pattern_match_help(constant, &pat.pat) {
28922893
true => return Some(sub_branch.success_block),
28932894
false => continue,
28942895
}
28952896
}
2896-
} else if self.static_pattern_match_help(value, &pat) {
2897+
} else if self.static_pattern_match_help(constant, &pat) {
28972898
return Some(branch.sub_branches[0].success_block);
28982899
}
28992900
}
29002901

29012902
None
29022903
}
29032904

2904-
fn static_pattern_match_help(&self, value: ExprId, pat: &DeconstructedPat<'_, 'tcx>) -> bool {
2905-
use rustc_middle::thir::ExprKind;
2905+
fn static_pattern_match_help(
2906+
&self,
2907+
constant: ConstOperand<'tcx>,
2908+
pat: &DeconstructedPat<'_, 'tcx>,
2909+
) -> bool {
29062910
use rustc_pattern_analysis::constructor::{IntRange, MaybeInfiniteInt};
29072911
use rustc_pattern_analysis::rustc::Constructor;
29082912

2909-
match pat.ctor() {
2910-
Constructor::Variant(variant_index) => match &self.thir[value].kind {
2911-
ExprKind::Adt(value_adt) => {
2912-
return *variant_index == value_adt.variant_index;
2913+
// Based on eval_unevaluated_mir_constant_to_valtree
2914+
let (valtree, ty) = 'a: {
2915+
assert!(!constant.const_.ty().has_param());
2916+
let (uv, ty) = match constant.const_ {
2917+
mir::Const::Unevaluated(uv, ty) => (uv.shrink(), ty),
2918+
mir::Const::Ty(_, c) => match c.kind() {
2919+
// A constant that came from a const generic but was then used as an argument to
2920+
// old-style simd_shuffle (passing as argument instead of as a generic param).
2921+
ty::ConstKind::Value(cv) => break 'a (cv.valtree, cv.ty),
2922+
other => span_bug!(constant.span, "{other:#?}"),
2923+
},
2924+
mir::Const::Val(mir::ConstValue::Scalar(mir::interpret::Scalar::Int(val)), ty) => {
2925+
break 'a (ValTree::from_scalar_int(self.tcx, val), ty);
29132926
}
2914-
other => todo!("{other:?}"),
2915-
},
2916-
Constructor::IntRange(int_range) => match &self.thir[value].kind {
2917-
ExprKind::Literal { lit, neg } => match &lit.node {
2918-
LitKind::Int(n, _) => {
2919-
let n = if pat.ty().is_signed() {
2920-
let size = pat.ty().primitive_size(self.tcx);
2921-
MaybeInfiniteInt::new_finite_int(
2922-
if *neg {
2923-
size.truncate((n.get() as i128).overflowing_neg().0 as u128)
2924-
} else {
2925-
n.get()
2926-
},
2927-
size.bits(),
2928-
)
2929-
} else {
2930-
MaybeInfiniteInt::new_finite_uint(n.get())
2931-
};
2932-
2933-
return IntRange::from_singleton(n).is_subrange(int_range);
2934-
}
2927+
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
2928+
// a constant and write that value back into `Operand`s. This could happen, but is
2929+
// unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take
2930+
// a lot of care around intrinsics. For an issue to happen here, it would require a
2931+
// macro expanding to a `simd_shuffle` call without wrapping the constant argument in a
2932+
// `const {}` block, but the user pass through arbitrary expressions.
2933+
// FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a
2934+
// real const generic, and get rid of this entire function.
2935+
other => span_bug!(constant.span, "{other:#?}"),
2936+
};
2937+
(
2938+
self.tcx
2939+
.const_eval_resolve_for_typeck(self.typing_env(), uv, constant.span)
2940+
.unwrap()
2941+
.unwrap(),
2942+
ty,
2943+
)
2944+
};
2945+
assert!(!ty.has_param());
29352946

2936-
other => todo!("{other:?}"),
2937-
},
2947+
match pat.ctor() {
2948+
Constructor::Variant(variant_index) => match *valtree {
2949+
ValTreeKind::Branch(box [actual_variant_idx]) => {
2950+
*variant_index
2951+
== VariantIdx::from_u32(actual_variant_idx.unwrap_leaf().to_u32())
2952+
}
29382953
other => todo!("{other:?}"),
29392954
},
2940-
Constructor::Wildcard => return true,
2955+
Constructor::IntRange(int_range) => {
2956+
let size = pat.ty().primitive_size(self.tcx);
2957+
let actual_int = valtree.unwrap_leaf().to_bits(size);
2958+
let actual_int = if pat.ty().is_signed() {
2959+
MaybeInfiniteInt::new_finite_int(actual_int, size.bits())
2960+
} else {
2961+
MaybeInfiniteInt::new_finite_uint(actual_int)
2962+
};
2963+
IntRange::from_singleton(actual_int).is_subrange(int_range)
2964+
}
2965+
Constructor::Wildcard => true,
2966+
// FIXME error out before static_pattern_match gets run and replace this with bug!()
29412967
_ => false,
29422968
}
29432969
}

compiler/rustc_mir_build/src/builder/scope.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
824824
span_bug!(span, "break value must be a scope")
825825
};
826826

827+
// FIXME accept bare MyEnum::Foo as constant
828+
let constant = self.as_constant(&self.thir[value]);
829+
827830
let break_index = self
828831
.scopes
829832
.const_continuable_scopes
@@ -865,7 +868,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
865868
};
866869

867870
let Some(real_target) =
868-
self.static_pattern_match(&cx, value, &*scope.arms, &scope.built_match_tree)
871+
self.static_pattern_match(&cx, constant, &*scope.arms, &scope.built_match_tree)
869872
else {
870873
self.tcx.dcx().emit_fatal(ConstContinueUnknownJumpTarget { span })
871874
};

tests/ui/feature-gates/feature-gate-loop-match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ fn main() {
1313
State::A => {
1414
#[const_continue]
1515
//~^ ERROR the `#[const_continue]` attribute is an experimental feature
16-
break 'blk State::B;
16+
break 'blk const { State::B };
1717
}
1818
State::B => {
1919
#[const_continue]
2020
//~^ ERROR the `#[const_continue]` attribute is an experimental feature
21-
break 'blk State::C;
21+
break 'blk const { State::C };
2222
}
2323
State::C => break 'a,
2424
}

tests/ui/loop-match/loop-match.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn main() {
1919
match state {
2020
State::A => {
2121
#[const_continue]
22-
break 'blk State::B;
22+
break 'blk const { State::B };
2323
}
2424
State::B => {
2525
// without special logic, the compiler believes this is a re-assignment to
@@ -29,10 +29,10 @@ fn main() {
2929

3030
if true {
3131
#[const_continue]
32-
break 'blk State::C;
32+
break 'blk const { State::C };
3333
} else {
3434
#[const_continue]
35-
break 'blk State::A;
35+
break 'blk const { State::A };
3636
}
3737
}
3838
State::C => break 'a,

tests/ui/loop-match/nested.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn run() -> String {
3737
State1::A => {
3838
accum.push('a');
3939
#[const_continue]
40-
break 'blk1 State1::B;
40+
break 'blk1 const { State1::B };
4141
}
4242
State1::B => {
4343
accum.push('b');
@@ -48,22 +48,22 @@ fn run() -> String {
4848
State2::X => {
4949
accum.push('x');
5050
#[const_continue]
51-
break 'blk2 State2::Y;
51+
break 'blk2 const { State2::Y };
5252
}
5353
State2::Y => {
5454
accum.push('y');
5555
#[const_continue]
56-
break 'blk2 State2::Z;
56+
break 'blk2 const { State2::Z };
5757
}
5858
State2::Z => {
5959
accum.push('z');
6060
if first {
6161
first = false;
6262
#[const_continue]
63-
break 'blk2 State2::X;
63+
break 'blk2 const { State2::X };
6464
} else {
6565
#[const_continue]
66-
break 'blk1 State1::C;
66+
break 'blk1 const { State1::C };
6767
}
6868
}
6969
}

tests/ui/loop-match/or-patterns.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@ fn main() {
2525
states.push(state);
2626
if first {
2727
#[const_continue]
28-
break 'blk State::B;
28+
break 'blk const { State::B };
2929
} else {
3030
#[const_continue]
31-
break 'blk State::D;
31+
break 'blk const { State::D };
3232
}
3333
}
3434
State::B | State::D => {
3535
states.push(state);
3636
if first {
3737
first = false;
3838
#[const_continue]
39-
break 'blk State::A;
39+
break 'blk const { State::A };
4040
} else {
4141
#[const_continue]
42-
break 'blk State::C;
42+
break 'blk const { State::C };
4343
}
4444
}
4545
State::C => {

0 commit comments

Comments
 (0)