2020-01-05 23:43:45 +01:00
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
2023-02-11 23:05:11 +00:00
use crate ::traits ::{ self , ObligationCause , ObligationCtxt } ;
2020-01-05 23:43:45 +01:00
2023-02-17 13:44:35 +00:00
use hir ::LangItem ;
2022-12-09 19:58:46 +00:00
use rustc_data_structures ::fx ::FxIndexSet ;
2020-01-05 23:43:45 +01:00
use rustc_hir as hir ;
2023-02-11 23:05:11 +00:00
use rustc_infer ::infer ::canonical ::Canonical ;
2022-12-09 20:59:26 +00:00
use rustc_infer ::infer ::{ RegionResolutionError , TyCtxtInferExt } ;
2023-02-12 19:32:07 +00:00
use rustc_infer ::traits ::query ::NoSolution ;
2022-12-09 20:59:26 +00:00
use rustc_infer ::{ infer ::outlives ::env ::OutlivesEnvironment , traits ::FulfillmentError } ;
2023-02-17 13:44:35 +00:00
use rustc_middle ::ty ::{ self , AdtDef , GenericArg , List , ParamEnv , Ty , TyCtxt , TypeVisitableExt } ;
2023-02-11 23:05:11 +00:00
use rustc_span ::DUMMY_SP ;
2020-02-22 11:44:18 +01:00
2022-12-09 19:58:46 +00:00
use super ::outlives_bounds ::InferCtxtExt ;
2020-01-05 23:43:45 +01:00
pub enum CopyImplementationError < ' tcx > {
2023-04-09 17:35:02 -04:00
InfringingFields ( Vec < ( & ' tcx ty ::FieldDef , Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
2020-01-05 23:43:45 +01:00
NotAnAdt ,
HasDestructor ,
}
2023-02-17 13:44:35 +00:00
pub enum ConstParamTyImplementationError < ' tcx > {
InfrigingFields ( Vec < ( & ' tcx ty ::FieldDef , Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
NotAnAdtOrBuiltinAllowed ,
}
2022-12-09 20:59:26 +00:00
pub enum InfringingFieldsReason < ' tcx > {
Fulfill ( Vec < FulfillmentError < ' tcx > > ) ,
Regions ( Vec < RegionResolutionError < ' tcx > > ) ,
}
2022-11-30 20:41:02 +00:00
/// Checks that the fields of the type (an ADT) all implement copy.
///
/// If fields don't implement copy, return an error containing a list of
2023-02-17 13:44:35 +00:00
/// those violating fields.
///
/// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`,
/// a reference or an array returns `Err(NotAnAdt)`.
2022-11-30 20:41:02 +00:00
pub fn type_allowed_to_implement_copy < ' tcx > (
2020-01-05 23:43:45 +01:00
tcx : TyCtxt < ' tcx > ,
param_env : ty ::ParamEnv < ' tcx > ,
self_type : Ty < ' tcx > ,
2022-06-03 19:17:12 -07:00
parent_cause : ObligationCause < ' tcx > ,
2020-01-05 23:43:45 +01:00
) -> Result < ( ) , CopyImplementationError < ' tcx > > {
2022-09-19 22:03:59 -05:00
let ( adt , substs ) = match self_type . kind ( ) {
// These types used to have a builtin impl.
// Now libcore provides that impl.
ty ::Uint ( _ )
| ty ::Int ( _ )
| ty ::Bool
| ty ::Float ( _ )
| ty ::Char
| ty ::RawPtr ( .. )
| ty ::Never
| ty ::Ref ( _ , _ , hir ::Mutability ::Not )
| ty ::Array ( .. ) = > return Ok ( ( ) ) ,
2020-01-05 23:43:45 +01:00
2023-02-17 13:44:35 +00:00
& ty ::Adt ( adt , substs ) = > ( adt , substs ) ,
2020-01-05 23:43:45 +01:00
2022-09-19 22:03:59 -05:00
_ = > return Err ( CopyImplementationError ::NotAnAdt ) ,
} ;
2020-01-05 23:43:45 +01:00
2023-02-17 13:44:35 +00:00
all_fields_implement_trait (
tcx ,
param_env ,
self_type ,
adt ,
substs ,
parent_cause ,
hir ::LangItem ::Copy ,
)
. map_err ( CopyImplementationError ::InfringingFields ) ? ;
if adt . has_dtor ( tcx ) {
return Err ( CopyImplementationError ::HasDestructor ) ;
}
Ok ( ( ) )
}
/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`.
///
/// If fields don't implement `ConstParamTy`, return an error containing a list of
/// those violating fields.
///
/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
pub fn type_allowed_to_implement_const_param_ty < ' tcx > (
tcx : TyCtxt < ' tcx > ,
param_env : ty ::ParamEnv < ' tcx > ,
self_type : Ty < ' tcx > ,
parent_cause : ObligationCause < ' tcx > ,
) -> Result < ( ) , ConstParamTyImplementationError < ' tcx > > {
let ( adt , substs ) = match self_type . kind ( ) {
// `core` provides these impls.
2023-03-03 18:02:11 +00:00
ty ::Uint ( _ )
| ty ::Int ( _ )
| ty ::Bool
| ty ::Char
| ty ::Str
| ty ::Array ( .. )
| ty ::Slice ( _ )
2023-05-17 03:00:19 +00:00
| ty ::Ref ( .. , hir ::Mutability ::Not )
| ty ::Tuple ( _ ) = > return Ok ( ( ) ) ,
2023-02-17 13:44:35 +00:00
& ty ::Adt ( adt , substs ) = > ( adt , substs ) ,
_ = > return Err ( ConstParamTyImplementationError ::NotAnAdtOrBuiltinAllowed ) ,
} ;
all_fields_implement_trait (
tcx ,
param_env ,
self_type ,
adt ,
substs ,
parent_cause ,
2023-02-23 11:54:09 +00:00
hir ::LangItem ::ConstParamTy ,
2023-02-17 13:44:35 +00:00
)
. map_err ( ConstParamTyImplementationError ::InfrigingFields ) ? ;
Ok ( ( ) )
}
/// Check that all fields of a given `adt` implement `lang_item` trait.
pub fn all_fields_implement_trait < ' tcx > (
tcx : TyCtxt < ' tcx > ,
param_env : ty ::ParamEnv < ' tcx > ,
self_type : Ty < ' tcx > ,
adt : AdtDef < ' tcx > ,
substs : & ' tcx List < GenericArg < ' tcx > > ,
parent_cause : ObligationCause < ' tcx > ,
lang_item : LangItem ,
) -> Result < ( ) , Vec < ( & ' tcx ty ::FieldDef , Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > > {
let trait_def_id = tcx . require_lang_item ( lang_item , Some ( parent_cause . span ) ) ;
2022-12-09 19:58:46 +00:00
2022-09-19 22:03:59 -05:00
let mut infringing = Vec ::new ( ) ;
for variant in adt . variants ( ) {
for field in & variant . fields {
2022-11-30 20:41:02 +00:00
// Do this per-field to get better error messages.
let infcx = tcx . infer_ctxt ( ) . build ( ) ;
let ocx = traits ::ObligationCtxt ::new ( & infcx ) ;
2023-01-11 20:26:12 +00:00
let unnormalized_ty = field . ty ( tcx , substs ) ;
if unnormalized_ty . references_error ( ) {
2022-09-19 22:03:59 -05:00
continue ;
2020-01-05 23:43:45 +01:00
}
2022-12-09 20:59:26 +00:00
let field_span = tcx . def_span ( field . did ) ;
let field_ty_span = match tcx . hir ( ) . get_if_local ( field . did ) {
Some ( hir ::Node ::Field ( field_def ) ) = > field_def . ty . span ,
_ = > field_span ,
} ;
2022-09-19 22:03:59 -05:00
// FIXME(compiler-errors): This gives us better spans for bad
// projection types like in issue-50480.
// If the ADT has substs, point to the cause we are given.
// If it does not, then this field probably doesn't normalize
// to begin with, and point to the bad field's span instead.
2022-12-09 20:59:26 +00:00
let normalization_cause = if field
2022-09-19 22:03:59 -05:00
. ty ( tcx , traits ::InternalSubsts ::identity_for_item ( tcx , adt . did ( ) ) )
. has_non_region_param ( )
{
parent_cause . clone ( )
} else {
2022-12-09 20:59:26 +00:00
ObligationCause ::dummy_with_span ( field_ty_span )
2022-09-19 22:03:59 -05:00
} ;
2023-01-11 20:26:12 +00:00
let ty = ocx . normalize ( & normalization_cause , param_env , unnormalized_ty ) ;
2022-11-30 20:41:02 +00:00
let normalization_errors = ocx . select_where_possible ( ) ;
2023-03-07 23:37:52 +00:00
// NOTE: The post-normalization type may also reference errors,
// such as when we project to a missing type or we have a mismatch
// between expected and found const-generic types. Don't report an
// additional copy error here, since it's not typically useful.
if ! normalization_errors . is_empty ( ) | | ty . references_error ( ) {
2023-02-17 13:44:35 +00:00
tcx . sess . delay_span_bug ( field_span , format! ( " couldn't normalize struct field ` {unnormalized_ty} ` when checking {tr} implementation " , tr = tcx . def_path_str ( trait_def_id ) ) ) ;
2022-11-30 20:41:02 +00:00
continue ;
}
2022-12-09 20:59:26 +00:00
ocx . register_bound (
ObligationCause ::dummy_with_span ( field_ty_span ) ,
param_env ,
ty ,
2023-02-17 13:44:35 +00:00
trait_def_id ,
2022-12-09 20:59:26 +00:00
) ;
let errors = ocx . select_all_or_error ( ) ;
if ! errors . is_empty ( ) {
infringing . push ( ( field , ty , InfringingFieldsReason ::Fulfill ( errors ) ) ) ;
2022-11-30 20:41:02 +00:00
}
2022-12-09 19:58:46 +00:00
// Check regions assuming the self type of the impl is WF
let outlives_env = OutlivesEnvironment ::with_bounds (
param_env ,
infcx . implied_bounds_tys (
param_env ,
parent_cause . body_id ,
FxIndexSet ::from_iter ( [ self_type ] ) ,
) ,
) ;
2022-12-09 20:59:26 +00:00
let errors = infcx . resolve_regions ( & outlives_env ) ;
if ! errors . is_empty ( ) {
infringing . push ( ( field , ty , InfringingFieldsReason ::Regions ( errors ) ) ) ;
2022-11-30 20:41:02 +00:00
}
2020-01-05 23:43:45 +01:00
}
2022-09-19 22:03:59 -05:00
}
2022-11-30 20:41:02 +00:00
2023-02-17 13:44:35 +00:00
if infringing . is_empty ( ) { Ok ( ( ) ) } else { Err ( infringing ) }
2020-01-05 23:43:45 +01:00
}
2023-02-11 23:05:11 +00:00
2023-02-12 19:32:07 +00:00
pub fn check_tys_might_be_eq < ' tcx > (
2023-02-11 23:05:11 +00:00
tcx : TyCtxt < ' tcx > ,
canonical : Canonical < ' tcx , ( ParamEnv < ' tcx > , Ty < ' tcx > , Ty < ' tcx > ) > ,
2023-02-12 19:32:07 +00:00
) -> Result < ( ) , NoSolution > {
2023-02-11 23:05:11 +00:00
let ( infcx , ( param_env , ty_a , ty_b ) , _ ) =
tcx . infer_ctxt ( ) . build_with_canonical ( DUMMY_SP , & canonical ) ;
let ocx = ObligationCtxt ::new ( & infcx ) ;
let result = ocx . eq ( & ObligationCause ::dummy ( ) , param_env , ty_a , ty_b ) ;
// use `select_where_possible` instead of `select_all_or_error` so that
// we don't get errors from obligations being ambiguous.
let errors = ocx . select_where_possible ( ) ;
2023-02-12 19:32:07 +00:00
if errors . len ( ) > 0 | | result . is_err ( ) { Err ( NoSolution ) } else { Ok ( ( ) ) }
2023-02-11 23:05:11 +00:00
}