Skip to content

Commit 62ec49c

Browse files
committed
Fix CoerceShared tuple matching and coverage
1 parent 83d9f22 commit 62ec49c

12 files changed

Lines changed: 147 additions & 37 deletions

compiler/rustc_const_eval/src/interpret/step.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
327327
false
328328
};
329329
if is_mut_to_shared_ref {
330+
// Leaf `&mut T` to `&T` CoerceShared creates a fresh shared reborrow of the
331+
// referent. It must not copy the `&mut T` value into an `&T` destination, because
332+
// that would bypass reference retagging and validity checks.
330333
let deref_source = source_place.project_deeper(&[mir::ProjectionElem::Deref], tcx);
331334
self.write_ref_to_place(deref_source, dest, RetagMode::Default)?;
332335
return interp_ok(());

compiler/rustc_middle/src/ty/reborrow.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ pub fn reborrow_data_fields<'tcx>(
5353

5454
/// Canonical CoerceShared source-to-target field correspondence.
5555
///
56-
/// Named structs match fields by name. Tuple structs match fields by position. `PhantomData`
57-
/// fields are ignored on both sides, so a target data field must correspond to a source data field.
58-
/// The field types are instantiated but intentionally not normalized here: validation, borrowck,
59-
/// interpretation, and codegen each need to normalize or relate aliases with their phase-specific
60-
/// inference or monomorphization context before deciding whether to copy or recurse.
56+
/// Named structs match non-`PhantomData` data fields by name. Tuple structs match
57+
/// non-`PhantomData` data fields by their filtered ordinal position. `PhantomData` fields are
58+
/// ignored on both sides for matching, but the returned fields preserve their original field
59+
/// indices for projection. The field types are instantiated but intentionally not normalized here:
60+
/// validation, borrowck, interpretation, and codegen each need to normalize or relate aliases with
61+
/// their phase-specific inference or monomorphization context before deciding whether to copy or
62+
/// recurse.
6163
pub fn coerce_shared_field_pairs<'tcx>(
6264
tcx: TyCtxt<'tcx>,
6365
source_def: ty::AdtDef<'tcx>,
@@ -71,20 +73,32 @@ pub fn coerce_shared_field_pairs<'tcx>(
7173
return Err(CoerceSharedFieldPairError::FieldStyleMismatch);
7274
}
7375

74-
let by_index = matches!(target_variant.ctor_kind(), Some(CtorKind::Fn));
76+
let by_position = matches!(target_variant.ctor_kind(), Some(CtorKind::Fn));
7577
let source_fields = reborrow_data_fields(tcx, source_def, source_args);
76-
reborrow_data_fields(tcx, target_def, target_args)
77-
.into_iter()
78-
.map(|target| {
79-
let source = source_fields
80-
.iter()
81-
.copied()
82-
.find(|source| {
83-
if by_index { source.index == target.index } else { source.name == target.name }
84-
})
85-
.ok_or(CoerceSharedFieldPairError::MissingSourceField { target })?;
78+
let target_fields = reborrow_data_fields(tcx, target_def, target_args);
8679

87-
Ok(CoerceSharedFieldPair { source, target })
88-
})
89-
.collect()
80+
if by_position {
81+
target_fields
82+
.into_iter()
83+
.zip(source_fields.into_iter().map(Some).chain(std::iter::repeat(None)))
84+
.map(|(target, source)| {
85+
let source =
86+
source.ok_or(CoerceSharedFieldPairError::MissingSourceField { target })?;
87+
Ok(CoerceSharedFieldPair { source, target })
88+
})
89+
.collect()
90+
} else {
91+
target_fields
92+
.into_iter()
93+
.map(|target| {
94+
let source = source_fields
95+
.iter()
96+
.copied()
97+
.find(|source| source.name == target.name)
98+
.ok_or(CoerceSharedFieldPairError::MissingSourceField { target })?;
99+
100+
Ok(CoerceSharedFieldPair { source, target })
101+
})
102+
.collect()
103+
}
90104
}

tests/ui/reborrow/coerce-shared-alias-projection.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55

66
use std::marker::{CoerceShared, Reborrow};
77

8+
// This test combines alias/projection normalization with the leaf `&mut T` to `&T`
9+
// shared reborrow path.
10+
811
struct InnerMut<'a, T> {
9-
value: &'a T,
12+
value: &'a mut T,
1013
}
1114

