Skip to content

Commit

Permalink
Check more requirements in WF clauses
Browse files Browse the repository at this point in the history
Check that `T: 'a` for `&'a T` and `&'a mut T`, check that
`T: Sized` in `[T]`, `[T; N]` and `(..., T, ...)`.
  • Loading branch information
matthewjasper committed Oct 3, 2021
1 parent d66bfe9 commit f8ba227
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 49 deletions.
185 changes: 149 additions & 36 deletions chalk-solve/src/clauses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use chalk_ir::interner::Interner;
use chalk_ir::*;
use rustc_hash::FxHashSet;
use std::iter;
use std::marker::PhantomData;
use tracing::{debug, instrument};

pub mod builder;
Expand Down Expand Up @@ -583,7 +584,7 @@ pub fn program_clauses_that_could_match<I: Interner>(
| DomainGoal::IsUpstream(ty)
| DomainGoal::DownstreamType(ty)
| DomainGoal::IsFullyVisible(ty)
| DomainGoal::IsLocal(ty) => match_ty(builder, environment, ty)?,
| DomainGoal::IsLocal(ty) => match_ty(builder, environment, &ty)?,
DomainGoal::FromEnv(_) => (), // Computed in the environment
DomainGoal::Normalize(Normalize { alias, ty: _ }) => match alias {
AliasTy::Projection(proj) => {
Expand Down Expand Up @@ -885,43 +886,150 @@ fn match_ty<I: Interner>(
.db
.fn_def_datum(*fn_def_id)
.to_program_clauses(builder, environment),
TyKind::Str | TyKind::Never | TyKind::Scalar(_) | TyKind::Foreign(_) => {
TyKind::Str
| TyKind::Never
| TyKind::Scalar(_)
| TyKind::Foreign(_)
| TyKind::Tuple(0, _) => {
// These have no substitutions, so they are trivially WF
builder.push_fact(WellFormed::Ty(ty.clone()));
}
TyKind::Raw(mutbl, _) => {
// forall<T> WF(*const T) :- WF(T);
builder.push_bound_ty(|builder, ty| {
builder.push_fact(WellFormed::Ty(
TyKind::Raw(*mutbl, ty).intern(builder.interner()),
));
builder.push_clause(
WellFormed::Ty(TyKind::Raw(*mutbl, ty.clone()).intern(builder.interner())),
Some(WellFormed::Ty(ty)),
);
});
}
TyKind::Ref(mutbl, _, _) => {
// forall<'a, T> WF(&'a T) :- WF(T), T: 'a
builder.push_bound_ty(|builder, ty| {
builder.push_bound_lifetime(|builder, lifetime| {
builder.push_fact(WellFormed::Ty(
TyKind::Ref(*mutbl, lifetime, ty).intern(builder.interner()),
));
let ref_ty = TyKind::Ref(*mutbl, lifetime.clone(), ty.clone())
.intern(builder.interner());
builder.push_clause(
WellFormed::Ty(ref_ty),
[
DomainGoal::WellFormed(WellFormed::Ty(ty.clone())),
DomainGoal::Holds(WhereClause::TypeOutlives(TypeOutlives {
ty,
lifetime,
})),
],
);
})
});
}
TyKind::Slice(_) => {
// forall<T> WF([T]) :- T: Sized, WF(T)
builder.push_bound_ty(|builder, ty| {
builder.push_fact(WellFormed::Ty(TyKind::Slice(ty).intern(builder.interner())));
let sized = builder.db.well_known_trait_id(WellKnownTrait::Sized);
builder.push_clause(
WellFormed::Ty(TyKind::Slice(ty.clone()).intern(builder.interner())),
sized
.map(|id| {
DomainGoal::Holds(WhereClause::Implemented(TraitRef {
trait_id: id,
substitution: Substitution::from1(interner, ty.clone()),
}))
})
.into_iter()
.chain(Some(DomainGoal::WellFormed(WellFormed::Ty(ty)))),
);
});
}
TyKind::Tuple(_, _)
| TyKind::Array(_, _)
| TyKind::Closure(_, _)
| TyKind::Generator(_, _)
| TyKind::GeneratorWitness(_, _) => {
TyKind::Array(..) => {
// forall<T. const N: usize> WF([T, N]) :- T: Sized
let interner = builder.interner();
let binders = Binders::new(
VariableKinds::from_iter(
interner,
[
VariableKind::Ty(TyVariableKind::General),
VariableKind::Const(
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(interner),
),
],
),
PhantomData::<I>,
);
builder.push_binders(binders, |builder, PhantomData| {
let placeholders_in_scope = builder.placeholders_in_scope();
let placeholder_count = placeholders_in_scope.len();
let ty = placeholders_in_scope[placeholder_count - 2]
.assert_ty_ref(interner)
.clone();
let size = placeholders_in_scope[placeholder_count - 1]
.assert_const_ref(interner)
.clone();

let sized = builder.db.well_known_trait_id(WellKnownTrait::Sized);
let array_ty = TyKind::Array(ty.clone(), size).intern(interner);
builder.push_clause(
WellFormed::Ty(array_ty),
sized
.map(|id| {
DomainGoal::Holds(WhereClause::Implemented(TraitRef {
trait_id: id,
substitution: Substitution::from1(interner, ty.clone()),
}))
})
.into_iter()
.chain(Some(DomainGoal::WellFormed(WellFormed::Ty(ty)))),
);
});
}
TyKind::Tuple(len, _) => {
// WF((T0, ..., Tn, U)) :- T0: Sized, ..., Tn: Sized, WF(T0), ..., WF(Tn), WF(U)
let interner = builder.interner();
let binders = Binders::new(
VariableKinds::from_iter(
interner,
iter::repeat_with(|| VariableKind::Ty(TyVariableKind::General)).take(*len),
),
PhantomData::<I>,
);
builder.push_binders(binders, |builder, PhantomData| {
let placeholders_in_scope = builder.placeholders_in_scope();

let substs = Substitution::from_iter(
builder.interner(),
&placeholders_in_scope[placeholders_in_scope.len() - len..],
);

let tuple_ty = TyKind::Tuple(*len, substs.clone()).intern(interner);
let sized = builder.db.well_known_trait_id(WellKnownTrait::Sized);
builder.push_clause(
WellFormed::Ty(tuple_ty),
substs.as_slice(interner)[..*len - 1]
.iter()
.filter_map(|s| {
let ty_var = s.assert_ty_ref(interner).clone();
sized.map(|id| {
DomainGoal::Holds(WhereClause::Implemented(TraitRef {
trait_id: id,
substitution: Substitution::from1(interner, ty_var),
}))
})
})
.chain(substs.iter(interner).map(|subst| {
DomainGoal::WellFormed(WellFormed::Ty(
subst.assert_ty_ref(interner).clone(),
))
})),
);
});
}
TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
let ty = generalize::Generalize::apply(builder.db.interner(), ty.clone());
builder.push_binders(ty, |builder, ty| {
builder.push_fact(WellFormed::Ty(ty.clone()));
});
}
TyKind::Placeholder(_) => {
builder.push_clause(WellFormed::Ty(ty.clone()), Some(FromEnv::Ty(ty.clone())));
builder.push_fact(WellFormed::Ty(ty.clone()));
}
TyKind::Alias(AliasTy::Projection(proj)) => builder
.db
Expand All @@ -945,30 +1053,35 @@ fn match_ty<I: Interner>(
// - Bounds on the associated types
// - Checking that all associated types are specified, including
// those on supertraits.
// - For trait objects with GATs, check that the bounds are fully
// general (`dyn for<'a> StreamingIterator<Item<'a> = &'a ()>` is OK,
// - For trait objects with GATs, if we allow them in the future,
// check that the bounds are fully general (
// `dyn for<'a> StreamingIterator<Item<'a> = &'a ()>` is OK,
// `dyn StreamingIterator<Item<'static> = &'static ()>` is not).
let bounds = dyn_ty
.bounds
.clone()
.substitute(interner, &[ty.clone().cast::<GenericArg<I>>(interner)]);

let mut wf_goals = Vec::new();

wf_goals.extend(bounds.iter(interner).flat_map(|bound| {
bound.map_ref(|bound| -> Vec<_> {
match bound {
WhereClause::Implemented(trait_ref) => {
vec![DomainGoal::WellFormed(WellFormed::Trait(trait_ref.clone()))]
let generalized_ty =
generalize::Generalize::apply(builder.db.interner(), dyn_ty.clone());
builder.push_binders(generalized_ty, |builder, dyn_ty| {
let bounds = dyn_ty
.bounds
.clone()
.substitute(interner, &[ty.clone().cast::<GenericArg<I>>(interner)]);

let mut wf_goals = Vec::new();

wf_goals.extend(bounds.iter(interner).flat_map(|bound| {
bound.map_ref(|bound| -> Vec<_> {
match bound {
WhereClause::Implemented(trait_ref) => {
vec![DomainGoal::WellFormed(WellFormed::Trait(trait_ref.clone()))]
}
WhereClause::AliasEq(_)
| WhereClause::LifetimeOutlives(_)
| WhereClause::TypeOutlives(_) => vec![],
}
WhereClause::AliasEq(_)
| WhereClause::LifetimeOutlives(_)
| WhereClause::TypeOutlives(_) => vec![],
}
})
}));
})
}));

builder.push_clause(WellFormed::Ty(ty.clone()), wf_goals);
builder.push_clause(WellFormed::Ty(ty.clone()), wf_goals);
});
}
})
}
Expand Down
3 changes: 1 addition & 2 deletions tests/lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,8 +806,7 @@ fn algebraic_data_types() {
type Owned: Borrow<Self>;
}

// FIXME(#435) should be `B: 'a + ToOwned`
enum Cow<'a, B> where B: ToOwned {
enum Cow<'a, B> where B: ToOwned, B: 'a {
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}
Expand Down
27 changes: 24 additions & 3 deletions tests/test/arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,37 @@ fn arrays_are_not_clone_if_element_not_clone() {
}

#[test]
fn arrays_are_well_formed() {
fn arrays_are_well_formed_if_elem_sized() {
test! {
program { }
program {
#[lang(sized)]
trait Sized { }
}

goal {
forall<const N, T> {
WellFormed([T; N])
if (T: Sized) {
WellFormed([T; N])
}
}
} yields {
"Unique"
}

goal {
forall<const N, T> {
WellFormed([T; N])
}
} yields {
"No possible solution"
}

goal {
exists<const N, T> {
WellFormed([T; N])
}
} yields {
"Ambiguous; no inference guidance"
}
}
}
17 changes: 17 additions & 0 deletions tests/test/existential_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,20 @@ fn dyn_associated_type_binding() {
}
}
}

#[test]
fn dyn_well_formed() {
test! {
program {
trait MyTrait {}
}

goal {
exists<'s> {
WellFormed(dyn MyTrait + 's)
}
} yields {
"Unique"
}
}
}
2 changes: 1 addition & 1 deletion tests/test/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ fn env_bound_vars() {
}
}
} yields {
"Unique"
"Ambiguous; definite substitution for<?U0> { [?0 := '^0.0] }"
}
goal {
exists<'a> {
Expand Down
20 changes: 16 additions & 4 deletions tests/test/refs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ use super::*;
#[test]
fn immut_refs_are_well_formed() {
test! {
program { }
program {
struct A { }
}

goal {
forall<'a, T> { WellFormed(&'a T) }
forall<'a, T> {
WellFormed(&'a T)
}
} yields {
"Unique; substitution [], lifetime constraints []"
"Unique; substitution [], lifetime constraints [InEnvironment { environment: Env([]), goal: !1_1: '!1_0 }]"
}

goal {
exists<'a> {
WellFormed(&'a A)
}
} yields {
"Unique; for<?U0> { substitution [?0 := '^0.0], lifetime constraints [InEnvironment { environment: Env([]), goal: A: '^0.0 }] }"
}
}
}
Expand Down Expand Up @@ -37,7 +49,7 @@ fn mut_refs_are_well_formed() {
goal {
forall<'a, T> { WellFormed(&'a mut T) }
} yields {
"Unique; substitution [], lifetime constraints []"
"Unique; substitution [], lifetime constraints [InEnvironment { environment: Env([]), goal: !1_1: '!1_0 }]"
}
}
}
Expand Down
12 changes: 10 additions & 2 deletions tests/test/slices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@ fn slices_are_not_sized() {
}

#[test]
fn slices_are_well_formed() {
fn slices_are_well_formed_if_elem_sized() {
test! {
program {
#[lang(sized)]
trait Sized { }
}

goal {
forall<T> { WellFormed([T]) }
forall<T> { if (T: Sized) { WellFormed([T]) } }
} yields {
"Unique; substitution [], lifetime constraints []"
}

goal {
forall<T> { WellFormed([T]) }
} yields {
"No possible solution"
}
}
}

Expand Down
Loading

0 comments on commit f8ba227

Please sign in to comment.