Skip to content

Commit 1d16b9e

Browse files
committed
add tests
1 parent 2cbebf4 commit 1d16b9e

File tree

13 files changed

+871
-302
lines changed

13 files changed

+871
-302
lines changed

chalk-engine/src/logic.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::{
1313
use chalk_ir::interner::Interner;
1414
use chalk_ir::{debug, debug_heading, info, info_heading};
1515
use chalk_ir::{
16-
Canonical, ConstrainedSubst, Floundered, Goal, GoalData, InEnvironment, NoSolution,
17-
Substitution, UCanonical, UniverseMap,
16+
Canonical, ConstrainedSubst, DomainGoal, Floundered, Goal, GoalData, InEnvironment, NoSolution,
17+
Substitution, UCanonical, UniverseMap, WhereClause,
1818
};
1919

2020
type RootSearchResult<T> = Result<T, RootSearchFail>;
@@ -251,8 +251,18 @@ impl<I: Interner, C: Context<I>> Forest<I, C> {
251251
) -> Table<I> {
252252
let mut table = Table::new(goal.clone(), context.is_coinductive(&goal));
253253
let (mut infer, subst, environment, goal) = context.instantiate_ucanonical_goal(&goal);
254-
match goal.data(context.interner()) {
255-
GoalData::DomainGoal(domain_goal) => {
254+
let goal_data = goal.data(context.interner());
255+
256+
let is_outlives_goal = |dg: &DomainGoal<I>| {
257+
if let DomainGoal::Holds(WhereClause::LifetimeOutlives(_)) = dg {
258+
true
259+
} else {
260+
false
261+
}
262+
};
263+
264+
match goal_data {
265+
GoalData::DomainGoal(domain_goal) if !is_outlives_goal(domain_goal) => {
256266
match context.program_clauses(&environment, &domain_goal, &mut infer) {
257267
Ok(clauses) => {
258268
for clause in clauses {

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

+83-42
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use chalk_ir::{
77
cast::Cast,
88
interner::HasInterner,
99
visit::{visitors::FindAny, SuperVisit, Visit, VisitResult, Visitor},
10-
ApplicationTy, Binders, DebruijnIndex, DomainGoal, DynTy, EqGoal, Goal, QuantifiedWhereClauses,
11-
Substitution, TraitId, Ty, TyData, TypeName, WhereClause,
10+
ApplicationTy, Binders, Const, ConstValue, DebruijnIndex, DomainGoal, DynTy, EqGoal, Goal,
11+
LifetimeOutlives, QuantifiedWhereClauses, Substitution, TraitId, Ty, TyData, TypeName,
12+
WhereClause,
1213
};
1314

1415
struct UnsizeParameterCollector<'a, I: Interner> {
@@ -24,14 +25,12 @@ impl<'a, I: Interner> Visitor<'a, I> for UnsizeParameterCollector<'a, I> {
2425
self
2526
}
2627

27-
// FIXME(areredify) when const generics land, collect const variables too
28-
2928
fn visit_ty(&mut self, ty: &Ty<I>, outer_binder: DebruijnIndex) -> Self::Result {
3029
let interner = self.interner;
3130

3231
match ty.data(interner) {
3332
TyData::BoundVar(bound_var) => {
34-
// check if bound var referse to the outermost binder
33+
// check if bound var refers to the outermost binder
3534
if bound_var.debruijn.shifted_in() == outer_binder {
3635
self.parameters.insert(bound_var.index);
3736
}
@@ -40,6 +39,20 @@ impl<'a, I: Interner> Visitor<'a, I> for UnsizeParameterCollector<'a, I> {
4039
}
4140
}
4241

42+
fn visit_const(&mut self, constant: &Const<I>, outer_binder: DebruijnIndex) -> Self::Result {
43+
let interner = self.interner;
44+
45+
match constant.data(interner).value {
46+
ConstValue::BoundVar(bound_var) => {
47+
// check if bound var refers to the outermost binder
48+
if bound_var.debruijn.shifted_in() == outer_binder {
49+
self.parameters.insert(bound_var.index);
50+
}
51+
}
52+
_ => (),
53+
}
54+
}
55+
4356
fn interner(&self) -> &'a I {
4457
self.interner
4558
}
@@ -87,6 +100,23 @@ impl<'a, 'p, I: Interner> Visitor<'a, I> for ParameterOccurenceCheck<'a, 'p, I>
87100
}
88101
}
89102

103+
fn visit_const(&mut self, constant: &Const<I>, outer_binder: DebruijnIndex) -> Self::Result {
104+
let interner = self.interner;
105+
106+
match constant.data(interner).value {
107+
ConstValue::BoundVar(bound_var) => {
108+
if bound_var.debruijn.shifted_in() == outer_binder
109+
&& self.parameters.contains(&bound_var.index)
110+
{
111+
FindAny::FOUND
112+
} else {
113+
FindAny::new()
114+
}
115+
}
116+
_ => FindAny::new(),
117+
}
118+
}
119+
90120
fn interner(&self) -> &'a I {
91121
self.interner
92122
}
@@ -110,36 +140,25 @@ fn principal_id<'a, I: Interner>(
110140
) -> Option<TraitId<I>> {
111141
let interner = db.interner();
112142

113-
let principal_id = bounds
143+
return bounds
114144
.skip_binders()
115145
.iter(interner)
116-
.next()
117-
.expect("Expected trait object to have at least one trait bound")
118-
.trait_id()?;
119-
120-
if db.trait_datum(principal_id).is_auto_trait() {
121-
None
122-
} else {
123-
Some(principal_id)
124-
}
146+
.filter_map(|b| b.trait_id())
147+
.filter(|&id| !db.trait_datum(id).is_auto_trait())
148+
.next();
125149
}
126150

127151
fn auto_trait_ids<'a, I: Interner>(
128-
db: &dyn RustIrDatabase<I>,
152+
db: &'a dyn RustIrDatabase<I>,
129153
bounds: &'a Binders<QuantifiedWhereClauses<I>>,
130154
) -> impl Iterator<Item = TraitId<I>> + 'a {
131155
let interner = db.interner();
132-
// all trait ref where clauses after the principal are auto traits
133-
let to_skip = if principal_id(db, bounds).is_some() {
134-
1
135-
} else {
136-
0
137-
};
156+
138157
bounds
139158
.skip_binders()
140159
.iter(interner)
141-
.skip(to_skip)
142160
.filter_map(|clause| clause.trait_id())
161+
.filter(move |&id| db.trait_datum(id).is_auto_trait())
143162
}
144163

145164
pub fn add_unsize_program_clauses<I: Interner>(
@@ -196,10 +215,11 @@ pub fn add_unsize_program_clauses<I: Interner>(
196215
}
197216

198217
// COMMENT FROM RUSTC:
218+
// ------------------
199219
// Require that the traits involved in this upcast are **equal**;
200220
// only the **lifetime bound** is changed.
201221
//
202-
// FIXME: This condition is arguably too strong -- it would
222+
// This condition is arguably too strong -- it would
203223
// suffice for the source trait to be a *subtype* of the target
204224
// trait. In particular, changing from something like
205225
// `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be
@@ -211,11 +231,13 @@ pub fn add_unsize_program_clauses<I: Interner>(
211231
// with what our behavior should be there. -nikomatsakis
212232
// ------------------
213233

214-
// Filter out auto traits of source that are not present in target
215-
// and change source lifetime to target lifetime
234+
// Construct a new trait object type by taking the source ty,
235+
// filtering out auto traits of source that are not present in target
236+
// and changing source lifetime to target lifetime.
216237
//
217-
// This new type should be equal to target type.
218-
let source_ty = TyData::Dyn(DynTy {
238+
// In order for the coercion to be valid, this new type
239+
// should be equal to target type.
240+
let new_source_ty = TyData::Dyn(DynTy {
219241
bounds: bounds_a.map_ref(|bounds| {
220242
QuantifiedWhereClauses::from(
221243
interner,
@@ -238,16 +260,15 @@ pub fn add_unsize_program_clauses<I: Interner>(
238260

239261
// Check that new source is equal to target
240262
let eq_goal = EqGoal {
241-
a: source_ty.cast(interner),
263+
a: new_source_ty.cast(interner),
242264
b: target_ty.clone().cast(interner),
243265
}
244266
.cast(interner);
245267

246-
// FIXME(areredify) change this to outlives once #419 lands
247-
let lifetime_outlives_goal = EqGoal {
248-
a: lifetime_a.clone().cast(interner),
249-
b: lifetime_b.clone().cast(interner),
250-
}
268+
let lifetime_outlives_goal: Goal<I> = WhereClause::LifetimeOutlives(LifetimeOutlives {
269+
a: lifetime_a.clone(),
270+
b: lifetime_b.clone(),
271+
})
251272
.cast(interner);
252273

253274
builder.push_clause(trait_ref.clone(), [eq_goal, lifetime_outlives_goal].iter());
@@ -294,6 +315,27 @@ pub fn add_unsize_program_clauses<I: Interner>(
294315
);
295316
}
296317

318+
(
319+
TyData::Apply(ApplicationTy {
320+
name: TypeName::Array,
321+
substitution: array_subst,
322+
}),
323+
TyData::Apply(ApplicationTy {
324+
name: TypeName::Slice,
325+
substitution: slice_subst,
326+
}),
327+
) => {
328+
let array_ty = array_subst.at(interner, 0);
329+
let slice_ty = slice_subst.at(interner, 0);
330+
331+
let eq_goal = EqGoal {
332+
a: array_ty.clone(),
333+
b: slice_ty.clone(),
334+
};
335+
336+
builder.push_clause(trait_ref.clone(), iter::once(eq_goal));
337+
}
338+
297339
// Struct<T> -> Struct<U>
298340
// Unsizing of enums is not allowed
299341
(
@@ -318,19 +360,18 @@ pub fn add_unsize_program_clauses<I: Interner>(
318360
return;
319361
}
320362

321-
let struct_tail_field = struct_datum
363+
let adt_tail_field = struct_datum
322364
.binders
323365
.map_ref(|bound| bound.fields.last().unwrap());
324366

325367
// Collect unsize parameters that last field contains and
326368
// ensure there at least one of them.
327369
let unsize_parameter_candidates =
328-
outer_binder_parameters_used(interner, &struct_tail_field);
370+
outer_binder_parameters_used(interner, &adt_tail_field);
329371

330372
if unsize_parameter_candidates.len() == 0 {
331373
return;
332374
}
333-
334375
// Ensure none of the other fields mention the parameters used
335376
// in unsizing.
336377
if uses_outer_binder_params(
@@ -345,15 +386,15 @@ pub fn add_unsize_program_clauses<I: Interner>(
345386

346387
let parameters_a = substitution_a.parameters(interner);
347388
let parameters_b = substitution_b.parameters(interner);
348-
// Check that the source struct with the target's
389+
// Check that the source adt with the target's
349390
// unsizing parameters is equal to the target.
350391
let substitution = Substitution::from(
351392
interner,
352393
parameters_a.iter().enumerate().map(|(i, p)| {
353394
if unsize_parameter_candidates.contains(&i) {
354-
p
355-
} else {
356395
&parameters_b[i]
396+
} else {
397+
p
357398
}
358399
}),
359400
);
@@ -370,8 +411,8 @@ pub fn add_unsize_program_clauses<I: Interner>(
370411
.cast(interner);
371412

372413
// Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`.
373-
let source_tail_field = struct_tail_field.substitute(interner, substitution_a);
374-
let target_tail_field = struct_tail_field.substitute(interner, substitution_b);
414+
let source_tail_field = adt_tail_field.substitute(interner, substitution_a);
415+
let target_tail_field = adt_tail_field.substitute(interner, substitution_b);
375416

376417
// Check that `TailField<T>: Unsize<TailField<U>>`
377418
let last_field_unsizing_goal: Goal<I> = TraitRef {

tests/test/auto_traits.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ use super::*;
66
fn auto_semantics() {
77
test! {
88
program {
9-
#[lang(sized)] trait Sized { }
9+
trait Sized { }
1010
#[auto] trait Send { }
1111

1212
struct TypeA { }
1313

1414
struct Ptr<T> { }
1515
impl<T> Send for Ptr<T> where T: Send { }
1616

17-
struct List<T> where T: Sized {
17+
struct List<T> {
1818
data: T,
1919
next: Ptr<List<T>>
2020
}

tests/test/builtin_impls.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mod clone;
2+
mod copy;
3+
mod sized;
4+
mod unsize;

tests/test/builtin_impls/clone.rs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use crate::test::*;
2+
3+
#[test]
4+
fn tuples_are_clone() {
5+
test! {
6+
program {
7+
#[non_enumerable] // see above
8+
#[lang(clone)]
9+
trait Clone { }
10+
11+
struct S {}
12+
13+
impl Clone for u8 {}
14+
}
15+
16+
goal {
17+
([u8],): Clone
18+
} yields {
19+
"No possible solution"
20+
}
21+
22+
goal {
23+
(u8, [u8]): Clone
24+
} yields {
25+
"No possible solution"
26+
}
27+
28+
goal {
29+
([u8], u8): Clone
30+
} yields {
31+
"No possible solution"
32+
}
33+
34+
goal {
35+
(): Clone
36+
} yields {
37+
"Unique; substitution [], lifetime constraints []"
38+
}
39+
40+
goal {
41+
(u8,): Clone
42+
} yields {
43+
"Unique; substitution [], lifetime constraints []"
44+
}
45+
46+
goal {
47+
(u8, u8): Clone
48+
} yields {
49+
"Unique; substitution [], lifetime constraints []"
50+
}
51+
52+
goal {
53+
exists<T> { (T, u8): Clone }
54+
} yields {
55+
"Ambiguous"
56+
}
57+
58+
goal {
59+
forall<T> { if (T: Clone) { (T, u8): Clone } }
60+
} yields {
61+
"Unique; substitution [], lifetime constraints []"
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)