Skip to content

Commit 597943f

Browse files
committed
add CoerceUnsized impl for structs
1 parent 336e50a commit 597943f

File tree

3 files changed

+262
-15
lines changed

3 files changed

+262
-15
lines changed

chalk-ir/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,17 @@ impl<I: Interner> Ty<I> {
477477
}
478478
}
479479

480+
/// Returns `Some(adt_id)` if this is an ADT, `None` otherwise
481+
pub fn adt_id(&self, interner: &I) -> Option<AdtId<I>> {
482+
match self.data(interner) {
483+
TyData::Apply(ApplicationTy {
484+
name: TypeName::Adt(adt_id),
485+
..
486+
}) => Some(*adt_id),
487+
_ => None,
488+
}
489+
}
490+
480491
/// True if this type contains "bound" types/lifetimes, and hence
481492
/// needs to be shifted across binders. This is a very inefficient
482493
/// check, intended only for debug assertions, because I am lazy.

chalk-solve/src/wf.rs

+91-15
Original file line numberDiff line numberDiff line change
@@ -872,13 +872,31 @@ impl WfWellKnownConstraints {
872872
.at(interner, 1)
873873
.assert_ty_ref(interner)
874874
.clone();
875+
876+
let mut place_in_environment = |goal| -> Goal<I> {
877+
gb.forall(
878+
&Binders::new(
879+
binders.clone(),
880+
(goal, trait_ref, &impl_datum.where_clauses),
881+
),
882+
(),
883+
|gb, _, (goal, trait_ref, where_clauses), ()| {
884+
let interner = gb.interner();
885+
gb.implies(
886+
impl_wf_environment(interner, &where_clauses, &trait_ref),
887+
|_| goal,
888+
)
889+
},
890+
)
891+
};
892+
875893
match (source.data(interner), target.data(interner)) {
876894
(TyData::Apply(source_app), TyData::Apply(target_app)) => {
877895
match (&source_app.name, &target_app.name) {
878896
(TypeName::Ref(s_m), TypeName::Ref(t_m))
879897
| (TypeName::Ref(s_m), TypeName::Raw(t_m))
880898
| (TypeName::Raw(s_m), TypeName::Raw(t_m)) => {
881-
if matches!((*s_m, *t_m), (Mutability::Not, Mutability::Mut)) {
899+
if (*s_m, *t_m) == (Mutability::Not, Mutability::Mut) {
882900
return false;
883901
}
884902

@@ -892,6 +910,7 @@ impl WfWellKnownConstraints {
892910
return false;
893911
};
894912

913+
// Source: Unsize<Target>
895914
let unsize_goal: Goal<I> = TraitRef {
896915
trait_id: unsize_trait_id,
897916
substitution: Substitution::from_iter(
@@ -901,23 +920,80 @@ impl WfWellKnownConstraints {
901920
}
902921
.cast(interner);
903922

904-
let unsize_goal = gb.forall(
905-
&Binders::new(
906-
binders,
907-
(unsize_goal, trait_ref, &impl_datum.where_clauses),
908-
),
909-
(),
910-
|gb, _, (unsize_goal, trait_ref, where_clauses), ()| {
911-
let interner = gb.interner();
912-
gb.implies(
913-
impl_wf_environment(interner, &where_clauses, &trait_ref),
914-
|_| unsize_goal,
915-
)
916-
},
917-
);
923+
// ImplEnv -> Source: Unsize<Target>
924+
let unsize_goal = place_in_environment(unsize_goal);
918925

919926
solver.has_unique_solution(db, &unsize_goal.into_closed_goal(interner))
920927
}
928+
(TypeName::Adt(source_id), TypeName::Adt(target_id)) => {
929+
let adt_datum = db.adt_datum(*source_id);
930+
931+
if source_id != target_id || adt_datum.kind != AdtKind::Struct {
932+
return false;
933+
}
934+
935+
let fields = adt_datum
936+
.binders
937+
.map_ref(|bound| &bound.variants.last().unwrap().fields);
938+
939+
let (source_fields, target_fields) = (
940+
fields.substitute(interner, &source_app.substitution),
941+
fields.substitute(interner, &target_app.substitution),
942+
);
943+
944+
// collect fields with unequal ids
945+
let uneq_field_ids: Vec<usize> = (0..source_fields.len())
946+
.filter(|&i| {
947+
// ignore phantom data fields
948+
if let Some(adt_id) = source_fields[i].adt_id(interner) {
949+
if db.adt_datum(adt_id).flags.phantom_data {
950+
return false;
951+
}
952+
}
953+
954+
let eq_goal: Goal<I> = EqGoal {
955+
a: source_fields[i].clone().cast(interner),
956+
b: target_fields[i].clone().cast(interner),
957+
}
958+
.cast(interner);
959+
960+
// ImplEnv -> Source.fields[i] = Target.fields[i]
961+
let eq_goal = place_in_environment(eq_goal);
962+
963+
// We are interested in !UNEQUAL! fields
964+
!solver.has_unique_solution(db, &eq_goal.into_closed_goal(interner))
965+
})
966+
.collect();
967+
968+
if uneq_field_ids.len() != 1 {
969+
return false;
970+
}
971+
972+
let field_id = uneq_field_ids[0];
973+
974+
// Source.fields[i]: CoerceUnsized<TargetFields[i]>
975+
let coerce_unsized_goal: Goal<I> = TraitRef {
976+
trait_id: trait_ref.trait_id,
977+
substitution: Substitution::from_iter(
978+
interner,
979+
[
980+
source_fields[field_id].clone(),
981+
target_fields[field_id].clone(),
982+
]
983+
.iter()
984+
.cloned(),
985+
),
986+
}
987+
.cast(interner);
988+
989+
// ImplEnv -> Source.fields[i]: CoerceUnsized<TargetFields[i]>
990+
let coerce_unsized_goal = place_in_environment(coerce_unsized_goal);
991+
992+
solver.has_unique_solution(
993+
db,
994+
&coerce_unsized_goal.into_closed_goal(interner),
995+
)
996+
}
921997
_ => false,
922998
}
923999
}

tests/test/wf_lowering.rs

+160
Original file line numberDiff line numberDiff line change
@@ -1141,9 +1141,11 @@ fn coerce_unsized_pointer() {
11411141
trait CoerceUnsized<T> {}
11421142

11431143
impl<'a, T, U> CoerceUnsized<*mut U> for &'a mut T where T: Unsize<U> {}
1144+
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
11441145
}
11451146
}
11461147

1148+
// T: Unsize<U> is not in the environment
11471149
lowering_error! {
11481150
program {
11491151
#[lang(unsize)]
@@ -1158,6 +1160,7 @@ fn coerce_unsized_pointer() {
11581160
}
11591161
}
11601162

1163+
// Test with builtin Unsize impl
11611164
lowering_success! {
11621165
program {
11631166
#[lang(unsize)]
@@ -1177,6 +1180,7 @@ fn coerce_unsized_pointer() {
11771180
}
11781181
}
11791182

1183+
// Test with builtin Unsize impl
11801184
lowering_error! {
11811185
program {
11821186
#[lang(unsize)]
@@ -1198,6 +1202,7 @@ fn coerce_unsized_pointer() {
11981202
}
11991203
}
12001204

1205+
// Test with builtin Unsize impl
12011206
lowering_success! {
12021207
program {
12031208
#[lang(unsize)]
@@ -1210,6 +1215,7 @@ fn coerce_unsized_pointer() {
12101215
}
12111216
}
12121217

1218+
// Coercing from shared to mut
12131219
lowering_error! {
12141220
program {
12151221
#[lang(unsize)]
@@ -1224,6 +1230,7 @@ fn coerce_unsized_pointer() {
12241230
}
12251231
}
12261232

1233+
// Coercing from shared to mut
12271234
lowering_error! {
12281235
program {
12291236
#[lang(unsize)]
@@ -1238,6 +1245,7 @@ fn coerce_unsized_pointer() {
12381245
}
12391246
}
12401247

1248+
// Coercing from shared to mut
12411249
lowering_error! {
12421250
program {
12431251
#[lang(unsize)]
@@ -1252,6 +1260,7 @@ fn coerce_unsized_pointer() {
12521260
}
12531261
}
12541262

1263+
// Coercing from raw pointer to ref
12551264
lowering_error! {
12561265
program {
12571266
#[lang(unsize)]
@@ -1265,7 +1274,10 @@ fn coerce_unsized_pointer() {
12651274
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
12661275
}
12671276
}
1277+
}
12681278

1279+
#[test]
1280+
fn coerce_unsized_struct() {
12691281
lowering_success! {
12701282
program {
12711283
#[lang(unsize)]
@@ -1274,7 +1286,155 @@ fn coerce_unsized_pointer() {
12741286
#[lang(coerce_unsized)]
12751287
trait CoerceUnsized<T> {}
12761288

1289+
struct Foo<'a, T> {
1290+
t: &'a T
1291+
}
1292+
1293+
struct Bar<T, U> {
1294+
extra: T,
1295+
ptr: *mut U,
1296+
}
1297+
1298+
impl<'a, T, U> CoerceUnsized<&'a U> for &'a T where T: Unsize<U> {}
1299+
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
1300+
impl<'a> CoerceUnsized<Foo<'a, [u32]>> for Foo<'a, [u32; 3]> {}
1301+
impl<T, U, V> CoerceUnsized<Bar<T, V>> for Bar<T, U> where U: Unsize<V> {}
1302+
}
1303+
}
1304+
1305+
// Unsizing different structs
1306+
lowering_error! {
1307+
program {
1308+
#[lang(unsize)]
1309+
trait Unsize<T> {}
1310+
1311+
#[lang(coerce_unsized)]
1312+
trait CoerceUnsized<T> {}
1313+
1314+
struct S1<T> {
1315+
t: T,
1316+
}
1317+
1318+
struct S2<T> {
1319+
t: T,
1320+
}
1321+
1322+
impl<T, U> CoerceUnsized<S2<U>> for S1<T> where T: CoerceUnsized<U> {}
1323+
} error_msg {
1324+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1325+
}
1326+
}
1327+
1328+
// Unsizing enums
1329+
lowering_error! {
1330+
program {
1331+
#[lang(unsize)]
1332+
trait Unsize<T> {}
1333+
1334+
#[lang(coerce_unsized)]
1335+
trait CoerceUnsized<T> {}
1336+
1337+
enum Foo<T> {
1338+
A {
1339+
t: T
1340+
}
1341+
}
1342+
1343+
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T> where T: CoerceUnsized<U> {}
1344+
} error_msg {
1345+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1346+
}
1347+
}
1348+
1349+
// Unsizing two fields
1350+
lowering_error! {
1351+
program {
1352+
#[lang(unsize)]
1353+
trait Unsize<T> {}
1354+
1355+
#[lang(coerce_unsized)]
1356+
trait CoerceUnsized<T> {}
1357+
1358+
struct Bar<T, U> {
1359+
ptr1: *mut T,
1360+
ptr2: *mut U,
1361+
}
1362+
1363+
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
1364+
impl<T, S, U, V> CoerceUnsized<Bar<T, V>> for Bar<S, U> where U: Unsize<V>, T: Unsize<S> {}
1365+
} error_msg {
1366+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1367+
}
1368+
}
1369+
1370+
// Unsizing no fields
1371+
lowering_error! {
1372+
program {
1373+
#[lang(unsize)]
1374+
trait Unsize<T> {}
1375+
1376+
#[lang(coerce_unsized)]
1377+
trait CoerceUnsized<T> {}
1378+
1379+
struct Bar<T, U> {
1380+
ptr1: *mut T,
1381+
ptr2: *mut U,
1382+
}
1383+
1384+
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
1385+
impl<T> CoerceUnsized<Bar<T, T>> for Bar<T, T> where T: Unsize<T> {}
1386+
} error_msg {
1387+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1388+
}
1389+
}
1390+
1391+
// No unsize in the environment
1392+
lowering_error! {
1393+
program {
1394+
#[lang(unsize)]
1395+
trait Unsize<T> {}
1396+
1397+
#[lang(coerce_unsized)]
1398+
trait CoerceUnsized<T> {}
1399+
1400+
struct Bar<T, U> {
1401+
extra: T,
1402+
ptr: *mut U,
1403+
}
1404+
1405+
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
1406+
impl<T, U, V> CoerceUnsized<Bar<T, V>> for Bar<T, U> {}
1407+
} error_msg {
1408+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1409+
}
1410+
}
1411+
1412+
// Phantom data test & CoerceUnsized in the environment test
1413+
lowering_success! {
1414+
program {
1415+
#[lang(unsize)]
1416+
trait Unsize<T> {}
1417+
1418+
#[lang(coerce_unsized)]
1419+
trait CoerceUnsized<T> {}
1420+
1421+
#[phantom_data]
1422+
struct PhantomData<T> {}
1423+
1424+
struct Foo<T, V> {
1425+
coerce: T,
1426+
phantom: PhantomData<V>,
1427+
}
1428+
1429+
struct Bar<T, U, V> {
1430+
extra: T,
1431+
phantom: PhantomData<V>,
1432+
ptr: *mut U,
1433+
}
1434+
12771435
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
1436+
impl<T, U, V, N, M> CoerceUnsized<Bar<T, V, N>> for Bar<T, U, M> where U: Unsize<V> {}
1437+
impl<T, U, V> CoerceUnsized<Foo<U, V>> for Foo<T, V> where T: CoerceUnsized<U> {}
12781438
}
12791439
}
12801440
}

0 commit comments

Comments
 (0)