Skip to content

Commit f0864c8

Browse files
committed
Auto merge of rust-lang#150512 - JonathanBrouwer:rollup-a3xpskp, r=JonathanBrouwer
Rollup of 2 pull requests Successful merges: - rust-lang#150422 (mir_build: Add a `SliceLenOp` enum for use by slice-length cases/tests) - rust-lang#150509 (Adds a regression test for rust-lang#137823) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 15f7c55 + 4b6f783 commit f0864c8

File tree

6 files changed

+139
-55
lines changed

6 files changed

+139
-55
lines changed

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

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use std::cmp::Ordering;
22

33
use rustc_data_structures::fx::FxIndexMap;
4-
use rustc_middle::mir::{BinOp, Place};
4+
use rustc_middle::mir::Place;
55
use rustc_middle::span_bug;
66
use tracing::debug;
77

88
use crate::builder::Builder;
9-
use crate::builder::matches::{Candidate, PatConstKind, Test, TestBranch, TestKind, TestableCase};
9+
use crate::builder::matches::{
10+
Candidate, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase,
11+
};
1012

1113
/// Output of [`Builder::partition_candidates_into_buckets`].
1214
pub(crate) struct PartitionedCandidates<'tcx, 'b, 'c> {
@@ -212,66 +214,71 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
212214
Some(if value { TestBranch::Success } else { TestBranch::Failure })
213215
}
214216

