Skip to content

Commit 70b2f11

Browse files
committed
emit an error for constants that are too generic
1 parent bbe7547 commit 70b2f11

File tree

6 files changed

+114
-53
lines changed

6 files changed

+114
-53
lines changed

compiler/rustc_mir_build/messages.ftl

+5-1
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,14 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
8484
8585
mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
8686
87+
mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
88+
.label = this value is too generic
89+
.note = the value must be a literal or a monomorphic const
90+
8791
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
8892
8993
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
90-
.note = this value must be an integer or enum literal
94+
.label = this value must be a literal or a monomorphic const
9195
9296
mir_build_const_defined_here = constant defined here
9397

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

+10-48
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@ use rustc_abi::VariantIdx;
1414
use rustc_data_structures::fx::FxIndexMap;
1515
use rustc_data_structures::stack::ensure_sufficient_stack;
1616
use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node};
17+
use rustc_middle::bug;
1718
use rustc_middle::middle::region;
1819
use rustc_middle::mir::{self, *};
1920
use rustc_middle::thir::{self, *};
20-
use rustc_middle::ty::{
21-
self, CanonicalUserTypeAnnotation, Ty, TypeVisitableExt, ValTree, ValTreeKind,
22-
};
23-
use rustc_middle::{bug, span_bug};
21+
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
2422
use rustc_pattern_analysis::constructor::RangeEnd;
2523
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
2624
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
@@ -2871,14 +2869,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
28712869
pub(crate) fn static_pattern_match(
28722870
&self,
28732871
cx: &RustcPatCtxt<'_, 'tcx>,
2874-
constant: ConstOperand<'tcx>,
2872+
valtree: ValTree<'tcx>,
28752873
arms: &[ArmId],
28762874
built_match_tree: &BuiltMatchTree<'tcx>,
28772875
) -> Option<BasicBlock> {
28782876
let it = arms.iter().zip(built_match_tree.branches.iter());
28792877
for (&arm_id, branch) in it {
28802878
let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern);
28812879

2880+
// Peel off or-patterns if they exist.
28822881
if let rustc_pattern_analysis::rustc::Constructor::Or = pat.ctor() {
28832882
for pat in pat.iter_fields() {
28842883
// For top-level or-patterns (the only ones we accept right now), when the
@@ -2890,66 +2889,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
28902889
.or_else(|| branch.sub_branches.last())
28912890
.unwrap();
28922891

2893-
match self.static_pattern_match_help(constant, &pat.pat) {
2892+
match self.static_pattern_match_inner(valtree, &pat.pat) {
28942893
true => return Some(sub_branch.success_block),
28952894
false => continue,
28962895
}
28972896
}
2898-
} else if self.static_pattern_match_help(constant, &pat) {
2897+
} else if self.static_pattern_match_inner(valtree, &pat) {
28992898
return Some(branch.sub_branches[0].success_block);
29002899
}
29012900
}
29022901

29032902
None
29042903
}
29052904

