Skip to content

Commit 336e50a

Browse files
committed
add implementation of coerce_unsized for pointers
1 parent 53a6bee commit 336e50a

File tree

3 files changed

+282
-12
lines changed

3 files changed

+282
-12
lines changed

chalk-ir/src/interner.rs

+10
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,16 @@ where
726726
type Interner = I;
727727
}
728728

729+
impl<A, B, C, I> HasInterner for (A, B, C)
730+
where
731+
A: HasInterner<Interner = I>,
732+
B: HasInterner<Interner = I>,
733+
C: HasInterner<Interner = I>,
734+
I: Interner,
735+
{
736+
type Interner = I;
737+
}
738+
729739
impl<'a, T: HasInterner> HasInterner for std::slice::Iter<'a, T> {
730740
type Interner = T::Interner;
731741
}

chalk-solve/src/wf.rs

+123-12
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
use std::{fmt, iter};
22

3-
use crate::ext::*;
4-
use crate::goal_builder::GoalBuilder;
5-
use crate::rust_ir::*;
6-
use crate::solve::Solver;
7-
use crate::split::Split;
8-
use crate::RustIrDatabase;
9-
use chalk_ir::cast::*;
10-
use chalk_ir::fold::shift::Shift;
11-
use chalk_ir::interner::Interner;
12-
use chalk_ir::visit::{Visit, Visitor};
13-
use chalk_ir::*;
3+
use crate::{
4+
ext::*, goal_builder::GoalBuilder, rust_ir::*, solve::Solver, split::Split, RustIrDatabase,
5+
};
6+
use chalk_ir::{
7+
cast::*,
8+
fold::shift::Shift,
9+
interner::Interner,
10+
visit::{Visit, Visitor},
11+
*,
12+
};
1413
use tracing::debug;
1514