1215
impl<'a, T> Reborrow for InnerMut<'a, T> {}
@@ -87,15 +90,18 @@ fn read_projection<'a>(outer: OuterProjectionRef<'a, u32>) -> (&'a u32, usize) {
8790
const fn const_accept_projection(_outer: OuterProjectionRef<'_, u32>) {}
8891

8992
const fn consteval_projection_reborrow() {
90-
let value = 11;
91-
const_accept_projection(OuterMut { inner: InnerMut { value: &value }, tag: 5 });
93+
let mut value = 11;
94+
const_accept_projection(OuterMut {
95+
inner: InnerMut { value: &mut value },
96+
tag: 5,
97+
});
9298
}
9399

94100
fn main() {
95101
const { consteval_projection_reborrow(); }
96102

97-
let value = 22;
98-
let outer = OuterMut { inner: InnerMut { value: &value }, tag: 7 };
103+
let mut value = 22;
104+
let outer = OuterMut { inner: InnerMut { value: &mut value }, tag: 7 };
99105

100106
let (alias_value, alias_tag) = read_alias(outer);
101107
assert_eq!((*alias_value, alias_tag), (22, 7));
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//@ run-pass
2+
3+
#![feature(reborrow)]
4+
#![allow(dead_code)]
5+
6+
use std::marker::{CoerceShared, PhantomData, Reborrow};
7+
8+
struct SourceLeadingMut<'a, T>(PhantomData<&'a mut T>, &'a mut T);
9+
10+
impl<'a, T> Reborrow for SourceLeadingMut<'a, T> {}
11+
12+
struct SourceLeadingRef<'a, T>(&'a T);
13+
14+
impl<'a, T> Clone for SourceLeadingRef<'a, T> {
15+
fn clone(&self) -> Self {
16+
*self
17+
}
18+
}
19+
20+
impl<'a, T> Copy for SourceLeadingRef<'a, T> {}
21+
22+
impl<'a, T> CoerceShared<SourceLeadingRef<'a, T>> for SourceLeadingMut<'a, T> {}
23+
24+
struct TargetLeadingMut<'a, T>(&'a mut T);
25+
26+
impl<'a, T> Reborrow for TargetLeadingMut<'a, T> {}
27+
28+
struct TargetLeadingRef<'a, T>(PhantomData<&'a T>, &'a T);
29+
30+
impl<'a, T> Clone for TargetLeadingRef<'a, T> {
31+
fn clone(&self) -> Self {
32+
*self
33+
}
34+
}
35+
36+
impl<'a, T> Copy for TargetLeadingRef<'a, T> {}
37+
38+
impl<'a, T> CoerceShared<TargetLeadingRef<'a, T>> for TargetLeadingMut<'a, T> {}
39+
40+
struct InterleavedMut<'a, T, U>(
41+
PhantomData<&'a mut T>,
42+
&'a mut T,
43+
PhantomData<&'a mut U>,
44+
&'a mut U,
45+
);
46+
47+
impl<'a, T, U> Reborrow for InterleavedMut<'a, T, U> {}
48+
49+
struct InterleavedRef<'a, T, U>(&'a T, PhantomData<&'a T>, &'a U, PhantomData<&'a U>);
50+
51+
impl<'a, T, U> Clone for InterleavedRef<'a, T, U> {
52+
fn clone(&self) -> Self {
53+
*self
54+
}
55+
}
56+
57+
impl<'a, T, U> Copy for InterleavedRef<'a, T, U> {}
58+
59+
impl<'a, T, U> CoerceShared<InterleavedRef<'a, T, U>> for InterleavedMut<'a, T, U> {}
60+
61+
fn read_source_leading<'a>(value: SourceLeadingRef<'a, i32>) -> &'a i32 {
62+
value.0
63+
}
64+
65+
fn read_target_leading<'a>(value: TargetLeadingRef<'a, i32>) -> &'a i32 {
66+
value.1
67+
}
68+
69+
fn read_interleaved<'a>(value: InterleavedRef<'a, i32, i64>) -> (&'a i32, &'a i64) {
70+
(value.0, value.2)
71+
}
72+
73+
fn main() {
74+
let mut source_leading = 10;
75+
let wrapped = SourceLeadingMut(PhantomData, &mut source_leading);
76+
assert_eq!(*read_source_leading(wrapped), 10);
77+
78+
let mut target_leading = 20;
79+
let wrapped = TargetLeadingMut(&mut target_leading);
80+
assert_eq!(*read_target_leading(wrapped), 20);
81+
82+
let mut first = 30;
83+
let mut second = 40_i64;
84+
let wrapped = InterleavedMut(PhantomData, &mut first, PhantomData, &mut second);
85+
let (first, second) = read_interleaved(wrapped);
86+
assert_eq!((*first, *second), (30, 40));
87+
}

