From 708288a3f651178a851053222e5de6596cd00c6e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 5 Jun 2020 17:44:24 -0400 Subject: [PATCH 01/14] Add FnOnce trait, and provide impl for Function type Works towards #363 I've followed the approach taken by rustc, where `FnOnce` has single generic argument: the tupled function parameters (e.g. `fn(u8, bool): FnOnce<(u8, bool)>`). I also extended the grammar to allow functions to take more than one argument. I've left `Fn` and `FnMut` for a separate PR - without a representation of closures in Chalk, they are not very useful. --- book/src/clauses/well_known_traits.md | 6 +- chalk-integration/src/lowering.rs | 13 ++-- chalk-parse/src/ast.rs | 3 +- chalk-parse/src/parser.lalrpop | 9 +-- chalk-solve/src/clauses/builtin_traits.rs | 4 ++ chalk-solve/src/clauses/builtin_traits/fn_.rs | 47 +++++++++++++++ chalk-solve/src/rust_ir.rs | 4 ++ chalk-solve/src/wf.rs | 4 +- tests/test/functions.rs | 60 +++++++++++++++++++ 9 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 chalk-solve/src/clauses/builtin_traits/fn_.rs diff --git a/book/src/clauses/well_known_traits.md b/book/src/clauses/well_known_traits.md index 33326a9fdf5..c2751957cc6 100644 --- a/book/src/clauses/well_known_traits.md +++ b/book/src/clauses/well_known_traits.md @@ -28,7 +28,7 @@ Some common examples of auto traits are `Send` and `Sync`. [coinductive_section]: ../engine/logic/coinduction.html#coinduction-and-refinement-strands # Current state -| Type | Copy | Clone | Sized | Unsize | Drop | Fn | Unpin | Generator | auto traits | +| Type | Copy | Clone | Sized | Unsize | Drop | FnOnce | Unpin | Generator | auto traits | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | tuple types | ✅ | ✅ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | | structs | ⚬ | ⚬ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | @@ -37,7 +37,7 @@ Some common examples of auto traits are `Send` and `Sync`. | never type | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | | trait objects | ⚬ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | | functions defs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ❌ | -| functions ptrs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ❌ | +| functions ptrs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ❌ | | raw ptrs | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | | immutable refs | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | | mutable refs | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | @@ -55,4 +55,4 @@ legend: 📚 - implementation provided in libcore ❌ - not implemented -❌ after a type name means that type is not yet in chalk \ No newline at end of file +❌ after a type name means that type is not yet in chalk diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 424bdd9f3ea..1c141246b70 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1394,18 +1394,20 @@ impl LowerTy for Ty { Ty::ForAll { ref lifetime_names, - ref ty, + ref types, } => { let quantified_env = env.introduce(lifetime_names.iter().map(|id| { chalk_ir::WithKind::new(chalk_ir::VariableKind::Lifetime, id.str.clone()) }))?; + let mut lowered_tys = Vec::with_capacity(types.len()); + for ty in types { + lowered_tys.push(ty.lower(&quantified_env)?.cast(interner)); + } + let function = chalk_ir::Fn { num_binders: lifetime_names.len(), - substitution: Substitution::from( - interner, - Some(ty.lower(&quantified_env)?.cast(interner)), - ), + substitution: Substitution::from(interner, lowered_tys), }; Ok(chalk_ir::TyData::Function(function).intern(interner)) } @@ -1822,6 +1824,7 @@ impl LowerWellKnownTrait for WellKnownTrait { Self::Copy => rust_ir::WellKnownTrait::Copy, Self::Clone => rust_ir::WellKnownTrait::Clone, Self::Drop => rust_ir::WellKnownTrait::Drop, + Self::FnOnceTrait => rust_ir::WellKnownTrait::FnOnceTrait, } } } diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index c806b7a1f1e..a1f05db2a52 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -68,6 +68,7 @@ pub enum WellKnownTrait { Copy, Clone, Drop, + FnOnceTrait, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -210,7 +211,7 @@ pub enum Ty { }, ForAll { lifetime_names: Vec, - ty: Box, + types: Vec>, }, Tuple { types: Vec>, diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 944f4733261..825bf3d7d7d 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -51,6 +51,7 @@ WellKnownTrait: WellKnownTrait = { "#" "[" "lang" "(" "copy" ")" "]" => WellKnownTrait::Copy, "#" "[" "lang" "(" "clone" ")" "]" => WellKnownTrait::Clone, "#" "[" "lang" "(" "drop" ")" "]" => WellKnownTrait::Drop, + "#" "[" "lang" "(" "fn_once" ")" "]" => WellKnownTrait::FnOnceTrait, }; StructDefn: StructDefn = { @@ -220,16 +221,16 @@ pub Ty: Ty = { }; TyWithoutId: Ty = { - "for" "<" > ">" "fn" "(" ")" => Ty::ForAll { + "for" "<" > ">" "fn" "(" > ")" => Ty::ForAll { lifetime_names: l, - ty: Box::new(t) + types: types.into_iter().map(Box::new).collect() }, => Ty::Scalar { ty: <> }, "str" => Ty::Str, "!" => Ty::Never, - "fn" "(" ")" => Ty::ForAll { + "fn" "(" > ")" => Ty::ForAll { lifetime_names: vec![], - ty: Box::new(t) + types: types.into_iter().map(Box::new).collect() }, "dyn" > "+" => Ty::Dyn { bounds: b, diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 3a6fa7f7514..d8543e9d98f 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -4,6 +4,7 @@ use chalk_ir::{Substitution, Ty}; mod clone; mod copy; +mod fn_; mod sized; /// For well known traits we have special hard-coded impls, either as an @@ -32,6 +33,9 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Sized => sized::add_sized_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), + WellKnownTrait::FnOnceTrait => { + fn_::add_fn_once_program_clauses(db, builder, &trait_ref, ty) + } // Drop impls are provided explicitly WellKnownTrait::Drop => (), } diff --git a/chalk-solve/src/clauses/builtin_traits/fn_.rs b/chalk-solve/src/clauses/builtin_traits/fn_.rs new file mode 100644 index 00000000000..ca404579aa7 --- /dev/null +++ b/chalk-solve/src/clauses/builtin_traits/fn_.rs @@ -0,0 +1,47 @@ +use crate::clauses::ClauseBuilder; +use crate::infer::instantiate::IntoBindersAndValue; +use crate::{Interner, RustIrDatabase, TraitRef}; +use chalk_ir::{ApplicationTy, Binders, Substitution, Ty, TyData, TypeName, VariableKinds}; + +pub fn add_fn_once_program_clauses( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + trait_ref: &TraitRef, + ty: &TyData, +) { + match ty { + TyData::Function(fn_val) => { + let interner = db.interner(); + let (binders, sub) = fn_val.into_binders_and_value(interner); + + // We are constructing a reference to `FnOnce`, where + // `Args` is a tuple of the function's argument types + let tupled = Ty::new( + interner, + TyData::Apply(ApplicationTy { + name: TypeName::Tuple(sub.len(interner)), + substitution: sub.clone(), + }), + ); + + let self_ty = Ty::new(interner, ty); + + let tupled_sub = Substitution::from(interner, vec![self_ty, tupled]); + // Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef` + // of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>` + let new_trait_ref = TraitRef { + trait_id: trait_ref.trait_id, + substitution: tupled_sub, + }; + + // Functions types come with a binder, which we push so + // that the `TraitRef` properly references any bound lifetimes + // (e.g. `for<'a> fn(&'a u8): FnOnce<(&'b u8)>`) + let bound_ref = Binders::new(VariableKinds::from(interner, binders), new_trait_ref); + builder.push_binders(&bound_ref, |this, inner_trait| { + this.push_fact(inner_trait); + }) + } + _ => {} + } +} diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index d0c57919e52..a04df392471 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -216,6 +216,10 @@ pub enum WellKnownTrait { Copy, Clone, Drop, + /// The trait `FnOnce` - the generic argument `Args` is always a tuple + /// corresponding to the arguments of a function implementing this trait. + /// E.g. `fn(u8, bool): FnOnce<(u8, bool)>` + FnOnceTrait, } impl TraitDatum { diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index a6a97d32a4b..45c2691d6bc 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -471,7 +471,7 @@ impl WfWellKnownGoals { ) -> Option> { match db.trait_datum(trait_ref.trait_id).well_known? { WellKnownTrait::Copy => Self::copy_impl_constraint(db, trait_ref), - WellKnownTrait::Drop | WellKnownTrait::Clone | WellKnownTrait::Sized => None, + WellKnownTrait::Drop | WellKnownTrait::Clone | WellKnownTrait::Sized | WellKnownTrait::FnOnceTrait => None, } } @@ -486,7 +486,7 @@ impl WfWellKnownGoals { match db.trait_datum(impl_datum.trait_id()).well_known? { // You can't add a manual implementation of Sized - WellKnownTrait::Sized => Some(GoalData::CannotProve(()).intern(interner)), + WellKnownTrait::Sized | WellKnownTrait::FnOnceTrait => Some(GoalData::CannotProve(()).intern(interner)), WellKnownTrait::Drop => Self::drop_impl_constraint(db, impl_datum), WellKnownTrait::Copy | WellKnownTrait::Clone => None, } diff --git a/tests/test/functions.rs b/tests/test/functions.rs index 08eab42e6e7..11e7e49d502 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -1,5 +1,65 @@ use super::*; +#[test] +fn function_implement_fn_once() { + test! { + program { + #[lang(fn_once)] + trait FnOnce { } + } + + goal { + fn(u8): FnOnce<(u8,)> + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + fn(u8, u32): FnOnce<(u8,u32)> + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + fn(i32): FnOnce<(bool,)> + } yields { + "No possible solution" + } + + goal { + forall<'a> { + for<'b> fn(&'b u8): FnOnce<(&'a u8,)> + } + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + forall<'a, 'b> { + for<'c> fn(&'c u8, &'c i32): FnOnce<(&'a u8, &'b i32)> + } + } yields { + "Unique; substitution [], lifetime constraints [InEnvironment { environment: Env([]), goal: '!1_0: '!1_1 }, InEnvironment { environment: Env([]), goal: '!1_1: '!1_0 }]" + } + + goal { + forall { + fn(T, T): FnOnce<(T, U)> + } + } yields { + "No possible solution" + } + + goal { + forall { + fn(T, U): FnOnce<(T, T)> + } + } yields { + "No possible solution" + } + } +} + #[test] fn functions_are_sized() { test! { From fc59d01db0ca9d180f30f7bdeb39e92b69da60ae Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 03:28:24 -0400 Subject: [PATCH 02/14] Add FnMut and Fn traits --- chalk-integration/src/lowering.rs | 2 ++ chalk-parse/src/ast.rs | 2 ++ chalk-parse/src/parser.lalrpop | 2 ++ chalk-solve/src/clauses/builtin_traits.rs | 4 ++-- chalk-solve/src/clauses/builtin_traits/fn_.rs | 3 ++- chalk-solve/src/rust_ir.rs | 2 ++ chalk-solve/src/wf.rs | 12 ++++++++++-- tests/test/functions.rs | 18 ++++++++++++++++++ 8 files changed, 40 insertions(+), 5 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 1c141246b70..92f10a549a2 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1825,6 +1825,8 @@ impl LowerWellKnownTrait for WellKnownTrait { Self::Clone => rust_ir::WellKnownTrait::Clone, Self::Drop => rust_ir::WellKnownTrait::Drop, Self::FnOnceTrait => rust_ir::WellKnownTrait::FnOnceTrait, + Self::FnMutTrait => rust_ir::WellKnownTrait::FnMutTrait, + Self::FnTrait => rust_ir::WellKnownTrait::FnTrait, } } } diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index a1f05db2a52..5ce7be6f619 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -69,6 +69,8 @@ pub enum WellKnownTrait { Clone, Drop, FnOnceTrait, + FnMutTrait, + FnTrait, } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 825bf3d7d7d..e2ed5b36336 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -52,6 +52,8 @@ WellKnownTrait: WellKnownTrait = { "#" "[" "lang" "(" "clone" ")" "]" => WellKnownTrait::Clone, "#" "[" "lang" "(" "drop" ")" "]" => WellKnownTrait::Drop, "#" "[" "lang" "(" "fn_once" ")" "]" => WellKnownTrait::FnOnceTrait, + "#" "[" "lang" "(" "fn_mut" ")" "]" => WellKnownTrait::FnMutTrait, + "#" "[" "lang" "(" "fn" ")" "]" => WellKnownTrait::FnTrait, }; StructDefn: StructDefn = { diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index d8543e9d98f..7336eb5b597 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -33,8 +33,8 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Sized => sized::add_sized_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), - WellKnownTrait::FnOnceTrait => { - fn_::add_fn_once_program_clauses(db, builder, &trait_ref, ty) + WellKnownTrait::FnOnceTrait | WellKnownTrait::FnMutTrait | WellKnownTrait::FnTrait => { + fn_::add_fn_trait_program_clauses(db, builder, &trait_ref, ty) } // Drop impls are provided explicitly WellKnownTrait::Drop => (), diff --git a/chalk-solve/src/clauses/builtin_traits/fn_.rs b/chalk-solve/src/clauses/builtin_traits/fn_.rs index ca404579aa7..222f4f8f5ba 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_.rs @@ -3,7 +3,8 @@ use crate::infer::instantiate::IntoBindersAndValue; use crate::{Interner, RustIrDatabase, TraitRef}; use chalk_ir::{ApplicationTy, Binders, Substitution, Ty, TyData, TypeName, VariableKinds}; -pub fn add_fn_once_program_clauses( +// Handles clauses for FnOnce/FnMut/Fn +pub fn add_fn_trait_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, trait_ref: &TraitRef, diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index a04df392471..4264029d6c9 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -220,6 +220,8 @@ pub enum WellKnownTrait { /// corresponding to the arguments of a function implementing this trait. /// E.g. `fn(u8, bool): FnOnce<(u8, bool)>` FnOnceTrait, + FnMutTrait, + FnTrait, } impl TraitDatum { diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index 45c2691d6bc..3668d6aca3e 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -471,7 +471,12 @@ impl WfWellKnownGoals { ) -> Option> { match db.trait_datum(trait_ref.trait_id).well_known? { WellKnownTrait::Copy => Self::copy_impl_constraint(db, trait_ref), - WellKnownTrait::Drop | WellKnownTrait::Clone | WellKnownTrait::Sized | WellKnownTrait::FnOnceTrait => None, + WellKnownTrait::Drop + | WellKnownTrait::Clone + | WellKnownTrait::Sized + | WellKnownTrait::FnOnceTrait + | WellKnownTrait::FnMutTrait + | WellKnownTrait::FnTrait => None, } } @@ -486,7 +491,10 @@ impl WfWellKnownGoals { match db.trait_datum(impl_datum.trait_id()).well_known? { // You can't add a manual implementation of Sized - WellKnownTrait::Sized | WellKnownTrait::FnOnceTrait => Some(GoalData::CannotProve(()).intern(interner)), + WellKnownTrait::Sized + | WellKnownTrait::FnOnceTrait + | WellKnownTrait::FnMutTrait + | WellKnownTrait::FnTrait => Some(GoalData::CannotProve(()).intern(interner)), WellKnownTrait::Drop => Self::drop_impl_constraint(db, impl_datum), WellKnownTrait::Copy | WellKnownTrait::Clone => None, } diff --git a/tests/test/functions.rs b/tests/test/functions.rs index 11e7e49d502..66bac5aac73 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -6,6 +6,12 @@ fn function_implement_fn_once() { program { #[lang(fn_once)] trait FnOnce { } + + #[lang(fn_mut)] + trait FnMut where Self: FnOnce { } + + #[lang(fn)] + trait Fn where Self: FnMut { } } goal { @@ -14,6 +20,18 @@ fn function_implement_fn_once() { "Unique; substitution [], lifetime constraints []" } + goal { + fn(u8): FnMut<(u8,)> + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + fn(u8): Fn<(u8,)> + } yields { + "Unique; substitution [], lifetime constraints []" + } + goal { fn(u8, u32): FnOnce<(u8,u32)> } yields { From cfb1d6687d0b84bbc675e34b28a5317cde79cfca Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 03:50:20 -0400 Subject: [PATCH 03/14] Handle `FnOnce::Output` --- chalk-parse/src/parser.lalrpop | 14 +++-- chalk-solve/src/clauses.rs | 6 ++ chalk-solve/src/clauses/builtin_traits.rs | 27 +++++++- chalk-solve/src/clauses/builtin_traits/fn_.rs | 63 ++++++++++++++++--- tests/test/functions.rs | 42 ++++++++++++- 5 files changed, 135 insertions(+), 17 deletions(-) diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index e2ed5b36336..92a091955d4 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -223,16 +223,22 @@ pub Ty: Ty = { }; TyWithoutId: Ty = { - "for" "<" > ">" "fn" "(" > ")" => Ty::ForAll { + "for" "<" > ">" "fn" "(" > ")" => Ty::ForAll { lifetime_names: l, - types: types.into_iter().map(Box::new).collect() + types: types + .into_iter() + .chain(std::iter::once(ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() }))) + .map(Box::new).collect() }, => Ty::Scalar { ty: <> }, "str" => Ty::Str, "!" => Ty::Never, - "fn" "(" > ")" => Ty::ForAll { + "fn" "(" > ")" => Ty::ForAll { lifetime_names: vec![], - types: types.into_iter().map(Box::new).collect() + types: types + .into_iter() + .chain(std::iter::once(ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() }))) + .map(Box::new).collect() }, "dyn" > "+" => Ty::Dyn { bounds: b, diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index 7cc4d2b0046..e7d07f890b6 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -367,6 +367,12 @@ fn program_clauses_that_could_match( return Err(Floundered); } + if let Some(well_known) = trait_datum.well_known { + builtin_traits::add_builtin_assoc_program_clauses( + db, builder, well_known, proj, + ); + } + push_program_clauses_for_associated_type_values_in_impls_of( builder, trait_id, diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 7336eb5b597..6fdbdbc6a42 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,6 +1,6 @@ use super::{builder::ClauseBuilder, generalize}; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{Substitution, Ty}; +use chalk_ir::{ProjectionTy, Substitution, Ty}; mod clone; mod copy; @@ -34,7 +34,7 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::FnOnceTrait | WellKnownTrait::FnMutTrait | WellKnownTrait::FnTrait => { - fn_::add_fn_trait_program_clauses(db, builder, &trait_ref, ty) + fn_::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, ty, false) } // Drop impls are provided explicitly WellKnownTrait::Drop => (), @@ -42,6 +42,29 @@ pub fn add_builtin_program_clauses( }); } +/// Like `add_builtin_program_clauses`, but for `DomainGoal::Normalize` involving +/// a projection (e.g. `>::Output`) +pub fn add_builtin_assoc_program_clauses( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + well_known: WellKnownTrait, + proj: &ProjectionTy, +) { + match well_known { + WellKnownTrait::FnOnceTrait => { + let interner = db.interner(); + let self_ty = proj + .substitution + .at(interner, 0) + .assert_ty_ref(interner) + .data(interner); + let trait_id = db.well_known_trait_id(well_known).unwrap(); + fn_::add_fn_trait_program_clauses(db, builder, trait_id, self_ty, true); + } + _ => {} + } +} + /// Given a trait ref `T0: Trait` and a list of types `U0..Un`, pushes a clause of the form /// `Implemented(T0: Trait) :- Implemented(U0: Trait) .. Implemented(Un: Trait)` pub fn needs_impl_for_tys( diff --git a/chalk-solve/src/clauses/builtin_traits/fn_.rs b/chalk-solve/src/clauses/builtin_traits/fn_.rs index 222f4f8f5ba..c58db40ea3f 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_.rs @@ -1,27 +1,46 @@ use crate::clauses::ClauseBuilder; use crate::infer::instantiate::IntoBindersAndValue; +use crate::rust_ir::WellKnownTrait; use crate::{Interner, RustIrDatabase, TraitRef}; -use chalk_ir::{ApplicationTy, Binders, Substitution, Ty, TyData, TypeName, VariableKinds}; +use chalk_ir::{ + AliasTy, ApplicationTy, Binders, Normalize, ProjectionTy, Substitution, TraitId, Ty, TyData, + TypeName, VariableKinds, +}; -// Handles clauses for FnOnce/FnMut/Fn +/// Handles clauses for FnOnce/FnMut/Fn. +/// If `assoc_output` is `true`, we push a clause of the form +/// `Normalize( B as FnOnce<(A,)>>::Output -> B) :- Implemented(fn(A) -> B as FnOnce<(A,)>` +/// +/// If `assoc_output` is `false`, we push a clause of the form +/// `Implemented(fn(A) -> B as FnOnce<(A,)>)` pub fn add_fn_trait_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, - trait_ref: &TraitRef, + trait_id: TraitId, ty: &TyData, + assoc_output: bool, ) { match ty { TyData::Function(fn_val) => { let interner = db.interner(); - let (binders, sub) = fn_val.into_binders_and_value(interner); + let (binders, orig_sub) = fn_val.into_binders_and_value(interner); + // Take all of the arguments except for the last one, which + // represents the return type + let arg_sub = Substitution::from( + interner, + orig_sub.iter(interner).take(orig_sub.len(interner) - 1), + ); + let fn_output_ty = orig_sub + .at(interner, orig_sub.len(interner) - 1) + .assert_ty_ref(interner); // We are constructing a reference to `FnOnce`, where // `Args` is a tuple of the function's argument types let tupled = Ty::new( interner, TyData::Apply(ApplicationTy { - name: TypeName::Tuple(sub.len(interner)), - substitution: sub.clone(), + name: TypeName::Tuple(arg_sub.len(interner)), + substitution: arg_sub.clone(), }), ); @@ -31,8 +50,8 @@ pub fn add_fn_trait_program_clauses( // Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef` // of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>` let new_trait_ref = TraitRef { - trait_id: trait_ref.trait_id, - substitution: tupled_sub, + trait_id, + substitution: tupled_sub.clone(), }; // Functions types come with a binder, which we push so @@ -40,7 +59,33 @@ pub fn add_fn_trait_program_clauses( // (e.g. `for<'a> fn(&'a u8): FnOnce<(&'b u8)>`) let bound_ref = Binders::new(VariableKinds::from(interner, binders), new_trait_ref); builder.push_binders(&bound_ref, |this, inner_trait| { - this.push_fact(inner_trait); + if assoc_output { + //The `Output` type is defined on the `FnOnceTrait` + let fn_once = db.trait_datum(trait_id); + assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnceTrait)); + let assoc_types = &fn_once.associated_ty_ids; + if assoc_types.len() != 1 { + panic!( + "FnOnce trait should have exactly one associated type, found {:?}", + assoc_types + ); + } + + // Construct `Normalize( B as FnOnce<(A,)>>::Output -> B)` + let assoc_output_ty = assoc_types[0]; + let proj_ty = ProjectionTy { + associated_ty_id: assoc_output_ty, + substitution: tupled_sub, + }; + let normalize = Normalize { + alias: AliasTy::Projection(proj_ty), + ty: fn_output_ty.clone(), + }; + + this.push_clause(normalize, std::iter::once(inner_trait)); + } else { + this.push_fact(inner_trait); + } }) } _ => {} diff --git a/tests/test/functions.rs b/tests/test/functions.rs index 66bac5aac73..556d0c0516c 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -1,11 +1,13 @@ use super::*; #[test] -fn function_implement_fn_once() { +fn function_implement_fn_traits() { test! { program { #[lang(fn_once)] - trait FnOnce { } + trait FnOnce { + type Output; + } #[lang(fn_mut)] trait FnMut where Self: FnOnce { } @@ -32,6 +34,42 @@ fn function_implement_fn_once() { "Unique; substitution [], lifetime constraints []" } + goal { + Normalize(>::Output -> ()) + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + Normalize( bool as FnOnce<(u8,)>>::Output -> bool) + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + Normalize( bool as FnOnce<(u8,)>>::Output -> u8) + } yields { + "No possible solution" + } + + goal { + forall { + Normalize( T as FnOnce<(u8, V)>>::Output -> V) + } + } yields { + "No possible solution" + } + + goal { + forall { + exists { + Normalize( T as FnOnce<(u8, V)>>::Output -> U) + } + } + } yields { + "Unique; substitution [?0 := !1_0], lifetime constraints []" + } + goal { fn(u8, u32): FnOnce<(u8,u32)> } yields { From dbdcbdd3b50f968211f4b1d1de1b241d026b34e8 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 17:21:00 -0400 Subject: [PATCH 04/14] Remove 'Trait' suffix on function traits --- chalk-integration/src/lowering.rs | 6 +++--- chalk-parse/src/ast.rs | 6 +++--- chalk-parse/src/parser.lalrpop | 6 +++--- chalk-solve/src/clauses/builtin_traits.rs | 4 ++-- chalk-solve/src/clauses/builtin_traits/fn_.rs | 4 ++-- chalk-solve/src/rust_ir.rs | 6 +++--- chalk-solve/src/wf.rs | 12 ++++++------ 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 92f10a549a2..f4c81f51eaa 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1824,9 +1824,9 @@ impl LowerWellKnownTrait for WellKnownTrait { Self::Copy => rust_ir::WellKnownTrait::Copy, Self::Clone => rust_ir::WellKnownTrait::Clone, Self::Drop => rust_ir::WellKnownTrait::Drop, - Self::FnOnceTrait => rust_ir::WellKnownTrait::FnOnceTrait, - Self::FnMutTrait => rust_ir::WellKnownTrait::FnMutTrait, - Self::FnTrait => rust_ir::WellKnownTrait::FnTrait, + Self::FnOnce => rust_ir::WellKnownTrait::FnOnce, + Self::FnMut => rust_ir::WellKnownTrait::FnMut, + Self::Fn => rust_ir::WellKnownTrait::Fn, } } } diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index 5ce7be6f619..62ec229cad7 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -68,9 +68,9 @@ pub enum WellKnownTrait { Copy, Clone, Drop, - FnOnceTrait, - FnMutTrait, - FnTrait, + FnOnce, + FnMut, + Fn, } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 92a091955d4..6e7756b72a5 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -51,9 +51,9 @@ WellKnownTrait: WellKnownTrait = { "#" "[" "lang" "(" "copy" ")" "]" => WellKnownTrait::Copy, "#" "[" "lang" "(" "clone" ")" "]" => WellKnownTrait::Clone, "#" "[" "lang" "(" "drop" ")" "]" => WellKnownTrait::Drop, - "#" "[" "lang" "(" "fn_once" ")" "]" => WellKnownTrait::FnOnceTrait, - "#" "[" "lang" "(" "fn_mut" ")" "]" => WellKnownTrait::FnMutTrait, - "#" "[" "lang" "(" "fn" ")" "]" => WellKnownTrait::FnTrait, + "#" "[" "lang" "(" "fn_once" ")" "]" => WellKnownTrait::FnOnce, + "#" "[" "lang" "(" "fn_mut" ")" "]" => WellKnownTrait::FnMut, + "#" "[" "lang" "(" "fn" ")" "]" => WellKnownTrait::Fn, }; StructDefn: StructDefn = { diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 6fdbdbc6a42..9e5bc0ae78e 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -33,7 +33,7 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Sized => sized::add_sized_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), - WellKnownTrait::FnOnceTrait | WellKnownTrait::FnMutTrait | WellKnownTrait::FnTrait => { + WellKnownTrait::FnOnce | WellKnownTrait::FnMut | WellKnownTrait::Fn => { fn_::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, ty, false) } // Drop impls are provided explicitly @@ -51,7 +51,7 @@ pub fn add_builtin_assoc_program_clauses( proj: &ProjectionTy, ) { match well_known { - WellKnownTrait::FnOnceTrait => { + WellKnownTrait::FnOnce => { let interner = db.interner(); let self_ty = proj .substitution diff --git a/chalk-solve/src/clauses/builtin_traits/fn_.rs b/chalk-solve/src/clauses/builtin_traits/fn_.rs index c58db40ea3f..e0f1ad204fe 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_.rs @@ -60,9 +60,9 @@ pub fn add_fn_trait_program_clauses( let bound_ref = Binders::new(VariableKinds::from(interner, binders), new_trait_ref); builder.push_binders(&bound_ref, |this, inner_trait| { if assoc_output { - //The `Output` type is defined on the `FnOnceTrait` + //The `Output` type is defined on the `FnOnce` let fn_once = db.trait_datum(trait_id); - assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnceTrait)); + assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnce)); let assoc_types = &fn_once.associated_ty_ids; if assoc_types.len() != 1 { panic!( diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index 4264029d6c9..63865a6f499 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -219,9 +219,9 @@ pub enum WellKnownTrait { /// The trait `FnOnce` - the generic argument `Args` is always a tuple /// corresponding to the arguments of a function implementing this trait. /// E.g. `fn(u8, bool): FnOnce<(u8, bool)>` - FnOnceTrait, - FnMutTrait, - FnTrait, + FnOnce, + FnMut, + Fn, } impl TraitDatum { diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index 3668d6aca3e..a28b82afb84 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -474,9 +474,9 @@ impl WfWellKnownGoals { WellKnownTrait::Drop | WellKnownTrait::Clone | WellKnownTrait::Sized - | WellKnownTrait::FnOnceTrait - | WellKnownTrait::FnMutTrait - | WellKnownTrait::FnTrait => None, + | WellKnownTrait::FnOnce + | WellKnownTrait::FnMut + | WellKnownTrait::Fn => None, } } @@ -492,9 +492,9 @@ impl WfWellKnownGoals { match db.trait_datum(impl_datum.trait_id()).well_known? { // You can't add a manual implementation of Sized WellKnownTrait::Sized - | WellKnownTrait::FnOnceTrait - | WellKnownTrait::FnMutTrait - | WellKnownTrait::FnTrait => Some(GoalData::CannotProve(()).intern(interner)), + | WellKnownTrait::FnOnce + | WellKnownTrait::FnMut + | WellKnownTrait::Fn => Some(GoalData::CannotProve(()).intern(interner)), WellKnownTrait::Drop => Self::drop_impl_constraint(db, impl_datum), WellKnownTrait::Copy | WellKnownTrait::Clone => None, } From 870958b17b0385438845060971d01f50296de99d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 20:49:20 -0400 Subject: [PATCH 05/14] Some cleanup --- chalk-solve/src/clauses/builtin_traits.rs | 14 +++++--------- .../builtin_traits/{fn_.rs => fn_family.rs} | 9 ++++----- 2 files changed, 9 insertions(+), 14 deletions(-) rename chalk-solve/src/clauses/builtin_traits/{fn_.rs => fn_family.rs} (95%) diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 9e5bc0ae78e..465753294a4 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,10 +1,10 @@ use super::{builder::ClauseBuilder, generalize}; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{ProjectionTy, Substitution, Ty}; +use chalk_ir::{ProjectionTy, Substitution, Ty, AliasTy}; mod clone; mod copy; -mod fn_; +mod fn_family; mod sized; /// For well known traits we have special hard-coded impls, either as an @@ -34,7 +34,7 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::FnOnce | WellKnownTrait::FnMut | WellKnownTrait::Fn => { - fn_::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, ty, false) + fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, ty) } // Drop impls are provided explicitly WellKnownTrait::Drop => (), @@ -53,13 +53,9 @@ pub fn add_builtin_assoc_program_clauses( match well_known { WellKnownTrait::FnOnce => { let interner = db.interner(); - let self_ty = proj - .substitution - .at(interner, 0) - .assert_ty_ref(interner) - .data(interner); + let self_ty = AliasTy::Projection(proj.clone()).self_type_parameter(interner); let trait_id = db.well_known_trait_id(well_known).unwrap(); - fn_::add_fn_trait_program_clauses(db, builder, trait_id, self_ty, true); + fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty.data(interner)); } _ => {} } diff --git a/chalk-solve/src/clauses/builtin_traits/fn_.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs similarity index 95% rename from chalk-solve/src/clauses/builtin_traits/fn_.rs rename to chalk-solve/src/clauses/builtin_traits/fn_family.rs index e0f1ad204fe..f6c5141b6b0 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -18,7 +18,6 @@ pub fn add_fn_trait_program_clauses( builder: &mut ClauseBuilder<'_, I>, trait_id: TraitId, ty: &TyData, - assoc_output: bool, ) { match ty { TyData::Function(fn_val) => { @@ -59,7 +58,9 @@ pub fn add_fn_trait_program_clauses( // (e.g. `for<'a> fn(&'a u8): FnOnce<(&'b u8)>`) let bound_ref = Binders::new(VariableKinds::from(interner, binders), new_trait_ref); builder.push_binders(&bound_ref, |this, inner_trait| { - if assoc_output { + this.push_fact(inner_trait.clone()); + + if let Some(WellKnownTrait::FnOnce) = db.trait_datum(trait_id).well_known { //The `Output` type is defined on the `FnOnce` let fn_once = db.trait_datum(trait_id); assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnce)); @@ -83,10 +84,8 @@ pub fn add_fn_trait_program_clauses( }; this.push_clause(normalize, std::iter::once(inner_trait)); - } else { - this.push_fact(inner_trait); } - }) + }); } _ => {} } From 157538511c90bf40ac5781f1ef5ab38ecba08e62 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 20:52:52 -0400 Subject: [PATCH 06/14] More cleanup --- chalk-solve/src/clauses/builtin_traits.rs | 4 ++-- chalk-solve/src/clauses/builtin_traits/fn_family.rs | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 465753294a4..b486f689552 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -34,7 +34,7 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::FnOnce | WellKnownTrait::FnMut | WellKnownTrait::Fn => { - fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, ty) + fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, self_ty) } // Drop impls are provided explicitly WellKnownTrait::Drop => (), @@ -55,7 +55,7 @@ pub fn add_builtin_assoc_program_clauses( let interner = db.interner(); let self_ty = AliasTy::Projection(proj.clone()).self_type_parameter(interner); let trait_id = db.well_known_trait_id(well_known).unwrap(); - fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty.data(interner)); + fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty); } _ => {} } diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index f6c5141b6b0..14ad28c962a 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -17,11 +17,11 @@ pub fn add_fn_trait_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, trait_id: TraitId, - ty: &TyData, + self_ty: Ty, ) { - match ty { + let interner = db.interner(); + match self_ty.data(interner) { TyData::Function(fn_val) => { - let interner = db.interner(); let (binders, orig_sub) = fn_val.into_binders_and_value(interner); // Take all of the arguments except for the last one, which // represents the return type @@ -43,9 +43,7 @@ pub fn add_fn_trait_program_clauses( }), ); - let self_ty = Ty::new(interner, ty); - - let tupled_sub = Substitution::from(interner, vec![self_ty, tupled]); + let tupled_sub = Substitution::from(interner, vec![self_ty.clone(), tupled]); // Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef` // of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>` let new_trait_ref = TraitRef { From 94b6272c6ba9d2ad52404232de5ddc96491e43af Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 20:55:28 -0400 Subject: [PATCH 07/14] Simplify some logic --- chalk-solve/src/clauses/builtin_traits.rs | 2 +- .../src/clauses/builtin_traits/fn_family.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index b486f689552..b129777b87e 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,6 +1,6 @@ use super::{builder::ClauseBuilder, generalize}; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{ProjectionTy, Substitution, Ty, AliasTy}; +use chalk_ir::{AliasTy, ProjectionTy, Substitution, Ty}; mod clone; mod copy; diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 14ad28c962a..08b7879543f 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -63,12 +63,12 @@ pub fn add_fn_trait_program_clauses( let fn_once = db.trait_datum(trait_id); assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnce)); let assoc_types = &fn_once.associated_ty_ids; - if assoc_types.len() != 1 { - panic!( - "FnOnce trait should have exactly one associated type, found {:?}", - assoc_types - ); - } + assert_eq!( + assoc_types.len(), + 1, + "FnOnce trait should have exactly one associated type, found {:?}", + assoc_types + ); // Construct `Normalize( B as FnOnce<(A,)>>::Output -> B)` let assoc_output_ty = assoc_types[0]; @@ -81,7 +81,7 @@ pub fn add_fn_trait_program_clauses( ty: fn_output_ty.clone(), }; - this.push_clause(normalize, std::iter::once(inner_trait)); + this.push_fact(normalize); } }); } From af543711ff501cc6de04e2ff4648576bfd148a06 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 20:56:59 -0400 Subject: [PATCH 08/14] Update book for FnOnce/FnMut/Fn --- book/src/clauses/well_known_traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/clauses/well_known_traits.md b/book/src/clauses/well_known_traits.md index c2751957cc6..d32b2d2434d 100644 --- a/book/src/clauses/well_known_traits.md +++ b/book/src/clauses/well_known_traits.md @@ -28,7 +28,7 @@ Some common examples of auto traits are `Send` and `Sync`. [coinductive_section]: ../engine/logic/coinduction.html#coinduction-and-refinement-strands # Current state -| Type | Copy | Clone | Sized | Unsize | Drop | FnOnce | Unpin | Generator | auto traits | +| Type | Copy | Clone | Sized | Unsize | Drop | FnOnce/FnMut/Fn | Unpin | Generator | auto traits | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | tuple types | ✅ | ✅ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | | structs | ⚬ | ⚬ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | From c887f52c28c4afd35d5224c399dae526a7db8ae3 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 21:01:17 -0400 Subject: [PATCH 09/14] Clean up argument handling code --- .../src/clauses/builtin_traits/fn_family.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 08b7879543f..1497dd009d6 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -23,15 +23,12 @@ pub fn add_fn_trait_program_clauses( match self_ty.data(interner) { TyData::Function(fn_val) => { let (binders, orig_sub) = fn_val.into_binders_and_value(interner); - // Take all of the arguments except for the last one, which - // represents the return type - let arg_sub = Substitution::from( - interner, - orig_sub.iter(interner).take(orig_sub.len(interner) - 1), - ); - let fn_output_ty = orig_sub - .at(interner, orig_sub.len(interner) - 1) - .assert_ty_ref(interner); + let all_params: Vec<_> = orig_sub.iter(interner).cloned().collect(); + + // The last parameter represents the function return type + let (arg_sub, fn_output_ty) = all_params.split_at(all_params.len() - 1); + let arg_sub = Substitution::from(interner, arg_sub); + let fn_output_ty = fn_output_ty[0].assert_ty_ref(interner); // We are constructing a reference to `FnOnce`, where // `Args` is a tuple of the function's argument types From ef9f6b617330340d12bdf9c034243b6d744973d6 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 21:04:23 -0400 Subject: [PATCH 10/14] Push binders at the start --- .../src/clauses/builtin_traits/fn_family.rs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 1497dd009d6..13a995afe0b 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -23,37 +23,35 @@ pub fn add_fn_trait_program_clauses( match self_ty.data(interner) { TyData::Function(fn_val) => { let (binders, orig_sub) = fn_val.into_binders_and_value(interner); - let all_params: Vec<_> = orig_sub.iter(interner).cloned().collect(); + let bound_ref = Binders::new(VariableKinds::from(interner, binders), orig_sub); + builder.push_binders(&bound_ref, |builder, orig_sub| { - // The last parameter represents the function return type - let (arg_sub, fn_output_ty) = all_params.split_at(all_params.len() - 1); - let arg_sub = Substitution::from(interner, arg_sub); - let fn_output_ty = fn_output_ty[0].assert_ty_ref(interner); + let all_params: Vec<_> = orig_sub.iter(interner).cloned().collect(); - // We are constructing a reference to `FnOnce`, where - // `Args` is a tuple of the function's argument types - let tupled = Ty::new( - interner, - TyData::Apply(ApplicationTy { - name: TypeName::Tuple(arg_sub.len(interner)), - substitution: arg_sub.clone(), - }), - ); + // The last parameter represents the function return type + let (arg_sub, fn_output_ty) = all_params.split_at(all_params.len() - 1); + let arg_sub = Substitution::from(interner, arg_sub); + let fn_output_ty = fn_output_ty[0].assert_ty_ref(interner); - let tupled_sub = Substitution::from(interner, vec![self_ty.clone(), tupled]); - // Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef` - // of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>` - let new_trait_ref = TraitRef { - trait_id, - substitution: tupled_sub.clone(), - }; + // We are constructing a reference to `FnOnce`, where + // `Args` is a tuple of the function's argument types + let tupled = Ty::new( + interner, + TyData::Apply(ApplicationTy { + name: TypeName::Tuple(arg_sub.len(interner)), + substitution: arg_sub.clone(), + }), + ); - // Functions types come with a binder, which we push so - // that the `TraitRef` properly references any bound lifetimes - // (e.g. `for<'a> fn(&'a u8): FnOnce<(&'b u8)>`) - let bound_ref = Binders::new(VariableKinds::from(interner, binders), new_trait_ref); - builder.push_binders(&bound_ref, |this, inner_trait| { - this.push_fact(inner_trait.clone()); + let tupled_sub = Substitution::from(interner, vec![self_ty.clone(), tupled]); + // Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef` + // of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>` + let new_trait_ref = TraitRef { + trait_id, + substitution: tupled_sub.clone(), + }; + + builder.push_fact(new_trait_ref.clone()); if let Some(WellKnownTrait::FnOnce) = db.trait_datum(trait_id).well_known { //The `Output` type is defined on the `FnOnce` @@ -78,7 +76,7 @@ pub fn add_fn_trait_program_clauses( ty: fn_output_ty.clone(), }; - this.push_fact(normalize); + builder.push_fact(normalize); } }); } From f4219dec6c3d0d90b23c0ae116eeb6a6517bf8e4 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 21:04:56 -0400 Subject: [PATCH 11/14] Run fmt --- chalk-solve/src/clauses/builtin_traits/fn_family.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 13a995afe0b..3b06be24eb9 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -25,7 +25,6 @@ pub fn add_fn_trait_program_clauses( let (binders, orig_sub) = fn_val.into_binders_and_value(interner); let bound_ref = Binders::new(VariableKinds::from(interner, binders), orig_sub); builder.push_binders(&bound_ref, |builder, orig_sub| { - let all_params: Vec<_> = orig_sub.iter(interner).cloned().collect(); // The last parameter represents the function return type From 13b5c36d33ed84d6977e44a7f8d35e8be6ee1431 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Jun 2020 21:33:54 -0400 Subject: [PATCH 12/14] Add comments to tests --- tests/test/functions.rs | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/test/functions.rs b/tests/test/functions.rs index 556d0c0516c..ec8e7eebdd9 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -16,42 +16,54 @@ fn function_implement_fn_traits() { trait Fn where Self: FnMut { } } + // Simple test: make sure a fully monomorphic type implements FnOnce goal { fn(u8): FnOnce<(u8,)> } yields { "Unique; substitution [], lifetime constraints []" } + // Same as above, but for FnMut goal { fn(u8): FnMut<(u8,)> } yields { "Unique; substitution [], lifetime constraints []" } + // Same as above, but for Fn goal { fn(u8): Fn<(u8,)> } yields { "Unique; substitution [], lifetime constraints []" } + // Function pointres implicity return `()` when no return + // type is specified - make sure that normalization understands + // this goal { Normalize(>::Output -> ()) } yields { "Unique; substitution [], lifetime constraints []" } + // Tests normalizing when an explicit return type is used goal { Normalize( bool as FnOnce<(u8,)>>::Output -> bool) } yields { "Unique; substitution [], lifetime constraints []" } + // Tests that we fail to normalize when there's a mismatch with + // fully monomorphic types. goal { Normalize( bool as FnOnce<(u8,)>>::Output -> u8) } yields { "No possible solution" } + // Ensures that we don't find a solution when doing so would + // require us to conclude that two different universally quantified + // types (T and V) are equal. goal { forall { Normalize( T as FnOnce<(u8, V)>>::Output -> V) @@ -60,6 +72,7 @@ fn function_implement_fn_traits() { "No possible solution" } + // Tests that we can normalize a generic function pointer type goal { forall { exists { @@ -70,18 +83,25 @@ fn function_implement_fn_traits() { "Unique; substitution [?0 := !1_0], lifetime constraints []" } + // Tests that we properly tuple function arguments when constrcting + // the `FnOnce` impl goal { fn(u8, u32): FnOnce<(u8,u32)> } yields { "Unique; substitution [], lifetime constraints []" } + // Tests that we don't find a solution when fully monomorphic + // types are mismatched goal { fn(i32): FnOnce<(bool,)> } yields { "No possible solution" } + // Tests function pointer types that use the function's binder + // Universally quantified lifetimes that differ only in their + // name ('a vs 'b) should be considered equivalent here goal { forall<'a> { for<'b> fn(&'b u8): FnOnce<(&'a u8,)> @@ -90,6 +110,10 @@ fn function_implement_fn_traits() { "Unique; substitution [], lifetime constraints []" } + // Tests that a 'stricter' function (requires lifetimes to be the same) + // can implement `FnOnce` for a 'less strict' signature (dose not require + // lifetimes to be the same), provided that the lifetimes are *actually* + // the same. goal { forall<'a, 'b> { for<'c> fn(&'c u8, &'c i32): FnOnce<(&'a u8, &'b i32)> @@ -98,6 +122,22 @@ fn function_implement_fn_traits() { "Unique; substitution [], lifetime constraints [InEnvironment { environment: Env([]), goal: '!1_0: '!1_1 }, InEnvironment { environment: Env([]), goal: '!1_1: '!1_0 }]" } + // Tests the opposite case as the previous test: a 'less strict' function + // (does not require lifetimes to be the same) can implement `FnOnce` for + // a 'stricter' signature (requires lifetimes to be the same) without + // any additional requirements + goal { + forall<'a> { + for<'b, 'c> fn(&'b u8, &'c i32): FnOnce<(&'a u8, &'a i32)> + } + } yields { + "Unique; substitution [], lifetime constraints []" + } + + // Similiar to the above test, but for types instead of lifetimes: + // a 'stricter' function (requires types to be the same) can never + // implement `FnOnce` for a 'less strict' signature (does not require + // types to be the same) goal { forall { fn(T, T): FnOnce<(T, U)> @@ -106,6 +146,9 @@ fn function_implement_fn_traits() { "No possible solution" } + // Tests the opposite case as a previous test: a 'less strict' + // function can never implement 'FnOnce' for a 'more strict' signature + // (does not require types to bthe same) goal { forall { fn(T, U): FnOnce<(T, T)> From f3763f3744664cd718ea461f76870cf3d6a1de7d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Jun 2020 00:05:39 -0400 Subject: [PATCH 13/14] Address review comments --- chalk-solve/src/clauses.rs | 8 ++++---- chalk-solve/src/clauses/builtin_traits.rs | 6 ++---- .../src/clauses/builtin_traits/fn_family.rs | 17 ++++++++++------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index e7d07f890b6..d0394b44107 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -358,18 +358,18 @@ fn program_clauses_that_could_match( let trait_datum = db.trait_datum(trait_id); + let self_ty = alias.self_type_parameter(interner); + // Flounder if the self-type is unknown and the trait is non-enumerable. // // e.g., Normalize(::Item = u32) - if (alias.self_type_parameter(interner).is_var(interner)) - && trait_datum.is_non_enumerable_trait() - { + if (self_ty.is_var(interner)) && trait_datum.is_non_enumerable_trait() { return Err(Floundered); } if let Some(well_known) = trait_datum.well_known { builtin_traits::add_builtin_assoc_program_clauses( - db, builder, well_known, proj, + db, builder, well_known, self_ty, ); } diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index b129777b87e..05111bcaaab 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,6 +1,6 @@ use super::{builder::ClauseBuilder, generalize}; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{AliasTy, ProjectionTy, Substitution, Ty}; +use chalk_ir::{Substitution, Ty}; mod clone; mod copy; @@ -48,12 +48,10 @@ pub fn add_builtin_assoc_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, - proj: &ProjectionTy, + self_ty: Ty, ) { match well_known { WellKnownTrait::FnOnce => { - let interner = db.interner(); - let self_ty = AliasTy::Projection(proj.clone()).self_type_parameter(interner); let trait_id = db.well_known_trait_id(well_known).unwrap(); fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty); } diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 3b06be24eb9..b4f388006a3 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -8,11 +8,14 @@ use chalk_ir::{ }; /// Handles clauses for FnOnce/FnMut/Fn. -/// If `assoc_output` is `true`, we push a clause of the form -/// `Normalize( B as FnOnce<(A,)>>::Output -> B) :- Implemented(fn(A) -> B as FnOnce<(A,)>` +/// If `self_ty` is a function, we push a clause of the form +/// `fn(A1, A2, ..., AN) -> O: FnTrait<(A1, A2, ..., AN)>`, where `FnTrait` +/// is the trait corresponding to `trait_id` (FnOnce/FnMut/Fn) /// -/// If `assoc_output` is `false`, we push a clause of the form -/// `Implemented(fn(A) -> B as FnOnce<(A,)>)` +/// If `trait_id` is `FnOnce`, we also push a clause for the output type of the form: +/// `Normalize( B as FnOnce<(A,)>>::Output -> B)` +/// We do not add the usual `Implemented(fn(A) -> b as FnOnce<(A,)>` clause +/// as a condition, since we already called `push_fact` with it pub fn add_fn_trait_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, @@ -25,10 +28,10 @@ pub fn add_fn_trait_program_clauses( let (binders, orig_sub) = fn_val.into_binders_and_value(interner); let bound_ref = Binders::new(VariableKinds::from(interner, binders), orig_sub); builder.push_binders(&bound_ref, |builder, orig_sub| { - let all_params: Vec<_> = orig_sub.iter(interner).cloned().collect(); - // The last parameter represents the function return type - let (arg_sub, fn_output_ty) = all_params.split_at(all_params.len() - 1); + let (arg_sub, fn_output_ty) = orig_sub + .parameters(interner) + .split_at(orig_sub.len(interner) - 1); let arg_sub = Substitution::from(interner, arg_sub); let fn_output_ty = fn_output_ty[0].assert_ty_ref(interner); From 1f6ea3a127240f974087e96fb8c77d8acd531eb9 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Jun 2020 16:36:53 -0400 Subject: [PATCH 14/14] Flounder in function trait when inference var or type alias is encountered --- chalk-solve/src/clauses.rs | 4 ++-- chalk-solve/src/clauses/builder.rs | 9 ++++++-- chalk-solve/src/clauses/builtin_traits.rs | 16 ++++++++------ .../src/clauses/builtin_traits/fn_family.rs | 11 ++++++---- tests/test/functions.rs | 22 +++++++++++++++++++ 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index d0394b44107..62e7707eedf 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -295,7 +295,7 @@ fn program_clauses_that_could_match( } if let Some(well_known) = trait_datum.well_known { - builtin_traits::add_builtin_program_clauses(db, builder, well_known, trait_ref); + builtin_traits::add_builtin_program_clauses(db, builder, well_known, trait_ref)?; } } DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => match &alias_eq.alias { @@ -370,7 +370,7 @@ fn program_clauses_that_could_match( if let Some(well_known) = trait_datum.well_known { builtin_traits::add_builtin_assoc_program_clauses( db, builder, well_known, self_ty, - ); + )?; } push_program_clauses_for_associated_type_values_in_impls_of( diff --git a/chalk-solve/src/clauses/builder.rs b/chalk-solve/src/clauses/builder.rs index 62880c107de..d9ab809da78 100644 --- a/chalk-solve/src/clauses/builder.rs +++ b/chalk-solve/src/clauses/builder.rs @@ -114,7 +114,11 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> { /// The new binders are always pushed onto the end of the internal /// list of binders; this means that any extant values where were /// created referencing the *old* list of binders are still valid. - pub fn push_binders(&mut self, binders: &Binders, op: impl FnOnce(&mut Self, V::Result)) + pub fn push_binders( + &mut self, + binders: &Binders, + op: impl FnOnce(&mut Self, V::Result) -> R, + ) -> R where V: Fold + HasInterner, V::Result: std::fmt::Debug, @@ -134,10 +138,11 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> { let value = binders.substitute(self.interner(), &self.parameters[old_len..]); debug!("push_binders: value={:?}", value); - op(self, value); + let res = op(self, value); self.binders.truncate(old_len); self.parameters.truncate(old_len); + res } /// Push a single binder, for a type, at the end of the binder diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 05111bcaaab..4a4259695cd 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,6 +1,6 @@ use super::{builder::ClauseBuilder, generalize}; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{Substitution, Ty}; +use chalk_ir::{Floundered, Substitution, Ty}; mod clone; mod copy; @@ -14,7 +14,7 @@ pub fn add_builtin_program_clauses( builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, trait_ref: &TraitRef, -) { +) -> Result<(), Floundered> { // If `trait_ref` contains bound vars, we want to universally quantify them. // `Generalize` collects them for us. let generalized = generalize::Generalize::apply(db.interner(), trait_ref); @@ -26,7 +26,7 @@ pub fn add_builtin_program_clauses( if force_impl { builder.push_fact(trait_ref.clone()); } - return; + return Ok(()); } match well_known { @@ -34,12 +34,13 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::FnOnce | WellKnownTrait::FnMut | WellKnownTrait::Fn => { - fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, self_ty) + fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, self_ty)? } // Drop impls are provided explicitly WellKnownTrait::Drop => (), } - }); + Ok(()) + }) } /// Like `add_builtin_program_clauses`, but for `DomainGoal::Normalize` involving @@ -49,14 +50,15 @@ pub fn add_builtin_assoc_program_clauses( builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, self_ty: Ty, -) { +) -> Result<(), Floundered> { match well_known { WellKnownTrait::FnOnce => { let trait_id = db.well_known_trait_id(well_known).unwrap(); - fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty); + fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty)?; } _ => {} } + Ok(()) } /// Given a trait ref `T0: Trait` and a list of types `U0..Un`, pushes a clause of the form diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index b4f388006a3..12c86516ba2 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -3,8 +3,8 @@ use crate::infer::instantiate::IntoBindersAndValue; use crate::rust_ir::WellKnownTrait; use crate::{Interner, RustIrDatabase, TraitRef}; use chalk_ir::{ - AliasTy, ApplicationTy, Binders, Normalize, ProjectionTy, Substitution, TraitId, Ty, TyData, - TypeName, VariableKinds, + AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, TraitId, + Ty, TyData, TypeName, VariableKinds, }; /// Handles clauses for FnOnce/FnMut/Fn. @@ -21,7 +21,7 @@ pub fn add_fn_trait_program_clauses( builder: &mut ClauseBuilder<'_, I>, trait_id: TraitId, self_ty: Ty, -) { +) -> Result<(), Floundered> { let interner = db.interner(); match self_ty.data(interner) { TyData::Function(fn_val) => { @@ -81,7 +81,10 @@ pub fn add_fn_trait_program_clauses( builder.push_fact(normalize); } }); + Ok(()) } - _ => {} + // Function traits are non-enumerable + TyData::InferenceVar(..) | TyData::Alias(..) => Err(Floundered), + _ => Ok(()), } } diff --git a/tests/test/functions.rs b/tests/test/functions.rs index ec8e7eebdd9..058e35d80ac 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -14,6 +14,12 @@ fn function_implement_fn_traits() { #[lang(fn)] trait Fn where Self: FnMut { } + + struct Ty { } + + trait Clone { } + opaque type MyOpaque: Clone = Ty; + } // Simple test: make sure a fully monomorphic type implements FnOnce @@ -156,6 +162,22 @@ fn function_implement_fn_traits() { } yields { "No possible solution" } + + // Tests that we flounder for inference variables + goal { + exists { + T: FnOnce<()> + } + } yields_first[SolverChoice::slg(3, None)] { + "Floundered" + } + + // Tests that we flounder for alias type (opaque) + goal { + MyOpaque: FnOnce<()> + } yields_first[SolverChoice::slg(3, None)] { + "Floundered" + } } }