From 8cced0f5b7ca6e537f8d47b55908d078a2577702 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 6 Feb 2020 20:20:44 -0500 Subject: [PATCH 01/70] Initial implementation of the Program Writer This is squashed from several WIP commits. The first being authored Jack Huey, and the following all authored by David Ross and Super Tuple. The majority of the structure here was decided and written by Super Tuple and David Ross. Co-authored-by: David Ross Co-authored-by: Super Tuple --- chalk-integration/src/db.rs | 13 + chalk-integration/src/lib.rs | 1 + chalk-integration/src/program.rs | 13 + chalk-integration/src/program_writer.rs | 232 +++++++++ chalk-ir/src/debug.rs | 20 + chalk-ir/src/lib.rs | 3 +- chalk-solve/src/display.rs | 593 ++++++++++++++++++++++++ chalk-solve/src/lib.rs | 11 + 8 files changed, 885 insertions(+), 1 deletion(-) create mode 100644 chalk-integration/src/program_writer.rs create mode 100644 chalk-solve/src/display.rs diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 880e7e8ec8f..5f60c655c54 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -197,4 +197,17 @@ impl RustIrDatabase for ChalkDatabase { .unwrap() .closure_fn_substitution(closure_id, substs) } + + fn trait_name(&self, trait_id: TraitId) -> String { + self.program_ir().unwrap().trait_name(trait_id) + } + + fn struct_name(&self, struct_id: StructId) -> String { + self.program_ir().unwrap().struct_name(struct_id) + } + + + fn identifier_name(&self, ident: &::Identifier) -> String { + self.program_ir().unwrap().identifier_name(ident) + } } diff --git a/chalk-integration/src/lib.rs b/chalk-integration/src/lib.rs index 1019f601567..f51fcaa93bb 100644 --- a/chalk-integration/src/lib.rs +++ b/chalk-integration/src/lib.rs @@ -7,6 +7,7 @@ pub mod interner; pub mod lowering; pub mod program; pub mod program_environment; +mod program_writer; pub mod query; pub mod tls; diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 33abafbc86f..6df90f0c6a8 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -2,6 +2,7 @@ use crate::interner::ChalkIr; use crate::{tls, Identifier, TypeKind}; use chalk_ir::could_match::CouldMatch; use chalk_ir::debug::Angle; +use chalk_ir::interner::{ Interner }; use chalk_ir::{ debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, Binders, ClosureId, FnDefId, GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, @@ -468,4 +469,16 @@ impl RustIrDatabase for Program { ) -> Substitution { substs.clone() } + + fn trait_name(&self, trait_id: TraitId) -> String { + self.trait_kinds.get(&trait_id).unwrap().name.to_string() + } + + fn struct_name(&self, struct_id: StructId) -> String { + self.struct_kinds.get(&struct_id).unwrap().name.to_string() + } + + fn identifier_name(&self, ident: &::Identifier) -> String { + ident.to_string() + } } diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs new file mode 100644 index 00000000000..f38e0b5262b --- /dev/null +++ b/chalk-integration/src/program_writer.rs @@ -0,0 +1,232 @@ +use chalk_solve::{ display::{ + WriterState, + RenderAsRust +} }; + +use crate::program::Program; + +pub trait WriteProgram { + fn write(&self) -> String; +} + +impl WriteProgram for Program { + fn write(&self) -> String { + let mut lines = vec![]; + let ws = WriterState::new(self); + self.struct_data.values().for_each(|datum| { + lines.push(datum.display(ws).to_string()); + }); + self.trait_data.values().for_each(|datum| { + lines.push(datum.display(ws).to_string()); + }); + self.impl_data.values().for_each(|datum| { + lines.push(datum.display(ws).to_string()); + }); + lines.join("\n") + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::lowering::LowerProgram; + use chalk_ir::tls; + use std::sync::Arc; + + #[test] + fn test_program_writer() { + let original_program_text = " + struct Foo { } + struct Vec { } + struct Map<_0, _1> { } + struct Ref<'a, T> { } + + trait Marker { } + impl Marker for Vec { } + + trait Clone { } + impl Clone for Foo { } + impl Clone for Vec where T: Clone { } + impl Clone for Map where T: Clone, U: Clone { } + + trait Deref<'a, U> { + type Assoc: Clone; + } + impl<'a, T, U> Deref<'a, T> for Ref<'a, U> { + type Assoc = Foo; + } + trait AssocWithGenerics { + type Assoc; + } + impl AssocWithGenerics for Foo { + type Assoc = Vec; + } + trait AssocTrait3 { + type Assoc; + } + impl AssocTrait3 for Vec { + type Assoc = Map; + } + trait AsRef { } + trait AssocTraitWithWhere { + type Assoc where U: AsRef; + } + impl AssocTraitWithWhere for Vec { + type Assoc = Map; + } + "; + + //trait A { } + //struct B { } + //struct C { } + //impl A for B> { } + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("{}", written); + }) + } + + #[test] + fn complicated_bounds() { + let original_program_text = " + struct Foo { } + trait Bar { } + trait Baz { } + trait Bax { type BaxT; } + trait Test { + type Assoc: Bar + Baz + Bax + where + Foo: Bax, + Foo: Bar, + dyn Bar: Baz; + } + "; + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("complicated_bounds:\n{}", written); + }) + } + #[test] + fn function_type() { + let original_program_text = " + struct Foo { } + trait Baz { } + impl Baz Foo> for Foo { } + "; + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("complicated_bounds:\n{}", written); + }) + } + #[test] + fn where_clauses_galore() { + let original_program_text = " + struct Foo where T: Baz, U: Bez { } + trait Baz { } + trait Bez { } + "; + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("complicated_bounds:\n{}", written); + }) + } + #[test] + fn use_as_clause() { + let original_program_text = " + struct Foo where >::Assoc: Baz { } + trait Baz { } + trait Bez { + type Assoc; + } + "; + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("complicated_bounds:\n{}", written); + }) + } + #[test] + fn placeholder_in_different_situations() { + let original_program_text = " + trait Baz<'a> {} + trait Bax<'a> {} + struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } + impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } + impl<'a> Bax<'a> for fn(Foo<'a>) { } + "; + /*struct Foo where forall<'a> U: Baz<'a> { } + trait Baz<'a> { } + trait Bez { + type Assoc Bez>; + }*/ + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("complicated_bounds:\n{}", written); + }) + } + #[test] + fn lifetimes_in_structs() { + let original_program_text = " + trait Baz<'a> {} + struct Foo<'b> { } + impl<'a> Baz<'a> for Foo<'a> { } + "; + /*struct Foo where forall<'a> U: Baz<'a> { } + trait Baz<'a> { } + trait Bez { + type Assoc Bez>; + }*/ + + let p = Arc::new( + chalk_parse::parse_program(&original_program_text) + .unwrap() + .lower() + .unwrap(), + ); + tls::set_current_program(&p, || { + let written = p.write(); + eprintln!("complicated_bounds:\n{}", written); + }) + } +} diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index 5781c902522..4d40ce01703 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -512,6 +512,8 @@ impl TraitRef { separator: ": ", } } + + pub fn as_impl(&self) -> impl std::fmt::Debug + '_ {} } impl Debug for TraitRef { @@ -556,6 +558,24 @@ impl<'a, 'me, I: Interner> Debug for SeparatorTraitRefDebug<'a, 'me, I> { } } +struct ImplTraitRef<'me, I: Interner> { + trait_ref: &'me TraitRef, + interner: &'me I, +} + +impl Debug for ImplTraitRef<'_, I> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + let parameters = self.trait_ref.substitution.parameters(self.interner); + write!( + fmt, + "impl{:?} {:?} for {:?}", + Angle(¶meters[1..]), + parameters[0], + self.trait_ref.trait_id, + ) + } +} + impl<'me, I: Interner> SeparatorTraitRef<'me, I> { /// Show debug output for the `SeperatorTraitRef`. pub fn debug<'a>(&'a self, interner: &'a I) -> SeparatorTraitRefDebug<'a, 'me, I> { diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 0a9f576d420..370f18b8f69 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -302,6 +302,7 @@ impl UniverseMap { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AdtId(pub I::InternedAdtId); + /// The id of a trait definition; could be used to load the trait datum by /// invoking the [`trait_datum`] method. /// @@ -455,7 +456,7 @@ pub enum TyData { /// an empty list). Apply(ApplicationTy), - /// instantiated form a universally quantified type, e.g., from + /// instantiated from a universally quantified type, e.g., from /// `forall { .. }`. Stands in as a representative of "some /// unknown type". Placeholder(PlaceholderIndex), diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs new file mode 100644 index 00000000000..84f1d7bfb45 --- /dev/null +++ b/chalk-solve/src/display.rs @@ -0,0 +1,593 @@ +use std::{ + fmt::{Display, Formatter, Result}, + sync::Arc, +}; + +use chalk_ir::{ + interner::Interner, AliasEq, AliasTy, ApplicationTy, Binders, BoundVar, Fn as ChalkFn, + LifetimeData, Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, TraitId, + TraitRef, Ty, TyData, TypeName, WhereClause, +}; + +use chalk_rust_ir::{ + AliasEqBound, AssociatedTyDatum, ImplDatum, InlineBound, QuantifiedInlineBound, StructDatum, + TraitBound, TraitDatum, +}; + +use crate::{split::Split, RustIrDatabase}; + +/// Displays `RenderAsRust` data. +/// +/// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. +pub struct DisplayRenderAsRust<'a, I: Interner, T> { + s: WriterState<'a, I>, + rar: &'a T, +} + +impl> Display for DisplayRenderAsRust<'_, I, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + self.rar.fmt(self.s, f) + } +} + +fn as_display) -> Result>(f: F) -> impl Display { + struct ClosureDisplay) -> Result>(F); + + impl) -> Result> Display for ClosureDisplay { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + self.0(f) + } + } + + ClosureDisplay(f) +} + +pub trait RenderAsRust { + fn fmt(&self, s: WriterState<'_, I>, f: &mut Formatter<'_>) -> Result; + fn display<'a>(&'a self, s: WriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> + where + Self: Sized, + { + DisplayRenderAsRust { s, rar: self } + } +} + +impl RenderAsRust for ImplDatum { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + dbg!(&self); + let binders: Vec<_> = s.binder_struct_var_names(&self.binders).collect(); + + let trait_ref = &self.binders.value.trait_ref; + + let binders_name = if binders.len() == 0 { + "".to_string() + } else { + format!("<{}>", binders.join(", ")) + }; + // Ignore automatically added Self parameter by skipping first parameter + let full_trait_name = display_trait_with_generics( + s, + trait_ref.trait_id, + &trait_ref.substitution.parameters(interner)[1..], + ); + write!( + f, + "impl{} {} for {}", + binders_name, + full_trait_name, + trait_ref + .self_type_parameter(interner) + .data(interner) + .display(s) + )?; + + Ok(()) + } +} + +impl RenderAsRust for InlineBound { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + // Foo: Vec + InlineBound::TraitBound(trait_bound) => trait_bound.fmt(s, f), + // Foo: Iterator + InlineBound::AliasEqBound(eq_bound) => eq_bound.fmt(s, f), + } + } +} + +impl RenderAsRust for TraitBound { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + display_trait_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) + } +} + +impl RenderAsRust for AliasEqBound { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + display_trait_with_assoc_ty_value( + s, + s.db.associated_ty_data(self.associated_ty_id), + &self.trait_bound.args_no_self, + &self.parameters, + &self.value, + ) + .fmt(f) + } +} + +impl RenderAsRust for TyData { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + match self { + TyData::Dyn(dyn_ty) => write!( + f, + "dyn {}", + dyn_ty + .bounds + .value + .iter() + .map(|bound| { + match &bound.value { + WhereClause::Implemented(trait_ref) => display_trait_with_generics( + s, + trait_ref.trait_id, + &trait_ref.substitution.parameters(interner)[1..], + ) + .to_string(), + WhereClause::AliasEq(alias_eq) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&alias_eq.alias); + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &alias_eq.ty, + ) + .to_string() + } + } + }) + .collect::>() + .join(" + ") + ), + TyData::BoundVar(bound_var) => write!(f, "{}", s.name_for_bound_var(bound_var)), + TyData::Alias(alias_ty) => alias_ty.fmt(s, f), + TyData::Apply(apply_ty) => apply_ty.fmt(s, f), + TyData::Placeholder(_) => write!(f, "{}", "todo-placeholder"), + TyData::Function(func) => func.fmt(s, f), + TyData::InferenceVar(_) => write!(f, "{}", "todo-inferenceVar"), + } + } +} + +impl RenderAsRust for AliasTy { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + // >::Z + + // Now, we split out A*, Y/Z and B*: + // trait_params is X, A1, A2, A3, + // assoc_type_params is B1, B2, B3, + // assoc_ty_datum stores info about Y and Z. + + let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self); + write!( + f, + "<{} as {}>::{}<{}>", + trait_params[0].data(interner).display(s), + display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), + s.db.identifier_name(&assoc_ty_datum.name), + assoc_type_params + .iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", ") + ) + } +} + +impl RenderAsRust for ChalkFn { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + let s = s.add_debrujin_index(); + if self.num_binders > 0 { + write!( + f, + "for<{}> ", + (0..self.num_binders).map(|n| format!("'{}", s.name_for_introduced_bound_var(n))).collect::>().join(", ") + )?; + } + write!( + f, + "fn({})", + self.parameters + .iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", ") + ) + } +} + +impl RenderAsRust for ApplicationTy { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + match self.name { + TypeName::Struct(sid) => write!( + f, + "{}<{}>", + s.db.struct_name(sid), + self.substitution + .parameters(interner) + .iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", "), + ), + TypeName::AssociatedType(assoc_type_id) => { + // (Iterator::Item)(x) + // should be written in Rust as ::Item + let datum = s.db.associated_ty_data(assoc_type_id); + assert!( + self.len_type_parameters(interner) >= 1, + "AssociatedType should have at least 1 parameter" + ); + write!( + f, + "<{} as {}>::{}<{}>", + self.first_type_parameter(interner) + .unwrap() + .data(interner) + .display(s), + s.db.trait_name(datum.trait_id), + s.db.identifier_name(&datum.name), + self.substitution.parameters(interner)[1..] + .iter() + .map(|ty| ty.data(interner).display(s).to_string()) + .collect::>() + .join(", "), + ) + } + TypeName::Error => write!(f, "{{error}}"), + } + } +} + +impl RenderAsRust for LifetimeData { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + LifetimeData::BoundVar(v) => write!(f, "'{}", s.name_for_bound_var(v)), + LifetimeData::InferenceVar(_) => write!(f, "'_"), + _ => write!(f, "liftimeother-todo3"), + } + } +} + +impl RenderAsRust for ParameterData { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + match self { + ParameterKind::Ty(ty) => write!(f, "{}", ty.data(interner).display(s)), + ParameterKind::Lifetime(lt) => write!(f, "{}", lt.data(interner).display(s)), + } + } +} + +impl RenderAsRust for QuantifiedWhereClause { + fn fmt(&self, mut s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // if we have any binders, then we're a "for<'a> X: Y" "quantified" + // where clause. Otherwise, we're just a regular where clause. + // TODO: do we actually want to exclude the added index if there is no for<>? + //if !self.binders.is_empty() { + //} + s = s.add_debrujin_index(); + self.value.fmt(s, f) + } +} + +impl RenderAsRust for QuantifiedInlineBound { + fn fmt(&self, mut s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: do we want to do something similar to QuantifiedWhereClause here? + s = s.add_debrujin_index(); + self.value.fmt(s, f) + } +} + +impl RenderAsRust for Vec> { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + write!( + f, + "{}", + self.iter() + .map(|where_clause| { where_clause.display(s).to_string() }) + .collect::>() + .join(", ") + )?; + Ok(()) + } +} + +impl RenderAsRust for WhereClause { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + WhereClause::Implemented(trait_ref) => trait_ref.fmt(s, f), + WhereClause::AliasEq(alias_eq) => alias_eq.fmt(s, f), + } + } +} + +/// Displays a trait with its parameters - something like `AsRef`. +/// +/// This is shared between where bounds & dyn Trait. +fn display_trait_with_generics<'a, I: Interner>( + s: WriterState<'a, I>, + trait_id: TraitId, + trait_params: impl IntoIterator> + 'a, +) -> impl Display + 'a { + let interner = s.db.interner(); + let trait_params = trait_params + .into_iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", "); + as_display(move |f| write!(f, "{}<{}>", s.db.trait_name(trait_id), trait_params,)) +} + +/// This implementation correct inside where clauses. +impl RenderAsRust for TraitRef { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + write!( + f, + "{}: {}", + self.self_type_parameter(interner).data(interner).display(s), + display_trait_with_generics( + s, + self.trait_id, + &self.substitution.parameters(interner)[1..] + ) + ) + } +} + +/// Displays a trait with its parameters and a single associated type - +/// something like `IntoIterator`. +/// +/// This is shared between where bounds & dyn Trait. +fn display_trait_with_assoc_ty_value<'a, I: Interner>( + s: WriterState<'a, I>, + assoc_ty_datum: Arc>, + trait_params: &'a [Parameter], + assoc_ty_params: &'a [Parameter], + assoc_ty_value: &'a Ty, +) -> impl Display + 'a { + let interner = s.db.interner(); + as_display(move |f| { + write!( + f, + "{}<{}, {}<{}>={}>", + s.db.trait_name(assoc_ty_datum.trait_id), + trait_params + .iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", "), + s.db.identifier_name(&assoc_ty_datum.name), + assoc_ty_params + .iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", "), + assoc_ty_value.data(interner).display(s) + ) + }) +} + +/// This implementation correct inside where clauses. +impl RenderAsRust for AliasEq { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + // we have: X: Y=D> + // B1, B2, B3, X, A1, A2, A3 are put into alias_eq.alias.substitution + // D is alias_eq.ty + // Z is alias_eq.alias.associated_ty_id + // Y is also packed into alias_eq.alias.associated_ty_id + // Now, we split out A*, Y/Z and B*: + // trait_params is X, A1, A2, A3, + // assoc_type_params is B1, B2, B3, + // assoc_ty_datum stores info about Y and Z. + let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self.alias); + // An alternate form might be `<{} as {}<{}>>::{}<{}> = {}` (with same + // parameter ordering). This alternate form would be using type equality + // constraints (https://github.com/rust-lang/rust/issues/20041). + write!( + f, + "{}: {}", + trait_params[0].data(interner).display(s), + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &self.ty + ), + ) + } +} + +impl RenderAsRust for AssociatedTyDatum { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let binders: Vec<_> = s.binder_var_names(&self.binders).collect(); + + write!( + f, + "{}<{}>", + s.db.identifier_name(&self.name), + binders.join(", ") + )?; + + let datum_bounds = &self.binders.value; + + if !(datum_bounds.bounds.is_empty() && datum_bounds.where_clauses.is_empty()) { + write!(f, ": ")?; + } + + // bounds is `A: V, B: D, C = E`? + // type Foo: X + Y + Z; + let bounds = datum_bounds + .bounds + .iter() + .map(|bound| bound.display(s).to_string()) + .collect::>() + .join(" + "); + write!(f, "{}", bounds)?; + + // where_clause is 'X: Y, Z: D' + // type Foo<...>: ... where X: Y, Z: D; + + // note: it's a quantified clause b/c we could have `for<'a> T: Foo<'a>` + // within 'where' + if !datum_bounds.where_clauses.is_empty() { + let where_clauses = datum_bounds.where_clauses.display(s); + write!(f, "\nwhere\n{}", where_clauses)?; + } + write!(f, ";")?; + Ok(()) + } +} + +impl RenderAsRust for TraitDatum { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let trait_name = s.db.trait_name(self.id); + if self.binders.len() == 0 { + write!(f, "trait {} {{}}", trait_name) + } else { + let binders: Vec<_> = s.binder_var_names(&self.binders).collect(); + let assoc_types = self + .associated_ty_ids + .iter() + .map(|assoc_ty_id| { + let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); + assoc_ty_data.display(s).to_string() + }) + .collect::(); + write!( + f, + "trait {}<{}> {{{}}}", + trait_name, + binders.join(", "), + assoc_types + ) + } + } +} + +impl RenderAsRust for StructDatum { + fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + let s = s.add_debrujin_index(); + write!( + f, + "struct {}<{}> ", + s.db.struct_name(self.id), + s.binder_struct_var_names(&self.binders) + .collect::>() + .join(", ") + )?; + if !self.binders.value.where_clauses.is_empty() { + write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + } + write!( + f, + "{{{}}}", + self.binders + .value + .fields + .iter() + .map(|field| { field.data(interner).display(s).to_string() }) + .collect::>() + .join(", ") + )?; + Ok(()) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct WriterState<'a, I: Interner> { + db: &'a dyn RustIrDatabase, + debrujin_indices_deep: u32, +} + +impl<'a, I: Interner> WriterState<'a, I> { + pub fn new(db: &'a dyn RustIrDatabase) -> Self { + WriterState { + db, + debrujin_indices_deep: 0, + } + } + + fn add_debrujin_index(&self) -> Self { + let mut new_state = self.clone(); + new_state.debrujin_indices_deep += 1; + new_state + } + + fn name_for_bound_var(&self, b: &BoundVar) -> impl Display + '_ { + // invert debrujin indexes so that we have a canonical name in rust + // source. After this, the outermost thing is '0', and the innermost is + // labeled by its depth, not the other way around. + // let debrujin_index_name = self + // .debrujin_indices_deep + // .checked_sub(b.debruijn.depth()) + // .expect("found debrujin index deeper than we thought possible"); + let debrujin_index_name = (self.debrujin_indices_deep as i64) - (b.debruijn.depth() as i64); + let within_debrujin = b.index; + as_display(move |f| write!(f, "_{}_{}", debrujin_index_name, within_debrujin)) + } + + fn name_for_introduced_bound_var(&self, idx: usize) -> impl Display + '_ { + // invert debrujin indexes so that we have a canonical name in rust + // source. After this, the outermost thing is '0', and the innermost is + // labeled by its depth, not the other way around. + + // freshly introduced bound vars will always have debrujin index of 0, + // they're always "innermost". + let debrujin_index_name = self.debrujin_indices_deep; + let within_debrujin = idx; + as_display(move |f| write!(f, "_{}_{}", debrujin_index_name, within_debrujin)) + } + + fn binder_var_names<'b, T: 'b>( + &'b self, + binders: &'b Binders, + ) -> impl Iterator + 'b { + binders.binders[1..] + .iter() + .enumerate() + .map(move |(idx, parameter)| match parameter { + ParameterKind::Ty(_) => format!("{}", self.name_for_introduced_bound_var(idx)), + ParameterKind::Lifetime(_) => { + format!("'{}", self.name_for_introduced_bound_var(idx)) + } + }) + } + + fn binder_struct_var_names<'b, T: 'b>( + &'b self, + binders: &'b Binders, + ) -> impl Iterator + 'b { + binders + .binders + .iter() + .enumerate() + .map(move |(idx, parameter)| match parameter { + ParameterKind::Ty(_) => format!("{}", self.name_for_introduced_bound_var(idx)), + ParameterKind::Lifetime(_) => { + format!("'{}", self.name_for_introduced_bound_var(idx)) + } + }) + } +} diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 17deb4b8b0d..5d571e40187 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -14,6 +14,7 @@ mod test_macros; pub mod clauses; pub mod coherence; mod coinductive_goal; +pub mod display; pub mod ext; pub mod goal_builder; mod infer; @@ -139,6 +140,16 @@ pub trait RustIrDatabase: Debug { closure_id: ClosureId, substs: &Substitution, ) -> Substitution; + + /// Retrieves a trait's original name. No uniqueness guarantees. + /// TODO: remove this, use only interner debug methods + fn trait_name(&self, trait_id: TraitId) -> String; + + /// Retrieves a struct's original name. No uniqueness guarantees. + fn struct_name(&self, struct_id: StructId) -> String; + + /// Retrieves the name of an identifier + fn identifier_name(&self, ident: &I::Identifier) -> String; } pub use clauses::program_clauses_for_env; From aa91bba1756957687186c88a49899cba506cec26 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 18 Apr 2020 18:17:15 -0700 Subject: [PATCH 02/70] Small cleanup Co-authored-by: David Ross --- chalk-ir/src/debug.rs | 20 -------------------- chalk-ir/src/lib.rs | 1 - 2 files changed, 21 deletions(-) diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index 4d40ce01703..5781c902522 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -512,8 +512,6 @@ impl TraitRef { separator: ": ", } } - - pub fn as_impl(&self) -> impl std::fmt::Debug + '_ {} } impl Debug for TraitRef { @@ -558,24 +556,6 @@ impl<'a, 'me, I: Interner> Debug for SeparatorTraitRefDebug<'a, 'me, I> { } } -struct ImplTraitRef<'me, I: Interner> { - trait_ref: &'me TraitRef, - interner: &'me I, -} - -impl Debug for ImplTraitRef<'_, I> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { - let parameters = self.trait_ref.substitution.parameters(self.interner); - write!( - fmt, - "impl{:?} {:?} for {:?}", - Angle(¶meters[1..]), - parameters[0], - self.trait_ref.trait_id, - ) - } -} - impl<'me, I: Interner> SeparatorTraitRef<'me, I> { /// Show debug output for the `SeperatorTraitRef`. pub fn debug<'a>(&'a self, interner: &'a I) -> SeparatorTraitRefDebug<'a, 'me, I> { diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 370f18b8f69..8284ef952f3 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -302,7 +302,6 @@ impl UniverseMap { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AdtId(pub I::InternedAdtId); - /// The id of a trait definition; could be used to load the trait datum by /// invoking the [`trait_datum`] method. /// From bcfecd20266c34e67bacaf430963080f1fc9c474 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 25 Apr 2020 18:42:17 -0700 Subject: [PATCH 03/70] Add reparse tests for ProgramWriter This adds some tests that parse, write and then reparse the program. This commit adds these tests, and some bugfixes based on the finidngs from the tests. Co-authored-by: David Ross --- Cargo.lock | 1 + chalk-integration/Cargo.toml | 4 + chalk-integration/src/program_writer.rs | 440 ++++++++++++++++++------ chalk-solve/src/display.rs | 252 ++++++++------ 4 files changed, 498 insertions(+), 199 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af3fee1cbed..61636ddbf5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,6 +203,7 @@ dependencies = [ "chalk-ir 0.16.0-dev.0", "chalk-parse 0.16.0-dev.0", "chalk-solve 0.16.0-dev.0", + "diff 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "salsa 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "tracing 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/chalk-integration/Cargo.toml b/chalk-integration/Cargo.toml index 03809616549..87123888d57 100644 --- a/chalk-integration/Cargo.toml +++ b/chalk-integration/Cargo.toml @@ -19,3 +19,7 @@ chalk-engine = { version = "0.16.0-dev.0", path = "../chalk-engine" } chalk-ir = { version = "0.16.0-dev.0", path = "../chalk-ir" } chalk-solve = { version = "0.16.0-dev.0", path = "../chalk-solve" } chalk-parse = { version = "0.16.0-dev.0", path = "../chalk-parse" } + +[dev-dependencies] +# used for program_writer test errors +diff = "0.1" diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index f38e0b5262b..beb96e1261a 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -1,7 +1,4 @@ -use chalk_solve::{ display::{ - WriterState, - RenderAsRust -} }; +use chalk_solve::display::{RenderAsRust, WriterState}; use crate::program::Program; @@ -31,66 +28,355 @@ mod test { use super::*; use crate::lowering::LowerProgram; use chalk_ir::tls; - use std::sync::Arc; + use std::{fmt::Debug, sync::Arc}; + + fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { + use std::fmt::Write; + + let mut out = String::new(); + let original = format!("{:#?}", original); + let produced = format!("{:#?}", produced); + for line in diff::lines(&original, &produced) { + match line { + diff::Result::Left(l) => write!(out, "-{}\n", l), + diff::Result::Both(l, _) => write!(out, " {}\n", l), + diff::Result::Right(r) => write!(out, "+{}\n", r), + } + .expect("writing to string never fails"); + } + out + } + + fn reparse_test(program_text: &str) { + let original_program = match chalk_parse::parse_program(program_text) { + Ok(v) => v, + Err(e) => panic!( + "unable to parse test program:\n{}\nSource:\n{}\n", + e, program_text + ), + }; + let original_program = Arc::new(original_program.lower().unwrap()); + let new_text = tls::set_current_program(&original_program, || original_program.write()); + let new_program = match chalk_parse::parse_program(&new_text) { + Ok(v) => v, + Err(e) => panic!( + "unable to reparse writer output:\n{}\nNew source:\n{}\n", + e, new_text + ), + }; + let new_program = match new_program.lower() { + Ok(v) => v, + Err(e) => panic!( + "error lowering writer output:\n{}\nNew source:\n{}\n", + e, new_text + ), + }; + if new_program != *original_program { + panic!( + "WriteProgram produced different program.\n\ + Diff:\n{}\n\ + Source:\n{}\n + New Source:\n{}\n", + program_diff(&original_program, &new_program), + program_text, + new_text + ); + } + } + + #[test] + fn test_simple_structs_and_bounds() { + reparse_test("struct Foo {}"); + reparse_test("struct Foo {}"); + // note: the order here matters! Traits must be after structs. + reparse_test( + " + struct Foo where T: Trait {} + trait Trait {} + ", + ); + } + + #[test] + fn test_simple_traits_and_bounds() { + reparse_test("trait Foo {}"); + reparse_test("trait Foo {}"); + reparse_test( + " + trait Foo where T: Trait {} + trait Trait {} + ", + ); + } + + #[test] + #[ignore] + fn test_self_in_where() { + reparse_test( + " + trait Baz<'a> {} + trait Foo where forall<'a> Self: Baz<'a> {} + ", + ); + } + + #[test] + fn test_forall_in_where() { + reparse_test( + " + trait Bax {} + trait Foo where forall T: Bax {} + ", + ); + reparse_test( + " + trait Buz<'a> {} + trait Foo where forall<'a> T: Buz<'a> {} + ", + ); + reparse_test( + " + struct Foo where forall T: Biz {} + trait Biz {} + ", + ); + reparse_test( + " + struct Foo where forall<'a> T: Bez<'a> {} + trait Bez<'a> {} + ", + ); + } + #[test] + fn test_forall_in_dyn() { + reparse_test( + " + trait Foo {} + trait Bar<'a> {} + impl Foo for dyn forall<'a> Bar<'a> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a> Baz<'a> + } + trait Baz<'a> {} + ", + ); + reparse_test( + " + trait Foo {} + trait Bax<'a, 'b> {} + impl Foo for dyn forall<'a, 'b> Bax<'a, 'b> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a, 'b> Bix<'a, 'b> + } + trait Bix<'a, 'b> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> + } + trait Bex<'a> {} + trait Byx<'a> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> + } + trait Bux<'a, 'b> {} + trait Brx<'a, 'b> {} + ", + ); + reparse_test( + " + struct Foo<'a> { + field: dyn forall<'b> Bpx<'a, 'b> + } + trait Bpx<'a, 'b> {} + ", + ); + } + + #[test] + fn test_simple_dyn() { + reparse_test( + " + struct Foo { + field: dyn Bax, + } + trait Bax {} + ", + ); + reparse_test( + " + struct Foo<'a> { + field: dyn Bix<'a>, + } + trait Bix<'a> {} + ", + ); + } + + #[test] + fn test_simple_assoc_type() { + reparse_test( + " + trait Foo { + type Assoc; + } + ", + ); + reparse_test( + " + trait Byz {} + trait Buzz {} + trait Foo { + type Assoc: Byz + Buzz; + } + ", + ); + } + + #[test] + fn test_simple_generic_assoc_type() { + reparse_test( + " + trait Trait {} + trait Foo { + type Assoc; + } + ", + ); + reparse_test( + " + trait Trait {} + trait Foo { + type Assoc: Trait; + } + ", + ); + reparse_test( + " + trait Trait {} + trait Foo { + type Assoc where Y: Trait; + } + ", + ); + } + + #[test] + fn test_assoc_type_in_generic_trait() { + reparse_test( + " + trait Foo { + type Assoc; + } + ", + ); + reparse_test( + " + trait Fou { + type Assoc; + } + ", + ); + reparse_test( + " + trait Bax {} + trait Foo { + type Assoc where T: Bax; + } + ", + ); + reparse_test( + " + trait Bix {} + trait Foo { + type Assoc where Y: Bix; + } + ", + ); + reparse_test( + " + trait Bix<_0> {} + trait Foo<_0_1> { + type Assoc<_1_1, _1_2> where _1_1: Bix<_1_2>; + } + ", + ); + } + + + #[test] + fn test_struct_fields() { + reparse_test( + " + struct Foo {} + struct Bar {} + struct Baz { + x: Foo, + b: Bar + } + ", + ); + } #[test] fn test_program_writer() { - let original_program_text = " + reparse_test( + " struct Foo { } struct Vec { } struct Map<_0, _1> { } struct Ref<'a, T> { } trait Marker { } - impl Marker for Vec { } - trait Clone { } - impl Clone for Foo { } - impl Clone for Vec where T: Clone { } - impl Clone for Map where T: Clone, U: Clone { } - trait Deref<'a, U> { type Assoc: Clone; } - impl<'a, T, U> Deref<'a, T> for Ref<'a, U> { - type Assoc = Foo; - } trait AssocWithGenerics { type Assoc; } - impl AssocWithGenerics for Foo { - type Assoc = Vec; - } trait AssocTrait3 { type Assoc; } - impl AssocTrait3 for Vec { - type Assoc = Map; - } trait AsRef { } + trait AssocTraitWithWhere { type Assoc where U: AsRef; } + + impl Marker for Vec { } + impl Clone for Foo { } + impl Clone for Vec where T: Clone { } + impl Clone for Map where T: Clone, U: Clone { } + + impl<'a, T, U> Deref<'a, T> for Ref<'a, U> { + type Assoc = Foo; + } + impl AssocWithGenerics for Foo { + type Assoc = Vec; + } + impl AssocTrait3 for Vec { + type Assoc = Map; + } impl AssocTraitWithWhere for Vec { type Assoc = Map; } - "; - - //trait A { } - //struct B { } - //struct C { } - //impl A for B> { } - - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("{}", written); - }) } #[test] @@ -122,22 +408,13 @@ mod test { } #[test] fn function_type() { - let original_program_text = " + reparse_test( + " struct Foo { } trait Baz { } - impl Baz Foo> for Foo { } - "; - - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + impl Baz for Foo { } + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("complicated_bounds:\n{}", written); - }) } #[test] fn where_clauses_galore() { @@ -160,73 +437,40 @@ mod test { } #[test] fn use_as_clause() { - let original_program_text = " + reparse_test( + " struct Foo where >::Assoc: Baz { } trait Baz { } trait Bez { type Assoc; } - "; - - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("complicated_bounds:\n{}", written); - }) } #[test] fn placeholder_in_different_situations() { - let original_program_text = " + reparse_test( + " + struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } trait Baz<'a> {} trait Bax<'a> {} - struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } + trait Biz { + type Bex: forall<'a> Bax<'a>; + } impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } impl<'a> Bax<'a> for fn(Foo<'a>) { } - "; - /*struct Foo where forall<'a> U: Baz<'a> { } - trait Baz<'a> { } - trait Bez { - type Assoc Bez>; - }*/ - - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> { } + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("complicated_bounds:\n{}", written); - }) } #[test] fn lifetimes_in_structs() { - let original_program_text = " - trait Baz<'a> {} + reparse_test( + " struct Foo<'b> { } + trait Baz<'a> {} impl<'a> Baz<'a> for Foo<'a> { } - "; - /*struct Foo where forall<'a> U: Baz<'a> { } - trait Baz<'a> { } - trait Bez { - type Assoc Bez>; - }*/ - - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("complicated_bounds:\n{}", written); - }) } } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 84f1d7bfb45..9a6d7def7d9 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -4,9 +4,9 @@ use std::{ }; use chalk_ir::{ - interner::Interner, AliasEq, AliasTy, ApplicationTy, Binders, BoundVar, Fn as ChalkFn, - LifetimeData, Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, TraitId, - TraitRef, Ty, TyData, TypeName, WhereClause, + interner::Interner, AliasEq, AliasTy, ApplicationTy, BoundVar, Fn as ChalkFn, LifetimeData, + Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, TraitId, TraitRef, Ty, TyData, + TypeName, WhereClause, }; use chalk_rust_ir::{ @@ -55,8 +55,7 @@ pub trait RenderAsRust { impl RenderAsRust for ImplDatum { fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); - dbg!(&self); - let binders: Vec<_> = s.binder_struct_var_names(&self.binders).collect(); + let binders: Vec<_> = s.binder_var_names(&self.binders.binders).collect(); let trait_ref = &self.binders.value.trait_ref; @@ -73,7 +72,7 @@ impl RenderAsRust for ImplDatum { ); write!( f, - "impl{} {} for {}", + "impl{} {} for {} {{}}", binders_name, full_trait_name, trait_ref @@ -81,6 +80,7 @@ impl RenderAsRust for ImplDatum { .data(interner) .display(s) )?; + // TODO: print associated types, self.associated_ty_value_ids Ok(()) } @@ -99,7 +99,6 @@ impl RenderAsRust for InlineBound { impl RenderAsRust for TraitBound { fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); display_trait_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) } } @@ -121,44 +120,66 @@ impl RenderAsRust for TyData { fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); match self { - TyData::Dyn(dyn_ty) => write!( - f, - "dyn {}", - dyn_ty - .bounds - .value - .iter() - .map(|bound| { - match &bound.value { - WhereClause::Implemented(trait_ref) => display_trait_with_generics( - s, - trait_ref.trait_id, - &trait_ref.substitution.parameters(interner)[1..], - ) - .to_string(), - WhereClause::AliasEq(alias_eq) => { - let (assoc_ty_datum, trait_params, assoc_type_params) = - s.db.split_projection(&alias_eq.alias); - display_trait_with_assoc_ty_value( - s, - assoc_ty_datum, - &trait_params[1..], - assoc_type_params, - &alias_eq.ty, - ) - .to_string() - } - } - }) - .collect::>() - .join(" + ") - ), + TyData::Dyn(dyn_ty) => { + let s = s.add_debrujin_index(); + write!(f, "dyn ")?; + // dyn_ty.bounds.binders creates a Self binding for the trait + write!( + f, + "{}", + dyn_ty + .bounds + .value + .iter() + .map(|bound| { + as_display(|f| { + // each individual trait within the 'dyn' can have a + // forall clause. + let s = s.add_debrujin_index(); + if !bound.binders.is_empty() { + write!( + f, + "forall<{}> ", + s.binder_var_names(&bound.binders) + .collect::>() + .join(", ") + )?; + } + match &bound.value { + WhereClause::Implemented(trait_ref) => { + display_trait_with_generics( + s, + trait_ref.trait_id, + &trait_ref.substitution.parameters(interner)[1..], + ) + .fmt(f) + } + WhereClause::AliasEq(alias_eq) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&alias_eq.alias); + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &alias_eq.ty, + ) + .fmt(f) + } + } + }) + .to_string() + }) + .collect::>() + .join(" + ") + ) + } TyData::BoundVar(bound_var) => write!(f, "{}", s.name_for_bound_var(bound_var)), TyData::Alias(alias_ty) => alias_ty.fmt(s, f), TyData::Apply(apply_ty) => apply_ty.fmt(s, f), - TyData::Placeholder(_) => write!(f, "{}", "todo-placeholder"), TyData::Function(func) => func.fmt(s, f), - TyData::InferenceVar(_) => write!(f, "{}", "todo-inferenceVar"), + TyData::Placeholder(_) => unreachable!("cannot print placeholder variables"), + _ => unimplemented!(), } } } @@ -197,7 +218,10 @@ impl RenderAsRust for ChalkFn { write!( f, "for<{}> ", - (0..self.num_binders).map(|n| format!("'{}", s.name_for_introduced_bound_var(n))).collect::>().join(", ") + (0..self.num_binders) + .map(|n| format!("'{}", s.name_for_introduced_bound_var(n))) + .collect::>() + .join(", ") )?; } write!( @@ -216,17 +240,21 @@ impl RenderAsRust for ApplicationTy { fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); match self.name { - TypeName::Struct(sid) => write!( - f, - "{}<{}>", - s.db.struct_name(sid), - self.substitution - .parameters(interner) - .iter() - .map(|param| param.data(interner).display(s).to_string()) - .collect::>() - .join(", "), - ), + TypeName::Struct(sid) => { + write!(f, "{}", s.db.struct_name(sid))?; + let parameters = self.substitution.parameters(interner); + if parameters.len() > 0 { + write!( + f, + "<{}>", + parameters + .iter() + .map(|param| param.data(interner).display(s).to_string()) + .collect::>() + .join(", "), + )?; + } + } TypeName::AssociatedType(assoc_type_id) => { // (Iterator::Item)(x) // should be written in Rust as ::Item @@ -237,22 +265,30 @@ impl RenderAsRust for ApplicationTy { ); write!( f, - "<{} as {}>::{}<{}>", + "<{} as {}>::{}", self.first_type_parameter(interner) .unwrap() .data(interner) .display(s), s.db.trait_name(datum.trait_id), s.db.identifier_name(&datum.name), - self.substitution.parameters(interner)[1..] - .iter() - .map(|ty| ty.data(interner).display(s).to_string()) - .collect::>() - .join(", "), - ) + )?; + let params = self.substitution.parameters(interner); + if params.len() > 1 { + write!( + f, + "<{}>", + params[1..] + .iter() + .map(|ty| ty.data(interner).display(s).to_string()) + .collect::>() + .join(", ") + )?; + } } - TypeName::Error => write!(f, "{{error}}"), + TypeName::Error => write!(f, "{{error}}")?, } + Ok(()) } } @@ -261,7 +297,10 @@ impl RenderAsRust for LifetimeData { match self { LifetimeData::BoundVar(v) => write!(f, "'{}", s.name_for_bound_var(v)), LifetimeData::InferenceVar(_) => write!(f, "'_"), - _ => write!(f, "liftimeother-todo3"), + LifetimeData::Placeholder(_) => unreachable!("cannot print placeholder variables"), + // Matching the void ensues at compile time that this code is + // unreachable + LifetimeData::Phantom(void, _) => match *void {}, } } } @@ -278,20 +317,32 @@ impl RenderAsRust for ParameterData { impl RenderAsRust for QuantifiedWhereClause { fn fmt(&self, mut s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // if we have any binders, then we're a "for<'a> X: Y" "quantified" - // where clause. Otherwise, we're just a regular where clause. - // TODO: do we actually want to exclude the added index if there is no for<>? - //if !self.binders.is_empty() { - //} s = s.add_debrujin_index(); + if !self.binders.is_empty() { + write!( + f, + "forall<{}> ", + s.binder_var_names(&self.binders) + .collect::>() + .join(", ") + )?; + } self.value.fmt(s, f) } } impl RenderAsRust for QuantifiedInlineBound { fn fmt(&self, mut s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // TODO: do we want to do something similar to QuantifiedWhereClause here? s = s.add_debrujin_index(); + if !self.binders.is_empty() { + write!( + f, + "forall<{}> ", + s.binder_var_names(&self.binders) + .collect::>() + .join(", ") + )?; + } self.value.fmt(s, f) } } @@ -420,11 +471,11 @@ impl RenderAsRust for AliasEq { impl RenderAsRust for AssociatedTyDatum { fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let binders: Vec<_> = s.binder_var_names(&self.binders).collect(); + let binders: Vec<_> = s.binder_var_names(&self.binders.binders[1..]).collect(); write!( f, - "{}<{}>", + "type {}<{}>", s.db.identifier_name(&self.name), binders.join(", ") )?; @@ -462,23 +513,31 @@ impl RenderAsRust for AssociatedTyDatum { impl RenderAsRust for TraitDatum { fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let trait_name = s.db.trait_name(self.id); + let s = s.add_debrujin_index(); if self.binders.len() == 0 { write!(f, "trait {} {{}}", trait_name) } else { - let binders: Vec<_> = s.binder_var_names(&self.binders).collect(); - let assoc_types = self - .associated_ty_ids - .iter() - .map(|assoc_ty_id| { - let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); - assoc_ty_data.display(s).to_string() - }) - .collect::(); + let binders: Vec<_> = s.binder_var_names(&self.binders.binders[1..]).collect(); write!( f, - "trait {}<{}> {{{}}}", + "trait {}<{}> ", trait_name, - binders.join(", "), + binders.join(", ") + )?; + if !self.binders.value.where_clauses.is_empty() { + write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + } + let assoc_types = self + .associated_ty_ids + .iter() + .map(|assoc_ty_id| { + let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); + assoc_ty_data.display(s).to_string() + }) + .collect::(); + write!( + f, + "{{{}}}", assoc_types ) } @@ -493,7 +552,7 @@ impl RenderAsRust for StructDatum { f, "struct {}<{}> ", s.db.struct_name(self.id), - s.binder_struct_var_names(&self.binders) + s.binder_var_names(&self.binders.binders) .collect::>() .join(", ") )?; @@ -507,7 +566,14 @@ impl RenderAsRust for StructDatum { .value .fields .iter() - .map(|field| { field.data(interner).display(s).to_string() }) + .enumerate() + .map(|(idx, field)| { + format!( + "field_{}: {}", + idx, + field.data(interner).display(s).to_string() + ) + }) .collect::>() .join(", ") )?; @@ -560,27 +626,11 @@ impl<'a, I: Interner> WriterState<'a, I> { as_display(move |f| write!(f, "_{}_{}", debrujin_index_name, within_debrujin)) } - fn binder_var_names<'b, T: 'b>( - &'b self, - binders: &'b Binders, - ) -> impl Iterator + 'b { - binders.binders[1..] - .iter() - .enumerate() - .map(move |(idx, parameter)| match parameter { - ParameterKind::Ty(_) => format!("{}", self.name_for_introduced_bound_var(idx)), - ParameterKind::Lifetime(_) => { - format!("'{}", self.name_for_introduced_bound_var(idx)) - } - }) - } - - fn binder_struct_var_names<'b, T: 'b>( + fn binder_var_names<'b>( &'b self, - binders: &'b Binders, + binders: &'b [ParameterKind<()>], ) -> impl Iterator + 'b { binders - .binders .iter() .enumerate() .map(move |(idx, parameter)| match parameter { From 096e10c0a0823259a43461e24da29910e21ff304 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 25 Apr 2020 19:20:06 -0700 Subject: [PATCH 04/70] Fix associated ty parameter names AssociatedTyDatum's parameters are created inside of a new enviroment, and include the surrounding trait implementation's parameters. This splits the trait's parameters, and then records their names in the trait's environment, and their name in the assoc ty's environment in a mapping table in WriterState. This table is then passed down, and whenever parameter names are needed, their assoc ty names are generated and then their names in the trait environment are retreived from this table. Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 7 +- chalk-solve/src/display.rs | 239 ++++++++++++++++-------- chalk-solve/src/split.rs | 35 ++++ 3 files changed, 201 insertions(+), 80 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index beb96e1261a..3bc841dbc13 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -9,7 +9,7 @@ pub trait WriteProgram { impl WriteProgram for Program { fn write(&self) -> String { let mut lines = vec![]; - let ws = WriterState::new(self); + let ws = &WriterState::new(self); self.struct_data.values().for_each(|datum| { lines.push(datum.display(ws).to_string()); }); @@ -82,6 +82,7 @@ mod test { new_text ); } + eprintln!("{}",new_text); } #[test] @@ -212,7 +213,7 @@ mod test { reparse_test( " struct Foo { - field: dyn Bax, + field: dyn Bax } trait Bax {} ", @@ -220,7 +221,7 @@ mod test { reparse_test( " struct Foo<'a> { - field: dyn Bix<'a>, + field: dyn Bix<'a> } trait Bix<'a> {} ", diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 9a6d7def7d9..95b37b0b427 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -1,5 +1,7 @@ use std::{ + collections::BTreeMap, fmt::{Display, Formatter, Result}, + rc::Rc, sync::Arc, }; @@ -20,7 +22,7 @@ use crate::{split::Split, RustIrDatabase}; /// /// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. pub struct DisplayRenderAsRust<'a, I: Interner, T> { - s: WriterState<'a, I>, + s: &'a WriterState<'a, I>, rar: &'a T, } @@ -43,8 +45,8 @@ fn as_display) -> Result>(f: F) -> impl Display { } pub trait RenderAsRust { - fn fmt(&self, s: WriterState<'_, I>, f: &mut Formatter<'_>) -> Result; - fn display<'a>(&'a self, s: WriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result; + fn display<'a>(&'a self, s: &'a WriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> where Self: Sized, { @@ -53,7 +55,7 @@ pub trait RenderAsRust { } impl RenderAsRust for ImplDatum { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); let binders: Vec<_> = s.binder_var_names(&self.binders.binders).collect(); @@ -87,7 +89,7 @@ impl RenderAsRust for ImplDatum { } impl RenderAsRust for InlineBound { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { // Foo: Vec InlineBound::TraitBound(trait_bound) => trait_bound.fmt(s, f), @@ -98,13 +100,13 @@ impl RenderAsRust for InlineBound { } impl RenderAsRust for TraitBound { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { display_trait_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) } } impl RenderAsRust for AliasEqBound { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { display_trait_with_assoc_ty_value( s, s.db.associated_ty_data(self.associated_ty_id), @@ -117,11 +119,11 @@ impl RenderAsRust for AliasEqBound { } impl RenderAsRust for TyData { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); match self { TyData::Dyn(dyn_ty) => { - let s = s.add_debrujin_index(); + let s = &s.add_debrujin_index(); write!(f, "dyn ")?; // dyn_ty.bounds.binders creates a Self binding for the trait write!( @@ -135,7 +137,7 @@ impl RenderAsRust for TyData { as_display(|f| { // each individual trait within the 'dyn' can have a // forall clause. - let s = s.add_debrujin_index(); + let s = &s.add_debrujin_index(); if !bound.binders.is_empty() { write!( f, @@ -185,7 +187,7 @@ impl RenderAsRust for TyData { } impl RenderAsRust for AliasTy { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); // >::Z @@ -211,9 +213,9 @@ impl RenderAsRust for AliasTy { } impl RenderAsRust for ChalkFn { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); - let s = s.add_debrujin_index(); + let s = &s.add_debrujin_index(); if self.num_binders > 0 { write!( f, @@ -237,7 +239,7 @@ impl RenderAsRust for ChalkFn { } impl RenderAsRust for ApplicationTy { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); match self.name { TypeName::Struct(sid) => { @@ -293,7 +295,7 @@ impl RenderAsRust for ApplicationTy { } impl RenderAsRust for LifetimeData { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { LifetimeData::BoundVar(v) => write!(f, "'{}", s.name_for_bound_var(v)), LifetimeData::InferenceVar(_) => write!(f, "'_"), @@ -306,7 +308,7 @@ impl RenderAsRust for LifetimeData { } impl RenderAsRust for ParameterData { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); match self { ParameterKind::Ty(ty) => write!(f, "{}", ty.data(interner).display(s)), @@ -316,8 +318,8 @@ impl RenderAsRust for ParameterData { } impl RenderAsRust for QuantifiedWhereClause { - fn fmt(&self, mut s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - s = s.add_debrujin_index(); + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let s = &s.add_debrujin_index(); if !self.binders.is_empty() { write!( f, @@ -332,8 +334,8 @@ impl RenderAsRust for QuantifiedWhereClause { } impl RenderAsRust for QuantifiedInlineBound { - fn fmt(&self, mut s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - s = s.add_debrujin_index(); + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let s = &s.add_debrujin_index(); if !self.binders.is_empty() { write!( f, @@ -348,7 +350,7 @@ impl RenderAsRust for QuantifiedInlineBound { } impl RenderAsRust for Vec> { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { write!( f, "{}", @@ -362,7 +364,7 @@ impl RenderAsRust for Vec> { } impl RenderAsRust for WhereClause { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { WhereClause::Implemented(trait_ref) => trait_ref.fmt(s, f), WhereClause::AliasEq(alias_eq) => alias_eq.fmt(s, f), @@ -374,7 +376,7 @@ impl RenderAsRust for WhereClause { /// /// This is shared between where bounds & dyn Trait. fn display_trait_with_generics<'a, I: Interner>( - s: WriterState<'a, I>, + s: &'a WriterState<'a, I>, trait_id: TraitId, trait_params: impl IntoIterator> + 'a, ) -> impl Display + 'a { @@ -389,7 +391,7 @@ fn display_trait_with_generics<'a, I: Interner>( /// This implementation correct inside where clauses. impl RenderAsRust for TraitRef { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); write!( f, @@ -409,7 +411,7 @@ impl RenderAsRust for TraitRef { /// /// This is shared between where bounds & dyn Trait. fn display_trait_with_assoc_ty_value<'a, I: Interner>( - s: WriterState<'a, I>, + s: &'a WriterState<'a, I>, assoc_ty_datum: Arc>, trait_params: &'a [Parameter], assoc_ty_params: &'a [Parameter], @@ -439,7 +441,7 @@ fn display_trait_with_assoc_ty_value<'a, I: Interner>( /// This implementation correct inside where clauses. impl RenderAsRust for AliasEq { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); // we have: X: Y=D> // B1, B2, B3, X, A1, A2, A3 are put into alias_eq.alias.substitution @@ -470,14 +472,38 @@ impl RenderAsRust for AliasEq { } impl RenderAsRust for AssociatedTyDatum { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let binders: Vec<_> = s.binder_var_names(&self.binders.binders[1..]).collect(); + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // In lowering, a completely new empty environment is created for each + // AssociatedTyDatum, and it's given generic parameters for each generic + // parameter that its trait had. We want to map the new binders for + // those generic parameters back into their original names. To do that, + // first find their original names (trait_binder_names), then the names + // they have inside the AssociatedTyDatum (my_names_for_trait_params), + // and then add that mapping to the WriterState when writing bounds and + // where clauses. + let trait_datum = s.db.trait_datum(self.trait_id); + let trait_binder_names = s.binder_var_indices(&trait_datum.binders.binders); + let s = &s.add_debrujin_index(); + let my_binder_names = s + .binder_var_indices(&self.binders.binders) + .collect::>(); + let my_binder_display = s + .binder_var_names(&self.binders.binders) + .collect::>(); + let (my_names_for_trait_params, _) = + s.db.split_associated_ty_parameters(&my_binder_names, self); + let s = &s.add_parameter_mapping( + my_names_for_trait_params.iter().copied(), + trait_binder_names, + ); + let (_, my_params) = + s.db.split_associated_ty_parameters(&my_binder_display, self); write!( f, "type {}<{}>", s.db.identifier_name(&self.name), - binders.join(", ") + my_params.join(", ") )?; let datum_bounds = &self.binders.value; @@ -511,43 +537,34 @@ impl RenderAsRust for AssociatedTyDatum { } impl RenderAsRust for TraitDatum { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let trait_name = s.db.trait_name(self.id); - let s = s.add_debrujin_index(); + let s = &s.add_debrujin_index(); if self.binders.len() == 0 { write!(f, "trait {} {{}}", trait_name) } else { - let binders: Vec<_> = s.binder_var_names(&self.binders.binders[1..]).collect(); - write!( - f, - "trait {}<{}> ", - trait_name, - binders.join(", ") - )?; + let binders: Vec<_> = s.binder_var_names(&self.binders.binders).skip(1).collect(); + write!(f, "trait {}<{}> ", trait_name, binders.join(", "))?; if !self.binders.value.where_clauses.is_empty() { write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; } let assoc_types = self - .associated_ty_ids - .iter() - .map(|assoc_ty_id| { - let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); - assoc_ty_data.display(s).to_string() - }) - .collect::(); - write!( - f, - "{{{}}}", - assoc_types - ) + .associated_ty_ids + .iter() + .map(|assoc_ty_id| { + let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); + assoc_ty_data.display(s).to_string() + }) + .collect::(); + write!(f, "{{{}}}", assoc_types) } } } impl RenderAsRust for StructDatum { - fn fmt(&self, s: WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); - let s = s.add_debrujin_index(); + let s = &s.add_debrujin_index(); write!( f, "struct {}<{}> ", @@ -581,10 +598,34 @@ impl RenderAsRust for StructDatum { } } -#[derive(Copy, Clone, Debug)] +/// Like a BoundVar, but with the debrujin index inverted so as to create a +/// canonical name we can use anywhere for each bound variable. +/// +/// In BoundVar, the innermost bound variables have debrujin index `0`, and +/// each further out BoundVar has a debrujin index `1` higher. +/// +/// In InvertedBoundVar, the outermost variables have inverted_debrujin_idx `0`, +/// and the innermost have their depth, not the other way around. +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +struct InvertedBoundVar { + /// The inverted debrujin index. Corresponds roughly to an inverted `DebrujinIndex::depth`. + inverted_debrujin_idx: i64, + /// The index within the debrujin index. Corresponds to `BoundVar::index`. + within_idx: usize, +} + +impl Display for InvertedBoundVar { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "_{}_{}", self.inverted_debrujin_idx, self.within_idx) + } +} + +#[derive(Clone, Debug)] pub struct WriterState<'a, I: Interner> { db: &'a dyn RustIrDatabase, debrujin_indices_deep: u32, + // lowered_(inverted_debrujin_idx, index) -> src_correct_(inverted_debrujin_idx, index) + remapping: Rc>, } impl<'a, I: Interner> WriterState<'a, I> { @@ -592,6 +633,7 @@ impl<'a, I: Interner> WriterState<'a, I> { WriterState { db, debrujin_indices_deep: 0, + remapping: Rc::new(BTreeMap::new()), } } @@ -601,29 +643,74 @@ impl<'a, I: Interner> WriterState<'a, I> { new_state } - fn name_for_bound_var(&self, b: &BoundVar) -> impl Display + '_ { - // invert debrujin indexes so that we have a canonical name in rust - // source. After this, the outermost thing is '0', and the innermost is - // labeled by its depth, not the other way around. - // let debrujin_index_name = self - // .debrujin_indices_deep - // .checked_sub(b.debruijn.depth()) - // .expect("found debrujin index deeper than we thought possible"); - let debrujin_index_name = (self.debrujin_indices_deep as i64) - (b.debruijn.depth() as i64); - let within_debrujin = b.index; - as_display(move |f| write!(f, "_{}_{}", debrujin_index_name, within_debrujin)) + /// Adds parameter remapping. + /// + /// Each of the parameters in `lowered_vars` will be mapped to its + /// corresponding variable in `original_vars` when printed through the + /// `WriterState` returned from this method. + /// + /// `lowered_vars` and `original_vars` must have the same length. + fn add_parameter_mapping( + &self, + lowered_vars: impl Iterator, + original_vars: impl Iterator, + ) -> Self { + let remapping = self + .remapping + .iter() + .map(|(a, b)| (*a, *b)) + .chain(lowered_vars.zip(original_vars)) + .collect::>(); + + WriterState { + db: self.db, + debrujin_indices_deep: self.debrujin_indices_deep, + remapping: Rc::new(remapping), + } + } + + /// Inverts the debrujin index so as to create a canonical name we can + /// anywhere for each bound variable. + /// + /// See [`InvertedBoundVar`][InvertedBoundVar]. + fn invert_debrujin_idx(&self, debrujin_idx: u32, index: usize) -> InvertedBoundVar { + InvertedBoundVar { + inverted_debrujin_idx: (self.debrujin_indices_deep as i64) - (debrujin_idx as i64), + within_idx: index, + } } - fn name_for_introduced_bound_var(&self, idx: usize) -> impl Display + '_ { - // invert debrujin indexes so that we have a canonical name in rust - // source. After this, the outermost thing is '0', and the innermost is - // labeled by its depth, not the other way around. + fn apply_substitutions(&self, b: InvertedBoundVar) -> impl Display { + // TODO: sometimes produce "Self" + self.remapping.get(&b).copied().unwrap_or(b) + } + fn indices_for_bound_var(&self, b: &BoundVar) -> InvertedBoundVar { + self.invert_debrujin_idx(b.debruijn.depth(), b.index) + } + + fn indices_for_introduced_bound_var(&self, idx: usize) -> InvertedBoundVar { // freshly introduced bound vars will always have debrujin index of 0, // they're always "innermost". - let debrujin_index_name = self.debrujin_indices_deep; - let within_debrujin = idx; - as_display(move |f| write!(f, "_{}_{}", debrujin_index_name, within_debrujin)) + self.invert_debrujin_idx(0, idx) + } + + fn name_for_bound_var(&self, b: &BoundVar) -> impl Display { + self.apply_substitutions(self.indices_for_bound_var(b)) + } + + fn name_for_introduced_bound_var(&self, idx: usize) -> impl Display { + self.apply_substitutions(self.indices_for_introduced_bound_var(idx)) + } + + fn binder_var_indices<'b>( + &'b self, + binders: &'b [ParameterKind<()>], + ) -> impl Iterator + 'b { + binders + .iter() + .enumerate() + .map(move |(idx, _param)| self.indices_for_introduced_bound_var(idx)) } fn binder_var_names<'b>( @@ -632,12 +719,10 @@ impl<'a, I: Interner> WriterState<'a, I> { ) -> impl Iterator + 'b { binders .iter() - .enumerate() - .map(move |(idx, parameter)| match parameter { - ParameterKind::Ty(_) => format!("{}", self.name_for_introduced_bound_var(idx)), - ParameterKind::Lifetime(_) => { - format!("'{}", self.name_for_introduced_bound_var(idx)) - } + .zip(self.binder_var_indices(binders)) + .map(move |(parameter, var)| match parameter { + ParameterKind::Ty(_) => format!("{}", self.apply_substitutions(var)), + ParameterKind::Lifetime(_) => format!("'{}", self.apply_substitutions(var)), }) } } diff --git a/chalk-solve/src/split.rs b/chalk-solve/src/split.rs index 616958d006a..32348042ee8 100644 --- a/chalk-solve/src/split.rs +++ b/chalk-solve/src/split.rs @@ -163,6 +163,41 @@ pub trait Split: RustIrDatabase { (impl_parameters, projection) } + + /// Given the full set of parameters (or binders) for an + /// associated type datum (the one appearing in a trait), splits + /// them into the parameters for the *trait* and those for the + /// *associated type*. + /// + /// # Example + /// + /// ```ignore (example) + /// trait Foo { + /// type Assoc<'a>; + /// } + /// ``` + /// + /// in this example, the full set of parameters would be `['x, + /// Y]`, where `'x` is the value for `'a` and `Y` is the value for + /// `T`. + /// + /// # Returns + /// + /// Returns the tuple of: + /// + /// * the parameters for the impl (`[Y]`, in our example) + /// * the parameters for the associated type value (`['a]`, in our example) + fn split_associated_ty_parameters<'p, P>( + &self, + parameters: &'p [P], + associated_ty_datum: &AssociatedTyDatum, + ) -> (&'p [P], &'p [P]) { + let trait_datum = &self.trait_datum(associated_ty_datum.trait_id); + let trait_num_params = trait_datum.binders.len(); + let split_point = parameters.len() - trait_num_params; + let (other_params, trait_params) = parameters.split_at(split_point); + (trait_params, other_params) + } } impl + ?Sized, I: Interner> Split for DB {} From ee8f073d75339af9c38dbce6509e1dd62691fdfe Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 26 Apr 2020 00:42:55 -0700 Subject: [PATCH 05/70] Refactor empty env code + tests Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 125 +++++++++++++++++------- chalk-solve/src/display.rs | 72 ++++++++------ 2 files changed, 130 insertions(+), 67 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 3bc841dbc13..133d51fed4b 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -47,6 +47,13 @@ mod test { out } + /// Parses the input, lowers it, prints it, then re-parses and re-lowers, + /// failing if the two lowered programs don't match. + /// + /// Note: the comparison here does include IDs, so input order matters. In + /// particular, ProgramWriter always writes traits, then structs, then + /// impls. So all traits must come first, then structs, then all impls, or + /// the reparse will fail. fn reparse_test(program_text: &str) { let original_program = match chalk_parse::parse_program(program_text) { Ok(v) => v, @@ -55,7 +62,11 @@ mod test { e, program_text ), }; - let original_program = Arc::new(original_program.lower().unwrap()); + let original_program = Arc::new( + original_program + .lower() + .expect("unable to lower test program"), + ); let new_text = tls::set_current_program(&original_program, || original_program.write()); let new_program = match chalk_parse::parse_program(&new_text) { Ok(v) => v, @@ -82,14 +93,13 @@ mod test { new_text ); } - eprintln!("{}",new_text); + eprintln!("{}", new_text); } #[test] fn test_simple_structs_and_bounds() { reparse_test("struct Foo {}"); reparse_test("struct Foo {}"); - // note: the order here matters! Traits must be after structs. reparse_test( " struct Foo where T: Trait {} @@ -111,8 +121,34 @@ mod test { } #[test] - #[ignore] - fn test_self_in_where() { + fn test_self() { + reparse_test( + " + trait Bkz {} + trait Foo where Self: Bkz {} + ", + ); + reparse_test( + " + trait Bez {} + trait Foo { + type Assoc where Self: Bez; + } + ", + ); + reparse_test( + " + trait Baz {} + struct Foo where Self: Baz {} + ", + ); + reparse_test( + " + trait Blz {} + struct Fzk {} + struct Foo where Self: Blz {} + ", + ); reparse_test( " trait Baz<'a> {} @@ -381,8 +417,9 @@ mod test { } #[test] - fn complicated_bounds() { - let original_program_text = " + fn test_complicated_bounds() { + reparse_test( + " struct Foo { } trait Bar { } trait Baz { } @@ -394,50 +431,61 @@ mod test { Foo: Bar, dyn Bar: Baz; } - "; + ", + ); + } - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + #[test] + fn test_assoc_ty_alias_bound() { + reparse_test( + " + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax; + } + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("complicated_bounds:\n{}", written); - }) } + #[test] - fn function_type() { + fn test_function_type() { reparse_test( " struct Foo { } trait Baz { } impl Baz for Foo { } - ", + ", ); } + #[test] - fn where_clauses_galore() { - let original_program_text = " + fn test_struct_where_clauses() { + reparse_test( + " struct Foo where T: Baz, U: Bez { } trait Baz { } trait Bez { } - "; - - let p = Arc::new( - chalk_parse::parse_program(&original_program_text) - .unwrap() - .lower() - .unwrap(), + ", ); - tls::set_current_program(&p, || { - let written = p.write(); - eprintln!("complicated_bounds:\n{}", written); - }) } #[test] - fn use_as_clause() { + fn test_impl_where_clauses() { + reparse_test( + " + struct Foo where T: Baz, U: Bez { } + trait Baz { } + trait Bez { } + impl Bez for Foo where T: Baz, U: Bez { } + ", + ); + // TODO: more of these + } + + #[test] + fn test_trait_projection() { reparse_test( " struct Foo where >::Assoc: Baz { } @@ -445,11 +493,12 @@ mod test { trait Bez { type Assoc; } - ", + ", ); } + #[test] - fn placeholder_in_different_situations() { + fn test_various_forall() { reparse_test( " struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } @@ -461,17 +510,17 @@ mod test { impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } impl<'a> Bax<'a> for fn(Foo<'a>) { } impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> { } - ", + ", ); } #[test] - fn lifetimes_in_structs() { + fn test_lifetimes_in_structs() { reparse_test( " struct Foo<'b> { } trait Baz<'a> {} impl<'a> Baz<'a> for Foo<'a> { } - ", + ", ); } } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 95b37b0b427..beaaed2a83c 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -57,7 +57,7 @@ pub trait RenderAsRust { impl RenderAsRust for ImplDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); - let binders: Vec<_> = s.binder_var_names(&self.binders.binders).collect(); + let binders: Vec<_> = s.binder_var_display(&self.binders.binders).collect(); let trait_ref = &self.binders.value.trait_ref; @@ -142,7 +142,7 @@ impl RenderAsRust for TyData { write!( f, "forall<{}> ", - s.binder_var_names(&bound.binders) + s.binder_var_display(&bound.binders) .collect::>() .join(", ") )?; @@ -176,7 +176,7 @@ impl RenderAsRust for TyData { .join(" + ") ) } - TyData::BoundVar(bound_var) => write!(f, "{}", s.name_for_bound_var(bound_var)), + TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), TyData::Alias(alias_ty) => alias_ty.fmt(s, f), TyData::Apply(apply_ty) => apply_ty.fmt(s, f), TyData::Function(func) => func.fmt(s, f), @@ -297,10 +297,10 @@ impl RenderAsRust for ApplicationTy { impl RenderAsRust for LifetimeData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { - LifetimeData::BoundVar(v) => write!(f, "'{}", s.name_for_bound_var(v)), + LifetimeData::BoundVar(v) => write!(f, "'{}", s.display_bound_var(v)), LifetimeData::InferenceVar(_) => write!(f, "'_"), LifetimeData::Placeholder(_) => unreachable!("cannot print placeholder variables"), - // Matching the void ensues at compile time that this code is + // Matching the void ensures at compile time that this code is // unreachable LifetimeData::Phantom(void, _) => match *void {}, } @@ -324,7 +324,7 @@ impl RenderAsRust for QuantifiedWhereClause { write!( f, "forall<{}> ", - s.binder_var_names(&self.binders) + s.binder_var_display(&self.binders) .collect::>() .join(", ") )?; @@ -340,7 +340,7 @@ impl RenderAsRust for QuantifiedInlineBound { write!( f, "forall<{}> ", - s.binder_var_names(&self.binders) + s.binder_var_display(&self.binders) .collect::>() .join(", ") )?; @@ -478,32 +478,43 @@ impl RenderAsRust for AssociatedTyDatum { // parameter that its trait had. We want to map the new binders for // those generic parameters back into their original names. To do that, // first find their original names (trait_binder_names), then the names - // they have inside the AssociatedTyDatum (my_names_for_trait_params), + // they have inside the AssociatedTyDatum (assoc_ty_names_for_trait_params), // and then add that mapping to the WriterState when writing bounds and // where clauses. + let trait_datum = s.db.trait_datum(self.trait_id); - let trait_binder_names = s.binder_var_indices(&trait_datum.binders.binders); + // inverted Debrujin indices for the trait's parameters in the trait + // environment + let trait_param_names_in_trait_env = s.binder_var_indices(&trait_datum.binders.binders); let s = &s.add_debrujin_index(); - let my_binder_names = s + // inverted Debrujin indices for the trait's parameters in the + // associated type environment + let param_names_in_assoc_ty_env = s .binder_var_indices(&self.binders.binders) .collect::>(); - let my_binder_display = s - .binder_var_names(&self.binders.binders) - .collect::>(); - let (my_names_for_trait_params, _) = - s.db.split_associated_ty_parameters(&my_binder_names, self); + // inverted Debrujin indices to render the trait's parameters in the + // associated type environment + let (trait_param_names_in_assoc_ty_env, _) = + s.db.split_associated_ty_parameters(¶m_names_in_assoc_ty_env, self); + let s = &s.add_parameter_mapping( - my_names_for_trait_params.iter().copied(), - trait_binder_names, + trait_param_names_in_assoc_ty_env.iter().copied(), + trait_param_names_in_trait_env, ); - let (_, my_params) = - s.db.split_associated_ty_parameters(&my_binder_display, self); + // rendered names for the associated type's generics in the associated + // type environment + let binder_display_in_assoc_ty = s + .binder_var_display(&self.binders.binders) + .collect::>(); + + let (_, assoc_ty_params) = + s.db.split_associated_ty_parameters(&binder_display_in_assoc_ty, self); write!( f, "type {}<{}>", s.db.identifier_name(&self.name), - my_params.join(", ") + assoc_ty_params.join(", ") )?; let datum_bounds = &self.binders.value; @@ -543,7 +554,10 @@ impl RenderAsRust for TraitDatum { if self.binders.len() == 0 { write!(f, "trait {} {{}}", trait_name) } else { - let binders: Vec<_> = s.binder_var_names(&self.binders.binders).skip(1).collect(); + let binders: Vec<_> = s + .binder_var_display(&self.binders.binders) + .skip(1) + .collect(); write!(f, "trait {}<{}> ", trait_name, binders.join(", "))?; if !self.binders.value.where_clauses.is_empty() { write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; @@ -569,7 +583,7 @@ impl RenderAsRust for StructDatum { f, "struct {}<{}> ", s.db.struct_name(self.id), - s.binder_var_names(&self.binders.binders) + s.binder_var_display(&self.binders.binders) .collect::>() .join(", ") )?; @@ -680,7 +694,7 @@ impl<'a, I: Interner> WriterState<'a, I> { } } - fn apply_substitutions(&self, b: InvertedBoundVar) -> impl Display { + fn apply_mappings(&self, b: InvertedBoundVar) -> impl Display { // TODO: sometimes produce "Self" self.remapping.get(&b).copied().unwrap_or(b) } @@ -695,12 +709,12 @@ impl<'a, I: Interner> WriterState<'a, I> { self.invert_debrujin_idx(0, idx) } - fn name_for_bound_var(&self, b: &BoundVar) -> impl Display { - self.apply_substitutions(self.indices_for_bound_var(b)) + fn display_bound_var(&self, b: &BoundVar) -> impl Display { + self.apply_mappings(self.indices_for_bound_var(b)) } fn name_for_introduced_bound_var(&self, idx: usize) -> impl Display { - self.apply_substitutions(self.indices_for_introduced_bound_var(idx)) + self.apply_mappings(self.indices_for_introduced_bound_var(idx)) } fn binder_var_indices<'b>( @@ -713,7 +727,7 @@ impl<'a, I: Interner> WriterState<'a, I> { .map(move |(idx, _param)| self.indices_for_introduced_bound_var(idx)) } - fn binder_var_names<'b>( + fn binder_var_display<'b>( &'b self, binders: &'b [ParameterKind<()>], ) -> impl Iterator + 'b { @@ -721,8 +735,8 @@ impl<'a, I: Interner> WriterState<'a, I> { .iter() .zip(self.binder_var_indices(binders)) .map(move |(parameter, var)| match parameter { - ParameterKind::Ty(_) => format!("{}", self.apply_substitutions(var)), - ParameterKind::Lifetime(_) => format!("'{}", self.apply_substitutions(var)), + ParameterKind::Ty(_) => format!("{}", self.apply_mappings(var)), + ParameterKind::Lifetime(_) => format!("'{}", self.apply_mappings(var)), }) } } From 0b2fefeb1f9fc1706e1f2d5aedd6c15b6e1396ff Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 26 Apr 2020 00:44:11 -0700 Subject: [PATCH 06/70] Implement associated ty value rendering in impl This commit renders the assocate type value assignments inside trait implemenations: ``` impl Foo for Bar { type Assoc = u32; } ``` Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 92 ++++++++++++++++++++++++- chalk-solve/src/display.rs | 80 ++++++++++++++++++--- 2 files changed, 158 insertions(+), 14 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 133d51fed4b..8d3bb87139e 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -344,16 +344,102 @@ mod test { } ", ); + } + + #[test] + fn test_simple_impl() { + reparse_test( + " + struct Foo {} + trait Bar {} + impl Bar for Foo {} + ", + ); + } + + #[test] + fn test_impl_assoc_ty() { + reparse_test( + " + struct Fuu {} + trait Bhz { + type Assoc; + } + impl Bhz for Fuu { + type Assoc = Fuu; + } + ", + ); + reparse_test( + " + struct Fou {} + trait Bax { + type Assoc; + } + impl Bax for Fou { + type Assoc = Fou; + } + ", + ); + reparse_test( + " + struct Fuu {} + trait Bmx { + type Assoc; + } + impl Bmx for Fuu { + type Assoc = T; + } + ", + ); + reparse_test( + " + struct Fuu {} + struct Guu {} + trait Bmx { + type Assoc; + } + impl Bmx for Fuu { + type Assoc = Guu; + } + ", + ); reparse_test( " - trait Bix<_0> {} - trait Foo<_0_1> { - type Assoc<_1_1, _1_2> where _1_1: Bix<_1_2>; + struct Fuu {} + struct Guu {} + trait Bmx { + type Assoc; + } + impl Bmx for Fuu { + type Assoc = Guu; } ", ); } + #[test] + fn test_impl_assoc_ty_alias() { + reparse_test( + " + struct Fow {} + struct Qac {} + trait Bow {} + trait Baq { + type Assoc: Boo; + } + trait Boo { + type Item; + } + impl Boo for Qac { + type Item = Fow; + } + impl Baq for Fow { + type Assoc = Qac; + } + ", + ); + } #[test] fn test_struct_fields() { diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index beaaed2a83c..48f9617988b 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -6,14 +6,14 @@ use std::{ }; use chalk_ir::{ - interner::Interner, AliasEq, AliasTy, ApplicationTy, BoundVar, Fn as ChalkFn, LifetimeData, - Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, TraitId, TraitRef, Ty, TyData, - TypeName, WhereClause, + interner::Interner, AliasEq, AliasTy, ApplicationTy, BoundVar, Fn as ChalkFn, Lifetime, + LifetimeData, Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, StructId, + TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, }; use chalk_rust_ir::{ - AliasEqBound, AssociatedTyDatum, ImplDatum, InlineBound, QuantifiedInlineBound, StructDatum, - TraitBound, TraitDatum, + AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, + QuantifiedInlineBound, StructDatum, TraitBound, TraitDatum, }; use crate::{split::Split, RustIrDatabase}; @@ -54,6 +54,50 @@ pub trait RenderAsRust { } } +impl RenderAsRust for AssociatedTyValue { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // see comments for a similar empty env operation in AssociatedTyDatum's + // impl of RenderAsRust. + let assoc_ty_data = s.db.associated_ty_data(self.associated_ty_id); + let impl_datum = s.db.impl_datum(self.impl_id); + + let impl_param_names_in_impl_env = s.binder_var_indices(&impl_datum.binders.binders); + + let s = &s.add_debrujin_index(); + + let param_names_in_assoc_ty_value_env = s + .binder_var_indices(&self.value.binders) + .collect::>(); + + let (impl_params_in_assoc_ty_value_env, _assoc_ty_value_params) = + s.db.split_associated_ty_value_parameters(¶m_names_in_assoc_ty_value_env, self); + + let s = &s.add_parameter_mapping( + impl_params_in_assoc_ty_value_env.iter().cloned(), + impl_param_names_in_impl_env, + ); + + // let params = s + // .binder_var_display(&self.value.binders) + // .collect::>(); + let display_params = s + .binder_var_display(&self.value.binders) + .collect::>(); + + let (_impl_display, assoc_ty_value_display) = + s.db.split_associated_ty_value_parameters(&display_params, self); + + write!( + f, + "type {}<{}> = {};", + s.db.identifier_name(&assoc_ty_data.name), + assoc_ty_value_display.join(", "), + self.value.value.ty.display(s) + )?; + Ok(()) + } +} + impl RenderAsRust for ImplDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); @@ -72,17 +116,31 @@ impl RenderAsRust for ImplDatum { trait_ref.trait_id, &trait_ref.substitution.parameters(interner)[1..], ); + + let assoc_ty_values = self + .associated_ty_value_ids + .iter() + .map(|assoc_ty_value| { + s.db.associated_ty_value(*assoc_ty_value) + .display(s) + .to_string() + }) + .collect::>() + .join("\n"); + write!( f, - "impl{} {} for {} {{}}", + "impl{} {} for {} ", binders_name, full_trait_name, - trait_ref - .self_type_parameter(interner) - .data(interner) - .display(s) + trait_ref.self_type_parameter(interner).display(s) )?; - // TODO: print associated types, self.associated_ty_value_ids + + if !self.binders.value.where_clauses.is_empty() { + write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + } + + write!(f, "{{{}}}", assoc_ty_values)?; Ok(()) } From de6924e890ffe018553e0c06b68ad7a017062ee4 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 26 Apr 2020 00:48:58 -0700 Subject: [PATCH 07/70] Implement RenderAsRust for various ID structs Co-authored-by: David Ross --- chalk-solve/src/display.rs | 92 ++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 48f9617988b..8f1a12719c6 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -176,6 +176,37 @@ impl RenderAsRust for AliasEqBound { } } +impl RenderAsRust for Ty { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // delegate to TyData + self.data(s.db.interner()).fmt(s, f) + } +} +impl RenderAsRust for Lifetime { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // delegate to LifetimeData + self.data(s.db.interner()).fmt(s, f) + } +} +impl RenderAsRust for Parameter { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // delegate to ParameterData + self.data(s.db.interner()).fmt(s, f) + } +} +impl RenderAsRust for StructId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + f.write_str(&s.db.struct_name(*self)) + } +} +impl RenderAsRust for TraitId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + f.write_str(&s.db.trait_name(*self)) + } +} + impl RenderAsRust for TyData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); @@ -246,7 +277,6 @@ impl RenderAsRust for TyData { impl RenderAsRust for AliasTy { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); // >::Z // Now, we split out A*, Y/Z and B*: @@ -258,12 +288,12 @@ impl RenderAsRust for AliasTy { write!( f, "<{} as {}>::{}<{}>", - trait_params[0].data(interner).display(s), + trait_params[0].display(s), display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), s.db.identifier_name(&assoc_ty_datum.name), assoc_type_params .iter() - .map(|param| param.data(interner).display(s).to_string()) + .map(|param| param.display(s).to_string()) .collect::>() .join(", ") ) @@ -272,7 +302,6 @@ impl RenderAsRust for AliasTy { impl RenderAsRust for ChalkFn { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); let s = &s.add_debrujin_index(); if self.num_binders > 0 { write!( @@ -289,7 +318,7 @@ impl RenderAsRust for ChalkFn { "fn({})", self.parameters .iter() - .map(|param| param.data(interner).display(s).to_string()) + .map(|param| param.display(s).to_string()) .collect::>() .join(", ") ) @@ -301,7 +330,7 @@ impl RenderAsRust for ApplicationTy { let interner = s.db.interner(); match self.name { TypeName::Struct(sid) => { - write!(f, "{}", s.db.struct_name(sid))?; + write!(f, "{}", sid.display(s))?; let parameters = self.substitution.parameters(interner); if parameters.len() > 0 { write!( @@ -309,7 +338,7 @@ impl RenderAsRust for ApplicationTy { "<{}>", parameters .iter() - .map(|param| param.data(interner).display(s).to_string()) + .map(|param| param.display(s).to_string()) .collect::>() .join(", "), )?; @@ -326,11 +355,8 @@ impl RenderAsRust for ApplicationTy { write!( f, "<{} as {}>::{}", - self.first_type_parameter(interner) - .unwrap() - .data(interner) - .display(s), - s.db.trait_name(datum.trait_id), + self.first_type_parameter(interner).unwrap().display(s), + datum.trait_id.display(s), s.db.identifier_name(&datum.name), )?; let params = self.substitution.parameters(interner); @@ -340,7 +366,7 @@ impl RenderAsRust for ApplicationTy { "<{}>", params[1..] .iter() - .map(|ty| ty.data(interner).display(s).to_string()) + .map(|ty| ty.display(s).to_string()) .collect::>() .join(", ") )?; @@ -367,10 +393,9 @@ impl RenderAsRust for LifetimeData { impl RenderAsRust for ParameterData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); match self { - ParameterKind::Ty(ty) => write!(f, "{}", ty.data(interner).display(s)), - ParameterKind::Lifetime(lt) => write!(f, "{}", lt.data(interner).display(s)), + ParameterKind::Ty(ty) => write!(f, "{}", ty.display(s)), + ParameterKind::Lifetime(lt) => write!(f, "{}", lt.display(s)), } } } @@ -438,13 +463,12 @@ fn display_trait_with_generics<'a, I: Interner>( trait_id: TraitId, trait_params: impl IntoIterator> + 'a, ) -> impl Display + 'a { - let interner = s.db.interner(); let trait_params = trait_params .into_iter() - .map(|param| param.data(interner).display(s).to_string()) + .map(|param| param.display(s).to_string()) .collect::>() .join(", "); - as_display(move |f| write!(f, "{}<{}>", s.db.trait_name(trait_id), trait_params,)) + as_display(move |f| write!(f, "{}<{}>", trait_id.display(s), trait_params,)) } /// This implementation correct inside where clauses. @@ -454,7 +478,7 @@ impl RenderAsRust for TraitRef { write!( f, "{}: {}", - self.self_type_parameter(interner).data(interner).display(s), + self.self_type_parameter(interner).display(s), display_trait_with_generics( s, self.trait_id, @@ -475,24 +499,23 @@ fn display_trait_with_assoc_ty_value<'a, I: Interner>( assoc_ty_params: &'a [Parameter], assoc_ty_value: &'a Ty, ) -> impl Display + 'a { - let interner = s.db.interner(); as_display(move |f| { write!( f, "{}<{}, {}<{}>={}>", - s.db.trait_name(assoc_ty_datum.trait_id), + assoc_ty_datum.trait_id.display(s), trait_params .iter() - .map(|param| param.data(interner).display(s).to_string()) + .map(|param| param.display(s).to_string()) .collect::>() .join(", "), s.db.identifier_name(&assoc_ty_datum.name), assoc_ty_params .iter() - .map(|param| param.data(interner).display(s).to_string()) + .map(|param| param.display(s).to_string()) .collect::>() .join(", "), - assoc_ty_value.data(interner).display(s) + assoc_ty_value.display(s) ) }) } @@ -500,7 +523,6 @@ fn display_trait_with_assoc_ty_value<'a, I: Interner>( /// This implementation correct inside where clauses. impl RenderAsRust for AliasEq { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); // we have: X: Y=D> // B1, B2, B3, X, A1, A2, A3 are put into alias_eq.alias.substitution // D is alias_eq.ty @@ -517,7 +539,7 @@ impl RenderAsRust for AliasEq { write!( f, "{}: {}", - trait_params[0].data(interner).display(s), + trait_params[0].display(s), display_trait_with_assoc_ty_value( s, assoc_ty_datum, @@ -607,16 +629,15 @@ impl RenderAsRust for AssociatedTyDatum { impl RenderAsRust for TraitDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let trait_name = s.db.trait_name(self.id); let s = &s.add_debrujin_index(); if self.binders.len() == 0 { - write!(f, "trait {} {{}}", trait_name) + write!(f, "trait {} {{}}", self.id.display(s)) } else { let binders: Vec<_> = s .binder_var_display(&self.binders.binders) .skip(1) .collect(); - write!(f, "trait {}<{}> ", trait_name, binders.join(", "))?; + write!(f, "trait {}<{}> ", self.id.display(s), binders.join(", "))?; if !self.binders.value.where_clauses.is_empty() { write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; } @@ -635,12 +656,11 @@ impl RenderAsRust for TraitDatum { impl RenderAsRust for StructDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); let s = &s.add_debrujin_index(); write!( f, "struct {}<{}> ", - s.db.struct_name(self.id), + self.id.display(s), s.binder_var_display(&self.binders.binders) .collect::>() .join(", ") @@ -656,13 +676,7 @@ impl RenderAsRust for StructDatum { .fields .iter() .enumerate() - .map(|(idx, field)| { - format!( - "field_{}: {}", - idx, - field.data(interner).display(s).to_string() - ) - }) + .map(|(idx, field)| { format!("field_{}: {}", idx, field.display(s).to_string()) }) .collect::>() .join(", ") )?; From b92e5325bd9cc5843f384ceacc80ea437f65b727 Mon Sep 17 00:00:00 2001 From: David Ross Date: Sat, 2 May 2020 19:28:39 -0700 Subject: [PATCH 08/70] Handle Self, trait polarity & trait attributes Co-authored-by: Super Tuple --- chalk-integration/src/program_writer.rs | 446 +++++++++++++++++++++--- chalk-solve/src/display.rs | 128 ++++--- 2 files changed, 494 insertions(+), 80 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 8d3bb87139e..580599acf8c 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -47,6 +47,26 @@ mod test { out } + /// Data from performing a reparse test which can be used to make additional + /// assertions. + /// + /// Not necessary for use unless additional assertions are necessary. + #[allow(unused)] + struct ReparseTestResult<'a> { + /// The program text for the original test code + original_text: &'a str, + /// The program text for the code the test says should be output + target_text: &'a str, + /// The actual reparsed output text + output_text: String, + /// Lowered version of `original_text` + original_program: Arc, + /// Lowered version of `target_text` + target_program: Arc, + /// Lowered version of `output_text` + output_program: Program, + } + /// Parses the input, lowers it, prints it, then re-parses and re-lowers, /// failing if the two lowered programs don't match. /// @@ -54,7 +74,16 @@ mod test { /// particular, ProgramWriter always writes traits, then structs, then /// impls. So all traits must come first, then structs, then all impls, or /// the reparse will fail. - fn reparse_test(program_text: &str) { + fn reparse_test(program_text: &str) -> ReparseTestResult<'_> { + reparse_into_different_test(program_text, program_text) + } + + /// [`reparse_test`], but allows a non-convergent test program to be tested + /// a different target. + fn reparse_into_different_test<'a>( + program_text: &'a str, + target_text: &'a str, + ) -> ReparseTestResult<'a> { let original_program = match chalk_parse::parse_program(program_text) { Ok(v) => v, Err(e) => panic!( @@ -62,38 +91,66 @@ mod test { e, program_text ), }; - let original_program = Arc::new( - original_program - .lower() - .expect("unable to lower test program"), - ); - let new_text = tls::set_current_program(&original_program, || original_program.write()); - let new_program = match chalk_parse::parse_program(&new_text) { + let original_program = Arc::new(original_program.lower().unwrap_or_else(|e| { + panic!( + "unable to lower test program:\n{}\nSource:\n{}\n", + e, program_text + ) + })); + let target_program = match chalk_parse::parse_program(target_text) { Ok(v) => v, Err(e) => panic!( - "unable to reparse writer output:\n{}\nNew source:\n{}\n", - e, new_text + "unable to parse test program:\n{}\nSource:\n{}\n", + e, program_text ), }; - let new_program = match new_program.lower() { - Ok(v) => v, - Err(e) => panic!( + let target_program = Arc::new(target_program.lower().unwrap_or_else(|e| { + panic!( + "unable to lower test program:\n{}\nSource:\n{}\n", + e, program_text + ) + })); + let output_text = tls::set_current_program(&original_program, || original_program.write()); + let output_program = chalk_parse::parse_program(&output_text).unwrap_or_else(|e| { + panic!( + "unable to reparse writer output:\n{}\nNew source:\n{}\n", + e, output_text + ) + }); + let output_program = output_program.lower().unwrap_or_else(|e| { + panic!( "error lowering writer output:\n{}\nNew source:\n{}\n", - e, new_text - ), - }; - if new_program != *original_program { + e, output_text + ) + }); + if output_program != *target_program { panic!( "WriteProgram produced different program.\n\ Diff:\n{}\n\ - Source:\n{}\n + Source:\n{}\n{}\ New Source:\n{}\n", - program_diff(&original_program, &new_program), + program_diff(&target_program, &output_program), program_text, - new_text + if target_text != program_text { + format!( + "Test Should Output (different from original):\n{}\n", + target_text + ) + } else { + String::new() + }, + output_text ); } - eprintln!("{}", new_text); + eprintln!("\nTest Succeeded:\n\n{}\n---", output_text); + ReparseTestResult { + original_text: program_text, + output_text, + target_text, + original_program, + output_program, + target_program, + } } #[test] @@ -121,13 +178,33 @@ mod test { } #[test] - fn test_self() { + fn test_self_in_trait_where() { reparse_test( " trait Bkz {} trait Foo where Self: Bkz {} ", ); + reparse_test( + " + trait Baz<'a> {} + trait Foo where forall<'a> Self: Baz<'a> {} + ", + ); + } + + #[test] + fn test_self_in_assoc_type() { + reparse_test( + " + trait Extra {} + trait Bez {} + trait Foo { + type Assoc: Extra; + } + ", + ); + reparse_test( " trait Bez {} @@ -136,6 +213,48 @@ mod test { } ", ); + reparse_test( + " + trait Biz {} + trait Foo { + type Assoc where Self: Biz; + } + ", + ); + } + + #[test] + fn test_self_in_dyn() { + reparse_test( + " + trait Bun {} + trait Foo { + type Assoc where dyn Bun: Bun; + } + ", + ); + reparse_test( + " + trait Has {} + trait Bun {} + trait Fiz { + type Assoc1: Has>; + type Assoc2: Has>; + } + ", + ); + } + + // Self doesn't work in these circumstances yet (test programs fail to lower) + #[ignore] + #[test] + fn test_self_in_struct_bounds() { + reparse_test( + " + trait Bax {} + struct Foo where T: Bax {} + ", + ); reparse_test( " trait Baz {} @@ -145,15 +264,47 @@ mod test { reparse_test( " trait Blz {} - struct Fzk {} - struct Foo where Self: Blz {} + struct Foo where Self: Blz {} ", ); + } + + // Self doesn't work in these circumstances yet (test programs fail to lower) + #[ignore] + #[test] + fn test_self_in_impl_blocks() { reparse_test( " - trait Baz<'a> {} - trait Foo where forall<'a> Self: Baz<'a> {} - ", + trait Foo { + type Assoc; + } + struct Bix {} + impl Foo for Bix { + type Assoc = Self; + } + ", + ); + reparse_test( + " + trait Foo {} + trait Fin {} + struct Bux {} + impl Foo for Bux where Self: Fin {} + ", + ); + reparse_test( + " + trait Faux {} + trait Paw { + type Assoc1; + type Assoc2; + } + struct Buzz {} + impl Paw for Buzz { + type Assoc1 = dyn Faux; + type Assoc2 = dyn Faux; + } + ", ); } @@ -455,6 +606,33 @@ mod test { ); } + #[test] + fn test_against_accidental_self() { + // In some of the writer code, it would be really easy to introduce a + // outputs the first generic parameter of things as "Self". + let in_structs = reparse_test( + " + struct Foo { + field: T + } + ", + ); + dbg!(in_structs.output_program); + assert!(!in_structs.output_text.contains("Self")); + let in_impl = reparse_test( + " + struct Foo {} + trait Bux { + type Assoc; + } + impl Bux for Foo { + type Assoc = T; + } + ", + ); + assert!(!in_impl.output_text.contains("Self")); + } + #[test] fn test_program_writer() { reparse_test( @@ -502,9 +680,62 @@ mod test { ); } + #[test] + fn test_assoc_ty_alias_bound() { + // Foo: Bax is lowered into two bounds, Bax and Bax, and + // the formatter does not coalesce those bounds. + reparse_into_different_test( + " + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax; + } + ", + " + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax, + Foo: Bax; + } + ", + ); + reparse_into_different_test( + " + struct Foo where T: Baux { } + trait Baux { type Assoc; } + ", + " + struct Foo where T: Baux, T: Baux { } + trait Baux { type Assoc; } + ", + ); + reparse_into_different_test( + " + struct Foo {} + trait Boz { type Assoc; } + impl Boz for Foo where T: Boz> { + type Assoc = Foo; + } + ", + " + struct Foo {} + trait Boz { type Assoc; } + impl Boz for Foo where T: Boz>, T: Boz { + type Assoc = Foo; + } + ", + ); + } + #[test] fn test_complicated_bounds() { - reparse_test( + reparse_into_different_test( " struct Foo { } trait Bar { } @@ -518,21 +749,19 @@ mod test { dyn Bar: Baz; } ", - ); - } - - #[test] - fn test_assoc_ty_alias_bound() { - reparse_test( " struct Foo { } - trait Bax { type BaxT; } + trait Bar { } + trait Baz { } + trait Bax { type BaxT; } trait Test { - type Assoc + type Assoc: Bar + Baz + Bax where - Foo: Bax; - } - ", + Foo: Bax, + Foo: Bax, + Foo: Bar, + dyn Bar: Baz; + }", ); } @@ -557,6 +786,7 @@ mod test { ", ); } + #[test] fn test_impl_where_clauses() { reparse_test( @@ -609,4 +839,142 @@ mod test { ", ); } + + #[test] + fn test_basic_trait_impl() { + reparse_test( + " + struct Foo { } + trait Bar {} + impl Bar for Foo { } + ", + ); + } + + #[test] + fn test_negative_auto_trait_impl() { + reparse_test( + " + struct Foo { } + #[auto] + trait Baz {} + impl !Baz for Foo { } + ", + ); + } + + #[test] + fn test_trait_flags() { + let flags = vec![ + "auto", + "marker", + "upstream", + "fundamental", + "non_enumerable", + "coinductive", + ]; + reparse_test(&format!( + "{}trait Hello {{}}", + flags + .iter() + .map(|f| format!("#[{}]", f)) + .collect::>() + .join("\n") + )); + for flag in flags { + reparse_test(&format!( + " + #[{0}] + trait Hello_{0} {{}} + ", + flag + )); + } + } + + #[test] + fn test_trait_impl_associated_type() { + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bar { + type Assoc; + } + impl Bar for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bax { + type Assoc1; + type Assoc2; + } + impl Bax for Foo { + type Assoc1 = Floo; + type Assoc2 = Foo; + } + ", + ); + } + + #[test] + fn test_trait_impl_associated_type_with_generics() { + reparse_test( + " + struct Foo { } + struct Floo { } + trait Baz { + type Assoc; + } + impl Baz for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bur { + type Assoc; + } + impl Bur for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bun { + type Assoc; + } + impl Bun for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bun { + type Assoc1; + type Assoc2; + type Assoc3; + } + impl Bun for Foo { + type Assoc1 = Floo; + type Assoc2 = Floo; + type Assoc3 = Floo, Floo>; + } + ", + ); + } } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 8f1a12719c6..160858c2792 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -10,9 +10,8 @@ use chalk_ir::{ LifetimeData, Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, }; - use chalk_rust_ir::{ - AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, + AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, Polarity, QuantifiedInlineBound, StructDatum, TraitBound, TraitDatum, }; @@ -63,7 +62,7 @@ impl RenderAsRust for AssociatedTyValue { let impl_param_names_in_impl_env = s.binder_var_indices(&impl_datum.binders.binders); - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); let param_names_in_assoc_ty_value_env = s .binder_var_indices(&self.value.binders) @@ -98,6 +97,15 @@ impl RenderAsRust for AssociatedTyValue { } } +impl RenderAsRust for Polarity { + fn fmt(&self, _s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + if !self.is_positive() { + write!(f, "!")?; + } + Ok(()) + } +} + impl RenderAsRust for ImplDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); @@ -130,8 +138,9 @@ impl RenderAsRust for ImplDatum { write!( f, - "impl{} {} for {} ", + "impl{} {}{} for {} ", binders_name, + self.polarity.display(s), full_trait_name, trait_ref.self_type_parameter(interner).display(s) )?; @@ -212,7 +221,7 @@ impl RenderAsRust for TyData { let interner = s.db.interner(); match self { TyData::Dyn(dyn_ty) => { - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); write!(f, "dyn ")?; // dyn_ty.bounds.binders creates a Self binding for the trait write!( @@ -226,7 +235,7 @@ impl RenderAsRust for TyData { as_display(|f| { // each individual trait within the 'dyn' can have a // forall clause. - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); if !bound.binders.is_empty() { write!( f, @@ -302,7 +311,7 @@ impl RenderAsRust for AliasTy { impl RenderAsRust for ChalkFn { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); if self.num_binders > 0 { write!( f, @@ -402,7 +411,7 @@ impl RenderAsRust for ParameterData { impl RenderAsRust for QuantifiedWhereClause { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); if !self.binders.is_empty() { write!( f, @@ -418,7 +427,7 @@ impl RenderAsRust for QuantifiedWhereClause { impl RenderAsRust for QuantifiedInlineBound { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); if !self.binders.is_empty() { write!( f, @@ -566,7 +575,7 @@ impl RenderAsRust for AssociatedTyDatum { // inverted Debrujin indices for the trait's parameters in the trait // environment let trait_param_names_in_trait_env = s.binder_var_indices(&trait_datum.binders.binders); - let s = &s.add_debrujin_index(); + let s = &s.add_debrujin_index(None); // inverted Debrujin indices for the trait's parameters in the // associated type environment let param_names_in_assoc_ty_env = s @@ -629,34 +638,49 @@ impl RenderAsRust for AssociatedTyDatum { impl RenderAsRust for TraitDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(); - if self.binders.len() == 0 { - write!(f, "trait {} {{}}", self.id.display(s)) - } else { - let binders: Vec<_> = s - .binder_var_display(&self.binders.binders) - .skip(1) - .collect(); - write!(f, "trait {}<{}> ", self.id.display(s), binders.join(", "))?; - if !self.binders.value.where_clauses.is_empty() { - write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + let s = &s.add_debrujin_index(Some(0)); + + macro_rules! trait_flags { + ($($n:ident),*) => { + $(if self.flags.$n { + write!(f,"#[{}]\n",stringify!($n))?; + })* } - let assoc_types = self - .associated_ty_ids - .iter() - .map(|assoc_ty_id| { - let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); - assoc_ty_data.display(s).to_string() - }) - .collect::(); - write!(f, "{{{}}}", assoc_types) } + + trait_flags!( + auto, + marker, + upstream, + fundamental, + non_enumerable, + coinductive + ); + let binders: Vec<_> = s + .binder_var_display(&self.binders.binders) + .skip(1) + .collect(); + write!(f, "trait {}<{}> ", self.id.display(s), binders.join(", "))?; + if !self.binders.value.where_clauses.is_empty() { + write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + } + let assoc_types = self + .associated_ty_ids + .iter() + .map(|assoc_ty_id| { + let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); + assoc_ty_data.display(s).to_string() + }) + .collect::(); + write!(f, "{{{}}}", assoc_types) } } impl RenderAsRust for StructDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(); + // When support for Self in structs is added, self_binding should be + // changed to Some(0) + let s = &s.add_debrujin_index(None); write!( f, "struct {}<{}> ", @@ -697,7 +721,7 @@ struct InvertedBoundVar { /// The inverted debrujin index. Corresponds roughly to an inverted `DebrujinIndex::depth`. inverted_debrujin_idx: i64, /// The index within the debrujin index. Corresponds to `BoundVar::index`. - within_idx: usize, + within_idx: IndexWithinBinding, } impl Display for InvertedBoundVar { @@ -712,20 +736,35 @@ pub struct WriterState<'a, I: Interner> { debrujin_indices_deep: u32, // lowered_(inverted_debrujin_idx, index) -> src_correct_(inverted_debrujin_idx, index) remapping: Rc>, + // the inverted_bound_var which maps to "Self" + self_mapping: Option, } - +type IndexWithinBinding = usize; impl<'a, I: Interner> WriterState<'a, I> { pub fn new(db: &'a dyn RustIrDatabase) -> Self { WriterState { db, debrujin_indices_deep: 0, remapping: Rc::new(BTreeMap::new()), + self_mapping: None, } } - fn add_debrujin_index(&self) -> Self { + /// Adds a level of debrujin index, and possibly a "Self" parameter. + /// + /// This should be called whenever recursing into the value within a + /// [`Binders`]. + /// + /// If `self_binding` is `Some`, then it will introduce a new variable named + /// `Self` with the within-debrujin index given within and the innermost + /// debrujian index after increasing debrujin index. + #[must_use = "this returns a new `WriterState`, and does not modify the existing one"] + fn add_debrujin_index(&self, self_binding: Option) -> Self { let mut new_state = self.clone(); new_state.debrujin_indices_deep += 1; + new_state.self_mapping = self_binding + .map(|idx| new_state.indices_for_introduced_bound_var(idx)) + .or(self.self_mapping); new_state } @@ -749,9 +788,8 @@ impl<'a, I: Interner> WriterState<'a, I> { .collect::>(); WriterState { - db: self.db, - debrujin_indices_deep: self.debrujin_indices_deep, remapping: Rc::new(remapping), + ..*self } } @@ -759,7 +797,11 @@ impl<'a, I: Interner> WriterState<'a, I> { /// anywhere for each bound variable. /// /// See [`InvertedBoundVar`][InvertedBoundVar]. - fn invert_debrujin_idx(&self, debrujin_idx: u32, index: usize) -> InvertedBoundVar { + fn invert_debrujin_idx( + &self, + debrujin_idx: u32, + index: IndexWithinBinding, + ) -> InvertedBoundVar { InvertedBoundVar { inverted_debrujin_idx: (self.debrujin_indices_deep as i64) - (debrujin_idx as i64), within_idx: index, @@ -767,15 +809,19 @@ impl<'a, I: Interner> WriterState<'a, I> { } fn apply_mappings(&self, b: InvertedBoundVar) -> impl Display { - // TODO: sometimes produce "Self" - self.remapping.get(&b).copied().unwrap_or(b) + let remapped = self.remapping.get(&b).copied().unwrap_or(b); + if self.self_mapping == Some(remapped) { + "Self".to_owned() + } else { + remapped.to_string() + } } fn indices_for_bound_var(&self, b: &BoundVar) -> InvertedBoundVar { self.invert_debrujin_idx(b.debruijn.depth(), b.index) } - fn indices_for_introduced_bound_var(&self, idx: usize) -> InvertedBoundVar { + fn indices_for_introduced_bound_var(&self, idx: IndexWithinBinding) -> InvertedBoundVar { // freshly introduced bound vars will always have debrujin index of 0, // they're always "innermost". self.invert_debrujin_idx(0, idx) @@ -785,7 +831,7 @@ impl<'a, I: Interner> WriterState<'a, I> { self.apply_mappings(self.indices_for_bound_var(b)) } - fn name_for_introduced_bound_var(&self, idx: usize) -> impl Display { + fn name_for_introduced_bound_var(&self, idx: IndexWithinBinding) -> impl Display { self.apply_mappings(self.indices_for_introduced_bound_var(idx)) } From 075c76f3aec6f72ec935210c1667db46c6268a7b Mon Sep 17 00:00:00 2001 From: David Ross Date: Sat, 2 May 2020 19:29:35 -0700 Subject: [PATCH 09/70] Clean up extraneous <> and add basic indentation Co-authored-by: Super Tuple --- chalk-solve/src/display.rs | 226 ++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 160858c2792..0844c9bbd23 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -14,6 +14,7 @@ use chalk_rust_ir::{ AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, Polarity, QuantifiedInlineBound, StructDatum, TraitBound, TraitDatum, }; +use itertools::Itertools; use crate::{split::Split, RustIrDatabase}; @@ -53,6 +54,17 @@ pub trait RenderAsRust { } } +macro_rules! write_joined_non_empty_list { + ($f:expr,$template:tt,$list:expr,$sep:expr) => {{ + let mut x = $list.into_iter().peekable(); + if x.peek().is_some() { + write!($f, $template, x.format($sep)) + } else { + Ok(()) + } + }}; +} + impl RenderAsRust for AssociatedTyValue { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // see comments for a similar empty env operation in AssociatedTyDatum's @@ -86,13 +98,9 @@ impl RenderAsRust for AssociatedTyValue { let (_impl_display, assoc_ty_value_display) = s.db.split_associated_ty_value_parameters(&display_params, self); - write!( - f, - "type {}<{}> = {};", - s.db.identifier_name(&assoc_ty_data.name), - assoc_ty_value_display.join(", "), - self.value.value.ty.display(s) - )?; + write!(f, "type {}", s.db.identifier_name(&assoc_ty_data.name))?; + write_joined_non_empty_list!(f, "<{}>", &assoc_ty_value_display, ", ")?; + write!(f, " = {};", self.value.value.ty.display(s))?; Ok(()) } } @@ -109,15 +117,9 @@ impl RenderAsRust for Polarity { impl RenderAsRust for ImplDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); - let binders: Vec<_> = s.binder_var_display(&self.binders.binders).collect(); + let binders = s.binder_var_display(&self.binders.binders); let trait_ref = &self.binders.value.trait_ref; - - let binders_name = if binders.len() == 0 { - "".to_string() - } else { - format!("<{}>", binders.join(", ")) - }; // Ignore automatically added Self parameter by skipping first parameter let full_trait_name = display_trait_with_generics( s, @@ -125,21 +127,17 @@ impl RenderAsRust for ImplDatum { &trait_ref.substitution.parameters(interner)[1..], ); - let assoc_ty_values = self - .associated_ty_value_ids - .iter() - .map(|assoc_ty_value| { - s.db.associated_ty_value(*assoc_ty_value) - .display(s) - .to_string() - }) - .collect::>() - .join("\n"); + let assoc_ty_values = self.associated_ty_value_ids.iter().map(|assoc_ty_value| { + s.db.associated_ty_value(*assoc_ty_value) + .display(s) + .to_string() + }); + write!(f, "impl")?; + write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; write!( f, - "impl{} {}{} for {} ", - binders_name, + " {}{} for {} ", self.polarity.display(s), full_trait_name, trait_ref.self_type_parameter(interner).display(s) @@ -148,9 +146,9 @@ impl RenderAsRust for ImplDatum { if !self.binders.value.where_clauses.is_empty() { write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; } - - write!(f, "{{{}}}", assoc_ty_values)?; - + write!(f, "{{")?; + write_joined_non_empty_list!(f, "\n{}\n", assoc_ty_values, "\n")?; + write!(f, "}}")?; Ok(()) } } @@ -296,15 +294,16 @@ impl RenderAsRust for AliasTy { let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self); write!( f, - "<{} as {}>::{}<{}>", + "<{} as {}>::{}", trait_params[0].display(s), display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), s.db.identifier_name(&assoc_ty_datum.name), - assoc_type_params - .iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", ") + )?; + write_joined_non_empty_list!( + f, + "<{}>", + assoc_type_params.iter().map(|param| param.display(s)), + ", " ) } } @@ -341,17 +340,8 @@ impl RenderAsRust for ApplicationTy { TypeName::Struct(sid) => { write!(f, "{}", sid.display(s))?; let parameters = self.substitution.parameters(interner); - if parameters.len() > 0 { - write!( - f, - "<{}>", - parameters - .iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", "), - )?; - } + let parameters = parameters.iter().map(|param| param.display(s)); + write_joined_non_empty_list!(f, "<{}>", parameters, ", ")?; } TypeName::AssociatedType(assoc_type_id) => { // (Iterator::Item)(x) @@ -369,17 +359,12 @@ impl RenderAsRust for ApplicationTy { s.db.identifier_name(&datum.name), )?; let params = self.substitution.parameters(interner); - if params.len() > 1 { - write!( - f, - "<{}>", - params[1..] - .iter() - .map(|ty| ty.display(s).to_string()) - .collect::>() - .join(", ") - )?; - } + write_joined_non_empty_list!( + f, + "<{}>", + params[1..].iter().map(|ty| ty.display(s)), + "," + )?; } TypeName::Error => write!(f, "{{error}}")?, } @@ -447,9 +432,9 @@ impl RenderAsRust for Vec> { f, "{}", self.iter() - .map(|where_clause| { where_clause.display(s).to_string() }) + .map(|where_clause| { format!("{}{}",s.indent(),where_clause.display(s)) }) .collect::>() - .join(", ") + .join(",\n") )?; Ok(()) } @@ -472,12 +457,11 @@ fn display_trait_with_generics<'a, I: Interner>( trait_id: TraitId, trait_params: impl IntoIterator> + 'a, ) -> impl Display + 'a { - let trait_params = trait_params - .into_iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", "); - as_display(move |f| write!(f, "{}<{}>", trait_id.display(s), trait_params,)) + use std::fmt::Write; + let trait_params = trait_params.into_iter().map(|param| param.display(s)); + let mut trait_params_str = String::new(); + write_joined_non_empty_list!(trait_params_str, "<{}>", trait_params, ", ").unwrap(); + as_display(move |f| write!(f, "{}{}", trait_id.display(s), trait_params_str)) } /// This implementation correct inside where clauses. @@ -509,23 +493,22 @@ fn display_trait_with_assoc_ty_value<'a, I: Interner>( assoc_ty_value: &'a Ty, ) -> impl Display + 'a { as_display(move |f| { - write!( + write!(f, "{}<", assoc_ty_datum.trait_id.display(s))?; + write_joined_non_empty_list!( f, - "{}<{}, {}<{}>={}>", - assoc_ty_datum.trait_id.display(s), - trait_params - .iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", "), - s.db.identifier_name(&assoc_ty_datum.name), - assoc_ty_params - .iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", "), - assoc_ty_value.display(s) - ) + "{}, ", + trait_params.iter().map(|param| param.display(s)), + ", " + )?; + write!(f, "{}", s.db.identifier_name(&assoc_ty_datum.name))?; + write_joined_non_empty_list!( + f, + "<{}>", + assoc_ty_params.iter().map(|param| param.display(s)), + ", " + )?; + write!(f, "={}>", assoc_ty_value.display(s))?; + Ok(()) }) } @@ -599,12 +582,8 @@ impl RenderAsRust for AssociatedTyDatum { let (_, assoc_ty_params) = s.db.split_associated_ty_parameters(&binder_display_in_assoc_ty, self); - write!( - f, - "type {}<{}>", - s.db.identifier_name(&self.name), - assoc_ty_params.join(", ") - )?; + write!(f, "type {}", s.db.identifier_name(&self.name),)?; + write_joined_non_empty_list!(f, "<{}>", assoc_ty_params, ", ")?; let datum_bounds = &self.binders.value; @@ -628,8 +607,9 @@ impl RenderAsRust for AssociatedTyDatum { // note: it's a quantified clause b/c we could have `for<'a> T: Foo<'a>` // within 'where' if !datum_bounds.where_clauses.is_empty() { - let where_clauses = datum_bounds.where_clauses.display(s); - write!(f, "\nwhere\n{}", where_clauses)?; + let where_s = &s.add_indent(); + let where_clauses = datum_bounds.where_clauses.display(where_s); + write!(f, "\n{}where\n{}", s.indent(), where_clauses)?; } write!(f, ";")?; Ok(()) @@ -656,23 +636,26 @@ impl RenderAsRust for TraitDatum { non_enumerable, coinductive ); - let binders: Vec<_> = s - .binder_var_display(&self.binders.binders) - .skip(1) - .collect(); - write!(f, "trait {}<{}> ", self.id.display(s), binders.join(", "))?; + let binders = s.binder_var_display(&self.binders.binders).skip(1); + write!(f, "trait {}", self.id.display(s))?; + write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; + write!(f, " ")?; if !self.binders.value.where_clauses.is_empty() { write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; } - let assoc_types = self - .associated_ty_ids - .iter() - .map(|assoc_ty_id| { + write!(f, "{{")?; + let s = &s.add_indent(); + write_joined_non_empty_list!( + f, + "\n{}\n", + self.associated_ty_ids.iter().map(|assoc_ty_id| { let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); - assoc_ty_data.display(s).to_string() - }) - .collect::(); - write!(f, "{{{}}}", assoc_types) + format!("{}{}", s.indent(), assoc_ty_data.display(s)) + }), + "\n" + )?; + write!(f, "}}")?; + Ok(()) } } @@ -681,29 +664,33 @@ impl RenderAsRust for StructDatum { // When support for Self in structs is added, self_binding should be // changed to Some(0) let s = &s.add_debrujin_index(None); - write!( + write!(f, "struct {}", self.id.display(s),)?; + write_joined_non_empty_list!( f, - "struct {}<{}> ", - self.id.display(s), - s.binder_var_display(&self.binders.binders) - .collect::>() - .join(", ") + "<{}> ", + s.binder_var_display(&self.binders.binders), + ", " )?; + write!(f, " ")?; if !self.binders.value.where_clauses.is_empty() { write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; } - write!( + write!(f, "{{")?; + let s = &s.add_indent(); + write_joined_non_empty_list!( f, - "{{{}}}", + "\n{}\n", self.binders .value .fields .iter() .enumerate() - .map(|(idx, field)| { format!("field_{}: {}", idx, field.display(s).to_string()) }) - .collect::>() - .join(", ") + .map(|(idx, field)| { + format!("{}field_{}: {}", s.indent(), idx, field.display(s)) + }), + ",\n" )?; + write!(f, "}}")?; Ok(()) } } @@ -733,6 +720,7 @@ impl Display for InvertedBoundVar { #[derive(Clone, Debug)] pub struct WriterState<'a, I: Interner> { db: &'a dyn RustIrDatabase, + indent_level: usize, debrujin_indices_deep: u32, // lowered_(inverted_debrujin_idx, index) -> src_correct_(inverted_debrujin_idx, index) remapping: Rc>, @@ -744,12 +732,24 @@ impl<'a, I: Interner> WriterState<'a, I> { pub fn new(db: &'a dyn RustIrDatabase) -> Self { WriterState { db, + indent_level: 0, debrujin_indices_deep: 0, remapping: Rc::new(BTreeMap::new()), self_mapping: None, } } + fn add_indent(&self) -> Self { + WriterState { + indent_level: self.indent_level + 1, + ..self.clone() + } + } + + fn indent(&self) -> impl Display { + std::iter::repeat(" ").take(self.indent_level).format("") + } + /// Adds a level of debrujin index, and possibly a "Self" parameter. /// /// This should be called whenever recursing into the value within a From 4a0748d37265c4ad11e835def8278b1ded9b7928 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 2 May 2020 20:00:05 -0700 Subject: [PATCH 10/70] Get recompiling after rebase Co-authored-by: David Ross --- chalk-integration/src/db.rs | 1 - chalk-solve/src/display.rs | 179 ++++++++++++++++++++---------------- chalk-solve/src/split.rs | 2 +- 3 files changed, 101 insertions(+), 81 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 5f60c655c54..a84c2ab8a09 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -206,7 +206,6 @@ impl RustIrDatabase for ChalkDatabase { self.program_ir().unwrap().struct_name(struct_id) } - fn identifier_name(&self, ident: &::Identifier) -> String { self.program_ir().unwrap().identifier_name(ident) } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 0844c9bbd23..140ece9fcec 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -7,8 +7,8 @@ use std::{ use chalk_ir::{ interner::Interner, AliasEq, AliasTy, ApplicationTy, BoundVar, Fn as ChalkFn, Lifetime, - LifetimeData, Parameter, ParameterData, ParameterKind, QuantifiedWhereClause, StructId, - TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, + LifetimeData, Parameter, ParameterData, ParameterKind, ParameterKinds, QuantifiedWhereClause, + StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, }; use chalk_rust_ir::{ AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, Polarity, @@ -75,6 +75,7 @@ impl RenderAsRust for AssociatedTyValue { let impl_param_names_in_impl_env = s.binder_var_indices(&impl_datum.binders.binders); let s = &s.add_debrujin_index(None); + let value = self.value.skip_binders(); let param_names_in_assoc_ty_value_env = s .binder_var_indices(&self.value.binders) @@ -100,7 +101,7 @@ impl RenderAsRust for AssociatedTyValue { write!(f, "type {}", s.db.identifier_name(&assoc_ty_data.name))?; write_joined_non_empty_list!(f, "<{}>", &assoc_ty_value_display, ", ")?; - write!(f, " = {};", self.value.value.ty.display(s))?; + write!(f, " = {};", value.ty.display(s))?; Ok(()) } } @@ -117,9 +118,11 @@ impl RenderAsRust for Polarity { impl RenderAsRust for ImplDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); + // TODO: investigate why this isn't calling s.add_debrujin_index() let binders = s.binder_var_display(&self.binders.binders); + let value = self.binders.skip_binders(); - let trait_ref = &self.binders.value.trait_ref; + let trait_ref = &value.trait_ref; // Ignore automatically added Self parameter by skipping first parameter let full_trait_name = display_trait_with_generics( s, @@ -143,8 +146,8 @@ impl RenderAsRust for ImplDatum { trait_ref.self_type_parameter(interner).display(s) )?; - if !self.binders.value.where_clauses.is_empty() { - write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + if !value.where_clauses.is_empty() { + write!(f, "where {} ", value.where_clauses.display(s))?; } write!(f, "{{")?; write_joined_non_empty_list!(f, "\n{}\n", assoc_ty_values, "\n")?; @@ -220,21 +223,20 @@ impl RenderAsRust for TyData { match self { TyData::Dyn(dyn_ty) => { let s = &s.add_debrujin_index(None); + let bounds = dyn_ty.bounds.skip_binders(); write!(f, "dyn ")?; // dyn_ty.bounds.binders creates a Self binding for the trait write!( f, "{}", - dyn_ty - .bounds - .value - .iter() + bounds + .iter(interner) .map(|bound| { as_display(|f| { // each individual trait within the 'dyn' can have a // forall clause. let s = &s.add_debrujin_index(None); - if !bound.binders.is_empty() { + if !bound.binders.is_empty(interner) { write!( f, "forall<{}> ", @@ -243,7 +245,7 @@ impl RenderAsRust for TyData { .join(", ") )?; } - match &bound.value { + match &bound.skip_binders() { WhereClause::Implemented(trait_ref) => { display_trait_with_generics( s, @@ -252,18 +254,21 @@ impl RenderAsRust for TyData { ) .fmt(f) } - WhereClause::AliasEq(alias_eq) => { - let (assoc_ty_datum, trait_params, assoc_type_params) = - s.db.split_projection(&alias_eq.alias); - display_trait_with_assoc_ty_value( - s, - assoc_ty_datum, - &trait_params[1..], - assoc_type_params, - &alias_eq.ty, - ) - .fmt(f) - } + WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { + AliasTy::Projection(projection_ty) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&projection_ty); + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &alias_eq.ty, + ) + .fmt(f) + } + AliasTy::Opaque(opaque) => todo!("todo impl Trait"), + }, } }) .to_string() @@ -290,26 +295,32 @@ impl RenderAsRust for AliasTy { // trait_params is X, A1, A2, A3, // assoc_type_params is B1, B2, B3, // assoc_ty_datum stores info about Y and Z. - - let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self); - write!( - f, - "<{} as {}>::{}", - trait_params[0].display(s), - display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), - s.db.identifier_name(&assoc_ty_datum.name), - )?; - write_joined_non_empty_list!( - f, - "<{}>", - assoc_type_params.iter().map(|param| param.display(s)), - ", " - ) + match self { + AliasTy::Projection(projection_ty) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&projection_ty); + write!( + f, + "<{} as {}>::{}", + trait_params[0].display(s), + display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), + s.db.identifier_name(&assoc_ty_datum.name), + )?; + write_joined_non_empty_list!( + f, + "<{}>", + assoc_type_params.iter().map(|param| param.display(s)), + ", " + ) + } + AliasTy::Opaque(_) => todo!("opaque types"), + } } } impl RenderAsRust for ChalkFn { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); let s = &s.add_debrujin_index(None); if self.num_binders > 0 { write!( @@ -324,7 +335,8 @@ impl RenderAsRust for ChalkFn { write!( f, "fn({})", - self.parameters + self.substitution + .parameters(interner) .iter() .map(|param| param.display(s).to_string()) .collect::>() @@ -366,6 +378,9 @@ impl RenderAsRust for ApplicationTy { "," )?; } + TypeName::Scalar(_) => todo!("scalar types"), + TypeName::Tuple(_) => todo!("scalar types"), + TypeName::OpaqueType(_) => todo!("opaque type usage"), TypeName::Error => write!(f, "{{error}}")?, } Ok(()) @@ -396,8 +411,9 @@ impl RenderAsRust for ParameterData { impl RenderAsRust for QuantifiedWhereClause { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); let s = &s.add_debrujin_index(None); - if !self.binders.is_empty() { + if !self.binders.is_empty(interner) { write!( f, "forall<{}> ", @@ -406,14 +422,15 @@ impl RenderAsRust for QuantifiedWhereClause { .join(", ") )?; } - self.value.fmt(s, f) + self.skip_binders().fmt(s, f) } } impl RenderAsRust for QuantifiedInlineBound { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); let s = &s.add_debrujin_index(None); - if !self.binders.is_empty() { + if !self.binders.is_empty(&interner) { write!( f, "forall<{}> ", @@ -422,7 +439,7 @@ impl RenderAsRust for QuantifiedInlineBound { .join(", ") )?; } - self.value.fmt(s, f) + self.skip_binders().fmt(s, f) } } @@ -432,7 +449,7 @@ impl RenderAsRust for Vec> { f, "{}", self.iter() - .map(|where_clause| { format!("{}{}",s.indent(),where_clause.display(s)) }) + .map(|where_clause| { format!("{}{}", s.indent(), where_clause.display(s)) }) .collect::>() .join(",\n") )?; @@ -524,22 +541,28 @@ impl RenderAsRust for AliasEq { // trait_params is X, A1, A2, A3, // assoc_type_params is B1, B2, B3, // assoc_ty_datum stores info about Y and Z. - let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self.alias); - // An alternate form might be `<{} as {}<{}>>::{}<{}> = {}` (with same - // parameter ordering). This alternate form would be using type equality - // constraints (https://github.com/rust-lang/rust/issues/20041). - write!( - f, - "{}: {}", - trait_params[0].display(s), - display_trait_with_assoc_ty_value( - s, - assoc_ty_datum, - &trait_params[1..], - assoc_type_params, - &self.ty - ), - ) + match &self.alias { + AliasTy::Projection(projection_ty) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&projection_ty); + // An alternate form might be `<{} as {}<{}>>::{}<{}> = {}` (with same + // parameter ordering). This alternate form would be using type equality + // constraints (https://github.com/rust-lang/rust/issues/20041). + write!( + f, + "{}: {}", + trait_params[0].display(s), + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &self.ty + ), + ) + } + AliasTy::Opaque(_) => todo!("opaque types"), + } } } @@ -553,7 +576,7 @@ impl RenderAsRust for AssociatedTyDatum { // they have inside the AssociatedTyDatum (assoc_ty_names_for_trait_params), // and then add that mapping to the WriterState when writing bounds and // where clauses. - + let interner = s.db.interner(); let trait_datum = s.db.trait_datum(self.trait_id); // inverted Debrujin indices for the trait's parameters in the trait // environment @@ -585,7 +608,7 @@ impl RenderAsRust for AssociatedTyDatum { write!(f, "type {}", s.db.identifier_name(&self.name),)?; write_joined_non_empty_list!(f, "<{}>", assoc_ty_params, ", ")?; - let datum_bounds = &self.binders.value; + let datum_bounds = &self.binders.skip_binders(); if !(datum_bounds.bounds.is_empty() && datum_bounds.where_clauses.is_empty()) { write!(f, ": ")?; @@ -618,7 +641,9 @@ impl RenderAsRust for AssociatedTyDatum { impl RenderAsRust for TraitDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); let s = &s.add_debrujin_index(Some(0)); + let value = self.binders.skip_binders(); macro_rules! trait_flags { ($($n:ident),*) => { @@ -640,8 +665,8 @@ impl RenderAsRust for TraitDatum { write!(f, "trait {}", self.id.display(s))?; write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; write!(f, " ")?; - if !self.binders.value.where_clauses.is_empty() { - write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + if !value.where_clauses.is_empty() { + write!(f, "where {} ", value.where_clauses.display(s))?; } write!(f, "{{")?; let s = &s.add_indent(); @@ -664,6 +689,7 @@ impl RenderAsRust for StructDatum { // When support for Self in structs is added, self_binding should be // changed to Some(0) let s = &s.add_debrujin_index(None); + let value = self.binders.skip_binders(); write!(f, "struct {}", self.id.display(s),)?; write_joined_non_empty_list!( f, @@ -672,22 +698,17 @@ impl RenderAsRust for StructDatum { ", " )?; write!(f, " ")?; - if !self.binders.value.where_clauses.is_empty() { - write!(f, "where {} ", self.binders.value.where_clauses.display(s))?; + if !value.where_clauses.is_empty() { + write!(f, "where {} ", value.where_clauses.display(s))?; } write!(f, "{{")?; let s = &s.add_indent(); write_joined_non_empty_list!( f, "\n{}\n", - self.binders - .value - .fields - .iter() - .enumerate() - .map(|(idx, field)| { - format!("{}field_{}: {}", s.indent(), idx, field.display(s)) - }), + value.fields.iter().enumerate().map(|(idx, field)| { + format!("{}field_{}: {}", s.indent(), idx, field.display(s)) + }), ",\n" )?; write!(f, "}}")?; @@ -837,20 +858,20 @@ impl<'a, I: Interner> WriterState<'a, I> { fn binder_var_indices<'b>( &'b self, - binders: &'b [ParameterKind<()>], + binders: &'b ParameterKinds, ) -> impl Iterator + 'b { binders - .iter() + .iter(self.db.interner()) .enumerate() .map(move |(idx, _param)| self.indices_for_introduced_bound_var(idx)) } fn binder_var_display<'b>( &'b self, - binders: &'b [ParameterKind<()>], + binders: &'b ParameterKinds, ) -> impl Iterator + 'b { binders - .iter() + .iter(self.db.interner()) .zip(self.binder_var_indices(binders)) .map(move |(parameter, var)| match parameter { ParameterKind::Ty(_) => format!("{}", self.apply_mappings(var)), diff --git a/chalk-solve/src/split.rs b/chalk-solve/src/split.rs index 32348042ee8..9a7b06dad70 100644 --- a/chalk-solve/src/split.rs +++ b/chalk-solve/src/split.rs @@ -193,7 +193,7 @@ pub trait Split: RustIrDatabase { associated_ty_datum: &AssociatedTyDatum, ) -> (&'p [P], &'p [P]) { let trait_datum = &self.trait_datum(associated_ty_datum.trait_id); - let trait_num_params = trait_datum.binders.len(); + let trait_num_params = trait_datum.binders.len(self.interner()); let split_point = parameters.len() - trait_num_params; let (other_params, trait_params) = parameters.split_at(split_point); (trait_params, other_params) From 9f45a1e7dadb65bd5713b57313e67569c9de0763 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 12:01:51 -0700 Subject: [PATCH 11/70] Get recompiling after rebase Co-authored-by: David Ross --- chalk-integration/src/program.rs | 2 +- chalk-integration/src/program_writer.rs | 2 +- chalk-solve/src/display.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 6df90f0c6a8..b22c09859ea 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -2,7 +2,7 @@ use crate::interner::ChalkIr; use crate::{tls, Identifier, TypeKind}; use chalk_ir::could_match::CouldMatch; use chalk_ir::debug::Angle; -use chalk_ir::interner::{ Interner }; +use chalk_ir::interner::Interner; use chalk_ir::{ debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, Binders, ClosureId, FnDefId, GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 580599acf8c..802ae0f3c50 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -27,7 +27,7 @@ impl WriteProgram for Program { mod test { use super::*; use crate::lowering::LowerProgram; - use chalk_ir::tls; + use crate::tls; use std::{fmt::Debug, sync::Arc}; fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 140ece9fcec..b27fa80eb22 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -381,6 +381,7 @@ impl RenderAsRust for ApplicationTy { TypeName::Scalar(_) => todo!("scalar types"), TypeName::Tuple(_) => todo!("scalar types"), TypeName::OpaqueType(_) => todo!("opaque type usage"), + TypeName::Raw(_) => write!(f, "raw ptr type")?, TypeName::Error => write!(f, "{{error}}")?, } Ok(()) From 40f3c3b76bdfac26aa14fab72730054d7285b571 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 15:20:23 -0700 Subject: [PATCH 12/70] Remove WriteProgram in preparation for moving tests Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 53 ++++++++++++------------- chalk-solve/src/display.rs | 11 +++++ 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 802ae0f3c50..f18c264780d 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -1,34 +1,30 @@ -use chalk_solve::display::{RenderAsRust, WriterState}; - -use crate::program::Program; - -pub trait WriteProgram { - fn write(&self) -> String; -} - -impl WriteProgram for Program { - fn write(&self) -> String { - let mut lines = vec![]; - let ws = &WriterState::new(self); - self.struct_data.values().for_each(|datum| { - lines.push(datum.display(ws).to_string()); - }); - self.trait_data.values().for_each(|datum| { - lines.push(datum.display(ws).to_string()); - }); - self.impl_data.values().for_each(|datum| { - lines.push(datum.display(ws).to_string()); - }); - lines.join("\n") - } -} - #[cfg(test)] mod test { - use super::*; + use std::{fmt::Debug, sync::Arc}; + + use chalk_solve::display; + use crate::lowering::LowerProgram; + use crate::program::Program; use crate::tls; - use std::{fmt::Debug, sync::Arc}; + + fn write_program(program: &Program) -> String { + let mut out = String::new(); + for datum in program.struct_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + for datum in program.trait_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + for datum in program.impl_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + for _datum in program.opaque_ty_data.values() { + todo!("opaque type display"); + //display::write_top_level(&mut out, program, &**datum).unwrap(); + } + out + } fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { use std::fmt::Write; @@ -110,7 +106,8 @@ mod test { e, program_text ) })); - let output_text = tls::set_current_program(&original_program, || original_program.write()); + let output_text = + tls::set_current_program(&original_program, || write_program(&original_program)); let output_program = chalk_parse::parse_program(&output_text).unwrap_or_else(|e| { panic!( "unable to reparse writer output:\n{}\nNew source:\n{}\n", diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index b27fa80eb22..5be14c3958f 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -18,6 +18,17 @@ use itertools::Itertools; use crate::{split::Split, RustIrDatabase}; +pub fn write_top_level(f: &mut F, db: &Db, v: &T) -> Result +where + I: Interner, + Db: RustIrDatabase, + T: RenderAsRust, + F: std::fmt::Write, +{ + let ws = &WriterState::new(db); + write!(f, "{}\n", v.display(ws)) +} + /// Displays `RenderAsRust` data. /// /// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. From b889edb7dd6b4fb55ab0703f376cce2c03e0a5ca Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 15:38:50 -0700 Subject: [PATCH 13/70] Add RustIrDatabase debug wrappers Provides wrappers over `RustIrDatabase` which record used definitions and write `.chalk` files containing those definitions. Added: * `LoggingRustIrDatabase` which wraps another `RustIrDatabase` (`DB`) and records which definitions are used. A full .chalk file containing all used definitions can be recovered through `LoggingRustIrDatabase`'s `Display` implementation. * `WriteOnDropRustIrDatabase` which wraps a [`RustIrDatabase`], and, when dropped, writes out all used definition to the given file. Uses [`LoggingRustIrDatabase`] internally. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 39 +++- chalk-solve/src/lib.rs | 1 + chalk-solve/src/logging_db.rs | 349 ++++++++++++++++++++++++++++++++++ 3 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 chalk-solve/src/logging_db.rs diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 5be14c3958f..bde1b5dcffc 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -16,12 +16,12 @@ use chalk_rust_ir::{ }; use itertools::Itertools; -use crate::{split::Split, RustIrDatabase}; +use crate::{logging_db::RecordedItemId, split::Split, RustIrDatabase}; -pub fn write_top_level(f: &mut F, db: &Db, v: &T) -> Result +pub fn write_top_level(f: &mut F, db: &DB, v: &T) -> Result where I: Interner, - Db: RustIrDatabase, + DB: RustIrDatabase, T: RenderAsRust, F: std::fmt::Write, { @@ -29,6 +29,39 @@ where write!(f, "{}\n", v.display(ws)) } +/// Writes out each item recorded by a [`LoggingRustIrDatabase`]. +/// +/// [`LoggingRustIrDatabase`]: crate::logging_db::LoggingRustIrDatabase +pub fn write_program(f: &mut Formatter<'_>, db: &DB, ids: T) -> Result +where + I: Interner, + DB: RustIrDatabase, + T: IntoIterator>, +{ + for id in ids { + match id { + RecordedItemId::Impl(id) => { + let v = db.impl_datum(id); + write_top_level(f, db, &*v)?; + } + RecordedItemId::Struct(id) => { + let v = db.struct_datum(id); + write_top_level(f, db, &*v)?; + } + RecordedItemId::Trait(id) => { + let v = db.trait_datum(id); + write_top_level(f, db, &*v)?; + } + RecordedItemId::OpaqueTy(id) => { + let _v = db.opaque_ty_data(id); + todo!("opaque ty display") + // write_top_level(f, db, v)?; + } + } + } + Ok(()) +} + /// Displays `RenderAsRust` data. /// /// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 5d571e40187..abb199a5a0f 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -19,6 +19,7 @@ pub mod ext; pub mod goal_builder; mod infer; pub mod logging; +pub mod logging_db; #[cfg(feature = "recursive-solver")] pub mod recursive; pub mod rust_ir; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs new file mode 100644 index 00000000000..33571e96c25 --- /dev/null +++ b/chalk-solve/src/logging_db.rs @@ -0,0 +1,349 @@ +//! Provides wrappers over `RustIrDatabase` which record used definitions and write +//! `.chalk` files containing those definitions. +use std::{ + cmp::Ord, cmp::Ordering, collections::BTreeSet, fmt, fmt::Display, io::Write, sync::Arc, + sync::Mutex, +}; + +use chalk_ir::{interner::Interner, ImplId, OpaqueTyId, StructId, TraitId}; +use chalk_rust_ir::{ImplDatum, OpaqueTyDatum, StructDatum, TraitDatum}; + +use crate::{display, RustIrDatabase}; +/// Wraps another `RustIrDatabase` (`DB`) and records which definitions are used. +/// +/// A full .chalk file containing all used definitions can be recovered through +/// `LoggingRustIrDatabase`'s `Display` implementation. +#[derive(Debug)] +pub struct LoggingRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, +{ + db: DB, + def_ids: Mutex>>, +} + +impl LoggingRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, +{ + pub fn new(db: DB) -> Self { + LoggingRustIrDatabase { + db, + def_ids: Default::default(), + } + } +} + +impl Display for LoggingRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let def_ids = self.def_ids.lock().unwrap(); + display::write_program(f, &self.db, def_ids.iter().copied()) + } +} + +impl LoggingRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, +{ + fn record(&self, id: impl Into>) { + self.def_ids.lock().unwrap().insert(id.into()); + } + fn record_all(&self, ids: T) + where + T: IntoIterator, + U: Into>, + { + self.def_ids + .lock() + .unwrap() + .extend(ids.into_iter().map(Into::into)); + } +} + +impl RustIrDatabase for LoggingRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, +{ + fn custom_clauses(&self) -> Vec> { + self.db.custom_clauses() + } + fn associated_ty_data( + &self, + ty: chalk_ir::AssocTypeId, + ) -> Arc> { + let ty_datum = self.db.associated_ty_data(ty); + self.record(ty_datum.trait_id); + ty_datum + } + fn trait_datum(&self, trait_id: TraitId) -> Arc> { + self.record(trait_id); + self.db.trait_datum(trait_id) + } + fn struct_datum(&self, struct_id: StructId) -> Arc> { + self.record(struct_id); + self.db.struct_datum(struct_id) + } + fn impl_datum(&self, impl_id: ImplId) -> Arc> { + self.record(impl_id); + self.db.impl_datum(impl_id) + } + fn associated_ty_value( + &self, + id: chalk_rust_ir::AssociatedTyValueId, + ) -> Arc> { + let value = self.db.associated_ty_value(id); + self.record(value.impl_id); + value + } + fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { + self.record(id); + self.db.opaque_ty_data(id) + } + fn impls_for_trait( + &self, + trait_id: TraitId, + parameters: &[chalk_ir::Parameter], + ) -> Vec> { + self.record(trait_id); + let impl_ids = self.db.impls_for_trait(trait_id, parameters); + self.record_all(impl_ids.iter().copied()); + impl_ids + } + fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { + self.record(trait_id); + self.db.local_impls_to_coherence_check(trait_id) + } + fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { + self.record(auto_trait_id); + self.record(struct_id); + self.db.impl_provided_for(auto_trait_id, struct_id) + } + fn well_known_trait_id( + &self, + well_known_trait: chalk_rust_ir::WellKnownTrait, + ) -> Option> { + let trait_id = self.db.well_known_trait_id(well_known_trait); + trait_id.map(|id| self.record(id)); + trait_id + } + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment, + ) -> chalk_ir::ProgramClauses { + self.db.program_clauses_for_env(environment) + } + fn interner(&self) -> &I { + self.db.interner() + } + fn trait_name(&self, trait_id: TraitId) -> String { + self.db.trait_name(trait_id) + } + fn struct_name(&self, struct_id: StructId) -> String { + self.db.struct_name(struct_id) + } + fn identifier_name(&self, ident: &I::Identifier) -> String { + self.db.identifier_name(ident) + } + fn is_object_safe(&self, trait_id: TraitId) -> bool { + self.record(trait_id); + self.db.is_object_safe(trait_id) + } +} + +/// Wraps a [`RustIrDatabase`], and, when dropped, writes out all used +/// definition to the given file. +/// +/// Uses [`LoggingRustIrDatabase`] internally. +pub struct WriteOnDropRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, + W: Write, +{ + db: LoggingRustIrDatabase, + write: W, +} + +impl fmt::Debug for WriteOnDropRustIrDatabase +where + I: Interner, + DB: RustIrDatabase + fmt::Debug, + W: Write, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WriteOnDropRustIrDatabase") + .field("db", &self.db) + .field("write", &"") + .finish() + } +} + +impl WriteOnDropRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, + W: Write, +{ + pub fn new(db: DB, write: W) -> Self { + WriteOnDropRustIrDatabase { + db: LoggingRustIrDatabase::new(db), + write, + } + } + + pub fn from_logging_db(db: LoggingRustIrDatabase, write: W) -> Self { + WriteOnDropRustIrDatabase { db, write } + } +} + +impl Drop for WriteOnDropRustIrDatabase +where + DB: RustIrDatabase, + I: Interner, + W: Write, +{ + fn drop(&mut self) { + write!(self.write, "{}", self.db) + .and_then(|_| self.write.flush()) + .expect("expected to be able to write rust ir database"); + } +} + +impl RustIrDatabase for WriteOnDropRustIrDatabase +where + DB: RustIrDatabase, + W: Write, + I: Interner, +{ + fn custom_clauses(&self) -> Vec> { + self.db.custom_clauses() + } + fn associated_ty_data( + &self, + ty: chalk_ir::AssocTypeId, + ) -> Arc> { + self.db.associated_ty_data(ty) + } + fn trait_datum(&self, trait_id: TraitId) -> Arc> { + self.db.trait_datum(trait_id) + } + fn struct_datum(&self, struct_id: StructId) -> Arc> { + self.db.struct_datum(struct_id) + } + fn impl_datum(&self, impl_id: ImplId) -> Arc> { + self.db.impl_datum(impl_id) + } + fn associated_ty_value( + &self, + id: chalk_rust_ir::AssociatedTyValueId, + ) -> Arc> { + self.db.associated_ty_value(id) + } + fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { + self.db.opaque_ty_data(id) + } + fn impls_for_trait( + &self, + trait_id: TraitId, + parameters: &[chalk_ir::Parameter], + ) -> Vec> { + self.db.impls_for_trait(trait_id, parameters) + } + fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { + self.db.local_impls_to_coherence_check(trait_id) + } + fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { + self.db.impl_provided_for(auto_trait_id, struct_id) + } + fn well_known_trait_id( + &self, + well_known_trait: chalk_rust_ir::WellKnownTrait, + ) -> Option> { + self.db.well_known_trait_id(well_known_trait) + } + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment, + ) -> chalk_ir::ProgramClauses { + self.db.program_clauses_for_env(environment) + } + fn interner(&self) -> &I { + self.db.interner() + } + fn trait_name(&self, trait_id: TraitId) -> String { + self.db.trait_name(trait_id) + } + fn struct_name(&self, struct_id: StructId) -> String { + self.db.struct_name(struct_id) + } + fn identifier_name(&self, ident: &I::Identifier) -> String { + self.db.identifier_name(ident) + } + + fn is_object_safe(&self, trait_id: TraitId) -> bool { + self.db.is_object_safe(trait_id) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum RecordedItemId { + Struct(StructId), + Trait(TraitId), + Impl(ImplId), + OpaqueTy(OpaqueTyId), +} + +impl From> for RecordedItemId { + fn from(v: StructId) -> Self { + RecordedItemId::Struct(v) + } +} +impl From> for RecordedItemId { + fn from(v: TraitId) -> Self { + RecordedItemId::Trait(v) + } +} + +impl From> for RecordedItemId { + fn from(v: ImplId) -> Self { + RecordedItemId::Impl(v) + } +} +impl From> for RecordedItemId { + fn from(v: OpaqueTyId) -> Self { + RecordedItemId::OpaqueTy(v) + } +} + +impl RecordedItemId { + /// Extract internal identifier. Allows for absolute ordering matching the + /// order in which chalk saw things (and thus reproducing that order in + /// printed programs) + fn def_id(&self) -> &I::DefId { + match self { + RecordedItemId::Trait(TraitId(x)) + | RecordedItemId::Struct(StructId(x)) + | RecordedItemId::Impl(ImplId(x)) + | RecordedItemId::OpaqueTy(OpaqueTyId(x)) => x, + } + } +} + +impl PartialOrd for RecordedItemId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for RecordedItemId { + fn cmp(&self, other: &Self) -> Ordering { + self.def_id().cmp(other.def_id()) + } +} From 47d3477e6787758443642f23c789d9f1fc91e34f Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 17:16:34 -0700 Subject: [PATCH 14/70] Add AssocTyId-specific name retrieval method Co-authored-by: David Ross --- chalk-integration/src/db.rs | 4 ++-- chalk-integration/src/program.rs | 9 ++++++--- chalk-solve/src/display.rs | 26 +++++++++++++++----------- chalk-solve/src/lib.rs | 2 +- chalk-solve/src/logging_db.rs | 10 +++++----- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index a84c2ab8a09..8d51afba22b 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -206,7 +206,7 @@ impl RustIrDatabase for ChalkDatabase { self.program_ir().unwrap().struct_name(struct_id) } - fn identifier_name(&self, ident: &::Identifier) -> String { - self.program_ir().unwrap().identifier_name(ident) + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { + self.program_ir().unwrap().assoc_type_name(assoc_ty_id) } } diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index b22c09859ea..5d3eca812ba 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -2,7 +2,6 @@ use crate::interner::ChalkIr; use crate::{tls, Identifier, TypeKind}; use chalk_ir::could_match::CouldMatch; use chalk_ir::debug::Angle; -use chalk_ir::interner::Interner; use chalk_ir::{ debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, Binders, ClosureId, FnDefId, GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, @@ -478,7 +477,11 @@ impl RustIrDatabase for Program { self.struct_kinds.get(&struct_id).unwrap().name.to_string() } - fn identifier_name(&self, ident: &::Identifier) -> String { - ident.to_string() + fn assoc_type_name(&self, assoc_type_id: AssocTypeId) -> String { + self.associated_ty_data + .get(&assoc_type_id) + .unwrap() + .name + .to_string() } } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index bde1b5dcffc..3ea8b3b9300 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -6,9 +6,9 @@ use std::{ }; use chalk_ir::{ - interner::Interner, AliasEq, AliasTy, ApplicationTy, BoundVar, Fn as ChalkFn, Lifetime, - LifetimeData, Parameter, ParameterData, ParameterKind, ParameterKinds, QuantifiedWhereClause, - StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, + interner::Interner, AliasEq, AliasTy, ApplicationTy, AssocTypeId, BoundVar, Fn as ChalkFn, + Lifetime, LifetimeData, Parameter, ParameterData, ParameterKind, ParameterKinds, + QuantifiedWhereClause, StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, }; use chalk_rust_ir::{ AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, Polarity, @@ -143,7 +143,7 @@ impl RenderAsRust for AssociatedTyValue { let (_impl_display, assoc_ty_value_display) = s.db.split_associated_ty_value_parameters(&display_params, self); - write!(f, "type {}", s.db.identifier_name(&assoc_ty_data.name))?; + write!(f, "type {}", assoc_ty_data.id.display(s))?; write_joined_non_empty_list!(f, "<{}>", &assoc_ty_value_display, ", ")?; write!(f, " = {};", value.ty.display(s))?; Ok(()) @@ -260,6 +260,12 @@ impl RenderAsRust for TraitId { f.write_str(&s.db.trait_name(*self)) } } +impl RenderAsRust for AssocTypeId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + f.write_str(&s.db.assoc_type_name(*self)) + } +} impl RenderAsRust for TyData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { @@ -311,7 +317,7 @@ impl RenderAsRust for TyData { ) .fmt(f) } - AliasTy::Opaque(opaque) => todo!("todo impl Trait"), + AliasTy::Opaque(_opaque) => todo!("todo impl Trait"), }, } }) @@ -348,7 +354,7 @@ impl RenderAsRust for AliasTy { "<{} as {}>::{}", trait_params[0].display(s), display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), - s.db.identifier_name(&assoc_ty_datum.name), + assoc_ty_datum.id.display(s), )?; write_joined_non_empty_list!( f, @@ -412,7 +418,7 @@ impl RenderAsRust for ApplicationTy { "<{} as {}>::{}", self.first_type_parameter(interner).unwrap().display(s), datum.trait_id.display(s), - s.db.identifier_name(&datum.name), + datum.id.display(s), )?; let params = self.substitution.parameters(interner); write_joined_non_empty_list!( @@ -562,7 +568,7 @@ fn display_trait_with_assoc_ty_value<'a, I: Interner>( trait_params.iter().map(|param| param.display(s)), ", " )?; - write!(f, "{}", s.db.identifier_name(&assoc_ty_datum.name))?; + write!(f, "{}", assoc_ty_datum.id.display(s))?; write_joined_non_empty_list!( f, "<{}>", @@ -621,7 +627,6 @@ impl RenderAsRust for AssociatedTyDatum { // they have inside the AssociatedTyDatum (assoc_ty_names_for_trait_params), // and then add that mapping to the WriterState when writing bounds and // where clauses. - let interner = s.db.interner(); let trait_datum = s.db.trait_datum(self.trait_id); // inverted Debrujin indices for the trait's parameters in the trait // environment @@ -650,7 +655,7 @@ impl RenderAsRust for AssociatedTyDatum { let (_, assoc_ty_params) = s.db.split_associated_ty_parameters(&binder_display_in_assoc_ty, self); - write!(f, "type {}", s.db.identifier_name(&self.name),)?; + write!(f, "type {}", self.id.display(s))?; write_joined_non_empty_list!(f, "<{}>", assoc_ty_params, ", ")?; let datum_bounds = &self.binders.skip_binders(); @@ -686,7 +691,6 @@ impl RenderAsRust for AssociatedTyDatum { impl RenderAsRust for TraitDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); let s = &s.add_debrujin_index(Some(0)); let value = self.binders.skip_binders(); diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index abb199a5a0f..fdec675e2a7 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -150,7 +150,7 @@ pub trait RustIrDatabase: Debug { fn struct_name(&self, struct_id: StructId) -> String; /// Retrieves the name of an identifier - fn identifier_name(&self, ident: &I::Identifier) -> String; + fn assoc_type_name(&self, ident: AssocTypeId) -> String; } pub use clauses::program_clauses_for_env; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 33571e96c25..42217a3aeb0 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -5,7 +5,7 @@ use std::{ sync::Mutex, }; -use chalk_ir::{interner::Interner, ImplId, OpaqueTyId, StructId, TraitId}; +use chalk_ir::{interner::Interner, AssocTypeId, ImplId, OpaqueTyId, StructId, TraitId}; use chalk_rust_ir::{ImplDatum, OpaqueTyDatum, StructDatum, TraitDatum}; use crate::{display, RustIrDatabase}; @@ -149,8 +149,8 @@ where fn struct_name(&self, struct_id: StructId) -> String { self.db.struct_name(struct_id) } - fn identifier_name(&self, ident: &I::Identifier) -> String { - self.db.identifier_name(ident) + fn assoc_type_name(&self, ident: AssocTypeId) -> String { + self.db.assoc_type_name(ident) } fn is_object_safe(&self, trait_id: TraitId) -> bool { self.record(trait_id); @@ -284,8 +284,8 @@ where fn struct_name(&self, struct_id: StructId) -> String { self.db.struct_name(struct_id) } - fn identifier_name(&self, ident: &I::Identifier) -> String { - self.db.identifier_name(ident) + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { + self.db.assoc_type_name(assoc_ty_id) } fn is_object_safe(&self, trait_id: TraitId) -> bool { From fc7c6a71568e2eab6d0424159277c092000e514c Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 19:46:21 -0700 Subject: [PATCH 15/70] Implement scalar types rendering Implements `RenderAsRust` for ScalarTy to display types such as bool, char, f32, i32, usize etc. Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 58 +++++++++++++++++++++++++ chalk-solve/src/display.rs | 53 ++++++++++++++++++++-- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index f18c264780d..cb5fb4c88d0 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -889,6 +889,64 @@ mod test { } } + #[test] + fn test_scalar_types() { + let basic = vec!["bool", "char", "f32", "f64"]; + let sizes = vec!["size", "8", "16", "32", "64", "128"]; + let prefixes = vec!["u", "i"]; + let ints = prefixes + .iter() + .flat_map(|&p| sizes.iter().map(move |&size| format!("{}{}", p, size))); + let scalars = basic.iter().copied().map(str::to_owned).chain(ints); + + for scalar in scalars { + reparse_test(&format!( + " + struct Foo {{ + field: {0} + }} + trait Bar {{ + type Baz; + }} + impl Bar for Foo {{ + type Baz = {0}; + }} + ", + scalar + )); + } + } + + #[test] + fn test_raw_ptr_types() { + reparse_test( + " + struct Foo { + field: *const T + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = *const u32; + } + ", + ); + reparse_test( + " + struct Foo { + field: *mut T + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = *mut u32; + } + ", + ); + } + #[test] fn test_trait_impl_associated_type() { reparse_test( diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 3ea8b3b9300..dfff630a792 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -7,8 +7,8 @@ use std::{ use chalk_ir::{ interner::Interner, AliasEq, AliasTy, ApplicationTy, AssocTypeId, BoundVar, Fn as ChalkFn, - Lifetime, LifetimeData, Parameter, ParameterData, ParameterKind, ParameterKinds, - QuantifiedWhereClause, StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, + Lifetime, LifetimeData, Mutability, Parameter, ParameterData, ParameterKind, ParameterKinds, + QuantifiedWhereClause, Scalar, StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, }; use chalk_rust_ir::{ AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, Polarity, @@ -428,16 +428,61 @@ impl RenderAsRust for ApplicationTy { "," )?; } - TypeName::Scalar(_) => todo!("scalar types"), + TypeName::Scalar(scalar) => write!(f, "{}", scalar.display(s))?, TypeName::Tuple(_) => todo!("scalar types"), TypeName::OpaqueType(_) => todo!("opaque type usage"), - TypeName::Raw(_) => write!(f, "raw ptr type")?, + TypeName::Raw(raw) => { + let mutability = match raw { + Mutability::Mut => "*mut ", + Mutability::Not => "*const ", + }; + write!( + f, + "{}{}", + mutability, + self.first_type_parameter(interner).unwrap().display(s) + )? + } TypeName::Error => write!(f, "{{error}}")?, } Ok(()) } } +impl RenderAsRust for Scalar { + fn fmt(&self, _s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + use chalk_ir::{FloatTy::*, IntTy::*, UintTy::*}; + write!( + f, + "{}", + match self { + Scalar::Bool => "bool", + Scalar::Char => "char", + Scalar::Int(int) => match int { + Isize => "isize", + I8 => "i8", + I16 => "i16", + I32 => "i32", + I64 => "i64", + I128 => "i128", + }, + Scalar::Uint(uint) => match uint { + Usize => "usize", + U8 => "u8", + U16 => "u16", + U32 => "u32", + U64 => "u64", + U128 => "u128", + }, + Scalar::Float(float) => match float { + F32 => "f32", + F64 => "f64", + }, + } + ) + } +} + impl RenderAsRust for LifetimeData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { From 8aea12868938c1c61c12756f1abb903d556814dd Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 19:57:43 -0700 Subject: [PATCH 16/70] Fix missing add_debrujin_index cal Co-authored-by: David Ross --- chalk-solve/src/display.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index dfff630a792..6678f6425f5 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -162,8 +162,10 @@ impl RenderAsRust for Polarity { impl RenderAsRust for ImplDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); - // TODO: investigate why this isn't calling s.add_debrujin_index() + + let s = &s.add_debrujin_index(None); let binders = s.binder_var_display(&self.binders.binders); + let value = self.binders.skip_binders(); let trait_ref = &value.trait_ref; From 21556f82a6104b88eb590d47775088236645224f Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 19:59:45 -0700 Subject: [PATCH 17/70] Add opaque type definition rendering Added rendering for opaque type defintions such as: `opaque type Foo: Baz = Buzz;` Co-authored-by: David Ross --- chalk-integration/src/db.rs | 3 + chalk-integration/src/program.rs | 8 + chalk-integration/src/program_writer.rs | 128 +++++++++++++- chalk-solve/src/display.rs | 213 +++++++++++++++--------- chalk-solve/src/lib.rs | 7 +- chalk-solve/src/logging_db.rs | 15 +- 6 files changed, 286 insertions(+), 88 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 8d51afba22b..e0ba1450831 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -209,4 +209,7 @@ impl RustIrDatabase for ChalkDatabase { fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.program_ir().unwrap().assoc_type_name(assoc_ty_id) } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { + self.program_ir().unwrap().opaque_type_name(opaque_ty_id) + } } diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 5d3eca812ba..bc3a6148039 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -484,4 +484,12 @@ impl RustIrDatabase for Program { .name .to_string() } + + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { + self.opaque_ty_kinds + .get(&opaque_ty_id) + .unwrap() + .name + .to_string() + } } diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index cb5fb4c88d0..1f1417ee9fb 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -19,8 +19,8 @@ mod test { for datum in program.impl_data.values() { display::write_top_level(&mut out, program, &**datum).unwrap(); } - for _datum in program.opaque_ty_data.values() { - todo!("opaque type display"); + for datum in program.opaque_ty_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); //display::write_top_level(&mut out, program, &**datum).unwrap(); } out @@ -628,6 +628,15 @@ mod test { ", ); assert!(!in_impl.output_text.contains("Self")); + let in_opaque = reparse_test( + " + struct Foo {} + trait Que {} + impl Que for Foo {} + opaque type Bar: Que = Foo; + ", + ); + assert!(!in_opaque.output_text.contains("Self")); } #[test] @@ -1032,4 +1041,119 @@ mod test { ", ); } + #[test] + fn opaque_types() { + reparse_test( + " + struct Bar {} + trait Buz {} + trait Baz { + type Hi; + } + impl Buz for Bar {} + impl Baz for Foo { + type Hi = Foo; + } + opaque type Foo: Buz = Bar; + ", + ); + } + + #[test] + fn test_generic_opaque_types() { + reparse_test( + " + struct Foo {} + trait Bar {} + opaque type Baz: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + struct Unit {} + trait Bar {} + opaque type Boz: Bar = Foo; + ", + ); + } + + #[test] + fn test_opaque_type_as_type_value() { + reparse_test( + " + struct Foo {} + trait Bar {} + trait Fuzz { + type Assoc: Bar; + } + impl Bar for Foo {} + impl Fuzz for Foo { + type Assoc = Bax; + } + opaque type Bax: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + trait Bar {} + trait Faz { + type Assoc; + } + impl Faz for Foo { + type Assoc = fn(Baz); + } + opaque type Baz: Bar = Foo; + ", + ); + } + + // Generic opaque types can't currently be used as types (these fail to lower) + #[ignore] + #[test] + fn test_generic_opaque_type_as_value1() { + reparse_test( + " + struct Foo {} + trait Bar {} + trait Fizz { + type Assoc: Bar; + } + impl Bar for Foo {} + impl Fizz for Foo { + type Assoc = Baz; + } + opaque type Baz: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + trait Bar {} + trait Faz { + type Assoc; + } + impl Faz for Foo { + type Assoc = fn(Baz); + } + opaque type Baz: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + struct Unit {} + trait Bar {} + trait Fez { + type Assoc; + } + impl Fez for Foo { + type Assoc = fn(Biiiz); + } + impl Bar for Foo {} + opaque type Biiiz: Bar = Foo; + ", + ); + } } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 6678f6425f5..3d1aded3ee3 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -7,12 +7,13 @@ use std::{ use chalk_ir::{ interner::Interner, AliasEq, AliasTy, ApplicationTy, AssocTypeId, BoundVar, Fn as ChalkFn, - Lifetime, LifetimeData, Mutability, Parameter, ParameterData, ParameterKind, ParameterKinds, - QuantifiedWhereClause, Scalar, StructId, TraitId, TraitRef, Ty, TyData, TypeName, WhereClause, + Lifetime, LifetimeData, Mutability, OpaqueTy, OpaqueTyId, Parameter, ParameterData, + ParameterKind, ParameterKinds, ProjectionTy, QuantifiedWhereClause, Scalar, StructId, TraitId, + TraitRef, Ty, TyData, TypeName, WhereClause, }; use chalk_rust_ir::{ - AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, Polarity, - QuantifiedInlineBound, StructDatum, TraitBound, TraitDatum, + AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, OpaqueTyDatum, + Polarity, QuantifiedInlineBound, StructDatum, TraitBound, TraitDatum, }; use itertools::Itertools; @@ -53,9 +54,8 @@ where write_top_level(f, db, &*v)?; } RecordedItemId::OpaqueTy(id) => { - let _v = db.opaque_ty_data(id); - todo!("opaque ty display") - // write_top_level(f, db, v)?; + let v = db.opaque_ty_data(id); + write_top_level(f, db, &*v)?; } } } @@ -268,6 +268,68 @@ impl RenderAsRust for AssocTypeId { f.write_str(&s.db.assoc_type_name(*self)) } } +impl RenderAsRust for OpaqueTyId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + f.write_str(&s.db.opaque_type_name(*self)) + } +} + +fn display_self_where_clauses_as_bounds<'a, I: Interner>( + s: &'a WriterState<'a, I>, + bounds: &'a [QuantifiedWhereClause], +) -> impl Display + 'a { + as_display(move |f| { + let interner = s.db.interner(); + write!( + f, + "{}", + bounds + .iter() + .map(|bound| { + as_display(|f| { + // each individual trait can have a forall + let s = &s.add_debrujin_index(None); + if !bound.binders.is_empty(interner) { + write!( + f, + "forall<{}> ", + s.binder_var_display(&bound.binders) + .collect::>() + .join(", ") + )?; + } + match &bound.skip_binders() { + WhereClause::Implemented(trait_ref) => display_trait_with_generics( + s, + trait_ref.trait_id, + &trait_ref.substitution.parameters(interner)[1..], + ) + .fmt(f), + WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { + AliasTy::Projection(projection_ty) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&projection_ty); + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &alias_eq.ty, + ) + .fmt(f) + } + AliasTy::Opaque(_opaque) => todo!("opaque type AliasTy"), + }, + } + }) + .to_string() + }) + .collect::>() + .join(" + ") + ) + }) +} impl RenderAsRust for TyData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { @@ -275,59 +337,14 @@ impl RenderAsRust for TyData { match self { TyData::Dyn(dyn_ty) => { let s = &s.add_debrujin_index(None); - let bounds = dyn_ty.bounds.skip_binders(); - write!(f, "dyn ")?; // dyn_ty.bounds.binders creates a Self binding for the trait + let bounds = dyn_ty.bounds.skip_binders(); write!( f, - "{}", - bounds - .iter(interner) - .map(|bound| { - as_display(|f| { - // each individual trait within the 'dyn' can have a - // forall clause. - let s = &s.add_debrujin_index(None); - if !bound.binders.is_empty(interner) { - write!( - f, - "forall<{}> ", - s.binder_var_display(&bound.binders) - .collect::>() - .join(", ") - )?; - } - match &bound.skip_binders() { - WhereClause::Implemented(trait_ref) => { - display_trait_with_generics( - s, - trait_ref.trait_id, - &trait_ref.substitution.parameters(interner)[1..], - ) - .fmt(f) - } - WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { - AliasTy::Projection(projection_ty) => { - let (assoc_ty_datum, trait_params, assoc_type_params) = - s.db.split_projection(&projection_ty); - display_trait_with_assoc_ty_value( - s, - assoc_ty_datum, - &trait_params[1..], - assoc_type_params, - &alias_eq.ty, - ) - .fmt(f) - } - AliasTy::Opaque(_opaque) => todo!("todo impl Trait"), - }, - } - }) - .to_string() - }) - .collect::>() - .join(" + ") - ) + "dyn {}", + display_self_where_clauses_as_bounds(s, bounds.as_slice(interner)) + )?; + Ok(()) } TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), TyData::Alias(alias_ty) => alias_ty.fmt(s, f), @@ -340,6 +357,15 @@ impl RenderAsRust for TyData { } impl RenderAsRust for AliasTy { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + AliasTy::Projection(projection_ty) => projection_ty.fmt(s, f), + AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(s, f), + } + } +} + +impl RenderAsRust for ProjectionTy { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // >::Z @@ -347,26 +373,35 @@ impl RenderAsRust for AliasTy { // trait_params is X, A1, A2, A3, // assoc_type_params is B1, B2, B3, // assoc_ty_datum stores info about Y and Z. - match self { - AliasTy::Projection(projection_ty) => { - let (assoc_ty_datum, trait_params, assoc_type_params) = - s.db.split_projection(&projection_ty); - write!( - f, - "<{} as {}>::{}", - trait_params[0].display(s), - display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), - assoc_ty_datum.id.display(s), - )?; - write_joined_non_empty_list!( - f, - "<{}>", - assoc_type_params.iter().map(|param| param.display(s)), - ", " - ) - } - AliasTy::Opaque(_) => todo!("opaque types"), - } + let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self); + write!( + f, + "<{} as {}>::{}", + trait_params[0].display(s), + display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), + assoc_ty_datum.id.display(s), + )?; + write_joined_non_empty_list!( + f, + "<{}>", + assoc_type_params.iter().map(|param| param.display(s)), + ", " + )?; + Ok(()) + } +} +impl RenderAsRust for OpaqueTy { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + write!( + f, + "{}", + display_trait_with_generics( + s, + self.opaque_ty_id, + self.substitution.parameters(interner), + ) + ) } } @@ -569,14 +604,14 @@ impl RenderAsRust for WhereClause { /// This is shared between where bounds & dyn Trait. fn display_trait_with_generics<'a, I: Interner>( s: &'a WriterState<'a, I>, - trait_id: TraitId, + trait_name: impl RenderAsRust + 'a, trait_params: impl IntoIterator> + 'a, ) -> impl Display + 'a { use std::fmt::Write; let trait_params = trait_params.into_iter().map(|param| param.display(s)); let mut trait_params_str = String::new(); write_joined_non_empty_list!(trait_params_str, "<{}>", trait_params, ", ").unwrap(); - as_display(move |f| write!(f, "{}{}", trait_id.display(s), trait_params_str)) + as_display(move |f| write!(f, "{}{}", trait_name.display(s), trait_params_str)) } /// This implementation correct inside where clauses. @@ -812,6 +847,26 @@ impl RenderAsRust for StructDatum { } } +impl RenderAsRust for OpaqueTyDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + let s = &s.add_debrujin_index(None); + let bounds = self.bound.skip_binders(); + write!(f, "opaque type {}", self.opaque_ty_id.display(s))?; + write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.bound.binders), ", ")?; + { + let s = &s.add_debrujin_index(Some(0)); + let clauses = bounds.bounds.skip_binders(); + write!( + f, + ": {} = ", + display_self_where_clauses_as_bounds(s, clauses) + )?; + } + write!(f, "{};", bounds.hidden_ty.display(s))?; + Ok(()) + } +} + /// Like a BoundVar, but with the debrujin index inverted so as to create a /// canonical name we can use anywhere for each bound variable. /// diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index fdec675e2a7..3c3a4b213a4 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -149,8 +149,11 @@ pub trait RustIrDatabase: Debug { /// Retrieves a struct's original name. No uniqueness guarantees. fn struct_name(&self, struct_id: StructId) -> String; - /// Retrieves the name of an identifier - fn assoc_type_name(&self, ident: AssocTypeId) -> String; + /// Retrieves the name of an associated type. + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String; + + /// Retrieves the name of an opaque type. + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String; } pub use clauses::program_clauses_for_env; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 42217a3aeb0..7e98a033d55 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -149,8 +149,11 @@ where fn struct_name(&self, struct_id: StructId) -> String { self.db.struct_name(struct_id) } - fn assoc_type_name(&self, ident: AssocTypeId) -> String { - self.db.assoc_type_name(ident) + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { + self.db.assoc_type_name(assoc_ty_id) + } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { + self.db.opaque_type_name(opaque_ty_id) } fn is_object_safe(&self, trait_id: TraitId) -> bool { self.record(trait_id); @@ -278,6 +281,9 @@ where fn interner(&self) -> &I { self.db.interner() } + fn is_object_safe(&self, trait_id: TraitId) -> bool { + self.db.is_object_safe(trait_id) + } fn trait_name(&self, trait_id: TraitId) -> String { self.db.trait_name(trait_id) } @@ -287,9 +293,8 @@ where fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.db.assoc_type_name(assoc_ty_id) } - - fn is_object_safe(&self, trait_id: TraitId) -> bool { - self.db.is_object_safe(trait_id) + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { + self.db.opaque_type_name(opaque_ty_id) } } From e6410cb3e8bb3923a2486a2fdf9a8ccab3895392 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 20:06:21 -0700 Subject: [PATCH 18/70] De-duplicated split_associated_ty_parameters Based on feedback from niko matsakis: https://github.com/rust-lang/chalk/pull/430#discussion_r419630159 `assocated_ty_parameters` re-implemented some logic in `split_projection`. This deletes the duplicated in `split_projection` and instead calls `split_associated_ty_parameters`. Co-authored-by: David Ross --- chalk-solve/src/split.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/chalk-solve/src/split.rs b/chalk-solve/src/split.rs index 9a7b06dad70..3339c019104 100644 --- a/chalk-solve/src/split.rs +++ b/chalk-solve/src/split.rs @@ -29,10 +29,8 @@ pub trait Split: RustIrDatabase { } = *projection; let parameters = substitution.parameters(interner); let associated_ty_data = &self.associated_ty_data(associated_ty_id); - let trait_datum = &self.trait_datum(associated_ty_data.trait_id); - let trait_num_params = trait_datum.binders.len(interner); - let split_point = parameters.len() - trait_num_params; - let (other_params, trait_params) = parameters.split_at(split_point); + let (trait_params, other_params) = + self.split_associated_ty_parameters(parameters, &**associated_ty_data); (associated_ty_data.clone(), trait_params, other_params) } From ae486ada10fc97f4133ef530816831a81ca6ef71 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 20:41:57 -0700 Subject: [PATCH 19/70] Add tuple type rendering Implements `RenderAsRust` for TypeName::Tuple Single element tuples are rendered with a trailing comma to disambiguate them from types surrounded by parentheses. Examples: (T,) (u32,Foo) Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 35 +++++++++++++++++++++++++ chalk-solve/src/display.rs | 18 ++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 1f1417ee9fb..116a73cff1b 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -956,6 +956,41 @@ mod test { ); } + #[test] + fn test_tuples() { + reparse_test( + " + struct Fuu { + fuu_field: () + } + ", + ); + reparse_test( + " + struct Uff { + fuu_field: (Iff,), + iff2_field: (Iff, Iff, Iff) + } + struct Iff { } + ", + ); + reparse_test( + " + struct Foo { + field: (u32,*const T,T), + field2: (T,), + field3: (T) + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = (T,Foo,u32); + } + ", + ); + } + #[test] fn test_trait_impl_associated_type() { reparse_test( diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 3d1aded3ee3..996a9509de3 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -466,7 +466,23 @@ impl RenderAsRust for ApplicationTy { )?; } TypeName::Scalar(scalar) => write!(f, "{}", scalar.display(s))?, - TypeName::Tuple(_) => todo!("scalar types"), + TypeName::Tuple(arity) => { + write!( + f, + "({}{})", + self.substitution + .parameters(interner) + .iter() + .map(|p| p.display(s)) + .format(", "), + if arity == 1 { + // need trailing single comma + "," + } else { + "" + } + )? + } TypeName::OpaqueType(_) => todo!("opaque type usage"), TypeName::Raw(raw) => { let mutability = match raw { From f4a46720074242dc8e36b18ede370b8f21c42237 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 20:44:44 -0700 Subject: [PATCH 20/70] Fix rendered chalk syntax formatting * Added indentation to trait and impl bodies. * Added indentation to where clauses on associated types, structs, and impls. * Removed comment. Co-authored-by: David Ross --- chalk-integration/src/program_writer.rs | 1 - chalk-solve/src/display.rs | 35 +++++++++++-------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs index 116a73cff1b..c389b187a60 100644 --- a/chalk-integration/src/program_writer.rs +++ b/chalk-integration/src/program_writer.rs @@ -21,7 +21,6 @@ mod test { } for datum in program.opaque_ty_data.values() { display::write_top_level(&mut out, program, &**datum).unwrap(); - //display::write_top_level(&mut out, program, &**datum).unwrap(); } out } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 996a9509de3..d8e35c6210d 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -143,7 +143,7 @@ impl RenderAsRust for AssociatedTyValue { let (_impl_display, assoc_ty_value_display) = s.db.split_associated_ty_value_parameters(&display_params, self); - write!(f, "type {}", assoc_ty_data.id.display(s))?; + write!(f, "{}type {}", s.indent(), assoc_ty_data.id.display(s))?; write_joined_non_empty_list!(f, "<{}>", &assoc_ty_value_display, ", ")?; write!(f, " = {};", value.ty.display(s))?; Ok(()) @@ -175,13 +175,6 @@ impl RenderAsRust for ImplDatum { trait_ref.trait_id, &trait_ref.substitution.parameters(interner)[1..], ); - - let assoc_ty_values = self.associated_ty_value_ids.iter().map(|assoc_ty_value| { - s.db.associated_ty_value(*assoc_ty_value) - .display(s) - .to_string() - }); - write!(f, "impl")?; write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; write!( @@ -191,12 +184,20 @@ impl RenderAsRust for ImplDatum { full_trait_name, trait_ref.self_type_parameter(interner).display(s) )?; - if !value.where_clauses.is_empty() { - write!(f, "where {} ", value.where_clauses.display(s))?; + let s = &s.add_indent(); + write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; } write!(f, "{{")?; - write_joined_non_empty_list!(f, "\n{}\n", assoc_ty_values, "\n")?; + { + let s = &s.add_indent(); + let assoc_ty_values = self.associated_ty_value_ids.iter().map(|assoc_ty_value| { + s.db.associated_ty_value(*assoc_ty_value) + .display(s) + .to_string() + }); + write_joined_non_empty_list!(f, "\n{}\n", assoc_ty_values, "\n")?; + } write!(f, "}}")?; Ok(()) } @@ -813,7 +814,7 @@ impl RenderAsRust for TraitDatum { write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; write!(f, " ")?; if !value.where_clauses.is_empty() { - write!(f, "where {} ", value.where_clauses.display(s))?; + write!(f, "\nwhere {}\n", value.where_clauses.display(s))?; } write!(f, "{{")?; let s = &s.add_indent(); @@ -838,15 +839,11 @@ impl RenderAsRust for StructDatum { let s = &s.add_debrujin_index(None); let value = self.binders.skip_binders(); write!(f, "struct {}", self.id.display(s),)?; - write_joined_non_empty_list!( - f, - "<{}> ", - s.binder_var_display(&self.binders.binders), - ", " - )?; + write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.binders.binders), ", ")?; write!(f, " ")?; if !value.where_clauses.is_empty() { - write!(f, "where {} ", value.where_clauses.display(s))?; + let s = &s.add_indent(); + write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; } write!(f, "{{")?; let s = &s.add_indent(); From 56f6378170d5b62992c78c1d393ae27b0177d571 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 9 May 2020 23:27:21 -0700 Subject: [PATCH 21/70] Move program writing tests into tests crate The program writing tests were originally located inside of `chalk-integration`. This moves them to the outer test crate to bring them in line with existing test practices in chalk. Co-authored-by: David Ross --- Cargo.lock | 2 +- Cargo.toml | 2 + chalk-integration/Cargo.toml | 4 - chalk-integration/src/lib.rs | 1 - chalk-integration/src/program_writer.rs | 1193 ----------------------- tests/lib.rs | 1 + tests/logging_db/assoc_ty.rs | 304 ++++++ tests/logging_db/built_ins.rs | 104 ++ tests/logging_db/dyn_.rs | 80 ++ tests/logging_db/impl_.rs | 23 + tests/logging_db/lifetimes.rs | 27 + tests/logging_db/mod.rs | 185 ++++ tests/logging_db/opaque_ty.rs | 116 +++ tests/logging_db/self_.rs | 167 ++++ tests/logging_db/struct_.rs | 27 + tests/logging_db/trait_.rs | 52 + tests/logging_db/where_clauses.rs | 110 +++ 17 files changed, 1199 insertions(+), 1199 deletions(-) delete mode 100644 chalk-integration/src/program_writer.rs create mode 100644 tests/logging_db/assoc_ty.rs create mode 100644 tests/logging_db/built_ins.rs create mode 100644 tests/logging_db/dyn_.rs create mode 100644 tests/logging_db/impl_.rs create mode 100644 tests/logging_db/lifetimes.rs create mode 100644 tests/logging_db/mod.rs create mode 100644 tests/logging_db/opaque_ty.rs create mode 100644 tests/logging_db/self_.rs create mode 100644 tests/logging_db/struct_.rs create mode 100644 tests/logging_db/trait_.rs create mode 100644 tests/logging_db/where_clauses.rs diff --git a/Cargo.lock b/Cargo.lock index 61636ddbf5e..f3519b1c760 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,7 @@ dependencies = [ "chalk-ir 0.16.0-dev.0", "chalk-parse 0.16.0-dev.0", "chalk-solve 0.16.0-dev.0", + "diff 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -203,7 +204,6 @@ dependencies = [ "chalk-ir 0.16.0-dev.0", "chalk-parse 0.16.0-dev.0", "chalk-solve 0.16.0-dev.0", - "diff 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "salsa 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "tracing 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index de2461281db..19228b9ddb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,5 +32,7 @@ chalk-integration = { version = "0.16.0-dev.0", path = "chalk-integration" } [workspace] [dev-dependencies] +# used for program_writer test errors +diff = "0.1" pretty_assertions = "0.6.1" tracing = "0.1" diff --git a/chalk-integration/Cargo.toml b/chalk-integration/Cargo.toml index 87123888d57..03809616549 100644 --- a/chalk-integration/Cargo.toml +++ b/chalk-integration/Cargo.toml @@ -19,7 +19,3 @@ chalk-engine = { version = "0.16.0-dev.0", path = "../chalk-engine" } chalk-ir = { version = "0.16.0-dev.0", path = "../chalk-ir" } chalk-solve = { version = "0.16.0-dev.0", path = "../chalk-solve" } chalk-parse = { version = "0.16.0-dev.0", path = "../chalk-parse" } - -[dev-dependencies] -# used for program_writer test errors -diff = "0.1" diff --git a/chalk-integration/src/lib.rs b/chalk-integration/src/lib.rs index f51fcaa93bb..1019f601567 100644 --- a/chalk-integration/src/lib.rs +++ b/chalk-integration/src/lib.rs @@ -7,7 +7,6 @@ pub mod interner; pub mod lowering; pub mod program; pub mod program_environment; -mod program_writer; pub mod query; pub mod tls; diff --git a/chalk-integration/src/program_writer.rs b/chalk-integration/src/program_writer.rs deleted file mode 100644 index c389b187a60..00000000000 --- a/chalk-integration/src/program_writer.rs +++ /dev/null @@ -1,1193 +0,0 @@ -#[cfg(test)] -mod test { - use std::{fmt::Debug, sync::Arc}; - - use chalk_solve::display; - - use crate::lowering::LowerProgram; - use crate::program::Program; - use crate::tls; - - fn write_program(program: &Program) -> String { - let mut out = String::new(); - for datum in program.struct_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); - } - for datum in program.trait_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); - } - for datum in program.impl_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); - } - for datum in program.opaque_ty_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); - } - out - } - - fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { - use std::fmt::Write; - - let mut out = String::new(); - let original = format!("{:#?}", original); - let produced = format!("{:#?}", produced); - for line in diff::lines(&original, &produced) { - match line { - diff::Result::Left(l) => write!(out, "-{}\n", l), - diff::Result::Both(l, _) => write!(out, " {}\n", l), - diff::Result::Right(r) => write!(out, "+{}\n", r), - } - .expect("writing to string never fails"); - } - out - } - - /// Data from performing a reparse test which can be used to make additional - /// assertions. - /// - /// Not necessary for use unless additional assertions are necessary. - #[allow(unused)] - struct ReparseTestResult<'a> { - /// The program text for the original test code - original_text: &'a str, - /// The program text for the code the test says should be output - target_text: &'a str, - /// The actual reparsed output text - output_text: String, - /// Lowered version of `original_text` - original_program: Arc, - /// Lowered version of `target_text` - target_program: Arc, - /// Lowered version of `output_text` - output_program: Program, - } - - /// Parses the input, lowers it, prints it, then re-parses and re-lowers, - /// failing if the two lowered programs don't match. - /// - /// Note: the comparison here does include IDs, so input order matters. In - /// particular, ProgramWriter always writes traits, then structs, then - /// impls. So all traits must come first, then structs, then all impls, or - /// the reparse will fail. - fn reparse_test(program_text: &str) -> ReparseTestResult<'_> { - reparse_into_different_test(program_text, program_text) - } - - /// [`reparse_test`], but allows a non-convergent test program to be tested - /// a different target. - fn reparse_into_different_test<'a>( - program_text: &'a str, - target_text: &'a str, - ) -> ReparseTestResult<'a> { - let original_program = match chalk_parse::parse_program(program_text) { - Ok(v) => v, - Err(e) => panic!( - "unable to parse test program:\n{}\nSource:\n{}\n", - e, program_text - ), - }; - let original_program = Arc::new(original_program.lower().unwrap_or_else(|e| { - panic!( - "unable to lower test program:\n{}\nSource:\n{}\n", - e, program_text - ) - })); - let target_program = match chalk_parse::parse_program(target_text) { - Ok(v) => v, - Err(e) => panic!( - "unable to parse test program:\n{}\nSource:\n{}\n", - e, program_text - ), - }; - let target_program = Arc::new(target_program.lower().unwrap_or_else(|e| { - panic!( - "unable to lower test program:\n{}\nSource:\n{}\n", - e, program_text - ) - })); - let output_text = - tls::set_current_program(&original_program, || write_program(&original_program)); - let output_program = chalk_parse::parse_program(&output_text).unwrap_or_else(|e| { - panic!( - "unable to reparse writer output:\n{}\nNew source:\n{}\n", - e, output_text - ) - }); - let output_program = output_program.lower().unwrap_or_else(|e| { - panic!( - "error lowering writer output:\n{}\nNew source:\n{}\n", - e, output_text - ) - }); - if output_program != *target_program { - panic!( - "WriteProgram produced different program.\n\ - Diff:\n{}\n\ - Source:\n{}\n{}\ - New Source:\n{}\n", - program_diff(&target_program, &output_program), - program_text, - if target_text != program_text { - format!( - "Test Should Output (different from original):\n{}\n", - target_text - ) - } else { - String::new() - }, - output_text - ); - } - eprintln!("\nTest Succeeded:\n\n{}\n---", output_text); - ReparseTestResult { - original_text: program_text, - output_text, - target_text, - original_program, - output_program, - target_program, - } - } - - #[test] - fn test_simple_structs_and_bounds() { - reparse_test("struct Foo {}"); - reparse_test("struct Foo {}"); - reparse_test( - " - struct Foo where T: Trait {} - trait Trait {} - ", - ); - } - - #[test] - fn test_simple_traits_and_bounds() { - reparse_test("trait Foo {}"); - reparse_test("trait Foo {}"); - reparse_test( - " - trait Foo where T: Trait {} - trait Trait {} - ", - ); - } - - #[test] - fn test_self_in_trait_where() { - reparse_test( - " - trait Bkz {} - trait Foo where Self: Bkz {} - ", - ); - reparse_test( - " - trait Baz<'a> {} - trait Foo where forall<'a> Self: Baz<'a> {} - ", - ); - } - - #[test] - fn test_self_in_assoc_type() { - reparse_test( - " - trait Extra {} - trait Bez {} - trait Foo { - type Assoc: Extra; - } - ", - ); - - reparse_test( - " - trait Bez {} - trait Foo { - type Assoc where Self: Bez; - } - ", - ); - reparse_test( - " - trait Biz {} - trait Foo { - type Assoc where Self: Biz; - } - ", - ); - } - - #[test] - fn test_self_in_dyn() { - reparse_test( - " - trait Bun {} - trait Foo { - type Assoc where dyn Bun: Bun; - } - ", - ); - reparse_test( - " - trait Has {} - trait Bun {} - trait Fiz { - type Assoc1: Has>; - type Assoc2: Has>; - } - ", - ); - } - - // Self doesn't work in these circumstances yet (test programs fail to lower) - #[ignore] - #[test] - fn test_self_in_struct_bounds() { - reparse_test( - " - trait Bax {} - struct Foo where T: Bax {} - ", - ); - reparse_test( - " - trait Baz {} - struct Foo where Self: Baz {} - ", - ); - reparse_test( - " - trait Blz {} - struct Foo where Self: Blz {} - ", - ); - } - - // Self doesn't work in these circumstances yet (test programs fail to lower) - #[ignore] - #[test] - fn test_self_in_impl_blocks() { - reparse_test( - " - trait Foo { - type Assoc; - } - struct Bix {} - impl Foo for Bix { - type Assoc = Self; - } - ", - ); - reparse_test( - " - trait Foo {} - trait Fin {} - struct Bux {} - impl Foo for Bux where Self: Fin {} - ", - ); - reparse_test( - " - trait Faux {} - trait Paw { - type Assoc1; - type Assoc2; - } - struct Buzz {} - impl Paw for Buzz { - type Assoc1 = dyn Faux; - type Assoc2 = dyn Faux; - } - ", - ); - } - - #[test] - fn test_forall_in_where() { - reparse_test( - " - trait Bax {} - trait Foo where forall T: Bax {} - ", - ); - reparse_test( - " - trait Buz<'a> {} - trait Foo where forall<'a> T: Buz<'a> {} - ", - ); - reparse_test( - " - struct Foo where forall T: Biz {} - trait Biz {} - ", - ); - reparse_test( - " - struct Foo where forall<'a> T: Bez<'a> {} - trait Bez<'a> {} - ", - ); - } - #[test] - fn test_forall_in_dyn() { - reparse_test( - " - trait Foo {} - trait Bar<'a> {} - impl Foo for dyn forall<'a> Bar<'a> {} - ", - ); - reparse_test( - " - struct Foo { - field: dyn forall<'a> Baz<'a> - } - trait Baz<'a> {} - ", - ); - reparse_test( - " - trait Foo {} - trait Bax<'a, 'b> {} - impl Foo for dyn forall<'a, 'b> Bax<'a, 'b> {} - ", - ); - reparse_test( - " - struct Foo { - field: dyn forall<'a, 'b> Bix<'a, 'b> - } - trait Bix<'a, 'b> {} - ", - ); - reparse_test( - " - struct Foo { - field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> - } - trait Bex<'a> {} - trait Byx<'a> {} - ", - ); - reparse_test( - " - struct Foo { - field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> - } - trait Bux<'a, 'b> {} - trait Brx<'a, 'b> {} - ", - ); - reparse_test( - " - struct Foo<'a> { - field: dyn forall<'b> Bpx<'a, 'b> - } - trait Bpx<'a, 'b> {} - ", - ); - } - - #[test] - fn test_simple_dyn() { - reparse_test( - " - struct Foo { - field: dyn Bax - } - trait Bax {} - ", - ); - reparse_test( - " - struct Foo<'a> { - field: dyn Bix<'a> - } - trait Bix<'a> {} - ", - ); - } - - #[test] - fn test_simple_assoc_type() { - reparse_test( - " - trait Foo { - type Assoc; - } - ", - ); - reparse_test( - " - trait Byz {} - trait Buzz {} - trait Foo { - type Assoc: Byz + Buzz; - } - ", - ); - } - - #[test] - fn test_simple_generic_assoc_type() { - reparse_test( - " - trait Trait {} - trait Foo { - type Assoc; - } - ", - ); - reparse_test( - " - trait Trait {} - trait Foo { - type Assoc: Trait; - } - ", - ); - reparse_test( - " - trait Trait {} - trait Foo { - type Assoc where Y: Trait; - } - ", - ); - } - - #[test] - fn test_assoc_type_in_generic_trait() { - reparse_test( - " - trait Foo { - type Assoc; - } - ", - ); - reparse_test( - " - trait Fou { - type Assoc; - } - ", - ); - reparse_test( - " - trait Bax {} - trait Foo { - type Assoc where T: Bax; - } - ", - ); - reparse_test( - " - trait Bix {} - trait Foo { - type Assoc where Y: Bix; - } - ", - ); - } - - #[test] - fn test_simple_impl() { - reparse_test( - " - struct Foo {} - trait Bar {} - impl Bar for Foo {} - ", - ); - } - - #[test] - fn test_impl_assoc_ty() { - reparse_test( - " - struct Fuu {} - trait Bhz { - type Assoc; - } - impl Bhz for Fuu { - type Assoc = Fuu; - } - ", - ); - reparse_test( - " - struct Fou {} - trait Bax { - type Assoc; - } - impl Bax for Fou { - type Assoc = Fou; - } - ", - ); - reparse_test( - " - struct Fuu {} - trait Bmx { - type Assoc; - } - impl Bmx for Fuu { - type Assoc = T; - } - ", - ); - reparse_test( - " - struct Fuu {} - struct Guu {} - trait Bmx { - type Assoc; - } - impl Bmx for Fuu { - type Assoc = Guu; - } - ", - ); - reparse_test( - " - struct Fuu {} - struct Guu {} - trait Bmx { - type Assoc; - } - impl Bmx for Fuu { - type Assoc = Guu; - } - ", - ); - } - - #[test] - fn test_impl_assoc_ty_alias() { - reparse_test( - " - struct Fow {} - struct Qac {} - trait Bow {} - trait Baq { - type Assoc: Boo; - } - trait Boo { - type Item; - } - impl Boo for Qac { - type Item = Fow; - } - impl Baq for Fow { - type Assoc = Qac; - } - ", - ); - } - - #[test] - fn test_struct_fields() { - reparse_test( - " - struct Foo {} - struct Bar {} - struct Baz { - x: Foo, - b: Bar - } - ", - ); - } - - #[test] - fn test_against_accidental_self() { - // In some of the writer code, it would be really easy to introduce a - // outputs the first generic parameter of things as "Self". - let in_structs = reparse_test( - " - struct Foo { - field: T - } - ", - ); - dbg!(in_structs.output_program); - assert!(!in_structs.output_text.contains("Self")); - let in_impl = reparse_test( - " - struct Foo {} - trait Bux { - type Assoc; - } - impl Bux for Foo { - type Assoc = T; - } - ", - ); - assert!(!in_impl.output_text.contains("Self")); - let in_opaque = reparse_test( - " - struct Foo {} - trait Que {} - impl Que for Foo {} - opaque type Bar: Que = Foo; - ", - ); - assert!(!in_opaque.output_text.contains("Self")); - } - - #[test] - fn test_program_writer() { - reparse_test( - " - struct Foo { } - struct Vec { } - struct Map<_0, _1> { } - struct Ref<'a, T> { } - - trait Marker { } - trait Clone { } - trait Deref<'a, U> { - type Assoc: Clone; - } - trait AssocWithGenerics { - type Assoc; - } - trait AssocTrait3 { - type Assoc; - } - trait AsRef { } - - trait AssocTraitWithWhere { - type Assoc where U: AsRef; - } - - impl Marker for Vec { } - impl Clone for Foo { } - impl Clone for Vec where T: Clone { } - impl Clone for Map where T: Clone, U: Clone { } - - impl<'a, T, U> Deref<'a, T> for Ref<'a, U> { - type Assoc = Foo; - } - impl AssocWithGenerics for Foo { - type Assoc = Vec; - } - impl AssocTrait3 for Vec { - type Assoc = Map; - } - impl AssocTraitWithWhere for Vec { - type Assoc = Map; - } - ", - ); - } - - #[test] - fn test_assoc_ty_alias_bound() { - // Foo: Bax is lowered into two bounds, Bax and Bax, and - // the formatter does not coalesce those bounds. - reparse_into_different_test( - " - struct Foo { } - trait Bax { type BaxT; } - trait Test { - type Assoc - where - Foo: Bax; - } - ", - " - struct Foo { } - trait Bax { type BaxT; } - trait Test { - type Assoc - where - Foo: Bax, - Foo: Bax; - } - ", - ); - reparse_into_different_test( - " - struct Foo where T: Baux { } - trait Baux { type Assoc; } - ", - " - struct Foo where T: Baux, T: Baux { } - trait Baux { type Assoc; } - ", - ); - reparse_into_different_test( - " - struct Foo {} - trait Boz { type Assoc; } - impl Boz for Foo where T: Boz> { - type Assoc = Foo; - } - ", - " - struct Foo {} - trait Boz { type Assoc; } - impl Boz for Foo where T: Boz>, T: Boz { - type Assoc = Foo; - } - ", - ); - } - - #[test] - fn test_complicated_bounds() { - reparse_into_different_test( - " - struct Foo { } - trait Bar { } - trait Baz { } - trait Bax { type BaxT; } - trait Test { - type Assoc: Bar + Baz + Bax - where - Foo: Bax, - Foo: Bar, - dyn Bar: Baz; - } - ", - " - struct Foo { } - trait Bar { } - trait Baz { } - trait Bax { type BaxT; } - trait Test { - type Assoc: Bar + Baz + Bax - where - Foo: Bax, - Foo: Bax, - Foo: Bar, - dyn Bar: Baz; - }", - ); - } - - #[test] - fn test_function_type() { - reparse_test( - " - struct Foo { } - trait Baz { } - impl Baz for Foo { } - ", - ); - } - - #[test] - fn test_struct_where_clauses() { - reparse_test( - " - struct Foo where T: Baz, U: Bez { } - trait Baz { } - trait Bez { } - ", - ); - } - - #[test] - fn test_impl_where_clauses() { - reparse_test( - " - struct Foo where T: Baz, U: Bez { } - trait Baz { } - trait Bez { } - impl Bez for Foo where T: Baz, U: Bez { } - ", - ); - // TODO: more of these - } - - #[test] - fn test_trait_projection() { - reparse_test( - " - struct Foo where >::Assoc: Baz { } - trait Baz { } - trait Bez { - type Assoc; - } - ", - ); - } - - #[test] - fn test_various_forall() { - reparse_test( - " - struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } - trait Baz<'a> {} - trait Bax<'a> {} - trait Biz { - type Bex: forall<'a> Bax<'a>; - } - impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } - impl<'a> Bax<'a> for fn(Foo<'a>) { } - impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> { } - ", - ); - } - #[test] - fn test_lifetimes_in_structs() { - reparse_test( - " - struct Foo<'b> { } - trait Baz<'a> {} - impl<'a> Baz<'a> for Foo<'a> { } - ", - ); - } - - #[test] - fn test_basic_trait_impl() { - reparse_test( - " - struct Foo { } - trait Bar {} - impl Bar for Foo { } - ", - ); - } - - #[test] - fn test_negative_auto_trait_impl() { - reparse_test( - " - struct Foo { } - #[auto] - trait Baz {} - impl !Baz for Foo { } - ", - ); - } - - #[test] - fn test_trait_flags() { - let flags = vec![ - "auto", - "marker", - "upstream", - "fundamental", - "non_enumerable", - "coinductive", - ]; - reparse_test(&format!( - "{}trait Hello {{}}", - flags - .iter() - .map(|f| format!("#[{}]", f)) - .collect::>() - .join("\n") - )); - for flag in flags { - reparse_test(&format!( - " - #[{0}] - trait Hello_{0} {{}} - ", - flag - )); - } - } - - #[test] - fn test_scalar_types() { - let basic = vec!["bool", "char", "f32", "f64"]; - let sizes = vec!["size", "8", "16", "32", "64", "128"]; - let prefixes = vec!["u", "i"]; - let ints = prefixes - .iter() - .flat_map(|&p| sizes.iter().map(move |&size| format!("{}{}", p, size))); - let scalars = basic.iter().copied().map(str::to_owned).chain(ints); - - for scalar in scalars { - reparse_test(&format!( - " - struct Foo {{ - field: {0} - }} - trait Bar {{ - type Baz; - }} - impl Bar for Foo {{ - type Baz = {0}; - }} - ", - scalar - )); - } - } - - #[test] - fn test_raw_ptr_types() { - reparse_test( - " - struct Foo { - field: *const T - } - trait Bar { - type Baz; - } - impl Bar for Foo { - type Baz = *const u32; - } - ", - ); - reparse_test( - " - struct Foo { - field: *mut T - } - trait Bar { - type Baz; - } - impl Bar for Foo { - type Baz = *mut u32; - } - ", - ); - } - - #[test] - fn test_tuples() { - reparse_test( - " - struct Fuu { - fuu_field: () - } - ", - ); - reparse_test( - " - struct Uff { - fuu_field: (Iff,), - iff2_field: (Iff, Iff, Iff) - } - struct Iff { } - ", - ); - reparse_test( - " - struct Foo { - field: (u32,*const T,T), - field2: (T,), - field3: (T) - } - trait Bar { - type Baz; - } - impl Bar for Foo { - type Baz = (T,Foo,u32); - } - ", - ); - } - - #[test] - fn test_trait_impl_associated_type() { - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bar { - type Assoc; - } - impl Bar for Foo { - type Assoc = Floo; - } - ", - ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bax { - type Assoc1; - type Assoc2; - } - impl Bax for Foo { - type Assoc1 = Floo; - type Assoc2 = Foo; - } - ", - ); - } - - #[test] - fn test_trait_impl_associated_type_with_generics() { - reparse_test( - " - struct Foo { } - struct Floo { } - trait Baz { - type Assoc; - } - impl Baz for Foo { - type Assoc = Floo; - } - ", - ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bur { - type Assoc; - } - impl Bur for Foo { - type Assoc = Floo; - } - ", - ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bun { - type Assoc; - } - impl Bun for Foo { - type Assoc = Floo; - } - ", - ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bun { - type Assoc1; - type Assoc2; - type Assoc3; - } - impl Bun for Foo { - type Assoc1 = Floo; - type Assoc2 = Floo; - type Assoc3 = Floo, Floo>; - } - ", - ); - } - #[test] - fn opaque_types() { - reparse_test( - " - struct Bar {} - trait Buz {} - trait Baz { - type Hi; - } - impl Buz for Bar {} - impl Baz for Foo { - type Hi = Foo; - } - opaque type Foo: Buz = Bar; - ", - ); - } - - #[test] - fn test_generic_opaque_types() { - reparse_test( - " - struct Foo {} - trait Bar {} - opaque type Baz: Bar = Foo; - ", - ); - reparse_test( - " - struct Foo {} - struct Unit {} - trait Bar {} - opaque type Boz: Bar = Foo; - ", - ); - } - - #[test] - fn test_opaque_type_as_type_value() { - reparse_test( - " - struct Foo {} - trait Bar {} - trait Fuzz { - type Assoc: Bar; - } - impl Bar for Foo {} - impl Fuzz for Foo { - type Assoc = Bax; - } - opaque type Bax: Bar = Foo; - ", - ); - reparse_test( - " - struct Foo {} - trait Bar {} - trait Faz { - type Assoc; - } - impl Faz for Foo { - type Assoc = fn(Baz); - } - opaque type Baz: Bar = Foo; - ", - ); - } - - // Generic opaque types can't currently be used as types (these fail to lower) - #[ignore] - #[test] - fn test_generic_opaque_type_as_value1() { - reparse_test( - " - struct Foo {} - trait Bar {} - trait Fizz { - type Assoc: Bar; - } - impl Bar for Foo {} - impl Fizz for Foo { - type Assoc = Baz; - } - opaque type Baz: Bar = Foo; - ", - ); - reparse_test( - " - struct Foo {} - trait Bar {} - trait Faz { - type Assoc; - } - impl Faz for Foo { - type Assoc = fn(Baz); - } - opaque type Baz: Bar = Foo; - ", - ); - reparse_test( - " - struct Foo {} - struct Unit {} - trait Bar {} - trait Fez { - type Assoc; - } - impl Fez for Foo { - type Assoc = fn(Biiiz); - } - impl Bar for Foo {} - opaque type Biiiz: Bar = Foo; - ", - ); - } -} diff --git a/tests/lib.rs b/tests/lib.rs index e28bac2f0e3..9c7dcb64409 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -3,6 +3,7 @@ mod test_util; #[macro_use] mod test; +mod logging_db; mod lowering; mod integration; diff --git a/tests/logging_db/assoc_ty.rs b/tests/logging_db/assoc_ty.rs new file mode 100644 index 00000000000..bccd9585652 --- /dev/null +++ b/tests/logging_db/assoc_ty.rs @@ -0,0 +1,304 @@ +use super::*; +#[test] +fn test_trait_impl_associated_type() { + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bar { + type Assoc; + } + impl Bar for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bax { + type Assoc1; + type Assoc2; + } + impl Bax for Foo { + type Assoc1 = Floo; + type Assoc2 = Foo; + } + ", + ); +} + +#[test] +fn test_trait_impl_associated_type_with_generics() { + reparse_test( + " + struct Foo { } + struct Floo { } + trait Baz { + type Assoc; + } + impl Baz for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bur { + type Assoc; + } + impl Bur for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bun { + type Assoc; + } + impl Bun for Foo { + type Assoc = Floo; + } + ", + ); + reparse_test( + " + struct Foo { } + struct Floo { } + trait Bun { + type Assoc1; + type Assoc2; + type Assoc3; + } + impl Bun for Foo { + type Assoc1 = Floo; + type Assoc2 = Floo; + type Assoc3 = Floo, Floo>; + } + ", + ); +} +#[test] +fn test_simple_assoc_type() { + reparse_test( + " + trait Foo { + type Assoc; + } + ", + ); + reparse_test( + " + trait Byz {} + trait Buzz {} + trait Foo { + type Assoc: Byz + Buzz; + } + ", + ); +} + +#[test] +fn test_simple_generic_assoc_type() { + reparse_test( + " + trait Trait {} + trait Foo { + type Assoc; + } + ", + ); + reparse_test( + " + trait Trait {} + trait Foo { + type Assoc: Trait; + } + ", + ); + reparse_test( + " + trait Trait {} + trait Foo { + type Assoc where Y: Trait; + } + ", + ); +} + +#[test] +fn test_assoc_type_in_generic_trait() { + reparse_test( + " + trait Foo { + type Assoc; + } + ", + ); + reparse_test( + " + trait Fou { + type Assoc; + } + ", + ); + reparse_test( + " + trait Bax {} + trait Foo { + type Assoc where T: Bax; + } + ", + ); + reparse_test( + " + trait Bix {} + trait Foo { + type Assoc where Y: Bix; + } + ", + ); +} + +#[test] +fn test_impl_assoc_ty() { + reparse_test( + " + struct Fuu {} + trait Bhz { + type Assoc; + } + impl Bhz for Fuu { + type Assoc = Fuu; + } + ", + ); + reparse_test( + " + struct Fou {} + trait Bax { + type Assoc; + } + impl Bax for Fou { + type Assoc = Fou; + } + ", + ); + reparse_test( + " + struct Fuu {} + trait Bmx { + type Assoc; + } + impl Bmx for Fuu { + type Assoc = T; + } + ", + ); + reparse_test( + " + struct Fuu {} + struct Guu {} + trait Bmx { + type Assoc; + } + impl Bmx for Fuu { + type Assoc = Guu; + } + ", + ); + reparse_test( + " + struct Fuu {} + struct Guu {} + trait Bmx { + type Assoc; + } + impl Bmx for Fuu { + type Assoc = Guu; + } + ", + ); +} + +#[test] +fn test_impl_assoc_ty_alias() { + reparse_test( + " + struct Fow {} + struct Qac {} + trait Bow {} + trait Baq { + type Assoc: Boo; + } + trait Boo { + type Item; + } + impl Boo for Qac { + type Item = Fow; + } + impl Baq for Fow { + type Assoc = Qac; + } + ", + ); +} + +#[test] +fn test_assoc_ty_alias_bound() { + // Foo: Bax is lowered into two bounds, Bax and Bax, and + // the formatter does not coalesce those bounds. + reparse_into_different_test( + " + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax; + } + ", + " + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax, + Foo: Bax; + } + ", + ); + reparse_into_different_test( + " + struct Foo where T: Baux { } + trait Baux { type Assoc; } + ", + " + struct Foo where T: Baux, T: Baux { } + trait Baux { type Assoc; } + ", + ); + reparse_into_different_test( + " + struct Foo {} + trait Boz { type Assoc; } + impl Boz for Foo where T: Boz> { + type Assoc = Foo; + } + ", + " + struct Foo {} + trait Boz { type Assoc; } + impl Boz for Foo where T: Boz>, T: Boz { + type Assoc = Foo; + } + ", + ); +} diff --git a/tests/logging_db/built_ins.rs b/tests/logging_db/built_ins.rs new file mode 100644 index 00000000000..32e4f9d2c58 --- /dev/null +++ b/tests/logging_db/built_ins.rs @@ -0,0 +1,104 @@ +use super::*; +#[test] +fn test_function_type() { + reparse_test( + " + struct Foo { } + trait Baz { } + impl Baz for Foo { } + ", + ); +} + +#[test] +fn test_scalar_types() { + let basic = vec!["bool", "char", "f32", "f64"]; + let sizes = vec!["size", "8", "16", "32", "64", "128"]; + let prefixes = vec!["u", "i"]; + let ints = prefixes + .iter() + .flat_map(|&p| sizes.iter().map(move |&size| format!("{}{}", p, size))); + let scalars = basic.iter().copied().map(str::to_owned).chain(ints); + + for scalar in scalars { + reparse_test(&format!( + " + struct Foo {{ + field: {0} + }} + trait Bar {{ + type Baz; + }} + impl Bar for Foo {{ + type Baz = {0}; + }} + ", + scalar + )); + } +} + +#[test] +fn test_raw_ptr_types() { + reparse_test( + " + struct Foo { + field: *const T + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = *const u32; + } + ", + ); + reparse_test( + " + struct Foo { + field: *mut T + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = *mut u32; + } + ", + ); +} + +#[test] +fn test_tuples() { + reparse_test( + " + struct Fuu { + fuu_field: () + } + ", + ); + reparse_test( + " + struct Uff { + fuu_field: (Iff,), + iff2_field: (Iff, Iff, Iff) + } + struct Iff { } + ", + ); + reparse_test( + " + struct Foo { + field: (u32,*const T,T), + field2: (T,), + field3: (T) + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = (T,Foo,u32); + } + ", + ); +} diff --git a/tests/logging_db/dyn_.rs b/tests/logging_db/dyn_.rs new file mode 100644 index 00000000000..b3db33184af --- /dev/null +++ b/tests/logging_db/dyn_.rs @@ -0,0 +1,80 @@ +use super::*; +#[test] +fn test_forall_in_dyn() { + reparse_test( + " + trait Foo {} + trait Bar<'a> {} + impl Foo for dyn forall<'a> Bar<'a> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a> Baz<'a> + } + trait Baz<'a> {} + ", + ); + reparse_test( + " + trait Foo {} + trait Bax<'a, 'b> {} + impl Foo for dyn forall<'a, 'b> Bax<'a, 'b> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a, 'b> Bix<'a, 'b> + } + trait Bix<'a, 'b> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> + } + trait Bex<'a> {} + trait Byx<'a> {} + ", + ); + reparse_test( + " + struct Foo { + field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> + } + trait Bux<'a, 'b> {} + trait Brx<'a, 'b> {} + ", + ); + reparse_test( + " + struct Foo<'a> { + field: dyn forall<'b> Bpx<'a, 'b> + } + trait Bpx<'a, 'b> {} + ", + ); +} + +#[test] +fn test_simple_dyn() { + reparse_test( + " + struct Foo { + field: dyn Bax + } + trait Bax {} + ", + ); + reparse_test( + " + struct Foo<'a> { + field: dyn Bix<'a> + } + trait Bix<'a> {} + ", + ); +} diff --git a/tests/logging_db/impl_.rs b/tests/logging_db/impl_.rs new file mode 100644 index 00000000000..568fa13cf31 --- /dev/null +++ b/tests/logging_db/impl_.rs @@ -0,0 +1,23 @@ +use super::*; +#[test] +fn test_negative_auto_trait_impl() { + reparse_test( + " + struct Foo { } + #[auto] + trait Baz {} + impl !Baz for Foo { } + ", + ); +} + +#[test] +fn test_simple_impl() { + reparse_test( + " + struct Foo {} + trait Bar {} + impl Bar for Foo {} + ", + ); +} diff --git a/tests/logging_db/lifetimes.rs b/tests/logging_db/lifetimes.rs new file mode 100644 index 00000000000..87a7cf6d387 --- /dev/null +++ b/tests/logging_db/lifetimes.rs @@ -0,0 +1,27 @@ +use super::*; +#[test] +fn test_various_forall() { + reparse_test( + " + struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } + trait Baz<'a> {} + trait Bax<'a> {} + trait Biz { + type Bex: forall<'a> Bax<'a>; + } + impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } + impl<'a> Bax<'a> for fn(Foo<'a>) { } + impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> { } + ", + ); +} +#[test] +fn test_lifetimes_in_structs() { + reparse_test( + " + struct Foo<'b> { } + trait Baz<'a> {} + impl<'a> Baz<'a> for Foo<'a> { } + ", + ); +} diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs new file mode 100644 index 00000000000..dfe76ba0bf9 --- /dev/null +++ b/tests/logging_db/mod.rs @@ -0,0 +1,185 @@ +use chalk_integration::{program::Program, query::LoweringDatabase, tls}; +use chalk_solve::display; +use std::{fmt::Debug, sync::Arc}; + +mod assoc_ty; +mod built_ins; +mod dyn_; +mod impl_; +mod lifetimes; +mod opaque_ty; +mod self_; +mod struct_; +mod trait_; +mod where_clauses; + +fn write_program(program: &Program) -> String { + let mut out = String::new(); + for datum in program.struct_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + for datum in program.trait_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + for datum in program.impl_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + for datum in program.opaque_ty_data.values() { + display::write_top_level(&mut out, program, &**datum).unwrap(); + } + out +} + +fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { + use std::fmt::Write; + + let mut out = String::new(); + let original = format!("{:#?}", original); + let produced = format!("{:#?}", produced); + for line in diff::lines(&original, &produced) { + match line { + diff::Result::Left(l) => write!(out, "-{}\n", l), + diff::Result::Both(l, _) => write!(out, " {}\n", l), + diff::Result::Right(r) => write!(out, "+{}\n", r), + } + .expect("writing to string never fails"); + } + out +} + +/// Data from performing a reparse test which can be used to make additional +/// assertions. +/// +/// Not necessary for use unless additional assertions are necessary. +#[allow(unused)] +struct ReparseTestResult<'a> { + /// The program text for the original test code + original_text: &'a str, + /// The program text for the code the test says should be output + target_text: &'a str, + /// The actual reparsed output text + output_text: String, + /// Lowered version of `original_text` + original_program: Arc, + /// Lowered version of `target_text` + target_program: Arc, + /// Lowered version of `output_text` + output_program: Arc, +} + +/// Parses the input, lowers it, prints it, then re-parses and re-lowers, +/// failing if the two lowered programs don't match. +/// +/// Note: the comparison here does include IDs, so input order matters. In +/// particular, ProgramWriter always writes traits, then structs, then +/// impls. So all traits must come first, then structs, then all impls, or +/// the reparse will fail. +fn reparse_test(program_text: &str) -> ReparseTestResult<'_> { + reparse_into_different_test(program_text, program_text) +} + +/// [`reparse_test`], but allows a non-convergent test program to be tested +/// a different target. +fn reparse_into_different_test<'a>( + program_text: &'a str, + target_text: &'a str, +) -> ReparseTestResult<'a> { + let original_db = chalk_integration::db::ChalkDatabase::with(program_text, <_>::default()); + let original_program = original_db.program_ir().unwrap_or_else(|e| { + panic!( + "unable to lower test program:\n{}\nSource:\n{}\n", + e, program_text + ) + }); + let target_db = chalk_integration::db::ChalkDatabase::with(target_text, <_>::default()); + let target_program = target_db.program_ir().unwrap_or_else(|e| { + panic!( + "unable to lower test program:\n{}\nSource:\n{}\n", + e, program_text + ) + }); + let output_text = + tls::set_current_program(&original_program, || write_program(&original_program)); + let output_db = chalk_integration::db::ChalkDatabase::with(&output_text, <_>::default()); + let output_program = output_db.program_ir().unwrap_or_else(|e| { + panic!( + "error lowering writer output:\n{}\nNew source:\n{}\n", + e, output_text + ) + }); + if output_program != target_program { + panic!( + "WriteProgram produced different program.\n\ + Diff:\n{}\n\ + Source:\n{}\n{}\ + New Source:\n{}\n", + program_diff(&target_program, &output_program), + program_text, + if target_text != program_text { + format!( + "Test Should Output (different from original):\n{}\n", + target_text + ) + } else { + String::new() + }, + output_text + ); + } + eprintln!("\nTest Succeeded:\n\n{}\n---", output_text); + ReparseTestResult { + original_text: program_text, + output_text, + target_text, + original_program, + output_program, + target_program, + } +} + +#[test] +fn test_program_writer() { + reparse_test( + " + struct Foo { } + struct Vec { } + struct Map<_0, _1> { } + struct Ref<'a, T> { } + + trait Marker { } + trait Clone { } + trait Deref<'a, U> { + type Assoc: Clone; + } + trait AssocWithGenerics { + type Assoc; + } + trait AssocTrait3 { + type Assoc; + } + trait AsRef { } + + trait AssocTraitWithWhere { + type Assoc where U: AsRef; + } + + impl Marker for Vec { } + impl Clone for Foo { } + impl Clone for Vec where T: Clone { } + impl Clone for Map where T: Clone, U: Clone { } + + impl<'a, T, U> Deref<'a, T> for Ref<'a, U> { + type Assoc = Foo; + } + impl AssocWithGenerics for Foo { + type Assoc = Vec; + } + impl AssocTrait3 for Vec { + type Assoc = Map; + } + impl AssocTraitWithWhere for Vec { + type Assoc = Map; + } + ", + ); +} diff --git a/tests/logging_db/opaque_ty.rs b/tests/logging_db/opaque_ty.rs new file mode 100644 index 00000000000..c401b3bd7e5 --- /dev/null +++ b/tests/logging_db/opaque_ty.rs @@ -0,0 +1,116 @@ +use super::*; +#[test] +fn opaque_types() { + reparse_test( + " + struct Bar {} + trait Buz {} + trait Baz { + type Hi; + } + impl Buz for Bar {} + impl Baz for Foo { + type Hi = Foo; + } + opaque type Foo: Buz = Bar; + ", + ); +} + +#[test] +fn test_generic_opaque_types() { + reparse_test( + " + struct Foo {} + trait Bar {} + opaque type Baz: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + struct Unit {} + trait Bar {} + opaque type Boz: Bar = Foo; + ", + ); +} + +#[test] +fn test_opaque_type_as_type_value() { + reparse_test( + " + struct Foo {} + trait Bar {} + trait Fuzz { + type Assoc: Bar; + } + impl Bar for Foo {} + impl Fuzz for Foo { + type Assoc = Bax; + } + opaque type Bax: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + trait Bar {} + trait Faz { + type Assoc; + } + impl Faz for Foo { + type Assoc = fn(Baz); + } + opaque type Baz: Bar = Foo; + ", + ); +} + +// Generic opaque types can't currently be used as types (these fail to lower) +#[ignore] +#[test] +fn test_generic_opaque_type_as_value1() { + reparse_test( + " + struct Foo {} + trait Bar {} + trait Fizz { + type Assoc: Bar; + } + impl Bar for Foo {} + impl Fizz for Foo { + type Assoc = Baz; + } + opaque type Baz: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + trait Bar {} + trait Faz { + type Assoc; + } + impl Faz for Foo { + type Assoc = fn(Baz); + } + opaque type Baz: Bar = Foo; + ", + ); + reparse_test( + " + struct Foo {} + struct Unit {} + trait Bar {} + trait Fez { + type Assoc; + } + impl Fez for Foo { + type Assoc = fn(Biiiz); + } + impl Bar for Foo {} + opaque type Biiiz: Bar = Foo; + ", + ); +} diff --git a/tests/logging_db/self_.rs b/tests/logging_db/self_.rs new file mode 100644 index 00000000000..d1249ab9128 --- /dev/null +++ b/tests/logging_db/self_.rs @@ -0,0 +1,167 @@ +use super::*; + +#[test] +fn test_self_in_trait_where() { + reparse_test( + " + trait Bkz {} + trait Foo where Self: Bkz {} + ", + ); + reparse_test( + " + trait Baz<'a> {} + trait Foo where forall<'a> Self: Baz<'a> {} + ", + ); +} + +#[test] +fn test_self_in_assoc_type() { + reparse_test( + " + trait Extra {} + trait Bez {} + trait Foo { + type Assoc: Extra; + } + ", + ); + + reparse_test( + " + trait Bez {} + trait Foo { + type Assoc where Self: Bez; + } + ", + ); + reparse_test( + " + trait Biz {} + trait Foo { + type Assoc where Self: Biz; + } + ", + ); +} + +#[test] +fn test_self_in_dyn() { + reparse_test( + " + trait Bun {} + trait Foo { + type Assoc where dyn Bun: Bun; + } + ", + ); + reparse_test( + " + trait Has {} + trait Bun {} + trait Fiz { + type Assoc1: Has>; + type Assoc2: Has>; + } + ", + ); +} + +// Self doesn't work in these circumstances yet (test programs fail to lower) +#[ignore] +#[test] +fn test_self_in_struct_bounds() { + reparse_test( + " + trait Bax {} + struct Foo where T: Bax {} + ", + ); + reparse_test( + " + trait Baz {} + struct Foo where Self: Baz {} + ", + ); + reparse_test( + " + trait Blz {} + struct Foo where Self: Blz {} + ", + ); +} + +// Self doesn't work in these circumstances yet (test programs fail to lower) +#[ignore] +#[test] +fn test_self_in_impl_blocks() { + reparse_test( + " + trait Foo { + type Assoc; + } + struct Bix {} + impl Foo for Bix { + type Assoc = Self; + } + ", + ); + reparse_test( + " + trait Foo {} + trait Fin {} + struct Bux {} + impl Foo for Bux where Self: Fin {} + ", + ); + reparse_test( + " + trait Faux {} + trait Paw { + type Assoc1; + type Assoc2; + } + struct Buzz {} + impl Paw for Buzz { + type Assoc1 = dyn Faux; + type Assoc2 = dyn Faux; + } + ", + ); +} + +#[test] +fn test_against_accidental_self() { + // In some of the writer code, it would be really easy to introduce a + // outputs the first generic parameter of things as "Self". + let in_structs = reparse_test( + " + struct Foo { + field: T + } + ", + ); + assert!(!in_structs.output_text.contains("Self")); + let in_impl = reparse_test( + " + struct Foo {} + trait Bux { + type Assoc; + } + impl Bux for Foo { + type Assoc = T; + } + ", + ); + assert!(!in_impl.output_text.contains("Self")); + let in_opaque = reparse_test( + " + struct Foo {} + trait Que {} + impl Que for Foo {} + opaque type Bar: Que = Foo; + ", + ); + assert!(!in_opaque.output_text.contains("Self")); +} diff --git a/tests/logging_db/struct_.rs b/tests/logging_db/struct_.rs new file mode 100644 index 00000000000..1116b345c93 --- /dev/null +++ b/tests/logging_db/struct_.rs @@ -0,0 +1,27 @@ +use super::*; + +#[test] +fn test_simple_structs_and_bounds() { + reparse_test("struct Foo {}"); + reparse_test("struct Foo {}"); + reparse_test( + " + struct Foo where T: Trait {} + trait Trait {} + ", + ); +} + +#[test] +fn test_struct_fields() { + reparse_test( + " + struct Foo {} + struct Bar {} + struct Baz { + x: Foo, + b: Bar + } + ", + ); +} diff --git a/tests/logging_db/trait_.rs b/tests/logging_db/trait_.rs new file mode 100644 index 00000000000..7156362f308 --- /dev/null +++ b/tests/logging_db/trait_.rs @@ -0,0 +1,52 @@ +use super::*; +#[test] +fn test_simple_traits_and_bounds() { + reparse_test("trait Foo {}"); + reparse_test("trait Foo {}"); + reparse_test( + " + trait Foo where T: Trait {} + trait Trait {} + ", + ); +} + +#[test] +fn test_basic_trait_impl() { + reparse_test( + " + struct Foo { } + trait Bar {} + impl Bar for Foo { } + ", + ); +} + +#[test] +fn test_trait_flags() { + let flags = vec![ + "auto", + "marker", + "upstream", + "fundamental", + "non_enumerable", + "coinductive", + ]; + reparse_test(&format!( + "{}trait Hello {{}}", + flags + .iter() + .map(|f| format!("#[{}]", f)) + .collect::>() + .join("\n") + )); + for flag in flags { + reparse_test(&format!( + " + #[{0}] + trait Hello_{0} {{}} + ", + flag + )); + } +} diff --git a/tests/logging_db/where_clauses.rs b/tests/logging_db/where_clauses.rs new file mode 100644 index 00000000000..e93aca3b5fa --- /dev/null +++ b/tests/logging_db/where_clauses.rs @@ -0,0 +1,110 @@ +use super::*; +#[test] +fn test_complicated_bounds() { + reparse_into_different_test( + " + struct Foo { } + trait Bar { } + trait Baz { } + trait Bax { type BaxT; } + trait Test { + type Assoc: Bar + Baz + Bax + where + Foo: Bax, + Foo: Bar, + dyn Bar: Baz; + } + ", + " + struct Foo { } + trait Bar { } + trait Baz { } + trait Bax { type BaxT; } + trait Test { + type Assoc: Bar + Baz + Bax + where + Foo: Bax, + Foo: Bax, + Foo: Bar, + dyn Bar: Baz; + }", + ); +} + +#[test] +fn test_struct_where_clauses() { + reparse_test( + " + struct Foo where T: Baz, U: Bez { } + trait Baz { } + trait Bez { } + ", + ); +} + +#[test] +fn test_impl_where_clauses() { + reparse_test( + " + struct Foo where T: Baz, U: Bez { } + trait Baz { } + trait Bez { } + impl Bez for Foo where T: Baz, U: Bez { } + ", + ); + // TODO: more of these +} + +#[test] +fn test_trait_projection() { + reparse_test( + " + struct Flux {} + struct Foo where U: Bez, >::Assoc: Baz { } + trait Baz { } + trait Bez { + type Assoc; + } + ", + ); +} +#[test] +fn test_trait_projection_with_dyn_arg() { + reparse_test( + " + struct Foo where U: Bez, >::Assoc: Baz { } + trait Baz { } + trait Bez { + type Assoc; + } + ", + ); +} + +#[test] +fn test_forall_in_where() { + reparse_test( + " + trait Bax {} + trait Foo where forall T: Bax {} + ", + ); + reparse_test( + " + trait Buz<'a> {} + trait Foo where forall<'a> T: Buz<'a> {} + ", + ); + reparse_test( + " + struct Foo where forall T: Biz {} + trait Biz {} + ", + ); + reparse_test( + " + struct Foo where forall<'a> T: Bez<'a> {} + trait Bez<'a> {} + ", + ); +} From d9ab6113efc5a317aaf440cf91951dc186d066c4 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 11:31:16 -0700 Subject: [PATCH 22/70] Add reference type rendering Adds rendering code for TypeName::Ref in `ApplicationTy`'s `RenderAsRust` impl. The two subtitution parameters used are the liftime and the reference type, which are parameters 0 and 1, respectively. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 13 +++++++++++++ tests/logging_db/built_ins.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index d8e35c6210d..31ad648777a 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -497,6 +497,19 @@ impl RenderAsRust for ApplicationTy { self.first_type_parameter(interner).unwrap().display(s) )? } + TypeName::Ref(mutability) => { + let mutability = match mutability { + Mutability::Mut => "mut ", + Mutability::Not => "", + }; + write!( + f, + "&{} {}{}", + self.substitution.at(interner, 0).display(s), + mutability, + self.substitution.at(interner, 1).display(s) + )?; + } TypeName::Error => write!(f, "{{error}}")?, } Ok(()) diff --git a/tests/logging_db/built_ins.rs b/tests/logging_db/built_ins.rs index 32e4f9d2c58..ae21b2c2902 100644 --- a/tests/logging_db/built_ins.rs +++ b/tests/logging_db/built_ins.rs @@ -68,6 +68,36 @@ fn test_raw_ptr_types() { ); } +#[test] +fn test_reference_types() { + reparse_test( + " + struct Foo<'a,T> { + field: &'a T + } + trait Bar { + type Baz; + } + impl<'a,T> Bar for Foo<'a,T> { + type Baz = &'a u32; + } + ", + ); + reparse_test( + " + struct Foo<'a,T> { + field: &'a mut T + } + trait Bar { + type Baz; + } + impl<'a,T> Bar for Foo<'a,T> { + type Baz = &'a mut u32; + } + ", + ); +} + #[test] fn test_tuples() { reparse_test( From 6e9a020b747e3875b6b39f17787d51584af1a793 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 11:34:25 -0700 Subject: [PATCH 23/70] Add str type rendering Adds rendering code for TypeName::Str in `ApplicationTy`'s `RenderAsRust` impl. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 1 + tests/logging_db/built_ins.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 31ad648777a..90cd24b0bcd 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -510,6 +510,7 @@ impl RenderAsRust for ApplicationTy { self.substitution.at(interner, 1).display(s) )?; } + TypeName::Str => write!(f, "str")?, TypeName::Error => write!(f, "{{error}}")?, } Ok(()) diff --git a/tests/logging_db/built_ins.rs b/tests/logging_db/built_ins.rs index ae21b2c2902..dc63b38ff50 100644 --- a/tests/logging_db/built_ins.rs +++ b/tests/logging_db/built_ins.rs @@ -38,6 +38,23 @@ fn test_scalar_types() { } } +#[test] +fn test_str_types() { + reparse_test( + " + struct Foo { + field: str + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = str; + } + ", + ); +} + #[test] fn test_raw_ptr_types() { reparse_test( From fcffabdb063696d7cbef72817fdac3819cb5ae8e Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 11:36:19 -0700 Subject: [PATCH 24/70] Add slice type rendering Adds rendering code for `TypeName::Slice` in `ApplicationTy`'s `RenderAsRust` impl. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 7 +++++++ tests/logging_db/built_ins.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 90cd24b0bcd..2fc305eb5f7 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -511,6 +511,13 @@ impl RenderAsRust for ApplicationTy { )?; } TypeName::Str => write!(f, "str")?, + TypeName::Slice => { + write!( + f, + "[{}]", + self.first_type_parameter(interner).unwrap().display(s) + )?; + } TypeName::Error => write!(f, "{{error}}")?, } Ok(()) diff --git a/tests/logging_db/built_ins.rs b/tests/logging_db/built_ins.rs index dc63b38ff50..13cc0878c9d 100644 --- a/tests/logging_db/built_ins.rs +++ b/tests/logging_db/built_ins.rs @@ -38,6 +38,22 @@ fn test_scalar_types() { } } +#[test] +fn test_slice_types() { + reparse_test( + " + struct Foo { + field: [T] + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = [T]; + }", + ); +} + #[test] fn test_str_types() { reparse_test( From e18d3432cce45ee845b4eeeba309c2c0798953d0 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 11:50:09 -0700 Subject: [PATCH 25/70] Add trait impl for built in tests Add tests for implementing a trait for a builtin in type, such as: * `impl Bar for u32 {}` * `impl for (T1,T2) {}` Co-authored-by: David Ross --- tests/logging_db/built_ins.rs | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/logging_db/built_ins.rs b/tests/logging_db/built_ins.rs index 13cc0878c9d..0ee55492dd8 100644 --- a/tests/logging_db/built_ins.rs +++ b/tests/logging_db/built_ins.rs @@ -8,6 +8,13 @@ fn test_function_type() { impl Baz for Foo { } ", ); + reparse_test( + " + struct Foo { } + trait Baz { } + impl Baz for fn(Foo) { } + ", + ); } #[test] @@ -32,6 +39,9 @@ fn test_scalar_types() { impl Bar for Foo {{ type Baz = {0}; }} + impl Bar for {0} {{ + type Baz = {0}; + }} ", scalar )); @@ -50,6 +60,9 @@ fn test_slice_types() { } impl Bar for Foo { type Baz = [T]; + } + impl Bar for [T] { + type Baz = Foo; }", ); } @@ -67,6 +80,9 @@ fn test_str_types() { impl Bar for Foo { type Baz = str; } + impl Bar for str { + type Baz = str; + } ", ); } @@ -84,6 +100,9 @@ fn test_raw_ptr_types() { impl Bar for Foo { type Baz = *const u32; } + impl Bar for *const u32 { + type Baz = *const u32; + } ", ); reparse_test( @@ -97,6 +116,22 @@ fn test_raw_ptr_types() { impl Bar for Foo { type Baz = *mut u32; } + impl Bar for *mut u32 { + type Baz = *mut u32; + } + ", + ); + reparse_test( + " + trait Bar { + type Baz; + } + impl Bar for *mut T { + type Baz = *mut T; + } + impl Bar for *const T { + type Baz = *const T; + } ", ); } @@ -114,6 +149,9 @@ fn test_reference_types() { impl<'a,T> Bar for Foo<'a,T> { type Baz = &'a u32; } + impl<'a> Bar for &'a u32 { + type Baz = &'a u32; + } ", ); reparse_test( @@ -127,6 +165,22 @@ fn test_reference_types() { impl<'a,T> Bar for Foo<'a,T> { type Baz = &'a mut u32; } + impl<'a> Bar for &'a mut u32 { + type Baz = &'a u32; + } + ", + ); + reparse_test( + " + trait Bar { + type Baz; + } + impl<'a,T> Bar for &'a mut T { + type Baz = &'a mut T; + } + impl<'a,T> Bar for &'a T { + type Baz = &'a T; + } ", ); } @@ -164,4 +218,18 @@ fn test_tuples() { } ", ); + reparse_test( + " + trait Blug {} + impl Blug for (T1,T2) { + + } + impl Blug for (T1,) { + + } + impl Blug for () { + + } + ", + ); } From 2fe84d0117886e616234c544635d0e6c9b137677 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 11:54:12 -0700 Subject: [PATCH 26/70] Add impl for generic test Add a test for implementing traits for generic types such as `impl Bar for T {}`. Co-authored-by: David Ross --- tests/logging_db/impl_.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/logging_db/impl_.rs b/tests/logging_db/impl_.rs index 568fa13cf31..e4d046d69ec 100644 --- a/tests/logging_db/impl_.rs +++ b/tests/logging_db/impl_.rs @@ -21,3 +21,19 @@ fn test_simple_impl() { ", ); } + +#[test] +fn test_impl_for_generic() { + reparse_test( + " + trait Bar {} + impl Bar for G {} + ", + ); + reparse_test( + " + trait Baz {} + impl Baz for T {} + ", + ); +} From c6d3de5af67abc1e97a870af9fbc74e2c3e840ae Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 14:36:30 -0700 Subject: [PATCH 27/70] Test and fix formatting * Tested where clauses and struct fields. * Added indentation and newlines to where clauses in trait blocks. * Removed extra spaces after struct and impl We chose to use regular expressions to match the output programs so we could pick up arbitrary whitespace errors (there were a couple we found) while having flexibility to avoid testing other aspecs of the programs like naming and certain spacing. Co-authored-by: David Ross --- Cargo.lock | 1 + Cargo.toml | 1 + chalk-solve/src/display.rs | 13 ++++-- tests/logging_db/formatting.rs | 79 ++++++++++++++++++++++++++++++++++ tests/logging_db/mod.rs | 19 ++++++++ 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 tests/logging_db/formatting.rs diff --git a/Cargo.lock b/Cargo.lock index f3519b1c760..fc1a25fd5fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,7 @@ dependencies = [ "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 6.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "salsa 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 19228b9ddb0..5dd66e16df2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,4 +35,5 @@ chalk-integration = { version = "0.16.0-dev.0", path = "chalk-integration" } # used for program_writer test errors diff = "0.1" pretty_assertions = "0.6.1" +regex = "1" tracing = "0.1" diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 2fc305eb5f7..dfee244ae84 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -179,7 +179,7 @@ impl RenderAsRust for ImplDatum { write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; write!( f, - " {}{} for {} ", + " {}{} for {}", self.polarity.display(s), full_trait_name, trait_ref.self_type_parameter(interner).display(s) @@ -187,6 +187,8 @@ impl RenderAsRust for ImplDatum { if !value.where_clauses.is_empty() { let s = &s.add_indent(); write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; + } else { + write!(f, " ")?; } write!(f, "{{")?; { @@ -833,9 +835,11 @@ impl RenderAsRust for TraitDatum { let binders = s.binder_var_display(&self.binders.binders).skip(1); write!(f, "trait {}", self.id.display(s))?; write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; - write!(f, " ")?; if !value.where_clauses.is_empty() { - write!(f, "\nwhere {}\n", value.where_clauses.display(s))?; + let s = &s.add_indent(); + write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; + } else { + write!(f, " ")?; } write!(f, "{{")?; let s = &s.add_indent(); @@ -861,10 +865,11 @@ impl RenderAsRust for StructDatum { let value = self.binders.skip_binders(); write!(f, "struct {}", self.id.display(s),)?; write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.binders.binders), ", ")?; - write!(f, " ")?; if !value.where_clauses.is_empty() { let s = &s.add_indent(); write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; + } else { + write!(f, " ")?; } write!(f, "{{")?; let s = &s.add_indent(); diff --git a/tests/logging_db/formatting.rs b/tests/logging_db/formatting.rs new file mode 100644 index 00000000000..76b3075646e --- /dev/null +++ b/tests/logging_db/formatting.rs @@ -0,0 +1,79 @@ +use super::*; + +#[test] +fn test_assoc_type_formatting() { + test_formatting( + " + struct Foo {} + trait Bar { + type Assoc; + } + impl Bar for Foo { + type Assoc = (); + } + ", + r#"struct [a-zA-Z0-9_-]+ \{\s*\} +trait [a-zA-Z0-9_-]+ \{ + type [a-zA-Z0-9_-]+; +\} +impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ \{ + type [a-zA-Z0-9_-]+ = \(\); +\}"#, + ); +} + +#[test] +fn test_struct_field_formatting() { + test_formatting( + " + struct Foo {} + struct Bar { + field1: Foo + } + struct Azg { + field1: Foo, + field2: Bar + } + ", + r#"struct [a-zA-Z0-9_-]+ \{\} +struct [a-zA-Z0-9_-]+ \{ + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ +\} +struct [a-zA-Z0-9_-]+ \{ + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ +\}"#, + ); +} + +#[test] +fn test_where_clause_formatting() { + test_formatting( + " + struct Foo where Foo: Baz, Foo: Bar {} + trait Bar where Foo: Baz, dyn Baz: Bar {} + trait Baz {} + impl Bar for Foo where Foo: Baz, (): Baz {} + impl Baz for Foo {} + impl Bar for dyn Baz {} + ", + r#"struct [a-zA-Z0-9_-]+ +where + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ +\{\s*\} +trait [a-zA-Z0-9_-]+ +where + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ +\{\s*\} +trait [a-zA-Z0-9_-]+ \{\} +impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ +where + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + \(\): [a-zA-Z0-9_-]+ +\{\} +impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ \{\} +impl [a-zA-Z0-9_-]+ for dyn [a-zA-Z0-9_-]+ \{\}"#, + ); +} diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index dfe76ba0bf9..91be9abcdc8 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -1,10 +1,12 @@ use chalk_integration::{program::Program, query::LoweringDatabase, tls}; use chalk_solve::display; +use regex::Regex; use std::{fmt::Debug, sync::Arc}; mod assoc_ty; mod built_ins; mod dyn_; +mod formatting; mod impl_; mod lifetimes; mod opaque_ty; @@ -137,6 +139,23 @@ fn reparse_into_different_test<'a>( } } +fn test_formatting(src: &str, acceptable: &str) { + let result = reparse_test(src); + let acceptable = Regex::new(acceptable).unwrap(); + if !acceptable.is_match(&result.output_text) { + panic!( + "output_text's formatting didn't match the criteria.\ + \noutput_text:\n\"{0}\"\ + \ncriteria:\n\"{1}\"\ + \ndebug output: {0:?}\ + \ndebug criteria: {2:?}\n", + result.output_text, + acceptable, + acceptable.as_str() + ); + } +} + #[test] fn test_program_writer() { reparse_test( From 884ebfedb2fe15fa8d561359a84788537140b1af Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 14:53:33 -0700 Subject: [PATCH 28/70] Test and fix assoc where clause formatting Removed the ":" from after the associated type when where clauses but no bounds are present. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 2 +- tests/logging_db/formatting.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index dfee244ae84..fc083306007 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -782,7 +782,7 @@ impl RenderAsRust for AssociatedTyDatum { let datum_bounds = &self.binders.skip_binders(); - if !(datum_bounds.bounds.is_empty() && datum_bounds.where_clauses.is_empty()) { + if !datum_bounds.bounds.is_empty() { write!(f, ": ")?; } diff --git a/tests/logging_db/formatting.rs b/tests/logging_db/formatting.rs index 76b3075646e..e57683f3343 100644 --- a/tests/logging_db/formatting.rs +++ b/tests/logging_db/formatting.rs @@ -77,3 +77,26 @@ impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ \{\} impl [a-zA-Z0-9_-]+ for dyn [a-zA-Z0-9_-]+ \{\}"#, ); } + +#[test] +fn test_assoc_ty_where_clause() { + test_formatting( + " + trait Bar {} + trait Fuzz { + type Assoc + where + dyn Bar: Bar, + Self: Bar; + } + ", + r#"trait [a-zA-Z0-9_-]+ \{\s*\} +trait [a-zA-Z0-9_-]+ \{ + type [a-zA-Z0-9_-]+ + where + dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+; +\} +"#, + ); +} From cd9df5c7ba53b46524a9c14b6f4d54c949922fff Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 15:16:47 -0700 Subject: [PATCH 29/70] Rename logging_db tests directory to display We are planning to put the tests for the actual logging db under a tests folder called logging_db, but we were already using that name for the functionality inside of display. Therefore we renamed the display tests to display, which matches the names of the respective modules in chalk-solve. Co-authored-by: David Ross --- tests/{logging_db => display}/assoc_ty.rs | 0 tests/{logging_db => display}/built_ins.rs | 0 tests/{logging_db => display}/dyn_.rs | 0 tests/{logging_db => display}/formatting.rs | 0 tests/{logging_db => display}/impl_.rs | 0 tests/{logging_db => display}/lifetimes.rs | 0 tests/{logging_db => display}/mod.rs | 0 tests/{logging_db => display}/opaque_ty.rs | 0 tests/{logging_db => display}/self_.rs | 0 tests/{logging_db => display}/struct_.rs | 0 tests/{logging_db => display}/trait_.rs | 0 tests/{logging_db => display}/where_clauses.rs | 0 tests/lib.rs | 2 +- 13 files changed, 1 insertion(+), 1 deletion(-) rename tests/{logging_db => display}/assoc_ty.rs (100%) rename tests/{logging_db => display}/built_ins.rs (100%) rename tests/{logging_db => display}/dyn_.rs (100%) rename tests/{logging_db => display}/formatting.rs (100%) rename tests/{logging_db => display}/impl_.rs (100%) rename tests/{logging_db => display}/lifetimes.rs (100%) rename tests/{logging_db => display}/mod.rs (100%) rename tests/{logging_db => display}/opaque_ty.rs (100%) rename tests/{logging_db => display}/self_.rs (100%) rename tests/{logging_db => display}/struct_.rs (100%) rename tests/{logging_db => display}/trait_.rs (100%) rename tests/{logging_db => display}/where_clauses.rs (100%) diff --git a/tests/logging_db/assoc_ty.rs b/tests/display/assoc_ty.rs similarity index 100% rename from tests/logging_db/assoc_ty.rs rename to tests/display/assoc_ty.rs diff --git a/tests/logging_db/built_ins.rs b/tests/display/built_ins.rs similarity index 100% rename from tests/logging_db/built_ins.rs rename to tests/display/built_ins.rs diff --git a/tests/logging_db/dyn_.rs b/tests/display/dyn_.rs similarity index 100% rename from tests/logging_db/dyn_.rs rename to tests/display/dyn_.rs diff --git a/tests/logging_db/formatting.rs b/tests/display/formatting.rs similarity index 100% rename from tests/logging_db/formatting.rs rename to tests/display/formatting.rs diff --git a/tests/logging_db/impl_.rs b/tests/display/impl_.rs similarity index 100% rename from tests/logging_db/impl_.rs rename to tests/display/impl_.rs diff --git a/tests/logging_db/lifetimes.rs b/tests/display/lifetimes.rs similarity index 100% rename from tests/logging_db/lifetimes.rs rename to tests/display/lifetimes.rs diff --git a/tests/logging_db/mod.rs b/tests/display/mod.rs similarity index 100% rename from tests/logging_db/mod.rs rename to tests/display/mod.rs diff --git a/tests/logging_db/opaque_ty.rs b/tests/display/opaque_ty.rs similarity index 100% rename from tests/logging_db/opaque_ty.rs rename to tests/display/opaque_ty.rs diff --git a/tests/logging_db/self_.rs b/tests/display/self_.rs similarity index 100% rename from tests/logging_db/self_.rs rename to tests/display/self_.rs diff --git a/tests/logging_db/struct_.rs b/tests/display/struct_.rs similarity index 100% rename from tests/logging_db/struct_.rs rename to tests/display/struct_.rs diff --git a/tests/logging_db/trait_.rs b/tests/display/trait_.rs similarity index 100% rename from tests/logging_db/trait_.rs rename to tests/display/trait_.rs diff --git a/tests/logging_db/where_clauses.rs b/tests/display/where_clauses.rs similarity index 100% rename from tests/logging_db/where_clauses.rs rename to tests/display/where_clauses.rs diff --git a/tests/lib.rs b/tests/lib.rs index 9c7dcb64409..d7e2fdbb7d9 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -3,7 +3,7 @@ mod test_util; #[macro_use] mod test; -mod logging_db; +mod display; mod lowering; mod integration; From 10d1009b04d68da9156f1c17c6373bdfca657983 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 16 May 2020 20:41:23 -0700 Subject: [PATCH 30/70] Implement name disambiguation Append a unique identifier after type names. We don't append the unqiue identifier to the first instance of the name, which avoids needing name-agnostic equality in the display tests. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 73 ++++++++++++++++++++++++++++++++----- tests/display/formatting.rs | 18 +++++++++ tests/display/mod.rs | 11 +++--- 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index fc083306007..ece74450981 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -1,4 +1,5 @@ use std::{ + cell::RefCell, collections::BTreeMap, fmt::{Display, Formatter, Result}, rc::Rc, @@ -19,14 +20,12 @@ use itertools::Itertools; use crate::{logging_db::RecordedItemId, split::Split, RustIrDatabase}; -pub fn write_top_level(f: &mut F, db: &DB, v: &T) -> Result +pub fn write_top_level(f: &mut F, ws: &WriterState<'_, I>, v: &T) -> Result where I: Interner, - DB: RustIrDatabase, T: RenderAsRust, F: std::fmt::Write, { - let ws = &WriterState::new(db); write!(f, "{}\n", v.display(ws)) } @@ -39,23 +38,24 @@ where DB: RustIrDatabase, T: IntoIterator>, { + let ws = &WriterState::new(db); for id in ids { match id { RecordedItemId::Impl(id) => { let v = db.impl_datum(id); - write_top_level(f, db, &*v)?; + write_top_level(f, ws, &*v)?; } RecordedItemId::Struct(id) => { let v = db.struct_datum(id); - write_top_level(f, db, &*v)?; + write_top_level(f, ws, &*v)?; } RecordedItemId::Trait(id) => { let v = db.trait_datum(id); - write_top_level(f, db, &*v)?; + write_top_level(f, ws, &*v)?; } RecordedItemId::OpaqueTy(id) => { let v = db.opaque_ty_data(id); - write_top_level(f, db, &*v)?; + write_top_level(f, ws, &*v)?; } } } @@ -256,13 +256,17 @@ impl RenderAsRust for Parameter { impl RenderAsRust for StructId { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // TODO: use debug methods? - f.write_str(&s.db.struct_name(*self)) + write!( + f, + "{}", + s.alias_for_id_name(self.0, s.db.struct_name(*self)) + ) } } impl RenderAsRust for TraitId { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // TODO: use debug methods? - f.write_str(&s.db.trait_name(*self)) + write!(f, "{}", s.alias_for_id_name(self.0, s.db.trait_name(*self))) } } impl RenderAsRust for AssocTypeId { @@ -274,7 +278,11 @@ impl RenderAsRust for AssocTypeId { impl RenderAsRust for OpaqueTyId { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // TODO: use debug methods? - f.write_str(&s.db.opaque_type_name(*self)) + write!( + f, + "{}", + s.alias_for_id_name(self.0, s.db.opaque_type_name(*self)) + ) } } @@ -928,6 +936,44 @@ impl Display for InvertedBoundVar { } } +#[derive(Debug)] +struct DefIdAliases { + /// Map from the DefIds we've encountered to a u32 alias id unique to all ids + /// the same name. + aliases: BTreeMap, + /// Map from each name to the next unused u32 alias id. + next_unused_for_name: BTreeMap, +} + +impl Default for DefIdAliases { + fn default() -> Self { + DefIdAliases { + aliases: BTreeMap::default(), + next_unused_for_name: BTreeMap::default(), + } + } +} + +impl DefIdAliases { + fn alias_for_id_name(&mut self, id: I::DefId, name: String) -> String { + let next_unused_for_name = &mut self.next_unused_for_name; + let alias = *self.aliases.entry(id).or_insert_with(|| { + let next_unused: &mut u32 = + dbg!(next_unused_for_name.entry(dbg!(&name).clone())).or_default(); + let id = *next_unused; + *next_unused += 1; + dbg!(id) + }); + // If there are no conflicts, keep the name the same so that we don't + // need name-agnostic equality in display tests. + if alias == 0 { + name + } else { + format!("{}_{}", name, alias) + } + } +} + #[derive(Clone, Debug)] pub struct WriterState<'a, I: Interner> { db: &'a dyn RustIrDatabase, @@ -937,6 +983,7 @@ pub struct WriterState<'a, I: Interner> { remapping: Rc>, // the inverted_bound_var which maps to "Self" self_mapping: Option, + def_id_aliases: Rc>>, } type IndexWithinBinding = usize; impl<'a, I: Interner> WriterState<'a, I> { @@ -947,6 +994,7 @@ impl<'a, I: Interner> WriterState<'a, I> { debrujin_indices_deep: 0, remapping: Rc::new(BTreeMap::new()), self_mapping: None, + def_id_aliases: Default::default(), } } @@ -961,6 +1009,10 @@ impl<'a, I: Interner> WriterState<'a, I> { std::iter::repeat(" ").take(self.indent_level).format("") } + fn alias_for_id_name(&self, id: I::DefId, name: String) -> impl Display { + self.def_id_aliases.borrow_mut().alias_for_id_name(id, name) + } + /// Adds a level of debrujin index, and possibly a "Self" parameter. /// /// This should be called whenever recursing into the value within a @@ -1000,6 +1052,7 @@ impl<'a, I: Interner> WriterState<'a, I> { WriterState { remapping: Rc::new(remapping), + def_id_aliases: self.def_id_aliases.clone(), ..*self } } diff --git a/tests/display/formatting.rs b/tests/display/formatting.rs index e57683f3343..2653f354c5c 100644 --- a/tests/display/formatting.rs +++ b/tests/display/formatting.rs @@ -100,3 +100,21 @@ trait [a-zA-Z0-9_-]+ \{ "#, ); } + +#[test] +fn test_name_disambiguation() { + // we can't actually test different structs or traits with the same name in + // Chalk - but luckily our implementation ignores types for name + // disambiguation, so we can test it indirectly by using a struct and trait + // of the same name + reparse_into_different_test( + " + struct Baz {} + trait Baz {} + ", + " + struct Baz {} + trait Baz_1 {} + ", + ); +} diff --git a/tests/display/mod.rs b/tests/display/mod.rs index 91be9abcdc8..bd362a216ca 100644 --- a/tests/display/mod.rs +++ b/tests/display/mod.rs @@ -1,5 +1,5 @@ use chalk_integration::{program::Program, query::LoweringDatabase, tls}; -use chalk_solve::display; +use chalk_solve::display::{self, WriterState}; use regex::Regex; use std::{fmt::Debug, sync::Arc}; @@ -17,17 +17,18 @@ mod where_clauses; fn write_program(program: &Program) -> String { let mut out = String::new(); + let ws = &WriterState::new(program); for datum in program.struct_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); + display::write_top_level(&mut out, ws, &**datum).unwrap(); } for datum in program.trait_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); + display::write_top_level(&mut out, ws, &**datum).unwrap(); } for datum in program.impl_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); + display::write_top_level(&mut out, ws, &**datum).unwrap(); } for datum in program.opaque_ty_data.values() { - display::write_top_level(&mut out, program, &**datum).unwrap(); + display::write_top_level(&mut out, ws, &**datum).unwrap(); } out } From d5b395c1687370369eca617867acb8bc65ca6570 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 23 May 2020 14:59:37 -0700 Subject: [PATCH 31/70] Fix renaming errors from rebase * Fixed `Parameter*` renaming * Migrated to `*` imports for chalk_ir * Added todo implementation for never, const types. * Added todo for fn_def information methods on RustIrDatabase Co-authored-by: David Ross --- chalk-integration/src/db.rs | 4 +- chalk-integration/src/program.rs | 4 +- chalk-solve/src/display.rs | 82 ++++++++++++++++---------------- chalk-solve/src/lib.rs | 2 +- chalk-solve/src/logging_db.rs | 82 +++++++++++++++++++------------- tests/display/formatting.rs | 18 ++++--- tests/display/mod.rs | 2 +- tests/integration/panic.rs | 12 +++++ 8 files changed, 119 insertions(+), 87 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index e0ba1450831..c2cba6a3c23 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -202,8 +202,8 @@ impl RustIrDatabase for ChalkDatabase { self.program_ir().unwrap().trait_name(trait_id) } - fn struct_name(&self, struct_id: StructId) -> String { - self.program_ir().unwrap().struct_name(struct_id) + fn adt_name(&self, struct_id: AdtId) -> String { + self.program_ir().unwrap().adt_name(struct_id) } fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index bc3a6148039..08998da67c6 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -473,8 +473,8 @@ impl RustIrDatabase for Program { self.trait_kinds.get(&trait_id).unwrap().name.to_string() } - fn struct_name(&self, struct_id: StructId) -> String { - self.struct_kinds.get(&struct_id).unwrap().name.to_string() + fn adt_name(&self, struct_id: AdtId) -> String { + self.adt_kinds.get(&struct_id).unwrap().name.to_string() } fn assoc_type_name(&self, assoc_type_id: AssocTypeId) -> String { diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index ece74450981..541edc4a4ba 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -2,20 +2,13 @@ use std::{ cell::RefCell, collections::BTreeMap, fmt::{Display, Formatter, Result}, + ops::Fn as StdFn, rc::Rc, sync::Arc, }; -use chalk_ir::{ - interner::Interner, AliasEq, AliasTy, ApplicationTy, AssocTypeId, BoundVar, Fn as ChalkFn, - Lifetime, LifetimeData, Mutability, OpaqueTy, OpaqueTyId, Parameter, ParameterData, - ParameterKind, ParameterKinds, ProjectionTy, QuantifiedWhereClause, Scalar, StructId, TraitId, - TraitRef, Ty, TyData, TypeName, WhereClause, -}; -use chalk_rust_ir::{ - AliasEqBound, AssociatedTyDatum, AssociatedTyValue, ImplDatum, InlineBound, OpaqueTyDatum, - Polarity, QuantifiedInlineBound, StructDatum, TraitBound, TraitDatum, -}; +use crate::rust_ir::*; +use chalk_ir::{interner::Interner, *}; use itertools::Itertools; use crate::{logging_db::RecordedItemId, split::Split, RustIrDatabase}; @@ -45,8 +38,8 @@ where let v = db.impl_datum(id); write_top_level(f, ws, &*v)?; } - RecordedItemId::Struct(id) => { - let v = db.struct_datum(id); + RecordedItemId::Adt(id) => { + let v = db.adt_datum(id); write_top_level(f, ws, &*v)?; } RecordedItemId::Trait(id) => { @@ -76,10 +69,10 @@ impl> Display for DisplayRenderAsRust<'_, I, T> } } -fn as_display) -> Result>(f: F) -> impl Display { - struct ClosureDisplay) -> Result>(F); +fn as_display) -> Result>(f: F) -> impl Display { + struct ClosureDisplay) -> Result>(F); - impl) -> Result> Display for ClosureDisplay { + impl) -> Result> Display for ClosureDisplay { fn fmt(&self, f: &mut Formatter<'_>) -> Result { self.0(f) } @@ -247,19 +240,19 @@ impl RenderAsRust for Lifetime { self.data(s.db.interner()).fmt(s, f) } } -impl RenderAsRust for Parameter { +impl RenderAsRust for GenericArg { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // delegate to ParameterData self.data(s.db.interner()).fmt(s, f) } } -impl RenderAsRust for StructId { +impl RenderAsRust for AdtId { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // TODO: use debug methods? write!( f, "{}", - s.alias_for_id_name(self.0, s.db.struct_name(*self)) + s.alias_for_adt_id_name(self.0, s.db.adt_name(*self)) ) } } @@ -416,7 +409,7 @@ impl RenderAsRust for OpaqueTy { } } -impl RenderAsRust for ChalkFn { +impl RenderAsRust for Fn { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); let s = &s.add_debrujin_index(None); @@ -447,7 +440,7 @@ impl RenderAsRust for ApplicationTy { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); match self.name { - TypeName::Struct(sid) => { + TypeName::Adt(sid) => { write!(f, "{}", sid.display(s))?; let parameters = self.substitution.parameters(interner); let parameters = parameters.iter().map(|param| param.display(s)); @@ -529,6 +522,8 @@ impl RenderAsRust for ApplicationTy { )?; } TypeName::Error => write!(f, "{{error}}")?, + TypeName::Never => todo!("never type"), + TypeName::FnDef(_) => todo!("fn def type"), } Ok(()) } @@ -581,11 +576,12 @@ impl RenderAsRust for LifetimeData { } } -impl RenderAsRust for ParameterData { +impl RenderAsRust for GenericArgData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { - ParameterKind::Ty(ty) => write!(f, "{}", ty.display(s)), - ParameterKind::Lifetime(lt) => write!(f, "{}", lt.display(s)), + GenericArgData::Ty(ty) => write!(f, "{}", ty.display(s)), + GenericArgData::Lifetime(lt) => write!(f, "{}", lt.display(s)), + GenericArgData::Const(_) => todo!(""), } } } @@ -653,7 +649,7 @@ impl RenderAsRust for WhereClause { fn display_trait_with_generics<'a, I: Interner>( s: &'a WriterState<'a, I>, trait_name: impl RenderAsRust + 'a, - trait_params: impl IntoIterator> + 'a, + trait_params: impl IntoIterator> + 'a, ) -> impl Display + 'a { use std::fmt::Write; let trait_params = trait_params.into_iter().map(|param| param.display(s)); @@ -686,8 +682,8 @@ impl RenderAsRust for TraitRef { fn display_trait_with_assoc_ty_value<'a, I: Interner>( s: &'a WriterState<'a, I>, assoc_ty_datum: Arc>, - trait_params: &'a [Parameter], - assoc_ty_params: &'a [Parameter], + trait_params: &'a [GenericArg], + assoc_ty_params: &'a [GenericArg], assoc_ty_value: &'a Ty, ) -> impl Display + 'a { as_display(move |f| { @@ -856,7 +852,7 @@ impl RenderAsRust for TraitDatum { "\n{}\n", self.associated_ty_ids.iter().map(|assoc_ty_id| { let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); - format!("{}{}", s.indent(), assoc_ty_data.display(s)) + format!("{}{}", s.indent(), (*assoc_ty_data).display(s)) }), "\n" )?; @@ -865,7 +861,7 @@ impl RenderAsRust for TraitDatum { } } -impl RenderAsRust for StructDatum { +impl RenderAsRust for AdtDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // When support for Self in structs is added, self_binding should be // changed to Some(0) @@ -937,15 +933,15 @@ impl Display for InvertedBoundVar { } #[derive(Debug)] -struct DefIdAliases { +struct DefIdAliases { /// Map from the DefIds we've encountered to a u32 alias id unique to all ids /// the same name. - aliases: BTreeMap, + aliases: BTreeMap, /// Map from each name to the next unused u32 alias id. next_unused_for_name: BTreeMap, } -impl Default for DefIdAliases { +impl Default for DefIdAliases { fn default() -> Self { DefIdAliases { aliases: BTreeMap::default(), @@ -954,8 +950,8 @@ impl Default for DefIdAliases { } } -impl DefIdAliases { - fn alias_for_id_name(&mut self, id: I::DefId, name: String) -> String { +impl DefIdAliases { + fn alias_for_id_name(&mut self, id: T, name: String) -> String { let next_unused_for_name = &mut self.next_unused_for_name; let alias = *self.aliases.entry(id).or_insert_with(|| { let next_unused: &mut u32 = @@ -983,7 +979,8 @@ pub struct WriterState<'a, I: Interner> { remapping: Rc>, // the inverted_bound_var which maps to "Self" self_mapping: Option, - def_id_aliases: Rc>>, + def_id_aliases: Rc>>, + adt_id_aliases: Rc>>, } type IndexWithinBinding = usize; impl<'a, I: Interner> WriterState<'a, I> { @@ -995,6 +992,7 @@ impl<'a, I: Interner> WriterState<'a, I> { remapping: Rc::new(BTreeMap::new()), self_mapping: None, def_id_aliases: Default::default(), + adt_id_aliases: Default::default(), } } @@ -1013,6 +1011,10 @@ impl<'a, I: Interner> WriterState<'a, I> { self.def_id_aliases.borrow_mut().alias_for_id_name(id, name) } + fn alias_for_adt_id_name(&self, id: I::InternedAdtId, name: String) -> impl Display { + self.adt_id_aliases.borrow_mut().alias_for_id_name(id, name) + } + /// Adds a level of debrujin index, and possibly a "Self" parameter. /// /// This should be called whenever recursing into the value within a @@ -1052,8 +1054,7 @@ impl<'a, I: Interner> WriterState<'a, I> { WriterState { remapping: Rc::new(remapping), - def_id_aliases: self.def_id_aliases.clone(), - ..*self + ..self.clone() } } @@ -1101,7 +1102,7 @@ impl<'a, I: Interner> WriterState<'a, I> { fn binder_var_indices<'b>( &'b self, - binders: &'b ParameterKinds, + binders: &'b VariableKinds, ) -> impl Iterator + 'b { binders .iter(self.db.interner()) @@ -1111,14 +1112,15 @@ impl<'a, I: Interner> WriterState<'a, I> { fn binder_var_display<'b>( &'b self, - binders: &'b ParameterKinds, + binders: &'b VariableKinds, ) -> impl Iterator + 'b { binders .iter(self.db.interner()) .zip(self.binder_var_indices(binders)) .map(move |(parameter, var)| match parameter { - ParameterKind::Ty(_) => format!("{}", self.apply_mappings(var)), - ParameterKind::Lifetime(_) => format!("'{}", self.apply_mappings(var)), + VariableKind::Ty => format!("{}", self.apply_mappings(var)), + VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), + VariableKind::Const(_) => todo!("const variable kinds"), }) } } diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 3c3a4b213a4..858ecaa1794 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -147,7 +147,7 @@ pub trait RustIrDatabase: Debug { fn trait_name(&self, trait_id: TraitId) -> String; /// Retrieves a struct's original name. No uniqueness guarantees. - fn struct_name(&self, struct_id: StructId) -> String; + fn adt_name(&self, struct_id: AdtId) -> String; /// Retrieves the name of an associated type. fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 7e98a033d55..f332327de18 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -5,8 +5,8 @@ use std::{ sync::Mutex, }; -use chalk_ir::{interner::Interner, AssocTypeId, ImplId, OpaqueTyId, StructId, TraitId}; -use chalk_rust_ir::{ImplDatum, OpaqueTyDatum, StructDatum, TraitDatum}; +use crate::rust_ir::*; +use chalk_ir::{interner::Interner, *}; use crate::{display, RustIrDatabase}; /// Wraps another `RustIrDatabase` (`DB`) and records which definitions are used. @@ -78,7 +78,7 @@ where fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, - ) -> Arc> { + ) -> Arc> { let ty_datum = self.db.associated_ty_data(ty); self.record(ty_datum.trait_id); ty_datum @@ -87,9 +87,9 @@ where self.record(trait_id); self.db.trait_datum(trait_id) } - fn struct_datum(&self, struct_id: StructId) -> Arc> { - self.record(struct_id); - self.db.struct_datum(struct_id) + fn adt_datum(&self, adt_id: AdtId) -> Arc> { + self.record(adt_id); + self.db.adt_datum(adt_id) } fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.record(impl_id); @@ -97,8 +97,8 @@ where } fn associated_ty_value( &self, - id: chalk_rust_ir::AssociatedTyValueId, - ) -> Arc> { + id: crate::rust_ir::AssociatedTyValueId, + ) -> Arc> { let value = self.db.associated_ty_value(id); self.record(value.impl_id); value @@ -110,7 +110,7 @@ where fn impls_for_trait( &self, trait_id: TraitId, - parameters: &[chalk_ir::Parameter], + parameters: &[chalk_ir::GenericArg], ) -> Vec> { self.record(trait_id); let impl_ids = self.db.impls_for_trait(trait_id, parameters); @@ -121,14 +121,14 @@ where self.record(trait_id); self.db.local_impls_to_coherence_check(trait_id) } - fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { + fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { self.record(auto_trait_id); - self.record(struct_id); - self.db.impl_provided_for(auto_trait_id, struct_id) + self.record(adt_id); + self.db.impl_provided_for(auto_trait_id, adt_id) } fn well_known_trait_id( &self, - well_known_trait: chalk_rust_ir::WellKnownTrait, + well_known_trait: crate::rust_ir::WellKnownTrait, ) -> Option> { let trait_id = self.db.well_known_trait_id(well_known_trait); trait_id.map(|id| self.record(id)); @@ -146,8 +146,8 @@ where fn trait_name(&self, trait_id: TraitId) -> String { self.db.trait_name(trait_id) } - fn struct_name(&self, struct_id: StructId) -> String { - self.db.struct_name(struct_id) + fn adt_name(&self, adt_id: AdtId) -> String { + self.db.adt_name(adt_id) } fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.db.assoc_type_name(assoc_ty_id) @@ -159,6 +159,9 @@ where self.record(trait_id); self.db.is_object_safe(trait_id) } + fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { + todo!() + } } /// Wraps a [`RustIrDatabase`], and, when dropped, writes out all used @@ -232,22 +235,22 @@ where fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, - ) -> Arc> { + ) -> Arc> { self.db.associated_ty_data(ty) } fn trait_datum(&self, trait_id: TraitId) -> Arc> { self.db.trait_datum(trait_id) } - fn struct_datum(&self, struct_id: StructId) -> Arc> { - self.db.struct_datum(struct_id) + fn adt_datum(&self, adt_id: AdtId) -> Arc> { + self.db.adt_datum(adt_id) } fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.db.impl_datum(impl_id) } fn associated_ty_value( &self, - id: chalk_rust_ir::AssociatedTyValueId, - ) -> Arc> { + id: crate::rust_ir::AssociatedTyValueId, + ) -> Arc> { self.db.associated_ty_value(id) } fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { @@ -256,19 +259,19 @@ where fn impls_for_trait( &self, trait_id: TraitId, - parameters: &[chalk_ir::Parameter], + parameters: &[chalk_ir::GenericArg], ) -> Vec> { self.db.impls_for_trait(trait_id, parameters) } fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { self.db.local_impls_to_coherence_check(trait_id) } - fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { - self.db.impl_provided_for(auto_trait_id, struct_id) + fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { + self.db.impl_provided_for(auto_trait_id, adt_id) } fn well_known_trait_id( &self, - well_known_trait: chalk_rust_ir::WellKnownTrait, + well_known_trait: crate::rust_ir::WellKnownTrait, ) -> Option> { self.db.well_known_trait_id(well_known_trait) } @@ -287,8 +290,8 @@ where fn trait_name(&self, trait_id: TraitId) -> String { self.db.trait_name(trait_id) } - fn struct_name(&self, struct_id: StructId) -> String { - self.db.struct_name(struct_id) + fn adt_name(&self, adt_id: AdtId) -> String { + self.db.adt_name(adt_id) } fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.db.assoc_type_name(assoc_ty_id) @@ -296,19 +299,22 @@ where fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.db.opaque_type_name(opaque_ty_id) } + fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { + todo!() + } } #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum RecordedItemId { - Struct(StructId), + Adt(AdtId), Trait(TraitId), Impl(ImplId), OpaqueTy(OpaqueTyId), } -impl From> for RecordedItemId { - fn from(v: StructId) -> Self { - RecordedItemId::Struct(v) +impl From> for RecordedItemId { + fn from(v: AdtId) -> Self { + RecordedItemId::Adt(v) } } impl From> for RecordedItemId { @@ -328,16 +334,23 @@ impl From> for RecordedItemId { } } +/// Utility for implementing Ord for RecordedItemId. +#[derive(PartialEq, Eq, PartialOrd, Ord)] +enum OrderedItemId<'a, DefId, AdtId> { + DefId(&'a DefId), + AdtId(&'a AdtId), +} + impl RecordedItemId { /// Extract internal identifier. Allows for absolute ordering matching the /// order in which chalk saw things (and thus reproducing that order in /// printed programs) - fn def_id(&self) -> &I::DefId { + fn ordered_item_id(&self) -> OrderedItemId<'_, I::DefId, I::InternedAdtId> { match self { RecordedItemId::Trait(TraitId(x)) - | RecordedItemId::Struct(StructId(x)) | RecordedItemId::Impl(ImplId(x)) - | RecordedItemId::OpaqueTy(OpaqueTyId(x)) => x, + | RecordedItemId::OpaqueTy(OpaqueTyId(x)) => OrderedItemId::DefId(x), + RecordedItemId::Adt(AdtId(x)) => OrderedItemId::AdtId(x), } } } @@ -347,8 +360,9 @@ impl PartialOrd for RecordedItemId { Some(self.cmp(other)) } } + impl Ord for RecordedItemId { fn cmp(&self, other: &Self) -> Ordering { - self.def_id().cmp(other.def_id()) + self.ordered_item_id().cmp(&other.ordered_item_id()) } } diff --git a/tests/display/formatting.rs b/tests/display/formatting.rs index 2653f354c5c..ee9a4477599 100644 --- a/tests/display/formatting.rs +++ b/tests/display/formatting.rs @@ -103,18 +103,22 @@ trait [a-zA-Z0-9_-]+ \{ #[test] fn test_name_disambiguation() { - // we can't actually test different structs or traits with the same name in - // Chalk - but luckily our implementation ignores types for name - // disambiguation, so we can test it indirectly by using a struct and trait - // of the same name + // we don't have modules in chalk so we can't actually test different + // structs or traits with the same name in Chalk - but luckily our + // implementation ignores types for name disambiguation, so we can test it + // indirectly by using a opaque type and trait of the same name. reparse_into_different_test( " - struct Baz {} + struct Foo {} trait Baz {} + impl Baz for Foo {} + opaque type Baz: Baz = Foo; ", " - struct Baz {} - trait Baz_1 {} + struct Foo {} + trait Baz {} + impl Baz for Foo {} + opaque type Baz_1: Baz = Foo; ", ); } diff --git a/tests/display/mod.rs b/tests/display/mod.rs index bd362a216ca..166613e2b00 100644 --- a/tests/display/mod.rs +++ b/tests/display/mod.rs @@ -18,7 +18,7 @@ mod where_clauses; fn write_program(program: &Program) -> String { let mut out = String::new(); let ws = &WriterState::new(program); - for datum in program.struct_data.values() { + for datum in program.adt_data.values() { display::write_top_level(&mut out, ws, &**datum).unwrap(); } for datum in program.trait_data.values() { diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index c206a34ee70..997d4f87163 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -221,6 +221,18 @@ impl RustIrDatabase for MockDatabase { ) -> Substitution { unimplemented!() } + fn trait_name(&self, trait_id: TraitId) -> String { + unimplemented!() + } + fn adt_name(&self, struct_id: AdtId) -> String { + unimplemented!() + } + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { + unimplemented!() + } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { + unimplemented!() + } } fn prepare_goal() -> UCanonical>> { From 62b40fd3d59c91e39d6cba5c1af9297edbc8c7f3 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 23 May 2020 17:27:06 -0700 Subject: [PATCH 32/70] Add smart pointer support to LoggingDatabase Allows wrapping a RustIrDatabase that is behind a smart pointer such as Arc. Co-authored-by: David Ross --- chalk-solve/src/logging_db.rs | 146 +++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 63 deletions(-) diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index f332327de18..8c3fbfb0ae7 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -1,55 +1,66 @@ //! Provides wrappers over `RustIrDatabase` which record used definitions and write //! `.chalk` files containing those definitions. use std::{ - cmp::Ord, cmp::Ordering, collections::BTreeSet, fmt, fmt::Display, io::Write, sync::Arc, - sync::Mutex, + borrow::Borrow, cmp::Ord, cmp::Ordering, collections::BTreeSet, fmt, fmt::Display, io::Write, + marker::PhantomData, sync::Arc, sync::Mutex, }; use crate::rust_ir::*; use chalk_ir::{interner::Interner, *}; use crate::{display, RustIrDatabase}; -/// Wraps another `RustIrDatabase` (`DB`) and records which definitions are used. +use fmt::Debug; +/// Wraps another `RustIrDatabase` (`DB`) and records which definitions are +/// used. /// /// A full .chalk file containing all used definitions can be recovered through /// `LoggingRustIrDatabase`'s `Display` implementation. +/// +/// Uses a separate type, `P`, for the database stored inside to account for +/// `Arc` or wrapping other storage mediums. #[derive(Debug)] -pub struct LoggingRustIrDatabase +pub struct LoggingRustIrDatabase where DB: RustIrDatabase, + P: Borrow, I: Interner, { - db: DB, + db: P, def_ids: Mutex>>, + _phantom: PhantomData, } -impl LoggingRustIrDatabase +impl LoggingRustIrDatabase where DB: RustIrDatabase, + P: Borrow, I: Interner, { - pub fn new(db: DB) -> Self { + pub fn new(db: P) -> Self { LoggingRustIrDatabase { db, def_ids: Default::default(), + _phantom: PhantomData, } } } -impl Display for LoggingRustIrDatabase +impl Display for LoggingRustIrDatabase where DB: RustIrDatabase, + P: Borrow, I: Interner, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let def_ids = self.def_ids.lock().unwrap(); - display::write_program(f, &self.db, def_ids.iter().copied()) + display::write_program(f, self.db.borrow(), def_ids.iter().copied()) } } -impl LoggingRustIrDatabase +impl LoggingRustIrDatabase where DB: RustIrDatabase, + P: Borrow, I: Interner, { fn record(&self, id: impl Into>) { @@ -67,45 +78,46 @@ where } } -impl RustIrDatabase for LoggingRustIrDatabase +impl RustIrDatabase for LoggingRustIrDatabase where DB: RustIrDatabase, + P: Borrow + Debug, I: Interner, { fn custom_clauses(&self) -> Vec> { - self.db.custom_clauses() + self.db.borrow().custom_clauses() } fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, ) -> Arc> { - let ty_datum = self.db.associated_ty_data(ty); + let ty_datum = self.db.borrow().associated_ty_data(ty); self.record(ty_datum.trait_id); ty_datum } fn trait_datum(&self, trait_id: TraitId) -> Arc> { self.record(trait_id); - self.db.trait_datum(trait_id) + self.db.borrow().trait_datum(trait_id) } fn adt_datum(&self, adt_id: AdtId) -> Arc> { self.record(adt_id); - self.db.adt_datum(adt_id) + self.db.borrow().adt_datum(adt_id) } fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.record(impl_id); - self.db.impl_datum(impl_id) + self.db.borrow().impl_datum(impl_id) } fn associated_ty_value( &self, id: crate::rust_ir::AssociatedTyValueId, ) -> Arc> { - let value = self.db.associated_ty_value(id); + let value = self.db.borrow().associated_ty_value(id); self.record(value.impl_id); value } fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { self.record(id); - self.db.opaque_ty_data(id) + self.db.borrow().opaque_ty_data(id) } fn impls_for_trait( &self, @@ -113,24 +125,24 @@ where parameters: &[chalk_ir::GenericArg], ) -> Vec> { self.record(trait_id); - let impl_ids = self.db.impls_for_trait(trait_id, parameters); + let impl_ids = self.db.borrow().impls_for_trait(trait_id, parameters); self.record_all(impl_ids.iter().copied()); impl_ids } fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { self.record(trait_id); - self.db.local_impls_to_coherence_check(trait_id) + self.db.borrow().local_impls_to_coherence_check(trait_id) } fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { self.record(auto_trait_id); self.record(adt_id); - self.db.impl_provided_for(auto_trait_id, adt_id) + self.db.borrow().impl_provided_for(auto_trait_id, adt_id) } fn well_known_trait_id( &self, well_known_trait: crate::rust_ir::WellKnownTrait, ) -> Option> { - let trait_id = self.db.well_known_trait_id(well_known_trait); + let trait_id = self.db.borrow().well_known_trait_id(well_known_trait); trait_id.map(|id| self.record(id)); trait_id } @@ -138,29 +150,29 @@ where &self, environment: &chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { - self.db.program_clauses_for_env(environment) + self.db.borrow().program_clauses_for_env(environment) } fn interner(&self) -> &I { - self.db.interner() + self.db.borrow().interner() } fn trait_name(&self, trait_id: TraitId) -> String { - self.db.trait_name(trait_id) + self.db.borrow().trait_name(trait_id) } fn adt_name(&self, adt_id: AdtId) -> String { - self.db.adt_name(adt_id) + self.db.borrow().adt_name(adt_id) } fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { - self.db.assoc_type_name(assoc_ty_id) + self.db.borrow().assoc_type_name(assoc_ty_id) } fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { - self.db.opaque_type_name(opaque_ty_id) + self.db.borrow().opaque_type_name(opaque_ty_id) } fn is_object_safe(&self, trait_id: TraitId) -> bool { self.record(trait_id); - self.db.is_object_safe(trait_id) + self.db.borrow().is_object_safe(trait_id) } fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { - todo!() + todo!("function def datum") } } @@ -168,21 +180,26 @@ where /// definition to the given file. /// /// Uses [`LoggingRustIrDatabase`] internally. -pub struct WriteOnDropRustIrDatabase +/// +/// Uses a separate type, `P`, for the database stored inside to account for +/// `Arc` or wrapping other storage mediums. +pub struct WriteOnDropRustIrDatabase where - DB: RustIrDatabase, I: Interner, W: Write, + DB: RustIrDatabase, + P: Borrow, { - db: LoggingRustIrDatabase, + db: LoggingRustIrDatabase, write: W, } -impl fmt::Debug for WriteOnDropRustIrDatabase +impl fmt::Debug for WriteOnDropRustIrDatabase where I: Interner, - DB: RustIrDatabase + fmt::Debug, W: Write, + DB: RustIrDatabase, + P: Borrow + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WriteOnDropRustIrDatabase") @@ -192,29 +209,31 @@ where } } -impl WriteOnDropRustIrDatabase +impl WriteOnDropRustIrDatabase where - DB: RustIrDatabase, I: Interner, W: Write, + DB: RustIrDatabase, + P: Borrow, { - pub fn new(db: DB, write: W) -> Self { + pub fn new(db: P, write: W) -> Self { WriteOnDropRustIrDatabase { db: LoggingRustIrDatabase::new(db), write, } } - pub fn from_logging_db(db: LoggingRustIrDatabase, write: W) -> Self { + pub fn from_logging_db(db: LoggingRustIrDatabase, write: W) -> Self { WriteOnDropRustIrDatabase { db, write } } } -impl Drop for WriteOnDropRustIrDatabase +impl Drop for WriteOnDropRustIrDatabase where - DB: RustIrDatabase, I: Interner, W: Write, + DB: RustIrDatabase, + P: Borrow, { fn drop(&mut self) { write!(self.write, "{}", self.db) @@ -223,84 +242,85 @@ where } } -impl RustIrDatabase for WriteOnDropRustIrDatabase +impl RustIrDatabase for WriteOnDropRustIrDatabase where - DB: RustIrDatabase, - W: Write, I: Interner, + W: Write, + DB: RustIrDatabase, + P: Borrow + Debug, { fn custom_clauses(&self) -> Vec> { - self.db.custom_clauses() + self.db.borrow().custom_clauses() } fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, ) -> Arc> { - self.db.associated_ty_data(ty) + self.db.borrow().associated_ty_data(ty) } fn trait_datum(&self, trait_id: TraitId) -> Arc> { - self.db.trait_datum(trait_id) + self.db.borrow().trait_datum(trait_id) } fn adt_datum(&self, adt_id: AdtId) -> Arc> { - self.db.adt_datum(adt_id) + self.db.borrow().adt_datum(adt_id) } fn impl_datum(&self, impl_id: ImplId) -> Arc> { - self.db.impl_datum(impl_id) + self.db.borrow().impl_datum(impl_id) } fn associated_ty_value( &self, id: crate::rust_ir::AssociatedTyValueId, ) -> Arc> { - self.db.associated_ty_value(id) + self.db.borrow().associated_ty_value(id) } fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { - self.db.opaque_ty_data(id) + self.db.borrow().opaque_ty_data(id) } fn impls_for_trait( &self, trait_id: TraitId, parameters: &[chalk_ir::GenericArg], ) -> Vec> { - self.db.impls_for_trait(trait_id, parameters) + self.db.borrow().impls_for_trait(trait_id, parameters) } fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { - self.db.local_impls_to_coherence_check(trait_id) + self.db.borrow().local_impls_to_coherence_check(trait_id) } fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { - self.db.impl_provided_for(auto_trait_id, adt_id) + self.db.borrow().impl_provided_for(auto_trait_id, adt_id) } fn well_known_trait_id( &self, well_known_trait: crate::rust_ir::WellKnownTrait, ) -> Option> { - self.db.well_known_trait_id(well_known_trait) + self.db.borrow().well_known_trait_id(well_known_trait) } fn program_clauses_for_env( &self, environment: &chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { - self.db.program_clauses_for_env(environment) + self.db.borrow().program_clauses_for_env(environment) } fn interner(&self) -> &I { - self.db.interner() + self.db.borrow().interner() } fn is_object_safe(&self, trait_id: TraitId) -> bool { - self.db.is_object_safe(trait_id) + self.db.borrow().is_object_safe(trait_id) } fn trait_name(&self, trait_id: TraitId) -> String { - self.db.trait_name(trait_id) + self.db.borrow().trait_name(trait_id) } fn adt_name(&self, adt_id: AdtId) -> String { - self.db.adt_name(adt_id) + self.db.borrow().adt_name(adt_id) } fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { - self.db.assoc_type_name(assoc_ty_id) + self.db.borrow().assoc_type_name(assoc_ty_id) } fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { - self.db.opaque_type_name(opaque_ty_id) + self.db.borrow().opaque_type_name(opaque_ty_id) } fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { - todo!() + todo!("function def datum") } } From 043968353731ad41ff1fc748df6b9fe51c2288f6 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 23 May 2020 17:30:58 -0700 Subject: [PATCH 33/70] Add test harness for LoggingRustIrDatabase The harness takes in a program and a goal using the same macro interface as the existing tests. It will then solve the goal, output the logged program, and then re-solve the goal to ensure all the information needed to solve the goal was captured. Includes a test we used to verify the harness worked. This also changes a few things in the existing test harness, mainly splitting `test!()` into two macros - the other being `parse_test_data!()`, so we can reuse the parsing without calling solve_goal afterwards. Co-authored-by: David Ross --- tests/lib.rs | 1 + tests/logging_db/mod.rs | 21 ++++++++ tests/logging_db/util.rs | 106 +++++++++++++++++++++++++++++++++++++++ tests/test/mod.rs | 36 +++++++------ 4 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 tests/logging_db/mod.rs create mode 100644 tests/logging_db/util.rs diff --git a/tests/lib.rs b/tests/lib.rs index d7e2fdbb7d9..98e1659c38e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -4,6 +4,7 @@ mod test_util; mod test; mod display; +mod logging_db; mod lowering; mod integration; diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs new file mode 100644 index 00000000000..716864d5339 --- /dev/null +++ b/tests/logging_db/mod.rs @@ -0,0 +1,21 @@ +#[macro_use] +mod util; + +#[test] +fn records_struct_and_trait() { + logging_db_output_sufficient! { + program { + struct S {} + + trait Trait {} + + impl Trait for S {} + } + + goal { + S: Trait + } yields { + "Unique" + } + } +} diff --git a/tests/logging_db/util.rs b/tests/logging_db/util.rs new file mode 100644 index 00000000000..8be22edf4c3 --- /dev/null +++ b/tests/logging_db/util.rs @@ -0,0 +1,106 @@ +//! Macros / utilities for logging_db tests. +//! +//! This is not a submodule of `test_util` as it depends on macros declared in +//! `test/mod.rs`, and `test_util.rs` is compiled both with and without access +//! to `test/`. We can't compile without access to `test/`, so we can't be under +//! of `test_util.rs`. +use chalk_integration::{ + db::ChalkDatabase, interner::ChalkIr, lowering::LowerGoal, program::Program, + query::LoweringDatabase, +}; +use chalk_solve::ext::*; +use chalk_solve::RustIrDatabase; +use chalk_solve::{logging_db::LoggingRustIrDatabase, SolverChoice}; + +use crate::test::{assert_result, TestGoal}; + +macro_rules! logging_db_output_sufficient { + ($($arg:tt)*) => {{ + use chalk_solve::SolverChoice; + use crate::test::*; + let (program, goals) = parse_test_data!($($arg)*); + crate::logging_db::util::logging_db_output_sufficient(program, goals) + }}; +} + +pub fn logging_db_output_sufficient( + program_text: &str, + goals: Vec<(&str, SolverChoice, TestGoal)>, +) { + println!("program {}", program_text); + assert!(program_text.starts_with("{")); + assert!(program_text.ends_with("}")); + + let output_text = { + let db = ChalkDatabase::with( + &program_text[1..program_text.len() - 1], + SolverChoice::default(), + ); + + let program = db.checked_program().unwrap(); + let wrapped = LoggingRustIrDatabase::<_, Program, _>::new(program.clone()); + for (goal_text, solver_choice, expected) in goals.clone() { + let mut solver = solver_choice.into_solver(); + + chalk_integration::tls::set_current_program(&program, || { + println!("----------------------------------------------------------------------"); + println!("---- first run on original test code ---------------------------------"); + println!("goal {}", goal_text); + assert!(goal_text.starts_with("{")); + assert!(goal_text.ends_with("}")); + let goal = chalk_parse::parse_goal(&goal_text[1..goal_text.len() - 1]) + .unwrap() + .lower(&*program) + .unwrap(); + + println!("using solver: {:?}", solver_choice); + let peeled_goal = goal.into_peeled_goal(db.interner()); + match expected { + TestGoal::Aggregated(expected) => { + let result = solver.solve(&wrapped, &peeled_goal); + assert_result(result, expected); + } + _ => panic!("only aggregated test goals supported for logger goals"), + } + }); + } + + wrapped.to_string() + }; + + println!("----------------------------------------------------------------------"); + println!("logging db output program:\n{}\n", output_text); + + let db = ChalkDatabase::with(&output_text, SolverChoice::default()); + + let new_program = match db.checked_program() { + Ok(v) => v, + Err(e) => panic!("Error checking recreated chalk program: {}", e), + }; + + for (goal_text, solver_choice, expected) in goals { + let mut solver = solver_choice.into_solver::(); + + chalk_integration::tls::set_current_program(&new_program, || { + println!("----------------------------------------------------------------------"); + println!("---- second run on code output by logger -----------------------------"); + println!("goal {}", goal_text); + assert!(goal_text.starts_with("{")); + assert!(goal_text.ends_with("}")); + let goal = chalk_parse::parse_goal(&goal_text[1..goal_text.len() - 1]) + .unwrap() + .lower(&*new_program) + .unwrap(); + + println!("using solver: {:?}", solver_choice); + let peeled_goal = goal.into_peeled_goal(db.interner()); + match expected { + TestGoal::Aggregated(expected) => { + let result = solver.solve(&db, &peeled_goal); + assert_result(result, expected); + } + _ => panic!("only aggregated test goals supported for logger goals"), + } + }); + } +} diff --git a/tests/test/mod.rs b/tests/test/mod.rs index eaf721f2f3a..848a3365230 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -16,7 +16,7 @@ mod bench; mod coherence; mod wf_lowering; -fn assert_result(mut result: Option>, expected: &str) { +pub fn assert_result(mut result: Option>, expected: &str) { // sort constraints, since the different solvers may output them in different order match &mut result { Some(Solution::Unique(solution)) => { @@ -36,7 +36,8 @@ fn assert_result(mut result: Option>, expected: &str) { } // different goals -enum TestGoal { +#[derive(Clone)] +pub enum TestGoal { // solver should produce same aggregated single solution Aggregated(&'static str), // solver should produce exactly multiple solutions @@ -46,14 +47,21 @@ enum TestGoal { } macro_rules! test { + (program $program:tt $($goals:tt)*) => {{ + let (program, goals) = parse_test_data!(program $program $($goals)*); + solve_goal(program, goals) + }}; +} + +macro_rules! parse_test_data { (program $program:tt $($goals:tt)*) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[] @unparsed_goals[$($goals)*]) }; (@program[$program:tt] @parsed_goals[$($parsed_goals:tt)*] @unparsed_goals[]) => { - solve_goal(stringify!($program), vec![$($parsed_goals),*]) + (stringify!($program), vec![$($parsed_goals),*]) }; // goal { G } yields { "Y" } -- test both solvers behave the same (the default) @@ -61,7 +69,7 @@ macro_rules! test { goal $goal:tt yields { $expected:expr } $($unparsed_goals:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), SolverChoice::slg_default(), TestGoal::Aggregated($expected)) @@ -77,7 +85,7 @@ macro_rules! test { goal $goal:tt yields_all { $($expected:expr),* } $($unparsed_goals:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), SolverChoice::slg_default(), TestGoal::All(vec![$($expected),*])) @@ -91,7 +99,7 @@ macro_rules! test { goal $goal:tt yields_first { $($expected:expr),* } $($unparsed_goals:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), SolverChoice::default(), TestGoal::First(vec![$($expected),*])) @@ -110,7 +118,7 @@ macro_rules! test { goal $goal:tt yields[$C:expr] { $expected:expr } goal $($unparsed_goals:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::Aggregated($expected)) @@ -124,7 +132,7 @@ macro_rules! test { yields[$C:expr] { $expected:expr } yields $($unparsed_tail:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::Aggregated($expected)) @@ -136,7 +144,7 @@ macro_rules! test { (@program[$program:tt] @parsed_goals[$($parsed_goals:tt)*] @unparsed_goals[ goal $goal:tt yields[$C:expr] { $expected:expr } ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::Aggregated($expected)) @@ -155,7 +163,7 @@ macro_rules! test { goal $goal:tt yields_all[$C:expr] { $($expected:expr),* } goal $($unparsed_goals:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::All(vec![$($expected),*])) @@ -167,7 +175,7 @@ macro_rules! test { (@program[$program:tt] @parsed_goals[$($parsed_goals:tt)*] @unparsed_goals[ goal $goal:tt yields_all[$C:expr] { $($expected:expr),* } ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::All(vec![$($expected),*])) @@ -186,7 +194,7 @@ macro_rules! test { goal $goal:tt yields_first[$C:expr] { $($expected:expr),* } goal $($unparsed_goals:tt)* ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::First(vec![$($expected),*])) @@ -198,7 +206,7 @@ macro_rules! test { (@program[$program:tt] @parsed_goals[$($parsed_goals:tt)*] @unparsed_goals[ goal $goal:tt yields_first[$C:expr] { $($expected:expr),* } ]) => { - test!(@program[$program] + parse_test_data!(@program[$program] @parsed_goals[ $($parsed_goals)* (stringify!($goal), $C, TestGoal::First(vec![$($expected),*])) From e757cc63247b073292b877323042e6ac9b72a75a Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 23 May 2020 18:56:25 -0700 Subject: [PATCH 34/70] Implement RenderAsRust for Const types Added rendering for basic const generics. No code was added for const values, as there are some open design questions documented here: https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/.2Echalk.20file.20writer/near/198560888 Co-authored-by: David Ross --- chalk-solve/src/display.rs | 43 +++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 541edc4a4ba..d3f38fb5374 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -240,6 +240,13 @@ impl RenderAsRust for Lifetime { self.data(s.db.interner()).fmt(s, f) } } + +impl RenderAsRust for Const { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + self.data(s.db.interner()).fmt(s, f) + } +} + impl RenderAsRust for GenericArg { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // delegate to ParameterData @@ -351,11 +358,13 @@ impl RenderAsRust for TyData { Ok(()) } TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), + TyData::InferenceVar(_) => write!(f, "_"), TyData::Alias(alias_ty) => alias_ty.fmt(s, f), TyData::Apply(apply_ty) => apply_ty.fmt(s, f), TyData::Function(func) => func.fmt(s, f), - TyData::Placeholder(_) => unreachable!("cannot print placeholder variables"), - _ => unimplemented!(), + TyData::Placeholder(_) => unreachable!( + "cannot print placeholder variables; these should only be in goals not programs" + ), } } } @@ -568,7 +577,9 @@ impl RenderAsRust for LifetimeData { match self { LifetimeData::BoundVar(v) => write!(f, "'{}", s.display_bound_var(v)), LifetimeData::InferenceVar(_) => write!(f, "'_"), - LifetimeData::Placeholder(_) => unreachable!("cannot print placeholder variables"), + LifetimeData::Placeholder(_) => unreachable!( + "cannot print placeholder variables; these should only be in goals not programs" + ), // Matching the void ensures at compile time that this code is // unreachable LifetimeData::Phantom(void, _) => match *void {}, @@ -576,12 +587,32 @@ impl RenderAsRust for LifetimeData { } } +impl RenderAsRust for ConstData { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + write!(f, "{}", self.value.display(s)) + } +} + +impl RenderAsRust for ConstValue { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + match self { + ConstValue::BoundVar(v) => write!(f, "{}", s.display_bound_var(v)), + ConstValue::InferenceVar(_) => write!(f, "_"), + ConstValue::Placeholder(_) => unreachable!( + "cannot print placeholder variables; these should only be in goals not programs" + ), + ConstValue::Concrete(value) => unimplemented!("const values"), + } + } +} + impl RenderAsRust for GenericArgData { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { match self { GenericArgData::Ty(ty) => write!(f, "{}", ty.display(s)), GenericArgData::Lifetime(lt) => write!(f, "{}", lt.display(s)), - GenericArgData::Const(_) => todo!(""), + GenericArgData::Const(const_ty) => write!(f, "{}", const_ty.display(s)), } } } @@ -1120,7 +1151,9 @@ impl<'a, I: Interner> WriterState<'a, I> { .map(move |(parameter, var)| match parameter { VariableKind::Ty => format!("{}", self.apply_mappings(var)), VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), - VariableKind::Const(_) => todo!("const variable kinds"), + VariableKind::Const(ty) => { + format!("const {}: {}", self.apply_mappings(var), ty.display(self)) + } }) } } From 249d49a61966c09e3a2dd9c3e706531f03d90b24 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 23 May 2020 21:29:07 -0700 Subject: [PATCH 35/70] Add logging db tests Added tests for: * Generic implementations - Succeeds * Trait where bounds - Fails because the impl code that is queried to solve the goal assumes the bounds on the trait are met. This means any traits used in the bounds are not queried, and don't show in the output. In the example, the Parent trait is missing * opaque types - Fails because checking opaque type bound does not check the underlying struct. * Associated type bounds - Fails because the trait's associated type bounds are not accessed when checking if a struct implements the traits. Co-authored-by: David Ross --- tests/logging_db/mod.rs | 188 +++++++++++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 21 deletions(-) diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 716864d5339..885cb9ff30f 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -1,21 +1,167 @@ -#[macro_use] -mod util; - -#[test] -fn records_struct_and_trait() { - logging_db_output_sufficient! { - program { - struct S {} - - trait Trait {} - - impl Trait for S {} - } - - goal { - S: Trait - } yields { - "Unique" - } - } -} +#[macro_use] +mod util; + +#[test] +fn records_struct_and_trait() { + logging_db_output_sufficient! { + program { + struct S {} + + trait Trait {} + + impl Trait for S {} + } + + goal { + S: Trait + } yields { + "Unique" + } + } +} + +#[test] +#[ignore] +fn records_opaque_type() { + logging_db_output_sufficient! { + program { + struct S {} + + trait Trait {} + impl Trait for S {} + + opaque type Foo: Trait = S; + } + + goal { + Foo: Trait + } yields { + "Unique" + } + } +} + +#[test] +#[ignore] +fn records_parents_parent() { + logging_db_output_sufficient! { + program { + struct S {} + + trait Grandparent {} + trait Parent where Self: Grandparent {} + trait Child where Self: Parent {} + impl Grandparent for S {} + impl Parent for S {} + impl Child for S {} + } + + goal { + S: Child + } yields { + "Unique" + } + } +} + +#[test] +#[ignore] +fn records_associated_type_bounds() { + logging_db_output_sufficient! { + program { + trait Foo { + type Assoc: Bar; + } + trait Bar { + + } + + struct S {} + impl Foo for S { + type Assoc = S; + } + impl Bar for S {} + } + + goal { + S: Foo + } yields { + "Unique" + } + } +} + +#[test] +fn records_generic_impls() { + logging_db_output_sufficient! { + program { + struct S {} + struct V {} + + trait Foo {} + trait Bar {} + + impl Foo for S {} + + impl Bar for T where T: Foo { + + } + } + + goal { + S: Bar + } yields { + "Unique" + } + } + + logging_db_output_sufficient! { + program { + struct S {} + struct V {} + + trait Foo {} + trait Bar {} + + impl Foo for S {} + + impl Bar for T where T: Foo { + + } + } + + goal { + V: Bar + } yields { + "No possible solution" + } + } +} + +#[test] +#[ignore] +fn records_necessary_separate_impl() { + // might leave out the "impl Bar for Fox" + logging_db_output_sufficient! { + program { + trait Box { + type Assoc: Bar; + } + trait Bar {} + + struct Foo {} + impl Box for Foo { + type Assoc = Fox; + } + + struct Fox {} + impl Bar for Fox {} + } + + goal { + Foo: Box + } yields { + "Unique" + } + } +} From 4f8763901fb2c63753f7aaf5286659e6c2b5b576 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 23 May 2020 22:54:10 -0700 Subject: [PATCH 36/70] Add reparse test macro Brings the .chalk file writer tests in line with the existing chalk tests which use macros for programs. This makes the test programs easier to read as they have syntax highlighting in IDEs, and are no longer surrounded by quotes. Co-authored-by: David Ross --- tests/display/assoc_ty.rs | 332 ++++++++++++++++----------------- tests/display/built_ins.rs | 107 +++++------ tests/display/dyn_.rs | 127 +++++++------ tests/display/formatting.rs | 110 +++++------ tests/display/impl_.rs | 77 ++++---- tests/display/lifetimes.rs | 13 +- tests/display/mod.rs | 156 +--------------- tests/display/opaque_ty.rs | 49 +++-- tests/display/self_.rs | 122 ++++++------ tests/display/struct_.rs | 42 +++-- tests/display/trait_.rs | 24 ++- tests/display/util.rs | 234 +++++++++++++++++++++++ tests/display/where_clauses.rs | 92 ++++----- 13 files changed, 794 insertions(+), 691 deletions(-) create mode 100644 tests/display/util.rs diff --git a/tests/display/assoc_ty.rs b/tests/display/assoc_ty.rs index bccd9585652..34b0d585496 100644 --- a/tests/display/assoc_ty.rs +++ b/tests/display/assoc_ty.rs @@ -1,175 +1,174 @@ -use super::*; #[test] fn test_trait_impl_associated_type() { - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bar { - type Assoc; - } - impl Bar for Foo { - type Assoc = Floo; + reparse_test!( + program { + struct Foo { } + struct Floo { } + trait Bar { + type Assoc; + } + impl Bar for Foo { + type Assoc = Floo; + } } - ", ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bax { - type Assoc1; - type Assoc2; - } - impl Bax for Foo { - type Assoc1 = Floo; - type Assoc2 = Foo; + reparse_test!( + program { + struct Foo { } + struct Floo { } + trait Bax { + type Assoc1; + type Assoc2; + } + impl Bax for Foo { + type Assoc1 = Floo; + type Assoc2 = Foo; + } } - ", ); } #[test] fn test_trait_impl_associated_type_with_generics() { - reparse_test( - " - struct Foo { } - struct Floo { } - trait Baz { - type Assoc; - } - impl Baz for Foo { - type Assoc = Floo; + reparse_test!( + program { + struct Foo { } + struct Floo { } + trait Baz { + type Assoc; + } + impl Baz for Foo { + type Assoc = Floo; + } } - ", ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bur { - type Assoc; - } - impl Bur for Foo { - type Assoc = Floo; + reparse_test!( + program { + struct Foo { } + struct Floo { } + trait Bur { + type Assoc; + } + impl Bur for Foo { + type Assoc = Floo; + } } - ", ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bun { - type Assoc; - } - impl Bun for Foo { - type Assoc = Floo; + reparse_test!( + program { + struct Foo { } + struct Floo { } + trait Bun { + type Assoc; + } + impl Bun for Foo { + type Assoc = Floo; + } } - ", ); - reparse_test( - " - struct Foo { } - struct Floo { } - trait Bun { - type Assoc1; - type Assoc2; - type Assoc3; - } - impl Bun for Foo { - type Assoc1 = Floo; - type Assoc2 = Floo; - type Assoc3 = Floo, Floo>; + reparse_test!( + program { + struct Foo { } + struct Floo { } + trait Bun { + type Assoc1; + type Assoc2; + type Assoc3; + } + impl Bun for Foo { + type Assoc1 = Floo; + type Assoc2 = Floo; + type Assoc3 = Floo, Floo>; + } } - ", ); } #[test] fn test_simple_assoc_type() { - reparse_test( - " + reparse_test!( + program { trait Foo { type Assoc; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Byz {} trait Buzz {} trait Foo { type Assoc: Byz + Buzz; } - ", + } ); } #[test] fn test_simple_generic_assoc_type() { - reparse_test( - " + reparse_test!( + program { trait Trait {} trait Foo { type Assoc; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Trait {} trait Foo { type Assoc: Trait; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Trait {} trait Foo { type Assoc where Y: Trait; } - ", + } ); } #[test] fn test_assoc_type_in_generic_trait() { - reparse_test( - " + reparse_test!( + program { trait Foo { type Assoc; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Fou { type Assoc; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Bax {} trait Foo { type Assoc where T: Bax; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Bix {} trait Foo { type Assoc where Y: Bix; } - ", + } ); } #[test] fn test_impl_assoc_ty() { - reparse_test( - " + reparse_test!( + program { struct Fuu {} trait Bhz { type Assoc; @@ -177,10 +176,10 @@ fn test_impl_assoc_ty() { impl Bhz for Fuu { type Assoc = Fuu; } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Fou {} trait Bax { type Assoc; @@ -188,10 +187,10 @@ fn test_impl_assoc_ty() { impl Bax for Fou { type Assoc = Fou; } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Fuu {} trait Bmx { type Assoc; @@ -199,10 +198,10 @@ fn test_impl_assoc_ty() { impl Bmx for Fuu { type Assoc = T; } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Fuu {} struct Guu {} trait Bmx { @@ -211,10 +210,10 @@ fn test_impl_assoc_ty() { impl Bmx for Fuu { type Assoc = Guu; } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Fuu {} struct Guu {} trait Bmx { @@ -223,30 +222,30 @@ fn test_impl_assoc_ty() { impl Bmx for Fuu { type Assoc = Guu; } - ", + } ); } #[test] fn test_impl_assoc_ty_alias() { - reparse_test( - " - struct Fow {} - struct Qac {} - trait Bow {} - trait Baq { - type Assoc: Boo; - } - trait Boo { - type Item; - } - impl Boo for Qac { - type Item = Fow; - } - impl Baq for Fow { - type Assoc = Qac; + reparse_test!( + program { + struct Fow {} + struct Qac {} + trait Bow {} + trait Baq { + type Assoc: Boo; + } + trait Boo { + type Item; + } + impl Boo for Qac { + type Item = Fow; + } + impl Baq for Fow { + type Assoc = Qac; + } } - ", ); } @@ -254,51 +253,52 @@ fn test_impl_assoc_ty_alias() { fn test_assoc_ty_alias_bound() { // Foo: Bax is lowered into two bounds, Bax and Bax, and // the formatter does not coalesce those bounds. - reparse_into_different_test( - " - struct Foo { } - trait Bax { type BaxT; } - trait Test { - type Assoc - where - Foo: Bax; + reparse_test!( + program { + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax; + } } - ", - " - struct Foo { } - trait Bax { type BaxT; } - trait Test { - type Assoc - where - Foo: Bax, - Foo: Bax; + produces { + struct Foo { } + trait Bax { type BaxT; } + trait Test { + type Assoc + where + Foo: Bax, + Foo: Bax; + } } - ", ); - reparse_into_different_test( - " - struct Foo where T: Baux { } - trait Baux { type Assoc; } - ", - " - struct Foo where T: Baux, T: Baux { } - trait Baux { type Assoc; } - ", + reparse_test!( + program { + struct Foo where T: Baux { } + trait Baux { type Assoc; } + } + produces { + struct Foo where T: Baux, T: Baux { } + trait Baux { type Assoc; } + } ); - reparse_into_different_test( - " - struct Foo {} - trait Boz { type Assoc; } - impl Boz for Foo where T: Boz> { - type Assoc = Foo; + reparse_test!( + program { + struct Foo {} + trait Boz { type Assoc; } + impl Boz for Foo where T: Boz> { + type Assoc = Foo; + } } - ", - " - struct Foo {} - trait Boz { type Assoc; } - impl Boz for Foo where T: Boz>, T: Boz { - type Assoc = Foo; + produces + { + struct Foo {} + trait Boz { type Assoc; } + impl Boz for Foo where T: Boz>, T: Boz { + type Assoc = Foo; + } } - ", ); } diff --git a/tests/display/built_ins.rs b/tests/display/built_ins.rs index 0ee55492dd8..f69eec8d93f 100644 --- a/tests/display/built_ins.rs +++ b/tests/display/built_ins.rs @@ -1,19 +1,19 @@ use super::*; #[test] fn test_function_type() { - reparse_test( - " + reparse_test!( + program { struct Foo { } trait Baz { } impl Baz for Foo { } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo { } trait Baz { } impl Baz for fn(Foo) { } - ", + } ); } @@ -50,27 +50,28 @@ fn test_scalar_types() { #[test] fn test_slice_types() { - reparse_test( - " - struct Foo { - field: [T] - } - trait Bar { - type Baz; - } - impl Bar for Foo { - type Baz = [T]; + reparse_test!( + program { + struct Foo { + field: [T] + } + trait Bar { + type Baz; + } + impl Bar for Foo { + type Baz = [T]; + } + impl Bar for [T] { + type Baz = Foo; + } } - impl Bar for [T] { - type Baz = Foo; - }", ); } #[test] fn test_str_types() { - reparse_test( - " + reparse_test!( + program { struct Foo { field: str } @@ -83,14 +84,14 @@ fn test_str_types() { impl Bar for str { type Baz = str; } - ", + } ); } #[test] fn test_raw_ptr_types() { - reparse_test( - " + reparse_test!( + program { struct Foo { field: *const T } @@ -103,10 +104,10 @@ fn test_raw_ptr_types() { impl Bar for *const u32 { type Baz = *const u32; } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo { field: *mut T } @@ -119,10 +120,10 @@ fn test_raw_ptr_types() { impl Bar for *mut u32 { type Baz = *mut u32; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Bar { type Baz; } @@ -132,14 +133,14 @@ fn test_raw_ptr_types() { impl Bar for *const T { type Baz = *const T; } - ", + } ); } #[test] fn test_reference_types() { - reparse_test( - " + reparse_test!( + program { struct Foo<'a,T> { field: &'a T } @@ -152,10 +153,10 @@ fn test_reference_types() { impl<'a> Bar for &'a u32 { type Baz = &'a u32; } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo<'a,T> { field: &'a mut T } @@ -168,10 +169,10 @@ fn test_reference_types() { impl<'a> Bar for &'a mut u32 { type Baz = &'a u32; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Bar { type Baz; } @@ -181,30 +182,30 @@ fn test_reference_types() { impl<'a,T> Bar for &'a T { type Baz = &'a T; } - ", + } ); } #[test] fn test_tuples() { - reparse_test( - " + reparse_test!( + program { struct Fuu { fuu_field: () } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Uff { fuu_field: (Iff,), iff2_field: (Iff, Iff, Iff) } struct Iff { } - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo { field: (u32,*const T,T), field2: (T,), @@ -216,13 +217,13 @@ fn test_tuples() { impl Bar for Foo { type Baz = (T,Foo,u32); } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Blug {} impl Blug for (T1,T2) { - + } impl Blug for (T1,) { @@ -230,6 +231,6 @@ fn test_tuples() { impl Blug for () { } - ", + } ); } diff --git a/tests/display/dyn_.rs b/tests/display/dyn_.rs index b3db33184af..3b62b2abeac 100644 --- a/tests/display/dyn_.rs +++ b/tests/display/dyn_.rs @@ -1,80 +1,79 @@ -use super::*; #[test] fn test_forall_in_dyn() { - reparse_test( - " - trait Foo {} - trait Bar<'a> {} - impl Foo for dyn forall<'a> Bar<'a> {} - ", + reparse_test!( + program { + trait Foo {} + trait Bar<'a> {} + impl Foo for dyn forall<'a> Bar<'a> {} + } ); - reparse_test( - " - struct Foo { - field: dyn forall<'a> Baz<'a> - } - trait Baz<'a> {} - ", + reparse_test!( + program { + struct Foo { + field: dyn forall<'a> Baz<'a> + } + trait Baz<'a> {} + } ); - reparse_test( - " - trait Foo {} - trait Bax<'a, 'b> {} - impl Foo for dyn forall<'a, 'b> Bax<'a, 'b> {} - ", + reparse_test!( + program { + trait Foo {} + trait Bax<'a, 'b> {} + impl Foo for dyn forall<'a, 'b> Bax<'a, 'b> {} + } ); - reparse_test( - " - struct Foo { - field: dyn forall<'a, 'b> Bix<'a, 'b> - } - trait Bix<'a, 'b> {} - ", + reparse_test!( + program { + struct Foo { + field: dyn forall<'a, 'b> Bix<'a, 'b> + } + trait Bix<'a, 'b> {} + } ); - reparse_test( - " - struct Foo { - field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> - } - trait Bex<'a> {} - trait Byx<'a> {} - ", + reparse_test!( + program { + struct Foo { + field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> + } + trait Bex<'a> {} + trait Byx<'a> {} + } ); - reparse_test( - " - struct Foo { - field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> - } - trait Bux<'a, 'b> {} - trait Brx<'a, 'b> {} - ", + reparse_test!( + program { + struct Foo { + field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> + } + trait Bux<'a, 'b> {} + trait Brx<'a, 'b> {} + } ); - reparse_test( - " - struct Foo<'a> { - field: dyn forall<'b> Bpx<'a, 'b> - } - trait Bpx<'a, 'b> {} - ", + reparse_test!( + program { + struct Foo<'a> { + field: dyn forall<'b> Bpx<'a, 'b> + } + trait Bpx<'a, 'b> {} + } ); } #[test] fn test_simple_dyn() { - reparse_test( - " - struct Foo { - field: dyn Bax - } - trait Bax {} - ", + reparse_test!( + program { + struct Foo { + field: dyn Bax + } + trait Bax {} + } ); - reparse_test( - " - struct Foo<'a> { - field: dyn Bix<'a> - } - trait Bix<'a> {} - ", + reparse_test!( + program { + struct Foo<'a> { + field: dyn Bix<'a> + } + trait Bix<'a> {} + } ); } diff --git a/tests/display/formatting.rs b/tests/display/formatting.rs index ee9a4477599..cc2595a9e8e 100644 --- a/tests/display/formatting.rs +++ b/tests/display/formatting.rs @@ -1,63 +1,64 @@ -use super::*; - #[test] fn test_assoc_type_formatting() { - test_formatting( - " - struct Foo {} - trait Bar { - type Assoc; - } - impl Bar for Foo { - type Assoc = (); + reparse_test!( + program { + struct Foo {} + trait Bar { + type Assoc; + } + impl Bar for Foo { + type Assoc = (); + } } - ", - r#"struct [a-zA-Z0-9_-]+ \{\s*\} + formatting matches +r#"struct [a-zA-Z0-9_-]+ \{\s*\} trait [a-zA-Z0-9_-]+ \{ type [a-zA-Z0-9_-]+; \} impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ \{ type [a-zA-Z0-9_-]+ = \(\); -\}"#, +\}"# ); } #[test] fn test_struct_field_formatting() { - test_formatting( - " - struct Foo {} - struct Bar { - field1: Foo - } - struct Azg { - field1: Foo, - field2: Bar + reparse_test!( + program { + struct Foo {} + struct Bar { + field1: Foo + } + struct Azg { + field1: Foo, + field2: Bar + } } - ", - r#"struct [a-zA-Z0-9_-]+ \{\} + formatting matches +r#"struct [a-zA-Z0-9_-]+ \{\} struct [a-zA-Z0-9_-]+ \{ [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ \} struct [a-zA-Z0-9_-]+ \{ [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ -\}"#, +\}"# ); } #[test] fn test_where_clause_formatting() { - test_formatting( - " + reparse_test!( + program { struct Foo where Foo: Baz, Foo: Bar {} trait Bar where Foo: Baz, dyn Baz: Bar {} trait Baz {} impl Bar for Foo where Foo: Baz, (): Baz {} impl Baz for Foo {} impl Bar for dyn Baz {} - ", - r#"struct [a-zA-Z0-9_-]+ + } + formatting matches +r#"struct [a-zA-Z0-9_-]+ where [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ @@ -74,30 +75,31 @@ where \(\): [a-zA-Z0-9_-]+ \{\} impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ \{\} -impl [a-zA-Z0-9_-]+ for dyn [a-zA-Z0-9_-]+ \{\}"#, +impl [a-zA-Z0-9_-]+ for dyn [a-zA-Z0-9_-]+ \{\}"# ); } #[test] fn test_assoc_ty_where_clause() { - test_formatting( - " - trait Bar {} - trait Fuzz { - type Assoc - where - dyn Bar: Bar, - Self: Bar; + reparse_test!( + program { + trait Bar {} + trait Fuzz { + type Assoc + where + dyn Bar: Bar, + Self: Bar; + } } - ", - r#"trait [a-zA-Z0-9_-]+ \{\s*\} + formatting matches +r#"trait [a-zA-Z0-9_-]+ \{\s*\} trait [a-zA-Z0-9_-]+ \{ type [a-zA-Z0-9_-]+ where dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+; \} -"#, +"# ); } @@ -107,18 +109,18 @@ fn test_name_disambiguation() { // structs or traits with the same name in Chalk - but luckily our // implementation ignores types for name disambiguation, so we can test it // indirectly by using a opaque type and trait of the same name. - reparse_into_different_test( - " - struct Foo {} - trait Baz {} - impl Baz for Foo {} - opaque type Baz: Baz = Foo; - ", - " - struct Foo {} - trait Baz {} - impl Baz for Foo {} - opaque type Baz_1: Baz = Foo; - ", + reparse_test! ( + program { + struct Foo {} + trait Baz {} + impl Baz for Foo {} + opaque type Baz: Baz = Foo; + } + produces { + struct Foo {} + trait Baz {} + impl Baz for Foo {} + opaque type Baz_1: Baz = Foo; + } ); } diff --git a/tests/display/impl_.rs b/tests/display/impl_.rs index e4d046d69ec..97f8b61bfc5 100644 --- a/tests/display/impl_.rs +++ b/tests/display/impl_.rs @@ -1,39 +1,38 @@ -use super::*; -#[test] -fn test_negative_auto_trait_impl() { - reparse_test( - " - struct Foo { } - #[auto] - trait Baz {} - impl !Baz for Foo { } - ", - ); -} - -#[test] -fn test_simple_impl() { - reparse_test( - " - struct Foo {} - trait Bar {} - impl Bar for Foo {} - ", - ); -} - -#[test] -fn test_impl_for_generic() { - reparse_test( - " - trait Bar {} - impl Bar for G {} - ", - ); - reparse_test( - " - trait Baz {} - impl Baz for T {} - ", - ); -} +#[test] +fn test_negative_auto_trait_impl() { + reparse_test!( + program { + struct Foo { } + #[auto] + trait Baz {} + impl !Baz for Foo { } + } + ); +} + +#[test] +fn test_simple_impl() { + reparse_test!( + program { + struct Foo {} + trait Bar {} + impl Bar for Foo {} + } + ); +} + +#[test] +fn test_impl_for_generic() { + reparse_test!( + program { + trait Bar {} + impl Bar for G {} + } + ); + reparse_test!( + program { + trait Baz {} + impl Baz for T {} + } + ); +} diff --git a/tests/display/lifetimes.rs b/tests/display/lifetimes.rs index 87a7cf6d387..2b5b3ee2b99 100644 --- a/tests/display/lifetimes.rs +++ b/tests/display/lifetimes.rs @@ -1,8 +1,7 @@ -use super::*; #[test] fn test_various_forall() { - reparse_test( - " + reparse_test!( + program { struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } trait Baz<'a> {} trait Bax<'a> {} @@ -12,16 +11,16 @@ fn test_various_forall() { impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } impl<'a> Bax<'a> for fn(Foo<'a>) { } impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> { } - ", + } ); } #[test] fn test_lifetimes_in_structs() { - reparse_test( - " + reparse_test!( + program { struct Foo<'b> { } trait Baz<'a> {} impl<'a> Baz<'a> for Foo<'a> { } - ", + } ); } diff --git a/tests/display/mod.rs b/tests/display/mod.rs index 166613e2b00..5f20438e0f6 100644 --- a/tests/display/mod.rs +++ b/tests/display/mod.rs @@ -1,7 +1,5 @@ -use chalk_integration::{program::Program, query::LoweringDatabase, tls}; -use chalk_solve::display::{self, WriterState}; -use regex::Regex; -use std::{fmt::Debug, sync::Arc}; +#[macro_use] +mod util; mod assoc_ty; mod built_ins; @@ -15,152 +13,12 @@ mod struct_; mod trait_; mod where_clauses; -fn write_program(program: &Program) -> String { - let mut out = String::new(); - let ws = &WriterState::new(program); - for datum in program.adt_data.values() { - display::write_top_level(&mut out, ws, &**datum).unwrap(); - } - for datum in program.trait_data.values() { - display::write_top_level(&mut out, ws, &**datum).unwrap(); - } - for datum in program.impl_data.values() { - display::write_top_level(&mut out, ws, &**datum).unwrap(); - } - for datum in program.opaque_ty_data.values() { - display::write_top_level(&mut out, ws, &**datum).unwrap(); - } - out -} - -fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { - use std::fmt::Write; - - let mut out = String::new(); - let original = format!("{:#?}", original); - let produced = format!("{:#?}", produced); - for line in diff::lines(&original, &produced) { - match line { - diff::Result::Left(l) => write!(out, "-{}\n", l), - diff::Result::Both(l, _) => write!(out, " {}\n", l), - diff::Result::Right(r) => write!(out, "+{}\n", r), - } - .expect("writing to string never fails"); - } - out -} - -/// Data from performing a reparse test which can be used to make additional -/// assertions. -/// -/// Not necessary for use unless additional assertions are necessary. -#[allow(unused)] -struct ReparseTestResult<'a> { - /// The program text for the original test code - original_text: &'a str, - /// The program text for the code the test says should be output - target_text: &'a str, - /// The actual reparsed output text - output_text: String, - /// Lowered version of `original_text` - original_program: Arc, - /// Lowered version of `target_text` - target_program: Arc, - /// Lowered version of `output_text` - output_program: Arc, -} - -/// Parses the input, lowers it, prints it, then re-parses and re-lowers, -/// failing if the two lowered programs don't match. -/// -/// Note: the comparison here does include IDs, so input order matters. In -/// particular, ProgramWriter always writes traits, then structs, then -/// impls. So all traits must come first, then structs, then all impls, or -/// the reparse will fail. -fn reparse_test(program_text: &str) -> ReparseTestResult<'_> { - reparse_into_different_test(program_text, program_text) -} - -/// [`reparse_test`], but allows a non-convergent test program to be tested -/// a different target. -fn reparse_into_different_test<'a>( - program_text: &'a str, - target_text: &'a str, -) -> ReparseTestResult<'a> { - let original_db = chalk_integration::db::ChalkDatabase::with(program_text, <_>::default()); - let original_program = original_db.program_ir().unwrap_or_else(|e| { - panic!( - "unable to lower test program:\n{}\nSource:\n{}\n", - e, program_text - ) - }); - let target_db = chalk_integration::db::ChalkDatabase::with(target_text, <_>::default()); - let target_program = target_db.program_ir().unwrap_or_else(|e| { - panic!( - "unable to lower test program:\n{}\nSource:\n{}\n", - e, program_text - ) - }); - let output_text = - tls::set_current_program(&original_program, || write_program(&original_program)); - let output_db = chalk_integration::db::ChalkDatabase::with(&output_text, <_>::default()); - let output_program = output_db.program_ir().unwrap_or_else(|e| { - panic!( - "error lowering writer output:\n{}\nNew source:\n{}\n", - e, output_text - ) - }); - if output_program != target_program { - panic!( - "WriteProgram produced different program.\n\ - Diff:\n{}\n\ - Source:\n{}\n{}\ - New Source:\n{}\n", - program_diff(&target_program, &output_program), - program_text, - if target_text != program_text { - format!( - "Test Should Output (different from original):\n{}\n", - target_text - ) - } else { - String::new() - }, - output_text - ); - } - eprintln!("\nTest Succeeded:\n\n{}\n---", output_text); - ReparseTestResult { - original_text: program_text, - output_text, - target_text, - original_program, - output_program, - target_program, - } -} - -fn test_formatting(src: &str, acceptable: &str) { - let result = reparse_test(src); - let acceptable = Regex::new(acceptable).unwrap(); - if !acceptable.is_match(&result.output_text) { - panic!( - "output_text's formatting didn't match the criteria.\ - \noutput_text:\n\"{0}\"\ - \ncriteria:\n\"{1}\"\ - \ndebug output: {0:?}\ - \ndebug criteria: {2:?}\n", - result.output_text, - acceptable, - acceptable.as_str() - ); - } -} +use self::util::*; #[test] fn test_program_writer() { - reparse_test( - " + reparse_test!( + program { struct Foo { } struct Vec { } struct Map<_0, _1> { } @@ -178,7 +36,7 @@ fn test_program_writer() { type Assoc; } trait AsRef { } - + trait AssocTraitWithWhere { type Assoc where U: AsRef; } @@ -200,6 +58,6 @@ fn test_program_writer() { impl AssocTraitWithWhere for Vec { type Assoc = Map; } - ", + } ); } diff --git a/tests/display/opaque_ty.rs b/tests/display/opaque_ty.rs index c401b3bd7e5..71ae57b580b 100644 --- a/tests/display/opaque_ty.rs +++ b/tests/display/opaque_ty.rs @@ -1,8 +1,7 @@ -use super::*; #[test] fn opaque_types() { - reparse_test( - " + reparse_test!( + program { struct Bar {} trait Buz {} trait Baz { @@ -13,33 +12,33 @@ fn opaque_types() { type Hi = Foo; } opaque type Foo: Buz = Bar; - ", + } ); } #[test] fn test_generic_opaque_types() { - reparse_test( - " + reparse_test!( + program { struct Foo {} trait Bar {} opaque type Baz: Bar = Foo; - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo {} struct Unit {} trait Bar {} opaque type Boz: Bar = Foo; - ", + } ); } #[test] fn test_opaque_type_as_type_value() { - reparse_test( - " + reparse_test!( + program { struct Foo {} trait Bar {} trait Fuzz { @@ -50,10 +49,10 @@ fn test_opaque_type_as_type_value() { type Assoc = Bax; } opaque type Bax: Bar = Foo; - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo {} trait Bar {} trait Faz { @@ -63,7 +62,7 @@ fn test_opaque_type_as_type_value() { type Assoc = fn(Baz); } opaque type Baz: Bar = Foo; - ", + } ); } @@ -71,8 +70,8 @@ fn test_opaque_type_as_type_value() { #[ignore] #[test] fn test_generic_opaque_type_as_value1() { - reparse_test( - " + reparse_test!( + program { struct Foo {} trait Bar {} trait Fizz { @@ -83,10 +82,10 @@ fn test_generic_opaque_type_as_value1() { type Assoc = Baz; } opaque type Baz: Bar = Foo; - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo {} trait Bar {} trait Faz { @@ -96,10 +95,10 @@ fn test_generic_opaque_type_as_value1() { type Assoc = fn(Baz); } opaque type Baz: Bar = Foo; - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo {} struct Unit {} trait Bar {} @@ -111,6 +110,6 @@ fn test_generic_opaque_type_as_value1() { } impl Bar for Foo {} opaque type Biiiz: Bar = Foo; - ", + } ); } diff --git a/tests/display/self_.rs b/tests/display/self_.rs index d1249ab9128..9e843abaa35 100644 --- a/tests/display/self_.rs +++ b/tests/display/self_.rs @@ -1,70 +1,68 @@ -use super::*; - #[test] fn test_self_in_trait_where() { - reparse_test( - " + reparse_test!( + program { trait Bkz {} trait Foo where Self: Bkz {} - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Baz<'a> {} trait Foo where forall<'a> Self: Baz<'a> {} - ", + } ); } #[test] fn test_self_in_assoc_type() { - reparse_test( - " + reparse_test!( + program { trait Extra {} trait Bez {} trait Foo { type Assoc: Extra; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Bez {} trait Foo { type Assoc where Self: Bez; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Biz {} trait Foo { type Assoc where Self: Biz; } - ", + } ); } #[test] fn test_self_in_dyn() { - reparse_test( - " + reparse_test!( + program { trait Bun {} trait Foo { type Assoc where dyn Bun: Bun; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Has {} trait Bun {} trait Fiz { type Assoc1: Has>; type Assoc2: Has>; } - ", + } ); } @@ -72,23 +70,23 @@ fn test_self_in_dyn() { #[ignore] #[test] fn test_self_in_struct_bounds() { - reparse_test( - " + reparse_test!( + program { trait Bax {} struct Foo where T: Bax {} - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Baz {} struct Foo where Self: Baz {} - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Blz {} struct Foo where Self: Blz {} - ", + } ); } @@ -96,8 +94,8 @@ fn test_self_in_struct_bounds() { #[ignore] #[test] fn test_self_in_impl_blocks() { - reparse_test( - " + reparse_test!( + program { trait Foo { type Assoc; } @@ -105,18 +103,18 @@ fn test_self_in_impl_blocks() { impl Foo for Bix { type Assoc = Self; } - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Foo {} trait Fin {} struct Bux {} impl Foo for Bux where Self: Fin {} - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Faux {} trait Paw { type Assoc1; @@ -127,7 +125,7 @@ fn test_self_in_impl_blocks() { type Assoc1 = dyn Faux; type Assoc2 = dyn Faux; } - ", + } ); } @@ -135,33 +133,33 @@ fn test_self_in_impl_blocks() { fn test_against_accidental_self() { // In some of the writer code, it would be really easy to introduce a // outputs the first generic parameter of things as "Self". - let in_structs = reparse_test( - " - struct Foo { - field: T + let in_structs = reparse_test!( + program { + struct Foo { + field: T + } } - ", ); assert!(!in_structs.output_text.contains("Self")); - let in_impl = reparse_test( - " - struct Foo {} - trait Bux { - type Assoc; - } - impl Bux for Foo { - type Assoc = T; + let in_impl = reparse_test!( + program { + struct Foo {} + trait Bux { + type Assoc; + } + impl Bux for Foo { + type Assoc = T; + } } - ", ); assert!(!in_impl.output_text.contains("Self")); - let in_opaque = reparse_test( - " - struct Foo {} - trait Que {} - impl Que for Foo {} - opaque type Bar: Que = Foo; - ", + let in_opaque = reparse_test!( + program { + struct Foo {} + trait Que {} + impl Que for Foo {} + opaque type Bar: Que = Foo; + } ); assert!(!in_opaque.output_text.contains("Self")); } diff --git a/tests/display/struct_.rs b/tests/display/struct_.rs index 1116b345c93..f2e0a75d9e0 100644 --- a/tests/display/struct_.rs +++ b/tests/display/struct_.rs @@ -1,27 +1,33 @@ -use super::*; - #[test] fn test_simple_structs_and_bounds() { - reparse_test("struct Foo {}"); - reparse_test("struct Foo {}"); - reparse_test( - " - struct Foo where T: Trait {} - trait Trait {} - ", + reparse_test!( + program { + struct Foo {} + } + ); + reparse_test!( + program { + struct Foo {} + } + ); + reparse_test!( + program { + struct Foo where T: Trait {} + trait Trait {} + } ); } #[test] fn test_struct_fields() { - reparse_test( - " - struct Foo {} - struct Bar {} - struct Baz { - x: Foo, - b: Bar - } - ", + reparse_test!( + program { + struct Foo {} + struct Bar {} + struct Baz { + x: Foo, + b: Bar + } + } ); } diff --git a/tests/display/trait_.rs b/tests/display/trait_.rs index 7156362f308..b5d314d8ce6 100644 --- a/tests/display/trait_.rs +++ b/tests/display/trait_.rs @@ -1,24 +1,32 @@ use super::*; #[test] fn test_simple_traits_and_bounds() { - reparse_test("trait Foo {}"); - reparse_test("trait Foo {}"); - reparse_test( - " + reparse_test!( + program { + trait Foo {} + } + ); + reparse_test!( + program { + trait Foo {} + } + ); + reparse_test!( + program { trait Foo where T: Trait {} trait Trait {} - ", + } ); } #[test] fn test_basic_trait_impl() { - reparse_test( - " + reparse_test!( + program { struct Foo { } trait Bar {} impl Bar for Foo { } - ", + } ); } diff --git a/tests/display/util.rs b/tests/display/util.rs new file mode 100644 index 00000000000..3023ec57130 --- /dev/null +++ b/tests/display/util.rs @@ -0,0 +1,234 @@ +//! Utilities and macros for use in display tests. +//! +//! This can't live as a submodule of `test_util.rs`, as then it would conflict +//! with `display/mod.rs` for the name `mod display` when `test_util.rs` is +//! compiled as a standalone test (rather than from `lib.rs`). +use chalk_integration::{program::Program, query::LoweringDatabase, tls}; +use chalk_solve::display::write_items; +use regex::Regex; +use std::{fmt::Debug, sync::Arc}; + +pub fn strip_leading_trailing_braces(input: &str) -> &str { + assert!(input.starts_with("{")); + assert!(input.ends_with("}")); + + &input[1..input.len() - 1] +} + +/// Performs a test on chalk's `display` code to render programs as `.chalk` files. +macro_rules! reparse_test { + // Test that a program, when rendered and then reparsed, results in a + // program identical to the input. + (program $program:tt) => { + crate::display::util::reparse_test(crate::display::util::strip_leading_trailing_braces( + stringify!($program), + )) + }; + // Tests that a program, when rendered and then reparsed, results in a + // second, different program. Good for cases where this process is non-convergent. + (program $program:tt produces $diff:tt) => { + crate::display::util::reparse_into_different_test( + crate::display::util::strip_leading_trailing_braces(stringify!($program)), + crate::display::util::strip_leading_trailing_braces(stringify!($diff)), + ) + }; + // Tests that a program, when rendered, results in a string which matches the + // given regex. + (program $program:tt formatting matches $res:literal) => { + crate::display::util::test_formatting( + crate::display::util::strip_leading_trailing_braces(stringify!($program)), + $res, + ) + }; +} + +/// Sends all items in a `chalk_integration::Program` through `display` code and +/// returns the string representing the program. +pub fn write_program(program: &Program) -> String { + let mut out = String::new(); + let ids = std::iter::empty() + .chain(program.adt_data.keys().copied().map(Into::into)) + .chain(program.trait_data.keys().copied().map(Into::into)) + .chain(program.impl_data.keys().copied().map(Into::into)) + .chain(program.opaque_ty_data.keys().copied().map(Into::into)); + write_items(&mut out, program, ids).unwrap(); + out +} + +/// Diffs two `Program`s. This diffs the verbose debug output of `Program`, so +/// that you can see exactly what parts have changed in case a test fails. +/// +/// Will produces something akin to the following: +/// +/// ```diff +/// Program { +/// - adt_ids: { +/// - Atom('Foo' type=inline): AdtId(#0), +/// - }, +/// - adt_kinds: { +/// - AdtId(#0): TypeKind { +/// - sort: Struct, +/// - name: Atom('Foo' type=inline), +/// - binders: for[] Unit, +/// - }, +/// - }, +/// + adt_ids: {}, +/// + adt_kinds: {}, +/// fn_def_ids: {}, +/// fn_def_kinds: {}, +/// trait_ids: {}, +/// trait_kinds: {}, +/// - adt_data: { +/// - AdtId(#0): AdtDatum { +/// - binders: for[] AdtDatumBound { +/// - fields: [], +/// - where_clauses: [], +/// - }, +/// - id: AdtId(#0), +/// - flags: AdtFlags { +/// - upstream: false, +/// - fundamental: false, +/// - }, +/// - }, +/// - }, +/// + adt_data: {}, +/// fn_def_data: {}, +/// impl_data: {}, +/// associated_ty_values: {}, +/// opaque_ty_ids: {}, +/// opaque_ty_kinds: {}, +/// opaque_ty_data: {}, +/// trait_data: {}, +/// well_known_traits: {}, +/// associated_ty_data: {}, +/// custom_clauses: [], +/// object_safe_traits: {}, +/// } +/// ``` +fn program_diff(original: &impl Debug, produced: &impl Debug) -> String { + use std::fmt::Write; + + let mut out = String::new(); + let original = format!("{:#?}", original); + let produced = format!("{:#?}", produced); + for line in diff::lines(&original, &produced) { + match line { + diff::Result::Left(l) => write!(out, "-{}\n", l), + diff::Result::Both(l, _) => write!(out, " {}\n", l), + diff::Result::Right(r) => write!(out, "+{}\n", r), + } + .expect("writing to string never fails"); + } + out +} + +/// Data from performing a reparse test which can be used to make additional +/// assertions. +/// +/// Not necessary for use unless additional assertions are necessary. +#[allow(unused)] +pub struct ReparseTestResult<'a> { + /// The program text for the original test code + pub original_text: &'a str, + /// The program text for the code the test says should be output + pub target_text: &'a str, + /// The actual reparsed output text + pub output_text: String, + /// Lowered version of `original_text` + pub original_program: Arc, + /// Lowered version of `target_text` + pub target_program: Arc, + /// Lowered version of `output_text` + pub output_program: Arc, +} + +/// Parses the input, lowers it, prints it, then re-parses and re-lowers, +/// failing if the two lowered programs don't match. +/// +/// Note: the comparison here does include IDs, so input order matters. In +/// particular, `write_program` always writes in the order adts, traits, impls, +/// then opaque_types. So the input program must also list things in this order, +/// or the test will fail. +pub fn reparse_test(program_text: &str) -> ReparseTestResult<'_> { + reparse_into_different_test(program_text, program_text) +} + +/// [`reparse_test`], but allows a non-convergent test program to be tested +/// a different target. +pub fn reparse_into_different_test<'a>( + program_text: &'a str, + target_text: &'a str, +) -> ReparseTestResult<'a> { + let original_db = chalk_integration::db::ChalkDatabase::with(program_text, <_>::default()); + let original_program = original_db.program_ir().unwrap_or_else(|e| { + panic!( + "unable to lower test program:\n{}\nSource:\n{}\n", + e, program_text + ) + }); + let target_db = chalk_integration::db::ChalkDatabase::with(target_text, <_>::default()); + let target_program = target_db.program_ir().unwrap_or_else(|e| { + panic!( + "unable to lower test program:\n{}\nSource:\n{}\n", + e, program_text + ) + }); + let output_text = + tls::set_current_program(&original_program, || write_program(&original_program)); + let output_db = chalk_integration::db::ChalkDatabase::with(&output_text, <_>::default()); + let output_program = output_db.program_ir().unwrap_or_else(|e| { + panic!( + "error lowering writer output:\n{}\nNew source:\n{}\n", + e, output_text + ) + }); + if output_program != target_program { + panic!( + "WriteProgram produced different program.\n\ + Diff:\n{}\n\ + Source:\n{}\n{}\ + New Source:\n{}\n", + program_diff(&target_program, &output_program), + program_text, + if target_text != program_text { + format!( + "Test Should Output (different from original):\n{}\n", + target_text + ) + } else { + String::new() + }, + output_text + ); + } + eprintln!("\nTest Succeeded:\n\n{}\n---", output_text); + ReparseTestResult { + original_text: program_text, + output_text, + target_text, + original_program, + output_program, + target_program, + } +} + +/// Tests that a string matches a given regex pattern, erroring out if it +/// doesn't. +/// +/// This is used for exact formatting tests, for testing things like indentation. +pub fn test_formatting(src: &str, acceptable: &str) { + let result = reparse_test(src); + let acceptable = Regex::new(acceptable).unwrap(); + if !acceptable.is_match(&result.output_text) { + panic!( + "output_text's formatting didn't match the criteria.\ + \noutput_text:\n\"{0}\"\ + \ncriteria:\n\"{1}\"\ + \ndebug output: {0:?}\ + \ndebug criteria: {2:?}\n", + result.output_text, + acceptable, + acceptable.as_str() + ); + } +} diff --git a/tests/display/where_clauses.rs b/tests/display/where_clauses.rs index e93aca3b5fa..3a34a47303b 100644 --- a/tests/display/where_clauses.rs +++ b/tests/display/where_clauses.rs @@ -1,8 +1,7 @@ -use super::*; #[test] fn test_complicated_bounds() { - reparse_into_different_test( - " + reparse_test!( + program { struct Foo { } trait Bar { } trait Baz { } @@ -14,8 +13,8 @@ fn test_complicated_bounds() { Foo: Bar, dyn Bar: Baz; } - ", - " + } + produces { struct Foo { } trait Bar { } trait Baz { } @@ -27,84 +26,85 @@ fn test_complicated_bounds() { Foo: Bax, Foo: Bar, dyn Bar: Baz; - }", + } + } ); } #[test] fn test_struct_where_clauses() { - reparse_test( - " - struct Foo where T: Baz, U: Bez { } - trait Baz { } - trait Bez { } - ", + reparse_test!( + program { + struct Foo where T: Baz, U: Bez { } + trait Baz { } + trait Bez { } + } ); } #[test] fn test_impl_where_clauses() { - reparse_test( - " - struct Foo where T: Baz, U: Bez { } - trait Baz { } - trait Bez { } - impl Bez for Foo where T: Baz, U: Bez { } - ", + reparse_test!( + program { + struct Foo where T: Baz, U: Bez { } + trait Baz { } + trait Bez { } + impl Bez for Foo where T: Baz, U: Bez { } + } ); // TODO: more of these } #[test] fn test_trait_projection() { - reparse_test( - " - struct Flux {} - struct Foo where U: Bez, >::Assoc: Baz { } - trait Baz { } - trait Bez { - type Assoc; + reparse_test!( + program { + struct Flux {} + struct Foo where U: Bez, >::Assoc: Baz { } + trait Baz { } + trait Bez { + type Assoc; + } } - ", ); } #[test] fn test_trait_projection_with_dyn_arg() { - reparse_test( - " - struct Foo where U: Bez, >::Assoc: Baz { } - trait Baz { } - trait Bez { - type Assoc; + reparse_test!( + program { + struct Foo where U: Bez, >::Assoc: Baz { } + trait Baz { } + trait Bez { + type Assoc; + } } - ", ); } #[test] fn test_forall_in_where() { - reparse_test( - " + reparse_test!( + program { trait Bax {} trait Foo where forall T: Bax {} - ", + } ); - reparse_test( - " + reparse_test!( + program { trait Buz<'a> {} trait Foo where forall<'a> T: Buz<'a> {} - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo where forall T: Biz {} trait Biz {} - ", + } ); - reparse_test( - " + reparse_test!( + program { struct Foo where forall<'a> T: Bez<'a> {} trait Bez<'a> {} - ", + } ); } From 88d244de20a090040813c5d3474ebd2c755fed72 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 24 May 2020 00:26:55 -0700 Subject: [PATCH 37/70] Fix unused variable warning Co-authored-by: David Ross --- chalk-solve/src/logging_db.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 8c3fbfb0ae7..ef62b743e59 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -171,7 +171,7 @@ where self.record(trait_id); self.db.borrow().is_object_safe(trait_id) } - fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { + fn fn_def_datum(&self, _fn_def_id: chalk_ir::FnDefId) -> Arc> { todo!("function def datum") } } @@ -319,7 +319,7 @@ where fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.db.borrow().opaque_type_name(opaque_ty_id) } - fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { + fn fn_def_datum(&self, _fn_def_id: chalk_ir::FnDefId) -> Arc> { todo!("function def datum") } } From 11ca42f1a3ca8fd4249d242f7e8721db2b2628e4 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 24 May 2020 00:28:12 -0700 Subject: [PATCH 38/70] Refactor display logic into modules Co-authored-by: David Ross --- chalk-solve/src/display.rs | 1035 +---------------------- chalk-solve/src/display/bounds.rs | 151 ++++ chalk-solve/src/display/identifiers.rs | 42 + chalk-solve/src/display/items.rs | 274 ++++++ chalk-solve/src/display/render_trait.rs | 28 + chalk-solve/src/display/state.rs | 233 +++++ chalk-solve/src/display/ty.rs | 310 +++++++ chalk-solve/src/display/utils.rs | 24 + 8 files changed, 1093 insertions(+), 1004 deletions(-) create mode 100644 chalk-solve/src/display/bounds.rs create mode 100644 chalk-solve/src/display/identifiers.rs create mode 100644 chalk-solve/src/display/items.rs create mode 100644 chalk-solve/src/display/render_trait.rs create mode 100644 chalk-solve/src/display/state.rs create mode 100644 chalk-solve/src/display/ty.rs create mode 100644 chalk-solve/src/display/utils.rs diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index d3f38fb5374..6fe60db0ab4 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -1,9 +1,5 @@ use std::{ - cell::RefCell, - collections::BTreeMap, fmt::{Display, Formatter, Result}, - ops::Fn as StdFn, - rc::Rc, sync::Arc, }; @@ -13,6 +9,21 @@ use itertools::Itertools; use crate::{logging_db::RecordedItemId, split::Split, RustIrDatabase}; +#[macro_use] +mod utils; + +mod bounds; +mod identifiers; +mod items; +mod render_trait; +mod state; +mod ty; + +pub use self::render_trait::*; +pub use self::state::*; + +use self::utils::as_display; + pub fn write_top_level(f: &mut F, ws: &WriterState<'_, I>, v: &T) -> Result where I: Interner, @@ -55,237 +66,23 @@ where Ok(()) } -/// Displays `RenderAsRust` data. +/// Displays a set of bounds, all targeting `Self`, as just the trait names, +/// separated by `+`. /// -/// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. -pub struct DisplayRenderAsRust<'a, I: Interner, T> { - s: &'a WriterState<'a, I>, - rar: &'a T, -} - -impl> Display for DisplayRenderAsRust<'_, I, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.rar.fmt(self.s, f) - } -} - -fn as_display) -> Result>(f: F) -> impl Display { - struct ClosureDisplay) -> Result>(F); - - impl) -> Result> Display for ClosureDisplay { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.0(f) - } - } - - ClosureDisplay(f) -} - -pub trait RenderAsRust { - fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result; - fn display<'a>(&'a self, s: &'a WriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> - where - Self: Sized, - { - DisplayRenderAsRust { s, rar: self } - } -} - -macro_rules! write_joined_non_empty_list { - ($f:expr,$template:tt,$list:expr,$sep:expr) => {{ - let mut x = $list.into_iter().peekable(); - if x.peek().is_some() { - write!($f, $template, x.format($sep)) - } else { - Ok(()) - } - }}; -} - -impl RenderAsRust for AssociatedTyValue { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // see comments for a similar empty env operation in AssociatedTyDatum's - // impl of RenderAsRust. - let assoc_ty_data = s.db.associated_ty_data(self.associated_ty_id); - let impl_datum = s.db.impl_datum(self.impl_id); - - let impl_param_names_in_impl_env = s.binder_var_indices(&impl_datum.binders.binders); - - let s = &s.add_debrujin_index(None); - let value = self.value.skip_binders(); - - let param_names_in_assoc_ty_value_env = s - .binder_var_indices(&self.value.binders) - .collect::>(); - - let (impl_params_in_assoc_ty_value_env, _assoc_ty_value_params) = - s.db.split_associated_ty_value_parameters(¶m_names_in_assoc_ty_value_env, self); - - let s = &s.add_parameter_mapping( - impl_params_in_assoc_ty_value_env.iter().cloned(), - impl_param_names_in_impl_env, - ); - - // let params = s - // .binder_var_display(&self.value.binders) - // .collect::>(); - let display_params = s - .binder_var_display(&self.value.binders) - .collect::>(); - - let (_impl_display, assoc_ty_value_display) = - s.db.split_associated_ty_value_parameters(&display_params, self); - - write!(f, "{}type {}", s.indent(), assoc_ty_data.id.display(s))?; - write_joined_non_empty_list!(f, "<{}>", &assoc_ty_value_display, ", ")?; - write!(f, " = {};", value.ty.display(s))?; - Ok(()) - } -} - -impl RenderAsRust for Polarity { - fn fmt(&self, _s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - if !self.is_positive() { - write!(f, "!")?; - } - Ok(()) - } -} - -impl RenderAsRust for ImplDatum { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - - let s = &s.add_debrujin_index(None); - let binders = s.binder_var_display(&self.binders.binders); - - let value = self.binders.skip_binders(); - - let trait_ref = &value.trait_ref; - // Ignore automatically added Self parameter by skipping first parameter - let full_trait_name = display_trait_with_generics( - s, - trait_ref.trait_id, - &trait_ref.substitution.parameters(interner)[1..], - ); - write!(f, "impl")?; - write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; - write!( - f, - " {}{} for {}", - self.polarity.display(s), - full_trait_name, - trait_ref.self_type_parameter(interner).display(s) - )?; - if !value.where_clauses.is_empty() { - let s = &s.add_indent(); - write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; - } else { - write!(f, " ")?; - } - write!(f, "{{")?; - { - let s = &s.add_indent(); - let assoc_ty_values = self.associated_ty_value_ids.iter().map(|assoc_ty_value| { - s.db.associated_ty_value(*assoc_ty_value) - .display(s) - .to_string() - }); - write_joined_non_empty_list!(f, "\n{}\n", assoc_ty_values, "\n")?; - } - write!(f, "}}")?; - Ok(()) - } -} - -impl RenderAsRust for InlineBound { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - match self { - // Foo: Vec - InlineBound::TraitBound(trait_bound) => trait_bound.fmt(s, f), - // Foo: Iterator - InlineBound::AliasEqBound(eq_bound) => eq_bound.fmt(s, f), - } - } -} - -impl RenderAsRust for TraitBound { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - display_trait_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) - } -} - -impl RenderAsRust for AliasEqBound { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - display_trait_with_assoc_ty_value( - s, - s.db.associated_ty_data(self.associated_ty_id), - &self.trait_bound.args_no_self, - &self.parameters, - &self.value, - ) - .fmt(f) - } -} - -impl RenderAsRust for Ty { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // delegate to TyData - self.data(s.db.interner()).fmt(s, f) - } -} -impl RenderAsRust for Lifetime { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // delegate to LifetimeData - self.data(s.db.interner()).fmt(s, f) - } -} - -impl RenderAsRust for Const { - fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { - self.data(s.db.interner()).fmt(s, f) - } -} - -impl RenderAsRust for GenericArg { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // delegate to ParameterData - self.data(s.db.interner()).fmt(s, f) - } -} -impl RenderAsRust for AdtId { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // TODO: use debug methods? - write!( - f, - "{}", - s.alias_for_adt_id_name(self.0, s.db.adt_name(*self)) - ) - } -} -impl RenderAsRust for TraitId { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // TODO: use debug methods? - write!(f, "{}", s.alias_for_id_name(self.0, s.db.trait_name(*self))) - } -} -impl RenderAsRust for AssocTypeId { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // TODO: use debug methods? - f.write_str(&s.db.assoc_type_name(*self)) - } -} -impl RenderAsRust for OpaqueTyId { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // TODO: use debug methods? - write!( - f, - "{}", - s.alias_for_id_name(self.0, s.db.opaque_type_name(*self)) - ) - } -} - +/// For example, a list of quantified where clauses which would normally be +/// displayed as: +/// +/// ```notrust +/// Self: A, Self: B, Self: C +/// ``` +/// +/// Is instead displayed by this function as: +/// +/// ```notrust +/// A + B + C +/// ``` +/// +/// Shared between the `Trait` in `dyn Trait` and [`OpaqueTyDatum`] bounds. fn display_self_where_clauses_as_bounds<'a, I: Interner>( s: &'a WriterState<'a, I>, bounds: &'a [QuantifiedWhereClause], @@ -342,338 +139,6 @@ fn display_self_where_clauses_as_bounds<'a, I: Interner>( }) } -impl RenderAsRust for TyData { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - match self { - TyData::Dyn(dyn_ty) => { - let s = &s.add_debrujin_index(None); - // dyn_ty.bounds.binders creates a Self binding for the trait - let bounds = dyn_ty.bounds.skip_binders(); - write!( - f, - "dyn {}", - display_self_where_clauses_as_bounds(s, bounds.as_slice(interner)) - )?; - Ok(()) - } - TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), - TyData::InferenceVar(_) => write!(f, "_"), - TyData::Alias(alias_ty) => alias_ty.fmt(s, f), - TyData::Apply(apply_ty) => apply_ty.fmt(s, f), - TyData::Function(func) => func.fmt(s, f), - TyData::Placeholder(_) => unreachable!( - "cannot print placeholder variables; these should only be in goals not programs" - ), - } - } -} - -impl RenderAsRust for AliasTy { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - match self { - AliasTy::Projection(projection_ty) => projection_ty.fmt(s, f), - AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(s, f), - } - } -} - -impl RenderAsRust for ProjectionTy { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // >::Z - - // Now, we split out A*, Y/Z and B*: - // trait_params is X, A1, A2, A3, - // assoc_type_params is B1, B2, B3, - // assoc_ty_datum stores info about Y and Z. - let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self); - write!( - f, - "<{} as {}>::{}", - trait_params[0].display(s), - display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), - assoc_ty_datum.id.display(s), - )?; - write_joined_non_empty_list!( - f, - "<{}>", - assoc_type_params.iter().map(|param| param.display(s)), - ", " - )?; - Ok(()) - } -} -impl RenderAsRust for OpaqueTy { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - write!( - f, - "{}", - display_trait_with_generics( - s, - self.opaque_ty_id, - self.substitution.parameters(interner), - ) - ) - } -} - -impl RenderAsRust for Fn { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - let s = &s.add_debrujin_index(None); - if self.num_binders > 0 { - write!( - f, - "for<{}> ", - (0..self.num_binders) - .map(|n| format!("'{}", s.name_for_introduced_bound_var(n))) - .collect::>() - .join(", ") - )?; - } - write!( - f, - "fn({})", - self.substitution - .parameters(interner) - .iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", ") - ) - } -} - -impl RenderAsRust for ApplicationTy { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - match self.name { - TypeName::Adt(sid) => { - write!(f, "{}", sid.display(s))?; - let parameters = self.substitution.parameters(interner); - let parameters = parameters.iter().map(|param| param.display(s)); - write_joined_non_empty_list!(f, "<{}>", parameters, ", ")?; - } - TypeName::AssociatedType(assoc_type_id) => { - // (Iterator::Item)(x) - // should be written in Rust as ::Item - let datum = s.db.associated_ty_data(assoc_type_id); - assert!( - self.len_type_parameters(interner) >= 1, - "AssociatedType should have at least 1 parameter" - ); - write!( - f, - "<{} as {}>::{}", - self.first_type_parameter(interner).unwrap().display(s), - datum.trait_id.display(s), - datum.id.display(s), - )?; - let params = self.substitution.parameters(interner); - write_joined_non_empty_list!( - f, - "<{}>", - params[1..].iter().map(|ty| ty.display(s)), - "," - )?; - } - TypeName::Scalar(scalar) => write!(f, "{}", scalar.display(s))?, - TypeName::Tuple(arity) => { - write!( - f, - "({}{})", - self.substitution - .parameters(interner) - .iter() - .map(|p| p.display(s)) - .format(", "), - if arity == 1 { - // need trailing single comma - "," - } else { - "" - } - )? - } - TypeName::OpaqueType(_) => todo!("opaque type usage"), - TypeName::Raw(raw) => { - let mutability = match raw { - Mutability::Mut => "*mut ", - Mutability::Not => "*const ", - }; - write!( - f, - "{}{}", - mutability, - self.first_type_parameter(interner).unwrap().display(s) - )? - } - TypeName::Ref(mutability) => { - let mutability = match mutability { - Mutability::Mut => "mut ", - Mutability::Not => "", - }; - write!( - f, - "&{} {}{}", - self.substitution.at(interner, 0).display(s), - mutability, - self.substitution.at(interner, 1).display(s) - )?; - } - TypeName::Str => write!(f, "str")?, - TypeName::Slice => { - write!( - f, - "[{}]", - self.first_type_parameter(interner).unwrap().display(s) - )?; - } - TypeName::Error => write!(f, "{{error}}")?, - TypeName::Never => todo!("never type"), - TypeName::FnDef(_) => todo!("fn def type"), - } - Ok(()) - } -} - -impl RenderAsRust for Scalar { - fn fmt(&self, _s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { - use chalk_ir::{FloatTy::*, IntTy::*, UintTy::*}; - write!( - f, - "{}", - match self { - Scalar::Bool => "bool", - Scalar::Char => "char", - Scalar::Int(int) => match int { - Isize => "isize", - I8 => "i8", - I16 => "i16", - I32 => "i32", - I64 => "i64", - I128 => "i128", - }, - Scalar::Uint(uint) => match uint { - Usize => "usize", - U8 => "u8", - U16 => "u16", - U32 => "u32", - U64 => "u64", - U128 => "u128", - }, - Scalar::Float(float) => match float { - F32 => "f32", - F64 => "f64", - }, - } - ) - } -} - -impl RenderAsRust for LifetimeData { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - match self { - LifetimeData::BoundVar(v) => write!(f, "'{}", s.display_bound_var(v)), - LifetimeData::InferenceVar(_) => write!(f, "'_"), - LifetimeData::Placeholder(_) => unreachable!( - "cannot print placeholder variables; these should only be in goals not programs" - ), - // Matching the void ensures at compile time that this code is - // unreachable - LifetimeData::Phantom(void, _) => match *void {}, - } - } -} - -impl RenderAsRust for ConstData { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - write!(f, "{}", self.value.display(s)) - } -} - -impl RenderAsRust for ConstValue { - fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - match self { - ConstValue::BoundVar(v) => write!(f, "{}", s.display_bound_var(v)), - ConstValue::InferenceVar(_) => write!(f, "_"), - ConstValue::Placeholder(_) => unreachable!( - "cannot print placeholder variables; these should only be in goals not programs" - ), - ConstValue::Concrete(value) => unimplemented!("const values"), - } - } -} - -impl RenderAsRust for GenericArgData { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - match self { - GenericArgData::Ty(ty) => write!(f, "{}", ty.display(s)), - GenericArgData::Lifetime(lt) => write!(f, "{}", lt.display(s)), - GenericArgData::Const(const_ty) => write!(f, "{}", const_ty.display(s)), - } - } -} - -impl RenderAsRust for QuantifiedWhereClause { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - let s = &s.add_debrujin_index(None); - if !self.binders.is_empty(interner) { - write!( - f, - "forall<{}> ", - s.binder_var_display(&self.binders) - .collect::>() - .join(", ") - )?; - } - self.skip_binders().fmt(s, f) - } -} - -impl RenderAsRust for QuantifiedInlineBound { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - let s = &s.add_debrujin_index(None); - if !self.binders.is_empty(&interner) { - write!( - f, - "forall<{}> ", - s.binder_var_display(&self.binders) - .collect::>() - .join(", ") - )?; - } - self.skip_binders().fmt(s, f) - } -} - -impl RenderAsRust for Vec> { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - write!( - f, - "{}", - self.iter() - .map(|where_clause| { format!("{}{}", s.indent(), where_clause.display(s)) }) - .collect::>() - .join(",\n") - )?; - Ok(()) - } -} - -impl RenderAsRust for WhereClause { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - match self { - WhereClause::Implemented(trait_ref) => trait_ref.fmt(s, f), - WhereClause::AliasEq(alias_eq) => alias_eq.fmt(s, f), - } - } -} - /// Displays a trait with its parameters - something like `AsRef`. /// /// This is shared between where bounds & dyn Trait. @@ -689,23 +154,6 @@ fn display_trait_with_generics<'a, I: Interner>( as_display(move |f| write!(f, "{}{}", trait_name.display(s), trait_params_str)) } -/// This implementation correct inside where clauses. -impl RenderAsRust for TraitRef { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let interner = s.db.interner(); - write!( - f, - "{}: {}", - self.self_type_parameter(interner).display(s), - display_trait_with_generics( - s, - self.trait_id, - &self.substitution.parameters(interner)[1..] - ) - ) - } -} - /// Displays a trait with its parameters and a single associated type - /// something like `IntoIterator`. /// @@ -736,424 +184,3 @@ fn display_trait_with_assoc_ty_value<'a, I: Interner>( Ok(()) }) } - -/// This implementation correct inside where clauses. -impl RenderAsRust for AliasEq { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // we have: X: Y=D> - // B1, B2, B3, X, A1, A2, A3 are put into alias_eq.alias.substitution - // D is alias_eq.ty - // Z is alias_eq.alias.associated_ty_id - // Y is also packed into alias_eq.alias.associated_ty_id - // Now, we split out A*, Y/Z and B*: - // trait_params is X, A1, A2, A3, - // assoc_type_params is B1, B2, B3, - // assoc_ty_datum stores info about Y and Z. - match &self.alias { - AliasTy::Projection(projection_ty) => { - let (assoc_ty_datum, trait_params, assoc_type_params) = - s.db.split_projection(&projection_ty); - // An alternate form might be `<{} as {}<{}>>::{}<{}> = {}` (with same - // parameter ordering). This alternate form would be using type equality - // constraints (https://github.com/rust-lang/rust/issues/20041). - write!( - f, - "{}: {}", - trait_params[0].display(s), - display_trait_with_assoc_ty_value( - s, - assoc_ty_datum, - &trait_params[1..], - assoc_type_params, - &self.ty - ), - ) - } - AliasTy::Opaque(_) => todo!("opaque types"), - } - } -} - -impl RenderAsRust for AssociatedTyDatum { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // In lowering, a completely new empty environment is created for each - // AssociatedTyDatum, and it's given generic parameters for each generic - // parameter that its trait had. We want to map the new binders for - // those generic parameters back into their original names. To do that, - // first find their original names (trait_binder_names), then the names - // they have inside the AssociatedTyDatum (assoc_ty_names_for_trait_params), - // and then add that mapping to the WriterState when writing bounds and - // where clauses. - let trait_datum = s.db.trait_datum(self.trait_id); - // inverted Debrujin indices for the trait's parameters in the trait - // environment - let trait_param_names_in_trait_env = s.binder_var_indices(&trait_datum.binders.binders); - let s = &s.add_debrujin_index(None); - // inverted Debrujin indices for the trait's parameters in the - // associated type environment - let param_names_in_assoc_ty_env = s - .binder_var_indices(&self.binders.binders) - .collect::>(); - // inverted Debrujin indices to render the trait's parameters in the - // associated type environment - let (trait_param_names_in_assoc_ty_env, _) = - s.db.split_associated_ty_parameters(¶m_names_in_assoc_ty_env, self); - - let s = &s.add_parameter_mapping( - trait_param_names_in_assoc_ty_env.iter().copied(), - trait_param_names_in_trait_env, - ); - - // rendered names for the associated type's generics in the associated - // type environment - let binder_display_in_assoc_ty = s - .binder_var_display(&self.binders.binders) - .collect::>(); - - let (_, assoc_ty_params) = - s.db.split_associated_ty_parameters(&binder_display_in_assoc_ty, self); - write!(f, "type {}", self.id.display(s))?; - write_joined_non_empty_list!(f, "<{}>", assoc_ty_params, ", ")?; - - let datum_bounds = &self.binders.skip_binders(); - - if !datum_bounds.bounds.is_empty() { - write!(f, ": ")?; - } - - // bounds is `A: V, B: D, C = E`? - // type Foo: X + Y + Z; - let bounds = datum_bounds - .bounds - .iter() - .map(|bound| bound.display(s).to_string()) - .collect::>() - .join(" + "); - write!(f, "{}", bounds)?; - - // where_clause is 'X: Y, Z: D' - // type Foo<...>: ... where X: Y, Z: D; - - // note: it's a quantified clause b/c we could have `for<'a> T: Foo<'a>` - // within 'where' - if !datum_bounds.where_clauses.is_empty() { - let where_s = &s.add_indent(); - let where_clauses = datum_bounds.where_clauses.display(where_s); - write!(f, "\n{}where\n{}", s.indent(), where_clauses)?; - } - write!(f, ";")?; - Ok(()) - } -} - -impl RenderAsRust for TraitDatum { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(Some(0)); - let value = self.binders.skip_binders(); - - macro_rules! trait_flags { - ($($n:ident),*) => { - $(if self.flags.$n { - write!(f,"#[{}]\n",stringify!($n))?; - })* - } - } - - trait_flags!( - auto, - marker, - upstream, - fundamental, - non_enumerable, - coinductive - ); - let binders = s.binder_var_display(&self.binders.binders).skip(1); - write!(f, "trait {}", self.id.display(s))?; - write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; - if !value.where_clauses.is_empty() { - let s = &s.add_indent(); - write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; - } else { - write!(f, " ")?; - } - write!(f, "{{")?; - let s = &s.add_indent(); - write_joined_non_empty_list!( - f, - "\n{}\n", - self.associated_ty_ids.iter().map(|assoc_ty_id| { - let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); - format!("{}{}", s.indent(), (*assoc_ty_data).display(s)) - }), - "\n" - )?; - write!(f, "}}")?; - Ok(()) - } -} - -impl RenderAsRust for AdtDatum { - fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - // When support for Self in structs is added, self_binding should be - // changed to Some(0) - let s = &s.add_debrujin_index(None); - let value = self.binders.skip_binders(); - write!(f, "struct {}", self.id.display(s),)?; - write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.binders.binders), ", ")?; - if !value.where_clauses.is_empty() { - let s = &s.add_indent(); - write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; - } else { - write!(f, " ")?; - } - write!(f, "{{")?; - let s = &s.add_indent(); - write_joined_non_empty_list!( - f, - "\n{}\n", - value.fields.iter().enumerate().map(|(idx, field)| { - format!("{}field_{}: {}", s.indent(), idx, field.display(s)) - }), - ",\n" - )?; - write!(f, "}}")?; - Ok(()) - } -} - -impl RenderAsRust for OpaqueTyDatum { - fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { - let s = &s.add_debrujin_index(None); - let bounds = self.bound.skip_binders(); - write!(f, "opaque type {}", self.opaque_ty_id.display(s))?; - write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.bound.binders), ", ")?; - { - let s = &s.add_debrujin_index(Some(0)); - let clauses = bounds.bounds.skip_binders(); - write!( - f, - ": {} = ", - display_self_where_clauses_as_bounds(s, clauses) - )?; - } - write!(f, "{};", bounds.hidden_ty.display(s))?; - Ok(()) - } -} - -/// Like a BoundVar, but with the debrujin index inverted so as to create a -/// canonical name we can use anywhere for each bound variable. -/// -/// In BoundVar, the innermost bound variables have debrujin index `0`, and -/// each further out BoundVar has a debrujin index `1` higher. -/// -/// In InvertedBoundVar, the outermost variables have inverted_debrujin_idx `0`, -/// and the innermost have their depth, not the other way around. -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] -struct InvertedBoundVar { - /// The inverted debrujin index. Corresponds roughly to an inverted `DebrujinIndex::depth`. - inverted_debrujin_idx: i64, - /// The index within the debrujin index. Corresponds to `BoundVar::index`. - within_idx: IndexWithinBinding, -} - -impl Display for InvertedBoundVar { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "_{}_{}", self.inverted_debrujin_idx, self.within_idx) - } -} - -#[derive(Debug)] -struct DefIdAliases { - /// Map from the DefIds we've encountered to a u32 alias id unique to all ids - /// the same name. - aliases: BTreeMap, - /// Map from each name to the next unused u32 alias id. - next_unused_for_name: BTreeMap, -} - -impl Default for DefIdAliases { - fn default() -> Self { - DefIdAliases { - aliases: BTreeMap::default(), - next_unused_for_name: BTreeMap::default(), - } - } -} - -impl DefIdAliases { - fn alias_for_id_name(&mut self, id: T, name: String) -> String { - let next_unused_for_name = &mut self.next_unused_for_name; - let alias = *self.aliases.entry(id).or_insert_with(|| { - let next_unused: &mut u32 = - dbg!(next_unused_for_name.entry(dbg!(&name).clone())).or_default(); - let id = *next_unused; - *next_unused += 1; - dbg!(id) - }); - // If there are no conflicts, keep the name the same so that we don't - // need name-agnostic equality in display tests. - if alias == 0 { - name - } else { - format!("{}_{}", name, alias) - } - } -} - -#[derive(Clone, Debug)] -pub struct WriterState<'a, I: Interner> { - db: &'a dyn RustIrDatabase, - indent_level: usize, - debrujin_indices_deep: u32, - // lowered_(inverted_debrujin_idx, index) -> src_correct_(inverted_debrujin_idx, index) - remapping: Rc>, - // the inverted_bound_var which maps to "Self" - self_mapping: Option, - def_id_aliases: Rc>>, - adt_id_aliases: Rc>>, -} -type IndexWithinBinding = usize; -impl<'a, I: Interner> WriterState<'a, I> { - pub fn new(db: &'a dyn RustIrDatabase) -> Self { - WriterState { - db, - indent_level: 0, - debrujin_indices_deep: 0, - remapping: Rc::new(BTreeMap::new()), - self_mapping: None, - def_id_aliases: Default::default(), - adt_id_aliases: Default::default(), - } - } - - fn add_indent(&self) -> Self { - WriterState { - indent_level: self.indent_level + 1, - ..self.clone() - } - } - - fn indent(&self) -> impl Display { - std::iter::repeat(" ").take(self.indent_level).format("") - } - - fn alias_for_id_name(&self, id: I::DefId, name: String) -> impl Display { - self.def_id_aliases.borrow_mut().alias_for_id_name(id, name) - } - - fn alias_for_adt_id_name(&self, id: I::InternedAdtId, name: String) -> impl Display { - self.adt_id_aliases.borrow_mut().alias_for_id_name(id, name) - } - - /// Adds a level of debrujin index, and possibly a "Self" parameter. - /// - /// This should be called whenever recursing into the value within a - /// [`Binders`]. - /// - /// If `self_binding` is `Some`, then it will introduce a new variable named - /// `Self` with the within-debrujin index given within and the innermost - /// debrujian index after increasing debrujin index. - #[must_use = "this returns a new `WriterState`, and does not modify the existing one"] - fn add_debrujin_index(&self, self_binding: Option) -> Self { - let mut new_state = self.clone(); - new_state.debrujin_indices_deep += 1; - new_state.self_mapping = self_binding - .map(|idx| new_state.indices_for_introduced_bound_var(idx)) - .or(self.self_mapping); - new_state - } - - /// Adds parameter remapping. - /// - /// Each of the parameters in `lowered_vars` will be mapped to its - /// corresponding variable in `original_vars` when printed through the - /// `WriterState` returned from this method. - /// - /// `lowered_vars` and `original_vars` must have the same length. - fn add_parameter_mapping( - &self, - lowered_vars: impl Iterator, - original_vars: impl Iterator, - ) -> Self { - let remapping = self - .remapping - .iter() - .map(|(a, b)| (*a, *b)) - .chain(lowered_vars.zip(original_vars)) - .collect::>(); - - WriterState { - remapping: Rc::new(remapping), - ..self.clone() - } - } - - /// Inverts the debrujin index so as to create a canonical name we can - /// anywhere for each bound variable. - /// - /// See [`InvertedBoundVar`][InvertedBoundVar]. - fn invert_debrujin_idx( - &self, - debrujin_idx: u32, - index: IndexWithinBinding, - ) -> InvertedBoundVar { - InvertedBoundVar { - inverted_debrujin_idx: (self.debrujin_indices_deep as i64) - (debrujin_idx as i64), - within_idx: index, - } - } - - fn apply_mappings(&self, b: InvertedBoundVar) -> impl Display { - let remapped = self.remapping.get(&b).copied().unwrap_or(b); - if self.self_mapping == Some(remapped) { - "Self".to_owned() - } else { - remapped.to_string() - } - } - - fn indices_for_bound_var(&self, b: &BoundVar) -> InvertedBoundVar { - self.invert_debrujin_idx(b.debruijn.depth(), b.index) - } - - fn indices_for_introduced_bound_var(&self, idx: IndexWithinBinding) -> InvertedBoundVar { - // freshly introduced bound vars will always have debrujin index of 0, - // they're always "innermost". - self.invert_debrujin_idx(0, idx) - } - - fn display_bound_var(&self, b: &BoundVar) -> impl Display { - self.apply_mappings(self.indices_for_bound_var(b)) - } - - fn name_for_introduced_bound_var(&self, idx: IndexWithinBinding) -> impl Display { - self.apply_mappings(self.indices_for_introduced_bound_var(idx)) - } - - fn binder_var_indices<'b>( - &'b self, - binders: &'b VariableKinds, - ) -> impl Iterator + 'b { - binders - .iter(self.db.interner()) - .enumerate() - .map(move |(idx, _param)| self.indices_for_introduced_bound_var(idx)) - } - - fn binder_var_display<'b>( - &'b self, - binders: &'b VariableKinds, - ) -> impl Iterator + 'b { - binders - .iter(self.db.interner()) - .zip(self.binder_var_indices(binders)) - .map(move |(parameter, var)| match parameter { - VariableKind::Ty => format!("{}", self.apply_mappings(var)), - VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), - VariableKind::Const(ty) => { - format!("const {}: {}", self.apply_mappings(var), ty.display(self)) - } - }) - } -} diff --git a/chalk-solve/src/display/bounds.rs b/chalk-solve/src/display/bounds.rs new file mode 100644 index 00000000000..4a45c314282 --- /dev/null +++ b/chalk-solve/src/display/bounds.rs @@ -0,0 +1,151 @@ +use std::fmt::{Display, Formatter, Result}; + +use crate::rust_ir::*; +use chalk_ir::{interner::Interner, *}; + +use super::{ + display_trait_with_assoc_ty_value, display_trait_with_generics, render_trait::RenderAsRust, + state::WriterState, +}; +use crate::split::Split; + +impl RenderAsRust for InlineBound { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + // Foo: Vec + InlineBound::TraitBound(trait_bound) => trait_bound.fmt(s, f), + // Foo: Iterator + InlineBound::AliasEqBound(eq_bound) => eq_bound.fmt(s, f), + } + } +} + +impl RenderAsRust for TraitBound { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + display_trait_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) + } +} + +impl RenderAsRust for AliasEqBound { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + display_trait_with_assoc_ty_value( + s, + s.db.associated_ty_data(self.associated_ty_id), + &self.trait_bound.args_no_self, + &self.parameters, + &self.value, + ) + .fmt(f) + } +} + +impl RenderAsRust for QuantifiedWhereClause { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + let s = &s.add_debrujin_index(None); + if !self.binders.is_empty(interner) { + write!( + f, + "forall<{}> ", + s.binder_var_display(&self.binders) + .collect::>() + .join(", ") + )?; + } + self.skip_binders().fmt(s, f) + } +} + +impl RenderAsRust for QuantifiedInlineBound { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + let s = &s.add_debrujin_index(None); + if !self.binders.is_empty(&interner) { + write!( + f, + "forall<{}> ", + s.binder_var_display(&self.binders) + .collect::>() + .join(", ") + )?; + } + self.skip_binders().fmt(s, f) + } +} + +impl RenderAsRust for Vec> { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + write!( + f, + "{}", + self.iter() + .map(|where_clause| { format!("{}{}", s.indent(), where_clause.display(s)) }) + .collect::>() + .join(",\n") + )?; + Ok(()) + } +} + +impl RenderAsRust for WhereClause { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + WhereClause::Implemented(trait_ref) => trait_ref.fmt(s, f), + WhereClause::AliasEq(alias_eq) => alias_eq.fmt(s, f), + } + } +} + +/// This implementation correct inside where clauses. +impl RenderAsRust for TraitRef { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + write!( + f, + "{}: {}", + self.self_type_parameter(interner).display(s), + display_trait_with_generics( + s, + self.trait_id, + &self.substitution.parameters(interner)[1..] + ) + ) + } +} + +/// This implementation correct inside where clauses. +impl RenderAsRust for AliasEq { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // we have: X: Y=D> + // B1, B2, B3, X, A1, A2, A3 are put into alias_eq.alias.substitution + // D is alias_eq.ty + // Z is alias_eq.alias.associated_ty_id + // Y is also packed into alias_eq.alias.associated_ty_id + // Now, we split out A*, Y/Z and B*: + // trait_params is X, A1, A2, A3, + // assoc_type_params is B1, B2, B3, + // assoc_ty_datum stores info about Y and Z. + match &self.alias { + AliasTy::Projection(projection_ty) => { + let (assoc_ty_datum, trait_params, assoc_type_params) = + s.db.split_projection(&projection_ty); + // An alternate form might be `<{} as {}<{}>>::{}<{}> = {}` (with same + // parameter ordering). This alternate form would be using type equality + // constraints (https://github.com/rust-lang/rust/issues/20041). + write!( + f, + "{}: {}", + trait_params[0].display(s), + display_trait_with_assoc_ty_value( + s, + assoc_ty_datum, + &trait_params[1..], + assoc_type_params, + &self.ty + ), + ) + } + AliasTy::Opaque(_) => todo!("opaque types"), + } + } +} diff --git a/chalk-solve/src/display/identifiers.rs b/chalk-solve/src/display/identifiers.rs new file mode 100644 index 00000000000..cec132430a0 --- /dev/null +++ b/chalk-solve/src/display/identifiers.rs @@ -0,0 +1,42 @@ +use std::fmt::{Formatter, Result}; + +use chalk_ir::interner::Interner; +use chalk_ir::*; + +use super::{render_trait::RenderAsRust, state::WriterState}; + +impl RenderAsRust for AdtId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + write!( + f, + "{}", + s.alias_for_adt_id_name(self.0, s.db.adt_name(*self)) + ) + } +} + +impl RenderAsRust for TraitId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + write!(f, "{}", s.alias_for_id_name(self.0, s.db.trait_name(*self))) + } +} + +impl RenderAsRust for AssocTypeId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + f.write_str(&s.db.assoc_type_name(*self)) + } +} + +impl RenderAsRust for OpaqueTyId { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // TODO: use debug methods? + write!( + f, + "{}", + s.alias_for_id_name(self.0, s.db.opaque_type_name(*self)) + ) + } +} diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs new file mode 100644 index 00000000000..9800fc67238 --- /dev/null +++ b/chalk-solve/src/display/items.rs @@ -0,0 +1,274 @@ +use std::fmt::{Formatter, Result}; + +use crate::rust_ir::*; +use crate::split::Split; +use chalk_ir::interner::Interner; +use itertools::Itertools; + +use super::{ + display_self_where_clauses_as_bounds, display_trait_with_generics, render_trait::RenderAsRust, + state::WriterState, +}; + +impl RenderAsRust for AdtDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // When support for Self in structs is added, self_binding should be + // changed to Some(0) + let s = &s.add_debrujin_index(None); + let value = self.binders.skip_binders(); + write!(f, "struct {}", self.id.display(s),)?; + write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.binders.binders), ", ")?; + if !value.where_clauses.is_empty() { + let s = &s.add_indent(); + write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; + } else { + write!(f, " ")?; + } + write!(f, "{{")?; + let s = &s.add_indent(); + write_joined_non_empty_list!( + f, + "\n{}\n", + value.fields.iter().enumerate().map(|(idx, field)| { + format!("{}field_{}: {}", s.indent(), idx, field.display(s)) + }), + ",\n" + )?; + write!(f, "}}")?; + Ok(()) + } +} + +impl RenderAsRust for Polarity { + fn fmt(&self, _s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + if !self.is_positive() { + write!(f, "!")?; + } + Ok(()) + } +} + +impl RenderAsRust for TraitDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let s = &s.add_debrujin_index(Some(0)); + let value = self.binders.skip_binders(); + + macro_rules! trait_flags { + ($($n:ident),*) => { + $(if self.flags.$n { + write!(f,"#[{}]\n",stringify!($n))?; + })* + } + } + + trait_flags!( + auto, + marker, + upstream, + fundamental, + non_enumerable, + coinductive + ); + let binders = s.binder_var_display(&self.binders.binders).skip(1); + write!(f, "trait {}", self.id.display(s))?; + write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; + if !value.where_clauses.is_empty() { + let s = &s.add_indent(); + write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; + } else { + write!(f, " ")?; + } + write!(f, "{{")?; + let s = &s.add_indent(); + write_joined_non_empty_list!( + f, + "\n{}\n", + self.associated_ty_ids.iter().map(|assoc_ty_id| { + let assoc_ty_data = s.db.associated_ty_data(*assoc_ty_id); + format!("{}{}", s.indent(), (*assoc_ty_data).display(s)) + }), + "\n" + )?; + write!(f, "}}")?; + Ok(()) + } +} + +impl RenderAsRust for ImplDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + + let s = &s.add_debrujin_index(None); + let binders = s.binder_var_display(&self.binders.binders); + + let value = self.binders.skip_binders(); + + let trait_ref = &value.trait_ref; + // Ignore automatically added Self parameter by skipping first parameter + let full_trait_name = display_trait_with_generics( + s, + trait_ref.trait_id, + &trait_ref.substitution.parameters(interner)[1..], + ); + write!(f, "impl")?; + write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; + write!( + f, + " {}{} for {}", + self.polarity.display(s), + full_trait_name, + trait_ref.self_type_parameter(interner).display(s) + )?; + if !value.where_clauses.is_empty() { + let s = &s.add_indent(); + write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; + } else { + write!(f, " ")?; + } + write!(f, "{{")?; + { + let s = &s.add_indent(); + let assoc_ty_values = self.associated_ty_value_ids.iter().map(|assoc_ty_value| { + s.db.associated_ty_value(*assoc_ty_value) + .display(s) + .to_string() + }); + write_joined_non_empty_list!(f, "\n{}\n", assoc_ty_values, "\n")?; + } + write!(f, "}}")?; + Ok(()) + } +} + +impl RenderAsRust for OpaqueTyDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + let s = &s.add_debrujin_index(None); + let bounds = self.bound.skip_binders(); + write!(f, "opaque type {}", self.opaque_ty_id.display(s))?; + write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.bound.binders), ", ")?; + { + let s = &s.add_debrujin_index(Some(0)); + let clauses = bounds.bounds.skip_binders(); + write!( + f, + ": {} = ", + display_self_where_clauses_as_bounds(s, clauses) + )?; + } + write!(f, "{};", bounds.hidden_ty.display(s))?; + Ok(()) + } +} + +impl RenderAsRust for AssociatedTyDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // In lowering, a completely new empty environment is created for each + // AssociatedTyDatum, and it's given generic parameters for each generic + // parameter that its trait had. We want to map the new binders for + // those generic parameters back into their original names. To do that, + // first find their original names (trait_binder_names), then the names + // they have inside the AssociatedTyDatum (assoc_ty_names_for_trait_params), + // and then add that mapping to the WriterState when writing bounds and + // where clauses. + let trait_datum = s.db.trait_datum(self.trait_id); + // inverted Debrujin indices for the trait's parameters in the trait + // environment + let trait_param_names_in_trait_env = s.binder_var_indices(&trait_datum.binders.binders); + let s = &s.add_debrujin_index(None); + // inverted Debrujin indices for the trait's parameters in the + // associated type environment + let param_names_in_assoc_ty_env = s + .binder_var_indices(&self.binders.binders) + .collect::>(); + // inverted Debrujin indices to render the trait's parameters in the + // associated type environment + let (trait_param_names_in_assoc_ty_env, _) = + s.db.split_associated_ty_parameters(¶m_names_in_assoc_ty_env, self); + + let s = &s.add_parameter_mapping( + trait_param_names_in_assoc_ty_env.iter().copied(), + trait_param_names_in_trait_env, + ); + + // rendered names for the associated type's generics in the associated + // type environment + let binder_display_in_assoc_ty = s + .binder_var_display(&self.binders.binders) + .collect::>(); + + let (_, assoc_ty_params) = + s.db.split_associated_ty_parameters(&binder_display_in_assoc_ty, self); + write!(f, "type {}", self.id.display(s))?; + write_joined_non_empty_list!(f, "<{}>", assoc_ty_params, ", ")?; + + let datum_bounds = &self.binders.skip_binders(); + + if !datum_bounds.bounds.is_empty() { + write!(f, ": ")?; + } + + // bounds is `A: V, B: D, C = E`? + // type Foo: X + Y + Z; + let bounds = datum_bounds + .bounds + .iter() + .map(|bound| bound.display(s).to_string()) + .collect::>() + .join(" + "); + write!(f, "{}", bounds)?; + + // where_clause is 'X: Y, Z: D' + // type Foo<...>: ... where X: Y, Z: D; + + // note: it's a quantified clause b/c we could have `for<'a> T: Foo<'a>` + // within 'where' + if !datum_bounds.where_clauses.is_empty() { + let where_s = &s.add_indent(); + let where_clauses = datum_bounds.where_clauses.display(where_s); + write!(f, "\n{}where\n{}", s.indent(), where_clauses)?; + } + write!(f, ";")?; + Ok(()) + } +} + +impl RenderAsRust for AssociatedTyValue { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // see comments for a similar empty env operation in AssociatedTyDatum's + // impl of RenderAsRust. + let assoc_ty_data = s.db.associated_ty_data(self.associated_ty_id); + let impl_datum = s.db.impl_datum(self.impl_id); + + let impl_param_names_in_impl_env = s.binder_var_indices(&impl_datum.binders.binders); + + let s = &s.add_debrujin_index(None); + let value = self.value.skip_binders(); + + let param_names_in_assoc_ty_value_env = s + .binder_var_indices(&self.value.binders) + .collect::>(); + + let (impl_params_in_assoc_ty_value_env, _assoc_ty_value_params) = + s.db.split_associated_ty_value_parameters(¶m_names_in_assoc_ty_value_env, self); + + let s = &s.add_parameter_mapping( + impl_params_in_assoc_ty_value_env.iter().cloned(), + impl_param_names_in_impl_env, + ); + + // let params = s + // .binder_var_display(&self.value.binders) + // .collect::>(); + let display_params = s + .binder_var_display(&self.value.binders) + .collect::>(); + + let (_impl_display, assoc_ty_value_display) = + s.db.split_associated_ty_value_parameters(&display_params, self); + + write!(f, "{}type {}", s.indent(), assoc_ty_data.id.display(s))?; + write_joined_non_empty_list!(f, "<{}>", &assoc_ty_value_display, ", ")?; + write!(f, " = {};", value.ty.display(s))?; + Ok(()) + } +} diff --git a/chalk-solve/src/display/render_trait.rs b/chalk-solve/src/display/render_trait.rs new file mode 100644 index 00000000000..e52ab9eb83e --- /dev/null +++ b/chalk-solve/src/display/render_trait.rs @@ -0,0 +1,28 @@ +use std::fmt::{Display, Formatter, Result}; + +use chalk_ir::interner::Interner; + +use super::state::WriterState; + +/// Displays `RenderAsRust` data. +/// +/// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. +pub struct DisplayRenderAsRust<'a, I: Interner, T> { + s: &'a WriterState<'a, I>, + rar: &'a T, +} + +impl> Display for DisplayRenderAsRust<'_, I, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + self.rar.fmt(self.s, f) + } +} +pub trait RenderAsRust { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result; + fn display<'a>(&'a self, s: &'a WriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> + where + Self: Sized, + { + DisplayRenderAsRust { s, rar: self } + } +} diff --git a/chalk-solve/src/display/state.rs b/chalk-solve/src/display/state.rs new file mode 100644 index 00000000000..9c70afbe962 --- /dev/null +++ b/chalk-solve/src/display/state.rs @@ -0,0 +1,233 @@ +use std::{ + cell::RefCell, + collections::BTreeMap, + fmt::{Display, Formatter, Result}, + rc::Rc, +}; + +use crate::RustIrDatabase; +use chalk_ir::{interner::Interner, *}; +use itertools::Itertools; + +use super::render_trait::RenderAsRust; + +/// Like a BoundVar, but with the debrujin index inverted so as to create a +/// canonical name we can use anywhere for each bound variable. +/// +/// In BoundVar, the innermost bound variables have debrujin index `0`, and +/// each further out BoundVar has a debrujin index `1` higher. +/// +/// In InvertedBoundVar, the outermost variables have inverted_debrujin_idx `0`, +/// and the innermost have their depth, not the other way around. +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct InvertedBoundVar { + /// The inverted debrujin index. Corresponds roughly to an inverted `DebrujinIndex::depth`. + inverted_debrujin_idx: i64, + /// The index within the debrujin index. Corresponds to `BoundVar::index`. + within_idx: IndexWithinBinding, +} + +impl Display for InvertedBoundVar { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "_{}_{}", self.inverted_debrujin_idx, self.within_idx) + } +} + +#[derive(Debug)] +pub struct DefIdAliases { + /// Map from the DefIds we've encountered to a u32 alias id unique to all ids + /// the same name. + aliases: BTreeMap, + /// Map from each name to the next unused u32 alias id. + next_unused_for_name: BTreeMap, +} + +impl Default for DefIdAliases { + fn default() -> Self { + DefIdAliases { + aliases: BTreeMap::default(), + next_unused_for_name: BTreeMap::default(), + } + } +} + +impl DefIdAliases { + fn alias_for_id_name(&mut self, id: T, name: String) -> String { + let next_unused_for_name = &mut self.next_unused_for_name; + let alias = *self.aliases.entry(id).or_insert_with(|| { + let next_unused: &mut u32 = + dbg!(next_unused_for_name.entry(dbg!(&name).clone())).or_default(); + let id = *next_unused; + *next_unused += 1; + dbg!(id) + }); + // If there are no conflicts, keep the name the same so that we don't + // need name-agnostic equality in display tests. + if alias == 0 { + name + } else { + format!("{}_{}", name, alias) + } + } +} + +#[derive(Clone, Debug)] +pub struct WriterState<'a, I: Interner> { + pub(super) db: &'a dyn RustIrDatabase, + indent_level: usize, + debrujin_indices_deep: u32, + // lowered_(inverted_debrujin_idx, index) -> src_correct_(inverted_debrujin_idx, index) + remapping: Rc>, + // the inverted_bound_var which maps to "Self" + self_mapping: Option, + def_id_aliases: Rc>>, + adt_id_aliases: Rc>>, +} + +type IndexWithinBinding = usize; +impl<'a, I: Interner> WriterState<'a, I> { + pub fn new(db: &'a dyn RustIrDatabase) -> Self { + WriterState { + db, + indent_level: 0, + debrujin_indices_deep: 0, + remapping: Rc::new(BTreeMap::new()), + self_mapping: None, + def_id_aliases: Default::default(), + adt_id_aliases: Default::default(), + } + } + + pub(super) fn add_indent(&self) -> Self { + WriterState { + indent_level: self.indent_level + 1, + ..self.clone() + } + } + + pub(super) fn indent(&self) -> impl Display { + std::iter::repeat(" ").take(self.indent_level).format("") + } + + pub(super) fn alias_for_id_name(&self, id: I::DefId, name: String) -> impl Display { + self.def_id_aliases.borrow_mut().alias_for_id_name(id, name) + } + + pub(super) fn alias_for_adt_id_name(&self, id: I::InternedAdtId, name: String) -> impl Display { + self.adt_id_aliases.borrow_mut().alias_for_id_name(id, name) + } + + /// Adds a level of debrujin index, and possibly a "Self" parameter. + /// + /// This should be called whenever recursing into the value within a + /// [`Binders`]. + /// + /// If `self_binding` is `Some`, then it will introduce a new variable named + /// `Self` with the within-debrujin index given within and the innermost + /// debrujian index after increasing debrujin index. + #[must_use = "this returns a new `WriterState`, and does not modify the existing one"] + pub(super) fn add_debrujin_index(&self, self_binding: Option) -> Self { + let mut new_state = self.clone(); + new_state.debrujin_indices_deep += 1; + new_state.self_mapping = self_binding + .map(|idx| new_state.indices_for_introduced_bound_var(idx)) + .or(self.self_mapping); + new_state + } + + /// Adds parameter remapping. + /// + /// Each of the parameters in `lowered_vars` will be mapped to its + /// corresponding variable in `original_vars` when printed through the + /// `WriterState` returned from this method. + /// + /// `lowered_vars` and `original_vars` must have the same length. + pub(super) fn add_parameter_mapping( + &self, + lowered_vars: impl Iterator, + original_vars: impl Iterator, + ) -> Self { + let remapping = self + .remapping + .iter() + .map(|(a, b)| (*a, *b)) + .chain(lowered_vars.zip(original_vars)) + .collect::>(); + + WriterState { + remapping: Rc::new(remapping), + ..self.clone() + } + } + + /// Inverts the debrujin index so as to create a canonical name we can + /// anywhere for each bound variable. + /// + /// See [`InvertedBoundVar`][InvertedBoundVar]. + pub(super) fn invert_debrujin_idx( + &self, + debrujin_idx: u32, + index: IndexWithinBinding, + ) -> InvertedBoundVar { + InvertedBoundVar { + inverted_debrujin_idx: (self.debrujin_indices_deep as i64) - (debrujin_idx as i64), + within_idx: index, + } + } + + pub(super) fn apply_mappings(&self, b: InvertedBoundVar) -> impl Display { + let remapped = self.remapping.get(&b).copied().unwrap_or(b); + if self.self_mapping == Some(remapped) { + "Self".to_owned() + } else { + remapped.to_string() + } + } + + pub(super) fn indices_for_bound_var(&self, b: &BoundVar) -> InvertedBoundVar { + self.invert_debrujin_idx(b.debruijn.depth(), b.index) + } + + pub(super) fn indices_for_introduced_bound_var( + &self, + idx: IndexWithinBinding, + ) -> InvertedBoundVar { + // freshly introduced bound vars will always have debrujin index of 0, + // they're always "innermost". + self.invert_debrujin_idx(0, idx) + } + + pub(super) fn display_bound_var(&self, b: &BoundVar) -> impl Display { + self.apply_mappings(self.indices_for_bound_var(b)) + } + + pub(super) fn name_for_introduced_bound_var(&self, idx: IndexWithinBinding) -> impl Display { + self.apply_mappings(self.indices_for_introduced_bound_var(idx)) + } + + pub(super) fn binder_var_indices<'b>( + &'b self, + binders: &'b VariableKinds, + ) -> impl Iterator + 'b { + binders + .iter(self.db.interner()) + .enumerate() + .map(move |(idx, _param)| self.indices_for_introduced_bound_var(idx)) + } + + pub(super) fn binder_var_display<'b>( + &'b self, + binders: &'b VariableKinds, + ) -> impl Iterator + 'b { + binders + .iter(self.db.interner()) + .zip(self.binder_var_indices(binders)) + .map(move |(parameter, var)| match parameter { + VariableKind::Ty => format!("{}", self.apply_mappings(var)), + VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), + VariableKind::Const(ty) => { + format!("const {}: {}", self.apply_mappings(var), ty.display(self)) + } + }) + } +} diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs new file mode 100644 index 00000000000..b741d1475ca --- /dev/null +++ b/chalk-solve/src/display/ty.rs @@ -0,0 +1,310 @@ +use std::fmt::{Formatter, Result}; + +use crate::split::Split; +use chalk_ir::{interner::Interner, *}; +use itertools::Itertools; + +use super::{ + display_self_where_clauses_as_bounds, display_trait_with_generics, render_trait::RenderAsRust, + state::WriterState, +}; + +impl RenderAsRust for TyData { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + match self { + TyData::Dyn(dyn_ty) => { + let s = &s.add_debrujin_index(None); + // dyn_ty.bounds.binders creates a Self binding for the trait + let bounds = dyn_ty.bounds.skip_binders(); + write!( + f, + "dyn {}", + display_self_where_clauses_as_bounds(s, bounds.as_slice(interner)) + )?; + Ok(()) + } + TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), + TyData::InferenceVar(_) => write!(f, "_"), + TyData::Alias(alias_ty) => alias_ty.fmt(s, f), + TyData::Apply(apply_ty) => apply_ty.fmt(s, f), + TyData::Function(func) => func.fmt(s, f), + TyData::Placeholder(_) => unreachable!( + "cannot print placeholder variables; these should only be in goals not programs" + ), + } + } +} + +impl RenderAsRust for AliasTy { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + AliasTy::Projection(projection_ty) => projection_ty.fmt(s, f), + AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(s, f), + } + } +} + +impl RenderAsRust for ProjectionTy { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // >::Z + + // Now, we split out A*, Y/Z and B*: + // trait_params is X, A1, A2, A3, + // assoc_type_params is B1, B2, B3, + // assoc_ty_datum stores info about Y and Z. + let (assoc_ty_datum, trait_params, assoc_type_params) = s.db.split_projection(&self); + write!( + f, + "<{} as {}>::{}", + trait_params[0].display(s), + display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), + assoc_ty_datum.id.display(s), + )?; + write_joined_non_empty_list!( + f, + "<{}>", + assoc_type_params.iter().map(|param| param.display(s)), + ", " + )?; + Ok(()) + } +} +impl RenderAsRust for OpaqueTy { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + write!( + f, + "{}", + display_trait_with_generics( + s, + self.opaque_ty_id, + self.substitution.parameters(interner), + ) + ) + } +} + +impl RenderAsRust for Fn { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + let s = &s.add_debrujin_index(None); + if self.num_binders > 0 { + write!( + f, + "for<{}> ", + (0..self.num_binders) + .map(|n| format!("'{}", s.name_for_introduced_bound_var(n))) + .collect::>() + .join(", ") + )?; + } + write!( + f, + "fn({})", + self.substitution + .parameters(interner) + .iter() + .map(|param| param.display(s).to_string()) + .collect::>() + .join(", ") + ) + } +} + +impl RenderAsRust for ApplicationTy { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + let interner = s.db.interner(); + match self.name { + TypeName::Adt(sid) => { + write!(f, "{}", sid.display(s))?; + let parameters = self.substitution.parameters(interner); + let parameters = parameters.iter().map(|param| param.display(s)); + write_joined_non_empty_list!(f, "<{}>", parameters, ", ")?; + } + TypeName::AssociatedType(assoc_type_id) => { + // (Iterator::Item)(x) + // should be written in Rust as ::Item + let datum = s.db.associated_ty_data(assoc_type_id); + assert!( + self.len_type_parameters(interner) >= 1, + "AssociatedType should have at least 1 parameter" + ); + write!( + f, + "<{} as {}>::{}", + self.first_type_parameter(interner).unwrap().display(s), + datum.trait_id.display(s), + datum.id.display(s), + )?; + let params = self.substitution.parameters(interner); + write_joined_non_empty_list!( + f, + "<{}>", + params[1..].iter().map(|ty| ty.display(s)), + "," + )?; + } + TypeName::Scalar(scalar) => write!(f, "{}", scalar.display(s))?, + TypeName::Tuple(arity) => { + write!( + f, + "({}{})", + self.substitution + .parameters(interner) + .iter() + .map(|p| p.display(s)) + .format(", "), + if arity == 1 { + // need trailing single comma + "," + } else { + "" + } + )? + } + TypeName::OpaqueType(_) => todo!("opaque type usage"), + TypeName::Raw(raw) => { + let mutability = match raw { + Mutability::Mut => "*mut ", + Mutability::Not => "*const ", + }; + write!( + f, + "{}{}", + mutability, + self.first_type_parameter(interner).unwrap().display(s) + )? + } + TypeName::Ref(mutability) => { + let mutability = match mutability { + Mutability::Mut => "mut ", + Mutability::Not => "", + }; + write!( + f, + "&{} {}{}", + self.substitution.at(interner, 0).display(s), + mutability, + self.substitution.at(interner, 1).display(s) + )?; + } + TypeName::Str => write!(f, "str")?, + TypeName::Slice => { + write!( + f, + "[{}]", + self.first_type_parameter(interner).unwrap().display(s) + )?; + } + TypeName::Error => write!(f, "{{error}}")?, + TypeName::Never => todo!("never type"), + TypeName::FnDef(_) => todo!("fn def type"), + } + Ok(()) + } +} + +impl RenderAsRust for Scalar { + fn fmt(&self, _s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + use chalk_ir::{FloatTy::*, IntTy::*, UintTy::*}; + write!( + f, + "{}", + match self { + Scalar::Bool => "bool", + Scalar::Char => "char", + Scalar::Int(int) => match int { + Isize => "isize", + I8 => "i8", + I16 => "i16", + I32 => "i32", + I64 => "i64", + I128 => "i128", + }, + Scalar::Uint(uint) => match uint { + Usize => "usize", + U8 => "u8", + U16 => "u16", + U32 => "u32", + U64 => "u64", + U128 => "u128", + }, + Scalar::Float(float) => match float { + F32 => "f32", + F64 => "f64", + }, + } + ) + } +} + +impl RenderAsRust for LifetimeData { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + LifetimeData::BoundVar(v) => write!(f, "'{}", s.display_bound_var(v)), + LifetimeData::InferenceVar(_) => write!(f, "'_"), + LifetimeData::Placeholder(_) => unreachable!( + "cannot print placeholder variables; these should only be in goals not programs" + ), + // Matching the void ensures at compile time that this code is + // unreachable + LifetimeData::Phantom(void, _) => match *void {}, + } + } +} + +impl RenderAsRust for ConstData { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + write!(f, "{}", self.value.display(s)) + } +} + +impl RenderAsRust for ConstValue { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + match self { + ConstValue::BoundVar(v) => write!(f, "{}", s.display_bound_var(v)), + ConstValue::InferenceVar(_) => write!(f, "_"), + ConstValue::Placeholder(_) => unreachable!( + "cannot print placeholder variables; these should only be in goals not programs" + ), + ConstValue::Concrete(_value) => unimplemented!("const values"), + } + } +} + +impl RenderAsRust for GenericArgData { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + match self { + GenericArgData::Ty(ty) => write!(f, "{}", ty.display(s)), + GenericArgData::Lifetime(lt) => write!(f, "{}", lt.display(s)), + GenericArgData::Const(const_ty) => write!(f, "{}", const_ty.display(s)), + } + } +} + +impl RenderAsRust for Ty { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // delegate to TyData + self.data(s.db.interner()).fmt(s, f) + } +} +impl RenderAsRust for Lifetime { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // delegate to LifetimeData + self.data(s.db.interner()).fmt(s, f) + } +} + +impl RenderAsRust for Const { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + self.data(s.db.interner()).fmt(s, f) + } +} + +impl RenderAsRust for GenericArg { + fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { + // delegate to GenericArgData + self.data(s.db.interner()).fmt(s, f) + } +} diff --git a/chalk-solve/src/display/utils.rs b/chalk-solve/src/display/utils.rs new file mode 100644 index 00000000000..955ee2a9555 --- /dev/null +++ b/chalk-solve/src/display/utils.rs @@ -0,0 +1,24 @@ +use std::fmt::{Display, Formatter, Result}; + +pub fn as_display) -> Result>(f: F) -> impl Display { + struct ClosureDisplay) -> Result>(F); + + impl) -> Result> Display for ClosureDisplay { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + self.0(f) + } + } + + ClosureDisplay(f) +} + +macro_rules! write_joined_non_empty_list { + ($f:expr,$template:tt,$list:expr,$sep:expr) => {{ + let mut x = $list.into_iter().peekable(); + if x.peek().is_some() { + write!($f, $template, x.format($sep)) + } else { + Ok(()) + } + }}; +} From dec01658afc0da383e7b5c8731253d77d94a537d Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 24 May 2020 00:51:21 -0700 Subject: [PATCH 39/70] Add linebreaks and fix imports Added linebreaks between function definitions within traits and toplevel items. Grouped messy imports. Co-authored-by: David Ross --- chalk-integration/src/db.rs | 1 + chalk-solve/src/display/render_trait.rs | 1 + chalk-solve/src/display/ty.rs | 2 + chalk-solve/src/logging_db.rs | 52 +++++++++++++++++++++++-- tests/display/assoc_ty.rs | 1 + tests/display/lifetimes.rs | 1 + tests/display/where_clauses.rs | 1 + tests/integration/panic.rs | 4 ++ 8 files changed, 59 insertions(+), 4 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index c2cba6a3c23..a4d1f8632a1 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -209,6 +209,7 @@ impl RustIrDatabase for ChalkDatabase { fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.program_ir().unwrap().assoc_type_name(assoc_ty_id) } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.program_ir().unwrap().opaque_type_name(opaque_ty_id) } diff --git a/chalk-solve/src/display/render_trait.rs b/chalk-solve/src/display/render_trait.rs index e52ab9eb83e..3800b5fa7ef 100644 --- a/chalk-solve/src/display/render_trait.rs +++ b/chalk-solve/src/display/render_trait.rs @@ -17,6 +17,7 @@ impl> Display for DisplayRenderAsRust<'_, I, T> self.rar.fmt(self.s, f) } } + pub trait RenderAsRust { fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result; fn display<'a>(&'a self, s: &'a WriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index b741d1475ca..61ed262362c 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -70,6 +70,7 @@ impl RenderAsRust for ProjectionTy { Ok(()) } } + impl RenderAsRust for OpaqueTy { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { let interner = s.db.interner(); @@ -289,6 +290,7 @@ impl RenderAsRust for Ty { self.data(s.db.interner()).fmt(s, f) } } + impl RenderAsRust for Lifetime { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // delegate to LifetimeData diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index ef62b743e59..73da4dc116b 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -1,15 +1,20 @@ //! Provides wrappers over `RustIrDatabase` which record used definitions and write //! `.chalk` files containing those definitions. use std::{ - borrow::Borrow, cmp::Ord, cmp::Ordering, collections::BTreeSet, fmt, fmt::Display, io::Write, - marker::PhantomData, sync::Arc, sync::Mutex, + borrow::Borrow, + cmp::{Ord, Ordering}, + collections::BTreeSet, + fmt::{self, Debug, Display}, + io::Write, + marker::PhantomData, + sync::Arc, + sync::Mutex, }; use crate::rust_ir::*; +use crate::{display, RustIrDatabase}; use chalk_ir::{interner::Interner, *}; -use crate::{display, RustIrDatabase}; -use fmt::Debug; /// Wraps another `RustIrDatabase` (`DB`) and records which definitions are /// used. /// @@ -66,6 +71,7 @@ where fn record(&self, id: impl Into>) { self.def_ids.lock().unwrap().insert(id.into()); } + fn record_all(&self, ids: T) where T: IntoIterator, @@ -87,6 +93,7 @@ where fn custom_clauses(&self) -> Vec> { self.db.borrow().custom_clauses() } + fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, @@ -95,18 +102,22 @@ where self.record(ty_datum.trait_id); ty_datum } + fn trait_datum(&self, trait_id: TraitId) -> Arc> { self.record(trait_id); self.db.borrow().trait_datum(trait_id) } + fn adt_datum(&self, adt_id: AdtId) -> Arc> { self.record(adt_id); self.db.borrow().adt_datum(adt_id) } + fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.record(impl_id); self.db.borrow().impl_datum(impl_id) } + fn associated_ty_value( &self, id: crate::rust_ir::AssociatedTyValueId, @@ -115,10 +126,12 @@ where self.record(value.impl_id); value } + fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { self.record(id); self.db.borrow().opaque_ty_data(id) } + fn impls_for_trait( &self, trait_id: TraitId, @@ -129,15 +142,18 @@ where self.record_all(impl_ids.iter().copied()); impl_ids } + fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { self.record(trait_id); self.db.borrow().local_impls_to_coherence_check(trait_id) } + fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { self.record(auto_trait_id); self.record(adt_id); self.db.borrow().impl_provided_for(auto_trait_id, adt_id) } + fn well_known_trait_id( &self, well_known_trait: crate::rust_ir::WellKnownTrait, @@ -146,31 +162,39 @@ where trait_id.map(|id| self.record(id)); trait_id } + fn program_clauses_for_env( &self, environment: &chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { self.db.borrow().program_clauses_for_env(environment) } + fn interner(&self) -> &I { self.db.borrow().interner() } + fn trait_name(&self, trait_id: TraitId) -> String { self.db.borrow().trait_name(trait_id) } + fn adt_name(&self, adt_id: AdtId) -> String { self.db.borrow().adt_name(adt_id) } + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.db.borrow().assoc_type_name(assoc_ty_id) } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.db.borrow().opaque_type_name(opaque_ty_id) } + fn is_object_safe(&self, trait_id: TraitId) -> bool { self.record(trait_id); self.db.borrow().is_object_safe(trait_id) } + fn fn_def_datum(&self, _fn_def_id: chalk_ir::FnDefId) -> Arc> { todo!("function def datum") } @@ -252,30 +276,37 @@ where fn custom_clauses(&self) -> Vec> { self.db.borrow().custom_clauses() } + fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, ) -> Arc> { self.db.borrow().associated_ty_data(ty) } + fn trait_datum(&self, trait_id: TraitId) -> Arc> { self.db.borrow().trait_datum(trait_id) } + fn adt_datum(&self, adt_id: AdtId) -> Arc> { self.db.borrow().adt_datum(adt_id) } + fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.db.borrow().impl_datum(impl_id) } + fn associated_ty_value( &self, id: crate::rust_ir::AssociatedTyValueId, ) -> Arc> { self.db.borrow().associated_ty_value(id) } + fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { self.db.borrow().opaque_ty_data(id) } + fn impls_for_trait( &self, trait_id: TraitId, @@ -283,42 +314,53 @@ where ) -> Vec> { self.db.borrow().impls_for_trait(trait_id, parameters) } + fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec> { self.db.borrow().local_impls_to_coherence_check(trait_id) } + fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { self.db.borrow().impl_provided_for(auto_trait_id, adt_id) } + fn well_known_trait_id( &self, well_known_trait: crate::rust_ir::WellKnownTrait, ) -> Option> { self.db.borrow().well_known_trait_id(well_known_trait) } + fn program_clauses_for_env( &self, environment: &chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { self.db.borrow().program_clauses_for_env(environment) } + fn interner(&self) -> &I { self.db.borrow().interner() } + fn is_object_safe(&self, trait_id: TraitId) -> bool { self.db.borrow().is_object_safe(trait_id) } + fn trait_name(&self, trait_id: TraitId) -> String { self.db.borrow().trait_name(trait_id) } + fn adt_name(&self, adt_id: AdtId) -> String { self.db.borrow().adt_name(adt_id) } + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.db.borrow().assoc_type_name(assoc_ty_id) } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.db.borrow().opaque_type_name(opaque_ty_id) } + fn fn_def_datum(&self, _fn_def_id: chalk_ir::FnDefId) -> Arc> { todo!("function def datum") } @@ -337,6 +379,7 @@ impl From> for RecordedItemId { RecordedItemId::Adt(v) } } + impl From> for RecordedItemId { fn from(v: TraitId) -> Self { RecordedItemId::Trait(v) @@ -348,6 +391,7 @@ impl From> for RecordedItemId { RecordedItemId::Impl(v) } } + impl From> for RecordedItemId { fn from(v: OpaqueTyId) -> Self { RecordedItemId::OpaqueTy(v) diff --git a/tests/display/assoc_ty.rs b/tests/display/assoc_ty.rs index 34b0d585496..a804547ad26 100644 --- a/tests/display/assoc_ty.rs +++ b/tests/display/assoc_ty.rs @@ -83,6 +83,7 @@ fn test_trait_impl_associated_type_with_generics() { } ); } + #[test] fn test_simple_assoc_type() { reparse_test!( diff --git a/tests/display/lifetimes.rs b/tests/display/lifetimes.rs index 2b5b3ee2b99..8f5b32482ac 100644 --- a/tests/display/lifetimes.rs +++ b/tests/display/lifetimes.rs @@ -14,6 +14,7 @@ fn test_various_forall() { } ); } + #[test] fn test_lifetimes_in_structs() { reparse_test!( diff --git a/tests/display/where_clauses.rs b/tests/display/where_clauses.rs index 3a34a47303b..3221e542910 100644 --- a/tests/display/where_clauses.rs +++ b/tests/display/where_clauses.rs @@ -68,6 +68,7 @@ fn test_trait_projection() { } ); } + #[test] fn test_trait_projection_with_dyn_arg() { reparse_test!( diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index 997d4f87163..b41544bec34 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -221,15 +221,19 @@ impl RustIrDatabase for MockDatabase { ) -> Substitution { unimplemented!() } + fn trait_name(&self, trait_id: TraitId) -> String { unimplemented!() } + fn adt_name(&self, struct_id: AdtId) -> String { unimplemented!() } + fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { unimplemented!() } + fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { unimplemented!() } From 485d363b1890e468860f064bf339ecc29044b622 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 24 May 2020 00:57:48 -0700 Subject: [PATCH 40/70] Remove unused dbg!() Co-authored-by: David Ross --- chalk-solve/src/display/state.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chalk-solve/src/display/state.rs b/chalk-solve/src/display/state.rs index 9c70afbe962..02b7f1292db 100644 --- a/chalk-solve/src/display/state.rs +++ b/chalk-solve/src/display/state.rs @@ -55,11 +55,10 @@ impl DefIdAliases { fn alias_for_id_name(&mut self, id: T, name: String) -> String { let next_unused_for_name = &mut self.next_unused_for_name; let alias = *self.aliases.entry(id).or_insert_with(|| { - let next_unused: &mut u32 = - dbg!(next_unused_for_name.entry(dbg!(&name).clone())).or_default(); + let next_unused: &mut u32 = next_unused_for_name.entry(name.clone()).or_default(); let id = *next_unused; *next_unused += 1; - dbg!(id) + id }); // If there are no conflicts, keep the name the same so that we don't // need name-agnostic equality in display tests. From 4aeb6f4cd7e87956a0c2eacb94efd7e2a7ac15e9 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 16:13:13 -0700 Subject: [PATCH 41/70] Add comment justifying manual reparse goal Co-authored-by: David Ross --- tests/display/where_clauses.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/display/where_clauses.rs b/tests/display/where_clauses.rs index 3221e542910..ee07cf562f4 100644 --- a/tests/display/where_clauses.rs +++ b/tests/display/where_clauses.rs @@ -1,5 +1,13 @@ #[test] fn test_complicated_bounds() { + // This test uses "produces" as a workaround for the lowering & writing + // code's behavior. Specifically, `Foo: Bax` will be transformed + // into `Foo: Bax, Foo: Bax`, even if the where clause already + // includes `Foo: Bax`. The writer code doesn't remove that addition. + // + // No matter how many `Foo: Bax` we have in our input, we're always going + // to get an extra `Foo: Bax` in the output, so they'll never be equal + // and we need the separate output program. reparse_test!( program { struct Foo { } From 4a349e8f27b616402762ce9d905acd9a4fc40578 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 16:17:33 -0700 Subject: [PATCH 42/70] Rename write_program to write_items This function writes an arbitrary set of toplevel items into a program. The name write_program implies that the input program is a holistic program, or of the `Program` type. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 17 +++++++++-------- chalk-solve/src/logging_db.rs | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 6fe60db0ab4..45cb88a6843 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -1,5 +1,5 @@ use std::{ - fmt::{Display, Formatter, Result}, + fmt::{Display, Result}, sync::Arc, }; @@ -24,11 +24,11 @@ pub use self::state::*; use self::utils::as_display; -pub fn write_top_level(f: &mut F, ws: &WriterState<'_, I>, v: &T) -> Result +pub fn write_item(f: &mut F, ws: &WriterState<'_, I>, v: &T) -> Result where + F: std::fmt::Write + ?Sized, I: Interner, T: RenderAsRust, - F: std::fmt::Write, { write!(f, "{}\n", v.display(ws)) } @@ -36,8 +36,9 @@ where /// Writes out each item recorded by a [`LoggingRustIrDatabase`]. /// /// [`LoggingRustIrDatabase`]: crate::logging_db::LoggingRustIrDatabase -pub fn write_program(f: &mut Formatter<'_>, db: &DB, ids: T) -> Result +pub fn write_items(f: &mut F, db: &DB, ids: T) -> Result where + F: std::fmt::Write + ?Sized, I: Interner, DB: RustIrDatabase, T: IntoIterator>, @@ -47,19 +48,19 @@ where match id { RecordedItemId::Impl(id) => { let v = db.impl_datum(id); - write_top_level(f, ws, &*v)?; + write_item(f, ws, &*v)?; } RecordedItemId::Adt(id) => { let v = db.adt_datum(id); - write_top_level(f, ws, &*v)?; + write_item(f, ws, &*v)?; } RecordedItemId::Trait(id) => { let v = db.trait_datum(id); - write_top_level(f, ws, &*v)?; + write_item(f, ws, &*v)?; } RecordedItemId::OpaqueTy(id) => { let v = db.opaque_ty_data(id); - write_top_level(f, ws, &*v)?; + write_item(f, ws, &*v)?; } } } diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 73da4dc116b..44a2b5d6edd 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -58,7 +58,7 @@ where { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let def_ids = self.def_ids.lock().unwrap(); - display::write_program(f, self.db.borrow(), def_ids.iter().copied()) + display::write_items(f, self.db.borrow(), def_ids.iter().copied()) } } From d0ddb6b8f87f3dc17793d6d31066cad6cf086a2f Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 20:59:46 -0700 Subject: [PATCH 43/70] Ignore TyKind data TyKind provides hints at the variables type. This information is not written in the program output, and can be safely ignored. Co-authored-by: David Ross --- chalk-solve/src/display/state.rs | 2 +- chalk-solve/src/display/ty.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chalk-solve/src/display/state.rs b/chalk-solve/src/display/state.rs index 02b7f1292db..05fe19a94c0 100644 --- a/chalk-solve/src/display/state.rs +++ b/chalk-solve/src/display/state.rs @@ -222,7 +222,7 @@ impl<'a, I: Interner> WriterState<'a, I> { .iter(self.db.interner()) .zip(self.binder_var_indices(binders)) .map(move |(parameter, var)| match parameter { - VariableKind::Ty => format!("{}", self.apply_mappings(var)), + VariableKind::Ty(_) => format!("{}", self.apply_mappings(var)), VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), VariableKind::Const(ty) => { format!("const {}: {}", self.apply_mappings(var), ty.display(self)) diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index 61ed262362c..a47f25f0325 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -25,7 +25,7 @@ impl RenderAsRust for TyData { Ok(()) } TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), - TyData::InferenceVar(_) => write!(f, "_"), + TyData::InferenceVar(_, _) => write!(f, "_"), TyData::Alias(alias_ty) => alias_ty.fmt(s, f), TyData::Apply(apply_ty) => apply_ty.fmt(s, f), TyData::Function(func) => func.fmt(s, f), From 4083532d6b37613ba5859bcdb15aed5aaadbd311 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 21:02:08 -0700 Subject: [PATCH 44/70] Update usage of hidden opaque types Updated the uesage of hidden opaques types to use the newly introduced method on `RustIrDatabase` rather than accessing the underlying data which has been hidden. Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 6 +++++- chalk-solve/src/logging_db.rs | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 9800fc67238..c299a6b698d 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -155,7 +155,11 @@ impl RenderAsRust for OpaqueTyDatum { display_self_where_clauses_as_bounds(s, clauses) )?; } - write!(f, "{};", bounds.hidden_ty.display(s))?; + write!( + f, + "{};", + s.db.hidden_opaque_type(self.opaque_ty_id).display(s) + )?; Ok(()) } } diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 44a2b5d6edd..082d184cb31 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -118,6 +118,11 @@ where self.db.borrow().impl_datum(impl_id) } + fn hidden_opaque_type(&self, id: OpaqueTyId) -> Ty { + self.record(id); + self.db.borrow().hidden_opaque_type(id) + } + fn associated_ty_value( &self, id: crate::rust_ir::AssociatedTyValueId, @@ -307,6 +312,10 @@ where self.db.borrow().opaque_ty_data(id) } + fn hidden_opaque_type(&self, id: OpaqueTyId) -> Ty { + self.db.borrow().hidden_opaque_type(id) + } + fn impls_for_trait( &self, trait_id: TraitId, From c54aa5ab361986c394407d794fab01f296613277 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 21:04:19 -0700 Subject: [PATCH 45/70] Implement RenderAsRust for LifetimeOutlives Add display code for LifetimeOutlives. This represents bounded liftimes such as `'a: 'b`. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 1 + chalk-solve/src/display/bounds.rs | 8 ++++++++ tests/display/lifetimes.rs | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 45cb88a6843..151d49cb59c 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -130,6 +130,7 @@ fn display_self_where_clauses_as_bounds<'a, I: Interner>( } AliasTy::Opaque(_opaque) => todo!("opaque type AliasTy"), }, + WhereClause::LifetimeOutlives(lifetime) => lifetime.display(s).fmt(f), } }) .to_string() diff --git a/chalk-solve/src/display/bounds.rs b/chalk-solve/src/display/bounds.rs index 4a45c314282..33c972ccb44 100644 --- a/chalk-solve/src/display/bounds.rs +++ b/chalk-solve/src/display/bounds.rs @@ -92,6 +92,7 @@ impl RenderAsRust for WhereClause { match self { WhereClause::Implemented(trait_ref) => trait_ref.fmt(s, f), WhereClause::AliasEq(alias_eq) => alias_eq.fmt(s, f), + WhereClause::LifetimeOutlives(lifetime) => lifetime.display(s).fmt(f), } } } @@ -149,3 +150,10 @@ impl RenderAsRust for AliasEq { } } } + +impl RenderAsRust for LifetimeOutlives { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + // a': 'b + write!(f, "{}: {}", self.a.display(s), self.b.display(s)) + } +} diff --git a/tests/display/lifetimes.rs b/tests/display/lifetimes.rs index 8f5b32482ac..a1270a5d435 100644 --- a/tests/display/lifetimes.rs +++ b/tests/display/lifetimes.rs @@ -25,3 +25,26 @@ fn test_lifetimes_in_structs() { } ); } + +#[test] +fn test_lifetime_outlives() { + reparse_test!( + program { + struct Foo<'a, 'b> + where + 'a: 'b + { } + + trait Baz<'a,'b> + where + 'a: 'b + { } + + impl<'a,'b,'c> Baz<'a,'b> for Foo<'a,'c> + where + 'a: 'c, + 'b: 'c + { } + } + ); +} From 24cb65be4481f25a8af933ed72b142b889b24318 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 21:05:51 -0700 Subject: [PATCH 46/70] Test and fix const generics Added tests for const generics, and removed the type bound, which is not supported. The type bound would appears as `Foo`, but only `Foo` is supported. Co-authored-by: David Ross --- chalk-solve/src/display/state.rs | 4 +--- tests/display/const_.rs | 18 ++++++++++++++++++ tests/display/mod.rs | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tests/display/const_.rs diff --git a/chalk-solve/src/display/state.rs b/chalk-solve/src/display/state.rs index 05fe19a94c0..fc88c597194 100644 --- a/chalk-solve/src/display/state.rs +++ b/chalk-solve/src/display/state.rs @@ -224,9 +224,7 @@ impl<'a, I: Interner> WriterState<'a, I> { .map(move |(parameter, var)| match parameter { VariableKind::Ty(_) => format!("{}", self.apply_mappings(var)), VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), - VariableKind::Const(ty) => { - format!("const {}: {}", self.apply_mappings(var), ty.display(self)) - } + VariableKind::Const(ty) => format!("const {}", self.apply_mappings(var)), }) } } diff --git a/tests/display/const_.rs b/tests/display/const_.rs new file mode 100644 index 00000000000..9854530406b --- /dev/null +++ b/tests/display/const_.rs @@ -0,0 +1,18 @@ +#[test] +fn test_const_generics() { + reparse_test!( + program { + struct Usize { } + struct Bar { } + trait Foo { } + trait AssocTy { + type Type; + } + impl Foo for Bar { } + impl AssocTy for Bar { + type Type = Usize; + } + opaque type Gee: Foo = Usize; + } + ); +} diff --git a/tests/display/mod.rs b/tests/display/mod.rs index 5f20438e0f6..8e9b2f238a4 100644 --- a/tests/display/mod.rs +++ b/tests/display/mod.rs @@ -3,6 +3,7 @@ mod util; mod assoc_ty; mod built_ins; +mod const_; mod dyn_; mod formatting; mod impl_; From 53f2e5102d6c45e05b6b4d4c572645d6d7f56734 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 21:08:00 -0700 Subject: [PATCH 47/70] Add display for array types Added display functionality for rendering array types such as `[Foo; T]` No tests were added for non generic usages, since rendering those is currently unsupported. Co-authored-by: David Ross --- chalk-solve/src/display/ty.rs | 6 ++++++ tests/display/built_ins.rs | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index a47f25f0325..a02e83c1020 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -201,6 +201,12 @@ impl RenderAsRust for ApplicationTy { TypeName::Error => write!(f, "{{error}}")?, TypeName::Never => todo!("never type"), TypeName::FnDef(_) => todo!("fn def type"), + TypeName::Array => write!( + f, + "[{}; {}]", + self.first_type_parameter(interner).unwrap().display(s), + self.substitution.at(interner, 1).display(s) + )?, } Ok(()) } diff --git a/tests/display/built_ins.rs b/tests/display/built_ins.rs index f69eec8d93f..738f5167a1c 100644 --- a/tests/display/built_ins.rs +++ b/tests/display/built_ins.rs @@ -234,3 +234,17 @@ fn test_tuples() { } ); } + +#[test] +fn test_array_types() { + reparse_test!( + program { + struct Bazz { } + struct Bar { + field: [Bazz; T] + } + trait Foo { } + impl Foo for [Bazz; T] { } + } + ); +} From 58001df468e34da3d92e3439c0ea74a2cdf007f6 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 22:33:21 -0700 Subject: [PATCH 48/70] Clean up tests - Add basic const value test, ignored - Remove comment which is no longer accurate (leave test ignored) - Split up complicated_bounds test into multiple tests - Rename test in logging_db to reflect new reality Co-authored-by: David Ross --- tests/display/const_.rs | 31 +++++++++ tests/display/opaque_ty.rs | 1 - tests/display/where_clauses.rs | 124 ++++++++++++++++++++++++++++++++- tests/logging_db/mod.rs | 5 +- 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/tests/display/const_.rs b/tests/display/const_.rs index 9854530406b..55df909b59c 100644 --- a/tests/display/const_.rs +++ b/tests/display/const_.rs @@ -16,3 +16,34 @@ fn test_const_generics() { } ); } + +#[test] +#[ignore] +fn test_basic_const_values() { + reparse_test!( + program { + struct Foo { } + trait Bar { } + impl Bar for Foo<0> { } + impl Bar for Foo<1> { } + impl Bar for Foo<2> { } + } + ); + reparse_test!( + program { + struct Foo { } + opaque type Zed: = Foo<0>; + } + ); + reparse_test!( + program { + struct Foo { } + trait Bar { + type Assoc; + } + impl Bar for Foo<0> { + type Assoc = Foo<1>; + } + } + ); +} diff --git a/tests/display/opaque_ty.rs b/tests/display/opaque_ty.rs index 71ae57b580b..5e120ed35d3 100644 --- a/tests/display/opaque_ty.rs +++ b/tests/display/opaque_ty.rs @@ -66,7 +66,6 @@ fn test_opaque_type_as_type_value() { ); } -// Generic opaque types can't currently be used as types (these fail to lower) #[ignore] #[test] fn test_generic_opaque_type_as_value1() { diff --git a/tests/display/where_clauses.rs b/tests/display/where_clauses.rs index ee07cf562f4..2a6628c1276 100644 --- a/tests/display/where_clauses.rs +++ b/tests/display/where_clauses.rs @@ -1,5 +1,5 @@ #[test] -fn test_complicated_bounds() { +fn test_alias_eq() { // This test uses "produces" as a workaround for the lowering & writing // code's behavior. Specifically, `Foo: Bax` will be transformed // into `Foo: Bax, Foo: Bax`, even if the where clause already @@ -8,6 +8,128 @@ fn test_complicated_bounds() { // No matter how many `Foo: Bax` we have in our input, we're always going // to get an extra `Foo: Bax` in the output, so they'll never be equal // and we need the separate output program. + reparse_test!( + program { + struct Foo { } + trait Bar { } + trait Third { + type Assoc; + } + impl Bar for Foo + where + Foo: Third + { + } + } + produces { + struct Foo { } + trait Bar { } + trait Third { + type Assoc; + } + impl Bar for Foo + where + Foo: Third, + Foo: Third + { + } + } + ); + reparse_test!( + program { + struct Foo { } + trait Bar { } + trait Third { + type Assoc; + } + impl Bar for Foo + where + Foo: Third, + ::Assoc: Third + { + } + } + ); +} + +#[test] +fn test_dyn_on_left() { + reparse_test!( + program { + struct Foo { } + trait Bar { } + impl Bar for Foo + where + dyn Bar: Bar + { + } + } + ); + reparse_test!( + program { + struct Foo { } + trait Bar { } + trait Baz { + type Assoc + where + dyn Bar: Bar; + } + } + ); +} + +#[test] +fn test_generic_vars_inside_assoc_bounds() { + reparse_test!( + program { + struct Foo { } + trait Bar { } + trait Baz { + type Assoc + where + dyn Bar: Bar, + T: Bar, + Foo: Bar; + } + } + ); + reparse_test!( + program { + struct Foo { } + trait Bar { } + trait Baz { + type Assoc + where + dyn Bar: Bar, + T: Bar, + Foo: Bar; + } + } + ); + reparse_test!( + program { + struct Foo { } + trait Bar { } + trait Baz { + type Assoc: Bar; + } + } + ); + reparse_test!( + program { + struct Foo { } + trait Bar { + type Assoc; + } + trait Baz { + type Assoc: Bar; + } + } + ); +} + +#[test] +fn test_complicated_bounds() { reparse_test!( program { struct Foo { } diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 885cb9ff30f..20944187e43 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -140,8 +140,9 @@ fn records_generic_impls() { #[test] #[ignore] -fn records_necessary_separate_impl() { - // might leave out the "impl Bar for Fox" +fn does_not_need_necessary_separate_impl() { + // this should leave out "impl Bar for Fox" and the result should pass the + // test (it won't be well-formed, but that's OK.) logging_db_output_sufficient! { program { trait Box { From 40cfff1fd48a5403c1eb667e45f7f28e5d876edb Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 22:39:18 -0700 Subject: [PATCH 49/70] Misc cleanup Co-authored-by: David Ross --- chalk-solve/src/display/state.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/chalk-solve/src/display/state.rs b/chalk-solve/src/display/state.rs index fc88c597194..40ff4f61fd2 100644 --- a/chalk-solve/src/display/state.rs +++ b/chalk-solve/src/display/state.rs @@ -9,8 +9,6 @@ use crate::RustIrDatabase; use chalk_ir::{interner::Interner, *}; use itertools::Itertools; -use super::render_trait::RenderAsRust; - /// Like a BoundVar, but with the debrujin index inverted so as to create a /// canonical name we can use anywhere for each bound variable. /// @@ -224,7 +222,7 @@ impl<'a, I: Interner> WriterState<'a, I> { .map(move |(parameter, var)| match parameter { VariableKind::Ty(_) => format!("{}", self.apply_mappings(var)), VariableKind::Lifetime => format!("'{}", self.apply_mappings(var)), - VariableKind::Const(ty) => format!("const {}", self.apply_mappings(var)), + VariableKind::Const(_ty) => format!("const {}", self.apply_mappings(var)), }) } } From e4587729f7f25b4c8a385f7c48eeb5ced6751434 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 30 May 2020 22:40:23 -0700 Subject: [PATCH 50/70] Implment FnDef rendering Added rendering for FnDef such as: ``` fn name(arg1: u32, arg2:T) -> Option where T: Foo; ``` Ignored the LoggingRustIrDatabase tests for function defintions because they currently don't work. Co-authored-by: David Ross --- chalk-integration/src/db.rs | 4 ++ chalk-integration/src/program.rs | 4 ++ chalk-solve/src/display.rs | 4 ++ chalk-solve/src/display/items.rs | 47 ++++++++++++++++++ chalk-solve/src/lib.rs | 3 ++ chalk-solve/src/logging_db.rs | 27 ++++++++-- tests/display/fn_.rs | 84 ++++++++++++++++++++++++++++++++ tests/display/formatting.rs | 20 ++++++++ tests/display/mod.rs | 1 + tests/display/util.rs | 3 +- tests/integration/panic.rs | 4 ++ tests/logging_db/mod.rs | 18 +++++++ 12 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 tests/display/fn_.rs diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index a4d1f8632a1..e561582eba5 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -213,4 +213,8 @@ impl RustIrDatabase for ChalkDatabase { fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.program_ir().unwrap().opaque_type_name(opaque_ty_id) } + + fn fn_def_name(&self, fn_def_id: FnDefId) -> String { + self.program_ir().unwrap().fn_def_name(fn_def_id) + } } diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 08998da67c6..12c9394732d 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -492,4 +492,8 @@ impl RustIrDatabase for Program { .name .to_string() } + + fn fn_def_name(&self, fn_def_id: FnDefId) -> String { + self.fn_def_kinds.get(&fn_def_id).unwrap().name.to_string() + } } diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 151d49cb59c..f1992d821f6 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -62,6 +62,10 @@ where let v = db.opaque_ty_data(id); write_item(f, ws, &*v)?; } + RecordedItemId::FnDef(id) => { + let v = db.fn_def_datum(id); + write_item(f, ws, &*v)?; + } } } Ok(()) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index c299a6b698d..1f0cca824fc 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -276,3 +276,50 @@ impl RenderAsRust for AssociatedTyValue { Ok(()) } } + +impl RenderAsRust for FnDefDatum { + fn fmt(&self, s: &WriterState<'_, I>, f: &mut Formatter<'_>) -> Result { + let s = &s.add_debrujin_index(None); + let bound_datum = self.binders.skip_binders(); + + // declaration + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^^^^ + write!(f, "fn {}", s.db.fn_def_name(self.id))?; + + // binders + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^ + let binders = s.binder_var_display(&self.binders.binders); + write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; + + // arguments + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^^^^^^^^^^^^^^^^^ + let arguments = bound_datum + .argument_types + .iter() + .enumerate() + .map(|(idx, arg)| format!("arg_{}: {}", idx, arg.display(s))) + .format(", "); + + write!(f, "({})", arguments)?; + + // return Type + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^^^^^^^^^^^ + write!(f, " -> {}", bound_datum.return_type.display(s))?; + + // where clause + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^^^^^^^^^^ + if !bound_datum.where_clauses.is_empty() { + let s = &s.add_indent(); + write!(f, "\nwhere\n{}", bound_datum.where_clauses.display(s))?; + } + + write!(f, ";")?; + + Ok(()) + } +} diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 858ecaa1794..cf14df3020e 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -154,6 +154,9 @@ pub trait RustIrDatabase: Debug { /// Retrieves the name of an opaque type. fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String; + + /// Retrieves the name of a function definition + fn fn_def_name(&self, fn_def_id: FnDefId) -> String; } pub use clauses::program_clauses_for_env; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 082d184cb31..ec8f6fd249b 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -200,8 +200,13 @@ where self.db.borrow().is_object_safe(trait_id) } - fn fn_def_datum(&self, _fn_def_id: chalk_ir::FnDefId) -> Arc> { - todo!("function def datum") + fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { + self.record(fn_def_id); + self.db.borrow().fn_def_datum(fn_def_id) + } + + fn fn_def_name(&self, fn_def_id: FnDefId) -> String { + self.db.borrow().fn_def_name(fn_def_id) } } @@ -370,8 +375,12 @@ where self.db.borrow().opaque_type_name(opaque_ty_id) } - fn fn_def_datum(&self, _fn_def_id: chalk_ir::FnDefId) -> Arc> { - todo!("function def datum") + fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { + self.db.borrow().fn_def_datum(fn_def_id) + } + + fn fn_def_name(&self, fn_def_id: FnDefId) -> String { + self.db.borrow().fn_def_name(fn_def_id) } } @@ -381,6 +390,7 @@ pub enum RecordedItemId { Trait(TraitId), Impl(ImplId), OpaqueTy(OpaqueTyId), + FnDef(FnDefId), } impl From> for RecordedItemId { @@ -407,6 +417,12 @@ impl From> for RecordedItemId { } } +impl From> for RecordedItemId { + fn from(v: FnDefId) -> Self { + RecordedItemId::FnDef(v) + } +} + /// Utility for implementing Ord for RecordedItemId. #[derive(PartialEq, Eq, PartialOrd, Ord)] enum OrderedItemId<'a, DefId, AdtId> { @@ -422,7 +438,8 @@ impl RecordedItemId { match self { RecordedItemId::Trait(TraitId(x)) | RecordedItemId::Impl(ImplId(x)) - | RecordedItemId::OpaqueTy(OpaqueTyId(x)) => OrderedItemId::DefId(x), + | RecordedItemId::OpaqueTy(OpaqueTyId(x)) + | RecordedItemId::FnDef(FnDefId(x)) => OrderedItemId::DefId(x), RecordedItemId::Adt(AdtId(x)) => OrderedItemId::AdtId(x), } } diff --git a/tests/display/fn_.rs b/tests/display/fn_.rs new file mode 100644 index 00000000000..71c62a48392 --- /dev/null +++ b/tests/display/fn_.rs @@ -0,0 +1,84 @@ +#[test] +fn test_basic_fn_def() { + reparse_test!( + program { + fn nothing(); + } + ); + reparse_test!( + program { + struct Foo {} + fn takes_foo(v: Foo); + fn gives_foo() -> Foo; + fn bar(a: Foo, _: Foo) -> Foo; + } + ); +} + +#[test] +fn test_generic_fn_def() { + reparse_test!( + program { + fn identity(arg: T) -> T; + } + ); + reparse_test!( + program { + struct Foo {} + struct Bar {} + fn transform(a: Foo) -> Bar; + fn wrap(v: T) -> Foo; + } + ); +} + +#[test] +fn test_const_generic_fn_def() { + reparse_test!( + program { + fn uses_n(arg: [T; N]); + } + ); +} + +#[test] +fn test_opaque_ty_with_fn_def() { + reparse_test!( + program { + opaque type Bar: = (); + fn gives_bar() -> Bar; + } + ); +} + +/// This test fails because lowering code discards function arguments when +/// lowering `foo` into an `fn()` type, and in the parser, `fn` types must +/// always have exactly one argument to parse correctly. So `fn bar() -> foo;` +/// becomes `fn bar() -> fn()` (note: not `fn(u32)`), and then the parser +/// rejects `fn()` because it has 0 arguments, not 1. +#[test] +#[ignore] +fn test_fn_as_type() { + reparse_test!( + program { + fn foo(arg: u32); + fn bar() -> foo; + } + ); + reparse_test!( + program { + trait Bar {} + fn foo(); + impl Bar for Foo {} + opaque type Zed: Bar = foo; + } + ); + reparse_test!( + program { + fn foo(); + struct Vi { + field: foo + } + } + ); +} diff --git a/tests/display/formatting.rs b/tests/display/formatting.rs index cc2595a9e8e..03935a00d88 100644 --- a/tests/display/formatting.rs +++ b/tests/display/formatting.rs @@ -103,6 +103,26 @@ trait [a-zA-Z0-9_-]+ \{ ); } +#[test] +fn test_fn_where_clause() { + reparse_test!( + program { + trait Bar {} + fn foo() -> T + where + dyn Bar: Bar, + T: Bar; + } + formatting matches +r#"trait [a-zA-Z0-9_-]+ \{\s*\} +fn foo<[a-zA-Z0-9_-]+>\(\) -> [a-zA-Z0-9_-]+ +where + dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+; +"# + ); +} + #[test] fn test_name_disambiguation() { // we don't have modules in chalk so we can't actually test different diff --git a/tests/display/mod.rs b/tests/display/mod.rs index 8e9b2f238a4..37c7cc7c8be 100644 --- a/tests/display/mod.rs +++ b/tests/display/mod.rs @@ -5,6 +5,7 @@ mod assoc_ty; mod built_ins; mod const_; mod dyn_; +mod fn_; mod formatting; mod impl_; mod lifetimes; diff --git a/tests/display/util.rs b/tests/display/util.rs index 3023ec57130..f29cd8a1213 100644 --- a/tests/display/util.rs +++ b/tests/display/util.rs @@ -50,7 +50,8 @@ pub fn write_program(program: &Program) -> String { .chain(program.adt_data.keys().copied().map(Into::into)) .chain(program.trait_data.keys().copied().map(Into::into)) .chain(program.impl_data.keys().copied().map(Into::into)) - .chain(program.opaque_ty_data.keys().copied().map(Into::into)); + .chain(program.opaque_ty_data.keys().copied().map(Into::into)) + .chain(program.fn_def_data.keys().copied().map(Into::into)); write_items(&mut out, program, ids).unwrap(); out } diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index b41544bec34..f0c1b88b1bc 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -237,6 +237,10 @@ impl RustIrDatabase for MockDatabase { fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { unimplemented!() } + + fn fn_def_name(&self, fn_def_id: FnDefId) -> String { + unimplemented!() + } } fn prepare_goal() -> UCanonical>> { diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 20944187e43..7b473d86189 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -41,6 +41,24 @@ fn records_opaque_type() { } } +#[test] +#[ignore] +fn records_fn_def() { + logging_db_output_sufficient! { + program { + #[lang(sized)] + trait Sized { } + + fn foo(); + } + goal { + foo: Sized + } yields { + "Unique" + } + } +} + #[test] #[ignore] fn records_parents_parent() { From 427b9482a461a5b53c4d7ddd7d49c2a202dff61e Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 21 Jun 2020 00:41:37 -0700 Subject: [PATCH 51/70] Add todo in LoggingDB for closures Added required trait methods to the logging db wrappers so they behave correctly. Also added a todo macro in the rendering code for Closures. Co-authored-by: David Ross --- chalk-solve/src/display/ty.rs | 1 + chalk-solve/src/logging_db.rs | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index a02e83c1020..76e7e591593 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -201,6 +201,7 @@ impl RenderAsRust for ApplicationTy { TypeName::Error => write!(f, "{{error}}")?, TypeName::Never => todo!("never type"), TypeName::FnDef(_) => todo!("fn def type"), + TypeName::Closure(..) => todo!("closure type"), TypeName::Array => write!( f, "[{}; {}]", diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index ec8f6fd249b..5a53b86daa0 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -208,6 +208,36 @@ where fn fn_def_name(&self, fn_def_id: FnDefId) -> String { self.db.borrow().fn_def_name(fn_def_id) } + + fn closure_kind(&self, closure_id: ClosureId, substs: &Substitution) -> ClosureKind { + // TODO: record closure IDs + self.db.borrow().closure_kind(closure_id, substs) + } + + fn closure_inputs_and_output( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Binders> { + // TODO: record closure IDs + self.db + .borrow() + .closure_inputs_and_output(closure_id, substs) + } + + fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders> { + // TODO: record closure IDs + self.db.borrow().closure_upvars(closure_id, substs) + } + + fn closure_fn_substitution( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Substitution { + // TODO: record closure IDs + self.db.borrow().closure_fn_substitution(closure_id, substs) + } } /// Wraps a [`RustIrDatabase`], and, when dropped, writes out all used @@ -382,6 +412,33 @@ where fn fn_def_name(&self, fn_def_id: FnDefId) -> String { self.db.borrow().fn_def_name(fn_def_id) } + + fn closure_kind(&self, closure_id: ClosureId, substs: &Substitution) -> ClosureKind { + // TODO: record closure IDs + self.db.borrow().closure_kind(closure_id, substs) + } + + fn closure_inputs_and_output( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Binders> { + self.db + .borrow() + .closure_inputs_and_output(closure_id, substs) + } + + fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders> { + self.db.borrow().closure_upvars(closure_id, substs) + } + + fn closure_fn_substitution( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Substitution { + self.db.borrow().closure_fn_substitution(closure_id, substs) + } } #[derive(Copy, Clone, PartialEq, Eq, Debug)] From 63a39554b2ba5744994ffdf5915ce10e70d0c1ad Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 21 Jun 2020 00:53:03 -0700 Subject: [PATCH 52/70] Add return type rendering for fn types For a function that takes u32 as an arg and returns a unit, it outputs: fn(u32) -> () The inputs and outputs are inside a Binders. We are not totally sure why this is, but we had increment the debrujin index as a result. Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 37 ++++++++++++++++++-------------- tests/display/built_ins.rs | 12 +++++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 1f0cca824fc..de6aa5e184c 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -293,22 +293,27 @@ impl RenderAsRust for FnDefDatum { let binders = s.binder_var_display(&self.binders.binders); write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; - // arguments - // fn foo(arg: u32, arg2: T) -> Result where T: Bar - // ^^^^^^^^^^^^^^^^^^^ - let arguments = bound_datum - .argument_types - .iter() - .enumerate() - .map(|(idx, arg)| format!("arg_{}: {}", idx, arg.display(s))) - .format(", "); - - write!(f, "({})", arguments)?; - - // return Type - // fn foo(arg: u32, arg2: T) -> Result where T: Bar - // ^^^^^^^^^^^^^ - write!(f, " -> {}", bound_datum.return_type.display(s))?; + { + let s = &s.add_debrujin_index(None); + let inputs_and_output = bound_datum.inputs_and_output.skip_binders(); + + // arguments + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^^^^^^^^^^^^^^^^^ + let arguments = inputs_and_output + .argument_types + .iter() + .enumerate() + .map(|(idx, arg)| format!("arg_{}: {}", idx, arg.display(s))) + .format(", "); + + write!(f, "({})", arguments)?; + + // return Type + // fn foo(arg: u32, arg2: T) -> Result where T: Bar + // ^^^^^^^^^^^^^ + write!(f, " -> {}", inputs_and_output.return_type.display(s))?; + } // where clause // fn foo(arg: u32, arg2: T) -> Result where T: Bar diff --git a/tests/display/built_ins.rs b/tests/display/built_ins.rs index 738f5167a1c..9ae9d871b20 100644 --- a/tests/display/built_ins.rs +++ b/tests/display/built_ins.rs @@ -17,6 +17,18 @@ fn test_function_type() { ); } +#[test] +fn test_function_type_generic() { + reparse_test!( + program { + struct Foo<'a, T> + { + bar: fn(&'a T) -> &'a (T, T) + } + } + ); +} + #[test] fn test_scalar_types() { let basic = vec!["bool", "char", "f32", "f64"]; From 934445be1527b6beb816e1f44cb86d6b0f16bcd7 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 21 Jun 2020 01:06:33 -0700 Subject: [PATCH 53/70] Optimize joining iterators with Itertools::format There were serveral places where we had code like: ``` .collect>().join(", ") ``` This is not optimal as it allocates twice, and is quite verbose. We replaced it with Itertools::format, which returns something implementing display, and will lazily write to the formatter without allocating. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 3 +-- chalk-solve/src/display/bounds.rs | 12 ++++-------- chalk-solve/src/display/items.rs | 3 +-- chalk-solve/src/display/ty.rs | 3 +-- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index f1992d821f6..a6a883647f2 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -139,8 +139,7 @@ fn display_self_where_clauses_as_bounds<'a, I: Interner>( }) .to_string() }) - .collect::>() - .join(" + ") + .format(" + ") ) }) } diff --git a/chalk-solve/src/display/bounds.rs b/chalk-solve/src/display/bounds.rs index 33c972ccb44..3ef16fc4acf 100644 --- a/chalk-solve/src/display/bounds.rs +++ b/chalk-solve/src/display/bounds.rs @@ -2,6 +2,7 @@ use std::fmt::{Display, Formatter, Result}; use crate::rust_ir::*; use chalk_ir::{interner::Interner, *}; +use itertools::Itertools; use super::{ display_trait_with_assoc_ty_value, display_trait_with_generics, render_trait::RenderAsRust, @@ -47,9 +48,7 @@ impl RenderAsRust for QuantifiedWhereClause { write!( f, "forall<{}> ", - s.binder_var_display(&self.binders) - .collect::>() - .join(", ") + s.binder_var_display(&self.binders).format(", ") )?; } self.skip_binders().fmt(s, f) @@ -64,9 +63,7 @@ impl RenderAsRust for QuantifiedInlineBound { write!( f, "forall<{}> ", - s.binder_var_display(&self.binders) - .collect::>() - .join(", ") + s.binder_var_display(&self.binders).format(", ") )?; } self.skip_binders().fmt(s, f) @@ -80,8 +77,7 @@ impl RenderAsRust for Vec> { "{}", self.iter() .map(|where_clause| { format!("{}{}", s.indent(), where_clause.display(s)) }) - .collect::>() - .join(",\n") + .format(",\n") )?; Ok(()) } diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index de6aa5e184c..33b1513744c 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -217,8 +217,7 @@ impl RenderAsRust for AssociatedTyDatum { .bounds .iter() .map(|bound| bound.display(s).to_string()) - .collect::>() - .join(" + "); + .format(" + "); write!(f, "{}", bounds)?; // where_clause is 'X: Y, Z: D' diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index 76e7e591593..400507f9c37 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -96,8 +96,7 @@ impl RenderAsRust for Fn { "for<{}> ", (0..self.num_binders) .map(|n| format!("'{}", s.name_for_introduced_bound_var(n))) - .collect::>() - .join(", ") + .format(", ") )?; } write!( From 1858d6a0e641ae6995aaacb72521acd31386b7de Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 21 Jun 2020 01:11:08 -0700 Subject: [PATCH 54/70] Add dyn trait lifetime bounds rendering The current chalk syntax mandates that every dyn be follow by a liftime bound: ``` dyn Foo + 'a ``` This updates the rendering code and tests to support this. Co-authored-by: David Ross --- chalk-solve/src/display/ty.rs | 35 ++++++++++++++++++++-------------- tests/display/dyn_.rs | 32 +++++++++++++++---------------- tests/display/formatting.rs | 20 +++++++++---------- tests/display/lifetimes.rs | 2 +- tests/display/self_.rs | 10 +++++----- tests/display/where_clauses.rs | 26 ++++++++++++------------- 6 files changed, 66 insertions(+), 59 deletions(-) diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index 400507f9c37..926b84907fd 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -14,14 +14,21 @@ impl RenderAsRust for TyData { let interner = s.db.interner(); match self { TyData::Dyn(dyn_ty) => { - let s = &s.add_debrujin_index(None); - // dyn_ty.bounds.binders creates a Self binding for the trait - let bounds = dyn_ty.bounds.skip_binders(); - write!( - f, - "dyn {}", - display_self_where_clauses_as_bounds(s, bounds.as_slice(interner)) - )?; + // the lifetime needs to be outside of the bounds, so we + // introduce a new scope for the bounds + { + let s = &s.add_debrujin_index(None); + // dyn_ty.bounds.binders creates a Self binding for the trait + let bounds = dyn_ty.bounds.skip_binders(); + + write!( + f, + "dyn {}", + display_self_where_clauses_as_bounds(s, bounds.as_slice(interner)), + )?; + } + + write!(f, " + {}", dyn_ty.lifetime.display(s))?; Ok(()) } TyData::BoundVar(bound_var) => write!(f, "{}", s.display_bound_var(bound_var)), @@ -99,15 +106,15 @@ impl RenderAsRust for Fn { .format(", ") )?; } + let parameters = self.substitution.parameters(interner); write!( f, - "fn({})", - self.substitution - .parameters(interner) + "fn({}) -> {}", + parameters[..parameters.len() - 1] .iter() - .map(|param| param.display(s).to_string()) - .collect::>() - .join(", ") + .map(|param| param.display(s)) + .format(", "), + parameters[parameters.len() - 1].display(s), ) } } diff --git a/tests/display/dyn_.rs b/tests/display/dyn_.rs index 3b62b2abeac..2da234b2cbd 100644 --- a/tests/display/dyn_.rs +++ b/tests/display/dyn_.rs @@ -2,38 +2,38 @@ fn test_forall_in_dyn() { reparse_test!( program { - trait Foo {} + trait Foo<'t> {} trait Bar<'a> {} - impl Foo for dyn forall<'a> Bar<'a> {} + impl<'t> Foo<'t> for dyn forall<'a> Bar<'a> + 't {} } ); reparse_test!( program { - struct Foo { - field: dyn forall<'a> Baz<'a> + struct Foo<'t> { + field: dyn forall<'a> Baz<'a> + 't } trait Baz<'a> {} } ); reparse_test!( program { - trait Foo {} + trait Foo<'t> {} trait Bax<'a, 'b> {} - impl Foo for dyn forall<'a, 'b> Bax<'a, 'b> {} + impl<'t> Foo<'t> for dyn forall<'a, 'b> Bax<'a, 'b> + 't {} } ); reparse_test!( program { - struct Foo { - field: dyn forall<'a, 'b> Bix<'a, 'b> + struct Foo<'t> { + field: dyn forall<'a, 'b> Bix<'a, 'b> + 't } trait Bix<'a, 'b> {} } ); reparse_test!( program { - struct Foo { - field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> + struct Foo<'t> { + field: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> + 't } trait Bex<'a> {} trait Byx<'a> {} @@ -41,8 +41,8 @@ fn test_forall_in_dyn() { ); reparse_test!( program { - struct Foo { - field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> + struct Foo<'t> { + field: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> + 't } trait Bux<'a, 'b> {} trait Brx<'a, 'b> {} @@ -51,7 +51,7 @@ fn test_forall_in_dyn() { reparse_test!( program { struct Foo<'a> { - field: dyn forall<'b> Bpx<'a, 'b> + field: dyn forall<'b> Bpx<'a, 'b> + 'a } trait Bpx<'a, 'b> {} } @@ -62,8 +62,8 @@ fn test_forall_in_dyn() { fn test_simple_dyn() { reparse_test!( program { - struct Foo { - field: dyn Bax + struct Foo<'a> { + field: dyn Bax + 'a } trait Bax {} } @@ -71,7 +71,7 @@ fn test_simple_dyn() { reparse_test!( program { struct Foo<'a> { - field: dyn Bix<'a> + field: dyn Bix<'a> + 'a } trait Bix<'a> {} } diff --git a/tests/display/formatting.rs b/tests/display/formatting.rs index 03935a00d88..512e9c8acfb 100644 --- a/tests/display/formatting.rs +++ b/tests/display/formatting.rs @@ -51,11 +51,11 @@ fn test_where_clause_formatting() { reparse_test!( program { struct Foo where Foo: Baz, Foo: Bar {} - trait Bar where Foo: Baz, dyn Baz: Bar {} + trait Bar where Foo: Baz, forall<'a> dyn Baz + 'a: Bar {} trait Baz {} impl Bar for Foo where Foo: Baz, (): Baz {} impl Baz for Foo {} - impl Bar for dyn Baz {} + impl<'a> Bar for dyn Baz + 'a {} } formatting matches r#"struct [a-zA-Z0-9_-]+ @@ -66,7 +66,7 @@ where trait [a-zA-Z0-9_-]+ where [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, - dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ + forall<'[a-zA-Z0-9_-]+> dyn [a-zA-Z0-9_-]+ \+ '[a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+ \{\s*\} trait [a-zA-Z0-9_-]+ \{\} impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ @@ -75,7 +75,7 @@ where \(\): [a-zA-Z0-9_-]+ \{\} impl [a-zA-Z0-9_-]+ for [a-zA-Z0-9_-]+ \{\} -impl [a-zA-Z0-9_-]+ for dyn [a-zA-Z0-9_-]+ \{\}"# +impl<'[a-zA-Z0-9_-]+> [a-zA-Z0-9_-]+ for dyn [a-zA-Z0-9_-]+ \+ '[a-zA-Z0-9_-]+ \{\}"# ); } @@ -87,7 +87,7 @@ fn test_assoc_ty_where_clause() { trait Fuzz { type Assoc where - dyn Bar: Bar, + u32: Bar, Self: Bar; } } @@ -96,7 +96,7 @@ r#"trait [a-zA-Z0-9_-]+ \{\s*\} trait [a-zA-Z0-9_-]+ \{ type [a-zA-Z0-9_-]+ where - dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + u32: [a-zA-Z0-9_-]+, [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+; \} "# @@ -108,16 +108,16 @@ fn test_fn_where_clause() { reparse_test!( program { trait Bar {} - fn foo() -> T + fn foo<'a, T>() -> T where - dyn Bar: Bar, + dyn Bar + 'a: Bar, T: Bar; } formatting matches r#"trait [a-zA-Z0-9_-]+ \{\s*\} -fn foo<[a-zA-Z0-9_-]+>\(\) -> [a-zA-Z0-9_-]+ +fn foo<'[a-zA-Z0-9_-]+, [a-zA-Z0-9_-]+>\(\) -> [a-zA-Z0-9_-]+ where - dyn [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, + dyn [a-zA-Z0-9_-]+ \+ '[a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+, [a-zA-Z0-9_-]+: [a-zA-Z0-9_-]+; "# ); diff --git a/tests/display/lifetimes.rs b/tests/display/lifetimes.rs index a1270a5d435..d0da44ba5dd 100644 --- a/tests/display/lifetimes.rs +++ b/tests/display/lifetimes.rs @@ -10,7 +10,7 @@ fn test_various_forall() { } impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } impl<'a> Bax<'a> for fn(Foo<'a>) { } - impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> { } + impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> + 'a { } } ); } diff --git a/tests/display/self_.rs b/tests/display/self_.rs index 9e843abaa35..bdc09126aa0 100644 --- a/tests/display/self_.rs +++ b/tests/display/self_.rs @@ -49,8 +49,8 @@ fn test_self_in_dyn() { reparse_test!( program { trait Bun {} - trait Foo { - type Assoc where dyn Bun: Bun; + trait Foo<'a, T> { + type Assoc where dyn Bun + 'a: Bun; } } ); @@ -58,9 +58,9 @@ fn test_self_in_dyn() { program { trait Has {} trait Bun {} - trait Fiz { - type Assoc1: Has>; - type Assoc2: Has>; + trait Fiz<'a, T> { + type Assoc1: Has + 'a>; + type Assoc2: Has + 'a>; } } ); diff --git a/tests/display/where_clauses.rs b/tests/display/where_clauses.rs index 2a6628c1276..41aa2795410 100644 --- a/tests/display/where_clauses.rs +++ b/tests/display/where_clauses.rs @@ -58,9 +58,9 @@ fn test_dyn_on_left() { program { struct Foo { } trait Bar { } - impl Bar for Foo + impl<'a> Bar for Foo where - dyn Bar: Bar + dyn Bar + 'a: Bar { } } @@ -69,10 +69,10 @@ fn test_dyn_on_left() { program { struct Foo { } trait Bar { } - trait Baz { + trait Baz<'a> { type Assoc where - dyn Bar: Bar; + dyn Bar + 'a: Bar; } } ); @@ -84,10 +84,10 @@ fn test_generic_vars_inside_assoc_bounds() { program { struct Foo { } trait Bar { } - trait Baz { + trait Baz<'a> { type Assoc where - dyn Bar: Bar, + dyn Bar + 'a: Bar, T: Bar, Foo: Bar; } @@ -97,10 +97,10 @@ fn test_generic_vars_inside_assoc_bounds() { program { struct Foo { } trait Bar { } - trait Baz { + trait Baz<'a, U> { type Assoc where - dyn Bar: Bar, + dyn Bar + 'a: Bar, T: Bar, Foo: Bar; } @@ -136,12 +136,12 @@ fn test_complicated_bounds() { trait Bar { } trait Baz { } trait Bax { type BaxT; } - trait Test { + trait Test<'a> { type Assoc: Bar + Baz + Bax where Foo: Bax, Foo: Bar, - dyn Bar: Baz; + dyn Bar + 'a: Baz; } } produces { @@ -149,13 +149,13 @@ fn test_complicated_bounds() { trait Bar { } trait Baz { } trait Bax { type BaxT; } - trait Test { + trait Test<'a> { type Assoc: Bar + Baz + Bax where Foo: Bax, Foo: Bax, Foo: Bar, - dyn Bar: Baz; + dyn Bar + 'a: Baz; } } ); @@ -203,7 +203,7 @@ fn test_trait_projection() { fn test_trait_projection_with_dyn_arg() { reparse_test!( program { - struct Foo where U: Bez, >::Assoc: Baz { } + struct Foo<'a, T, U> where U: Bez, >::Assoc: Baz { } trait Baz { } trait Bez { type Assoc; From 68210e0f02b5775e439feda7daaec16f658ac4e4 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 21 Jun 2020 02:19:16 -0700 Subject: [PATCH 55/70] Implement stub items in logging_db This implements the suggested fix where if LoggingRustIrDatabase finds an name in the output which it isn't already outputting, it'll prepend a "stub" version of that thing - it'll have the right generics, but no where bounds and no content. Structs are empty, traits have no methods, etc. This doesn't entirely work - mainly, there are some bugs with associated type bounds not being visited to find missing IDs, and we don't have enough tests - but the framework is there! Co-authored-by: David Ross --- chalk-solve/src/display.rs | 68 ++++++++++ chalk-solve/src/logging_db.rs | 4 + chalk-solve/src/logging_db/id_collector.rs | 147 +++++++++++++++++++++ chalk-solve/src/rust_ir.rs | 49 +++++-- tests/logging_db/mod.rs | 1 - tests/logging_db/util.rs | 6 +- 6 files changed, 263 insertions(+), 12 deletions(-) create mode 100644 chalk-solve/src/logging_db/id_collector.rs diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index a6a883647f2..95113823886 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -33,6 +33,74 @@ where write!(f, "{}\n", v.display(ws)) } +/// Writes stubs for items which were referenced by name, but for which we +/// didn't directly access. For instance, traits mentioned in where bounds which +/// are only usually checked during well-formedness, when we waren't recording +/// well-formedness. +/// +/// The "stub" nature of this means it writes output with the right names and +/// the right number of generics, but nothing else. Where clauses, bounds, and +/// fields are skipped. Associated types are ???? skipped. +/// +/// `RecordedItemId::Impl` is not supported. +pub fn write_stub_items(f: &mut F, db: &DB, ids: T) -> Result +where + F: std::fmt::Write + ?Sized, + I: Interner, + DB: RustIrDatabase, + T: IntoIterator>, +{ + let ws = &WriterState::new(db); + for id in ids { + match id { + RecordedItemId::Impl(_id) => unreachable!(), + RecordedItemId::Adt(id) => { + let mut v = (*db.adt_datum(id)).clone(); + v.binders = Binders::new( + VariableKinds::new(ws.db.interner()), + AdtDatumBound { + fields: Vec::new(), + where_clauses: Vec::new(), + }, + ); + write_item(f, ws, &v)?; + } + RecordedItemId::Trait(id) => { + let mut v = (*db.trait_datum(id)).clone(); + v.binders = Binders::new( + VariableKinds::new(ws.db.interner()), + TraitDatumBound { + where_clauses: Vec::new(), + }, + ); + write_item(f, ws, &v)?; + } + RecordedItemId::OpaqueTy(id) => { + let mut v = (*db.opaque_ty_data(id)).clone(); + v.bound = Binders::new( + VariableKinds::new(ws.db.interner()), + OpaqueTyDatumBound { + bounds: Binders::new(VariableKinds::new(ws.db.interner()), Vec::new()), + }, + ); + write_item(f, ws, &v)?; + } + RecordedItemId::FnDef(id) => { + let mut v = (*db.fn_def_datum(id)).clone(); + v.binders = Binders::new( + VariableKinds::new(ws.db.interner()), + FnDefDatumBound { + inputs_and_output: v.binders.skip_binders().inputs_and_output.clone(), + where_clauses: Vec::new(), + }, + ); + write_item(f, ws, &v)?; + } + } + } + Ok(()) +} + /// Writes out each item recorded by a [`LoggingRustIrDatabase`]. /// /// [`LoggingRustIrDatabase`]: crate::logging_db::LoggingRustIrDatabase diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 5a53b86daa0..405e7e957d8 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -15,6 +15,8 @@ use crate::rust_ir::*; use crate::{display, RustIrDatabase}; use chalk_ir::{interner::Interner, *}; +mod id_collector; + /// Wraps another `RustIrDatabase` (`DB`) and records which definitions are /// used. /// @@ -58,6 +60,8 @@ where { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let def_ids = self.def_ids.lock().unwrap(); + let stub_ids = id_collector::collect_unrecorded_ids(self.db.borrow(), &def_ids); + display::write_stub_items(f, self.db.borrow(), stub_ids)?; display::write_items(f, self.db.borrow(), def_ids.iter().copied()) } } diff --git a/chalk-solve/src/logging_db/id_collector.rs b/chalk-solve/src/logging_db/id_collector.rs new file mode 100644 index 00000000000..f9d487b18b1 --- /dev/null +++ b/chalk-solve/src/logging_db/id_collector.rs @@ -0,0 +1,147 @@ +use super::RecordedItemId; +use crate::RustIrDatabase; +use chalk_ir::{ + interner::Interner, + visit::Visitor, + visit::{SuperVisit, Visit}, + AliasTy, DebruijnIndex, TyData, TypeName, WhereClause, +}; +use std::collections::BTreeSet; + +/// Collects the identifiers needed to resolve all the names for a given +/// set of identifers, excluding identifiers we already have. +/// +/// For instance, if we have: +/// +/// ```rust,ignore +/// struct S {} +/// +/// trait Parent {} +/// trait Child where Self: Parent {} +/// impl Parent for S {} +/// impl Child for S {} +/// ``` +/// If we render the identifiers for only for `S`, `impl Child for S`, and +/// `trait Child`, it will not parse because the `Child` trait's definition +/// references parent. IdCollector solves this by collecting all of the directly +/// related identifiers, allowing those to be rendered as well, ensuring name +/// resolution is successful. +pub fn collect_unrecorded_ids<'i, I: Interner, DB: RustIrDatabase>( + db: &'i DB, + identifiers: &'_ BTreeSet>, +) -> BTreeSet> { + let mut collector = IdCollector { + db, + found_identifiers: BTreeSet::new(), + }; + for id in identifiers { + match *id { + RecordedItemId::Adt(adt_id) => { + collector + .db + .adt_datum(adt_id) + .visit_with(&mut collector, DebruijnIndex::INNERMOST); + } + RecordedItemId::FnDef(fn_def) => { + collector + .db + .fn_def_datum(fn_def) + .visit_with(&mut collector, DebruijnIndex::INNERMOST); + } + RecordedItemId::Trait(trait_id) => { + let trait_datum = collector.db.trait_datum(trait_id); + + trait_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST); + } + RecordedItemId::OpaqueTy(opaque_id) => { + collector + .db + .opaque_ty_data(opaque_id) + .visit_with(&mut collector, DebruijnIndex::INNERMOST); + } + RecordedItemId::Impl(impl_id) => { + collector + .db + .impl_datum(impl_id) + .visit_with(&mut collector, DebruijnIndex::INNERMOST); + } + } + } + collector + .found_identifiers + .difference(identifiers) + .copied() + .collect() +} + +struct IdCollector<'i, I: Interner, DB: RustIrDatabase> { + db: &'i DB, + found_identifiers: BTreeSet>, +} + +impl<'i, I: Interner, DB: RustIrDatabase> IdCollector<'i, I, DB> { + fn record(&mut self, id: impl Into>) { + self.found_identifiers.insert(id.into()); + } +} + +impl<'i, I: Interner, DB: RustIrDatabase> Visitor<'i, I> for IdCollector<'i, I, DB> +where + I: 'i, +{ + type Result = (); + fn as_dyn(&mut self) -> &mut dyn Visitor<'i, I, Result = Self::Result> { + self + } + fn interner(&self) -> &'i I { + self.db.interner() + } + + fn visit_ty( + &mut self, + ty: &chalk_ir::Ty, + outer_binder: chalk_ir::DebruijnIndex, + ) -> Self::Result { + let ty_data = ty.data(self.db.interner()); + match ty_data { + TyData::Apply(apply_ty) => match apply_ty.name { + TypeName::Adt(adt) => self.record(adt), + TypeName::FnDef(fn_def) => self.record(fn_def), + TypeName::OpaqueType(opaque) => self.record(opaque), + _ => {} + }, + TyData::Alias(alias) => match alias { + AliasTy::Projection(projection_ty) => { + let assoc_ty_datum = self.db.associated_ty_data(projection_ty.associated_ty_id); + self.record(assoc_ty_datum.trait_id) + } + AliasTy::Opaque(_opaque_ty) => todo!("opaque types!"), + }, + TyData::BoundVar(..) => (), + TyData::Dyn(..) => (), + TyData::Function(..) => (), + TyData::InferenceVar(..) => (), + TyData::Placeholder(..) => (), + } + ty.super_visit_with(self, outer_binder) + } + + fn visit_where_clause( + &mut self, + where_clause: &WhereClause, + outer_binder: DebruijnIndex, + ) -> Self::Result { + match where_clause { + WhereClause::Implemented(trait_ref) => self.record(trait_ref.trait_id), + WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { + AliasTy::Projection(projection_ty) => { + let assoc_ty_datum = self.db.associated_ty_data(projection_ty.associated_ty_id); + self.record(assoc_ty_datum.trait_id) + } + AliasTy::Opaque(_opaque_ty) => todo!("opaque types!"), + }, + WhereClause::LifetimeOutlives(_lifetime_outlives) => (), + } + where_clause.super_visit_with(self.as_dyn(), outer_binder) + } +} diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index 8473663fc2a..4d29b98ceb7 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -9,6 +9,7 @@ use chalk_ir::cast::Cast; use chalk_ir::fold::shift::Shift; use chalk_ir::interner::{Interner, TargetInterner}; use chalk_ir::{ + visit::{Visit, VisitResult}, AdtId, AliasEq, AliasTy, AssocTypeId, Binders, DebruijnIndex, FnDefId, GenericArg, ImplId, OpaqueTyId, ProjectionTy, QuantifiedWhereClause, Substitution, ToGenericArg, TraitId, TraitRef, Ty, TyData, TypeName, VariableKind, WhereClause, WithKind, @@ -19,9 +20,10 @@ use std::iter; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AssociatedTyValueId(pub I::DefId); +chalk_ir::id_visit!(AssociatedTyValueId); chalk_ir::id_fold!(AssociatedTyValueId); -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Visit)] pub struct ImplDatum { pub polarity: Polarity, pub binders: Binders>, @@ -55,7 +57,7 @@ impl ImplDatum { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner, Fold)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner, Fold, Visit)] pub struct ImplDatumBound { pub trait_ref: TraitRef, pub where_clauses: Vec>, @@ -67,6 +69,8 @@ pub enum ImplType { External, } +chalk_ir::const_visit!(ImplType); + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct DefaultImplDatum { pub binders: Binders>, @@ -78,7 +82,7 @@ pub struct DefaultImplDatumBound { pub accessible_tys: Vec>, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Visit)] pub struct AdtDatum { pub binders: Binders>, pub id: AdtId, @@ -91,7 +95,7 @@ impl AdtDatum { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner, Visit)] pub struct AdtDatumBound { pub fields: Vec>, pub where_clauses: Vec>, @@ -104,6 +108,8 @@ pub struct AdtFlags { pub phantom_data: bool, } +chalk_ir::const_visit!(AdtFlags); + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct AdtRepr { pub repr_c: bool, @@ -129,9 +135,27 @@ pub struct FnDefDatum { pub binders: Binders>, } +/// Avoids visiting `I::FnAbi` +impl Visit for FnDefDatum { + fn visit_with<'i, R: VisitResult>( + &self, + visitor: &mut dyn chalk_ir::visit::Visitor<'i, I, Result = R>, + outer_binder: DebruijnIndex, + ) -> R + where + I: 'i, + { + let result = R::new().combine(self.id.visit_with(visitor, outer_binder)); + if result.return_early() { + return result; + } + return result.combine(self.binders.visit_with(visitor, outer_binder)); + } +} + /// Represents the inputs and outputs on a `FnDefDatum`. This is split /// from the where clauses, since these can contain bound lifetimes. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner, Visit)] pub struct FnDefInputsAndOutputDatum { /// Types of the function's arguments /// ```ignore @@ -148,7 +172,7 @@ pub struct FnDefInputsAndOutputDatum { pub return_type: Ty, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner, Visit)] /// Represents the bounds on a `FnDefDatum`, including /// the function definition's type signature and where clauses. pub struct FnDefDatumBound { @@ -199,6 +223,7 @@ pub struct FnDefDatumBound { /// /// [`ImplDatum`]: struct.ImplDatum.html /// [`AssociatedTyDatum`]: struct.AssociatedTyDatum.html +#[derive(Visit)] pub struct TraitDatum { pub id: TraitId, @@ -233,6 +258,8 @@ pub enum WellKnownTrait { Unsize, } +chalk_ir::const_visit!(WellKnownTrait); + impl TraitDatum { pub fn is_auto_trait(&self) -> bool { self.flags.auto @@ -257,7 +284,7 @@ impl TraitDatum { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner, Visit)] pub struct TraitDatumBound { /// Where clauses defined on the trait: /// @@ -301,6 +328,8 @@ pub struct TraitFlags { pub coinductive: bool, } +chalk_ir::const_visit!(TraitFlags); + /// An inline bound, e.g. `: Foo` in `impl> SomeType`. #[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit, HasInterner)] pub enum InlineBound { @@ -568,7 +597,7 @@ pub struct AssociatedTyValueBound { /// ```ignore /// opaque type T: A + B = HiddenTy; /// ``` -#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit)] pub struct OpaqueTyDatum { /// The placeholder `!T` that corresponds to the opaque type `T`. pub opaque_ty_id: OpaqueTyId, @@ -577,7 +606,7 @@ pub struct OpaqueTyDatum { pub bound: Binders>, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner, Visit)] pub struct OpaqueTyDatumBound { /// Trait bounds for the opaque type. pub bounds: Binders>>, @@ -589,6 +618,8 @@ pub enum Polarity { Negative, } +chalk_ir::const_visit!(Polarity); + impl Polarity { pub fn is_positive(&self) -> bool { match *self { diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 7b473d86189..04cf9bc5cff 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -60,7 +60,6 @@ fn records_fn_def() { } #[test] -#[ignore] fn records_parents_parent() { logging_db_output_sufficient! { program { diff --git a/tests/logging_db/util.rs b/tests/logging_db/util.rs index 8be22edf4c3..839c6361b39 100644 --- a/tests/logging_db/util.rs +++ b/tests/logging_db/util.rs @@ -37,7 +37,7 @@ pub fn logging_db_output_sufficient( SolverChoice::default(), ); - let program = db.checked_program().unwrap(); + let program = db.program_ir().unwrap(); let wrapped = LoggingRustIrDatabase::<_, Program, _>::new(program.clone()); for (goal_text, solver_choice, expected) in goals.clone() { let mut solver = solver_choice.into_solver(); @@ -73,7 +73,9 @@ pub fn logging_db_output_sufficient( let db = ChalkDatabase::with(&output_text, SolverChoice::default()); - let new_program = match db.checked_program() { + // Note: we are explicitly not calling `.checked_program()`, as our output + // is not intended to be well formed. + let new_program = match db.program_ir() { Ok(v) => v, Err(e) => panic!("Error checking recreated chalk program: {}", e), }; From e3cce4585265ad2694b2eaa52711765e1db22298 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 11:55:23 -0700 Subject: [PATCH 56/70] Fix missing names from assoc type bounds The logging db that wraps RustIrDatabase was not collecting the ids for items referenced in associated types. We extended the id collector to convert an associated type's inline bounds to a where clause, which is then visited using the existing where clause visitor to pickup the referenced ids. Co-authored-by: David Ross --- chalk-solve/src/logging_db/id_collector.rs | 16 ++++++++++++++-- chalk-solve/src/rust_ir.rs | 22 ++++++++++++++++++++++ tests/logging_db/mod.rs | 20 +++++++++++++++++++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/chalk-solve/src/logging_db/id_collector.rs b/chalk-solve/src/logging_db/id_collector.rs index f9d487b18b1..faf044c598d 100644 --- a/chalk-solve/src/logging_db/id_collector.rs +++ b/chalk-solve/src/logging_db/id_collector.rs @@ -11,6 +11,10 @@ use std::collections::BTreeSet; /// Collects the identifiers needed to resolve all the names for a given /// set of identifers, excluding identifiers we already have. /// +/// When recording identifiers to print, the `LoggingRustIrDatabase` only +/// records identifiers the solver uses. But the solver assumes well-formedness, +/// and thus skips over many names referenced in the definitions. +/// /// For instance, if we have: /// /// ```rust,ignore @@ -21,8 +25,9 @@ use std::collections::BTreeSet; /// impl Parent for S {} /// impl Child for S {} /// ``` -/// If we render the identifiers for only for `S`, `impl Child for S`, and -/// `trait Child`, it will not parse because the `Child` trait's definition +/// +/// And our goal is `S: Child`, we will only render `S`, `impl Child for S`, and +/// `trait Child`. This will not parse because the `Child` trait's definition /// references parent. IdCollector solves this by collecting all of the directly /// related identifiers, allowing those to be rendered as well, ensuring name /// resolution is successful. @@ -52,6 +57,13 @@ pub fn collect_unrecorded_ids<'i, I: Interner, DB: RustIrDatabase>( let trait_datum = collector.db.trait_datum(trait_id); trait_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST); + for assoc_ty_id in &trait_datum.associated_ty_ids { + let assoc_ty_datum = collector.db.associated_ty_data(*assoc_ty_id); + assoc_ty_datum + .bounds_on_self(collector.db.interner()) + .visit_with(&mut collector, DebruijnIndex::INNERMOST); + assoc_ty_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST) + } } RecordedItemId::OpaqueTy(opaque_id) => { collector diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index 4d29b98ceb7..cc96ab1d971 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -483,6 +483,28 @@ pub struct AssociatedTyDatum { pub binders: Binders>, } +// Manual implementation to avoid I::Identifier type. +impl Visit for AssociatedTyDatum { + fn visit_with<'i, R: VisitResult>( + &self, + visitor: &mut dyn chalk_ir::visit::Visitor<'i, I, Result = R>, + outer_binder: DebruijnIndex, + ) -> R + where + I: 'i, + { + let result = R::new().combine(self.trait_id.visit_with(visitor, outer_binder)); + if result.return_early() { + return result; + } + let result = result.combine(self.id.visit_with(visitor, outer_binder)); + if result.return_early() { + return result; + } + return result.combine(self.binders.visit_with(visitor, outer_binder)); + } +} + /// Encodes the parts of `AssociatedTyDatum` where the parameters /// `P0..Pm` are in scope (`bounds` and `where_clauses`). #[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit, HasInterner)] diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 04cf9bc5cff..b10143cfa2b 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -82,7 +82,6 @@ fn records_parents_parent() { } #[test] -#[ignore] fn records_associated_type_bounds() { logging_db_output_sufficient! { program { @@ -155,6 +154,25 @@ fn records_generic_impls() { } } +#[test] +fn catches_assoc_type_bounds() { + logging_db_output_sufficient! { + program { + trait Foo { + type Assoc: Bar; + } + trait Bar {} + impl Foo for () {} + } + + goal { + (): Foo + } yields { + "Unique" + } + } +} + #[test] #[ignore] fn does_not_need_necessary_separate_impl() { From fa5572879206d5f9d2a5c0d557b880dd0283ba33 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 12:30:42 -0700 Subject: [PATCH 57/70] Add adt_repr to Logging DB Fix needed after rebasing program-writer branch on latest master since `LoggingRustIrDatabase` implements `RustIrDatabase`. Co-authored-by: David Ross --- chalk-solve/src/logging_db.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 405e7e957d8..894a56bfcf8 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -117,6 +117,11 @@ where self.db.borrow().adt_datum(adt_id) } + fn adt_repr(&self, id: AdtId) -> AdtRepr { + self.record(id); + self.db.borrow().adt_repr(id) + } + fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.record(impl_id); self.db.borrow().impl_datum(impl_id) @@ -336,6 +341,10 @@ where self.db.borrow().adt_datum(adt_id) } + fn adt_repr(&self, id: AdtId) -> AdtRepr { + self.db.borrow().adt_repr(id) + } + fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.db.borrow().impl_datum(impl_id) } From 35fc303cc156ca5ad991a218304f127baa1e13bb Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 12:40:48 -0700 Subject: [PATCH 58/70] Fix logging db missing assoc ty value names The logging db that wraps RustIrDatabase was not collecting the ids for items referenced in assocated type values. The visitor we are repurposing from the solver does not cross identifier boundaries. The `AssocTyDatum` stores the values as a set of ids, so they aren't visited. We extended the id collector to resolve the assoc ty value ids in an impl block, and then visit them. Co-authored-by: David Ross --- chalk-solve/src/logging_db/id_collector.rs | 10 +++++---- tests/logging_db/mod.rs | 26 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/chalk-solve/src/logging_db/id_collector.rs b/chalk-solve/src/logging_db/id_collector.rs index faf044c598d..7946341286e 100644 --- a/chalk-solve/src/logging_db/id_collector.rs +++ b/chalk-solve/src/logging_db/id_collector.rs @@ -72,10 +72,12 @@ pub fn collect_unrecorded_ids<'i, I: Interner, DB: RustIrDatabase>( .visit_with(&mut collector, DebruijnIndex::INNERMOST); } RecordedItemId::Impl(impl_id) => { - collector - .db - .impl_datum(impl_id) - .visit_with(&mut collector, DebruijnIndex::INNERMOST); + let impl_datum = collector.db.impl_datum(impl_id); + for id in &impl_datum.associated_ty_value_ids { + let assoc_ty_value = collector.db.associated_ty_value(*id); + assoc_ty_value.visit_with(&mut collector, DebruijnIndex::INNERMOST); + } + impl_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST); } } } diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index b10143cfa2b..d5702fdc288 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -162,7 +162,30 @@ fn catches_assoc_type_bounds() { type Assoc: Bar; } trait Bar {} - impl Foo for () {} + impl Foo for () { + type Assoc = (); + } + } + + goal { + (): Foo + } yields { + "Unique" + } + } +} + +#[test] +fn catches_assoc_type_values() { + logging_db_output_sufficient! { + program { + trait Foo { + type Assoc; + } + struct Baz {} + impl Foo for () { + type Assoc = Baz; + } } goal { @@ -174,7 +197,6 @@ fn catches_assoc_type_bounds() { } #[test] -#[ignore] fn does_not_need_necessary_separate_impl() { // this should leave out "impl Bar for Fox" and the result should pass the // test (it won't be well-formed, but that's OK.) From d9b4e48013243d154d25fca55b07c2846d3ac06c Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 13:39:51 -0700 Subject: [PATCH 59/70] Refactor flag macro to error on missing flags Refactor the flag macro out of the TraitDatum impl, since it is also needed for struct flags. Additionally, we altered the macro to leverage struct destructuring exhaustiveness to error if any flags are missing. Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 73 ++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 33b1513744c..8e051054f5a 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -10,6 +10,48 @@ use super::{ state::WriterState, }; +/// Used in `AdtDatum` and `TraitDatum` to write n flags from a flags struct +/// to a writer. Each flag field turns into an if expression + write!, so we can +/// just list the names and not repeat this pattern over and over. +/// +/// This macro will error if unknown flags are specified. This will also error +/// if any flags are missing. +/// +/// # Usage +/// +/// ```rust,ignore +/// write_flags!(f, self.flags, XFlags { red, green }) +/// ``` +/// +/// Turns into +/// +/// ```rust,ignore +/// match self.flags { +/// XFlags { red, green } => { +/// if red { +/// write!(f, "#[red]")?; +/// } +/// if green { +/// write!(f, "#[green]")?; +/// } +/// } +/// } +/// ``` +macro_rules! write_flags { + ($writer:ident, $val:expr, $struct_name:ident { $($n:ident),* }) => { + match $val { + // if any fields are missing, the destructuring will error + $struct_name { + $($n,)* + } => { + $(if $n { + write!($writer,"#[{}]\n",stringify!($n))?; + })* + } + } + } +} + impl RenderAsRust for AdtDatum { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // When support for Self in structs is added, self_binding should be @@ -53,31 +95,34 @@ impl RenderAsRust for TraitDatum { let s = &s.add_debrujin_index(Some(0)); let value = self.binders.skip_binders(); - macro_rules! trait_flags { - ($($n:ident),*) => { - $(if self.flags.$n { - write!(f,"#[{}]\n",stringify!($n))?; - })* + // flags + write_flags!( + f, + self.flags, + TraitFlags { + auto, + marker, + upstream, + fundamental, + non_enumerable, + coinductive } - } - - trait_flags!( - auto, - marker, - upstream, - fundamental, - non_enumerable, - coinductive ); + + // trait declaration let binders = s.binder_var_display(&self.binders.binders).skip(1); write!(f, "trait {}", self.id.display(s))?; write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; + + // where clauses if !value.where_clauses.is_empty() { let s = &s.add_indent(); write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; } else { write!(f, " ")?; } + + // body write!(f, "{{")?; let s = &s.add_indent(); write_joined_non_empty_list!( From 83304fa50101d46efa856e361bec6309ef6597f7 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 13:41:01 -0700 Subject: [PATCH 60/70] Add struct flag rendering Adds the phantom_data, upstream, and fundamental flags: ``` struct Bar {} ``` Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 18 ++++++++++++++++++ tests/display/struct_.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 8e051054f5a..1fedf77251c 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -58,14 +58,32 @@ impl RenderAsRust for AdtDatum { // changed to Some(0) let s = &s.add_debrujin_index(None); let value = self.binders.skip_binders(); + + // flags + write_flags!( + f, + self.flags, + AdtFlags { + // Ordering matters + upstream, + fundamental, + phantom_data + } + ); + + // name write!(f, "struct {}", self.id.display(s),)?; write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.binders.binders), ", ")?; + + // where clauses if !value.where_clauses.is_empty() { let s = &s.add_indent(); write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; } else { write!(f, " ")?; } + + // body write!(f, "{{")?; let s = &s.add_indent(); write_joined_non_empty_list!( diff --git a/tests/display/struct_.rs b/tests/display/struct_.rs index f2e0a75d9e0..bb8930e0672 100644 --- a/tests/display/struct_.rs +++ b/tests/display/struct_.rs @@ -31,3 +31,33 @@ fn test_struct_fields() { } ); } + +#[test] +fn test_struct_keywords() { + reparse_test!( + program { + #[upstream] + struct UpstreamFoo {} + } + ); + reparse_test!( + program { + #[fundamental] + struct FundamentalFoo {} + } + ); + reparse_test!( + program { + #[phantom_data] + struct PhantomFoo {} + } + ); + reparse_test!( + program { + #[upstream] + #[fundamental] + #[phantom_data] + struct Bar {} + } + ); +} From 949c75a334693883f171b5ab337c37bb3dec3a8a Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 13:44:00 -0700 Subject: [PATCH 61/70] Add impl type flag rendering Adds rendering for the `#[upstream]` flag on impls. Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 34 ++++++++++++++++++++++++++++---- tests/display/impl_.rs | 12 +++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 1fedf77251c..73b15d00750 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -163,18 +163,36 @@ impl RenderAsRust for ImplDatum { let s = &s.add_debrujin_index(None); let binders = s.binder_var_display(&self.binders.binders); - let value = self.binders.skip_binders(); + // annotations + // #[upstream] + // ^^^^^^^^^^^ + // impl Foo for Bar where T: Baz { } + if self.impl_type == ImplType::External { + write!(f, "#[upstream]\n")?; + } + + // impl keyword + // impl Foo for Bar where T: Baz { } + // ^^^^ + write!(f, "impl")?; let trait_ref = &value.trait_ref; - // Ignore automatically added Self parameter by skipping first parameter + + // generic binders + // impl Foo for Bar where T: Baz + // ^^^ + write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; + + // trait, type and parameters + // impl Foo for Bar where T: Baz { } + // ^^^^^^^^^^^^^^^^^ let full_trait_name = display_trait_with_generics( s, trait_ref.trait_id, + // Ignore automatically added Self parameter by skipping first parameter &trait_ref.substitution.parameters(interner)[1..], ); - write!(f, "impl")?; - write_joined_non_empty_list!(f, "<{}>", binders, ", ")?; write!( f, " {}{} for {}", @@ -182,12 +200,20 @@ impl RenderAsRust for ImplDatum { full_trait_name, trait_ref.self_type_parameter(interner).display(s) )?; + + // where clauses + // impl Foo for Bar where T: Baz { } + // ^^^^^^^^^^^^ if !value.where_clauses.is_empty() { let s = &s.add_indent(); write!(f, "\nwhere\n{}\n", value.where_clauses.display(s))?; } else { write!(f, " ")?; } + + // body + // impl Foo for Bar where T: Baz { } + // ^^^ write!(f, "{{")?; { let s = &s.add_indent(); diff --git a/tests/display/impl_.rs b/tests/display/impl_.rs index 97f8b61bfc5..4f84cf24279 100644 --- a/tests/display/impl_.rs +++ b/tests/display/impl_.rs @@ -36,3 +36,15 @@ fn test_impl_for_generic() { } ); } + +#[test] +fn test_upstream_impl_keyword() { + reparse_test!( + program { + struct Bar {} + trait Foo {} + #[upstream] + impl Foo for Bar {} + } + ); +} From 06d5e4bdf4607bd43a7e092ed72f549e9db27fd1 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 13:45:09 -0700 Subject: [PATCH 62/70] Document display sub modules Added documentation to describe the purpose and scope of each of the display sub modules. Co-authored-by: David Ross --- chalk-solve/src/display/bounds.rs | 3 +++ chalk-solve/src/display/identifiers.rs | 4 ++++ chalk-solve/src/display/items.rs | 5 +++++ chalk-solve/src/display/render_trait.rs | 1 + chalk-solve/src/display/state.rs | 3 +++ chalk-solve/src/display/ty.rs | 3 +++ chalk-solve/src/display/utils.rs | 1 + 7 files changed, 20 insertions(+) diff --git a/chalk-solve/src/display/bounds.rs b/chalk-solve/src/display/bounds.rs index 3ef16fc4acf..f58c54e3836 100644 --- a/chalk-solve/src/display/bounds.rs +++ b/chalk-solve/src/display/bounds.rs @@ -1,3 +1,6 @@ +//! Writer logic for `where` clauses and other bounds. +//! +//! Contains logic for writing the various forms of `Foo: Bar`. use std::fmt::{Display, Formatter, Result}; use crate::rust_ir::*; diff --git a/chalk-solve/src/display/identifiers.rs b/chalk-solve/src/display/identifiers.rs index cec132430a0..e8ec230871a 100644 --- a/chalk-solve/src/display/identifiers.rs +++ b/chalk-solve/src/display/identifiers.rs @@ -1,3 +1,7 @@ +//! Writer logic for simple IDs +//! +//! `RenderAsRust` impls for identifiers which are either too small or too +//! shared to belong anywhere else belong here. use std::fmt::{Formatter, Result}; use chalk_ir::interner::Interner; diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 73b15d00750..d8eae979d31 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -1,3 +1,8 @@ +//! Writer logic for top level items. +//! +//! Contains code specific to top-level items and other structures specific to a +//! single top-level item. + use std::fmt::{Formatter, Result}; use crate::rust_ir::*; diff --git a/chalk-solve/src/display/render_trait.rs b/chalk-solve/src/display/render_trait.rs index 3800b5fa7ef..17344741ecb 100644 --- a/chalk-solve/src/display/render_trait.rs +++ b/chalk-solve/src/display/render_trait.rs @@ -1,3 +1,4 @@ +//! `RenderAsRust` trait and related utils. use std::fmt::{Display, Formatter, Result}; use chalk_ir::interner::Interner; diff --git a/chalk-solve/src/display/state.rs b/chalk-solve/src/display/state.rs index 40ff4f61fd2..5923d837f12 100644 --- a/chalk-solve/src/display/state.rs +++ b/chalk-solve/src/display/state.rs @@ -1,3 +1,6 @@ +//! Persistent state passed down between writers. +//! +//! This is essentially `WriterState` and other things supporting that. use std::{ cell::RefCell, collections::BTreeMap, diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index 926b84907fd..902c6993702 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -1,3 +1,6 @@ +//! Writer logic for types. +//! +//! Contains the highly-recursive logic for writing `TyData` and its variants. use std::fmt::{Formatter, Result}; use crate::split::Split; diff --git a/chalk-solve/src/display/utils.rs b/chalk-solve/src/display/utils.rs index 955ee2a9555..d260a7171ab 100644 --- a/chalk-solve/src/display/utils.rs +++ b/chalk-solve/src/display/utils.rs @@ -1,3 +1,4 @@ +//! Render utilities which don't belong anywhere else. use std::fmt::{Display, Formatter, Result}; pub fn as_display) -> Result>(f: F) -> impl Display { From e656484056947bea8eaebeb3042243fe0eba6e6a Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 15:29:51 -0700 Subject: [PATCH 63/70] Add struct repr rendering This supports #[repr(C)] and #[repr(Rust)] on adts. Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 24 ++++++++++++++++++--- tests/display/struct_.rs | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index d8eae979d31..791e8b0a291 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -43,18 +43,24 @@ use super::{ /// } /// ``` macro_rules! write_flags { - ($writer:ident, $val:expr, $struct_name:ident { $($n:ident),* }) => { + ($writer:ident, $val:expr, $struct_name:ident { $($n:ident $(: $extra_arg:tt)?),* }) => { match $val { // if any fields are missing, the destructuring will error $struct_name { $($n,)* } => { $(if $n { - write!($writer,"#[{}]\n",stringify!($n))?; + write!($writer, "#[{}]\n", write_flags!(@default $n $(: $extra_arg)*))?; })* } } - } + }; + (@default $n:ident : $name:literal) => { + $name + }; + (@default $n:ident ) => { + stringify!($n) + }; } impl RenderAsRust for AdtDatum { @@ -76,6 +82,18 @@ impl RenderAsRust for AdtDatum { } ); + // repr + let repr = s.db.adt_repr(self.id); + + write_flags!( + f, + repr, + AdtRepr { + repr_c: "repr(C)", + repr_packed: "repr(packed)" + } + ); + // name write!(f, "struct {}", self.id.display(s),)?; write_joined_non_empty_list!(f, "<{}>", s.binder_var_display(&self.binders.binders), ", ")?; diff --git a/tests/display/struct_.rs b/tests/display/struct_.rs index bb8930e0672..f0cdf27b185 100644 --- a/tests/display/struct_.rs +++ b/tests/display/struct_.rs @@ -61,3 +61,40 @@ fn test_struct_keywords() { } ); } + +#[test] +fn test_struct_repr() { + reparse_test!( + program { + #[repr(C)] + struct CFoo {} + } + ); + reparse_test!( + program { + #[repr(packed)] + struct PackedFoo {} + } + ); + reparse_test!( + program { + #[repr(C)] + #[repr(packed)] + struct CPackedFoo {} + } + ); + reparse_test!( + program { + #[repr(packed)] + #[repr(C)] + struct PackedCFoo {} + } + ); + reparse_test!( + program { + #[upstream] + #[repr(C)] + struct UpstreamCFoo {} + } + ); +} From 750733ffc9ae38f5ef520bae181f493d21e456b6 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 15:49:12 -0700 Subject: [PATCH 64/70] Implement rendering for well known traits Added rendering for well known trait markers, such as: ``` trait Sized { } ``` Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 15 +++++++++++++++ tests/display/trait_.rs | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 791e8b0a291..28391147fdb 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -150,6 +150,21 @@ impl RenderAsRust for TraitDatum { } ); + // well-known + if let Some(well_known) = self.well_known { + let name = match well_known { + WellKnownTrait::Sized => "sized", + WellKnownTrait::Copy => "copy", + WellKnownTrait::Clone => "clone", + WellKnownTrait::Drop => "drop", + WellKnownTrait::FnOnce => "fn_once", + WellKnownTrait::FnMut => "fn_mut", + WellKnownTrait::Fn => "fn", + WellKnownTrait::Unsize => "unsize", + }; + write!(f, "#[lang({})]\n", name)?; + } + // trait declaration let binders = s.binder_var_display(&self.binders.binders).skip(1); write!(f, "trait {}", self.id.display(s))?; diff --git a/tests/display/trait_.rs b/tests/display/trait_.rs index b5d314d8ce6..cab0e5884c8 100644 --- a/tests/display/trait_.rs +++ b/tests/display/trait_.rs @@ -58,3 +58,28 @@ fn test_trait_flags() { )); } } + +#[test] +fn test_wellknown_traits() { + let well_knowns = vec![ + "sized", "copy", "clone", "drop", "fn_once", "fn_mut", "fn", "unsize", + ]; + for flag in well_knowns { + reparse_test(&format!( + " + #[lang({0})] + trait Hello_{0} {{}} + ", + flag + )); + } + reparse_test!( + program { + #[auto] + #[lang(sized)] + trait Foo { + + } + } + ); +} From 68263e8f359439574792592b3f0b75050bf30b46 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 16:06:42 -0700 Subject: [PATCH 65/70] Add object_safe trait flag rendering The object_safe flag is handled differently to the other flags. It is accessed by calling a seperate method, `RustIrDatabase::is_object_safe` instead of being included in the `TraitFlags` data structure. Co-authored-by: David Ross --- chalk-solve/src/display/items.rs | 5 +++++ tests/display/trait_.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 28391147fdb..7b554e0b962 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -150,6 +150,11 @@ impl RenderAsRust for TraitDatum { } ); + // object safe + if s.db.is_object_safe(self.id) { + write!(f, "#[object_safe]\n")?; + } + // well-known if let Some(well_known) = self.well_known { let name = match well_known { diff --git a/tests/display/trait_.rs b/tests/display/trait_.rs index cab0e5884c8..a716ae59af6 100644 --- a/tests/display/trait_.rs +++ b/tests/display/trait_.rs @@ -39,6 +39,7 @@ fn test_trait_flags() { "fundamental", "non_enumerable", "coinductive", + "object_safe", ]; reparse_test(&format!( "{}trait Hello {{}}", From a5c160e9dc61b6b5d4cda6b5fc68f9709eb4d7fa Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sat, 27 Jun 2020 16:10:14 -0700 Subject: [PATCH 66/70] Improve LoggingRustIrDatabase tests The following improvements were made: * Added a documentation comment for tests/logging_db/mod.rs * Renamed some methods for clarity * Updated outdated documentation comment * Re-enabled tests that are now working Co-authored-by: David Ross --- tests/display/fn_.rs | 6 +----- tests/logging_db/mod.rs | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/tests/display/fn_.rs b/tests/display/fn_.rs index 71c62a48392..9bd10fd2af3 100644 --- a/tests/display/fn_.rs +++ b/tests/display/fn_.rs @@ -51,11 +51,7 @@ fn test_opaque_ty_with_fn_def() { ); } -/// This test fails because lowering code discards function arguments when -/// lowering `foo` into an `fn()` type, and in the parser, `fn` types must -/// always have exactly one argument to parse correctly. So `fn bar() -> foo;` -/// becomes `fn bar() -> fn()` (note: not `fn(u32)`), and then the parser -/// rejects `fn()` because it has 0 arguments, not 1. +/// We do not yet support "fn def" types, which this uses. #[test] #[ignore] fn test_fn_as_type() { diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index d5702fdc288..343fd5c010d 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -1,8 +1,19 @@ +//! Tests for `LoggingRustIrDatabase` which tests its functionality to record +//! types and stubs. +//! +//! Each tests records the trait solver solving something, and then runs the +//! solver on the output `LoggingRustIrDatabase` writes.These tests _don't_ test +//! that the output program is identical to the input, only that the resulting +//! program allows solving the same goals. +//! +//! Note that this does not, and should not, test the majority of the rendering +//! code. The code to render specific items and syntax details is rigorously +//! tested in `tests/display/`. #[macro_use] mod util; #[test] -fn records_struct_and_trait() { +fn records_struct_trait_and_impl() { logging_db_output_sufficient! { program { struct S {} @@ -21,7 +32,6 @@ fn records_struct_and_trait() { } #[test] -#[ignore] fn records_opaque_type() { logging_db_output_sufficient! { program { @@ -42,7 +52,6 @@ fn records_opaque_type() { } #[test] -#[ignore] fn records_fn_def() { logging_db_output_sufficient! { program { @@ -59,6 +68,28 @@ fn records_fn_def() { } } +#[test] +fn records_generics() { + logging_db_output_sufficient! { + program { + struct Foo {} + trait Bar {} + impl Bar for Foo<()> {} + } + + goal { + Foo<()>: Bar + } yields { + "Unique" + } + goal { + Foo: Bar + } yields { + "No possible solution" + } + } +} + #[test] fn records_parents_parent() { logging_db_output_sufficient! { @@ -155,7 +186,7 @@ fn records_generic_impls() { } #[test] -fn catches_assoc_type_bounds() { +fn catches_assoc_type_bounds_not_mentioned() { logging_db_output_sufficient! { program { trait Foo { @@ -176,7 +207,7 @@ fn catches_assoc_type_bounds() { } #[test] -fn catches_assoc_type_values() { +fn catches_assoc_type_values_not_mentioned() { logging_db_output_sufficient! { program { trait Foo { From 024a8a4f2b5fb8083f73b6f6d95b19c96c688b73 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 28 Jun 2020 01:51:50 -0700 Subject: [PATCH 67/70] Add opaque type Ty rendering This adds rendering for using an opaque type as a type, for instance, OpaqueTy. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 9 +++++---- chalk-solve/src/display/bounds.rs | 6 +++--- chalk-solve/src/display/items.rs | 4 ++-- chalk-solve/src/display/ty.rs | 18 ++++++++++++++---- tests/display/opaque_ty.rs | 26 +++++++++++++++++++++++--- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 95113823886..23ee2b67773 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -181,7 +181,7 @@ fn display_self_where_clauses_as_bounds<'a, I: Interner>( )?; } match &bound.skip_binders() { - WhereClause::Implemented(trait_ref) => display_trait_with_generics( + WhereClause::Implemented(trait_ref) => display_type_with_generics( s, trait_ref.trait_id, &trait_ref.substitution.parameters(interner)[1..], @@ -212,10 +212,11 @@ fn display_self_where_clauses_as_bounds<'a, I: Interner>( }) } -/// Displays a trait with its parameters - something like `AsRef`. +/// Displays a type with its parameters - something like `AsRef`, +/// OpaqueTyName, or `AdtName`. /// -/// This is shared between where bounds & dyn Trait. -fn display_trait_with_generics<'a, I: Interner>( +/// This is shared between where bounds, OpaqueTy, & dyn Trait. +fn display_type_with_generics<'a, I: Interner>( s: &'a WriterState<'a, I>, trait_name: impl RenderAsRust + 'a, trait_params: impl IntoIterator> + 'a, diff --git a/chalk-solve/src/display/bounds.rs b/chalk-solve/src/display/bounds.rs index f58c54e3836..ea9bf511922 100644 --- a/chalk-solve/src/display/bounds.rs +++ b/chalk-solve/src/display/bounds.rs @@ -8,7 +8,7 @@ use chalk_ir::{interner::Interner, *}; use itertools::Itertools; use super::{ - display_trait_with_assoc_ty_value, display_trait_with_generics, render_trait::RenderAsRust, + display_trait_with_assoc_ty_value, display_type_with_generics, render_trait::RenderAsRust, state::WriterState, }; use crate::split::Split; @@ -26,7 +26,7 @@ impl RenderAsRust for InlineBound { impl RenderAsRust for TraitBound { fn fmt(&self, s: &WriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { - display_trait_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) + display_type_with_generics(s, self.trait_id, &self.args_no_self).fmt(f) } } @@ -104,7 +104,7 @@ impl RenderAsRust for TraitRef { f, "{}: {}", self.self_type_parameter(interner).display(s), - display_trait_with_generics( + display_type_with_generics( s, self.trait_id, &self.substitution.parameters(interner)[1..] diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 7b554e0b962..62c86e7dde2 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -11,7 +11,7 @@ use chalk_ir::interner::Interner; use itertools::Itertools; use super::{ - display_self_where_clauses_as_bounds, display_trait_with_generics, render_trait::RenderAsRust, + display_self_where_clauses_as_bounds, display_type_with_generics, render_trait::RenderAsRust, state::WriterState, }; @@ -230,7 +230,7 @@ impl RenderAsRust for ImplDatum { // trait, type and parameters // impl Foo for Bar where T: Baz { } // ^^^^^^^^^^^^^^^^^ - let full_trait_name = display_trait_with_generics( + let full_trait_name = display_type_with_generics( s, trait_ref.trait_id, // Ignore automatically added Self parameter by skipping first parameter diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index 902c6993702..25f498f7788 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -8,7 +8,7 @@ use chalk_ir::{interner::Interner, *}; use itertools::Itertools; use super::{ - display_self_where_clauses_as_bounds, display_trait_with_generics, render_trait::RenderAsRust, + display_self_where_clauses_as_bounds, display_type_with_generics, render_trait::RenderAsRust, state::WriterState, }; @@ -68,7 +68,7 @@ impl RenderAsRust for ProjectionTy { f, "<{} as {}>::{}", trait_params[0].display(s), - display_trait_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), + display_type_with_generics(s, assoc_ty_datum.trait_id, &trait_params[1..]), assoc_ty_datum.id.display(s), )?; write_joined_non_empty_list!( @@ -87,7 +87,7 @@ impl RenderAsRust for OpaqueTy { write!( f, "{}", - display_trait_with_generics( + display_type_with_generics( s, self.opaque_ty_id, self.substitution.parameters(interner), @@ -173,7 +173,17 @@ impl RenderAsRust for ApplicationTy { } )? } - TypeName::OpaqueType(_) => todo!("opaque type usage"), + TypeName::OpaqueType(opaque_ty_id) => { + write!( + f, + "{}", + display_type_with_generics( + s, + opaque_ty_id, + self.substitution.parameters(interner) + ) + )?; + } TypeName::Raw(raw) => { let mutability = match raw { Mutability::Mut => "*mut ", diff --git a/tests/display/opaque_ty.rs b/tests/display/opaque_ty.rs index 5e120ed35d3..05472d7551b 100644 --- a/tests/display/opaque_ty.rs +++ b/tests/display/opaque_ty.rs @@ -16,6 +16,15 @@ fn opaque_types() { ); } +#[test] +fn opaque_ty_no_bounds() { + reparse_test!( + program { + opaque type Foo: = (); + } + ); +} + #[test] fn test_generic_opaque_types() { reparse_test!( @@ -66,7 +75,6 @@ fn test_opaque_type_as_type_value() { ); } -#[ignore] #[test] fn test_generic_opaque_type_as_value1() { reparse_test!( @@ -104,11 +112,23 @@ fn test_generic_opaque_type_as_value1() { trait Fez { type Assoc; } - impl Fez for Foo { + impl Fez for Foo { type Assoc = fn(Biiiz); } - impl Bar for Foo {} + impl Bar for Foo {} opaque type Biiiz: Bar = Foo; } ); } + +#[test] +fn multiple_bounds() { + reparse_test!( + program { + struct Baz {} + trait Foo {} + trait Fuu {} + opaque type Bar: Foo + Fuu = Baz; + } + ); +} From e0f325e49efb4be3316f56633e1214252396f3dd Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 28 Jun 2020 02:30:06 -0700 Subject: [PATCH 68/70] Fix missing opaque ty stubs Added code to collect the identifiers of opaque ty values + test. Co-authored-by: David Ross --- chalk-solve/src/logging_db/id_collector.rs | 4 ++++ tests/logging_db/mod.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/chalk-solve/src/logging_db/id_collector.rs b/chalk-solve/src/logging_db/id_collector.rs index 7946341286e..534d9d6a6c7 100644 --- a/chalk-solve/src/logging_db/id_collector.rs +++ b/chalk-solve/src/logging_db/id_collector.rs @@ -70,6 +70,10 @@ pub fn collect_unrecorded_ids<'i, I: Interner, DB: RustIrDatabase>( .db .opaque_ty_data(opaque_id) .visit_with(&mut collector, DebruijnIndex::INNERMOST); + collector + .db + .hidden_opaque_type(opaque_id) + .visit_with(&mut collector, DebruijnIndex::INNERMOST); } RecordedItemId::Impl(impl_id) => { let impl_datum = collector.db.impl_datum(impl_id); diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 343fd5c010d..97c92b410fc 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -227,6 +227,24 @@ fn catches_assoc_type_values_not_mentioned() { } } +#[test] +fn stubs_types_from_opaque_ty_bounds() { + logging_db_output_sufficient! { + program { + trait Foo {} + trait Fuu {} + struct Baz {} + opaque type Bar: Foo + Fuu = Baz; + } + + goal { + Bar: Foo + } yields { + "Unique" + } + } +} + #[test] fn does_not_need_necessary_separate_impl() { // this should leave out "impl Bar for Fox" and the result should pass the From 20fe0c63dd64b158bd00204b8a870921388585b1 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 28 Jun 2020 02:37:40 -0700 Subject: [PATCH 69/70] Refactor stub code into RustIrDatabase wrapper We were stubbing collected ids (ids that were not directly captured by the logging db but were needed for name resolution to work) by cloning top level item data structures, removing bounds and other references to addtional types, then feeding them into the display code in a method called `write_stub_items`. This approach was not successful, because we were unable to remove certain bounds, like those on associated types, using this method because that data is read from the DB by the display code, rather than getting passed down, making it untouchable. To work around this, we have created yet another RustIrDatabase wrapper that will return data structures with bounds etc. removed. Co-authored-by: David Ross --- chalk-solve/src/display.rs | 53 +------- chalk-solve/src/display/stub.rs | 230 ++++++++++++++++++++++++++++++++ tests/logging_db/mod.rs | 142 +++++++++++++++++++- 3 files changed, 373 insertions(+), 52 deletions(-) create mode 100644 chalk-solve/src/display/stub.rs diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 23ee2b67773..28064aed3da 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -17,6 +17,7 @@ mod identifiers; mod items; mod render_trait; mod state; +mod stub; mod ty; pub use self::render_trait::*; @@ -35,7 +36,7 @@ where /// Writes stubs for items which were referenced by name, but for which we /// didn't directly access. For instance, traits mentioned in where bounds which -/// are only usually checked during well-formedness, when we waren't recording +/// are only usually checked during well-formedness, when we weren't recording /// well-formedness. /// /// The "stub" nature of this means it writes output with the right names and @@ -50,55 +51,7 @@ where DB: RustIrDatabase, T: IntoIterator>, { - let ws = &WriterState::new(db); - for id in ids { - match id { - RecordedItemId::Impl(_id) => unreachable!(), - RecordedItemId::Adt(id) => { - let mut v = (*db.adt_datum(id)).clone(); - v.binders = Binders::new( - VariableKinds::new(ws.db.interner()), - AdtDatumBound { - fields: Vec::new(), - where_clauses: Vec::new(), - }, - ); - write_item(f, ws, &v)?; - } - RecordedItemId::Trait(id) => { - let mut v = (*db.trait_datum(id)).clone(); - v.binders = Binders::new( - VariableKinds::new(ws.db.interner()), - TraitDatumBound { - where_clauses: Vec::new(), - }, - ); - write_item(f, ws, &v)?; - } - RecordedItemId::OpaqueTy(id) => { - let mut v = (*db.opaque_ty_data(id)).clone(); - v.bound = Binders::new( - VariableKinds::new(ws.db.interner()), - OpaqueTyDatumBound { - bounds: Binders::new(VariableKinds::new(ws.db.interner()), Vec::new()), - }, - ); - write_item(f, ws, &v)?; - } - RecordedItemId::FnDef(id) => { - let mut v = (*db.fn_def_datum(id)).clone(); - v.binders = Binders::new( - VariableKinds::new(ws.db.interner()), - FnDefDatumBound { - inputs_and_output: v.binders.skip_binders().inputs_and_output.clone(), - where_clauses: Vec::new(), - }, - ); - write_item(f, ws, &v)?; - } - } - } - Ok(()) + write_items(f, &stub::StubWrapper::new(db), ids) } /// Writes out each item recorded by a [`LoggingRustIrDatabase`]. diff --git a/chalk-solve/src/display/stub.rs b/chalk-solve/src/display/stub.rs new file mode 100644 index 00000000000..525a25a481c --- /dev/null +++ b/chalk-solve/src/display/stub.rs @@ -0,0 +1,230 @@ +//! Contains a `LoggingIrDatabase` which returns stub versions of everything +//! queried. +use std::sync::Arc; + +use crate::{ + rust_ir::{ + AdtDatumBound, AssociatedTyDatumBound, FnDefDatumBound, OpaqueTyDatumBound, TraitDatumBound, + }, + RustIrDatabase, +}; +use chalk_ir::{interner::Interner, ApplicationTy, Binders, TypeName, VariableKinds}; + +#[derive(Debug)] +pub struct StubWrapper<'a, DB> { + db: &'a DB, +} + +impl<'a, DB> StubWrapper<'a, DB> { + pub fn new(db: &'a DB) -> Self { + StubWrapper { db } + } +} + +impl> RustIrDatabase for StubWrapper<'_, DB> { + fn custom_clauses(&self) -> Vec> { + self.db.custom_clauses() + } + + fn associated_ty_data( + &self, + ty: chalk_ir::AssocTypeId, + ) -> std::sync::Arc> { + let mut v = (*self.db.associated_ty_data(ty)).clone(); + v.binders = Binders::new( + v.binders.binders.clone(), + AssociatedTyDatumBound { + where_clauses: Vec::new(), + bounds: Vec::new(), + }, + ); + Arc::new(v) + } + + fn trait_datum( + &self, + trait_id: chalk_ir::TraitId, + ) -> std::sync::Arc> { + let mut v = (*self.db.trait_datum(trait_id)).clone(); + v.binders = Binders::new( + v.binders.binders.clone(), + TraitDatumBound { + where_clauses: Vec::new(), + }, + ); + Arc::new(v) + } + + fn adt_datum(&self, adt_id: chalk_ir::AdtId) -> std::sync::Arc> { + let mut v = (*self.db.adt_datum(adt_id)).clone(); + v.binders = Binders::new( + v.binders.binders.clone(), + AdtDatumBound { + fields: Vec::new(), + where_clauses: Vec::new(), + }, + ); + Arc::new(v) + } + + fn adt_repr(&self, id: chalk_ir::AdtId) -> crate::rust_ir::AdtRepr { + self.db.adt_repr(id) + } + + fn fn_def_datum( + &self, + fn_def_id: chalk_ir::FnDefId, + ) -> std::sync::Arc> { + let mut v = (*self.db.fn_def_datum(fn_def_id)).clone(); + v.binders = Binders::new( + v.binders.binders.clone(), + FnDefDatumBound { + inputs_and_output: v.binders.skip_binders().inputs_and_output.clone(), + where_clauses: Vec::new(), + }, + ); + Arc::new(v) + } + + fn impl_datum( + &self, + _impl_id: chalk_ir::ImplId, + ) -> std::sync::Arc> { + unreachable!("impl items should never be stubbed") + } + + fn associated_ty_value( + &self, + _id: crate::rust_ir::AssociatedTyValueId, + ) -> std::sync::Arc> { + unreachable!("associated type values should never be stubbed") + } + + fn opaque_ty_data( + &self, + id: chalk_ir::OpaqueTyId, + ) -> std::sync::Arc> { + let mut v = (*self.db.opaque_ty_data(id)).clone(); + v.bound = Binders::new( + v.bound.binders, + OpaqueTyDatumBound { + bounds: Binders::new(VariableKinds::new(self.db.interner()), Vec::new()), + }, + ); + Arc::new(v) + } + + fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { + // Return a unit since the particular hidden type doesn't matter (If it + // did matter, it would have been recorded) + chalk_ir::TyData::Apply(ApplicationTy { + name: TypeName::Tuple(0), + substitution: chalk_ir::Substitution::from( + self.db.interner(), + Vec::>::new(), + ), + }) + .intern(self.db.interner()) + } + + fn impls_for_trait( + &self, + _trait_id: chalk_ir::TraitId, + _parameters: &[chalk_ir::GenericArg], + ) -> Vec> { + // We panic here because the returned ids may not be collected, + // resulting in unresolvable names. + unimplemented!("stub display code should call this") + } + + fn local_impls_to_coherence_check( + &self, + trait_id: chalk_ir::TraitId, + ) -> Vec> { + self.db.local_impls_to_coherence_check(trait_id) + } + + fn impl_provided_for( + &self, + _auto_trait_id: chalk_ir::TraitId, + _adt_id: chalk_ir::AdtId, + ) -> bool { + // We panic here because the returned ids may not be collected, + // resulting in unresolvable names. + unimplemented!("stub display code should call this") + } + + fn well_known_trait_id( + &self, + well_known_trait: crate::rust_ir::WellKnownTrait, + ) -> Option> { + self.db.well_known_trait_id(well_known_trait) + } + + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment, + ) -> chalk_ir::ProgramClauses { + self.db.program_clauses_for_env(environment) + } + + fn interner(&self) -> &I { + self.db.interner() + } + + fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { + self.db.is_object_safe(trait_id) + } + + fn closure_kind( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> crate::rust_ir::ClosureKind { + unimplemented!("cannot stub closures") + } + + fn closure_inputs_and_output( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> chalk_ir::Binders> { + unimplemented!("cannot stub closures") + } + + fn closure_upvars( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> chalk_ir::Binders> { + unimplemented!("cannot stub closures") + } + + fn closure_fn_substitution( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> chalk_ir::Substitution { + unimplemented!("cannot stub closures") + } + + fn trait_name(&self, trait_id: chalk_ir::TraitId) -> String { + self.db.trait_name(trait_id) + } + + fn adt_name(&self, struct_id: chalk_ir::AdtId) -> String { + self.db.adt_name(struct_id) + } + + fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId) -> String { + self.db.assoc_type_name(assoc_ty_id) + } + + fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId) -> String { + self.db.opaque_type_name(opaque_ty_id) + } + + fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId) -> String { + self.db.fn_def_name(fn_def_id) + } +} diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 97c92b410fc..65774a87af3 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -186,7 +186,7 @@ fn records_generic_impls() { } #[test] -fn catches_assoc_type_bounds_not_mentioned() { +fn stubs_types_from_assoc_type_bounds() { logging_db_output_sufficient! { program { trait Foo { @@ -207,7 +207,7 @@ fn catches_assoc_type_bounds_not_mentioned() { } #[test] -fn catches_assoc_type_values_not_mentioned() { +fn stubs_types_from_assoc_type_values_not_mentioned() { logging_db_output_sufficient! { program { trait Foo { @@ -245,6 +245,144 @@ fn stubs_types_from_opaque_ty_bounds() { } } +#[test] +fn stubs_types_in_dyn_ty() { + logging_db_output_sufficient! { + program { + trait Foo { + type Assoc<'a>; + } + trait Other {} + impl Foo for () { + type Assoc<'a> = dyn Other + 'a; + } + } + + goal { + (): Foo + } yields { + "Unique" + } + } +} + +#[test] +fn can_stub_traits_with_unreferenced_assoc_ty() { + // None of our code will bring in `SuperNotReferenced`'s definition, so if + // we fail to remove the bounds on `NotReferenced::Assoc`, then it will fail. + + // two tests where we don't reference the assoc ty. + logging_db_output_sufficient! { + program { + trait SuperNotReferenced {} + trait NotReferenced { + type Assoc: SuperNotReferenced; + } + trait Referenced where Self: NotReferenced {} + impl Referenced for () {} + } + + goal { + (): Referenced + } yields { + "Unique" + } + } + logging_db_output_sufficient! { + program { + trait SuperNotReferenced {} + trait NotReferenced { + type Assoc where Self: SuperNotReferenced; + } + trait Referenced where Self: NotReferenced {} + impl Referenced for () {} + } + + goal { + (): Referenced + } yields { + "Unique" + } + } +} + +#[test] +fn can_stub_traits_with_referenced_assoc_ty() { + // two tests where we do reference the assoc ty + logging_db_output_sufficient! { + program { + trait SuperNotReferenced {} + trait NotReferenced { + type Assoc: SuperNotReferenced; + } + trait Referenced where Self: NotReferenced {} + impl Referenced for () {} + } + + goal { + (): Referenced + } yields { + "Unique" + } + } + logging_db_output_sufficient! { + program { + trait SuperNotReferenced {} + trait NotReferenced { + type Assoc where (): SuperNotReferenced; + } + trait Referenced where Self: NotReferenced {} + impl Referenced for () {} + } + + goal { + (): Referenced + } yields { + "Unique" + } + } +} + +#[test] +fn can_stub_types_referenced_in_alias_ty_generics() { + logging_db_output_sufficient! { + program { + struct ThisTypeShouldBeStubbed {} + trait HasGenericAssoc { + type Assoc; + } + trait Referenced where Self: HasGenericAssoc=()> {} + impl Referenced for () {} + } + + goal { + (): Referenced + } yields { + "Unique" + } + } +} + +#[test] +fn can_stub_types_referenced_in_alias_ty_bounds() { + logging_db_output_sufficient! { + program { + struct ThisTypeShouldBeStubbed {} + trait HasAssoc { + type Assoc; + } + trait Referenced where Self: HasAssoc {} + impl Referenced for () {} + } + + goal { + (): Referenced + } yields { + "Unique" + } + } +} + #[test] fn does_not_need_necessary_separate_impl() { // this should leave out "impl Bar for Fox" and the result should pass the From 232823b2e6c0764c5b67ef8a5d4ef83ece3d9c00 Mon Sep 17 00:00:00 2001 From: Super Tuple Date: Sun, 28 Jun 2020 03:03:47 -0700 Subject: [PATCH 70/70] Implement opaque_ty id collection Collects ids of opaque_tys used as tys in logged items. For isntance, if bar is logged, we will collect Baq's id: ``` opaque type Baq: Foo + Fuu = Baz; opaque type Bar: Foo + Fuu = Baq; ^^^ ^^^ | logged | collected ``` Co-authored-by: David Ross --- chalk-solve/src/logging_db/id_collector.rs | 8 +++- tests/logging_db/mod.rs | 47 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/chalk-solve/src/logging_db/id_collector.rs b/chalk-solve/src/logging_db/id_collector.rs index 534d9d6a6c7..9b111048d26 100644 --- a/chalk-solve/src/logging_db/id_collector.rs +++ b/chalk-solve/src/logging_db/id_collector.rs @@ -133,7 +133,9 @@ where let assoc_ty_datum = self.db.associated_ty_data(projection_ty.associated_ty_id); self.record(assoc_ty_datum.trait_id) } - AliasTy::Opaque(_opaque_ty) => todo!("opaque types!"), + AliasTy::Opaque(opaque_ty) => { + self.record(opaque_ty.opaque_ty_id); + } }, TyData::BoundVar(..) => (), TyData::Dyn(..) => (), @@ -156,7 +158,9 @@ where let assoc_ty_datum = self.db.associated_ty_data(projection_ty.associated_ty_id); self.record(assoc_ty_datum.trait_id) } - AliasTy::Opaque(_opaque_ty) => todo!("opaque types!"), + AliasTy::Opaque(opaque_ty) => { + self.record(opaque_ty.opaque_ty_id); + } }, WhereClause::LifetimeOutlives(_lifetime_outlives) => (), } diff --git a/tests/logging_db/mod.rs b/tests/logging_db/mod.rs index 65774a87af3..16b1d5ea9a0 100644 --- a/tests/logging_db/mod.rs +++ b/tests/logging_db/mod.rs @@ -245,6 +245,53 @@ fn stubs_types_from_opaque_ty_bounds() { } } +#[test] +fn opaque_ty_in_opaque_ty() { + logging_db_output_sufficient! { + program { + trait Foo {} + trait Fuu {} + struct Baz {} + opaque type Baq: Foo + Fuu = Baz; + opaque type Bar: Foo + Fuu = Baq; + } + + goal { + Bar: Foo + } yields { + "Unique" + } + } +} + +#[test] +fn opaque_ty_in_projection() { + logging_db_output_sufficient! { + program { + struct Baz {} + trait Foo {} + trait Fuu {} + trait Fuut { + type Assoc; + } + impl Fuut for Baz { + type Assoc = Baq; + } + impl Foo for Baz + where + Baz: Fuut + { } + opaque type Baq: Foo + Fuu = Baz; + } + + goal { + Baz: Foo + } yields { + "Unique" + } + } +} + #[test] fn stubs_types_in_dyn_ty() { logging_db_output_sufficient! {