2019-11-26 08:26:08 -06:00
//! Helper functions for working with def, which don't need to be a separate
//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
2021-10-22 01:23:29 -05:00
use std ::iter ;
2021-04-29 13:18:41 -05:00
2021-06-29 10:35:37 -05:00
use base_db ::CrateId ;
2021-04-07 14:26:24 -05:00
use chalk_ir ::{ fold ::Shift , BoundVar , DebruijnIndex } ;
2019-11-26 07:59:24 -06:00
use hir_def ::{
db ::DefDatabase ,
2020-12-17 15:01:42 -06:00
generics ::{
2022-06-12 07:40:37 -05:00
GenericParams , TypeOrConstParamData , TypeParamProvenance , WherePredicate ,
2021-12-29 07:35:59 -06:00
WherePredicateTypeTarget ,
2020-12-17 15:01:42 -06:00
} ,
2021-04-04 20:50:10 -05:00
intern ::Interned ,
2019-11-26 07:59:24 -06:00
resolver ::{ HasResolver , TypeNs } ,
2021-06-14 07:36:56 -05:00
type_ref ::{ TraitBoundModifier , TypeRef } ,
2022-04-07 11:33:03 -05:00
ConstParamId , FunctionId , GenericDefId , ItemContainerId , Lookup , TraitId , TypeAliasId ,
TypeOrConstParamId , TypeParamId ,
2019-11-26 07:59:24 -06:00
} ;
2022-06-23 13:08:29 -05:00
use hir_expand ::name ::{ known , Name } ;
2022-03-09 12:50:24 -06:00
use itertools ::Either ;
2021-04-29 13:18:41 -05:00
use rustc_hash ::FxHashSet ;
2021-12-10 13:01:24 -06:00
use smallvec ::{ smallvec , SmallVec } ;
use syntax ::SmolStr ;
2019-11-26 07:59:24 -06:00
2021-04-29 13:18:41 -05:00
use crate ::{
2022-03-09 12:50:24 -06:00
db ::HirDatabase , ChalkTraitId , ConstData , ConstValue , GenericArgData , Interner , Substitution ,
TraitRef , TraitRefExt , TyKind , WhereClause ,
2021-04-29 13:18:41 -05:00
} ;
2020-04-26 09:56:25 -05:00
2021-06-29 10:35:37 -05:00
pub ( crate ) fn fn_traits ( db : & dyn DefDatabase , krate : CrateId ) -> impl Iterator < Item = TraitId > {
2021-10-22 01:23:29 -05:00
[
2021-12-10 13:01:24 -06:00
db . lang_item ( krate , SmolStr ::new_inline ( " fn " ) ) ,
db . lang_item ( krate , SmolStr ::new_inline ( " fn_mut " ) ) ,
db . lang_item ( krate , SmolStr ::new_inline ( " fn_once " ) ) ,
2021-10-22 01:23:29 -05:00
]
. into_iter ( )
. flatten ( )
. flat_map ( | it | it . as_trait ( ) )
2021-06-29 10:35:37 -05:00
}
2021-12-10 13:01:24 -06:00
fn direct_super_traits ( db : & dyn DefDatabase , trait_ : TraitId ) -> SmallVec < [ TraitId ; 4 ] > {
2019-11-26 07:59:24 -06:00
let resolver = trait_ . resolver ( db ) ;
// returning the iterator directly doesn't easily work because of
// lifetime problems, but since there usually shouldn't be more than a
// few direct traits this should be fine (we could even use some kind of
// SmallVec if performance is a concern)
2020-01-31 08:17:48 -06:00
let generic_params = db . generic_params ( trait_ . into ( ) ) ;
let trait_self = generic_params . find_trait_self_param ( ) ;
generic_params
2019-11-26 07:59:24 -06:00
. where_predicates
. iter ( )
2020-12-11 06:49:32 -06:00
. filter_map ( | pred | match pred {
2020-12-17 15:01:42 -06:00
WherePredicate ::ForLifetime { target , bound , .. }
2022-06-23 13:08:29 -05:00
| WherePredicate ::TypeBound { target , bound } = > {
let is_trait = match target {
WherePredicateTypeTarget ::TypeRef ( type_ref ) = > match & * * type_ref {
TypeRef ::Path ( p ) = > p . is_self_type ( ) ,
_ = > false ,
} ,
WherePredicateTypeTarget ::TypeOrConstParam ( local_id ) = > {
Some ( * local_id ) = = trait_self
}
} ;
match is_trait {
true = > bound . as_path ( ) ,
false = > None ,
2020-12-11 06:49:32 -06:00
}
2022-06-23 13:08:29 -05:00
}
2020-12-17 15:01:42 -06:00
WherePredicate ::Lifetime { .. } = > None ,
2019-11-26 07:59:24 -06:00
} )
2022-06-23 13:08:29 -05:00
. filter ( | ( _ , bound_modifier ) | matches! ( bound_modifier , TraitBoundModifier ::None ) )
. filter_map ( | ( path , _ ) | match resolver . resolve_path_in_type_ns_fully ( db , path . mod_path ( ) ) {
2019-11-26 07:59:24 -06:00
Some ( TypeNs ::TraitId ( t ) ) = > Some ( t ) ,
_ = > None ,
} )
. collect ( )
}
2020-04-26 09:56:25 -05:00
fn direct_super_trait_refs ( db : & dyn HirDatabase , trait_ref : & TraitRef ) -> Vec < TraitRef > {
// returning the iterator directly doesn't easily work because of
// lifetime problems, but since there usually shouldn't be more than a
// few direct traits this should be fine (we could even use some kind of
// SmallVec if performance is a concern)
2021-03-18 15:53:19 -05:00
let generic_params = db . generic_params ( trait_ref . hir_trait_id ( ) . into ( ) ) ;
2020-04-26 09:56:25 -05:00
let trait_self = match generic_params . find_trait_self_param ( ) {
2021-12-29 07:35:59 -06:00
Some ( p ) = > TypeOrConstParamId { parent : trait_ref . hir_trait_id ( ) . into ( ) , local_id : p } ,
2020-04-26 09:56:25 -05:00
None = > return Vec ::new ( ) ,
} ;
2022-02-03 05:43:15 -06:00
db . generic_predicates_for_param ( trait_self . parent , trait_self , None )
2020-04-26 09:56:25 -05:00
. iter ( )
. filter_map ( | pred | {
2021-03-21 11:40:14 -05:00
pred . as_ref ( ) . filter_map ( | pred | match pred . skip_binders ( ) {
2021-03-24 17:07:54 -05:00
// FIXME: how to correctly handle higher-ranked bounds here?
2021-04-05 12:15:13 -05:00
WhereClause ::Implemented ( tr ) = > Some (
tr . clone ( )
2021-12-19 10:58:39 -06:00
. shifted_out_to ( Interner , DebruijnIndex ::ONE )
2021-04-05 12:15:13 -05:00
. expect ( " FIXME unexpected higher-ranked trait bound " ) ,
) ,
2020-04-26 09:56:25 -05:00
_ = > None ,
} )
} )
2021-12-19 10:58:39 -06:00
. map ( | pred | pred . substitute ( Interner , & trait_ref . substitution ) )
2020-04-26 09:56:25 -05:00
. collect ( )
}
2019-11-26 07:59:24 -06:00
/// Returns an iterator over the whole super trait hierarchy (including the
/// trait itself).
2021-12-10 13:01:24 -06:00
pub fn all_super_traits ( db : & dyn DefDatabase , trait_ : TraitId ) -> SmallVec < [ TraitId ; 4 ] > {
2019-11-26 07:59:24 -06:00
// we need to take care a bit here to avoid infinite loops in case of cycles
// (i.e. if we have `trait A: B; trait B: A;`)
2021-12-10 13:01:24 -06:00
let mut result = smallvec! [ trait_ ] ;
2019-11-26 07:59:24 -06:00
let mut i = 0 ;
2021-12-10 13:01:24 -06:00
while let Some ( & t ) = result . get ( i ) {
2019-11-26 07:59:24 -06:00
// yeah this is quadratic, but trait hierarchies should be flat
// enough that this doesn't matter
for tt in direct_super_traits ( db , t ) {
if ! result . contains ( & tt ) {
result . push ( tt ) ;
}
}
i + = 1 ;
}
result
}
2019-11-26 08:42:21 -06:00
2020-04-26 09:56:25 -05:00
/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
/// super traits. The original trait ref will be included. So the difference to
/// `all_super_traits` is that we keep track of type parameters; for example if
/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
/// `Self: OtherTrait<i32>`.
2022-07-20 08:02:08 -05:00
pub ( super ) fn all_super_trait_refs ( db : & dyn HirDatabase , trait_ref : TraitRef ) -> SuperTraits < '_ > {
2021-04-29 13:18:41 -05:00
SuperTraits { db , seen : iter ::once ( trait_ref . trait_id ) . collect ( ) , stack : vec ! [ trait_ref ] }
}
2021-04-05 12:15:13 -05:00
2021-04-29 13:18:41 -05:00
pub ( super ) struct SuperTraits < ' a > {
db : & ' a dyn HirDatabase ,
stack : Vec < TraitRef > ,
seen : FxHashSet < ChalkTraitId > ,
}
impl < ' a > SuperTraits < ' a > {
fn elaborate ( & mut self , trait_ref : & TraitRef ) {
let mut trait_refs = direct_super_trait_refs ( self . db , trait_ref ) ;
trait_refs . retain ( | tr | ! self . seen . contains ( & tr . trait_id ) ) ;
self . stack . extend ( trait_refs ) ;
}
}
impl < ' a > Iterator for SuperTraits < ' a > {
type Item = TraitRef ;
fn next ( & mut self ) -> Option < Self ::Item > {
if let Some ( next ) = self . stack . pop ( ) {
self . elaborate ( & next ) ;
Some ( next )
} else {
None
2020-04-26 09:56:25 -05:00
}
}
}
2019-11-26 09:02:50 -06:00
pub ( super ) fn associated_type_by_name_including_super_traits (
2020-06-19 09:29:38 -05:00
db : & dyn HirDatabase ,
trait_ref : TraitRef ,
2019-11-26 08:42:21 -06:00
name : & Name ,
2020-06-19 09:29:38 -05:00
) -> Option < ( TraitRef , TypeAliasId ) > {
2021-04-29 13:18:41 -05:00
all_super_trait_refs ( db , trait_ref ) . find_map ( | t | {
2021-03-18 15:53:19 -05:00
let assoc_type = db . trait_data ( t . hir_trait_id ( ) ) . associated_type_by_name ( name ) ? ;
2020-06-19 09:29:38 -05:00
Some ( ( t , assoc_type ) )
} )
2019-11-26 08:42:21 -06:00
}
2019-11-27 07:25:01 -06:00
2020-03-13 10:05:46 -05:00
pub ( crate ) fn generics ( db : & dyn DefDatabase , def : GenericDefId ) -> Generics {
2019-12-07 04:50:36 -06:00
let parent_generics = parent_generic_def ( db , def ) . map ( | def | Box ::new ( generics ( db , def ) ) ) ;
2022-04-02 06:04:04 -05:00
if parent_generics . is_some ( ) & & matches! ( def , GenericDefId ::TypeAliasId ( _ ) ) {
2022-04-11 00:44:01 -05:00
let params = db . generic_params ( def ) ;
2022-06-12 09:07:08 -05:00
let has_consts =
params . iter ( ) . any ( | ( _ , x ) | matches! ( x , TypeOrConstParamData ::ConstParamData ( _ ) ) ) ;
return if has_consts {
2022-04-11 00:44:01 -05:00
// XXX: treat const generic associated types as not existing to avoid crashes (#11769)
//
// Chalk expects the inner associated type's parameters to come
// *before*, not after the trait's generics as we've always done it.
// Adapting to this requires a larger refactoring
cov_mark ::hit! ( ignore_gats ) ;
2022-06-12 09:07:08 -05:00
Generics { def , params : Interned ::new ( Default ::default ( ) ) , parent_generics }
2022-04-11 00:44:01 -05:00
} else {
2022-06-12 09:07:08 -05:00
Generics { def , params , parent_generics }
} ;
2022-04-02 06:04:04 -05:00
}
2019-12-07 04:50:36 -06:00
Generics { def , params : db . generic_params ( def ) , parent_generics }
}
2020-06-19 09:29:38 -05:00
#[ derive(Debug) ]
2019-12-07 04:50:36 -06:00
pub ( crate ) struct Generics {
def : GenericDefId ,
2021-04-04 20:50:10 -05:00
pub ( crate ) params : Interned < GenericParams > ,
2019-12-07 04:50:36 -06:00
parent_generics : Option < Box < Generics > > ,
}
impl Generics {
2022-03-09 12:50:24 -06:00
pub ( crate ) fn iter_id < ' a > (
2022-03-08 10:21:35 -06:00
& ' a self ,
2022-03-09 12:50:24 -06:00
) -> impl Iterator < Item = Either < TypeParamId , ConstParamId > > + ' a {
self . iter ( ) . map ( | ( id , data ) | match data {
TypeOrConstParamData ::TypeParamData ( _ ) = > Either ::Left ( TypeParamId ::from_unchecked ( id ) ) ,
TypeOrConstParamData ::ConstParamData ( _ ) = > {
Either ::Right ( ConstParamId ::from_unchecked ( id ) )
}
} )
}
/// Iterator over types and const params of parent, then self.
pub ( crate ) fn iter < ' a > (
& ' a self ,
) -> impl DoubleEndedIterator < Item = ( TypeOrConstParamId , & ' a TypeOrConstParamData ) > + ' a {
2022-06-12 09:07:08 -05:00
let to_toc_id = | it : & ' a Generics | {
move | ( local_id , p ) | ( TypeOrConstParamId { parent : it . def , local_id } , p )
} ;
self . parent_generics ( )
2022-03-08 10:21:35 -06:00
. into_iter ( )
2022-06-12 09:07:08 -05:00
. flat_map ( move | it | it . params . iter ( ) . map ( to_toc_id ( it ) ) )
. chain ( self . params . iter ( ) . map ( to_toc_id ( self ) ) )
2022-03-08 10:21:35 -06:00
}
2022-03-09 12:50:24 -06:00
/// Iterator over types and const params of parent.
2020-02-07 08:13:15 -06:00
pub ( crate ) fn iter_parent < ' a > (
& ' a self ,
2021-12-29 07:35:59 -06:00
) -> impl Iterator < Item = ( TypeOrConstParamId , & ' a TypeOrConstParamData ) > + ' a {
2022-06-12 09:07:08 -05:00
self . parent_generics ( ) . into_iter ( ) . flat_map ( | it | {
let to_toc_id =
move | ( local_id , p ) | ( TypeOrConstParamId { parent : it . def , local_id } , p ) ;
it . params . iter ( ) . map ( to_toc_id )
2020-02-07 08:13:15 -06:00
} )
2019-12-07 04:50:36 -06:00
}
2019-12-07 06:05:05 -06:00
pub ( crate ) fn len ( & self ) -> usize {
2022-06-12 09:07:08 -05:00
let parent = self . parent_generics ( ) . map_or ( 0 , Generics ::len ) ;
2022-03-09 12:50:24 -06:00
let child = self . params . type_or_consts . len ( ) ;
2022-06-12 09:07:08 -05:00
parent + child
2019-12-07 04:50:36 -06:00
}
2020-01-25 16:38:33 -06:00
2022-03-08 10:21:35 -06:00
/// (parent total, self param, type param list, const param list, impl trait)
pub ( crate ) fn provenance_split ( & self ) -> ( usize , usize , usize , usize , usize ) {
2022-06-12 09:07:08 -05:00
let ty_iter = | | self . params . iter ( ) . filter_map ( | x | x . 1. type_param ( ) ) ;
let self_params =
ty_iter ( ) . filter ( | p | p . provenance = = TypeParamProvenance ::TraitSelf ) . count ( ) ;
let type_params =
ty_iter ( ) . filter ( | p | p . provenance = = TypeParamProvenance ::TypeParamList ) . count ( ) ;
let impl_trait_params =
ty_iter ( ) . filter ( | p | p . provenance = = TypeParamProvenance ::ArgumentImplTrait ) . count ( ) ;
2022-03-09 12:50:24 -06:00
let const_params = self . params . iter ( ) . filter_map ( | x | x . 1. const_param ( ) ) . count ( ) ;
2022-06-12 09:07:08 -05:00
let parent_len = self . parent_generics ( ) . map_or ( 0 , Generics ::len ) ;
( parent_len , self_params , type_params , const_params , impl_trait_params )
2020-01-25 16:38:33 -06:00
}
2021-12-29 07:35:59 -06:00
pub ( crate ) fn param_idx ( & self , param : TypeOrConstParamId ) -> Option < usize > {
2020-01-31 09:52:43 -06:00
Some ( self . find_param ( param ) ? . 0 )
2019-12-07 04:50:36 -06:00
}
2020-01-25 16:38:33 -06:00
2021-12-29 07:35:59 -06:00
fn find_param ( & self , param : TypeOrConstParamId ) -> Option < ( usize , & TypeOrConstParamData ) > {
2019-12-07 04:50:36 -06:00
if param . parent = = self . def {
let ( idx , ( _local_id , data ) ) = self
. params
. iter ( )
. enumerate ( )
. find ( | ( _ , ( idx , _ ) ) | * idx = = param . local_id )
. unwrap ( ) ;
2022-06-12 09:07:08 -05:00
let parent_len = self . parent_generics ( ) . map_or ( 0 , Generics ::len ) ;
2020-04-05 11:24:18 -05:00
Some ( ( parent_len + idx , data ) )
2020-01-31 09:52:43 -06:00
} else {
2022-06-12 09:07:08 -05:00
self . parent_generics ( ) . and_then ( | g | g . find_param ( param ) )
2019-12-07 04:50:36 -06:00
}
}
2021-04-04 06:07:06 -05:00
2022-06-12 09:07:08 -05:00
fn parent_generics ( & self ) -> Option < & Generics > {
self . parent_generics . as_ref ( ) . map ( | it | & * * it )
}
2021-04-04 06:07:06 -05:00
/// Returns a Substitution that replaces each parameter by a bound variable.
2022-03-09 12:50:24 -06:00
pub ( crate ) fn bound_vars_subst (
& self ,
db : & dyn HirDatabase ,
debruijn : DebruijnIndex ,
) -> Substitution {
2021-04-04 06:07:06 -05:00
Substitution ::from_iter (
2021-12-19 10:58:39 -06:00
Interner ,
2022-03-09 12:50:24 -06:00
self . iter_id ( ) . enumerate ( ) . map ( | ( idx , id ) | match id {
Either ::Left ( _ ) = > GenericArgData ::Ty (
TyKind ::BoundVar ( BoundVar ::new ( debruijn , idx ) ) . intern ( Interner ) ,
)
. intern ( Interner ) ,
Either ::Right ( id ) = > GenericArgData ::Const (
ConstData {
value : ConstValue ::BoundVar ( BoundVar ::new ( debruijn , idx ) ) ,
ty : db . const_param_ty ( id ) ,
}
. intern ( Interner ) ,
)
. intern ( Interner ) ,
} ) ,
2021-04-04 06:07:06 -05:00
)
}
/// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`).
2022-03-09 12:50:24 -06:00
pub ( crate ) fn placeholder_subst ( & self , db : & dyn HirDatabase ) -> Substitution {
2021-04-04 06:07:06 -05:00
Substitution ::from_iter (
2021-12-19 10:58:39 -06:00
Interner ,
2022-03-09 12:50:24 -06:00
self . iter_id ( ) . map ( | id | match id {
Either ::Left ( id ) = > GenericArgData ::Ty (
TyKind ::Placeholder ( crate ::to_placeholder_idx ( db , id . into ( ) ) ) . intern ( Interner ) ,
)
. intern ( Interner ) ,
Either ::Right ( id ) = > GenericArgData ::Const (
ConstData {
value : ConstValue ::Placeholder ( crate ::to_placeholder_idx ( db , id . into ( ) ) ) ,
ty : db . const_param_ty ( id ) ,
}
. intern ( Interner ) ,
)
. intern ( Interner ) ,
2021-04-04 06:07:06 -05:00
} ) ,
)
}
2019-12-07 04:50:36 -06:00
}
2020-03-13 10:05:46 -05:00
fn parent_generic_def ( db : & dyn DefDatabase , def : GenericDefId ) -> Option < GenericDefId > {
2019-12-07 04:50:36 -06:00
let container = match def {
GenericDefId ::FunctionId ( it ) = > it . lookup ( db ) . container ,
GenericDefId ::TypeAliasId ( it ) = > it . lookup ( db ) . container ,
GenericDefId ::ConstId ( it ) = > it . lookup ( db ) . container ,
GenericDefId ::EnumVariantId ( it ) = > return Some ( it . parent . into ( ) ) ,
GenericDefId ::AdtId ( _ ) | GenericDefId ::TraitId ( _ ) | GenericDefId ::ImplId ( _ ) = > return None ,
} ;
match container {
2021-12-07 10:31:26 -06:00
ItemContainerId ::ImplId ( it ) = > Some ( it . into ( ) ) ,
ItemContainerId ::TraitId ( it ) = > Some ( it . into ( ) ) ,
ItemContainerId ::ModuleId ( _ ) | ItemContainerId ::ExternBlockId ( _ ) = > None ,
2019-12-07 04:50:36 -06:00
}
}
2022-04-07 11:33:03 -05:00
pub fn is_fn_unsafe_to_call ( db : & dyn HirDatabase , func : FunctionId ) -> bool {
let data = db . function_data ( func ) ;
if data . has_unsafe_kw ( ) {
return true ;
}
match func . lookup ( db . upcast ( ) ) . container {
hir_def ::ItemContainerId ::ExternBlockId ( block ) = > {
// Function in an `extern` block are always unsafe to call, except when it has
// `"rust-intrinsic"` ABI there are a few exceptions.
let id = block . lookup ( db . upcast ( ) ) . id ;
2022-06-12 09:07:08 -05:00
! matches! (
id . item_tree ( db . upcast ( ) ) [ id . value ] . abi . as_deref ( ) ,
Some ( " rust-intrinsic " ) if ! is_intrinsic_fn_unsafe ( & data . name )
)
2022-04-07 11:33:03 -05:00
}
_ = > false ,
}
}
/// Returns `true` if the given intrinsic is unsafe to call, or false otherwise.
fn is_intrinsic_fn_unsafe ( name : & Name ) -> bool {
// Should be kept in sync with https://github.com/rust-lang/rust/blob/532d2b14c05f9bc20b2d27cbb5f4550d28343a36/compiler/rustc_typeck/src/check/intrinsic.rs#L72-L106
! [
known ::abort ,
known ::add_with_overflow ,
known ::bitreverse ,
known ::black_box ,
known ::bswap ,
known ::caller_location ,
known ::ctlz ,
known ::ctpop ,
known ::cttz ,
known ::discriminant_value ,
known ::forget ,
known ::likely ,
known ::maxnumf32 ,
known ::maxnumf64 ,
known ::min_align_of ,
known ::minnumf32 ,
known ::minnumf64 ,
known ::mul_with_overflow ,
known ::needs_drop ,
known ::ptr_guaranteed_eq ,
known ::ptr_guaranteed_ne ,
known ::rotate_left ,
known ::rotate_right ,
known ::rustc_peek ,
known ::saturating_add ,
known ::saturating_sub ,
known ::size_of ,
known ::sub_with_overflow ,
known ::type_id ,
known ::type_name ,
known ::unlikely ,
known ::variant_count ,
known ::wrapping_add ,
known ::wrapping_mul ,
known ::wrapping_sub ,
]
. contains ( name )
}