2906-
/// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`.
2907-
fn eval_unevaluated_mir_constant_to_valtree(
2908-
&self,
2909-
constant: ConstOperand<'tcx>,
2910-
) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
2911-
assert!(!constant.const_.ty().has_param());
2912-
let (uv, ty) = match constant.const_ {
2913-
mir::Const::Unevaluated(uv, ty) => (uv.shrink(), ty),
2914-
mir::Const::Ty(_, c) => match c.kind() {
2915-
// A constant that came from a const generic but was then used as an argument to
2916-
// old-style simd_shuffle (passing as argument instead of as a generic param).
2917-
ty::ConstKind::Value(cv) => return (cv.valtree, cv.ty),
2918-
other => span_bug!(constant.span, "{other:#?}"),
2919-
},
2920-
mir::Const::Val(mir::ConstValue::Scalar(mir::interpret::Scalar::Int(val)), ty) => {
2921-
return (ValTree::from_scalar_int(self.tcx, val), ty);
2922-
}
2923-
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
2924-
// a constant and write that value back into `Operand`s. This could happen, but is
2925-
// unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take
2926-
// a lot of care around intrinsics. For an issue to happen here, it would require a
2927-
// macro expanding to a `simd_shuffle` call without wrapping the constant argument in a
2928-
// `const {}` block, but the user pass through arbitrary expressions.
2929-
2930-
// FIXME(oli-obk): Replace the magic const generic argument of `simd_shuffle` with a
2931-
// real const generic, and get rid of this entire function.
2932-
other => span_bug!(constant.span, "{other:#?}"),
2933-
};
2934-
2935-
match self.tcx.const_eval_resolve_for_typeck(self.typing_env(), uv, constant.span) {
2936-
Ok(Ok(valtree)) => (valtree, ty),
2937-
Ok(Err(ty)) => span_bug!(constant.span, "could not convert {ty:?} to a valtree"),
2938-
Err(_) => span_bug!(constant.span, "unable to evaluate this constant"),
2939-
}
2940-
}
2941-
2942-
fn static_pattern_match_help(
2905+
/// Helper for [`Self::static_pattern_match`]. It does not recurse, meaning that it does not
2906+
/// handle or-patterns, or patterns for types with fields.
2907+
fn static_pattern_match_inner(
29432908
&self,
2944-
constant: ConstOperand<'tcx>,
2909+
valtree: ty::ValTree<'tcx>,
29452910
pat: &DeconstructedPat<'_, 'tcx>,
29462911
) -> bool {
29472912
use rustc_pattern_analysis::constructor::{IntRange, MaybeInfiniteInt};
29482913
use rustc_pattern_analysis::rustc::Constructor;
29492914

2950-
let (valtree, ty) = self.eval_unevaluated_mir_constant_to_valtree(constant);
2951-
assert!(!ty.has_param());
2952-
29532915
match pat.ctor() {
29542916
Constructor::Variant(variant_index) => {
29552917
let ValTreeKind::Branch(box [actual_variant_idx]) = *valtree else {

compiler/rustc_mir_build/src/builder/scope.rs

+54-4
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,14 @@ that contains only loops and breakable blocks. It tracks where a `break`,
8383

8484
use std::mem;
8585

86+
use interpret::ErrorHandled;
8687
use rustc_data_structures::fx::FxHashMap;
8788
use rustc_hir::HirId;
8889
use rustc_index::{IndexSlice, IndexVec};
8990
use rustc_middle::middle::region;
90-
use rustc_middle::mir::*;
91+
use rustc_middle::mir::{self, *};
9192
use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind, LintLevel};
92-
use rustc_middle::ty::ValTree;
93+
use rustc_middle::ty::{Ty, TypeVisitableExt, ValTree};
9394
use rustc_middle::{bug, span_bug, ty};
9495
use rustc_pattern_analysis::rustc::RustcPatCtxt;
9596
use rustc_session::lint::Level;
@@ -99,7 +100,7 @@ use tracing::{debug, instrument};
99100

100101
use super::matches::BuiltMatchTree;
101102
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
102-
use crate::errors::ConstContinueUnknownJumpTarget;
103+
use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
103104

104105
#[derive(Debug)]
105106
pub(crate) struct Scopes<'tcx> {
@@ -813,6 +814,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
813814
self.cfg.start_new_block().unit()
814815
}
815816

817+
/// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`.
818+
fn eval_unevaluated_mir_constant_to_valtree(
819+
&self,
820+
constant: ConstOperand<'tcx>,
821+
) -> Result<(ty::ValTree<'tcx>, Ty<'tcx>), interpret::ErrorHandled> {
822+
assert!(!constant.const_.ty().has_param());
823+
let (uv, ty) = match constant.const_ {
824+
mir::Const::Unevaluated(uv, ty) => (uv.shrink(), ty),
825+
mir::Const::Ty(_, c) => match c.kind() {
826+
// A constant that came from a const generic but was then used as an argument to
827+
// old-style simd_shuffle (passing as argument instead of as a generic param).
828+
ty::ConstKind::Value(cv) => return Ok((cv.valtree, cv.ty)),
829+
other => span_bug!(constant.span, "{other:#?}"),
830+
},
831+
mir::Const::Val(mir::ConstValue::Scalar(mir::interpret::Scalar::Int(val)), ty) => {
832+
return Ok((ValTree::from_scalar_int(self.tcx, val), ty));
833+
}
834+
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
835+
// a constant and write that value back into `Operand`s. This could happen, but is
836+
// unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take
837+
// a lot of care around intrinsics. For an issue to happen here, it would require a
838+
// macro expanding to a `simd_shuffle` call without wrapping the constant argument in a
839+
// `const {}` block, but the user pass through arbitrary expressions.
840+
841+
// FIXME(oli-obk): Replace the magic const generic argument of `simd_shuffle` with a
842+
// real const generic, and get rid of this entire function.
843+
other => span_bug!(constant.span, "{other:#?}"),
844+
};
845+
846+
match self.tcx.const_eval_resolve_for_typeck(self.typing_env(), uv, constant.span) {
847+
Ok(Ok(valtree)) => Ok((valtree, ty)),
848+
Ok(Err(ty)) => span_bug!(constant.span, "could not convert {ty:?} to a valtree"),
849+
Err(e) => Err(e),
850+
}
851+
}
852+
816853
/// Sets up the drops for jumping from `block` to `scope`.
817854
pub(crate) fn break_const_continuable_scope(
818855
&mut self,
@@ -890,8 +927,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
890927
known_valid_scrutinee: true,
891928
};
892929

930+
let valtree = match self.eval_unevaluated_mir_constant_to_valtree(constant) {
931+
Ok((valtree, ty)) => {
932+
// Defensively check that the type is monomorphic.
933+
assert!(!ty.has_param());
934+
935+
valtree
936+
}
937+
Err(ErrorHandled::Reported(..)) => return self.cfg.start_new_block().unit(),
938+
Err(ErrorHandled::TooGeneric(_)) => {
939+
self.tcx.dcx().emit_fatal(ConstContinueBadConst { span: constant.span });
940+
}
941+
};
942+
893943
let Some(real_target) =
894-
self.static_pattern_match(&cx, constant, &*scope.arms, &scope.built_match_tree)
944+
self.static_pattern_match(&cx, valtree, &*scope.arms, &scope.built_match_tree)
895945
else {
896946
self.tcx.dcx().emit_fatal(ConstContinueUnknownJumpTarget { span })
897947
};

compiler/rustc_mir_build/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,14 @@ pub(crate) struct LoopMatchArmWithGuard {
12161216
pub span: Span,
12171217
}
12181218

1219+
#[derive(Diagnostic)]
1220+
#[diag(mir_build_const_continue_bad_const)]
1221+
pub(crate) struct ConstContinueBadConst {
1222+
#[primary_span]
1223+
#[label]
1224+
pub span: Span,
1225+
}
1226+
12191227
#[derive(Diagnostic)]
12201228
#[diag(mir_build_const_continue_missing_value)]
12211229
pub(crate) struct ConstContinueMissingValue {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Test that a `#[const_continue]` that breaks on a polymorphic constant produces an error.
2+
// A polymorphic constant does not have a concrete value at MIR building time, and therefore the
3+
// `#[loop_match]~ desugaring can't handle such values.
4+
#![allow(incomplete_features)]
5+
#![feature(loop_match)]
6+
#![crate_type = "lib"]
7+
8+
trait Foo {
9+
const Target: u8;
10+
11+
fn test_u8(mut state: u8) -> &'static str {
12+
#[loop_match]
13+
loop {
14+
state = 'blk: {
15+
match state {
16+
0 => {
17+
#[const_continue]
18+
break 'blk Self::Target;
19+
//~^ ERROR could not determine the target branch for this `#[const_continue]`
20+
}
21+
22+
1 => return "bar",
23+
2 => return "baz",
24+
_ => unreachable!(),
25+
}
26+
}
27+
}
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: could not determine the target branch for this `#[const_continue]`
2+
--> $DIR/const-continue-to-polymorphic-const.rs:18:36
3+
|
4+
LL | break 'blk Self::Target;
5+
| ^^^^^^^^^^^^ this value is too generic
6+
7+
error: aborting due to 1 previous error
8+

0 commit comments

Comments
 (0)