tests/ui/reborrow/coerce_shared_consteval.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use std::marker::{CoerceShared, Reborrow};
99

10-
pub struct MyMut<'a>(&'a u8);
10+
pub struct MyMut<'a>(&'a mut u8);
1111

1212
impl Reborrow for MyMut<'_> {}
1313

@@ -17,8 +17,8 @@ pub struct MyRef<'a>(&'a u8);
1717
impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
1818

1919
const fn consteval_reproducer() {
20-
let value = 1;
21-
foo(MyMut(&value));
20+
let mut value = 1;
21+
foo(MyMut(&mut value));
2222
}
2323

2424
const fn foo(_x: MyRef<'_>) {}

tests/ui/reborrow/issue-156315-corrected.rs renamed to tests/ui/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs

File renamed without changes.

tests/ui/reborrow/issue-156315-corrected.stderr renamed to tests/ui/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field
2-
--> $DIR/issue-156315-corrected.rs:11:25
2+
--> $DIR/corrected-field-mismatch-coerce-shared-issue-156315.rs:11:25
33
|
44
LL | struct CustomMut<'a, T>(&'a mut T);
55
| --------- source field `0` has type `&'a mut T`

tests/ui/reborrow/issue-156309.rs renamed to tests/ui/reborrow/malformed-marker-coerce-shared-issue-156309.rs

File renamed without changes.

tests/ui/reborrow/issue-156309.stderr renamed to tests/ui/reborrow/malformed-marker-coerce-shared-issue-156309.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: expected type, found lifetime
2-
--> $DIR/issue-156309.rs:21:43
2+
--> $DIR/malformed-marker-coerce-shared-issue-156309.rs:21:43
33
|
44
LL | fn method<'a>(_a: CustomMarkerRef<'a>) -> 'a () {
55
| ^^ expected type
@@ -10,7 +10,7 @@ LL | fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () {
1010
| +
1111

1212
error[E0573]: expected type, found derive macro `Debug`
13-
--> $DIR/issue-156309.rs:12:41
13+
--> $DIR/malformed-marker-coerce-shared-issue-156309.rs:12:41
1414
|
1515
LL | struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, Copy)>);
1616
| ^^^^^ not a type
@@ -21,7 +21,7 @@ LL + use std::fmt::Debug;
2121
|
2222

2323
error[E0782]: expected a type, found a trait
24-
--> $DIR/issue-156309.rs:12:48
24+
--> $DIR/malformed-marker-coerce-shared-issue-156309.rs:12:48
2525
|
2626
LL | struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, Copy)>);
2727
| ^^^^^
@@ -32,7 +32,7 @@ LL | struct CustomMarkerRef<'a>(PhantomData<(Debug, dyn Clone, Copy)>);
3232
| +++
3333

3434
error[E0782]: expected a type, found a trait
35-
--> $DIR/issue-156309.rs:12:55
35+
--> $DIR/malformed-marker-coerce-shared-issue-156309.rs:12:55
3636
|
3737
LL | struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, Copy)>);
3838
| ^^^^
@@ -43,7 +43,7 @@ LL | struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, dyn Copy)>);
4343
| +++
4444

4545
error[E0277]: the trait bound `CustomMarkerRef<'a>: Copy` is not satisfied
46-
--> $DIR/issue-156309.rs:18:10
46+
--> $DIR/malformed-marker-coerce-shared-issue-156309.rs:18:10
4747
|
4848
LL | impl<'a> CoerceShared<CustomMarkerRef<'a>> for CustomMarker<'a> {}
4949
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `CustomMarkerRef<'a>`

tests/ui/reborrow/issue-156309-corrected.rs renamed to tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs

File renamed without changes.

0 commit comments

Comments
 (0)