Skip to content

Commit 44bcf8d

Browse files
committed
emit an error for constants that are too generic
1 parent 7ec1ca3 commit 44bcf8d

File tree

6 files changed

+114
-53
lines changed

6 files changed

+114
-53
lines changed

compiler/rustc_mir_build/messages.ftl

Lines changed: 5 additions & 1 deletion
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

Lines changed: 10 additions & 48 deletions
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};
@@ -2872,14 +2870,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
28722870
pub(crate) fn static_pattern_match(
28732871
&self,
28742872
cx: &RustcPatCtxt<'_, 'tcx>,
2875-
constant: ConstOperand<'tcx>,
2873+
valtree: ValTree<'tcx>,
28762874
arms: &[ArmId],
28772875
built_match_tree: &BuiltMatchTree<'tcx>,
28782876
) -> Option<BasicBlock> {
28792877
let it = arms.iter().zip(built_match_tree.branches.iter());
28802878
for (&arm_id, branch) in it {
28812879
let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern);
28822880

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

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

29042903
None
29052904
}
29062905

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

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

compiler/rustc_mir_build/src/builder/scope.rs

Lines changed: 54 additions & 4 deletions
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::{self, TyCtxt, ValTree};
93+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
9394
use rustc_middle::{bug, span_bug};
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> {
@@ -815,6 +816,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
815816
self.cfg.start_new_block().unit()
816817
}
817818

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

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

compiler/rustc_mir_build/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,14 @@ pub(crate) struct LoopMatchArmWithGuard {
12041204
pub span: Span,
12051205
}
12061206

1207+
#[derive(Diagnostic)]
1208+
#[diag(mir_build_const_continue_bad_const)]
1209+
pub(crate) struct ConstContinueBadConst {
1210+
#[primary_span]
1211+
#[label]
1212+
pub span: Span,
1213+
}
1214+
12071215
#[derive(Diagnostic)]
12081216
#[diag(mir_build_const_continue_missing_value)]
12091217
pub(crate) struct ConstContinueMissingValue {
Lines changed: 29 additions & 0 deletions
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+
}
Lines changed: 8 additions & 0 deletions
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)