Skip to content

Add support for quantified bounds on GATs #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion chalk-parse/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub struct TraitFlags {
pub struct AssocTyDefn {
pub name: Identifier,
pub parameter_kinds: Vec<ParameterKind>,
pub bounds: Vec<InlineBound>,
pub bounds: Vec<QuantifiedInlineBound>,
pub where_clauses: Vec<QuantifiedWhereClause>,
}

Expand All @@ -85,6 +85,12 @@ pub enum InlineBound {
ProjectionEqBound(ProjectionEqBound),
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct QuantifiedInlineBound {
pub parameter_kinds: Vec<ParameterKind>,
pub bound: InlineBound,
}

#[derive(Clone, PartialEq, Eq, Debug)]
/// Represents a trait bound on e.g. a type or type parameter.
/// Does not know anything about what it's binding.
Expand Down
14 changes: 13 additions & 1 deletion chalk-parse/src/parser.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ TraitDefn: TraitDefn = {
};

AssocTyDefn: AssocTyDefn = {
"type" <name:Id> <p:Angle<ParameterKind>> <b:(":" <Plus<InlineBound>>)?>
"type" <name:Id> <p:Angle<ParameterKind>> <b:(":" <Plus<QuantifiedInlineBound>>)?>
<w:QuantifiedWhereClauses> ";" =>
{
AssocTyDefn {
Expand Down Expand Up @@ -114,6 +114,18 @@ ProjectionEqBound: ProjectionEqBound = {
}
};

QuantifiedInlineBound: QuantifiedInlineBound = {
<b:InlineBound> => QuantifiedInlineBound {
parameter_kinds: vec![],
bound: b,
},

"forall" "<" <pk:Comma<ParameterKind>> ">" <b:InlineBound> => QuantifiedInlineBound {
parameter_kinds: pk,
bound: b,
},
};

Impl: Impl = {
"impl" <p:Angle<ParameterKind>> <mark:"!"?> <t:Id> <a:Angle<Parameter>> "for" <s:Ty>
<w:QuantifiedWhereClauses> "{" <assoc:AssocTyValue*> "}" =>
Expand Down
26 changes: 20 additions & 6 deletions src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,19 +275,34 @@ pub enum InlineBound {
ProjectionEqBound(ProjectionEqBound),
}

pub type QuantifiedInlineBound = Binders<InlineBound>;

impl InlineBound {
/// Applies the `InlineBound` to `self_ty` and lowers to a [`DomainGoal`].
///
/// Because an `InlineBound` does not know anything about what it's binding,
/// you must provide that type as `self_ty`.
crate fn into_where_clauses(&self, self_ty: Ty) -> Vec<WhereClause> {
fn into_where_clauses(&self, self_ty: Ty) -> Vec<WhereClause> {
match self {
InlineBound::TraitBound(b) => b.into_where_clauses(self_ty),
InlineBound::ProjectionEqBound(b) => b.into_where_clauses(self_ty),
}
}
}

impl QuantifiedInlineBound {
crate fn into_where_clauses(&self, self_ty: Ty) -> Vec<QuantifiedWhereClause> {
let self_ty = self_ty.up_shift(self.binders.len());
self.value.into_where_clauses(self_ty).into_iter().map(|wc| {
Binders {
binders: self.binders.clone(),
value: wc,
}
}).collect()
}
}


/// Represents a trait bound on e.g. a type or type parameter.
/// Does not know anything about what it's binding.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand All @@ -297,7 +312,7 @@ pub struct TraitBound {
}

impl TraitBound {
crate fn into_where_clauses(&self, self_ty: Ty) -> Vec<WhereClause> {
fn into_where_clauses(&self, self_ty: Ty) -> Vec<WhereClause> {
let trait_ref = self.as_trait_ref(self_ty);
vec![WhereClause::Implemented(trait_ref)]
}
Expand All @@ -310,7 +325,6 @@ impl TraitBound {
}
}
}

/// Represents a projection equality bound on e.g. a type or type parameter.
/// Does not know anything about what it's binding.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand All @@ -323,7 +337,7 @@ pub struct ProjectionEqBound {
}

impl ProjectionEqBound {
crate fn into_where_clauses(&self, self_ty: Ty) -> Vec<WhereClause> {
fn into_where_clauses(&self, self_ty: Ty) -> Vec<WhereClause> {
let trait_ref = self.trait_bound.as_trait_ref(self_ty);

let mut parameters = self.parameters.clone();
Expand Down Expand Up @@ -361,7 +375,7 @@ pub struct AssociatedTyDatum {
///
/// These must be proven by the implementer, for all possible parameters that
/// would result in a well-formed projection.
crate bounds: Vec<InlineBound>,
crate bounds: Vec<QuantifiedInlineBound>,

/// Where clauses that must hold for the projection to be well-formed.
crate where_clauses: Vec<QuantifiedWhereClause>,
Expand All @@ -373,7 +387,7 @@ impl AssociatedTyDatum {
/// ```notrust
/// Implemented(<?0 as Foo>::Item<?1>: Sized)
/// ```
crate fn bounds_on_self(&self) -> Vec<WhereClause> {
crate fn bounds_on_self(&self) -> Vec<QuantifiedWhereClause> {
let parameters = self.parameter_kinds
.anonymize()
.iter()
Expand Down
34 changes: 21 additions & 13 deletions src/ir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,17 +414,11 @@ impl LowerWhereClauses for Impl {
}
}

trait LowerWhereClauseVec<T> {
fn lower(&self, env: &Env) -> Result<Vec<T>>;
trait LowerWhereClauseVec {
fn lower(&self, env: &Env) -> Result<Vec<ir::QuantifiedWhereClause>>;
}

impl LowerWhereClauseVec<ir::WhereClause> for [WhereClause] {
fn lower(&self, env: &Env) -> Result<Vec<ir::WhereClause>> {
self.iter().flat_map(|wc| wc.lower(env).apply_result()).collect()
}
}

impl LowerWhereClauseVec<ir::QuantifiedWhereClause> for [QuantifiedWhereClause] {
impl LowerWhereClauseVec for [QuantifiedWhereClause] {
fn lower(&self, env: &Env) -> Result<Vec<ir::QuantifiedWhereClause>> {
self.iter()
.flat_map(|wc| match wc.lower(env) {
Expand Down Expand Up @@ -723,12 +717,26 @@ impl LowerInlineBound for InlineBound {
}
}

trait LowerInlineBounds {
fn lower(&self, env: &Env) -> Result<Vec<ir::InlineBound>>;
trait LowerQuantifiedInlineBound {
fn lower(&self, env: &Env) -> Result<ir::QuantifiedInlineBound>;
}

impl LowerQuantifiedInlineBound for QuantifiedInlineBound {
fn lower(&self, env: &Env) -> Result<ir::QuantifiedInlineBound> {
let parameter_kinds = self.parameter_kinds.iter().map(|pk| pk.lower());
let binders = env.in_binders(parameter_kinds, |env| {
Ok(self.bound.lower(env)?)
})?;
Ok(binders)
}
}

trait LowerQuantifiedInlineBoundVec {
fn lower(&self, env: &Env) -> Result<Vec<ir::QuantifiedInlineBound>>;
}

impl LowerInlineBounds for Vec<InlineBound> {
fn lower(&self, env: &Env) -> Result<Vec<ir::InlineBound>> {
impl LowerQuantifiedInlineBoundVec for [QuantifiedInlineBound] {
fn lower(&self, env: &Env) -> Result<Vec<ir::QuantifiedInlineBound>> {
self.iter()
.map(|b| b.lower(env))
.collect()
Expand Down
28 changes: 28 additions & 0 deletions src/ir/lowering/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,21 @@ fn gat_parse() {
}
}

#[test]
fn gat_higher_ranked_bound() {
lowering_success! {
program {
trait Fn<T> {}
trait Ref<'a, T> {}
trait Sized {}

trait Foo {
type Item<T>: forall<'a> Fn<Ref<'a, T>> + Sized;
}
}
}
}

#[test]
fn duplicate_parameters() {
lowering_error! {
Expand Down Expand Up @@ -388,6 +403,19 @@ fn duplicate_parameters() {
"duplicate or shadowed parameters"
}
}

lowering_error! {
program {
trait Fn<T> {}
trait Ref<'a, T> {}

trait Foo<'a> {
type Item<T>: forall<'a> Fn<Ref<'a, T>>;
}
} error_msg {
"duplicate or shadowed parameters"
}
}
}

#[test]
Expand Down
8 changes: 5 additions & 3 deletions src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,12 +717,14 @@ impl AssociatedTyDatum {
// FromEnv(<T as Foo>::Assoc: Bounds) :- FromEnv(Self: Foo)
// }
clauses.extend(self.bounds_on_self().into_iter().map(|bound| {
// Same as above in case of higher-ranked inline bounds.
let shift = bound.binders.len();
Binders {
binders: binders.clone(),
binders: bound.binders.iter().chain(binders.iter()).cloned().collect(),
value: ProgramClauseImplication {
consequence: bound.into_from_env_goal(),
consequence: bound.value.clone().into_from_env_goal(),
conditions: vec![
FromEnv::Trait(trait_ref.clone()).cast()
FromEnv::Trait(trait_ref.clone()).up_shift(shift).cast()
],
}
}.cast()
Expand Down
8 changes: 5 additions & 3 deletions src/rules/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ impl WfSolver {

let wf_goals =
input_types.into_iter()
.map(|ty| DomainGoal::WellFormed(WellFormed::Ty(ty)));
.map(|ty| DomainGoal::WellFormed(WellFormed::Ty(ty)))
.casted();

let trait_ref = trait_ref.up_shift(assoc_ty.value.binders.len());

Expand All @@ -233,9 +234,10 @@ impl WfSolver {
bounds.iter()
.map(|b| Subst::apply(&all_parameters, b))
.flat_map(|b| b.into_where_clauses(assoc_ty.value.value.ty.clone()))
.map(|g| g.into_well_formed_goal());
.map(|wc| wc.map(|bound| bound.into_well_formed_goal()))
.casted();

let goals = wf_goals.chain(bound_goals).casted();
let goals = wf_goals.chain(bound_goals);
let goal = match goals.fold1(|goal, leaf| Goal::And(Box::new(goal), Box::new(leaf))) {
Some(goal) => goal,
None => return None,
Expand Down
44 changes: 44 additions & 0 deletions src/rules/wf/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,3 +519,47 @@ fn higher_ranked_cyclic_requirements() {
}
}
}

#[test]
fn higher_ranked_inline_bound_on_gat() {
lowering_success! {
program {
trait Fn<T> { }
struct Ref<'a, T> { }
struct i32 {}

struct fn<T> { }

impl<'a, T> Fn<Ref<'a, T>> for for<'b> fn<Ref<'b, T>> { }

trait Bar {
type Item<T>: forall<'a> Fn<Ref<'a, T>>;
}

impl Bar for i32 {
type Item<T> = for<'a> fn<Ref<'a, T>>;
}
}
}

lowering_error! {
program {
trait Fn<T, U> { }
struct i32 {}

struct fn<T, U> { }

impl<T, U> Fn<T, U> for fn<T, U> { }

trait Bar {
type Item<T>: forall<U> Fn<T, U>;
}

impl Bar for i32 {
type Item<T> = fn<T, i32>;
}
} error_msg {
"trait impl for \"Bar\" does not meet well-formedness requirements"
}
}
}
24 changes: 24 additions & 0 deletions src/solve/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,30 @@ fn gat_implied_bounds() {
"No possible solution"
}
}

test! {
program {
trait Fn<T> { }
struct Ref<'a, T> { }
trait Sized { }

trait Foo {
type Item<T>: forall<'a> Fn<Ref<'a, T>> + Sized;
}
}

goal {
forall<Type> {
if (Type: Foo) {
forall<'a, T> {
<Type as Foo>::Item<T>: Fn<Ref<'a, T>>
}
}
}
} yields {
"Unique"
}
}
}

#[test]
Expand Down