1615
#[derive(Debug)]
@@ -338,7 +337,14 @@ where
338337
WellKnownTrait::Drop => {
339338
WfWellKnownConstraints::drop_impl_constraint(&mut *solver, self.db, &impl_datum)
340339
}
341-
WellKnownTrait::Clone | WellKnownTrait::Unpin | WellKnownTrait::CoerceUnsized => true,
340+
WellKnownTrait::CoerceUnsized => {
341+
WfWellKnownConstraints::coerce_unsized_impl_constraint(
342+
&mut *solver,
343+
self.db,
344+
&impl_datum,
345+
)
346+
}
347+
WellKnownTrait::Clone | WellKnownTrait::Unpin => true,
342348
// You can't add a manual implementation for the following traits:
343349
WellKnownTrait::Fn
344350
| WellKnownTrait::FnOnce
@@ -813,4 +819,109 @@ impl WfWellKnownConstraints {
813819

814820
solver.has_unique_solution(db, &well_formed_goal.into_closed_goal(interner))
815821
}
822+
823+
/// Verify constraints a CoerceUnsized impl.
824+
/// Rules for CoerceUnsized impl to be considered well-formed:
825+
/// a) pointer conversions: &[mut] T -> &[mut] U, &[mut] T -> *[mut] U,
826+
/// *[mut] T -> *[mut] U are considered valid if
827+
/// 1) T: Unsize<U>
828+
/// 2) mutability is respected, i.e. immutable -> immutable, mutable -> immutable,
829+
/// mutable -> mutable conversions are allowed, immutable -> mutable is not.
830+
/// b) struct conversions of structures with the same definition, `S<P0...Pn>` -> `S<Q0...Qn>`.
831+
/// To check if this impl is legal, we would walk down the fields of `S`
832+
/// and consider their types with both substitutes. We are looking to find
833+
/// exactly one (non-phantom) field that has changed its type (from T to U), and
834+
/// expect T to be unsizeable to U, i.e. T: CoerceUnsized<U>.
835+
///
836+
/// As an example, consider a struct
837+
/// ```rust
838+
/// struct Foo<T, U> {
839+
/// extra: T,
840+
/// ptr: *mut U,
841+
/// }
842+
/// ```
843+
///
844+
/// We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
845+
/// to `Foo<T, [i32]>`. That impl would look like:
846+
/// ```rust,ignore
847+
/// impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
848+
/// ```
849+
/// In this case:
850+
///
851+
/// - `extra` has type `T` before and type `T` after
852+
/// - `ptr` has type `*mut U` before and type `*mut V` after
853+
///
854+
/// Since just one field changed, we would then check that `*mut U: CoerceUnsized<*mut V>`
855+
/// is implemented. This will work out because `U: Unsize<V>`, and we have a libcore rule
856+
/// that `*mut U` can be coerced to `*mut V` if `U: Unsize<V>`.
857+
fn coerce_unsized_impl_constraint<I: Interner>(
858+
solver: &mut dyn Solver<I>,
859+
db: &dyn RustIrDatabase<I>,
860+
impl_datum: &ImplDatum<I>,
861+
) -> bool {
862+
let interner = db.interner();
863+
let mut gb = GoalBuilder::new(db);
864+
865+
let (binders, impl_datum) = impl_datum.binders.as_ref().into();
866+
867+
let trait_ref: &TraitRef<I> = &impl_datum.trait_ref;
868+
869+
let source = trait_ref.self_type_parameter(interner);
870+
let target = trait_ref
871+
.substitution
872+
.at(interner, 1)
873+
.assert_ty_ref(interner)
874+
.clone();
875+
match (source.data(interner), target.data(interner)) {
876+
(TyData::Apply(source_app), TyData::Apply(target_app)) => {
877+
match (&source_app.name, &target_app.name) {
878+
(TypeName::Ref(s_m), TypeName::Ref(t_m))
879+
| (TypeName::Ref(s_m), TypeName::Raw(t_m))
880+
| (TypeName::Raw(s_m), TypeName::Raw(t_m)) => {
881+
if matches!((*s_m, *t_m), (Mutability::Not, Mutability::Mut)) {
882+
return false;
883+
}
884+
885+
let source = source_app.first_type_parameter(interner).unwrap();
886+
let target = target_app.first_type_parameter(interner).unwrap();
887+
888+
let unsize_trait_id =
889+
if let Some(id) = db.well_known_trait_id(WellKnownTrait::Unsize) {
890+
id
891+
} else {
892+
return false;
893+
};
894+
895+
let unsize_goal: Goal<I> = TraitRef {
896+
trait_id: unsize_trait_id,
897+
substitution: Substitution::from_iter(
898+
interner,
899+
[source, target].iter().cloned(),
900+
),
901+
}
902+
.cast(interner);
903+
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+
);
918+
919+
solver.has_unique_solution(db, &unsize_goal.into_closed_goal(interner))
920+
}
921+
_ => false,
922+
}
923+
}
924+
_ => false,
925+
}
926+
}
816927
}

tests/test/wf_lowering.rs

