From 6a77ec7bbe6ddbf663dce9529d11d1bb56c5489a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Aug 2020 16:35:29 +0200 Subject: Rename ra_hir_ty -> hir_ty --- crates/hir_ty/src/traits/chalk.rs | 586 +++++++++++++++++++++ crates/hir_ty/src/traits/chalk/interner.rs | 383 ++++++++++++++ crates/hir_ty/src/traits/chalk/mapping.rs | 787 +++++++++++++++++++++++++++++ crates/hir_ty/src/traits/chalk/tls.rs | 358 +++++++++++++ 4 files changed, 2114 insertions(+) create mode 100644 crates/hir_ty/src/traits/chalk.rs create mode 100644 crates/hir_ty/src/traits/chalk/interner.rs create mode 100644 crates/hir_ty/src/traits/chalk/mapping.rs create mode 100644 crates/hir_ty/src/traits/chalk/tls.rs (limited to 'crates/hir_ty/src/traits') diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs new file mode 100644 index 000000000..b33653417 --- /dev/null +++ b/crates/hir_ty/src/traits/chalk.rs @@ -0,0 +1,586 @@ +//! Conversion code from/to Chalk. +use std::sync::Arc; + +use log::debug; + +use chalk_ir::{fold::shift::Shift, CanonicalVarKinds, GenericArg, TypeName}; +use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; + +use base_db::{salsa::InternKey, CrateId}; +use hir_def::{ + lang_item::{lang_attr, LangItemTarget}, + AssocContainerId, AssocItemId, HasModule, Lookup, TypeAliasId, +}; + +use super::ChalkContext; +use crate::{ + db::HirDatabase, + display::HirDisplay, + method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, + utils::generics, + CallableDefId, DebruijnIndex, FnSig, GenericPredicate, Substs, Ty, TypeCtor, +}; +use mapping::{ + convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue, +}; + +pub use self::interner::*; + +pub(super) mod tls; +mod interner; +mod mapping; + +pub(super) trait ToChalk { + type Chalk; + fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; + fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; +} + +pub(super) fn from_chalk(db: &dyn HirDatabase, chalk: ChalkT) -> T +where + T: ToChalk, +{ + T::from_chalk(db, chalk) +} + +impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { + fn associated_ty_data(&self, id: AssocTypeId) -> Arc { + self.db.associated_ty_data(id) + } + fn trait_datum(&self, trait_id: TraitId) -> Arc { + self.db.trait_datum(self.krate, trait_id) + } + fn adt_datum(&self, struct_id: AdtId) -> Arc { + self.db.struct_datum(self.krate, struct_id) + } + fn adt_repr(&self, _struct_id: AdtId) -> rust_ir::AdtRepr { + rust_ir::AdtRepr { repr_c: false, repr_packed: false } + } + fn impl_datum(&self, impl_id: ImplId) -> Arc { + self.db.impl_datum(self.krate, impl_id) + } + + fn fn_def_datum( + &self, + fn_def_id: chalk_ir::FnDefId, + ) -> Arc> { + self.db.fn_def_datum(self.krate, fn_def_id) + } + + fn impls_for_trait( + &self, + trait_id: TraitId, + parameters: &[GenericArg], + binders: &CanonicalVarKinds, + ) -> Vec { + debug!("impls_for_trait {:?}", trait_id); + let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); + + let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone()); + + fn binder_kind(ty: &Ty, binders: &CanonicalVarKinds) -> Option { + if let Ty::Bound(bv) = ty { + let binders = binders.as_slice(&Interner); + if bv.debruijn == DebruijnIndex::INNERMOST { + if let chalk_ir::VariableKind::Ty(tk) = binders[bv.index].kind { + return Some(tk); + } + } + } + None + } + + let self_ty_fp = TyFingerprint::for_impl(&ty); + let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { + Some(chalk_ir::TyKind::Integer) => &ALL_INT_FPS, + Some(chalk_ir::TyKind::Float) => &ALL_FLOAT_FPS, + _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), + }; + + // Note: Since we're using impls_for_trait, only impls where the trait + // can be resolved should ever reach Chalk. `impl_datum` relies on that + // and will panic if the trait can't be resolved. + let in_deps = self.db.trait_impls_in_deps(self.krate); + let in_self = self.db.trait_impls_in_crate(self.krate); + let impl_maps = [in_deps, in_self]; + + let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); + + let result: Vec<_> = if fps.is_empty() { + debug!("Unrestricted search for {:?} impls...", trait_); + impl_maps + .iter() + .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk)) + .collect() + } else { + impl_maps + .iter() + .flat_map(|crate_impl_defs| { + fps.iter().flat_map(move |fp| { + crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) + }) + }) + .collect() + }; + + debug!("impls_for_trait returned {} impls", result.len()); + result + } + fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: AdtId) -> bool { + debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); + false // FIXME + } + fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc { + self.db.associated_ty_value(self.krate, id) + } + + fn custom_clauses(&self) -> Vec> { + vec![] + } + fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec { + // We don't do coherence checking (yet) + unimplemented!() + } + fn interner(&self) -> &Interner { + &Interner + } + fn well_known_trait_id( + &self, + well_known_trait: rust_ir::WellKnownTrait, + ) -> Option> { + let lang_attr = lang_attr_from_well_known_trait(well_known_trait); + let trait_ = match self.db.lang_item(self.krate, lang_attr.into()) { + Some(LangItemTarget::TraitId(trait_)) => trait_, + _ => return None, + }; + Some(trait_.to_chalk(self.db)) + } + + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment, + ) -> chalk_ir::ProgramClauses { + self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) + } + + fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { + let interned_id = crate::db::InternedOpaqueTyId::from(id); + let full_id = self.db.lookup_intern_impl_trait_id(interned_id); + let (func, idx) = match full_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx), + }; + let datas = + self.db.return_type_impl_traits(func).expect("impl trait id without impl traits"); + let data = &datas.value.impl_traits[idx as usize]; + let bound = OpaqueTyDatumBound { + bounds: make_binders( + data.bounds + .value + .iter() + .cloned() + .filter(|b| !b.is_error()) + .map(|b| b.to_chalk(self.db)) + .collect(), + 1, + ), + where_clauses: make_binders(vec![], 0), + }; + let num_vars = datas.num_binders; + Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) }) + } + + fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { + // FIXME: actually provide the hidden type; it is relevant for auto traits + Ty::Unknown.to_chalk(self.db) + } + + fn is_object_safe(&self, _trait_id: chalk_ir::TraitId) -> bool { + // FIXME: implement actual object safety + true + } + + fn closure_kind( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> rust_ir::ClosureKind { + // Fn is the closure kind that implements all three traits + rust_ir::ClosureKind::Fn + } + fn closure_inputs_and_output( + &self, + _closure_id: chalk_ir::ClosureId, + substs: &chalk_ir::Substitution, + ) -> chalk_ir::Binders> { + let sig_ty: Ty = + from_chalk(self.db, substs.at(&Interner, 0).assert_ty_ref(&Interner).clone()); + let sig = FnSig::from_fn_ptr_substs( + &sig_ty.substs().expect("first closure param should be fn ptr"), + false, + ); + let io = rust_ir::FnDefInputsAndOutputDatum { + argument_types: sig.params().iter().map(|ty| ty.clone().to_chalk(self.db)).collect(), + return_type: sig.ret().clone().to_chalk(self.db), + }; + make_binders(io.shifted_in(&Interner), 0) + } + fn closure_upvars( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> chalk_ir::Binders> { + let ty = Ty::unit().to_chalk(self.db); + make_binders(ty, 0) + } + fn closure_fn_substitution( + &self, + _closure_id: chalk_ir::ClosureId, + _substs: &chalk_ir::Substitution, + ) -> chalk_ir::Substitution { + Substs::empty().to_chalk(self.db) + } + + fn trait_name(&self, _trait_id: chalk_ir::TraitId) -> String { + unimplemented!() + } + fn adt_name(&self, _struct_id: chalk_ir::AdtId) -> String { + unimplemented!() + } + fn assoc_type_name(&self, _assoc_ty_id: chalk_ir::AssocTypeId) -> String { + unimplemented!() + } + fn opaque_type_name(&self, _opaque_ty_id: chalk_ir::OpaqueTyId) -> String { + unimplemented!() + } + fn fn_def_name(&self, _fn_def_id: chalk_ir::FnDefId) -> String { + unimplemented!() + } +} + +pub(crate) fn program_clauses_for_chalk_env_query( + db: &dyn HirDatabase, + krate: CrateId, + environment: chalk_ir::Environment, +) -> chalk_ir::ProgramClauses { + chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment) +} + +pub(crate) fn associated_ty_data_query( + db: &dyn HirDatabase, + id: AssocTypeId, +) -> Arc { + debug!("associated_ty_data {:?}", id); + let type_alias: TypeAliasId = from_chalk(db, id); + let trait_ = match type_alias.lookup(db.upcast()).container { + AssocContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + // Lower bounds -- we could/should maybe move this to a separate query in `lower` + let type_alias_data = db.type_alias_data(type_alias); + let generic_params = generics(db.upcast(), type_alias.into()); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); + let ctx = crate::TyLoweringContext::new(db, &resolver) + .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); + let self_ty = Ty::Bound(crate::BoundVar::new(crate::DebruijnIndex::INNERMOST, 0)); + let bounds = type_alias_data + .bounds + .iter() + .flat_map(|bound| GenericPredicate::from_type_bound(&ctx, bound, self_ty.clone())) + .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) + .map(|bound| make_binders(bound.shifted_in(&Interner), 0)) + .collect(); + + let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); + let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; + let datum = AssociatedTyDatum { + trait_id: trait_.to_chalk(db), + id, + name: type_alias, + binders: make_binders(bound_data, generic_params.len()), + }; + Arc::new(datum) +} + +pub(crate) fn trait_datum_query( + db: &dyn HirDatabase, + krate: CrateId, + trait_id: TraitId, +) -> Arc { + debug!("trait_datum {:?}", trait_id); + let trait_: hir_def::TraitId = from_chalk(db, trait_id); + let trait_data = db.trait_data(trait_); + debug!("trait {:?} = {:?}", trait_id, trait_data.name); + let generic_params = generics(db.upcast(), trait_.into()); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); + let flags = rust_ir::TraitFlags { + auto: trait_data.auto, + upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, + non_enumerable: true, + coinductive: false, // only relevant for Chalk testing + // FIXME: set these flags correctly + marker: false, + fundamental: false, + }; + let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); + let associated_ty_ids = + trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); + let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; + let well_known = + lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); + let trait_datum = TraitDatum { + id: trait_id, + binders: make_binders(trait_datum_bound, bound_vars.len()), + flags, + associated_ty_ids, + well_known, + }; + Arc::new(trait_datum) +} + +fn well_known_trait_from_lang_attr(name: &str) -> Option { + Some(match name { + "sized" => WellKnownTrait::Sized, + "copy" => WellKnownTrait::Copy, + "clone" => WellKnownTrait::Clone, + "drop" => WellKnownTrait::Drop, + "fn_once" => WellKnownTrait::FnOnce, + "fn_mut" => WellKnownTrait::FnMut, + "fn" => WellKnownTrait::Fn, + "unsize" => WellKnownTrait::Unsize, + _ => return None, + }) +} + +fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str { + match attr { + 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", + } +} + +pub(crate) fn struct_datum_query( + db: &dyn HirDatabase, + krate: CrateId, + struct_id: AdtId, +) -> Arc { + debug!("struct_datum {:?}", struct_id); + let type_ctor: TypeCtor = from_chalk(db, TypeName::Adt(struct_id)); + debug!("struct {:?} = {:?}", struct_id, type_ctor); + let num_params = type_ctor.num_ty_params(db); + let upstream = type_ctor.krate(db) != Some(krate); + let where_clauses = type_ctor + .as_generic_def() + .map(|generic_def| { + let generic_params = generics(db.upcast(), generic_def); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); + convert_where_clauses(db, generic_def, &bound_vars) + }) + .unwrap_or_else(Vec::new); + let flags = rust_ir::AdtFlags { + upstream, + // FIXME set fundamental and phantom_data flags correctly + fundamental: false, + phantom_data: false, + }; + // FIXME provide enum variants properly (for auto traits) + let variant = rust_ir::AdtVariantDatum { + fields: Vec::new(), // FIXME add fields (only relevant for auto traits), + }; + let struct_datum_bound = rust_ir::AdtDatumBound { variants: vec![variant], where_clauses }; + let struct_datum = StructDatum { + // FIXME set ADT kind + kind: rust_ir::AdtKind::Struct, + id: struct_id, + binders: make_binders(struct_datum_bound, num_params), + flags, + }; + Arc::new(struct_datum) +} + +pub(crate) fn impl_datum_query( + db: &dyn HirDatabase, + krate: CrateId, + impl_id: ImplId, +) -> Arc { + let _p = profile::span("impl_datum"); + debug!("impl_datum {:?}", impl_id); + let impl_: hir_def::ImplId = from_chalk(db, impl_id); + impl_def_datum(db, krate, impl_id, impl_) +} + +fn impl_def_datum( + db: &dyn HirDatabase, + krate: CrateId, + chalk_id: ImplId, + impl_id: hir_def::ImplId, +) -> Arc { + let trait_ref = db + .impl_trait(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach Chalk + .expect("invalid impl passed to Chalk") + .value; + let impl_data = db.impl_data(impl_id); + + let generic_params = generics(db.upcast(), impl_id.into()); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); + let trait_ = trait_ref.trait_; + let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { + rust_ir::ImplType::Local + } else { + rust_ir::ImplType::External + }; + let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars); + let negative = impl_data.is_negative; + debug!( + "impl {:?}: {}{} where {:?}", + chalk_id, + if negative { "!" } else { "" }, + trait_ref.display(db), + where_clauses + ); + let trait_ref = trait_ref.to_chalk(db); + + let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; + + let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses }; + let trait_data = db.trait_data(trait_); + let associated_ty_value_ids = impl_data + .items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(type_alias) => Some(*type_alias), + _ => None, + }) + .filter(|&type_alias| { + // don't include associated types that don't exist in the trait + let name = &db.type_alias_data(type_alias).name; + trait_data.associated_type_by_name(name).is_some() + }) + .map(|type_alias| TypeAliasAsValue(type_alias).to_chalk(db)) + .collect(); + debug!("impl_datum: {:?}", impl_datum_bound); + let impl_datum = ImplDatum { + binders: make_binders(impl_datum_bound, bound_vars.len()), + impl_type, + polarity, + associated_ty_value_ids, + }; + Arc::new(impl_datum) +} + +pub(crate) fn associated_ty_value_query( + db: &dyn HirDatabase, + krate: CrateId, + id: AssociatedTyValueId, +) -> Arc { + let type_alias: TypeAliasAsValue = from_chalk(db, id); + type_alias_associated_ty_value(db, krate, type_alias.0) +} + +fn type_alias_associated_ty_value( + db: &dyn HirDatabase, + _krate: CrateId, + type_alias: TypeAliasId, +) -> Arc { + let type_alias_data = db.type_alias_data(type_alias); + let impl_id = match type_alias.lookup(db.upcast()).container { + AssocContainerId::ImplId(it) => it, + _ => panic!("assoc ty value should be in impl"), + }; + + let trait_ref = db.impl_trait(impl_id).expect("assoc ty value should not exist").value; // we don't return any assoc ty values if the impl'd trait can't be resolved + + let assoc_ty = db + .trait_data(trait_ref.trait_) + .associated_type_by_name(&type_alias_data.name) + .expect("assoc ty value should not exist"); // validated when building the impl data as well + let ty = db.ty(type_alias.into()); + let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; + let value = rust_ir::AssociatedTyValue { + impl_id: impl_id.to_chalk(db), + associated_ty_id: assoc_ty.to_chalk(db), + value: make_binders(value_bound, ty.num_binders), + }; + Arc::new(value) +} + +pub(crate) fn fn_def_datum_query( + db: &dyn HirDatabase, + _krate: CrateId, + fn_def_id: FnDefId, +) -> Arc { + let callable_def: CallableDefId = from_chalk(db, fn_def_id); + let generic_params = generics(db.upcast(), callable_def.into()); + let sig = db.callable_item_signature(callable_def); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); + let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars); + let bound = rust_ir::FnDefDatumBound { + // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway + inputs_and_output: make_binders( + rust_ir::FnDefInputsAndOutputDatum { + argument_types: sig + .value + .params() + .iter() + .map(|ty| ty.clone().to_chalk(db)) + .collect(), + return_type: sig.value.ret().clone().to_chalk(db), + } + .shifted_in(&Interner), + 0, + ), + where_clauses, + }; + let datum = FnDefDatum { + id: fn_def_id, + abi: (), + safety: chalk_ir::Safety::Safe, + variadic: sig.value.is_varargs, + binders: make_binders(bound, sig.num_binders), + }; + Arc::new(datum) +} + +impl From for crate::db::InternedCallableDefId { + fn from(fn_def_id: FnDefId) -> Self { + InternKey::from_intern_id(fn_def_id.0) + } +} + +impl From for FnDefId { + fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self { + chalk_ir::FnDefId(callable_def_id.as_intern_id()) + } +} + +impl From for crate::db::InternedOpaqueTyId { + fn from(id: OpaqueTyId) -> Self { + InternKey::from_intern_id(id.0) + } +} + +impl From for OpaqueTyId { + fn from(id: crate::db::InternedOpaqueTyId) -> Self { + chalk_ir::OpaqueTyId(id.as_intern_id()) + } +} + +impl From> for crate::db::ClosureId { + fn from(id: chalk_ir::ClosureId) -> Self { + Self::from_intern_id(id.0) + } +} + +impl From for chalk_ir::ClosureId { + fn from(id: crate::db::ClosureId) -> Self { + chalk_ir::ClosureId(id.as_intern_id()) + } +} diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs new file mode 100644 index 000000000..fc0f9c201 --- /dev/null +++ b/crates/hir_ty/src/traits/chalk/interner.rs @@ -0,0 +1,383 @@ +//! Implementation of the Chalk `Interner` trait, which allows customizing the +//! representation of the various objects Chalk deals with (types, goals etc.). + +use super::tls; +use base_db::salsa::InternId; +use chalk_ir::{GenericArg, Goal, GoalData}; +use hir_def::TypeAliasId; +use std::{fmt, sync::Arc}; + +#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct Interner; + +pub type AssocTypeId = chalk_ir::AssocTypeId; +pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; +pub type TraitId = chalk_ir::TraitId; +pub type TraitDatum = chalk_solve::rust_ir::TraitDatum; +pub type AdtId = chalk_ir::AdtId; +pub type StructDatum = chalk_solve::rust_ir::AdtDatum; +pub type ImplId = chalk_ir::ImplId; +pub type ImplDatum = chalk_solve::rust_ir::ImplDatum; +pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId; +pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue; +pub type FnDefId = chalk_ir::FnDefId; +pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; +pub type OpaqueTyId = chalk_ir::OpaqueTyId; +pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum; + +impl chalk_ir::interner::Interner for Interner { + type InternedType = Box>; // FIXME use Arc? + type InternedLifetime = chalk_ir::LifetimeData; + type InternedConst = Arc>; + type InternedConcreteConst = (); + type InternedGenericArg = chalk_ir::GenericArgData; + type InternedGoal = Arc>; + type InternedGoals = Vec>; + type InternedSubstitution = Vec>; + type InternedProgramClause = chalk_ir::ProgramClauseData; + type InternedProgramClauses = Arc<[chalk_ir::ProgramClause]>; + type InternedQuantifiedWhereClauses = Vec>; + type InternedVariableKinds = Vec>; + type InternedCanonicalVarKinds = Vec>; + type InternedConstraints = Vec>>; + type DefId = InternId; + type InternedAdtId = hir_def::AdtId; + type Identifier = TypeAliasId; + type FnAbi = (); + + fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt))) + } + + fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt))) + } + + fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt))) + } + + fn debug_alias( + alias: &chalk_ir::AliasTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt))) + } + + fn debug_projection_ty( + proj: &chalk_ir::ProjectionTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) + } + + fn debug_opaque_ty( + opaque_ty: &chalk_ir::OpaqueTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt))) + } + + fn debug_opaque_ty_id( + opaque_ty_id: chalk_ir::OpaqueTyId, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt))) + } + + fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) + } + + fn debug_lifetime( + lifetime: &chalk_ir::Lifetime, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt))) + } + + fn debug_generic_arg( + parameter: &GenericArg, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt))) + } + + fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt))) + } + + fn debug_goals( + goals: &chalk_ir::Goals, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt))) + } + + fn debug_program_clause_implication( + pci: &chalk_ir::ProgramClauseImplication, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt))) + } + + fn debug_application_ty( + application_ty: &chalk_ir::ApplicationTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt))) + } + + fn debug_substitution( + substitution: &chalk_ir::Substitution, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt))) + } + + fn debug_separator_trait_ref( + separator_trait_ref: &chalk_ir::SeparatorTraitRef, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| { + Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt)) + }) + } + + fn debug_fn_def_id( + fn_def_id: chalk_ir::FnDefId, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt))) + } + fn debug_const( + constant: &chalk_ir::Const, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt))) + } + fn debug_variable_kinds( + variable_kinds: &chalk_ir::VariableKinds, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt))) + } + fn debug_variable_kinds_with_angles( + variable_kinds: &chalk_ir::VariableKinds, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| { + Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt)) + }) + } + fn debug_canonical_var_kinds( + canonical_var_kinds: &chalk_ir::CanonicalVarKinds, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| { + Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt)) + }) + } + fn debug_program_clause( + clause: &chalk_ir::ProgramClause, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt))) + } + fn debug_program_clauses( + clauses: &chalk_ir::ProgramClauses, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt))) + } + fn debug_quantified_where_clauses( + clauses: &chalk_ir::QuantifiedWhereClauses, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt))) + } + + fn intern_ty(&self, ty: chalk_ir::TyData) -> Box> { + Box::new(ty) + } + + fn ty_data<'a>(&self, ty: &'a Box>) -> &'a chalk_ir::TyData { + ty + } + + fn intern_lifetime( + &self, + lifetime: chalk_ir::LifetimeData, + ) -> chalk_ir::LifetimeData { + lifetime + } + + fn lifetime_data<'a>( + &self, + lifetime: &'a chalk_ir::LifetimeData, + ) -> &'a chalk_ir::LifetimeData { + lifetime + } + + fn intern_const(&self, constant: chalk_ir::ConstData) -> Arc> { + Arc::new(constant) + } + + fn const_data<'a>( + &self, + constant: &'a Arc>, + ) -> &'a chalk_ir::ConstData { + constant + } + + fn const_eq(&self, _ty: &Box>, _c1: &(), _c2: &()) -> bool { + true + } + + fn intern_generic_arg( + &self, + parameter: chalk_ir::GenericArgData, + ) -> chalk_ir::GenericArgData { + parameter + } + + fn generic_arg_data<'a>( + &self, + parameter: &'a chalk_ir::GenericArgData, + ) -> &'a chalk_ir::GenericArgData { + parameter + } + + fn intern_goal(&self, goal: GoalData) -> Arc> { + Arc::new(goal) + } + + fn intern_goals( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect() + } + + fn goal_data<'a>(&self, goal: &'a Arc>) -> &'a GoalData { + goal + } + + fn goals_data<'a>(&self, goals: &'a Vec>) -> &'a [Goal] { + goals + } + + fn intern_substitution( + &self, + data: impl IntoIterator, E>>, + ) -> Result>, E> { + data.into_iter().collect() + } + + fn substitution_data<'a>( + &self, + substitution: &'a Vec>, + ) -> &'a [GenericArg] { + substitution + } + + fn intern_program_clause( + &self, + data: chalk_ir::ProgramClauseData, + ) -> chalk_ir::ProgramClauseData { + data + } + + fn program_clause_data<'a>( + &self, + clause: &'a chalk_ir::ProgramClauseData, + ) -> &'a chalk_ir::ProgramClauseData { + clause + } + + fn intern_program_clauses( + &self, + data: impl IntoIterator, E>>, + ) -> Result]>, E> { + data.into_iter().collect() + } + + fn program_clauses_data<'a>( + &self, + clauses: &'a Arc<[chalk_ir::ProgramClause]>, + ) -> &'a [chalk_ir::ProgramClause] { + &clauses + } + + fn intern_quantified_where_clauses( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect() + } + + fn quantified_where_clauses_data<'a>( + &self, + clauses: &'a Self::InternedQuantifiedWhereClauses, + ) -> &'a [chalk_ir::QuantifiedWhereClause] { + clauses + } + + fn intern_generic_arg_kinds( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect() + } + + fn variable_kinds_data<'a>( + &self, + parameter_kinds: &'a Self::InternedVariableKinds, + ) -> &'a [chalk_ir::VariableKind] { + ¶meter_kinds + } + + fn intern_canonical_var_kinds( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect() + } + + fn canonical_var_kinds_data<'a>( + &self, + canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, + ) -> &'a [chalk_ir::CanonicalVarKind] { + &canonical_var_kinds + } + + fn intern_constraints( + &self, + data: impl IntoIterator>, E>>, + ) -> Result { + data.into_iter().collect() + } + + fn constraints_data<'a>( + &self, + constraints: &'a Self::InternedConstraints, + ) -> &'a [chalk_ir::InEnvironment>] { + constraints + } + fn debug_closure_id( + _fn_def_id: chalk_ir::ClosureId, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + fn debug_constraints( + _clauses: &chalk_ir::Constraints, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } +} + +impl chalk_ir::interner::HasInterner for Interner { + type Interner = Self; +} diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs new file mode 100644 index 000000000..fe62f3fa7 --- /dev/null +++ b/crates/hir_ty/src/traits/chalk/mapping.rs @@ -0,0 +1,787 @@ +//! This module contains the implementations of the `ToChalk` trait, which +//! handles conversion between our data types and their corresponding types in +//! Chalk (in both directions); plus some helper functions for more specialized +//! conversions. + +use chalk_ir::{ + cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName, + UniverseIndex, +}; +use chalk_solve::rust_ir; + +use base_db::salsa::InternKey; +use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId}; + +use crate::{ + db::HirDatabase, + primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness}, + traits::{Canonical, Obligation}, + ApplicationTy, CallableDefId, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, + ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor, +}; + +use super::interner::*; +use super::*; + +impl ToChalk for Ty { + type Chalk = chalk_ir::Ty; + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty { + match self { + Ty::Apply(apply_ty) => match apply_ty.ctor { + TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), + TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), + TypeCtor::FnPtr { num_args: _, is_varargs } => { + let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); + chalk_ir::TyData::Function(chalk_ir::FnPointer { + num_binders: 0, + abi: (), + safety: chalk_ir::Safety::Safe, + variadic: is_varargs, + substitution, + }) + .intern(&Interner) + } + _ => { + let name = apply_ty.ctor.to_chalk(db); + let substitution = apply_ty.parameters.to_chalk(db); + chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) + } + }, + Ty::Projection(proj_ty) => { + let associated_ty_id = proj_ty.associated_ty.to_chalk(db); + let substitution = proj_ty.parameters.to_chalk(db); + chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }) + .cast(&Interner) + .intern(&Interner) + } + Ty::Placeholder(id) => { + let interned_id = db.intern_type_param_id(id); + PlaceholderIndex { + ui: UniverseIndex::ROOT, + idx: interned_id.as_intern_id().as_usize(), + } + .to_ty::(&Interner) + } + Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner), + Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), + Ty::Dyn(predicates) => { + let where_clauses = chalk_ir::QuantifiedWhereClauses::from_iter( + &Interner, + predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)), + ); + let bounded_ty = chalk_ir::DynTy { + bounds: make_binders(where_clauses, 1), + lifetime: FAKE_PLACEHOLDER.to_lifetime(&Interner), + }; + chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) + } + Ty::Opaque(opaque_ty) => { + let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db); + let substitution = opaque_ty.parameters.to_chalk(db); + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id, + substitution, + })) + .intern(&Interner) + } + Ty::Unknown => { + let substitution = chalk_ir::Substitution::empty(&Interner); + let name = TypeName::Error; + chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) + } + } + } + fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty) -> Self { + match chalk.data(&Interner).clone() { + chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name { + TypeName::Error => Ty::Unknown, + TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution), + TypeName::Array => array_from_chalk(db, apply_ty.substitution), + _ => { + let ctor = from_chalk(db, apply_ty.name); + let parameters = from_chalk(db, apply_ty.substitution); + Ty::Apply(ApplicationTy { ctor, parameters }) + } + }, + chalk_ir::TyData::Placeholder(idx) => { + assert_eq!(idx.ui, UniverseIndex::ROOT); + let interned_id = crate::db::GlobalTypeParamId::from_intern_id( + crate::salsa::InternId::from(idx.idx), + ); + Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) + } + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => { + let associated_ty = from_chalk(db, proj.associated_ty_id); + let parameters = from_chalk(db, proj.substitution); + Ty::Projection(ProjectionTy { associated_ty, parameters }) + } + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => { + let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id); + let parameters = from_chalk(db, opaque_ty.substitution); + Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) + } + chalk_ir::TyData::Function(chalk_ir::FnPointer { + num_binders, + variadic, + substitution, + .. + }) => { + assert_eq!(num_binders, 0); + let parameters: Substs = from_chalk( + db, + substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), + ); + Ty::Apply(ApplicationTy { + ctor: TypeCtor::FnPtr { + num_args: (parameters.len() - 1) as u16, + is_varargs: variadic, + }, + parameters, + }) + } + chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), + chalk_ir::TyData::InferenceVar(_iv, _kind) => Ty::Unknown, + chalk_ir::TyData::Dyn(where_clauses) => { + assert_eq!(where_clauses.bounds.binders.len(&Interner), 1); + let predicates = where_clauses + .bounds + .skip_binders() + .iter(&Interner) + .map(|c| from_chalk(db, c.clone())) + .collect(); + Ty::Dyn(predicates) + } + } + } +} + +const FAKE_PLACEHOLDER: PlaceholderIndex = + PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX }; + +/// We currently don't model lifetimes, but Chalk does. So, we have to insert a +/// fake lifetime here, because Chalks built-in logic may expect it to be there. +fn ref_to_chalk( + db: &dyn HirDatabase, + mutability: Mutability, + subst: Substs, +) -> chalk_ir::Ty { + let arg = subst[0].clone().to_chalk(db); + let lifetime = FAKE_PLACEHOLDER.to_lifetime(&Interner); + chalk_ir::ApplicationTy { + name: TypeName::Ref(mutability.to_chalk(db)), + substitution: chalk_ir::Substitution::from_iter( + &Interner, + vec![lifetime.cast(&Interner), arg.cast(&Interner)], + ), + } + .intern(&Interner) +} + +/// Here we remove the lifetime from the type we got from Chalk. +fn ref_from_chalk( + db: &dyn HirDatabase, + mutability: chalk_ir::Mutability, + subst: chalk_ir::Substitution, +) -> Ty { + let tys = subst + .iter(&Interner) + .filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone()))) + .collect(); + Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys)) +} + +/// We currently don't model constants, but Chalk does. So, we have to insert a +/// fake constant here, because Chalks built-in logic may expect it to be there. +fn array_to_chalk(db: &dyn HirDatabase, subst: Substs) -> chalk_ir::Ty { + let arg = subst[0].clone().to_chalk(db); + let usize_ty = chalk_ir::ApplicationTy { + name: TypeName::Scalar(Scalar::Uint(chalk_ir::UintTy::Usize)), + substitution: chalk_ir::Substitution::empty(&Interner), + } + .intern(&Interner); + let const_ = FAKE_PLACEHOLDER.to_const(&Interner, usize_ty); + chalk_ir::ApplicationTy { + name: TypeName::Array, + substitution: chalk_ir::Substitution::from_iter( + &Interner, + vec![arg.cast(&Interner), const_.cast(&Interner)], + ), + } + .intern(&Interner) +} + +/// Here we remove the const from the type we got from Chalk. +fn array_from_chalk(db: &dyn HirDatabase, subst: chalk_ir::Substitution) -> Ty { + let tys = subst + .iter(&Interner) + .filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone()))) + .collect(); + Ty::apply(TypeCtor::Array, Substs(tys)) +} + +impl ToChalk for Substs { + type Chalk = chalk_ir::Substitution; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution { + chalk_ir::Substitution::from_iter(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db))) + } + + fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution) -> Substs { + let tys = parameters + .iter(&Interner) + .map(|p| match p.ty(&Interner) { + Some(ty) => from_chalk(db, ty.clone()), + None => unimplemented!(), + }) + .collect(); + Substs(tys) + } +} + +impl ToChalk for TraitRef { + type Chalk = chalk_ir::TraitRef; + + fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef { + let trait_id = self.trait_.to_chalk(db); + let substitution = self.substs.to_chalk(db); + chalk_ir::TraitRef { trait_id, substitution } + } + + fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef) -> Self { + let trait_ = from_chalk(db, trait_ref.trait_id); + let substs = from_chalk(db, trait_ref.substitution); + TraitRef { trait_, substs } + } +} + +impl ToChalk for hir_def::TraitId { + type Chalk = TraitId; + + fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId { + chalk_ir::TraitId(self.as_intern_id()) + } + + fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId { + InternKey::from_intern_id(trait_id.0) + } +} + +impl ToChalk for OpaqueTyId { + type Chalk = chalk_ir::OpaqueTyId; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId { + db.intern_impl_trait_id(self).into() + } + + fn from_chalk( + db: &dyn HirDatabase, + opaque_ty_id: chalk_ir::OpaqueTyId, + ) -> OpaqueTyId { + db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + } +} + +impl ToChalk for TypeCtor { + type Chalk = TypeName; + + fn to_chalk(self, db: &dyn HirDatabase) -> TypeName { + match self { + TypeCtor::AssociatedType(type_alias) => { + let type_id = type_alias.to_chalk(db); + TypeName::AssociatedType(type_id) + } + + TypeCtor::OpaqueType(impl_trait_id) => { + let id = impl_trait_id.to_chalk(db); + TypeName::OpaqueType(id) + } + + TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), + TypeCtor::Char => TypeName::Scalar(Scalar::Char), + TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)), + TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) => { + TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) + } + TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) => { + TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) + } + + TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()), + TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)), + TypeCtor::Slice => TypeName::Slice, + TypeCtor::Array => TypeName::Array, + TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)), + TypeCtor::Str => TypeName::Str, + TypeCtor::FnDef(callable_def) => { + let id = callable_def.to_chalk(db); + TypeName::FnDef(id) + } + TypeCtor::Never => TypeName::Never, + + TypeCtor::Closure { def, expr } => { + let closure_id = db.intern_closure((def, expr)); + TypeName::Closure(closure_id.into()) + } + + TypeCtor::Adt(adt_id) => TypeName::Adt(chalk_ir::AdtId(adt_id)), + + TypeCtor::FnPtr { .. } => { + // This should not be reached, since Chalk doesn't represent + // function pointers with TypeName + unreachable!() + } + } + } + + fn from_chalk(db: &dyn HirDatabase, type_name: TypeName) -> TypeCtor { + match type_name { + TypeName::Adt(struct_id) => TypeCtor::Adt(struct_id.0), + TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), + TypeName::OpaqueType(opaque_type_id) => { + TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) + } + + TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, + TypeName::Scalar(Scalar::Char) => TypeCtor::Char, + TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(IntTy { + signedness: Signedness::Signed, + bitness: bitness_from_chalk_int(int_ty), + }), + TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(IntTy { + signedness: Signedness::Unsigned, + bitness: bitness_from_chalk_uint(uint_ty), + }), + TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => { + TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) + } + TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => { + TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) + } + TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 }, + TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)), + TypeName::Slice => TypeCtor::Slice, + TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)), + TypeName::Str => TypeCtor::Str, + TypeName::Never => TypeCtor::Never, + + TypeName::FnDef(fn_def_id) => { + let callable_def = from_chalk(db, fn_def_id); + TypeCtor::FnDef(callable_def) + } + TypeName::Array => TypeCtor::Array, + + TypeName::Closure(id) => { + let id: crate::db::ClosureId = id.into(); + let (def, expr) = db.lookup_intern_closure(id); + TypeCtor::Closure { def, expr } + } + + TypeName::Error => { + // this should not be reached, since we don't represent TypeName::Error with TypeCtor + unreachable!() + } + } + } +} + +fn bitness_from_chalk_uint(uint_ty: chalk_ir::UintTy) -> IntBitness { + use chalk_ir::UintTy; + + match uint_ty { + UintTy::Usize => IntBitness::Xsize, + UintTy::U8 => IntBitness::X8, + UintTy::U16 => IntBitness::X16, + UintTy::U32 => IntBitness::X32, + UintTy::U64 => IntBitness::X64, + UintTy::U128 => IntBitness::X128, + } +} + +fn bitness_from_chalk_int(int_ty: chalk_ir::IntTy) -> IntBitness { + use chalk_ir::IntTy; + + match int_ty { + IntTy::Isize => IntBitness::Xsize, + IntTy::I8 => IntBitness::X8, + IntTy::I16 => IntBitness::X16, + IntTy::I32 => IntBitness::X32, + IntTy::I64 => IntBitness::X64, + IntTy::I128 => IntBitness::X128, + } +} + +fn int_ty_to_chalk(int_ty: IntTy) -> Scalar { + use chalk_ir::{IntTy, UintTy}; + + match int_ty.signedness { + Signedness::Signed => Scalar::Int(match int_ty.bitness { + IntBitness::Xsize => IntTy::Isize, + IntBitness::X8 => IntTy::I8, + IntBitness::X16 => IntTy::I16, + IntBitness::X32 => IntTy::I32, + IntBitness::X64 => IntTy::I64, + IntBitness::X128 => IntTy::I128, + }), + Signedness::Unsigned => Scalar::Uint(match int_ty.bitness { + IntBitness::Xsize => UintTy::Usize, + IntBitness::X8 => UintTy::U8, + IntBitness::X16 => UintTy::U16, + IntBitness::X32 => UintTy::U32, + IntBitness::X64 => UintTy::U64, + IntBitness::X128 => UintTy::U128, + }), + } +} + +impl ToChalk for Mutability { + type Chalk = chalk_ir::Mutability; + fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk { + match self { + Mutability::Shared => chalk_ir::Mutability::Not, + Mutability::Mut => chalk_ir::Mutability::Mut, + } + } + fn from_chalk(_db: &dyn HirDatabase, chalk: Self::Chalk) -> Self { + match chalk { + chalk_ir::Mutability::Mut => Mutability::Mut, + chalk_ir::Mutability::Not => Mutability::Shared, + } + } +} + +impl ToChalk for hir_def::ImplId { + type Chalk = ImplId; + + fn to_chalk(self, _db: &dyn HirDatabase) -> ImplId { + chalk_ir::ImplId(self.as_intern_id()) + } + + fn from_chalk(_db: &dyn HirDatabase, impl_id: ImplId) -> hir_def::ImplId { + InternKey::from_intern_id(impl_id.0) + } +} + +impl ToChalk for CallableDefId { + type Chalk = FnDefId; + + fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId { + db.intern_callable_def(self).into() + } + + fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId { + db.lookup_intern_callable_def(fn_def_id.into()) + } +} + +impl ToChalk for TypeAliasId { + type Chalk = AssocTypeId; + + fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId { + chalk_ir::AssocTypeId(self.as_intern_id()) + } + + fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId { + InternKey::from_intern_id(type_alias_id.0) + } +} + +pub struct TypeAliasAsValue(pub TypeAliasId); + +impl ToChalk for TypeAliasAsValue { + type Chalk = AssociatedTyValueId; + + fn to_chalk(self, _db: &dyn HirDatabase) -> AssociatedTyValueId { + rust_ir::AssociatedTyValueId(self.0.as_intern_id()) + } + + fn from_chalk( + _db: &dyn HirDatabase, + assoc_ty_value_id: AssociatedTyValueId, + ) -> TypeAliasAsValue { + TypeAliasAsValue(TypeAliasId::from_intern_id(assoc_ty_value_id.0)) + } +} + +impl ToChalk for GenericPredicate { + type Chalk = chalk_ir::QuantifiedWhereClause; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause { + match self { + GenericPredicate::Implemented(trait_ref) => { + let chalk_trait_ref = trait_ref.to_chalk(db); + let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner); + make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0) + } + GenericPredicate::Projection(projection_pred) => { + let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner); + let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner); + let alias = chalk_ir::AliasTy::Projection(projection); + make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0) + } + GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), + } + } + + fn from_chalk( + db: &dyn HirDatabase, + where_clause: chalk_ir::QuantifiedWhereClause, + ) -> GenericPredicate { + // we don't produce any where clauses with binders and can't currently deal with them + match where_clause + .skip_binders() + .shifted_out(&Interner) + .expect("unexpected bound vars in where clause") + { + chalk_ir::WhereClause::Implemented(tr) => { + GenericPredicate::Implemented(from_chalk(db, tr)) + } + chalk_ir::WhereClause::AliasEq(projection_eq) => { + let projection_ty = from_chalk( + db, + match projection_eq.alias { + chalk_ir::AliasTy::Projection(p) => p, + _ => unimplemented!(), + }, + ); + let ty = from_chalk(db, projection_eq.ty); + GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty }) + } + + chalk_ir::WhereClause::LifetimeOutlives(_) => { + // we shouldn't get these from Chalk + panic!("encountered LifetimeOutlives from Chalk") + } + + chalk_ir::WhereClause::TypeOutlives(_) => { + // we shouldn't get these from Chalk + panic!("encountered TypeOutlives from Chalk") + } + } + } +} + +impl ToChalk for ProjectionTy { + type Chalk = chalk_ir::ProjectionTy; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy { + chalk_ir::ProjectionTy { + associated_ty_id: self.associated_ty.to_chalk(db), + substitution: self.parameters.to_chalk(db), + } + } + + fn from_chalk( + db: &dyn HirDatabase, + projection_ty: chalk_ir::ProjectionTy, + ) -> ProjectionTy { + ProjectionTy { + associated_ty: from_chalk(db, projection_ty.associated_ty_id), + parameters: from_chalk(db, projection_ty.substitution), + } + } +} + +impl ToChalk for ProjectionPredicate { + type Chalk = chalk_ir::AliasEq; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq { + chalk_ir::AliasEq { + alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)), + ty: self.ty.to_chalk(db), + } + } + + fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq) -> Self { + unimplemented!() + } +} + +impl ToChalk for Obligation { + type Chalk = chalk_ir::DomainGoal; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal { + match self { + Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner), + Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner), + } + } + + fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal) -> Self { + unimplemented!() + } +} + +impl ToChalk for Canonical +where + T: ToChalk, + T::Chalk: HasInterner, +{ + type Chalk = chalk_ir::Canonical; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical { + let kinds = self + .kinds + .iter() + .map(|k| match k { + TyKind::General => chalk_ir::TyKind::General, + TyKind::Integer => chalk_ir::TyKind::Integer, + TyKind::Float => chalk_ir::TyKind::Float, + }) + .map(|tk| { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(tk), + chalk_ir::UniverseIndex::ROOT, + ) + }); + let value = self.value.to_chalk(db); + chalk_ir::Canonical { + value, + binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds), + } + } + + fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical) -> Canonical { + let kinds = canonical + .binders + .iter(&Interner) + .map(|k| match k.kind { + chalk_ir::VariableKind::Ty(tk) => match tk { + chalk_ir::TyKind::General => TyKind::General, + chalk_ir::TyKind::Integer => TyKind::Integer, + chalk_ir::TyKind::Float => TyKind::Float, + }, + chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"), + chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"), + }) + .collect(); + Canonical { kinds, value: from_chalk(db, canonical.value) } + } +} + +impl ToChalk for Arc { + type Chalk = chalk_ir::Environment; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment { + let mut clauses = Vec::new(); + for pred in &self.predicates { + if pred.is_error() { + // for env, we just ignore errors + continue; + } + let program_clause: chalk_ir::ProgramClause = + pred.clone().to_chalk(db).cast(&Interner); + clauses.push(program_clause.into_from_env_clause(&Interner)); + } + chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses) + } + + fn from_chalk( + _db: &dyn HirDatabase, + _env: chalk_ir::Environment, + ) -> Arc { + unimplemented!() + } +} + +impl ToChalk for InEnvironment +where + T::Chalk: chalk_ir::interner::HasInterner, +{ + type Chalk = chalk_ir::InEnvironment; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment { + chalk_ir::InEnvironment { + environment: self.environment.to_chalk(db), + goal: self.value.to_chalk(db), + } + } + + fn from_chalk( + db: &dyn HirDatabase, + in_env: chalk_ir::InEnvironment, + ) -> InEnvironment { + InEnvironment { + environment: from_chalk(db, in_env.environment), + value: from_chalk(db, in_env.goal), + } + } +} + +pub(super) fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders +where + T: HasInterner, +{ + chalk_ir::Binders::new( + chalk_ir::VariableKinds::from_iter( + &Interner, + std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)).take(num_vars), + ), + value, + ) +} + +pub(super) fn convert_where_clauses( + db: &dyn HirDatabase, + def: GenericDefId, + substs: &Substs, +) -> Vec> { + let generic_predicates = db.generic_predicates(def); + let mut result = Vec::with_capacity(generic_predicates.len()); + for pred in generic_predicates.iter() { + if pred.value.is_error() { + // skip errored predicates completely + continue; + } + result.push(pred.clone().subst(substs).to_chalk(db)); + } + result +} + +pub(super) fn generic_predicate_to_inline_bound( + db: &dyn HirDatabase, + pred: &GenericPredicate, + self_ty: &Ty, +) -> Option> { + // An InlineBound is like a GenericPredicate, except the self type is left out. + // We don't have a special type for this, but Chalk does. + match pred { + GenericPredicate::Implemented(trait_ref) => { + if &trait_ref.substs[0] != self_ty { + // we can only convert predicates back to type bounds if they + // have the expected self type + return None; + } + let args_no_self = trait_ref.substs[1..] + .iter() + .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) + .collect(); + let trait_bound = + rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self }; + Some(rust_ir::InlineBound::TraitBound(trait_bound)) + } + GenericPredicate::Projection(proj) => { + if &proj.projection_ty.parameters[0] != self_ty { + return None; + } + let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container { + AssocContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + let args_no_self = proj.projection_ty.parameters[1..] + .iter() + .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) + .collect(); + let alias_eq_bound = rust_ir::AliasEqBound { + value: proj.ty.clone().to_chalk(db), + trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self }, + associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db), + parameters: Vec::new(), // FIXME we don't support generic associated types yet + }; + Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound)) + } + GenericPredicate::Error => None, + } +} diff --git a/crates/hir_ty/src/traits/chalk/tls.rs b/crates/hir_ty/src/traits/chalk/tls.rs new file mode 100644 index 000000000..db915625c --- /dev/null +++ b/crates/hir_ty/src/traits/chalk/tls.rs @@ -0,0 +1,358 @@ +//! Implementation of Chalk debug helper functions using TLS. +use std::fmt; + +use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName}; +use itertools::Itertools; + +use super::{from_chalk, Interner}; +use crate::{db::HirDatabase, CallableDefId, TypeCtor}; +use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; + +pub use unsafe_tls::{set_current_program, with_current_program}; + +pub struct DebugContext<'a>(&'a dyn HirDatabase); + +impl DebugContext<'_> { + pub fn debug_struct_id( + &self, + id: super::AdtId, + f: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Adt(id)); + match type_ctor { + TypeCtor::Bool => write!(f, "bool")?, + TypeCtor::Char => write!(f, "char")?, + TypeCtor::Int(t) => write!(f, "{}", t)?, + TypeCtor::Float(t) => write!(f, "{}", t)?, + TypeCtor::Str => write!(f, "str")?, + TypeCtor::Slice => write!(f, "slice")?, + TypeCtor::Array => write!(f, "array")?, + TypeCtor::RawPtr(m) => write!(f, "*{}", m.as_keyword_for_ptr())?, + TypeCtor::Ref(m) => write!(f, "&{}", m.as_keyword_for_ref())?, + TypeCtor::Never => write!(f, "!")?, + TypeCtor::Tuple { .. } => { + write!(f, "()")?; + } + TypeCtor::FnPtr { .. } => { + write!(f, "fn")?; + } + TypeCtor::FnDef(def) => { + let name = match def { + CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(), + CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(), + CallableDefId::EnumVariantId(e) => { + let enum_data = self.0.enum_data(e.parent); + enum_data.variants[e.local_id].name.clone() + } + }; + match def { + CallableDefId::FunctionId(_) => write!(f, "{{fn {}}}", name)?, + CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { + write!(f, "{{ctor {}}}", name)? + } + } + } + TypeCtor::Adt(def_id) => { + let name = match def_id { + AdtId::StructId(it) => self.0.struct_data(it).name.clone(), + AdtId::UnionId(it) => self.0.union_data(it).name.clone(), + AdtId::EnumId(it) => self.0.enum_data(it).name.clone(), + }; + write!(f, "{}", name)?; + } + TypeCtor::AssociatedType(type_alias) => { + let trait_ = match type_alias.lookup(self.0.upcast()).container { + AssocContainerId::TraitId(it) => it, + _ => panic!("not an associated type"), + }; + let trait_name = self.0.trait_data(trait_).name.clone(); + let name = self.0.type_alias_data(type_alias).name.clone(); + write!(f, "{}::{}", trait_name, name)?; + } + TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + write!(f, "{{impl trait {} of {:?}}}", idx, func)?; + } + }, + TypeCtor::Closure { def, expr } => { + write!(f, "{{closure {:?} in ", expr.into_raw())?; + match def { + DefWithBodyId::FunctionId(func) => { + write!(f, "fn {}", self.0.function_data(func).name)? + } + DefWithBodyId::StaticId(s) => { + if let Some(name) = self.0.static_data(s).name.as_ref() { + write!(f, "body of static {}", name)?; + } else { + write!(f, "body of unnamed static {:?}", s)?; + } + } + DefWithBodyId::ConstId(c) => { + if let Some(name) = self.0.const_data(c).name.as_ref() { + write!(f, "body of const {}", name)?; + } else { + write!(f, "body of unnamed const {:?}", c)?; + } + } + }; + write!(f, "}}")?; + } + } + Ok(()) + } + + pub fn debug_trait_id( + &self, + id: super::TraitId, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + let trait_: hir_def::TraitId = from_chalk(self.0, id); + let trait_data = self.0.trait_data(trait_); + write!(fmt, "{}", trait_data.name) + } + + pub fn debug_assoc_type_id( + &self, + id: super::AssocTypeId, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + let type_alias: TypeAliasId = from_chalk(self.0, id); + let type_alias_data = self.0.type_alias_data(type_alias); + let trait_ = match type_alias.lookup(self.0.upcast()).container { + AssocContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + let trait_data = self.0.trait_data(trait_); + write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) + } + + pub fn debug_opaque_ty_id( + &self, + opaque_ty_id: chalk_ir::OpaqueTyId, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish() + } + + pub fn debug_alias( + &self, + alias_ty: &AliasTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + match alias_ty { + AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), + AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt), + } + } + + pub fn debug_projection_ty( + &self, + projection_ty: &chalk_ir::ProjectionTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id); + let type_alias_data = self.0.type_alias_data(type_alias); + let trait_ = match type_alias.lookup(self.0.upcast()).container { + AssocContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + let trait_data = self.0.trait_data(trait_); + let params = projection_ty.substitution.as_slice(&Interner); + write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?; + if params.len() > 1 { + write!( + fmt, + "<{}>", + ¶ms[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), + )?; + } + write!(fmt, ">::{}", type_alias_data.name) + } + + pub fn debug_opaque_ty( + &self, + opaque_ty: &chalk_ir::OpaqueTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", opaque_ty.opaque_ty_id) + } + + pub fn debug_ty( + &self, + ty: &chalk_ir::Ty, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", ty.data(&Interner)) + } + + pub fn debug_lifetime( + &self, + lifetime: &Lifetime, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", lifetime.data(&Interner)) + } + + pub fn debug_generic_arg( + &self, + parameter: &GenericArg, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) + } + + pub fn debug_goal( + &self, + goal: &Goal, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + let goal_data = goal.data(&Interner); + write!(fmt, "{:?}", goal_data) + } + + pub fn debug_goals( + &self, + goals: &Goals, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", goals.debug(&Interner)) + } + + pub fn debug_program_clause_implication( + &self, + pci: &ProgramClauseImplication, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", pci.debug(&Interner)) + } + + pub fn debug_application_ty( + &self, + application_ty: &chalk_ir::ApplicationTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", application_ty.debug(&Interner)) + } + + pub fn debug_substitution( + &self, + substitution: &chalk_ir::Substitution, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", substitution.debug(&Interner)) + } + + pub fn debug_separator_trait_ref( + &self, + separator_trait_ref: &chalk_ir::SeparatorTraitRef, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) + } + + pub fn debug_fn_def_id( + &self, + fn_def_id: chalk_ir::FnDefId, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + let def: CallableDefId = from_chalk(self.0, fn_def_id); + let name = match def { + CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(), + CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(), + CallableDefId::EnumVariantId(e) => { + let enum_data = self.0.enum_data(e.parent); + enum_data.variants[e.local_id].name.clone() + } + }; + match def { + CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name), + CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { + write!(fmt, "{{ctor {}}}", name) + } + } + } + + pub fn debug_const( + &self, + _constant: &chalk_ir::Const, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "const") + } + + pub fn debug_variable_kinds( + &self, + variable_kinds: &chalk_ir::VariableKinds, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "{:?}", variable_kinds.as_slice(&Interner)) + } + pub fn debug_variable_kinds_with_angles( + &self, + variable_kinds: &chalk_ir::VariableKinds, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner)) + } + pub fn debug_canonical_var_kinds( + &self, + canonical_var_kinds: &chalk_ir::CanonicalVarKinds, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner)) + } + pub fn debug_program_clause( + &self, + clause: &chalk_ir::ProgramClause, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "{:?}", clause.data(&Interner)) + } + pub fn debug_program_clauses( + &self, + clauses: &chalk_ir::ProgramClauses, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "{:?}", clauses.as_slice(&Interner)) + } + pub fn debug_quantified_where_clauses( + &self, + clauses: &chalk_ir::QuantifiedWhereClauses, + fmt: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(fmt, "{:?}", clauses.as_slice(&Interner)) + } +} + +mod unsafe_tls { + use super::DebugContext; + use crate::db::HirDatabase; + use scoped_tls::scoped_thread_local; + + scoped_thread_local!(static PROGRAM: DebugContext); + + pub fn with_current_program( + op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R, + ) -> R { + if PROGRAM.is_set() { + PROGRAM.with(|prog| op(Some(prog))) + } else { + op(None) + } + } + + pub fn set_current_program(p: &dyn HirDatabase, op: OP) -> R + where + OP: FnOnce() -> R, + { + let ctx = DebugContext(p); + // we're transmuting the lifetime in the DebugContext to static. This is + // fine because we only keep the reference for the lifetime of this + // function, *and* the only way to access the context is through + // `with_current_program`, which hides the lifetime through the `for` + // type. + let static_p: &DebugContext<'static> = + unsafe { std::mem::transmute::<&DebugContext, &DebugContext<'static>>(&ctx) }; + PROGRAM.set(static_p, || op()) + } +} -- cgit v1.2.3