217+
// Determine how the proposed slice-length test interacts with the
218+
// slice pattern we're currently looking at.
219+
//
220+
// Keep in mind the invariant that a case is not allowed to succeed
221+
// on multiple arms of the same test. For example, even though the
222+
// test `len == 4` logically implies `len >= 4` on its success arm,
223+
// the case `len >= 4` could also succeed on the test's failure arm,
224+
// so it can't be included in the success bucket or failure bucket.
215225
(
216-
&TestKind::Len { len: test_len, op: BinOp::Eq },
217-
&TestableCase::Slice { len, variable_length },
226+
&TestKind::SliceLen { len: test_len, op: SliceLenOp::Equal },
227+
&TestableCase::Slice { len: pat_len, op: pat_op },
218228
) => {
219-
match (test_len.cmp(&len), variable_length) {
220-
(Ordering::Equal, false) => {
221-
// on true, min_len = len = $actual_length,
222-
// on false, len != $actual_length
229+
match (test_len.cmp(&pat_len), pat_op) {
230+
(Ordering::Equal, SliceLenOp::Equal) => {
231+
// E.g. test is `len == 4` and pattern is `len == 4`.
232+
// Pattern is fully matched on the success arm.
223233
fully_matched = true;
224234
Some(TestBranch::Success)
225235
}
226236
(Ordering::Less, _) => {
227-
// test_len < pat_len. If $actual_len = test_len,
228-
// then $actual_len < pat_len and we don't have
229-
// enough elements.
237+
// E.g. test is `len == 4` and pattern is `len == 5` or `len >= 5`.
238+
// Pattern can only succeed on the failure arm, but isn't fully matched.
230239
fully_matched = false;
231240
Some(TestBranch::Failure)
232241
}
233-
(Ordering::Equal | Ordering::Greater, true) => {
234-
// This can match both if $actual_len = test_len >= pat_len,
235-
// and if $actual_len > test_len. We can't advance.
242+
(Ordering::Equal | Ordering::Greater, SliceLenOp::GreaterOrEqual) => {
243+
// E.g. test is `len == 4` and pattern is `len >= 4` or `len >= 3`.
244+
// Pattern could succeed on both arms, so it can't be bucketed.
236245
fully_matched = false;
237246
None
238247
}
239-
(Ordering::Greater, false) => {
240-
// test_len != pat_len, so if $actual_len = test_len, then
241-
// $actual_len != pat_len.
248+
(Ordering::Greater, SliceLenOp::Equal) => {
249+
// E.g. test is `len == 4` and pattern is `len == 3`.
250+
// Pattern can only succeed on the failure arm, but isn't fully matched.
242251
fully_matched = false;
243252
Some(TestBranch::Failure)
244253
}
245254
}
246255
}
247256
(
248-
&TestKind::Len { len: test_len, op: BinOp::Ge },
249-
&TestableCase::Slice { len, variable_length },
257+
&TestKind::SliceLen { len: test_len, op: SliceLenOp::GreaterOrEqual },
258+
&TestableCase::Slice { len: pat_len, op: pat_op },
250259
) => {
251-
// the test is `$actual_len >= test_len`
252-
match (test_len.cmp(&len), variable_length) {
253-
(Ordering::Equal, true) => {
254-
// $actual_len >= test_len = pat_len,
255-
// so we can match.
260+
match (test_len.cmp(&pat_len), pat_op) {
261+
(Ordering::Equal, SliceLenOp::GreaterOrEqual) => {
262+
// E.g. test is `len >= 4` and pattern is `len >= 4`.
263+
// Pattern is fully matched on the success arm.
256264
fully_matched = true;
257265
Some(TestBranch::Success)
258266
}
259-
(Ordering::Less, _) | (Ordering::Equal, false) => {
260-
// test_len <= pat_len. If $actual_len < test_len,
261-
// then it is also < pat_len, so the test passing is
262-
// necessary (but insufficient).
267+
(Ordering::Less, _) | (Ordering::Equal, SliceLenOp::Equal) => {
268+
// E.g. test is `len >= 4` and pattern is `len == 5` or `len >= 5` or `len == 4`.
269+
// Pattern can only succeed on the success arm, but isn't fully matched.
263270
fully_matched = false;
264271
Some(TestBranch::Success)
265272
}
266-
(Ordering::Greater, false) => {
267-
// test_len > pat_len. If $actual_len >= test_len > pat_len,
268-
// then we know we won't have a match.
273+
(Ordering::Greater, SliceLenOp::Equal) => {
274+
// E.g. test is `len >= 4` and pattern is `len == 3`.
275+
// Pattern can only succeed on the failure arm, but isn't fully matched.
269276
fully_matched = false;
270277
Some(TestBranch::Failure)
271278
}
272-
(Ordering::Greater, true) => {
273-
// test_len < pat_len, and is therefore less
274-
// strict. This can still go both ways.
279+
(Ordering::Greater, SliceLenOp::GreaterOrEqual) => {
280+
// E.g. test is `len >= 4` and pattern is `len >= 3`.
281+
// Pattern could succeed on both arms, so it can't be bucketed.
275282
fully_matched = false;
276283
None
277284
}
@@ -338,7 +345,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
338345
TestKind::Switch { .. }
339346
| TestKind::SwitchInt { .. }
340347
| TestKind::If
341-
| TestKind::Len { .. }
348+
| TestKind::SliceLen { .. }
342349
| TestKind::Range { .. }
343350
| TestKind::Eq { .. }
344351
| TestKind::Deref { .. },

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
88
use crate::builder::Builder;
99
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
1010
use crate::builder::matches::{
11-
FlatPat, MatchPairTree, PatConstKind, PatternExtraData, TestableCase,
11+
FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase,
1212
};
1313

1414
impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -277,11 +277,20 @@ impl<'tcx> MatchPairTree<'tcx> {
277277
);
278278

279279
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
280+
// This pattern is shaped like `[..]`. It can match a slice
281+
// of any length, so no length test is needed.
280282
None
281283
} else {
284+
// Any other shape of slice pattern requires a length test.
285+
// Slice patterns with a `..` subpattern require a minimum
286+
// length; those without `..` require an exact length.
282287
Some(TestableCase::Slice {
283288
len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
284-
variable_length: slice.is_some(),
289+
op: if slice.is_some() {
290+
SliceLenOp::GreaterOrEqual
291+
} else {
292+
SliceLenOp::Equal
293+
},
285294
})
286295
}
287296
}

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

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ enum TestableCase<'tcx> {
12641264
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
12651265
Constant { value: ty::Value<'tcx>, kind: PatConstKind },
12661266
Range(Arc<PatRange<'tcx>>),
1267-
Slice { len: u64, variable_length: bool },
1267+
Slice { len: u64, op: SliceLenOp },
12681268
Deref { temp: Place<'tcx>, mutability: Mutability },
12691269
Never,
12701270
Or { pats: Box<[FlatPat<'tcx>]> },
@@ -1332,7 +1332,21 @@ pub(crate) struct MatchPairTree<'tcx> {
13321332
pattern_span: Span,
13331333
}
13341334

1335-
/// See [`Test`] for more.
1335+
/// A runtime test to perform to determine which candidates match a scrutinee place.
1336+
///
1337+
/// The kind of test to perform is indicated by [`TestKind`].
1338+
#[derive(Debug)]
1339+
pub(crate) struct Test<'tcx> {
1340+
span: Span,
1341+
kind: TestKind<'tcx>,
1342+
}
1343+
1344+
/// The kind of runtime test to perform to determine which candidates match a
1345+
/// scrutinee place. This is the main component of [`Test`].
1346+
///
1347+
/// Some of these variants don't contain the constant value(s) being tested
1348+
/// against, because those values are stored in the corresponding bucketed
1349+
/// candidates instead.
13361350
#[derive(Clone, Debug, PartialEq)]
13371351
enum TestKind<'tcx> {
13381352
/// Test what enum variant a value is.
@@ -1368,7 +1382,7 @@ enum TestKind<'tcx> {
13681382
Range(Arc<PatRange<'tcx>>),
13691383

13701384
/// Test that the length of the slice is `== len` or `>= len`.
1371-
Len { len: u64, op: BinOp },
1385+
SliceLen { len: u64, op: SliceLenOp },
13721386

13731387
/// Call `Deref::deref[_mut]` on the value.
13741388
Deref {
@@ -1381,14 +1395,15 @@ enum TestKind<'tcx> {
13811395
Never,
13821396
}
13831397

1384-
/// A test to perform to determine which [`Candidate`] matches a value.
1385-
///
1386-
/// [`Test`] is just the test to perform; it does not include the value
1387-
/// to be tested.
1388-
#[derive(Debug)]
1389-
pub(crate) struct Test<'tcx> {
1390-
span: Span,
1391-
kind: TestKind<'tcx>,
1398+
/// Indicates the kind of slice-length constraint imposed by a slice pattern,
1399+
/// or its corresponding test.
1400+
#[derive(Debug, Clone, Copy, PartialEq)]
1401+
enum SliceLenOp {
1402+
/// The slice pattern can only match a slice with exactly `len` elements.
1403+
Equal,
1404+
/// The slice pattern can match a slice with `len` or more elements
1405+
/// (i.e. it contains a `..` subpattern in the middle).
1406+
GreaterOrEqual,
13921407
}
13931408

13941409
/// The branch to be taken after a test.

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use tracing::{debug, instrument};
2020

2121
use crate::builder::Builder;
2222
use crate::builder::matches::{
23-
MatchPairTree, PatConstKind, Test, TestBranch, TestKind, TestableCase,
23+
MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase,
2424
};
2525

2626
impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -50,10 +50,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5050
TestKind::Range(Arc::clone(range))
5151
}
5252

53-
TestableCase::Slice { len, variable_length } => {
54-
let op = if variable_length { BinOp::Ge } else { BinOp::Eq };
55-
TestKind::Len { len, op }
56-
}
53+
TestableCase::Slice { len, op } => TestKind::SliceLen { len, op },
5754

5855
TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
5956

@@ -312,7 +309,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
312309
}
313310
}
314311

315-
TestKind::Len { len, op } => {
312+
TestKind::SliceLen { len, op } => {
316313
let usize_ty = self.tcx.types.usize;
317314
let actual = self.temp(usize_ty, test.span);
318315

@@ -332,7 +329,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
332329
success_block,
333330
fail_block,
334331
source_info,
335-
op,
332+
match op {
333+
SliceLenOp::Equal => BinOp::Eq,
334+
SliceLenOp::GreaterOrEqual => BinOp::Ge,
335+
},
336336
Operand::Move(actual),
337337
Operand::Move(expected),
338338
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//@ build-fail
2+
3+
// Regression test for issue #137823
4+
// Tests that recursive monomorphization with associated types produces
5+
// a proper "recursion limit" error instead of an ICE.
6+
7+
fn convert<S: Converter>() -> S::Out {
8+
convert2::<ConvertWrap<S>>()
9+
//~^ ERROR: reached the recursion limit while instantiating
10+
}
11+
12+
fn convert2<S: Converter>() -> S::Out {
13+
convert::<S>()
14+
}
15+
16+
fn main() {
17+
convert::<Ser>();
18+
}
19+
20+
trait Converter {
21+
type Out;
22+
}
23+
24+
struct Ser;
25+
26+
impl Converter for Ser {
27+
type Out = ();
28+
}
29+
30+
struct ConvertWrap<S> {
31+
_d: S,
32+
}
33+
34+
impl<S> Converter for ConvertWrap<S>
35+
where
36+
S: Converter,
37+
{
38+
type Out = S::Out;
39+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: reached the recursion limit while instantiating `convert2::<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<ConvertWrap<Ser>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
2+
--> $DIR/recursion-issue-137823.rs:8:5
3+
|
4+
LL | convert2::<ConvertWrap<S>>()
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: `convert2` defined here
8+
--> $DIR/recursion-issue-137823.rs:12:1
9+
|
10+
LL | fn convert2<S: Converter>() -> S::Out {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

0 commit comments

Comments
 (0)