2020-01-05 23:43:45 +01:00
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
2024-08-11 12:10:36 -04:00
use std ::assert_matches ::assert_matches ;
2023-02-17 13:44:35 +00:00
use hir ::LangItem ;
2024-07-14 13:38:51 +01:00
use rustc_ast ::Mutability ;
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 ;
2024-06-01 14:12:34 -04:00
use rustc_infer ::infer ::outlives ::env ::OutlivesEnvironment ;
2022-12-09 20:59:26 +00:00
use rustc_infer ::infer ::{ RegionResolutionError , TyCtxtInferExt } ;
2024-10-18 00:28:43 +02:00
use rustc_middle ::ty ::{ self , AdtDef , Ty , TyCtxt , TypeVisitableExt , TypingMode } ;
2020-02-22 11:44:18 +01:00
2022-12-09 19:58:46 +00:00
use super ::outlives_bounds ::InferCtxtExt ;
2023-12-18 22:45:34 +00:00
use crate ::regions ::InferCtxtRegionExt ;
2024-06-01 14:12:34 -04:00
use crate ::traits ::{ self , FulfillmentError , ObligationCause } ;
2022-12-09 19:58:46 +00:00
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 ,
2024-12-06 17:11:36 +00:00
HasUnsafeFields ,
2020-01-05 23:43:45 +01:00
}
2023-02-17 13:44:35 +00:00
pub enum ConstParamTyImplementationError < ' tcx > {
2024-07-14 13:38:51 +01:00
UnsizedConstParamsFeatureRequired ,
2024-07-14 12:50:41 +01:00
InvalidInnerTyOfBuiltinTy ( Vec < ( Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
2023-02-17 13:44:35 +00:00
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)`.
2024-12-06 17:11:36 +00:00
///
/// If the impl is `Safe`, `self_type` must not have unsafe fields. When used to
/// generate suggestions in lints, `Safe` should be supplied so as to not
/// suggest implementing `Copy` for types with unsafe fields.
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 > ,
2024-12-06 17:11:36 +00:00
impl_safety : hir ::Safety ,
2020-01-05 23:43:45 +01:00
) -> Result < ( ) , CopyImplementationError < ' tcx > > {
2023-07-11 22:35:29 +01:00
let ( adt , args ) = match self_type . kind ( ) {
2020-01-05 23:43:45 +01:00
// 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
2021-06-05 17:17:35 -04:00
| ty ::Ref ( _ , _ , hir ::Mutability ::Not )
| ty ::Array ( .. ) = > return Ok ( ( ) ) ,
2020-01-05 23:43:45 +01:00
2023-07-11 22:35:29 +01:00
& ty ::Adt ( adt , args ) = > ( adt , args ) ,
2020-01-05 23:43:45 +01:00
_ = > return Err ( CopyImplementationError ::NotAnAdt ) ,
} ;
2023-02-17 13:44:35 +00:00
all_fields_implement_trait (
tcx ,
param_env ,
self_type ,
adt ,
2023-07-11 22:35:29 +01:00
args ,
2023-02-17 13:44:35 +00:00
parent_cause ,
hir ::LangItem ::Copy ,
)
. map_err ( CopyImplementationError ::InfringingFields ) ? ;
if adt . has_dtor ( tcx ) {
return Err ( CopyImplementationError ::HasDestructor ) ;
}
2024-12-13 12:19:46 +00:00
if impl_safety . is_safe ( ) & & self_type . has_unsafe_fields ( ) {
2024-12-06 17:11:36 +00:00
return Err ( CopyImplementationError ::HasUnsafeFields ) ;
}
2023-02-17 13:44:35 +00:00
Ok ( ( ) )
}
2024-07-14 13:38:51 +01:00
/// Checks that the fields of the type (an ADT) all implement `(Unsized?)ConstParamTy`.
2023-02-17 13:44:35 +00:00
///
2024-07-14 13:38:51 +01:00
/// If fields don't implement `(Unsized?)ConstParamTy`, 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` 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 > ,
2024-07-14 13:38:51 +01:00
lang_item : LangItem ,
2023-02-17 13:44:35 +00:00
parent_cause : ObligationCause < ' tcx > ,
) -> Result < ( ) , ConstParamTyImplementationError < ' tcx > > {
2024-08-11 12:10:36 -04:00
assert_matches! ( lang_item , LangItem ::ConstParamTy | LangItem ::UnsizedConstParamTy ) ;
2023-02-17 13:44:35 +00:00
2024-07-14 12:50:41 +01:00
let inner_tys : Vec < _ > = match * self_type . kind ( ) {
// Trivially okay as these types are all:
// - Sized
// - Contain no nested types
// - Have structural equality
ty ::Uint ( _ ) | ty ::Int ( _ ) | ty ::Bool | ty ::Char = > return Ok ( ( ) ) ,
2024-07-14 13:38:51 +01:00
// Handle types gated under `feature(unsized_const_params)`
// FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references
ty ::Slice ( inner_ty ) | ty ::Ref ( _ , inner_ty , Mutability ::Not )
if lang_item = = LangItem ::UnsizedConstParamTy = >
{
vec! [ inner_ty ]
}
ty ::Str if lang_item = = LangItem ::UnsizedConstParamTy = > {
vec! [ Ty ::new_slice ( tcx , tcx . types . u8 ) ]
}
ty ::Str | ty ::Slice ( .. ) | ty ::Ref ( _ , _ , Mutability ::Not ) = > {
return Err ( ConstParamTyImplementationError ::UnsizedConstParamsFeatureRequired ) ;
}
2024-07-14 12:50:41 +01:00
2024-07-14 13:38:51 +01:00
ty ::Array ( inner_ty , _ ) = > vec! [ inner_ty ] ,
2024-07-14 12:50:41 +01:00
2024-07-14 13:38:51 +01:00
// `str` morally acts like a newtype around `[u8]`
2024-07-14 12:50:41 +01:00
ty ::Tuple ( inner_tys ) = > inner_tys . into_iter ( ) . collect ( ) ,
ty ::Adt ( adt , args ) if adt . is_enum ( ) | | adt . is_struct ( ) = > {
all_fields_implement_trait (
tcx ,
param_env ,
self_type ,
adt ,
args ,
parent_cause . clone ( ) ,
2024-07-14 13:38:51 +01:00
lang_item ,
2024-07-14 12:50:41 +01:00
)
. map_err ( ConstParamTyImplementationError ::InfrigingFields ) ? ;
vec! [ ]
}
2023-02-17 13:44:35 +00:00
_ = > return Err ( ConstParamTyImplementationError ::NotAnAdtOrBuiltinAllowed ) ,
} ;
2024-07-14 12:50:41 +01:00
let mut infringing_inner_tys = vec! [ ] ;
for inner_ty in inner_tys {
// We use an ocx per inner ty for better diagnostics
2024-10-18 00:28:43 +02:00
let infcx = tcx . infer_ctxt ( ) . build ( TypingMode ::non_body_analysis ( ) ) ;
2024-07-14 12:50:41 +01:00
let ocx = traits ::ObligationCtxt ::new_with_diagnostics ( & infcx ) ;
ocx . register_bound (
parent_cause . clone ( ) ,
param_env ,
inner_ty ,
2024-07-14 13:38:51 +01:00
tcx . require_lang_item ( lang_item , Some ( parent_cause . span ) ) ,
2024-07-14 12:50:41 +01:00
) ;
let errors = ocx . select_all_or_error ( ) ;
if ! errors . is_empty ( ) {
infringing_inner_tys . push ( ( inner_ty , InfringingFieldsReason ::Fulfill ( errors ) ) ) ;
continue ;
}
// 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 ] ) ,
) ,
) ;
let errors = infcx . resolve_regions ( & outlives_env ) ;
if ! errors . is_empty ( ) {
infringing_inner_tys . push ( ( inner_ty , InfringingFieldsReason ::Regions ( errors ) ) ) ;
continue ;
}
}
if ! infringing_inner_tys . is_empty ( ) {
return Err ( ConstParamTyImplementationError ::InvalidInnerTyOfBuiltinTy (
infringing_inner_tys ,
) ) ;
}
2023-02-17 13:44:35 +00:00
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 > ,
2024-05-09 19:47:08 +00:00
args : ty ::GenericArgsRef < ' tcx > ,
2023-02-17 13:44:35 +00:00
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
2020-01-05 23:43:45 +01:00
let mut infringing = Vec ::new ( ) ;
2022-03-05 07:28:41 +11:00
for variant in adt . variants ( ) {
2020-01-05 23:43:45 +01:00
for field in & variant . fields {
2022-11-30 20:41:02 +00:00
// Do this per-field to get better error messages.
2024-10-18 00:28:43 +02:00
let infcx = tcx . infer_ctxt ( ) . build ( TypingMode ::non_body_analysis ( ) ) ;
2024-06-01 14:51:31 -04:00
let ocx = traits ::ObligationCtxt ::new_with_diagnostics ( & infcx ) ;
2022-11-30 20:41:02 +00:00
2023-07-11 22:35:29 +01:00
let unnormalized_ty = field . ty ( tcx , args ) ;
2023-01-11 20:26:12 +00:00
if unnormalized_ty . references_error ( ) {
2022-09-09 15:08:06 -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-02-06 15:57:29 -08:00
// FIXME(compiler-errors): This gives us better spans for bad
// projection types like in issue-50480.
2023-07-11 22:35:29 +01:00
// If the ADT has args, point to the cause we are given.
2022-02-06 15:57:29 -08:00
// 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
2023-07-11 22:35:29 +01:00
. ty ( tcx , traits ::GenericArgs ::identity_for_item ( tcx , adt . did ( ) ) )
2022-10-04 09:43:34 +00:00
. has_non_region_param ( )
2022-09-19 22:03:59 -05:00
{
2022-06-03 19:17:12 -07:00
parent_cause . clone ( )
2022-09-19 22:03:59 -05:00
} 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-12-18 22:21:37 +11:00
tcx . dcx ( ) . span_delayed_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 ,
2024-01-16 18:39:00 -05:00
infcx . implied_bounds_tys (
2022-12-09 19:58:46 +00:00
param_env ,
parent_cause . body_id ,
2023-12-10 20:13:21 -05:00
& FxIndexSet ::from_iter ( [ self_type ] ) ,
2022-12-09 19:58:46 +00:00
) ,
) ;
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
}