//! Provides wrappers over `RustIrDatabase` which record used definitions and write //! `.chalk` files containing those definitions. use std::{ borrow::Borrow, fmt::{self, Debug, Display}, io::Write, marker::PhantomData, sync::Arc, sync::Mutex, }; use crate::rust_ir::*; use crate::{ display::{self, WriterState}, RustIrDatabase, }; use chalk_ir::{interner::Interner, *}; use indexmap::IndexSet; mod id_collector; /// 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 where DB: RustIrDatabase, P: Borrow, I: Interner, { ws: WriterState, def_ids: Mutex>>, _phantom: PhantomData, } impl LoggingRustIrDatabase where DB: RustIrDatabase, P: Borrow, I: Interner, { pub fn new(db: P) -> Self { LoggingRustIrDatabase { ws: WriterState::new(db), def_ids: Default::default(), _phantom: PhantomData, } } } 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(); let stub_ids = id_collector::collect_unrecorded_ids(self.ws.db(), &def_ids); display::write_stub_items(f, &self.ws, stub_ids)?; display::write_items(f, &self.ws, def_ids.iter().copied()) } } impl LoggingRustIrDatabase where DB: RustIrDatabase, P: Borrow, 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 UnificationDatabase for LoggingRustIrDatabase where DB: RustIrDatabase, P: Borrow + Debug, I: Interner, { fn fn_def_variance(&self, fn_def_id: chalk_ir::FnDefId) -> Variances { self.ws .db() .unification_database() .fn_def_variance(fn_def_id) } fn adt_variance(&self, adt_id: chalk_ir::AdtId) -> Variances { self.ws.db().unification_database().adt_variance(adt_id) } } impl RustIrDatabase for LoggingRustIrDatabase where DB: RustIrDatabase, P: Borrow + Debug, I: Interner, { fn custom_clauses(&self) -> Vec> { self.ws.db().custom_clauses() } fn associated_ty_data( &self, ty: chalk_ir::AssocTypeId, ) -> Arc> { let ty_datum = self.ws.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.ws.db().trait_datum(trait_id) } fn adt_datum(&self, adt_id: AdtId) -> Arc> { self.record(adt_id); self.ws.db().adt_datum(adt_id) } fn generator_datum(&self, generator_id: GeneratorId) -> Arc> { self.record(generator_id); self.ws.db().borrow().generator_datum(generator_id) } fn generator_witness_datum( &self, generator_id: GeneratorId, ) -> Arc> { self.record(generator_id); self.ws.db().borrow().generator_witness_datum(generator_id) } fn adt_repr(&self, id: AdtId) -> Arc> { self.record(id); self.ws.db().adt_repr(id) } fn adt_size_align(&self, id: chalk_ir::AdtId) -> Arc { self.record(id); self.ws.db().adt_size_align(id) } fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.record(impl_id); self.ws.db().impl_datum(impl_id) } fn hidden_opaque_type(&self, id: OpaqueTyId) -> Ty { self.record(id); self.ws.db().hidden_opaque_type(id) } fn associated_ty_value( &self, id: crate::rust_ir::AssociatedTyValueId, ) -> Arc> { let value = self.ws.db().associated_ty_value(id); self.record(value.impl_id); value } fn opaque_ty_data(&self, id: OpaqueTyId) -> Arc> { self.record(id); self.ws.db().opaque_ty_data(id) } fn impls_for_trait( &self, trait_id: TraitId, parameters: &[chalk_ir::GenericArg], binders: &CanonicalVarKinds, ) -> Vec> { self.record(trait_id); let impl_ids = self.ws.db().impls_for_trait(trait_id, parameters, binders); 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.ws.db().local_impls_to_coherence_check(trait_id) } fn impl_provided_for(&self, auto_trait_id: TraitId, ty: &TyKind) -> bool { self.record(auto_trait_id); if let TyKind::Adt(adt_id, _) = ty { self.record(*adt_id); } self.ws.db().impl_provided_for(auto_trait_id, ty) } fn well_known_trait_id( &self, well_known_trait: crate::rust_ir::WellKnownTrait, ) -> Option> { let trait_id = self.ws.db().well_known_trait_id(well_known_trait); if let Some(id) = trait_id { self.record(id); } trait_id } fn program_clauses_for_env( &self, environment: &chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { self.ws.db().program_clauses_for_env(environment) } fn interner(&self) -> I { self.ws.db().interner() } fn trait_name(&self, trait_id: TraitId) -> String { self.ws.db().trait_name(trait_id) } fn adt_name(&self, adt_id: AdtId) -> String { self.ws.db().adt_name(adt_id) } fn assoc_type_name(&self, assoc_ty_id: AssocTypeId) -> String { self.ws.db().assoc_type_name(assoc_ty_id) } fn opaque_type_name(&self, opaque_ty_id: OpaqueTyId) -> String { self.ws.db().opaque_type_name(opaque_ty_id) } fn is_object_safe(&self, trait_id: TraitId) -> bool { self.record(trait_id); self.ws.db().is_object_safe(trait_id) } fn fn_def_datum(&self, fn_def_id: chalk_ir::FnDefId) -> Arc> { self.record(fn_def_id); self.ws.db().fn_def_datum(fn_def_id) } fn fn_def_name(&self, fn_def_id: FnDefId) -> String { self.ws.db().fn_def_name(fn_def_id) } fn closure_kind(&self, closure_id: ClosureId, substs: &Substitution) -> ClosureKind { // TODO: record closure IDs self.ws.db().closure_kind(closure_id, substs) } fn closure_inputs_and_output( &self, closure_id: ClosureId, substs: &Substitution, ) -> Binders> { // TODO: record closure IDs self.ws.db().closure_inputs_and_output(closure_id, substs) } fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders> { // TODO: record closure IDs self.ws.db().closure_upvars(closure_id, substs) } fn closure_fn_substitution( &self, closure_id: ClosureId, substs: &Substitution, ) -> Substitution { // TODO: record closure IDs self.ws.db().closure_fn_substitution(closure_id, substs) } fn discriminant_type(&self, ty: Ty) -> Ty { self.ws.db().discriminant_type(ty) } fn unification_database(&self) -> &dyn UnificationDatabase { self } } /// Wraps a [`RustIrDatabase`], and, when dropped, writes out all used /// definition to the given file. /// /// Uses [`LoggingRustIrDatabase`] internally. /// /// Uses a separate type, `P`, for the database stored inside to account for /// `Arc` or wrapping other storage mediums. pub struct WriteOnDropRustIrDatabase where I: Interner, W: Write, DB: RustIrDatabase, P: Borrow, { db: LoggingRustIrDatabase, write: W, } impl fmt::Debug for WriteOnDropRustIrDatabase where I: Interner, W: Write, DB: RustIrDatabase, P: Borrow + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WriteOnDropRustIrDatabase") .field("db", &self.db) .field("write", &"") .finish() } } impl WriteOnDropRustIrDatabase where I: Interner, W: Write, DB: RustIrDatabase, P: Borrow, { pub fn new(db: P, 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 I: Interner, W: Write, DB: RustIrDatabase, P: Borrow, { 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 UnificationDatabase for WriteOnDropRustIrDatabase where I: Interner, W: Write, DB: RustIrDatabase, P: Borrow + Debug, { fn fn_def_variance(&self, fn_def_id: chalk_ir::FnDefId) -> Variances { self.db .borrow() .unification_database() .fn_def_variance(fn_def_id) } fn adt_variance(&self, adt_id: chalk_ir::AdtId) -> Variances { self.db.borrow().unification_database().adt_variance(adt_id) } } impl RustIrDatabase for WriteOnDropRustIrDatabase where I: Interner, W: Write, DB: RustIrDatabase, P: Borrow + Debug, { 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 adt_datum(&self, adt_id: AdtId) -> Arc> { self.db.adt_datum(adt_id) } fn generator_datum(&self, generator_id: GeneratorId) -> Arc> { self.db.borrow().generator_datum(generator_id) } /// Returns the generator witness datum for the generator with the given id. fn generator_witness_datum( &self, generator_id: GeneratorId, ) -> Arc> { self.db.borrow().generator_witness_datum(generator_id) } fn adt_repr(&self, id: AdtId) -> Arc> { self.db.adt_repr(id) } fn adt_size_align(&self, id: chalk_ir::AdtId) -> Arc { self.db.adt_size_align(id) } fn impl_datum(&self, impl_id: ImplId) -> Arc> { self.db.impl_datum(impl_id) } fn associated_ty_value( &self, id: crate::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 hidden_opaque_type(&self, id: OpaqueTyId) -> Ty { self.db.hidden_opaque_type(id) } fn impls_for_trait( &self, trait_id: TraitId, parameters: &[chalk_ir::GenericArg], binders: &CanonicalVarKinds, ) -> Vec> { self.db.impls_for_trait(trait_id, parameters, binders) } 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, ty: &TyKind) -> bool { self.db.impl_provided_for(auto_trait_id, ty) } 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: TraitId) -> bool { self.db.is_object_safe(trait_id) } fn unification_database(&self) -> &dyn UnificationDatabase { self } fn trait_name(&self, trait_id: TraitId) -> String { self.db.trait_name(trait_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) } 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> { self.db.fn_def_datum(fn_def_id) } fn fn_def_name(&self, fn_def_id: FnDefId) -> String { self.db.fn_def_name(fn_def_id) } fn closure_kind(&self, closure_id: ClosureId, substs: &Substitution) -> ClosureKind { // TODO: record closure IDs self.db.closure_kind(closure_id, substs) } fn closure_inputs_and_output( &self, closure_id: ClosureId, substs: &Substitution, ) -> Binders> { self.db.closure_inputs_and_output(closure_id, substs) } fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders> { self.db.closure_upvars(closure_id, substs) } fn closure_fn_substitution( &self, closure_id: ClosureId, substs: &Substitution, ) -> Substitution { self.db.closure_fn_substitution(closure_id, substs) } fn discriminant_type(&self, ty: Ty) -> Ty { self.db.discriminant_type(ty) } } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum RecordedItemId { Adt(AdtId), Trait(TraitId), Impl(ImplId), OpaqueTy(OpaqueTyId), FnDef(FnDefId), Generator(GeneratorId), } impl From> for RecordedItemId { fn from(v: AdtId) -> Self { RecordedItemId::Adt(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 From> for RecordedItemId { fn from(v: FnDefId) -> Self { RecordedItemId::FnDef(v) } } impl From> for RecordedItemId { fn from(v: GeneratorId) -> Self { RecordedItemId::Generator(v) } }