+149
Original file line numberDiff line numberDiff line change
@@ -1129,3 +1129,152 @@ fn ill_formed_opaque_ty() {
11291129
}
11301130
}
11311131
}
1132+
1133+
#[test]
1134+
fn coerce_unsized_pointer() {
1135+
lowering_success! {
1136+
program {
1137+
#[lang(unsize)]
1138+
trait Unsize<T> {}
1139+
1140+
#[lang(coerce_unsized)]
1141+
trait CoerceUnsized<T> {}
1142+
1143+
impl<'a, T, U> CoerceUnsized<*mut U> for &'a mut T where T: Unsize<U> {}
1144+
}
1145+
}
1146+
1147+
lowering_error! {
1148+
program {
1149+
#[lang(unsize)]
1150+
trait Unsize<T> {}
1151+
1152+
#[lang(coerce_unsized)]
1153+
trait CoerceUnsized<T> {}
1154+
1155+
impl<'a, T, U> CoerceUnsized<*mut U> for &'a mut T {}
1156+
} error_msg {
1157+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1158+
}
1159+
}
1160+
1161+
lowering_success! {
1162+
program {
1163+
#[lang(unsize)]
1164+
trait Unsize<T> {}
1165+
1166+
#[lang(coerce_unsized)]
1167+
trait CoerceUnsized<T> {}
1168+
1169+
#[object_safe]
1170+
trait Foo {}
1171+
1172+
#[auto]
1173+
#[object_safe]
1174+
trait Auto {}
1175+
1176+
impl<'a> CoerceUnsized<&'a (dyn Foo + 'a)> for &'a (dyn Foo + Auto + 'a) {}
1177+
}
1178+
}
1179+
1180+
lowering_error! {
1181+
program {
1182+
#[lang(unsize)]
1183+
trait Unsize<T> {}
1184+
1185+
#[lang(coerce_unsized)]
1186+
trait CoerceUnsized<T> {}
1187+
1188+
#[object_safe]
1189+
trait Foo {}
1190+
1191+
#[auto]
1192+
#[object_safe]
1193+
trait Auto {}
1194+
1195+
impl<'a> CoerceUnsized<&'a (dyn Foo + Auto + 'a)> for &'a (dyn Foo + 'a) {}
1196+
} error_msg {
1197+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1198+
}
1199+
}
1200+
1201+
lowering_success! {
1202+
program {
1203+
#[lang(unsize)]
1204+
trait Unsize<T> {}
1205+
1206+
#[lang(coerce_unsized)]
1207+
trait CoerceUnsized<T> {}
1208+
1209+
impl<'a> CoerceUnsized<&'a [f32]> for &'a [f32; 3] {}
1210+
}
1211+
}
1212+
1213+
lowering_error! {
1214+
program {
1215+
#[lang(unsize)]
1216+
trait Unsize<T> {}
1217+
1218+
#[lang(coerce_unsized)]
1219+
trait CoerceUnsized<T> {}
1220+
1221+
impl<'a, T, U> CoerceUnsized<*mut U> for &'a T where T: Unsize<U> {}
1222+
} error_msg {
1223+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1224+
}
1225+
}
1226+
1227+
lowering_error! {
1228+
program {
1229+
#[lang(unsize)]
1230+
trait Unsize<T> {}
1231+
1232+
#[lang(coerce_unsized)]
1233+
trait CoerceUnsized<T> {}
1234+
1235+
impl<'a, T, U> CoerceUnsized<&'a mut U> for &'a T where T: Unsize<U> {}
1236+
} error_msg {
1237+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1238+
}
1239+
}
1240+
1241+
lowering_error! {
1242+
program {
1243+
#[lang(unsize)]
1244+
trait Unsize<T> {}
1245+
1246+
#[lang(coerce_unsized)]
1247+
trait CoerceUnsized<T> {}
1248+
1249+
impl<T, U> CoerceUnsized<*mut U> for *const T where T: Unsize<U> {}
1250+
} error_msg {
1251+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1252+
}
1253+
}
1254+
1255+
lowering_error! {
1256+
program {
1257+
#[lang(unsize)]
1258+
trait Unsize<T> {}
1259+
1260+
#[lang(coerce_unsized)]
1261+
trait CoerceUnsized<T> {}
1262+
1263+
impl<'a, T, U> CoerceUnsized<&'a U> for *const T where T: Unsize<U> {}
1264+
} error_msg {
1265+
"trait impl for `CoerceUnsized` does not meet well-formedness requirements"
1266+
}
1267+
}
1268+
1269+
lowering_success! {
1270+
program {
1271+
#[lang(unsize)]
1272+
trait Unsize<T> {}
1273+
1274+
#[lang(coerce_unsized)]
1275+
trait CoerceUnsized<T> {}
1276+
1277+
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> {}
1278+
}
1279+
}
1280+
}

0 commit comments

Comments
 (0)