1 //! Various extensions traits for Chalk types. 2 3 use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy}; 4 use hir_def::{ 5 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, 6 generics::TypeOrConstParamData, 7 lang_item::LangItem, 8 type_ref::Rawness, 9 DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, 10 }; 11 12 use crate::{ 13 db::HirDatabase, 14 from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, 15 to_chalk_trait_id, 16 utils::{generics, ClosureSubst}, 17 AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, 18 ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, 19 QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, 20 }; 21 22 pub trait TyExt { is_unit(&self) -> bool23 fn is_unit(&self) -> bool; is_integral(&self) -> bool24 fn is_integral(&self) -> bool; is_scalar(&self) -> bool25 fn is_scalar(&self) -> bool; is_floating_point(&self) -> bool26 fn is_floating_point(&self) -> bool; is_never(&self) -> bool27 fn is_never(&self) -> bool; is_unknown(&self) -> bool28 fn is_unknown(&self) -> bool; contains_unknown(&self) -> bool29 fn contains_unknown(&self) -> bool; is_ty_var(&self) -> bool30 fn is_ty_var(&self) -> bool; 31 as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>32 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; as_builtin(&self) -> Option<BuiltinType>33 fn as_builtin(&self) -> Option<BuiltinType>; as_tuple(&self) -> Option<&Substitution>34 fn as_tuple(&self) -> Option<&Substitution>; as_closure(&self) -> Option<ClosureId>35 fn as_closure(&self) -> Option<ClosureId>; as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>36 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>37 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; as_raw_ptr(&self) -> Option<(&Ty, Mutability)>38 fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>; as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>39 fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>; as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>40 fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>; 41 callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>42 fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>; callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>43 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>; 44 strip_references(&self) -> &Ty45 fn strip_references(&self) -> &Ty; strip_reference(&self) -> &Ty46 fn strip_reference(&self) -> &Ty; 47 48 /// If this is a `dyn Trait`, returns that trait. dyn_trait(&self) -> Option<TraitId>49 fn dyn_trait(&self) -> Option<TraitId>; 50 impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>51 fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>; associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>52 fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>; is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool53 fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool; 54 55 /// FIXME: Get rid of this, it's not a good abstraction equals_ctor(&self, other: &Ty) -> bool56 fn equals_ctor(&self, other: &Ty) -> bool; 57 } 58 59 impl TyExt for Ty { is_unit(&self) -> bool60 fn is_unit(&self) -> bool { 61 matches!(self.kind(Interner), TyKind::Tuple(0, _)) 62 } 63 is_integral(&self) -> bool64 fn is_integral(&self) -> bool { 65 matches!( 66 self.kind(Interner), 67 TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)) 68 | TyKind::InferenceVar(_, TyVariableKind::Integer) 69 ) 70 } 71 is_scalar(&self) -> bool72 fn is_scalar(&self) -> bool { 73 matches!(self.kind(Interner), TyKind::Scalar(_)) 74 } 75 is_floating_point(&self) -> bool76 fn is_floating_point(&self) -> bool { 77 matches!( 78 self.kind(Interner), 79 TyKind::Scalar(Scalar::Float(_)) | TyKind::InferenceVar(_, TyVariableKind::Float) 80 ) 81 } 82 is_never(&self) -> bool83 fn is_never(&self) -> bool { 84 matches!(self.kind(Interner), TyKind::Never) 85 } 86 is_unknown(&self) -> bool87 fn is_unknown(&self) -> bool { 88 matches!(self.kind(Interner), TyKind::Error) 89 } 90 contains_unknown(&self) -> bool91 fn contains_unknown(&self) -> bool { 92 self.data(Interner).flags.contains(TypeFlags::HAS_ERROR) 93 } 94 is_ty_var(&self) -> bool95 fn is_ty_var(&self) -> bool { 96 matches!(self.kind(Interner), TyKind::InferenceVar(_, _)) 97 } 98 as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>99 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { 100 match self.kind(Interner) { 101 TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), 102 _ => None, 103 } 104 } 105 as_builtin(&self) -> Option<BuiltinType>106 fn as_builtin(&self) -> Option<BuiltinType> { 107 match self.kind(Interner) { 108 TyKind::Str => Some(BuiltinType::Str), 109 TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool), 110 TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char), 111 TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty { 112 FloatTy::F64 => BuiltinFloat::F64, 113 FloatTy::F32 => BuiltinFloat::F32, 114 })), 115 TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity { 116 IntTy::Isize => BuiltinInt::Isize, 117 IntTy::I8 => BuiltinInt::I8, 118 IntTy::I16 => BuiltinInt::I16, 119 IntTy::I32 => BuiltinInt::I32, 120 IntTy::I64 => BuiltinInt::I64, 121 IntTy::I128 => BuiltinInt::I128, 122 })), 123 TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity { 124 UintTy::Usize => BuiltinUint::Usize, 125 UintTy::U8 => BuiltinUint::U8, 126 UintTy::U16 => BuiltinUint::U16, 127 UintTy::U32 => BuiltinUint::U32, 128 UintTy::U64 => BuiltinUint::U64, 129 UintTy::U128 => BuiltinUint::U128, 130 })), 131 _ => None, 132 } 133 } 134 as_tuple(&self) -> Option<&Substitution>135 fn as_tuple(&self) -> Option<&Substitution> { 136 match self.kind(Interner) { 137 TyKind::Tuple(_, substs) => Some(substs), 138 _ => None, 139 } 140 } 141 as_closure(&self) -> Option<ClosureId>142 fn as_closure(&self) -> Option<ClosureId> { 143 match self.kind(Interner) { 144 TyKind::Closure(id, _) => Some(*id), 145 _ => None, 146 } 147 } 148 as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>149 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> { 150 match self.callable_def(db) { 151 Some(CallableDefId::FunctionId(func)) => Some(func), 152 Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None, 153 } 154 } 155 as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>156 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> { 157 match self.kind(Interner) { 158 TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)), 159 _ => None, 160 } 161 } 162 as_raw_ptr(&self) -> Option<(&Ty, Mutability)>163 fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> { 164 match self.kind(Interner) { 165 TyKind::Raw(mutability, ty) => Some((ty, *mutability)), 166 _ => None, 167 } 168 } 169 as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>170 fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { 171 match self.kind(Interner) { 172 TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)), 173 TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)), 174 _ => None, 175 } 176 } 177 as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>178 fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> { 179 match *self.kind(Interner) { 180 TyKind::Adt(AdtId(adt), ..) => Some(adt.into()), 181 TyKind::FnDef(callable, ..) => { 182 Some(db.lookup_intern_callable_def(callable.into()).into()) 183 } 184 TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()), 185 TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()), 186 _ => None, 187 } 188 } 189 callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>190 fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> { 191 match self.kind(Interner) { 192 &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())), 193 _ => None, 194 } 195 } 196 callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>197 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> { 198 match self.kind(Interner) { 199 TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), 200 TyKind::FnDef(def, parameters) => { 201 let callable_def = db.lookup_intern_callable_def((*def).into()); 202 let sig = db.callable_item_signature(callable_def); 203 Some(sig.substitute(Interner, parameters)) 204 } 205 TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db), 206 _ => None, 207 } 208 } 209 dyn_trait(&self) -> Option<TraitId>210 fn dyn_trait(&self) -> Option<TraitId> { 211 let trait_ref = match self.kind(Interner) { 212 // The principal trait bound should be the first element of the bounds. This is an 213 // invariant ensured by `TyLoweringContext::lower_dyn_trait()`. 214 // FIXME: dyn types may not have principal trait and we don't want to return auto trait 215 // here. 216 TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| { 217 match b.skip_binders() { 218 WhereClause::Implemented(trait_ref) => Some(trait_ref), 219 _ => None, 220 } 221 }), 222 _ => None, 223 }?; 224 Some(from_chalk_trait_id(trait_ref.trait_id)) 225 } 226 strip_references(&self) -> &Ty227 fn strip_references(&self) -> &Ty { 228 let mut t: &Ty = self; 229 while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(Interner) { 230 t = ty; 231 } 232 t 233 } 234 strip_reference(&self) -> &Ty235 fn strip_reference(&self) -> &Ty { 236 self.as_reference().map_or(self, |(ty, _, _)| ty) 237 } 238 impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>239 fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> { 240 match self.kind(Interner) { 241 TyKind::OpaqueType(opaque_ty_id, subst) => { 242 match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) { 243 ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => { 244 let krate = def.module(db.upcast()).krate(); 245 if let Some(future_trait) = 246 db.lang_item(krate, LangItem::Future).and_then(|item| item.as_trait()) 247 { 248 // This is only used by type walking. 249 // Parameters will be walked outside, and projection predicate is not used. 250 // So just provide the Future trait. 251 let impl_bound = Binders::empty( 252 Interner, 253 WhereClause::Implemented(TraitRef { 254 trait_id: to_chalk_trait_id(future_trait), 255 substitution: Substitution::empty(Interner), 256 }), 257 ); 258 Some(vec![impl_bound]) 259 } else { 260 None 261 } 262 } 263 ImplTraitId::ReturnTypeImplTrait(func, idx) => { 264 db.return_type_impl_traits(func).map(|it| { 265 let data = 266 (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); 267 data.substitute(Interner, &subst).into_value_and_skipped_binders().0 268 }) 269 } 270 } 271 } 272 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { 273 let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()) 274 { 275 ImplTraitId::ReturnTypeImplTrait(func, idx) => { 276 db.return_type_impl_traits(func).map(|it| { 277 let data = 278 (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); 279 data.substitute(Interner, &opaque_ty.substitution) 280 }) 281 } 282 // It always has an parameter for Future::Output type. 283 ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), 284 }; 285 286 predicates.map(|it| it.into_value_and_skipped_binders().0) 287 } 288 TyKind::Placeholder(idx) => { 289 let id = from_placeholder_idx(db, *idx); 290 let generic_params = db.generic_params(id.parent); 291 let param_data = &generic_params.type_or_consts[id.local_id]; 292 match param_data { 293 TypeOrConstParamData::TypeParamData(p) => match p.provenance { 294 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { 295 let substs = TyBuilder::placeholder_subst(db, id.parent); 296 let predicates = db 297 .generic_predicates(id.parent) 298 .iter() 299 .map(|pred| pred.clone().substitute(Interner, &substs)) 300 .filter(|wc| match &wc.skip_binders() { 301 WhereClause::Implemented(tr) => { 302 &tr.self_type_parameter(Interner) == self 303 } 304 WhereClause::AliasEq(AliasEq { 305 alias: AliasTy::Projection(proj), 306 ty: _, 307 }) => &proj.self_type_parameter(db) == self, 308 _ => false, 309 }) 310 .collect::<Vec<_>>(); 311 312 Some(predicates) 313 } 314 _ => None, 315 }, 316 _ => None, 317 } 318 } 319 _ => None, 320 } 321 } 322 associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>323 fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> { 324 match self.kind(Interner) { 325 TyKind::AssociatedType(id, ..) => { 326 match from_assoc_type_id(*id).lookup(db.upcast()).container { 327 ItemContainerId::TraitId(trait_id) => Some(trait_id), 328 _ => None, 329 } 330 } 331 TyKind::Alias(AliasTy::Projection(projection_ty)) => { 332 match from_assoc_type_id(projection_ty.associated_ty_id) 333 .lookup(db.upcast()) 334 .container 335 { 336 ItemContainerId::TraitId(trait_id) => Some(trait_id), 337 _ => None, 338 } 339 } 340 _ => None, 341 } 342 } 343 is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool344 fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool { 345 let crate_id = owner.module(db.upcast()).krate(); 346 let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else { 347 return false; 348 }; 349 let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build(); 350 let env = db.trait_environment_for_body(owner); 351 let goal = Canonical { 352 value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), 353 binders: CanonicalVarKinds::empty(Interner), 354 }; 355 db.trait_solve(crate_id, None, goal).is_some() 356 } 357 equals_ctor(&self, other: &Ty) -> bool358 fn equals_ctor(&self, other: &Ty) -> bool { 359 match (self.kind(Interner), other.kind(Interner)) { 360 (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, 361 (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => { 362 true 363 } 364 (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2, 365 (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2, 366 (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => { 367 ty_id == ty_id2 368 } 369 (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2, 370 (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2, 371 (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..)) 372 | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => { 373 mutability == mutability2 374 } 375 ( 376 TyKind::Function(FnPointer { num_binders, sig, .. }), 377 TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }), 378 ) => num_binders == num_binders2 && sig == sig2, 379 (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => { 380 cardinality == cardinality2 381 } 382 (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true, 383 (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2, 384 _ => false, 385 } 386 } 387 } 388 389 pub trait ProjectionTyExt { trait_ref(&self, db: &dyn HirDatabase) -> TraitRef390 fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef; trait_(&self, db: &dyn HirDatabase) -> TraitId391 fn trait_(&self, db: &dyn HirDatabase) -> TraitId; self_type_parameter(&self, db: &dyn HirDatabase) -> Ty392 fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty; 393 } 394 395 impl ProjectionTyExt for ProjectionTy { trait_ref(&self, db: &dyn HirDatabase) -> TraitRef396 fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef { 397 // FIXME: something like `Split` trait from chalk-solve might be nice. 398 let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into()); 399 let substitution = Substitution::from_iter( 400 Interner, 401 self.substitution.iter(Interner).skip(generics.len_self()), 402 ); 403 TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution } 404 } 405 trait_(&self, db: &dyn HirDatabase) -> TraitId406 fn trait_(&self, db: &dyn HirDatabase) -> TraitId { 407 match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container { 408 ItemContainerId::TraitId(it) => it, 409 _ => panic!("projection ty without parent trait"), 410 } 411 } 412 self_type_parameter(&self, db: &dyn HirDatabase) -> Ty413 fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty { 414 self.trait_ref(db).self_type_parameter(Interner) 415 } 416 } 417 418 pub trait DynTyExt { principal(&self) -> Option<&TraitRef>419 fn principal(&self) -> Option<&TraitRef>; 420 } 421 422 impl DynTyExt for DynTy { principal(&self) -> Option<&TraitRef>423 fn principal(&self) -> Option<&TraitRef> { 424 self.bounds.skip_binders().interned().get(0).and_then(|b| match b.skip_binders() { 425 crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), 426 _ => None, 427 }) 428 } 429 } 430 431 pub trait TraitRefExt { hir_trait_id(&self) -> TraitId432 fn hir_trait_id(&self) -> TraitId; 433 } 434 435 impl TraitRefExt for TraitRef { hir_trait_id(&self) -> TraitId436 fn hir_trait_id(&self) -> TraitId { 437 from_chalk_trait_id(self.trait_id) 438 } 439 } 440