From 977b62895aae6ee6f1e11d0f24912e29a1a78726 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 6 Apr 2020 21:39:55 -0700 Subject: [PATCH 1/7] Add scalar and tuple to `TypeName` --- chalk-ir/src/debug.rs | 2 ++ chalk-ir/src/fold/boring_impls.rs | 4 +++ chalk-ir/src/lib.rs | 41 +++++++++++++++++++++++++++++++ chalk-solve/src/clauses.rs | 2 ++ 4 files changed, 49 insertions(+) diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index 6f0d82d65be..7c025589cd7 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -100,6 +100,8 @@ impl Debug for TypeName { match self { TypeName::Struct(id) => write!(fmt, "{:?}", id), TypeName::AssociatedType(assoc_ty) => write!(fmt, "{:?}", assoc_ty), + TypeName::Scalar(scalar) => write!(fmt, "{:?}", scalar), + TypeName::Tuple(arity) => write!(fmt, "{:?}", arity), TypeName::Error => write!(fmt, "{{error}}"), } } diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index 88a27d13219..add51e91629 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -200,6 +200,10 @@ copy_fold!(DebruijnIndex); copy_fold!(chalk_engine::TableIndex); copy_fold!(chalk_engine::TimeStamp); copy_fold!(()); +copy_fold!(UintTy); +copy_fold!(IntTy); +copy_fold!(FloatTy); +copy_fold!(Scalar); #[macro_export] macro_rules! id_fold { diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index ad4c9058114..95ba67c2aaf 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -96,6 +96,41 @@ impl HasInterner for InEnvironment { type Interner = G::Interner; } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum IntTy { + Isize, + I8, + I16, + I32, + I64, + I128, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum UintTy { + Usize, + U8, + U16, + U32, + U64, + U128, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum FloatTy { + F32, + F64, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Scalar { + Bool, + Char, + Int(IntTy), + Uint(UintTy), + Float(FloatTy), +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Fold)] pub enum TypeName { /// a type like `Vec` @@ -104,6 +139,12 @@ pub enum TypeName { /// an associated type like `Iterator::Item`; see `AssociatedType` for details AssociatedType(AssocTypeId), + /// a scalar type like `bool` or `u32` + Scalar(Scalar), + + /// a tuple of the given arity + Tuple(usize), + /// This can be used to represent an error, e.g. during name resolution of a type. /// Chalk itself will not produce this, just pass it through when given. Error, diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index 0c3a3707dba..488da330254 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -381,6 +381,8 @@ fn match_type_name(builder: &mut ClauseBuilder<'_, I>, name: TypeNa .db .associated_ty_data(type_id) .to_program_clauses(builder), + TypeName::Scalar(_) => todo!("Scalar match type name"), + TypeName::Tuple(_) => todo!("Tuple match type name"), } } From ed392f4e1d235eab451d01db9b3865900573a61b Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 13 Apr 2020 22:10:51 -0700 Subject: [PATCH 2/7] Add parsing for scalar types and tuples --- chalk-integration/src/lowering.rs | 38 +++++++++++ chalk-parse/src/ast.rs | 41 ++++++++++++ chalk-parse/src/parser.lalrpop | 44 ++++++++++++- chalk-solve/src/clauses.rs | 4 +- tests/lowering/mod.rs | 63 ++++++++++++++++-- tests/test/auto_traits.rs | 22 +++---- tests/test/coherence.rs | 30 ++++----- tests/test/cycle.rs | 16 ++--- tests/test/implied_bounds.rs | 8 +-- tests/test/impls.rs | 102 +++++++++++++++--------------- tests/test/misc.rs | 39 ++++++------ tests/test/negation.rs | 40 ++++++------ tests/test/projection.rs | 42 ++++++------ tests/test/wf_lowering.rs | 48 +++++++------- 14 files changed, 355 insertions(+), 182 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index f7ff420971c..be3a29f119a 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1074,6 +1074,17 @@ impl LowerTy for Ty { }; Ok(chalk_ir::TyData::Function(function).intern(interner)) } + Ty::Tuple { arity } => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Tuple(arity), + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner)), + + Ty::Scalar { ty } => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Scalar(ast_scalar_to_chalk_scalar(ty)), + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner)), } } } @@ -1420,3 +1431,30 @@ impl Kinded for chalk_ir::Parameter { self.data(interner).kind() } } + +fn ast_scalar_to_chalk_scalar(scalar: ScalarType) -> chalk_ir::Scalar { + match scalar { + ScalarType::Int(int) => chalk_ir::Scalar::Int(match int { + IntTy::I8 => chalk_ir::IntTy::I8, + IntTy::I16 => chalk_ir::IntTy::I16, + IntTy::I32 => chalk_ir::IntTy::I32, + IntTy::I64 => chalk_ir::IntTy::I64, + IntTy::I128 => chalk_ir::IntTy::I128, + IntTy::Isize => chalk_ir::IntTy::Isize, + }), + ScalarType::Uint(uint) => chalk_ir::Scalar::Uint(match uint { + UintTy::U8 => chalk_ir::UintTy::U8, + UintTy::U16 => chalk_ir::UintTy::U16, + UintTy::U32 => chalk_ir::UintTy::U32, + UintTy::U64 => chalk_ir::UintTy::U64, + UintTy::U128 => chalk_ir::UintTy::U128, + UintTy::Usize => chalk_ir::UintTy::Usize, + }), + ScalarType::Float(float) => chalk_ir::Scalar::Float(match float { + FloatTy::F32 => chalk_ir::FloatTy::F32, + FloatTy::F64 => chalk_ir::FloatTy::F64, + }), + ScalarType::Bool => chalk_ir::Scalar::Bool, + ScalarType::Char => chalk_ir::Scalar::Char, + } +} diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index ee24415f7a8..4b426e145f1 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -175,6 +175,47 @@ pub enum Ty { lifetime_names: Vec, ty: Box, }, + Tuple { + arity: usize, + }, + Scalar { + ty: ScalarType, + }, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum IntTy { + Isize, + I8, + I16, + I32, + I64, + I128, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum UintTy { + Usize, + U8, + U16, + U32, + U64, + U128, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum FloatTy { + F32, + F64, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum ScalarType { + Bool, + Char, + Int(IntTy), + Uint(UintTy), + Float(FloatTy), } #[derive(Copy, Clone, PartialEq, Eq, Debug)] diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 98a5679fc5b..a7fb859d93e 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -178,6 +178,7 @@ pub Ty: Ty = { }; TyWithoutFor: Ty = { + => Ty::Scalar { ty: <> }, => Ty::Id { name: n}, "fn" "(" ")" => Ty::ForAll { lifetime_names: vec![], @@ -188,7 +189,33 @@ TyWithoutFor: Ty = { }, "<" > ">" => Ty::Apply { name: n, args: a }, => Ty::Alias { alias: a }, - "(" ")", + "(" ")" => t, +}; + +ScalarType: ScalarType = { + "u8" => ScalarType::Uint(UintTy::U8), + "u16" => ScalarType::Uint(UintTy::U16), + "u32" => ScalarType::Uint(UintTy::U32), + "u64" => ScalarType::Uint(UintTy::U64), + "u128" => ScalarType::Uint(UintTy::U128), + "usize" => ScalarType::Uint(UintTy::Usize), + "i8" => ScalarType::Int(IntTy::I8), + "i16" => ScalarType::Int(IntTy::I16), + "i32" => ScalarType::Int(IntTy::I32), + "i64" => ScalarType::Int(IntTy::I64), + "i128" => ScalarType::Int(IntTy::I128), + "isize" => ScalarType::Int(IntTy::Isize), + "f32" => ScalarType::Float(FloatTy::F32), + "f64" => ScalarType::Float(FloatTy::F64), + "bool" => ScalarType::Bool, + "char" => ScalarType::Char, +}; + +TupleOrParensInner: Ty = { + > => Ty::Tuple { arity: l.len() }, + "," => Ty::Tuple { arity: 1 }, + , + () => Ty::Tuple { arity: 0 }, }; Lifetime: Lifetime = { @@ -343,11 +370,26 @@ Separator1: Vec = { } }; +Separator2: Vec = { + S => vec![t1, t2], + S S > => { + let mut v = v; + v.push(t1); + v.push(t2); + v + } +}; + #[inline] Comma: Vec = { > }; +#[inline] +Comma2: Vec = { + > +}; + #[inline] SemiColon: Vec = { > diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index 488da330254..a54256038b4 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -381,8 +381,8 @@ fn match_type_name(builder: &mut ClauseBuilder<'_, I>, name: TypeNa .db .associated_ty_data(type_id) .to_program_clauses(builder), - TypeName::Scalar(_) => todo!("Scalar match type name"), - TypeName::Tuple(_) => todo!("Tuple match type name"), + TypeName::Scalar(_) => (), + TypeName::Tuple(_) => (), } } diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index cac5bf492d1..8d624c54de9 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -74,8 +74,6 @@ fn negative_impl() { type Item; } - struct i32 { } - impl !Foo for i32 { type Item = i32; } @@ -93,8 +91,6 @@ fn negative_impl() { type Item; } - struct i32 { } - impl !Foo for T where T: Iterator { } } } @@ -229,9 +225,9 @@ fn check_parameter_kinds() { lowering_error! { program { struct Foo<'a> { } - struct i32 { } + struct Myi32 { } trait Bar { } - impl Bar for Foo { } + impl Bar for Foo { } } error_msg { "incorrect parameter kind for `Foo`: expected lifetime, found type" @@ -425,3 +421,58 @@ fn fundamental_multiple_type_parameters() { } } } + +#[test] +fn tuples() { + lowering_success! { + program { + trait Foo { } + + // `()` is an empty tuple + impl Foo for () { } + // `(i32,)` is a tuple + impl Foo for (i32,) { } + // `(i32)` is `i32` is a scalar + impl Foo for (i32) { } + impl Foo for (i32, u32) { } + impl Foo for (i32, u32, f32) { } + } + } +} + +#[test] +fn scalars() { + lowering_success! { + program { + trait Foo { } + + impl Foo for i8 { } + impl Foo for i16 { } + impl Foo for i32 { } + impl Foo for i64 { } + impl Foo for i128 { } + impl Foo for isize { } + impl Foo for u8 { } + impl Foo for u16 { } + impl Foo for u32 { } + impl Foo for u64 { } + impl Foo for u128 { } + impl Foo for usize { } + impl Foo for f32 { } + impl Foo for f64 { } + impl Foo for bool { } + impl Foo for char { } + } + } + + lowering_error! { + program { + struct i32 { } + } + + // TODO: improve this error message + error_msg { + "parse error: UnrecognizedToken { token: (8, Token(45, \"i32\"), 11), expected: [\"r#\\\"([A-Za-z]|_)([A-Za-z0-9]|_)*\\\"#\"] }" + } + } +} diff --git a/tests/test/auto_traits.rs b/tests/test/auto_traits.rs index 02d7313991f..6d51edf3451 100644 --- a/tests/test/auto_traits.rs +++ b/tests/test/auto_traits.rs @@ -8,7 +8,7 @@ fn auto_semantics() { program { #[auto] trait Send { } - struct i32 { } + struct TypeA { } struct Ptr { } impl Send for Ptr where T: Send { } @@ -37,7 +37,7 @@ fn auto_semantics() { } goal { - List: Send + List: Send } yields { "Unique" } @@ -58,7 +58,7 @@ fn auto_trait_without_impls() { program { #[auto] trait Send { } - struct i32 { } + struct TypeA { } struct Useless { } @@ -68,7 +68,7 @@ fn auto_trait_without_impls() { } goal { - i32: Send + TypeA: Send } yields { "Unique" } @@ -100,34 +100,34 @@ fn auto_trait_with_impls() { program { #[auto] trait Send { } - struct i32 { } - struct f32 { } + struct TypeA { } + struct TypeB { } struct Vec { } impl Send for Vec where T: Send { } - impl !Send for i32 { } + impl !Send for TypeA { } } goal { - i32: Send + TypeA: Send } yields { "No possible solution" } goal { - f32: Send + TypeB: Send } yields { "Unique" } goal { - Vec: Send + Vec: Send } yields { "No possible solution" } goal { - Vec: Send + Vec: Send } yields { "Unique" } diff --git a/tests/test/coherence.rs b/tests/test/coherence.rs index b272c353dd9..d2547d6236b 100644 --- a/tests/test/coherence.rs +++ b/tests/test/coherence.rs @@ -192,10 +192,10 @@ fn overlapping_negative_positive_impls() { lowering_error! { program { trait Send { } - struct i32 { } + struct MyType { } - impl Send for i32 { } - impl !Send for i32 { } + impl Send for MyType { } + impl !Send for MyType { } } error_msg { "overlapping impls of trait `Send`" } @@ -211,10 +211,10 @@ fn overlapping_negative_impls() { trait Bar { } struct Vec { } - struct i32 { } + struct MyType { } - impl Foo for i32 { } - impl Bar for i32 { } + impl Foo for MyType { } + impl Bar for MyType { } impl !Send for Vec where T: Foo { } impl !Send for Vec where T: Bar { } @@ -365,17 +365,17 @@ fn orphan_check() { program { #[auto] #[upstream] trait Send { } #[upstream] trait TheTrait { } - #[upstream] struct isize { } - #[upstream] struct usize { } + #[upstream] struct TypeA { } + #[upstream] struct TypeB { } struct TheType { } // These impls should be fine because they contain the local type - impl TheTrait for isize { } - impl TheTrait for TheType { } + impl TheTrait for TypeA { } + impl TheTrait for TheType { } // This impl should fail because it contains only upstream type - impl TheTrait for isize { } + impl TheTrait for TypeA { } } error_msg { "impl for trait `TheTrait` violates the orphan rules" } @@ -385,9 +385,9 @@ fn orphan_check() { program { #[auto] #[upstream] trait Send { } #[upstream] struct Vec { } - #[upstream] struct isize { } + #[upstream] struct TypeA { } - impl !Send for Vec { } + impl !Send for Vec { } } error_msg { "impl for trait `Send` violates the orphan rules" } @@ -410,11 +410,11 @@ fn orphan_check() { program { #[upstream] trait Remote1 { } #[upstream] struct Pair { } - #[upstream] struct i32 { } + #[upstream] struct TypeA { } struct Local { } - impl Remote1>> for i32 { } + impl Remote1>> for TypeA { } } error_msg { "impl for trait `Remote1` violates the orphan rules" } diff --git a/tests/test/cycle.rs b/tests/test/cycle.rs index 4a8fa06cfc4..c3897b19759 100644 --- a/tests/test/cycle.rs +++ b/tests/test/cycle.rs @@ -18,11 +18,11 @@ fn inner_cycle() { #[marker] trait B { } - struct i32 { } + struct Foo { } struct Vec { } impl A for T where T: B { } - impl A for i32 { } + impl A for Foo { } impl B for T where T: A { } impl B for Vec where T: B { } @@ -62,12 +62,12 @@ fn cycle_many_solutions() { program { trait Foo { } struct S { } - struct i32 { } + struct Zero { } impl Foo for S where T: Foo { } - impl Foo for i32 { } + impl Foo for Zero { } } - // infinite family of solutions: {i32, S, S>, ... } + // infinite family of solutions: {Zero, S, S>, ... } goal { exists { T: Foo @@ -85,9 +85,9 @@ fn cycle_unique_solution() { trait Foo { } trait Bar { } struct S { } - struct i32 { } + struct Zero { } impl Foo for S where T: Foo, T: Bar { } - impl Foo for i32 { } + impl Foo for Zero { } } goal { @@ -95,7 +95,7 @@ fn cycle_unique_solution() { T: Foo } } yields { - "Unique; substitution [?0 := i32]" + "Unique; substitution [?0 := Zero]" } } } diff --git a/tests/test/implied_bounds.rs b/tests/test/implied_bounds.rs index b3c276e337e..bcecd00c7c5 100644 --- a/tests/test/implied_bounds.rs +++ b/tests/test/implied_bounds.rs @@ -8,12 +8,12 @@ fn implied_bounds() { program { trait Clone { } trait Iterator where Self: Clone { type Item; } - struct u32 { } + struct Struct { } } goal { forall { - if (T: Iterator) { + if (T: Iterator) { T: Clone } } @@ -29,7 +29,7 @@ fn gat_implied_bounds() { program { trait Clone { } trait Foo { type Item: Clone; } - struct u32 { } + struct Struct { } } goal { @@ -47,7 +47,7 @@ fn gat_implied_bounds() { program { trait Clone { } trait Foo { type Item; } - struct u32 { } + struct Struct { } } goal { diff --git a/tests/test/impls.rs b/tests/test/impls.rs index 7c9cd168f61..21d29b7490f 100644 --- a/tests/test/impls.rs +++ b/tests/test/impls.rs @@ -160,10 +160,10 @@ fn prove_forall() { fn higher_ranked() { test! { program { - struct u8 { } + struct BestType { } struct SomeType { } trait Foo { } - impl Foo for SomeType { } + impl Foo for SomeType { } } goal { @@ -173,7 +173,7 @@ fn higher_ranked() { } } } yields { - "Unique; substitution [?0 := u8], lifetime constraints []" + "Unique; substitution [?0 := BestType], lifetime constraints []" } } } @@ -205,18 +205,18 @@ fn normalize_rev_infer() { test! { program { trait Identity { type Item; } - struct u32 { } - struct i32 { } - impl Identity for u32 { type Item = u32; } - impl Identity for i32 { type Item = i32; } + struct A { } + struct B { } + impl Identity for A { type Item = A; } + impl Identity for B { type Item = B; } } goal { exists { - T: Identity + T: Identity } } yields { - "Unique; substitution [?0 := u32]" + "Unique; substitution [?0 := A]" } } } @@ -228,20 +228,20 @@ fn normalize_rev_infer_gat() { test! { program { trait Combine { type Item; } - struct u32 { } - struct i32 { } + struct A { } + struct B { } struct Either { } - impl Combine for u32 { type Item = Either; } - impl Combine for i32 { type Item = Either; } + impl Combine for A { type Item = Either; } + impl Combine for B { type Item = Either; } } goal { exists { - T: Combine = Either> + T: Combine = Either> } } yields { // T is ?1 and U is ?0, so this is surprising, but correct! (See #126.) - "Unique; substitution [?0 := i32, ?1 := u32]" + "Unique; substitution [?0 := B, ?1 := A]" } } } @@ -362,11 +362,11 @@ fn suggested_subst() { trait SomeTrait {} struct Foo {} struct Bar {} - struct i32 {} - struct bool {} - impl SomeTrait for Foo {} - impl SomeTrait for Bar {} - impl SomeTrait for Bar {} + struct Baz {} + struct Qux {} + impl SomeTrait for Foo {} + impl SomeTrait for Bar {} + impl SomeTrait for Bar {} } goal { @@ -374,42 +374,42 @@ fn suggested_subst() { Foo: SomeTrait } } yields { - "Unique; substitution [?0 := i32]" + "Unique; substitution [?0 := Baz]" } goal { exists { - if (i32: SomeTrait) { - i32: SomeTrait + if (Baz: SomeTrait) { + Baz: SomeTrait } } } yields { - "Unique; substitution [?0 := bool]" + "Unique; substitution [?0 := Qux]" } goal { exists { - if (i32: SomeTrait) { + if (Baz: SomeTrait) { Foo: SomeTrait } } } yields { - "Unique; substitution [?0 := i32]" + "Unique; substitution [?0 := Baz]" } goal { exists { - if (Foo: SomeTrait) { + if (Foo: SomeTrait) { Foo: SomeTrait } } } yields { - "Unique; substitution [?0 := i32]" + "Unique; substitution [?0 := Baz]" } goal { exists { - if (Foo: SomeTrait) { + if (Foo: SomeTrait) { Foo: SomeTrait } } @@ -422,7 +422,7 @@ fn suggested_subst() { goal { exists { if (Foo: SomeTrait) { - if (Foo: SomeTrait) { + if (Foo: SomeTrait) { Foo: SomeTrait } } @@ -441,7 +441,7 @@ fn suggested_subst() { goal { exists { - if (Bar: SomeTrait) { + if (Bar: SomeTrait) { Bar: SomeTrait } } @@ -452,8 +452,8 @@ fn suggested_subst() { goal { exists { - if (Bar: SomeTrait) { - if (Bar: SomeTrait) { + if (Bar: SomeTrait) { + if (Bar: SomeTrait) { Bar: SomeTrait } } @@ -490,18 +490,18 @@ fn where_clause_trumps() { fn inapplicable_assumption_does_not_shadow() { test! { program { - struct i32 { } - struct u32 { } + struct A { } + struct B { } trait Foo { } - impl Foo for T { } + impl Foo for T { } } goal { forall { exists { - if (i32: Foo) { + if (A: Foo) { T: Foo } } @@ -520,11 +520,11 @@ fn partial_overlap_2() { trait Foo {} trait Bar {} - struct i32 {} - struct u32 {} + struct TypeA {} + struct TypeB {} - impl Marker for T where T: Foo {} - impl Marker for T where T: Bar {} + impl Marker for T where T: Foo {} + impl Marker for T where T: Bar {} } goal { @@ -540,7 +540,7 @@ fn partial_overlap_2() { goal { forall { if (T: Foo; T: Bar) { - T: Marker + T: Marker } } } yields { @@ -550,7 +550,7 @@ fn partial_overlap_2() { goal { forall { if (T: Foo; T: Bar) { - T: Marker + T: Marker } } } yields { @@ -570,9 +570,9 @@ fn partial_overlap_3() { impl Marker for T where T: Foo {} impl Marker for T where T: Bar {} - struct i32 {} - impl Foo for i32 {} - impl Bar for i32 {} + struct Struct {} + impl Foo for Struct {} + impl Bar for Struct {} } goal { @@ -584,7 +584,7 @@ fn partial_overlap_3() { } goal { - i32: Marker + Struct: Marker } yields { "Unique" } @@ -597,7 +597,7 @@ fn clauses_in_if_goals() { program { trait Foo { } struct Vec { } - struct i32 { } + struct A { } } goal { @@ -622,8 +622,8 @@ fn clauses_in_if_goals() { goal { if (forall { Vec: Foo :- T: Foo }) { - if (i32: Foo) { - Vec: Foo + if (A: Foo) { + Vec: Foo } } } yields { @@ -632,7 +632,7 @@ fn clauses_in_if_goals() { goal { if (forall { Vec: Foo :- T: Foo }) { - Vec: Foo + Vec: Foo } } yields { "No possible solution" diff --git a/tests/test/misc.rs b/tests/test/misc.rs index a3d76bf00f7..d19c62efb12 100644 --- a/tests/test/misc.rs +++ b/tests/test/misc.rs @@ -47,8 +47,8 @@ fn basic() { program { trait Sized { } - struct i32 { } - impl Sized for i32 { } + struct Foo { } + impl Sized for Foo { } } goal { @@ -108,8 +108,9 @@ fn only_draw_so_many() { struct Vec { } impl Sized for Vec where T: Sized { } - struct i32 { } - impl Sized for i32 { } + struct Foo { } + impl Sized for Foo { } + struct Slice { } impl Sized for Slice where T: Sized { } @@ -118,11 +119,11 @@ fn only_draw_so_many() { goal { exists { T: Sized } } yields_first[SolverChoice::slg(10, None)] { - "substitution [?0 := i32], lifetime constraints []", - "substitution [?0 := Slice], lifetime constraints []", - "substitution [?0 := Vec], lifetime constraints []", - "substitution [?0 := Slice>], lifetime constraints []", - "substitution [?0 := Vec>], lifetime constraints []" + "substitution [?0 := Foo], lifetime constraints []", + "substitution [?0 := Slice], lifetime constraints []", + "substitution [?0 := Vec], lifetime constraints []", + "substitution [?0 := Slice>], lifetime constraints []", + "substitution [?0 := Vec>], lifetime constraints []" } goal { @@ -144,8 +145,8 @@ fn only_draw_so_many_blow_up() { impl Sized for Vec where T: Sized { } impl Foo for Vec where T: Sized { } - struct i32 { } - impl Sized for i32 { } + struct Alice { } + impl Sized for Alice { } struct Slice { } impl Sized for Slice where T: Sized { } @@ -166,7 +167,7 @@ fn subgoal_cycle_uninhabited() { trait Foo { } struct Box { } struct Vec { } - struct u32 { } + struct Alice { } impl Foo for Box where Box>: Foo { } } @@ -194,16 +195,16 @@ fn subgoal_cycle_uninhabited() { // However, if we come across a negative goal that exceeds our // size threshold, we have a problem. goal { - exists { T = Vec, not { Vec>: Foo } } + exists { T = Vec, not { Vec>: Foo } } } yields_first[SolverChoice::slg(2, None)] { "Floundered" } // Same query with larger threshold works fine, though. goal { - exists { T = Vec, not { Vec>: Foo } } + exists { T = Vec, not { Vec>: Foo } } } yields_all[SolverChoice::slg(4, None)] { - "substitution [?0 := Vec], lifetime constraints []" + "substitution [?0 := Vec], lifetime constraints []" } // Here, due to the hypothesis, there does indeed exist a suitable T, `U`. @@ -223,16 +224,16 @@ fn subgoal_cycle_inhabited() { trait Foo { } struct Box { } struct Vec { } - struct u32 { } + struct Alice { } impl Foo for Box where Box>: Foo { } - impl Foo for u32 { } + impl Foo for Alice { } } // Exceeds size threshold -> flounder goal { exists { T: Foo } } yields_first[SolverChoice::slg(3, None)] { - "substitution [?0 := u32], lifetime constraints []", + "substitution [?0 := Alice], lifetime constraints []", "Floundered" } } @@ -244,7 +245,7 @@ fn basic_region_constraint_from_positive_impl() { program { trait Foo { } struct Ref<'a, 'b, T> { } - struct u32 { } + struct Bar { } impl<'x, T> Foo for Ref<'x, 'x, T> { } } diff --git a/tests/test/negation.rs b/tests/test/negation.rs index 20c2ec9ae0e..bd1a0b7461f 100644 --- a/tests/test/negation.rs +++ b/tests/test/negation.rs @@ -6,19 +6,19 @@ use super::*; fn simple_negation() { test! { program { - struct i32 {} + struct Bar {} trait Foo {} } goal { - not { i32: Foo } + not { Bar: Foo } } yields { "Unique" } goal { not { - not { i32: Foo } + not { Bar: Foo } } } yields { "No" @@ -27,7 +27,7 @@ fn simple_negation() { goal { not { not { - not { i32: Foo } + not { Bar: Foo } } } } yields { @@ -101,8 +101,8 @@ fn deep_negation() { fn negation_quantifiers() { test! { program { - struct i32 {} - struct u32 {} + struct Alice {} + struct Bob {} } goal { @@ -142,10 +142,10 @@ fn negation_free_vars() { test! { program { struct Vec {} - struct i32 {} - struct u32 {} + struct Alice {} + struct Bob {} trait Foo {} - impl Foo for Vec {} + impl Foo for Vec {} } goal { @@ -166,14 +166,14 @@ fn negative_loop() { program { trait P { } trait Q { } - struct u32 { } + struct Alice { } - forall<> { u32: P if not { u32: Q } } - forall<> { u32: Q if not { u32: P } } + forall<> { Alice: P if not { Alice: Q } } + forall<> { Alice: Q if not { Alice: P } } } goal { - u32: P + Alice: P } yields_all[SolverChoice::slg(10, None)] { // Negative cycle -> panic "" @@ -273,13 +273,13 @@ fn contradiction() { test! { program { trait P { } - struct u32 { } + struct Alice { } - forall<> { u32: P if not { u32: P } } + forall<> { Alice: P if not { Alice: P } } } goal { - u32: P + Alice: P } yields_all[SolverChoice::slg(3, None)] { // Negative cycle -> panic "" @@ -295,14 +295,14 @@ fn negative_answer_ambiguous() { program { trait P { } trait Q { } - struct u32 { } + struct Alice { } - forall<> { u32: P if not { u32: Q } } - forall<> { u32: Q if not { u32: Q } } + forall<> { Alice: P if not { Alice: Q } } + forall<> { Alice: Q if not { Alice: Q } } } goal { - u32: P + Alice: P } yields_all[SolverChoice::slg(3, None)] { // Negative cycle -> panic "" diff --git a/tests/test/projection.rs b/tests/test/projection.rs index bd8eb56e638..38cf8dbf2be 100644 --- a/tests/test/projection.rs +++ b/tests/test/projection.rs @@ -8,7 +8,7 @@ fn normalize_basic() { program { trait Iterator { type Item; } struct Vec { } - struct u32 { } + struct Foo { } impl Iterator for Vec { type Item = T; } @@ -34,8 +34,8 @@ fn normalize_basic() { goal { forall { - if (T: Iterator) { - ::Item = u32 + if (T: Iterator) { + ::Item = Foo } } } yields { @@ -98,7 +98,7 @@ fn normalize_into_iterator() { trait IntoIterator { type Item; } trait Iterator { type Item; } struct Vec { } - struct u32 { } + struct Foo { } impl IntoIterator for Vec { type Item = T; } @@ -129,10 +129,10 @@ fn projection_equality() { trait Trait2 { } impl Trait2 for U where U: Trait1 {} - struct u32 {} + struct Foo {} struct S {} impl Trait1 for S { - type Type = u32; + type Type = Foo; } } @@ -142,7 +142,7 @@ fn projection_equality() { } } yields { // FIXME(rust-lang/chalk#234) -- there is really only one - // *reasonable* solution here, which is `u32`, but we get + // *reasonable* solution here, which is `Foo`, but we get // confused because `(Trait1::Type)` seems valid too. "Ambiguous; no inference guidance" } @@ -196,7 +196,7 @@ fn normalize_gat2() { trait StreamingIterator { type Item<'a>; } struct Span<'a, T> { } struct StreamIterMut { } - struct u32 { } + struct Foo { } impl StreamingIterator for StreamIterMut { type Item<'a> = Span<'a, T>; } @@ -281,8 +281,8 @@ fn normalize_gat_with_where_clause2() { type Item where U: Bar; } - struct i32 { } - impl Foo for i32 { + struct Baz { } + impl Foo for Baz { type Item = U; } } @@ -290,7 +290,7 @@ fn normalize_gat_with_where_clause2() { goal { forall { exists { - Normalize(>::Item -> V) + Normalize(>::Item -> V) } } } yields { @@ -301,7 +301,7 @@ fn normalize_gat_with_where_clause2() { forall { exists { if (U: Bar) { - Normalize(>::Item -> V) + Normalize(>::Item -> V) } } } @@ -316,15 +316,15 @@ fn normalize_gat_with_higher_ranked_trait_bound() { test! { program { trait Foo<'a, T> { } - struct i32 { } + struct Baz { } trait Bar<'a, T> { type Item: Foo<'a, T> where forall<'b> V: Foo<'b, T>; } - impl<'a, T> Foo<'a, T> for i32 { } - impl<'a, T> Bar<'a, T> for i32 { - type Item = i32; + impl<'a, T> Foo<'a, T> for Baz { } + impl<'a, T> Bar<'a, T> for Baz { + type Item = Baz; } } @@ -332,12 +332,12 @@ fn normalize_gat_with_higher_ranked_trait_bound() { forall<'a, T, V> { if (forall<'b> { V: Foo<'b, T> }) { exists { - Normalize(>::Item -> U) + Normalize(>::Item -> U) } } } } yields { - "Unique; substitution [?0 := i32], lifetime constraints []" + "Unique; substitution [?0 := Baz], lifetime constraints []" } } } @@ -594,9 +594,9 @@ fn projection_from_env_slow() { struct Slice where T: Sized { } impl Sized for Slice { } - struct u32 { } - impl Clone for u32 { } - impl Sized for u32 { } + struct Foo { } + impl Clone for Foo { } + impl Sized for Foo { } trait SliceExt where ::Item: Clone diff --git a/tests/test/wf_lowering.rs b/tests/test/wf_lowering.rs index 482e50fda4b..f55f0956e18 100644 --- a/tests/test/wf_lowering.rs +++ b/tests/test/wf_lowering.rs @@ -7,10 +7,10 @@ fn well_formed_trait_decl() { trait Clone { } trait Copy where Self: Clone { } - struct i32 { } + struct Foo { } - impl Clone for i32 { } - impl Copy for i32 { } + impl Clone for Foo { } + impl Copy for Foo { } } } } @@ -22,9 +22,9 @@ fn ill_formed_trait_decl() { trait Clone { } trait Copy where Self: Clone { } - struct i32 { } + struct Foo { } - impl Copy for i32 { } + impl Copy for Foo { } } error_msg { "trait impl for `Copy` does not meet well-formedness requirements" } @@ -94,15 +94,15 @@ fn ill_formed_assoc_ty() { trait Foo { } struct OnlyFoo where T: Foo { } - struct i32 { } + struct MyType { } trait Bar { type Value; } - impl Bar for i32 { - // `OnlyFoo` is ill-formed because `i32: Foo` does not hold. - type Value = OnlyFoo; + impl Bar for MyType { + // `OnlyFoo` is ill-formed because `MyType: Foo` does not hold. + type Value = OnlyFoo; } } error_msg { "trait impl for `Bar` does not meet well-formedness requirements" @@ -442,9 +442,9 @@ fn higher_ranked_trait_bounds() { program { trait Foo<'a> { } trait Bar where forall<'a> Self: Foo<'a> { } - struct i32 { } + struct Baz { } - impl Bar for i32 { } + impl Bar for Baz { } } error_msg { "trait impl for `Bar` does not meet well-formedness requirements" } @@ -454,10 +454,10 @@ fn higher_ranked_trait_bounds() { program { trait Foo<'a> { } trait Bar where forall<'a> Self: Foo<'a> { } - struct i32 { } + struct Baz { } - impl<'a> Foo<'a> for i32 { } - impl Bar for i32 { } + impl<'a> Foo<'a> for Baz { } + impl Bar for Baz { } } } } @@ -467,13 +467,13 @@ fn higher_ranked_trait_bound_on_gat() { lowering_success! { program { trait Foo<'a> { } - struct i32 { } + struct Baz { } trait Bar<'a> { type Item: Foo<'a> where forall<'b> V: Foo<'b>; } - impl<'a> Bar<'a> for i32 { + impl<'a> Bar<'a> for Baz { type Item = V; } } @@ -524,7 +524,7 @@ fn higher_ranked_inline_bound_on_gat() { program { trait Fn { } struct Ref<'a, T> { } - struct i32 {} + struct Val {} struct fun { } @@ -534,7 +534,7 @@ fn higher_ranked_inline_bound_on_gat() { type Item: forall<'a> Fn>; } - impl Bar for i32 { + impl Bar for Val { type Item = for<'a> fn(fun>); } } @@ -543,7 +543,7 @@ fn higher_ranked_inline_bound_on_gat() { lowering_error! { program { trait Fn { } - struct i32 {} + struct Val {} struct fun { } @@ -553,8 +553,8 @@ fn higher_ranked_inline_bound_on_gat() { type Item: forall Fn; } - impl Bar for i32 { - type Item = fun; + impl Bar for Val { + type Item = fun; } } error_msg { "trait impl for `Bar` does not meet well-formedness requirements" @@ -575,10 +575,10 @@ fn assoc_type_recursive_bound() { type Item: Sized where ::Item: Sized; } - struct i32 { } + struct Number { } struct str { } // not sized - impl Foo for i32 { + impl Foo for Number { // Well-formedness checks require that the following // goal is true: // ``` @@ -628,7 +628,7 @@ fn assoc_type_recursive_bound() { // } // fn main() { - // bar::() // ok, `Implemented(i32: Bar)` hold + // bar::() // ok, `Implemented(Number: Bar)` hold // } // ``` } error_msg { From 9a96759108d3c26607138fbf78a63cc957bd0b4f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 15 Apr 2020 22:22:34 -0700 Subject: [PATCH 3/7] Lower tuples correctly, clean up parsing --- chalk-integration/src/lowering.rs | 10 ++-- chalk-parse/src/ast.rs | 2 +- chalk-parse/src/parser.lalrpop | 25 +++------- tests/lowering/mod.rs | 82 ++++++++++++++++++++++++++++++- 4 files changed, 96 insertions(+), 23 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index be3a29f119a..ff4468d994c 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1074,14 +1074,18 @@ impl LowerTy for Ty { }; Ok(chalk_ir::TyData::Function(function).intern(interner)) } - Ty::Tuple { arity } => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { - name: chalk_ir::TypeName::Tuple(arity), - substitution: chalk_ir::Substitution::empty(interner), + Ty::Tuple { ref types } => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Tuple(types.len()), + substitution: chalk_ir::Substitution::from_fallible( + interner, + types.iter().map(|t| Ok(t.lower(env)?)), + )?, }) .intern(interner)), Ty::Scalar { ty } => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { name: chalk_ir::TypeName::Scalar(ast_scalar_to_chalk_scalar(ty)), + // substitution: chalk_ir::Substitution::from_fallible(interner, ty.lower(env)?)?, substitution: chalk_ir::Substitution::empty(interner), }) .intern(interner)), diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index 4b426e145f1..fe960690cb4 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -176,7 +176,7 @@ pub enum Ty { ty: Box, }, Tuple { - arity: usize, + types: Vec>, }, Scalar { ty: ScalarType, diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index a7fb859d93e..f949da2594c 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -212,10 +212,14 @@ ScalarType: ScalarType = { }; TupleOrParensInner: Ty = { - > => Ty::Tuple { arity: l.len() }, - "," => Ty::Tuple { arity: 1 }, , - () => Ty::Tuple { arity: 0 }, + "," > => { + let mut types = Vec::with_capacity(rest.len() + 1); + types.push(Box::new(first)); + types.extend(rest.into_iter().map(Box::new)); + Ty::Tuple { types } + }, + () => Ty::Tuple { types: vec![] }, }; Lifetime: Lifetime = { @@ -370,26 +374,11 @@ Separator1: Vec = { } }; -Separator2: Vec = { - S => vec![t1, t2], - S S > => { - let mut v = v; - v.push(t1); - v.push(t2); - v - } -}; - #[inline] Comma: Vec = { > }; -#[inline] -Comma2: Vec = { - > -}; - #[inline] SemiColon: Vec = { > diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index 8d624c54de9..37753ff2c23 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -470,9 +470,89 @@ fn scalars() { struct i32 { } } - // TODO: improve this error message error_msg { "parse error: UnrecognizedToken { token: (8, Token(45, \"i32\"), 11), expected: [\"r#\\\"([A-Za-z]|_)([A-Za-z0-9]|_)*\\\"#\"] }" } } } + +#[test] +fn tuple_trait_impl() { + let db = ChalkDatabase::with( + " +trait Foo { } +struct S1 { } +impl Foo for (S1, S1) { } +", + SolverChoice::default(), + ); + let goal = db.parse_and_lower_goal("(S1, S1): Foo").unwrap(); + db.with_program(|_| { + assert_eq!(format!("{:?}", goal), "Implemented(2: Foo)"); + }); + let db = ChalkDatabase::with( + " +trait Foo { } +impl Foo for (i32, i32, (i32,)) { } +", + SolverChoice::default(), + ); + let goal = db.parse_and_lower_goal("(i32, i32, (i32,)): Foo").unwrap(); + db.with_program(|_| { + assert_eq!( + format!("{:?}", goal), + "Implemented(3>: Foo)" + ); + }); +} + +#[test] +fn scalar_trait_impl() { + let db = ChalkDatabase::with( + " +trait Foo { } +impl Foo for usize { } +impl Foo for isize { } +impl Foo for (T1, T2) where T1: Foo, T2: Foo { } +impl Foo for (T,T,T) where T: Foo { } +", + SolverChoice::default(), + ); + let goal = db.parse_and_lower_goal("(usize, usize): Foo").unwrap(); + db.with_program(|_| { + assert_eq!( + format!("{:?}", goal), + "Implemented(2: Foo)" + ); + }); + let goal = db.parse_and_lower_goal("(usize, isize): Foo").unwrap(); + db.with_program(|_| { + assert_eq!( + format!("{:?}", goal), + "Implemented(2: Foo)" + ); + }); + let goal = db.parse_and_lower_goal("(usize, bool): Foo").unwrap(); + db.with_program(|_| { + // TODO: This should fail (Foo is not implemented for bool) + assert_eq!( + format!("{:?}", goal), + "Implemented(2: Foo)" + ); + }); + let goal = db.parse_and_lower_goal("(usize,usize,usize): Foo").unwrap(); + db.with_program(|_| { + assert_eq!( + format!("{:?}", goal), + "Implemented(3: Foo)" + ); + }); + let goal = db.parse_and_lower_goal("(char,u8,i8): Foo").unwrap(); + db.with_program(|_| { + // TODO: This should fail (the three types are not the same) + assert_eq!( + format!("{:?}", goal), + "Implemented(3: Foo)" + ); + }); +} From 2d6be4bd792044c9e23892deee7fb1fdf6d78eab Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 16 Apr 2020 08:24:15 -0700 Subject: [PATCH 4/7] Fix scalar, tuple tests to properly assert --- chalk-integration/src/lowering.rs | 1 - tests/lowering/mod.rs | 81 ---------------------------- tests/test/mod.rs | 2 + tests/test/scalars.rs | 87 +++++++++++++++++++++++++++++++ tests/test/tuples.rs | 36 +++++++++++++ 5 files changed, 125 insertions(+), 82 deletions(-) create mode 100644 tests/test/scalars.rs create mode 100644 tests/test/tuples.rs diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index ff4468d994c..a808ccef3e5 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1085,7 +1085,6 @@ impl LowerTy for Ty { Ty::Scalar { ty } => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { name: chalk_ir::TypeName::Scalar(ast_scalar_to_chalk_scalar(ty)), - // substitution: chalk_ir::Substitution::from_fallible(interner, ty.lower(env)?)?, substitution: chalk_ir::Substitution::empty(interner), }) .intern(interner)), diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index 37753ff2c23..8665a3f8465 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -475,84 +475,3 @@ fn scalars() { } } } - -#[test] -fn tuple_trait_impl() { - let db = ChalkDatabase::with( - " -trait Foo { } -struct S1 { } -impl Foo for (S1, S1) { } -", - SolverChoice::default(), - ); - let goal = db.parse_and_lower_goal("(S1, S1): Foo").unwrap(); - db.with_program(|_| { - assert_eq!(format!("{:?}", goal), "Implemented(2: Foo)"); - }); - let db = ChalkDatabase::with( - " -trait Foo { } -impl Foo for (i32, i32, (i32,)) { } -", - SolverChoice::default(), - ); - let goal = db.parse_and_lower_goal("(i32, i32, (i32,)): Foo").unwrap(); - db.with_program(|_| { - assert_eq!( - format!("{:?}", goal), - "Implemented(3>: Foo)" - ); - }); -} - -#[test] -fn scalar_trait_impl() { - let db = ChalkDatabase::with( - " -trait Foo { } -impl Foo for usize { } -impl Foo for isize { } -impl Foo for (T1, T2) where T1: Foo, T2: Foo { } -impl Foo for (T,T,T) where T: Foo { } -", - SolverChoice::default(), - ); - let goal = db.parse_and_lower_goal("(usize, usize): Foo").unwrap(); - db.with_program(|_| { - assert_eq!( - format!("{:?}", goal), - "Implemented(2: Foo)" - ); - }); - let goal = db.parse_and_lower_goal("(usize, isize): Foo").unwrap(); - db.with_program(|_| { - assert_eq!( - format!("{:?}", goal), - "Implemented(2: Foo)" - ); - }); - let goal = db.parse_and_lower_goal("(usize, bool): Foo").unwrap(); - db.with_program(|_| { - // TODO: This should fail (Foo is not implemented for bool) - assert_eq!( - format!("{:?}", goal), - "Implemented(2: Foo)" - ); - }); - let goal = db.parse_and_lower_goal("(usize,usize,usize): Foo").unwrap(); - db.with_program(|_| { - assert_eq!( - format!("{:?}", goal), - "Implemented(3: Foo)" - ); - }); - let goal = db.parse_and_lower_goal("(char,u8,i8): Foo").unwrap(); - db.with_program(|_| { - // TODO: This should fail (the three types are not the same) - assert_eq!( - format!("{:?}", goal), - "Implemented(3: Foo)" - ); - }); -} diff --git a/tests/test/mod.rs b/tests/test/mod.rs index f1db8e262cb..34a0fc55f38 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -270,5 +270,7 @@ mod impls; mod misc; mod negation; mod projection; +mod scalars; +mod tuples; mod unify; mod wf_goals; diff --git a/tests/test/scalars.rs b/tests/test/scalars.rs new file mode 100644 index 00000000000..439c0e24acb --- /dev/null +++ b/tests/test/scalars.rs @@ -0,0 +1,87 @@ +use super::*; + +#[test] +fn scalar_in_tuple_trait_impl() { + test! { + program { + trait Foo { } + impl Foo for usize { } + impl Foo for isize { } + impl Foo for (T1, T2) where T1: Foo, T2: Foo { } + impl Foo for (T,T,T) where T: Foo { } + } + + goal { + (usize, usize): Foo + } yields { + "Unique" + } + + goal { + (usize, isize): Foo + } yields { + "Unique" + } + + goal { + (usize, bool): Foo + } yields { + "No possible solution" + } + + goal { + (usize, usize, usize): Foo + } yields { + "Unique" + } + + goal { + (char, u8, i8): Foo + } yields { + "No possible solution" + } + } +} + +#[test] +fn scalar_trait_impl() { + test! { + program { + trait Foo { } + + impl Foo for i8 { } + impl Foo for i16 { } + impl Foo for i32 { } + impl Foo for i64 { } + impl Foo for i128 { } + impl Foo for isize { } + impl Foo for u8 { } + impl Foo for u16 { } + impl Foo for u32 { } + impl Foo for u64 { } + impl Foo for u128 { } + impl Foo for usize { } + impl Foo for f32 { } + impl Foo for f64 { } + impl Foo for bool { } + impl Foo for char { } + } + + goal { i8: Foo } yields { "Unique" } + goal { i16: Foo } yields { "Unique" } + goal { i32: Foo } yields { "Unique" } + goal { i64: Foo } yields { "Unique" } + goal { i128: Foo } yields { "Unique" } + goal { isize: Foo } yields { "Unique" } + goal { u8: Foo } yields { "Unique" } + goal { u16: Foo } yields { "Unique" } + goal { u32: Foo } yields { "Unique" } + goal { u64: Foo } yields { "Unique" } + goal { u128: Foo } yields { "Unique" } + goal { usize: Foo } yields { "Unique" } + goal { f32: Foo } yields { "Unique" } + goal { f64: Foo } yields { "Unique" } + goal { bool: Foo } yields { "Unique" } + goal { char: Foo } yields { "Unique" } + } +} diff --git a/tests/test/tuples.rs b/tests/test/tuples.rs new file mode 100644 index 00000000000..5542f8ae2d3 --- /dev/null +++ b/tests/test/tuples.rs @@ -0,0 +1,36 @@ +use super::*; + +#[test] +fn tuple_trait_impl() { + test! { + program { + trait Foo { } + struct S1 { } + impl Foo for (S1, S1) { } + impl Foo for () { } + } + goal { + (S1, S1): Foo + } yields { + "Unique" + } + + goal { + (): Foo + } yields { + "Unique" + } + } + test! { + program { + trait Foo { } + impl Foo for (i32, i32, (i32,)) { } + } + + goal { + (i32, i32, (i32, )): Foo + } yields { + "Unique" + } + } +} From fd150684729fd348b3913916ba63ba7b242487cf Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 16 Apr 2020 09:18:42 -0700 Subject: [PATCH 5/7] Fix up from merge; address feedback; some tests still failing --- chalk-ir/src/visit/boring_impls.rs | 12 ++++++++---- tests/test/projection.rs | 18 ++++++++---------- tests/test/scalars.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/chalk-ir/src/visit/boring_impls.rs b/chalk-ir/src/visit/boring_impls.rs index c2c4d01e240..5cb14be6912 100644 --- a/chalk-ir/src/visit/boring_impls.rs +++ b/chalk-ir/src/visit/boring_impls.rs @@ -5,10 +5,10 @@ //! The more interesting impls of `Visit` remain in the `visit` module. use crate::{ - AssocTypeId, ClausePriority, DebruijnIndex, Goals, ImplId, Interner, OpaqueTyId, Parameter, - ParameterKind, PlaceholderIndex, ProgramClause, ProgramClauseData, ProgramClauses, - QuantifiedWhereClauses, QuantifierKind, StructId, Substitution, SuperVisit, TraitId, - UniverseIndex, Visit, VisitResult, Visitor, + AssocTypeId, ClausePriority, DebruijnIndex, FloatTy, Goals, ImplId, IntTy, Interner, + OpaqueTyId, Parameter, ParameterKind, PlaceholderIndex, ProgramClause, ProgramClauseData, + ProgramClauses, QuantifiedWhereClauses, QuantifierKind, Scalar, StructId, Substitution, + SuperVisit, TraitId, UintTy, UniverseIndex, Visit, VisitResult, Visitor, }; use chalk_engine::{context::Context, ExClause, FlounderedSubgoal, Literal}; use std::{marker::PhantomData, sync::Arc}; @@ -207,6 +207,10 @@ const_visit!(chalk_engine::TableIndex); const_visit!(chalk_engine::TimeStamp); const_visit!(ClausePriority); const_visit!(()); +const_visit!(Scalar); +const_visit!(UintTy); +const_visit!(IntTy); +const_visit!(FloatTy); #[macro_export] macro_rules! id_visit { diff --git a/tests/test/projection.rs b/tests/test/projection.rs index 795af751fbc..c0673344008 100644 --- a/tests/test/projection.rs +++ b/tests/test/projection.rs @@ -129,10 +129,9 @@ fn projection_equality() { trait Trait2 { } impl Trait2 for U where U: Trait1 {} - struct Foo {} struct S {} impl Trait1 for S { - type Type = Foo; + type Type = u32; } } @@ -144,7 +143,7 @@ fn projection_equality() { // this is wrong, chalk#234 "Ambiguous" } yields[SolverChoice::recursive()] { - "Unique; substitution [?0 := u32]" + "Unique; substitution [?0 := Uint(U32)]" } goal { @@ -155,7 +154,7 @@ fn projection_equality() { // this is wrong, chalk#234 "Ambiguous" } yields[SolverChoice::recursive()] { - "Unique; substitution [?0 := u32]" + "Unique; substitution [?0 := Uint(U32)]" } } } @@ -168,7 +167,6 @@ fn projection_equality_priority1() { type Type; } - struct u32 {} struct S1 {} struct S2 {} struct S3 {} @@ -196,7 +194,7 @@ fn projection_equality_priority1() { // constrained `T` at all? I can't come up with // an example where that's the case, so maybe // not. -Niko - "Unique; substitution [?0 := S2, ?1 := u32]" + "Unique; substitution [?0 := S2, ?1 := Uint(U32)]" } } } @@ -277,7 +275,7 @@ fn projection_equality_priority2() { } yields[SolverChoice::recursive()] { // Constraining Out1 = S1 gives us only one choice, use the impl, // and the recursive solver prefers the normalized form. - "Unique; substitution [?0 := S1, ?1 := u32], lifetime constraints []" + "Unique; substitution [?0 := S1, ?1 := Uint(U32)], lifetime constraints []" } } } @@ -304,7 +302,7 @@ fn projection_equality_from_env() { // this is wrong, chalk#234 "Ambiguous" } yields[SolverChoice::recursive()] { - "Unique; substitution [?0 := u32]" + "Unique; substitution [?0 := Uint(U32)]" } } } @@ -334,7 +332,7 @@ fn projection_equality_nested() { // this is wrong, chalk#234 "Ambiguous" } yields[SolverChoice::recursive()] { - "Unique; substitution [?0 := u32]" + "Unique; substitution [?0 := Uint(U32)]" } } } @@ -378,7 +376,7 @@ fn iterator_flatten() { // this is wrong, chalk#234 "Ambiguous" } yields[SolverChoice::recursive()] { - "Unique; substitution [?0 := u32]" + "Unique; substitution [?0 := Uint(U32)]" } } } diff --git a/tests/test/scalars.rs b/tests/test/scalars.rs index 439c0e24acb..bbd91d521c2 100644 --- a/tests/test/scalars.rs +++ b/tests/test/scalars.rs @@ -48,6 +48,7 @@ fn scalar_trait_impl() { test! { program { trait Foo { } + trait UnsignedFoo { } impl Foo for i8 { } impl Foo for i16 { } @@ -65,6 +66,14 @@ fn scalar_trait_impl() { impl Foo for f64 { } impl Foo for bool { } impl Foo for char { } + + impl UnsignedFoo for u8 { } + impl UnsignedFoo for u16 { } + impl UnsignedFoo for u32 { } + impl UnsignedFoo for u64 { } + impl UnsignedFoo for u128 { } + impl UnsignedFoo for usize { } + } goal { i8: Foo } yields { "Unique" } @@ -83,5 +92,23 @@ fn scalar_trait_impl() { goal { f64: Foo } yields { "Unique" } goal { bool: Foo } yields { "Unique" } goal { char: Foo } yields { "Unique" } + + goal { i8: UnsignedFoo } yields { "No possible solution" } + goal { i16: UnsignedFoo } yields { "No possible solution" } + goal { i32: UnsignedFoo } yields { "No possible solution" } + goal { i64: UnsignedFoo } yields { "No possible solution" } + goal { i128: UnsignedFoo } yields { "No possible solution" } + goal { isize: UnsignedFoo } yields { "No possible solution" } + goal { u8: UnsignedFoo } yields { "Unique" } + goal { u16: UnsignedFoo } yields { "Unique" } + goal { u32: UnsignedFoo } yields { "Unique" } + goal { u64: UnsignedFoo } yields { "Unique" } + goal { u128: UnsignedFoo } yields { "Unique" } + goal { usize: UnsignedFoo } yields { "Unique" } + goal { f32: UnsignedFoo } yields { "No possible solution" } + goal { f64: UnsignedFoo } yields { "No possible solution" } + goal { bool: UnsignedFoo } yields { "No possible solution" } + goal { char: UnsignedFoo } yields { "No possible solution" } + } } From 2fd7fb7ef4ca6bfdb18ae29e48e0bea06f5939c9 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 16 Apr 2020 22:08:18 -0700 Subject: [PATCH 6/7] Implement WellFormed for scalars --- chalk-solve/src/clauses.rs | 6 ++++++ libstd.chalk | 2 -- tests/lowering/mod.rs | 2 +- tests/test/projection.rs | 7 ------- tests/test/scalars.rs | 24 ++++++++++++++++++++++++ 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index 20932413746..bd22b8ceacd 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -416,6 +416,12 @@ fn match_ty( ) -> Result<(), Floundered> { let interner = builder.interner(); Ok(match ty.data(interner) { + TyData::Apply(ApplicationTy { + name: TypeName::Scalar(_), + .. + }) => { + builder.push_fact(WellFormed::Ty(ty.clone())); + } TyData::Apply(application_ty) => match_type_name(builder, application_ty.name), TyData::Placeholder(_) => { builder.push_clause(WellFormed::Ty(ty.clone()), Some(FromEnv::Ty(ty.clone()))); diff --git a/libstd.chalk b/libstd.chalk index 6d94f246667..c94b6047924 100644 --- a/libstd.chalk +++ b/libstd.chalk @@ -10,12 +10,10 @@ trait Clone { } trait Copy where Self: Clone { } trait Sized { } -struct i32 { } impl Copy for i32 { } impl Clone for i32 { } impl Sized for i32 { } -struct u32 { } impl Copy for u32 { } impl Clone for u32 { } impl Sized for u32 { } diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index 8665a3f8465..10e17c38a39 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -471,7 +471,7 @@ fn scalars() { } error_msg { - "parse error: UnrecognizedToken { token: (8, Token(45, \"i32\"), 11), expected: [\"r#\\\"([A-Za-z]|_)([A-Za-z0-9]|_)*\\\"#\"] }" + "parse error: UnrecognizedToken { token: (8, Token(49, \"i32\"), 11), expected: [\"r#\\\"([A-Za-z]|_)([A-Za-z0-9]|_)*\\\"#\"] }" } } } diff --git a/tests/test/projection.rs b/tests/test/projection.rs index c0673344008..cde486084f1 100644 --- a/tests/test/projection.rs +++ b/tests/test/projection.rs @@ -207,7 +207,6 @@ fn projection_equality_priority2() { type Type; } - struct u32 {} struct S1 {} struct S2 {} struct S3 {} @@ -286,8 +285,6 @@ fn projection_equality_from_env() { trait Trait1 { type Type; } - - struct u32 {} } goal { @@ -314,8 +311,6 @@ fn projection_equality_nested() { trait Iterator { type Item; } - - struct u32 {} } goal { @@ -360,8 +355,6 @@ fn iterator_flatten() { { type Item = ::Item; } - - struct u32 {} } goal { diff --git a/tests/test/scalars.rs b/tests/test/scalars.rs index bbd91d521c2..d612e135f64 100644 --- a/tests/test/scalars.rs +++ b/tests/test/scalars.rs @@ -112,3 +112,27 @@ fn scalar_trait_impl() { } } + +#[test] +fn scalars_are_well_formed() { + test! { + program { } + + goal { WellFormed(i8) } yields { "Unique" } + goal { WellFormed(i16) } yields { "Unique" } + goal { WellFormed(i32) } yields { "Unique" } + goal { WellFormed(i64) } yields { "Unique" } + goal { WellFormed(i128) } yields { "Unique" } + goal { WellFormed(isize) } yields { "Unique" } + goal { WellFormed(u8) } yields { "Unique" } + goal { WellFormed(u16) } yields { "Unique" } + goal { WellFormed(u32) } yields { "Unique" } + goal { WellFormed(u64) } yields { "Unique" } + goal { WellFormed(u128) } yields { "Unique" } + goal { WellFormed(usize) } yields { "Unique" } + goal { WellFormed(f32) } yields { "Unique" } + goal { WellFormed(f64) } yields { "Unique" } + goal { WellFormed(bool) } yields { "Unique" } + goal { WellFormed(char) } yields { "Unique" } + } +} From 8d09a2d0ef6832ef61da0a623f287b6d569c1039 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 20 Apr 2020 20:52:49 -0700 Subject: [PATCH 7/7] Move WellFormed Scalar logic to match_type_name --- chalk-solve/src/clauses.rs | 20 ++++++++++---------- chalk-solve/src/clauses/env_elaborator.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index bd22b8ceacd..71d340824d9 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -416,13 +416,7 @@ fn match_ty( ) -> Result<(), Floundered> { let interner = builder.interner(); Ok(match ty.data(interner) { - TyData::Apply(ApplicationTy { - name: TypeName::Scalar(_), - .. - }) => { - builder.push_fact(WellFormed::Ty(ty.clone())); - } - TyData::Apply(application_ty) => match_type_name(builder, application_ty.name), + TyData::Apply(application_ty) => match_type_name(builder, interner, application_ty), TyData::Placeholder(_) => { builder.push_clause(WellFormed::Ty(ty.clone()), Some(FromEnv::Ty(ty.clone()))); } @@ -448,8 +442,12 @@ fn match_ty( }) } -fn match_type_name(builder: &mut ClauseBuilder<'_, I>, name: TypeName) { - match name { +fn match_type_name( + builder: &mut ClauseBuilder<'_, I>, + interner: &I, + application: &ApplicationTy, +) { + match application.name { TypeName::Struct(struct_id) => match_struct(builder, struct_id), TypeName::OpaqueType(opaque_ty_id) => builder .db @@ -460,7 +458,9 @@ fn match_type_name(builder: &mut ClauseBuilder<'_, I>, name: TypeNa .db .associated_ty_data(type_id) .to_program_clauses(builder), - TypeName::Scalar(_) => (), + TypeName::Scalar(_) => { + builder.push_fact(WellFormed::Ty(application.clone().intern(interner))) + } TypeName::Tuple(_) => (), } } diff --git a/chalk-solve/src/clauses/env_elaborator.rs b/chalk-solve/src/clauses/env_elaborator.rs index f0885ad6610..8589ddbe8fa 100644 --- a/chalk-solve/src/clauses/env_elaborator.rs +++ b/chalk-solve/src/clauses/env_elaborator.rs @@ -61,7 +61,7 @@ impl<'me, I: Interner> Visitor<'me, I> for EnvElaborator<'me, I> { let interner = self.db.interner(); match ty.data(interner) { TyData::Apply(application_ty) => { - match_type_name(&mut self.builder, application_ty.name) + match_type_name(&mut self.builder, interner, application_ty) } TyData::Alias(alias_ty) => match_alias_ty(&mut self.builder, alias_ty), TyData::Placeholder(_) => {}