Skip to content

Commit 068433d

Browse files
committed
Allow unsizing dyn Trait -> dyn Trait + Auto if Trait: Auto
1 parent 798f945 commit 068433d

File tree

3 files changed

+81
-14
lines changed

3 files changed

+81
-14
lines changed

chalk-solve/src/clauses/builtin_traits/unsize.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::iter;
33
use std::ops::ControlFlow;
44

55
use crate::clauses::ClauseBuilder;
6+
use crate::clauses::super_traits::super_traits;
67
use crate::rust_ir::AdtKind;
78
use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait};
89
use chalk_ir::{
@@ -204,7 +205,20 @@ pub fn add_unsize_program_clauses<I: Interner>(
204205
let principal_a = principal_id(db, bounds_a);
205206
let principal_b = principal_id(db, bounds_b);
206207

207-
let auto_trait_ids_a: Vec<_> = auto_trait_ids(db, bounds_a).collect();
208+
// Include super traits in a list of auto traits for A,
209+
// to allow `dyn Trait -> dyn Trait + X` if `Trait: X`.
210+
let auto_trait_ids_a: Vec<_> = auto_trait_ids(db, bounds_a)
211+
.chain(principal_a.into_iter().flat_map(|principal_a| {
212+
super_traits(db, principal_a)
213+
.into_value_and_skipped_binders()
214+
.0
215+
.0
216+
.into_iter()
217+
.map(|x| x.skip_binders().trait_id)
218+
.filter(|&x| db.trait_datum(x).is_auto_trait())
219+
}))
220+
.collect();
221+
208222
let auto_trait_ids_b: Vec<_> = auto_trait_ids(db, bounds_b).collect();
209223

210224
let may_apply = principal_a == principal_b
@@ -234,7 +248,7 @@ pub fn add_unsize_program_clauses<I: Interner>(
234248
// ------------------
235249

236250
// Construct a new trait object type by taking the source ty,
237-
// filtering out auto traits of source that are not present in target
251+
// replacing auto traits of source with those of target,
238252
// and changing source lifetime to target lifetime.
239253
//
240254
// In order for the coercion to be valid, this new type
@@ -243,17 +257,33 @@ pub fn add_unsize_program_clauses<I: Interner>(
243257
bounds: bounds_a.map_ref(|bounds| {
244258
QuantifiedWhereClauses::from_iter(
245259
interner,
246-
bounds.iter(interner).filter(|bound| {
247-
let trait_id = match bound.trait_id() {
248-
Some(id) => id,
249-
None => return true,
250-
};
251-
252-
if auto_trait_ids_a.iter().all(|&id_a| id_a != trait_id) {
253-
return true;
254-
}
255-
auto_trait_ids_b.iter().any(|&id_b| id_b == trait_id)
256-
}),
260+
bounds
261+
.iter(interner)
262+
.cloned()
263+
.filter_map(|bound| {
264+
let Some(trait_id) = bound.trait_id() else {
265+
// Keep non-"implements" bounds as-is
266+
return Some(bound);
267+
};
268+
269+
// Auto traits are already checked above, ignore them
270+
// (we'll use the ones from B below)
271+
if db.trait_datum(trait_id).is_auto_trait() {
272+
return None;
273+
}
274+
275+
// The only "implements" bound that is not an auto trait, is the principal
276+
assert_eq!(Some(trait_id), principal_a);
277+
Some(bound)
278+
})
279+
// Add auto traits from B (again, they are already checked above).
280+
.chain(bounds_b.skip_binders().iter(interner).cloned().filter(
281+
|bound| {
282+
bound.trait_id().is_some_and(|trait_id| {
283+
db.trait_datum(trait_id).is_auto_trait()
284+
})
285+
},
286+
)),
257287
)
258288
}),
259289
lifetime: lifetime_b.clone(),

chalk-solve/src/clauses/super_traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub(super) fn push_trait_super_clauses<I: Interner>(
7373
}
7474
}
7575

76-
fn super_traits<I: Interner>(
76+
pub(crate) fn super_traits<I: Interner>(
7777
db: &dyn RustIrDatabase<I>,
7878
trait_id: TraitId<I>,
7979
) -> Binders<(

tests/test/unsize.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,43 @@ fn dyn_to_dyn_unsizing() {
142142
}
143143
}
144144

145+
#[test]
146+
fn super_auto_trait() {
147+
test! {
148+
program {
149+
#[lang(unsize)]
150+
trait Unsize<T> {}
151+
152+
#[object_safe]
153+
trait Principal where Self: SuperAuto {}
154+
155+
#[auto]
156+
#[object_safe]
157+
trait SuperAuto {}
158+
159+
#[auto]
160+
#[object_safe]
161+
trait Auto {}
162+
}
163+
164+
goal {
165+
forall<'a> {
166+
dyn Principal + 'a: Unsize<dyn Principal + SuperAuto + 'a>
167+
}
168+
} yields {
169+
expect!["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: '!1_0: '!1_0 }]"]
170+
}
171+
172+
goal {
173+
forall<'a> {
174+
dyn Principal + Auto + 'a: Unsize<dyn Principal + Auto + SuperAuto + 'a>
175+
}
176+
} yields {
177+
expect!["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: '!1_0: '!1_0 }]"]
178+
}
179+
}
180+
}
181+
145182
#[test]
146183
fn ty_to_dyn_unsizing() {
147184
test! {

0 commit comments

Comments
 (0)