mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Replace Projection variant in GenericPredicate with AliasEq
This commit is contained in:
parent
86878443b1
commit
8996b1a235
@ -56,9 +56,9 @@ use hir_ty::{
|
|||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
to_assoc_type_id,
|
to_assoc_type_id,
|
||||||
traits::{FnTrait, Solution, SolutionVariables},
|
traits::{FnTrait, Solution, SolutionVariables},
|
||||||
AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
|
AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex,
|
||||||
InEnvironment, Interner, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substitution,
|
GenericPredicate, InEnvironment, Interner, Obligation, ProjectionTy, Scalar, Substitution, Ty,
|
||||||
Ty, TyDefId, TyKind, TyVariableKind,
|
TyDefId, TyKind, TyVariableKind,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
@ -1786,17 +1786,17 @@ impl Type {
|
|||||||
.push(self.ty.value.clone())
|
.push(self.ty.value.clone())
|
||||||
.fill(args.iter().map(|t| t.ty.value.clone()))
|
.fill(args.iter().map(|t| t.ty.value.clone()))
|
||||||
.build();
|
.build();
|
||||||
let predicate = ProjectionPredicate {
|
|
||||||
projection_ty: ProjectionTy {
|
|
||||||
associated_ty_id: to_assoc_type_id(alias.id),
|
|
||||||
substitution: subst,
|
|
||||||
},
|
|
||||||
ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner),
|
|
||||||
};
|
|
||||||
let goal = Canonical {
|
let goal = Canonical {
|
||||||
value: InEnvironment::new(
|
value: InEnvironment::new(
|
||||||
self.ty.environment.clone(),
|
self.ty.environment.clone(),
|
||||||
Obligation::Projection(predicate),
|
Obligation::AliasEq(AliasEq {
|
||||||
|
alias: AliasTy::Projection(ProjectionTy {
|
||||||
|
associated_ty_id: to_assoc_type_id(alias.id),
|
||||||
|
substitution: subst,
|
||||||
|
}),
|
||||||
|
ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
|
||||||
|
.intern(&Interner),
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
kinds: Arc::new([TyVariableKind::General]),
|
kinds: Arc::new([TyVariableKind::General]),
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,8 @@ use crate::{
|
|||||||
to_assoc_type_id, to_chalk_trait_id,
|
to_assoc_type_id, to_chalk_trait_id,
|
||||||
traits::{InEnvironment, Solution},
|
traits::{InEnvironment, Solution},
|
||||||
utils::generics,
|
utils::generics,
|
||||||
BoundVar, Canonical, DebruijnIndex, Interner, Obligation, Substitution, TraitRef, Ty, TyKind,
|
AliasEq, AliasTy, BoundVar, Canonical, DebruijnIndex, Interner, Obligation, ProjectionTy,
|
||||||
|
Substitution, TraitRef, Ty, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
||||||
@ -82,16 +83,16 @@ fn deref_by_trait(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now do the assoc type projection
|
// Now do the assoc type projection
|
||||||
let projection = super::traits::ProjectionPredicate {
|
let projection = AliasEq {
|
||||||
ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len()))
|
alias: AliasTy::Projection(ProjectionTy {
|
||||||
.intern(&Interner),
|
|
||||||
projection_ty: super::ProjectionTy {
|
|
||||||
associated_ty_id: to_assoc_type_id(target),
|
associated_ty_id: to_assoc_type_id(target),
|
||||||
substitution: parameters,
|
substitution: parameters,
|
||||||
},
|
}),
|
||||||
|
ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len()))
|
||||||
|
.intern(&Interner),
|
||||||
};
|
};
|
||||||
|
|
||||||
let obligation = super::Obligation::Projection(projection);
|
let obligation = super::Obligation::AliasEq(projection);
|
||||||
|
|
||||||
let in_env = InEnvironment { value: obligation, environment: ty.environment };
|
let in_env = InEnvironment { value: obligation, environment: ty.environment };
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ use hir_expand::name::Name;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive,
|
db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive,
|
||||||
to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasTy, CallableDefId,
|
to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasEq, AliasTy,
|
||||||
CallableSig, GenericPredicate, ImplTraitId, Interner, Lifetime, Obligation, OpaqueTy,
|
CallableDefId, CallableSig, GenericPredicate, ImplTraitId, Interner, Lifetime, Obligation,
|
||||||
ProjectionTy, Scalar, Substitution, TraitRef, Ty, TyKind,
|
OpaqueTy, ProjectionTy, Scalar, Substitution, TraitRef, Ty, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct HirFormatter<'a> {
|
pub struct HirFormatter<'a> {
|
||||||
@ -268,6 +268,16 @@ impl HirDisplay for ProjectionTy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HirDisplay for OpaqueTy {
|
||||||
|
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||||
|
if f.should_truncate() {
|
||||||
|
return write!(f, "{}", TYPE_HINT_TRUNCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.substitution[0].hir_fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HirDisplay for Ty {
|
impl HirDisplay for Ty {
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||||
if f.should_truncate() {
|
if f.should_truncate() {
|
||||||
@ -700,12 +710,12 @@ fn write_bounds_like_dyn_trait(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GenericPredicate::Projection(projection_pred) if is_fn_trait => {
|
GenericPredicate::AliasEq(alias_eq) if is_fn_trait => {
|
||||||
is_fn_trait = false;
|
is_fn_trait = false;
|
||||||
write!(f, " -> ")?;
|
write!(f, " -> ")?;
|
||||||
projection_pred.ty.hir_fmt(f)?;
|
alias_eq.ty.hir_fmt(f)?;
|
||||||
}
|
}
|
||||||
GenericPredicate::Projection(projection_pred) => {
|
GenericPredicate::AliasEq(AliasEq { ty, alias }) => {
|
||||||
// in types in actual Rust, these will always come
|
// in types in actual Rust, these will always come
|
||||||
// after the corresponding Implemented predicate
|
// after the corresponding Implemented predicate
|
||||||
if angle_open {
|
if angle_open {
|
||||||
@ -714,11 +724,12 @@ fn write_bounds_like_dyn_trait(
|
|||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
angle_open = true;
|
angle_open = true;
|
||||||
}
|
}
|
||||||
let type_alias = f.db.type_alias_data(from_assoc_type_id(
|
if let AliasTy::Projection(proj) = alias {
|
||||||
projection_pred.projection_ty.associated_ty_id,
|
let type_alias =
|
||||||
));
|
f.db.type_alias_data(from_assoc_type_id(proj.associated_ty_id));
|
||||||
write!(f, "{} = ", type_alias.name)?;
|
write!(f, "{} = ", type_alias.name)?;
|
||||||
projection_pred.ty.hir_fmt(f)?;
|
}
|
||||||
|
ty.hir_fmt(f)?;
|
||||||
}
|
}
|
||||||
GenericPredicate::Error => {
|
GenericPredicate::Error => {
|
||||||
if angle_open {
|
if angle_open {
|
||||||
@ -775,20 +786,20 @@ impl HirDisplay for GenericPredicate {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
|
GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
|
||||||
GenericPredicate::Projection(projection_pred) => {
|
GenericPredicate::AliasEq(AliasEq {
|
||||||
|
alias: AliasTy::Projection(projection_ty),
|
||||||
|
ty,
|
||||||
|
}) => {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
|
projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
">::{} = ",
|
">::{} = ",
|
||||||
f.db.type_alias_data(from_assoc_type_id(
|
f.db.type_alias_data(from_assoc_type_id(projection_ty.associated_ty_id)).name,
|
||||||
projection_pred.projection_ty.associated_ty_id
|
|
||||||
))
|
|
||||||
.name,
|
|
||||||
)?;
|
)?;
|
||||||
projection_pred.ty.hir_fmt(f)?;
|
ty.hir_fmt(f)?;
|
||||||
}
|
}
|
||||||
GenericPredicate::Error => write!(f, "{{error}}")?,
|
GenericPredicate::AliasEq(_) | GenericPredicate::Error => write!(f, "{{error}}")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -815,11 +826,14 @@ impl HirDisplay for Obligation {
|
|||||||
tr.hir_fmt(f)?;
|
tr.hir_fmt(f)?;
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
Obligation::Projection(proj) => {
|
Obligation::AliasEq(AliasEq { alias, ty }) => {
|
||||||
write!(f, "Normalize(")?;
|
write!(f, "Normalize(")?;
|
||||||
proj.projection_ty.hir_fmt(f)?;
|
match alias {
|
||||||
|
AliasTy::Projection(projection_ty) => projection_ty.hir_fmt(f)?,
|
||||||
|
AliasTy::Opaque(opaque) => opaque.hir_fmt(f)?,
|
||||||
|
}
|
||||||
write!(f, " => ")?;
|
write!(f, " => ")?;
|
||||||
proj.ty.hir_fmt(f)?;
|
ty.hir_fmt(f)?;
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,12 @@ use stdx::impl_from;
|
|||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
traits::{Guidance, Obligation, Solution},
|
||||||
InEnvironment, ProjectionTy, Substitution, TraitEnvironment, TraitRef, Ty, TypeWalk,
|
InEnvironment, ProjectionTy, Substitution, TraitEnvironment, TraitRef, Ty, TypeWalk,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
|
db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
|
||||||
to_assoc_type_id, to_chalk_trait_id, AliasTy, Interner, TyKind,
|
to_assoc_type_id, to_chalk_trait_id, AliasEq, AliasTy, Interner, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use unify::unify;
|
pub(crate) use unify::unify;
|
||||||
@ -396,15 +396,15 @@ impl<'a> InferenceContext<'a> {
|
|||||||
.build();
|
.build();
|
||||||
let trait_ref =
|
let trait_ref =
|
||||||
TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs.clone() };
|
TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs.clone() };
|
||||||
let projection = ProjectionPredicate {
|
let alias_eq = AliasEq {
|
||||||
ty: ty.clone(),
|
alias: AliasTy::Projection(ProjectionTy {
|
||||||
projection_ty: ProjectionTy {
|
|
||||||
associated_ty_id: to_assoc_type_id(res_assoc_ty),
|
associated_ty_id: to_assoc_type_id(res_assoc_ty),
|
||||||
substitution: substs,
|
substitution: substs,
|
||||||
},
|
}),
|
||||||
|
ty: ty.clone(),
|
||||||
};
|
};
|
||||||
self.obligations.push(Obligation::Trait(trait_ref));
|
self.obligations.push(Obligation::Trait(trait_ref));
|
||||||
self.obligations.push(Obligation::Projection(projection));
|
self.obligations.push(Obligation::AliasEq(alias_eq));
|
||||||
self.resolve_ty_as_possible(ty)
|
self.resolve_ty_as_possible(ty)
|
||||||
}
|
}
|
||||||
None => self.err_ty(),
|
None => self.err_ty(),
|
||||||
@ -429,8 +429,8 @@ impl<'a> InferenceContext<'a> {
|
|||||||
|
|
||||||
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
||||||
let var = self.table.new_type_var();
|
let var = self.table.new_type_var();
|
||||||
let predicate = ProjectionPredicate { projection_ty: proj_ty, ty: var.clone() };
|
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
|
||||||
let obligation = Obligation::Projection(predicate);
|
let obligation = Obligation::AliasEq(alias_eq);
|
||||||
self.obligations.push(obligation);
|
self.obligations.push(obligation);
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
|
|||||||
|
|
||||||
use super::{InferenceContext, Obligation};
|
use super::{InferenceContext, Obligation};
|
||||||
use crate::{
|
use crate::{
|
||||||
BoundVar, Canonical, DebruijnIndex, FnPointer, GenericPredicate, InEnvironment, InferenceVar,
|
AliasEq, AliasTy, BoundVar, Canonical, DebruijnIndex, FnPointer, GenericPredicate,
|
||||||
Interner, Scalar, Substitution, Ty, TyKind, TypeWalk,
|
InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyKind, TypeWalk,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
@ -93,8 +93,8 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
|
|||||||
Obligation::Trait(tr) => {
|
Obligation::Trait(tr) => {
|
||||||
Obligation::Trait(self.do_canonicalize(tr, DebruijnIndex::INNERMOST))
|
Obligation::Trait(self.do_canonicalize(tr, DebruijnIndex::INNERMOST))
|
||||||
}
|
}
|
||||||
Obligation::Projection(pr) => {
|
Obligation::AliasEq(alias_eq) => {
|
||||||
Obligation::Projection(self.do_canonicalize(pr, DebruijnIndex::INNERMOST))
|
Obligation::AliasEq(self.do_canonicalize(alias_eq, DebruijnIndex::INNERMOST))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.into_canonicalized(InEnvironment {
|
self.into_canonicalized(InEnvironment {
|
||||||
@ -394,14 +394,25 @@ impl InferenceTable {
|
|||||||
{
|
{
|
||||||
self.unify_substs(&tr1.substitution, &tr2.substitution, depth + 1)
|
self.unify_substs(&tr1.substitution, &tr2.substitution, depth + 1)
|
||||||
}
|
}
|
||||||
(GenericPredicate::Projection(proj1), GenericPredicate::Projection(proj2))
|
(
|
||||||
if proj1.projection_ty.associated_ty_id == proj2.projection_ty.associated_ty_id =>
|
GenericPredicate::AliasEq(AliasEq { alias: alias1, ty: ty1 }),
|
||||||
{
|
GenericPredicate::AliasEq(AliasEq { alias: alias2, ty: ty2 }),
|
||||||
self.unify_substs(
|
) => {
|
||||||
&proj1.projection_ty.substitution,
|
let (substitution1, substitution2) = match (alias1, alias2) {
|
||||||
&proj2.projection_ty.substitution,
|
(AliasTy::Projection(projection_ty1), AliasTy::Projection(projection_ty2))
|
||||||
depth + 1,
|
if projection_ty1.associated_ty_id == projection_ty2.associated_ty_id =>
|
||||||
) && self.unify_inner(&proj1.ty, &proj2.ty, depth + 1)
|
{
|
||||||
|
(&projection_ty1.substitution, &projection_ty2.substitution)
|
||||||
|
}
|
||||||
|
(AliasTy::Opaque(opaque1), AliasTy::Opaque(opaque2))
|
||||||
|
if opaque1.opaque_ty_id == opaque2.opaque_ty_id =>
|
||||||
|
{
|
||||||
|
(&opaque1.substitution, &opaque2.substitution)
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
self.unify_substs(&substitution1, &substitution2, depth + 1)
|
||||||
|
&& self.unify_inner(&ty1, &ty2, depth + 1)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ pub use lower::{
|
|||||||
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
|
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
|
||||||
TyDefId, TyLoweringContext, ValueTyDefId,
|
TyDefId, TyLoweringContext, ValueTyDefId,
|
||||||
};
|
};
|
||||||
pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
|
pub use traits::{AliasEq, InEnvironment, Obligation, TraitEnvironment};
|
||||||
|
|
||||||
pub use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind};
|
pub use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind};
|
||||||
|
|
||||||
@ -72,6 +72,20 @@ pub struct OpaqueTy {
|
|||||||
pub substitution: Substitution,
|
pub substitution: Substitution,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TypeWalk for OpaqueTy {
|
||||||
|
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||||
|
self.substitution.walk(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_mut_binders(
|
||||||
|
&mut self,
|
||||||
|
f: &mut impl FnMut(&mut Ty, DebruijnIndex),
|
||||||
|
binders: DebruijnIndex,
|
||||||
|
) {
|
||||||
|
self.substitution.walk_mut_binders(f, binders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A "projection" type corresponds to an (unnormalized)
|
/// A "projection" type corresponds to an (unnormalized)
|
||||||
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
|
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
|
||||||
/// trait and all its parameters are fully known.
|
/// trait and all its parameters are fully known.
|
||||||
@ -133,6 +147,25 @@ pub enum AliasTy {
|
|||||||
Opaque(OpaqueTy),
|
Opaque(OpaqueTy),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TypeWalk for AliasTy {
|
||||||
|
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||||
|
match self {
|
||||||
|
AliasTy::Projection(it) => it.walk(f),
|
||||||
|
AliasTy::Opaque(it) => it.walk(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_mut_binders(
|
||||||
|
&mut self,
|
||||||
|
f: &mut impl FnMut(&mut Ty, DebruijnIndex),
|
||||||
|
binders: DebruijnIndex,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
AliasTy::Projection(it) => it.walk_mut_binders(f, binders),
|
||||||
|
AliasTy::Opaque(it) => it.walk_mut_binders(f, binders),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/// A type.
|
/// A type.
|
||||||
///
|
///
|
||||||
/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
|
/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
|
||||||
@ -535,7 +568,7 @@ pub enum GenericPredicate {
|
|||||||
/// The given trait needs to be implemented for its type parameters.
|
/// The given trait needs to be implemented for its type parameters.
|
||||||
Implemented(TraitRef),
|
Implemented(TraitRef),
|
||||||
/// An associated type bindings like in `Iterator<Item = T>`.
|
/// An associated type bindings like in `Iterator<Item = T>`.
|
||||||
Projection(ProjectionPredicate),
|
AliasEq(AliasEq),
|
||||||
/// We couldn't resolve the trait reference. (If some type parameters can't
|
/// We couldn't resolve the trait reference. (If some type parameters can't
|
||||||
/// be resolved, they will just be Unknown).
|
/// be resolved, they will just be Unknown).
|
||||||
Error,
|
Error,
|
||||||
@ -553,8 +586,10 @@ impl GenericPredicate {
|
|||||||
pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> {
|
pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> {
|
||||||
match self {
|
match self {
|
||||||
GenericPredicate::Implemented(tr) => Some(tr.clone()),
|
GenericPredicate::Implemented(tr) => Some(tr.clone()),
|
||||||
GenericPredicate::Projection(proj) => Some(proj.projection_ty.trait_ref(db)),
|
GenericPredicate::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => {
|
||||||
GenericPredicate::Error => None,
|
Some(proj.trait_ref(db))
|
||||||
|
}
|
||||||
|
GenericPredicate::AliasEq(_) | GenericPredicate::Error => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -563,7 +598,7 @@ impl TypeWalk for GenericPredicate {
|
|||||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||||
match self {
|
match self {
|
||||||
GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f),
|
GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f),
|
||||||
GenericPredicate::Projection(projection_pred) => projection_pred.walk(f),
|
GenericPredicate::AliasEq(alias_eq) => alias_eq.walk(f),
|
||||||
GenericPredicate::Error => {}
|
GenericPredicate::Error => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,9 +610,7 @@ impl TypeWalk for GenericPredicate {
|
|||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut_binders(f, binders),
|
GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut_binders(f, binders),
|
||||||
GenericPredicate::Projection(projection_pred) => {
|
GenericPredicate::AliasEq(alias_eq) => alias_eq.walk_mut_binders(f, binders),
|
||||||
projection_pred.walk_mut_binders(f, binders)
|
|
||||||
}
|
|
||||||
GenericPredicate::Error => {}
|
GenericPredicate::Error => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ use crate::{
|
|||||||
all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
|
all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
|
||||||
variant_data,
|
variant_data,
|
||||||
},
|
},
|
||||||
AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, FnPointer, FnSig, GenericPredicate,
|
AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, FnPointer, FnSig,
|
||||||
ImplTraitId, OpaqueTy, PolyFnSig, ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait,
|
GenericPredicate, ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, ReturnTypeImplTrait,
|
||||||
ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, Ty, TyKind, TypeWalk,
|
ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, Ty, TyKind, TypeWalk,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -750,9 +750,9 @@ impl<'a> TyLoweringContext<'a> {
|
|||||||
);
|
);
|
||||||
if let Some(type_ref) = &binding.type_ref {
|
if let Some(type_ref) = &binding.type_ref {
|
||||||
let ty = self.lower_ty(type_ref);
|
let ty = self.lower_ty(type_ref);
|
||||||
let projection_predicate =
|
let alias_eq =
|
||||||
ProjectionPredicate { projection_ty: projection_ty.clone(), ty };
|
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
|
||||||
preds.push(GenericPredicate::Projection(projection_predicate));
|
preds.push(GenericPredicate::AliasEq(alias_eq));
|
||||||
}
|
}
|
||||||
for bound in &binding.bounds {
|
for bound in &binding.bounds {
|
||||||
preds.extend(self.lower_type_bound(
|
preds.extend(self.lower_type_bound(
|
||||||
|
@ -8,10 +8,9 @@ use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
|
|||||||
use hir_def::{lang_item::LangItemTarget, TraitId};
|
use hir_def::{lang_item::LangItemTarget, TraitId};
|
||||||
use stdx::panic_context;
|
use stdx::panic_context;
|
||||||
|
|
||||||
use crate::{db::HirDatabase, DebruijnIndex, Substitution};
|
use crate::{
|
||||||
|
db::HirDatabase, AliasTy, Canonical, DebruijnIndex, GenericPredicate, HirDisplay, Substitution,
|
||||||
use super::{
|
TraitRef, Ty, TyKind, TypeWalk,
|
||||||
Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TyKind, TypeWalk,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::chalk::{from_chalk, Interner, ToChalk};
|
use self::chalk::{from_chalk, Interner, ToChalk};
|
||||||
@ -93,31 +92,32 @@ pub enum Obligation {
|
|||||||
/// Prove that a certain type implements a trait (the type is the `Self` type
|
/// Prove that a certain type implements a trait (the type is the `Self` type
|
||||||
/// parameter to the `TraitRef`).
|
/// parameter to the `TraitRef`).
|
||||||
Trait(TraitRef),
|
Trait(TraitRef),
|
||||||
Projection(ProjectionPredicate),
|
AliasEq(AliasEq),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Obligation {
|
impl Obligation {
|
||||||
pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
|
pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
|
||||||
match predicate {
|
match predicate {
|
||||||
GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
|
GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
|
||||||
GenericPredicate::Projection(projection_pred) => {
|
GenericPredicate::AliasEq(alias_eq) => Some(Obligation::AliasEq(alias_eq)),
|
||||||
Some(Obligation::Projection(projection_pred))
|
|
||||||
}
|
|
||||||
GenericPredicate::Error => None,
|
GenericPredicate::Error => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ProjectionPredicate {
|
pub struct AliasEq {
|
||||||
pub projection_ty: ProjectionTy,
|
pub alias: AliasTy,
|
||||||
pub ty: Ty,
|
pub ty: Ty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeWalk for ProjectionPredicate {
|
impl TypeWalk for AliasEq {
|
||||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||||
self.projection_ty.walk(f);
|
|
||||||
self.ty.walk(f);
|
self.ty.walk(f);
|
||||||
|
match &self.alias {
|
||||||
|
AliasTy::Projection(projection_ty) => projection_ty.walk(f),
|
||||||
|
AliasTy::Opaque(opaque) => opaque.walk(f),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_mut_binders(
|
fn walk_mut_binders(
|
||||||
@ -125,8 +125,11 @@ impl TypeWalk for ProjectionPredicate {
|
|||||||
f: &mut impl FnMut(&mut Ty, DebruijnIndex),
|
f: &mut impl FnMut(&mut Ty, DebruijnIndex),
|
||||||
binders: DebruijnIndex,
|
binders: DebruijnIndex,
|
||||||
) {
|
) {
|
||||||
self.projection_ty.walk_mut_binders(f, binders);
|
|
||||||
self.ty.walk_mut_binders(f, binders);
|
self.ty.walk_mut_binders(f, binders);
|
||||||
|
match &mut self.alias {
|
||||||
|
AliasTy::Projection(projection_ty) => projection_ty.walk_mut_binders(f, binders),
|
||||||
|
AliasTy::Opaque(opaque) => opaque.walk_mut_binders(f, binders),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,12 +141,14 @@ pub(crate) fn trait_solve_query(
|
|||||||
) -> Option<Solution> {
|
) -> Option<Solution> {
|
||||||
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.value {
|
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.value {
|
||||||
Obligation::Trait(it) => db.trait_data(it.hir_trait_id()).name.to_string(),
|
Obligation::Trait(it) => db.trait_data(it.hir_trait_id()).name.to_string(),
|
||||||
Obligation::Projection(_) => "projection".to_string(),
|
Obligation::AliasEq(_) => "alias_eq".to_string(),
|
||||||
});
|
});
|
||||||
log::info!("trait_solve_query({})", goal.value.value.display(db));
|
log::info!("trait_solve_query({})", goal.value.value.display(db));
|
||||||
|
|
||||||
if let Obligation::Projection(pred) = &goal.value.value {
|
if let Obligation::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), .. }) =
|
||||||
if let TyKind::BoundVar(_) = &pred.projection_ty.substitution[0].interned(&Interner) {
|
&goal.value.value
|
||||||
|
{
|
||||||
|
if let TyKind::BoundVar(_) = &projection_ty.substitution[0].interned(&Interner) {
|
||||||
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
|
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
|
||||||
return Some(Solution::Ambig(Guidance::Unknown));
|
return Some(Solution::Ambig(Guidance::Unknown));
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ use crate::{
|
|||||||
method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||||
to_assoc_type_id, to_chalk_trait_id,
|
to_assoc_type_id, to_chalk_trait_id,
|
||||||
utils::generics,
|
utils::generics,
|
||||||
BoundVar, CallableDefId, CallableSig, DebruijnIndex, FnDefId, GenericPredicate,
|
AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, DebruijnIndex, FnDefId,
|
||||||
ProjectionPredicate, ProjectionTy, Substitution, TraitRef, Ty, TyKind,
|
GenericPredicate, ProjectionTy, Substitution, TraitRef, Ty, TyKind,
|
||||||
};
|
};
|
||||||
use mapping::{
|
use mapping::{
|
||||||
convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue,
|
convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue,
|
||||||
@ -229,18 +229,18 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||||||
.intern(&Interner),
|
.intern(&Interner),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
let proj_bound = GenericPredicate::Projection(ProjectionPredicate {
|
let proj_bound = GenericPredicate::AliasEq(AliasEq {
|
||||||
// The parameter of the opaque type.
|
alias: AliasTy::Projection(ProjectionTy {
|
||||||
ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 })
|
|
||||||
.intern(&Interner),
|
|
||||||
projection_ty: ProjectionTy {
|
|
||||||
associated_ty_id: to_assoc_type_id(future_output),
|
associated_ty_id: to_assoc_type_id(future_output),
|
||||||
// Self type as the first parameter.
|
// Self type as the first parameter.
|
||||||
substitution: Substitution::single(
|
substitution: Substitution::single(
|
||||||
TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
|
TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
|
||||||
.intern(&Interner),
|
.intern(&Interner),
|
||||||
),
|
),
|
||||||
},
|
}),
|
||||||
|
// The parameter of the opaque type.
|
||||||
|
ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 })
|
||||||
|
.intern(&Interner),
|
||||||
});
|
});
|
||||||
let bound = OpaqueTyDatumBound {
|
let bound = OpaqueTyDatumBound {
|
||||||
bounds: make_binders(
|
bounds: make_binders(
|
||||||
|
@ -14,8 +14,8 @@ use crate::{
|
|||||||
from_assoc_type_id,
|
from_assoc_type_id,
|
||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
traits::{Canonical, Obligation},
|
traits::{Canonical, Obligation},
|
||||||
AliasTy, CallableDefId, FnPointer, GenericPredicate, InEnvironment, OpaqueTy,
|
AliasTy, CallableDefId, FnPointer, GenericPredicate, InEnvironment, OpaqueTy, ProjectionTy,
|
||||||
ProjectionPredicate, ProjectionTy, Scalar, Substitution, TraitRef, Ty,
|
Scalar, Substitution, TraitRef, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::interner::*;
|
use super::interner::*;
|
||||||
@ -314,12 +314,10 @@ impl ToChalk for GenericPredicate {
|
|||||||
let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
|
let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
|
||||||
make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
|
make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
|
||||||
}
|
}
|
||||||
GenericPredicate::Projection(projection_pred) => {
|
GenericPredicate::AliasEq(alias_eq) => make_binders(
|
||||||
let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
|
chalk_ir::WhereClause::AliasEq(alias_eq.to_chalk(db).shifted_in(&Interner)),
|
||||||
let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
|
0,
|
||||||
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"),
|
GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,16 +336,8 @@ impl ToChalk for GenericPredicate {
|
|||||||
chalk_ir::WhereClause::Implemented(tr) => {
|
chalk_ir::WhereClause::Implemented(tr) => {
|
||||||
GenericPredicate::Implemented(from_chalk(db, tr))
|
GenericPredicate::Implemented(from_chalk(db, tr))
|
||||||
}
|
}
|
||||||
chalk_ir::WhereClause::AliasEq(projection_eq) => {
|
chalk_ir::WhereClause::AliasEq(alias_eq) => {
|
||||||
let projection_ty = from_chalk(
|
GenericPredicate::AliasEq(from_chalk(db, alias_eq))
|
||||||
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(_) => {
|
chalk_ir::WhereClause::LifetimeOutlives(_) => {
|
||||||
@ -383,19 +373,55 @@ impl ToChalk for ProjectionTy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ToChalk for OpaqueTy {
|
||||||
|
type Chalk = chalk_ir::OpaqueTy<Interner>;
|
||||||
|
|
||||||
impl ToChalk for ProjectionPredicate {
|
fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk {
|
||||||
type Chalk = chalk_ir::AliasEq<Interner>;
|
chalk_ir::OpaqueTy {
|
||||||
|
opaque_ty_id: self.opaque_ty_id,
|
||||||
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
|
substitution: self.substitution.to_chalk(db),
|
||||||
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<Interner>) -> Self {
|
fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
|
||||||
unimplemented!()
|
OpaqueTy {
|
||||||
|
opaque_ty_id: chalk.opaque_ty_id,
|
||||||
|
substitution: from_chalk(db, chalk.substitution),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToChalk for AliasTy {
|
||||||
|
type Chalk = chalk_ir::AliasTy<Interner>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk {
|
||||||
|
match self {
|
||||||
|
AliasTy::Projection(projection_ty) => {
|
||||||
|
chalk_ir::AliasTy::Projection(projection_ty.to_chalk(db))
|
||||||
|
}
|
||||||
|
AliasTy::Opaque(opaque_ty) => chalk_ir::AliasTy::Opaque(opaque_ty.to_chalk(db)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
|
||||||
|
match chalk {
|
||||||
|
chalk_ir::AliasTy::Projection(projection_ty) => {
|
||||||
|
AliasTy::Projection(from_chalk(db, projection_ty))
|
||||||
|
}
|
||||||
|
chalk_ir::AliasTy::Opaque(opaque_ty) => AliasTy::Opaque(from_chalk(db, opaque_ty)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToChalk for AliasEq {
|
||||||
|
type Chalk = chalk_ir::AliasEq<Interner>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
|
||||||
|
chalk_ir::AliasEq { alias: self.alias.to_chalk(db), ty: self.ty.to_chalk(db) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(db: &dyn HirDatabase, alias_eq: chalk_ir::AliasEq<Interner>) -> Self {
|
||||||
|
AliasEq { alias: from_chalk(db, alias_eq.alias), ty: from_chalk(db, alias_eq.ty) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +431,7 @@ impl ToChalk for Obligation {
|
|||||||
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
|
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
|
||||||
match self {
|
match self {
|
||||||
Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
|
Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
|
||||||
Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner),
|
Obligation::AliasEq(alias_eq) => alias_eq.to_chalk(db).cast(&Interner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,29 +553,29 @@ pub(super) fn generic_predicate_to_inline_bound(
|
|||||||
let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self };
|
let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self };
|
||||||
Some(rust_ir::InlineBound::TraitBound(trait_bound))
|
Some(rust_ir::InlineBound::TraitBound(trait_bound))
|
||||||
}
|
}
|
||||||
GenericPredicate::Projection(proj) => {
|
GenericPredicate::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
||||||
if &proj.projection_ty.substitution[0] != self_ty {
|
if &projection_ty.substitution[0] != self_ty {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let trait_ = match from_assoc_type_id(proj.projection_ty.associated_ty_id)
|
let trait_ = match from_assoc_type_id(projection_ty.associated_ty_id)
|
||||||
.lookup(db.upcast())
|
.lookup(db.upcast())
|
||||||
.container
|
.container
|
||||||
{
|
{
|
||||||
AssocContainerId::TraitId(t) => t,
|
AssocContainerId::TraitId(t) => t,
|
||||||
_ => panic!("associated type not in trait"),
|
_ => panic!("associated type not in trait"),
|
||||||
};
|
};
|
||||||
let args_no_self = proj.projection_ty.substitution[1..]
|
let args_no_self = projection_ty.substitution[1..]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.clone().to_chalk(db).cast(&Interner))
|
.map(|ty| ty.clone().to_chalk(db).cast(&Interner))
|
||||||
.collect();
|
.collect();
|
||||||
let alias_eq_bound = rust_ir::AliasEqBound {
|
let alias_eq_bound = rust_ir::AliasEqBound {
|
||||||
value: proj.ty.clone().to_chalk(db),
|
value: ty.clone().to_chalk(db),
|
||||||
trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self },
|
trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self },
|
||||||
associated_ty_id: proj.projection_ty.associated_ty_id,
|
associated_ty_id: projection_ty.associated_ty_id,
|
||||||
parameters: Vec::new(), // FIXME we don't support generic associated types yet
|
parameters: Vec::new(), // FIXME we don't support generic associated types yet
|
||||||
};
|
};
|
||||||
Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
|
Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
|
||||||
}
|
}
|
||||||
GenericPredicate::Error => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user