diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index e2f6387d5d2..db54807664e 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -313,7 +313,9 @@ 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, binders, + )?; } } DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => match &alias_eq.alias { diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index ff1ed83cffb..d21a4b602b6 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,5 +1,5 @@ use super::{builder::ClauseBuilder, generalize}; -use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; +use crate::{CanonicalVarKinds, Interner, RustIrDatabase, TraitRef, WellKnownTrait}; use chalk_ir::{Floundered, Substitution, Ty}; mod clone; @@ -15,6 +15,7 @@ pub fn add_builtin_program_clauses( builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, trait_ref: &TraitRef, + binders: &CanonicalVarKinds, ) -> Result<(), Floundered> { // If `trait_ref` contains bound vars, we want to universally quantify them. // `Generalize` collects them for us. @@ -25,9 +26,15 @@ pub fn add_builtin_program_clauses( let ty = self_ty.data(db.interner()); match well_known { - 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::Sized => { + sized::add_sized_program_clauses(db, builder, &trait_ref, ty, binders) + } + WellKnownTrait::Copy => { + copy::add_copy_program_clauses(db, builder, &trait_ref, ty, binders) + } + WellKnownTrait::Clone => { + clone::add_clone_program_clauses(db, builder, &trait_ref, ty, binders) + } WellKnownTrait::FnOnce | WellKnownTrait::FnMut | WellKnownTrait::Fn => { fn_family::add_fn_trait_program_clauses(db, builder, well_known, self_ty)? } diff --git a/chalk-solve/src/clauses/builtin_traits/clone.rs b/chalk-solve/src/clauses/builtin_traits/clone.rs index 7fb62fbd0a1..69943be85db 100644 --- a/chalk-solve/src/clauses/builtin_traits/clone.rs +++ b/chalk-solve/src/clauses/builtin_traits/clone.rs @@ -1,6 +1,6 @@ use crate::clauses::ClauseBuilder; use crate::{Interner, RustIrDatabase, TraitRef}; -use chalk_ir::TyData; +use chalk_ir::{CanonicalVarKinds, TyData}; use super::copy::add_copy_program_clauses; @@ -9,9 +9,8 @@ pub fn add_clone_program_clauses( builder: &mut ClauseBuilder<'_, I>, trait_ref: &TraitRef, ty: &TyData, + binders: &CanonicalVarKinds, ) { - let _interner = db.interner(); - // Implement Clone for types that automaticly implement Copy - add_copy_program_clauses(db, builder, trait_ref, ty); + add_copy_program_clauses(db, builder, trait_ref, ty, binders); } diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index 81df1ac2fb2..5fd9c1a17ac 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -1,8 +1,11 @@ use crate::clauses::builtin_traits::needs_impl_for_tys; use crate::clauses::ClauseBuilder; use crate::{Interner, RustIrDatabase, TraitRef}; -use chalk_ir::{ApplicationTy, Substitution, TyData, TypeName}; +use chalk_ir::{ + ApplicationTy, CanonicalVarKinds, Substitution, TyData, TyKind, TypeName, VariableKind, +}; use std::iter; +use tracing::instrument; fn push_tuple_copy_conditions( db: &dyn RustIrDatabase, @@ -29,11 +32,13 @@ fn push_tuple_copy_conditions( ); } +#[instrument(skip(db, builder))] pub fn add_copy_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, trait_ref: &TraitRef, ty: &TyData, + binders: &CanonicalVarKinds, ) { match ty { TyData::Apply(ApplicationTy { name, substitution }) => match name { @@ -58,11 +63,38 @@ pub fn add_copy_program_clauses( let upvars = upvars.substitute(db.interner(), &closure_fn_substitution); needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); } - _ => {} + + // these impls are in libcore + TypeName::Ref(_) + | TypeName::Raw(_) + | TypeName::Scalar(_) + | TypeName::Never + | TypeName::Str => {} + + TypeName::Adt(_) + | TypeName::AssociatedType(_) + | TypeName::Slice + | TypeName::OpaqueType(_) + | TypeName::Error => {} }, + TyData::Function(_) => builder.push_fact(trait_ref.clone()), - // TODO(areredify) - // when #368 lands, extend this to handle everything accordingly - _ => {} + + TyData::InferenceVar(_, kind) => match kind { + TyKind::Integer | TyKind::Float => builder.push_fact(trait_ref.clone()), + TyKind::General => {} + }, + + TyData::BoundVar(bound_var) => { + let var_kind = &binders.at(db.interner(), bound_var.index).kind; + match var_kind { + VariableKind::Ty(TyKind::Integer) | VariableKind::Ty(TyKind::Float) => { + builder.push_fact(trait_ref.clone()) + } + VariableKind::Ty(_) | VariableKind::Const(_) | VariableKind::Lifetime => {} + } + } + + TyData::Alias(_) | TyData::Dyn(_) | TyData::Placeholder(_) => {} }; } diff --git a/chalk-solve/src/clauses/builtin_traits/sized.rs b/chalk-solve/src/clauses/builtin_traits/sized.rs index 90fd6b56255..786c705a29c 100644 --- a/chalk-solve/src/clauses/builtin_traits/sized.rs +++ b/chalk-solve/src/clauses/builtin_traits/sized.rs @@ -4,7 +4,9 @@ use crate::clauses::builtin_traits::needs_impl_for_tys; use crate::clauses::ClauseBuilder; use crate::rust_ir::AdtKind; use crate::{Interner, RustIrDatabase, TraitRef}; -use chalk_ir::{AdtId, ApplicationTy, Substitution, TyData, TypeName}; +use chalk_ir::{ + AdtId, ApplicationTy, CanonicalVarKinds, Substitution, TyData, TyKind, TypeName, VariableKind, +}; fn push_adt_sized_conditions( db: &dyn RustIrDatabase, @@ -69,6 +71,7 @@ pub fn add_sized_program_clauses( builder: &mut ClauseBuilder<'_, I>, trait_ref: &TraitRef, ty: &TyData, + binders: &CanonicalVarKinds, ) { match ty { TyData::Apply(ApplicationTy { name, substitution }) => match name { @@ -85,11 +88,31 @@ pub fn add_sized_program_clauses( | TypeName::Scalar(_) | TypeName::Raw(_) | TypeName::Ref(_) => builder.push_fact(trait_ref.clone()), - _ => {} + + TypeName::AssociatedType(_) + | TypeName::Slice + | TypeName::OpaqueType(_) + | TypeName::Str + | TypeName::Error => {} }, - TyData::Function(_) => builder.push_fact(trait_ref.clone()), - // TODO(areredify) - // when #368 lands, extend this to handle everything accordingly - _ => {} + + TyData::Function(_) + | TyData::InferenceVar(_, TyKind::Float) + | TyData::InferenceVar(_, TyKind::Integer) => builder.push_fact(trait_ref.clone()), + + TyData::BoundVar(bound_var) => { + let var_kind = &binders.at(db.interner(), bound_var.index).kind; + match var_kind { + VariableKind::Ty(TyKind::Integer) | VariableKind::Ty(TyKind::Float) => { + builder.push_fact(trait_ref.clone()) + } + VariableKind::Ty(_) | VariableKind::Const(_) | VariableKind::Lifetime => {} + } + } + + TyData::InferenceVar(_, TyKind::General) + | TyData::Placeholder(_) + | TyData::Dyn(_) + | TyData::Alias(_) => {} } } diff --git a/tests/test/builtin_impls.rs b/tests/test/builtin_impls.rs deleted file mode 100644 index f3884946dee..00000000000 --- a/tests/test/builtin_impls.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod clone; -mod copy; -mod fn_family; -mod sized; -mod unsize; diff --git a/tests/test/builtin_impls/clone.rs b/tests/test/builtin_impls/clone.rs deleted file mode 100644 index 1ab37f581f3..00000000000 --- a/tests/test/builtin_impls/clone.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::test::*; - -#[test] -fn tuples_are_clone() { - test! { - program { - #[non_enumerable] // see above - #[lang(clone)] - trait Clone { } - - struct S {} - - impl Clone for u8 {} - } - - goal { - ([u8],): Clone - } yields { - "No possible solution" - } - - goal { - (u8, [u8]): Clone - } yields { - "No possible solution" - } - - goal { - ([u8], u8): Clone - } yields { - "No possible solution" - } - - goal { - (): Clone - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (u8,): Clone - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (u8, u8): Clone - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - exists { (T, u8): Clone } - } yields { - "Ambiguous" - } - - goal { - forall { if (T: Clone) { (T, u8): Clone } } - } yields { - "Unique; substitution [], lifetime constraints []" - } - } -} diff --git a/tests/test/builtin_impls/copy.rs b/tests/test/builtin_impls/copy.rs deleted file mode 100644 index 542b29beef2..00000000000 --- a/tests/test/builtin_impls/copy.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::test::*; - -#[test] -fn tuples_are_copy() { - test! { - program { - // FIXME: If we don't declare Copy non-enumerable, `exists { T: - // Copy }` gives wrong results, because it doesn't consider the - // built-in impls. - #[non_enumerable] - #[lang(copy)] - trait Copy { } - - struct S {} - - impl Copy for u8 {} - } - - goal { - ([u8],): Copy - } yields { - "No possible solution" - } - - goal { - (u8, [u8]): Copy - } yields { - "No possible solution" - } - - goal { - ([u8], u8): Copy - } yields { - "No possible solution" - } - - goal { - (): Copy - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (u8,): Copy - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (u8, u8): Copy - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - exists { (T, u8): Copy } - } yields { - "Ambiguous" - } - - goal { - forall { if (T: Copy) { (T, u8): Copy } } - } yields { - "Unique; substitution [], lifetime constraints []" - } - } -} diff --git a/tests/test/builtin_impls/fn_family.rs b/tests/test/builtin_impls/fn_family.rs deleted file mode 100644 index 82b78edcac7..00000000000 --- a/tests/test/builtin_impls/fn_family.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::test::*; - -#[test] -fn function_implement_fn_traits() { - test! { - program { - #[lang(fn_once)] - trait FnOnce { - type Output; - } - - #[lang(fn_mut)] - trait FnMut where Self: FnOnce { } - - #[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 - 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 []" - } - - // Make sure unsafe function pointers don't implement FnOnce - goal { - unsafe fn(u8): FnOnce<(u8,)> - } yields { - "No possible solution" - } - // Same as above but for FnMut - goal { - unsafe fn(u8): FnMut<(u8,)> - } yields { - "No possible solution" - } - // Same as above but for Fn - goal { - unsafe fn(u8): Fn<(u8,)> - } yields { - "No possible solution" - } - - // 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) - } - } yields { - "No possible solution" - } - - // Tests that we can normalize a generic function pointer type - goal { - forall { - exists { - Normalize( T as FnOnce<(u8, V)>>::Output -> U) - } - } - } yields { - "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,)> - } - } yields { - "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)> - } - } yields { - "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)> - } - } yields { - "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)> - } - } 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" - } - } -} diff --git a/tests/test/builtin_impls/sized.rs b/tests/test/builtin_impls/sized.rs deleted file mode 100644 index 4132e2689d5..00000000000 --- a/tests/test/builtin_impls/sized.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::test::*; - -#[test] -fn tuples_are_sized() { - test! { - program { - #[lang(sized)] - trait Sized { } - - trait Foo {} - } - - goal { - ([u8],): Sized - } yields { - "No possible solution" - } - - goal { - (u8, [u8]): Sized - } yields { - "No possible solution" - } - - // It should not be well-formed because for tuples, only - // the last element is allowed not to be Sized. - goal { - ([u8], u8): Sized - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (): Sized - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (u8,): Sized - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - (u8, u8): Sized - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - exists { (T, u8): Sized } - } yields { - "Unique; for { substitution [?0 := ^0.0], lifetime constraints [] }" - } - - goal { - forall { (T, u8): Sized } - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - forall { (u8, T): Sized } - } yields { - "No possible solution" - } - - goal { - forall { if (T: Sized) { (u8, T): Sized } } - } yields { - "Unique; substitution [], lifetime constraints []" - } - } -} - -#[test] -fn functions_are_sized() { - test! { - program { - #[lang(sized)] - trait Sized { } - - trait Copy {} - } - - goal { - fn(()): Sized - } yields { - "Unique; substitution [], lifetime constraints []" - } - - goal { - fn([u8]): Sized - } yields { - "Unique; substitution [], lifetime constraints []" - } - } -} - -#[test] -fn scalars_are_sized() { - test! { - program { - #[lang(sized)] trait Sized { } - } - - goal { i8: Sized } yields { "Unique" } - goal { i16: Sized } yields { "Unique" } - goal { i32: Sized } yields { "Unique" } - goal { i64: Sized } yields { "Unique" } - goal { i128: Sized } yields { "Unique" } - goal { isize: Sized } yields { "Unique" } - goal { u8: Sized } yields { "Unique" } - goal { u16: Sized } yields { "Unique" } - goal { u32: Sized } yields { "Unique" } - goal { u64: Sized } yields { "Unique" } - goal { u128: Sized } yields { "Unique" } - goal { usize: Sized } yields { "Unique" } - goal { f32: Sized } yields { "Unique" } - goal { f64: Sized } yields { "Unique" } - goal { bool: Sized } yields { "Unique" } - goal { char: Sized } yields { "Unique" } - } -} diff --git a/tests/test/fn_def.rs b/tests/test/fn_def.rs index ec95d7dae99..a96738d1bc4 100644 --- a/tests/test/fn_def.rs +++ b/tests/test/fn_def.rs @@ -166,3 +166,62 @@ fn generic_fn_implements_fn_traits() { } } } + +#[test] +fn fn_defs() { + test! { + program { + trait Foo { } + + struct Bar { } + + struct Xyzzy { } + impl Foo for Xyzzy { } + + fn baz(quux: T) -> T + where T: Foo; + + fn garply(thud: i32) -> i32; + } + + goal { + WellFormed(baz) + } yields { + "No possible solution" + } + + goal { + WellFormed(baz) + } yields { + "Unique" + } + + goal { + WellFormed(garply) + } yields { + "Unique" + } + + } +} + +#[test] +fn fn_def_implied_bounds_from_env() { + test! { + program { + trait Foo { } + + struct Bar { } + impl Foo for Bar { } + + fn baz() where T: Foo; + } + goal { + if (FromEnv(baz)) { + Bar: Foo + } + } yields { + "Unique" + } + } +} diff --git a/tests/test/functions.rs b/tests/test/functions.rs index 48a4efa8ae5..df82529b187 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -1,60 +1,244 @@ use super::*; #[test] -fn fn_defs() { +fn functions_are_sized() { test! { program { - trait Foo { } - - struct Bar { } - - struct Xyzzy { } - impl Foo for Xyzzy { } - - fn baz(quux: T) -> T - where T: Foo; - - fn garply(thud: i32) -> i32; + #[lang(sized)] + trait Sized { } } goal { - WellFormed(baz) + fn(()): Sized } yields { - "No possible solution" + "Unique; substitution [], lifetime constraints []" } goal { - WellFormed(baz) + fn([u8]): Sized } yields { - "Unique" + "Unique; substitution [], lifetime constraints []" + } + } +} + +fn functions_are_copy() { + test! { + program { + #[lang(copy)] + trait Copy { } } goal { - WellFormed(garply) + fn(()): Copy } yields { - "Unique" + "Unique; substitution [], lifetime constraints []" } + goal { + fn([u8]): Copy + } yields { + "Unique; substitution [], lifetime constraints []" + } } } #[test] -fn fn_def_implied_bounds_from_env() { +fn function_implement_fn_traits() { test! { program { - trait Foo { } + #[lang(fn_once)] + trait FnOnce { + type Output; + } + + #[lang(fn_mut)] + trait FnMut where Self: FnOnce { } + + #[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 + 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 []" + } + + // Make sure unsafe function pointers don't implement FnOnce + goal { + unsafe fn(u8): FnOnce<(u8,)> + } yields { + "No possible solution" + } + // Same as above but for FnMut + goal { + unsafe fn(u8): FnMut<(u8,)> + } yields { + "No possible solution" + } + // Same as above but for Fn + goal { + unsafe fn(u8): Fn<(u8,)> + } yields { + "No possible solution" + } + + // 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 []" + } - struct Bar { } - impl Foo for Bar { } + // 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" + } - fn baz() where T: Foo; + // 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) + } + } yields { + "No possible solution" } + + // Tests that we can normalize a generic function pointer type goal { - if (FromEnv(baz)) { - Bar: Foo + forall { + exists { + Normalize( T as FnOnce<(u8, V)>>::Output -> U) + } } } yields { - "Unique" + "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,)> + } + } yields { + "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)> + } + } yields { + "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)> + } + } yields { + "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)> + } + } 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" } } } diff --git a/tests/test/mod.rs b/tests/test/mod.rs index 5bdb2789da4..13ee775fb9c 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -317,7 +317,6 @@ fn solve_goal(program_text: &str, goals: Vec<(&str, SolverChoice, TestGoal)>) { mod arrays; mod auto_traits; -mod builtin_impls; mod closures; mod coherence_goals; mod coinduction; @@ -340,4 +339,5 @@ mod slices; mod string; mod tuples; mod unify; +mod unsize; mod wf_goals; diff --git a/tests/test/numerics.rs b/tests/test/numerics.rs index c2e16c7e638..d349294a5dc 100644 --- a/tests/test/numerics.rs +++ b/tests/test/numerics.rs @@ -189,3 +189,39 @@ fn integers_are_not_floats() { } } } + +#[test] +fn integers_are_copy() { + test! { + program { + #[lang(copy)] + trait Copy { } + } + + goal { + exists { + I: Copy + } + } yields { + "Unique" + } + } +} + +#[test] +fn integers_are_sized() { + test! { + program { + #[lang(sized)] + trait Sized { } + } + + goal { + exists { + I: Sized + } + } yields { + "Unique" + } + } +} diff --git a/tests/test/scalars.rs b/tests/test/scalars.rs index d612e135f64..210516ad855 100644 --- a/tests/test/scalars.rs +++ b/tests/test/scalars.rs @@ -136,3 +136,29 @@ fn scalars_are_well_formed() { goal { WellFormed(char) } yields { "Unique" } } } + +#[test] +fn scalars_are_sized() { + test! { + program { + #[lang(sized)] trait Sized { } + } + + goal { i8: Sized } yields { "Unique" } + goal { i16: Sized } yields { "Unique" } + goal { i32: Sized } yields { "Unique" } + goal { i64: Sized } yields { "Unique" } + goal { i128: Sized } yields { "Unique" } + goal { isize: Sized } yields { "Unique" } + goal { u8: Sized } yields { "Unique" } + goal { u16: Sized } yields { "Unique" } + goal { u32: Sized } yields { "Unique" } + goal { u64: Sized } yields { "Unique" } + goal { u128: Sized } yields { "Unique" } + goal { usize: Sized } yields { "Unique" } + goal { f32: Sized } yields { "Unique" } + goal { f64: Sized } yields { "Unique" } + goal { bool: Sized } yields { "Unique" } + goal { char: Sized } yields { "Unique" } + } +} diff --git a/tests/test/tuples.rs b/tests/test/tuples.rs index 5542f8ae2d3..ba12428ec03 100644 --- a/tests/test/tuples.rs +++ b/tests/test/tuples.rs @@ -34,3 +34,206 @@ fn tuple_trait_impl() { } } } + +#[test] +fn tuples_are_copy() { + test! { + program { + // FIXME: If we don't declare Copy non-enumerable, `exists { T: + // Copy }` gives wrong results, because it doesn't consider the + // built-in impls. + #[non_enumerable] + #[lang(copy)] + trait Copy { } + + struct S {} + + impl Copy for u8 {} + } + + goal { + ([u8],): Copy + } yields { + "No possible solution" + } + + goal { + (u8, [u8]): Copy + } yields { + "No possible solution" + } + + goal { + ([u8], u8): Copy + } yields { + "No possible solution" + } + + goal { + (): Copy + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (u8,): Copy + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (u8, u8): Copy + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + exists { (T, u8): Copy } + } yields { + "Ambiguous" + } + + goal { + forall { if (T: Copy) { (T, u8): Copy } } + } yields { + "Unique; substitution [], lifetime constraints []" + } + } +} + +#[test] +fn tuples_are_sized() { + test! { + program { + #[lang(sized)] + trait Sized { } + + trait Foo {} + } + + goal { + ([u8],): Sized + } yields { + "No possible solution" + } + + goal { + (u8, [u8]): Sized + } yields { + "No possible solution" + } + + // It should not be well-formed because for tuples, only + // the last element is allowed not to be Sized. + goal { + ([u8], u8): Sized + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (): Sized + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (u8,): Sized + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (u8, u8): Sized + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + exists { (T, u8): Sized } + } yields { + "Unique; for { substitution [?0 := ^0.0], lifetime constraints [] }" + } + + goal { + forall { (T, u8): Sized } + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + forall { (u8, T): Sized } + } yields { + "No possible solution" + } + + goal { + forall { if (T: Sized) { (u8, T): Sized } } + } yields { + "Unique; substitution [], lifetime constraints []" + } + } +} + +#[test] +fn tuples_are_clone() { + test! { + program { + #[non_enumerable] // see above + #[lang(clone)] + trait Clone { } + + struct S {} + + impl Clone for u8 {} + } + + goal { + ([u8],): Clone + } yields { + "No possible solution" + } + + goal { + (u8, [u8]): Clone + } yields { + "No possible solution" + } + + goal { + ([u8], u8): Clone + } yields { + "No possible solution" + } + + goal { + (): Clone + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (u8,): Clone + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + (u8, u8): Clone + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + exists { (T, u8): Clone } + } yields { + "Ambiguous" + } + + goal { + forall { if (T: Clone) { (T, u8): Clone } } + } yields { + "Unique; substitution [], lifetime constraints []" + } + } +} diff --git a/tests/test/builtin_impls/unsize.rs b/tests/test/unsize.rs similarity index 100% rename from tests/test/builtin_impls/unsize.rs rename to tests/test/unsize.rs